Example #1
0
        /// <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);
        }
Example #2
0
        public override void PrepareFromPool(ParticlePool pool)
        {
            if (pool == null)
            {
                FieldAccessor = ParticleFieldAccessor <ParticleCollisionAttribute> .Invalid();

                return;
            }

            FieldAccessor = pool.GetField(ParticleFields.CollisionControl);
        }
Example #3
0
        /// <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);
        }
Example #5
0
        /// <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());
        }
Example #6
0
        /// <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));
        }
Example #7
0
        /// <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);
        }
Example #9
0
        /// <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));
        }
Example #10
0
        /// <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);
        }
Example #12
0
        /// <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;
        }
Example #14
0
        /// <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));
        }
Example #16
0
        /// <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;
            }
        }
Example #17
0
        /// <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;
            }
        }