예제 #1
0
        /// <summary>
        /// If the particle pool has changed the sorter must also be updated to reflect those changes
        /// </summary>
        private void PoolChangedNotification()
        {
            if (SortingPolicy == EmitterSortingPolicy.None || pool.ParticleCapacity <= 0)
            {
                ParticleSorter = new ParticleSorterDefault(pool);
                return;
            }

            if (SortingPolicy == EmitterSortingPolicy.ByDepth)
            {
                GetSortIndex <Vector3> sortByDepth = value =>
                {
                    var depth = Vector3.Dot(depthSortVector, value);
                    return(depth);
                };

                ParticleSorter = new ParticleSorterCustom <Vector3>(pool, ParticleFields.Position, sortByDepth);
                return;
            }

            if (SortingPolicy == EmitterSortingPolicy.ByAge)
            {
                GetSortIndex <float> sortByAge = value => { return(-value); };

                ParticleSorter = new ParticleSorterCustom <float>(pool, ParticleFields.Life, sortByAge);
                return;
            }

            // Default - no sorting
            ParticleSorter = new ParticleSorterDefault(pool);
        }
예제 #2
0
        /// <inheritdoc />
        public override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY, ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            SamplerPosition?.UpdateChanges();

            SamplerSize?.UpdateChanges();

            return 0;
        }
예제 #3
0
        /// <inheritdoc />
        public override void PrepareForDraw(ParticleVertexBuilder vertexBuilder, ParticleSorter sorter)
        {
            base.PrepareForDraw(vertexBuilder, sorter);

            // Probe if the particles have a color field and if we need to support it
            var colorField = sorter.GetField(ParticleFields.Color);

            if (colorField.IsValid() != HasColorField)
            {
                HasVertexLayoutChanged = true;
                HasColorField          = colorField.IsValid();
            }
        }
예제 #4
0
        /// <summary>
        /// If the particle pool has changed the sorter must also be updated to reflect those changes
        /// </summary>
        private void PoolChangedNotification()
        {
            if (SortingPolicy == EmitterSortingPolicy.None || pool.ParticleCapacity <= 0)
            {
                ParticleSorter = new ParticleSorterDefault(pool);
                return;
            }

            if (SortingPolicy == EmitterSortingPolicy.ByDepth)
            {
                GetSortIndex <Vector3> sortByDepth = value =>
                {
                    var depth = Vector3.Dot(depthSortVector, value);
                    return(depth);
                };

                ParticleSorter = new ParticleSorterCustom <Vector3>(pool, ParticleFields.Position, sortByDepth);
                return;
            }

            if (SortingPolicy == EmitterSortingPolicy.ByAge)
            {
                GetSortIndex <float> sortByAge = value => { return(-value); };

                ParticleSorter = new ParticleSorterCustom <float>(pool, ParticleFields.Life, sortByAge);
                return;
            }

            if (SortingPolicy == EmitterSortingPolicy.ByOrder)
            {
                // This sorting policy doesn't check if you actually have a Order field.
                // The ParticleSorterCustom will just skip sorting the particles if the field is invalid
                GetSortIndex <uint> sortByOrder = value => BitConverter.ToSingle(BitConverter.GetBytes(value), 0) * -1f;

                ParticleSorter = new ParticleSorterCustom <uint>(pool, ParticleFields.Order, sortByOrder);
                return;
            }

            // Default - no sorting
            ParticleSorter = new ParticleSorterDefault(pool);
        }
예제 #5
0
 /// <summary>
 /// Patch the particle's vertex buffer which was already built by the <see cref="ShapeBuilders.ShapeBuilder"/>
 /// This involes animating hte uv coordinates and filling per-particle fields, such as the color field
 /// </summary>
 /// <param name="vertexBuilder">The target buffer builder to use for patching the vertex data</param>
 /// <param name="invViewX">Unit vector X (right) in camera space, extracted from the inverse view matrix</param>
 /// <param name="invViewY">Unit vector Y (up) in camera space, extracted from the inverse view matrix</param>
 /// <param name="sorter">Particle enumerator which can be iterated and returns sported particles</param>
 public virtual void PatchVertexBuffer(ParticleVertexBuilder vertexBuilder, Vector3 invViewX, Vector3 invViewY, ParticleSorter sorter)
 {
 }
예제 #6
0
 /// <summary>
 /// Prepares the material for drawing the current frame with the current <see cref="ParticleVertexBuilder"/> and <see cref="ParticleSorter"/>
 /// </summary>
 /// <param name="vertexBuilder">Current <see cref="ParticleVertexBuilder"/></param>
 /// <param name="sorter">Current <see cref="ParticleSorter"/></param>
 public virtual void PrepareForDraw(ParticleVertexBuilder vertexBuilder, ParticleSorter sorter)
 {
 }
