/// <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; } 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; } }
/// <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="quadsPerParticle">The required number of quads per each particle</param> public unsafe void Ribbonize(ParticleVertexBuilder vtxBuilder, int quadsPerParticle) { if (lastParticle <= 0) return; 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); var axis0 = positions[0] - positions[1]; axis0.Normalize(); var oldPoint = positions[0]; var oldUnitX = directions[0]; // 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]; // Directions for smoothing var axis1 = (i + 1 < lastParticle) ? positions[i] - positions[i + 1] : positions[lastParticle - 2] - positions[lastParticle - 1]; axis1.Normalize(); var unitX = directions[i]; // Particle rotation - intentionally IGNORED for ribbon var particlePos = (EdgePolicy == EdgePolicy.Edge) ? oldPoint - oldUnitX : oldPoint; 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 += (EdgePolicy == EdgePolicy.Edge) ? oldUnitX * 2 : oldUnitX; 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 -= (EdgePolicy == EdgePolicy.Edge) ? unitX * 2 : unitX; 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; } }