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 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 cc.size(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.width = cc.canvas.width; //this._openGLView.getDesignResolutionSize(); 775 this._winSizeInPoints.height = 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 }); 1310 cc.Director._fpsImage.src = "data:image/png;base64,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"; 1311 1312 1313