void CSmokeTrailPainter::OnUpdate (SEffectUpdateCtx &Ctx) // OnUpdate // // Update the painter { if (m_iLastDirection != -1) { // Update particle motion m_Particles.UpdateMotionLinear(); // Create new particles (we create the particles after we update // the motion because we start out with some particles) if (m_iEmitLifetime == -1 || m_iTick < m_iEmitLifetime) { CreateNewParticles(m_pCreator->GetNewParticleCount(), m_iLastDirection); } // Update m_iTick++; } }
void CSmokeTrailPainter::GetRect (RECT *retRect) const // GetRect // // Returns the RECT of the effect centered on 0,0 { *retRect = m_Particles.GetBounds(); }
void CParticleJetEffectPainter::GetRect (RECT *retRect) const // GetRect // // Returns the rect bounds of the image { *retRect = m_Particles.GetBounds(); }
void CSmokeTrailPainter::Paint (CG16bitImage &Dest, int x, int y, SViewportPaintCtx &Ctx) // Paint // // Paint { int iParticleLifetime = m_pCreator->GetParticleLifetimeMax(); // Particles move the opposite direction from the shot int iTrailDirection = (Ctx.iRotation + m_pCreator->GetRotation()) % 360; // If we haven't created any particles yet, do it now if (m_iLastDirection == -1) { CreateNewParticles(m_pCreator->GetNewParticleCount(), iTrailDirection); } // Paint with the painter if (m_pParticlePainter) { // If we can get a paint descriptor, use that because it is faster SParticlePaintDesc Desc; if (m_pParticlePainter->GetParticlePaintDesc(&Desc)) { Desc.iMaxLifetime = iParticleLifetime; m_Particles.Paint(Dest, x, y, Ctx, Desc); } // Otherwise, we use the painter for each particle else m_Particles.Paint(Dest, x, y, Ctx, m_pParticlePainter); } // Update m_iLastDirection = iTrailDirection; }
void CParticleJetEffectPainter::CreateLinearParticles (CSpaceObject *pObj, int iCount, const CVector &vInitialPos, const CVector &vInitialVel) // CreateLinearParticles // // Creates new particles on a straight line { int i; // Compute some basic stuff const Metric rJitterFactor = LIGHT_SPEED / 100000.0; Metric rCurRotation = AngleToRadians(180 + m_iXformRotation + m_iCurDirection); // Compute the spread angle, in radians Metric rSpread = AngleToRadians(Max(0, m_SpreadAngle.Roll())); Metric rHalfSpread = 0.5 * rSpread; // Calculate where last tick's particles would be based on the last rotation. Metric rAveSpeed = m_EmitSpeed.GetAveValue() * LIGHT_SPEED / 100.0; CVector vCurStart = (m_vLastEmitPos + (m_rXformTime * ::PolarToVectorRadians(rCurRotation, rAveSpeed * g_SecondsPerUpdate))) - vInitialPos; // Create particles for (i = 0; i < iCount; i++) { Metric rSlide = mathRandom(0, 9999) / 10000.0; // We place the particle along the line betwen the current // and last emit positions CVector vPos = vInitialPos + rSlide * vCurStart; // Generate a random velocity backwards Metric rRotation = rCurRotation + (rHalfSpread * mathRandom(-1000, 1000) / 1000.0); Metric rSpeed = m_EmitSpeed.Roll() * LIGHT_SPEED / 100.0; CVector vVel = m_rXformTime * (vInitialVel + ::PolarToVectorRadians(rRotation, rSpeed + rJitterFactor * mathRandom(-500, 500))); // Lifetime int iLifeLeft = m_ParticleLifetime.Roll(); // Add the particle m_Particles.AddParticle(vPos, vVel, iLifeLeft, AngleToDegrees(rRotation)); } // Remember the last position m_iLastDirection = m_iCurDirection; m_vLastEmitPos = vInitialPos; }
void CParticleJetEffectPainter::OnMove (SEffectMoveCtx &Ctx, bool *retbBoundsChanged) // OnMove // // Move the particles { // Update the single-particle painter if (m_pParticlePainter) m_pParticlePainter->OnMove(Ctx); // Update particle motion m_Particles.UpdateMotionLinear(); // If we're using the object's motion, adjust now if (m_bUseObjectMotion && Ctx.pObj) { CVector vToOldPos; if (m_bTrackingObject) { CVector vCurPos = Ctx.pObj->GetPos(); vToOldPos = Ctx.vOldPos - vCurPos; } else { Metric rSpeed = Ctx.pObj->GetVel().Length(); vToOldPos = ::PolarToVector(180 + m_iLastDirection, rSpeed * g_SecondsPerUpdate); } // Move all particles by the given amount m_Particles.Move(vToOldPos); } // Bounds are always changing if (retbBoundsChanged) *retbBoundsChanged = true; }
void CParticleJetEffectPainter::InitParticles (const CVector &vInitialPos) // Init // // Make sure particle array is initialized { if (m_Particles.GetCount() == 0) { // Compute the maximum number of particles that we could ever have int iNewParticleRate = m_EmitRate.GetMaxValue(); int iParticleLifetime = m_ParticleLifetime.GetMaxValue(); int iMaxParticleCount = Max(0, iParticleLifetime * iNewParticleRate); // Initialize the array m_Particles.Init(iMaxParticleCount); } }
void CSmokeTrailPainter::OnWriteToStream (IWriteStream *pStream) // OnWriteToStream // // Write to stream { m_Particles.WriteToStream(pStream); pStream->Write((char *)&m_iLastDirection, sizeof(DWORD)); pStream->Write((char *)&m_iEmitLifetime, sizeof(DWORD)); pStream->Write((char *)&m_iTick, sizeof(DWORD)); CEffectCreator::WritePainterToStream(pStream, m_pParticlePainter); }
void CSmokeTrailPainter::OnReadFromStream (SLoadCtx &Ctx) // OnReadFromStream // // Load from stream { m_Particles.ReadFromStream(Ctx); Ctx.pStream->Read((char *)&m_iLastDirection, sizeof(DWORD)); Ctx.pStream->Read((char *)&m_iEmitLifetime, sizeof(DWORD)); Ctx.pStream->Read((char *)&m_iTick, sizeof(DWORD)); if (m_pParticlePainter) m_pParticlePainter->Delete(); m_pParticlePainter = CEffectCreator::CreatePainterFromStreamAndCreator(Ctx, m_pCreator->GetParticleEffect()); }
void CParticleJetEffectPainter::OnUpdate (SEffectUpdateCtx &Ctx) // OnUpdate // // Update the painter { // Update the single-particle painter if (m_pParticlePainter) m_pParticlePainter->OnUpdate(Ctx); // LATER: Support damage #if 0 // If we have a wake potential or if the particles do damage // then we need to hit test against all objects in the system. Ctx.pDamageDesc = m_pCreator->GetDamageDesc(); Ctx.iWakePotential = m_pCreator->GetWakePotential(); if ((Ctx.pDamageDesc || Ctx.iWakePotential > 0) && Ctx.pSystem) { // Update m_Particles.Update(Ctx); } #endif // Create new particles if (!Ctx.bFade) CreateNewParticles(Ctx.pObj, m_EmitRate.Roll(), Ctx.vEmitPos, CalcInitialVel(Ctx.pObj)); else if (m_bUseObjectMotion && Ctx.pObj) m_vLastEmitPos = Ctx.pObj->GetPos(); // If we're fading, reset direction (otherwise, when painting thruster // effects we'll try to interpolate between stale directions). if (Ctx.bFade) m_iCurDirection = -1; }
void CSmokeTrailPainter::CreateNewParticles (int iCount, int iDirection) // CreateNewParticles // // Create the new particles for a tick { int i; int iAngleRandomness = m_pCreator->GetSpread() / 7; for (i = 0; i < iCount; i++) { // Generate a random velocity for the particle in a cone around the direction Metric rInitialSpeed = m_pCreator->GetEmitSpeed(); CVector vVel = PolarToVector( (iDirection + 360 + mathRandom(-iAngleRandomness, iAngleRandomness)), rInitialSpeed); // Add a random little kick vVel = vVel + PolarToVector(mathRandom(0, 359), rInitialSpeed * m_pCreator->GetSpread() / 100.0); // Position CVector vPos = vVel * (mathRandom(0, 100) / 100.0); // Lifetime int iLifeLeft = m_pCreator->GetParticleLifetime(); // Add the particle m_Particles.AddParticle(vPos, vVel, iLifeLeft); } }
void CParticleJetEffectPainter::Paint (CG32bitImage &Dest, int x, int y, SViewportPaintCtx &Ctx) // Paint // // Paint the effect { int iParticleLifetime = m_ParticleLifetime.GetMaxValue(); // Particles move the opposite direction from the shot int iTrailDirection = Ctx.iRotation; // If we're using the object center then we paint at the object center. // Otherwise we paint at the given position. int xPaint; int yPaint; if (m_bUseObjectCenter) { if (Ctx.pObj) Ctx.XForm.Transform(Ctx.pObj->GetPos(), &xPaint, &yPaint); else { // If we don't have an object then we use the viewport center. This // handles the case where we paint in TransData (where there is // no object). xPaint = Ctx.xCenter; yPaint = Ctx.yCenter; } } else { xPaint = x; yPaint = y; } // If we haven't created any particles yet, do it now if (m_iCurDirection == -1 && !Ctx.bFade) { m_iLastDirection = iTrailDirection; m_iCurDirection = iTrailDirection; // Figure out the position where we create particles CVector vPos; // If we're using the object center then it means that x,y is where // we emit particles from. We need to convert from screen coordinates // to object-relative coordinates. if (m_bUseObjectCenter) vPos = CVector((x - xPaint) * g_KlicksPerPixel, (yPaint - y) * g_KlicksPerPixel); // Initialize last emit position m_vLastEmitPos = (m_bUseObjectMotion && Ctx.pObj ? Ctx.pObj->GetPos() + vPos : vPos); // Create particles CreateNewParticles(Ctx.pObj, m_EmitRate.Roll(), vPos, CalcInitialVel(Ctx.pObj)); } // Paint with the painter if (m_pParticlePainter) { // If we can get a paint descriptor, use that because it is faster SParticlePaintDesc Desc; if (m_pParticlePainter->GetParticlePaintDesc(&Desc)) { Desc.iMaxLifetime = iParticleLifetime; m_Particles.Paint(Dest, xPaint, yPaint, Ctx, Desc); } // Otherwise, we use the painter for each particle else m_Particles.Paint(Dest, xPaint, yPaint, Ctx, m_pParticlePainter); } // Update m_iCurDirection = iTrailDirection; }
void CParticleJetEffectPainter::CreateInterpolatedParticles (CSpaceObject *pObj, int iCount, const CVector &vInitialPos, const CVector &vInitialVel) // CreateInterpolatedParticles // // Creates particles interpolated between to directions. { int i; // Compute some basic stuff const Metric rJitterFactor = LIGHT_SPEED / 100000.0; Metric rLastRotation = AngleToRadians(180 + m_iXformRotation + m_iLastDirection); Metric rCurRotation = AngleToRadians(180 + m_iXformRotation + m_iCurDirection); // Compute the spread angle, in radians Metric rSpread = AngleToRadians(Max(0, m_SpreadAngle.Roll())); Metric rHalfSpread = 0.5 * rSpread; // Calculate where last tick's particles would be based on the last rotation. Metric rAveSpeed = m_EmitSpeed.GetAveValue() * LIGHT_SPEED / 100.0; CVector vLastStart = (m_vLastEmitPos + (m_rXformTime * ::PolarToVectorRadians(rLastRotation, rAveSpeed * g_SecondsPerUpdate))) - vInitialPos; // Calculate where last tick's particles would be IF we have used the current // rotation. This allows us to interpolate a turn. CVector vCurStart = (m_vLastEmitPos + (m_rXformTime * ::PolarToVectorRadians(rCurRotation, rAveSpeed * g_SecondsPerUpdate))) - vInitialPos; // Create particles for (i = 0; i < iCount; i++) { Metric rSlide = mathRandom(0, 9999) / 10000.0; // Compute two points along the two slide vectors (last and current) CVector vSlide1 = rSlide * vLastStart; CVector vSlide2 = rSlide * vCurStart; CVector vAdj = (rSlide * vSlide1) + ((1.0 - rSlide) * vSlide2); // We place the particle along the line betwen the current // and last emit positions CVector vPos = vInitialPos + vAdj; // We blend the rotation as well if (Absolute(rCurRotation - rLastRotation) > g_Pi) { if (rLastRotation < rCurRotation) rLastRotation += g_Pi * 2.0; else rCurRotation += g_Pi * 2.0; } Metric rSlideRotation = (rSlide * rLastRotation) + ((1.0 - rSlide) * rCurRotation); // Generate a random velocity backwards Metric rRotation = rSlideRotation + (rHalfSpread * mathRandom(-1000, 1000) / 1000.0); Metric rSpeed = m_EmitSpeed.Roll() * LIGHT_SPEED / 100.0; CVector vVel = m_rXformTime * (vInitialVel + ::PolarToVectorRadians(rRotation, rSpeed + rJitterFactor * mathRandom(-500, 500))); // Lifetime int iLifeLeft = m_ParticleLifetime.Roll(); // Add the particle m_Particles.AddParticle(vPos, vVel, iLifeLeft, AngleToDegrees(rRotation)); } // Remember the last position m_iLastDirection = m_iCurDirection; m_vLastEmitPos = vInitialPos; }
void CParticleJetEffectPainter::CreateFixedParticles (CSpaceObject *pObj, int iCount, const CVector &vInitialPos, const CVector &vInitialVel) // CreateFixedParticles // // Creates particles along the objects path (e.g., missile exhaust). { int i; // Calculate a vector to our previous position // // NOTE: In this mode m_vLastEmitPos is the last position of the object. CVector vCurPos = (pObj ? pObj->GetPos() : CVector()); CVector vToOldPos; if (m_bTrackingObject) { Metric rAveSpeed = m_rXformTime * m_EmitSpeed.GetAveValue() * LIGHT_SPEED / 100.0; vToOldPos = m_vLastEmitPos - (vCurPos + vInitialPos) + ::PolarToVector(180 + m_iLastDirection, rAveSpeed * g_SecondsPerUpdate); } else { Metric rSpeed = (pObj ? pObj->GetVel().Length() : 0.0); vToOldPos = ::PolarToVector(180 + m_iLastDirection, rSpeed * g_SecondsPerUpdate); } // Compute two orthogonal coordinates CVector vAxis = ::PolarToVector(m_iCurDirection + 180, 1.0); CVector vTangent = ::PolarToVector(m_iCurDirection + 90, 1.0); // Create particles for (i = 0; i < iCount; i++) { Metric rSlide = mathRandom(0, 9999) / 10000.0; // Compute a position randomly along the line between the current and // last emit positions. CVector vPos = vInitialPos + rSlide * vToOldPos; // Generate a random velocity along the tangent Metric rTangentSlide = mathRandom(-9999, 9999) / 10000.0; Metric rAxisJitter = mathRandom(-50, 50) / 100.0; CVector vVel = (vTangent * rTangentSlide * m_rXformTime * m_TangentSpeed.Roll() * LIGHT_SPEED / 100.0) + (vAxis * (m_EmitSpeed.Roll() + rAxisJitter) * LIGHT_SPEED / 100.0); // Lifetime int iLifeLeft = m_ParticleLifetime.Roll(); // Add the particle m_Particles.AddParticle(vPos, vVel, iLifeLeft, m_iCurDirection); } // Remember the last position m_iLastDirection = m_iCurDirection; m_vLastEmitPos = vCurPos + vInitialPos; }