예제 #7
0
        /// <inheritdoc />
        public unsafe override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY,
                                                     ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            // Update the curve samplers if required
            base.BuildVertexBuffer(vtxBuilder, invViewX, invViewY, ref spaceTranslation, ref spaceRotation, spaceScale, sorter);

            // Get all the required particle fields
            var positionField = sorter.GetField(ParticleFields.Position);

            if (!positionField.IsValid())
            {
                return(0);
            }
            var lifeField      = sorter.GetField(ParticleFields.Life);
            var sizeField      = sorter.GetField(ParticleFields.Size);
            var directionField = sorter.GetField(ParticleFields.Direction);

            // Check if the draw space is identity - in this case we don't need to transform the position, scale and rotation vectors
            var trsIdentity = (spaceScale == 1f);

            trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0)));
            trsIdentity = trsIdentity && (spaceRotation.Equals(Quaternion.Identity));


            var renderedParticles = 0;

            var posAttribute = vtxBuilder.GetAccessor(VertexAttributes.Position);
            var texAttribute = vtxBuilder.GetAccessor(vtxBuilder.DefaultTexCoords);

            foreach (var particle in sorter)
            {
                var centralPos = GetParticlePosition(particle, positionField, lifeField);

                var centralOffset = (directionField.IsValid()) ? particle.Get(directionField) : new Vector3(0, 1, 0);

                var particleSize = GetParticleSize(particle, sizeField, lifeField);

                if (!trsIdentity)
                {
                    spaceRotation.Rotate(ref centralPos);
                    centralPos = centralPos * spaceScale + spaceTranslation;

                    spaceRotation.Rotate(ref centralOffset);
                    centralOffset = centralOffset * spaceScale;

                    particleSize *= spaceScale;
                }

                var unitX = invViewX;
                var unitY = invViewY;
                {
                    var   centralAxis = centralOffset;
                    float dotX;
                    Vector3.Dot(ref centralAxis, ref unitX, out dotX);
                    float dotY;
                    Vector3.Dot(ref centralAxis, ref unitY, out dotY);

                    unitX = unitX * dotY - unitY * dotX;
                    unitX.Normalize();
                    unitY = centralOffset;
                }

                // Use half size to make a Size = 1 result in a Billboard of 1m x 1m
                unitX *= (particleSize * 0.5f);
                if (ScaleLength)
                {
                    unitY *= (LengthFactor * particleSize * 0.5f);
                }
                else
                {
                    unitY *= (LengthFactor * 0.5f);
                }

                var particlePos = centralPos - unitX + unitY;
                var uvCoord     = new Vector2(0, 0);
                // 0f 0f
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 0f
                particlePos += unitX * 2;
                uvCoord.X    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 1f
                particlePos -= unitY * 2;
                uvCoord.Y    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 0f 1f
                particlePos -= unitX * 2;
                uvCoord.X    = 0;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();

                renderedParticles++;
            }

            var vtxPerShape = 4 * QuadsPerParticle;

            return(renderedParticles * vtxPerShape);
        }
예제 #8
0
        /// <summary>
        /// If the particle pool has changed the sorter must also be updated to reflect those changes
        /// </summary>
        private void PoolChangedNotification()
        {
            if (SortingPolicy == EmitterSortingPolicy.None || pool.ParticleCapacity <= 0)
            {
                ParticleSorter = new ParticleSorterDefault(pool);
                return;
            }

            if (SortingPolicy == EmitterSortingPolicy.ByDepth)
            {
                GetSortIndex<Vector3> sortByDepth = value =>
                {
                    var depth = Vector3.Dot(depthSortVector, value);
                    return depth;
                };

                ParticleSorter = new ParticleSorterCustom<Vector3>(pool, ParticleFields.Position, sortByDepth);
                return;
            }

            if (SortingPolicy == EmitterSortingPolicy.ByAge)
            {
                GetSortIndex<float> sortByAge = value => { return -value; };

                ParticleSorter = new ParticleSorterCustom<float>(pool, ParticleFields.Life, sortByAge);
                return;
            }

            if (SortingPolicy == EmitterSortingPolicy.ByOrder)
            {
                // This sorting policy doesn't check if you actually have a Order field.
                // The ParticleSorterCustom will just skip sorting the particles if the field is invalid
                GetSortIndex<uint> sortByOrder = value => BitConverter.ToSingle(BitConverter.GetBytes(value), 0) * -1f;

                ParticleSorter = new ParticleSorterCustom<uint>(pool, ParticleFields.Order, sortByOrder);
                return;
            }

            // Default - no sorting
            ParticleSorter = new ParticleSorterDefault(pool);
        }
