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 wanted to try and see if our library will work any better (or rather, how much better) wth tracemonkey instead of the current javascript engine. I got a copy of the relevant branch and built it, but that wasn’t happy with the extension, because the extension has a max version in it that’s too low.

So I installed the nightly tester tools, and overrode the version check.

Sadly the result is that irefox crashes when I go to a page with a canvas3d on it. I rebuilt firefox with debug enabled, and this is what it told me:

++DOMWINDOW == 11 (0x145be80c) [serial = 14] [outer = 0x145acfd0]
WARNING: NS_ENSURE_SUCCESS(rv, rv) failed with result 0x80520012: file /Users/andrew/tracemonkey/content/base/src/nsScriptLoader.cpp, line 446
Reading symbols for shared libraries . done
nsGLPbuffer: gActiveBuffers: 1
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
Reading symbols for shared libraries . done
AGL_SAMPLE_BUFFERS_ARB 0
AGL_SAMPLES_ARB 0
AGL_MULTISAMPLE 0
AGL_SUPERSAMPLE 0
dyld: lazy symbol binding failed: Symbol not found: __ZN15gfxImageSurfaceC1ERK10gfxIntSizeN11gfxASurface14gfxImageFormatE
  Referenced from: /Users/andrew/Library/Application Support/Firefox/Profiles/ie0j09jy.newer/extensions/canvas3d@mozilla.com/platform/Darwin/components/libcanvas3d.dylib
  Expected in: /Users/andrew/tracemonkey/ff-opt/toolkit/library/XUL

dyld: Symbol not found: __ZN15gfxImageSurfaceC1ERK10gfxIntSizeN11gfxASurface14gfxImageFormatE
  Referenced from: /Users/andrew/Library/Application Support/Firefox/Profiles/ie0j09jy.newer/extensions/canvas3d@mozilla.com/platform/Darwin/components/libcanvas3d.dylib
  Expected in: /Users/andrew/tracemonkey/ff-opt/toolkit/library/XUL

Program received signal SIGTRAP, Trace/breakpoint trap.
0x8fe01045 in __dyld_dyld_fatal_error ()

