Move mesh code to shared base class and reordanize files
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
#include "Engine/Content/Assets/Material.h"
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Transform.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
@@ -132,27 +131,9 @@ namespace
|
||||
const auto colors = colorsObj ? MCore::Array::GetAddress<Color32>(colorsObj) : nullptr;
|
||||
return UpdateMesh<IndexType>(mesh, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
|
||||
}
|
||||
|
||||
template<typename IndexType>
|
||||
bool UpdateTriangles(Mesh* mesh, int32 triangleCount, const MArray* trianglesObj)
|
||||
{
|
||||
const auto model = mesh->GetModel();
|
||||
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
|
||||
}
|
||||
|
||||
bool Mesh::HasVertexColors() const
|
||||
{
|
||||
return _vertexBuffers[2] != nullptr && _vertexBuffers[2]->IsAllocated();
|
||||
}
|
||||
|
||||
bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const void* ib, bool use16BitIndices)
|
||||
{
|
||||
auto model = (Model*)_model;
|
||||
@@ -188,31 +169,6 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* ve
|
||||
return ::UpdateMesh<uint32>(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors);
|
||||
}
|
||||
|
||||
bool Mesh::UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices)
|
||||
{
|
||||
// Cache data
|
||||
uint32 indicesCount = triangleCount * 3;
|
||||
uint32 ibStride = use16BitIndices ? sizeof(uint16) : sizeof(uint32);
|
||||
|
||||
// Create index buffer
|
||||
GPUBuffer* indexBuffer = GPUDevice::Instance->CreateBuffer(String::Empty);
|
||||
if (indexBuffer->Init(GPUBufferDescription::Index(ibStride, indicesCount, ib)))
|
||||
{
|
||||
Delete(indexBuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: update collision proxy
|
||||
|
||||
// Initialize
|
||||
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
||||
_indexBuffer = indexBuffer;
|
||||
_triangles = triangleCount;
|
||||
_use16BitIndexBuffer = use16BitIndices;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Mesh::Init(Model* model, int32 lodIndex, int32 index, int32 materialSlotIndex, const BoundingBox& box, const BoundingSphere& sphere, bool hasLightmapUVs)
|
||||
{
|
||||
_model = model;
|
||||
@@ -231,15 +187,6 @@ void Mesh::Init(Model* model, int32 lodIndex, int32 index, int32 materialSlotInd
|
||||
_indexBuffer = nullptr;
|
||||
}
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
// Release buffers
|
||||
SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[0]);
|
||||
SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[1]);
|
||||
SAFE_DELETE_GPU_RESOURCE(_vertexBuffers[2]);
|
||||
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
||||
}
|
||||
|
||||
bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void* vb1, const void* vb2, const void* ib, bool use16BitIndexBuffer)
|
||||
{
|
||||
// Cache data
|
||||
@@ -292,9 +239,9 @@ bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void*
|
||||
_triangles = triangles;
|
||||
_vertices = vertices;
|
||||
_use16BitIndexBuffer = use16BitIndexBuffer;
|
||||
_cachedVertexBuffer[0].Clear();
|
||||
_cachedVertexBuffer[1].Clear();
|
||||
_cachedVertexBuffer[2].Clear();
|
||||
_cachedVertexBuffers[0].Clear();
|
||||
_cachedVertexBuffers[1].Clear();
|
||||
_cachedVertexBuffers[2].Clear();
|
||||
|
||||
return false;
|
||||
|
||||
@@ -308,108 +255,6 @@ ERROR_LOAD_END:
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mesh::Unload()
|
||||
{
|
||||
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);
|
||||
_cachedVertexBuffer[0].Clear();
|
||||
_cachedVertexBuffer[1].Clear();
|
||||
_cachedVertexBuffer[2].Clear();
|
||||
}
|
||||
|
||||
bool Mesh::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 Mesh::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
|
||||
}
|
||||
|
||||
void Mesh::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 Mesh::Render(GPUContext* context) const
|
||||
{
|
||||
if (!IsInitialized())
|
||||
return;
|
||||
|
||||
context->BindVB(ToSpan((GPUBuffer**)_vertexBuffers, 3));
|
||||
context->BindIB(_indexBuffer);
|
||||
context->DrawIndexed(_triangles * 3);
|
||||
}
|
||||
|
||||
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int8 sortOrder) const
|
||||
{
|
||||
if (!material || !material->IsSurface() || !IsInitialized())
|
||||
@@ -579,51 +424,9 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
|
||||
renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, drawModes, info.Flags, shadowsMode, info.Bounds, drawCall, entry.ReceiveDecals, info.SortOrder);
|
||||
}
|
||||
|
||||
bool Mesh::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* Mesh::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;
|
||||
}
|
||||
|
||||
bool Mesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& count) const
|
||||
{
|
||||
if (_cachedVertexBuffer[0].IsEmpty())
|
||||
if (_cachedVertexBuffers[0].IsEmpty())
|
||||
{
|
||||
PROFILE_CPU();
|
||||
auto model = GetModel();
|
||||
@@ -679,10 +482,10 @@ bool Mesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& c
|
||||
// Cache mesh data
|
||||
_cachedIndexBufferCount = indicesCount;
|
||||
_cachedIndexBuffer.Set(ib, indicesCount * ibStride);
|
||||
_cachedVertexBuffer[0].Set((const byte*)vb0, vertices * sizeof(VB0ElementType));
|
||||
_cachedVertexBuffer[1].Set((const byte*)vb1, vertices * sizeof(VB1ElementType));
|
||||
_cachedVertexBuffers[0].Set((const byte*)vb0, vertices * sizeof(VB0ElementType));
|
||||
_cachedVertexBuffers[1].Set((const byte*)vb1, vertices * sizeof(VB1ElementType));
|
||||
if (hasColors)
|
||||
_cachedVertexBuffer[2].Set((const byte*)vb2, vertices * sizeof(VB2ElementType));
|
||||
_cachedVertexBuffers[2].Set((const byte*)vb2, vertices * sizeof(VB2ElementType));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -694,16 +497,16 @@ bool Mesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& c
|
||||
count = _cachedIndexBufferCount;
|
||||
break;
|
||||
case MeshBufferType::Vertex0:
|
||||
result.Link(_cachedVertexBuffer[0]);
|
||||
count = _cachedVertexBuffer[0].Count() / sizeof(VB0ElementType);
|
||||
result.Link(_cachedVertexBuffers[0]);
|
||||
count = _cachedVertexBuffers[0].Count() / sizeof(VB0ElementType);
|
||||
break;
|
||||
case MeshBufferType::Vertex1:
|
||||
result.Link(_cachedVertexBuffer[1]);
|
||||
count = _cachedVertexBuffer[1].Count() / sizeof(VB1ElementType);
|
||||
result.Link(_cachedVertexBuffers[1]);
|
||||
count = _cachedVertexBuffers[1].Count() / sizeof(VB1ElementType);
|
||||
break;
|
||||
case MeshBufferType::Vertex2:
|
||||
result.Link(_cachedVertexBuffer[2]);
|
||||
count = _cachedVertexBuffer[2].Count() / sizeof(VB2ElementType);
|
||||
result.Link(_cachedVertexBuffers[2]);
|
||||
count = _cachedVertexBuffers[2].Count() / sizeof(VB2ElementType);
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
@@ -711,11 +514,6 @@ bool Mesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& c
|
||||
return false;
|
||||
}
|
||||
|
||||
ScriptingObject* Mesh::GetParentModel()
|
||||
{
|
||||
return _model;
|
||||
}
|
||||
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
|
||||
bool Mesh::UpdateMeshUInt(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj)
|
||||
@@ -728,16 +526,6 @@ bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, const MArray
|
||||
return ::UpdateMesh<uint16>(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj);
|
||||
}
|
||||
|
||||
bool Mesh::UpdateTrianglesUInt(int32 triangleCount, const MArray* trianglesObj)
|
||||
{
|
||||
return ::UpdateTriangles<uint32>(this, triangleCount, trianglesObj);
|
||||
}
|
||||
|
||||
bool Mesh::UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj)
|
||||
{
|
||||
return ::UpdateTriangles<uint16>(this, triangleCount, trianglesObj);
|
||||
}
|
||||
|
||||
enum class InternalBufferType
|
||||
{
|
||||
VB0 = 0,
|
||||
|
||||
@@ -101,16 +101,6 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
public Model ParentModel => (Model)Internal_GetParentModel(__unmanagedPtr);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the material slot used by this mesh during rendering.
|
||||
/// </summary>
|
||||
public MaterialSlot MaterialSlot => ParentModel.MaterialSlots[MaterialSlotIndex];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a format of the mesh index buffer.
|
||||
/// </summary>
|
||||
public PixelFormat IndexBufferFormat => Use16BitIndexBuffer ? PixelFormat.R16_UInt : PixelFormat.R32_UInt;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh vertex and index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
@@ -435,82 +425,6 @@ namespace FlaxEngine
|
||||
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
/// Mesh data will be cached and uploaded to the GPU with a delay.
|
||||
/// </summary>
|
||||
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param>
|
||||
public void UpdateTriangles(int[] triangles)
|
||||
{
|
||||
if (!ParentModel.IsVirtual)
|
||||
throw new InvalidOperationException("Only virtual models can be updated at runtime.");
|
||||
if (triangles == null)
|
||||
throw new ArgumentNullException(nameof(triangles));
|
||||
if (triangles.Length == 0 || triangles.Length % 3 != 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(triangles));
|
||||
|
||||
if (Internal_UpdateTrianglesUInt(__unmanagedPtr, triangles.Length / 3, triangles))
|
||||
throw new Exception("Failed to update mesh data.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
/// Mesh data will be cached and uploaded to the GPU with a delay.
|
||||
/// </summary>
|
||||
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param>
|
||||
public void UpdateTriangles(List<int> triangles)
|
||||
{
|
||||
if (!ParentModel.IsVirtual)
|
||||
throw new InvalidOperationException("Only virtual models can be updated at runtime.");
|
||||
if (triangles == null)
|
||||
throw new ArgumentNullException(nameof(triangles));
|
||||
if (triangles.Count == 0 || triangles.Count % 3 != 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(triangles));
|
||||
|
||||
if (Internal_UpdateTrianglesUInt(__unmanagedPtr, triangles.Count / 3, Utils.ExtractArrayFromList(triangles)))
|
||||
throw new Exception("Failed to update mesh data.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
/// Mesh data will be cached and uploaded to the GPU with a delay.
|
||||
/// </summary>
|
||||
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param>
|
||||
public void UpdateTriangles(ushort[] triangles)
|
||||
{
|
||||
if (!ParentModel.IsVirtual)
|
||||
throw new InvalidOperationException("Only virtual models can be updated at runtime.");
|
||||
if (triangles == null)
|
||||
throw new ArgumentNullException(nameof(triangles));
|
||||
if (triangles.Length == 0 || triangles.Length % 3 != 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(triangles));
|
||||
|
||||
if (Internal_UpdateTrianglesUShort(__unmanagedPtr, triangles.Length / 3, triangles))
|
||||
throw new Exception("Failed to update mesh data.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
/// Mesh data will be cached and uploaded to the GPU with a delay.
|
||||
/// </summary>
|
||||
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param>
|
||||
public void UpdateTriangles(List<ushort> triangles)
|
||||
{
|
||||
if (!ParentModel.IsVirtual)
|
||||
throw new InvalidOperationException("Only virtual models can be updated at runtime.");
|
||||
if (triangles == null)
|
||||
throw new ArgumentNullException(nameof(triangles));
|
||||
if (triangles.Count == 0 || triangles.Count % 3 != 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(triangles));
|
||||
|
||||
if (Internal_UpdateTrianglesUShort(__unmanagedPtr, triangles.Count / 3, Utils.ExtractArrayFromList(triangles)))
|
||||
throw new Exception("Failed to update mesh data.");
|
||||
}
|
||||
|
||||
internal enum InternalBufferType
|
||||
{
|
||||
VB0 = 0,
|
||||
@@ -609,7 +523,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Downloads the index buffer that contains mesh triangles data. To download data from GPU set <paramref name="forceGpu"/> to true and call this method from the thread other than main thread (see <see cref="Platform.IsInMainThread"/>).
|
||||
/// </summary>
|
||||
/// <remarks>If mesh index buffer format (see <see cref="IndexBufferFormat"/>) is <see cref="PixelFormat.R16_UInt"/> then it's faster to call .</remarks>
|
||||
/// <remarks>If mesh index buffer format (see <see cref="MeshBase.IndexBufferFormat"/>) is <see cref="PixelFormat.R16_UInt"/> then it's faster to call .</remarks>
|
||||
/// <param name="forceGpu">If set to <c>true</c> the data will be downloaded from the GPU, otherwise it can be loaded from the drive (source asset file) or from memory (if cached). Downloading mesh from GPU requires this call to be made from the other thread than main thread. Virtual assets are always downloaded from GPU memory due to lack of dedicated storage container for the asset data.</param>
|
||||
/// <returns>The gathered data.</returns>
|
||||
public uint[] DownloadIndexBuffer(bool forceGpu = false)
|
||||
@@ -623,7 +537,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Downloads the index buffer that contains mesh triangles data. To download data from GPU set <paramref name="forceGpu"/> to true and call this method from the thread other than main thread (see <see cref="Platform.IsInMainThread"/>).
|
||||
/// </summary>
|
||||
/// <remarks>If mesh index buffer format (see <see cref="IndexBufferFormat"/>) is <see cref="PixelFormat.R32_UInt"/> then data won't be downloaded.</remarks>
|
||||
/// <remarks>If mesh index buffer format (see <see cref="MeshBase.IndexBufferFormat"/>) is <see cref="PixelFormat.R32_UInt"/> then data won't be downloaded.</remarks>
|
||||
/// <param name="forceGpu">If set to <c>true</c> the data will be downloaded from the GPU, otherwise it can be loaded from the drive (source asset file) or from memory (if cached). Downloading mesh from GPU requires this call to be made from the other thread than main thread. Virtual assets are always downloaded from GPU memory due to lack of dedicated storage container for the asset data.</param>
|
||||
/// <returns>The gathered data.</returns>
|
||||
public ushort[] DownloadIndexBufferUShort(bool forceGpu = false)
|
||||
@@ -6,9 +6,6 @@
|
||||
#include "ModelInstanceEntry.h"
|
||||
#include "Config.h"
|
||||
#include "Types.h"
|
||||
#if USE_PRECISE_MESH_INTERSECTS
|
||||
#include "CollisionProxy.h"
|
||||
#endif
|
||||
|
||||
class Lightmap;
|
||||
|
||||
@@ -18,16 +15,9 @@ class Lightmap;
|
||||
API_CLASS(NoSpawn) class FLAXENGINE_API Mesh : public MeshBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(Mesh, MeshBase);
|
||||
|
||||
protected:
|
||||
bool _hasLightmapUVs;
|
||||
GPUBuffer* _vertexBuffers[3] = {};
|
||||
GPUBuffer* _indexBuffer = nullptr;
|
||||
#if USE_PRECISE_MESH_INTERSECTS
|
||||
CollisionProxy _collisionProxy;
|
||||
#endif
|
||||
mutable Array<byte> _cachedVertexBuffer[3];
|
||||
mutable Array<byte> _cachedIndexBuffer;
|
||||
mutable int32 _cachedIndexBufferCount;
|
||||
|
||||
public:
|
||||
Mesh(const Mesh& other)
|
||||
@@ -38,11 +28,6 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="Mesh"/> class.
|
||||
/// </summary>
|
||||
~Mesh();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the model owning this mesh.
|
||||
@@ -52,37 +37,6 @@ public:
|
||||
return (Model*)_model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index buffer.
|
||||
/// </summary>
|
||||
FORCE_INLINE GPUBuffer* GetIndexBuffer() const
|
||||
{
|
||||
return _indexBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertex buffer.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <returns>The buffer.</returns>
|
||||
FORCE_INLINE GPUBuffer* GetVertexBuffer(int32 index) const
|
||||
{
|
||||
return _vertexBuffers[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this mesh is initialized (has vertex and index buffers initialized).
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsInitialized() const
|
||||
{
|
||||
return _vertexBuffers[0] != nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this mesh has a vertex colors buffer.
|
||||
/// </summary>
|
||||
API_PROPERTY() bool HasVertexColors() const;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this mesh contains valid lightmap texture coordinates data.
|
||||
/// </summary>
|
||||
@@ -91,16 +45,6 @@ public:
|
||||
return _hasLightmapUVs;
|
||||
}
|
||||
|
||||
#if USE_PRECISE_MESH_INTERSECTS
|
||||
/// <summary>
|
||||
/// Gets the collision proxy used by the mesh.
|
||||
/// </summary>
|
||||
FORCE_INLINE const CollisionProxy& GetCollisionProxy() const
|
||||
{
|
||||
return _collisionProxy;
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
|
||||
@@ -179,38 +123,6 @@ public:
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Float3* normals = nullptr, const Float3* tangents = nullptr, const Float2* uvs = nullptr, const Color32* colors = nullptr);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer (used by the virtual models created with Init rather than Load).
|
||||
/// </summary>
|
||||
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
||||
/// <param name="ib">The index buffer.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint32* ib)
|
||||
{
|
||||
return UpdateTriangles(triangleCount, ib, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer (used by the virtual models created with Init rather than Load).
|
||||
/// </summary>
|
||||
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
||||
/// <param name="ib">The index buffer.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint16* ib)
|
||||
{
|
||||
return UpdateTriangles(triangleCount, ib, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer (used by the virtual models created with Init rather than Load).
|
||||
/// </summary>
|
||||
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
||||
/// <param name="ib">The index buffer.</param>
|
||||
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
bool UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes instance of the <see cref="Mesh"/> class.
|
||||
@@ -237,45 +149,7 @@ public:
|
||||
/// <returns>True if cannot load data, otherwise false.</returns>
|
||||
bool Load(uint32 vertices, uint32 triangles, const void* vb0, const void* vb1, const void* vb2, const void* ib, bool use16BitIndexBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads the mesh data (vertex buffers and cache). The opposite to Load.
|
||||
/// </summary>
|
||||
void Unload();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Determines if there is an intersection between the mesh and a ray in given world
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test</param>
|
||||
/// <param name="world">World to transform box</param>
|
||||
/// <param name="distance">When the method completes and returns true, contains the distance of the intersection (if any valid).</param>
|
||||
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
|
||||
/// <returns>True whether the two objects intersected</returns>
|
||||
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if there is an intersection between the mesh and a ray in given world
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test</param>
|
||||
/// <param name="transform">The instance transformation.</param>
|
||||
/// <param name="distance">When the method completes and returns true, contains the distance of the intersection (if any valid).</param>
|
||||
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
|
||||
/// <returns>True whether the two objects intersected</returns>
|
||||
bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the draw call geometry for this mesh. Sets the index and vertex buffers.
|
||||
/// </summary>
|
||||
/// <param name="drawCall">The draw call.</param>
|
||||
void GetDrawCallGeometry(DrawCall& drawCall) const;
|
||||
|
||||
/// <summary>
|
||||
/// Draws the mesh. Binds vertex and index buffers and invokes the draw call.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context.</param>
|
||||
void Render(GPUContext* context) const;
|
||||
|
||||
/// <summary>
|
||||
/// Draws the mesh.
|
||||
/// </summary>
|
||||
@@ -307,18 +181,13 @@ public:
|
||||
|
||||
public:
|
||||
// [MeshBase]
|
||||
bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override;
|
||||
Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const override;
|
||||
bool DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& count) const override;
|
||||
|
||||
private:
|
||||
// Internal bindings
|
||||
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
API_FUNCTION(NoProxy) bool UpdateMeshUInt(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj);
|
||||
API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj);
|
||||
API_FUNCTION(NoProxy) bool UpdateTrianglesUInt(int32 triangleCount, const MArray* trianglesObj);
|
||||
API_FUNCTION(NoProxy) bool UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj);
|
||||
API_FUNCTION(NoProxy) MArray* DownloadBuffer(bool forceGpu, MTypeObject* resultType, int32 typeI);
|
||||
#endif
|
||||
};
|
||||
|
||||
240
Source/Engine/Graphics/Models/MeshBase.cpp
Normal file
240
Source/Engine/Graphics/Models/MeshBase.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
// 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);
|
||||
}
|
||||
|
||||
void MeshBase::Unload()
|
||||
{
|
||||
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
|
||||
96
Source/Engine/Graphics/Models/MeshBase.cs
Normal file
96
Source/Engine/Graphics/Models/MeshBase.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
partial class MeshBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the material slot used by this mesh during rendering.
|
||||
/// </summary>
|
||||
public MaterialSlot MaterialSlot => ModelBase.MaterialSlots[MaterialSlotIndex];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a format of the mesh index buffer.
|
||||
/// </summary>
|
||||
public PixelFormat IndexBufferFormat => Use16BitIndexBuffer ? PixelFormat.R16_UInt : PixelFormat.R32_UInt;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
/// Mesh data will be cached and uploaded to the GPU with a delay.
|
||||
/// </summary>
|
||||
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param>
|
||||
public void UpdateTriangles(int[] triangles)
|
||||
{
|
||||
if (!ModelBase.IsVirtual)
|
||||
throw new InvalidOperationException("Only virtual models can be updated at runtime.");
|
||||
if (triangles == null)
|
||||
throw new ArgumentNullException(nameof(triangles));
|
||||
if (triangles.Length == 0 || triangles.Length % 3 != 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(triangles));
|
||||
|
||||
if (Internal_UpdateTrianglesUInt(__unmanagedPtr, triangles.Length / 3, triangles))
|
||||
throw new Exception("Failed to update mesh data.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
/// Mesh data will be cached and uploaded to the GPU with a delay.
|
||||
/// </summary>
|
||||
/// <param name="triangles">The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null.</param>
|
||||
public void UpdateTriangles(List<int> triangles)
|
||||
{
|
||||
if (!ModelBase.IsVirtual)
|
||||
throw new InvalidOperationException("Only virtual models can be updated at runtime.");
|
||||
if (triangles == null)
|
||||
throw new ArgumentNullException(nameof(triangles));
|
||||
if (triangles.Count == 0 || triangles.Count % 3 != 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(triangles));
|
||||
|
||||
if (Internal_UpdateTrianglesUInt(__unmanagedPtr, triangles.Count / 3, Utils.ExtractArrayFromList(triangles)))
|
||||
throw new Exception("Failed to update mesh data.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
/// Mesh data will be cached and uploaded to the GPU with a delay.
|
||||
/// </summary>
|
||||
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param>
|
||||
public void UpdateTriangles(ushort[] triangles)
|
||||
{
|
||||
if (!ModelBase.IsVirtual)
|
||||
throw new InvalidOperationException("Only virtual models can be updated at runtime.");
|
||||
if (triangles == null)
|
||||
throw new ArgumentNullException(nameof(triangles));
|
||||
if (triangles.Length == 0 || triangles.Length % 3 != 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(triangles));
|
||||
|
||||
if (Internal_UpdateTrianglesUShort(__unmanagedPtr, triangles.Length / 3, triangles))
|
||||
throw new Exception("Failed to update mesh data.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
/// Mesh data will be cached and uploaded to the GPU with a delay.
|
||||
/// </summary>
|
||||
/// <param name="triangles">The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null.</param>
|
||||
public void UpdateTriangles(List<ushort> triangles)
|
||||
{
|
||||
if (!ModelBase.IsVirtual)
|
||||
throw new InvalidOperationException("Only virtual models can be updated at runtime.");
|
||||
if (triangles == null)
|
||||
throw new ArgumentNullException(nameof(triangles));
|
||||
if (triangles.Count == 0 || triangles.Count % 3 != 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(triangles));
|
||||
|
||||
if (Internal_UpdateTrianglesUShort(__unmanagedPtr, triangles.Count / 3, Utils.ExtractArrayFromList(triangles)))
|
||||
throw new Exception("Failed to update mesh data.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,15 @@
|
||||
#include "Engine/Core/Math/BoundingBox.h"
|
||||
#include "Engine/Core/Math/BoundingSphere.h"
|
||||
#include "Engine/Core/Types/DataContainer.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Graphics/Enums.h"
|
||||
#include "Engine/Graphics/Models/Types.h"
|
||||
#include "Engine/Level/Types.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
#include "Config.h"
|
||||
#if USE_PRECISE_MESH_INTERSECTS
|
||||
#include "CollisionProxy.h"
|
||||
#endif
|
||||
|
||||
struct GeometryDrawStateData;
|
||||
struct RenderContext;
|
||||
@@ -21,15 +26,17 @@ class SkinnedMeshDrawData;
|
||||
class BlendShapesInstance;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for model resources meshes.
|
||||
/// Base class for mesh objects.
|
||||
/// </summary>
|
||||
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API MeshBase : public ScriptingObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(MeshBase);
|
||||
|
||||
protected:
|
||||
ModelBase* _model;
|
||||
BoundingBox _box;
|
||||
BoundingSphere _sphere;
|
||||
|
||||
int32 _index;
|
||||
int32 _lodIndex;
|
||||
uint32 _vertices;
|
||||
@@ -37,12 +44,25 @@ protected:
|
||||
int32 _materialSlotIndex;
|
||||
bool _use16BitIndexBuffer;
|
||||
|
||||
GPUBuffer* _vertexBuffers[3] = {};
|
||||
GPUBuffer* _indexBuffer = nullptr;
|
||||
|
||||
mutable Array<byte> _cachedVertexBuffers[3];
|
||||
mutable Array<byte> _cachedIndexBuffer;
|
||||
mutable int32 _cachedIndexBufferCount;
|
||||
|
||||
#if USE_PRECISE_MESH_INTERSECTS
|
||||
CollisionProxy _collisionProxy;
|
||||
#endif
|
||||
|
||||
explicit MeshBase(const SpawnParams& params)
|
||||
: ScriptingObject(params)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
~MeshBase();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model owning this mesh.
|
||||
/// </summary>
|
||||
@@ -107,6 +127,29 @@ public:
|
||||
return _use16BitIndexBuffer;
|
||||
}
|
||||
|
||||
#if USE_PRECISE_MESH_INTERSECTS
|
||||
/// <summary>
|
||||
/// Gets the collision proxy used by the mesh.
|
||||
/// </summary>
|
||||
FORCE_INLINE const CollisionProxy& GetCollisionProxy() const
|
||||
{
|
||||
return _collisionProxy;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this mesh is initialized (has vertex and index buffers initialized).
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsInitialized() const
|
||||
{
|
||||
return _vertexBuffers[0] != nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this mesh has a vertex colors buffer.
|
||||
/// </summary>
|
||||
API_PROPERTY() bool HasVertexColors() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the material slot to use during this mesh rendering.
|
||||
/// </summary>
|
||||
@@ -126,14 +169,91 @@ public:
|
||||
/// <param name="box">The bounding box.</param>
|
||||
void SetBounds(const BoundingBox& box);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index buffer.
|
||||
/// </summary>
|
||||
FORCE_INLINE GPUBuffer* GetIndexBuffer() const
|
||||
{
|
||||
return _indexBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertex buffer.
|
||||
/// </summary>
|
||||
/// <param name="index">The bind slot index.</param>
|
||||
/// <returns>The buffer or null if not used.</returns>
|
||||
FORCE_INLINE GPUBuffer* GetVertexBuffer(int32 index) const
|
||||
{
|
||||
return _vertexBuffers[index];
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Extract mesh buffer data from GPU. Cannot be called from the main thread.
|
||||
/// Unloads the mesh data (vertex buffers and cache). The opposite to Load.
|
||||
/// </summary>
|
||||
void Unload();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer.
|
||||
/// </summary>
|
||||
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
||||
/// <param name="ib">The index buffer.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint32* ib)
|
||||
{
|
||||
return UpdateTriangles(triangleCount, ib, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer.
|
||||
/// </summary>
|
||||
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
||||
/// <param name="ib">The index buffer.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint16* ib)
|
||||
{
|
||||
return UpdateTriangles(triangleCount, ib, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the model mesh index buffer.
|
||||
/// </summary>
|
||||
/// <param name="triangleCount">The amount of triangles in the index buffer.</param>
|
||||
/// <param name="ib">The index buffer.</param>
|
||||
/// <param name="use16BitIndices">True if index buffer uses 16-bit index buffer, otherwise 32-bit.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
bool UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Determines if there is an intersection between the mesh and a ray in given world.
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test.</param>
|
||||
/// <param name="world">The mesh instance transformation.</param>
|
||||
/// <param name="distance">When the method completes and returns true, contains the distance of the intersection (if any valid).</param>
|
||||
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
|
||||
/// <returns>True whether the two objects intersected, otherwise false.</returns>
|
||||
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if there is an intersection between the mesh and a ray in given world
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test</param>
|
||||
/// <param name="transform">The mesh instance transformation.</param>
|
||||
/// <param name="distance">When the method completes and returns true, contains the distance of the intersection (if any valid).</param>
|
||||
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
|
||||
/// <returns>True whether the two objects intersected, otherwise false.</returns>
|
||||
bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Extracts mesh buffer data from a GPU. Cannot be called from the main thread.
|
||||
/// </summary>
|
||||
/// <param name="type">Buffer type</param>
|
||||
/// <param name="result">The result data</param>
|
||||
/// <returns>True if failed, otherwise false</returns>
|
||||
virtual bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const = 0;
|
||||
bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const;
|
||||
|
||||
/// <summary>
|
||||
/// Extracts mesh buffer data from GPU in the async task.
|
||||
@@ -141,7 +261,7 @@ public:
|
||||
/// <param name="type">Buffer type</param>
|
||||
/// <param name="result">The result data</param>
|
||||
/// <returns>Created async task used to gather the buffer data.</returns>
|
||||
virtual Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const = 0;
|
||||
Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const;
|
||||
|
||||
/// <summary>
|
||||
/// Extract mesh buffer data from CPU. Cached internally.
|
||||
@@ -246,4 +366,24 @@ public:
|
||||
float LightmapScale = -1.0f;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the draw call geometry for this mesh. Sets the index and vertex buffers.
|
||||
/// </summary>
|
||||
/// <param name="drawCall">The draw call.</param>
|
||||
void GetDrawCallGeometry(struct DrawCall& drawCall) const;
|
||||
|
||||
/// <summary>
|
||||
/// Draws the mesh. Binds vertex and index buffers and invokes the draw call.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context.</param>
|
||||
void Render(GPUContext* context) const;
|
||||
|
||||
private:
|
||||
// Internal bindings
|
||||
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel() const;
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
API_FUNCTION(NoProxy) bool UpdateTrianglesUInt(int32 triangleCount, const MArray* trianglesObj);
|
||||
API_FUNCTION(NoProxy) bool UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj);
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -105,19 +105,13 @@ void SkinnedMesh::Init(SkinnedModel* model, int32 lodIndex, int32 index, int32 m
|
||||
_sphere = sphere;
|
||||
_vertices = 0;
|
||||
_triangles = 0;
|
||||
_vertexBuffer = nullptr;
|
||||
_vertexBuffers[0] = nullptr;
|
||||
_indexBuffer = nullptr;
|
||||
_cachedIndexBuffer.Clear();
|
||||
_cachedVertexBuffer.Clear();
|
||||
_cachedVertexBuffers[0].Clear();
|
||||
BlendShapes.Clear();
|
||||
}
|
||||
|
||||
SkinnedMesh::~SkinnedMesh()
|
||||
{
|
||||
SAFE_DELETE_GPU_RESOURCE(_vertexBuffer);
|
||||
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
||||
}
|
||||
|
||||
bool SkinnedMesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void* ib, bool use16BitIndexBuffer)
|
||||
{
|
||||
// Cache data
|
||||
@@ -146,7 +140,7 @@ bool SkinnedMesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const
|
||||
goto ERROR_LOAD_END;
|
||||
|
||||
// Initialize
|
||||
_vertexBuffer = vertexBuffer;
|
||||
_vertexBuffers[0] = vertexBuffer;
|
||||
_indexBuffer = indexBuffer;
|
||||
_triangles = triangles;
|
||||
_vertices = vertices;
|
||||
@@ -161,17 +155,6 @@ ERROR_LOAD_END:
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkinnedMesh::Unload()
|
||||
{
|
||||
SAFE_DELETE_GPU_RESOURCE(_vertexBuffer);
|
||||
SAFE_DELETE_GPU_RESOURCE(_indexBuffer);
|
||||
_cachedIndexBuffer.Clear();
|
||||
_cachedVertexBuffer.Clear();
|
||||
_triangles = 0;
|
||||
_vertices = 0;
|
||||
_use16BitIndexBuffer = false;
|
||||
}
|
||||
|
||||
bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const void* ib, bool use16BitIndices)
|
||||
{
|
||||
auto model = (SkinnedModel*)_model;
|
||||
@@ -192,37 +175,6 @@ bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0
|
||||
return failed;
|
||||
}
|
||||
|
||||
bool SkinnedMesh::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const
|
||||
{
|
||||
// Transform points
|
||||
BoundingBox transformedBox;
|
||||
Vector3::Transform(_box.Minimum, world, transformedBox.Minimum);
|
||||
Vector3::Transform(_box.Maximum, world, transformedBox.Maximum);
|
||||
|
||||
// Test ray on a transformed box
|
||||
return transformedBox.Intersects(ray, distance, normal);
|
||||
}
|
||||
|
||||
bool SkinnedMesh::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const
|
||||
{
|
||||
// Transform points
|
||||
BoundingBox transformedBox;
|
||||
transform.LocalToWorld(_box.Minimum, transformedBox.Minimum);
|
||||
transform.LocalToWorld(_box.Maximum, transformedBox.Maximum);
|
||||
|
||||
// Test ray on a transformed box
|
||||
return transformedBox.Intersects(ray, distance, normal);
|
||||
}
|
||||
|
||||
void SkinnedMesh::Render(GPUContext* context) const
|
||||
{
|
||||
ASSERT(IsInitialized());
|
||||
|
||||
context->BindVB(ToSpan(&_vertexBuffer, 1));
|
||||
context->BindIB(_indexBuffer);
|
||||
context->DrawIndexed(_triangles * 3);
|
||||
}
|
||||
|
||||
void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const
|
||||
{
|
||||
const auto& entry = info.Buffer->At(_materialSlotIndex);
|
||||
@@ -250,7 +202,7 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
|
||||
// Setup draw call
|
||||
DrawCall drawCall;
|
||||
drawCall.Geometry.IndexBuffer = _indexBuffer;
|
||||
drawCall.Geometry.VertexBuffers[0] = _vertexBuffer;
|
||||
drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0];
|
||||
if (info.Deformation)
|
||||
info.Deformation->RunDeformers(this, MeshBufferType::Vertex0, drawCall.Geometry.VertexBuffers[0]);
|
||||
drawCall.Draw.StartIndex = 0;
|
||||
@@ -292,7 +244,7 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI
|
||||
// Setup draw call
|
||||
DrawCall drawCall;
|
||||
drawCall.Geometry.IndexBuffer = _indexBuffer;
|
||||
drawCall.Geometry.VertexBuffers[0] = _vertexBuffer;
|
||||
drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0];
|
||||
if (info.Deformation)
|
||||
info.Deformation->RunDeformers(this, MeshBufferType::Vertex0, drawCall.Geometry.VertexBuffers[0]);
|
||||
drawCall.Draw.IndicesCount = _triangles * 3;
|
||||
@@ -315,39 +267,9 @@ void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawI
|
||||
renderContextBatch.GetMainContext().List->AddDrawCall(renderContextBatch, drawModes, StaticFlags::None, shadowsMode, info.Bounds, drawCall, entry.ReceiveDecals, info.SortOrder);
|
||||
}
|
||||
|
||||
bool SkinnedMesh::DownloadDataGPU(MeshBufferType type, BytesContainer& result) const
|
||||
{
|
||||
GPUBuffer* buffer = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case MeshBufferType::Index:
|
||||
buffer = _indexBuffer;
|
||||
break;
|
||||
case MeshBufferType::Vertex0:
|
||||
buffer = _vertexBuffer;
|
||||
break;
|
||||
}
|
||||
return buffer && buffer->DownloadData(result);
|
||||
}
|
||||
|
||||
Task* SkinnedMesh::DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const
|
||||
{
|
||||
GPUBuffer* buffer = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case MeshBufferType::Index:
|
||||
buffer = _indexBuffer;
|
||||
break;
|
||||
case MeshBufferType::Vertex0:
|
||||
buffer = _vertexBuffer;
|
||||
break;
|
||||
}
|
||||
return buffer ? buffer->DownloadDataAsync(result) : nullptr;
|
||||
}
|
||||
|
||||
bool SkinnedMesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& count) const
|
||||
{
|
||||
if (_cachedVertexBuffer.IsEmpty())
|
||||
if (_cachedVertexBuffers[0].IsEmpty())
|
||||
{
|
||||
PROFILE_CPU();
|
||||
auto model = GetSkinnedModel();
|
||||
@@ -409,7 +331,7 @@ bool SkinnedMesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, i
|
||||
// Cache mesh data
|
||||
_cachedIndexBufferCount = indicesCount;
|
||||
_cachedIndexBuffer.Set(ib, indicesCount * ibStride);
|
||||
_cachedVertexBuffer.Set((const byte*)vb0, vertices * sizeof(VB0SkinnedElementType));
|
||||
_cachedVertexBuffers[0].Set((const byte*)vb0, vertices * sizeof(VB0SkinnedElementType));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -421,8 +343,8 @@ bool SkinnedMesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, i
|
||||
count = _cachedIndexBufferCount;
|
||||
break;
|
||||
case MeshBufferType::Vertex0:
|
||||
result.Link(_cachedVertexBuffer);
|
||||
count = _cachedVertexBuffer.Count() / sizeof(VB0SkinnedElementType);
|
||||
result.Link(_cachedVertexBuffers[0]);
|
||||
count = _cachedVertexBuffers[0].Count() / sizeof(VB0SkinnedElementType);
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
@@ -430,11 +352,6 @@ bool SkinnedMesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, i
|
||||
return false;
|
||||
}
|
||||
|
||||
ScriptingObject* SkinnedMesh::GetParentModel()
|
||||
{
|
||||
return _model;
|
||||
}
|
||||
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
|
||||
template<typename IndexType>
|
||||
|
||||
@@ -88,16 +88,6 @@ namespace FlaxEngine
|
||||
/// </summary>
|
||||
public SkinnedModel ParentSkinnedModel => (SkinnedModel)Internal_GetParentModel(__unmanagedPtr);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the material slot used by this mesh during rendering.
|
||||
/// </summary>
|
||||
public MaterialSlot MaterialSlot => ParentSkinnedModel.MaterialSlots[MaterialSlotIndex];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a format of the mesh index buffer.
|
||||
/// </summary>
|
||||
public PixelFormat IndexBufferFormat => Use16BitIndexBuffer ? PixelFormat.R16_UInt : PixelFormat.R32_UInt;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the skinned model mesh vertex and index buffer data.
|
||||
/// Can be used only for virtual assets (see <see cref="Asset.IsVirtual"/> and <see cref="Content.CreateVirtualAsset{T}"/>).
|
||||
@@ -313,7 +303,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Downloads the index buffer that contains mesh triangles data. To download data from GPU set <paramref name="forceGpu"/> to true and call this method from the thread other than main thread (see <see cref="Platform.IsInMainThread"/>).
|
||||
/// </summary>
|
||||
/// <remarks>If mesh index buffer format (see <see cref="IndexBufferFormat"/>) is <see cref="PixelFormat.R16_UInt"/> then it's faster to call .</remarks>
|
||||
/// <remarks>If mesh index buffer format (see <see cref="MeshBase.IndexBufferFormat"/>) is <see cref="PixelFormat.R16_UInt"/> then it's faster to call .</remarks>
|
||||
/// <param name="forceGpu">If set to <c>true</c> the data will be downloaded from the GPU, otherwise it can be loaded from the drive (source asset file) or from memory (if cached). Downloading mesh from GPU requires this call to be made from the other thread than main thread. Virtual assets are always downloaded from GPU memory due to lack of dedicated storage container for the asset data.</param>
|
||||
/// <returns>The gathered data.</returns>
|
||||
public uint[] DownloadIndexBuffer(bool forceGpu = false)
|
||||
@@ -327,7 +317,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Downloads the index buffer that contains mesh triangles data. To download data from GPU set <paramref name="forceGpu"/> to true and call this method from the thread other than main thread (see <see cref="Platform.IsInMainThread"/>).
|
||||
/// </summary>
|
||||
/// <remarks>If mesh index buffer format (see <see cref="IndexBufferFormat"/>) is <see cref="PixelFormat.R32_UInt"/> then data won't be downloaded.</remarks>
|
||||
/// <remarks>If mesh index buffer format (see <see cref="MeshBase.IndexBufferFormat"/>) is <see cref="PixelFormat.R32_UInt"/> then data won't be downloaded.</remarks>
|
||||
/// <param name="forceGpu">If set to <c>true</c> the data will be downloaded from the GPU, otherwise it can be loaded from the drive (source asset file) or from memory (if cached). Downloading mesh from GPU requires this call to be made from the other thread than main thread. Virtual assets are always downloaded from GPU memory due to lack of dedicated storage container for the asset data.</param>
|
||||
/// <returns>The gathered data.</returns>
|
||||
public ushort[] DownloadIndexBufferUShort(bool forceGpu = false)
|
||||
@@ -3,7 +3,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "MeshBase.h"
|
||||
#include "Types.h"
|
||||
#include "BlendShape.h"
|
||||
|
||||
/// <summary>
|
||||
@@ -12,12 +11,6 @@
|
||||
API_CLASS(NoSpawn) class FLAXENGINE_API SkinnedMesh : public MeshBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(SkinnedMesh, MeshBase);
|
||||
protected:
|
||||
GPUBuffer* _vertexBuffer = nullptr;
|
||||
GPUBuffer* _indexBuffer = nullptr;
|
||||
mutable Array<byte> _cachedIndexBuffer;
|
||||
mutable Array<byte> _cachedVertexBuffer;
|
||||
mutable int32 _cachedIndexBufferCount;
|
||||
|
||||
public:
|
||||
SkinnedMesh(const SkinnedMesh& other)
|
||||
@@ -28,11 +21,6 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="SkinnedMesh"/> class.
|
||||
/// </summary>
|
||||
~SkinnedMesh();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the skinned model owning this mesh.
|
||||
@@ -42,14 +30,6 @@ public:
|
||||
return (SkinnedModel*)_model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this mesh is initialized (has vertex and index buffers initialized).
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsInitialized() const
|
||||
{
|
||||
return _vertexBuffer != nullptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blend shapes used by this mesh.
|
||||
/// </summary>
|
||||
@@ -78,11 +58,6 @@ public:
|
||||
/// <returns>True if cannot load data, otherwise false.</returns>
|
||||
bool Load(uint32 vertices, uint32 triangles, const void* vb0, const void* ib, bool use16BitIndexBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Unloads the mesh data (vertex buffers and cache). The opposite to Load.
|
||||
/// </summary>
|
||||
void Unload();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
|
||||
@@ -135,33 +110,6 @@ public:
|
||||
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const void* ib, bool use16BitIndices);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Determines if there is an intersection between the mesh and a ray in given world
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test</param>
|
||||
/// <param name="world">World to transform box</param>
|
||||
/// <param name="distance">When the method completes and returns true, contains the distance of the intersection (if any valid).</param>
|
||||
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
|
||||
/// <returns>True whether the two objects intersected</returns>
|
||||
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if there is an intersection between the mesh and a ray in given world
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test</param>
|
||||
/// <param name="transform">Instance transformation</param>
|
||||
/// <param name="distance">When the method completes and returns true, contains the distance of the intersection (if any valid).</param>
|
||||
/// <param name="normal">When the method completes, contains the intersection surface normal vector (if any valid).</param>
|
||||
/// <returns>True whether the two objects intersected</returns>
|
||||
bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Draws the mesh. Binds vertex and index buffers and invokes the draw call.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context.</param>
|
||||
void Render(GPUContext* context) const;
|
||||
|
||||
/// <summary>
|
||||
/// Draws the mesh.
|
||||
/// </summary>
|
||||
@@ -180,13 +128,10 @@ public:
|
||||
|
||||
public:
|
||||
// [MeshBase]
|
||||
bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override;
|
||||
Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const override;
|
||||
bool DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& count) const override;
|
||||
|
||||
private:
|
||||
// Internal bindings
|
||||
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
|
||||
#if !COMPILE_WITHOUT_CSHARP
|
||||
API_FUNCTION(NoProxy) bool UpdateMeshUInt(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj);
|
||||
API_FUNCTION(NoProxy) bool UpdateMeshUShort(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj);
|
||||
|
||||
@@ -640,20 +640,3 @@ int32 MipLevelsCount(int32 width, int32 height, int32 depth)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user