예제 #9
0
        /// <inheritdoc />
        public unsafe override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY,
                                                     ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            // Get all required particle fields
            var positionField = sorter.GetField(ParticleFields.Position);

            if (!positionField.IsValid())
            {
                return(0);
            }
            var sizeField   = sorter.GetField(ParticleFields.Size);
            var rotField    = sorter.GetField(ParticleFields.Quaternion);
            var hasRotation = rotField.IsValid();


            // Check if the draw space is identity - in this case we don't need to transform the position, scale and rotation vectors
            var trsIdentity = (spaceScale == 1f);

            trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0)));
            trsIdentity = trsIdentity && (spaceRotation.Equals(new Quaternion(0, 0, 0, 1)));


            var renderedParticles = 0;

            var posAttribute = vtxBuilder.GetAccessor(VertexAttributes.Position);
            var texAttribute = vtxBuilder.GetAccessor(vtxBuilder.DefaultTexCoords);

            foreach (var particle in sorter)
            {
                var centralPos = particle.Get(positionField);

                var particleSize = sizeField.IsValid() ? particle.Get(sizeField) : 1f;

                var unitX = new Vector3(1, 0, 0);
                var unitY = new Vector3(0, 0, 1);

                if (hasRotation)
                {
                    var particleRotation = particle.Get(rotField);
                    particleRotation.Rotate(ref unitX);
                    particleRotation.Rotate(ref unitY);
                }

                // The TRS matrix is not an identity, so we need to transform the quad
                if (!trsIdentity)
                {
                    spaceRotation.Rotate(ref centralPos);
                    centralPos    = centralPos * spaceScale + spaceTranslation;
                    particleSize *= spaceScale;

                    spaceRotation.Rotate(ref unitX);
                    spaceRotation.Rotate(ref unitY);
                }

                unitX *= particleSize;
                unitY *= particleSize;

                var particlePos = centralPos - unitX + unitY;
                var uvCoord     = new Vector2(0, 0);
                // 0f 0f
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 0f
                particlePos += unitX * 2;
                uvCoord.X    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 1f
                particlePos -= unitY * 2;
                uvCoord.Y    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 0f 1f
                particlePos -= unitX * 2;
                uvCoord.X    = 0;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();

                renderedParticles++;
            }

            var vtxPerShape = 4 * QuadsPerParticle;

            return(renderedParticles * vtxPerShape);
        }
예제 #10
0
        /// <inheritdoc />
        public unsafe override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY,
            ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            // Get all the required particle fields
            var positionField = sorter.GetField(ParticleFields.Position);
            if (!positionField.IsValid())
                return 0;
            var sizeField = sorter.GetField(ParticleFields.Size);
            var directionField = sorter.GetField(ParticleFields.Direction);

            // Check if the draw space is identity - in this case we don't need to transform the position, scale and rotation vectors
            var trsIdentity = (spaceScale == 1f);
            trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0)));
            trsIdentity = trsIdentity && (spaceRotation.Equals(Quaternion.Identity));


            var renderedParticles = 0;
            vtxBuilder.RestartBuffer();

            uint oldOrderValue = 0;
            var orderField = sorter.GetField(ParticleFields.Order);

            foreach (var particle in sorter)
            {
                if (orderField.IsValid())
                {
                    var orderValue = (*((uint*)particle[orderField]));

                    if ((orderValue >> 16) != (oldOrderValue >> 16))
                    {
                        ribbonizer.Ribbonize(vtxBuilder, QuadsPerParticle);
                        ribbonizer.RibbonSplit();
                    }

                    oldOrderValue = orderValue;
                }

                var centralPos = particle.Get(positionField);

                var particleSize = sizeField.IsValid() ? particle.Get(sizeField) : 1f;
                var particleDirection = directionField.IsValid() ? particle.Get(directionField) * particleSize : new Vector3(0f, particleSize, 0f);

                if (!trsIdentity)
                {
                    spaceRotation.Rotate(ref centralPos);
                    centralPos = centralPos * spaceScale + spaceTranslation;

                    // Direction
                    spaceRotation.Rotate(ref particleDirection);
                    particleDirection *= spaceScale;
                }

                ribbonizer.AddParticle(ref centralPos, ref particleDirection);

                renderedParticles++;
            }

            ribbonizer.Ribbonize(vtxBuilder, QuadsPerParticle);

            var vtxPerShape = 4 * QuadsPerParticle;
            return renderedParticles * vtxPerShape;
        }
