Three days of slow and painful progress culminating in an epic 48-hour substance-fueled devbinge, yielding astounding results. That's pretty much how I would sum up this week. As much as I have tried to distance myself from these "devbinges" after they took their toll on my mental health and sanity during our exhausting Kickstarter campaign, sometimes old habits die hard... and sometimes a little bit of decadent self destruction yields significant progress and is exactly what the doctor ordered. I suppose this blog will inevitably reflect the delicate dance with insanity one must perform in order to succeed with an indie startup company. In the name of my company and my dream, I regret nothing... Judge me.
In my quest to bestow upon Patryk everything he could possibly need to create an aesthetic masterpiece of a game, taking the entire pixel art concept to the next level, I have continued my obsessive compulsive bugfixing and request-driven feature development for ESTkv1.5.7 "Sex and Candy" Release of the Elysian Shadows toolkit. The majority of the minor bugs and stability issues have been resolved, now I'm tackling far more fundamental issues and am implementing far more complex features, remaining in my beloved low-level R&D land while the rest of my team utilizes my work several layers of abstraction above, creating the actual experience of Elysian Shadows as a game.
Flip/Rotated Normals
If you remember from my previous blog post, I already addressed one fundamental issue with normal mapped tiles not being lit perfectly on orthogonal planes. It turns out we have yet another lighting issue that has been part of our engine since day one and has only now been recognized by Patryk during his latest round of tile and normal map production. The problem stems from reorienting tiles within the engine not resulting in reorienting their normal maps.
Our engine allows our artists to arbitrarily flip and rotate every tile to maximize sheet reusability and minimize our VRAM footprint. Unfortunately the act of rotating a tile affects its tangent vector, which is utilized in the fragment shader to convert tangent-space normal map vectors into world space before being fed into the lighting algorithm. Since our engine deals with a preset number of possible texture orientations on a preset number of planes, I have opted to hardcode these tangent vectors to reduce run-time calculations. My solution here was to create a lookup table of these tangent vectors to be indexed by each texture's orientation enumeration. These resulting tangent vectors are then submitted to the fragment shader as uniforms upon changes in texture orientation.
Unfortunately the act of flipping a tile is not so simple. Not only does it affect the tangent vector, but it also affects the normal vector as well as the interpretation of the normal map. When flipping a tile in 3D space, its normal becomes flipped as well, pointing in the opposite direction. For a colormap, this simply means sampling the UV coordinates in reverse to flip the tile. However, you cannot simply sample a normal map in this manner due to the nature of its representation. Along with a 2D lookup table of normal vectors indexed by plane and texture orientation, I have also had to add special logic to our fragment shader to flip certain channels sampled from our normal map when the corresponding textures are flipped. The results are seamlessly lit arbitrarily oriented tiles in 3D space:
Dynamic Shadows
Dynamic shadows have long been a hallmark of our engine, yet they were never completely integrated within ESTk. Now they're back, and they are toggle-able along with the entire lighting engine itself. These advanced rendering features can be enabled and disabled to optimize performance while designing content or to emulate the engine running on less capable hardware.
Dynamic Level and Area Reloading
First we allowed for the artists to refresh texture maps, normal maps, and specular maps at run-time. Then last week I implemented the ability to refresh shaders at run-time for my own debugging and development purposes. This week our Lua scripter, Tyler Rogers, got jealous of our pristine workflows and requested similar functionality for his scripts. As a result, I implemented the ability to fully refresh levels and areas within ESTk at run-time. This serves to refresh all corresponding Lua scripts. It also prevents us from having to exit out of the Toolkit and reload the entire project every time we pull down the latest assets committed by our team mates.
Lua Table Inspection, Injection, and Run-Time Debugging
This is honestly one of the most abstract, complex, and difficult software feats I have ever attempted. It has required countless rewrites, filled up several walls in our dev studio, and has consumed many pages of notebook paper... In an attempt to allow artists, designers, and scripters to create, modify, and debug every aspect of our engine and Lua behaviors at run-time without requiring programming knowledge, we dreamt up allowing ESTk to serve as a type of run-time Lua debugger, allowing for the access and modification of every table and attribute maintained by the Lua VM.
After nearly destroying myself implementing this bitch (that's what I worked on during the binge, haha), the end result is what we're calling the "Visual Studio" of Lua development. An entire IDE/debugging suite for the Lua programming language. We have an interactive prompt for executing Lua code dynamically at any point during the engine's run-time also serving as a log for interpreter output, displaying errors, catching exceptions, offering stack traces, table dumps, memory usage, and even dumps of the virtual C stack for communicating between Lua and the engine.
The most exciting aspect of this entire endeavor for us is that we believe it will essentially allow ESTk to "write itself." Instead of having to develop specialized widgets for every new component or behavior as they are created, we can simply create attribute Lua tables to be modified through ESTk in this manner, removing any need for us to develop specialized GUIs in the future!
To my team: I'm compiling the new Windows build right now and will have it up on Owncloud within the next few minutes. To inspect a Lua table, use estk.inspectTable(luaTable) in the Lua console. Now go forward, brothers. These features will help you on your journey, because it sure as hell isn't safe to go alone.