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