Jump to content

Josh

Staff
  • Posts

    23,516
  • Joined

  • Last visited

Blog Entries posted by Josh

  1. Josh
    Based on a little observation, I've made the following changes to the publish routine on the beta branch:
    The FPS Player script has been updated so that the footstep sound files are explicitly named, so they don't get missed in the publish step. You will need to update your project to get the new script (or just copy FPSPlayer.lua to your project).
    There is now a checkbox to "Only include used files". If this is checked, then the new packing routine will be used, otherwise the old behavior of including all files is used.
    The file extension filter will now be used so you can include files like XML, TXT, or whatever else you want.
    The routine has been fixed to pick up terrain textures and include those.
    A few people suggested a "tagging" system so the user can mark files to be included or not. This is a very bad idea. The whole point of the selective file inclusion system is that the user doesn't want to maintain a clean directory. So adding a manual system to overcome the limitations of the automatic system...it would be easier just to delete the unwanted files. When your feature requests lead to more feature requests, to fix the problems of the previous feature, that is a strong sign of bad design. So we won't be going down that path.
    If you are using the selective file inclusion and you want to force a file to be included that would otherwise be impossible to detect, an easy way to do this is to just add a commented-out line in your main script that names the file:
    --myfile.tex Anyways, the new build is available now on the beta branch. Let me know how you like it.
  2. Josh
    I am now accepting additional paid content from select authors to publish their Workshop content. Valve will take their standard 30% cut, you will receive 50% of each sale, and Leadwerks will take just 20%. If you are interested in reaching a new audience with the Workshop store, please contact me. You must be a seller with 3D models or textures in an existing marketplace for 3D content, or have a website showing your work.
     

     
    More information on publishing here:
    http://www.leadwerks.com/werkspace/page/tutorials/_/workshop-r11
  3. Josh
    Leadwerks Game Engine 4.4, scheduled for release soon, features some updated and enhanced visual effects.  In this blog I will talk about some of the adjustments I made.  Having "The Zone" scene that Aggror recreated actually helped a lot to see how shaders could be improved.
    Bloom / Iris Adjustment / HDR
    The bloom and iris adjustment shaders have been updated to give bloom a wider and softer blur.  Iris adjustment is faster and more intense now, which will make the outdoors areas seem very bright when you are indoors, and the indoors areas will look very dark when you are outdoors.


    SSAO
    The SSAO shader has multiple passes added to it, resulting in a much smoother yet crisp result. 



    Vegetation Shaders
    A new vegetation shader called "groundrocks" will make objects align to the terrain.  This lets you easily paint clusters of rocks all across a landscape, resulting in a nice chunky look that breaks up boring heightmap terrain.

    Built-in Fog
    Although several fog shaders have been available in the Workshop for some time, version 4.4 adds a built-in distance fog you can use to make spooky scenes.

    The fog is built into several shaders in order to give correct reflections.  It works great with water.  Notice the reflection is also foggy, and the water itself is affected by fog, giving a nice misty look to the far side of the lake.

    You can try Leadwerks Game Engine 4.4 right now by opting into the beta branch on Steam.
  4. Josh
    I started to implement quads for tessellation, and at that point the shader system reached the point of being unmanageable. Rendering an object to a shadow map and to a color buffer are two different processes that require two different shaders. Turbo introduces an early Z-pass which can use another shader, and if variance shadow maps are not in use this can be a different shader from the shadow shader. Rendering with tessellation requires another set of shaders, with one different set for each primitive type (isolines, triangles, and quads). And then each one of these needs a masked and opaque option, if alpha discard is enabled.
    All in all, there are currently 48 different shaders a material could use based on what is currently being drawn. This is unmanageable.
    To handle this I am introducing the concept of a "shader family". This is a JSON file that lists all possible permutations of a shader. Instead of setting lots of different shaders in a material, you just set the shader family one:
    shaderFamily: "PBR.json" Or in code:
    material->SetShaderFamily(LoadShaderFamily("PBR.json")); The shader family file is a big JSON structure that contains all the different shader modules for each different rendering configuration: Here are the partial contents of my PBR.json file:
    { "turboShaderFamily" : { "OPAQUE": { "default": { "base": { "vertex": "Shaders/PBR.vert.spv", "fragment": "Shaders/PBR.frag.spv" }, "depthPass": { "vertex": "Shaders/Depthpass.vert.spv" }, "shadow": { "vertex": "Shaders/Shadow.vert.spv" } }, "isolines": { "base": { "vertex": "Shaders/PBR_Tess.vert.spv", "tessellationControl": "Shaders/Isolines.tesc.spv", "tessellationEvaluation": "Shaders/Isolines.tese.spv", "fragment": "Shaders/PBR_Tess.frag.spv" }, "shadow": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "Shaders/DepthPass_Isolines.tesc.spv", "tessellationEvaluation": "Shaders/DepthPass_Isolines.tese.spv" }, "depthPass": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "DepthPass_Isolines.tesc.spv", "tessellationEvaluation": "DepthPass_Isolines.tese.spv" } }, "triangles": { "base": { "vertex": "Shaders/PBR_Tess.vert.spv", "tessellationControl": "Shaders/Triangles.tesc.spv", "tessellationEvaluation": "Shaders/Triangles.tese.spv", "fragment": "Shaders/PBR_Tess.frag.spv" }, "shadow": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "Shaders/DepthPass_Triangles.tesc.spv", "tessellationEvaluation": "Shaders/DepthPass_Triangles.tese.spv" }, "depthPass": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "DepthPass_Triangles.tesc.spv", "tessellationEvaluation": "DepthPass_Triangles.tese.spv" } }, "quads": { "base": { "vertex": "Shaders/PBR_Tess.vert.spv", "tessellationControl": "Shaders/Quads.tesc.spv", "tessellationEvaluation": "Shaders/Quads.tese.spv", "fragment": "Shaders/PBR_Tess.frag.spv" }, "shadow": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "Shaders/DepthPass_Quads.tesc.spv", "tessellationEvaluation": "Shaders/DepthPass_Quads.tese.spv" }, "depthPass": { "vertex": "Shaders/DepthPass_Tess.vert.spv", "tessellationControl": "DepthPass_Quads.tesc.spv", "tessellationEvaluation": "DepthPass_Quads.tese.spv" } } } } } A shader family file can indicate a root to inherit values from. The Blinn-Phong shader family pulls settings from the PBR file and just switches some of the fragment shader values.
    { "turboShaderFamily" : { "root": "PBR.json", "OPAQUE": { "default": { "base": { "fragment": "Shaders/Blinn-Phong.frag.spv" } }, "isolines": { "base": { "fragment": "Shaders/Blinn-Phong_Tess.frag.spv" } }, "triangles": { "base": { "fragment": "Shaders/Blinn-Phong_Tess.frag.spv" } }, "quads": { "base": { "fragment": "Shaders/Blinn-Phong_Tess.frag.spv" } } } } } If you want to implement a custom shader, this is more work because you have to define all your changes for each possible shader variation. But once that is done, you have a new shader that will work with all of these different settings, which in the end is easier. I considered making a more complex inheritance / cascading schema but I think eliminating ambiguity is the most important goal in this and that should override any concern about the verbosity of these files. After all, I only plan on providing a couple of these files and you aren't going to need any more unless you are doing a lot of custom shaders. And if you are, this is the best solution for you anyways.
    Consequently, the baseShader, depthShader, etc. values in the material file definition are going away. Leadwerks .mat files will always use the Blinn-Phong shader family, and there is no way to change this without creating a material file in the new JSON material format.
    The shader class is no longer derived from the Asset class because it doesn't correspond to a single file. Instead, it is just a dumb container. A ShaderModule class derived from the Asset class has been added, and this does correspond with a single .spv file. But you, the user, won't really need to deal with any of this.
    The result of this is that one material will work with tessellation enabled or disabled, quad, triangle, or line meshes, and animated meshes. I also added an optional parameter in the CreatePlane(), CreateBox(), and CreateQuadSphere() commands that will create these primitives out of quads instead of triangles. The main reason for supporting quad meshes is that the tessellation is cleaner when quads are used. (Note that Vulkan still displays quads in wireframe mode as if they are triangles. I think the renderer probably converts them to normal triangles after the tessellation stage.)


    I also was able to implement PN Quads, which is a quad version of the Bezier curve that PN Triangles add to tessellation.



    Basically all the complexity is being packed into the shader family file so that these decisions only have to be made once instead of thousands of times for each different material.
  5. Josh
    I have shadow caching working now in Turbo. This feature is already in Leadwerks Game Engine 4. The idea is that static scene geometry should not be redrawn when a dynamic object moves. Imagine a character (6000 polys) walking across a highly detailed room (100,000 polys), with one point light in the room. If we mark the scene geometry as static and the character as dynamic, then we can render a shadow map cache of the static scene once. When the character moves, the static cache is copied into the rendering buffer, and then the character is drawn on top of that, instead of re-rendering the entire scene. When used correctly, this will make a huge difference in the amount of geometry the renderer has to draw to update lighting.
    Here is my test. The helmet spins, causing the point light shadow to re-draw. The surrounding scene is marked as static and consists of 260,000 polys. By using shadow caching we can reduce scene polys rendered from about 540,000 to 280,000, in this case.

    I actually changed the light type to a point light, which is more demanding since it uses six passes to oover all faces of the cubemap. After performing optimizations, the test runs at 180 FPS, with a point light, with shadow caching enabled. Without shadow caching it ran at about 118. This is with Intel integrated graphics, so a discrete card is sure to be much faster.
    I also found that using variance shadow maps and multisampled shadows DO make a big difference in performance on Intel graphics (about half the framerate with 4X MSAA VSMs), but I don't think they will make any difference on a high-end card.
    There is still a bit of an issue with shadow updates syncing with the rendering thread, but all in all it was a good day's work.
     
  6. Josh

    Articles
    Happy Friday! I am taking a break from global illumination to take care of some various remaining odds and ends in Ultra Engine.
    Variance shadow maps are a type of shadowmap filter technique that use a statistical sample of the depth at each pixel to do some funky math stuff. GPU Gems 3 has a nice chapter on the technique. The end result is softer shadows that run faster. I was wondering where my variance shadow map code went, until I realized this is something I only prototyped in OpenGL and never implemented in Vulkan until now. Here's my first pass at variance shadow maps in Vulkan:

    There are a few small issues but they are no problem to work out. The blurring is taking place before the scene render, in the shadow map itself, which is a floating point color texture instead of a depth texture. (This makes VSMs faster than normal shadow maps.) The seams you see on the edges in the shot above are caused by that blurring, but there's a way we can fix that. If we store one sharp and one blurred image in the variance shadow map, we can interpolate between those based on distance from the shadow caster. Not only does this get rid of the ugly artifacts (say goodbye to shadow acne forever), but it also creates a realistic penumbra, as you can see in the shot of my original OpenGL implementation. Close to the shadow caster, the shadow is well-defined and sharp, but it gets much blurrier the further away it gets from the object:

    Instead of blurring the near image after rendering, we can use MSAA to give it a fine-but-smooth edge. There is no such thing as an MSAA depth shadow sampler in GLSL, although I think there should be, and I have lobbied on behalf of this idea.

    Finally, in my Vulkan implementation I used a compute shader instead of a fragment shader to perform the blurring. The advantage is that a compute shader can gather a bunch of samples and store them in memory, then access them to process a group of pixels at once. Instead of reading 9x9 pixels for each fragment, it can read a block of pixels and process them all at once, performing the same number of image writes, but much fewer reads:
    // Read all required pixel samples x = int(gl_WorkGroupID.x) * BATCHSIZE; y = int(gl_WorkGroupID.y) * BATCHSIZE; for (coord.x = max(x - EXTENTS, 0); coord.x < min(x + BATCHSIZE + EXTENTS, outsize.x); ++coord.x) { for (coord.y = max(y - EXTENTS, 0); coord.y < min(y + BATCHSIZE + EXTENTS, outsize.y); ++coord.y) { color = imageLoad(imagearrayCube[inputimage], coord); samples[coord.x - int(gl_WorkGroupID.x) * BATCHSIZE + EXTENTS][coord.y - int(gl_WorkGroupID.y) * BATCHSIZE + EXTENTS] = color; } } This same technique will be used to make post-processing effects faster. I previously thought the speed of those would be pretty much the same in every engine, but now I see ways they can be improved significantly for a general-use speed increase. @klepto2 has been talking about the benefits of compute shaders for a while, and he is right, they are very cool. Most performance-intensive post-processing effects perform some kind of gather operation, so compute shaders can make a big improvement there.
    One issue with conventional VSMs is that all objects must cast a shadow. Otherwise, an object that appears in front of a shadow caster will be dark. However, I added some math of my own to fix this problem, and it appears to work with no issues. So that's not something we need to worry about.
    All around, variance shadow maps are a big win. They run faster, look better, and eliminate shadow acne, so they basically kill three birds with one stone.
  7. Josh
    As described previously, the open-market model store approach does not work for Leadwerks and I am shifting my focus to providing DLCs made by third-party authors. The first thing I want to do is create more consistent branding that all DLC model packs will use. Here's what I came up with:
     

     

     

     

     
    I'm also developing terminology to apply consistently to say what the DLC contains. The meaning of "Material Pack" and "Model Pack" is obvious. "Action Figures" will refer to character models that are scripted and completely ready to use. "Weapon Pack" will likewise refer to weapons that are scripted and completely ready to use.
     
    Unscripted models without sounds are something I would am hesitant to add. I feel like the expectation for DLCs will be that each item is completely ready to use. This is one of the ways this approach is more limiting than the Workshop Store.
  8. Josh
    A few people have asked me about this, so now seems like a good time to talk about my experience with Mac computers. My first computer was actually an old Macintosh SE30, but of course in the 1990's Windows took over and I forgot about Mac. When I started developing Leadwerks3D I invested in a 27" iMac. It includes a 3.2 ghz quad core i3 CPU and an ATI Radeon 5750.
     
    At the most minimal, the computer needs one cord for power, and that's it. The keyboard and mouse are wireless, and a built-in wifi receiver is convenient before you string a cable over from your router.
     
    It took a while for me to adjust to it, and I didn't have a big motivation to because it was just a dev machine. However, I started noticing the way it integrated really nicely with my iPhone and iPad (which I also only bought for development). Xcode 4 is a great IDE, and since it uses GCC build times are very quick. I think the build time for Leadwerks3D is about 1-2 minutes, compared to five on Windows. iOS provisioning profiles, which you need to run your app on a iOS device, are a pain but you can run the iOS simulator with no iOS dev account, and it does a good job.
     
    OSX Lion supports OpenGL 3.2. I had some early problems getting it to work, but Apple actually analyzed my application and told me what I was doing wrong. In a situation where a lot of new developers are working with OpenGL in a way that wasn't previously supported on Mac, I think it's understandable that they would need to provide a little extra support. So with that resolved, I don't see any problems with rendering on Mac, EXCEPT:
    -Even thought the hardware supports it, you can't do hardware tessellation, since OpenGL 4 isn't supported.
    -Deferred rendering with MSAA isn't supported. Well, technically it is supported, but the maximum number of samples is one. So it isn't.
     
    As for the OpenGL 2 renderer we use to match mobile device rendering, there's no disadvantage there.
     
    iCloud is a new part of iOS 5, and it's great because my bookmarks are always synced. I know there are various plugins that do this for other browsers, but with Apple it just always works, without requiring any effort on your part.
     
    Mail through me.com, which you can get a free account for, is always synced. I've tried setting up IMAP mail systems, and they just never really work properly. iCloud mail is by far the best email syncing I have ever seen. It's as instantaneous as GMail, but uses a local client so it is fast and responsive. The Mac Mail app itself is both simple and sophisticated. It doesn't have nearly the number of features as Outlook, yet somehow it does exactly what I want. Email search is near-instant, and it separates accounts so I can look at all mail, or easily select a single account and only view email for that address.
     
    There's a lot of good third-party apps available in the Mac App Store. Transmit is a solid FTP client, and Cornerstone is a great SVN client. The iWork suite provides an adequate replacement for Office. You can even install Steam, although the only games I have that run on Mac are Valve's library and Amnesia: The Dark Descent.
     
    iCal, the calendar app, syncs wirelessly through iCloud. This is the first time in my life I have ever actually used a calendar program for real life. It can send out alerts before an event, and everything is synced across my iPhone, iPad, and Mac. There does seem to be a bug right now where events created on my Mac don't go out to the mobile devices.
     
    Mostly due to the strength of the Mail app and Xcode, I now consider the Mac my "main" machine. For me, Windows is just a platform to run Steam on and compile builds for Windows.
     
    The purpose of this blog entry isn't to convince you one OS is better than any other. I would not be able to get by without my PC for some things. If you're thinking about getting into iOS or OSX development, Mac computers now have my seal of approval. Of course, you can install Windows with Boot Camp, so it doesn't have to be an either/or choice. The only reason I still have a physical PC is because mine has hardware that isn't available on Mac, like a Blu-Ray burner and a massive GEForce 480 GPU. I'd have a hard time giving up the infinite upgradability of a PC, but I do like my Mac.
  9. Josh
    It is now possible to compile shaders into a single self-contained file that can loaded by any Vulkan program, but it's not obvious how this is done. After poking around for a while I found all the pieces I needed to put this together.
    Compiling
    First, you need to compile each shader stage from a source code file into a precompiled SPIR-V file. There are several tools available to do this, but I prefer GLSlangValidator because it supports the Google #include extension. Put your vertex shader code in a text file named "shader.vert" and your pixel shader code in a file called "shader.frag". Create a .bat file in the same directory with the following contents:
    glslangValidator.exe "shader.vert" -V -o "vert.spv" glslangValidator.exe "shader.frag" -V -o "frag.spv" Run the bat file and two .spv files will be saved.
    Linking
    Now we want to combine our two files representing different shader stages into a single file. This is done with the link tool from Khronos. Add the following lines to your .bat file to compile the two .spv files into one. It will also delete the existing files to clean things up a little:
    spirv-link "vert.spv" "frag.spv" -o "shader.spv" del "vert.spv" del "frag.spv" This will save a single file named "shader.spv" that you can load as one shader module and use for different stages in Vulkan.
    Here are the required executables and a .bat file:
    BuildShader.zip
    Parsing
    If you always use vertex and fragment stages then there is no problem, but what if the combined .spv file contains other stages, or is missing a fragment stage? We can easily account for this with a minimal SPIR-V file parser. We're not going to include any big bloated libraries to do this because we only need some basic information about what stages are contained in the shader. Fortunately, the SPIR-V specification is pretty simple and it doesn't take much code to extract the information we want:
    std::string entrypointname[6]; auto stream = ReadFile(L"Shaders/shader.spv"); // Parse SPIR-V data Assert(stream->ReadInt() == 0x07230203); int version = stream->ReadInt(); int genmagnum = stream->ReadInt(); int bound = stream->ReadInt(); int reserved = stream->ReadInt(); bool stages[6] = {false,false,false,false,false,false}; // Instruction stream while (stream->Ended() == false) { int pos = stream->GetPos(); unsigned int bytes = stream->ReadUInt(); int opcode = LOWORD(bytes); int wordcount = HIWORD(bytes); if (opcode == 15) { int executionmodel = stream->ReadInt(); Assert(executionmodel >= 0); if (executionmodel < 6) { stream->ReadInt(); // entry point stages[executionmodel] = true; entrypointname[executionmodel] = stream->ReadString(); } } stream->Seek(pos + wordcount * 4); } This code even retrieves the entry point name for each stage, so you can be sure you are loadng the shader correctly.
    Here are the different shader stages from the SPIR-V specification:
    0: Vertex 1: TessellationControl 2: TessellationEvaluation 3: Geometry 4: Fragment 5: GLCompute That's it! We now have a standard single-file shader format for Vulkan programs. Your code for creating these will look something like this:
    VkShaderModule shadermodule; // Create shader module VkShaderModuleCreateInfo shaderCreateInfo = {}; shaderCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; shaderCreateInfo.codeSize = bank->GetSize(); shaderCreateInfo.pCode = reinterpret_cast<const uint32_t*>(bank->buf); VkAssert(vkCreateShaderModule(device->device, &shaderCreateInfo, nullptr, &shadermodule)); // Create vertex stage info VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vertShaderStageInfo.module = shadermodule; vertShaderStageInfo.pName = entrypointname[0].c_str(); VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; if (stages[4]) { // Create fragment stage info fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fragShaderStageInfo.module = shadermodule; fragShaderStageInfo.pName = entrypointname[4].c_str(); } // Create your graphics pipeline...  
  10. Josh
    It's been nearly a year since I made the decision to port our OpenGL renderer over to Vulkan, and it has been very difficult work, but we are getting back up to speed. I was able to get skinned animation working for the first time in Vulkan yesterday, using a slightly modified version of our original animation shader code.
    The system works exactly the same as in Leadwerks 4, with a few differences:
    Animation features are confined to the Model class only, and are no longer a general Entity feature. Bones are no longer an entity. This reduces a lot of overhead when animation is processed. There will be some type of "AttachToBone" method in the entity class you can use to place a weapon in a character's hand. The Model class has a new Skeleton member. A skeleton is only present in animated models. Skeletons can be shared between models. This allows the engine to process animation once and multiple models can share the skeleton. The models can be instances of the same model, or different models. This is very good for rendering large crowds of enemies. (However, you might not even need to bother with this type of optimization, because 1000 unique skeletons works with no issues, as seen below.) Animation is executed on one or more separate threads which makes it very very fast. Animated characters are now batched instead of drawn one at a time. With the OpenGL prototype renderer we saw very good performance with this technique, so it will be interesting to see how Vulkan compares:
    I have not added support for GLTF animation loading yet, which will give us a wide variety of ready-to-use animated models.
  11. Josh
    The slider widget is used for scrollbars, trackbars, and steppers. It has a lot of style options, so it's really six different widgets packed into one. (In Leadwerks Editor, a slider and textfield widget are combined to make the numerical entries you use to adjust positions and other values.)
     

     
    To create a new slider you just create a widget and set the slider script:

    local slider = Widget:Create(230+40,140,20,20,panel) slider:SetScript("Scripts/GUI/Slider.lua")
     
    You can then set the style. The style should be set after the script is assigned. This is because the style flags are initialized in the slider script, so if it hasn't been run already those values will be nil:

    slider:SetStyle(Style.Slider.Stepper + Style.Slider.Horizontal)
     
    The slider can be any of these styles:
    Style.Slider.Scrollbar
    Style.Slider.Trackbar
    Style.Slider.Stepper

     
    And can be combined with the following to be either horizontal or vertical:
    Style.Slider.Horizontal
    Style.Slider.Vertical

     
    The default appearance is a vertical scrollbar.
     
    When a slider is moved either by clicking and dragging it with the mouse, clicking on the trackbar, moving the mouse wheel when the widget is focused or pressing the arrow keys when the widget is focused, the widget will emit a WidgetAction event, with the slider value stored in the event.data member.
     
    So far none of the widgets are using any images. The arrows and slider knob are using DrawPolygon() to render the shape.
     
    This is available now on the beta branch on Steam, for Lua on Windows only at this time.
  12. Josh
    Leadwerks Game Engine: Indie Edition on Steam is now just "Leadwerks Game Engine".
     
    Leadwerks Game Engine: Standard Edition on Steam is now "Leadwerks Game Engine: Professional Edition".
     
    When I first put Leadwerks on Steam, it was just an experiment. Separating out the Lua version was something I had not done before. In fact originally, Leadwerks was a C++ library only, and Lua support was added later. After the last two years on Steam, it makes sense to consider the Lua version our main product, and the C++ add-on is more of a high-end advanced option.
  13. Josh
    Everything I'm working on right now is a real grind. Trying to get some window code to run on Mac and chatting with Valve on how to optimize the Workshop Store. If I didn't have a plan this would not feel like I was going anywhere. I think that's why programmers often fail to reach beyond a certain level of success. I don't mean simply financial success, I mean they don't go big enough to make a really complete product. They make a car with 22 different gears and no seat. If you don't have a really clear idea of what you are going to bring into existence you just get stuck on little details forever.
     
    In six months there will be a classroom full of students learning game development with Leadwerks for the first time, and loving it.
  14. Josh
    In the next beta update, the way entity fields work in the script properties will be a little different. Instead of dragging an object from the scene tree onto the field, you will type in the name of the object (or copy and paste the name from the object's name field). This is being done to make the behavior more intuitive. After working with both approaches, I find the new way much easier to use.
     

     
    This does however mean that an object that is used in this manner must have a unique name, since that is what is used to make the linkage. Additionally, prefab limbs can now have their names changed without breaking the prefab, in order to accommodate this design.
     
    Your existing maps will be unaffected until they are resaved in the editor, but if you have multiple target objects with the same name, your map will need to be updated.
     
    Alos, the texture list in the material editor is being reverted back to its original design, which was a bit more obvious in its presentation and usage.
     

  15. Josh
    Most entity types will start with collision type=0 now.
    Fix so collidable emitter particles won't collide with the emitter itself anymore.
    Added lens flares to lighting prefabs in showcase map.
    Added bloom to showcase map.
    Modified the way the Flicker effect script works.

  16. Josh
    Two issues in the art pipeline were giving me some doubt. I stopped working on them a couple weeks ago, and I'm glad I did because the solutions became clear to me.
     

    Shader Files
    First, there is the matter of shaders. A shader can actually consist of half a dozen files:bumpmapped.opengl3.vert
    bumpmapped.opengl3.frag
    bumpmapped.opengl4.vert
    bumpmapped.opengl4.vert
    bumpmapped.opengles.vert
    bumpmapped.opengles.vert
     
    I originally thought the .shd extension would be good for shaders, in keeping with our extension naming scheme, but there's actually no information an .shd file even needs to contain! I also considered creating a simple format that would pack all the shader strings into a single file, and display the different ones for whatever graphics driver was in use at the time, but that approach reeked of future confusion. I'd like to still be able to open .vert and .frag files in Notepad++ or another text editor.
     
    I came up with the idea to use a .shd file that contains all these shader files, but instead of packing them into a file format, the .shd file will just be a renamed .pak. That way you can easily extract the original text files, but shaders can be distributed and loaded as a single file.
     

    Material Textures
    I've been working with a system that uses texture names defined in the shader file. It works and it's really cool, but I think I have something even cooler. Instead of requiring a shader to define texture unit names, it would be less confusing to just decide on a fixed naming scheme and stick with it, something like this:texture0 - Diffuse
    texture1 - Normal
    texture2 - Specular
    texture3 - Reflection
    texture4 - Emission
    texture5 - Height
     
    And in C++ you would have constants like this:

    #define TEXTURE_DIFFUSE 0 #define TEXTURE_NORMAL 1 ...
    If you want to create a bumpmapped material, you can simply assign textures 0 and 1, and the material will choose a shader with the assumption those are supposed to be the diffuse and normal map. If the current surface being rendered contains bone weights, an animated version of the shader will be used. If a shader is explicitly defined, that of course will override the material's automatic selection. This way, all you have to do is drag a few textures onto a material, and 95% of the time no shader will even have to be explicitly defined. This is similar to the materials system in 3ds max.
     
    The programmer in me screams that this makes no sense, since texture units are completely arbitrary, but from a usage standpoint it makes sense, since the exceptions to this paradigm probably make up less than 5% of the materials in any scene.
     
    Now for custom shaders, you might end up with a situation where you are dragging a texture into the "reflection" slot, when it's really going to be used to indicate team colors or something, but it's still far simpler to say "set the reflection texture with...". Everyone knows what you mean, textures can be set in code without having to look up the slot number in the shader, and there's no ambiguity.
     
    Anyways, that's just two small design problems overcome that I thought I would share with you. I think the texture issue really demonstrates the difference between engineering and product design.
     

    http://www.youtube.com/watch?v=5U6Hzjz5220
  17. Josh
    This is something I typed up for some colleagues and I thought it might be useful info for C++ programmers.
    To create an object:
    shared_ptr<TypeID> type = make_shared<TypeID>(constructor args…) This is pretty verbose, so I always do this:
    auto type = make_shared<TypeID>(constructor args…) When all references to the shared pointer are gone, the object is instantly deleted. There’s no garbage collection pauses, and deletion is always instant:
    auto thing = make_shared<Thing>(); auto second_ref = thing; thing = NULL; second_ref = NULL;//poof! Shared pointers are fast and thread-safe. (Don’t ask me how.)
    To get a shared pointer within an object’s method, you need to derive the class from “enable_shared_from_this<SharedObject>”. (You can inherit a class from multiple types, remember):
    class SharedObject : public enable_shared_from_this<SharedObject> And you can implement a Self() method like so, if you want:
    shared_ptr<SharedObject> SharedObject::Self() { return shared_from_this(); } Casting a type is done like this:
    auto bird = dynamic_pointer_cast<Bird>(animal); Dynamic pointer casts will return NULL if the animal is not a bird. Static pointer casts don’t have any checks and are a little faster I guess, but there’s no reason to ever use them.
    You cannot call shared_from_this() in the constructor, because the shared pointer does not exist yet, and you cannot call it in the destructor, because the shared pointer is already gone!
    Weak pointers can be used to store a value, but will not prevent the object from being deleted:
    auto thing = make_shared<Thing>(); weak_ptr<Thing> thingwptr = thing; shared_ptr<Thing> another_ref_to_thing = thingwptr.lock(); //creates a new shared pointer to “thing” auto thing = make_shared<Thing>(); weak_ptr<Thing> thingwptr = thing; thing = NULL; shared_ptr<Thing> another_ref_to_thing = thingwptr.lock(); //returns NULL! If you want to set a weak pointer’s value to NULL without the object going out of scope, just call reset():
    auto thing = make_shared<Thing>(); weak_ptr<Thing> thingwptr = thing; thingwptr.reset(); shared_ptr<Thing> another_ref_to_thing = thingwptr.lock(); //returns NULL! Because no garbage collection is used, circular references can occur, but they are rare:
    auto son = make_shared<Child>(); auto daughter = make_shared<Child>(); son->sister = daughter; daughter->brother = son; son = NULL; daughter = NULL;//nothing is deleted! The problem above can be solved by making the sister and brother members weak pointers instead of shared pointers, thus removing the circular references.
    That’s all you need to know!
  18. Josh

    Articles
    I've actually been doing a lot of work to finalize the terrain system, but I got into tessellation, and another rabbit hole opened up. I've been thinking about detailed models in VR. Tessellation is a nice way to easily increase model detail. It does two things:
    Curved surfaces get smoother (using point-normal triangles or quads) A displacement map can be used to make small geometric detail to a surface. These are really nice features because they don't require a lot of memory or disk space, and they're automatic. However, tessellation also has a nasty tendency to create cracks in geometry, so it tends to work best with organic models like rocks that are carefully wrapped. I was able to mitigate some of these problems with a per-vertex displacement value, which dampens displacement wherever there is a mesh seam:

    However, this does not help round edges become rounder. With this cylinder, because the faces are not a continuous rounded surface, a crack appears at the sharp edges:

    What if there was a way to properly tessellate mechanical shapes like this? Wouldn't it be great if all your models could just automatically gain new detail that scales with the camera distance, with no need for extra Lod versions?
    Well, I had an idea how it might be possible, and a few hours later I came up with something. Here is the original model with no tessellation applied:


    Here is the same model with my new and improved tessellation shader, suitable for organic and mechanical shapes:


    A closeup view reveals perfectly aligned geometry with no gaps or cracks:

    The distribution of polygons can probably be made more uniform with additional work. This feature uses two additional bytes in the vertex structure to control a normal for tessellated curves. I think it will be possible to automatically detect seams and assign tessellation normals in the final editor. If no separate tessellation normals are assigned, then the default tessellation behavior occurs. This is a very promising technique because it could allow you to add a lot of detail to any model very easily with barely any effort.
  19. Josh
    Smoothing groups are one of the most frequently requested features for 3D World Studio, so I am happy to show you their implementation in Leadwerks3D, finally. Smoothing groups are usually stored as a bitwise flag. When two faces share a vertex at a certain position, if they have one or more smoothing groups in common, their vertex normal is calculated as if they are sharing a vertex.
     
    I first attempted to write an algorithm based on edges, which worked great for cylinders but failed for geometry that had faces that influence vertex normals they are not connected to by an edge. I wrote another routine, which uses an n*n*n loop (big programming no-no, but sometimes it's necessary) and got good results with cylinders and spheres. Cones are a difficult case, and if you don't believe me, just crack out a cone in your favorite 3D modeling app and inspect the tip. I got around this problem by adding a max smoothing angle member to the brush class, which allows the smoothing algorithm to discard faces that are too 'different' from the one being processed.
     
    Here's the result:

     
    In order to make shapes like arches appear smoothed, the smoothing algorithm has to span multiple objects. This will be accomplished by first grabbing all the brushes in the nearby vicinity to the brush being processed, and then searching for vertices that occupy the same position, on faces that have a smoothing group in common.
     
    In the past, we've seen some pretty advanced modeling done with 3D World Studio, but the lack of smoothing groups always limited it. It will be awesome to see what people come up with, when those limits are removed and they can model exactly what they want.





  20. Josh
    The last day of the season was today, so I was at North Star. We had a storm for the last three days, so the snow was pretty good, just sticky in some places. Even though it was a Sunday, crowds were not bad. Towards the end of the day, most people were hanging out at the lodge and the runs were completely empty. All in all, it was a pretty awesome day.

  21. Josh
    The design of the App table in Lua was originally created to work with mobile, which has a more restricted program flow. The application calls App:Start() once to initialize it, and then will continually call App:Loop() until the function returns false. These two functions are found in the file "Scripts\App.lua".
     
    Since we are focused on the PC now, it does not make sense to keep this structure. It also makes it more confusing to teach scripting. A conventional program layout with one main script is much simpler than having two functions that are called by the interpreter itself. We are going to transition to a conventional program layout in a way that doesn't break compatibility with existing scripts.
     
    First, if you do nothing and keep the App.lua script as it is, your program will continue to work. However, your App.lua file no longer is required to contains the App table, or the two Start() and Loop() functions. If your App.lua file just contains text like this, it will work just fine:

    window = Window:Create() context = Context:Create(window) world = World:Create() local camera = Camera:Create() camera:Move(0,0,-3) local light = DirectionalLight:Create() light:SetRotation(35,35,0) model = Model:Box() model:SetColor(0.0,0.0,1.0) while true do if window:Closed() or window:KeyHit(Key.Escape) then return false end model:Turn(0,Time:GetSpeed(),0) Time:Update() world:Update() world:Render() context:Sync() end
     
    Doesn't that code look simpler?
     
    Second, we are going to introduce a new file called "Main.lua" into the project template, and remove App.lua from the original template. If both App.lua and Main.lua are present, then App.lua will be called. If only Main.lua is present, then it will be used. This means that new projects will use our preferred method of calling Main.lua, but existing projects will be unaffected, even when the project is updated.
     
    All existing code examples in the documentation will continue to work when pasted into App.lua or Main.lua.
     
    TLDR: Changes are coming but it won't affect your existing projects.
  22. Josh
    Leadwerks 3 / 4 was aimed at beginners who were completely new to game development. Since we were the first game engine on Steam, this made a lot of sense at the time, and the decision resulted in a successful outcome. However, in the next engine we are taking a different approach. (This is a direct result of Steam Direct.)
    Enterprise is a new market I am pursuing, and we have a lot of interest from aerospace and defense VR developers. The fact that I am American also helps here. There are special features these customers need that aren't necessarily needed by game developers, but I think you will like some of the possibilities this unlocks.
    For game developers, I have been moving back to an approach more like Leadwerks 2 where we focus on extreme high-end PC game technology, so that comes down to graphical quality and performance. I think most people here will be pretty happy with that direction. We're going to sell on Steam, Humble Store, Amazon, Microsoft store, Mac App Store, and direct from our website. Less importance will be attached to Steam, as they are just one more storefront we sell through. We're not going to use Steam Workshop.
    For pricing of the non-enterprise version, I am thinking $59.99 / $99.99 standard / pro with a monthly subscription option at $4.99 / $9.99. This is actually cheaper than the pricing of Leadwerks, but I think keeping things under $100 is better for the consumer market.
    The paid beta subscription is going to end before the end of the year, and it will be replaced with an open beta. The reason I did this was because I wanted a very small group of people testing the early betas (really more of an alpha), and I wanted to test out our subscriptions system. During that time we found and fixed a couple of small issues, so this was a good idea. A big thanks to all who participated and bought me many espressos.  ☕
    Finally, we are not going to call Leadwerks 5 "Turbo Game Engine". The name just isn't sticking for me, and I don't think we can get rid of the :"retro" connotations it has. My new technology has developed quite a lot since then, and speed is not the only advantage it brings to the table. I do have a new name in mind, but I am not ready to announce it yet. Until then, I will refer to the new engine as "Leadwerks 5 beta".
  23. Josh
    This is a quick blog to fill you in on some of the features planned for Leadwerks Engine 3.0.
     
    -Any entity can be a physics body. The body command set will become general entity commands, i.e. Entity.SetVelocity(). You can make an entity physically interactive with Entity.SetShape( shape ).
     
    -Any entity can have any number of scripts attached to it. The basic model script functions will be supported for all entities, plus specialized script functions for certain entities. For example, particle emitters will have an optional script function that is called when a particle is reset. This could be used to make a fire emitter "burn" a mesh. Script functions are called in sequence, so you can attach a chain of scripts to an entity to combine behaviors.
     
    -Prefabs will be supported, with the ability to drag entities together to make a hierarchy, add lights, emitters, and other entities, attach scripts, and then save a prefab.
     
    -Record screenshots and movies from the editor!
     
    -Tons of scripted behavior you can attach to your own models, and lots of scripted assets you can download. Basic AI features will be supplied.
     
    -The editor is being designed so you only need a mouse to make games. If you want to script or program or make artwork you can, but at the simplest level, the whole workflow can be controlled in the editor, with reloading assets, automated imports, and lots of drag-and-drop functionality.
     
     
    That's all for now. I can't tell you some of the coolest features, but this gives you an idea of where we are headed.
  24. Josh
    Here's some LE3 code for a simple program. The Graphics / Window stuff is not worked out 100%, and it's a little tricky because of the different operating systems and different kinds of windows.
     
    I think we'll see a little more consistency across various languages with the LE3 syntax. It was suggested that the C syntax use the following scheme:
    verb-class-noun

    SetEntityPosition() GetMaterialTexture()
    I agree with this suggestion.
     

    SetGraphicsDriver( OpenGLGraphicsDriver() ); CreateGraphics(1024,768,4); World* world = CreateWorld(); //Load a shader Shader* shader = LoadShader("Shaders/minimal.shader"); //Create a material; Material* mat = CreateMaterial(); mat->SetShader(shader); mat->SetColor(1,0,0,1); //Create a box; Mesh* box = CreateMeshBox(1,1,1); box->SetMaterial(mat); Camera* camera = CreateCamera(); camera->SetClearColor( Vec4(0,1,0,1) ); box->SetPosition(0,0,-2,false); float yaw = 0.0; while (!window->Closed()) { yaw++; box->SetRotation(yaw,0,0,false); camera->Render(); window->Flip(); }
×
×
  • Create New...