예제 #11
0
 /// <summary>
 /// Builds the actual vertex buffer for the current frame using the particle data
 /// </summary>
 /// <param name="vtxBuilder">Target vertex buffer builder</param>
 /// <param name="invViewX">Unit vector X (right) in camera space, extracted from the inverse view matrix</param>
 /// <param name="invViewY">Unit vector Y (up) in camera space, extracted from the inverse view matrix</param>
 /// <param name="spaceTranslation">Translation of the target draw space in regard to the particle data (world or local)</param>
 /// <param name="spaceRotation">Rotation of the target draw space in regard to the particle data (world or local)</param>
 /// <param name="spaceScale">Uniform scale of the target draw space in regard to the particle data (world or local)</param>
 /// <param name="sorter">Particle enumerator which can be iterated and returns sported particles</param>
 /// <returns></returns>
 public abstract int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY,
                                       ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter);
예제 #12
0
 /// <summary>
 /// Patch the particle's vertex buffer which was already built by the <see cref="ShapeBuilders.ShapeBuilder"/>
 /// This involes animating hte uv coordinates and filling per-particle fields, such as the color field
 /// </summary>
 /// <param name="vertexBuilder">The target buffer builder to use for patching the vertex data</param>
 /// <param name="invViewX">Unit vector X (right) in camera space, extracted from the inverse view matrix</param>
 /// <param name="invViewY">Unit vector Y (up) in camera space, extracted from the inverse view matrix</param>
 /// <param name="sorter">Particle enumerator which can be iterated and returns sported particles</param>
 public virtual void PatchVertexBuffer(ParticleVertexBuilder vertexBuilder, Vector3 invViewX, Vector3 invViewY, ParticleSorter sorter)
 {
 }
예제 #13
0
        /// <inheritdoc />
        public override unsafe void BuildUVCoordinates(ParticleVertexBuilder vertexBuilder, ParticleSorter sorter, AttributeDescription texCoordsDescription)
        {
            var lifeField = sorter.GetField(ParticleFields.RemainingLife);

            if (!lifeField.IsValid())
            {
                return;
            }

            var texAttribute = vertexBuilder.GetAccessor(texCoordsDescription);

            if (texAttribute.Size == 0 && texAttribute.Offset == 0)
            {
                return;
            }

            var texDefault = vertexBuilder.GetAccessor(vertexBuilder.DefaultTexCoords);

            if (texDefault.Size == 0 && texDefault.Offset == 0)
            {
                return;
            }


            foreach (var particle in sorter)
            {
                var normalizedTimeline = 1f - *(float *)(particle[lifeField]);

                var spriteId = startingFrame + (int)(normalizedTimeline * animationSpeedOverLife);

                uvTransform = new Vector4((spriteId % xDivisions) * xStep, (spriteId / xDivisions) * yStep, xStep, yStep);

                vertexBuilder.TransformAttributePerParticle(texDefault, texAttribute, this);

                vertexBuilder.NextParticle();
            }


            vertexBuilder.RestartBuffer();
        }
예제 #14
0
        /// <inheritdoc />
        public unsafe override void PatchVertexBuffer(ParticleVertexBuilder vertexBuilder, Vector3 invViewX, Vector3 invViewY, ParticleSorter sorter)
        {
            // If you want, you can implement the base builder here and not call it. It should result in slight speed up
            base.PatchVertexBuffer(vertexBuilder, invViewX, invViewY, sorter);

            //  The UV Builder, if present, animates the basic (0, 0, 1, 1) uv coordinates of each billboard
            UVBuilder?.BuildUVCoordinates(vertexBuilder, sorter, vertexBuilder.DefaultTexCoords);
            vertexBuilder.RestartBuffer();

            // If the particles have color field, the base class should have already passed the information
            if (HasColorField)
            {
                return;
            }

            // If the particles don't have color field but there is no color stream either we don't need to fill anything
            var colAttribute = vertexBuilder.GetAccessor(VertexAttributes.Color);

            if (colAttribute.Size <= 0)
            {
                return;
            }

            // Since the particles don't have their own color field, set the default color to white
            var color = 0xFFFFFFFF;

            // TODO: for loop. Remove IEnumerable from sorter
            foreach (var particle in sorter)
            {
                vertexBuilder.SetAttributePerParticle(colAttribute, (IntPtr)(&color));

                vertexBuilder.NextParticle();
            }

            vertexBuilder.RestartBuffer();
        }
예제 #15
0
 public void Init()
 {
     cs = new ParticleSorter();
 }