(gdb) bt
#0  0x8fe01045 in __dyld_dyld_fatal_error ()
#1  0x8fe06fa7 in __dyld__ZN4dyld14bindLazySymbolEPK11mach_headerPm ()
#2  0x8fe18b6f in __dyld_stub_binding_helper_interface2 ()
#3  0x0dbfa505 in NSGetModule ()
#4  0x0dc05aa4 in NSGetModule ()
#5  0x0dc05ea5 in NSGetModule ()
#6  0x0dbfb8f3 in NSGetModule ()
#7  0x0a97e3af in nsHTMLCanvasElement::GetContext (this=0xe907d00, aContextId=@0xbfffc898, aContext=0xbfffc8a8) at /Users/andrew/tracemonkey/content/html/content/src/nsHTMLCanvasElement.cpp:475
#8  0x0916eb4c in nsIDOMHTMLCanvasElement_GetContext (cx=0x145ad180, argc=1, vp=0xcda3d8) at dom_quickstubs.cpp:10350
#9  0x001e5ab8 in js_Interpret (cx=0x145ad180) at /Users/andrew/tracemonkey/js/src/jsinterp.cpp:4960
#10 0x001fe6b8 in js_Invoke (cx=0x145ad180, argc=1, vp=0xcda230, flags=0) at jsinterp.cpp:1324
#11 0x001fe8d9 in js_InternalInvoke (cx=0x145ad180, obj=0x144f5720, fval=230193408, flags=0, argc=1, argv=0xcda22c, rval=0xbfffcec0) at jsinterp.cpp:1381
#12 0x00198b76 in JS_CallFunctionValue (cx=0x145ad180, obj=0x8fe2fcc0, fval=-1880949568, argc=2414017728, argv=0x8fe2fcc0, rval=0x8fe2fcc0) at /Users/andrew/tracemonkey/js/src/jsapi.cpp:5071
#13 0x0ab3f8e9 in nsJSContext::CallEventHandler (this=0x145ac6a0, aTarget=0x145be80c, aScope=0x144f5720, aHandler=0xdb87900, aargv=0xe906af0, arv=0xbfffd064) at /Users/andrew/tracemonkey/dom/src/base/nsJSEnvironment.cpp:1998
#14 0x0abb8254 in nsJSEventListener::HandleEvent (this=0x10c45870, aEvent=0xe906a60) at /Users/andrew/tracemonkey/dom/src/events/nsJSEventListener.cpp:248
#15 0x0a90e65b in nsEventListenerManager::HandleEventSubType (this=0x151e6180, aListenerStruct=0x151e61a4, aListener=0x10c45870, aDOMEvent=0xe906a60, aCurrentTarget=0x145ad00c, aPhaseFlags=6) at /Users/andrew/tracemonkey/content/events/src/nsEventListenerManager.cpp:1080
#16 0x0a90f08e in nsEventListenerManager::HandleEvent (this=0x151e6180, aPresContext=0x14698890, aEvent=0xbfffd40c, aDOMEvent=0xbfffd388, aCurrentTarget=0x145ad00c, aFlags=6, aEventStatus=0xbfffd38c) at /Users/andrew/tracemonkey/content/events/src/nsEventListenerManager.cpp:1185
#17 0x0a94bd5d in nsEventTargetChainItem::HandleEvent (this=0xe07c20, aVisitor=@0xbfffd380, aFlags=6) at /Users/andrew/tracemonkey/content/events/src/nsEventDispatcher.cpp:211
#18 0x0a94c133 in nsEventTargetChainItem::HandleEventTargetChain (this=0xe07d40, aVisitor=@0xbfffd380, aFlags=6, aCallback=0x0) at /Users/andrew/tracemonkey/content/events/src/nsEventDispatcher.cpp:269
#19 0x0a94ca18 in nsEventDispatcher::Dispatch (aTarget=0x145acfd0, aPresContext=0x14698890, aEvent=0xbfffd40c, aDOMEvent=0x0, aEventStatus=0xbfffd438, aCallback=0x0) at /Users/andrew/tracemonkey/content/events/src/nsEventDispatcher.cpp:479
#20 0x0a5521bd in DocumentViewerImpl::LoadComplete (this=0xe83f160, aStatus=0) at /Users/andrew/tracemonkey/layout/base/nsDocumentViewer.cpp:985
#21 0x115ab0f1 in nsDocShell::EndPageLoad (this=0x145ac820, aProgress=0x145ac834, aChannel=0xe726600, aStatus=0) at /Users/andrew/tracemonkey/docshell/base/nsDocShell.cpp:5082
#22 0x115b7870 in nsWebShell::EndPageLoad (this=0x145ac820, aProgress=0x145ac834, channel=0xe726600, aStatus=0) at /Users/andrew/tracemonkey/docshell/base/nsWebShell.cpp:1013
#23 0x115ab953 in nsDocShell::OnStateChange (this=0x145ac820, aProgress=0x145ac834, aRequest=0xe726600, aStateFlags=131088, aStatus=0) at /Users/andrew/tracemonkey/docshell/base/nsDocShell.cpp:4987
#24 0x115cdf21 in nsDocLoader::FireOnStateChange (this=0x145ac820, aProgress=0x145ac834, aRequest=0xe726600, aStateFlags=131088, aStatus=0) at /Users/andrew/tracemonkey/uriloader/base/nsDocLoader.cpp:1235
#25 0x115ce636 in nsDocLoader::doStopDocumentLoad (this=0x145ac820, request=0xe726600, aStatus=0) at /Users/andrew/tracemonkey/uriloader/base/nsDocLoader.cpp:858
#26 0x115ce966 in nsDocLoader::DocLoaderIsEmpty (this=0xe726600) at /Users/andrew/tracemonkey/uriloader/base/nsDocLoader.cpp:763
#27 0x115cf0d4 in nsDocLoader::OnStopRequest (this=0x145ac820, aRequest=0x145d94c0, aCtxt=0x0, aStatus=0) at /Users/andrew/tracemonkey/uriloader/base/nsDocLoader.cpp:679
#28 0x0927f24f in nsLoadGroup::RemoveRequest (this=0x145acbd0, request=0x145d94c0, ctxt=0x0, aStatus=0) at /Users/andrew/tracemonkey/netwerk/base/src/nsLoadGroup.cpp:688
#29 0x0a840739 in nsDocument::DoUnblockOnload (this=0xa66600) at /Users/andrew/tracemonkey/content/base/src/nsDocument.cpp:6520
#30 0x0a848431 in nsDocument::DispatchContentLoadedEvents (this=0xa66600) at /Users/andrew/tracemonkey/content/base/src/nsDocument.cpp:3451
#31 0x0a861c9b in nsRunnableMethod::Run (this=0xe7fbf30) at nsThreadUtils.h:264
#32 0x003ef792 in nsThread::ProcessNextEvent (this=0x613d60, mayWait=0, result=0xbfffdcbc) at /Users/andrew/tracemonkey/xpcom/threads/nsThread.cpp:510
#33 0x0037c5f7 in NS_ProcessPendingEvents_P (thread=0x613d60, timeout=20) at nsThreadUtils.cpp:180
#34 0x094ea7f2 in nsBaseAppShell::NativeEventCallback (this=0x6355c0) at /Users/andrew/tracemonkey/widget/src/xpwidgets/nsBaseAppShell.cpp:121
#35 0x094ab3ea in nsAppShell::ProcessGeckoEvents (aInfo=0x6355c0) at /Users/andrew/tracemonkey/widget/src/cocoa/nsAppShell.mm:302
#36 0x90625615 in CFRunLoopRunSpecific ()
#37 0x90625cf8 in CFRunLoopRunInMode ()
#38 0x96bc2da4 in RunCurrentEventLoopInMode ()
#39 0x96bc2af6 in ReceiveNextEventCommon ()
#40 0x96bc2a31 in BlockUntilNextEventMatchingListInMode ()
#41 0x95ab8505 in _DPSNextEvent ()
#42 0x95ab7db8 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()
#43 0x95ab0df3 in -[NSApplication run] ()
#44 0x094aabaa in nsAppShell::Run (this=0x6355c0) at /Users/andrew/tracemonkey/widget/src/cocoa/nsAppShell.mm:591
#45 0x0a01ba0e in nsAppStartup::Run (this=0x64f310) at /Users/andrew/tracemonkey/toolkit/components/startup/src/nsAppStartup.cpp:182
#46 0x000a5104 in XRE_main (argc=1, argv=0xbffff23c, aAppData=0x60e5e0) at /Users/andrew/tracemonkey/toolkit/xre/nsAppRunner.cpp:3220
#47 0x000029ec in main (argc=1, argv=0x60baf0) at /Users/andrew/tracemonkey/browser/app/nsBrowserApp.cpp:156
Long story short, the extension uses stuff in Firefox 3 that no longer exists in current versions. I guess there’s also a possibility that the tracemonkey branch is very much out of date, but I’m having trouble believing that. The idea behind adding addFlotingText() to the API was to give the user a way to display text inside the 3D Canvas. Intuitively, but certainly not technically trivial – the text should always face the user no matter where the camera is. I took this challenge on without much hope, it’s been a very long time since I last did any 3D math. But somehow I managed to get it to work. I guess at least some of what Evan and Chris S. taught me stuck. Have a look at the new index.html, it has a moving camera, a teapot, and a floating text. The teapot does not float, so it appears to move away, while the text stays where it was in the beginning. Sadly this wasn’t the only hole in the floating text. If the camera is rotated (as opposed to moved), my childish math falls to pieces, and the text sinks. I think I’ll leave that for Andor to fix, he probably knows exactly how to do it. The code is in the first ‘if’ in Model::update() Also the text is upside down. An easy way to fix that would be to rotate the plane model it’s on, but perhaps it would be better to fix the root of the problem, which is (I suspect), the plane model itself, defined in Scene::addTextToModel() I’m gonna reply to Jeremy’s post a piece at a time, becuse I can’t keep it all in mind at once. First thing first, something he mentioned we should be able to do, and I thought we could do, but I couldn’t remember how: using a 2D canvas for a texture, but letting the user draw whatever on the said canvas. The code from the Example usage section from Jeremy’s post can also be written the following way, and this already works with the library now (tested with a teapot). n is the model:
var c2d = scene.create2Dcanvas(128, 128);
var ctx = c2d.getContext('2d');
   	ctx.beginPath();
   	ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
   	ctx.moveTo(110,75);
	ctx.arc(75,75,35,0,Math.PI,false);   // Mouth (clockwise)
	ctx.moveTo(65,65);
	ctx.arc(60,65,5,0,Math.PI*2,true);  // Left eye
	ctx.moveTo(95,65);
	ctx.arc(90,65,5,0,Math.PI*2,true);  // Right eye
	ctx.stroke();
