1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga 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 //Possible OpenGL projections used by director
 30 /**
 31  * sets a 2D projection (orthogonal projection)
 32  * @constant
 33  * @type Number
 34  */
 35 cc.DIRECTOR_PROJECTION_2D = 0;
 36 
 37 /**
 38  * sets a 3D projection with a fovy=60, znear=0.5f and zfar=1500.
 39  * @constant
 40  * @type Number
 41  */
 42 cc.DIRECTOR_PROJECTION_3D = 1;
 43 
 44 /**
 45  * it calls "updateProjection" on the projection delegate.
 46  * @constant
 47  * @type Number
 48  */
 49 cc.DIRECTOR_PROJECTION_CUSTOM = 3;
 50 
 51 /**
 52  * Detault projection is 3D projection
 53  * @constant
 54  * @type Number
 55  */
 56 cc.DIRECTOR_PROJECTION_DEFAULT = cc.DIRECTOR_PROJECTION_3D;
 57 
 58 //----------------------------------------------------------------------------------------------------------------------
 59 //Possible device orientations
 60 /**
 61  * Device oriented vertically, home button on the bottom (UIDeviceOrientationPortrait)
 62  * @constant
 63  * @type Number
 64  */
 65 cc.DEVICE_ORIENTATION_PORTRAIT = 0;
 66 
 67 /**
 68  * Device oriented horizontally, home button on the right (UIDeviceOrientationLandscapeLeft)
 69  * @constant
 70  * @type Number
 71  */
 72 cc.DEVICE_ORIENTATION_LANDSCAPE_LEFT = 1;
 73 
 74 /**
 75  * Device oriented vertically, home button on the top (UIDeviceOrientationPortraitUpsideDown)
 76  * @constant
 77  * @type Number
 78  */
 79 cc.DEVICE_ORIENTATION_PORTRAIT_UPSIDE_DOWN = 2;
 80 
 81 /**
 82  * Device oriented horizontally, home button on the left (UIDeviceOrientationLandscapeRight)
 83  * @constant
 84  * @type Number
 85  */
 86 cc.DEVICE_ORIENTATION_LANDSCAPE_RIGHT = 3;
 87 
 88 /**
 89  * In browsers, we only support 2 orientations by change window size.
 90  * @constant
 91  * @type Number
 92  */
 93 cc.DEVICE_MAX_ORIENTATIONS = 2;
 94 
 95 /**
 96  * OpenGL projection protocol
 97  * @class
 98  * @extends cc.Class
 99  */