예제 #16
0
        /// <inheritdoc />
        public unsafe override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY,
                                                     ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            // Update the curve samplers if required
            base.BuildVertexBuffer(vtxBuilder, invViewX, invViewY, ref spaceTranslation, ref spaceRotation, spaceScale, sorter);

            SamplerRotation?.UpdateChanges();



            // Get all the required particle fields
            var positionField = sorter.GetField(ParticleFields.Position);

            if (!positionField.IsValid())
            {
                return(0);
            }
            var lifeField  = sorter.GetField(ParticleFields.Life);
            var sizeField  = sorter.GetField(ParticleFields.Size);
            var angleField = sorter.GetField(ParticleFields.Angle);
            var hasAngle   = angleField.IsValid() || (SamplerRotation != null);


            // Check if the draw space is identity - in this case we don't need to transform the position, scale and rotation vectors
            var trsIdentity = (spaceScale == 1f);

            trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0)));
            trsIdentity = trsIdentity && (spaceRotation.Equals(new Quaternion(0, 0, 0, 1)));


            var renderedParticles = 0;

            var posAttribute = vtxBuilder.GetAccessor(VertexAttributes.Position);
            var texAttribute = vtxBuilder.GetAccessor(vtxBuilder.DefaultTexCoords);

            foreach (var particle in sorter)
            {
                var centralPos = GetParticlePosition(particle, positionField, lifeField);

                var particleSize = GetParticleSize(particle, sizeField, lifeField);

                if (!trsIdentity)
                {
                    spaceRotation.Rotate(ref centralPos);
                    centralPos    = centralPos * spaceScale + spaceTranslation;
                    particleSize *= spaceScale;
                }

                // Use half size to make a Size = 1 result in a Billboard of 1m x 1m
                var unitX = invViewX * (particleSize * 0.5f);
                var unitY = invViewY * (particleSize * 0.5f);

                // Particle rotation. Positive value means clockwise rotation.
                if (hasAngle)
                {
                    var rotationAngle = GetParticleRotation(particle, angleField, lifeField);

                    var cosA  = (float)Math.Cos(rotationAngle);
                    var sinA  = (float)Math.Sin(rotationAngle);
                    var tempX = unitX * cosA - unitY * sinA;
                    unitY = unitY * cosA + unitX * sinA;
                    unitX = tempX;
                }


                var particlePos = centralPos - unitX + unitY;
                var uvCoord     = new Vector2(0, 0);
                // 0f 0f
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 0f
                particlePos += unitX * 2;
                uvCoord.X    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 1f
                particlePos -= unitY * 2;
                uvCoord.Y    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 0f 1f
                particlePos -= unitX * 2;
                uvCoord.X    = 0;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();

                renderedParticles++;
            }

            var vtxPerShape = 4 * QuadsPerParticle;

            return(renderedParticles * vtxPerShape);
        }
예제 #17
0
        public unsafe override void PatchVertexBuffer(ParticleVertexBuilder vertexBuilder, Vector3 invViewX, Vector3 invViewY, ParticleSorter sorter)
        {
            // If you want, you can integrate the base builder here and not call it. It should result in slight speed up
            base.PatchVertexBuffer(vertexBuilder, invViewX, invViewY, sorter);

            // Update the non-default coordinates first, because they update off the default ones
            UVBuilder1?.BuildUVCoordinates(vertexBuilder, sorter, texCoord1);

            // Update the default coordinates last
            UVBuilder0?.BuildUVCoordinates(vertexBuilder, sorter, texCoord0);

            // If the particles have color field, the base class should have already passed the information
            if (HasColorField)
            {
                return;
            }

            // If there is no color stream we don't need to fill anything
            var colAttribute = vertexBuilder.GetAccessor(VertexAttributes.Color);

            if (colAttribute.Size <= 0)
            {
                return;
            }

            // Since the particles don't have their own color field, set the default color to white
            var color = 0xFFFFFFFF;

            vertexBuilder.RestartBuffer();
            foreach (var particle in sorter)
            {
                vertexBuilder.SetAttributePerParticle(colAttribute, (IntPtr)(&color));

                vertexBuilder.NextParticle();
            }

            vertexBuilder.RestartBuffer();
        }
