Jump to content

Josh

Staff
  • Posts

    23,141
  • Joined

  • Last visited

Blog Entries posted by Josh

  1. Josh
    I created a test of 1000 animated crawler models to see how the performance of Vulkan stacks up against our older OpenGL renderer. Here it is in OpenGL running at 37 FPS, which is pretty respectable considering how many animations are playing (and about 4 million polygons).

    With Vulkan the same test yields a framerate of 66 FPS, 78% faster than the OpenGL version.

    Here is a video of the characters animating. Each skeleton is its own unique animation system, there are no shared skeletons in this demo.
     
  2. Josh
    Leadwerks 5 uses a different engine architecture with a game loop that runs at either 30 (default) or 60 updates per second. Frames are passed to the rendering thread, which runs at an independent framerate that can be set to 60, 90, or unlimited. This is great for performance but there are some challenges in timing. In order to smooth out the motion of the frames, the results of the last two frames received are interpolated between. Animation is a big challenge for this. There could potentially be many, many bones, and interpolating entire skeletons could slow down the renderer.
    In the screen capture below, I have slowed the game update loop down to 5 updates per second to exaggerate the problem that occurs when no interpolation is used:

    My solution was to upload the 4x4 matrices of the previous two frames and perform the tweening inside the vertex shader:
    //Vertex Skinning mat4 animmatrix[8]; for (int n=0; n<4; ++n) { if (vertex_boneweights[n] > 0.0f) { animmatrix[n] = GetAnimationMatrix(vertex_boneindices[n],0); animmatrix[n + 4] = GetAnimationMatrix(vertex_boneindices[n],1); } } vec4 vertexpos = vec4(vertex_position,1.0f); vec4 modelvertexposition; for (int n=0; n<4; ++n) { if (vertex_boneweights[n] > 0.0f) { modelvertexposition += animmatrix[n] * vertexpos * vertex_boneweights[n] * rendertweening + animmatrix[n+4] * vertexpos * vertex_boneweights[n] * (1.0f - rendertweening); } } modelvertexposition = entitymatrix * modelvertexposition; Bone matrices are retrieved from an RGBA floating point texture with this function:
    mat4 GetAnimationMatrix(const in int index, const in int frame) { ivec2 coord = ivec2(index * 4, gl_InstanceID * 2 + frame); mat4 bonematrix; bonematrix[0] = texelFetch(texture14, coord, 0); bonematrix[1] = texelFetch(texture14, coord + ivec2(1,0), 0); //bonematrix[2] = texelFetch(texture14, coord + ivec2(2,0), 0); bonematrix[2].xyz = cross(bonematrix[0].xyz,bonematrix[1].xyz); //removes one texture lookup! bonematrix[2].w = 0.0f; bonematrix[3] = texelFetch(texture14, coord + ivec2(3,0), 0); return bonematrix; } This slows down the shader because up to 24 texel fetches might be performed per vertex, but it saves the CPU from having to precompute interpolated matrices for each bone. In VR, I think this cost savings is critical. Doing a linear interpolation between vertex positions is not exactly correct, but it's a lot faster than slerping a lot of quaternions and converting them to matrices, and the results are so close you can't tell any difference.
    There's actually a similar concept in 2D animation I remember reading about.when I was a kid. The book is called The Illusion of Life: Disney Animation and it's a really interesting read with lots of nice illustrations.

    Here is the same scene with interpolation enabled. It's recorded at 15 FPS so the screen capture still looks a little jittery, but you get the idea: Adding interpolation brought this scene down to 130 FPS from 200 on an Intel chip, simply because of the increased number of texel fetches in the vertex shader. Each character consists of about 4000 vertices. I expect on a discrete card this would be running at pretty much the max framerate (1000 or so).

    With this in place, I can now confirm that my idea for the fast rendering architecture in Leadwerks Game Engine 5 definitely works.
    The next step will be to calculate animations on a separate thread (or maybe two). My test scene here is using a single skeleton shared by all characters, but putting the animation on its own thread will allow many more characters to all be animated uniquely.
  3. Josh
    Today we are pleased to announce the release of Leadwerks Game Engine: Enterprise Edition, a standalone version of our popular 3D development software. The Enterprise Edition allows business users to install and use Leadwerks without the need for the Steam client. The new product joins the existing Standard Edition with Lua scripting and the Professional Edition with C++ and Visual Studio support, both sold on Steam.
    The Enterprise Edition has already been approved for sale through NASA’s ACES approval program for software, and NASA has joined a lineup of business customers using Leadwerks products that includes Lockheed Martin, Northrop Grumman, and the British Royal Navy.
    In the near future the capabilities of our software will continue to expand to support the needs of business customers in the VR and simulation spaces. The development of Leadwerks Game Engine 5 is being directed with a focus on performance, hyperrealism, and improved ease of use.
    Leadwerks Game Engine: Enterprise Edition is available to purchase for $499 on our website.

    Image courtesy of NASA Satellite Servicing Projects Division.
  4. Josh
    We've added a new website feature called Projects to help teams collaborate on their games. A project can be created with several privacy features so you can use this for public open-source games everyone can participate in, or for your team's secret project. I myself have started a project I intend to develop to demonstrate Leadwerks multiplayer capabilities:
    You can add a forum, blog, and downloads section to your project and use it to host files, carry out discussions, and post updates to your team. The project creator can also moderate the content and has the ability to invite and approve new members.
    I hope you find this feature useful for team development.
  5. Josh
    A new build is available on the beta branch on Steam. This fixes a couple of issues.
    Oculus view projection is fixed. Added VR.AButton, VR.BButton, VR.GripAxis for compatibility with Oculus Touch controllers:
    https://www.leadwerks.com/community/topic/17052-vive-and-oculus-controls/ Fixed terrain collision bug:
    https://www.leadwerks.com/community/topic/16985-character-controller-falls-through-terrain/
  6. Josh
    I have the basis of CSG editing written into the engine. It was a bit challenging to figure out just how to structure the Brush class. We have a lightweight ConvexHull entity, which is used for the camera frustum. This is an invisible volume that changes frequently, and is used to test whether objects intersect the camera's field of view. I didn't want to bloat this class with a lot of renderable surfaces, texture coordinates, vertex normals, etc. I initially thought I would derive the Brush class from the ConvexHull class, and make brushes a more complex renderable "thing" in the world.
     
    I didn't want to create multiple rendering pathways in the octree system, so it made sense to derive the Brush class from the Entity class, so all the entity rendering pathways could be used to tell whether a brush should be drawn. However, both the Entity and ConvexHull classes are derived from the base class in Leadwerks3D, the Object class. This creates the diamond inheritance problem, where there are two Objects a Brush is derived from:

     
    In the end, I derived the Brush class from the Entity class, and just gave it a ConvexHull for a member. This meets the needs of the system and is simple:

    class Brush : public Entity { ConvexHull* convexhull; ... };
     
    So with that out of the way, it's on to editing. There are three types of objects you can create in the Leadwerks3D editor. You can drag a model into the scene from the asset browser. You can create and modify brushes like in 3D World Studio. You can also create special entities like lights, particle emitters, sound emitters, and other items.
     
    I'm ready to start CSG editing, but the thing that strikes me right away is I have no idea how object creation should work. You can drag models into the scene right now, from the Asset Browser. However, CSG brushes work better when they are drawn like architecture drafting:

     
    For "point entities" 3D World Studio lets you position a crosshair, then hit enter to create the current object:

     
    I'm not sure how we should handle brush and model editing in here. Usually when I run into a problem and am not sure how to proceed, I struggle with it a few days, but when I'm done, I am certain the approach I chose was best. Here's another one of those situations. Feel free to discuss and explore ideas with me in the comments below. Here's what the editor looks like at the moment:

     
    --Edit--
     
    Here's something I am playing with. The "Objects" menu contains a lot of brushes and special entities you can create:

     
    This idea actually goes back to the old Quake 3 Arena editor, Radiant (which was otherwise a pretty difficult tool to use!):

     
    You would still be able to drag a model into the scene from the Asset Browser at any time, but the brush and entity creation would be more like 3D World Studio. Of course, we also want the ability to move, rotate, and scale things in the 3D viewport as well! I generally don't like having multiple ways to do a thing, but the editing choices here make good sense. I'll keep playing with ideas and see what I come up with. Feel free to chime in, in the comments below.
  7. Josh
    This is great news. I was a bit worried about the state of OpenGL on the Mac, since they presently only support version 2.1. With OpenGL3 support, we can have a uniform graphics API on Macintosh, Linux, Windows XP, Windows Vista, and Windows 7.
    http://www.theinquirer.net/inquirer/news/1585830/apple-slowly-opengl
  8. Josh
    I was thinking this morning how Apple has influenced my life and the company Leadwerks. You might ask why I am talking about this, since we are only now coming out with our first product for any Apple operating system. Well, before Windows existed, back in the 1980's, this was my first computer:
     

     
    The first "game" I ever made was in Macintosh Paint. I drew a road from a top-down view, with barriers randomly placed on either side. I drew a race car, zoomed in the image, and used the selection lasso to cut it from the page. By dragging the selected pixels to the top of the screen, I could make the image scroll, causing the road to move beneath my car, and I had to move the mouse left and right to avoid the barriers. (I tried to forget the order I had placed them in.)
     
    I also used this machine to paint my first textures, before texture mapping was invented. I drew sides of buildings with variations with windows and doors. Although the monitor displayed only two colors, black and white, you could approximate gray by stippling pixels. There was also the option to use colored pencils once you printed out the images. I would print these on paper and then construct buildings out of paper with them, a precursor to the work we would do later with CSG modeling.
     
    My uncle also wrote some of the early Mac games, including "Save the Farm", and "Hot Air Balloon". I actually have the original apps and amazingly they run on my 2010 iMac with an emulator.
     

     
    Last year, as we began development of Leadwerks3D, I bought my first Apple products in my adult life, including a 27" iMac, iPhone 4, and iPad 2. I've been really impressed with the quality and design of these products, and although I am not quite sure their OpenGL 3.2 drivers are there yet, I think high-end graphics on Mac has a good future. (I had it confirmed from another developer that OpenGL 3.2 MRTs do work on Mac, so I still have some experimentation to do. I suspect the multisampled texture format I am using may be causing a problem...) Ever since I started putting out videos of our new engine running on mobile devices, there has been a tremendous response from developers, both inside the Leadwerks community and beyond.
     
    I think the rise of the iPhone as a gaming device was sort of accidental. They had a device that could run apps, then they discovered people were playing games more and more on them. However, all indie game developers should be grateful Apple developed a thriving ecosystem for third party developers, because without them I don't think we would see the resurgence of indie game development like we have. Even if you are developing primarily for Android, you have to admit that platform would not be as good as it is without the influence of iOS.
     

     
    In a sea of mediocrity, one man cared about his work enough to make sure that everything his company put out was a cohesive reflection of his tastes and standards. I don't agree with every decision Apple has made, but I have a great deal of respect for the way Steve Jobs had a coherent vision and brought it to life, instead of just blindly reacting to what he thought would make money. He rejected the "low quality, low confidence, low price, low effort" approach the rest of the tech industry has taken, and won. Without him I think Leadwerks would be without what I think will be our most successful platforms, and indie game developers would not have the same opportunities that are present today. We should all be grateful for that.
     


     

    http://www.youtube.com/watch?v=uwOjsYNcUW0
  9. Josh
    Three things I am working on right now:
    We need a high quality default skybox. I'm pretty happy with the results I got, after a lot of experimentation and different programs. This will be a whole huge blog post itself.
    Commissioning development of one high-end character model. I'm going with a fairly expensive option this time. My goal is to just get ONE character I am happy with, with the idea that if we can do it once we can do it again. More on this later.
    Investigating a better default vwep. Nothing more to say about it at the moment.

     
    This is very difficult stuff to progress on, but I think it is critical to develop the company's productive capability for game artwork.
  10. Josh
    I'm testing the Leadwerks3D AI navigation and getting some interesting results. I'm not sure why it's acting this way, but we'll get it figured out soon. It seems like the navigation gets "stuck" on corners, fails to enter some tiles, and likes to take the scenic route to some destinations. B)
     


  11. Josh
    Our all-purpose action figure concept is complete, and he looks fantastic.
     

     
    I am reminded of the great artwork on the packaging of GI Joe action figures from the 80s.
     

     
    Rich has had some difficulty modeling a higher-detail pickup truck, inspired by the design of a Dodge Ram. According to him, curved vehicle bodies are much more difficult to get right than more angular designs. So that tells me in the future, we can get a jeep or something done much more easily than a Corvette, for example. But it's coming along.
     

  12. Josh
    There's a discussion on the forum that sort of veered into talking about Khronos' GLTF file format specification:
    Some of this gave me some ideas for changes in the art pipeline in Turbo Game Engine. I was not feeling very concentrated today so I decided to do some easy work and implement a loader class:
    class Loader : public SharedObject { public: std::vector<wstring> extensions; virtual bool Reload(shared_ptr<Stream> stream, shared_ptr<SharedObject> o, const int flags = 0)=0; }; Then I created a TEX texture loader class:
    class TEXTextureLoader : public Loader { public: virtual bool Reload(shared_ptr<Stream> stream, shared_ptr<SharedObject> o, const int flags = 0)=0; }; When the engine is initialized it creates a TEXTexLoader object and saves it in a list of texture loaders:
    GameEngine::Initialize() { textureloaders.push_back(make_shared<TEXTextureLoader>()); } And the class constructor itself creates an array of file extensions the loader supports:
    TEXTextureLoader::TEXTextureLoader() { extensions = { "tex" }; } When a texture is loaded, the engine looks for a texture loader that matches the extension or the loaded file path.
    At this point, all we have done is restructured the engine to do the same thing it did before, with more code. But now we can add new loaders for other file formats. And that is exactly what I did. You can now load textures directly from PNG files:
    class PNGTextureLoader : public Loader { public: virtual bool Reload(shared_ptr<Stream> stream, shared_ptr<SharedObject> o, const int flags = 0)=0; }; I've done the same thing for model file loading as well, although I have not yet added support for any other file types.
    If we can add a C++ loader, we should be able to add one with Lua too. I haven't worked out the details yet, but it could work something like this:
    function LoadModelObj( stream, model ) while stream:EOF() == false do -------------- end end AddModelLoader(LoadModelObj) So with that, you could download a Lua script that would add support for loading a new file format.
    I think in the new engine we will support all common image formats, including Photoshop PSD, as well as DDS, KTX, and our existing TEX files. Textures loaded from image formats will be less optimal because mipmaps will be generated on loading. Settings like clamping and filter modes will be stored in the associated .meta files, which means these files will start getting published along with your game asset files. This is a change from the Leadwerks 4 way of doing things.
  13. Josh
    We're finally offering our first product in the new assets section of the site. I have wanted for a long time to have a means where assets can be produced and sold in a sustainable manner. I am very interested in working with third-party sites that produce game artwork. Orders are still processed by hand, but we're working on a system to automate things. You can be sure I won't let anything into the product listing unless it is 100% game-ready for Leadwerks Engine.
  14. Josh
    Once again, I like to attack the things I haven't done in the past first. I know how to make a CSG editor. What I haven't done in the past is a visual editor for gameplay and logic.
     
    In the shot below, you can see the properties editor, with the "Scripts" tab selected. I moved the name and angle editor outside the tabbed panel, with the idea that these are so important they belong in the main window, but the real reason was to break up the boredom of a full-window tabbed panel. Under the scripts tab, there's a list on the left where you can drag scripts onto to assign a script to an entity. You can also drag the scripts around within the list to rearrange the order they are executed in. When a script item is selected in the list, its properties appear on the right in the bordered area. This has a border because if the number of controls exceeds the available height, a scrollbar will appear to allow you to scroll down and access all of them. Now I personally am not a fan of this, because I think it makes things more complicated, but the user feedback overwhelmingly suggests users want multiple scripts attached to a single object. I think this is so users can combine behaviors and adjust them through trial and error. This doesn't occur to me because for me it's no big deal to just open a single script and program it to do exactly what I want. However, this isn't as trivial for everyone, and for every programmer out there, there are probably 99 non-programmers. This is a good example of why we need to understand how everyone uses the software. The resolution we have satisfies the need for trial and error design, without restricting what you can do with it.

     
    You can drag an entity script or an entity into the flow graph editor to make its attached scripts appear. Each script has inputs and outputs, and you can connect one to another. I think I am going to put both script output functions and exposed attributes on the right, with a different icon for the two. Output functions can be linked to inputs on other scripts, while attributes can be linked along the curved line connecting the two, as function arguments. This is a cool idea I think will make it unique, and it only works due to the flexibility of Lua; you can skip function arguments, and the function call will be executed anyways without any problem.
     
    I'll have some special flowgraph nodes that only appear in the flowgraph editor for logic, timing, etc. It is not my goal to make a visual script editor. I don't think the challenge of programming is because people don't understand text code. The problem is thinking in an extremely logical and meticulous manner. A visual script doesn't get rid of this requirement, it just uses a different method to display the logic, one that will baffle both programmers and non-programmers alike. Think about (most) artists trying to design "Pong" with a flowgraph; I don't think it would work. Trying to code behavior through a visual representation of script is not something I want happening in the flowgraph, except on a fairly lightweight level. People are welcome to try it, but if we expected that everyone would be able to write complex programs with it, I think it would fail and we'd be back to the drawing board.
     
    My goal for the flowgraph system is to allow a designer to connect behaviors to create interesting sequences of events in a game. Individual behavior of objects will still be coded in script, in a manner designed to be as general-purpose as possible. A button opens a door, a cat chases a mouse, a switch turns an elevator on, then opens a trap door, and turns on a machine ten seconds later.
     
    Leadwerks Engine 2 had individual entity scripts, but without a standard system for interactions, there's little you can code in a self-contained script, other than some looping behavior. Setting names and targets is functional but rather abstract and cumbersome when working with complex interactions. With the Leadwerks flowgraph editor, we're all working together on the same game...yet the programmer still has complete control and freedom.
  15. Josh
    Keeping your social media accounts active with screenshots, videos, and updates is important, but as an indie developer you probably don't have time to post on there every day. In this blog I will show you how to easily automate your social media accounts so that a constant stream of new content is going out to your fans.
     
    First, you will need to create a free account with dlvr.it. Connect your social media accounts to it. Facebook, Twitter, and Google+ are the important ones.
     
    Now you need some RSS feeds to act as inputs. There is an RSS feed for your game's announcements here:
    http://steamcommunity.com/games/YOUR_APP_ID/rss/
     
    Set this RSS feed as an input in your dlvr.it account and it will make posts automatically to your social media account.
     
    We can use the Steam web API to create an RSS feed of your game's screenshots, videos, and Workshop items. The code below will do exactly that. Just input your game's app ID and a web API key you generate in the Steamworks interface:

    /*----------------------------------------------------*/ /* This code is free to use. Do not redistribute it. */
    /* Leadwerks Software 2016 */
    /*----------------------------------------------------*/
     
    $appID = 'xxxxxxx';
    $webAPIKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
    $recordCount=20;
    $fileType = '-1';
    $queryType = 1;
     
    function LoadXML($path)
    {
    $content = utf8_encode(file_get_contents($path));
    $content = str_replace(chr(11),'',$content);
    if (trim($content=='')) return NULL;
    return simplexml_load_string($content);
    }
     
    $steamcontent = @LoadXML('http://api.steampowered.com/IPublishedFileService/QueryFiles/v0001?key='.$webAPIKey.'&format=xml&query_type='.$queryType.'&page=1&numperpage='.$recordCount.'&appid='.$appID.'&filetype='.$fileType.'&return_vote_data=1&return_short_description=1');
     
    function clean($string)
    {
    return preg_replace('/[^A-Za-z0-9\- ().,!]/', '', $string); // Removes special chars.
    }
     
    function truncate($string, $length)
    {
    if (strlen($string)>$length)
    {
    return substr($string,0,$length-3)."...";
    }
    else
    {
    return substr($string,0,$length);
    }
    }
     
    echo('<?xml version="1.0" encoding="UTF-8" ?>'."\n");
    echo('<rss version="2.0">'."\n");
    echo('<channel>'."\n");
    echo("<title>Leadwerks Community Activity</title>"."\n");
    echo('<link>http://www.leadwerks.com</link>'."\n");
    echo("<description>Activity feed from Leadwerks Community Hub on Steam.</description>"."\n");
     
    for ($i=0; $i<min($recordCount,$steamcontent->total); $i=$i+1)
    {
    /* Only show screenshots above a certain score */
    if (($steamcontent->publishedfiledetails->message[$i]->vote_data->score>0.4 || $steamcontent->publishedfiledetails->message[$i]->file_type!=5) && $steamcontent->publishedfiledetails->message[$i]->result==1)
    {
    $file_type = $steamcontent->publishedfiledetails->message[$i]->file_type;
    if ($file_type!=0 && $file_type!=5 && $file_type!=4) continue;
    echo("<item>"."\n");
     
    $title = clean($steamcontent->publishedfiledetails->message[$i]->title);
    if ($title=="") $title = truncate(clean($steamcontent->publishedfiledetails->message[$i]->short_description),35);
    if ($title=="Screenshot") $title = "";
     
    /* Get author name */
    $userid = $steamcontent->publishedfiledetails->message[$i]->creator;
    $userdata = simplexml_load_file('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?format=xml&key='.$webAPIKey.'&steamids='.$userid);
    $username = $userdata->players->player[0]->personaname;
     
    if ($steamcontent->publishedfiledetails->message[$i]->file_type==0)
    {
    echo("\t<title>Workshop item by ".$username.": ".$title."</title>\n");
    }
    else if ($steamcontent->publishedfiledetails->message[$i]->file_type==5)
    {
    echo("\t<title>Screenshot by ".$username.": ".$title."</title>\n");
    }
    else if ($steamcontent->publishedfiledetails->message[$i]->file_type==4)
    {
    echo("\t<title>Video by ".$username.": ".$title."</title>\n");
    }
    else
    {
    continue;
    }
     
    if ($steamcontent->publishedfiledetails->message[$i]->file_type==4)
    {
    echo("\t<link>http://www.youtube.com/watch?v=".$steamcontent->publishedfiledetails->message[$i]->youtubevideoid."</link>\n");
    }
    else
    {
    echo("\t<link>http://steamcommunity.com/sharedfiles/filedetails/?id=".$steamcontent->publishedfiledetails->message[$i]->publishedfileid."</link>\n");
    }
     
    echo("\t<description>\n\t\t<![CDATA[");
     
    $description = clean($steamcontent->publishedfiledetails->message[$i]->short_description);
    $description = $description.' #gamedev #indiedev';
     
    if ($steamcontent->publishedfiledetails->message[$i]->file_type==4)
    {
    if (!empty($description)) echo('<p />'.$description);
    echo("<br />http://www.youtube.com/watch?v=".$steamcontent->publishedfiledetails->message[$i]->youtubevideoid);
    }
    else
    {
    echo("<p /><a href='http://steamcommunity.com/sharedfiles/filedetails/?id=".$steamcontent->publishedfiledetails->message[$i]->publishedfileid."'><img width='600px;' src='".$steamcontent->publishedfiledetails->message[$i]->preview_url."' /></a>");
    if (!empty($description)) echo('<br />'.$description);
    }
     
    echo("]]>\n\t</description>\n");
     
    if ($steamcontent->publishedfiledetails->message[$i]->file_type!=4)
    {
    echo("<enclosure url='".$steamcontent->publishedfiledetails->message[$i]->preview_url."' length='".$steamcontent->publishedfiledetails->message[$i]->file_size."' type='image/jpeg' />");
    }
     
    echo("<guid>".$steamcontent->publishedfiledetails->message[$i]->publishedfileid."</guid>");
    $displaytime = $steamcontent->publishedfiledetails->message[$i]->time_created;
    $displaytime = date('D, d M Y, h:m:s', intval($displaytime));
     
    echo("\t<pubDate>".$displaytime."</pubDate>\n");
     
    echo("\t<category>gamedev</category>\n");
    echo("\t<category>indiedev</category>\n");
    echo("\t<category>Leadwerks</category>\n");
     
    echo("</item>\n");
    }
    }
     
    echo("</channel>\n");
    echo("</rss>\n");
     
    There are a lot of settings in dlvr.it you can experiment with, but this is enough to get you running. Once this is set up you can keep your social media accounts active without having to log in and post items manually several times a day.
  16. Josh
    So, most of December was eaten up on some NASA VR projects. There was a conference last week in Seattle that I attended for a couple of days. Then I had meetings in northern California and Arizona.
    Unfortunately, I can't really talk much about what I am doing with those. Rest assured I am working on a plan to grow the company so we can provide better products and support for you. I'm taking a hit on productivity now in order to make a bigger plan happen.
    Today is my first day back home after all that, and I now have time to focus on the software. Thanks for your patience while I get this all sorted out.
  17. Josh
    It's pretty amazing how long the materials, textures, and shader system is taking, but it encompasses a lot of areas: Automatic asset reloading, autogenerated shaders, GLSL syntax highlighting, texture conversion options, and more. All this attention to detail is shaping up to create what I hope will be the best art pipeline, ever.
     
    The Asset Browser displays all files in your project directory. You don't have to use Windows Explorer at all anymore. You can rename, move, copy, cut, paste, and delete files, without ever leaving the editor:

     
    All recognized asset files are displayed in the editor, including materials, textures, shaders, sounds, scripts, models, and fonts:

     
    You can edit textures, materials, and shaders, and see how your changes interact in real-time:

  18. Josh
    In my last development blog, I wrote about some design problems I faced, and why I wasn't sure how to proceed. I've resolved these issues by building the debugger and game output display close to where they will be most likely to be used, the Script Editor.

     
    I was pleased to find that my Lua debugger code I wrote about a year ago works perfectly. This allows breakpoints, code stepping, and the display of variable values in your game. It works with any Leadwerks3D executable, so you can even examine and debug the Lua state in your C++ executables. (This does not debug C++ code or other languages, as there are already appropriate tools for that.) I like to combine the callstack and variable display into one tree view, so you can quickly navigate the program.
     
    I decided it made sense to build the game output and debugger into the script editor, because that is where you are most likely to use it. Even if you are debugging Lua in a C++ application, you still want to be able to double-click on the call stack and display a script in the editor. The only situation where this design doesn't really make sense is if you launch a C++ application and just want to look at the program's output. In that situation, it is a little strange to need to open the script editor to do that.
     
    The actual executable to be launched will be controlled in the project settings. The default for a Lua program will be a copy of the interpreter executable that gets added the project folder. You can specify the script in a command line field, like this:
    +script $scriptfile
     
    Where "$scriptfile" gets replaced with the file name of the currently selected script file in the script editor. For a C++ application, the project will simply be set to call the executable the C++ template sets up a project for. If any of that sounded confusing, take a look at how maps are launched in 3D World Studio (or even Valve's Hammer Editor, for that matter). The bottom line is it's set up automatically when a project is created, and you probably will never need to change it, but the capability is there if you need to.
     
    Previously I wrote that it's better to name a thing with a specific name that describes most of what it does, rather than to broaden the description to become fully encompassing but vague. The same holds true for program and workflow design. The debugger and game output display are built into the script editor, where they will be the most useful overall. It's not "correct" in a hyper-analytical way, but it makes sense and is convenient.
  19. Josh
    I came across a very interesting presentation that talks about how to avoid "banding" artifacts in game graphics. The author uses dithering to add noise to an image and break up the visible lines your eye can detect. This even works with audio. When noise is added to the mix, the original tune appears to become more high-fidelity than it actually is:
     


     
    I was able to use this concept to improve the appearance of banding in shadow acne in a low-resolution spotlight with a large volume. Here is the original situation, purposely created to maximize the banding effect:
     

     
    And here is the result when some slight dithering is added:
     

     
    The trick to dithering is to know how much noise to add. If we add too much it starts becoming very apparent:
     

     
    You want to calculate your noise amplitude as the difference between the two discrete levels you are trying to bridge. In the case of the spotlight shader, random noise is being added to the z coordinate of the shadow lookup, so I want the noise amplitude to be equal to the minimum depth difference the shadowmap can display. I calculated this as follows, but more experimentation is needed to make sure its right:
     

    float noiselevel = 0.000001 * lightrange.y; shadowcoord.z += rand(lightnormal.xy) * noiselevel - noiselevel * 0.5;
     
    You also want to make sure your random seed is really random. Using the screen coordinate alone produces bad results because the same random seeds will stay in place as you look around and cause visible patterns. If you use the "currenttime" shader uniform the noise will constantly change as the camera stays still, resulting in a film grain effect. I find it is best to multiply the xy components of the fragment coordinate by some other vec2 value.
     
    Here's another example with a directional light exhibiting a banding appearance due to a low angle on the ground:
     

     
    And here is the improved image with dithering applied:
     

     
    This code does not eliminate shadow acne, it just breaks it up with some random noise so that your eye can't as easily detect a continuous line.
     
    The same technique can be used to improve the appearance of the new godrays shader I am working on. With only 16 samples for the rays, banding is very easily visible in this image.
     

     
    When we add a random offset to the ray starting position, with an amplitude equal to the length of the distance between steps, banding disappears.
     

     
    Of course more samples are always better, but even at higher sample counts, dithering makes a huge improvement in quality. Leadwerks Engine 2 actually used 64 samples, with worse results than what I got using just 16 samples above. The ease with which I can now modify shaders in Leadwerks Editor and see the results instantly really helped develop these techniques.
  20. Josh
    I did some work optimizing the new renderer to discrete cards and came up with a new benchmark. This scene is interesting because there is not much in the scene, so the culling does not benefit much xrom multithreading, since there is so little work to do. The screen is filled with a spotlight in the foreground, and in the background a point light is visible in the distance. Both engines are running at 1280x720 resolution. This is really a pure test of deferred vs. forward lighting.
    AMD R9 200
    Leadwerks: 448 FPS
    Turbo: 648 FPS (44% faster)
    On this older AMD card we see that forward rendering automatically gives a significant boost in performance, likely due to not having to pass around a lot of big textures that are being written to.


    GEForce 1070M (Laptop)
    Leadwerks 4: 120 FPS
    Turbo: 400 FPS (333% faster)
    Here we see an even bigger performance boost. However, these are two very different cards and I don't think it's safe to say yet if one GPU vendor will get more from the new engine.
    Intel Graphics 4000
    Leadwerks 4: 67 FPS  (18% faster)
    Turbo: 57 FPS
    This is an interesting result because integrated graphics tend to struggle with pixel fillrate, but seem to have no problems with texture bandwidth since they are using regular old CPU memory for everything. Although I think the new engine will run faster on Intel graphics (especially newer CPUs) when the scene is loaded down, it seems to run a bit slower in a nearly empty scene. I think this demonstrates that if you build a renderer specifically to take advantage of one type of hardware (discrete GPUs) it will not automatically be optimal on other types of hardware with different architectures. I commented out the lighting code and the framerate shot way up, so it definitely does seem to be a function of the pixel shader, which is already about as optimized as can be.
    Overall, I think the new engine will show higher relative performance the more you throw at it, but an empty scene may run a bit slower on older Intel chips since the new renderer is optimized very specifically for discrete GPUs. The parallel capabilities of discrete GPUs are relied on to handle complex lighting in a single pass, while texture bandwidth is reduced to a minimum. This is in line with the hardware trend of computing power increasing faster than memory bandwidth. This also shows that low to mid-range discrete GPUs stand to make the most gains.
  21. Josh
    Last year Google and Binomial LLC partnered to release the Basic Universal library as open-source. This library is the successor to Crunch. Both these libraries are like OGG compression for textures. They compress data very well into small file sizes, but once loaded the data takes the same space in memory as it normally does. The benefit is that it can reduce the size of your game files. Crunch only supports DXT compression, but the newer Basis library supports modern compression formats like BC5, which gives artifact-free compressed normal maps, and BC7, which uses the same amount of space as DXT5 textures but pretty much eliminates blocky DXT artifacts on color images.
    I've added a Basis plugin to the Plugin SDK on Github so you can now load and save .basis texture files in the Leadwerks Game Engine 5 beta. Testing with a large 4096x2048 image in a variety of formats, here are my results.
    TGA (uncompressed): 24 MB PNG (lossless compression): 6.7 MB DDS (DXT1 compression): 5.33 MB Zipped DDS: 2.84 MB JPEG: 1.53 MB BASIS: 573 KB
    The zipped DXT option is the most like what you are using now in Leadwerks Game Engine 4. Since the overwhelming majority of your game size comes from texture files, we can extrapolate that Basis textures can reduce your game's data size to as little as 20% what it is now.
    With all the new image IO features, I wanted to write a script to convert texture files out of the Leadwerks 4 .tex file format and into something more transparent. If you drop the script below into the "Scripts/Start" folder it will automatically detect and convert .tex files into .dds:
    function BatchConvertTextures(path, in_ext, out_ext, recursive) local dir = LoadDir(path) for n = 1, #dir do local ftype = FileType(path.."/"..dir[n]) if ftype == 1 then if ExtractExt(dir[n]) == in_ext then local savefile = path.."/"..StripExt(dir[n]).."."..out_ext if FileTime(path.."/"..dir[n]) > FileTime(savefile) then local pixmap = LoadPixmap(path.."/"..dir[n]) if pixmap ~= nil then pixmap:Save(savefile) end end end elseif ftype == 2 and recursive == true then BatchConvertTextures(path.."/"..dir[n], in_ext, out_ext, true) end end end BatchConvertTextures(".", "tex", "dds", true) Here are my freightyard materials, out of the opaque .tex format and back into something I can easily view in Windows Explorer:

    These files are all using the original raw pixels as the .tex files, which internally are very similar to DDS. In order to convert them to .basis format, we need to get them into RGBA format, which means we need a DXT decompression routine. I found one and quickly integrated it, then revised my script to convert the loaded pixmaps to uncompressed RGBA format and save as PNG files.
    --Make sure FreeImage plugin is loaded if Plugin_FITextureLoader == nil then Plugin_FITextureLoader = LoadPlugin("Plugins/FITextureLoader.dll") end function BatchConvertTextures(path, in_ext, out_ext, recursive, format) local dir = LoadDir(path) for n = 1, #dir do local ftype = FileType(path.."/"..dir[n]) if ftype == 1 then if ExtractExt(dir[n]) == in_ext then local savefile = path.."/"..StripExt(dir[n]).."."..out_ext if FileTime(path.."/"..dir[n]) > FileTime(savefile) then --Load pixmap local pixmap = LoadPixmap(path.."/"..dir[n]) --Convert to desired format, if specified if format ~= nil then if pixmap ~= nil then if pixmap.format ~= format then pixmap = pixmap:Convert(format) end end end --Save if pixmap ~= nil then pixmap:Save(savefile) end end end elseif ftype == 2 and recursive == true then BatchConvertTextures(path.."/"..dir[n], in_ext, out_ext, true) end end end BatchConvertTextures("Materials/Freightyard", "tex", "png", true, TEXTURE_RGBA) Cool! Now I have all my .tex files back out as PNGs I can open and edit in any paint program. If you have .tex files that you have lost the source images for, there is now a way to convert them back.

    Now I am going to try converting this folder of .tex files into super-compressed .basis files. First I will add a line of code to make sure the Basis plugin has been loaded:
    --Make sure Basis plugin is loaded if Plugin_Basis == nil then Plugin_Basis = LoadPlugin("Plugins/Basis.dll") end And then I can simply change the save file extension to "basis" and it should work:
    BatchConvertTextures("Materials/Freightyard", "tex", "basis", true, TEXTURE_RGBA) And here it is! Notice the file size of the selected image.

    If you want to view basis textures in Windows explorer there is a handy-dandy thumbnail previewer available.
    Now let's see what the final file sizes are for this texture set.
    Uncompressed TEX files (DXT only): 35.2 MB Zipped TEX files: 25.8 MB BASIS: 7.87 MB ? When converted to Basis files the same textures take just 30% the size of the zipped package, and 22% the size of the extracted TEX files. That makes Basis Universal definitely worth using!
  22. Josh
    Along with vegetation, Leadwerks 3.7 is planned to include new features that turn the script editor into a powerful IDE. Auto-completion makes it easier to remember the command you are looking for and greatly enhances coding productivity. A built-in project explorer is also being added to make it easy to quickly navigate to and open any script or shader file in your project.
     

     
    These and other planned enhancements provide a powerful coding environment built seamlessly into Leadwerks Editor.
  23. Josh
    I wanted to work on something a bit different, and this sure is different. I've got a framework of a new particle system worked out. What's really special about this system is the amount of interactivity the particles will allow.
    Particle-world collisions. Particle-particle collisions (repulsion) Particle-particle cohesion (fluids with surface tension) Instead of just being a visual effect, I want our new particles to be fully interactive with physics so that particles can exert forces on objects. This will allow you to simulate fluids, smoke, and other effects in a realistic manner, not just dumb collision of particles bounding off walls. It should even be possible to simulate hydrophobic and hydrophillic liquids if you mix two together with different cohesion values.
    Basically what I want is something like Nvidia Flow on the CPU and exerting forces on the world. So if you had water falling on a water wheel the wheel would move because of the forces, or a blast of wind could knock objects over without any special force fields or other fake effects.
    I also have a technique worked out that will allow lighting of clouds and other masses of gas, with back-scattering.
    Emitters can be instanced so if you have one really high-quality torch effect, for example, you can instance it and use it as much as you like without any additional computational cost per instance.
    Particle emitters can be controlled with a Lua script or C++ actor. Two new functions are available, UpdateParticle() and EmitParticle(). Here is a script that controls particle behavior over time:
    entity.particleVelocity = Vec3(0,0,1) entity.particleAcceleration = Vec3(0,-1,0) entity.inverseSquareFalloff = true entity.particleRadiusBegin = 0.1 entity.particleRadiusEnd = 0.2 entity.particleColorBegin = Vec4(1,1,1,1) entity.particleColorEnd = Vec4(1,1,1,0) entity.particleMass = 1 entity.particleSpin = 5 function entity:Start() self.particleColorBeginHSL = HSL(self.particleColorBegin.rgb) self.particleColorEndHSL = HSL(self.particleColorEnd.rgb) local emitter = Emitter(self) if emitter == nil then return end local n for n = 1, #emitter.particles do emitter.particles[n].mass = self.particleMass emitter.particles[n].falloff = (n-1) / (#emitter.particles - 1) end end function entity:EmitParticle(index) local emitter = Emitter(self) if emitter == nil then return end emitter.particles[index].position = self:GetPosition(true) emitter.particles[index].velocity = TransformVector(self.particleVelocity,self,nil) emitter.particles[index].radius = self.particleRadiusBegin emitter.particles[index].color = self.particleColorBegin end function entity:UpdateParticle(index) local emitter = Emitter(self) if emitter == nil then return end emitter.particles[index].velocity = emitter.particles[index].velocity + self.particleAcceleration / 60 local falloff = emitter.particles[index].falloff if self.inverseSquareFalloff then falloff = falloff * falloff end emitter.particles[index].color.rgb = RGB(self.particleColorBeginHSL * (1 - falloff) + self.particleColorEndHSL * falloff) emitter.particles[index].color.a = self.particleColorBegin.a * (1 - falloff) + self.particleColorEnd.a * falloff emitter.particles[index].radius = self.particleRadiusBegin * (1 - falloff) + self.particleRadiusEnd * falloff emitter.particles[index].rotation = emitter.particles[index].rotation + self.particleSpin / 60 end A different script could be used to make particles emit from vertices of a model, to make the model appear to be on fire, or other effects. This will allow infinite customization to create any behavior you want.
    Particle physics will be calculated on the physics thread so I expect them to be very fast.
  24. Josh
    If you prefer the classic start menu like I do, there are several solutions available to get the classic start menu in Windows 7. My favorite is Classic Shell.
     
    Classic Start Menu is a clone of the original start menu, which you can find in all versions of Windows from 95 to Vista. It has a variety of advanced features:

    Drag and drop to let you organize your applications
    Options to show Favorites, expand Control Panel, etc
    Shows recently used documents. The number of documents to display is customizable
    Translated in 35 languages, including Right-to-left support for Arabic and Hebrew
    Does not disable the original start menu in Windows. You can access it by Shift+Click on the start button
    Right-click on an item in the menu to delete, rename, sort, or perform other tasks
    Available for 32 and 64-bit operating systems
    Has support for skins, including additional 3rd party skins
    Fully customizable in both looks and functionality
    Support for Microsoft’s Active Accessibility
    And last but not least – it's FREE!

     

×
×
  • Create New...