/// <inheritdoc /> public override unsafe void PatchVertexBuffer(ref ParticleBufferState bufferState, Vector3 invViewX, Vector3 invViewY, ref ParticleList sorter) { // If you want, you can integrate the base builder here and not call it. It should result in slight speed up base.PatchVertexBuffer(ref bufferState, invViewX, invViewY, ref sorter); var colorField = sorter.GetField(ParticleFields.Color); if (!colorField.IsValid()) { return; } var colAttribute = bufferState.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(); bufferState.SetAttributePerSegment(colAttribute, (IntPtr)(&color)); bufferState.NextSegment(); } bufferState.StartOver(); }
public override unsafe void PatchVertexBuffer(ref ParticleBufferState bufferState, Vector3 invViewX, Vector3 invViewY, ref ParticleList sorter) { // If you want, you can integrate the base builder here and not call it. It should result in slight speed up base.PatchVertexBuffer(ref bufferState, invViewX, invViewY, ref sorter); // Update the non-default coordinates first, because they update off the default ones if (UVBuilder1 != null) { UVBuilder1.BuildUVCoordinates(ref bufferState, ref sorter, texCoord1); } // Update the default coordinates last if (UVBuilder0 != null) { UVBuilder0.BuildUVCoordinates(ref bufferState, ref 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 = bufferState.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; bufferState.StartOver(); foreach (var particle in sorter) { bufferState.SetAttributePerParticle(colAttribute, (IntPtr)(&color)); bufferState.NextParticle(); } bufferState.StartOver(); }
/// <inheritdoc /> public unsafe override void PatchVertexBuffer(ref ParticleBufferState bufferState, Vector3 invViewX, Vector3 invViewY, ref ParticleList sorter) { // If you want, you can implement the base builder here and not call it. It should result in slight speed up base.PatchVertexBuffer(ref bufferState, invViewX, invViewY, ref sorter); // The UV Builder, if present, animates the basic (0, 0, 1, 1) uv coordinates of each billboard UVBuilder?.BuildUVCoordinates(ref bufferState, ref sorter, bufferState.DefaultTexCoords); bufferState.StartOver(); // 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 = bufferState.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) { bufferState.SetAttributePerParticle(colAttribute, (IntPtr)(&color)); bufferState.NextParticle(); } bufferState.StartOver(); }
/// <summary> /// Build the vertex buffer from particle data /// </summary> /// <param name="sharedBufferPtr">The shared vertex buffer position where the particle data should be output</param> /// <param name="invViewMatrix">The current camera's inverse view matrix</param> public void BuildVertexBuffer(IntPtr sharedBufferPtr, ref Matrix invViewMatrix, ref Matrix viewProj) { // 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); // Not the best solution, might want to improve var depthVector = Vector3.Cross(unitX, unitY); if (simulationSpace == EmitterSimulationSpace.Local) { var inverseRotation = drawTransform.WorldRotation; inverseRotation.W *= -1; inverseRotation.Rotate(ref depthVector); } var sortedList = ParticleSorter.GetSortedList(depthVector); // 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 = Quaternion.Identity; var scaleIdentity = 1f; if (simulationSpace == EmitterSimulationSpace.Local) { posIdentity = drawTransform.WorldPosition; rotIdentity = drawTransform.WorldRotation; scaleIdentity = drawTransform.WorldScale.X; } ParticleBufferState bufferState = new ParticleBufferState(sharedBufferPtr, VertexBuilder); ShapeBuilder.SetRequiredQuads(ShapeBuilder.QuadsPerParticle, pool.LivingParticles, pool.ParticleCapacity); ShapeBuilder.BuildVertexBuffer(ref bufferState, unitX, unitY, ref posIdentity, ref rotIdentity, scaleIdentity, ref sortedList, ref viewProj); bufferState.StartOver(); Material.PatchVertexBuffer(ref bufferState, unitX, unitY, ref sortedList); ParticleSorter.FreeSortedList(ref sortedList); }
/// <inheritdoc /> public override unsafe void BuildUVCoordinates(ref ParticleBufferState bufferState, ref ParticleList sorter, AttributeDescription texCoordsDescription) { var lifeField = sorter.GetField(ParticleFields.RemainingLife); if (!lifeField.IsValid()) { return; } var texAttribute = bufferState.GetAccessor(texCoordsDescription); if (texAttribute.Size == 0 && texAttribute.Offset == 0) { return; } var texDefault = bufferState.GetAccessor(bufferState.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); Vector4 uvTransform = new Vector4((spriteId % xDivisions) * xStep, (spriteId / xDivisions) * yStep, xStep, yStep); bufferState.TransformAttributePerParticle(texDefault, texAttribute, this, ref uvTransform); bufferState.NextParticle(); } bufferState.StartOver(); }
/// <inheritdoc /> public unsafe override void BuildUVCoordinates(ref ParticleBufferState bufferState, ref ParticleList sorter, AttributeDescription texCoordsDescription) { var lifeField = sorter.GetField(ParticleFields.RemainingLife); if (!lifeField.IsValid()) { return; } var texAttribute = bufferState.GetAccessor(texCoordsDescription); if (texAttribute.Size == 0 && texAttribute.Offset == 0) { return; } var texDefault = bufferState.GetAccessor(bufferState.DefaultTexCoords); if (texDefault.Size == 0 && texDefault.Offset == 0) { return; } foreach (var particle in sorter) { var normalizedTimeline = 1f - *(float *)(particle[lifeField]); Vector4 uvTransform = Vector4.Lerp(StartFrame, EndFrame, normalizedTimeline); uvTransform.Z -= uvTransform.X; uvTransform.W -= uvTransform.Y; bufferState.TransformAttributePerSegment(texDefault, texAttribute, this, ref uvTransform); bufferState.NextSegment(); } bufferState.StartOver(); }
/// <inheritdoc /> public override unsafe int BuildVertexBuffer(ref ParticleBufferState bufferState, Vector3 invViewX, Vector3 invViewY, ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ref ParticleList 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 // ReSharper disable once CompareOfFloatsByEqualityOperator var trsIdentity = (spaceScale == 1f); trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0))); trsIdentity = trsIdentity && (spaceRotation.Equals(Quaternion.Identity)); var ribbonizer = new Ribbonizer(this, currentTotalParticles, currentQuadsPerParticle); var renderedParticles = 0; bufferState.StartOver(); uint oldOrderValue = 0; foreach (var particle in sorter) { if (orderField.IsValid()) { var orderValue = (*((uint *)particle[orderField])); if ((orderValue >> SpawnOrderConst.GroupBitOffset) != (oldOrderValue >> SpawnOrderConst.GroupBitOffset)) { ribbonizer.Ribbonize(ref bufferState, 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(ref bufferState, invViewX, invViewY, QuadsPerParticle); ribbonizer.Free(); var vtxPerShape = 4 * QuadsPerParticle; return(renderedParticles * vtxPerShape); }
/// <summary> /// Build the vertex buffer from particle data /// </summary> /// <param name="sharedBufferPtr">The shared vertex buffer position where the particle data should be output</param> /// <param name="invViewMatrix">The current camera's inverse view matrix</param> public void BuildVertexBuffer(IntPtr sharedBufferPtr, 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); // Not the best solution, might want to improve var depthVector = Vector3.Cross(unitX, unitY); if (simulationSpace == EmitterSimulationSpace.Local) { var inverseRotation = drawTransform.WorldRotation; inverseRotation.W *= -1; inverseRotation.Rotate(ref depthVector); } var sortedList = ParticleSorter.GetSortedList(depthVector); // 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 = Quaternion.Identity; var scaleIdentity = 1f; if (simulationSpace == EmitterSimulationSpace.Local) { posIdentity = drawTransform.WorldPosition; rotIdentity = drawTransform.WorldRotation; scaleIdentity = drawTransform.WorldScale.X; } ParticleBufferState bufferState = new ParticleBufferState(sharedBufferPtr, VertexBuilder); ShapeBuilder.SetRequiredQuads(ShapeBuilder.QuadsPerParticle, pool.LivingParticles, pool.ParticleCapacity); ShapeBuilder.BuildVertexBuffer(ref bufferState, unitX, unitY, ref posIdentity, ref rotIdentity, scaleIdentity, ref sortedList); bufferState.StartOver(); Material.PatchVertexBuffer(ref bufferState, unitX, unitY, ref sortedList); ParticleSorter.FreeSortedList(ref sortedList); }
/// <inheritdoc /> public override unsafe int BuildVertexBuffer(ref ParticleBufferState bufferState, Vector3 invViewX, Vector3 invViewY, ref Vector3 spaceTranslation, ref Quaternion spaceRotation, float spaceScale, ref ParticleList 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 // ReSharper disable once CompareOfFloatsByEqualityOperator var trsIdentity = (spaceScale == 1f); trsIdentity = trsIdentity && (spaceTranslation.Equals(new Vector3(0, 0, 0))); trsIdentity = trsIdentity && (spaceRotation.Equals(Quaternion.Identity)); var ribbonizer = new Ribbonizer(this, currentTotalParticles, currentQuadsPerParticle); var renderedParticles = 0; bufferState.StartOver(); uint oldOrderValue = 0; foreach (var particle in sorter) { if (orderField.IsValid()) { var orderValue = (*((uint*)particle[orderField])); if ((orderValue >> SpawnOrderConst.GroupBitOffset) != (oldOrderValue >> SpawnOrderConst.GroupBitOffset)) { ribbonizer.Ribbonize(ref bufferState, 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(ref bufferState, invViewX, invViewY, QuadsPerParticle); ribbonizer.Free(); var vtxPerShape = 4 * QuadsPerParticle; return renderedParticles * vtxPerShape; }
public override unsafe void PatchVertexBuffer(ref ParticleBufferState bufferState, Vector3 invViewX, Vector3 invViewY, ref ParticleList sorter) { // If you want, you can integrate the base builder here and not call it. It should result in slight speed up base.PatchVertexBuffer(ref bufferState, invViewX, invViewY, ref sorter); // Update the non-default coordinates first, because they update off the default ones if (UVBuilder1 != null) UVBuilder1.BuildUVCoordinates(ref bufferState, ref sorter, texCoord1); // Update the default coordinates last if (UVBuilder0 != null) UVBuilder0.BuildUVCoordinates(ref bufferState, ref 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 = bufferState.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; bufferState.StartOver(); foreach (var particle in sorter) { bufferState.SetAttributePerParticle(colAttribute, (IntPtr)(&color)); bufferState.NextParticle(); } bufferState.StartOver(); }
/// <inheritdoc /> public unsafe override void PatchVertexBuffer(ref ParticleBufferState bufferState, Vector3 invViewX, Vector3 invViewY, ref ParticleList sorter) { // If you want, you can implement the base builder here and not call it. It should result in slight speed up base.PatchVertexBuffer(ref bufferState, invViewX, invViewY, ref sorter); // The UV Builder, if present, animates the basic (0, 0, 1, 1) uv coordinates of each billboard UVBuilder?.BuildUVCoordinates(ref bufferState, ref sorter, bufferState.DefaultTexCoords); bufferState.StartOver(); // 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 = bufferState.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) { bufferState.SetAttributePerParticle(colAttribute, (IntPtr)(&color)); bufferState.NextParticle(); } bufferState.StartOver(); }
/// <inheritdoc /> public override unsafe void PatchVertexBuffer(ref ParticleBufferState bufferState, Vector3 invViewX, Vector3 invViewY, ref ParticleList sorter) { // If you want, you can integrate the base builder here and not call it. It should result in slight speed up base.PatchVertexBuffer(ref bufferState, invViewX, invViewY, ref sorter); var colorField = sorter.GetField(ParticleFields.Color); if (!colorField.IsValid()) return; var colAttribute = bufferState.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(); bufferState.SetAttributePerSegment(colAttribute, (IntPtr)(&color)); bufferState.NextSegment(); } bufferState.StartOver(); }