diff options
| author | Starla Insigna <starla4444@gmail.com> | 2011-07-30 11:19:14 -0400 |
|---|---|---|
| committer | Starla Insigna <starla4444@gmail.com> | 2011-07-30 11:19:14 -0400 |
| commit | 9cd57b731ab1c666d4a1cb725538fdc137763d12 (patch) | |
| tree | 5bac45ae5157a1cb10c6e45500cbf72789917980 /libs/cocos2d/CCParticleSystem.m | |
| download | cartcollect-9cd57b731ab1c666d4a1cb725538fdc137763d12.tar.gz cartcollect-9cd57b731ab1c666d4a1cb725538fdc137763d12.tar.bz2 cartcollect-9cd57b731ab1c666d4a1cb725538fdc137763d12.zip | |
Initial commit (version 0.2.1)
Diffstat (limited to 'libs/cocos2d/CCParticleSystem.m')
| -rwxr-xr-x | libs/cocos2d/CCParticleSystem.m | 808 |
1 files changed, 808 insertions, 0 deletions
| diff --git a/libs/cocos2d/CCParticleSystem.m b/libs/cocos2d/CCParticleSystem.m new file mode 100755 index 0000000..742676e --- /dev/null +++ b/libs/cocos2d/CCParticleSystem.m | |||
| @@ -0,0 +1,808 @@ | |||
| 1 | /* | ||
| 2 | * cocos2d for iPhone: http://www.cocos2d-iphone.org | ||
| 3 | * | ||
| 4 | * Copyright (c) 2008-2010 Ricardo Quesada | ||
| 5 | * Copyright (c) 2011 Zynga Inc. | ||
| 6 | * | ||
| 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 8 | * of this software and associated documentation files (the "Software"), to deal | ||
| 9 | * in the Software without restriction, including without limitation the rights | ||
| 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 11 | * copies of the Software, and to permit persons to whom the Software is | ||
| 12 | * furnished to do so, subject to the following conditions: | ||
| 13 | * | ||
| 14 | * The above copyright notice and this permission notice shall be included in | ||
| 15 | * all copies or substantial portions of the Software. | ||
| 16 | * | ||
| 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 23 | * THE SOFTWARE. | ||
| 24 | * | ||
| 25 | */ | ||
| 26 | |||
| 27 | |||
| 28 | // ideas taken from: | ||
| 29 | // . The ocean spray in your face [Jeff Lander] | ||
| 30 | // http://www.double.co.nz/dust/col0798.pdf | ||
| 31 | // . Building an Advanced Particle System [John van der Burg] | ||
| 32 | // http://www.gamasutra.com/features/20000623/vanderburg_01.htm | ||
| 33 | // . LOVE game engine | ||
| 34 | // http://love2d.org/ | ||
| 35 | // | ||
| 36 | // | ||
| 37 | // Radius mode support, from 71 squared | ||
| 38 | // http://particledesigner.71squared.com/ | ||
| 39 | // | ||
| 40 | // IMPORTANT: Particle Designer is supported by cocos2d, but | ||
| 41 | // 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d, | ||
| 42 | // cocos2d uses a another approach, but the results are almost identical. | ||
| 43 | // | ||
| 44 | |||
| 45 | // opengl | ||
| 46 | #import "Platforms/CCGL.h" | ||
| 47 | |||
| 48 | // cocos2d | ||
| 49 | #import "ccConfig.h" | ||
| 50 | #if CC_ENABLE_PROFILERS | ||
| 51 | #import "Support/CCProfiling.h" | ||
| 52 | #endif | ||
| 53 | #import "CCParticleSystem.h" | ||
| 54 | #import "CCTextureCache.h" | ||
| 55 | #import "ccMacros.h" | ||
| 56 | |||
| 57 | // support | ||
| 58 | #import "Support/OpenGL_Internal.h" | ||
| 59 | #import "Support/CGPointExtension.h" | ||
| 60 | #import "Support/base64.h" | ||
| 61 | #import "Support/ZipUtils.h" | ||
| 62 | #import "Support/CCFileUtils.h" | ||
| 63 | |||
| 64 | @implementation CCParticleSystem | ||
| 65 | @synthesize active, duration; | ||
| 66 | @synthesize sourcePosition, posVar; | ||
| 67 | @synthesize particleCount; | ||
| 68 | @synthesize life, lifeVar; | ||
| 69 | @synthesize angle, angleVar; | ||
| 70 | @synthesize startColor, startColorVar, endColor, endColorVar; | ||
| 71 | @synthesize startSpin, startSpinVar, endSpin, endSpinVar; | ||
| 72 | @synthesize emissionRate; | ||
| 73 | @synthesize totalParticles; | ||
| 74 | @synthesize startSize, startSizeVar; | ||
| 75 | @synthesize endSize, endSizeVar; | ||
| 76 | @synthesize blendFunc = blendFunc_; | ||
| 77 | @synthesize positionType = positionType_; | ||
| 78 | @synthesize autoRemoveOnFinish = autoRemoveOnFinish_; | ||
| 79 | @synthesize emitterMode = emitterMode_; | ||
| 80 | |||
| 81 | |||
| 82 | +(id) particleWithFile:(NSString*) plistFile | ||
| 83 | { | ||
| 84 | return [[[self alloc] initWithFile:plistFile] autorelease]; | ||
| 85 | } | ||
| 86 | |||
| 87 | -(id) init { | ||
| 88 | NSAssert(NO, @"CCParticleSystem: Init not supported."); | ||
| 89 | [self release]; | ||
| 90 | return nil; | ||
| 91 | } | ||
| 92 | |||
| 93 | -(id) initWithFile:(NSString *)plistFile | ||
| 94 | { | ||
| 95 | NSString *path = [CCFileUtils fullPathFromRelativePath:plistFile]; | ||
| 96 | NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; | ||
| 97 | |||
| 98 | NSAssert( dict != nil, @"Particles: file not found"); | ||
| 99 | return [self initWithDictionary:dict]; | ||
| 100 | } | ||
| 101 | |||
| 102 | -(id) initWithDictionary:(NSDictionary *)dictionary | ||
| 103 | { | ||
| 104 | NSUInteger maxParticles = [[dictionary valueForKey:@"maxParticles"] intValue]; | ||
| 105 | // self, not super | ||
| 106 | if ((self=[self initWithTotalParticles:maxParticles] ) ) { | ||
| 107 | |||
| 108 | // angle | ||
| 109 | angle = [[dictionary valueForKey:@"angle"] floatValue]; | ||
| 110 | angleVar = [[dictionary valueForKey:@"angleVariance"] floatValue]; | ||
| 111 | |||
| 112 | // duration | ||
| 113 | duration = [[dictionary valueForKey:@"duration"] floatValue]; | ||
| 114 | |||
| 115 | // blend function | ||
| 116 | blendFunc_.src = [[dictionary valueForKey:@"blendFuncSource"] intValue]; | ||
| 117 | blendFunc_.dst = [[dictionary valueForKey:@"blendFuncDestination"] intValue]; | ||
| 118 | |||
| 119 | // color | ||
| 120 | float r,g,b,a; | ||
| 121 | |||
| 122 | r = [[dictionary valueForKey:@"startColorRed"] floatValue]; | ||
| 123 | g = [[dictionary valueForKey:@"startColorGreen"] floatValue]; | ||
| 124 | b = [[dictionary valueForKey:@"startColorBlue"] floatValue]; | ||
| 125 | a = [[dictionary valueForKey:@"startColorAlpha"] floatValue]; | ||
| 126 | startColor = (ccColor4F) {r,g,b,a}; | ||
| 127 | |||
| 128 | r = [[dictionary valueForKey:@"startColorVarianceRed"] floatValue]; | ||
| 129 | g = [[dictionary valueForKey:@"startColorVarianceGreen"] floatValue]; | ||
| 130 | b = [[dictionary valueForKey:@"startColorVarianceBlue"] floatValue]; | ||
| 131 | a = [[dictionary valueForKey:@"startColorVarianceAlpha"] floatValue]; | ||
| 132 | startColorVar = (ccColor4F) {r,g,b,a}; | ||
| 133 | |||
| 134 | r = [[dictionary valueForKey:@"finishColorRed"] floatValue]; | ||
| 135 | g = [[dictionary valueForKey:@"finishColorGreen"] floatValue]; | ||
| 136 | b = [[dictionary valueForKey:@"finishColorBlue"] floatValue]; | ||
| 137 | a = [[dictionary valueForKey:@"finishColorAlpha"] floatValue]; | ||
| 138 | endColor = (ccColor4F) {r,g,b,a}; | ||
| 139 | |||
| 140 | r = [[dictionary valueForKey:@"finishColorVarianceRed"] floatValue]; | ||
| 141 | g = [[dictionary valueForKey:@"finishColorVarianceGreen"] floatValue]; | ||
| 142 | b = [[dictionary valueForKey:@"finishColorVarianceBlue"] floatValue]; | ||
| 143 | a = [[dictionary valueForKey:@"finishColorVarianceAlpha"] floatValue]; | ||
| 144 | endColorVar = (ccColor4F) {r,g,b,a}; | ||
| 145 | |||
| 146 | // particle size | ||
| 147 | startSize = [[dictionary valueForKey:@"startParticleSize"] floatValue]; | ||
| 148 | startSizeVar = [[dictionary valueForKey:@"startParticleSizeVariance"] floatValue]; | ||
| 149 | endSize = [[dictionary valueForKey:@"finishParticleSize"] floatValue]; | ||
| 150 | endSizeVar = [[dictionary valueForKey:@"finishParticleSizeVariance"] floatValue]; | ||
| 151 | |||
| 152 | |||
| 153 | // position | ||
| 154 | float x = [[dictionary valueForKey:@"sourcePositionx"] floatValue]; | ||
| 155 | float y = [[dictionary valueForKey:@"sourcePositiony"] floatValue]; | ||
| 156 | self.position = ccp(x,y); | ||
| 157 | posVar.x = [[dictionary valueForKey:@"sourcePositionVariancex"] floatValue]; | ||
| 158 | posVar.y = [[dictionary valueForKey:@"sourcePositionVariancey"] floatValue]; | ||
| 159 | |||
| 160 | |||
| 161 | // Spinning | ||
| 162 | startSpin = [[dictionary valueForKey:@"rotationStart"] floatValue]; | ||
| 163 | startSpinVar = [[dictionary valueForKey:@"rotationStartVariance"] floatValue]; | ||
| 164 | endSpin = [[dictionary valueForKey:@"rotationEnd"] floatValue]; | ||
| 165 | endSpinVar = [[dictionary valueForKey:@"rotationEndVariance"] floatValue]; | ||
| 166 | |||
| 167 | emitterMode_ = [[dictionary valueForKey:@"emitterType"] intValue]; | ||
| 168 | |||
| 169 | // Mode A: Gravity + tangential accel + radial accel | ||
| 170 | if( emitterMode_ == kCCParticleModeGravity ) { | ||
| 171 | // gravity | ||
| 172 | mode.A.gravity.x = [[dictionary valueForKey:@"gravityx"] floatValue]; | ||
| 173 | mode.A.gravity.y = [[dictionary valueForKey:@"gravityy"] floatValue]; | ||
| 174 | |||
| 175 | // | ||
| 176 | // speed | ||
| 177 | mode.A.speed = [[dictionary valueForKey:@"speed"] floatValue]; | ||
| 178 | mode.A.speedVar = [[dictionary valueForKey:@"speedVariance"] floatValue]; | ||
| 179 | |||
| 180 | // radial acceleration | ||
| 181 | NSString *tmp = [dictionary valueForKey:@"radialAcceleration"]; | ||
| 182 | mode.A.radialAccel = tmp ? [tmp floatValue] : 0; | ||
| 183 | |||
| 184 | tmp = [dictionary valueForKey:@"radialAccelVariance"]; | ||
| 185 | mode.A.radialAccelVar = tmp ? [tmp floatValue] : 0; | ||
| 186 | |||
| 187 | // tangential acceleration | ||
| 188 | tmp = [dictionary valueForKey:@"tangentialAcceleration"]; | ||
| 189 | mode.A.tangentialAccel = tmp ? [tmp floatValue] : 0; | ||
| 190 | |||
| 191 | tmp = [dictionary valueForKey:@"tangentialAccelVariance"]; | ||
| 192 | mode.A.tangentialAccelVar = tmp ? [tmp floatValue] : 0; | ||
| 193 | } | ||
| 194 | |||
| 195 | |||
| 196 | // or Mode B: radius movement | ||
| 197 | else if( emitterMode_ == kCCParticleModeRadius ) { | ||
| 198 | float maxRadius = [[dictionary valueForKey:@"maxRadius"] floatValue]; | ||
| 199 | float maxRadiusVar = [[dictionary valueForKey:@"maxRadiusVariance"] floatValue]; | ||
| 200 | float minRadius = [[dictionary valueForKey:@"minRadius"] floatValue]; | ||
| 201 | |||
| 202 | mode.B.startRadius = maxRadius; | ||
| 203 | mode.B.startRadiusVar = maxRadiusVar; | ||
| 204 | mode.B.endRadius = minRadius; | ||
| 205 | mode.B.endRadiusVar = 0; | ||
| 206 | mode.B.rotatePerSecond = [[dictionary valueForKey:@"rotatePerSecond"] floatValue]; | ||
| 207 | mode.B.rotatePerSecondVar = [[dictionary valueForKey:@"rotatePerSecondVariance"] floatValue]; | ||
| 208 | |||
| 209 | } else { | ||
| 210 | NSAssert( NO, @"Invalid emitterType in config file"); | ||
| 211 | } | ||
| 212 | |||
| 213 | // life span | ||
| 214 | life = [[dictionary valueForKey:@"particleLifespan"] floatValue]; | ||
| 215 | lifeVar = [[dictionary valueForKey:@"particleLifespanVariance"] floatValue]; | ||
| 216 | |||
| 217 | // emission Rate | ||
| 218 | emissionRate = totalParticles/life; | ||
| 219 | |||
| 220 | // texture | ||
| 221 | // Try to get the texture from the cache | ||
| 222 | NSString *textureName = [dictionary valueForKey:@"textureFileName"]; | ||
| 223 | |||
| 224 | CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:textureName]; | ||
| 225 | |||
| 226 | if( tex ) | ||
| 227 | self.texture = tex; | ||
| 228 | |||
| 229 | else { | ||
| 230 | |||
| 231 | NSString *textureData = [dictionary valueForKey:@"textureImageData"]; | ||
| 232 | NSAssert( textureData, @"CCParticleSystem: Couldn't load texture"); | ||
| 233 | |||
| 234 | // if it fails, try to get it from the base64-gzipped data | ||
| 235 | unsigned char *buffer = NULL; | ||
| 236 | int len = base64Decode((unsigned char*)[textureData UTF8String], (unsigned int)[textureData length], &buffer); | ||
| 237 | NSAssert( buffer != NULL, @"CCParticleSystem: error decoding textureImageData"); | ||
| 238 | |||
| 239 | unsigned char *deflated = NULL; | ||
| 240 | NSUInteger deflatedLen = ccInflateMemory(buffer, len, &deflated); | ||
| 241 | free( buffer ); | ||
| 242 | |||
| 243 | NSAssert( deflated != NULL, @"CCParticleSystem: error ungzipping textureImageData"); | ||
| 244 | NSData *data = [[NSData alloc] initWithBytes:deflated length:deflatedLen]; | ||
| 245 | |||
| 246 | #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED | ||
| 247 | UIImage *image = [[UIImage alloc] initWithData:data]; | ||
| 248 | #elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) | ||
| 249 | NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data]; | ||
| 250 | #endif | ||
| 251 | |||
| 252 | free(deflated); deflated = NULL; | ||
| 253 | |||
| 254 | self.texture = [[CCTextureCache sharedTextureCache] addCGImage:[image CGImage] forKey:textureName]; | ||
| 255 | [data release]; | ||
| 256 | [image release]; | ||
| 257 | } | ||
| 258 | |||
| 259 | NSAssert( [self texture] != NULL, @"CCParticleSystem: error loading the texture"); | ||
| 260 | |||
| 261 | } | ||
| 262 | |||
| 263 | return self; | ||
| 264 | } | ||
| 265 | |||
| 266 | -(id) initWithTotalParticles:(NSUInteger) numberOfParticles | ||
| 267 | { | ||
| 268 | if( (self=[super init]) ) { | ||
| 269 | |||
| 270 | totalParticles = numberOfParticles; | ||
| 271 | |||
| 272 | particles = calloc( totalParticles, sizeof(tCCParticle) ); | ||
| 273 | |||
| 274 | if( ! particles ) { | ||
| 275 | NSLog(@"Particle system: not enough memory"); | ||
| 276 | [self release]; | ||
| 277 | return nil; | ||
| 278 | } | ||
| 279 | |||
| 280 | // default, active | ||
| 281 | active = YES; | ||
| 282 | |||
| 283 | // default blend function | ||
| 284 | blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST }; | ||
| 285 | |||
| 286 | // default movement type; | ||
| 287 | positionType_ = kCCPositionTypeFree; | ||
| 288 | |||
| 289 | // by default be in mode A: | ||
| 290 | emitterMode_ = kCCParticleModeGravity; | ||
| 291 | |||
| 292 | // default: modulate | ||
| 293 | // XXX: not used | ||
| 294 | // colorModulate = YES; | ||
| 295 | |||
| 296 | autoRemoveOnFinish_ = NO; | ||
| 297 | |||
| 298 | // profiling | ||
| 299 | #if CC_ENABLE_PROFILERS | ||
| 300 | _profilingTimer = [[CCProfiler timerWithName:@"particle system" andInstance:self] retain]; | ||
| 301 | #endif | ||
| 302 | |||
| 303 | // Optimization: compile udpateParticle method | ||
| 304 | updateParticleSel = @selector(updateQuadWithParticle:newPosition:); | ||
| 305 | updateParticleImp = (CC_UPDATE_PARTICLE_IMP) [self methodForSelector:updateParticleSel]; | ||
| 306 | |||
| 307 | // udpate after action in run! | ||
| 308 | [self scheduleUpdateWithPriority:1]; | ||
| 309 | |||
| 310 | } | ||
| 311 | |||
| 312 | return self; | ||
| 313 | } | ||
| 314 | |||
| 315 | -(void) dealloc | ||
| 316 | { | ||
| 317 | free( particles ); | ||
| 318 | |||
| 319 | [texture_ release]; | ||
| 320 | // profiling | ||
| 321 | #if CC_ENABLE_PROFILERS | ||
| 322 | [CCProfiler releaseTimer:_profilingTimer]; | ||
| 323 | #endif | ||
| 324 | |||
| 325 | [super dealloc]; | ||
| 326 | } | ||
| 327 | |||
| 328 | -(BOOL) addParticle | ||
| 329 | { | ||
| 330 | if( [self isFull] ) | ||
| 331 | return NO; | ||
| 332 | |||
| 333 | tCCParticle * particle = &particles[ particleCount ]; | ||
| 334 | |||
| 335 | [self initParticle: particle]; | ||
| 336 | particleCount++; | ||
| 337 | |||
| 338 | return YES; | ||
| 339 | } | ||
| 340 | |||
| 341 | -(void) initParticle: (tCCParticle*) particle | ||
| 342 | { | ||
| 343 | |||
| 344 | // timeToLive | ||
| 345 | // no negative life. prevent division by 0 | ||
| 346 | particle->timeToLive = life + lifeVar * CCRANDOM_MINUS1_1(); | ||
| 347 | particle->timeToLive = MAX(0, particle->timeToLive); | ||
| 348 | |||
| 349 | // position | ||
| 350 | particle->pos.x = sourcePosition.x + posVar.x * CCRANDOM_MINUS1_1(); | ||
| 351 | particle->pos.x *= CC_CONTENT_SCALE_FACTOR(); | ||
| 352 | particle->pos.y = sourcePosition.y + posVar.y * CCRANDOM_MINUS1_1(); | ||
| 353 | particle->pos.y *= CC_CONTENT_SCALE_FACTOR(); | ||
| 354 | |||
| 355 | // Color | ||
| 356 | ccColor4F start; | ||
| 357 | start.r = clampf( startColor.r + startColorVar.r * CCRANDOM_MINUS1_1(), 0, 1); | ||
| 358 | start.g = clampf( startColor.g + startColorVar.g * CCRANDOM_MINUS1_1(), 0, 1); | ||
| 359 | start.b = clampf( startColor.b + startColorVar.b * CCRANDOM_MINUS1_1(), 0, 1); | ||
| 360 | start.a = clampf( startColor.a + startColorVar.a * CCRANDOM_MINUS1_1(), 0, 1); | ||
| 361 | |||
| 362 | ccColor4F end; | ||
| 363 | end.r = clampf( endColor.r + endColorVar.r * CCRANDOM_MINUS1_1(), 0, 1); | ||
| 364 | end.g = clampf( endColor.g + endColorVar.g * CCRANDOM_MINUS1_1(), 0, 1); | ||
| 365 | end.b = clampf( endColor.b + endColorVar.b * CCRANDOM_MINUS1_1(), 0, 1); | ||
| 366 | end.a = clampf( endColor.a + endColorVar.a * CCRANDOM_MINUS1_1(), 0, 1); | ||
| 367 | |||
| 368 | particle->color = start; | ||
| 369 | particle->deltaColor.r = (end.r - start.r) / particle->timeToLive; | ||
| 370 | particle->deltaColor.g = (end.g - start.g) / particle->timeToLive; | ||
| 371 | particle->deltaColor.b = (end.b - start.b) / particle->timeToLive; | ||
| 372 | particle->deltaColor.a = (end.a - start.a) / particle->timeToLive; | ||
| 373 | |||
| 374 | // size | ||
| 375 | float startS = startSize + startSizeVar * CCRANDOM_MINUS1_1(); | ||
| 376 | startS = MAX(0, startS); // No negative value | ||
| 377 | startS *= CC_CONTENT_SCALE_FACTOR(); | ||
| 378 | |||
| 379 | particle->size = startS; | ||
| 380 | if( endSize == kCCParticleStartSizeEqualToEndSize ) | ||
| 381 | particle->deltaSize = 0; | ||
| 382 | else { | ||
| 383 | float endS = endSize + endSizeVar * CCRANDOM_MINUS1_1(); | ||
| 384 | endS = MAX(0, endS); // No negative values | ||
| 385 | endS *= CC_CONTENT_SCALE_FACTOR(); | ||
| 386 | particle->deltaSize = (endS - startS) / particle->timeToLive; | ||
| 387 | } | ||
| 388 | |||
| 389 | // rotation | ||
| 390 | float startA = startSpin + startSpinVar * CCRANDOM_MINUS1_1(); | ||
| 391 | float endA = endSpin + endSpinVar * CCRANDOM_MINUS1_1(); | ||
| 392 | particle->rotation = startA; | ||
| 393 | particle->deltaRotation = (endA - startA) / particle->timeToLive; | ||
| 394 | |||
| 395 | // position | ||
| 396 | if( positionType_ == kCCPositionTypeFree ) { | ||
| 397 | CGPoint p = [self convertToWorldSpace:CGPointZero]; | ||
| 398 | particle->startPos = ccpMult( p, CC_CONTENT_SCALE_FACTOR() ); | ||
| 399 | } | ||
| 400 | else if( positionType_ == kCCPositionTypeRelative ) { | ||
| 401 | particle->startPos = ccpMult( position_, CC_CONTENT_SCALE_FACTOR() ); | ||
| 402 | } | ||
| 403 | |||
| 404 | // direction | ||
| 405 | float a = CC_DEGREES_TO_RADIANS( angle + angleVar * CCRANDOM_MINUS1_1() ); | ||
| 406 | |||
| 407 | // Mode Gravity: A | ||
| 408 | if( emitterMode_ == kCCParticleModeGravity ) { | ||
| 409 | |||
| 410 | CGPoint v = {cosf( a ), sinf( a )}; | ||
| 411 | float s = mode.A.speed + mode.A.speedVar * CCRANDOM_MINUS1_1(); | ||
| 412 | s *= CC_CONTENT_SCALE_FACTOR(); | ||
| 413 | |||
| 414 | // direction | ||
| 415 | particle->mode.A.dir = ccpMult( v, s ); | ||
| 416 | |||
| 417 | // radial accel | ||
| 418 | particle->mode.A.radialAccel = mode.A.radialAccel + mode.A.radialAccelVar * CCRANDOM_MINUS1_1(); | ||
| 419 | particle->mode.A.radialAccel *= CC_CONTENT_SCALE_FACTOR(); | ||
| 420 | |||
| 421 | // tangential accel | ||
| 422 | particle->mode.A.tangentialAccel = mode.A.tangentialAccel + mode.A.tangentialAccelVar * CCRANDOM_MINUS1_1(); | ||
| 423 | particle->mode.A.tangentialAccel *= CC_CONTENT_SCALE_FACTOR(); | ||
| 424 | |||
| 425 | } | ||
| 426 | |||
| 427 | // Mode Radius: B | ||
| 428 | else { | ||
| 429 | // Set the default diameter of the particle from the source position | ||
| 430 | float startRadius = mode.B.startRadius + mode.B.startRadiusVar * CCRANDOM_MINUS1_1(); | ||
| 431 | float endRadius = mode.B.endRadius + mode.B.endRadiusVar * CCRANDOM_MINUS1_1(); | ||
| 432 | |||
| 433 | startRadius *= CC_CONTENT_SCALE_FACTOR(); | ||
| 434 | endRadius *= CC_CONTENT_SCALE_FACTOR(); | ||
| 435 | |||
| 436 | particle->mode.B.radius = startRadius; | ||
| 437 | |||
| 438 | if( mode.B.endRadius == kCCParticleStartRadiusEqualToEndRadius ) | ||
| 439 | particle->mode.B.deltaRadius = 0; | ||
| 440 | else | ||
| 441 | particle->mode.B.deltaRadius = (endRadius - startRadius) / particle->timeToLive; | ||
| 442 | |||
| 443 | particle->mode.B.angle = a; | ||
| 444 | particle->mode.B.degreesPerSecond = CC_DEGREES_TO_RADIANS(mode.B.rotatePerSecond + mode.B.rotatePerSecondVar * CCRANDOM_MINUS1_1()); | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | -(void) stopSystem | ||
| 449 | { | ||
| 450 | active = NO; | ||
| 451 | elapsed = duration; | ||
| 452 | emitCounter = 0; | ||
| 453 | } | ||
| 454 | |||
| 455 | -(void) resetSystem | ||
| 456 | { | ||
| 457 | active = YES; | ||
| 458 | elapsed = 0; | ||
| 459 | for(particleIdx = 0; particleIdx < particleCount; ++particleIdx) { | ||
| 460 | tCCParticle *p = &particles[particleIdx]; | ||
| 461 | p->timeToLive = 0; | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | -(BOOL) isFull | ||
| 466 | { | ||
| 467 | return (particleCount == totalParticles); | ||
| 468 | } | ||
| 469 | |||
| 470 | #pragma mark ParticleSystem - MainLoop | ||
| 471 | -(void) update: (ccTime) dt | ||
| 472 | { | ||
| 473 | if( active && emissionRate ) { | ||
| 474 | float rate = 1.0f / emissionRate; | ||
| 475 | emitCounter += dt; | ||
| 476 | while( particleCount < totalParticles && emitCounter > rate ) { | ||
| 477 | [self addParticle]; | ||
| 478 | emitCounter -= rate; | ||
| 479 | } | ||
| 480 | |||
| 481 | elapsed += dt; | ||
| 482 | if(duration != -1 && duration < elapsed) | ||
| 483 | [self stopSystem]; | ||
| 484 | } | ||
| 485 | |||
| 486 | particleIdx = 0; | ||
| 487 | |||
| 488 | |||
| 489 | #if CC_ENABLE_PROFILERS | ||
| 490 | CCProfilingBeginTimingBlock(_profilingTimer); | ||
| 491 | #endif | ||
| 492 | |||
| 493 | |||
| 494 | CGPoint currentPosition = CGPointZero; | ||
| 495 | if( positionType_ == kCCPositionTypeFree ) { | ||
| 496 | currentPosition = [self convertToWorldSpace:CGPointZero]; | ||
| 497 | currentPosition.x *= CC_CONTENT_SCALE_FACTOR(); | ||
| 498 | currentPosition.y *= CC_CONTENT_SCALE_FACTOR(); | ||
| 499 | } | ||
| 500 | else if( positionType_ == kCCPositionTypeRelative ) { | ||
| 501 | currentPosition = position_; | ||
| 502 | currentPosition.x *= CC_CONTENT_SCALE_FACTOR(); | ||
| 503 | currentPosition.y *= CC_CONTENT_SCALE_FACTOR(); | ||
| 504 | } | ||
| 505 | |||
| 506 | while( particleIdx < particleCount ) | ||
| 507 | { | ||
| 508 | tCCParticle *p = &particles[particleIdx]; | ||
| 509 | |||
| 510 | // life | ||
| 511 | p->timeToLive -= dt; | ||
| 512 | |||
| 513 | if( p->timeToLive > 0 ) { | ||
| 514 | |||
| 515 | // Mode A: gravity, direction, tangential accel & radial accel | ||
| 516 | if( emitterMode_ == kCCParticleModeGravity ) { | ||
| 517 | CGPoint tmp, radial, tangential; | ||
| 518 | |||
| 519 | radial = CGPointZero; | ||
| 520 | // radial acceleration | ||
| 521 | if(p->pos.x || p->pos.y) | ||
| 522 | radial = ccpNormalize(p->pos); | ||
| 523 | |||
| 524 | tangential = radial; | ||
| 525 | radial = ccpMult(radial, p->mode.A.radialAccel); | ||
| 526 | |||
| 527 | // tangential acceleration | ||
| 528 | float newy = tangential.x; | ||
| 529 | tangential.x = -tangential.y; | ||
| 530 | tangential.y = newy; | ||
| 531 | tangential = ccpMult(tangential, p->mode.A.tangentialAccel); | ||
| 532 | |||
| 533 | // (gravity + radial + tangential) * dt | ||
| 534 | tmp = ccpAdd( ccpAdd( radial, tangential), mode.A.gravity); | ||
| 535 | tmp = ccpMult( tmp, dt); | ||
| 536 | p->mode.A.dir = ccpAdd( p->mode.A.dir, tmp); | ||
| 537 | tmp = ccpMult(p->mode.A.dir, dt); | ||
| 538 | p->pos = ccpAdd( p->pos, tmp ); | ||
| 539 | } | ||
| 540 | |||
| 541 | // Mode B: radius movement | ||
| 542 | else { | ||
| 543 | // Update the angle and radius of the particle. | ||
| 544 | p->mode.B.angle += p->mode.B.degreesPerSecond * dt; | ||
| 545 | p->mode.B.radius += p->mode.B.deltaRadius * dt; | ||
| 546 | |||
| 547 | p->pos.x = - cosf(p->mode.B.angle) * p->mode.B.radius; | ||
| 548 | p->pos.y = - sinf(p->mode.B.angle) * p->mode.B.radius; | ||
| 549 | } | ||
| 550 | |||
| 551 | // color | ||
| 552 | p->color.r += (p->deltaColor.r * dt); | ||
| 553 | p->color.g += (p->deltaColor.g * dt); | ||
| 554 | p->color.b += (p->deltaColor.b * dt); | ||
| 555 | p->color.a += (p->deltaColor.a * dt); | ||
| 556 | |||
| 557 | // size | ||
| 558 | p->size += (p->deltaSize * dt); | ||
| 559 | p->size = MAX( 0, p->size ); | ||
| 560 | |||
| 561 | // angle | ||
| 562 | p->rotation += (p->deltaRotation * dt); | ||
| 563 | |||
| 564 | // | ||
| 565 | // update values in quad | ||
| 566 | // | ||
| 567 | |||
| 568 | CGPoint newPos; | ||
| 569 | |||
| 570 | if( positionType_ == kCCPositionTypeFree || positionType_ == kCCPositionTypeRelative ) { | ||
| 571 | CGPoint diff = ccpSub( currentPosition, p->startPos ); | ||
| 572 | newPos = ccpSub(p->pos, diff); | ||
| 573 | |||
| 574 | } else | ||
| 575 | newPos = p->pos; | ||
| 576 | |||
| 577 | |||
| 578 | updateParticleImp(self, updateParticleSel, p, newPos); | ||
| 579 | |||
| 580 | // update particle counter | ||
| 581 | particleIdx++; | ||
| 582 | |||
| 583 | } else { | ||
| 584 | // life < 0 | ||
| 585 | if( particleIdx != particleCount-1 ) | ||
| 586 | particles[particleIdx] = particles[particleCount-1]; | ||
| 587 | particleCount--; | ||
| 588 | |||
| 589 | if( particleCount == 0 && autoRemoveOnFinish_ ) { | ||
| 590 | [self unscheduleUpdate]; | ||
| 591 | [parent_ removeChild:self cleanup:YES]; | ||
| 592 | return; | ||
| 593 | } | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | #if CC_ENABLE_PROFILERS | ||
| 598 | CCProfilingEndTimingBlock(_profilingTimer); | ||
| 599 | #endif | ||
| 600 | |||
| 601 | #ifdef CC_USES_VBO | ||
| 602 | [self postStep]; | ||
| 603 | #endif | ||
| 604 | } | ||
| 605 | |||
| 606 | -(void) updateQuadWithParticle:(tCCParticle*)particle newPosition:(CGPoint)pos; | ||
| 607 | { | ||
| 608 | // should be overriden | ||
| 609 | } | ||
| 610 | |||
| 611 | -(void) postStep | ||
| 612 | { | ||
| 613 | // should be overriden | ||
| 614 | } | ||
| 615 | |||
| 616 | #pragma mark ParticleSystem - CCTexture protocol | ||
| 617 | |||
| 618 | -(void) setTexture:(CCTexture2D*) texture | ||
| 619 | { | ||
| 620 | [texture_ release]; | ||
| 621 | texture_ = [texture retain]; | ||
| 622 | |||
| 623 | // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it | ||
| 624 | if( texture_ && ! [texture hasPremultipliedAlpha] && | ||
| 625 | ( blendFunc_.src == CC_BLEND_SRC && blendFunc_.dst == CC_BLEND_DST ) ) { | ||
| 626 | |||
| 627 | blendFunc_.src = GL_SRC_ALPHA; | ||
| 628 | blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; | ||
| 629 | } | ||
| 630 | } | ||
| 631 | |||
| 632 | -(CCTexture2D*) texture | ||
| 633 | { | ||
| 634 | return texture_; | ||
| 635 | } | ||
| 636 | |||
| 637 | #pragma mark ParticleSystem - Additive Blending | ||
| 638 | -(void) setBlendAdditive:(BOOL)additive | ||
| 639 | { | ||
| 640 | if( additive ) { | ||
| 641 | blendFunc_.src = GL_SRC_ALPHA; | ||
| 642 | blendFunc_.dst = GL_ONE; | ||
| 643 | |||
| 644 | } else { | ||
| 645 | |||
| 646 | if( texture_ && ! [texture_ hasPremultipliedAlpha] ) { | ||
| 647 | blendFunc_.src = GL_SRC_ALPHA; | ||
| 648 | blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; | ||
| 649 | } else { | ||
| 650 | blendFunc_.src = CC_BLEND_SRC; | ||
| 651 | blendFunc_.dst = CC_BLEND_DST; | ||
| 652 | } | ||
| 653 | } | ||
| 654 | } | ||
| 655 | |||
| 656 | -(BOOL) blendAdditive | ||
| 657 | { | ||
| 658 | return( blendFunc_.src == GL_SRC_ALPHA && blendFunc_.dst == GL_ONE); | ||
| 659 | } | ||
| 660 | |||
| 661 | #pragma mark ParticleSystem - Properties of Gravity Mode | ||
| 662 | -(void) setTangentialAccel:(float)t | ||
| 663 | { | ||
| 664 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 665 | mode.A.tangentialAccel = t; | ||
| 666 | } | ||
| 667 | -(float) tangentialAccel | ||
| 668 | { | ||
| 669 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 670 | return mode.A.tangentialAccel; | ||
| 671 | } | ||
| 672 | |||
| 673 | -(void) setTangentialAccelVar:(float)t | ||
| 674 | { | ||
| 675 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 676 | mode.A.tangentialAccelVar = t; | ||
| 677 | } | ||
| 678 | -(float) tangentialAccelVar | ||
| 679 | { | ||
| 680 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 681 | return mode.A.tangentialAccelVar; | ||
| 682 | } | ||
| 683 | |||
| 684 | -(void) setRadialAccel:(float)t | ||
| 685 | { | ||
| 686 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 687 | mode.A.radialAccel = t; | ||
| 688 | } | ||
| 689 | -(float) radialAccel | ||
| 690 | { | ||
| 691 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 692 | return mode.A.radialAccel; | ||
| 693 | } | ||
| 694 | |||
| 695 | -(void) setRadialAccelVar:(float)t | ||
| 696 | { | ||
| 697 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 698 | mode.A.radialAccelVar = t; | ||
| 699 | } | ||
| 700 | -(float) radialAccelVar | ||
| 701 | { | ||
| 702 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 703 | return mode.A.radialAccelVar; | ||
| 704 | } | ||
| 705 | |||
| 706 | -(void) setGravity:(CGPoint)g | ||
| 707 | { | ||
| 708 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 709 | mode.A.gravity = g; | ||
| 710 | } | ||
| 711 | -(CGPoint) gravity | ||
| 712 | { | ||
| 713 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 714 | return mode.A.gravity; | ||
| 715 | } | ||
| 716 | |||
| 717 | -(void) setSpeed:(float)speed | ||
| 718 | { | ||
| 719 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 720 | mode.A.speed = speed; | ||
| 721 | } | ||
| 722 | -(float) speed | ||
| 723 | { | ||
| 724 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 725 | return mode.A.speed; | ||
| 726 | } | ||
| 727 | |||
| 728 | -(void) setSpeedVar:(float)speedVar | ||
| 729 | { | ||
| 730 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 731 | mode.A.speedVar = speedVar; | ||
| 732 | } | ||
| 733 | -(float) speedVar | ||
| 734 | { | ||
| 735 | NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); | ||
| 736 | return mode.A.speedVar; | ||
| 737 | } | ||
| 738 | |||
| 739 | #pragma mark ParticleSystem - Properties of Radius Mode | ||
| 740 | |||
| 741 | -(void) setStartRadius:(float)startRadius | ||
| 742 | { | ||
| 743 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 744 | mode.B.startRadius = startRadius; | ||
| 745 | } | ||
| 746 | -(float) startRadius | ||
| 747 | { | ||
| 748 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 749 | return mode.B.startRadius; | ||
| 750 | } | ||
| 751 | |||
| 752 | -(void) setStartRadiusVar:(float)startRadiusVar | ||
| 753 | { | ||
| 754 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 755 | mode.B.startRadiusVar = startRadiusVar; | ||
| 756 | } | ||
| 757 | -(float) startRadiusVar | ||
| 758 | { | ||
| 759 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 760 | return mode.B.startRadiusVar; | ||
| 761 | } | ||
| 762 | |||
| 763 | -(void) setEndRadius:(float)endRadius | ||
| 764 | { | ||
| 765 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 766 | mode.B.endRadius = endRadius; | ||
| 767 | } | ||
| 768 | -(float) endRadius | ||
| 769 | { | ||
| 770 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 771 | return mode.B.endRadius; | ||
| 772 | } | ||
| 773 | |||
| 774 | -(void) setEndRadiusVar:(float)endRadiusVar | ||
| 775 | { | ||
| 776 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 777 | mode.B.endRadiusVar = endRadiusVar; | ||
| 778 | } | ||
| 779 | -(float) endRadiusVar | ||
| 780 | { | ||
| 781 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 782 | return mode.B.endRadiusVar; | ||
| 783 | } | ||
| 784 | |||
| 785 | -(void) setRotatePerSecond:(float)degrees | ||
| 786 | { | ||
| 787 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 788 | mode.B.rotatePerSecond = degrees; | ||
| 789 | } | ||
| 790 | -(float) rotatePerSecond | ||
| 791 | { | ||
| 792 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 793 | return mode.B.rotatePerSecond; | ||
| 794 | } | ||
| 795 | |||
| 796 | -(void) setRotatePerSecondVar:(float)degrees | ||
| 797 | { | ||
| 798 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 799 | mode.B.rotatePerSecondVar = degrees; | ||
| 800 | } | ||
| 801 | -(float) rotatePerSecondVar | ||
| 802 | { | ||
| 803 | NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); | ||
| 804 | return mode.B.rotatePerSecondVar; | ||
| 805 | } | ||
| 806 | @end | ||
| 807 | |||
| 808 | |||
