/****************************** File ********************************* Author: Rocco Bowling Usage: Generalized particle system generator ***************************** License ******************************** This code can be freely used as long as this header, in its entirety, is kept with the code This code is presented as is. The author takes no responsibilities for any version of this code. (c) 2006 Rocco Bowling *********************************************************************/ #pragma mark *** Includes *** #include "particles.h" #include "main.h" #include "glGeometry.h" #pragma mark *** Definitions *** #pragma mark *** Globals *** static GLuint gBulletinBoardVertexShader; static bool gShaderInited = false; #define random_1() (random_float() * 2.0f - 1.0f) #pragma mark *** Prototypes *** #pragma mark *** Code *** /** @brief Allocates and initializes a new particle system generator * * @return A pointer to a structure which defined the * particle system generator. This value will * need to be passed to other particle system * functions. */ ParticleSystemInfo * particles_init() { ParticleSystemInfo * info = (ParticleSystemInfo *)calloc(sizeof(ParticleSystemInfo), 1); info->geom_particle_array = mutableDataArray_initWithCapacity(kDefaultParticles, sizeof(GLColoredTexturedNormalQuad)); info->info_particle_array = mutableDataArray_initWithCapacity(kDefaultParticles, sizeof(ParticleInfo)); info->color_stages_array = mutableDataArray_initWithCapacity(1, sizeof(ColorStageInfo)); info->wind_areas_array = mutableArray_initWithCapacity(1); info->attractors_array = mutableArray_initWithCapacity(1); info->spawn_chain_array = mutableDataArray_initWithCapacity(1, sizeof(SpawnChainInfo)); info->brightness = 1.0; info->texture_ref = -1; info->spawn_debt = 0; if(gShaderInited == false) { gShaderInited = shaders_load("Resources/Shaders/ARBvp_billboard.txt", &gBulletinBoardVertexShader); } return info; } /** @brief Deallocates a particle system generator * * @param info Pointer to the particle system (returned by * particles_init()). */ void particles_destruct(ParticleSystemInfo * info) { mutableDataArray_destruct(info->spawn_chain_array); mutableArray_destruct(info->wind_areas_array); mutableArray_destruct(info->attractors_array); mutableDataArray_destruct(info->color_stages_array); mutableDataArray_destruct(info->geom_particle_array); mutableDataArray_destruct(info->info_particle_array); free(info); } #pragma mark - /** @brief Spawn chains provide a method for linking multiple particle * system generators together. In the case where you want to * have a fire particle generate a smoke particle after it * dies, you can use spawn chains to accomplish that. * * @param src_info The particle system which triggers new particles * @param dst_info The system where new particles will be generated * @param s Number of update frames a particle in the src_info * system needs to wait before causing a new particle * to be generated in the dst_info system * @param change [0.0, 1.0] - random factor that a spawn occurs * * @return A reference value needed to pass to spawn chain functions. */ void * particles_addSpawnChain( ParticleSystemInfo * src_info, ParticleSystemInfo * dst_info, int s, float chance) { SpawnChainInfo a; a.dst_info = dst_info; a.stage = s; a.chance = chance; return mutableDataArray_addObject( src_info->spawn_chain_array, &a); } /** @brief Removes a previously defined spawn chain * * @param info Pointer to the particle system * @param ref Value returned by particles_addSpawnChain() */ void particles_removeSpawnChain(ParticleSystemInfo * info, void * ref) { mutableDataArray_removeObject( info->spawn_chain_array, ref); } #pragma mark - /** @brief Adds a wind deformation area to the particle system. The area * is defined by a cylinder, inside which a particle's velocity * is modified by the wind force (with variation). * * @param info Pointer to the particle system * @param p1 One end of the cylinder * @param p2 Other end of the cylinder * @param radius Radius of the cylinder * @param wind Velocity vector of the window * @param variation Variation to be applied to the wind vector * * @return A reference value needed to pass to wind area functions. */ void * particles_addWindArea( ParticleSystemInfo * info, float p1[3], float p2[3], float radius, float wind[3], float variation[3]) { WindAreaInfo a; float t[3]; VEC_SET(a.p1, p1[0], p1[1], p1[2]); VEC_SET(a.p2, p2[0], p2[1], p2[2]); a.radius_sq = radius * radius; VEC_DIFF(t, p2, p1); VEC_FAST_LENGTH(a.length_sq, t); memcpy(a.wind, wind, sizeof(a.wind)); memcpy(a.variation, variation, sizeof(a.variation)); return mutableArray_addObject( info->wind_areas_array, &a, sizeof(a)); } /** @brief Removes a previously defined wind area * * @param info Pointer to the particle system * @param ref Value returned by particles_addWindArea() */ void particles_removeWindArea( ParticleSystemInfo * info, void * ref) { mutableArray_removeObject( info->wind_areas_array, ref); } #pragma mark - /** @brief Attractors attract particles (doh) * * @param info Pointer to the particle system * @param locationOffset Offset to the location of the particle system to * to which particles are attracted. Defined in this * manner so that you can move the whole particle * without affecting the general shape of the * particles. * @param radius Clipping radius at which particles start being attracted * @param strength How strongly particles are attracted * @param inner_radius Particles begin to fade when they are inside the * inner radius * * @return A reference value needed to pass to attractor functions. */ void * particles_addAttractor( ParticleSystemInfo * info, float * locationOffset, float radius, float strength, float inner_radius) { AttractorInfo a; VEC_COPY(a.locationOffset, locationOffset); a.radius = radius; a.strength = strength; a.inner_radius = inner_radius; return mutableArray_addObject(info->attractors_array, &a, sizeof(a)); } /** @brief Removes a previously defined particle attractor * * @param info Pointer to the particle system * @param ref Value returned by particles_addAttractor() */ void particles_removeAttractor( ParticleSystemInfo * info, void * ref) { mutableArray_removeObject( info->attractors_array, ref); } #pragma mark - /** @brief Set the gravity for the particle system * * @param info Pointer to the particle system * @param x,y,z Gravity velocity */ void particles_setGravity(ParticleSystemInfo * info, float x, float y, float z) { VEC_SET(info->gravity, x, y, z); } /** @brief Set the spawn location for new particles * * @param info Pointer to the particle system * @param x,y,z Location for spawning new particles */ void particles_setLocation(ParticleSystemInfo * info, float x, float y, float z) { VEC_SET(info->location, x, y, z); } /** @brief Set the variant for the spawn location * * @param info Pointer to the particle system * @param x,y,z Variant on location for spawning new particles * * n.b. How variants work. Variants work by taking a random value in the * range of [-x, x] and adding that to the spawn location. If you want * a line of particle to be generated along the X-axis, you can set the * location of the center of that line as the spawn x location, and * then set the variant to 1/2 the length of the total line. */ void particles_setLocationVariant(ParticleSystemInfo * info, float x, float y, float z) { VEC_SET(info->range, x, y, z); } /** @brief Set the color of new particles * * @param info Pointer to the particle system * @param r,g,b,a Red, green, blue, alpha colors */ void particles_setColor(ParticleSystemInfo * info, float r, float g, float b, float a) { VEC_SET_4(info->color, r, g, b, a); } /** @brief Set the color variant of new particles * * @param info Pointer to the particle system * @param r,g,b,a Red, green, blue, alpha color variants */ void particles_setColorVariant(ParticleSystemInfo * info, float r, float g, float b, float a) { VEC_SET_4(info->colorVariant, r, g, b, a); } /** @brief Add a color stage to the particles * * @param info Pointer to the particle system * @param s Number of update frames over which the particle changes * @param r,g,b,a Red, green, blue, alpha destination colors * @param vr,vg,vb,va Red, green, blue, alpha destination color variants */ void particles_setColorStage(ParticleSystemInfo * info, int s, float r, float g, float b, float a, float vr, float vg, float vb, float va) { if(s == 0) { particles_setColor(info, r, g, b, a); particles_setColorVariant(info, vr, vg, vb, va); } else { ColorStageInfo cinfo = {s, r, g, b, a, vr, vg, vb, va}; mutableDataArray_addObject( info->color_stages_array, &cinfo); } } /** @brief Set the velocity for new particles * * @param info Pointer to the particle system * @param x,y,z Velocity vector of new particles */ void particles_setVelocity(ParticleSystemInfo * info, float x, float y, float z) { VEC_SET(info->velocity, x, y, z); } /** @brief Set the velocity variant for new particles * * @param info Pointer to the particle system * @param x,y,z Velocity variant of new particles */ void particles_setVelocityVariant(ParticleSystemInfo * info, float x, float y, float z) { VEC_SET(info->velocityVariant, x, y, z); } /** @brief Set the scale for new particles * * @param info Pointer to the particle system * @param x,y,z Scale of new particles */ void particles_setScale(ParticleSystemInfo * info, float x, float y, float z) { VEC_SET(info->scale, x, y, z); } /** @brief Set the scale variant for new particles * * @param info Pointer to the particle system * @param x,y,z Scale variant of new particles */ void particles_setScaleVariant(ParticleSystemInfo * info, float x, float y, float z) { VEC_SET(info->scaleVariant, x, y, z); } /** @brief Set the spawn rate for new particles * * @param info Pointer to the particle system * @param value If this value is positive, it defined the number of * update frames to wait before spawning a new particle. * If this value is zero, then no new particles will be spawned * If this value is negative, it defines the number of spawns * per update frame you want to occur. */ void particles_setRate(ParticleSystemInfo * info, int value) { info->rate = value; info->rate_counter = info->rate; } /** @brief Set the rate variant for new particles * * @param info Pointer to the particle system * @param value Set the rate variant for new particle spawns */ void particles_setRateVariant(ParticleSystemInfo * info, float value) { info->rateVariant = value; } /** @brief Set the time-to-live (TTL) for new particles * * @param info Pointer to the particle system * @param value Number of update frames a particle lives for */ void particles_setTTL(ParticleSystemInfo * info, int value) { info->ttl = value; } /** @brief Set the TTL variant for new particles * * @param info Pointer to the particle system * @param value Variant number of frames a particle lives for */ void particles_setTTLVariant(ParticleSystemInfo * info, float value) { info->ttlVariant = value; } /** @brief Set the OpenGL texture name for new particles * * @param info Pointer to the particle system * @param value OpenGL texture name */ void particles_setTexture(ParticleSystemInfo * info, int value) { info->texture_ref = value; } /** @brief Set floor Y position and hardness. Simple bounce effect. * * @param info Pointer to the particle system * @param pos Set the Y coordinate at which the "floor" is * @param hardness Elasticity of particle bounces */ void particles_setFloor(ParticleSystemInfo * info, float pos, float hardness) { info->floor = pos; info->hardness = hardness; info->use_floor = true; } /** @brief The number of currently active particles in this system * * @return The number of currently active particles in this system */ int particles_active(ParticleSystemInfo * info) { return mutableDataArray_count(info->info_particle_array); } /** @brief Set the animation flag for the particle system * * @param info Pointer to the particle system */ void particles_setAnimate(ParticleSystemInfo * info, bool anim) { info->animation = anim; } /** @brief Set the initial delay for particles to be spawned * * @param info Pointer to the particle system * @param wait Number of update frames to wait before spawning */ void particles_setInitialDelay(ParticleSystemInfo * info, int wait) { info->wait_counter = wait; } /** @brief Brightness is multiplied by the color values of all particles * * @param info Pointer to the particle system * @param brightness [0.0,1.0] brightness value */ void particles_setBrightness(ParticleSystemInfo * info, float brightness) { info->brightness = brightness; } #pragma mark - /** @brief Fills out a ParticleInfo structure. Used internally. * * @param info Pointer to the particle system * @param pinfo Pointer to a ParticleInfo structure * @param ginfo Pointer to the geometry info * @param location Location spawn point of the particle (can be NULL) * @param velocity Velocity of the new particle if inherited (can be NULL) */ void particles_new_particle(ParticleSystemInfo * info, ParticleInfo * pinfo, GLColoredTexturedNormalQuad * ginfo, float * location, float * velocity) { ColorStageInfo * cinfo; SpawnChainInfo * sinfo; float normal[3]; float scale_v = random_1(); memset(pinfo, 0, sizeof(ParticleInfo)); memset(ginfo, 0, sizeof(GLColoredTexturedNormalQuad)); if(!location) { location = info->location; } else { if(velocity) { float l; VEC_COPY(normal, velocity); VEC_NORMALIZE(normal); VEC_LENGTH(l, info->location); VEC_SCALE(normal, l, normal); VEC_SUM(location, location, normal); } else { VEC_SUM(location, location, info->location); } } velocity = info->velocity; // Generate the geometry for this particle GLSetQuadNormals( *ginfo, -(info->scale[0] + scale_v * info->scaleVariant[0]), -(info->scale[1] + scale_v * info->scaleVariant[1]), 0.0, (info->scale[0] + scale_v * info->scaleVariant[0]), -(info->scale[1] + scale_v * info->scaleVariant[1]), 0.0, (info->scale[0] + scale_v * info->scaleVariant[0]), (info->scale[1] + scale_v * info->scaleVariant[1]), 0.0, -(info->scale[0] + scale_v * info->scaleVariant[0]), (info->scale[1] + scale_v * info->scaleVariant[1]), 0.0); GLSetQuadTextures( *ginfo, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0); VEC_SET_4( pinfo->color, info->color[0] + random_1() * info->colorVariant[0], info->color[1] + random_1() * info->colorVariant[1], info->color[2] + random_1() * info->colorVariant[2], info->color[3] + random_1() * info->colorVariant[3]); GLSetQuadColors( *ginfo, pinfo->color[0], pinfo->color[1], pinfo->color[2], pinfo->color[3], pinfo->color[0], pinfo->color[1], pinfo->color[2], pinfo->color[3], pinfo->color[0], pinfo->color[1], pinfo->color[2], pinfo->color[3], pinfo->color[0], pinfo->color[1], pinfo->color[2], pinfo->color[3]); GLTranslateQuad( *ginfo, location[0] + random_1() * info->range[0], location[1] + random_1() * info->range[1], location[2] + random_1() * info->range[2]); VEC_COPY(pinfo->scale, info->scale); VEC_COPY(pinfo->velocity, velocity); pinfo->velocity[0] += (random_float() * 2.0 - 1.0) * info->velocityVariant[0]; pinfo->velocity[1] += (random_float() * 2.0 - 1.0) * info->velocityVariant[1]; pinfo->velocity[2] += (random_float() * 2.0 - 1.0) * info->velocityVariant[2]; // Handle color stages... cinfo = (ColorStageInfo *)mutableDataArray_objectAtIndex(info->color_stages_array, 0); if(cinfo) { float color_target[4]; color_target[0] = cinfo->color[0] + random_1() * cinfo->colorVariant[0]; color_target[1] = cinfo->color[1] + random_1() * cinfo->colorVariant[1]; color_target[2] = cinfo->color[2] + random_1() * cinfo->colorVariant[2]; color_target[3] = cinfo->color[3] + random_1() * cinfo->colorVariant[3]; VEC_DIFF_4(pinfo->color_delta, color_target, pinfo->color); VEC_SCALE_4(pinfo->color_delta, (1.0f / (float)cinfo->ttl), pinfo->color_delta); pinfo->color_ttl = cinfo->ttl; pinfo->color_stage = cinfo; } // Handle spawn chains... sinfo = (SpawnChainInfo *)mutableDataArray_objectAtIndex(info->spawn_chain_array, 0); if(sinfo) { pinfo->spawn_ttl = sinfo->stage; pinfo->spawn_stage = sinfo; } pinfo->ttl = info->ttl + (int)(random_1() * info->ttlVariant); } /** @brief Force a particle to be spawned. * * @param info Pointer to the particle system * @param location Location spawn point of the particle (can be NULL) * @param velocity Velocity of the new particle if inherited (can be NULL) */ void particle_spawn(ParticleSystemInfo * info, float location[3], float velocity[3]) { ParticleInfo pinfo = {0}; GLColoredTexturedNormalQuad ginfo = {0}; particles_new_particle(info, &pinfo, &ginfo, location, velocity); // Need to spawn a new particle... mutableDataArray_addObject( info->geom_particle_array, &ginfo); mutableDataArray_addObject(info->info_particle_array, &pinfo); } /** @brief Update the particle system by one update frame * * @param info Pointer to the particle system */ void particles_update(ParticleSystemInfo * info) { // Run though all particles, calling the update function... void * info_enumerator; void * geom_enumerator; void * wind_enumerator; void * attractor_enumerator; ParticleInfo * pinfo; GLColoredTexturedNormalQuad * ginfo; WindAreaInfo * winfo; AttractorInfo * ainfo; float r1 = random_1(); float r2 = random_1(); float r3 = random_1(); float r4 = random_1(); if(info->animation == false) { return; } if(info->wait_counter) { info->wait_counter--; return; } info->rate_counter--; if(info->rate_counter <= 0) { int n = 1; if(info->rate < 0) { n = abs(info->rate); } info->rate_counter = info->rate + (int)((random_float() * 2.0 - 1.0) * info->rateVariant); if(info->spawn_debt) { if(n > info->spawn_debt) { n -= info->spawn_debt; info->spawn_debt = 0; } else { info->spawn_debt -= n; n = 0; } } if(info->rate == 0) { n = 0; } while(n--) { ParticleInfo pinfo = {0}; GLColoredTexturedNormalQuad ginfo = {0}; particles_new_particle(info, &pinfo, &ginfo, 0, 0); // Need to spawn a new particle... mutableDataArray_addObject( info->geom_particle_array, &ginfo); mutableDataArray_addObject(info->info_particle_array, &pinfo); } } // Update all particles... info_enumerator = mutableDataArray_objectEnumerator(info->info_particle_array); geom_enumerator = mutableDataArray_objectEnumerator(info->geom_particle_array); while( (info_enumerator = mutableDataArray_nextObject(info->info_particle_array, info_enumerator)) && (geom_enumerator = mutableDataArray_nextObject(info->geom_particle_array, geom_enumerator))) { GLVertex origin; float location[3]; pinfo = (ParticleInfo *)mutableDataArray_enumeratedData(info->info_particle_array, info_enumerator); ginfo = (GLColoredTexturedNormalQuad *)mutableDataArray_enumeratedData(info->geom_particle_array, geom_enumerator); GLGetQuadOrigin(*ginfo, origin); VEC_SET(location, origin.x, origin.y, origin.z); if(info->ttl != 0) { pinfo->ttl--; if(pinfo->ttl <= 0) { // Particle died... make a new one! //particles_new_particle(info, pinfo, ginfo); //info->spawn_debt++; mutableDataArray_removeObject(info->info_particle_array, pinfo); mutableDataArray_removeObject(info->geom_particle_array, ginfo); } } VEC_SUM(pinfo->velocity, pinfo->velocity, info->gravity); // Connect with a wind area? wind_enumerator = mutableArray_objectEnumerator(info->wind_areas_array); while(wind_enumerator = mutableArray_nextObject(info->wind_areas_array, wind_enumerator)) { winfo = (WindAreaInfo*)mutableArray_enumeratedData(info->wind_areas_array, wind_enumerator); //if(origin is inside our box) if(winfo) { if(isPointInCylinder( winfo->p1, winfo->p2, winfo->length_sq, winfo->radius_sq, location) >= 0.0) { pinfo->velocity[0] += winfo->wind[0] + r1 * winfo->variation[0]; pinfo->velocity[1] += winfo->wind[1] + r2 * winfo->variation[1]; pinfo->velocity[2] += winfo->wind[2] + r3 * winfo->variation[2]; } } } // Particle attractors? attractor_enumerator = mutableArray_objectEnumerator(info->attractors_array); while(attractor_enumerator = mutableArray_nextObject(info->attractors_array, attractor_enumerator)) { ainfo = (AttractorInfo*)mutableArray_enumeratedData(info->attractors_array, attractor_enumerator); //if(origin is inside our box) if(ainfo) { float l; float origin[3]; float vector[3]; VEC_SUM(origin, ainfo->locationOffset, info->location); VEC_DIFF(vector, origin, location); VEC_FAST_LENGTH(l, vector); if(l <= (ainfo->radius * ainfo->radius)) { VEC_NORMALIZE(vector); VEC_SCALE(vector, ainfo->strength, vector); VEC_SUM(pinfo->velocity, pinfo->velocity, vector); if(ainfo->inner_radius > 0.0) { // Modify the alpha of close particles, remove // particles that hit the "hole" if(l < ainfo->inner_radius * ainfo->inner_radius) { pinfo->color_delta[3] = 0; pinfo->color[3] = l / (ainfo->inner_radius * ainfo->inner_radius); if(l < ainfo->radius * ainfo->radius * 0.01) { // remove the particle! pinfo->ttl = 0; } } } } } } GLTranslateQuad( *ginfo, pinfo->velocity[0], pinfo->velocity[1], pinfo->velocity[2]); if(info->use_floor) { // Going down? if(pinfo->velocity[1] < 0.0) { if(origin.y < info->floor) { pinfo->velocity[1] = pinfo->velocity[1] * -1.0 * info->hardness; pinfo->velocity[0] *= info->hardness; pinfo->velocity[2] *= info->hardness; GLTranslateQuad( *ginfo, 0.0, fabs(info->floor - origin.y), 0.0); } } } // Spawn chaining support... if(pinfo->spawn_stage) { pinfo->spawn_ttl--; if(pinfo->spawn_ttl <= 0) { if(random_float() < pinfo->spawn_stage->chance) { // create a new particle... particle_spawn( pinfo->spawn_stage->dst_info, location, pinfo->velocity); } pinfo->spawn_stage = (SpawnChainInfo*)mutableDataArray_nextObject(info->spawn_chain_array, pinfo->spawn_stage); if(pinfo->spawn_stage) { pinfo->spawn_ttl = pinfo->spawn_stage->stage; } } } // Color transitions... if(pinfo->color_stage) { pinfo->color_ttl--; if(pinfo->color_ttl <= 0) { float color_target[4]; pinfo->color_stage = (ColorStageInfo*)mutableDataArray_nextObject(info->color_stages_array, pinfo->color_stage); if(!pinfo->color_stage) { // reset! pinfo->color_stage = (ColorStageInfo *)mutableDataArray_objectAtIndex(info->color_stages_array, 0); } color_target[0] = pinfo->color_stage->color[0] + r1 * pinfo->color_stage->colorVariant[0]; color_target[1] = pinfo->color_stage->color[1] + r2 * pinfo->color_stage->colorVariant[1]; color_target[2] = pinfo->color_stage->color[2] + r3 * pinfo->color_stage->colorVariant[2]; color_target[3] = pinfo->color_stage->color[3] + r4 * pinfo->color_stage->colorVariant[3]; pinfo->color_ttl = pinfo->color_stage->ttl; VEC_DIFF_4(pinfo->color_delta, color_target, pinfo->color); VEC_SCALE_4(pinfo->color_delta, (1.0f / (float)pinfo->color_stage->ttl), pinfo->color_delta); } else { VEC_SUM_4(pinfo->color, pinfo->color, pinfo->color_delta); GLSetQuadColors( *ginfo, pinfo->color[0]*info->brightness, pinfo->color[1]*info->brightness, pinfo->color[2]*info->brightness, pinfo->color[3], pinfo->color[0]*info->brightness, pinfo->color[1]*info->brightness, pinfo->color[2]*info->brightness, pinfo->color[3], pinfo->color[0]*info->brightness, pinfo->color[1]*info->brightness, pinfo->color[2]*info->brightness, pinfo->color[3], pinfo->color[0]*info->brightness, pinfo->color[1]*info->brightness, pinfo->color[2]*info->brightness, pinfo->color[3]); } } } } /** @brief Render the particle system * * @param info Pointer to the particle system */ void particles_render(ParticleSystemInfo * info) { int style = 0; if(info->wait_counter) { return; } getPreference("Shader Group", &style); if(style != 0 && use_vertex_programs == 0) { setPreference("Shader Group", 0); style = 0; } if(info->texture_ref >= 0) { glTexture_setTexture(gTxtMgrRef, info->texture_ref); } switch(style) { case 0: // No shaders { float right[3]; float up[3]; float mat[16]; GLColoredTexturedNormalQuad * quad = (GLColoredTexturedNormalQuad *)mutableDataArray_bytes(info->geom_particle_array); long count = mutableDataArray_count(info->geom_particle_array); float alpha = 1.0; glGetFloatv(GL_MODELVIEW_MATRIX, mat); VEC_SET(right, mat[0], mat[4], mat[8]); VEC_SET(up, mat[1], mat[5], mat[9]); glColor3f(1.0, 0.0, 0.0); glBegin(GL_QUADS); while(count--) { glTexCoord2d(quad->at.u, quad->at.v); glColor4f(quad->ac.r, quad->ac.g, quad->ac.b, quad->ac.a * alpha); glVertex3f( quad->a.x + right[0] * quad->an.x + up[0] * quad->an.y, quad->a.y + right[1] * quad->an.x + up[1] * quad->an.y, quad->a.z + right[2] * quad->an.x + up[2] * quad->an.y); glTexCoord2d(quad->bt.u, quad->bt.v); glColor4f(quad->bc.r, quad->bc.g, quad->bc.b, quad->bc.a * alpha); glVertex3f( quad->a.x + right[0] * quad->bn.x + up[0] * quad->bn.y, quad->a.y + right[1] * quad->bn.x + up[1] * quad->bn.y, quad->a.z + right[2] * quad->bn.x + up[2] * quad->bn.y); glTexCoord2d(quad->ct.u, quad->ct.v); glColor4f(quad->cc.r, quad->cc.g, quad->cc.b, quad->cc.a * alpha); glVertex3f( quad->a.x + right[0] * quad->cn.x + up[0] * quad->cn.y, quad->a.y + right[1] * quad->cn.x + up[1] * quad->cn.y, quad->a.z + right[2] * quad->cn.x + up[2] * quad->cn.y); glTexCoord2d(quad->dt.u, quad->dt.v); glColor4f(quad->dc.r, quad->dc.g, quad->dc.b, quad->dc.a * alpha); glVertex3f( quad->a.x + right[0] * quad->dn.x + up[0] * quad->dn.y, quad->a.y + right[1] * quad->dn.x + up[1] * quad->dn.y, quad->a.z + right[2] * quad->dn.x + up[2] * quad->dn.y); quad++; } glEnd(); } break; case 1: // Billboarding shader glInterleavedArrays(GL_T2F_C4F_N3F_V3F, 0, mutableDataArray_bytes(info->geom_particle_array)); glDrawArrays( GL_QUADS, 0, mutableDataArray_count(info->geom_particle_array) * 4); break; } if(info->texture_ref >= 0) { glTexture_endTexture(gTxtMgrRef, info->texture_ref); } } /** @brief Set up state for rendering the particle system * */ void particle_pre_render() { int style; getPreference("Shader Group", &style); if(style != 0 && use_vertex_programs == 0) { setPreference("Shader Group", 0); style = 0; } if(style == 1) { glBindProgramARB(GL_VERTEX_PROGRAM_ARB, gBulletinBoardVertexShader); glEnable(GL_VERTEX_PROGRAM_ARB); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, 0, 1.0, 1.0, 1.0, 1.0); } } /** @brief Tear down state for rendering the particle system * */ void particle_post_render() { int style; getPreference("Shader Group", &style); if(style != 0 && use_vertex_programs == 0) { setPreference("Shader Group", 0); style = 0; } if(style == 1) { glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisable(GL_VERTEX_PROGRAM_ARB); } }