/* =============== idParticleStage::ParticleOrigin =============== */ void idParticleStage::ParticleOrigin( particleGen_t* g, idVec3& origin ) const { if( customPathType == PPATH_STANDARD ) { // // find intial origin distribution // float radiusSqr, angle1, angle2; switch( distributionType ) { case PDIST_RECT: // ( sizeX sizeY sizeZ ) { origin[0] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * distributionParms[0]; origin[1] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * distributionParms[1]; origin[2] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * distributionParms[2]; break; } case PDIST_CYLINDER: // ( sizeX sizeY sizeZ ringFraction ) { angle1 = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ) * idMath::TWO_PI; idMath::SinCos16( angle1, origin[0], origin[1] ); origin[2] = ( ( randomDistribution ) ? g->random.CRandomFloat() : 1.0f ); // reproject points that are inside the ringFraction to the outer band if( distributionParms[3] > 0.0f ) { radiusSqr = origin[0] * origin[0] + origin[1] * origin[1]; if( radiusSqr < distributionParms[3] * distributionParms[3] ) { // if we are inside the inner reject zone, rescale to put it out into the good zone float f = sqrt( radiusSqr ) / distributionParms[3]; float invf = 1.0f / f; float newRadius = distributionParms[3] + f * ( 1.0f - distributionParms[3] ); float rescale = invf * newRadius; origin[0] *= rescale; origin[1] *= rescale; } } origin[0] *= distributionParms[0]; origin[1] *= distributionParms[1]; origin[2] *= distributionParms[2]; break; } case PDIST_SPHERE: // ( sizeX sizeY sizeZ ringFraction ) { // iterating with rejection is the only way to get an even distribution over a sphere if( randomDistribution ) { do { origin[0] = g->random.CRandomFloat(); origin[1] = g->random.CRandomFloat(); origin[2] = g->random.CRandomFloat(); radiusSqr = origin[0] * origin[0] + origin[1] * origin[1] + origin[2] * origin[2]; } while( radiusSqr > 1.0f ); } else { origin.Set( 1.0f, 1.0f, 1.0f ); radiusSqr = 3.0f; } if( distributionParms[3] > 0.0f ) { // we could iterate until we got something that also satisfied ringFraction, // but for narrow rings that could be a lot of work, so reproject inside points instead if( radiusSqr < distributionParms[3] * distributionParms[3] ) { // if we are inside the inner reject zone, rescale to put it out into the good zone float f = sqrt( radiusSqr ) / distributionParms[3]; float invf = 1.0f / f; float newRadius = distributionParms[3] + f * ( 1.0f - distributionParms[3] ); float rescale = invf * newRadius; origin[0] *= rescale; origin[1] *= rescale; origin[2] *= rescale; } } origin[0] *= distributionParms[0]; origin[1] *= distributionParms[1]; origin[2] *= distributionParms[2]; break; } } // offset will effect all particle origin types // add this before the velocity and gravity additions origin += offset; // // add the velocity over time // idVec3 dir; switch( directionType ) { case PDIR_CONE: { // angle is the full angle, so 360 degrees is any spherical direction angle1 = g->random.CRandomFloat() * directionParms[0] * idMath::M_DEG2RAD; angle2 = g->random.CRandomFloat() * idMath::PI; float s1, c1, s2, c2; idMath::SinCos16( angle1, s1, c1 ); idMath::SinCos16( angle2, s2, c2 ); dir[0] = s1 * c2; dir[1] = s1 * s2; dir[2] = c1; break; } case PDIR_OUTWARD: { dir = origin; dir.Normalize(); dir[2] += directionParms[0]; break; } } // add speed float iSpeed = speed.Integrate( g->frac, g->random ); origin += dir * iSpeed * particleLife; } else { // // custom paths completely override both the origin and velocity calculations, but still // use the standard gravity // float angle1, angle2, speed1, speed2; switch( customPathType ) { case PPATH_HELIX: // ( sizeX sizeY sizeZ radialSpeed axialSpeed ) { speed1 = g->random.CRandomFloat(); speed2 = g->random.CRandomFloat(); angle1 = g->random.RandomFloat() * idMath::TWO_PI + customPathParms[3] * speed1 * g->age; float s1, c1; idMath::SinCos16( angle1, s1, c1 ); origin[0] = c1 * customPathParms[0]; origin[1] = s1 * customPathParms[1]; origin[2] = g->random.RandomFloat() * customPathParms[2] + customPathParms[4] * speed2 * g->age; break; } case PPATH_FLIES: // ( radialSpeed axialSpeed size ) { speed1 = idMath::ClampFloat( 0.4f, 1.0f, g->random.CRandomFloat() ); speed2 = idMath::ClampFloat( 0.4f, 1.0f, g->random.CRandomFloat() ); angle1 = g->random.RandomFloat() * idMath::PI * 2 + customPathParms[0] * speed1 * g->age; angle2 = g->random.RandomFloat() * idMath::PI * 2 + customPathParms[1] * speed1 * g->age; float s1, c1, s2, c2; idMath::SinCos16( angle1, s1, c1 ); idMath::SinCos16( angle2, s2, c2 ); origin[0] = c1 * c2; origin[1] = s1 * c2; origin[2] = -s2; origin *= customPathParms[2]; break; } case PPATH_ORBIT: // ( radius speed axis ) { angle1 = g->random.RandomFloat() * idMath::TWO_PI + customPathParms[1] * g->age; float s1, c1; idMath::SinCos16( angle1, s1, c1 ); origin[0] = c1 * customPathParms[0]; origin[1] = s1 * customPathParms[0]; origin.ProjectSelfOntoSphere( customPathParms[0] ); break; } case PPATH_DRIP: // ( speed ) { origin[0] = 0.0f; origin[1] = 0.0f; origin[2] = -( g->age * customPathParms[0] ); break; } default: { common->Error( "idParticleStage::ParticleOrigin: bad customPathType" ); } } origin += offset; } // adjust for the per-particle smoke offset origin *= g->axis; origin += g->origin; // add gravity after adjusting for axis if( worldGravity ) { idVec3 gra( 0, 0, -gravity ); gra *= g->renderEnt->axis.Transpose(); origin += gra * g->age * g->age; } else { origin[2] -= gravity * g->age * g->age; } }