// // NSGLParticleEngine.m // CocoaGL // // Created by Katherine Tattersall on Thu Aug 08 2002. // Copyright (c) 2001 ZeroByZero. All rights reserved. // #import "NSGLParticleEngine.h" #define CRUDITY 50 @implementation NSGLParticleEngine - (id) initWithFile: (NSString *)filename { int i; [self readFile: filename]; if( ttl == 0 ) ttl = 0.01; // otherwise we'll end up with divide by zero errors for( i=0; i < numParticles; i++ ) [self initParticle: i]; numParticles = MAXPARTICLES; lastupdate = TickCount(); return self; } - (void) initParticle: (int)i { float random_number; float random_pitch, random_yaw; // time to live = the average time to live * random number * 2 ( [-1,1]*ttl ) particle[i].ttl = ttl * frand() * 2; // this means that we will likely have a shade of the initial color random_number = frand()*2 - 1; particle[i].color[0] = clamp( start_color[0] + random_number*color_variance[0], 0, 1); random_number = frand()*2 - 1; particle[i].color[1] = clamp( start_color[1] + random_number*color_variance[1], 0, 1); random_number = frand()*2 - 1; particle[i].color[2] = clamp( start_color[2] + random_number*color_variance[2], 0, 1); random_number = frand()*2 - 1; particle[i].color[3] = clamp( start_color[3] + random_number*color_variance[3], 0, 1); // this means that we will likely have a shade of the final color random_number = frand()*2 - 1; particle[i].color_vector[0] = clamp( dest_color[0] + random_number*color_variance[0] - particle[i].color[0], -1, 1)/particle[i].ttl; random_number = frand()*2 - 1; particle[i].color_vector[1] = clamp( dest_color[1] + random_number*color_variance[1] - particle[i].color[1], -1, 1)/particle[i].ttl; random_number = frand()*2 - 1; particle[i].color_vector[2] = clamp( dest_color[2] + random_number*color_variance[2] - particle[i].color[2], -1, 1)/particle[i].ttl; random_number = frand()*2 - 1; particle[i].color_vector[3] = clamp( dest_color[3] + random_number*color_variance[3] - particle[i].color[3], -1, 1)/particle[i].ttl; // this means that we will start around the area of the particle engine random_number = frand()*2 - 1; particle[i].location[0] = location[0] + random_number*location_variance[0]; random_number = frand()*2 - 1; particle[i].location[1] = location[1] + random_number*location_variance[1]; random_number = frand()*2 - 1; particle[i].location[2] = location[2] + random_number*location_variance[2]; random_number = frand()*2 - 1; random_yaw = frand() * PI * 2.0; //random_number * PI * 2.0; random_pitch = frand() * angle * PI / 180.0; //random_number * angle * PI / 180.0; particle[i].velocity[0] = velocity[0]*usevel[0] + velocity[0]*cos(rotation*PI/180.0) * cos(random_pitch); //velocity[0] * cos(random_pitch) + velocity[0]*cos(rotation*PI/180.0); particle[i].velocity[1] = velocity[1]*usevel[1] + velocity[1]*sin(rotation*PI/180.0) + velocity[1] * sin(random_pitch) * cos(random_yaw); //velocity[1] * sin(random_pitch) * cos(random_yaw) + velocity[1]*sin(rotation*PI/180.0); particle[i].velocity[2] = velocity[2]*usevel[2] + velocity[2] * sin(random_pitch) * sin(random_yaw); //velocity[2] * sin(random_pitch) * sin(random_yaw) ; random_number = frand()*2 - 1; particle[i].size = start_size; particle[i].size_vector = dest_size + random_number*size_variance - particle[i].size; } - (void) readFile: (NSString *) nsPath { const char *path = [nsPath cString]; NSString *s; FILE *file; unsigned char buffer[1024]; float f, c0, c1, c2, c3; size_t size; file = fopen( path, "r" ); if( file == nil ) { NSLog(@"File did not open"); } else { while( ! feof(file) ) { fscanf( file, "%s", buffer ); s = [NSString stringWithCString: buffer]; if( [s hasPrefix: @"//"] ) fgetln( file, &size ); else if( [s isEqualToString: @"ttl" ] ) { fscanf( file, "%s%f", buffer, &f ); ttl = f; NSLog(@"ttl = %f", ttl); } else if( [s isEqualToString: @"texture" ] ) { fscanf( file, "%s", buffer ); fscanf( file, "%s", buffer ); s = [NSString stringWithCString: buffer]; texture = getTextures( s ); NSLog(@"texture = %d", texture); NSLog( s ); } else if( [s isEqualToString: @"usevelocity" ] ) { fscanf( file, "%s%f%f%f", buffer, &f, &c0, &c1 ); usevel[0] = f; usevel[1] = c0; usevel[2] = c1; NSLog(@"usevel = %f %f %f", usevel[0], usevel[1], usevel[2]); } else if( [s isEqualToString: @"numparticles" ] ) //part_per_sec_change_function { fscanf( file, "%s%f", buffer, &f ); numParticles = f; NSLog(@"numP = %f", numParticles); } else if( [s isEqualToString: @"start_color" ] ) { fscanf( file, "%s%f%f%f%f", buffer, &c0, &c1, &c2, &c3); start_color[0] = c0; start_color[1] = c1; start_color[2] = c2; start_color[3] = c3; NSLog(@"color = %f %f %f %f", c0, c1, c2, c3); } else if( [s isEqualToString: @"dest_color" ] ) { fscanf( file, "%s%f%f%f%f", buffer, &c0, &c1, &c2, &c3); dest_color[0] = c0; dest_color[1] = c1; dest_color[2] = c2; dest_color[3] = c3; NSLog(@"dest color = %f %f %f %f", c0, c1, c2, c3); } else if( [s isEqualToString: @"color_var" ] ) { fscanf( file, "%s%f%f%f%f", buffer, &c0, &c1, &c2, &c3); color_variance[0] = c0; color_variance[1] = c1; color_variance[2] = c2; color_variance[3] = c3; NSLog(@"color var = %f %f %f %f", c0, c1, c2, c3);} else if( [s isEqualToString: @"location" ] ) { fscanf( file, "%s%f%f%f", buffer, &c0, &c1, &c2); location[0] = c0; location[1] = c1; location[2] = c2; NSLog(@"location = %f %f %f", c0, c1, c2); } else if( [s isEqualToString: @"location_var" ] ) { fscanf( file, "%s%f%f%f", buffer, &c0, &c1, &c2); location_variance[0] = c0; location_variance[1] = c1; location_variance[2] = c2; NSLog(@"location var = %f %f %f", c0, c1, c2); } else if( [s isEqualToString: @"velocity" ] ) { fscanf( file, "%s%f%f%f", buffer, &c0, &c1, &c2); velocity[0] = c0; velocity[1] = c1; velocity[2] = c2; NSLog(@"velocity = %f %f %f", c0, c1, c2); } else if( [s isEqualToString: @"gravity" ] ) { fscanf( file, "%s%f%f%f", buffer, &c0, &c1, &c2); gravity[0] = c0; gravity[1] = c1; gravity[2] = c2; NSLog(@"gravity = %f %f %f", c0, c1, c2); } else if( [s isEqualToString: @"part_per_sec" ] ) { fscanf( file, "%s%f", buffer, &c0 ); particlesPerSecond = c0; NSLog(@"pps = %f", c0); } else if( [s isEqualToString: @"start_size" ] ) { fscanf( file, "%s%f", buffer, &c0 ); start_size = c0; NSLog(@"size = %f", c0); } else if( [s isEqualToString: @"dest_size" ] ) { fscanf( file, "%s%f", buffer, &c0 ); dest_size = c0; NSLog(@"dsize = %f", c0); } else if( [s isEqualToString: @"size_var" ] ) { fscanf( file, "%s%f", buffer, &c0 ); size_variance = c0; NSLog(@"sizevar = %f", c0); } else if( [s isEqualToString: @"angle" ] ) { fscanf( file, "%s%f", buffer, &c0 ); angle = c0; NSLog(@"angle = %f", c0); } else if( [s isEqualToString: @"spin" ] ) { fscanf( file, "%s%f", buffer, &c0 ); spin = c0; NSLog(@"spin = %f", c0); } } } } /* - (void) explode { int i; for( i = 0; i < MAXPARTICLES; i++ ) { int j = i*12/MAXPARTICLES; particle[i].active = TRUE; particle[i].life = 1.0f; particle[i].fade = (float)(rand()%100)/1000.0f+0.003f; particle[i].r=colors[j][0]; // we could multiply each of these, but particle[i].g=colors[j][1]; // multiplication takes longer than particle[i].b=colors[j][2]; // assignment particle[i].xi=(float)((rand()%50)-26.0f)*10.0f; particle[i].yi=(float)(rand() %50)*10.0f; particle[i].zi=(float)((rand()%50)-26.0f)*10.0f; } xg = 0.0f; yg =-0.8f; zg = 0.0f; slowdown = 1; } */ - (void) dealloc { glDeleteTextures(1, &texture); [super dealloc]; } - (void) draw { int i; glBindTexture( GL_TEXTURE_2D, texture ); for( i = 0; i < MAXPARTICLES ; i++ ) { if( particle[i].ttl > 0 ) { float v[3]; glColor4fv( particle[i].color ); v[2] = particle[i].location[2]; glBegin( GL_TRIANGLE_STRIP ); v[0] = particle[i].location[0] + particle[i].size; v[1] = particle[i].location[1] + particle[i].size; glTexCoord2d(1,1); glVertex3fv( v ); v[0] = particle[i].location[0] - particle[i].size; v[1] = particle[i].location[1] + particle[i].size; glTexCoord2d(0,1);glVertex3fv( v ); v[0] = particle[i].location[0] + particle[i].size; v[1] = particle[i].location[1] - particle[i].size; glTexCoord2d(1,0); glVertex3fv( v ); v[0] = particle[i].location[0] - particle[i].size; v[1] = particle[i].location[1] - particle[i].size; glTexCoord2d(0,0); glVertex3fv( v ); glEnd(); } } } - (int) numParticles { return numParticles; } - (void) reduceParticles { if( numParticles > CRUDITY ) numParticles -= CRUDITY; } - (void) increaseParticles { if( numParticles < MAXPARTICLES ) numParticles += CRUDITY; } - (void) updateParticle: (int)i deltaT: (double)timecount { particle[i].ttl -= timecount; particle[i].color[0] = clamp( particle[i].color[0] + particle[i].color_vector[0]*timecount, 0, 1); particle[i].color[1] = clamp( particle[i].color[1] + particle[i].color_vector[1]*timecount, 0, 1); particle[i].color[2] = clamp( particle[i].color[2] + particle[i].color_vector[2]*timecount, 0, 1); particle[i].color[3] = clamp( particle[i].color[3] + particle[i].color_vector[3]*timecount, 0, 1); particle[i].size += particle[i].size_vector*timecount; particle[i].size = particle[i].size < 0 ? 0 : particle[i].size; particle[i].velocity[0] += gravity[0]*timecount; particle[i].velocity[1] += gravity[1]*timecount; particle[i].velocity[2] += gravity[2]*timecount; particle[i].location[0] += particle[i].velocity[0]*timecount; particle[i].location[1] += particle[i].velocity[1]*timecount; particle[i].location[2] += particle[i].velocity[2]*timecount; } - (void) updateEngineWithFlag: (int) f { int particles_needed = numParticles, particles_created; int i, numAlive = 0; double timecount; timecount = (TickCount() - lastupdate)/CLK_TCK; lastupdate = TickCount(); if( f == UPDATE || f == UPDATE_AND_CREATE ) { rotation += spin*timecount; for( i = 0; i < MAXPARTICLES ; i++ ) { if( particle[i].ttl > 0 ) { [self updateParticle:i deltaT: timecount]; numAlive ++; } } } if( f == CREATE || f == UPDATE_AND_CREATE ) { particles_needed += particlesPerSecond * timecount + emissionResidue; particles_created = (unsigned int)clamp((particlesPerSecond * timecount + emissionResidue), 0, numParticles); emissionResidue = particlesPerSecond * timecount + emissionResidue - particles_created; for( i=0; i < MAXPARTICLES && particles_created > 0; i++ ) { if( particle[i].ttl <= 0 ) { [self initParticle: i]; particles_created --; } } } } @end