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