1 //////////////////////////////////////////////////////////////////////////////////
  2 //										//
  3 //////////////////////////////////////////////////////////////////////////////////
  4 
  5 /**
  6  * Handle the rendering loop
  7  *
  8  * @class This class handle the rendering loop
  9  *
 10  * @param {THREE.World} world the world to display (optional)
 11 */
 12 tQuery.Loop	= function()
 13 {	
 14 	// internally if world present do that
 15 	this._hooks	= [];
 16 	this._lastTime	= null;
 17 };
 18 
 19 // make it pluginable
 20 tQuery.pluginsInstanceOn(tQuery.Loop);
 21 
 22 /**
 23  * destructor
 24 */
 25 tQuery.Loop.prototype.destroy	= function()
 26 {
 27 	this.stop();
 28 }
 29 
 30 //////////////////////////////////////////////////////////////////////////////////
 31 //										//
 32 //////////////////////////////////////////////////////////////////////////////////
 33 
 34 /**
 35  * start looping
 36  * 
 37  * @returns {tQuery.Loop} chained API
 38 */
 39 tQuery.Loop.prototype.start	= function()
 40 {
 41 	if( this._timerId )	this.stop();
 42 	this._timerId	= requestAnimationFrame( this._onAnimationFrame.bind(this) );
 43 	// for chained API
 44 	return this;
 45 }
 46 
 47 /**
 48  * stop looping
 49  * 
 50  * @returns {tQuery.Loop} chained API
 51 */
 52 tQuery.Loop.prototype.stop	= function()
 53 {
 54 	cancelAnimationFrame(this._timerId);
 55 	this._timerId	= null;
 56 	// for chained API
 57 	return this;
 58 }
 59 
 60 tQuery.Loop.prototype._onAnimationFrame	= function()
 61 {
 62 	// loop on request animation loop
 63 	// - it has to be at the begining of the function
 64 	// - see details at http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
 65 	this._timerId	= requestAnimationFrame( this._onAnimationFrame.bind(this) );
 66 
 67 	// update time values
 68 	var now		= tQuery.now()/1000;
 69 	if( !this._lastTime )	this._lastTime = now - 1/60;
 70 	var delta	= now - this._lastTime;
 71 	this._lastTime	= now;
 72 
 73 	// run all the hooks - from lower priority to higher - in order of registration
 74 	for(var priority = 0; priority <= this._hooks.length; priority++){
 75 		if( this._hooks[priority] === undefined )	continue;
 76 		var callbacks	= this._hooks[priority].slice(0)
 77 		for(var i = 0; i < callbacks.length; i++){
 78 			callbacks[i](delta, now);
 79 		}
 80 	}
 81 }
 82 
 83 //////////////////////////////////////////////////////////////////////////////////
 84 //		Handle the hooks						//
 85 //////////////////////////////////////////////////////////////////////////////////
 86 
 87 tQuery.Loop.prototype.PRE_RENDER		= 20;
 88 tQuery.Loop.prototype.ON_RENDER		= 50;
 89 tQuery.Loop.prototype.POST_RENDER	= 80;
 90 
 91 /**
 92  * hook a callback at a given priority
 93  *
 94  * @param {Number} priority for this callback
 95  * @param {Function} callback the function which will be called function(time){}
 96  * @returns {Function} the callback function. usefull for this._$callback = loop.hook(this._callback.bind(this))
 97  *                     and later loop.unhook(this._$callback)
 98 */
 99 tQuery.Loop.prototype.hook	= function(priority, callback)
100 {
101 	// handle parameters
102 	if( typeof priority === 'function' ){
103 		callback	= priority;
104 		priority	= this.PRE_RENDER;
105 	}
106 
107 	this._hooks[priority]	= this._hooks[priority] || [];
108 	console.assert(this._hooks[priority].indexOf(callback) === -1)
109 	this._hooks[priority].push(callback);
110 	return callback;
111 }
112 
113 /**
114  * unhook a callback at a given priority
115  *
116  * @param {Number} priority for this callback
117  * @param {Function} callback the function which will be called function(time){}
118  * @returns {tQuery.Loop} chained API
119 */
120 tQuery.Loop.prototype.unhook	= function(priority, callback)
121 {
122 	// handle parameters
123 	if( typeof priority === 'function' ){
124 		callback	= priority;
125 		priority	= this.PRE_RENDER;
126 	}
127 
128 	var index	= this._hooks[priority].indexOf(callback);
129 	console.assert(index !== -1);
130 	this._hooks[priority].splice(index, 1);
131 	this._hooks[priority].length === 0 && delete this._hooks[priority]
132 	// for chained API
133 	return this;
134 }
135 
136 
137 // bunch of shortcut
138 // - TODO should it be in a plugin ?
139 
140 tQuery.Loop.prototype.hookPreRender	= function(callback){ return this.hook(this.PRE_RENDER, callback);	};
141 tQuery.Loop.prototype.hookOnRender	= function(callback){ return this.hook(this.ON_RENDER, callback);	};
142 tQuery.Loop.prototype.hookPostRender	= function(callback){ return this.hook(this.POST_RENDER, callback);	};
143 tQuery.Loop.prototype.unhookPreRender	= function(callback){ return this.unhook(this.PRE_RENDER, callback);	};
144 tQuery.Loop.prototype.unhookOnRender	= function(callback){ return this.unhook(this.ON_RENDER, callback);	};
145 tQuery.Loop.prototype.unhookPostRender	= function(callback){ return this.unhook(this.POST_RENDER, callback);	};
146