예제 #18
0
        /// <inheritdoc />
        public unsafe override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY,
                                                     ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            // Step 1 - get all required fields to build the particle shapes. Some fields may not exist if no initializer or updater operates on them
            //  In that case we just decide on a default value for that field and skip the update

            var positionField = sorter.GetField(ParticleFields.Position);

            if (!positionField.IsValid())
            {
                return(0); // We can't display the particles without position. All other fields are optional
            }
            var sizeField  = sorter.GetField(ParticleFields.Size);
            var angleField = sorter.GetField(ParticleFields.Angle);
            var hasAngle   = angleField.IsValid();

            var rectField   = sorter.GetField(CustomParticleFields.RectangleXY);
            var isRectangle = rectField.IsValid();

            // In case of Local space particles they are simulated in local emitter space, but drawn in world space
            //  If the draw space is identity (i.e. simulation space = draw space) skip transforming the particle's location later
            var trsIdentity = (spaceScale == 1f);

            trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0)));
            trsIdentity = trsIdentity && (spaceRotation.Equals(new Quaternion(0, 0, 0, 1)));

            // Custom feature - fix the Y axis to always point up in world space rather than screen space
            if (FixYAxis)
            {
                invViewY   = new Vector3(0, 1, 0);
                invViewX.Y = 0;
                invViewX.Normalize();
            }

            var renderedParticles = 0;

            var posAttribute = vtxBuilder.GetAccessor(VertexAttributes.Position);
            var texAttribute = vtxBuilder.GetAccessor(vtxBuilder.DefaultTexCoords);

            foreach (var particle in sorter)
            {
                var centralPos = particle.Get(positionField);

                var particleSize = sizeField.IsValid() ? particle.Get(sizeField) : 1f;

                if (!trsIdentity)
                {
                    spaceRotation.Rotate(ref centralPos);
                    centralPos    = centralPos * spaceScale + spaceTranslation;
                    particleSize *= spaceScale;
                }

                var unitX = invViewX * particleSize;
                var unitY = invViewY * particleSize;
                if (isRectangle)
                {
                    var rectSize = particle.Get(rectField);

                    unitX *= rectSize.X;
                    unitY *= rectSize.Y;
                }

                // Particle rotation. Positive value means clockwise rotation.
                if (hasAngle)
                {
                    var rotationAngle = particle.Get(angleField);
                    var cosA          = (float)Math.Cos(rotationAngle);
                    var sinA          = (float)Math.Sin(rotationAngle);
                    var tempX         = unitX * cosA - unitY * sinA;
                    unitY = unitY * cosA + unitX * sinA;
                    unitX = tempX;
                }


                var particlePos = centralPos - unitX + unitY;
                var uvCoord     = new Vector2(0, 0);
                // 0f 0f
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 0f
                particlePos += unitX * 2;
                uvCoord.X    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 1f
                particlePos -= unitY * 2;
                uvCoord.Y    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 0f 1f
                particlePos -= unitX * 2;
                uvCoord.X    = 0;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();

                renderedParticles++;
            }

            // Return the number of updated vertices
            var vtxPerShape = 4 * QuadsPerParticle;

            return(renderedParticles * vtxPerShape);
        }
        /// <inheritdoc />
        public unsafe override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY,
                                                     ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            // Get all required particle fields
            var positionField = sorter.GetField(ParticleFields.Position);

            if (!positionField.IsValid())
            {
                return(0);
            }
            var sizeField = sorter.GetField(ParticleFields.Size);

            var angleField = sorter.GetField(ParticleFields.Angle);
            var hasAngle   = angleField.IsValid();


            // Check if the draw space is identity - in this case we don't need to transform the position, scale and rotation vectors
            var trsIdentity = (spaceScale == 1f);

            trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0)));
            trsIdentity = trsIdentity && (spaceRotation.Equals(new Quaternion(0, 0, 0, 1)));


            var renderedParticles = 0;

            var posAttribute = vtxBuilder.GetAccessor(VertexAttributes.Position);
            var texAttribute = vtxBuilder.GetAccessor(vtxBuilder.DefaultTexCoords);

            foreach (var particle in sorter)
            {
                var centralPos = particle.Get(positionField);

                var particleSize = sizeField.IsValid() ? particle.Get(sizeField) : 1f;

                if (!trsIdentity)
                {
                    spaceRotation.Rotate(ref centralPos);
                    centralPos    = centralPos * spaceScale + spaceTranslation;
                    particleSize *= spaceScale;
                    // TODO Rotation
                }

                var unitX = invViewX * particleSize;
                var unitY = invViewY * particleSize;

                // Particle rotation. Positive value means clockwise rotation.
                if (hasAngle)
                {
                    var rotationAngle = particle.Get(angleField);
                    var cosA          = (float)Math.Cos(rotationAngle);
                    var sinA          = (float)Math.Sin(rotationAngle);
                    var tempX         = unitX * cosA - unitY * sinA;
                    unitY = unitY * cosA + unitX * sinA;
                    unitX = tempX;
                }

                // vertex.Size = particleSize;

                const float Sqrt3Half = 0.86602540378f;
                unitY *= Sqrt3Half;
                var halfX = unitX * 0.5f;

                var particlePos = centralPos - halfX + unitY;
                var uvCoord     = new Vector2(0.25f, 0.5f - Sqrt3Half * 0.5f);


                // Upper half

                // 0f 0f
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 0f
                particlePos += unitX;
                uvCoord.X    = 0.75f;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 1f
                particlePos += halfX;
                particlePos -= unitY;
                uvCoord.X    = 1;
                uvCoord.Y    = 0.5f;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 0f 1f
                particlePos -= unitX * 2;
                uvCoord.X    = 0;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();

                // Upper half

                // 0f 0f
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 0f
                particlePos += unitX * 2;
                uvCoord.X    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 1f 1f
                particlePos -= halfX;
                particlePos -= unitY;
                uvCoord.X    = 0.75f;
                uvCoord.Y    = 1;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();


                // 0f 1f
                particlePos -= unitX;
                uvCoord.X    = 0.25f;
                vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                vtxBuilder.NextVertex();

                renderedParticles++;
            }

            var vtxPerShape = 4 * QuadsPerParticle;

            return(renderedParticles * vtxPerShape);
        }
