Canvas 3d JS Library

WebGL made easy!
  • rss
  • What is C3DL?
  • Download
  • Tutorials
    • Tutorial #1: WebGL Browsers
    • Tutorial #2: A simple scene
    • Tutorial #3: Callback
    • Tutorial #4: Models
    • Tutorial #5: Light effects
    • Tutorial #6: Picking
  • Development News
  • Documentation
  • Community
  • Resources
  • Contact
  • About
I ran into a nasty bug that has left me scratching my head recently. It involved taking the work Patrick has done on picking, more specifically his bounding box code and integrating it with Collada objects. It seemed pretty straightforward when I first started working on it, but I have had little success. Hopefully it’s just the bugs that are the causing the problem. So I decided to rewrite my initial design. After some thought I believe I have figured out what went wrong. It’s a series of problems. It starts when a call is made to Collada::getBoundingBox(). This function is defined something like this: Collada::getBoundingBox() { // The Collada object has a scene graph which always has one root. // We pass in an identity matrix since the Root is a TransformNode // which wants a matrix. We could also pass in null and do a check. return sceneGraph.getRoot().getBoudingBox(identityMatrix); } A root node is actually just an instance of a TransformNode which main purpose is to hold a transformation matrix. Calling the TransformNode’s getBoundingBox will walk down the graph until we reach the base case and call the GeometryNode’s getBoundingBox(). TransformNode::getBoundingBox(matrix) { // don’t set these to zero as the smallest vertex may actually be a // positive number, instead use undefined to flag there are no values // associated with these variables. This was one of my bugs. var minVertex, maxVertex, boudingBox; for( all child nodes) { // get the bounding box of all the children. var boundingBox = child[iter].getBoundingBox(matrix * thisTransform); // if we don’t have any values for the min and max, then use the values // returned form the first child. if(minVertex is not defined) { minVertex = new Array(boundingBox[0], boundingBox[1], boundingBox[2]); maxVertex = new Array(boundingBox[3], boundingBox[4], boundingBox[5]); } else { for( i =0; i < 3 i++ ) { if( boundingBox[i] < min) min[i] = boundingBox; } for( i =3; i < 6 i++ ) { if( boundingBox[i] > max) max[i] = boundingBox; } } } return boundingBox; } The purpose of the geometry node is to store geometry, in our case it will store a Model instance, which has all the vertices. GeometryNode::getBoudingBox(matrix) { var minVertex, maxVertex; // Here was another bug. Previously I had been just getting the bounding box of the // model (in model space), returning it, then transforming it and then finally comparing. // The vertices need to be transformed first then compared. for( all verts in model) { var v = vert * matrix; if(minVertex is undefined) { minVertex = maxVertex = v; } else { for(var i=0; i < 3; i++) { if( v[i] < minVertex[i] ) minVertex[i] = v[i]; } for(var i=0; i < 3; i++) { if( v[i] > maxVertex[i] ) maxVertex[i] = v[i]; } } } return [minVertex[0], minVertex[1], minVertex[2], maxVertex[0], maxVertex[1], maxVertex[2]]; } Going back to the scenegraph having only one root, this created some problems. When I wrote the parser, I noticed the standard allowed many nodes to be at the top level. However, I needed to have only a single root node, which would make transforming entire Collada objects simple. If a Collada object was transformed, it would just relay that transformation to the scenegraph’s root.

Therefore if the collada file has 2 nodes, one transform and one geometry, my parser adds a root node. The Collada object in memory will therefore have 3 nodes: the root with an identity, then the transform node and finally the geometry. This caused me some frustration when I tried getting the transform of the Collada object and it was returning an identity, but the object obviously wasn’t at the origin.

Halfway through my debugging I decided to add a visual box, numbers weren’t cutting it anymore. It helped me quite a bit.

Alas, I have approached the summit. With my hands, I have created a Mac application, which runs, and has the customized chrome interface. This alone is an incredible feat for me, since I rarely create GUI-based programs, and I certainly haven’t developed them on a Mac before. The only problem is that the canvas element isn’t rendering. I’m so close, goshdarnit… In the real world there are many phenomena which exist as set of particles, such as falling snow, sparks from arc welding, rain and fireworks. Systems which manage (create, update and destroy) a set of these particles are known as particle systems. Particle systems typically keep track of these particles with a collection such as an array, vector or list. If you examine falling snow, you will notice the particles all behave similarly, falling downwards to the ground, but do so slightly differently. They will have different positions and vary slightly in x and z speeds. This slight variation applies to all particle systems and gives them a sense of realism. However, particle systems aren’t limited to the previous examples, but can also model things like fog, smoke, fire, water, waterfalls, a flock of birds or almost anything given the proper logic for moving the particles.

When I started designing the basics of implementing a particle system in the library, I decided to break down the implementation into 3 main parts: the particle, subsystem and the actual particle system.

At its most basic implementation a particle will have properties such as position, velocity, age and lifetime. The particle is placed somewhere in the world and moves about, after its age exceeds its lifetime, the particle dies and is no longer updated and drawn. When a new particle is required, it can reuse the old one by resetting its attributes. Additional attributes I decided to add are color and size.

A SubSystem is a particle system in itself, albeit downgraded just to create a class which a particle system can hold. For example, in a fire particle system, there are many different types of particle systems: the embers which do not move, but glow, flames which move quickly, smoke which has a long lifetime and sparks which follow a trajectory-like path. Each of these can be a subsystem added into a fire particle system. Their positions will be relative to the particle system and if the particle system moves, so do the subsystems. A subsystem will have properties every particle will have such as image/texture, blend modes, acceleration and animation state(playing, paused, stopped).

When a particle is born, it will receive random values for all of its properties based on a range which the user provides. If the user is specifying smoke particles, they’ll likely want the smoke to range from gray to black.

Finally the Particle System will contain a list of subsystems. The user will be able to pause the animation of individual subsystems and move the subsystems around relative to the particle system.

Technicalities

In the past, when implementing a particle system, one would create a textured, billboarded quadrilateral for each particle. This would be a primitive with four vertices which always faces the camera. That way the texture is not skewed when the camera isn’t looking at it. Billboarding can be a waste of processing and using four vertices is a waste of memory, since what users really want is to slap a texture onto a point. Rendering API architects then provided support for point sprites. As for OpenGL, they started supporting it with version 1.5. Point sprites are view-space aligned textured points. That is, single vertices with a textured placed at the center of the point. This makes implementing particles system much more efficient and easy. Users no longer have to manually orient the particles and they have to specify only one vertex instead of four. The trade off of point sprites is they cannot be rotated; the sides of the texture will always be parallel to the viewport. There are some cases in which rotating a particle is desired, for example when modeling falling leaves or a spiral texture that spins.

No point sprites

OpenGLES does not support point sprites, which is a shame. It doesn’t support quads either; however it is possible to specify vertices as a triangle fan, effectively keeping particles limited to four points. Since we are stuck using a quad, there might as well be an option to assign angular velocity to the particles as well as a choice of billboarding them.

Future Work

Particle systems can create impressive effects, they can be even more interesting events are added. Events can change the attributes of a particle which would not usually change, such as color or size. We can create time-based events which change the size of the particle as it ages, making its disappearance less abrupt. Events could also be position-based, which are triggered when a particle gets to a certain height at which point its color changes. Once the basics are in place of the subsystems and particle system, I will create the classes necessary to make events.

Usage

Right now I’m treating the subsystem as the particle system itself as I build it up. This is how I’m setting up the subsystem. var psys = new SubSystem(); psys.setMinVelocity([-1,-1, -1]); psys.setMaxVelocity([1,1, 1]); psys.setMinLifetime(1); psys.setMaxLifetime(15); psys.setMinColor([0,0,.4,1]); psys.setMaxColor([0,0,1,1]); psys.setTexture("particle.jpg"); psys.setAcceleration([0,1,0]); psys.init(50); scn.addObjectToScene(psys);

Example

You can check out a primitive example here if you have the Canvas3D extension. Recently we have added 3 more people to our team so I thought it fitting to do a brief introduction. Shawn Dinis, Michael Segal, and Chay Edry will now be joining our team to help us make some cool demos. I wanted to start by welcoming them to our team. The three of them will be working on making use of our library for making cool demos. Some of the stuff coming up will be very interesting. One of the first things that we need to change desparately is our front page. That red spinning teapot is just not cutting it anymore. Our library is capable of doing so much more now. I would love to see something cooler looking there. My initial thought was to do a small carnival type game on the front. Something that moved and showed off the library. Chay’s words were “that wouldn’t be very interesting to look at. i have a much better idea!”. I’ll hold off on describing what Chay is planning for now but I if it even comes close to doing what he said it would, it would definitely be much better than my little carnival booth. I’ve been toying around with refactoring the canvs3d library using namespace is “proper” object oriented inheritance. Proper meaning within the construct of the JavaScript language.The refactoring is far from complete however, a major milestone (in my eyes) has been accomplished today. Rendering a triangle on the screen and using the camera to rotate around it, using the OpenGLES1.1 rendering context. I’ve been working on bits and pieces and leaving empty stubs for the OpenGLES20 context code. The intention to get a working proof of concept for the code layout as soon as possible. The code has been designed with the intention of supporting both contexts though, so it should only be a matter of going back and filling in 2.0 context specific code where its needed.

One of the many objectives with the refactorization is to clear up the global scope of all the library variables, functions and objects–and that has certainly been accomplished. Everything resides within the global variable “c3dl”. For example, constants are defined in the c3dl property “const”. For example the “TOLERANCE” constant that represents the smallest size of a number before it’s considered to be zero can be accessed as “c3dl.const.TOLERANCE”.

Another new feature is the use of default parameters for certain function calls. Currently, because only such a small part of the code base has been converted, only one or two functions make use of the feature–but as the code gets converted more functions certainly will. The call to the scene constructor now makes use of a second parameter called options. The options is an object literal where you can specify attributes to overide the scene defaults. Take a look at this code from the api source, it defines the defaults that are currently used for the scene constructor.

// default parameters passed to scene constructor
c3dl.scene.defaults = {
// background color of scene
clearColor: new Array(0.4, 0.4, 0.5, 1.0),
// default to newest and greatest gles
renderer: c3dl.renderer.OpenGLES11,
// default camera
camera: c3dl.camera
};


What this means is that the minimal code required for creating a scene and getting it going has become much simpler. The following code is taken from the refactorization folder in demos, the hello world demo demonstrates utilizing c3dl api to render a triangle on the screen. This code is all that is needed to render a triangle on the screen.

var scn = new c3dl.scene('viewport');
// add an object
var tri = new c3dl.primitive.triangle();
scn.add(tri);
scn.start();


As you can see, all you need to do is pass the canvas tag Id to the scene constructor, create a triangle primitive and add it to the scene and you are off and running.This new simplicity does not hinder your ability be more specific though! What if you wanted to use the OpenGLES2.0 context, and use a chase camera instead of a free camera? Easy, simply override the defaults. The following code accomplishes just that.

var scn = new c3dl.scene('viewport', {camera: c3dl.camera.chase, renderer: c3dl.renderer.OpenGles20});
// add an object
var tri = new c3dl.primitive.triangle();
scn.add(tri);
scn.start();


As you can see this will finally allow us to deliver extremely simple API use with optional fine grain control.

So anyway, the new code is available in SVN under c3dl_namespace of the root folder. I invite you to take a look.

Cheers.

PS: The demo scene loop uses a little speed hack to acheive frames per second upwards of 400. Heres a screen shot of the demo running at ~800 fps.
800+ frames per second!

800+ frames per second!

Videos

Demos

  • Asteroids-3D
  • Particle Systems Demo
  • Cross-Browser Orbiter
  • Mocap Demo With Spheres
  • Google Maps-3D

C3DL Development News

C3DL 2.0-WebGL and beyond

It has been a long time coming but we have now updated all the core features of C3DL to use WebGL. You can dowload our 2.0 release here. We have also updated all our demos to use WebGL. Our tutorials have all been updated (tutorial 5 and 6 needs a better example [...]

Preliminary WebGL RTS Game

Cathy asked me to make a cool demo using our library. After thinking about, I started getting many ideas, but creating a preliminary real-time strategy game made the most sense. It not only demonstrates a lot of C3DL features such as model loading, transformations, lighting, shaders, picking, cameras, textures, etc, but since animation is kept [...]

Tutorials

  • Tutorial #1: WebGL Browsers
  • Tutorial #2: A simple scene
  • Tutorial #3: Callback
  • Tutorial #4: Models
  • Tutorial #5: Light effects
  • Tutorial #6: Picking

Documentation

Archives

Archives

C3DL Development News

Recent Comments

  • February 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • September 2008
  • August 2008
  • July 2008
  • June 2008
  • May 2008
  • April 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • C3DL 2.0-WebGL and beyond
  • Preliminary WebGL RTS Game
  • Asteroids in 3D… and a bit of 2D
  • Another demo updated
  • Simplifying the Interface
  • Updating Demos
  • Cross-browser progress update
  • let there be vars
  • Creating tester pages
  • Problems with porting
  • keep it coming ve... - gero3
  • congrats on a great... - Paul Brunt
  • c++ not c# actually... - Cathy Leung
  • It's unbelievable ho... - Paul
  • Wow, now that's a co... - Andor Salga
  • Hi, is the project... - Sascha Hendel
  • I agree with both co... - Cathy Leung
  • Your library is real... - Sascha Hendel
  • Hi, as a more gener... - Sascha Hendel
  • Thanks. I just pull... - peter



Canvas 3d JS Library

©2007- 2009 Canvas 3d JS Library

Disclaimer: This website is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.5 Canada License.
The Canvas 3d JS Library and Demos found on this website are licenced under the MIT License

Creative Commons License