Development Days
Some of the Leadwerks Game Engine design was originally developed to run on PC and mobile. In order to supported multiple renderers (OpenGL and OpenGLES) I implemented a system that uses an abstract base class with an API-specific class derived from that:
-
Texture
- OpenGLTexture
All OpenGL code was contained in the OpenGLTexture class. This worked fine, and theoretically it would have allowed us to support multiple renderers within one build, like OpenGL and DirectX. In practice it's a feature that was never used, and created a lot of complicate class hierarchies, with functionality split between the base and derived classes.
In the new engine, all rendering code is completely separated in a separate thread, and we have a separate class that is a stripped-down representation of the object the programmer interfaces with:
- Texture
- RenderTexture
When the programmer calls a command that makes a change to the Texture object, an instruction is added to a queue of commands that is sent to the rendering thread, and their change is also made on that RenderTexture object, although not instantaneously.
Right now I am stripping out the derived classes and turning classes that were previously abstract into full classes. It's quite a big job to restructure a complex program like this but it needs to be done. Even when we switch over to Vulkan / Metal I don't see us every supporting multiple APIs within a single build, and I am glad to get rid of this aspect of the engine.
I'm also doing the same thing for physics. An Entity object in the main thread can have a PhysicsNode object that lives in the physics thread. However, this does not get created unless there is some physics command performed on the entity, like setting the mass or adding a collision shape.
Other stuff I want to change:
- Get rid of GetClass() / GetClassName() method.
- Get rid of Object::ModelClass etc. constants.
- Replace all static class constants with global variables, i.e. WINDOW_FULLSCREEN instead of Window::Fullscreen.
It's actually best to declare constants with enum because then they get evaluated as a constant and can be used in array declarations and switch statements. It only needs to be declared once, in the header, but unlike a macro it stays contained within the namespace it is declared in:
enum { MAX_PHYSICS_THREADS = 32 };
The next step is to create a usable programming SDK with models, lights, scene loading, scripting, and physics. This will allow beta testers to actually start developing games. The lack of a visual editor is a challenge, but at the same time we are now using more standard file formats like DDS and GLTF, which gives us better consistency with the output of various modeling programs. I'd like to start looking at a Lua debugger for Visual Studio Code soon. There seems to be some debuggers out there already, but I have no idea how the communication between the debugger and the game is supposed to work. I invented my own network data protocol in Leadwerks and there isn't any standard I am aware of.
2D graphics in the new engine are quite different from in Leadwerks, which used drawing commands to control what gets displayed on screen. Since the rendering all occurs asynchronously on another thread, this approach does not make sense at all. I also had a problem with the GUI in this design. The GUI system uses a script with a drawing command to redraw each widget, but we don't want any Lua code running in the rendering thread.
The solution is to make 2D graphical elements persistent objects:
auto window = CreateWindow(); auto context = CreateContext(window); auto world = CreateWorld(); //Create some 2D graphics auto rect = CreateRect(context,10,10,200,75,true); rect->SetColor(0,0,1); auto line = CreateLine(context,10,10,200,200); line->SetColor(1,0,0); auto text = CreateText(context,"Hello!",0,0,200,75,TEXT_CENTER); text->SetPosition(10,10) text->SetFont(LoadFont("Fonts/arial.ttf",18); while (not window->Closed()) { world->Render(context); }
Just like with an entity, you can set the variable to null to stop drawing the element.
while (not window->Closed()) { if (window->KeyHit(KEY_SPACE)) rect = nullptr; world->Render(context); }
2D elements can have a hierarchy, so you can create one element that gets drawn on top of another:
auto rect = CreateRect(context,10,10,200,75,true); rect->SetColor(0,0,1); auto rect2 = CreateRect(rect,4,4,rect.size.x-8,rect.size.y-8,true); rect2->SetColor(1,1,1);
We need the GUI working for some VR projects I want to use the new engine in soon. Once the items above are all working, that will give us everything we need to start working on the new editor.
- 1
7 Comments
Recommended Comments