예제 #20
0
 /// <summary>
 /// Enhances or animates the texture coordinates using already existing base coordinates of (0, 0, 1, 1) or similar
 /// (base texture coordinates may differ depending on the actual shape)
 /// </summary>
 /// <param name="vertexBuilder">Target vertex buffer builder to use</param>
 /// <param name="sorter"><see cref="ParticleSorter"/> to use to iterate over all particles drawn this frame</param>
 /// <param name="texCoordsDescription">Attribute description of the texture coordinates in the current vertex layout</param>
 public abstract void BuildUVCoordinates(ParticleVertexBuilder vertexBuilder, ParticleSorter sorter, AttributeDescription texCoordsDescription);
예제 #21
0
 /// <summary>
 /// Prepares the material for drawing the current frame with the current <see cref="ParticleVertexBuilder"/> and <see cref="ParticleSorter"/>
 /// </summary>
 /// <param name="vertexBuilder">Current <see cref="ParticleVertexBuilder"/></param>
 /// <param name="sorter">Current <see cref="ParticleSorter"/></param>
 public virtual void PrepareForDraw(ParticleVertexBuilder vertexBuilder, ParticleSorter sorter)
 {
 }
예제 #22
0
        /// <inheritdoc />
        public unsafe override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY,
                                                     ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            // Get all the required particle fields
            var positionField = sorter.GetField(ParticleFields.Position);

            if (!positionField.IsValid())
            {
                return(0);
            }
            var sizeField = sorter.GetField(ParticleFields.Size);

            var orderField = sorter.GetField(ParticleFields.Order);

            // Check if the draw space is identity - in this case we don't need to transform the position, scale and rotation vectors
            var trsIdentity = (spaceScale == 1f);

            trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0)));
            trsIdentity = trsIdentity && (spaceRotation.Equals(Quaternion.Identity));


            var renderedParticles = 0;

            vtxBuilder.RestartBuffer();

            uint oldOrderValue = 0;

            foreach (var particle in sorter)
            {
                if (orderField.IsValid())
                {
                    var orderValue = (*((uint *)particle[orderField]));

                    if ((orderValue >> 16) != (oldOrderValue >> 16))
                    {
                        ribbonizer.Ribbonize(vtxBuilder, invViewX, invViewY, QuadsPerParticle);
                        ribbonizer.RibbonSplit();
                    }

                    oldOrderValue = orderValue;
                }

                var centralPos = particle.Get(positionField);

                var particleSize = sizeField.IsValid() ? particle.Get(sizeField) : 1f;

                if (!trsIdentity)
                {
                    spaceRotation.Rotate(ref centralPos);
                    centralPos    = centralPos * spaceScale + spaceTranslation;
                    particleSize *= spaceScale;
                }

                ribbonizer.AddParticle(ref centralPos, particleSize);
                renderedParticles++;
            }

            ribbonizer.Ribbonize(vtxBuilder, invViewX, invViewY, QuadsPerParticle);

            var vtxPerShape = 4 * QuadsPerParticle;

            return(renderedParticles * vtxPerShape);
        }
