325 lines
9.8 KiB
C++
325 lines
9.8 KiB
C++
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
#include "MeshBase.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "Engine/Content/Assets/ModelBase.h"
|
|
#include "Engine/Core/Math/Transform.h"
|
|
#include "Engine/Graphics/GPUBuffer.h"
|
|
#include "Engine/Graphics/GPUContext.h"
|
|
#include "Engine/Graphics/GPUDevice.h"
|
|
#include "Engine/Renderer/DrawCall.h"
|
|
#include "Engine/Scripting/ManagedCLR/MCore.h"
|
|
|
|
namespace
|
|
{
|
|
#if !COMPILE_WITHOUT_CSHARP
|
|
template<typename IndexType>
|
|
bool UpdateTriangles(MeshBase* mesh, int32 triangleCount, const MArray* trianglesObj)
|
|
{
|
|
const auto model = mesh->GetModelBase();
|
|
ASSERT(model && model->IsVirtual() && trianglesObj);
|
|
|
|
// Get buffer data
|
|
ASSERT(MCore::Array::GetLength(trianglesObj) / 3 >= triangleCount);
|
|
auto ib = MCore::Array::GetAddress<IndexType>(trianglesObj);
|
|
|
|
return mesh->UpdateTriangles(triangleCount, ib);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MeshBase::~MeshBase()
|
|
{
|
|
SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[0]);
|
|
SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[1]);
|
|
SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[2]);
|
|
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
|
}
|
|
|
|
bool MeshBase::HasVertexColors() const
|
|
{
|
|
return _vertexBuffers[2] != nullptr && _vertexBuffers[2]->IsAllocated();
|
|
}
|
|
|
|
void MeshBase::SetMaterialSlotIndex(int32 value)
|
|
{
|
|
if (value < 0 || value >= _model->MaterialSlots.Count())
|
|
{
|
|
LOG(Warning, "Cannot set mesh material slot to {0} while model has {1} slots.", value, _model->MaterialSlots.Count());
|
|
return;
|
|
}
|
|
_materialSlotIndex = value;
|
|
}
|
|
|
|
void MeshBase::SetBounds(const BoundingBox& box)
|
|
{
|
|
_box = box;
|
|
BoundingSphere::FromBox(box, _sphere);
|
|
_hasBounds = true;
|
|
}
|
|
|
|
void MeshBase::SetBounds(const BoundingBox& box, const BoundingSphere& sphere)
|
|
{
|
|
_box = box;
|
|
_sphere = sphere;
|
|
_hasBounds = true;
|
|
if (_model && _model->IsLoaded())
|
|
{
|
|
// Send event (actors using this model can update bounds, etc.)
|
|
_model->onLoaded();
|
|
}
|
|
}
|
|
|
|
bool MeshBase::Init(uint32 vertices, uint32 triangles, const Array<const void*, FixedAllocation<3>>& vbData, const void* ibData, bool use16BitIndexBuffer, const Array<GPUVertexLayout*, FixedAllocation<3>>& vbLayout)
|
|
{
|
|
CHECK_RETURN(vbData.HasItems() && vertices, true);
|
|
CHECK_RETURN(ibData, true);
|
|
CHECK_RETURN(vbLayout.Count() >= vbData.Count(), true);
|
|
ASSERT(_model);
|
|
GPUBuffer* vertexBuffer0 = nullptr;
|
|
GPUBuffer* vertexBuffer1 = nullptr;
|
|
GPUBuffer* vertexBuffer2 = nullptr;
|
|
GPUBuffer* indexBuffer = nullptr;
|
|
|
|
// Create GPU buffers
|
|
#if GPU_ENABLE_RESOURCE_NAMING
|
|
const String& modelPath = _model->GetPath();
|
|
#define MESH_BUFFER_NAME(postfix) modelPath + TEXT(postfix)
|
|
#else
|
|
#define MESH_BUFFER_NAME(postfix) String::Empty
|
|
#endif
|
|
vertexBuffer0 = GPUDevice::Instance->CreateBuffer(MESH_BUFFER_NAME(".VB0"));
|
|
if (vertexBuffer0->Init(GPUBufferDescription::Vertex(vbLayout[0], vertices, vbData[0])))
|
|
goto ERROR_LOAD_END;
|
|
if (vbData.Count() >= 2 && vbData[1])
|
|
{
|
|
vertexBuffer1 = GPUDevice::Instance->CreateBuffer(MESH_BUFFER_NAME(".VB1"));
|
|
if (vertexBuffer1->Init(GPUBufferDescription::Vertex(vbLayout[1], vertices, vbData[1])))
|
|
goto ERROR_LOAD_END;
|
|
}
|
|
if (vbData.Count() >= 3 && vbData[2])
|
|
{
|
|
vertexBuffer2 = GPUDevice::Instance->CreateBuffer(MESH_BUFFER_NAME(".VB2"));
|
|
if (vertexBuffer2->Init(GPUBufferDescription::Vertex(vbLayout[2], vertices, vbData[2])))
|
|
goto ERROR_LOAD_END;
|
|
}
|
|
indexBuffer = GPUDevice::Instance->CreateBuffer(MESH_BUFFER_NAME(".IB"));
|
|
if (indexBuffer->Init(GPUBufferDescription::Index(use16BitIndexBuffer ? sizeof(uint16) : sizeof(uint32), triangles * 3, ibData)))
|
|
goto ERROR_LOAD_END;
|
|
|
|
// Init collision proxy
|
|
#if USE_PRECISE_MESH_INTERSECTS
|
|
if (!_collisionProxy.HasData())
|
|
{
|
|
if (use16BitIndexBuffer)
|
|
_collisionProxy.Init<uint16>(vertices, triangles, (const Float3*)vbData[0], (const uint16*)ibData);
|
|
else
|
|
_collisionProxy.Init<uint32>(vertices, triangles, (const Float3*)vbData[0], (const uint32*)ibData);
|
|
}
|
|
#endif
|
|
|
|
// Initialize
|
|
_vertexBuffers[0] = vertexBuffer0;
|
|
_vertexBuffers[1] = vertexBuffer1;
|
|
_vertexBuffers[2] = vertexBuffer2;
|
|
_indexBuffer = indexBuffer;
|
|
_triangles = triangles;
|
|
_vertices = vertices;
|
|
_use16BitIndexBuffer = use16BitIndexBuffer;
|
|
_cachedVertexBuffers[0].Clear();
|
|
_cachedVertexBuffers[1].Clear();
|
|
_cachedVertexBuffers[2].Clear();
|
|
|
|
return false;
|
|
|
|
#undef MESH_BUFFER_NAME
|
|
ERROR_LOAD_END:
|
|
|
|
SAFE_DELETE_GPU_RESOURCE(vertexBuffer0);
|
|
SAFE_DELETE_GPU_RESOURCE(vertexBuffer1);
|
|
SAFE_DELETE_GPU_RESOURCE(vertexBuffer2);
|
|
SAFE_DELETE_GPU_RESOURCE(indexBuffer);
|
|
return true;
|
|
}
|
|
|
|
void MeshBase::Release()
|
|
{
|
|
SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[0]);
|
|
SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[1]);
|
|
SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[2]);
|
|
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
|
_triangles = 0;
|
|
_vertices = 0;
|
|
_use16BitIndexBuffer = false;
|
|
_cachedIndexBuffer.Resize(0);
|
|
_cachedVertexBuffers[0].Clear();
|
|
_cachedVertexBuffers[1].Clear();
|
|
_cachedVertexBuffers[2].Clear();
|
|
}
|
|
|
|
bool MeshBase::UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices)
|
|
{
|
|
uint32 indicesCount = triangleCount * 3;
|
|
uint32 ibStride = use16BitIndices ? sizeof(uint16) : sizeof(uint32);
|
|
if (!_indexBuffer)
|
|
_indexBuffer = GPUDevice::Instance->CreateBuffer(TEXT("DynamicMesh.IB"));
|
|
if (_indexBuffer->Init(GPUBufferDescription::Index(ibStride, indicesCount, ib)))
|
|
{
|
|
_triangles = 0;
|
|
return true;
|
|
}
|
|
|
|
// TODO: update collision proxy
|
|
|
|
_triangles = triangleCount;
|
|
_use16BitIndexBuffer = use16BitIndices;
|
|
return false;
|
|
}
|
|
|
|
bool MeshBase::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const
|
|
{
|
|
// Get bounding box of the mesh bounds transformed by the instance world matrix
|
|
Vector3 corners[8];
|
|
_box.GetCorners(corners);
|
|
Vector3 tmp;
|
|
Vector3::Transform(corners[0], world, tmp);
|
|
Vector3 min = tmp;
|
|
Vector3 max = tmp;
|
|
for (int32 i = 1; i < 8; i++)
|
|
{
|
|
Vector3::Transform(corners[i], world, tmp);
|
|
min = Vector3::Min(min, tmp);
|
|
max = Vector3::Max(max, tmp);
|
|
}
|
|
const BoundingBox transformedBox(min, max);
|
|
|
|
// Test ray on box
|
|
#if USE_PRECISE_MESH_INTERSECTS
|
|
if (transformedBox.Intersects(ray, distance))
|
|
{
|
|
// Use exact test on raw geometry
|
|
return _collisionProxy.Intersects(ray, world, distance, normal);
|
|
}
|
|
distance = 0;
|
|
normal = Vector3::Up;
|
|
return false;
|
|
#else
|
|
return transformedBox.Intersects(ray, distance, normal);
|
|
#endif
|
|
}
|
|
|
|
bool MeshBase::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const
|
|
{
|
|
// Get bounding box of the mesh bounds transformed by the instance world matrix
|
|
Vector3 corners[8];
|
|
_box.GetCorners(corners);
|
|
Vector3 tmp;
|
|
transform.LocalToWorld(corners[0], tmp);
|
|
Vector3 min = tmp;
|
|
Vector3 max = tmp;
|
|
for (int32 i = 1; i < 8; i++)
|
|
{
|
|
transform.LocalToWorld(corners[i], tmp);
|
|
min = Vector3::Min(min, tmp);
|
|
max = Vector3::Max(max, tmp);
|
|
}
|
|
const BoundingBox transformedBox(min, max);
|
|
|
|
// Test ray on box
|
|
#if USE_PRECISE_MESH_INTERSECTS
|
|
if (transformedBox.Intersects(ray, distance))
|
|
{
|
|
// Use exact test on raw geometry
|
|
return _collisionProxy.Intersects(ray, transform, distance, normal);
|
|
}
|
|
distance = 0;
|
|
normal = Vector3::Up;
|
|
return false;
|
|
#else
|
|
return transformedBox.Intersects(ray, distance, normal);
|
|
#endif
|
|
}
|
|
|
|
bool MeshBase::DownloadDataGPU(MeshBufferType type, BytesContainer& result) const
|
|
{
|
|
GPUBuffer* buffer = nullptr;
|
|
switch (type)
|
|
{
|
|
case MeshBufferType::Index:
|
|
buffer = _indexBuffer;
|
|
break;
|
|
case MeshBufferType::Vertex0:
|
|
buffer = _vertexBuffers[0];
|
|
break;
|
|
case MeshBufferType::Vertex1:
|
|
buffer = _vertexBuffers[1];
|
|
break;
|
|
case MeshBufferType::Vertex2:
|
|
buffer = _vertexBuffers[2];
|
|
break;
|
|
}
|
|
return buffer && buffer->DownloadData(result);
|
|
}
|
|
|
|
Task* MeshBase::DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const
|
|
{
|
|
GPUBuffer* buffer = nullptr;
|
|
switch (type)
|
|
{
|
|
case MeshBufferType::Index:
|
|
buffer = _indexBuffer;
|
|
break;
|
|
case MeshBufferType::Vertex0:
|
|
buffer = _vertexBuffers[0];
|
|
break;
|
|
case MeshBufferType::Vertex1:
|
|
buffer = _vertexBuffers[1];
|
|
break;
|
|
case MeshBufferType::Vertex2:
|
|
buffer = _vertexBuffers[2];
|
|
break;
|
|
}
|
|
return buffer ? buffer->DownloadDataAsync(result) : nullptr;
|
|
}
|
|
|
|
void MeshBase::GetDrawCallGeometry(DrawCall& drawCall) const
|
|
{
|
|
drawCall.Geometry.IndexBuffer = _indexBuffer;
|
|
drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0];
|
|
drawCall.Geometry.VertexBuffers[1] = _vertexBuffers[1];
|
|
drawCall.Geometry.VertexBuffers[2] = _vertexBuffers[2];
|
|
drawCall.Geometry.VertexBuffersOffsets[0] = 0;
|
|
drawCall.Geometry.VertexBuffersOffsets[1] = 0;
|
|
drawCall.Geometry.VertexBuffersOffsets[2] = 0;
|
|
drawCall.Draw.StartIndex = 0;
|
|
drawCall.Draw.IndicesCount = _triangles * 3;
|
|
}
|
|
|
|
void MeshBase::Render(GPUContext* context) const
|
|
{
|
|
if (!IsInitialized())
|
|
return;
|
|
context->BindVB(ToSpan(_vertexBuffers, 3));
|
|
context->BindIB(_indexBuffer);
|
|
context->DrawIndexed(_triangles * 3);
|
|
}
|
|
|
|
ScriptingObject* MeshBase::GetParentModel() const
|
|
{
|
|
return _model;
|
|
}
|
|
|
|
#if !COMPILE_WITHOUT_CSHARP
|
|
|
|
bool MeshBase::UpdateTrianglesUInt(int32 triangleCount, const MArray* trianglesObj)
|
|
{
|
|
return ::UpdateTriangles<uint32>(this, triangleCount, trianglesObj);
|
|
}
|
|
|
|
bool MeshBase::UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj)
|
|
{
|
|
return ::UpdateTriangles<uint16>(this, triangleCount, trianglesObj);
|
|
}
|
|
|
|
#endif
|