/libs/cocos2d/CCDirector.m
Objective C | 1231 lines | 848 code | 247 blank | 136 comment | 99 complexity | 20723224fb727a6a029fd0ce3d842d12 MD5 | raw file
Possible License(s): Apache-2.0
1/* 2 * cocos2d for iPhone: http://www.cocos2d-iphone.org 3 * 4 * Copyright (c) 2008-2010 Ricardo Quesada 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 26/* Idea of decoupling Window from Director taken from OC3D project: http://code.google.com/p/oc3d/ 27 */ 28 29#import <unistd.h> 30 31// cocos2d imports 32#import "CCDirector.h" 33#import "CCTouchDelegateProtocol.h" 34#import "CCCamera.h" 35#import "CCScheduler.h" 36#import "CCActionManager.h" 37#import "CCTextureCache.h" 38#import "CCLabelAtlas.h" 39#import "ccMacros.h" 40#import "ccExceptions.h" 41#import "CCTransition.h" 42#import "CCScene.h" 43#import "CCTouchDispatcher.h" 44#import "CCSpriteFrameCache.h" 45#import "CCTexture2D.h" 46#import "CCBitmapFontAtlas.h" 47 48// support imports 49#import "Support/glu.h" 50#import "Support/OpenGL_Internal.h" 51#import "Support/CGPointExtension.h" 52 53#import "CCLayer.h" 54 55#if CC_ENABLE_PROFILERS 56#import "Support/CCProfiling.h" 57#endif 58 59#define kDefaultFPS 60.0 // 60 frames per second 60 61extern NSString * cocos2dVersion(void); 62 63 64@interface CCDirector (Private) 65-(BOOL)isOpenGLAttached; 66-(BOOL)initOpenGLViewWithView:(UIView *)view withFrame:(CGRect)rect; 67 68-(void) preMainLoop; 69-(void) setNextScene; 70// shows the FPS in the screen 71-(void) showFPS; 72// calculates delta time since last time it was called 73-(void) calculateDeltaTime; 74-(void) updateContentScaleFactor; 75 76#if CC_ENABLE_PROFILERS 77- (void) showProfilers; 78#endif 79 80@end 81 82@implementation CCDirector 83 84@synthesize animationInterval=animationInterval_; 85@synthesize runningScene = runningScene_; 86@synthesize displayFPS = displayFPS_; 87@synthesize pixelFormat=pixelFormat_; 88@synthesize nextDeltaTimeZero=nextDeltaTimeZero_; 89@synthesize deviceOrientation=deviceOrientation_; 90@synthesize isPaused=isPaused_; 91@synthesize sendCleanupToScene=sendCleanupToScene_; 92 93// 94// singleton stuff 95// 96static CCDirector *_sharedDirector = nil; 97 98+ (CCDirector *)sharedDirector 99{ 100 if (!_sharedDirector) { 101 102 // 103 // Default Director is TimerDirector 104 // 105 if( [ [CCDirector class] isEqual:[self class]] ) 106 _sharedDirector = [[CCTimerDirector alloc] init]; 107 else 108 _sharedDirector = [[self alloc] init]; 109 } 110 111 return _sharedDirector; 112} 113 114+ (void) purgeSharedDirector 115{ 116 [_sharedDirector release]; 117} 118 119+ (BOOL) setDirectorType:(ccDirectorType)type 120{ 121 NSAssert(_sharedDirector==nil, @"A Director was alloced. setDirectorType must be the first call to Director"); 122 123 if( type == CCDirectorTypeDisplayLink ) { 124 NSString *reqSysVer = @"3.1"; 125 NSString *currSysVer = [[UIDevice currentDevice] systemVersion]; 126 127 if([currSysVer compare:reqSysVer options:NSNumericSearch] == NSOrderedAscending) 128 return NO; 129 } 130 switch (type) { 131 case CCDirectorTypeNSTimer: 132 [CCTimerDirector sharedDirector]; 133 break; 134 case CCDirectorTypeDisplayLink: 135 [CCDisplayLinkDirector sharedDirector]; 136 break; 137 case CCDirectorTypeMainLoop: 138 [CCFastDirector sharedDirector]; 139 break; 140 case CCDirectorTypeThreadMainLoop: 141 [CCThreadedFastDirector sharedDirector]; 142 break; 143 default: 144 NSAssert(NO,@"Unknown director type"); 145 } 146 147 return YES; 148} 149 150+(id)alloc 151{ 152 NSAssert(_sharedDirector == nil, @"Attempted to allocate a second instance of a singleton."); 153 return [super alloc]; 154} 155 156- (id) init 157{ 158 CCLOG(@"cocos2d: %@", cocos2dVersion() ); 159 160 if( (self=[super init]) ) { 161 162 CCLOG(@"cocos2d: Using Director Type:%@", [self class]); 163 164 // default values 165 pixelFormat_ = kCCPixelFormatDefault; 166 depthBufferFormat_ = 0; 167 168 // scenes 169 runningScene_ = nil; 170 nextScene_ = nil; 171 172 oldAnimationInterval_ = animationInterval_ = 1.0 / kDefaultFPS; 173 scenesStack_ = [[NSMutableArray alloc] initWithCapacity:10]; 174 175 // landscape 176 deviceOrientation_ = CCDeviceOrientationPortrait; 177 178 // FPS 179 displayFPS_ = NO; 180 frames_ = 0; 181 182 // paused ? 183 isPaused_ = NO; 184 185 contentScaleFactor_ = 1; 186 screenSize_ = surfaceSize_ = CGSizeZero; 187 isContentScaleSupported_ = NO; 188 } 189 190 return self; 191} 192 193- (void) dealloc 194{ 195 CCLOGINFO(@"cocos2d: deallocing %@", self); 196 197#if CC_DIRECTOR_FAST_FPS 198 [FPSLabel_ release]; 199#endif 200 [runningScene_ release]; 201 [scenesStack_ release]; 202 203 _sharedDirector = nil; 204 205 [super dealloc]; 206} 207 208-(void) setGLDefaultValues 209{ 210 // This method SHOULD be called only after openGLView_ was initialized 211 NSAssert( openGLView_, @"openGLView_ must be initialized"); 212 213 [self setAlphaBlending: YES]; 214 [self setDepthTest: YES]; 215 [self setProjection: kCCDirectorProjectionDefault]; 216 217 // set other opengl default values 218 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 219 220#if CC_DIRECTOR_FAST_FPS 221 if (!FPSLabel_) { 222 CCTexture2DPixelFormat currentFormat = [CCTexture2D defaultAlphaPixelFormat]; 223 [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444]; 224 FPSLabel_ = [[CCLabelAtlas labelAtlasWithString:@"00.0" charMapFile:@"fps_images.png" itemWidth:16 itemHeight:24 startCharMap:'.'] retain]; 225 [CCTexture2D setDefaultAlphaPixelFormat:currentFormat]; 226 } 227#endif // CC_DIRECTOR_FAST_FPS 228} 229 230// 231// Draw the Scene 232// 233- (void) drawScene 234{ 235 /* calculate "global" dt */ 236 [self calculateDeltaTime]; 237 238 /* tick before glClear: issue #533 */ 239 if( ! isPaused_ ) { 240 [[CCScheduler sharedScheduler] tick: dt]; 241 } 242 243 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 244 245 /* to avoid flickr, nextScene MUST be here: after tick and before draw. 246 XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */ 247 if( nextScene_ ) 248 [self setNextScene]; 249 250 glPushMatrix(); 251 252 [self applyOrientation]; 253 254 // By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D 255 CC_ENABLE_DEFAULT_GL_STATES(); 256 257 /* draw the scene */ 258 [runningScene_ visit]; 259 if( displayFPS_ ) 260 [self showFPS]; 261 262#if CC_ENABLE_PROFILERS 263 [self showProfilers]; 264#endif 265 266 CC_DISABLE_DEFAULT_GL_STATES(); 267 268 glPopMatrix(); 269 270 /* swap buffers */ 271 [openGLView_ swapBuffers]; 272} 273 274-(void) calculateDeltaTime 275{ 276 struct timeval now; 277 278 if( gettimeofday( &now, NULL) != 0 ) { 279 CCLOG(@"cocos2d: error in gettimeofday"); 280 dt = 0; 281 return; 282 } 283 284 // new delta time 285 if( nextDeltaTimeZero_ ) { 286 dt = 0; 287 nextDeltaTimeZero_ = NO; 288 } else { 289 dt = (now.tv_sec - lastUpdate_.tv_sec) + (now.tv_usec - lastUpdate_.tv_usec) / 1000000.0f; 290 dt = MAX(0,dt); 291 } 292 293 lastUpdate_ = now; 294} 295 296#pragma mark Director Scene iPhone Specific 297 298-(void) setPixelFormat: (tPixelFormat) format 299{ 300 NSAssert( ! [self isOpenGLAttached], @"Can't change the pixel format after the director was initialized" ); 301 pixelFormat_ = format; 302} 303 304-(void) setDepthBufferFormat: (tDepthBufferFormat) format 305{ 306 NSAssert( ! [self isOpenGLAttached], @"Can't change the depth buffer format after the director was initialized"); 307 depthBufferFormat_ = format; 308} 309 310#pragma mark Director - Memory Helper 311 312-(void) purgeCachedData 313{ 314 [CCBitmapFontAtlas purgeCachedData]; 315 [CCTextureCache purgeSharedTextureCache]; 316} 317 318#pragma mark Director - Scene OpenGL Helper 319 320-(ccDirectorProjection) projection 321{ 322 return projection_; 323} 324 325-(float) getZEye 326{ 327 return ( surfaceSize_.height / 1.1566f ); 328} 329 330-(void) setProjection:(ccDirectorProjection)projection 331{ 332 CGSize size = surfaceSize_; 333 switch (projection) { 334 case kCCDirectorProjection2D: 335 glViewport(0, 0, size.width, size.height); 336 glMatrixMode(GL_PROJECTION); 337 glLoadIdentity(); 338 glOrthof(0, size.width, 0, size.height, -1024, 1024); 339 glMatrixMode(GL_MODELVIEW); 340 glLoadIdentity(); 341 break; 342 343 case kCCDirectorProjection3D: 344 glViewport(0, 0, size.width, size.height); 345 glMatrixMode(GL_PROJECTION); 346 glLoadIdentity(); 347 gluPerspective(60, (GLfloat)size.width/size.height, 0.5f, 1500.0f); 348 349 glMatrixMode(GL_MODELVIEW); 350 glLoadIdentity(); 351 gluLookAt( size.width/2, size.height/2, [self getZEye], 352 size.width/2, size.height/2, 0, 353 0.0f, 1.0f, 0.0f); 354 break; 355 356 case kCCDirectorProjectionCustom: 357 // if custom, ignore it. The user is resposible for setting the correct projection 358 break; 359 360 default: 361 CCLOG(@"cocos2d: Director: unrecognized projecgtion"); 362 break; 363 } 364 365 projection_ = projection; 366} 367 368- (void) setAlphaBlending: (BOOL) on 369{ 370 if (on) { 371 glEnable(GL_BLEND); 372 glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); 373 374 } else 375 glDisable(GL_BLEND); 376} 377 378- (void) setDepthTest: (BOOL) on 379{ 380 if (on) { 381 glClearDepthf(1.0f); 382 glEnable(GL_DEPTH_TEST); 383 glDepthFunc(GL_LEQUAL); 384 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); 385 } else 386 glDisable( GL_DEPTH_TEST ); 387} 388 389#pragma mark Director Integration with a UIKit view 390 391// is the view currently attached 392-(BOOL)isOpenGLAttached 393{ 394 return ([openGLView_ superview]!=nil); 395} 396 397// detach or attach to a view or a window 398-(BOOL)detach 399{ 400 NSAssert([self isOpenGLAttached], @"FATAL: Director: Can't detach the OpenGL View, because it is not attached. Attach it first."); 401 402 // remove from the superview 403 [openGLView_ removeFromSuperview]; 404 405 NSAssert(![self isOpenGLAttached], @"FATAL: Director: Can't detach the OpenGL View, it is still attached to the superview."); 406 407 408 return YES; 409} 410 411// XXX: Deprecated method 412-(BOOL)attachInWindow:(UIWindow *)window 413{ 414 if([self initOpenGLViewWithView:window withFrame:[window bounds]]) 415 { 416 return YES; 417 } 418 419 return NO; 420} 421 422// XXX: Deprecated method 423-(BOOL)attachInView:(UIView *)view 424{ 425 if([self initOpenGLViewWithView:view withFrame:[view bounds]]) 426 { 427 return YES; 428 } 429 430 return NO; 431} 432 433// XXX: Deprecated method 434-(BOOL)attachInView:(UIView *)view withFrame:(CGRect)frame 435{ 436 if([self initOpenGLViewWithView:view withFrame:frame]) 437 { 438 return YES; 439 } 440 441 return NO; 442} 443 444// XXX: Deprecated method 445-(BOOL)initOpenGLViewWithView:(UIView *)view withFrame:(CGRect)rect 446{ 447 NSAssert( ! [self isOpenGLAttached], @"FATAL: Can't re-attach the OpenGL View, because it is already attached. Detach it first"); 448 449 // check if the view is not initialized 450 if(!openGLView_) 451 { 452 // define the pixel format 453 NSString *pFormat = nil; 454 GLuint depthFormat = 0; 455 456 if(pixelFormat_==kCCPixelFormatRGBA8888) 457 pFormat = kEAGLColorFormatRGBA8; 458 else if(pixelFormat_== kCCPixelFormatRGB565) 459 pFormat = kEAGLColorFormatRGB565; 460 else { 461 CCLOG(@"cocos2d: Director: Unknown pixel format."); 462 } 463 464 if(depthBufferFormat_ == kCCDepthBuffer16) 465 depthFormat = GL_DEPTH_COMPONENT16_OES; 466 else if(depthBufferFormat_ == kCCDepthBuffer24) 467 depthFormat = GL_DEPTH_COMPONENT24_OES; 468 else if(depthBufferFormat_ == kCCDepthBufferNone) 469 depthFormat = 0; 470 else { 471 CCLOG(@"cocos2d: Director: Unknown buffer depth."); 472 } 473 474 // alloc and init the opengl view 475 openGLView_ = [[EAGLView alloc] initWithFrame:rect pixelFormat:pFormat depthFormat:depthFormat preserveBackbuffer:NO]; 476 477 // check if the view was alloced and initialized 478 NSAssert( openGLView_, @"FATAL: Could not alloc and init the OpenGL view. "); 479 480 // opaque by default (faster) 481 openGLView_.opaque = YES; 482 } 483 else 484 { 485 // set the (new) frame of the glview 486 [openGLView_ setFrame:rect]; 487 } 488 489 screenSize_ = rect.size; 490 surfaceSize_ = CGSizeMake(screenSize_.width * contentScaleFactor_, screenSize_.height * contentScaleFactor_); 491 492 493 // set the touch delegate of the glview to self 494 [openGLView_ setTouchDelegate: [CCTouchDispatcher sharedDispatcher]]; 495 496 497 // check if the superview has touchs enabled and enable it in our view 498 if([view isUserInteractionEnabled]) 499 { 500 [openGLView_ setUserInteractionEnabled:YES]; 501 [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: YES]; 502 } 503 else 504 { 505 [openGLView_ setUserInteractionEnabled:NO]; 506 [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: NO]; 507 } 508 509 // check if multi touches are enabled and set them 510 if([view isMultipleTouchEnabled]) 511 { 512 [openGLView_ setMultipleTouchEnabled:YES]; 513 } 514 else 515 { 516 [openGLView_ setMultipleTouchEnabled:NO]; 517 } 518 519 // add the glview to his (new) superview 520 [view addSubview:openGLView_]; 521 522 523 NSAssert( [self isOpenGLAttached], @"FATAL: Director: Could not attach OpenGL view"); 524 525 [self setGLDefaultValues]; 526 return YES; 527} 528 529-(EAGLView*) openGLView 530{ 531 return openGLView_; 532} 533 534-(void) setOpenGLView:(EAGLView *)view 535{ 536 NSAssert( view, @"EAGView must be non-nil"); 537 538 if( view != openGLView_ ) { 539 [openGLView_ release]; 540 openGLView_ = [view retain]; 541 542 // set size 543 screenSize_ = [view bounds].size; 544 surfaceSize_ = CGSizeMake(screenSize_.width * contentScaleFactor_, screenSize_.height *contentScaleFactor_); 545 546 if( contentScaleFactor_ != 1 ) 547 [self updateContentScaleFactor]; 548 549 CCTouchDispatcher *touchDispatcher = [CCTouchDispatcher sharedDispatcher]; 550 [openGLView_ setTouchDelegate: touchDispatcher]; 551 [touchDispatcher setDispatchEvents: YES]; 552 553 554 [self setGLDefaultValues]; 555 } 556} 557 558-(void) updateContentScaleFactor 559{ 560 // Based on code snippet from: http://developer.apple.com/iphone/prerelease/library/snippets/sp2010/sp28.html 561 if ([openGLView_ respondsToSelector:@selector(setContentScaleFactor:)]) 562 { 563 // XXX: To avoid compile warning when using Xcode 3.2.2 564 // Version 1.0 will only support Xcode 3.2.3 or newer 565 typedef void (*CC_CONTENT_SCALE)(id, SEL, float); 566 567 SEL selector = @selector(setContentScaleFactor:); 568 CC_CONTENT_SCALE method = (CC_CONTENT_SCALE) [openGLView_ methodForSelector:selector]; 569 method(openGLView_,selector, contentScaleFactor_); 570 571// [openGLView_ setContentScaleFactor: contentScaleFactor_]; 572 573 isContentScaleSupported_ = YES; 574 } 575 else 576 { 577 CCLOG(@"cocos2d: WARNING: calling setContentScaleFactor on iOS < 4. Using fallback mechanism"); 578 /* on pre-4.0 iOS, use bounds/transform */ 579 openGLView_.bounds = CGRectMake(0, 0, 580 openGLView_.bounds.size.width * contentScaleFactor_, 581 openGLView_.bounds.size.height * contentScaleFactor_); 582 openGLView_.transform = CGAffineTransformScale(openGLView_.transform, 1 / contentScaleFactor_, 1 / contentScaleFactor_); 583 584 isContentScaleSupported_ = NO; 585 } 586} 587 588-(void) recalculateProjectionAndEAGLViewSize 589{ 590 screenSize_ = [openGLView_ bounds].size; 591 surfaceSize_ = CGSizeMake(screenSize_.width * contentScaleFactor_, screenSize_.height *contentScaleFactor_); 592 593 [self setProjection:projection_]; 594} 595 596#pragma mark Director Scene Landscape 597 598-(CGPoint)convertToGL:(CGPoint)uiPoint 599{ 600 CGSize s = screenSize_; 601 float newY = s.height - uiPoint.y; 602 float newX = s.width - uiPoint.x; 603 604 CGPoint ret = CGPointZero; 605 switch ( deviceOrientation_) { 606 case CCDeviceOrientationPortrait: 607 ret = ccp( uiPoint.x, newY ); 608 break; 609 case CCDeviceOrientationPortraitUpsideDown: 610 ret = ccp(newX, uiPoint.y); 611 break; 612 case CCDeviceOrientationLandscapeLeft: 613 ret.x = uiPoint.y; 614 ret.y = uiPoint.x; 615 break; 616 case CCDeviceOrientationLandscapeRight: 617 ret.x = newY; 618 ret.y = newX; 619 break; 620 } 621 622 if( contentScaleFactor_ != 1 && isContentScaleSupported_ ) 623 ret = ccpMult(ret, contentScaleFactor_); 624 return ret; 625} 626 627-(CGPoint)convertToUI:(CGPoint)glPoint 628{ 629 CGSize winSize = surfaceSize_; 630 int oppositeX = winSize.width - glPoint.x; 631 int oppositeY = winSize.height - glPoint.y; 632 CGPoint uiPoint = CGPointZero; 633 switch ( deviceOrientation_) { 634 case CCDeviceOrientationPortrait: 635 uiPoint = ccp(glPoint.x, oppositeY); 636 break; 637 case CCDeviceOrientationPortraitUpsideDown: 638 uiPoint = ccp(oppositeX, glPoint.y); 639 break; 640 case CCDeviceOrientationLandscapeLeft: 641 uiPoint = ccp(glPoint.y, glPoint.x); 642 break; 643 case CCDeviceOrientationLandscapeRight: 644 // Can't use oppositeX/Y because x/y are flipped 645 uiPoint = ccp(winSize.width-glPoint.y, winSize.height-glPoint.x); 646 break; 647 } 648 649 uiPoint = ccpMult(uiPoint, 1/contentScaleFactor_); 650 return uiPoint; 651} 652 653// get the current size of the glview 654-(CGSize)winSize 655{ 656 CGSize s = surfaceSize_; 657 658 if( deviceOrientation_ == CCDeviceOrientationLandscapeLeft || deviceOrientation_ == CCDeviceOrientationLandscapeRight ) { 659 // swap x,y in landscape mode 660 CGSize tmp = s; 661 s.width = tmp.height; 662 s.height = tmp.width; 663 } 664 return s; 665} 666 667// return the current frame size 668-(CGSize)displaySize 669{ 670 return surfaceSize_; 671} 672 673- (void) setDeviceOrientation:(ccDeviceOrientation) orientation 674{ 675 if( deviceOrientation_ != orientation ) { 676 deviceOrientation_ = orientation; 677 switch( deviceOrientation_) { 678 case CCDeviceOrientationPortrait: 679 [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:NO]; 680 break; 681 case CCDeviceOrientationPortraitUpsideDown: 682 [[UIApplication sharedApplication] setStatusBarOrientation: UIDeviceOrientationPortraitUpsideDown animated:NO]; 683 break; 684 case CCDeviceOrientationLandscapeLeft: 685 [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO]; 686 break; 687 case CCDeviceOrientationLandscapeRight: 688 [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft animated:NO]; 689 break; 690 default: 691 NSLog(@"Director: Unknown device orientation"); 692 break; 693 } 694 } 695} 696 697-(void) applyOrientation 698{ 699 CGSize s = surfaceSize_; 700 float w = s.width / 2; 701 float h = s.height / 2; 702 703 // XXX it's using hardcoded values. 704 // What if the the screen size changes in the future? 705 switch ( deviceOrientation_ ) { 706 case CCDeviceOrientationPortrait: 707 // nothing 708 break; 709 case CCDeviceOrientationPortraitUpsideDown: 710 // upside down 711 glTranslatef(w,h,0); 712 glRotatef(180,0,0,1); 713 glTranslatef(-w,-h,0); 714 break; 715 case CCDeviceOrientationLandscapeRight: 716 glTranslatef(w,h,0); 717 glRotatef(90,0,0,1); 718 glTranslatef(-h,-w,0); 719 break; 720 case CCDeviceOrientationLandscapeLeft: 721 glTranslatef(w,h,0); 722 glRotatef(-90,0,0,1); 723 glTranslatef(-h,-w,0); 724 break; 725 } 726} 727 728#pragma mark Director Scene Management 729 730- (void)runWithScene:(CCScene*) scene 731{ 732 NSAssert( scene != nil, @"Argument must be non-nil"); 733 NSAssert( runningScene_ == nil, @"You can't run an scene if another Scene is running. Use replaceScene or pushScene instead"); 734 735 [self pushScene:scene]; 736 [self startAnimation]; 737} 738 739-(void) replaceScene: (CCScene*) scene 740{ 741 NSAssert( scene != nil, @"Argument must be non-nil"); 742 743 NSUInteger index = [scenesStack_ count]; 744 745 sendCleanupToScene_ = YES; 746 [scenesStack_ replaceObjectAtIndex:index-1 withObject:scene]; 747 nextScene_ = scene; // nextScene_ is a weak ref 748} 749 750- (void) pushScene: (CCScene*) scene 751{ 752 NSAssert( scene != nil, @"Argument must be non-nil"); 753 754 sendCleanupToScene_ = NO; 755 756 [scenesStack_ addObject: scene]; 757 nextScene_ = scene; // nextScene_ is a weak ref 758} 759 760-(void) popScene 761{ 762 NSAssert( runningScene_ != nil, @"A running Scene is needed"); 763 764 [scenesStack_ removeLastObject]; 765 NSUInteger c = [scenesStack_ count]; 766 767 if( c == 0 ) { 768 [self end]; 769 } else { 770 nextScene_ = [scenesStack_ objectAtIndex:c-1]; 771 } 772} 773 774-(void) end 775{ 776 [runningScene_ onExit]; 777 [runningScene_ cleanup]; 778 [runningScene_ release]; 779 780 runningScene_ = nil; 781 nextScene_ = nil; 782 783 // remove all objects, but don't release it. 784 // runWithScene might be executed after 'end'. 785 [scenesStack_ removeAllObjects]; 786 787 // don't release the event handlers 788 // They are needed in case the director is run again 789 [[CCTouchDispatcher sharedDispatcher] removeAllDelegates]; 790 791 [self stopAnimation]; 792 793#if CC_DIRECTOR_FAST_FPS 794 [FPSLabel_ release]; 795 FPSLabel_ = nil; 796#endif 797 798 // Purge bitmap cache 799 [CCBitmapFontAtlas purgeCachedData]; 800 801 // Purge all managers 802 [CCSpriteFrameCache purgeSharedSpriteFrameCache]; 803 [CCScheduler purgeSharedScheduler]; 804 [CCActionManager purgeSharedManager]; 805 [CCTextureCache purgeSharedTextureCache]; 806 807 808 // OpenGL view 809 810 // Since the director doesn't attach the openglview to the window 811 // it shouldn't remove it from the window too. 812// [openGLView_ removeFromSuperview]; 813 814 [openGLView_ release]; 815 openGLView_ = nil; 816} 817 818-(void) setNextScene 819{ 820 Class transClass = [CCTransitionScene class]; 821 BOOL runningIsTransition = [runningScene_ isKindOfClass:transClass]; 822 BOOL newIsTransition = [nextScene_ isKindOfClass:transClass]; 823 824 // If it is not a transition, call onExit/cleanup 825 if( ! newIsTransition ) { 826 [runningScene_ onExit]; 827 828 // issue #709. the root node (scene) should receive the cleanup message too 829 // otherwise it might be leaked. 830 if( sendCleanupToScene_) 831 [runningScene_ cleanup]; 832 } 833 834 [runningScene_ release]; 835 836 runningScene_ = [nextScene_ retain]; 837 nextScene_ = nil; 838 839 if( ! runningIsTransition ) { 840 [runningScene_ onEnter]; 841 [runningScene_ onEnterTransitionDidFinish]; 842 } 843} 844 845-(void) pause 846{ 847 if( isPaused_ ) 848 return; 849 850 oldAnimationInterval_ = animationInterval_; 851 852 // when paused, don't consume CPU 853 [self setAnimationInterval:1/4.0]; 854 isPaused_ = YES; 855} 856 857-(void) resume 858{ 859 if( ! isPaused_ ) 860 return; 861 862 [self setAnimationInterval: oldAnimationInterval_]; 863 864 if( gettimeofday( &lastUpdate_, NULL) != 0 ) { 865 CCLOG(@"cocos2d: Director: Error in gettimeofday"); 866 } 867 868 isPaused_ = NO; 869 dt = 0; 870} 871 872- (void)startAnimation 873{ 874 CCLOG(@"cocos2d: Director#startAnimation. Override me"); 875} 876 877- (void)stopAnimation 878{ 879 CCLOG(@"cocos2d: Director#stopAnimation. Override me"); 880} 881 882- (void)setAnimationInterval:(NSTimeInterval)interval 883{ 884 CCLOG(@"cocos2d: Director#setAnimationInterval. Override me"); 885} 886 887#if CC_DIRECTOR_FAST_FPS 888 889// display the FPS using a LabelAtlas 890// updates the FPS every frame 891-(void) showFPS 892{ 893 frames_++; 894 accumDt_ += dt; 895 896 if ( accumDt_ > CC_DIRECTOR_FPS_INTERVAL) { 897 frameRate_ = frames_/accumDt_; 898 frames_ = 0; 899 accumDt_ = 0; 900 901// sprintf(format,"%.1f",frameRate); 902// [FPSLabel setCString:format]; 903 904 NSString *str = [[NSString alloc] initWithFormat:@"%.1f", frameRate_]; 905 [FPSLabel_ setString:str]; 906 [str release]; 907 } 908 909 [FPSLabel_ draw]; 910} 911#else 912// display the FPS using a manually generated Texture (very slow) 913// updates the FPS 3 times per second aprox. 914-(void) showFPS 915{ 916 frames++; 917 accumDt += dt; 918 919 if ( accumDt > CC_DIRECTOR_FPS_INTERVAL) { 920 frameRate = frames/accumDt; 921 frames = 0; 922 accumDt = 0; 923 } 924 925 NSString *str = [NSString stringWithFormat:@"%.2f",frameRate]; 926 CCTexture2D *texture = [[CCTexture2D alloc] initWithString:str dimensions:CGSizeMake(100,30) alignment:UITextAlignmentLeft fontName:@"Arial" fontSize:24]; 927 928 // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY 929 // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY 930 // Unneeded states: GL_COLOR_ARRAY 931 glDisableClientState(GL_COLOR_ARRAY); 932 933 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 934 935 glColor4ub(224,224,244,200); 936 [texture drawAtPoint: ccp(5,2)]; 937 [texture release]; 938 939 glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); 940 941 // restore default GL state 942 glEnableClientState(GL_COLOR_ARRAY); 943} 944#endif 945 946#if CC_ENABLE_PROFILERS 947- (void) showProfilers { 948 accumDtForProfiler_ += dt; 949 if (accumDtForProfiler_ > 1.0f) { 950 accumDtForProfiler_ = 0; 951 [[CCProfiler sharedProfiler] displayTimers]; 952 } 953} 954#endif 955 956-(CGFloat) contentScaleFactor 957{ 958 return contentScaleFactor_; 959} 960 961-(void) setContentScaleFactor:(CGFloat)scaleFactor 962{ 963 if( scaleFactor != contentScaleFactor_ ) { 964 965 contentScaleFactor_ = scaleFactor; 966 surfaceSize_ = CGSizeMake( screenSize_.width * scaleFactor, screenSize_.height * scaleFactor ); 967 968 if( openGLView_ ) 969 [self updateContentScaleFactor]; 970 971 // update projection 972 [self setProjection:projection_]; 973 } 974} 975 976@end 977 978#pragma mark - 979#pragma mark Director TimerDirector 980 981@implementation CCTimerDirector 982- (void)startAnimation 983{ 984 NSAssert( animationTimer == nil, @"animationTimer must be nil. Calling startAnimation twice?"); 985 986 if( gettimeofday( &lastUpdate_, NULL) != 0 ) { 987 CCLOG(@"cocos2d: Director: Error in gettimeofday"); 988 } 989 990 animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval_ target:self selector:@selector(drawScene) userInfo:nil repeats:YES]; 991 992 // 993 // If you want to attach the opengl view into UIScrollView 994 // uncomment this line to prevent 'freezing'. 995 // It doesn't work on with the Fast Director 996 // 997 // [[NSRunLoop currentRunLoop] addTimer:animationTimer 998 // forMode:NSRunLoopCommonModes]; 999} 1000 1001- (void)stopAnimation 1002{ 1003 [animationTimer invalidate]; 1004 animationTimer = nil; 1005} 1006 1007- (void)setAnimationInterval:(NSTimeInterval)interval 1008{ 1009 animationInterval_ = interval; 1010 1011 if(animationTimer) { 1012 [self stopAnimation]; 1013 [self startAnimation]; 1014 } 1015} 1016 1017-(void) dealloc 1018{ 1019 [animationTimer release]; 1020 [super dealloc]; 1021} 1022@end 1023 1024 1025#pragma mark - 1026#pragma mark Director FastDirector 1027 1028@implementation CCFastDirector 1029 1030- (id) init 1031{ 1032 if(( self = [super init] )) { 1033 1034#if CC_DIRECTOR_DISPATCH_FAST_EVENTS 1035 CCLOG(@"cocos2d: Fast Events enabled"); 1036#else 1037 CCLOG(@"cocos2d: Fast Events disabled"); 1038#endif 1039 isRunning = NO; 1040 1041 // XXX: 1042 // XXX: Don't create any autorelease object before calling "fast director" 1043 // XXX: else it will be leaked 1044 // XXX: 1045 autoreleasePool = [NSAutoreleasePool new]; 1046 } 1047 1048 return self; 1049} 1050 1051- (void) startAnimation 1052{ 1053 // XXX: 1054 // XXX: release autorelease objects created 1055 // XXX: between "use fast director" and "runWithScene" 1056 // XXX: 1057 [autoreleasePool release]; 1058 autoreleasePool = nil; 1059 1060 if ( gettimeofday( &lastUpdate_, NULL) != 0 ) { 1061 CCLOG(@"cocos2d: Director: Error in gettimeofday"); 1062 } 1063 1064 1065 isRunning = YES; 1066 1067 SEL selector = @selector(preMainLoop); 1068 NSMethodSignature* sig = [[[CCDirector sharedDirector] class] 1069 instanceMethodSignatureForSelector:selector]; 1070 NSInvocation* invocation = [NSInvocation 1071 invocationWithMethodSignature:sig]; 1072 [invocation setTarget:[CCDirector sharedDirector]]; 1073 [invocation setSelector:selector]; 1074 [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) 1075 withObject:[CCDirector sharedDirector] waitUntilDone:NO]; 1076 1077// NSInvocationOperation *loopOperation = [[[NSInvocationOperation alloc] 1078// initWithTarget:self selector:@selector(preMainLoop) object:nil] 1079// autorelease]; 1080// 1081// [loopOperation performSelectorOnMainThread:@selector(start) withObject:nil 1082// waitUntilDone:NO]; 1083} 1084 1085-(void) preMainLoop 1086{ 1087 while (isRunning) { 1088 1089 NSAutoreleasePool *loopPool = [NSAutoreleasePool new]; 1090 1091#if CC_DIRECTOR_DISPATCH_FAST_EVENTS 1092 while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource); 1093#else 1094 while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource); 1095#endif 1096 1097 if (isPaused_) { 1098 usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps. 1099 } 1100 1101 [self drawScene]; 1102 1103#if CC_DIRECTOR_DISPATCH_FAST_EVENTS 1104 while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource); 1105#else 1106 while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource); 1107#endif 1108 1109 [loopPool release]; 1110 } 1111} 1112- (void) stopAnimation 1113{ 1114 isRunning = NO; 1115} 1116 1117- (void)setAnimationInterval:(NSTimeInterval)interval 1118{ 1119 NSLog(@"FastDirectory doesn't support setAnimationInterval, yet"); 1120} 1121@end 1122 1123#pragma mark - 1124#pragma mark Director ThreadedFastDirector 1125 1126@implementation CCThreadedFastDirector 1127 1128- (id) init 1129{ 1130 if(( self = [super init] )) { 1131 isRunning = NO; 1132 } 1133 1134 return self; 1135} 1136 1137- (void) startAnimation 1138{ 1139 1140 if ( gettimeofday( &lastUpdate_, NULL) != 0 ) { 1141 CCLOG(@"cocos2d: ThreadedFastDirector: Error on gettimeofday"); 1142 } 1143 1144 isRunning = YES; 1145 1146 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(preMainLoop) object:nil]; 1147 [thread start]; 1148 [thread release]; 1149} 1150 1151-(void) preMainLoop 1152{ 1153 while( ![[NSThread currentThread] isCancelled] ) { 1154 if( isRunning ) 1155 [self performSelectorOnMainThread:@selector(drawScene) withObject:nil waitUntilDone:YES]; 1156 1157 if (isPaused_) { 1158 usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps. 1159 } else { 1160// usleep(2000); 1161 } 1162 } 1163} 1164- (void) stopAnimation 1165{ 1166 isRunning = NO; 1167} 1168 1169- (void)setAnimationInterval:(NSTimeInterval)interval 1170{ 1171 NSLog(@"FastDirector doesn't support setAnimationInterval, yet"); 1172} 1173@end 1174 1175#pragma mark - 1176#pragma mark DisplayLinkDirector 1177 1178// Allows building DisplayLinkDirector for pre-3.1 SDKS 1179// without getting compiler warnings. 1180@interface NSObject(CADisplayLink) 1181+ (id) displayLinkWithTarget:(id)arg1 selector:(SEL)arg2; 1182- (void) addToRunLoop:(id)arg1 forMode:(id)arg2; 1183- (void) setFrameInterval:(int)interval; 1184- (void) invalidate; 1185@end 1186 1187@implementation CCDisplayLinkDirector 1188 1189- (void)setAnimationInterval:(NSTimeInterval)interval 1190{ 1191 animationInterval_ = interval; 1192 if(displayLink){ 1193 [self stopAnimation]; 1194 [self startAnimation]; 1195 } 1196} 1197 1198- (void) startAnimation 1199{ 1200 if ( gettimeofday( &lastUpdate_, NULL) != 0 ) { 1201 CCLOG(@"cocos2d: DisplayLinkDirector: Error on gettimeofday"); 1202 } 1203 1204 // approximate frame rate 1205 // assumes device refreshes at 60 fps 1206 int frameInterval = (int) floor(animationInterval_ * 60.0f); 1207 1208 CCLOG(@"cocos2d: Frame interval: %d", frameInterval); 1209 1210 displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(preMainLoop:)]; 1211 [displayLink setFrameInterval:frameInterval]; 1212 [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 1213} 1214 1215-(void) preMainLoop:(id)sender 1216{ 1217 [self drawScene]; 1218} 1219 1220- (void) stopAnimation 1221{ 1222 [displayLink invalidate]; 1223 displayLink = nil; 1224} 1225 1226-(void) dealloc 1227{ 1228 [displayLink release]; 1229 [super dealloc]; 1230} 1231@end