Shibboleth Dev Log - 05
New year, and a new resolve to blog more! So let’s get in to it!
Scripting⌗
Over the Christmas break I spent a huge chunk of time working on Shibboleth. Majority of that time was spent take a passing interest in a LISP-like programming language called Janet. I integrated it into the engine to the point where it was 99% of the way done, but ended up abandoning it. The main reason being is that the language was clearly not designed to be embedded in an environment that wasn’t purely statically linked. This caused a number of headaches, and the last straw was dealing with script compilation issues that were nonsensical. Namely, attempting to call functions on my code I didn’t actually write, and when trying to return true
from a function, it actually attempted to call it as a function, even though I never wrote it as a function call. In the end, I could no longer continue justifying spending more time on it and removed it. However, it wasn’t a total waste of time. Since the both the Lua and Janet integrations shared a large chunk of code, I did catch a number of bugs and made several improvements to the Lua integration.
Debugging⌗
Debug Menu⌗
I never made an official blog post about it, but I have had Dear ImGui integrated into the engine for a while, but I haven’t actually built a proper system around it. That has changed. There is a proper Debug
module in the engine. One of the things it allows you to do is reflect variables and functions and have them show up in a debug menu.
NS_SHIBBOLETH
// In Shibboleth_PhysicsManager.h
class PhysicsManager final : public IManager
{
public:
enum class DebugFlag
{
DrawRigidBodies,
// Other flags eventually added here.
Count
};
// Other stuff ...
private:
Gaff::Flags<DebugFlag> _debug_flags;
// Other stuff ...
};
SHIB_REFLECTION_DECLARE(PhysicsManager::DebugFlag)
NS_END
// In Shibboleth_PhysicsManager.cpp
SHIB_REFLECTION_DEFINE_BEGIN(PhysicsManager::DebugFlag)
.entry("Draw Rigid Bodies", PhysicsManager::DebugFlag::DrawRigidBodies)
SHIB_REFLECTION_DEFINE_END(PhysicsManager::DebugFlag)
SHIB_REFLECTION_DEFINE_BEGIN(PhysicsManager)
.base<IManager>()
.ctor<>()
.var(
"Debug Flags",
&PhysicsManager::_debug_flags,
DebugMenuItemAttribute("Physics"),
NoSerializeAttribute()
)
SHIB_REFLECTION_DEFINE_END(PhysicsManager)
NS_SHIBBOLETH
SHIB_REFLECTION_CLASS_DEFINE(PhysicsManager)
NS_END
The code example above is taken from the Physics Manager. The DebugMenuItemAttribute("Physics")
line says to put this variable under the Physics
path in the debug menu. Path submenus are separated by a forward slash (/
). The full path will turn into <path_given_to_attribute>/<var_or_function_name>
. In this instance, these flags will appear in the path Physics/Debug Flags/<flag_name>
. These flags are checkboxes in the debug menu. If a function is given instead, the function will be called when clicked.
This GIF is what the above reflection code produces. The attribute will add the reflection debug items when an instance of the reflected object is instantiated. What this means is the debug menu is dynamic. If multiple instances of an object are instantiated, then it will start appending numbers to the end until it finds an unused number. For example, if I instantiated another instance of PhysicsManager
, we would find another set of flags under the path Physics/Debug Flags 2
.
While this is a nifty feature, I don’t expect it to be used. The main intent is for the debug menu to be used on managers, and managers only ever have one instance.
NOTE: While I did say that the menu gets populated when an instance of the object is created, it only actually works when they are allocated via reflection. This is because the attributes rely on callbacks from the reflection system to properly function. If you were to write new PhysicsManager()
, the debug menu would not populate.
Debug Rendering⌗
Another debugging feature I worked on over the holiday break (as seen in the above GIF) was adding proper debug rendering! Debug rendering supports a handful of built-in shapes and any arbitrary meshes. Arbitrary meshes are supported by passing in a ModelResourcePtr
to the DebugManager
.
Built-in shape types include:
- Line
- Plane
- Sphere
- Box
- Cone
- Capsule
- Arrow
- Cylinder
An arrow is basically a cylinder with a cone at the end to denote direction. Every debug rendering API takes in basic information about the shape (usually position and size) and what color you want the shape to be and whether you want the shape to be depth tested.
All shapes are drawn in two-sided, wireframe mode with no alpha blending. All colors will be opaque with no transparency. All shapes mesh data are programmatically generated and can be subdivided if desired. Currently the engine hardcodes majority of the shapes to 8 subdivisions.
Every renderDebug*()
function will return a DebugRenderHandle
. This handle will give you a reference to the DebugRenderInstance
. This will allow you to update the transform and change the color of the shape. If the destructor is called on the handle, the instance will be removed from the render list.
Physics⌗
Also not covered in a prior blog post, but I did get some basic support for PhysX in the engine. Nothing too fancy right now, but here’s a small GIF showing it in action.
Input⌗
I added support for input states to the engine. Key bindings can now optionally be bounded to various input states. When the InputManager
is requested to change input states, if the binding is not part of that input state, then that binding will always report 0. This was used to facilitate things like the debug menu, where I wanted to be able to move the mouse without causing the camera to rotate.
Unit Tests⌗
I spent a little bit of time getting unit tests into a better state. A lot time ago I ditched Catch2 for doctest. I have since reversed this and moved back to Catch2. The main reason is because in doctest’s attempt at being thread safe, it does things with global, static thread local variables that do not mix well with my project setup. This causes difficulty when the App
class needs to be fully initialized when running a test, but the allocator hasn’t been instantiated yet. Moving back to Catch2 alleviates these issues, since it doesn’t support multithreading out of the box, and honestly never should.
Source Control and Continuous Integration⌗
I never really mentioned it before, but I moved my repo over from BitBucket to GitHub. The reason for this is because BitBucket dropped support for Mercurial. Because of this, I migrated everything over to Git and GitHub. I also created a dev
branch where I will do all my dev work so that master
can remain stable.
I also put some very basic CI support into the project. Currently, whenever anything gets committed to the master
branch, an Azure DevOps instance will do a build of the game with the Debug|x64
configuration on Windows. Currently it only compiles and links the game. It does not run any unit tests, nor does it save the final executables to a cache somewhere. At the very least I plan on adding running the unit tests and building other configurations of the game.
Until We Meet Again⌗
This update has been a long time coming. I always say I need to get better at posting about progress, but at the very least, I do consistently work on the engine, even if I don’t talk about it. :P
Thanks for reading, and see you next time!