//------------------------------------------------------------------------------ // void gosFX::ParticleCloud::Start(ExecuteInfo *info) { Check_Object(this); Check_Pointer(info); // //-------------------------------------------------------------------------- // Let effect initialize, then figure out how many particles we want to make //-------------------------------------------------------------------------- // Effect::Start(info); Specification *spec = GetSpecification(); Check_Object(spec); Stuff::Scalar newbies = spec->m_startingPopulation.ComputeValue(m_age, m_seed); Min_Clamp(newbies, 0.0f); m_birthAccumulator += newbies; }
//------------------------------------------------------------------------------ // void gosFX::Effect::Start(ExecuteInfo *info) { Check_Object(this); Check_Pointer(info); gos_PushCurrentHeap(Heap); // //--------------------------------------------------------------------- // Don't override m_lastran if we are issuing a Start command while the // effect is already running //--------------------------------------------------------------------- // if (!IsExecuted() || m_lastRan == -1.0) m_lastRan = info->m_time; SetExecuteOn(); // //------------------------------------------- // If no seed was provided, pick one randomly //------------------------------------------- // m_seed = (info->m_seed == -1.0f) ? Stuff::Random::GetFraction() : info->m_seed; Verify(m_seed >= 0.0f && m_seed <= 1.0f); // //-------------------------------------------------------------------- // Figure out how long the emitter will live and its initial age based // upon the effect seed //-------------------------------------------------------------------- // Check_Object(m_specification); if (info->m_age == -1.0f) { Stuff::Scalar lifetime = m_specification->m_lifeSpan.ComputeValue(m_seed, 0.0f); Min_Clamp(lifetime, 0.033333f); m_ageRate = 1.0f / lifetime; m_age = 0; } else { m_age = info->m_age; m_ageRate = info->m_ageRate; Verify(m_age >= 0.0f && m_age <= 1.0f); } // //-------------------- // Set up the matrices //-------------------- // Check_Object(info->m_parentToWorld); m_localToWorld.Multiply(m_localToParent, *info->m_parentToWorld); // //------------------------- // Set up the event pointer //------------------------- // m_event.First(); gos_PopCurrentHeap(); }
// //############################################################################# //############################################################################# // UnitQuaternion& UnitQuaternion::operator=(const LinearMatrix4D &matrix) { Check_Pointer(this); Check_Object(&matrix); // //------------------------------------------------------------------------ // Compute the w component. If it is close enough to zero, then we have a // 180 degree pivot, so figure out the correct axis to rotate around //------------------------------------------------------------------------ // w = (1.0f + matrix(0,0) + matrix(1,1) + matrix(2,2)) * 0.25f; if (Small_Enough(w,1e-2f)) { Verify(w >= -SMALL); if (w<0.0f) { w = 0.0f; } // //---------------------------------------------------------------- // Figure out the length of each component of the axis of rotation //---------------------------------------------------------------- // Scalar temp = (1.0f + matrix(0,0)) * 0.5f - w; Min_Clamp(temp, 0.0f); x = Sqrt(temp); temp = (1.0f + matrix(1,1)) * 0.5f - w; Min_Clamp(temp, 0.0f); y = Sqrt(temp); temp = (1.0f + matrix(2,2)) * 0.5f - w; Min_Clamp(temp, 0.0f); z = Sqrt(temp); w = Sqrt(w); // //------------------------------------------- // Now figure out the signs of the components //------------------------------------------- // if (matrix(0,1) < matrix(1,0)) { z = -z; } if (matrix(2,0) < matrix(0,2)) { y = -y; } if (matrix(1,2) < matrix(2,1)) { x = -x; } } // //---------------------------------------------------------- // Otherwise, determine x, y, and z directly from the matrix //---------------------------------------------------------- // else { Verify(w>0.0f); w = Sqrt(w); x = (matrix(1,2) - matrix(2,1)) * 0.25f / w; y = (matrix(2,0) - matrix(0,2)) * 0.25f / w; z = (matrix(0,1) - matrix(1,0)) * 0.25f / w; } Normalize(); return *this; }
//------------------------------------------------------------------------------ // void gosFX::ParticleCloud::CreateNewParticle( unsigned index, Stuff::Point3D *translation ) { Check_Object(this); // //---------------------------------------------------- // Figure out the age and age rate of the new particle //---------------------------------------------------- // Specification *spec = GetSpecification(); Check_Object(spec); Particle *particle = GetParticle(index); Check_Object(particle); particle->m_age = 0.0f; Stuff::Scalar min_seed = spec->m_minimumChildSeed.ComputeValue(m_age, m_seed); Stuff::Scalar seed_range = spec->m_maximumChildSeed.ComputeValue(m_age, m_seed) - min_seed; Stuff::Scalar seed = Stuff::Random::GetFraction()*seed_range + min_seed; Clamp(seed, 0.0f, 1.0f); particle->m_seed = seed; Stuff::Scalar lifetime = spec->m_pLifeSpan.ComputeValue(m_age, seed); Min_Clamp(lifetime, 0.0333333f); particle->m_ageRate = 1.0f / lifetime; // //-------------------------------- // Figure out the initial position //-------------------------------- // Stuff::YawPitchRange initial_p( Stuff::Random::GetFraction() * Stuff::Two_Pi, Stuff::Random::GetFraction() * Stuff::Pi - Stuff::Pi_Over_2, Stuff::Random::GetFraction() ); Stuff::Vector3D position(initial_p); translation->x = position.x * spec->m_emitterSizeX.ComputeValue(m_age, seed); translation->y = position.y * spec->m_emitterSizeY.ComputeValue(m_age, seed); translation->z = position.z * spec->m_emitterSizeZ.ComputeValue(m_age, seed); // //-------------------------------- // Figure out the initial velocity //-------------------------------- // Stuff::Scalar pitch_min = spec->m_minimumDeviation.ComputeValue(m_age, seed); Stuff::Scalar pitch_range = spec->m_maximumDeviation.ComputeValue(m_age, seed) - pitch_min; if (pitch_range < 0.0f) pitch_range = 0.0f; pitch_min += pitch_range * Stuff::Random::GetFraction() - Stuff::Pi_Over_2; Stuff::YawPitchRange initial_v( Stuff::Random::GetFraction() * Stuff::Two_Pi, pitch_min, spec->m_startingSpeed.ComputeValue(m_age, seed) ); particle->m_localLinearVelocity = initial_v; }
//------------------------------------------------------------------------------ // bool gosFX::ParticleCloud::Execute(ExecuteInfo *info) { Check_Object(this); Check_Object(info); Verify(IsExecuted()); // //-------------------------------------------------------------------- // If we were given a new matrix, see if we have a parent. If so, // concatenate the two and figure out its inverse. If no parent, then // just invert the new matrix, otherwise just use the existing one //-------------------------------------------------------------------- // Stuff::LinearMatrix4D new_world_to_local; Stuff::LinearMatrix4D *matrix = NULL; int sim_mode = GetSimulationMode(); if (sim_mode == DynamicWorldSpaceSimulationMode) { Stuff::LinearMatrix4D local_to_world; local_to_world.Multiply(m_localToParent, *info->m_parentToWorld); new_world_to_local.Invert(local_to_world); matrix = &new_world_to_local; } // //-------------------------------------------------------- // Figure out the birth rate and request the new particles //-------------------------------------------------------- // Specification *spec = GetSpecification(); Check_Object(spec); Stuff::Scalar dT = static_cast<Stuff::Scalar>(info->m_time - m_lastRan); Verify(dT >= 0.0f); Stuff::Scalar prev_age = m_age; m_age += dT * m_ageRate; if (m_age >= 1.0f) m_birthAccumulator = 0.0f; else { Stuff::Scalar new_life = spec->m_particlesPerSecond.ComputeValue(m_age, m_seed); Min_Clamp(new_life, 0.0f); m_birthAccumulator += dT * new_life; } // //----------------------------------- // Deal with all the active particles //----------------------------------- // int i; int last_real = -1; for (i = 0; i < m_activeParticleCount; i++) { // //-------------------------------------------------------------------- // If the particle is active, age it and if it is not yet time to die, // go to the next particle, otherwise kill it //-------------------------------------------------------------------- // Particle *particle = GetParticle(i); Check_Object(particle); if (particle->m_age < 1.0f) { particle->m_age += dT*particle->m_ageRate; if (AnimateParticle(i, matrix, info->m_time)) { last_real = i; continue; } DestroyParticle(i); } // //-------------------------------------------------------------------- // If there are new particles to be born, go ahead and create them now //-------------------------------------------------------------------- // if (m_birthAccumulator >= 1.0f) { Stuff::Point3D translation; CreateNewParticle(i, &translation); if (AnimateParticle(i, matrix, info->m_time)) last_real = i; else DestroyParticle(i); m_birthAccumulator -= 1.0f; } } m_activeParticleCount = last_real + 1; // //---------------------------------------------------------------------- // If there are still new particles to be born, then we must try to grow // the active particle count //---------------------------------------------------------------------- // while ( m_birthAccumulator >= 1.0f && m_activeParticleCount < spec->m_maxParticleCount ) { i = m_activeParticleCount++; Stuff::Point3D translation; CreateNewParticle(i, &translation); if (!AnimateParticle(i, matrix, info->m_time)) { DestroyParticle(i); --m_activeParticleCount; } m_birthAccumulator -= 1.0f; } // //--------------------------------------------------------- // Only allow fractional births to carry over to next frame //--------------------------------------------------------- // m_birthAccumulator -= static_cast<Stuff::Scalar>(floor(m_birthAccumulator)); // //---------------------------- // Now let effect do its thing //---------------------------- // m_age = prev_age; return Effect::Execute(info); }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Scalar Line3D::GetDistanceTo( const Sphere &sphere, Scalar *penetration ) const { Check_Object(this); Check_Object(&sphere); Check_Pointer(penetration); // //------------------------------------------------------------------- // Determine if ray intersects bounding sphere of object. If sphere // is (X-C)*(X-C) = R^2 and ray is X = t*D+L for t >= 0, then // intersection is obtained by plugging X into sphere equation to // get quadratic: (D*D)t^2 + 2*(D*(L-C))t + (L-C)*(L-C) = 0 // Define a = D*D = 1.0f, b = 2*(D*(L-C)), and c = (L-C)*(L-C). //------------------------------------------------------------------- // Vector3D diff; diff.Subtract(origin, sphere.center); Scalar b = (direction*diff) * 2.0f; Scalar c = (diff*diff) - sphere.radius*sphere.radius; // //------------------------------------------------------------------------- // If penetration is negative, we couldn't hit the sphere at all. If it is // really small, it touches at only one place //------------------------------------------------------------------------- // *penetration = b*b - 4.0f*c; if (*penetration < -SMALL) { return -1.0f; } b *= -0.5f; if (*penetration<SMALL) { *penetration = 0.0f; Min_Clamp(b, 0.0f); return (b > length) ? -1.0f : b; } // //------------------------------------------------------------- // We know we hit the sphere, so figure out where it first hits //------------------------------------------------------------- // *penetration = 0.5f * Sqrt(*penetration); if (b + *penetration < -SMALL) { return -1.0f; } b -= *penetration; if (b > length) { return -1.0f; } Min_Clamp(b, 0.0f); return b; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Scalar Line3D::GetDistanceTo( const OBB& box, int *first_axis ) { Check_Object(this); Check_Object(&box); Check_Pointer(first_axis); // //------------------------------------------------------------------------ // Get the vector from the line to the centerpoint of the OBB. All planes // will be generated relative to this //------------------------------------------------------------------------ // Point3D center; center = box.localToParent; Vector3D delta; delta.Subtract(center, origin); // //-------------------------------------------------- // Set up the loop to examine each of the three axes //-------------------------------------------------- // Scalar enters = -100.0f - length; Scalar leaves = length + 100.0f; for (int axis=X_Axis; axis <= Z_Axis; ++axis) { UnitVector3D normal( box.localToParent(axis, X_Axis), box.localToParent(axis, Y_Axis), box.localToParent(axis, Z_Axis) ); // //---------------------------------------------------------------------- // Now, we have to calculate how far the line moves along the normal per // unit traveled down the line. If it is perpendicular to the normal, // then it will hit or miss based solely upon the origin location //---------------------------------------------------------------------- // Scalar drift = direction * normal; Scalar distance; if (Small_Enough(drift)) { distance = delta * normal; if (Fabs(distance) > box.axisExtents[axis]) return -1.0f; else continue; } // //-------------------------------------------------------------------- // We know the line is not parallel, so we will now calculate how long // the line will stay inside the box. We also will calculate how far // from the origin to the centerplane of the OBB //-------------------------------------------------------------------- // drift = 1.0f / drift; Scalar span = box.axisExtents[axis] * Fabs(drift); distance = (delta * normal) * drift; // //-------------------------------------------------------------------- // Now adjust where the line can enter and leave the OBB, and if it is // no longer possible to hit, stop checking //-------------------------------------------------------------------- // Scalar enter = distance - span; Scalar leave = distance + span; if (enter > enters) { *first_axis = axis; enters = enter; } if (leave < leaves) leaves = leave; if (enters > leaves) return -1.0f; } // //------------------------------------------------------------------------- // If we got here, then the line in theory can hit the OBB, so now we check // to make sure it hits it within the allowed span of the line //------------------------------------------------------------------------- // if (leaves < 0.0f || enters > length) return -1.0f; Min_Clamp(enters, 0.0f); return enters; }