Fix ribbon particles triangle indices ordering to prevent artifacts
This commit is contained in:
@@ -28,13 +28,13 @@
|
||||
#include "Editor/Editor.h"
|
||||
#endif
|
||||
|
||||
struct SpriteParticleVertex
|
||||
{
|
||||
PACK_STRUCT(struct SpriteParticleVertex
|
||||
{
|
||||
float X;
|
||||
float Y;
|
||||
float U;
|
||||
float V;
|
||||
};
|
||||
});
|
||||
|
||||
class SpriteParticleRenderer
|
||||
{
|
||||
@@ -82,6 +82,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
PACK_STRUCT(struct RibbonParticleVertex {
|
||||
uint32 Order;
|
||||
uint32 ParticleIndex;
|
||||
uint32 PrevParticleIndex;
|
||||
float Distance;
|
||||
// TODO: pack into half/uint16 data
|
||||
});
|
||||
|
||||
struct EmitterCache
|
||||
{
|
||||
double LastTimeUsed;
|
||||
@@ -113,7 +121,6 @@ namespace ParticlesDrawCPU
|
||||
Array<uint32> SortingKeys[2];
|
||||
Array<int32> SortingIndices;
|
||||
Array<int32> SortedIndices;
|
||||
Array<float> RibbonTotalDistances;
|
||||
}
|
||||
|
||||
class ParticleManagerService : public EngineService
|
||||
@@ -289,14 +296,17 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
|
||||
{
|
||||
// Prepare ribbon data
|
||||
if (!buffer->GPU.RibbonIndexBufferDynamic)
|
||||
{
|
||||
buffer->GPU.RibbonIndexBufferDynamic = New<DynamicIndexBuffer>(0, (uint32)sizeof(uint16), TEXT("RibbonIndexBufferDynamic"));
|
||||
}
|
||||
buffer->GPU.RibbonIndexBufferDynamic->Clear();
|
||||
else
|
||||
buffer->GPU.RibbonIndexBufferDynamic->Clear();
|
||||
if (!buffer->GPU.RibbonVertexBufferDynamic)
|
||||
buffer->GPU.RibbonVertexBufferDynamic = New<DynamicVertexBuffer>(0, (uint32)sizeof(RibbonParticleVertex), TEXT("RibbonVertexBufferDynamic"));
|
||||
else
|
||||
buffer->GPU.RibbonVertexBufferDynamic->Clear();
|
||||
auto& indexBuffer = buffer->GPU.RibbonIndexBufferDynamic->Data;
|
||||
auto& vertexBuffer = buffer->GPU.RibbonVertexBufferDynamic->Data;
|
||||
|
||||
// Setup all ribbon modules
|
||||
auto& totalDistances = ParticlesDrawCPU::RibbonTotalDistances;
|
||||
totalDistances.Clear();
|
||||
for (int32 index = 0; index < renderModulesIndices.Count(); index++)
|
||||
{
|
||||
const int32 moduleIndex = renderModulesIndices[index];
|
||||
@@ -313,74 +323,88 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
|
||||
int32 count = buffer->CPU.Count;
|
||||
ASSERT(buffer->CPU.RibbonOrder.Count() == emitter->Graph.RibbonRenderingModules.Count() * buffer->Capacity);
|
||||
int32* ribbonOrderData = buffer->CPU.RibbonOrder.Get() + module->RibbonOrderOffset;
|
||||
|
||||
ParticleBufferCPUDataAccessor<Float3> positionData(buffer, emitter->Graph.Layout.GetAttributeOffset(module->Attributes[0]));
|
||||
|
||||
int32 indices = 0;
|
||||
|
||||
// Write ribbon indices/vertices
|
||||
int32 indices = 0, segmentCount = 0;
|
||||
float totalDistance = 0.0f;
|
||||
uint32 lastParticleIdx = ribbonOrderData[0];
|
||||
for (int32 i = 0; i < count; i++)
|
||||
int32 firstVertexIndex = vertexBuffer.Count();
|
||||
uint32 idxPrev = ribbonOrderData[0], vertexPrev = 0;
|
||||
{
|
||||
bool isNotLast = i != count - 1;
|
||||
uint32 idx0 = ribbonOrderData[i];
|
||||
uint32 idx1 = 0;
|
||||
Float3 direction;
|
||||
if (isNotLast)
|
||||
uint32 idxThis = ribbonOrderData[0];
|
||||
|
||||
// 2 vertices
|
||||
{
|
||||
idx1 = ribbonOrderData[i + 1];
|
||||
direction = positionData[idx1] - positionData[lastParticleIdx];
|
||||
}
|
||||
else
|
||||
{
|
||||
idx1 = ribbonOrderData[i - 1];
|
||||
direction = positionData[lastParticleIdx] - positionData[idx1];
|
||||
vertexBuffer.AddUninitialized(2 * sizeof(RibbonParticleVertex));
|
||||
auto ptr = (RibbonParticleVertex*)(vertexBuffer.Get() + firstVertexIndex);
|
||||
|
||||
RibbonParticleVertex v = { 0, idxThis, idxThis, totalDistance };
|
||||
|
||||
*ptr++ = v;
|
||||
*ptr++ = v;
|
||||
}
|
||||
|
||||
if (direction.LengthSquared() > 0.002f || !isNotLast)
|
||||
idxPrev = idxThis;
|
||||
}
|
||||
for (uint32 i = 1; i < count; i++)
|
||||
{
|
||||
uint32 idxThis = ribbonOrderData[i];
|
||||
Float3 direction = positionData[idxThis] - positionData[idxPrev];
|
||||
const float distance = direction.Length();
|
||||
if (distance > 0.002f)
|
||||
{
|
||||
totalDistances.Add(totalDistance);
|
||||
lastParticleIdx = idx1;
|
||||
totalDistance += distance;
|
||||
|
||||
if (isNotLast)
|
||||
// 2 vertices
|
||||
{
|
||||
auto idx = buffer->GPU.RibbonIndexBufferDynamic->Data.Count();
|
||||
buffer->GPU.RibbonIndexBufferDynamic->Data.AddDefault(6 * sizeof(uint16));
|
||||
auto ptr = (uint16*)(buffer->GPU.RibbonIndexBufferDynamic->Data.Get() + idx);
|
||||
auto idx = vertexBuffer.Count();
|
||||
vertexBuffer.AddUninitialized(2 * sizeof(RibbonParticleVertex));
|
||||
auto ptr = (RibbonParticleVertex*)(vertexBuffer.Get() + idx);
|
||||
|
||||
idx0 *= 2;
|
||||
idx1 *= 2;
|
||||
// TODO: this could be optimized by manually fetching per-particle data in vertex shader (2x less data to send and fetch)
|
||||
RibbonParticleVertex v = { i, idxThis, idxPrev, totalDistance };
|
||||
|
||||
*ptr++ = idx0 + 1;
|
||||
*ptr++ = idx1;
|
||||
*ptr++ = idx0;
|
||||
*ptr++ = v;
|
||||
*ptr++ = v;
|
||||
}
|
||||
|
||||
*ptr++ = idx0 + 1;
|
||||
*ptr++ = idx1 + 1;
|
||||
*ptr++ = idx1;
|
||||
// 2 triangles
|
||||
{
|
||||
auto idx = indexBuffer.Count();
|
||||
indexBuffer.AddUninitialized(6 * sizeof(uint16));
|
||||
auto ptr = (uint16*)(indexBuffer.Get() + idx);
|
||||
|
||||
uint32 i0 = vertexPrev;
|
||||
uint32 i1 = vertexPrev + 2;
|
||||
|
||||
*ptr++ = i0;
|
||||
*ptr++ = i0 + 1;
|
||||
*ptr++ = i1;
|
||||
|
||||
*ptr++ = i0 + 1;
|
||||
*ptr++ = i1 + 1;
|
||||
*ptr++ = i1;
|
||||
|
||||
indices += 6;
|
||||
}
|
||||
}
|
||||
|
||||
totalDistance += direction.Length();
|
||||
idxPrev = idxThis;
|
||||
segmentCount++;
|
||||
vertexPrev += 2;
|
||||
}
|
||||
}
|
||||
if (segmentCount == 0)
|
||||
continue;
|
||||
{
|
||||
// Fix first particle vertex data to have proper direction
|
||||
auto ptr0 = (RibbonParticleVertex*)(vertexBuffer.Get() + firstVertexIndex);
|
||||
auto ptr1 = ptr0 + 1;
|
||||
auto ptr2 = ptr1 + 1;
|
||||
ptr0->PrevParticleIndex = ptr1->PrevParticleIndex = ptr2->ParticleIndex;
|
||||
}
|
||||
if (indices == 0)
|
||||
break;
|
||||
|
||||
// Setup ribbon data
|
||||
ribbonModulesSegmentCount[ribbonModuleIndex] = totalDistances.Count();
|
||||
if (totalDistances.HasItems())
|
||||
{
|
||||
auto& ribbonSegmentDistancesBuffer = buffer->GPU.RibbonSegmentDistances[index];
|
||||
if (!ribbonSegmentDistancesBuffer)
|
||||
{
|
||||
ribbonSegmentDistancesBuffer = GPUDevice::Instance->CreateBuffer(TEXT("RibbonSegmentDistances"));
|
||||
ribbonSegmentDistancesBuffer->Init(GPUBufferDescription::Typed(buffer->Capacity, PixelFormat::R32_Float, false, GPUResourceUsage::Dynamic));
|
||||
}
|
||||
context->UpdateBuffer(ribbonSegmentDistancesBuffer, totalDistances.Get(), totalDistances.Count() * sizeof(float));
|
||||
}
|
||||
|
||||
ribbonModulesSegmentCount[ribbonModuleIndex] = segmentCount;
|
||||
ribbonModulesDrawIndicesCount[index] = indices;
|
||||
ribbonModulesDrawIndicesPos += indices;
|
||||
|
||||
@@ -391,6 +415,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
|
||||
{
|
||||
// Upload data to the GPU buffer
|
||||
buffer->GPU.RibbonIndexBufferDynamic->Flush(context);
|
||||
buffer->GPU.RibbonVertexBufferDynamic->Flush(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,13 +515,12 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
|
||||
ribbon.UVScaleX *= sortUScale;
|
||||
ribbon.UVOffsetX += sortUOffset * uvScale.X;
|
||||
}
|
||||
ribbon.SegmentDistances = ribbon.SegmentCount != 0 ? buffer->GPU.RibbonSegmentDistances[index] : nullptr;
|
||||
|
||||
// TODO: invert particles rendering order if camera is closer to the ribbon end than start
|
||||
|
||||
// Submit draw call
|
||||
drawCall.Geometry.IndexBuffer = buffer->GPU.RibbonIndexBufferDynamic->GetBuffer();
|
||||
drawCall.Geometry.VertexBuffers[0] = nullptr;
|
||||
drawCall.Geometry.VertexBuffers[0] = buffer->GPU.RibbonVertexBufferDynamic->GetBuffer();
|
||||
drawCall.Geometry.VertexBuffers[1] = nullptr;
|
||||
drawCall.Geometry.VertexBuffers[2] = nullptr;
|
||||
drawCall.Geometry.VertexBuffersOffsets[0] = 0;
|
||||
@@ -1187,7 +1211,6 @@ void ParticleManagerService::Dispose()
|
||||
ParticlesDrawCPU::SortingKeys[1].SetCapacity(0);
|
||||
ParticlesDrawCPU::SortingIndices.SetCapacity(0);
|
||||
ParticlesDrawCPU::SortedIndices.SetCapacity(0);
|
||||
ParticlesDrawCPU::RibbonTotalDistances.SetCapacity(0);
|
||||
|
||||
PoolLocker.Lock();
|
||||
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
|
||||
|
||||
@@ -18,8 +18,7 @@ ParticleBuffer::~ParticleBuffer()
|
||||
SAFE_DELETE_GPU_RESOURCE(GPU.SortingKeysBuffer);
|
||||
SAFE_DELETE_GPU_RESOURCE(GPU.SortedIndices);
|
||||
SAFE_DELETE(GPU.RibbonIndexBufferDynamic);
|
||||
for (auto& e : GPU.RibbonSegmentDistances)
|
||||
SAFE_DELETE_GPU_RESOURCE(e);
|
||||
SAFE_DELETE(GPU.RibbonVertexBufferDynamic);
|
||||
}
|
||||
|
||||
bool ParticleBuffer::Init(ParticleEmitter* emitter)
|
||||
|
||||
@@ -17,6 +17,7 @@ class ParticleEmitter;
|
||||
class Particles;
|
||||
class GPUBuffer;
|
||||
class DynamicIndexBuffer;
|
||||
class DynamicVertexBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// The particle attribute that defines a single particle layout component.
|
||||
@@ -302,9 +303,9 @@ public:
|
||||
DynamicIndexBuffer* RibbonIndexBufferDynamic = nullptr;
|
||||
|
||||
/// <summary>
|
||||
/// The ribbon particles rendering segment distances buffer. Stored one per ribbon module.
|
||||
/// The ribbon particles rendering vertex buffer (dynamic GPU access).
|
||||
/// </summary>
|
||||
GPUBuffer* RibbonSegmentDistances[PARTICLE_EMITTER_MAX_RIBBONS] = {};
|
||||
DynamicVertexBuffer* RibbonVertexBufferDynamic = nullptr;
|
||||
|
||||
/// <summary>
|
||||
/// The flag used to indicate that GPU buffers data should be cleared before next simulation.
|
||||
|
||||
Reference in New Issue
Block a user