I just finished committing and issuing a pull request for the bones of Gladius’ graphics service. Alan and I have been talking about how to do this properly for at least two months now, so it really feels good to write down some code. Here’s how it looks:
A little explanation first…
Keeping modern game engine design in mind, Gladius implements an Entity/Component system where Components such as Models or Cameras are added to Entities to give them functionality. Components are supported by different systems called Services, like the Graphics service. This approach circumvents the need for a bloated, all-inclusive base class from which plants, paladins, and planets all inherit to receive common functionality. Instead, with a proper dependency system, a Camera or Model component can insist that there is a Transform component on the entity to which it’s being added so it can move through – and interact with – space without spilling remnants of itself onto other game objects through inheritance.
So, making an object appear on the screen is as simple as adding a model component to an entity in a scene. Of course, a model needs data, and there are plans being forged to support all sorts of inputs for meshes. However, that’s another discussion entirely. Right now, all you need to know is that Gladius has the fundamentals in place to build basic procedural meshes (a cube, really).
Rendering
The graphics service can achieve a complete render of the entire engine’s state by following this short, pithy, pseudo-code:
- For each scene in the engine, find two lists: cameras and models.
- For each active camera in a scene, render all of the scene’s models.
This tiny algorithm translates into this longer, Gladiusized-pseudo-code:
- Clear the current drawable object (right now, a <canvas>).
- For each scene in the engine, get lists of entities with camera and model components respectively.
- For each entry in the camera list, grab the camera component; if its an active camera, render the mesh in each model in the model list.
It’s a great starting point, but there are very obvious inefficiencies and missing features in the above process, including…
- The inability to render to anything except a canvas: In the near future, we’ll write code to present the proper abstractions to developers which will allow different renderables to be rendered for a given camera. You may want to render to a buffer, a canvas, or a textbox. We were thinking about naming these Films, but Renderable (which is less cool) might also work.
- A lack of culling: Right now, rendering from the perspective of a camera is expensive in a large scene with many objects. We need a way to slice up the scene so that we can discard what’s not in view and only render what’s important. But, don’t worry: my octree.js library is nearing completion and will soon fill the void for a space partition culling mechanism. It’s very similar in spirit to the octree implementation in CubicVR.js which I also wrote (unfortunately, with strong evidence of the job I did porting it from a Java implementation I wrote a couple of years ago).
Also, since the graphics service distributes cameras, there is the potential to avoid storing a list of scenes. Camera entities are parented to a scene, so a list of models (and other important things in the future) relavent to a camera can be extracted from its parent. This approach will be useful when spatial partitioning lands, since a run through an octree with the frustum of a specific camera will yield a list of visible models to render.
CubicVR.js & Graphics Services
And where would we be without CubicVR? Well, there would be a significant gap in the Dreamcast homebrew 3D engine community. But, more importantly, we wouldn’t have the talent and engineering behind the engine that drove Flight of the Navigator, No Comply, and Rescuefox.
CubicVR.js is Gladius’ first rendering core, and we’re glad to have it. It gracefully handles a complex and feature-rich shader pipeline, and supports all sorts of neat mesh building and deformation techniques (now with dynamic vbo’s). Check out the samples directory for a bunch of hacker’s delight and eye candy.
Currently, we’re wrapping CubicVR’s Camera and Mesh objects with responsibly named components: Camera and Mesh. Since the graphics service currently in Gladius is specific to CubicVR.js, it knows about the native CubicVR.js objects which live on components and how to make them cooperate.
Our hope, of course, is to support a wide variety of rendering engines in the future – even 2D ones. Each would require an implementation of the graphics service and the core grpahics components to comply with the particulars of the renderer.
Try it!
The most important part of all of this is that you can grab the branch and try it out right now. It won’t do much yet, but you can tell it’s working because gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); is doing its job by blacking out the canvas on the test page. You can change that by contributing :).
For now though, I’m just happy all the supporting infrastrcture is finally in working order to hack on this renderer. I can almost feel the games we’ll create with this engine.