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