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