Particle System – Collector Corner Cases

Well, it turns out I was able to guarantee the destination of particles by a deadline. This requires a little bit of cheating; as the lifetime of the particle advances, the velocity vector dictated by the physics is blended with a velocity vector of the same magnitude pointing directly at the destination. This, in combination with a maximum velocity can virtually guarantee that the particle will hit its destination by a specific time. If the velocity vector is blended to point directly at the destination by half the lifetime, and the forces acting on the particle only contribute to increase the velocity towards the destination regardless of its position, then the particle is guaranteed to hit the destination by the end of its life.

The following code accumulates the forces acting on the particle, blends the velocity vector with a vector pointing directly at the destination based on lifetime percentage, and detects if the particle passed through the destination in order to “stick” it to the destination. The code isn’t optimized, and is a bit messy, so take it with a grain of salt. It’s also not quite as elegant as I would like. Notice that within a distance of 1, the velocity vector points directly into the collector. This can be considered the event horizon; to save me the headache of dealing with corner cases when calculating forces given a magVector length < 1.

void Collector::UpdateParticle(float dt, Particle& particle, UINT index)
{

    D3DXVECTOR3 magVector = GetVector(particle, index);
    float magLength = D3DXVec3Length(&magVector);

    if (magLength >= 1)
    {
        D3DXVECTOR3 force = D3DXVECTOR3(0.0f, 0.0f ,0.0f);
        force += GetSpringForce(magVector, magLength, particle.m_velocity);
        force += GetOrbitalDecayForce(magVector, magLength, particle.m_velocity,
                                      particle.m_pos, GetDestination(index), dt);
        particle.m_velocity += force*dt;

        // Calculate blended vector
        float velLength = D3DXVec3Length(&particle.m_velocity);
        float perc = ((float)particle.m_timeElapsed / ((float)particle.m_lifetime)/2);
        perc = min(perc, 1.0f);
        perc = pow(perc, 1);
        D3DXVECTOR3 vec1 = particle.m_velocity / velLength * (1.0f - perc);
        D3DXVECTOR3 vec2 = magVector / magLength * perc;
        D3DXVECTOR3 vec3 = vec1 + vec2;
        D3DXVec3Normalize(&vec3, &vec3);
        velLength = min(velLength, MAX_VELOCITY);
        particle.m_velocity = vec3 * velLength;
        particle.m_pos += particle.m_velocity * dt;
    }
    else
    {
        D3DXVec3Normalize(&magVector, &magVector);
        float velLength = D3DXVec3Length(&particle.m_velocity);
        velLength = min(velLength, MAX_VELOCITY);
        particle.m_velocity = magVector * velLength;
        particle.m_pos += particle.m_velocity * dt;
    }
    // Fix position if passing through particle
    D3DXVECTOR3 newMagVector = GetVector(particle, index);
    D3DXVec3Normalize(&newMagVector, &newMagVector);

    float dot = D3DXVec3Dot(&magVector, &newMagVector);
    if ( dot <= (-1.0f + EPSILON) && dot >= -1.0f)
    {
        particle.m_pos = GetDestination(index);
    }
}

So, with this new code, I decided to make a nice little demo. Consider this a sneak peak at the various shapes and animations possible by associating individual particles at different collectors :)

Leave a Comment

Tags: , ,

February 4, 2011 Graphics, Physics