100 cc.DirectorDelegate = cc.Class.extend(/** @lends cc.DirectorDelegate# */{
101     /**
102      * Called by CCDirector when the projection is updated, and "custom" projection is used
103      */
104     updateProjection:function () {
105     }
106 });
107 
108 cc.GLToClipTransform = function (transformOut) {
109     var projection = new cc.kmMat4();
110     cc.kmGLGetMatrix(cc.KM_GL_PROJECTION, projection);
111 
112     var modelview = new cc.kmMat4();
113     cc.kmGLGetMatrix(cc.KM_GL_MODELVIEW, modelview);
114 
115     cc.kmMat4Multiply(transformOut, projection, modelview);
116 };
117 //----------------------------------------------------------------------------------------------------------------------
118 
119 /**
120  * <p>
121  *    Class that creates and handle the main Window and manages how<br/>
122  *    and when to execute the Scenes.<br/>
123  *    <br/>
124  *    The cc.Director is also responsible for:<br/>
125  *      - initializing the OpenGL context<br/>
126  *      - setting the OpenGL pixel format (default on is RGB565)<br/>
127  *      - setting the OpenGL pixel format (default on is RGB565)<br/>
128  *      - setting the OpenGL buffer depth (default one is 0-bit)<br/>
129  *      - setting the projection (default one is 3D)<br/>
130  *      - setting the orientation (default one is Protrait)<br/>
131  *      <br/>
132  *    Since the cc.Director is a singleton, the standard way to use it is by calling:<br/>
133  *      - cc.Director.getInstance().methodName(); <br/>
134  *    <br/>
135  *    The CCDirector also sets the default OpenGL context:<br/>
136  *      - GL_TEXTURE_2D is enabled<br/>
137  *      - GL_VERTEX_ARRAY is enabled<br/>
138  *      - GL_COLOR_ARRAY is enabled<br/>
139  *      - GL_TEXTURE_COORD_ARRAY is enabled<br/>
140  * </p>
141  * @class
142  * @extends cc.Class
143  */
144 cc.Director = cc.Class.extend(/** @lends cc.Director# */{
145     //Variables
146     _landscape:false,
147     _nextDeltaTimeZero:false,
148     _paused:false,
149     _purgeDirecotorInNextLoop:false,
150     _sendCleanupToScene:false,
151     _animationInterval:0.0,
152     _oldAnimationInterval:0.0,
153     _projection:0,
154     _accumDt:0.0,
155     _contentScaleFactor:1.0,
156 
157     _displayStats:false,
158     _deltaTime:0.0,
159     _frameRate:0.0,
160 
161     _FPSLabel:null,
162     _SPFLabel:null,
163     _drawsLabel:null,
164 
165     _winSizeInPoints:null,
166 
167     _lastUpdate:null,
168     _nextScene:null,
169     _notificationNode:null,
170     _openGLView:null,
171     _scenesStack:null,
172     _projectionDelegate:null,
173     _runningScene:null,
174 
175     _frames:0,
176     _totalFrames:0,
177     _secondsPerFrame:0,
178 
179     _dirtyRegion:null,
180 
181     _scheduler:null,
182     _actionManager:null,
183     _touchDispatcher:null,
184     _keyboardDispatcher:null,
185     _accelerometer:null,
186     _mouseDispatcher:null,
187 
188     _isBlur:false,
189 
190     /**
191      * Constructor
192      */
193     ctor:function () {
194         this._lastUpdate = Date.now();
195         if (!cc.isAddedHiddenEvent) {
196             var selfPointer = this;
197             window.addEventListener("focus", function () {
198                 selfPointer._lastUpdate = Date.now();
199             }, false);
200         }
201     },
202 
203     _resetLastUpdate:function () {
204         this._lastUpdate = Date.now();
205     },
206 
207     /**
208      * initializes cc.Director
209      * @return {Boolean}
210      */
211     init:function () {
212         // scenes
213         this._oldAnimationInterval = this._animationInterval = 1.0 / cc.defaultFPS;
214         this._scenesStack = [];
215         // Set default projection (3D)
216         this._projection = cc.DIRECTOR_PROJECTION_DEFAULT;
217         // projection delegate if "Custom" projection is used
218         this._projectionDelegate = null;
219 
220         //FPS
221         this._accumDt = 0;
222         this._frameRate = 0;
223         this._displayStats = false;//can remove
224         this._totalFrames = this._frames = 0;
225         this._lastUpdate = Date.now();
226 
227         //Paused?
228         this._paused = false;
229 
230         //purge?
231         this._purgeDirecotorInNextLoop = false;
232 
233         this._winSizeInPoints = cc.size(0, 0);
234 
235         this._openGLView = null;
236         this._contentScaleFactor = 1.0;
237 
238         //scheduler
239         this._scheduler = new cc.Scheduler();
240         //action manager
241         this._actionManager = new cc.ActionManager();
242         this._scheduler.scheduleUpdateForTarget(this._actionManager, cc.PRIORITY_SYSTEM, false);
243         //touchDispatcher
244         this._touchDispatcher = new cc.TouchDispatcher();
245         this._touchDispatcher.init();
246 
247         //KeyboardDispatcher
248         this._keyboardDispatcher = cc.KeyboardDispatcher.getInstance();
249 
250         //accelerometer
251         this._accelerometer = new cc.Accelerometer();
252 
253         //MouseDispatcher
254         this._mouseDispatcher = new cc.MouseDispatcher();
255         this._mouseDispatcher.init();
256 
257         return true;
258     },
259 
260     /**
261      * calculates delta time since last time it was called
262      */
263     calculateDeltaTime:function () {
264         var now = Date.now();
265 
266         // new delta time.
267         if (this._nextDeltaTimeZero) {
268             this._deltaTime = 0;
269             this._nextDeltaTimeZero = false;
270         } else {
271             this._deltaTime = (now - this._lastUpdate) / 1000;
272         }
273 
274         if ((cc.COCOS2D_DEBUG > 0) && (this._deltaTime > 0.2))
275             this._deltaTime = 1 / 60.0;
276 
277         this._lastUpdate = now;
278     },
279 
280     /**
281      * <p>
282      *     converts a UIKit coordinate to an OpenGL coordinate<br/>
283      *     Useful to convert (multi) touches coordinates to the current layout (portrait or landscape)
284      * </p>
285      * @param {cc.Point} uiPoint
286      * @return {cc.Point}
287      */
288     convertToGL:function (uiPoint) {
289         var transform = new cc.kmMat4();
290         cc.GLToClipTransform(transform);
291 
292         var transformInv = new cc.kmMat4();
293         cc.kmMat4Inverse(transformInv, transform);
294 
295         // Calculate z=0 using -> transform*[0, 0, 0, 1]/w
296         var zClip = transform.mat[14] / transform.mat[15];
297 
298         var glSize = this._openGLView.getDesignResolutionSize();
299         var clipCoord = new cc.kmVec3(2.0 * uiPoint.x / glSize.width - 1.0, 1.0 - 2.0 * uiPoint.y / glSize.height, zClip);
300 
301         var glCoord = new cc.kmVec3();
302         cc.kmVec3TransformCoord(glCoord, clipCoord, transformInv);
303 
304         return cc.p(glCoord.x, glCoord.y);
305     },
306 
307     /**
308      * <p>converts an OpenGL coordinate to a UIKit coordinate<br/>
309      * Useful to convert node points to window points for calls such as glScissor</p>
310      * @param {cc.Point} glPoint
311      * @return {cc.Point}
312      */
313     convertToUI:function (glPoint) {
314         var transform = new cc.kmMat4();
315         cc.GLToClipTransform(transform);
316 
317         var clipCoord = new cc.kmVec3();
318         // Need to calculate the zero depth from the transform.
319         var glCoord = new cc.kmVec3(glPoint.x, glPoint.y, 0.0);
320         cc.kmVec3TransformCoord(clipCoord, glCoord, transform);
321 
322         var glSize = this._openGLView.getDesignResolutionSize();
323         return cc.p(glSize.width * (clipCoord.x * 0.5 + 0.5), glSize.height * (-clipCoord.y * 0.5 + 0.5));
324     },
325 
326     /**
327      *  Draw the scene. This method is called every frame. Don't call it manually.
328      */
329     drawScene: null,
330 
331     _drawSceneForCanvas: function () {
332         // calculate "global" dt
333         this.calculateDeltaTime();
334 
335         //tick before glClear: issue #533
336         if (!this._paused)
337             this._scheduler.update(this._deltaTime);
338 
339         cc.renderContext.clearRect(0, 0, cc.canvas.width, -cc.canvas.height);
340 
341         /* to avoid flickr, nextScene MUST be here: after tick and before draw.
342          XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
343         if (this._nextScene) {
344             this.setNextScene();
345         }
346         // draw the scene
347         if (this._runningScene)
348             this._runningScene.visit();
349 
350         // draw the notifications node
351         if (this._notificationNode)
352             this._notificationNode.visit();
353 
354         if (this._displayStats)
355             this._showStats();
356 
357         this._totalFrames++;
358 
359         if (this._displayStats)
360             this._calculateMPF();
361     },
362 
363     _drawSceneForWebGL: function () {
364         // calculate "global" dt
365         this.calculateDeltaTime();
366 
367         //tick before glClear: issue #533
368         if (!this._paused)
369             this._scheduler.update(this._deltaTime);
370 
371         var gl = cc.renderContext;
372         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
373 
374         /* to avoid flickr, nextScene MUST be here: after tick and before draw.
375          XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
376         if (this._nextScene)
377             this.setNextScene();
378 
379         cc.kmGLPushMatrix();
380 
381         // draw the scene
382         if (this._runningScene)
383             this._runningScene.visit();
384 
385         // draw the notifications node
386         if (this._notificationNode)
387             this._notificationNode.visit();
388 
389         if (this._displayStats)
390             this._showStats();
391 
392         cc.kmGLPopMatrix();
393 
394         this._totalFrames++;
395 
396         if (this._displayStats)
397             this._calculateMPF();
398     },
399 
400     /**
401      * end director
402      */
403     end:function () {
404         this._purgeDirecotorInNextLoop = true;
405     },
406 
407     /**
408      * <p>get the size in pixels of the surface. It could be different than the screen size.<br/>
409      *   High-res devices might have a higher surface size than the screen size.<br/>
410      *   Only available when compiled using SDK >= 4.0.
411      * </p>
412      * @return {Number}
413      */
414     getContentScaleFactor:function () {
415         return this._contentScaleFactor;
416     },
417 
418     /**
419      * <p>
420      *    This object will be visited after the main scene is visited.<br/>
421      *    This object MUST implement the "visit" selector.<br/>
422      *    Useful to hook a notification object, like CCNotifications (http://github.com/manucorporat/CCNotifications)
423      * </p>
424      * @return {cc.Node}
425      */
426     getNotificationNode:function () {
427         return this._notificationNode;
428     },
429 
430     /**
431      * <p>
432      *     returns the size of the OpenGL view in points.<br/>
433      *     It takes into account any possible rotation (device orientation) of the window
434      * </p>
435      * @return {cc.Size}
436      */
437     getWinSize:function () {
438         return this._winSizeInPoints;
439     },
440 
441     /**
442      * <p>
443      *   returns the size of the OpenGL view in pixels.<br/>
444      *   It takes into account any possible rotation (device orientation) of the window.<br/>
445      *   On Mac winSize and winSizeInPixels return the same value.
446      * </p>
447      * @return {cc.Size}
448      */
449     getWinSizeInPixels:function () {
450         return cc.size(this._winSizeInPoints.width * this._contentScaleFactor, this._winSizeInPoints.height * this._contentScaleFactor);
451     },
452 
453     getVisibleSize:function () {
454         if (this._openGLView) {
455             return this._openGLView.getVisibleSize();
456         }
457         else {
458             return cc.size(0,0);
459         }
460     },
461 
462     getVisibleOrigin:function () {
463         if (this._openGLView) {
464             return this._openGLView.getVisibleOrigin();
465         }
466         else {
467             return cc.p(0, 0);
468         }
469     },
470 
471     getZEye:function () {
472         return (this._winSizeInPoints.height / 1.1566 );
473     },
474 
475     /**
476      * pause director
477      */
478     pause:function () {
479         if (this._paused)
480             return;
481 
482         this._oldAnimationInterval = this._animationInterval;
483         // when paused, don't consume CPU
484         this.setAnimationInterval(1 / 4.0);
485         this._paused = true;
486     },
487 
488     /**
489      * <p>
490      *     Pops out a scene from the queue.<br/>
491      *     This scene will replace the running one.<br/>
492      *     The running scene will be deleted. If there are no more scenes in the stack the execution is terminated.<br/>
493      *     ONLY call it if there is a running scene.
494      * </p>
495      */
496     popScene:function () {
497         cc.Assert(this._runningScene != null, "running scene should not null");
498 
499         //this.addRegionToDirtyRegion(cc.rect(0, 0, cc.canvas.width, cc.canvas.height));
500 
501         this._scenesStack.pop();
502         var c = this._scenesStack.length;
503 
504         if (c == 0)
505             this.end();
506          else {
507             this._sendCleanupToScene = true;
508             this._nextScene = this._scenesStack[c - 1];
509         }
510     },
511 
512     /**
513      * Removes cached all cocos2d cached data. It will purge the CCTextureCache, CCSpriteFrameCache, CCLabelBMFont cache
514      */
515     purgeCachedData:function () {
516         cc.LabelBMFont.purgeCachedData();
517         //cc.TextureCache.getInstance().removeUnusedTextures();
518     },
519 
520     /**
521      * purge Director
522      */
523     purgeDirector:function () {
524         //cleanup scheduler
525         this.getScheduler().unscheduleAllCallbacks();
526 
527         // don't release the event handlers
528         // They are needed in case the director is run again
529         this._touchDispatcher.removeAllDelegates();
530 
531         if (this._runningScene) {
532             this._runningScene.onExitTransitionDidStart();
533             this._runningScene.onExit();
534             this._runningScene.cleanup();
535         }
536 
537         this._runningScene = null;
538         this._nextScene = null;
539 
540         // remove all objects, but don't release it.
541         // runWithScene might be executed after 'end'.
542         this._scenesStack = [];
543 
544         this.stopAnimation();
545 
546         // purge bitmap cache
547         cc.LabelBMFont.purgeCachedData();
548 
549         // purge all managers
550         cc.AnimationCache.purgeSharedAnimationCache();
551         cc.SpriteFrameCache.purgeSharedSpriteFrameCache();
552         cc.TextureCache.purgeSharedTextureCache();
553 
554         //CCShaderCache::purgeSharedShaderCache();
555         //CCFileUtils::purgeFileUtils();
556         //CCConfiguration::purgeConfiguration();
557         //extension::CCNotificationCenter::purgeNotificationCenter();
558         //extension::CCTextureWatcher::purgeTextureWatcher();
559         //extension::CCNodeLoaderLibrary::purgeSharedCCNodeLoaderLibrary();
560         //cc.UserDefault.purgeSharedUserDefault();
561         //ccGLInvalidateStateCache();
562 
563         cc.CHECK_GL_ERROR_DEBUG();
564 
565         // OpenGL view
566         //this._openGLView.end();
567         //this._openGLView = null;
568     },
569 
570     /**
571      * <p>
572      *    Suspends the execution of the running scene, pushing it on the stack of suspended scenes.<br/>
573      *    The new scene will be executed.<br/>
574      *    Try to avoid big stacks of pushed scenes to reduce memory allocation.<br/>
575      *    ONLY call it if there is a running scene.
576      * </p>
577      * @param {cc.Scene} scene
578      */
579     pushScene:function (scene) {
580         cc.Assert(scene, "the scene should not null");
581 
582         this._sendCleanupToScene = false;
583 
584         this._scenesStack.push(scene);
585         this._nextScene = scene;
586     },
587 
588     /**
589      * Replaces the running scene with a new one. The running scene is terminated. ONLY call it if there is a running scene.
590      * @param {cc.Scene} scene
591      */
592     replaceScene:function (scene) {
593         cc.Assert(this._runningScene, "Use runWithScene: instead to start the director");
594         cc.Assert(scene != null, "the scene should not be null");
595 
596         var i = this._scenesStack.length;
597         if(i === 0){
598             this._sendCleanupToScene = true;
599             this._scenesStack[i] = scene;
600             this._nextScene = scene;
601         } else {
602             this._sendCleanupToScene = true;
603             this._scenesStack[i - 1] = scene;
604             this._nextScene = scene;
605         }
606     },
607 
608     /**
609      * resume director
610      */
611     resume:function () {
612         if (!this._paused) {
613             return;
614         }
615 
616         this.setAnimationInterval(this._oldAnimationInterval);
617         this._lastUpdate = Date.now();
618         if (!this._lastUpdate) {
619             cc.log("cocos2d: Director: Error in gettimeofday");
620         }
621 
622         this._paused = false;
623         this._deltaTime = 0;
624     },
625 
626     /**
627      * <p>
628      *    Enters the Director's main loop with the given Scene.<br/>
629      *    Call it to run only your FIRST scene.<br/>
630      *    Don't call it if there is already a running scene.
631      * </p>
632      * @param {cc.Scene} scene
633      */
634     runWithScene:function (scene) {
635         cc.Assert(scene != null, "This command can only be used to start the CCDirector. There is already a scene present.");
636         cc.Assert(this._runningScene == null, "_runningScene should be null");
637 
638         this.pushScene(scene);
639         this.startAnimation();
640     },
641 
642     /**
643      * enables/disables OpenGL alpha blending
644      * @param {Boolean} on
645      */
646     setAlphaBlending:function (on) {
647         if (on)
648             cc.glBlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
649         else
650             cc.glBlendFunc(cc.renderContext.ONE, cc.renderContext.ZERO);
651 
652         //cc.CHECK_GL_ERROR_DEBUG();
653     },
654 
655     /**
656      * <p>
657      *   The size in pixels of the surface. It could be different than the screen size.<br/>
658      *   High-res devices might have a higher surface size than the screen size.<br/>
659      *   Only available when compiled using SDK >= 4.0.
660      * </p>
661      * @param {Number} scaleFactor
662      */
663     setContentScaleFactor:function (scaleFactor) {
664         if (scaleFactor != this._contentScaleFactor) {
665             this._contentScaleFactor = scaleFactor;
666             this._createStatsLabel();
667         }
668     },
669 
670     /**
671      * enables/disables OpenGL depth test
672      * @param {Boolean} on
673      */
674     setDepthTest:function (on) {
675         if(cc.renderContextType === cc.CANVAS)
676             return;
677 
678         if (on) {
679             cc.renderContext.clearDepth(1.0);
680             cc.renderContext.enable(cc.renderContext.DEPTH_TEST);
681             cc.renderContext.depthFunc(cc.renderContext.LEQUAL);
682             //cc.renderContext.hint(cc.renderContext.PERSPECTIVE_CORRECTION_HINT, cc.renderContext.NICEST);
683         } else {
684             cc.renderContext.disable(cc.renderContext.DEPTH_TEST);
685         }
686         //cc.CHECK_GL_ERROR_DEBUG();
687     },
688 
689     /**
690      * sets the default values based on the CCConfiguration info
691      */
692     setDefaultValues:function(){
693 
694     },
695 
696     /**
697      * sets the OpenGL default values
698      */
699     setGLDefaultValues:function () {
700         this.setAlphaBlending(true);
701         // XXX: Fix me, should enable/disable depth test according the depth format as cocos2d-iphone did
702         // [self setDepthTest: view_.depthFormat];
703         this.setDepthTest(false);
704         this.setProjection(this._projection);
705 
706         // set other opengl default values
707         cc.renderContext.clearColor(0.0, 0.0, 0.0, 1.0);
708     },
709 
710     /**
711      * set next delta time is zero
712      * @param {Boolean} nextDeltaTimeZero
713      */
714     setNextDeltaTimeZero:function (nextDeltaTimeZero) {
715         this._nextDeltaTimeZero = nextDeltaTimeZero;
716     },
717 
718     /**
719      * set next scene
720      */
721     setNextScene:function () {
722         var runningIsTransition = this._runningScene ? this._runningScene instanceof cc.TransitionScene : false;
723 
724         var newIsTransition = this._nextScene ? this._nextScene instanceof cc.TransitionScene : false;
725 
726         // If it is not a transition, call onExit/cleanup
727         if (!newIsTransition) {
728             if (this._runningScene) {
729                 this._runningScene.onExitTransitionDidStart();
730                 this._runningScene.onExit();
731             }
732 
733             // issue #709. the root node (scene) should receive the cleanup message too
734             // otherwise it might be leaked.
735             if (this._sendCleanupToScene && this._runningScene)
736                 this._runningScene.cleanup();
737         }
738 
739         this._runningScene = this._nextScene;
740 
741         this._nextScene = null;
742         if ((!runningIsTransition) && (this._runningScene != null)) {
743             this._runningScene.onEnter();
744             this._runningScene.onEnterTransitionDidFinish();
745         }
746     },
747 
748     /**
749      * set Notification Node
750      * @param {cc.Node} node
751      */
752     setNotificationNode:function (node) {
753         this._notificationNode = node;
754     },
755 
756     /**
757      *  CCDirector delegate. It shall implemente the CCDirectorDelegate protocol
758      *  @return {cc.DirectorDelegate}
759      */
760     getDelegate:function () {
761         return this._projectionDelegate;
762     },
763 
764     setDelegate:function (delegate) {
765         this._projectionDelegate = delegate;
766     },
767 
768     /**
769      * Set the CCEGLView, where everything is rendered
770      * @param {*} openGLView
771      */
772     setOpenGLView:function (openGLView) {
773         // set size
774         this._winSizeInPoints = cc.size(cc.canvas.width, cc.canvas.height);        //this._openGLView.getDesignResolutionSize();
775         this._openGLView = openGLView || cc.EGLView.getInstance();
776 
777         if (cc.renderContextType === cc.CANVAS)
778             return;
779 
780         // Configuration. Gather GPU info
781         var conf = cc.Configuration.getInstance();
782         conf.gatherGPUInfo();
783         conf.dumpInfo();
784 
785             // set size
786             //this._winSizeInPoints = this._openGLView.getDesignResolutionSize();
787             //this._winSizeInPixels = cc.size(this._winSizeInPoints.width * this._contentScaleFactor, this._winSizeInPoints.height * this._contentScaleFactor);
788 
789         //if (this._openGLView != openGLView) {
790         // because EAGLView is not kind of CCObject
791 
792         this._createStatsLabel();
793 
794         //if (this._openGLView)
795         this.setGLDefaultValues();
796 
797             /* if (this._contentScaleFactor != 1) {
798              this.updateContentScaleFactor();
799              }*/
800 
801         this._touchDispatcher.setDispatchEvents(true);
802         //}
803     },
804 
805     /**
806      * Sets the glViewport
807      */
808     setViewport:function(){
809         if(this._openGLView) {
810             var locWinSizeInPoints = this._winSizeInPoints;
811             this._openGLView.setViewPortInPoints(0,0, locWinSizeInPoints.width, locWinSizeInPoints.height);
812         }
813     },
814 
815     /**
816      * Sets an OpenGL projection
817      * @param {Number} projection
818      */
819     setProjection:function (projection) {
820         var size = this._winSizeInPoints;
821 
822         if(cc.renderContextType === cc.WEBGL){
823             this.setViewport();
824 
825             switch (projection) {
826                 case cc.DIRECTOR_PROJECTION_2D:
827                     cc.kmGLMatrixMode(cc.KM_GL_PROJECTION);
828                     cc.kmGLLoadIdentity();
829                     var orthoMatrix = new cc.kmMat4();
830                     cc.kmMat4OrthographicProjection(orthoMatrix, 0, size.width, 0, size.height, -1024, 1024);
831                     cc.kmGLMultMatrix(orthoMatrix);
832                     cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW);
833                     cc.kmGLLoadIdentity();
834                     break;
835                 case cc.DIRECTOR_PROJECTION_3D:
836                     var zeye = this.getZEye();
837                     var matrixPerspective = new cc.kmMat4(), matrixLookup = new cc.kmMat4();
838                     cc.kmGLMatrixMode(cc.KM_GL_PROJECTION);
839                     cc.kmGLLoadIdentity();
840 
841                     // issue #1334
842                     cc.kmMat4PerspectiveProjection(matrixPerspective, 60, size.width / size.height, 0.1, zeye * 2);
843                     // kmMat4PerspectiveProjection( &matrixPerspective, 60, (GLfloat)size.width/size.height, 0.1f, 1500);
844 
845                     cc.kmGLMultMatrix(matrixPerspective);
846 
847                     cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW);
848                     cc.kmGLLoadIdentity();
849                     var eye = cc.kmVec3Fill(null, size.width / 2, size.height / 2, zeye);
850                     var center = cc.kmVec3Fill(null, size.width / 2, size.height / 2, 0.0);
851                     var up = cc.kmVec3Fill(null, 0.0, 1.0, 0.0);
852                     cc.kmMat4LookAt(matrixLookup, eye, center, up);
853                     cc.kmGLMultMatrix(matrixLookup);
854                     break;
855                 case cc.DIRECTOR_PROJECTION_CUSTOM:
856                     if (this._projectionDelegate)
857                         this._projectionDelegate.updateProjection();
858                     break;
859                 default:
860                     cc.log("cocos2d: Director: unrecognized projection");
861                     break;
862             }
863         }
864         this._projection = projection;
865         cc.setProjectionMatrixDirty();
866     },
867 
868     /**
869      * shows the FPS in the screen
870      */
871     _showStats:function () {
872         this._frames++;
873         this._accumDt += this._deltaTime;
874         if (this._displayStats) {
875             if (this._FPSLabel && this._SPFLabel && this._drawsLabel) {
876                 if (this._accumDt > cc.DIRECTOR_FPS_INTERVAL) {
877                     this._SPFLabel.setString(this._secondsPerFrame.toFixed(3));
878 
879                     this._frameRate = this._frames / this._accumDt;
880                     this._frames = 0;
881                     this._accumDt = 0;
882 
883                     this._FPSLabel.setString(this._frameRate.toFixed(1));
884                     this._drawsLabel.setString((0 | cc.g_NumberOfDraws).toString());
885                 }
886                 this._FPSLabel.visit();
887                 this._SPFLabel.visit();
888                 this._drawsLabel.visit();
889             } else {
890                 this._createStatsLabel();
891             }
892         }
893         cc.g_NumberOfDraws = 0;
894     },
895 
896     /**
897      * <p>
898      *    Whether or not the replaced scene will receive the cleanup message.<br>
899      *    If the new scene is pushed, then the old scene won't receive the "cleanup" message.<br/>
900      *    If the new scene replaces the old one, the it will receive the "cleanup" message.
901      * </p>
902      * @return {Boolean}
903      */
904     isSendCleanupToScene:function () {
905         return this._sendCleanupToScene;
906     },
907 
908     /**
909      * Get current running Scene. Director can only run one Scene at the time
910      * @return {cc.Scene}
911      */
912     getRunningScene:function () {
913         return this._runningScene;
914     },
915 
916     /**
917      * Get the FPS value
918      * @return {Number}
919      */
920     getAnimationInterval:function () {
921         return this._animationInterval;
922     },
923 
924     /**
925      * Whether or not to display the FPS on the bottom-left corner
926      * @return {Boolean}
927      */
928     isDisplayStats:function () {
929         return this._displayStats;
930     },
931 
932     /**
933      * Display the FPS on the bottom-left corner
934      * @param {Boolean} displayStats
935      */
936     setDisplayStats:function (displayStats) {
937         this._displayStats = displayStats;
938     },
939 
940     /**
941      * seconds per frame
942      */
943     getSecondsPerFrame:function () {
944         return this._secondsPerFrame;
945     },
946 
947     /**
948      *  Get the CCEGLView, where everything is rendered
949      * @return {*}
950      */
951     getOpenGLView:function () {
952         return this._openGLView;
953     },
954 
955     /**
956      * is next delta time zero
957      * @return {Boolean}
958      */
959     isNextDeltaTimeZero:function () {
960         return this._nextDeltaTimeZero;
961     },
962 
963     /**
964      * Whether or not the Director is paused
965      * @return {Boolean}
966      */
967     isPaused:function () {
968         return this._paused;
969     },
970 
971     /**
972      * How many frames were called since the director started
973      * @return {Number}
974      */
975     getTotalFrames:function () {
976         return this._totalFrames;
977     },
978 
979     /**
980      * Sets an OpenGL projection
981      * @return {Number}
982      */
983     getProjection:function () {
984         return this._projection;
985     },
986 
987     /**
988      * <p>
989      *     Pops out all scenes from the queue until the root scene in the queue. <br/>
990      *     This scene will replace the running one.  <br/>
991      *     Internally it will call `popToSceneStackLevel(1)`
992      * </p>
993      */
994     popToRootScene:function () {
995         this.popToSceneStackLevel(1);
996     },
997 
998     /**
999      * <p>
1000      *     Pops out all scenes from the queue until it reaches `level`.                             <br/>
1001      *     If level is 0, it will end the director.                                                 <br/>
1002      *     If level is 1, it will pop all scenes until it reaches to root scene.                    <br/>
1003      *     If level is <= than the current stack level, it won't do anything.
1004      * </p>
1005      * @param {Number} level
1006      */
1007     popToSceneStackLevel: function (level) {
1008         cc.Assert(this._runningScene != null, "A running Scene is needed");
1009         var locScenesStack = this._scenesStack;
1010         var c = locScenesStack.length;
1011 
1012         if (c == 0) {
1013             this.end();
1014             return;
1015         }
1016         // current level or lower -> nothing
1017         if (level > c)
1018             return;
1019 
1020         // pop stack until reaching desired level
1021         while (c > level) {
1022             var current = locScenesStack.pop();
1023             if (current.isRunning()) {
1024                 current.onExitTransitionDidStart();
1025                 current.onExit();
1026             }
1027             current.cleanup();
1028             c--;
1029         }
1030         this._nextScene = locScenesStack[locScenesStack.length - 1];
1031         this._sendCleanupToScene = false;
1032     },
1033 
1034     /**
1035      * (cc.Scheduler associated with this director)
1036      */
1037     getScheduler:function () {
1038         return this._scheduler;
1039     },
1040 
1041     setScheduler:function (scheduler) {
1042         if (this._scheduler != scheduler) {
1043             this._scheduler = scheduler;
1044         }
1045     },
1046 
1047     getActionManager:function () {
1048         return this._actionManager;
1049     },
1050     setActionManager:function (actionManager) {
1051         if (this._actionManager != actionManager) {
1052             this._actionManager = actionManager;
1053         }
1054     },
1055 
1056     getTouchDispatcher:function () {
1057         return this._touchDispatcher;
1058     },
1059     setTouchDispatcher:function (touchDispatcher) {
1060         if (this._touchDispatcher != touchDispatcher) {
1061             this._touchDispatcher = touchDispatcher;
1062         }
1063     },
1064 
1065     getKeyboardDispatcher:function () {
1066         return this._keyboardDispatcher;
1067     },
1068     setKeyboardDispatcher:function (keyboardDispatcher) {
1069         this._keyboardDispatcher = keyboardDispatcher;
1070     },
1071 
1072     getAccelerometer:function () {
1073         return this._accelerometer;
1074     },
1075     setAccelerometer:function (accelerometer) {
1076         if (this._accelerometer != accelerometer) {
1077             this._accelerometer = accelerometer;
1078         }
1079     },
1080 
1081     getDeltaTime:function(){
1082         return this._deltaTime;
1083     },
1084 
1085     getMouseDispatcher:function () {
1086         return this._mouseDispatcher;
1087     },
1088 
1089     setMouseDispatcher:function (mouseDispatcher) {
1090         if (this._mouseDispatcher != mouseDispatcher)
1091             this._mouseDispatcher = mouseDispatcher;
1092     },
1093 
1094     _createStatsLabel: null,
1095 
1096     _createStatsLabelForWebGL:function(){
1097         if((cc.Director._fpsImageLoaded == null) || (cc.Director._fpsImageLoaded == false))
1098             return;
1099 
1100         var texture = new cc.Texture2D();
1101         texture.initWithElement(cc.Director._fpsImage);
1102         texture.handleLoadedTexture();
1103 
1104         /*
1105          We want to use an image which is stored in the file named ccFPSImage.c
1106          for any design resolutions and all resource resolutions.
1107 
1108          To achieve this,
1109 
1110          Firstly, we need to ignore 'contentScaleFactor' in 'CCAtlasNode' and 'CCLabelAtlas'.
1111          So I added a new method called 'setIgnoreContentScaleFactor' for 'CCAtlasNode',
1112          this is not exposed to game developers, it's only used for displaying FPS now.
1113 
1114          Secondly, the size of this image is 480*320, to display the FPS label with correct size,
1115          a factor of design resolution ratio of 480x320 is also needed.
1116          */
1117         var factor = cc.EGLView.getInstance().getDesignResolutionSize().height / 320.0;
1118         if(factor === 0)
1119             factor = this._winSizeInPoints.height / 320.0;
1120 
1121         var tmpLabel = new cc.LabelAtlas();
1122         tmpLabel._setIgnoreContentScaleFactor(true);
1123         tmpLabel.initWithString("00.0", texture, 12, 32 , '.');
1124         tmpLabel.setScale(factor);
1125         this._FPSLabel = tmpLabel;
1126 
1127         tmpLabel = new cc.LabelAtlas();
1128         tmpLabel._setIgnoreContentScaleFactor(true);
1129         tmpLabel.initWithString("0.000", texture, 12, 32, '.');
1130         tmpLabel.setScale(factor);
1131         this._SPFLabel = tmpLabel;
1132 
1133         tmpLabel = new cc.LabelAtlas();
1134         tmpLabel._setIgnoreContentScaleFactor(true);
1135         tmpLabel.initWithString("000", texture, 12, 32, '.');
1136         tmpLabel.setScale(factor);
1137         this._drawsLabel = tmpLabel;
1138 
1139         var locStatsPosition = cc.DIRECTOR_STATS_POSITION;
1140         this._drawsLabel.setPosition(cc.pAdd(cc.p(0, 34 * factor), locStatsPosition));
1141         this._SPFLabel.setPosition(cc.pAdd(cc.p(0, 17 * factor), locStatsPosition));
1142         this._FPSLabel.setPosition(locStatsPosition);
1143     },
1144 
1145     _createStatsLabelForCanvas:function(){
1146         var fontSize = 0;
1147         if (this._winSizeInPoints.width > this._winSizeInPoints.height)
1148             fontSize = 0 | (this._winSizeInPoints.height / 320 * 24);
1149         else
1150             fontSize = 0 | (this._winSizeInPoints.width / 320 * 24);
1151 
1152         this._FPSLabel = cc.LabelTTF.create("000.0", "Arial", fontSize);
1153         this._SPFLabel = cc.LabelTTF.create("0.000", "Arial", fontSize);
1154         this._drawsLabel = cc.LabelTTF.create("0000", "Arial", fontSize);
1155 
1156         var locStatsPosition = cc.DIRECTOR_STATS_POSITION;
1157         var contentSize = this._drawsLabel.getContentSize();
1158         this._drawsLabel.setPosition(cc.pAdd(cc.p(contentSize.width / 2, contentSize.height * 5 / 2), locStatsPosition));
1159         contentSize = this._SPFLabel.getContentSize();
1160         this._SPFLabel.setPosition(cc.pAdd(cc.p(contentSize.width / 2, contentSize.height * 3 / 2), locStatsPosition));
1161         contentSize = this._FPSLabel.getContentSize();
1162         this._FPSLabel.setPosition(cc.pAdd(cc.p(contentSize.width / 2, contentSize.height / 2), locStatsPosition));
1163     },
1164 
1165     _calculateMPF: function () {
1166         var now = Date.now();
1167         this._secondsPerFrame = (now - this._lastUpdate) / 1000;
1168     }
1169 });
1170 
1171 if (cc.Browser.supportWebGL) {
1172     cc.Director.prototype.drawScene = cc.Director.prototype._drawSceneForWebGL;
1173     cc.Director.prototype._createStatsLabel = cc.Director.prototype._createStatsLabelForWebGL;
1174 } else {
1175     cc.Director.prototype.drawScene = cc.Director.prototype._drawSceneForCanvas;
1176     cc.Director.prototype._createStatsLabel = cc.Director.prototype._createStatsLabelForCanvas;
1177 }
1178 
1179 /***************************************************
1180  * implementation of DisplayLinkDirector
1181  **************************************************/
1182 // should we afford 4 types of director ??
1183 // I think DisplayLinkDirector is enough
1184 // so we now only support DisplayLinkDirector
1185 /**
1186  * <p>
1187  *   DisplayLinkDirector is a Director that synchronizes timers with the refresh rate of the display.<br/>
1188  *   Features and Limitations:<br/>
1189  *      - Scheduled timers & drawing are synchronizes with the refresh rate of the display<br/>
1190  *      - Only supports animation intervals of 1/60 1/30 & 1/15<br/>
1191  * </p>
1192  * @class
1193  * @extends cc.Director
1194  */
1195 cc.DisplayLinkDirector = cc.Director.extend(/** @lends cc.DisplayLinkDirector# */{
1196     invalid:false,
1197 
1198     /**
1199      * start Animation
1200      */
1201     startAnimation:function () {
1202         this._nextDeltaTimeZero = true;
1203         this.invalid = false;
1204         cc.Application.getInstance().setAnimationInterval(this._animationInterval);
1205     },
1206 
1207     /**
1208      * main loop of director
1209      */
1210     mainLoop:function () {
1211         if (this._purgeDirecotorInNextLoop) {
1212             this._purgeDirecotorInNextLoop = false;
1213             this.purgeDirector();
1214         }
1215         else if (!this.invalid) {
1216             this.drawScene();
1217         }
1218     },
1219 
1220     /**
1221      * stop animation
1222      */
1223     stopAnimation:function () {
1224         this.invalid = true;
1225     },
1226 
1227     /**
1228      * set Animation Interval
1229      * @param {Number} value
1230      */
1231     setAnimationInterval:function (value) {
1232         this._animationInterval = value;
1233         if (!this.invalid) {
1234             this.stopAnimation();
1235             this.startAnimation();
1236         }
1237     }
1238 });
1239 
1240 cc.s_SharedDirector = null;
1241 
1242 cc.firstUseDirector = true;
1243 
1244 /**
1245  * returns a shared instance of the director
1246  * @function
1247  * @return {cc.Director}
1248  */
1249 cc.Director.getInstance = function () {
1250     if (cc.firstUseDirector) {
1251         cc.firstUseDirector = false;
1252         cc.s_SharedDirector = new cc.DisplayLinkDirector();
1253         cc.s_SharedDirector.init();
1254         cc.s_SharedDirector.setOpenGLView(cc.EGLView.getInstance());
1255     }
1256     return cc.s_SharedDirector;
1257 };
1258 
1259 /**
1260  * is director first run
1261  * @type Boolean
1262  */
1263 cc.firstRun = true;
1264 
1265 /**
1266  * set default fps to 60
1267  * @type Number
1268  */
1269 cc.defaultFPS = 60;
1270 
1271 /*
1272  window.onfocus = function () {
1273  if (!cc.firstRun) {
1274  cc.Director.getInstance().addRegionToDirtyRegion(cc.rect(0, 0, cc.canvas.width, cc.canvas.height));
1275  }
1276  };
1277  */
1278 cc.Director._fpsImage = new Image();
1279 cc.Director._fpsImage.addEventListener("load", function () {
1280     cc.Director._fpsImageLoaded = true;
1281     this.removeEventListener('load', arguments.callee, false);
1282 });
1283 cc.Director._fpsImage.src = "data:image/png;base64,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";
1284 
1285 
1286