/// <summary> /// Update given particle over passed time interval, in seconds. /// </summary> /// <param name="particle">Particle to integrate.</param> /// <param name="dt">Amount to advance time, in seconds.</param> public void IntegrateParticle(ref T2DParticle particle, float dt) { // Increase particle age. particle.Age += dt; // Calculate Particle unit-age. float unitAge = particle.Age / particle.Lifetime; // ********************************************************************************************************************** // Scale Speed. // ********************************************************************************************************************** float renderSpeed = particle._speed * _emitterData.SpeedLife[unitAge]; // ********************************************************************************************************************** // Scale Fixed-Force. // ********************************************************************************************************************** float renderFixedForce = particle._fixedForce * _emitterData.FixedForceLife[unitAge]; // ********************************************************************************************************************** // Scale Random-Motion. // ********************************************************************************************************************** float renderRandomMotion = particle._randomMotion * _emitterData.RandomMotionLife[unitAge]; // ********************************************************************************************************************** // Calculate New Velocity... // ********************************************************************************************************************** // Is random motion non-zero? if (Epsilon.FloatIsNotZero(renderRandomMotion)) { // Yes, so fetch half random motion. float randomMotion = renderRandomMotion * 0.5f; // Add integrated random motion into velocity. particle._velocity.X += TorqueUtil.GetFastRandomFloat(-randomMotion, randomMotion) * dt; particle._velocity.Y += TorqueUtil.GetFastRandomFloat(-randomMotion, randomMotion) * dt; } Vector2 outVel; // Add integrated fixed force into velocity. //particle._velocity += _emitterData.FixedForceDirection * renderFixedForce * dt; Vector2 dir = _emitterData.FixedForceDirection; Vector2.Multiply(ref dir, renderFixedForce * dt, out outVel); Vector2.Add(ref outVel, ref particle._velocity, out particle._velocity); // Adjust particle position. //particle._position += particle._velocity * renderSpeed * dt; Vector2.Multiply(ref particle._velocity, renderSpeed * dt, out outVel); Vector2.Add(ref outVel, ref particle._position, out particle._position); // ********************************************************************************************************************** // Aligning to motion. // ********************************************************************************************************************** // Are we aligning to motion? if (_emitterData.ParticleOrientation == T2DParticleEmitterData.ParticleOrientationMode.Aligned) { // Yes, so calculate last movement direction. float movementAngle = T2DVectorUtil.AngleFromVector(particle._velocity); // Set new Orientation Angle. particle._rotationAngle = movementAngle + _emitterData.OrientationAngleOffset; } else { // Calculate render spin. float renderSpin = particle._spin * _emitterData.SpinLife[unitAge]; // Have we got some spin? if (Epsilon.FloatIsNotZero(renderSpin)) { // Yes, so add integrated spin into orientation. particle._rotationAngle += renderSpin * dt; // Keep within bounds. particle._rotationAngle %= 360.0f; } } // ********************************************************************************************************************** // Update emitter clip boundary // ********************************************************************************************************************** //Vector2 renderSize = particle._size * _maxParticleSize; Vector2 renderSize; Vector2.Multiply(ref particle._size, ref _maxParticleSize, out renderSize); Vector2 center = particle._position; // compute upper bound for maximum extent from center float radius = renderSize.X + renderSize.Y; radius += Math.Abs(_emitterData.ParticlePivotPoint.X) * _maxParticleSize.X; radius += Math.Abs(_emitterData.ParticlePivotPoint.Y) * _maxParticleSize.Y; if (center.X - radius < _particleExtentTopLeft.X) _particleExtentTopLeft.X = center.X - radius; if (center.X + radius > _particleExtentBottomRight.X) _particleExtentBottomRight.X = center.X + radius; if (center.Y - radius < _particleExtentTopLeft.Y) _particleExtentTopLeft.Y = center.Y - radius; if (center.Y + radius > _particleExtentBottomRight.Y) _particleExtentBottomRight.Y = center.Y + radius; }
/// <summary> /// Initialize given particle. /// </summary> /// <param name="particle">Particle to initialize.</param> public void InitializeParticle(ref T2DParticle particle) { // Fetch Effect Age. float effectAge = _parentEffect.Age; // Fetch Effect Position. Vector2 effectPosition = _parentEffect.Position; // Fetch Parent Effect Effect-Data. T2DParticleEffectData parentEffectData = _parentEffect.CurrentEffectData; // ********************************************************************************************************************** // Calculate Particle Position... // ********************************************************************************************************************** // Single Particle? if (_emitterData.SingleParticle) { // Yes, so are we using effect position? if (_emitterData.AttachPositionToEmitter) { // Yes, so set position. particle.Position = Vector2.Zero; } else { // No, so use effect position. particle.Position = effectPosition; } } else { // No, so select emitter type. switch (_emitterData.EmitterArea) { // Point. case T2DParticleEmitterData.EmitterAreaType.Point: { // Set Particle position. particle.Position = Vector2.Zero; } break; // LineX. case T2DParticleEmitterData.EmitterAreaType.LineX: { // Calculate Area Size X. float areaSizeX = 0.5f * TorqueUtil.GetFastRandomFloat(0.0f, _parentEffect.Size.X); // Calculate Sign. if ((TorqueUtil.GetFastRandomInt() & 1) == 0) { areaSizeX = -areaSizeX; } // Choose particle location. particle.PositionX = areaSizeX; particle.PositionY = 0.0f; } break; // LineY. case T2DParticleEmitterData.EmitterAreaType.LineY: { // Calculate Area Size Y. float areaSizeY = 0.5f * TorqueUtil.GetFastRandomFloat(0.0f, _parentEffect.Size.Y); // Calculate Sign. if ((TorqueUtil.GetFastRandomInt() & 1) == 0) { areaSizeY = -areaSizeY; } // Choose particle location. particle.PositionX = 0.0f; particle.PositionY = 0.0f + areaSizeY; } break; // Rectangle. case T2DParticleEmitterData.EmitterAreaType.Rectangle: { // Calculate Min/Max Area Size X. float minAreaSizeX = 0.0f; float maxAreaSizeX = 0.5f * _parentEffect.Size.X; // Inline Clamp. if (maxAreaSizeX < minAreaSizeX) { maxAreaSizeX = minAreaSizeX; } // Choose Random Area. float areaSizeX = TorqueUtil.GetFastRandomFloat(maxAreaSizeX); // Assume Fixed Area Aspect. float minAreaSizeY; float maxAreaSizeY; float areaSizeY; if (_emitterData.FixedAreaAspect) { // Yes, so calculate Min/Max Area Size Y (using X). minAreaSizeY = minAreaSizeX; maxAreaSizeY = maxAreaSizeX; } else { // No, so calculate Area Size Y. minAreaSizeY = 0.0f; maxAreaSizeY = 0.5f * _parentEffect.Size.Y; } // Inline Clamp. if (maxAreaSizeY < minAreaSizeY) { maxAreaSizeY = minAreaSizeY; } // Choose Random Area. areaSizeY = TorqueUtil.GetFastRandomFloat(maxAreaSizeY); // Inner Region X? if (areaSizeX < minAreaSizeX && areaSizeY < minAreaSizeY) { // Yes, so randomize within outer region. areaSizeX = minAreaSizeX + (areaSizeX % Math.Abs(maxAreaSizeX - minAreaSizeX + Epsilon.Value)); } // Inner Region Y? if (areaSizeY < minAreaSizeY && areaSizeX < minAreaSizeX) { // Yes, so randomize within outer region. areaSizeY = minAreaSizeY + (areaSizeY % Math.Abs(maxAreaSizeY - minAreaSizeY + Epsilon.Value)); } // Calculate Sign. int areaFlip = TorqueUtil.GetFastRandomInt(); if ((areaFlip & 1) == 0) { areaSizeX = -areaSizeX; } // Calculate Sign. if ((areaFlip & 2) == 0) { areaSizeY = -areaSizeY; } // Choose particle location. particle.PositionX = areaSizeX; particle.PositionY = areaSizeY; } break; } // Attach position to emitter? if (!_emitterData.AttachPositionToEmitter) { // No, so attach rotation to emitter? if (!_emitterData.AttachRotationToEmitter) { // No, so we need to fully transform in non-scaled effect world-space. particle.Position = Vector2.Transform(particle.Position, _parentEffect.EffectRotationTranslationMatrix); } else { // Yes, so we need to transform in non-scaled, non-rotated effect world-space. particle.Position = Vector2.Transform(particle.Position, _parentEffect.EffectTranslationMatrix); } } } // ********************************************************************************************************************** // Calculate Particle Lifetime. // ********************************************************************************************************************** // Reset Age. particle.Age = 0.0f; // Calculate Lifetime. particle.Lifetime = T2DKeyGraph.CalcGraphBVE(_emitterData.ParticleLifeBase, _emitterData.ParticleLifeVariation, parentEffectData.ParticleLifeScale, effectAge); if (particle.Lifetime < Epsilon.Value) particle.Lifetime = Epsilon.Value; // ********************************************************************************************************************** // Calculate Particle Size. // ********************************************************************************************************************** // Calculate SizeX. particle.SizeX = T2DKeyGraph.CalcGraphBVE(_emitterData.SizeXBase, _emitterData.SizeXVariation, parentEffectData.SizeXScale, effectAge); particle.SizeY = _emitterData.FixedParticleAspect ? particle.SizeX : T2DKeyGraph.CalcGraphBVE(_emitterData.SizeYBase, _emitterData.SizeYVariation, parentEffectData.SizeYScale, effectAge); Assert.Fatal(particle.SizeX > 0.0f && particle.SizeY > 0.0f, "Zero or negative size particles not recommended."); // ********************************************************************************************************************** // Calculate Speed, Fixed-Force, Random Motion // ********************************************************************************************************************** if (_emitterData.SingleParticle) { particle.Speed = 0.0f; particle.FixedForce = 0.0f; particle.RandomMotion = 0.0f; } else { particle.Speed = T2DKeyGraph.CalcGraphBVE(_emitterData.SpeedBase, _emitterData.SpeedVariation, parentEffectData.SpeedScale, effectAge); particle.FixedForce = T2DKeyGraph.CalcGraphBVE(_emitterData.FixedForceBase, _emitterData.FixedForceVariation, parentEffectData.FixedForceScale, effectAge); particle.RandomMotion = T2DKeyGraph.CalcGraphBVE(_emitterData.RandomMotionBase, _emitterData.RandomMotionVariation, parentEffectData.RandomMotionScale, effectAge); } // ********************************************************************************************************************** // Calculate Spin. // ********************************************************************************************************************** particle.Spin = T2DKeyGraph.CalcGraphBVE(_emitterData.SpinBase, _emitterData.SpinVariation, parentEffectData.SpinScale, effectAge); // ********************************************************************************************************************** // Calculate Emission Angle. // ********************************************************************************************************************** float emissionForce = 0.0f; float emissionAngle = 0.0f; float emissionArc = 0.0f; // Ignore if we're using Single Particle. if (!_emitterData.SingleParticle) { // Are we using effect emission? if (_emitterData.UseEffectEmission) { // Yes, so calculate emission from effect. emissionForce = -T2DKeyGraph.CalcGraphBV(parentEffectData.EmissionForceBase, parentEffectData.EmissionForceVariation, effectAge); emissionAngle = T2DKeyGraph.CalcGraphBV(parentEffectData.EmissionAngleBase, parentEffectData.EmissionAngleVariation, effectAge); // NOTE:- We're only interested in half the emission arc! emissionArc = T2DKeyGraph.CalcGraphBV(parentEffectData.EmissionArcBase, parentEffectData.EmissionArcVariation, effectAge) * 0.5f; } else { // No, so calculate emission from emitter. emissionForce = T2DKeyGraph.CalcGraphBV(_emitterData.EmissionForceBase, _emitterData.EmissionForceVariation, effectAge); // No, so calculate standard emission angle. emissionAngle = T2DKeyGraph.CalcGraphBV(_emitterData.EmissionAngleBase, _emitterData.EmissionAngleVariation, effectAge); // Calculate Emission Arc. // NOTE:- We're only interested in half the emission arc! emissionArc = T2DKeyGraph.CalcGraphBV(_emitterData.EmissionArcBase, _emitterData.EmissionArcVariation, effectAge) * 0.5f; } // Is the emission rotation linked? if (_emitterData.LinkEmissionRotation) { // Yes, so add effect rotation onto emission angle. emissionAngle += _parentEffect.Rotation; } // Calculate final emission angle by choosing random arc. emissionAngle = TorqueUtil.GetFastRandomFloat(emissionAngle - emissionArc, emissionAngle + emissionArc) % 360.0f; float emissionAngleRad = MathHelper.ToRadians(emissionAngle); particle.VelocityX = emissionForce * (float)Math.Sin(emissionAngleRad); particle.VelocityY = emissionForce * (float)-Math.Cos(emissionAngleRad); } // ********************************************************************************************************************** // Calculate Orientation Angle. // ********************************************************************************************************************** switch (_emitterData.ParticleOrientation) { // Aligned. case T2DParticleEmitterData.ParticleOrientationMode.Aligned: { // Use the emission angle plus offset. particle.RotationAngle = (emissionAngle + _emitterData.OrientationAngleOffset) % 360.0f; } break; // Fixed. case T2DParticleEmitterData.ParticleOrientationMode.Fixed: { // Use fixed angle. particle.RotationAngle = _emitterData.OrientationAngleOffset % 360.0f; } break; // Random. case T2DParticleEmitterData.ParticleOrientationMode.Random: { float halfRandomArc = _emitterData.OrientationRandomArc * 0.5f; // Use the emission angle plus offset. particle.RotationAngle = TorqueUtil.GetFastRandomFloat(_emitterData.OrientationAngleOffset - halfRandomArc, _emitterData.OrientationAngleOffset + halfRandomArc) % 360.0f; } break; } }