I've made progress with the new vehicle system and it is shaping up nicely. The vehicle wheels consist of a slider joint with a spring (the strut) connected to a pivot, connected to the wheel by a hinge joint (the axle). If the wheel can be steered, an additional pivot is inserted between the strut and axle, with a motorized hinge to control steering. There were two big problems in addition to this that need to be solved in order to make a vehicle that is stable at high speeds.
First, the m
It's nice to have my big monster computer back, and everything is just the same as I left it. I have a triple-boot machine with Windows 10 and both 32 and 64 bit versions of Ubuntu 16.04. This is easier than trying to set up multi-arch compiling on one install of the OS, though I look forward to the day I no longer have to bother with it.
I'm running out of hard drive space on my Windows 10 500 GB SSD so I ordered a 1 TB SSD, and I plan to transfer the 500 GB SSD into my laptop to replace t
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
Here is a script for a first-person player. All of this is actually working now. It's very interesting that physics, rendering, culling, and Lua game code are all executing at the same time on different threads. CPU usage in a simple scene and some physics is about 35% on a quad core processor, which is good.
I think the most interesting thing here is that to break the kinematic joint used to control an object the player picks up, you simply set the variable to nil. However, I did run into
I've successfully converter the sliding door script over to the new API. This will all look very familiar, but there are some cool points to note.
The entity mass can be retrieved and set with a property as if it was just a regular variable. Underneath the hood, the engine is making method calls.
I decided to prefix most field names with "slidingdoor_" to prevent other scripts from accidentally interfering. The "enabled" value, however, is meant to be shared.
The Entity is
With Christmas approaching I am now turning my attention to finishing Leadwerks Game Engine 4.6. The major features planned are peer-to-peer networking and a new vehicles system, as well as miscellaneous bug fixes. A beta build will be made available early on Steam for testing.
A big update for the beta of the upcoming Turbo Game Engine is now available, adding support for VR and Lua script!
VR Rendering
Turbo Game Engine now supports VR rendering, with support for true single-pass stereoscopic rendering on Nvidia GPUs. Other hardware will use a double-rendering path that is still faster than Leadwerks. To turn VR on simply call EnableVR(). Controllers are not yet supported, just rendering.
Lua Scripting
Lua script is now supported in Turb
During development of Leadwerks Game Engine, there was some debate on whether we should allow multiple scripts per entity or just associate a single script with an entity. My first iteration of the scripting system actually used multiple scripts, but after using it to develop the Darkness Awaits example I saw a lot of problems with this. Each script used a different classname to store its variables and functions in, so you ended up with code like this:
function Script:HurtEnemy(amount)
if s
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 milli
I'm using the excellent sol2 library to interface C++ and Lua in the upcoming Turbo Game Engine. I've decided not to create an automatic header parser like I did for tolua++ in Leadwerks 4, for the following reasons:
There are a lot of different options and special cases that would probably make a header parser a very involved task with me continually discovering new cases I have to account for.
sol2 is really easy to use.
Each class I want available to Lua will have a stat
Building on the Asset Loader class I talked about in my previous blog post, I have added a loader to import textures from SVG files. In 2D graphics there are two types of image files. Rasterized images are made up of a grid of pixels. Vector images, on the other hand, are made up of shapes like Bezier curves. One example of vector graphics you are probably familiar with are the fonts used on your computer.
SVG files are a vector image format that can be created in Adobe Illustrator and othe
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 f
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
As I work with the new engine more and more I keep finding new ways it makes life happy and productive.
Smart Pointers
I have talked about how great these are at length, but I keep finding new reasons I love them. The behind-the-scenes design has been a lot of fun, and it's so cool to be able to write lines of code like this without any fear of memory leaks:
LoadSound("Sound/Music/fully_loaded_60.wav")->Play();
What do you think that code does? It plays a sound, keeps it in m
I've got the basic GI algorithm working but it needs a lot of work to be correct. I tend to do very well when the exact outcome is well-defined, but I am not as good at dealing with open-ended "artistic" programming. I may end up outsourcing the details of the GI shader to someone else, but the underlying data management is solid enough that I am not scared of it anymore.
There's a lot of aspects of the design I'm not scared of anymore. We worked out smart pointers (including Lua integratio
I finally have a cool screenshot to show you of our new real-time global illumination working.
Here is a comparison screenshot showing direct lighting only:
Now there are still lots of small issues to worry about. Right now I am only using a single cone trace. More cones will improve accuracy, but I think light leaking is just always going to be a fact of life with this technique. Still, the results looks great, require no precalculation, respond to environment changes, and
Now that we have our voxel light data in a 3D texture we can generate mipmaps and perform cone step tracing. The basic idea is to cast a ray out from each side of each voxel and use a lower-resolution mipmap for each ray step. We start with mipmap 1 at a distance that is 1.5 texels away from the position we are testing, and then double the distance with each steo of the ray. Because we are using linear filtering we don't have to make the sample coordinates line up exactly to a texel center, and
I have successfully transferred lit voxel data into a 3D texture. The texture is now being used to display the lighting at each voxel. Soft edges are appearing due to linear filtering in the texture. To achieve this, I used an OpenGL 4.2 feature which allows you to write values into any arbitrary position in a texture. This could also be used for motion blur or fluid simulations in the future. However, since Mac support for OpenGL only goes up to 4.1, it means we cannot use real-time GI on a Mac
We left off on voxels when I realized the direct lighting needed to be performed on the GPU. So I had to go and implement a new clustered forward renderer before I could do anything else. Well, I did that and now I finally have voxel lighting calculation being performed with the same code that renders lighting. This gives us the data we need to perform cone step tracing for real-time dynamic global illumination.
The shadows you see here are calculated using the scene shadowmaps, not b
After three days of intense work, I am proud to show you this amazing screenshot:
What is so special about this image? I am now successfully uploading voxel data to the GPU and writing lighting into another texture, using a texture buffer object to store the voxel positions as unsigned char uvec3s. The gray color is the ambient light term coming from the Blinn-Phong shading used in the GI direct light calculation. The next step is to create a light grid for the clustered forward rende
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
An online implementation of physically-based rendering in the Khronos Github was pointed out to me by @IgorBgz90 and @shadmar. This is very useful because it's an official implementation of PBR that removes a lot of guesswork. Here is my first attempt, which is not using any cubemap reflections:
And here it is with cubemap reflections added:
I plan to use the real-time global illumination system to generate the reflection data, instead of using environment probes. T
The Model class is being slightly restructured to add support for built-in LOD without the need for separate entities. Previously, a list of surfaces was included in the Model class itself:
class Model
{
std::vector<shared_ptr<Surface> > surfaces;
};
This is being replaced with a new LOD class, which allows multiple lists of surfaces containing less detail to be stored in the same model:
class LOD
{
std::vector<shared_ptr<Surface> > surfaces;
};
class Model
With the help of @martyj I was able to test out occlusion culling in the new engine. This was a great chance to revisit an existing feature and see how it can be improved. The first thing I found is that determining visibility based on whether a single pixel is visible isn't necessarily a good idea. If small cracks are present in the scene one single pixel peeking through can cause a lot of unnecessary drawing without improving the visual quality. I changed the occlusion culling more to record t