Esempio n. 1
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;

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

                vertexBuilder.NextParticle();
            }

            vertexBuilder.RestartBuffer();
        }
Esempio n. 2
0
        /// <summary>
        /// Build the vertex buffer from particle data
        /// Should come before <see cref="KickVertexBuffer"/>
        /// </summary>
        /// <param name="device">The graphics device, used to rebuild vertex layouts and shaders if needed</param>
        /// <param name="invViewMatrix">The current camera's inverse view matrix</param>
        public void BuildVertexBuffer(CommandList commandList, ref Matrix invViewMatrix)
        {
            // Get camera-space X and Y axes for billboard expansion and sort the particles if needed
            var unitX = new Vector3(invViewMatrix.M11, invViewMatrix.M12, invViewMatrix.M13);
            var unitY = new Vector3(invViewMatrix.M21, invViewMatrix.M22, invViewMatrix.M23);

            depthSortVector = Vector3.Cross(unitX, unitY);
            ParticleSorter.Sort();


            // If the particles are in world space they don't need to be fixed as their coordinates are already in world space
            // If the particles are in local space they need to be drawn in world space using the emitter's current location matrix
            var posIdentity   = new Vector3(0, 0, 0);
            var rotIdentity   = new Quaternion(0, 0, 0, 1);
            var scaleIdentity = 1f;

            if (simulationSpace == EmitterSimulationSpace.Local)
            {
                posIdentity   = drawPosition;
                rotIdentity   = drawRotation;
                scaleIdentity = drawScale;
            }

            VertexBuilder.MapBuffer(commandList);

            ShapeBuilder.BuildVertexBuffer(VertexBuilder, unitX, unitY, ref posIdentity, ref rotIdentity, scaleIdentity, ParticleSorter);

            VertexBuilder.RestartBuffer();

            ShapeBuilder.SetRequiredQuads(ShapeBuilder.QuadsPerParticle, pool.LivingParticles, pool.ParticleCapacity);
            Material.PatchVertexBuffer(VertexBuilder, unitX, unitY, ParticleSorter);

            VertexBuilder.UnmapBuffer(commandList);
        }
Esempio n. 3
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();
        }
Esempio n. 4
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();
        }
        /// <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 spriteId = startingFrame + (int)(normalizedTimeline * animationSpeedOverLife);

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

                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();
        }
Esempio n. 6
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();
        }
Esempio n. 7
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);
        }
Esempio n. 8
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;
        }
Esempio n. 9
0
            /// <summary>
            /// Constructs the ribbon by outputting vertex stream based on the positions and sizes specified previously
            /// </summary>
            /// <param name="vtxBuilder">Target <see cref="ParticleVertexBuilder"/></param> to use
            /// <param name="invViewX">Unit vector X in clip space as calculated from the inverse view matrix</param>
            /// <param name="invViewY">Unit vector Y in clip space as calculated from the inverse view matrix</param>
            /// <param name="quadsPerParticle">The required number of quads per each particle</param>
            /// <param name="texPolicy">Texture coordinates stretching and stitching policy</param>
            /// <param name="texFactor">Texture coordinates stretching and stitching coefficient</param>
            /// <param name="uvRotate">Texture coordinates rotate and flip policy</param>
            public unsafe void Ribbonize(ParticleVertexBuilder vtxBuilder, Vector3 invViewX, Vector3 invViewY, int quadsPerParticle)
            {
                if (lastParticle <= 0)
                {
                    return;
                }

                vtxBuilder.RestartBuffer();

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

                if (lastParticle <= sections)
                {
                    // Optional - connect first particle to the origin/emitter

                    // Draw a dummy quad for the first particle
                    var particlePos = new Vector3(0, 0, 0);
                    var uvCoord     = new Vector2(0, 0);

                    for (var particleIdx = 0; particleIdx < lastParticle; particleIdx++)
                    {
                        for (var vtxIdx = 0; vtxIdx < 4; vtxIdx++)
                        {
                            vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));
                            vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&uvCoord));
                            vtxBuilder.NextVertex();
                        }
                    }

                    return;
                }

                if (sections > 1)
                {
                    if (SmoothingPolicy == SmoothingPolicy.Best)
                    {
                        ExpandVertices_Circular();
                    }
                    else // if (SmoothingPolicy == SmoothingPolicy.Fast)
                    {
                        ExpandVertices_CatmullRom();
                    }
                }

                vtxBuilder.SetVerticesPerSegment(quadsPerParticle * 6, quadsPerParticle * 4, quadsPerParticle * 2);

                // Step 1 - Determine the origin of the ribbon
                var invViewZ = Vector3.Cross(invViewX, invViewY);

                invViewZ.Normalize();

                var axis0 = positions[0] - positions[1];

                axis0.Normalize();

                var oldPoint = positions[0];
                var oldUnitX = GetWidthVector(sizes[0], ref invViewZ, ref axis0, ref axis0);

                // Step 2 - Draw each particle, connecting it to the previous (front) position

                var vCoordOld = 0f;

                for (int i = 0; i < lastParticle; i++)
                {
                    var centralPos = positions[i];

                    var particleSize = sizes[i];

                    // Directions for smoothing
                    var axis1 = (i + 1 < lastParticle) ? positions[i] - positions[i + 1] : positions[lastParticle - 2] - positions[lastParticle - 1];
                    axis1.Normalize();

                    var unitX = GetWidthVector(particleSize, ref invViewZ, ref axis0, ref axis1);

                    axis0 = axis1;

                    // Particle rotation - intentionally IGNORED for ribbon

                    var particlePos  = oldPoint - oldUnitX;
                    var uvCoord      = new Vector2(0, 0);
                    var rotatedCoord = uvCoord;


                    // Top Left - 0f 0f
                    uvCoord.Y = (TextureCoordinatePolicy == TextureCoordinatePolicy.AsIs) ? 0 : vCoordOld;
                    vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));

                    rotatedCoord = UVRotate.GetCoords(uvCoord);
                    vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&rotatedCoord));

                    vtxBuilder.NextVertex();


                    // Top Right - 1f 0f
                    particlePos += oldUnitX * 2;
                    vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));

                    uvCoord.X    = 1;
                    rotatedCoord = UVRotate.GetCoords(uvCoord);
                    vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&rotatedCoord));

                    vtxBuilder.NextVertex();


                    // Move the position to the next particle in the ribbon
                    particlePos += centralPos - oldPoint;
                    particlePos += unitX - oldUnitX;
                    vCoordOld    = (TextureCoordinatePolicy == TextureCoordinatePolicy.Stretched) ?
                                   ((i + 1) / (float)(lastParticle) * TexCoordsFactor) : ((centralPos - oldPoint).Length() * TexCoordsFactor) + vCoordOld;


                    // Bottom Left - 1f 1f
                    uvCoord.Y = (TextureCoordinatePolicy == TextureCoordinatePolicy.AsIs) ? 1 : vCoordOld;
                    vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));

                    rotatedCoord = UVRotate.GetCoords(uvCoord);
                    vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&rotatedCoord));

                    vtxBuilder.NextVertex();


                    // Bottom Right - 0f 1f
                    particlePos -= unitX * 2;
                    vtxBuilder.SetAttribute(posAttribute, (IntPtr)(&particlePos));

                    uvCoord.X    = 0;
                    rotatedCoord = UVRotate.GetCoords(uvCoord);
                    vtxBuilder.SetAttribute(texAttribute, (IntPtr)(&rotatedCoord));

                    vtxBuilder.NextVertex();


                    // Preserve the old attributes for the next cycle
                    oldUnitX = unitX;
                    oldPoint = centralPos;
                }
            }