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