Some time ago we had a demo that would give a 3D representation of routes obtained from the Google maps api, along with some points of interest the user could search for (like coffee shops). While the library keeps getting updated, this demo did not, and no longer worked on systems running recent versions of the library. This was unfortunate as the demo showed some neat applications for C3DL, so we decided to resurrect it.
Having never seen the original working, I had a difficult decision to make. Do I update the existing code to work with the newer versions of the library? Or do I write a new demo from scratch? In theory the update should have been the easier route, but it was using code that had been rendered obsolete before I joined this project. Instead of learning an outdated version of the library to update one demo, I kept only the basis of the code (mostly the interaction with the Google Maps API), and wrote most of the rest. As the demo progressed we had several ideas about things to be improved or added. For example, each of those points of interest gets a little 3D model (a tetrahedron for now) to show where it is, but we realized it would be better if the user could click on that point of interest and find out what it is, what the address is etc., so now you get a pop-up with the name, address and phone-number of the point.
Without further ado, here’s a picture of (and link to) the demo:
I did have to limit the number of roads that are visible at one time, and only show points of interest that are close by, just to reduce the strain on the system (A trip from Toronto to Vancouver involves a lot of roads and an extraordinary number of coffee shops).
There are a few things that I’d like to improve on when the opportunity presents itself, or would be happy to see other people try.
- This uses an old version of Google’s API. It would be nice to bring it up to version 3.
- The overhead compass would look nicer if it was partially transparent (So you could see the letters indicating the directions, but not the rectangle they sit on). While this concept is supported by OpenGL and the png format, support for it isn’t in the library yet.
- The tetrahedron’s that indicate points of interest work, but a better model would make the demo look nicer.
- A road model that was more than just a flat rectangle could make the demo look nicer too.
- When you click on a point of interest, it causes a pop-up with information about that location, instead it would be nice to have that information appear as floating text in the canvas, so it doesn’t interrupt the demo.
- It would also be nice to get elevation data, but the Google Maps API doesn’t provide it, so it would have to come from somewhere else. Then we could make the roads go up and down with the elevation instead of assuming the world is perfectly flat. That would require a little extra code to accommodate the new data, but most of the hard calculations are already done.
- The overhead compass often misses getting drawn when the page first loads, but will show up on a refresh. This is peculiar as the code is supposed to wait until all models are loaded before it actually starts.
mozilla-central/obj-ff-dbg/dist/bin/jsI started the shell and tried the folowing:
macbook-pro:bin andor$ ./js
js> var ns = {};
js> ns.test = function(){};
(function () {})
js> var iTest = new ns.test();
js> print(iTest instanceof ns.test);
false
Still false. I decided to update my repository and rebuild. Maybe the issue was already resolved and only existed for a few days?
I went back to my console and updated.
hg pullI then did a full build.
make -f client.mk buildThe first thing I noticed is that the small icons in each tab are now cute little progress bars.
Back to the issue, I ran the test again and it still fails. I’m pretty much stuck and this point in regards to the bug. I’ll either have to wait for someone else to confirm or I may try on another system. While I wait for that, I’ll be returning my attention back to porting to WebGL.
As promised, I posted the updated orbiter demo which works with the 1.1 version of our library.
The old version of the demo used a zoom() method which I thought should be divided into two functions: goCloser() and goFarther().
In my last post, I managed to get our library to run without crashing using WebGL. After playing with the code a bit more, I was able to render untextured models. Clearing the color buffer and now untextured models. I would say progress isn’t too bad.
Today I sat down and started cleaning up the code and I focused on getting points to render. I figured using VBOs to render points would give me some practice before trying to apply them to more difficult situations, such as with COLLADA files. COLLADA models load, but the VBO code isn’t in the right place. With a bit of effort, I managed to render the points just like we used to with the Canvas 3D plug-in.
I experimented with the different types of buffer usages (static, dynamic and stream drawing). I didn’t see any performance change. Albeit, this is probably because I have to specify the data for each frame. When rendering COLLADA models, the data won’t change per frame and we may see a performance increase.
C3DL currently uses the Canvas3D plug-in. If a user wants to see any of our demos run in the browser, they’ll need to install the plug-in. Canvas3D is precursor to WebGL, which was recently added to the Mozilla nightly trunk and will soon be included in a future release of Firefox. Therefore, we need to update our library so demos will run without requiring any plug-ins.Before I started to update the library, I took another look at Vlad’s spore creature viewer. I noticed he was using VBOs. This is interesting because when I tried using them in C3DL a while ago, I didn’t experience any performance increase I should have. Maybe there was an issue and it was resolved? I’ll come back to VBOs in a later blog.
On my system I have Firefox 3.0.13, 3.5.3 and 3.7a1pre. I’ll be using the two latest versions to get this going. I use the Canvas3D plug-in with 3.5. Firefox 3.7 is endowed with the awesomeness of WebGL, so it should be enough to run some of my local C3DL demos. Theoretically, the only line which needs changing is the line which acquires the rendering context. Unfortunately, you’ll see how that’s not the case.
So I opened up the C3DL rendering file and updated how the library acquires a rendering context:
try
{
// Does the user have the Canvas3D plugin?
glCanvas3D = cvs.getContext('moz-glweb20');
}
catch (err)
{
glCanvas3D = null;
}
if(!glCanvas3D)
{
try
{
// Does the user have a browser that supports WebGL?
// If so, use that instead.
glCanvas3D = cvs.getContext('moz-webgl');
}
catch (err)
{
glCanvas3D = null;
}
}
I started Minefield (3.7) and tried to open a demo. I immediately received a C3DL error. This is a bit odd because the conditional below returns true on 3.5 and false on 3.7.
if(effectTemplate instanceof c3dl.EffectTemplate){...}
I decided to make the conditional pass regardless, since I knew for a fact effectTemplate WAS an instaceof c3dl.EffectTemplate. At the same time I thought it would be nice to have a JavaScript debugger in case I need it. Unfortunately, I didn’t find a compatible version of Firebug for 3.7 and I didn’t know how to use Venkman after I had installed it. I was a bit impatient so I just used printf equivalents.
c3dl.debug.logInfo("I'm running");
After forcing the conditional to pass, I tried my code again and got the following error in the Firefox console:
Error: uncaught exception: [Exception... "Not enough arguments [nsICanvasRenderingContextWebGL.uniformMatrix4fv]" nsresult: "0x80570001 (NS_ERROR_XPC_NOT_ENOUGH_ARGS)" location: "JS frame :: file:///Users/andor/Documents/Canvas3D/canvas3dapi/ renderer/rendereropengles20.js :: anonymous :: line 985" data: no]It looked like it was blowing up on this line:
glCanvas3D.uniformMatrix4fv(varLocation, matrix);I checked out the WebGL IDL file and it had the function defined as such:
void uniformMatrix4fv (in GLint location, in GLboolean transpose, in nsICanvasArray value);It seemed some things have been changed since Canvas3D. I only needed to add another parameter, but decided to comment out the line and see what other functions changed.
I got this exception next:
Error: uncaught exception: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsICanvasRenderingContextWebGL. vertexAttribPointer]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: file:///Users/andor/Documents/Canvas3D/canvas3dapi/shaders/ model/standard/std_callback.js :: anonymous :: line 66" data: no]My code:
glCanvas3D.vertexAttribPointer(normalAttribLoc, 3, glCanvas3D.FLOAT, false, 0, currColl.getNormals());No data exists in currColl.getNormals()? I know it still works with 3.5, Commented. Next I got this one:
Error: uncaught exception: [Exception... "Could not convert JavaScript argument arg 1 [nsICanvasRenderingContextWebGL.bindTexture]" nsresult: "0x80570009 (NS_ERROR_XPC_BAD_CONVERT_JS)" location: "JS frame :: file:///Users/andor/Documents/Canvas3D/canvas3dapi/shaders/model/ standard/std_callback.js :: anonymous :: line 110" data: no]It was happening on this line:
glCanvas3D.bindTexture(glCanvas3D.TEXTURE_2D,-1);Here I bind to an invalid texture object in case an object isn’t textured. This prevents the last active texture from being used by this object. Commented.
Next one.
Error: uncaught exception: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsICanvasRenderingContextWebGL. vertexAttribPointer]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: file:///Users/andor/Documents/Canvas3D/canvas3dapi/renderer/ rendereropengles20.js :: anonymous :: line 950" data: no]The code:
this.setVertexAttribArray = function(shader, varName, size, array)
{
let attribLoc = glCanvas3D.getAttribLocation(shader, varName);
if(attribLoc != c3dl.const.SHADER_VAR_NOT_FOUND)
{
// This line is blowing up.
// glCanvas3D.vertexAttribPointer(attribLoc, size, glCanvas3D.FLOAT, false, 0, array);
glCanvas3D.enableVertexAttribArray(attribLoc);
}
else
{
c3dl.debug.logError('Attribute variable "' + varName + '" not found in shader with ID = ' + shader);
}
}
This is a wrapper I wrote to make the C3DL code a bit cleaner, since the third, fourth and fifth parameters always had to be the same. Commented.The next line wasn’t really a surprise. There are enough changes to the library now that this is expected.
No VBO bound to index 0 (or it's been deleted)! Error: uncaught exception: [Exception... "Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE) [nsICanvasRenderingContextWebGL. drawArrays]" nsresult: "0x80070057 (NS_ERROR_ILLEGAL_VALUE)" location: "JS frame :: file:///Users/andor/Documents/Canvas3D/canvas3dapi/ shaders/model/standard/std_callback.js :: anonymous :: line 120" data: no]My line:
glCanvas3D.drawArrays(renderer.getFillMode(), 0, currColl.getVertices(). length/3);Commented, next.
No VBO bound to index 0 (or it's been deleted)! Error: uncaught exception: [Exception... "Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE) [nsICanvasRenderingContextWebGL. drawArrays]" nsresult: "0x80070057 (NS_ERROR_ILLEGAL_VALUE)" location: "JS frame :: file:///Users/andor/Documents/Canvas3D/canvas3dapi/renderer/ rendereropengles20.js :: anonymous :: line 928" data: no]This is because I’m running a mocap demo and I’m rendering points.
glCanvas3D.drawArrays(c3dl.const.FILL,0, (c3dl.const.POINT_VERTICES.length)/3);Again, I commented the line and finally got an interesting one:
Error: glCanvas3D.swapBuffers is not a function Source File: file:///Users/andor/Documents/Canvas3D/canvas3dapi/renderer/ rendereropengles20.js Line: 222I first thought the name might have changed to SwapBuffers (uppercase ‘S’). I looked up the API and saw it was no longer present. This had to be a mistake. I need to tell OpenGL when the backbuffer should be swapped with the front buffer. I commented it and that’s when the errors ceased. I saw the FPS changing on my page, but the context was white. I should have been blue.
I was pretty excited since I was sure that now I had to be getting a graphics context, but the context should have been colored blue. Was it really working? Something was wrong. I sat thinking about it and thought no rendering was happening because I commented out the swapBuffers() call. It’s probably drawing to the backbuffer and not swapping to the front. I went back to Vlad’s spore creature viewer demo and poked around wondering how he was doing it. I saw there weren’t any call to swapBuffers. The only lines that gave a hint were:
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, numVertexPoints);He’s clearing the color and depth buffer in one call and drawing what he needs. I couldn’t argue with the code since it was working and mine wasn’t. I was a bit stuck. I turned to my IRC client and pinged joe, mark, dave. Bas and mstange also got involved, trying to help out too. Thanks guys! So I was told that the swapping is probably happening automatically. I decided this is something I can accept since Firefox is now handling the rendering. It uses double buffering to display the content, probably does the same with the canvas. Still, I couldn’t get the canvas to change color.
I thought I would then try to set the clear color and do a clear right after I got the graphics context. No other C3DL code would execute in between. I thought it might be worth a try…
glCanvas3D = cvs.getContext('moz-webgl');
glCanvas3D.clearColor(0,1,0,1);
glCanvas3D.clear(glCanvas3D.COLOR_BUFFER_BIT);
It worked!
I admit, my demo is not quite as impressive as Vlad’s demo, but hey, I have something!Next, I’ll have to address all those functions which have different signatures and find out what is preventing the framebuffer from being cleared.
