1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 cc.g_NumberOfDraws = 0;
 28 
 29 cc.GLToClipTransform = function (transformOut) {
 30     //var projection = new cc.math.Matrix4();
 31     //cc.kmGLGetMatrix(cc.KM_GL_PROJECTION, projection);
 32     cc.kmGLGetMatrix(cc.KM_GL_PROJECTION, transformOut);
 33 
 34     var modelview = new cc.math.Matrix4();
 35     cc.kmGLGetMatrix(cc.KM_GL_MODELVIEW, modelview);
 36 
 37     transformOut.multiply(modelview);
 38 };
 39 //----------------------------------------------------------------------------------------------------------------------
 40 
 41 /**
 42  * <p>
 43  *    ATTENTION: USE cc.director INSTEAD OF cc.Director.<br/>
 44  *    cc.director is a singleton object which manage your game's logic flow.<br/>
 45  *    Since the cc.director is a singleton, you don't need to call any constructor or create functions,<br/>
 46  *    the standard way to use it is by calling:<br/>
 47  *      - cc.director.methodName(); <br/>
 48  *
 49  *    It creates and handle the main Window and manages how and when to execute the Scenes.<br/>
 50  *    <br/>
 51  *    The cc.director is also responsible for:<br/>
 52  *      - initializing the OpenGL context<br/>
 53  *      - setting the OpenGL pixel format (default on is RGB565)<br/>
 54  *      - setting the OpenGL pixel format (default on is RGB565)<br/>
 55  *      - setting the OpenGL buffer depth (default one is 0-bit)<br/>
 56  *      - setting the projection (default one is 3D)<br/>
 57  *      - setting the orientation (default one is Portrait)<br/>
 58  *      <br/>
 59  *    <br/>
 60  *    The cc.director also sets the default OpenGL context:<br/>
 61  *      - GL_TEXTURE_2D is enabled<br/>
 62  *      - GL_VERTEX_ARRAY is enabled<br/>
 63  *      - GL_COLOR_ARRAY is enabled<br/>
 64  *      - GL_TEXTURE_COORD_ARRAY is enabled<br/>
 65  * </p>
 66  * <p>
 67  *   cc.director also synchronizes timers with the refresh rate of the display.<br/>
 68  *   Features and Limitations:<br/>
 69  *      - Scheduled timers & drawing are synchronizes with the refresh rate of the display<br/>
 70  *      - Only supports animation intervals of 1/60 1/30 & 1/15<br/>
 71  * </p>
 72  * @class
 73  * @name cc.Director
 74  */
 75 cc.Director = cc.Class.extend(/** @lends cc.Director# */{
 76     //Variables
 77     _landscape: false,
 78     _nextDeltaTimeZero: false,
 79     _paused: false,
 80     _purgeDirectorInNextLoop: false,
 81     _sendCleanupToScene: false,
 82     _animationInterval: 0.0,
 83     _oldAnimationInterval: 0.0,
 84     _projection: 0,
 85     _accumDt: 0.0,
 86     _contentScaleFactor: 1.0,
 87 
 88     _displayStats: false,
 89     _deltaTime: 0.0,
 90     _frameRate: 0.0,
 91 
 92     _FPSLabel: null,
 93     _SPFLabel: null,
 94     _drawsLabel: null,
 95 
 96     _winSizeInPoints: null,
 97 
 98     _lastUpdate: null,
 99     _nextScene: null,
100     _notificationNode: null,
101     _openGLView: null,
102     _scenesStack: null,
103     _projectionDelegate: null,
104     _runningScene: null,
105 
106     _frames: 0,
107     _totalFrames: 0,
108     _secondsPerFrame: 0,
109 
110     _dirtyRegion: null,
111 
112     _scheduler: null,
113     _actionManager: null,
114     _eventProjectionChanged: null,
115     _eventAfterDraw: null,
116     _eventAfterVisit: null,
117     _eventAfterUpdate: null,
118 
119     ctor: function () {
120         var self = this;
121         self._lastUpdate = Date.now();
122         cc.eventManager.addCustomListener(cc.game.EVENT_SHOW, function () {
123             self._lastUpdate = Date.now();
124         });
125     },
126 
127     init: function () {
128         // scenes
129         this._oldAnimationInterval = this._animationInterval = 1.0 / cc.defaultFPS;
130         this._scenesStack = [];
131         // Set default projection (3D)
132         this._projection = cc.Director.PROJECTION_DEFAULT;
133         // projection delegate if "Custom" projection is used
134         this._projectionDelegate = null;
135 
136         //FPS
137         this._accumDt = 0;
138         this._frameRate = 0;
139         this._displayStats = false;//can remove
140         this._totalFrames = this._frames = 0;
141         this._lastUpdate = Date.now();
142 
143         //Paused?
144         this._paused = false;
145 
146         //purge?
147         this._purgeDirectorInNextLoop = false;
148 
149         this._winSizeInPoints = cc.size(0, 0);
150 
151         this._openGLView = null;
152         this._contentScaleFactor = 1.0;
153 
154         //scheduler
155         this._scheduler = new cc.Scheduler();
156         //action manager
157         this._actionManager = cc.ActionManager ? new cc.ActionManager() : null;
158         this._scheduler.scheduleUpdate(this._actionManager, cc.Scheduler.PRIORITY_SYSTEM, false);
159 
160         this._eventAfterDraw = new cc.EventCustom(cc.Director.EVENT_AFTER_DRAW);
161         this._eventAfterDraw.setUserData(this);
162         this._eventAfterVisit = new cc.EventCustom(cc.Director.EVENT_AFTER_VISIT);
163         this._eventAfterVisit.setUserData(this);
164         this._eventAfterUpdate = new cc.EventCustom(cc.Director.EVENT_AFTER_UPDATE);
165         this._eventAfterUpdate.setUserData(this);
166         this._eventProjectionChanged = new cc.EventCustom(cc.Director.EVENT_PROJECTION_CHANGED);
167         this._eventProjectionChanged.setUserData(this);
168 
169         return true;
170     },
171 
172     /**
173      * calculates delta time since last time it was called
174      */
175     calculateDeltaTime: function () {
176         var now = Date.now();
177 
178         // new delta time.
179         if (this._nextDeltaTimeZero) {
180             this._deltaTime = 0;
181             this._nextDeltaTimeZero = false;
182         } else {
183             this._deltaTime = (now - this._lastUpdate) / 1000;
184         }
185 
186         if ((cc.game.config[cc.game.CONFIG_KEY.debugMode] > 0) && (this._deltaTime > 0.2))
187             this._deltaTime = 1 / 60.0;
188 
189         this._lastUpdate = now;
190     },
191 
192     /**
193      * Converts a view coordinate to an WebGL coordinate<br/>
194      * Useful to convert (multi) touches coordinates to the current layout (portrait or landscape)<br/>
195      * Implementation can be found in CCDirectorWebGL
196      * @function
197      * @param {cc.Point} uiPoint
198      * @return {cc.Point}
199      */
200     convertToGL: null,
201 
202     /**
203      * Converts an WebGL coordinate to a view coordinate<br/>
204      * Useful to convert node points to window points for calls such as glScissor<br/>
205      * Implementation can be found in CCDirectorWebGL
206      * @function
207      * @param {cc.Point} glPoint
208      * @return {cc.Point}
209      */
210     convertToUI: null,
211 
212     /**
213      *  Draw the scene. This method is called every frame. Don't call it manually.
214      */
215     drawScene: function () {
216         var renderer = cc.renderer;
217         // calculate "global" dt
218         this.calculateDeltaTime();
219 
220         //tick before glClear: issue #533
221         if (!this._paused) {
222             this._scheduler.update(this._deltaTime);
223             cc.eventManager.dispatchEvent(this._eventAfterUpdate);
224         }
225 
226         this._clear();
227 
228         /* to avoid flickr, nextScene MUST be here: after tick and before draw.
229          XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
230         if (this._nextScene) {
231             this.setNextScene();
232         }
233 
234         if (this._beforeVisitScene)
235             this._beforeVisitScene();
236 
237         // draw the scene
238         if (this._runningScene) {
239             if (renderer.childrenOrderDirty === true) {
240                 cc.renderer.clearRenderCommands();
241                 this._runningScene._renderCmd._curLevel = 0;                          //level start from 0;
242                 this._runningScene.visit();
243                 renderer.resetFlag();
244             } else if (renderer.transformDirty() === true)
245                 renderer.transform();
246 
247             cc.eventManager.dispatchEvent(this._eventAfterVisit);
248         }
249 
250         // draw the notifications node
251         if (this._notificationNode)
252             this._notificationNode.visit();
253 
254         if (this._displayStats)
255             this._showStats();
256 
257         if (this._afterVisitScene)
258             this._afterVisitScene();
259 
260         renderer.rendering(cc._renderContext);
261         cc.eventManager.dispatchEvent(this._eventAfterDraw);
262         this._totalFrames++;
263 
264         if (this._displayStats)
265             this._calculateMPF();
266     },
267 
268     _beforeVisitScene: null,
269     _afterVisitScene: null,
270 
271     /**
272      * End the life of director in the next frame
273      */
274     end: function () {
275         this._purgeDirectorInNextLoop = true;
276     },
277 
278     /**
279      * Returns the size in pixels of the surface. It could be different than the screen size.<br/>
280      * High-res devices might have a higher surface size than the screen size.
281      * @return {Number}
282      */
283     getContentScaleFactor: function () {
284         return this._contentScaleFactor;
285     },
286 
287     /**
288      * This object will be visited after the main scene is visited.<br/>
289      * This object MUST implement the "visit" selector.<br/>
290      * Useful to hook a notification object
291      * @return {cc.Node}
292      */
293     getNotificationNode: function () {
294         return this._notificationNode;
295     },
296 
297     /**
298      * Returns the size of the WebGL view in points.<br/>
299      * It takes into account any possible rotation (device orientation) of the window
300      * @return {cc.Size}
301      */
302     getWinSize: function () {
303         return cc.size(this._winSizeInPoints);
304     },
305 
306     /**
307      * Returns the size of the OpenGL view in pixels.<br/>
308      * It takes into account any possible rotation (device orientation) of the window.<br/>
309      * On Mac winSize and winSizeInPixels return the same value.
310      * @return {cc.Size}
311      */
312     getWinSizeInPixels: function () {
313         return cc.size(this._winSizeInPoints.width * this._contentScaleFactor, this._winSizeInPoints.height * this._contentScaleFactor);
314     },
315 
316     /**
317      * getVisibleSize/getVisibleOrigin move to CCDirectorWebGL/CCDirectorCanvas
318      * getZEye move to CCDirectorWebGL
319      */
320 
321     /**
322      * Returns the visible size of the running scene
323      * @function
324      * @return {cc.Size}
325      */
326     getVisibleSize: null,
327 
328     /**
329      * Returns the visible origin of the running scene
330      * @function
331      * @return {cc.Point}
332      */
333     getVisibleOrigin: null,
334 
335     /**
336      * Returns the z eye, only available in WebGL mode
337      * @function
338      * @return {Number}
339      */
340     getZEye: null,
341 
342     /**
343      * Pause the director's ticker
344      */
345     pause: function () {
346         if (this._paused)
347             return;
348 
349         this._oldAnimationInterval = this._animationInterval;
350         // when paused, don't consume CPU
351         this.setAnimationInterval(1 / 4.0);
352         this._paused = true;
353     },
354 
355     /**
356      * Pops out a scene from the queue.<br/>
357      * This scene will replace the running one.<br/>
358      * The running scene will be deleted. If there are no more scenes in the stack the execution is terminated.<br/>
359      * ONLY call it if there is a running scene.
360      */
361     popScene: function () {
362 
363         cc.assert(this._runningScene, cc._LogInfos.Director_popScene);
364 
365         this._scenesStack.pop();
366         var c = this._scenesStack.length;
367 
368         if (c === 0)
369             this.end();
370         else {
371             this._sendCleanupToScene = true;
372             this._nextScene = this._scenesStack[c - 1];
373         }
374     },
375 
376     /**
377      * Removes cached all cocos2d cached data. It will purge the cc.textureCache, cc.spriteFrameCache, cc.animationCache
378      */
379     purgeCachedData: function () {
380         cc.animationCache._clear();
381         cc.spriteFrameCache._clear();
382         cc.textureCache._clear();
383     },
384 
385     /**
386      * Purge the cc.director itself, including unschedule all schedule, remove all event listeners, clean up and exit the running scene, stops all animations, clear cached data.
387      */
388     purgeDirector: function () {
389         //cleanup scheduler
390         this.getScheduler().unscheduleAll();
391 
392         // Disable event dispatching
393         if (cc.eventManager)
394             cc.eventManager.setEnabled(false);
395 
396         // don't release the event handlers
397         // They are needed in case the director is run again
398 
399         if (this._runningScene) {
400             this._runningScene.onExitTransitionDidStart();
401             this._runningScene.onExit();
402             this._runningScene.cleanup();
403         }
404 
405         this._runningScene = null;
406         this._nextScene = null;
407 
408         // remove all objects, but don't release it.
409         // runScene might be executed after 'end'.
410         this._scenesStack.length = 0;
411 
412         this.stopAnimation();
413 
414         // Clear all caches
415         this.purgeCachedData();
416 
417         cc.checkGLErrorDebug();
418     },
419 
420     /**
421      * Suspends the execution of the running scene, pushing it on the stack of suspended scenes.<br/>
422      * The new scene will be executed.<br/>
423      * Try to avoid big stacks of pushed scenes to reduce memory allocation.<br/>
424      * ONLY call it if there is a running scene.
425      * @param {cc.Scene} scene
426      */
427     pushScene: function (scene) {
428 
429         cc.assert(scene, cc._LogInfos.Director_pushScene);
430 
431         this._sendCleanupToScene = false;
432 
433         this._scenesStack.push(scene);
434         this._nextScene = scene;
435     },
436 
437     /**
438      * Run a scene. Replaces the running scene with a new one or enter the first scene.
439      * @param {cc.Scene} scene
440      */
441     runScene: function (scene) {
442 
443         cc.assert(scene, cc._LogInfos.Director_pushScene);
444 
445         if (!this._runningScene) {
446             //start scene
447             this.pushScene(scene);
448             this.startAnimation();
449         } else {
450             //replace scene
451             var i = this._scenesStack.length;
452             if (i === 0) {
453                 this._sendCleanupToScene = true;
454                 this._scenesStack[i] = scene;
455                 this._nextScene = scene;
456             } else {
457                 this._sendCleanupToScene = true;
458                 this._scenesStack[i - 1] = scene;
459                 this._nextScene = scene;
460             }
461         }
462     },
463 
464     /**
465      * Resume director after pause, if the current scene is not paused, nothing will happen.
466      */
467     resume: function () {
468         if (!this._paused) {
469             return;
470         }
471 
472         this.setAnimationInterval(this._oldAnimationInterval);
473         this._lastUpdate = Date.now();
474         if (!this._lastUpdate) {
475             cc.log(cc._LogInfos.Director_resume);
476         }
477 
478         this._paused = false;
479         this._deltaTime = 0;
480     },
481 
482     /**
483      * The size in pixels of the surface. It could be different than the screen size.<br/>
484      * High-res devices might have a higher surface size than the screen size.
485      * @param {Number} scaleFactor
486      */
487     setContentScaleFactor: function (scaleFactor) {
488         if (scaleFactor !== this._contentScaleFactor) {
489             this._contentScaleFactor = scaleFactor;
490             this._createStatsLabel();
491         }
492     },
493 
494     /**
495      * Enables or disables WebGL depth test.<br/>
496      * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js
497      * @function
498      * @param {Boolean} on
499      */
500     setDepthTest: null,
501 
502     /**
503      * Sets the default values based on the CCConfiguration info
504      */
505     setDefaultValues: function () {
506 
507     },
508 
509     /**
510      * Sets whether next delta time equals to zero
511      * @param {Boolean} nextDeltaTimeZero
512      */
513     setNextDeltaTimeZero: function (nextDeltaTimeZero) {
514         this._nextDeltaTimeZero = nextDeltaTimeZero;
515     },
516 
517     /**
518      * Starts the registered next scene
519      */
520     setNextScene: function () {
521         var runningIsTransition = false, newIsTransition = false;
522         if (cc.TransitionScene) {
523             runningIsTransition = this._runningScene ? this._runningScene instanceof cc.TransitionScene : false;
524             newIsTransition = this._nextScene ? this._nextScene instanceof cc.TransitionScene : false;
525         }
526 
527         // If it is not a transition, call onExit/cleanup
528         if (!newIsTransition) {
529             var locRunningScene = this._runningScene;
530             if (locRunningScene) {
531                 locRunningScene.onExitTransitionDidStart();
532                 locRunningScene.onExit();
533             }
534 
535             // issue #709. the root node (scene) should receive the cleanup message too
536             // otherwise it might be leaked.
537             if (this._sendCleanupToScene && locRunningScene)
538                 locRunningScene.cleanup();
539         }
540 
541         this._runningScene = this._nextScene;
542         cc.renderer.childrenOrderDirty = true;
543 
544         this._nextScene = null;
545         if ((!runningIsTransition) && (this._runningScene !== null)) {
546             this._runningScene.onEnter();
547             this._runningScene.onEnterTransitionDidFinish();
548         }
549     },
550 
551     /**
552      * Sets Notification Node
553      * @param {cc.Node} node
554      */
555     setNotificationNode: function (node) {
556         this._notificationNode = node;
557     },
558 
559     /**
560      * Returns the cc.director delegate.
561      * @return {cc.DirectorDelegate}
562      */
563     getDelegate: function () {
564         return this._projectionDelegate;
565     },
566 
567     /**
568      * Sets the cc.director delegate. It shall implement the CCDirectorDelegate protocol
569      * @return {cc.DirectorDelegate}
570      */
571     setDelegate: function (delegate) {
572         this._projectionDelegate = delegate;
573     },
574 
575     /**
576      * Sets the view, where everything is rendered, do not call this function.<br/>
577      * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js.
578      * @function
579      * @param {cc.view} openGLView
580      */
581     setOpenGLView: null,
582 
583     /**
584      * Sets an OpenGL projection.<br/>
585      * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js.
586      * @function
587      * @param {Number} projection
588      */
589     setProjection: null,
590 
591     /**
592      * Update the view port.<br/>
593      * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js.
594      * @function
595      */
596     setViewport: null,
597 
598     /**
599      * Get the CCEGLView, where everything is rendered.<br/>
600      * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js.
601      * @function
602      * @return {cc.view}
603      */
604     getOpenGLView: null,
605 
606     /**
607      * Sets an OpenGL projection.<br/>
608      * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js.
609      * @function
610      * @return {Number}
611      */
612     getProjection: null,
613 
614     /**
615      * Enables/disables OpenGL alpha blending.<br/>
616      * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js.
617      * @function
618      * @param {Boolean} on
619      */
620     setAlphaBlending: null,
621 
622     _showStats: function () {
623         this._frames++;
624         this._accumDt += this._deltaTime;
625         if (this._FPSLabel && this._SPFLabel && this._drawsLabel) {
626             if (this._accumDt > cc.DIRECTOR_FPS_INTERVAL) {
627                 this._SPFLabel.string = this._secondsPerFrame.toFixed(3);
628 
629                 this._frameRate = this._frames / this._accumDt;
630                 this._frames = 0;
631                 this._accumDt = 0;
632 
633                 this._FPSLabel.string = this._frameRate.toFixed(1);
634                 this._drawsLabel.string = (0 | cc.g_NumberOfDraws).toString();
635             }
636             this._FPSLabel.visit();
637             this._SPFLabel.visit();
638             this._drawsLabel.visit();
639         } else
640             this._createStatsLabel();
641         cc.g_NumberOfDraws = 0;
642     },
643 
644     /**
645      * Returns whether or not the replaced scene will receive the cleanup message.<br>
646      * If the new scene is pushed, then the old scene won't receive the "cleanup" message.<br/>
647      * If the new scene replaces the old one, the it will receive the "cleanup" message.
648      * @return {Boolean}
649      */
650     isSendCleanupToScene: function () {
651         return this._sendCleanupToScene;
652     },
653 
654     /**
655      * Returns current running Scene. Director can only run one Scene at the time
656      * @return {cc.Scene}
657      */
658     getRunningScene: function () {
659         return this._runningScene;
660     },
661 
662     /**
663      * Returns the FPS value
664      * @return {Number}
665      */
666     getAnimationInterval: function () {
667         return this._animationInterval;
668     },
669 
670     /**
671      * Returns whether or not to display the FPS informations
672      * @return {Boolean}
673      */
674     isDisplayStats: function () {
675         return this._displayStats;
676     },
677 
678     /**
679      * Sets whether display the FPS on the bottom-left corner
680      * @param {Boolean} displayStats
681      */
682     setDisplayStats: function (displayStats) {
683         this._displayStats = displayStats;
684     },
685 
686     /**
687      * Returns seconds per frame
688      * @return {Number}
689      */
690     getSecondsPerFrame: function () {
691         return this._secondsPerFrame;
692     },
693 
694     /**
695      * Returns whether next delta time equals to zero
696      * @return {Boolean}
697      */
698     isNextDeltaTimeZero: function () {
699         return this._nextDeltaTimeZero;
700     },
701 
702     /**
703      * Returns whether or not the Director is paused
704      * @return {Boolean}
705      */
706     isPaused: function () {
707         return this._paused;
708     },
709 
710     /**
711      * Returns how many frames were called since the director started
712      * @return {Number}
713      */
714     getTotalFrames: function () {
715         return this._totalFrames;
716     },
717 
718     /**
719      * Pops out all scenes from the queue until the root scene in the queue. <br/>
720      * This scene will replace the running one.  <br/>
721      * Internally it will call "popToSceneStackLevel(1)"
722      */
723     popToRootScene: function () {
724         this.popToSceneStackLevel(1);
725     },
726 
727     /**
728      * Pops out all scenes from the queue until it reaches "level".                             <br/>
729      * If level is 0, it will end the director.                                                 <br/>
730      * If level is 1, it will pop all scenes until it reaches to root scene.                    <br/>
731      * If level is <= than the current stack level, it won't do anything.
732      * @param {Number} level
733      */
734     popToSceneStackLevel: function (level) {
735         cc.assert(this._runningScene, cc._LogInfos.Director_popToSceneStackLevel_2);
736 
737         var locScenesStack = this._scenesStack;
738         var c = locScenesStack.length;
739 
740         if (c === 0) {
741             this.end();
742             return;
743         }
744         // current level or lower -> nothing
745         if (level > c)
746             return;
747 
748         // pop stack until reaching desired level
749         while (c > level) {
750             var current = locScenesStack.pop();
751             if (current.running) {
752                 current.onExitTransitionDidStart();
753                 current.onExit();
754             }
755             current.cleanup();
756             c--;
757         }
758         this._nextScene = locScenesStack[locScenesStack.length - 1];
759         this._sendCleanupToScene = false;
760     },
761 
762     /**
763      * Returns the cc.Scheduler associated with this director
764      * @return {cc.Scheduler}
765      */
766     getScheduler: function () {
767         return this._scheduler;
768     },
769 
770     /**
771      * Sets the cc.Scheduler associated with this director
772      * @param {cc.Scheduler} scheduler
773      */
774     setScheduler: function (scheduler) {
775         if (this._scheduler !== scheduler) {
776             this._scheduler = scheduler;
777         }
778     },
779 
780     /**
781      * Returns the cc.ActionManager associated with this director
782      * @return {cc.ActionManager}
783      */
784     getActionManager: function () {
785         return this._actionManager;
786     },
787     /**
788      * Sets the cc.ActionManager associated with this director
789      * @param {cc.ActionManager} actionManager
790      */
791     setActionManager: function (actionManager) {
792         if (this._actionManager !== actionManager) {
793             this._actionManager = actionManager;
794         }
795     },
796 
797     /**
798      * Returns the delta time since last frame
799      * @return {Number}
800      */
801     getDeltaTime: function () {
802         return this._deltaTime;
803     },
804 
805     _createStatsLabel: null,
806 
807     _calculateMPF: function () {
808         var now = Date.now();
809         this._secondsPerFrame = (now - this._lastUpdate) / 1000;
810     }
811 });
812 
813 /**
814  * The event projection changed of cc.Director
815  * @constant
816  * @type {string}
817  * @example
818  *   cc.eventManager.addCustomListener(cc.Director.EVENT_PROJECTION_CHANGED, function(event) {
819  *           cc.log("Projection changed.");
820  *       });
821  */
822 cc.Director.EVENT_PROJECTION_CHANGED = "director_projection_changed";
823 
824 /**
825  * The event after draw of cc.Director
826  * @constant
827  * @type {string}
828  * @example
829  *   cc.eventManager.addCustomListener(cc.Director.EVENT_AFTER_DRAW, function(event) {
830  *           cc.log("after draw event.");
831  *       });
832  */
833 cc.Director.EVENT_AFTER_DRAW = "director_after_draw";
834 
835 /**
836  * The event after visit of cc.Director
837  * @constant
838  * @type {string}
839  * @example
840  *   cc.eventManager.addCustomListener(cc.Director.EVENT_AFTER_VISIT, function(event) {
841  *           cc.log("after visit event.");
842  *       });
843  */
844 cc.Director.EVENT_AFTER_VISIT = "director_after_visit";
845 
846 /**
847  * The event after update of cc.Director
848  * @constant
849  * @type {string}
850  * @example
851  *   cc.eventManager.addCustomListener(cc.Director.EVENT_AFTER_UPDATE, function(event) {
852  *           cc.log("after update event.");
853  *       });
854  */
855 cc.Director.EVENT_AFTER_UPDATE = "director_after_update";
856 
857 /***************************************************
858  * implementation of DisplayLinkDirector
859  **************************************************/
860 cc.DisplayLinkDirector = cc.Director.extend(/** @lends cc.Director# */{
861     invalid: false,
862 
863     /**
864      * Starts Animation
865      */
866     startAnimation: function () {
867         this._nextDeltaTimeZero = true;
868         this.invalid = false;
869     },
870 
871     /**
872      * Run main loop of director
873      */
874     mainLoop: function () {
875         if (this._purgeDirectorInNextLoop) {
876             this._purgeDirectorInNextLoop = false;
877             this.purgeDirector();
878         }
879         else if (!this.invalid) {
880             this.drawScene();
881         }
882     },
883 
884     /**
885      * Stops animation
886      */
887     stopAnimation: function () {
888         this.invalid = true;
889     },
890 
891     /**
892      * Sets animation interval
893      * @param {Number} value the animation interval desired
894      */
895     setAnimationInterval: function (value) {
896         this._animationInterval = value;
897         if (!this.invalid) {
898             this.stopAnimation();
899             this.startAnimation();
900         }
901     }
902 });
903 
904 cc.Director.sharedDirector = null;
905 cc.Director.firstUseDirector = true;
906 
907 cc.Director._getInstance = function () {
908     if (cc.Director.firstUseDirector) {
909         cc.Director.firstUseDirector = false;
910         cc.Director.sharedDirector = new cc.DisplayLinkDirector();
911         cc.Director.sharedDirector.init();
912     }
913     return cc.Director.sharedDirector;
914 };
915 
916 /**
917  * Default fps is 60
918  * @type {Number}
919  */
920 cc.defaultFPS = 60;
921 
922 //Possible OpenGL projections used by director
923 /**
924  * Constant for 2D projection (orthogonal projection)
925  * @constant
926  * @type {Number}
927  */
928 cc.Director.PROJECTION_2D = 0;
929 
930 /**
931  * Constant for 3D projection with a fovy=60, znear=0.5f and zfar=1500.
932  * @constant
933  * @type {Number}
934  */
935 cc.Director.PROJECTION_3D = 1;
936 
937 /**
938  * Constant for custom projection, if cc.Director's projection set to it, it calls "updateProjection" on the projection delegate.
939  * @constant
940  * @type {Number}
941  */
942 cc.Director.PROJECTION_CUSTOM = 3;
943 
944 /**
945  * Constant for default projection of cc.Director, default projection is 3D projection
946  * @constant
947  * @type {Number}
948  */
949 cc.Director.PROJECTION_DEFAULT = cc.Director.PROJECTION_3D;
950 
951 if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
952 
953     var _p = cc.Director.prototype;
954 
955     _p.setProjection = function (projection) {
956         this._projection = projection;
957         cc.eventManager.dispatchEvent(this._eventProjectionChanged);
958     };
959 
960     _p.setDepthTest = function () {
961     };
962 
963     _p.setOpenGLView = function (openGLView) {
964         // set size
965         this._winSizeInPoints.width = cc._canvas.width;      //this._openGLView.getDesignResolutionSize();
966         this._winSizeInPoints.height = cc._canvas.height;
967         this._openGLView = openGLView || cc.view;
968         if (cc.eventManager)
969             cc.eventManager.setEnabled(true);
970     };
971 
972     _p._clear = function () {
973         var viewport = this._openGLView.getViewPortRect();
974         var context = cc._renderContext.getContext();
975         context.setTransform(1,0,0,1, 0, 0);
976         context.clearRect(-viewport.x, viewport.y, viewport.width, viewport.height);
977     };
978 
979     _p._createStatsLabel = function () {
980         var _t = this;
981         var fontSize = 0;
982         if (_t._winSizeInPoints.width > _t._winSizeInPoints.height)
983             fontSize = 0 | (_t._winSizeInPoints.height / 320 * 24);
984         else
985             fontSize = 0 | (_t._winSizeInPoints.width / 320 * 24);
986 
987         _t._FPSLabel = new cc.LabelTTF("000.0", "Arial", fontSize);
988         _t._SPFLabel = new cc.LabelTTF("0.000", "Arial", fontSize);
989         _t._drawsLabel = new cc.LabelTTF("0000", "Arial", fontSize);
990 
991         var locStatsPosition = cc.DIRECTOR_STATS_POSITION;
992         _t._drawsLabel.setPosition(_t._drawsLabel.width / 2 + locStatsPosition.x, _t._drawsLabel.height * 5 / 2 + locStatsPosition.y);
993         _t._SPFLabel.setPosition(_t._SPFLabel.width / 2 + locStatsPosition.x, _t._SPFLabel.height * 3 / 2 + locStatsPosition.y);
994         _t._FPSLabel.setPosition(_t._FPSLabel.width / 2 + locStatsPosition.x, _t._FPSLabel.height / 2 + locStatsPosition.y);
995     };
996 
997     _p.getVisibleSize = function () {
998         //if (this._openGLView) {
999         //return this._openGLView.getVisibleSize();
1000         //} else {
1001         return this.getWinSize();
1002         //}
1003     };
1004 
1005     _p.getVisibleOrigin = function () {
1006         //if (this._openGLView) {
1007         //return this._openGLView.getVisibleOrigin();
1008         //} else {
1009         return cc.p(0, 0);
1010         //}
1011     };
1012 } else {
1013     cc.Director._fpsImage = new Image();
1014     cc._addEventListener(cc.Director._fpsImage, "load", function () {
1015         cc.Director._fpsImageLoaded = true;
1016     });
1017     if (cc._fpsImage) {
1018         cc.Director._fpsImage.src = cc._fpsImage;
1019     }
1020 }