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