n.setTextureFromCanvas2D(c2d.id);
scene.getTextureManager().addTextureFromCanvas2D(c2d.id);
The other interesting suggestion Jeremy had was to make the texture an object, to deal with the multiple parameters. I’m gonna have a look at that now. I probably should have written this months ago, would have saved Cathy from a lot of trouble. Usage example: ./release.sh 0.5 Will create canvas3dapi-0.5.zip with all the files in it, compressed as well as JavaScript will allow, except for some newlines, which don’t take a lot of space but make even the compressed version sort-of readable. But just in case the script will also create an uncompressed zip file. We can distribute both so interested folks can hack on the source without figuring out how to use subversion. For the compression I used a neat program called jsmin. I rebuild it every time the script is run, so it works on everyone’s machine. It may even work in windows from MinGW (or Cygwin, does anyone sane still use that?). Here’s the licence for jsmin:
Copyright (c) 2002 Douglas Crockford (www.crockford.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The Software shall be used for Good, not Evil.
That’s cute. Fortunately Cathy will be using it (not me), so I think we’re ok from a legal point of view. This proposal addresses some of the issues brought up in Andrew’s post about providing a text API for the C3DL framework with out over complicating parametrization or usage. The nature of how text is accomplished is inherently related to a 2D drawing API, so the proposal tackles both issues. The suggested method for providing an easy to use, fast interface for 2D drawing and text is to provide an extended canvas element with a built in “to GL Texture” method. A method that which when called, converts the content currently on the 2D context into a GL Texture that can be applied to any model or primitive. From there, we can add any additional helpers to speed up and improve efficiency of drawing or writing text–as demonstrated by the TextHelper function which provides a wrapper to the above stated canvas2DPresenter designed to simply drawing text. Use cases for the proposed methods are show below the code for the function. I apologize for the inconsistency in thoroughness, as some parts of the code have been left as ambiguous pseudo code — ie present() which actually creates the GL Texture. This was left in its condition as the code is available in other areas of the source code. Changes to the Scene object:
/**
* Helper function that takes a any texture (but especially a texture from canvas2DPresenter.present()) and applies it to a 3D plane.
*
* @return PlanePrimitive
*/
this.addTexturePlane(glTexture) {
// create a 3d plane
// .. var plane = new PlanePrimitive();
// apply glTexture to plane
// .. plane.addTexture(glTexture);
// add plane to scene
this.addObjectToScene(plane);
// return reference to plane so user can adjust position/orientation if desired
return plane;
}

/**
* Helper Function that takes any texture (but especially a texture from canvas2dPresneter.present()) and applies it to a given model.
* However, ideally, this method should be obsolete as any model should incorporate a method that applies a given texture to itself.
*/
this.addTextureToModel = function(glTexture, model)
{
model.applyTexture(glTexture);
}

/**
Create a 2D canvas for drawing text and other stuff. Keep a reference to it.
*/
this.create2Dcanvas = function(width, height)
{
var newCanvas = document.createElement('canvas');
newCanvas.id = 'changemetorandomstring';
newCanvas.width = width;
newCanvas.height = height;
cvs.appendChild(newCanvas);

canvas2Dlist.push(newCanvas);

return newCanvas;
}

/**
* returns a new canvas element with an additional function "present" which generates a glTexture to be applied to models/primitives.
*
* @return CanvasElement
*/
this.create2DPresenter = function () {
var newcanvas = document.createElement('canvas');

/**
* Takes current 2d canvas context and turns it into a glTexture
*
* @return glTexture
*/
newcanvas.present = function () {
// psuedo code
var texture = createGlTexture(this);
return texture;
}
// return extended canvas element
return newcanvas;
}

/**
* TextHelper is a wrapper to the extended canvas we get from create2DPresenter that has been specialized for text writing.
*
* @return TextHelper
*/
	this.getTextHelper = function () {
		// return an object TextHelper
		return function () {
			// set some defaults so it works with out any setup calls
			this.fontStyle = "12pt Sans-Serif";
			this.width = 0; // default to auto
			this.height = 0; // default to auto
			this.fillStyle = '#000000'; // supports any valid canvas fillStyle (grad, rgb, rgba, #hex)
			this.globalAlpha = 1.0; // float: 1 = opaque, 0 = transparent
			this.backgroundFillStyle = null; // default to no background

			this.drawtext = function (text) {
				/**
				 * Get ourselves a cavas2dPresenter that lets us have access to canvas's built in 2d api (including text) with a built in toGlCanvas method.
			     */
			 	this.canvas = Scene.create2DPresenter();
			 	this.context = this.canvas.getContext("2d");

			 	var w, h, lw, lh;
			 	var fsize;

				lw = this.width;
				lh = this.height;
				// if width or height was set manually, we dont want to reset
				// to autosize after drawing text, so we save current values and
				// refer to them to check if we reset to 0 (autosize) or leave it as is.

			 	// calculate width/height
		 		var tempSpan = document.createElement('span');
		 		var tempText = document.createTextNode(text);
		 		tempSpan.appendChild(tempText);
		 		tempSpan.style.font = this.fontStyle;
		 		// get the font-size used
		 		fsize = parseInt(tempSpan.style.fontSize);
		 		tempSpan.style.visibility = 'hidden'; // dont show element on page
		 		tempSpan.style.padding = '0';
		 		document.body.appendChild(tempSpan);
		 		// chose set size, or html size, or 1
		 		w = this.width || tempSpan.offsetWidth || 1; // no zeros allowed!
		 		h = this.height || tempSpan.offsetHeight || 1; // no zeros allowed!
		 		// remove junk html tag
		 		document.body.removeChild(tempSpan);

				if(!lw) this.width = 0; // reset for autosize
				if(!lh) this.height = 0; // reset for autosize

				// round w/h to power of 2
				// ..

				// if background color was specified
				this.canvas.width = w;
				this.canvas.height = h;
				if(this.backgroundFillStyle) {
					this.context.fillStyle = this.backgroundFillStyle;
			 		this.context.fillRect(0, 0, w, h);
				}

				// apply helper settings to context
				this.context.fillStyle = this.fillStyle;
				this.context.globalAlpha = this.globalAlpha;
				this.context.width = w;
				this.context.height = h;

				// move to draw origin
				// height hack to center verticaly & account for span padding
				// works best on pt values for font size
				this.context.translate(0, h - ((h-fsize)/2));
			    // render text using font style
			    this.context.mozTextStyle = this.fontStyle;
			    this.context.mozDrawText(text);
			    this.canvas.focus();
			 }

			 /**
			  * Accessor to our canvas2DPresenter present method
			  * @return
			  */
			 this.present = function () {
			 	return this.canvas.present();
			 }
		}();
	}
The suggested use then, to either draw in 2D or draw text, is to ask the API for a canvas2DPresenter, work with it as you would any canvas element for graphic drawing (see Mozilla Developer Center on Drawing with the Canvas) and then call the extended present() to generate a GL Texture for you. Once you have the texture, you can do anything you want with it, from applying it to a model, or a primitive shape. Example usage:
   var c2dp = Scene.create2DPresenter();
   var ctx = c2dp.getContext('2D');

   c2dp.width = 150;
   c2dp.height = 150;
   ctx.beginPath();
   ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
   ctx.moveTo(110,75);
   ctx.arc(75,75,35,0,Math.PI,false);   // Mouth (clockwise)
   ctx.moveTo(65,65);
   ctx.arc(60,65,5,0,Math.PI*2,true);  // Left eye
   ctx.moveTo(95,65);
   ctx.arc(90,65,5,0,Math.PI*2,true);  // Right eye
   ctx.stroke();

   var texture = c2dp.present();

   var model = new PrimitivePlane();
   model.applyTexture(texture);
   Scene.addModel(model);
Example using a specialized helper
   var th = Scene.textHelper();
   th.drawText("Hello World");
   var texture = th.present();

   var model = new PrimitivePlane();
   model.applyTexture(texture);
   Scene.addModel(model);

// more advanced usage of helper
   var th3 = new textHelper();
   var lingrad = th3.context.createLinearGradient(0,0,0,150);
   lingrad.addColorStop(0, '#00ABEB');
   lingrad.addColorStop(0.5, '#fff');

   var img = document.getElementById('pattern');
   var ptrn = th3.context.createPattern(img, 'repeat');
   th3.fillStyle = ptrn;
   th3.fontStyle = "40pt Arial Black";
   th3.drawtext("Hello World!");

   var texture = th3.present();
   var model = new PrimitivePlane();
   model.applyTexture(texture);
   Scene.addModel(model);
My use cases employ a fictional PrimitivePlane class that ideally creates a 3D plane facing the viewer and applies its texture stretched onto the plane. In any case I hope this post clarified my ideas for the canvas2D drawing and text support implementation.

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