After what seemed like a totally unproductive and inconsequential morning, and an equally slow and frustrating afternoon, I managed to at least prototype two rather important things that have been lingering around Paladin and CubicVR for a long time.
First, Gladius had a rendering engine, but it was almost entirely useless unless you really like clearing a canvas. Second, CubicVR.js could only draw to one canvas on a page. Not “one canvas at a time”, just one canvas.
Now, the hard part of both of those problems are dealt with, and it’s no coincidence that they were both attacked in the same day.
State of Gladius
Progress on Gladius has been steady as Alan irons out the last bits of the framework for the looming 0.1 deadline (roughly, the end of December). Doing my part, last month I boasted about Gladius getting a renderer, but it was really only partially complete.
What it did: cleared the screen
What it does now: builds models from meshes and materials, clears the screen, renders those models, and has a scheduled engine task to repeat it all
Clearly, today’s work yields far better results than the prior engine. Obviously, we still lack a lot of things that one would assume a modern graphics engine would provide to game developers, but this is a big leap.
In short here’s how preparing and rendering something looks like right now:
- create a Mesh resource from a script
- create a Material resource from a script
- create a Model component
- create an Entity to hold the Model
- feed the Mesh and Material to the Model
- add the Model to the Entity
- render!
This approach is interesting because it relies heavily on prepared scripts to build resources like Meshes and Materials. For instance, there is a procedural cube Mesh resource which will (unsurprisingly) create a Mesh that is a cube. There is also a procedural sample Material resource which will create a simple red Material (no textures yet, just colour). Then, as a game developer, it’s easy to swap out Mesh or Material resources when you want different ones for a particular Model, and it’s easy to reuse the same procedural resource to give you lots of slightly different Meshes or Materials.
Also, it may not be entirely obvious that, by using procedural resources, we have the ability to build an entire game without loading a single bitmap or otherwise baked asset. Imagine building a game that involves a simulation of a universe. Instead of forcing the user to wait precious seconds to download a large file describing the shape and state of the universe, they can download a tiny piece of code which, when used with a particular seed, creates the same universe. This same technique can be used to create not only Meshes, Textures and Materials, but even Sounds, Forces, or Terrain. A lot of demoscene groups solve size problems similarly to create the notoriously mind-boggling 64k intro category.
Just imaging stuffing this into 64k:
Some have even attempted procedurally-generated games:
Coasting on back from that tangent, Gladius is a long way off from being able to produce these kind of effects, but that’s generally what we’ve got in mind. We want devs to really be able to use their imaginations with Gladius, especially in a climate where fat downloads are often not appreciated.
Massaging CubicVR.js
The way we’ve used CubicVR.js in the past it may be surprising that its in need of some caretaking. I don’t at all mean to imply that it lacks functionality. In fact, it continues to surprise me even as I flip through test html pages fixing syntax problems I’ve introduced. However, there are a couple pieces of CubicVR.js that require some love:
- The Octree implementation has drifted slightly from the progress and structure of the rest of the engine, so it needs repair. The current implementation looks a lot like Java (my fault), and there is a new, mostly complete JavaScript-inspired version called octree.js which is available to start integrating.
- CubicVR can only render to one canvas on a page. Rendering to multiple canvas elements should be possible, and indeed easy.
Luckily, one of Dave Humphrey’s students has offered to help bring CubicVR.js’ Octree back to life, which he has already completed to some degree. Soon, while octree.js gets wedged in, we’ll need a few tests to compare it with CubicVR.js’ current implementation – for sanity, and for speed comparison.
Multi-<canvas> Support
While getting Gladius’ renderer in order today, I encountered a few pieces of CubicVR.js on which I’ve had a grudge for some while. The initialization sequence and global namespace CubicVR.js uses are cumbersome when being used inside a project like Gladius, which we’re trying to keep as flexible as possible. Much of my perturbation stems from the question, “What happens if the developers wants to render to two independent canvas elements on a page?”
Gladius is an especially good example because of its qunit testbed, which uses async tests likely more often than it should. Guess what that means. You probably thought, “multiple simultaenous renders!” Since we need to test multiple engine initialization scenarios, Gladius’ testbed will try to render to one canvas multiple times, or several canvases once each. I chose to implement the latter because of the async demands, but I really sidestepped the problem by only rendering once in the entire test module, …which is bad. Our testbed will only get more complicated, so, CubicVR.js needs a multi-canvas solution fast.
Digging Into the Problem
It’s not immediately obvious what will happen if I try to do this with a current build of CubicVR.js (which I’ve actually documented):
1 2 | |
In fact, we end up in something of an undefined space here when we want to continue to use both canvases. When you create a texture, to which WebGL context does it belong? If you try to find out, Firefox will likely crash like it did for me today a few dozen times while I debugged the situation.
Initially, I attempted to solve this problem in a stateful manner, inspired by the way WebGL itself works:
1 2 3 4 | |
In this simple example, we attempt to create a Texture for each canvas. It’s clear which canvas owns which Texture, since explicitly switch “contexts”. CJ was quick to point out the pitfalls of this approach, and the problem sat unanswered for a while. Just think of the nightmare inflicted upon forgetful developers when they leave out a setContext somewhere and don’t understand why their renders are incorrect.
Proposal for a Solution
Fortunately, there was some work done to create Automated Testing which skirted on the multiple-canvas problem, but didn’t fully address it. It did, however influence the codebase in a way which was already occuring in several other spots.
CubicVR.js uses a require.js-like module structure, where modules are registered by the core, and executed before initialization to ensure the CubicVR namepsace is full of all the goodies that one would expect, like CubicVR.Mesh and CubicVR.Scene. What I hadn’t thought of before was how much this design allows the isolated initialization of each module.
In other words, why not initialize each registered module whenever a call to CubicVR.init or CubicVR.start occurs, and attach the result to a properly scoped object? So, I did just that: I forced much of the CubicVR namepsace into a closure which is instantiated by a call to CubicVR.init or CubicVR.start. My proposed solution makes the return value from a call to either function a CubicVR “Core”, which is basically the entire namespace as you would expect it. For example:
1 2 3 4 5 6 7 8 9 | |
This code looks remarkably similar to current CubicVR.js code. In case you missed it, the only change is that I’ve replaced every CubicVR with core, except where completely necessary, like initialization. With this method, only a few fundamental functions are attached to the global CubicVR namespace, so many versions of CubicVR.js’ modules can exist in harmony.
The branch in which I’m doing my multi-canvas support work contains a test of the functionality in the instancing test folder, which demonstrates the ability to have CubicVR.js render to multiple canvas elements, and control them independently with respective MouseViewController objects. I thought interactivity would be particularly convincing.
A bit more work needs to be done to update the rest of the tests and samples with the syntax change I’ve proposed, and I’d like to get some more feedback from CJ, but I’d say this work puts us in the position to have multiple canvas support in the very near future of CubicVR.js.
Rendering a Conclusion…
I’m glad Paladin and Gladius are affecting CubicVR.js so positively. The focus is forcing me to address problems that are otherwise easy to ignore. More importantly, however, there is much more activity from contributors recently, many of which are involved in Dave Humphrey’s courses at Seneca. The sudden increase in bug fixes and feature additions in Paladin as a whole – constituents included – is really starting to show in the way of more stable tools.
More progress soon. Expect big things.