Files
FlaxEngine/Source/Engine/Graphics/Models/MeshDeformation.cpp

167 lines
5.1 KiB
C++

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "MeshDeformation.h"
#include "Engine/Graphics/Models/MeshBase.h"
#include "Engine/Profiler/ProfilerCPU.h"
struct Key
{
union
{
struct
{
uint8 Lod;
uint8 BufferType;
uint16 MeshIndex;
};
uint32 Value;
};
};
FORCE_INLINE static uint32 GetKey(int32 lodIndex, int32 meshIndex, MeshBufferType type)
{
Key key;
key.Value = 0;
key.Lod = (uint8)lodIndex;
key.BufferType = (uint8)type;
key.MeshIndex = (uint16)meshIndex;
return key.Value;
}
void MeshDeformation::GetBounds(int32 lodIndex, int32 meshIndex, BoundingBox& bounds) const
{
const auto key = GetKey(lodIndex, meshIndex, MeshBufferType::Vertex0);
for (MeshDeformationData* deformation : _deformations)
{
if (deformation->Key == key)
{
bounds = deformation->Bounds;
break;
}
}
}
void MeshDeformation::Clear()
{
for (MeshDeformationData* e : _deformations)
Delete(e);
_deformations.Clear();
}
void MeshDeformation::Dirty()
{
for (MeshDeformationData* deformation : _deformations)
deformation->Dirty = true;
}
void MeshDeformation::Dirty(int32 lodIndex, int32 meshIndex, MeshBufferType type)
{
const auto key = GetKey(lodIndex, meshIndex, type);
for (MeshDeformationData* deformation : _deformations)
{
if (deformation->Key == key)
{
deformation->Dirty = true;
break;
}
}
}
void MeshDeformation::Dirty(int32 lodIndex, int32 meshIndex, MeshBufferType type, const BoundingBox& bounds)
{
const auto key = GetKey(lodIndex, meshIndex, type);
for (MeshDeformationData* deformation : _deformations)
{
if (deformation->Key == key)
{
deformation->Dirty = true;
deformation->Bounds = bounds;
break;
}
}
}
void MeshDeformation::AddDeformer(int32 lodIndex, int32 meshIndex, MeshBufferType type, const Function<void(const MeshBase* mesh, MeshDeformationData& deformation)>& deformer)
{
const auto key = GetKey(lodIndex, meshIndex, type);
_deformers[key].Bind(deformer);
Dirty(lodIndex, meshIndex, type);
}
void MeshDeformation::RemoveDeformer(int32 lodIndex, int32 meshIndex, MeshBufferType type, const Function<void(const MeshBase* mesh, MeshDeformationData& deformation)>& deformer)
{
const auto key = GetKey(lodIndex, meshIndex, type);
_deformers[key].Unbind(deformer);
Dirty(lodIndex, meshIndex, type);
}
void MeshDeformation::RunDeformers(const MeshBase* mesh, MeshBufferType type, GPUBuffer*& vertexBuffer)
{
const auto key = GetKey(mesh->GetLODIndex(), mesh->GetIndex(), type);
if (const auto* e = _deformers.TryGet(key))
{
PROFILE_CPU();
const int32 vertexStride = vertexBuffer->GetStride();
// Get mesh deformation container
MeshDeformationData* deformation = nullptr;
for (int32 i = 0; i < _deformations.Count(); i++)
{
if (_deformations[i]->Key == key)
{
deformation = _deformations[i];
break;
}
}
if (!e->IsBinded())
{
// Auto-recycle unused deformations
if (deformation)
{
_deformations.Remove(deformation);
Delete(deformation);
}
_deformers.Remove(key);
return;
}
if (!deformation)
{
deformation = New<MeshDeformationData>(key, vertexStride);
deformation->VertexBuffer.Data.Resize(vertexBuffer->GetSize());
deformation->Bounds = mesh->GetBox();
_deformations.Add(deformation);
}
if (deformation->Dirty)
{
// Get original mesh vertex buffer data (cached on CPU)
BytesContainer vertexData;
int32 vertexCount;
if (mesh->DownloadDataCPU(type, vertexData, vertexCount))
return;
ASSERT(vertexData.Length() / vertexCount == vertexStride);
// Init dirty range with valid data (use the dirty range from the previous update to be cleared with initial data)
deformation->VertexBuffer.Data.Resize(vertexData.Length());
const uint32 dirtyDataStart = Math::Min<uint32>(deformation->DirtyMinIndex, vertexCount - 1) * vertexStride;
const uint32 dirtyDataLength = Math::Min<uint32>(deformation->DirtyMaxIndex - deformation->DirtyMinIndex + 1, vertexCount) * vertexStride;
Platform::MemoryCopy(deformation->VertexBuffer.Data.Get() + dirtyDataStart, vertexData.Get() + dirtyDataStart, dirtyDataLength);
// Reset dirty state
deformation->DirtyMinIndex = MAX_uint32 - 1;
deformation->DirtyMaxIndex = 0;
deformation->Dirty = false;
// Run deformers
(*e)(mesh, *deformation);
// Upload modified vertex data to the GPU
deformation->VertexBuffer.Flush();
}
// Override vertex buffer for draw call
vertexBuffer = deformation->VertexBuffer.GetBuffer();
}
}