From fae682e4063061f1f240d7151f647fed9296f2d7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Aug 2021 16:40:28 +0200 Subject: [PATCH] Implement DownloadDataCPU for Mesh and add result entries count --- Source/Engine/Content/Assets/Model.h | 10 - Source/Engine/Content/Assets/ModelBase.h | 11 +- Source/Engine/Content/Assets/SkinnedModel.cpp | 8 +- Source/Engine/Content/Assets/SkinnedModel.h | 10 - Source/Engine/Graphics/Models/BlendShape.cpp | 4 +- Source/Engine/Graphics/Models/Mesh.cpp | 251 +++++++++--------- Source/Engine/Graphics/Models/Mesh.h | 19 +- Source/Engine/Graphics/Models/MeshBase.h | 7 +- Source/Engine/Graphics/Models/SkinnedMesh.cpp | 17 +- Source/Engine/Graphics/Models/SkinnedMesh.h | 6 +- 10 files changed, 170 insertions(+), 173 deletions(-) diff --git a/Source/Engine/Content/Assets/Model.h b/Source/Engine/Content/Assets/Model.h index e51ae5ff8..2134fb936 100644 --- a/Source/Engine/Content/Assets/Model.h +++ b/Source/Engine/Content/Assets/Model.h @@ -5,16 +5,6 @@ #include "ModelBase.h" #include "Engine/Graphics/Models/ModelLOD.h" -// Note: we use the first chunk as a header, next is the highest quality lod and then lower ones -// -// Example: -// Chunk 0: Header -// Chunk 1: LOD0 -// Chunk 2: LOD1 -// .. -// -#define MODEL_LOD_TO_CHUNK_INDEX(lod) (lod + 1) - class Mesh; class StreamModelLODTask; diff --git a/Source/Engine/Content/Assets/ModelBase.h b/Source/Engine/Content/Assets/ModelBase.h index 527a7ac61..4833c3244 100644 --- a/Source/Engine/Content/Assets/ModelBase.h +++ b/Source/Engine/Content/Assets/ModelBase.h @@ -7,6 +7,16 @@ #include "Engine/Graphics/Models/MaterialSlot.h" #include "Engine/Streaming/StreamableResource.h" +// Note: we use the first chunk as a header, next is the highest quality lod and then lower ones +// +// Example: +// Chunk 0: Header +// Chunk 1: LOD0 +// Chunk 2: LOD1 +// .. +// +#define MODEL_LOD_TO_CHUNK_INDEX(lod) (lod + 1) + class MeshBase; /// @@ -58,7 +68,6 @@ public: /// /// Gets amount of the level of details in the model. /// - /// Amount of the level of details in the model. virtual int32 GetLODsCount() const = 0; /// diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index 1ce72f4b4..abd94b511 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -147,13 +147,13 @@ Array SkinnedModel::GetBlendShapes() ContentLoadTask* SkinnedModel::RequestLODDataAsync(int32 lodIndex) { - const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex); + const int32 chunkIndex = MODEL_LOD_TO_CHUNK_INDEX(lodIndex); return RequestChunkDataAsync(chunkIndex); } void SkinnedModel::GetLODData(int32 lodIndex, BytesContainer& data) const { - const int32 chunkIndex = SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex); + const int32 chunkIndex = MODEL_LOD_TO_CHUNK_INDEX(lodIndex); GetChunkData(chunkIndex, data); } @@ -617,7 +617,7 @@ bool SkinnedModel::Save(bool withMeshDataFromGpu, const StringView& path) } // Override meshes data chunk with the fetched GPU meshes memory - auto lodChunk = GET_CHUNK(SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex)); + auto lodChunk = GET_CHUNK(MODEL_LOD_TO_CHUNK_INDEX(lodIndex)); if (lodChunk == nullptr) return true; lodChunk->Data.Copy(meshesStream.GetHandle(), meshesStream.GetPosition()); @@ -631,7 +631,7 @@ bool SkinnedModel::Save(bool withMeshDataFromGpu, const StringView& path) // Load all chunks with a mesh data for (int32 lodIndex = 0; lodIndex < LODs.Count(); lodIndex++) { - if (LoadChunk(SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lodIndex))) + if (LoadChunk(MODEL_LOD_TO_CHUNK_INDEX(lodIndex))) return true; } } diff --git a/Source/Engine/Content/Assets/SkinnedModel.h b/Source/Engine/Content/Assets/SkinnedModel.h index 08cbf23b0..32be6df3d 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.h +++ b/Source/Engine/Content/Assets/SkinnedModel.h @@ -9,16 +9,6 @@ class StreamSkinnedModelLODTask; -// Note: we use the first chunk as a header, next is the highest quality lod and then lower ones -// -// Example: -// Chunk 0: Header -// Chunk 1: LOD0 -// Chunk 2: LOD1 -// .. -// -#define SKINNED_MODEL_LOD_TO_CHUNK_INDEX(lod) (lod + 1) - /// /// Skinned model asset that contains model object made of meshes that can be rendered on the GPU using skeleton bones skinning. /// diff --git a/Source/Engine/Graphics/Models/BlendShape.cpp b/Source/Engine/Graphics/Models/BlendShape.cpp index 0b21a57cd..1a0a4e8aa 100644 --- a/Source/Engine/Graphics/Models/BlendShape.cpp +++ b/Source/Engine/Graphics/Models/BlendShape.cpp @@ -78,11 +78,11 @@ void BlendShapesInstance::Update(SkinnedModel* skinnedModel) if (!instance.IsUsed) continue; const SkinnedMesh* mesh = e.Key; - const int32 vertexCount = mesh->GetVertexCount(); // Get skinned mesh vertex buffer data (original, cached on CPU) BytesContainer vertexBuffer; - if (mesh->DownloadDataCPU(MeshBufferType::Vertex0, vertexBuffer)) + int32 vertexCount; + if (mesh->DownloadDataCPU(MeshBufferType::Vertex0, vertexBuffer, vertexCount)) { // Don't use this mesh if failed to get it's vertices data instance.IsUsed = false; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 3e0a47f4e..ad64a3f61 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -7,7 +7,7 @@ #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/RenderTask.h" -#include "Engine/Level/Scene/Scene.h" +#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Serialization/MemoryReadStream.h" #include "Engine/Threading/Threading.h" @@ -293,6 +293,9 @@ bool Mesh::Load(uint32 vertices, uint32 triangles, void* vb0, void* vb1, void* v _triangles = triangles; _vertices = vertices; _use16BitIndexBuffer = use16BitIndexBuffer; + _cachedVertexBuffer[0].Clear(); + _cachedVertexBuffer[1].Clear(); + _cachedVertexBuffer[2].Clear(); return false; @@ -314,6 +317,10 @@ void Mesh::Unload() _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, float& distance, Vector3& normal) const @@ -506,13 +513,94 @@ Task* Mesh::DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) co return buffer ? buffer->DownloadDataAsync(result) : nullptr; } -bool Mesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result) const +bool Mesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& count) const { -#if !BUILD_RELEASE - // TODO: implement this - LOG(Error, "Mesh::DownloadDataCPU not implemented."); -#endif - return true; + if (_cachedVertexBuffer[0].IsEmpty()) + { + PROFILE_CPU(); + auto model = GetModel(); + ScopeLock lock(model->Locker); + if (model->IsVirtual()) + { + LOG(Error, "Cannot access CPU data of virtual models. Use GPU data download"); + return true; + } + + // Fetch chunk with data from drive/memory + const auto chunkIndex = MODEL_LOD_TO_CHUNK_INDEX(GetLODIndex()); + if (model->LoadChunk(chunkIndex)) + return true; + const auto chunk = model->GetChunk(chunkIndex); + if (!chunk) + { + LOG(Error, "Missing chunk."); + return true; + } + + MemoryReadStream stream(chunk->Get(), chunk->Size()); + + // Seek to find mesh location + for (int32 i = 0; i <= _index; i++) + { + // #MODEL_DATA_FORMAT_USAGE + uint32 vertices; + stream.ReadUint32(&vertices); + uint32 triangles; + stream.ReadUint32(&triangles); + uint32 indicesCount = triangles * 3; + bool use16BitIndexBuffer = indicesCount <= MAX_uint16; + uint32 ibStride = use16BitIndexBuffer ? sizeof(uint16) : sizeof(uint32); + if (vertices == 0 || triangles == 0) + { + LOG(Error, "Invalid mesh data."); + return true; + } + auto vb0 = stream.Read(vertices); + auto vb1 = stream.Read(vertices); + bool hasColors = stream.ReadBool(); + VB2ElementType18* vb2 = nullptr; + if (hasColors) + { + vb2 = stream.Read(vertices); + } + auto ib = stream.Read(indicesCount * ibStride); + + if (i != _index) + continue; + + // 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)); + if (hasColors) + _cachedVertexBuffer[2].Set((const byte*)vb2, vertices * sizeof(VB2ElementType)); + break; + } + } + + switch (type) + { + case MeshBufferType::Index: + result.Link(_cachedIndexBuffer); + count = _cachedIndexBufferCount; + break; + case MeshBufferType::Vertex0: + result.Link(_cachedVertexBuffer[0]); + count = _cachedVertexBuffer[0].Count() / sizeof(VB0ElementType); + break; + case MeshBufferType::Vertex1: + result.Link(_cachedVertexBuffer[1]); + count = _cachedVertexBuffer[1].Count() / sizeof(VB1ElementType); + break; + case MeshBufferType::Vertex2: + result.Link(_cachedVertexBuffer[2]); + count = _cachedVertexBuffer[2].Count() / sizeof(VB2ElementType); + break; + default: + return true; + } + return false; } ScriptingObject* Mesh::GetParentModel() @@ -645,145 +733,50 @@ bool Mesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI) } } - // Check if load data from GPU + MeshBufferType bufferType; + switch (type) + { + case InternalBufferType::VB0: + bufferType = MeshBufferType::Vertex0; + break; + case InternalBufferType::VB1: + bufferType = MeshBufferType::Vertex1; + break; + case InternalBufferType::VB2: + bufferType = MeshBufferType::Vertex2; + break; + case InternalBufferType::IB16: + case InternalBufferType::IB32: + bufferType = MeshBufferType::Index; + break; + default: + return true; + } + BytesContainer data; if (forceGpu) { - MeshBufferType bufferType; - switch (type) - { - case InternalBufferType::VB0: - bufferType = MeshBufferType::Vertex0; - break; - case InternalBufferType::VB1: - bufferType = MeshBufferType::Vertex1; - break; - case InternalBufferType::VB2: - bufferType = MeshBufferType::Vertex2; - break; - case InternalBufferType::IB16: - case InternalBufferType::IB32: - bufferType = MeshBufferType::Index; - break; - default: CRASH; - return true; - } - + // Get data from GPU // TODO: support reusing the input memory buffer to perform a single copy from staging buffer to the input CPU buffer - BytesContainer data; auto task = mesh->DownloadDataGPUAsync(bufferType, data); if (task == nullptr) return true; - - model->Locker.Unlock(); - task->Start(); + model->Locker.Unlock(); if (task->Wait()) { LOG(Error, "Task failed."); return true; } - - ConvertMeshData(mesh, type, resultObj, data.Get()); - model->Locker.Lock(); - - return false; } - - // Get data from drive/memory + else { - // Fetch chunk with data - const auto chunkIndex = MODEL_LOD_TO_CHUNK_INDEX(mesh->GetLODIndex()); - if (model->LoadChunk(chunkIndex)) + // Get data from CPU + int32 count; + if (DownloadDataCPU(bufferType, data, count)) return true; - const auto chunk = model->GetChunk(chunkIndex); - if (!chunk) - { - LOG(Error, "Missing chunk."); - return true; - } - - MemoryReadStream stream(chunk->Get(), chunk->Size()); - - // Seek to find mesh location - for (int32 i = 0; i < mesh->GetIndex(); i++) - { - // #MODEL_DATA_FORMAT_USAGE - uint32 vertices; - stream.ReadUint32(&vertices); - uint32 triangles; - stream.ReadUint32(&triangles); - uint32 indicesCount = triangles * 3; - bool use16BitIndexBuffer = indicesCount <= MAX_uint16; - uint32 ibStride = use16BitIndexBuffer ? sizeof(uint16) : sizeof(uint32); - if (vertices == 0 || triangles == 0) - { - LOG(Error, "Invalid mesh data."); - return true; - } - auto vb0 = stream.Read(vertices); - auto vb1 = stream.Read(vertices); - bool hasColors = stream.ReadBool(); - VB2ElementType18* vb2 = nullptr; - if (hasColors) - { - vb2 = stream.Read(vertices); - } - auto ib = stream.Read(indicesCount * ibStride); - } - - // Get mesh data - void* data = nullptr; - { - // #MODEL_DATA_FORMAT_USAGE - uint32 vertices; - stream.ReadUint32(&vertices); - uint32 triangles; - stream.ReadUint32(&triangles); - uint32 indicesCount = triangles * 3; - bool use16BitIndexBuffer = indicesCount <= MAX_uint16; - uint32 ibStride = use16BitIndexBuffer ? sizeof(uint16) : sizeof(uint32); - if (vertices == 0 || triangles == 0) - { - LOG(Error, "Invalid mesh data."); - return true; - } - auto vb0 = stream.Read(vertices); - auto vb1 = stream.Read(vertices); - bool hasColors = stream.ReadBool(); - VB2ElementType18* vb2 = nullptr; - if (hasColors) - { - vb2 = stream.Read(vertices); - } - auto ib = stream.Read(indicesCount * ibStride); - - if (mesh->HasVertexColors() != hasColors || mesh->Use16BitIndexBuffer() != use16BitIndexBuffer) - { - LOG(Error, "Invalid mesh data loaded from chunk."); - return true; - } - - switch (type) - { - case InternalBufferType::VB0: - data = vb0; - break; - case InternalBufferType::VB1: - data = vb1; - break; - case InternalBufferType::VB2: - data = vb2; - break; - case InternalBufferType::IB16: - case InternalBufferType::IB32: - data = ib; - break; - } - } - - ConvertMeshData(mesh, type, resultObj, data); } + ConvertMeshData(mesh, type, resultObj, data.Get()); return false; } diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index ca5174f7b..4af23d3fe 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -22,6 +22,7 @@ API_CLASS(NoSpawn) class FLAXENGINE_API Mesh : public MeshBase { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(Mesh, MeshBase); protected: + int32 _index; int32 _lodIndex; bool _hasLightmapUVs; @@ -30,8 +31,12 @@ protected: #if USE_PRECISE_MESH_INTERSECTS CollisionProxy _collisionProxy; #endif + mutable Array _cachedVertexBuffer[3]; + mutable Array _cachedIndexBuffer; + mutable int32 _cachedIndexBufferCount; public: + Mesh(const Mesh& other) : Mesh() { @@ -46,6 +51,7 @@ public: ~Mesh(); public: + /// /// Gets the model owning this mesh. /// @@ -92,7 +98,6 @@ public: /// /// Determines whether this mesh is initialized (has vertex and index buffers initialized). /// - /// True if this instance is initialized, otherwise false. FORCE_INLINE bool IsInitialized() const { return _vertexBuffers[0] != nullptr; @@ -101,13 +106,11 @@ public: /// /// Determines whether this mesh has a vertex colors buffer. /// - /// True if this mesh has a vertex colors buffers. API_PROPERTY() bool HasVertexColors() const; /// /// Determines whether this mesh contains valid lightmap texture coordinates data. /// - /// True if this mesh has a vertex colors buffers. API_PROPERTY() FORCE_INLINE bool HasLightmapUVs() const { return _hasLightmapUVs; @@ -118,7 +121,6 @@ public: /// /// Gets the collision proxy used by the mesh. /// - /// The collisions proxy container object reference. FORCE_INLINE const CollisionProxy& GetCollisionProxy() const { return _collisionProxy; @@ -127,6 +129,7 @@ public: #endif public: + /// /// Updates the model mesh (used by the virtual models created with Init rather than Load). /// @@ -205,6 +208,7 @@ public: bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr); public: + /// /// Updates the model mesh index buffer (used by the virtual models created with Init rather than Load). /// @@ -237,6 +241,7 @@ public: bool UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices); public: + /// /// Initializes instance of the class. /// @@ -268,6 +273,7 @@ public: void Unload(); public: + /// /// Determines if there is an intersection between the mesh and a ray in given world /// @@ -288,6 +294,7 @@ public: } public: + /// /// Gets the draw call geometry for this mesh. Sets the index and vertex buffers. /// @@ -387,12 +394,14 @@ public: void Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const; public: + // [MeshBase] bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override; Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const override; - bool DownloadDataCPU(MeshBufferType type, BytesContainer& result) const override; + bool DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& count) const override; private: + // Internal bindings API_FUNCTION(NoProxy) ScriptingObject* GetParentModel(); API_FUNCTION(NoProxy) bool UpdateMeshInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj); diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h index bedb57c90..7a17630c7 100644 --- a/Source/Engine/Graphics/Models/MeshBase.h +++ b/Source/Engine/Graphics/Models/MeshBase.h @@ -45,7 +45,6 @@ public: /// /// Gets the triangle count. /// - /// The triangles API_PROPERTY() FORCE_INLINE int32 GetTriangleCount() const { return _triangles; @@ -54,7 +53,6 @@ public: /// /// Gets the vertex count. /// - /// The vertices API_PROPERTY() FORCE_INLINE int32 GetVertexCount() const { return _vertices; @@ -63,7 +61,6 @@ public: /// /// Gets the box. /// - /// The bounding box. API_PROPERTY() FORCE_INLINE const BoundingBox& GetBox() const { return _box; @@ -72,7 +69,6 @@ public: /// /// Gets the sphere. /// - /// The bounding sphere. API_PROPERTY() FORCE_INLINE const BoundingSphere& GetSphere() const { return _sphere; @@ -129,6 +125,7 @@ public: /// /// Buffer type /// The result data + /// The amount of items inside the result buffer. /// True if failed, otherwise false - virtual bool DownloadDataCPU(MeshBufferType type, BytesContainer& result) const = 0; + virtual bool DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& count) const = 0; }; diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index b11ceb544..b5c67bff9 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -227,16 +227,21 @@ Task* SkinnedMesh::DownloadDataGPUAsync(MeshBufferType type, BytesContainer& res return buffer ? buffer->DownloadDataAsync(result) : nullptr; } -bool SkinnedMesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result) const +bool SkinnedMesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& count) const { if (_cachedVertexBuffer.IsEmpty()) { PROFILE_CPU(); auto model = GetSkinnedModel(); ScopeLock lock(model->Locker); + if (model->IsVirtual()) + { + LOG(Error, "Cannot access CPU data of virtual models. Use GPU data download"); + return true; + } // Fetch chunk with data from drive/memory - const auto chunkIndex = _lodIndex + 1; + const auto chunkIndex = MODEL_LOD_TO_CHUNK_INDEX(_lodIndex); if (model->LoadChunk(chunkIndex)) return true; const auto chunk = model->GetChunk(chunkIndex); @@ -283,6 +288,7 @@ bool SkinnedMesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result) c continue; // Cache mesh data + _cachedIndexBufferCount = indicesCount; _cachedIndexBuffer.Set(ib, indicesCount * ibStride); _cachedVertexBuffer.Set((const byte*)vb0, vertices * sizeof(VB0SkinnedElementType)); break; @@ -293,9 +299,11 @@ bool SkinnedMesh::DownloadDataCPU(MeshBufferType type, BytesContainer& result) c { case MeshBufferType::Index: result.Link(_cachedIndexBuffer); + count = _cachedIndexBufferCount; break; case MeshBufferType::Vertex0: result.Link(_cachedVertexBuffer); + count = _cachedVertexBuffer.Count() / sizeof(VB0SkinnedElementType); break; default: return true; @@ -533,16 +541,19 @@ bool SkinnedMesh::DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 type if (task == nullptr) return true; task->Start(); + model->Locker.Unlock(); if (task->Wait()) { LOG(Error, "Task failed."); return true; } + model->Locker.Lock(); } else { // Get data from CPU - if (DownloadDataCPU(bufferType, data)) + int32 count; + if (DownloadDataCPU(bufferType, data, count)) return true; } diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h index eb854189e..108f36b33 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.h +++ b/Source/Engine/Graphics/Models/SkinnedMesh.h @@ -25,6 +25,7 @@ protected: GPUBuffer* _indexBuffer; mutable Array _cachedIndexBuffer; mutable Array _cachedVertexBuffer; + mutable int32 _cachedIndexBufferCount; public: @@ -46,7 +47,6 @@ public: /// /// Gets the skinned model owning this mesh. /// - /// The skinned model FORCE_INLINE SkinnedModel* GetSkinnedModel() const { return (SkinnedModel*)_model; @@ -55,7 +55,6 @@ public: /// /// Gets the mesh index. /// - /// The index FORCE_INLINE int32 GetIndex() const { return _index; @@ -64,7 +63,6 @@ public: /// /// Determines whether this mesh is initialized (has vertex and index buffers initialized). /// - /// True if this instance is initialized, otherwise false. FORCE_INLINE bool IsInitialized() const { return _vertexBuffer != nullptr; @@ -241,7 +239,7 @@ public: // [MeshBase] bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override; Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const override; - bool DownloadDataCPU(MeshBufferType type, BytesContainer& result) const override; + bool DownloadDataCPU(MeshBufferType type, BytesContainer& result, int32& count) const override; private: