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