예제 #23
0
        /// <inheritdoc />
        public override unsafe void PatchVertexBuffer(ParticleVertexBuilder vertexBuilder, Vector3 invViewX, Vector3 invViewY, ParticleSorter sorter)
        {
            // If you want, you can integrate the base builder here and not call it. It should result in slight speed up
            base.PatchVertexBuffer(vertexBuilder, invViewX, invViewY, sorter);

            var colorField = sorter.GetField(ParticleFields.Color);

            if (!colorField.IsValid())
            {
                return;
            }

            var colAttribute = vertexBuilder.GetAccessor(VertexAttributes.Color);

            if (colAttribute.Size <= 0)
            {
                return;
            }

            foreach (var particle in sorter)
            {
                // Set the vertex color attribute to the particle's color field
                var color = (uint)(*(Color4 *)particle[colorField]).ToRgba();
                vertexBuilder.SetAttributePerParticle(colAttribute, (IntPtr)(&color));

                vertexBuilder.NextParticle();
            }

            vertexBuilder.RestartBuffer();
        }
예제 #24
0
 /// <summary>
 /// Check if ParticleVertexElements should be changed and set VertexLayoutHasChanged = true; if they do
 /// </summary>
 /// <param name="vertexBuilder">Target vertex buffer stream builder which will be used for the current frame</param>
 /// <param name="sorter">Enumerator which accesses all particles in a sorted manner</param>
 public virtual void PrepareForDraw(ParticleVertexBuilder vertexBuilder, ParticleSorter sorter)
 {
     // Check if ParticleVertexElements should be changed and set VertexLayoutHasChanged = true; if they do
 }
예제 #25
0
        /// <inheritdoc />
        public unsafe override void BuildUVCoordinates(ParticleVertexBuilder vertexBuilder, ParticleSorter sorter, AttributeDescription texCoordsDescription)
        {
            var lifeField = sorter.GetField(ParticleFields.RemainingLife);

            if (!lifeField.IsValid())
            {
                return;
            }

            var texAttribute = vertexBuilder.GetAccessor(texCoordsDescription);

            if (texAttribute.Size == 0 && texAttribute.Offset == 0)
            {
                return;
            }

            var texDefault = vertexBuilder.GetAccessor(vertexBuilder.DefaultTexCoords);

            if (texDefault.Size == 0 && texDefault.Offset == 0)
            {
                return;
            }

            foreach (var particle in sorter)
            {
                var normalizedTimeline = 1f - *(float *)(particle[lifeField]);;

                var uvTransform = Vector4.Lerp(StartFrame, EndFrame, normalizedTimeline);
                uvTransform.Z -= uvTransform.X;
                uvTransform.W -= uvTransform.Y;

                ParticleVertexBuilder.TransformAttributeDelegate <Vector2> transformCoords =
                    (ref Vector2 value) =>
                {
                    value.X = uvTransform.X + uvTransform.Z * value.X;
                    value.Y = uvTransform.Y + uvTransform.W * value.Y;
                };

                vertexBuilder.TransformAttributePerParticle(texDefault, texAttribute, transformCoords);

                vertexBuilder.NextParticle();
            }


            vertexBuilder.RestartBuffer();
        }
예제 #26
0
        /// <inheritdoc />
        public override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY, ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            SamplerPosition?.UpdateChanges();

            SamplerSize?.UpdateChanges();

            return(0);
        }
예제 #27
0
        /// <inheritdoc />
        public override int BuildVertexBuffer(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY,
                                              ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ParticleSorter sorter)
        {
            // Get all the required particle fields
            var positionField = sorter.GetField(ParticleFields.Position);

            if (!positionField.IsValid())
            {
                return(0);
            }
            var sizeField      = sorter.GetField(ParticleFields.Size);
            var directionField = sorter.GetField(ParticleFields.Direction);

            // Check if the draw space is identity - in this case we don't need to transform the position, scale and rotation vectors
            var trsIdentity = (spaceScale == 1f);

            trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0)));
            trsIdentity = trsIdentity && (spaceRotation.Equals(new Quaternion(0, 0, 0, 1)));


            var renderedParticles = 0;

            foreach (var particle in sorter)
            {
                var centralPos = particle.Get(positionField);

                var particleSize      = sizeField.IsValid() ? particle.Get(sizeField) : 1f;
                var particleDirection = directionField.IsValid() ? particle.Get(directionField) * particleSize : new Vector3(0f, particleSize, 0f);

                if (!trsIdentity)
                {
                    spaceRotation.Rotate(ref centralPos);
                    centralPos = centralPos * spaceScale + spaceTranslation;

                    // Direction
                    spaceRotation.Rotate(ref particleDirection);
                    particleDirection *= spaceScale;
                }

                ribbonizer.AddParticle(ref centralPos, ref particleDirection);

                renderedParticles++;
            }

            ribbonizer.Ribbonize(vtxBuilder, QuadsPerParticle);

            var vtxPerShape = 4 * QuadsPerParticle;

            return(renderedParticles * vtxPerShape);
        }