Jump to content

Josh

Staff
  • Posts

    23,339
  • Joined

  • Last visited

Blog Entries posted by Josh

  1. Josh
    I found and fixed the cause of the cubemap seams in variance shadow maps so we now have nice soft seamless shadows.

    I also changed the engine so that point lights use six 2D textures instead of a separate cubemap texture array. This means that all light types are sharing one big 2D array texture, and it frees up one texture slot. I am not sure if I want to have a hard limit on number of shadow-casting lights in the scene, or if I want to implement a system that moves lights in and out of a fixed number of shadowmap slots.
  2. Josh
    I'm in DC this week helping the folks at NASA wrap up some projects. I'm going to move back to a supportive role and focus on development of Leadwerks 4.6 and the new engine, and I am helping them to hire some programmers to replace me. We found some very talented people who I am confident will do a fantastic job, and I can't wait to see what they create using Leadwerks Game Engine.
    I helped a team using Leadwerks at NASA get through some big milestones and expand. I hope that someday soon we will be able to tell the story of what happened, because it really has been an amazing experience with some really awesome people. I think it would make a nice movie.
    I will start working hard on Leadwerks 4.6 next Monday., and during March I will finish up my last projects so I can devote all my time to Leadwerks. I'm actually really looking forward to long days of doing nothing but engine coding. We're also going to hold a game tournament this Summer, with prizes. It was worth spending time to help NASA because we picked up a lot of new business customers and it helped focus development of the new engine in a direction everyone seems very happy about, but I need to get back to doing what I love best, which is building great software for you.
  3. Josh
    I am surprised at how quickly Vulkan development is coming together. The API is ridiculously verbose, but at the same time it eliminates a lot of hidden states and implicit behavior that made OpenGL difficult to work with. I have vertex buffers working now. Vertices in the new engine will always use this layout:
        struct VkVertex     {         float position[3];         float normal[3];         float texcoords0[2];         float texcoords1[2];         float tangent[3];         unsigned char color[4];         unsigned char boneweights[4];         unsigned char boneindices[4];     }; Note there are no longer vertex binormals, as these are calculated in the vertex shader, with the assumption that the texture coordinates have no shearing. There are two sets of UV coordinates available to use. Up to 256 bones per mesh are supported.
    I am creating a few internal classes for Vulkan, out of necessity, and the structure of the new renderer is forming. It's very interesting stuff:
    class VkMesh { public: Vk* environment; VkBuffer vertexBuffer; VmaAllocation allocation; VkBuffer indexBuffer; VmaAllocation indexallocation; VkMesh(); ~VkMesh(); }; I have hit the memory management part of Vulkan. Something that used to be neatly done for you is now pushed onto the developer for no apparent reason. I think this is really pointless because we're all going to end up using a bunch of open-source helper libraries anyways. It's like they are partially open-sourcing the driver.

    You can't just allocate memory buffers as you wish. From vulkan-tutorial.com:
    Nvidia explains it visually. It is better to allocate a smaller number of memory blocks and buffers and split them up:

    I added the Vulkan Memory Allocator library and it works. I honestly have no idea what it is doing, but I am able to delete the Vulkan instance with no errors so that's good.
    Shared contexts are also working so we can have multiple Windows, just like in the OpenGL renderer:

  4. Josh
    Virtual reality rendering is very demanding on hardware for two reasons. First, the refresh rate of most VR headsets is 90 hz instead of the standard 60 hz refresh rate most computer monitors operate at. This means that rendering must complete in about 11 milliseconds instead of 16. Second, we have to render the scene twice with two different views, one for each eye. Without any special optimizations, this roughly means that we have to pack 16 milliseconds worth of rendering code into five milliseconds.
    There are a few optimizations we can make to improve efficiency over a naive implementation. Although VR rendering requires two different views to be rendered, the two views are only a few centimeters apart.

    We can perform culling for both views at once by simply widening the camera frustum a little bit so that both eyes are included the camera volume.

    For rendering, Nvidia provides an extension for single-pass stereo rendering. This doubles up the geometry and renders to two different viewports at once, ensuring that the engine makes the same number of draw calls when rendering in stereo or normal mode. (The same can be done using a geometry shader with Intel graphics, although it has yet to be determined if VR will be possible on this hardware.) Here is the result:
    Combined with all the other optimizations I've packed into Turbo, like clustered forward rendering and a multithreaded rendering architecture designed specifically for VR, this makes VR rendering blazingly fast. How fast is it? Well, it's so fast that SteamVR diagnostics thinks that it is going backwards in time. Take a look at the timer in the lower left corner here:

    The obvious conclusion is that we have successfully broken the speed of light barrier and time travel will be possible shortly. Start thinking about what items or information you want to gift your past self with now, so that you can be prepared when I start offering temporal tourism packages on this site.

  5. Josh
    The next update will include a new networking system with built-in voice chat for multiplayer games.
    Voice communication is built into your game and can be enabled simply by turning it on when a specific key is pressed:
    void Voice::SetRecording(window->KeyDown(Key::T)) You can selectively filter out users so your voice only gets sent to your own team or to a specific player:
    void Voice::SetFilter(const uint64 steamid, const bool state) When another player sends their voice data to you, the engine will automatically receive and play the sound. You can also retrieve a sound source for a specific player and make adjustments to it. This can be used to enable 3D spatialization and position the sound source where the player is, for VR voice chat.
    Source* Voice::GetPlayerSource(const uint64_t steamid) When I first implemented this the sound was sometimes choppy. I added some automatic adjustment of the pitch to slow down the sound a bit if new data is not received yet, and to speed it up if it falls too far behind. This seems to work really well and I'm sure it must be a common technique in applications like Twitch and Skype.
    A new multiplayer game template will be provided that shows how to set up the framework for a multiplayer game. Here's a video preview of the voice communication in action.
     
  6. Josh
    It turns out GLTF is actually three different file formats. ? Textures can be loaded from external files, embedded in a binary .glb file, but they can also be saved in an ASCII GLTF files using base64 encoding. Having three different ways to store textures is not a good design decision, but at least it's better than the disaster called Collada. (Note to Khronos: If your file format specification has more pages than a Tom Clancy novel it probably sucks.)
    Our GLTF loader now supports files with textures embedded in the file, in both binary (.glb) and embedded formats. We also support the Microsoft DDS extension, and support for the Microsoft LOD extension is on the way. The new editor will be able to save loaded models as GLTF files, and we can pack our own information away in our own file extensions, while keeping the file loadable by other applications.

    Additionally, the new engine now supports textures loaded from DDS, PNG, JPG, BMP, PCX, PSD, GIF, ICO, TIF, EXR, and HDR formats, as well as Leadwerks TEX files. I'd like to get a new update out soon for the new engine, and then continue working on bug fixes for Leadwerks Game Engine 4.6.

  7. Josh
    A new beta is available with the following changes:
    Script prefixes are now changed to lowercase entity:Update(), entity:Start(), etc., as well as widget:Draw(), etc. This is because Entity() and Widget() are going to be functions to cast an object to that type. Sprites are now created on a sprite layer object. A sprite layer is created in a world and added to a camera. This allows control over what camera sees what set of sprites. See the examples for details. GUI system is partially working. Resizing the window won't reposition items correctly. Only four widget types are supported, panel, button, hyperlink, and label. Example in the FPSGame demo. The game menu system is packed into an entity script and works really nicely. Widget scripts are a little harder to develop now since they use a system of persistent objects, but performance is very much better than LE4. An interesting detail is that you get free interpolation of position and color at a rate of 60 hz. A lot of work was done to improve the Lua binding system. See details here. Error reporting and handling is much improved. No work was done on sound. No work has been done to the multicam demo, which some people had trouble with. Actors crashing / Lua stack error bug fixed. Changed .bat files to use release version of EXE instead of debug. New commands EmitEvent() and GetEvent(). If the returned event type is EVENT_NONE, there is no event. EVENT_QUIT can be emitted anywhere in the program to signal the main loop to exit. Typical usage is like this: while window:Closed() == false do while true do local event = GetEvent() if event.id == EVENT_QUIT then return end if event.id == EVENT_NONE then break end if event.id == EVENT_WINDOW_CLOSE and Window(event.source) == window then return end--you don't need this when Window:Closed() is being checked for already end world:Update() world:Render(framebuffer) end  
  8. Josh
    I have not gone in several years because everything we were doing revolved around Steam, and it just didn't seem very important. But this year I had some business to attend to so I spent the last three days in San Francisco.
    I still have a lot of friends in the game industry, and the reaction to my plans for the new engine was very positive. A few years ago people would have groaned at the idea of another engine, but it seems they are now bored with technology and very open to something new. The angle we are taking plays well to the audience. Basically all my predictions about how to sell a new game engine in 2020 were confirmed.
    We do need to have tip-top examples to show off next year, and that starts with good artwork. At this point I am probably only planning to show a first-person shooter, an offroad racing game, and then whatever the NASA team comes up with. And in order to get that done in time, I need to start planning, now!
    I feel very focused on what needs to happen, what is important, and what is not. My idea of what I want is so clear. It doesn't seem that hard to complete.
  9. Josh
    One of the best points of Vulkan is how shaders are loaded from precompiled Spir-V files. This means GLSL shaders either work or they don't. Unlike OpenGL, there is no different outcome on Intel, AMD, or nVidia hardware. SPIR-V files can be compiled using a couple of different utilities. I favor LunarG's compiler because it supports #include directives.
    Shader.vert:
    #version 450 #extension GL_ARB_separate_shader_objects : enable #include "VertexLayout.glsl" layout(push_constant) uniform pushBlock { vec4 materialcolor; } pushConstantsBlock; layout(location = 0) out vec3 fragColor; void main() { gl_Position = vec4(inPosition.xy, 0.0, 1.0); fragColor = inColor.rgb * materialcolor.rgb; } VertexLayout.glsl:
    layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; layout(location = 2) in vec2 inTexCoords0; layout(location = 3) in vec2 inTexCoords1; layout(location = 4) in vec3 inTangent; layout(location = 5) in vec4 inColor; layout(location = 6) in vec4 inBoneWeights; layout(location = 7) in uvec4 inBoneIndices; If the shader compiles successfully, then you don't have to worry about whether it works on different manufacturers' hardware. It just works. So if someone writes a new post-processing effect they don't need to test on other hardware or worry about people asking for help when it doesn't work. Because it always works the same.
    You can try it yourself with these files:
    Shaders.zip
  10. Josh
    Following this tutorial, I have managed to add uniform buffers into my Vulkan graphics pipeline. Since each image in the swapchain has a different graphics pipeline object, and uniform buffers are tied to a pipeline, you end up uploading all the data three times every time it changes. OpenGL might be doing something like this under the hood, but I am not sure this is a good approach. There are three ways to get data to a shader in Vulkan. Push constants are synonymous with GLSL uniforms, although much more restrictive. Uniform buffers are the same in OpenGL, and this is where I store light data in the clustered forward renderer. Shader storage buffers are the slowest to update but they can have a very large capacity, usually as big as your entire VRAM. I have the first two working now. Below you can see a range of instanced boxes being rendered with an offset for each instance, which is being read from a uniform buffer using the instance ID as the array index.

    To prove that Vulkan can render more than just boxes, here is a model loaded from GLTF format and rendered in Vulkan:

    Figuring out the design of the new renderer using OpenGL was very smart. I would not have been able to invent it if I had jumped straight into Vulkan.
  11. Josh
    Having completed a hard-coded rendering pipeline for one single shader, I am now working to create a more flexible system that can handle multiple material and shader definitions. If there's one way I can describe Vulkan, it's "take every single possible OpenGL setting, put it into a structure, and create an immutable cached object based on those settings that you can then use and reuse". This design is pretty rigid, but it's one of the reasons Vulkan is giving us an 80% performance increase over OpenGL. Something as simple as disabling backface culling requires recreation of the entire graphics pipeline, and I think this option is going away. The only thing we use it for is the underside of tree branches and fronds, so that light appears to shine through them, but that is not really correct lighting. If you shine a flashlight on the underside of the palm frond it won't brighten the surface if we are just showing the result of the backface lighting.

    A more correct way to do this would be to calculate the lighting for the surface normal, and for the reverse vector, and then add the results together for the final color. In order to give the geometry faces for both direction, a plugin could be added that adds reverse triangles for all the faces of a selected part of the model in the model editor. At first the design of Vulkan feels restrictive, but I also appreciate the fact that it has a design goal other than "let's just do what feels good".
    Using indirect drawing in Vulkan, we can create batches of batches, sorted by shader. This feature is also available in OpenGL, and in fact is used in our vegetation rendering system. Of course the code for all this is quite complex. Draw commands, instance IDs, material IDs, entity 4x4 matrices, and material data all has to be uploaded to the GPU in memory buffers, some of which are more or less static, and some of which are updated each frame, and some for each new visibility set. It is complicated stuff, but after some time I was able to get it working. The screenshot below shows a scene with five unique objects being drawn in one single draw call, and accessing two different materials with different diffuse colors. That means an entire complex scene like The Zone will be rendered in one or just a few passes, with the GPU treating all geometry as if it was a single collapsed object, even as different objects are hidden and shown. Everyone knows that instanced rendering is faster than unique objects, but at some point the number of batches can get high enough to be a bottleneck. Indirect rendering batches the batches to eliminate this slowdown.

    This is one of the features that will help our new renderer run an order of magnitude faster, for high-performance VR and regular 3D games.
  12. Josh
    Now that we have lights working in our clustered forward Vulkan renderer (same great technique the latest DOOM games are using) I am starting to implement shadow maps. The first issue that came up was managing render-to-texture when the texture might still be in use rendering the previous frame. At first I thought multiple shadowmaps would be needed per light, like a double-buffering system, but that would double the number of shadow textures and video memory. Instead, I created a simple object pool which stores spare shadowmaps that can be swapped around and used as they are needed.
    It turns out I already have pretty much all the code I need because Vulkan's swapchain creation works exactly the same way, by rendering to a series of images. I worked through the code slowly and came up with this by the end of the day, which runs successfully without throwing any validation errors:
    bool RenderBuffer::Initialize(shared_ptr<RenderContext> context, const int width, const int height, const int depth, const int colorcomponents, const bool depthcomponent, const int samples) { this->device = GameEngine::Get()->renderingthreadmanager->instance; int totalcomponents = colorcomponents; if (depthcomponent) totalcomponents++; std::fill(colortexture.begin(), colortexture.end(), nullptr); depthtexture = nullptr; //Create color images for (int i = 0; i < colorcomponents; ++i) { colortexture[i] = make_shared<RenderTexture>(); colortexture[i]->Initialize(VK_IMAGE_TYPE_2D, device->chaininfo.imageFormat, width, height, 1, 0, false, -1,-1,-1,-1,1, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); colorimages[i] = colortexture[i]->vkimage; } //Create depth image if (depthcomponent) { depthtexture = make_shared<RenderTexture>(); depthtexture->Initialize(VK_IMAGE_TYPE_2D, device->depthformat, width, height, 1, samples, false, -1, -1, -1, -1, 1, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); } //Create image views imageviews.resize(totalcomponents); VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.subresourceRange.baseMipLevel = 0; createInfo.subresourceRange.levelCount = 1; createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; // Create color image views for (size_t i = 0; i < colorcomponents; i++) { createInfo.image = colorimages[i]; createInfo.format = device->chaininfo.imageFormat; createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkAssert(vkCreateImageView(device->device, &createInfo, nullptr, &imageviews[i])); } //Create depth image view if (depthcomponent) { createInfo.image = depthtexture->vkimage; createInfo.format = depthtexture->format; createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; VkAssert(vkCreateImageView(device->device, &createInfo, nullptr, &imageviews[colorcomponents])); } //Create framebuffer VkFramebufferCreateInfo framebufferInfo = {}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = device->renderpass->pass; framebufferInfo.attachmentCount = 2; framebufferInfo.pAttachments = imageviews.data(); framebufferInfo.width = width; framebufferInfo.height = height; framebufferInfo.layers = 1; VkAssert(vkCreateFramebuffer(device->device, &framebufferInfo, nullptr, &framebuffer)); return true; } The blog cover image isn't the Vulkan renderer, I just found a random image of Leadwerks shadows.
  13. Josh
    Leadwerks 4 supports compressed and encrypted game files in ZIP format, but there are many different custom package formats different games use. The plugin system in Leadwerks 5 allows us to create importers for these different package formats so we can access content directly from commercial games you have installed on your computer. The example below shows how to load a VTF texture from the game Half-Life 2 directly from the game's install files.
    First we need to load some plugins to deal with these weird file formats.
    --Load VPK package loader plugin local vpkloader = LoadPlugin("Plugins/VPK.dll") --Load VTF texture loader plugin local vtfloader = LoadPlugin("Plugins/VTF.dll") Next we will load a single VPK package directly from the Half-Life 2 install directory. If you have Steam installed somewhere other than the default path you can change the value of the STEAM_PATH variable to load the package from somewhere else.
    --Steam installation path local STEAM_PATH = "C:/Program Files (x86)/Steam" --Load package (Half-Life 2 must be installed) local pkg = LoadPackage(STEAM_PATH.."/steamapps/common/Half-Life 2/hl2/hl2_textures_dir.vpk") Now we can load content directly from the Half-Life 2 game files. Although the file specified below does not actually exist in the game folder, it will be loaded as if it was because of the VPK package we previously loaded.
    --Load texture local tex = LoadTexture("materials/building_template/buildingset040a.vtf") Here is the result when I run the program:

    Now if you wanted to load all the HL2 data files when your game starts, that is easy to do too. If you place this script in the "Scripts/Start" folder all the content will be automatically made available:
    local STEAM_PATH = "C:/Program Files (x86)/Steam/" local HL2_PATH = STEAM_PATH.."steamapps/common/Half-Life 2/hl2/" local dir = LoadDir(HL2_PATH) local k,v LoadedPackages = {} for k,v in ipairs(dir) do local ext = Lower(ExtractExt(v)) if ext == "vpk" then local pkg = LoadPackage(v) if pkg ~= nil then table.insert(LoadedPackages, pkg) end end end What is this good for? You can browse and view all the content in your installed games and use some assets as placeholders. Valve is pretty lenient with their IP so if your game is only published on Steam, you could probably use all the Source games textures and models. You could make a mod that replaces the original game engine but requires the game to be installed to run. It would also not be too hard to integrate these features into the new editor so that it can be used as a modding tool for different games and allow you to create new game levels.
    You can get the source for this and other plugins on Github.
  14. Josh
    The GMF2 SDK has been updated with tangents and bounds calculation, object colors, and save-to-file functionality.
    The GMF2 SDK is a lightweight engine-agnostic open-source toolkit for loading and saving of game models. The GMF2 format can work as a standalone file format or simply as a data format for import and export plugins. This gives us a protocol we can pull model data into and export model data from, and a format that loads large data much faster than alternative file formats.
    Here is an example showing how to construct a GMF2 model and save it to a file:
    //Create a GMF file GMFFile* file = new GMFFile; //Create a model GMFNode* node = new GMFNode(file, GMF_TYPE_MODEL); //Set the orientation node->SetMatrix(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); //Set the object color node->SetColor(0,0,1,1); //Add one LOD level GMFLOD* lod = node->AddLOD(); //Create a triangle mesh and add it to the LOD. (Meshes can be shared.) GMFMesh* mesh = new GMFMesh(file, 3); lod->AddMesh(mesh); //Add vertices mesh->AddVertex(-0.5,0.5,0, 0,0,1, 0,0); mesh->AddVertex(0.5,0.5,0, 0,0,1, 1,0); mesh->AddVertex(0.5,-0.5,0, 0,0,1, 1,1); mesh->AddVertex(-0.5,-0.5,0, 0,0,1, 0,1); //Add triangles mesh->AddPolygon(0,1,2); mesh->AddPolygon(0,2,3); //Build vertex tangents (optional) mesh->UpdateTangents(); //Save data to file file->Save("out.gmf"); //Cleanup - This will get rid of all created objects delete file; You can get the GMF2 SDK on GitHub.
  15. Josh
    Multithreading is very useful for processes that can be split into a lot of parallel parts, like image and video processing. I wanted to speed up the normal updating for the new terrain system so I added a new thread creation function that accepts any function as the input, so I can use std::bind with it, the same way I have been easily using this to send instructions in between threads:
    shared_ptr<Thread> CreateThread(std::function<void()> instruction); The terrain update normal function has two overloads. Once can accept parameters for the exact area to update, but if no parameters are supplied the entire terrain is updated:
    virtual void UpdateNormals(const int x, const int y, const int width, const int height); virtual void UpdateNormals(); This is what the second overloaded function looked like before:
    void Terrain::UpdateNormals() { UpdateNormals(0, 0, resolution.x, resolution.y); } And this is what it looks like now:
    void Terrain::UpdateNormals() { const int MAX_THREADS_X = 4; const int MAX_THREADS_Y = 4; std::array<shared_ptr<Thread>, MAX_THREADS_X * MAX_THREADS_Y> threads; Assert((resolution.x / MAX_THREADS_X) * MAX_THREADS_X == resolution.x); Assert((resolution.y / MAX_THREADS_Y) * MAX_THREADS_Y == resolution.y); for (int y = 0; y < MAX_THREADS_Y; ++y) { for (int x = 0; x < MAX_THREADS_X; ++x) { threads[y * MAX_THREADS_X + x] = CreateThread(std::bind((void(Terrain::*)(int, int, int, int)) & Terrain::UpdateNormals, this, x * resolution.x / MAX_THREADS_X, y * resolution.y / MAX_THREADS_Y, resolution.x / MAX_THREADS_X, resolution.y / MAX_THREADS_Y)); } } for (auto thread : threads) { thread->Resume(); } for (auto thread : threads) { thread->Wait(); } } Here are the results, using a 2048x2048 terrain. You can see that multithreading dramatically reduced the update time. Interestingly, four threads runs more than four times faster than a single thread. It looks like 16 threads is the sweet spot, at least on this machine, with a 10x improvement in performance.

  16. Josh
    Texture loading plugins allow us to move some big libraries like FreeImage into separate optional plugins, and it also allows you to write plugins to load new texture and image formats.
    To demonstrate the feature, I have added a working VTF plugin to the GMF2 SDK. This plugin will load Valve texture files used in Source Engine games like Half-Life 2 and Portal. Here are the results, showing a texture loaded directly from VTF format and also displayed in Nem’s nifty VTFEdit tool.

    Just like we saw with the MD3 model loader, loading a texture with a plugin is done by first loading the plugin, and then the texture. (Make sure you keep a handle to the plugin or it will be automatically unloaded.)
    auto vtfloader = LoadPlugin("Plugins/VTF.dll"); auto tex = LoadTexture("Materials/HL2/gordon.vtf”); This will be included in the next update to the Turbo Game Engine update, and at that point there is nothing stopping you from creating your own plugins. I hope to someone smart write a plugin for Crunch .crn files, which could cut the size of your game's distribution files down by half.
    Unlike the GMF2 model format, which provides fast loading of large model files, my internal texture format ("GTF2") has no advantage over other texture formats, and I do not plan to make standalone files in this format. I recommend using DDS files for textures, using BC7 compression for most images and BC5 for normal maps.
  17. Josh
    A big update is now available for beta subscribers!
    Multipass Rendering
    You can use several cameras to increase the depth range or mix 2D and 3D graphics.
    2D Graphics
    As discussed earlier, 2D graphics are now supported using persistent 2D objects.
    Depth Sorting
    Multiple layers of transparency will now render in correct order, with lighting in each layer. Note that I used an empty script called "null.lua" to prevent the glass surfaces here from being collapsed at load.

    Coroutine Sequences
    Lua coroutine sequences are working now. Lua coroutines can be added to an entity and they will be updated each frame, in order:
    function Start() self:AddCoroutine(self.MoveToPoint,10,0,0) self:AddCoroutine(self.MoveToPoint,10,0,10) self:AddCoroutine(self.MoveToPoint,0,0,10) end Key / Action Bindings
    Key bindings are now working for Lua scripts or C++ actors. You supply a default key code and an action name. Action name is not implemented yet but later it will allow the player to remap their keys.
    Localization
    Along with text rendering and Unicode support, you can now load language definition sets to automatically replace your text when you call CreateText.
    auto lang = LoadLanguage("Config/Localization/German.json"); SetLanguage(lang); Language files are just a bunch of JSON key pairs:
    { "Hello": "Guten Tag", "How are you?": "Wie gehts?", "I must have an apple.": "Ich muss einen Apfel haben." } Vertical fonts are not currently supported.
    Notification Boxes and File Dialogs
    In anticipation of our new editor, these have been added.
  18. Josh
    The following changes have been made to the GLTF model loader:
    Correctly loaded rotations and orientations. Mesh collision caching for faster loading. Supports transforms with negative scale and correct face orientation. Support for adjustable alpha cutoff value. Support for KHR_materials_unlit extension (full bright materials). This model from SketchFab was useful for testing because it uses so many features of the GLTF format:
    https://sketchfab.com/3d-models/ftm-0970f30574d047b1976ba0aa6f2ef855
    This scene uses alpha masking, transparency, and the full-bright extension, since it already includes lightmaps. The cartoon outline you see on the edges is actually mesh-based.
    Update is available now for beta subscribers. At this point, if you find a GLTF file that does not load correctly, or if there is an extension you would like to see support added for, I would like to hear it.
    I'm also proposing standalone GLTF material files here:
    https://github.com/KhronosGroup/glTF/issues/1420

  19. Josh
    An update is available for the Leadwerks 5 beta.
    Shadow updating is fixed so the lights no longer turn black during the update whenever a shadow changes.
    We're now using multiview to draw all six faces of a cube shadow map in one single pass! Point light shadow updates are something that used to be a considerable bottleneck in Leadwerks 4, and their performance impact is now very insignificant. Thanks to Sascha Williams for his excellent Vulkan examples.
    Joints are finished, a new upvector joint is added to lock the Y axis to a vector, all collision shape types are now supported, and body mass centers are fixed for rigid body physics will work correctly.
  20. Josh
    The GUIBlock structure now has an iVec4 "clipregion" member. This allows you to set a clipping region around a block to prevent it from drawing outside the region. The region will automatically be shrunk to show only the visible area of the widget, within the visible area of its parent. The purpose of this is to prevent text from overflowing outside of the widget. I found this necessary because the multi-line text area widget involves the dynamic creation of different persistent 2D elements, and keeping the outer border on top was becoming a problem.
    The Sprite class now has a SetClipRegion(iVec4) method, which is what gets used to display the above effect when the GUI is rendered with 3D graphics.
    Precompiled headers are enabled for the Visual Studio project. The first build will still be prohibitively slow but subsequent builds should be faster.
    Vulkan headers and lib are now included in the project, so you don't have to download the Vulkan SDK separately.
    Some other new things are included, so be sure to take a look.
  21. Josh
    Here are some things I did in the last couple days to fix a computer that was basically unusable.
    It seems that Superfetch was rebranded to "SysMain" in an update and automatically re-enabled. If your computer is grinding away either the CPU or disk usage while doing nothing, this is the culprit. Disable it in Windows services.
    The XBox games bar is suspect. I recommend disabling it now that FRAPS supports Vulkan.
    Some features in Visual Studio are making it unusably slow.
    In Project settings > Link > Debugging, set "Generate Debug Info" to "DEBUG:FASTLINK" (in the debug build only) for faster linking.
    Disable these two settings in the general program Options:
    Enable Diagnostic Tools while debugging Show elapsed time PerfTip while debugging
  22. Josh
    Putting all the pieces together, I was able to create a GUI with a sprite layer, attach it to a camera with a texture buffer render target, and render the GUI onto a texture applied to a 3D surface. Then I used the picked UV coords to convert to mouse coordinates and send user events to the GUI. Here is the result:

    This can be used for GUIs rendered onto surfaces in your game, or for a user interface that can be interacted with in VR. This example will be included in the next beta update.
  23. Josh
    A new beta is uploaded with lots of new features and improvements. Things are really taking shape!
    Animation is now supported and it's really fast. Two examples are included. Package loader plugins now supported, VPK package loader for Source Engine games included with example. Added localization example. Shaders folder very neatly organized, now contains shader family files. Config folder eliminated. Engine headers cleaned up and organized. Lots of third party libraries removed. SVG texture loader removed. Printed console output is now much quieter. Current directory is stripped from load messages. DebugError() command renamed to RuntimeError(). DebugWarning() command renamed to Warning().
  24. Josh
    A new update is available for beta testers.
    Terrain
    The terrain building API is now available and you can begin working with it, This allows you to construct and modify terrains in pure code. Terrain supports up to 256 materials, each with its own albedo, normal, and displacement maps. Collision and raycasting are currently not supported.
    Fast C++ Builds
    Precompiled headers have been integrated into the example project. The Debug build will compile in about 20 seconds the first run, and compile in just 2-3 seconds thereafter. An example class is included which shows how to add files to your game project for optimum compile times. Even if you edit one of your header files, your game will still compile in just a few seconds in debug mode! Integrating precompiled headers into the engine actually brought the size of the static libraries down significantly, so the download is only about 350 MB now.
    Enums Everywhere
    Integer arguments have been replaced with enum values for window styles, entity bounds, and load flags. This is nice because the C++ compiler has some error checking so you don't do something like this:
    LoadTexture("grass.dds", WINDOW_FULLSCREEN); Operators have been added to allow combining enum values as bitwise flags.
    A new LOAD_DUMP_INFO LoadFlags value has been added which will print out information about loaded files (I need this to debug the GLTF loader!).
    Early Spring Cleaning
    Almost all the pre-processor macros have been removed from the Visual Studio project, with just a couple ones left. Overall the headers and project structure have been massively cleaned up.
  25. Josh
    A new update is available for beta testers. This adds a new LOD system to the terrain system, fixes the terrain normals, and adds some new features. The terrain example has been updated ans shows how to apply multiple material layers and save the data.

    Terrain in LE4 uses a system of tiles. The tiles are rendered at a different resolution based on distance. This works great for medium sized terrains, but problems arise when we have very large view distances. This is why it is okay to use a 4096x4096 terrain in LE4, but your camera range should only show a portion of the terrain at once. A terrain that size will use 1024 separate tiles, and having them all onscreen at once can cause slowdown just because of the number of objects. That have to be culled and drawn.

    Another approach is to progressively divide the terrain up into quadrants starting from the top and working down to the lowest level. When a box is created that is a certain distance from the camera, the algorithm stops subdividing it and draws a tile. The same resolution tile is drawn over and over, but it is stretched to cover different sized areas.

    This approach is much better suited to cover very large areas. At the furthest distance, the entire terrain will be drawn with just one single 32x32 patch. Here it is in action with a 2048x2048 terrain, the same size as The Zone:
    This example shows how to load a heightmap, add some layers to it, and save the data, or load the data we previously saved:
    --Create terrain local terrain = CreateTerrain(world,2048,32) terrain:SetScale(1,150,1) --Load heightmap terrain:LoadHeightmap("Terrain/2048/2048.r16", VK_FORMAT_R16_UNORM) --Add base layer local mtl = LoadMaterial("Materials/Dirt/dirt01.mat") local layerID = terrain:AddLayer(mtl) --Add rock layer mtl = LoadMaterial("Materials/Rough-rockface1.json") rockLayerID = terrain:AddLayer(mtl) terrain:SetLayerSlopeConstraints(rockLayerID, 35, 90, 25) --Add snow layer mtl = LoadMaterial("Materials/Snow/snow01.mat") snowLayerID = terrain:AddLayer(mtl) terrain:SetLayerHeightConstraints(snowLayerID, 50, 1000, 8) terrain:SetLayerSlopeConstraints(snowLayerID, 0, 35, 10) --Normals if FileType("Terrain/2048/2048_N.raw") == 0 then terrain:UpdateNormals() terrain:SaveNormals("Terrain/2048/2048_N.raw") else terrain:LoadNormals("Terrain/2048/2048_N.raw") end --Layers if FileType("Terrain/2048/2048_L.raw") == 0 or FileType("Terrain/2048/2048_A.raw") == 0 then terrain:SetLayer(rockLayerID, 1) terrain:SetLayer(snowLayerID, 1) terrain:SaveLayers(WriteFile("Terrain/2048/2048_L.raw")) terrain:SaveAlpha(WriteFile("Terrain/2048/2048_A.raw")) else terrain:LoadLayers("Terrain/2048/2048_L.raw") terrain:LoadAlpha("Terrain/2048/2048_A.raw") end The x86 build configurations have also been removed from the game template project. This is available now in the beta tester forum.
×
×
  • Create New...