/// <summary> /// Gets the combined position for the particle, adding its field value (if any) to its sampled value from the curve /// </summary> /// <param name="particle"></param> /// <param name="positionField"></param> /// <param name="lifeField">Normalized life for sampling</param> /// <returns>Particle's current 3D position</returns> protected unsafe Vector3 GetParticlePosition(Particle particle, ParticleFieldAccessor<Vector3> positionField, ParticleFieldAccessor<float> lifeField) { if (SamplerPosition == null) return particle.Get(positionField); var life = 1f - (*((float*)particle[lifeField])); // The Life field contains remaining life, so for sampling we take (1 - life) return particle.Get(positionField) + SamplerPosition.Evaluate(life); }
public override void PrepareFromPool(ParticlePool pool) { if (pool == null) { FieldAccessor = ParticleFieldAccessor <ParticleCollisionAttribute> .Invalid(); return; } FieldAccessor = pool.GetField(ParticleFields.CollisionControl); }
/// <summary> /// Gets the combined size for the particle, adding its field value (if any) to its sampled value from the curve /// </summary> /// <param name="particle">Target particle</param> /// <param name="sizeField">Size field accessor</param> /// <param name="lifeField">Normalized life for sampling</param> /// <returns>Particle's current uniform size</returns> protected unsafe float GetParticleSize(Particle particle, ParticleFieldAccessor<float> sizeField, ParticleFieldAccessor<float> lifeField) { var particleSize = sizeField.IsValid() ? particle.Get(sizeField) : 1f; if (SamplerSize == null) return particleSize; var life = 1f - (*((float*)particle[lifeField])); // The Life field contains remaining life, so for sampling we take (1 - life) return particleSize * SamplerSize.Evaluate(life); }
public override void PrepareFromPool(ParticlePool pool) { if (pool == null) { FieldAccessor = ParticleFieldAccessor <float> .Invalid(); return; } FieldAccessor = pool.GetField(ParticleFields.RemainingLife); }
/// <summary> /// Gets a field accessor to the parent emitter's spawn control field, if it exists /// </summary> /// <returns></returns> protected ParticleFieldAccessor <ParticleChildrenAttribute> GetSpawnControlField() { var groupIndex = (int)parentControlFlag; if (groupIndex >= ParticleFields.ChildrenFlags.Length) { return(ParticleFieldAccessor <ParticleChildrenAttribute> .Invalid()); } return(Parent?.Pool?.GetField(ParticleFields.ChildrenFlags[groupIndex]) ?? ParticleFieldAccessor <ParticleChildrenAttribute> .Invalid()); }
/// <summary> /// Gets the combined position for the particle, adding its field value (if any) to its sampled value from the curve /// </summary> /// <param name="particle"></param> /// <param name="positionField"></param> /// <param name="lifeField">Normalized life for sampling</param> /// <returns>Particle's current 3D position</returns> protected unsafe Vector3 GetParticlePosition(Particle particle, ParticleFieldAccessor <Vector3> positionField, ParticleFieldAccessor <float> lifeField) { if (SamplerPosition == null) { return(particle.Get(positionField)); } var life = 1f - (*((float *)particle[lifeField])); // The Life field contains remaining life, so for sampling we take (1 - life) return(particle.Get(positionField) + SamplerPosition.Evaluate(life)); }
/// <summary> /// Gets the combined rotation for the particle, adding its field value (if any) to its sampled value from the curve /// </summary> /// <param name="particle">Target particle</param> /// <param name="rotationField">Rotation field accessor</param> /// <param name="lifeField">Normalized particle life for sampling</param> /// <returns>Quaternion rotation of the quad particle, assuming flat horizontal square at neutral rotation</returns> protected unsafe Quaternion GetParticleRotation(Particle particle, ParticleFieldAccessor <Quaternion> rotationField, ParticleFieldAccessor <float> lifeField) { var particleRotation = rotationField.IsValid() ? particle.Get(rotationField) : Quaternion.Identity; if (SamplerRotation == null) { return(particleRotation); } var life = 1f - (*((float *)particle[lifeField])); // The Life field contains remaining life, so for sampling we take (1 - life) return(SamplerRotation.Evaluate(life) * particleRotation); }
public override void PrepareFromPool(ParticlePool pool) { limitsAreInOrder = (LifetimeLowerLimit <= LifetimeUpperLimit); if (pool == null) { FieldAccessor = ParticleFieldAccessor <float> .Invalid(); return; } FieldAccessor = pool.GetField(ParticleFields.RemainingLife); }
/// <summary> /// Gets the combined size for the particle, adding its field value (if any) to its sampled value from the curve /// </summary> /// <param name="particle">Target particle</param> /// <param name="sizeField">Size field accessor</param> /// <param name="lifeField">Normalized life for sampling</param> /// <returns>Particle's current uniform size</returns> protected unsafe float GetParticleSize(Particle particle, ParticleFieldAccessor <float> sizeField, ParticleFieldAccessor <float> lifeField) { var particleSize = sizeField.IsValid() ? particle.Get(sizeField) : 1f; if (SamplerSize == null) { return(particleSize); } var life = 1f - (*((float *)particle[lifeField])); // The Life field contains remaining life, so for sampling we take (1 - life) return(particleSize * SamplerSize.Evaluate(life)); }
/// <summary> /// Gets the combined rotation for the particle, adding its field value (if any) to its sampled value from the curve /// </summary> /// <param name="particle">Target particle</param> /// <param name="rotationField">Rotation field accessor</param> /// <param name="lifeField">Normalized particle life for sampling</param> /// <returns>Screenspace rotation in radians, positive is clockwise</returns> protected unsafe float GetParticleRotation(Particle particle, ParticleFieldAccessor <float> rotationField, ParticleFieldAccessor <float> lifeField) { var particleRotation = rotationField.IsValid() ? particle.Get(rotationField) : 0f; if (SamplerRotation == null) { return(particleRotation); } var life = 1f - (*((float *)particle[lifeField])); // The Life field contains remaining life, so for sampling we take (1 - life) return(particleRotation + MathUtil.DegreesToRadians(SamplerRotation.Evaluate(life))); }
public override void PrepareFromPool(ParticlePool pool) { if (pool == null) { FieldAccessor = ParticleFieldAccessor <Vector3> .Invalid(); SecondFieldAccessor = ParticleFieldAccessor <Vector3> .Invalid(); return; } FieldAccessor = pool.GetField(ParticleFields.Position); SecondFieldAccessor = pool.GetField(ParticleFields.OldPosition); }
/// <inheritdoc /> public unsafe override void Initialize(ParticlePool pool, int startIdx, int endIdx, int maxCapacity) { if (!pool.FieldExists(ParticleFields.Order)) { return; } // Collect the total number of living particles in the parent pool which have a Order field var parentPool = Parent?.Pool; var parentParticlesCount = parentPool?.LivingParticles ?? 0; var orderFieldParent = parentPool?.GetField(ParticleFields.Order) ?? ParticleFieldAccessor <uint> .Invalid(); var childOrderFieldParent = parentPool?.GetField(ParticleFields.ChildOrder) ?? ParticleFieldAccessor <uint> .Invalid(); if (!orderFieldParent.IsValid()) { parentParticlesCount = 0; } var spawnControlField = GetSpawnControlField(); var orderField = pool.GetField(ParticleFields.Order); var randomField = pool.GetField(ParticleFields.RandomSeed); var sequentialParentIndex = 0; var sequentialParentParticles = 0; var parentIndex = 0; var i = startIdx; while (i != endIdx) { var particle = pool.FromIndex(i); // Will loop every so often, but the loop condition should be unreachable for normal games uint particleOrder = spawnOrder++; if (parentParticlesCount > 0) { uint parentParticleOrder = 0; // Spawn is fixed - parent particles have spawned a very specific number of children each if (spawnControlField.IsValid()) { while (sequentialParentParticles == 0) { // Early out - no more fixed number children. Rest of the particles (if any) are skipped intentionally if (sequentialParentIndex >= parentParticlesCount) { return; } parentIndex = sequentialParentIndex; var tempParentParticle = parentPool.FromIndex(parentIndex); sequentialParentIndex++; var childrenAttribute = (*((ParticleChildrenAttribute *)tempParentParticle[spawnControlField])); sequentialParentParticles = (int)childrenAttribute.ParticlesToEmit; } sequentialParentParticles--; var parentParticle = parentPool.FromIndex(parentIndex); parentParticleOrder = (*((uint *)parentParticle[orderFieldParent])); if (childOrderFieldParent.IsValid()) { particleOrder = (*((uint *)parentParticle[childOrderFieldParent])); (*((uint *)parentParticle[childOrderFieldParent])) = (particleOrder + 1); particleOrder = (particleOrder & 0x0000FFFF) | ((parentParticleOrder << 16) & 0xFFFF0000); } else { particleOrder = (particleOrder & 0x000FFFFF) | ((parentParticleOrder << 20) & 0xFFF00000); } } // Spawn is not fixed - pick a parent at random else { var randSeed = particle.Get(randomField); parentIndex = (int)(parentParticlesCount * randSeed.GetFloat(RandomOffset.Offset1A + ParentSeedOffset)); var parentParticle = parentPool.FromIndex(parentIndex); parentParticleOrder = (*((uint *)parentParticle[orderFieldParent])); if (childOrderFieldParent.IsValid()) { particleOrder = (*((uint *)parentParticle[childOrderFieldParent])); (*((uint *)parentParticle[childOrderFieldParent])) = (particleOrder + 1); particleOrder = (particleOrder & 0x0000FFFF) | ((parentParticleOrder << 16) & 0xFFFF0000); } else { particleOrder = (particleOrder & 0x000FFFFF) | ((parentParticleOrder << 20) & 0xFFF00000); } } } (*((uint *)particle[orderField])) = particleOrder; i = (i + 1) % maxCapacity; } }
/// <summary> /// Gets the combined rotation for the particle, adding its field value (if any) to its sampled value from the curve /// </summary> /// <param name="particle">Target particle</param> /// <param name="rotationField">Rotation field accessor</param> /// <param name="lifeField">Normalized particle life for sampling</param> /// <returns>Quaternion rotation of the quad particle, assuming flat horizontal square at neutral rotation</returns> protected unsafe Quaternion GetParticleRotation(Particle particle, ParticleFieldAccessor<Quaternion> rotationField, ParticleFieldAccessor<float> lifeField) { var particleRotation = rotationField.IsValid() ? particle.Get(rotationField) : Quaternion.Identity; if (SamplerRotation == null) return particleRotation; var life = 1f - (*((float*)particle[lifeField])); // The Life field contains remaining life, so for sampling we take (1 - life) return SamplerRotation.Evaluate(life) * particleRotation; }
/// <inheritdoc /> public unsafe override void Initialize(ParticlePool pool, int startIdx, int endIdx, int maxCapacity) { if (!pool.FieldExists(ParticleFields.Position) || !pool.FieldExists(ParticleFields.RandomSeed)) { return; } // Collect the total number of living particles in the parent pool which have a Position field var parentPool = Parent?.Pool; var parentParticlesCount = parentPool?.LivingParticles ?? 0; var posFieldParent = parentPool?.GetField(ParticleFields.Position) ?? ParticleFieldAccessor <Vector3> .Invalid(); if (!posFieldParent.IsValid()) { parentParticlesCount = 0; } var oldPosFieldParent = parentPool?.GetField(ParticleFields.OldPosition) ?? ParticleFieldAccessor <Vector3> .Invalid(); var spawnControlField = GetSpawnControlField(); var posField = pool.GetField(ParticleFields.Position); var rndField = pool.GetField(ParticleFields.RandomSeed); var oldField = pool.GetField(ParticleFields.OldPosition); var leftCorner = PositionMin * WorldScale; var xAxis = new Vector3(PositionMax.X * WorldScale.X - leftCorner.X, 0, 0); var yAxis = new Vector3(0, PositionMax.Y * WorldScale.Y - leftCorner.Y, 0); var zAxis = new Vector3(0, 0, PositionMax.Z * WorldScale.Z - leftCorner.Z); if (!WorldRotation.IsIdentity) { WorldRotation.Rotate(ref leftCorner); WorldRotation.Rotate(ref xAxis); WorldRotation.Rotate(ref yAxis); WorldRotation.Rotate(ref zAxis); } // Already inheriting from parent if (parentParticlesCount == 0) { leftCorner += WorldPosition; } var sequentialParentIndex = 0; var sequentialParentParticles = 0; var parentIndex = 0; // Interpolation - if parent particle has OldPosition field var stepF = 0f; var stepTotal = 0f; var positionDistance = new Vector3(0, 0, 0); var i = startIdx; while (i != endIdx) { var particle = pool.FromIndex(i); var randSeed = particle.Get(rndField); var particleRandPos = leftCorner; particleRandPos += xAxis * randSeed.GetFloat(RandomOffset.Offset3A + SeedOffset); particleRandPos += yAxis * randSeed.GetFloat(RandomOffset.Offset3B + SeedOffset); particleRandPos += zAxis * randSeed.GetFloat(RandomOffset.Offset3C + SeedOffset); if (parentParticlesCount > 0) { var parentParticlePosition = new Vector3(0, 0, 0); // Spawn is fixed - parent particles have spawned a very specific number of children each if (spawnControlField.IsValid()) { // Interpolation - if parent particle has OldPosition field while (sequentialParentParticles == 0) { // Early out - no more fixed number children. Rest of the particles (if any) are skipped intentionally if (sequentialParentIndex >= parentParticlesCount) { return; } parentIndex = sequentialParentIndex; var tempParentParticle = parentPool.FromIndex(parentIndex); sequentialParentIndex++; var childrenAttribute = (*((ParticleChildrenAttribute *)tempParentParticle[spawnControlField])); sequentialParentParticles = (int)childrenAttribute.ParticlesToEmit; if (oldPosFieldParent.IsValid()) { stepF = (sequentialParentParticles > 0) ? (1f / (float)sequentialParentParticles) : 1; stepTotal = 0f; positionDistance = ((*((Vector3 *)tempParentParticle[oldPosFieldParent])) - (*((Vector3 *)tempParentParticle[posFieldParent]))); } } sequentialParentParticles--; var parentParticle = parentPool.FromIndex(parentIndex); parentParticlePosition = (*((Vector3 *)parentParticle[posFieldParent])); parentParticlePosition += positionDistance * stepTotal; stepTotal += stepF; } // Spawn is not fixed - pick a parent at random else { parentIndex = (int)(parentParticlesCount * randSeed.GetFloat(RandomOffset.Offset1A + ParentSeedOffset)); var parentParticle = parentPool.FromIndex(parentIndex); parentParticlePosition = (*((Vector3 *)parentParticle[posFieldParent])); } // Convert from Local -> World space if needed if (Parent.SimulationSpace == EmitterSimulationSpace.Local) { WorldRotation.Rotate(ref parentParticlePosition); parentParticlePosition *= WorldScale.X; parentParticlePosition += WorldPosition; } particleRandPos += parentParticlePosition; } (*((Vector3 *)particle[posField])) = particleRandPos; if (oldField.IsValid()) { (*((Vector3 *)particle[oldField])) = particleRandPos; } i = (i + 1) % maxCapacity; } }
/// <summary> /// Gets the combined rotation for the particle, adding its field value (if any) to its sampled value from the curve /// </summary> /// <param name="particle">Target particle</param> /// <param name="rotationField">Rotation field accessor</param> /// <param name="lifeField">Normalized particle life for sampling</param> /// <returns>Screen space rotation in radians, positive is clockwise</returns> protected unsafe float GetParticleRotation(Particle particle, ParticleFieldAccessor<float> rotationField, ParticleFieldAccessor<float> lifeField) { var particleRotation = rotationField.IsValid() ? particle.Get(rotationField) : 1f; if (SamplerRotation == null) return particleRotation; var life = 1f - (*((float*)particle[lifeField])); // The Life field contains remaining life, so for sampling we take (1 - life) return particleRotation + MathUtil.DegreesToRadians(SamplerRotation.Evaluate(life)); }
/// <inheritdoc /> public unsafe override void Initialize(ParticlePool pool, int startIdx, int endIdx, int maxCapacity) { if (!pool.FieldExists(ParticleFields.Size) || !pool.FieldExists(ParticleFields.RandomSeed)) { return; } var parentPool = Parent?.Pool; var parentParticlesCount = parentPool?.LivingParticles ?? 0; var sizeFieldParent = parentPool?.GetField(ParticleFields.Size) ?? ParticleFieldAccessor <float> .Invalid(); if (!sizeFieldParent.IsValid()) { parentParticlesCount = 0; } var spawnControlField = GetSpawnControlField(); var sizeField = pool.GetField(ParticleFields.Size); var rndField = pool.GetField(ParticleFields.RandomSeed); var minSize = WorldScale.X * RandomSize.X; var sizeGap = WorldScale.X * RandomSize.Y - minSize; var sequentialParentIndex = 0; var sequentialParentParticles = 0; var parentIndex = 0; var i = startIdx; while (i != endIdx) { var particle = pool.FromIndex(i); var randSeed = particle.Get(rndField); var particleRandomSize = minSize + sizeGap * randSeed.GetFloat(RandomOffset.Offset1A + SeedOffset); if (parentParticlesCount > 0) { var parentParticleSize = 1f; // Spawn is fixed - parent particles have spawned a very specific number of children each if (spawnControlField.IsValid()) { while (sequentialParentParticles == 0) { // Early out - no more fixed number children. Rest of the particles (if any) are skipped intentionally if (sequentialParentIndex >= parentParticlesCount) { return; } parentIndex = sequentialParentIndex; var tempParentParticle = parentPool.FromIndex(parentIndex); sequentialParentIndex++; var childrenAttribute = (*((ParticleChildrenAttribute *)tempParentParticle[spawnControlField])); sequentialParentParticles = (int)childrenAttribute.ParticlesToEmit; } sequentialParentParticles--; var parentParticle = parentPool.FromIndex(parentIndex); parentParticleSize = (*((float *)parentParticle[sizeFieldParent])); } // Spawn is not fixed - pick a parent at random else { parentIndex = (int)(parentParticlesCount * randSeed.GetFloat(RandomOffset.Offset1A + ParentSeedOffset)); var parentParticle = parentPool.FromIndex(parentIndex); parentParticleSize = (*((float *)parentParticle[sizeFieldParent])); } // Convert from Local -> World space if needed if (Parent.SimulationSpace == EmitterSimulationSpace.Local) { parentParticleSize *= WorldScale.X; } particleRandomSize *= parentParticleSize; } (*((float *)particle[sizeField])) = particleRandomSize; i = (i + 1) % maxCapacity; } }
/// <inheritdoc /> public unsafe override void Initialize(ParticlePool pool, int startIdx, int endIdx, int maxCapacity) { if (!pool.FieldExists(ParticleFields.Color) || !pool.FieldExists(ParticleFields.RandomSeed)) { return; } // Collect the total number of living particles in the parent pool which have a Color field var parentPool = Parent?.Pool; var parentParticlesCount = parentPool?.LivingParticles ?? 0; var colorFieldParent = parentPool?.GetField(ParticleFields.Color) ?? ParticleFieldAccessor <Color4> .Invalid(); if (!colorFieldParent.IsValid()) { parentParticlesCount = 0; } var spawnControlField = GetSpawnControlField(); var colorField = pool.GetField(ParticleFields.Color); var rndField = pool.GetField(ParticleFields.RandomSeed); var sequentialParentIndex = 0; var sequentialParentParticles = 0; var parentIndex = 0; var i = startIdx; while (i != endIdx) { var particle = pool.FromIndex(i); var randSeed = particle.Get(rndField); var color = Color4.Lerp(ColorMin, ColorMax, randSeed.GetFloat(RandomOffset.Offset1A + SeedOffset)); // If there are living parent particles, the newly created particle will inherit Color from one of them if (parentParticlesCount > 0) { var parentParticleColor = new Color4(1, 1, 1, 1); // Spawn is fixed - parent particles have spawned a very specific number of children each if (spawnControlField.IsValid()) { while (sequentialParentParticles == 0) { // Early out - no more fixed number children. Rest of the particles (if any) are skipped intentionally if (sequentialParentIndex >= parentParticlesCount) { return; } parentIndex = sequentialParentIndex; var tempParentParticle = parentPool.FromIndex(parentIndex); sequentialParentIndex++; var childrenAttribute = (*((ParticleChildrenAttribute *)tempParentParticle[spawnControlField])); sequentialParentParticles = (int)childrenAttribute.ParticlesToEmit; } sequentialParentParticles--; var parentParticle = parentPool.FromIndex(parentIndex); parentParticleColor = (*((Color4 *)parentParticle[colorFieldParent])); } // Spawn is not fixed - pick a parent at random else { parentIndex = (int)(parentParticlesCount * randSeed.GetFloat(RandomOffset.Offset1A + ParentSeedOffset)); var parentParticle = parentPool.FromIndex(parentIndex); parentParticleColor = (*((Color4 *)parentParticle[colorFieldParent])); } color *= parentParticleColor; } // Premultiply alpha // This can't be done in advance for ColorMin and ColorMax because it will change the math color.R *= color.A; color.G *= color.A; color.B *= color.A; (*((Color4 *)particle[colorField])) = color; i = (i + 1) % maxCapacity; } }
/// <inheritdoc /> public unsafe override void Initialize(ParticlePool pool, int startIdx, int endIdx, int maxCapacity) { if (!pool.FieldExists(ParticleFields.Velocity) || !pool.FieldExists(ParticleFields.RandomSeed)) { return; } // Collect the total number of living particles in the parent pool which have a Velocity field var parentPool = Parent?.Pool; var parentParticlesCount = parentPool?.LivingParticles ?? 0; var velFieldParent = parentPool?.GetField(ParticleFields.Velocity) ?? ParticleFieldAccessor <Vector3> .Invalid(); if (!velFieldParent.IsValid()) { parentParticlesCount = 0; } var spawnControlField = GetSpawnControlField(); var velField = pool.GetField(ParticleFields.Velocity); var rndField = pool.GetField(ParticleFields.RandomSeed); var leftCorner = VelocityMin * WorldScale; var xAxis = new Vector3(VelocityMax.X * WorldScale.X - leftCorner.X, 0, 0); var yAxis = new Vector3(0, VelocityMax.Y * WorldScale.Y - leftCorner.Y, 0); var zAxis = new Vector3(0, 0, VelocityMax.Z * WorldScale.Z - leftCorner.Z); if (!WorldRotation.IsIdentity) { WorldRotation.Rotate(ref leftCorner); WorldRotation.Rotate(ref xAxis); WorldRotation.Rotate(ref yAxis); WorldRotation.Rotate(ref zAxis); } var sequentialParentIndex = 0; var sequentialParentParticles = 0; var parentIndex = 0; var i = startIdx; while (i != endIdx) { var particle = pool.FromIndex(i); var randSeed = particle.Get(rndField); var particleRandVel = leftCorner; particleRandVel += xAxis * randSeed.GetFloat(RandomOffset.Offset3A + SeedOffset); particleRandVel += yAxis * randSeed.GetFloat(RandomOffset.Offset3B + SeedOffset); particleRandVel += zAxis * randSeed.GetFloat(RandomOffset.Offset3C + SeedOffset); if (parentParticlesCount > 0) { var parentParticleVelocity = new Vector3(0, 0, 0); // Spawn is fixed - parent particles have spawned a very specific number of children each if (spawnControlField.IsValid()) { while (sequentialParentParticles == 0) { // Early out - no more fixed number children. Rest of the particles (if any) are skipped intentionally if (sequentialParentIndex >= parentParticlesCount) { return; } parentIndex = sequentialParentIndex; var tempParentParticle = parentPool.FromIndex(parentIndex); sequentialParentIndex++; var childrenAttribute = (*((ParticleChildrenAttribute *)tempParentParticle[spawnControlField])); sequentialParentParticles = (int)childrenAttribute.ParticlesToEmit; } sequentialParentParticles--; var parentParticle = parentPool.FromIndex(parentIndex); parentParticleVelocity = (*((Vector3 *)parentParticle[velFieldParent])); } // Spawn is not fixed - pick a parent at random else { parentIndex = (int)(parentParticlesCount * randSeed.GetFloat(RandomOffset.Offset1A + ParentSeedOffset)); var parentParticle = parentPool.FromIndex(parentIndex); parentParticleVelocity = (*((Vector3 *)parentParticle[velFieldParent])); } // Convert from Local -> World space if needed if (Parent.SimulationSpace == EmitterSimulationSpace.Local) { WorldRotation.Rotate(ref parentParticleVelocity); parentParticleVelocity *= WorldScale.X; } particleRandVel += parentParticleVelocity * ParentVelocityFactor; } (*((Vector3 *)particle[velField])) = particleRandVel; i = (i + 1) % maxCapacity; } }