summary refs log tree commit diff stats
path: root/libs/cocos2d/CCParticleSystem.m
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cocos2d/CCParticleSystem.m')
-rwxr-xr-xlibs/cocos2d/CCParticleSystem.m808
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