1 //////////////////////////////////////////////////////////////////////////////////
  2 //										//
  3 //////////////////////////////////////////////////////////////////////////////////
  4 
  5 /**
  6  * Handle world (aka scene+camera+renderer)
  7  *
  8  * @class tQuery.World
  9  * 
 10  * @param {THREE.Material} object an instance or an array of instance
 11 */
 12 tQuery.World	= function(opts)
 13 {
 14 	// handle parameters
 15 	opts	= opts	|| {};
 16 	opts	= tQuery.extend(opts, {
 17 		renderW		: window.innerWidth,
 18 		renderH		: window.innerHeight,
 19 		webGLNeeded	: true, 
 20 		autoRendering	: true,
 21 		scene		: null,
 22 		camera		: null,
 23 		renderer	: null
 24 	});
 25 	this._opts	= opts;
 26 
 27 	// update default world.
 28 	// - TODO no sanity check ?
 29 	// - not clear what to do with this...
 30 	// - tQuery.world is the user world. like the camera controls
 31 	console.assert( !tQuery.word );
 32 	tQuery.world	= this;
 33 
 34 	this._autoRendering	= true;
 35 	
 36 	// create a scene
 37 	this._scene	= opts.scene	||(new THREE.Scene());
 38 
 39  	// create a camera in the scene
 40 	if( !opts.camera ){
 41 		this._camera	= new THREE.PerspectiveCamera(35, opts.renderW / opts.renderH, 0.01, 10000 );
 42 		this._camera.position.set(0, 0, 3);
 43 		this._scene.add(this._camera);
 44 	}else{
 45 		this._camera	= opts.camera;
 46 	}
 47 	
 48 	// create the loop
 49 	this._loop	= new tQuery.Loop();
 50 
 51 	// hook the render function in this._loop
 52 	this._$loopCb	= this._loop.hookOnRender(function(delta, now){
 53 		this.render(delta);
 54 	}.bind(this));
 55 
 56 	// create a renderer
 57 	if( opts.renderer ){
 58 		this._renderer	= opts.renderer;
 59 	}else if( tQuery.World.hasWebGL() ){
 60 		this._renderer	= new THREE.WebGLRenderer({
 61 			antialias		: true,	// to get smoother output
 62 			preserveDrawingBuffer	: true	// to allow screenshot
 63 		});
 64 	}else if( !opts.webGLNeeded ){
 65 		this._renderer	= new THREE.CanvasRenderer();
 66 	}else{
 67 		this._addGetWebGLMessage();
 68 		throw new Error("WebGL required and not available")
 69 	}
 70 	this._renderer.setClearColorHex( 0xBBBBBB, 1 );
 71 	this._renderer.setSize( opts.renderW, opts.renderH );
 72 };
 73 
 74 // make it pluginable
 75 tQuery.pluginsInstanceOn(tQuery.World);
 76 
 77 // make it eventable
 78 tQuery.MicroeventMixin(tQuery.World.prototype)
 79 
 80 /**
 81  * destructor
 82  */
 83 tQuery.World.prototype.destroy	= function(){
 84 	// microevent.js notification
 85 	this.trigger('destroy');
 86 	// unhook the render function in this._loop
 87 	this._loop.unhookOnRender(this._$loopCb);
 88 	// destroy the loop
 89 	this._loop.destroy();
 90 	// remove this._cameraControls if needed
 91 	this.removeCameraControls();
 92 	// remove renderer element
 93 	var parent	= this._renderer.domElement.parentElement;
 94 	parent	&& parent.removeChild(this._renderer.domElement);
 95 	
 96 	// clear the global if needed
 97 	if( tQuery.world === this )	tQuery.world = null;
 98 }
 99 
