diff options
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 | |||