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