100 //////////////////////////////////////////////////////////////////////////////////
101 //		WebGL Support							//
102 //////////////////////////////////////////////////////////////////////////////////
103 
104 tQuery.World._hasWebGL	= undefined;
105 /**
106  * @returns {Boolean} true if webgl is available, false otherwise
107 */
108 tQuery.World.hasWebGL	= function(){
109 	if( tQuery.World._hasWebGL !== undefined )	return tQuery.World._hasWebGL;
110 
111 	// test from Detector.js
112 	try{
113 		tQuery.World._hasWebGL	= !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' );
114 	} catch( e ){
115 		tQuery.World._hasWebGL	= false;
116 	}
117 	return tQuery.World._hasWebGL;
118 };
119 
120 /**
121  * display 'add webgl message' - taken from detector.js
122  * @param   {DOMElement?} parent dom element to which we hook it
123  * @private
124  */
125 tQuery.World.prototype._addGetWebGLMessage	= function(parent)
126 {
127 	parent	= parent || document.body;
128 	
129 	// message directly taken from Detector.js
130 	var domElement = document.createElement( 'div' );
131 	domElement.style.fontFamily	= 'monospace';
132 	domElement.style.fontSize	= '13px';
133 	domElement.style.textAlign	= 'center';
134 	domElement.style.background	= '#eee';
135 	domElement.style.color		= '#000';
136 	domElement.style.padding	= '1em';
137 	domElement.style.width		= '475px';
138 	domElement.style.margin		= '5em auto 0';
139 	domElement.innerHTML		= window.WebGLRenderingContext ? [
140 		'Your graphics card does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a>.<br />',
141 		'Find out how to get it <a href="http://get.webgl.org/">here</a>.'
142 	].join( '\n' ) : [
143 		'Your browser does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a>.<br/>',
144 		'Find out how to get it <a href="http://get.webgl.org/">here</a>.'
145 	].join( '\n' );
146 
147 	parent.appendChild(domElement);
148 }
149 
150 //////////////////////////////////////////////////////////////////////////////////
151 //		add/remove object3D						//
152 //////////////////////////////////////////////////////////////////////////////////
153 
154 // TODO why not a getter/setter here
155 tQuery.World.prototype.setCameraControls	= function(control){
156 	if( this.hasCameraControls() )	this.removeCameraControls();
157 	this._cameraControls	= control;
158 	return this;	// for chained API
159 };
160 
161 tQuery.World.prototype.getCameraControls	= function(){
162 	return this._cameraControls;
163 };
164 
165 /**
166  * remove the camera controls
167  * @return {tQuery.World} for chained API
168  */
169 tQuery.World.prototype.removeCameraControls	= function(){
170 	if( this.hasCameraControls() === false )	return this;
171 	this._cameraControls	= undefined;
172 	return this;	// for chained API
173 };
174 
175 /**
176  * test if there is a camera controls
177  * @return {Boolean} true if there is, false otherwise
178  */
179 tQuery.World.prototype.hasCameraControls	= function(){
180 	return this._cameraControls !== undefined ? true : false;
181 };
182 
183 //////////////////////////////////////////////////////////////////////////////////
184 //		add/remove object3D						//
185 //////////////////////////////////////////////////////////////////////////////////
186 
187 /**
188  * add an object to the scene
189  * 
190  * @param {tQuery.Object3D} object3D to add to the scene (THREE.Object3D is accepted)
191 */
192 tQuery.World.prototype.add	= function(object3d)
193 {
194 	if( object3d instanceof tQuery.Object3D ){
195 		object3d.each(function(object3d){
196 			this._scene.add(object3d)			
197 		}.bind(this));
198 	}else if( object3d instanceof THREE.Object3D ){
199 		this._scene.add(object3d)		
200 	}else	console.assert(false, "invalid type");
201 	// for chained API
202 	return this;
203 }
204 
205 /**
206  * remove an object to the scene
207  * 
208  * @param {tQuery.Object3D} object3D to add to the scene (THREE.Object3D is accepted)
209 */
210 tQuery.World.prototype.remove	= function(object3d)
211 {
212 	if( object3d instanceof tQuery.Object3D ){
213 		object3d.each(function(object3d){
214 			this._scene.remove(object3d)
215 		}.bind(this));
216 	}else if( object3d instanceof THREE.Object3D ){
217 		this._scene.remove(object3d)
218 	}else	console.assert(false, "invalid type");
219 	// for chained API
220 	return this;
221 }
222 
223 /**
224  * append renderer domElement
225  * @param  {DOMElement} domElement the domelement which will be parent
226  * @return {tQuery.World} for chained API
227  */
228 tQuery.World.prototype.appendTo	= function(domElement)
229 {
230 	domElement.appendChild(this._renderer.domElement)
231 	// for chained API
232 	return this;
233 }
234 
235 /**
236  * Start the loop
237 */
238 tQuery.World.prototype.start	= function(){
239 	this._loop.start();
240 	return this;	// for chained API
241 }
242 /**
243  * Stop the loop
244 */
245 tQuery.World.prototype.stop	= function(){
246 	this._loop.stop();
247 	return this;	// for chained API
248 }
249 
250 tQuery.World.prototype.loop	= function(){ return this._loop;	}
251 
252 tQuery.World.prototype.tRenderer= function(){ return this._renderer;	}
253 tQuery.World.prototype.tScene	= function(){ return this._scene;	}
254 tQuery.World.prototype.tCamera	= function(){ return this._camera;	}
255 
256 
257 // backward compatible functions to remove
258 tQuery.World.prototype.renderer	= function(){  console.trace();console.warn("world.renderer() is ovbslete, use .tRenderer() instead");
259 						return this._renderer;	}
260 tQuery.World.prototype.camera	= function(){ console.trace();console.warn("world.camera() is obsolete, use .tCamerar() instead");
261 						return this._camera;	}
262 tQuery.World.prototype.scene	= function(){ console.trace();console.warn("world.scene() is obsolete, use .tScene() instead");
263 						return this._scene;	}
264 tQuery.World.prototype.get	= function(){ return this._scene;	}
265 
266 //////////////////////////////////////////////////////////////////////////////////
267 //										//
268 //////////////////////////////////////////////////////////////////////////////////
269 
270 tQuery.World.prototype.autoRendering	= function(value){
271 	if(value === undefined)	return this._autoRendering;
272 	this._autoRendering	= value;
273 	return this;
274 }
275 
276 
277 tQuery.World.prototype.render	= function(delta)
278 {
279 	// update the cameraControl
280 	if( this.hasCameraControls() )	this._cameraControls.update(delta);
281 	// render the scene 
282 	if( this._autoRendering )	this._renderer.render( this._scene, this._camera );
283 }
284