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