diff --git a/Source/Engine/Graphics/MaterialInfo.cs b/Source/Engine/Graphics/Materials/MaterialInfo.cs similarity index 100% rename from Source/Engine/Graphics/MaterialInfo.cs rename to Source/Engine/Graphics/Materials/MaterialInfo.cs diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index b0ab1356b..f948ab51d 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -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(colorsObj) : nullptr; return UpdateMesh(mesh, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors); } - - template - 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(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(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(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); } -bool Mesh::UpdateTrianglesUInt(int32 triangleCount, const MArray* trianglesObj) -{ - return ::UpdateTriangles(this, triangleCount, trianglesObj); -} - -bool Mesh::UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj) -{ - return ::UpdateTriangles(this, triangleCount, trianglesObj); -} - enum class InternalBufferType { VB0 = 0, diff --git a/Source/Engine/Graphics/Mesh.cs b/Source/Engine/Graphics/Models/Mesh.cs similarity index 87% rename from Source/Engine/Graphics/Mesh.cs rename to Source/Engine/Graphics/Models/Mesh.cs index 42604c872..8870c14f6 100644 --- a/Source/Engine/Graphics/Mesh.cs +++ b/Source/Engine/Graphics/Models/Mesh.cs @@ -101,16 +101,6 @@ namespace FlaxEngine /// public Model ParentModel => (Model)Internal_GetParentModel(__unmanagedPtr); - /// - /// Gets the material slot used by this mesh during rendering. - /// - public MaterialSlot MaterialSlot => ParentModel.MaterialSlots[MaterialSlotIndex]; - - /// - /// Gets a format of the mesh index buffer. - /// - public PixelFormat IndexBufferFormat => Use16BitIndexBuffer ? PixelFormat.R16_UInt : PixelFormat.R32_UInt; - /// /// Updates the model mesh vertex and index buffer data. /// Can be used only for virtual assets (see and ). @@ -435,82 +425,6 @@ namespace FlaxEngine UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); } - /// - /// Updates the model mesh index buffer data. - /// Can be used only for virtual assets (see and ). - /// Mesh data will be cached and uploaded to the GPU with a delay. - /// - /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. - 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."); - } - - /// - /// Updates the model mesh index buffer data. - /// Can be used only for virtual assets (see and ). - /// Mesh data will be cached and uploaded to the GPU with a delay. - /// - /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. - public void UpdateTriangles(List 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."); - } - - /// - /// Updates the model mesh index buffer data. - /// Can be used only for virtual assets (see and ). - /// Mesh data will be cached and uploaded to the GPU with a delay. - /// - /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. - 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."); - } - - /// - /// Updates the model mesh index buffer data. - /// Can be used only for virtual assets (see and ). - /// Mesh data will be cached and uploaded to the GPU with a delay. - /// - /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. - public void UpdateTriangles(List 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 /// /// Downloads the index buffer that contains mesh triangles data. To download data from GPU set to true and call this method from the thread other than main thread (see ). /// - /// If mesh index buffer format (see ) is then it's faster to call . + /// If mesh index buffer format (see ) is then it's faster to call . /// If set to true 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. /// The gathered data. public uint[] DownloadIndexBuffer(bool forceGpu = false) @@ -623,7 +537,7 @@ namespace FlaxEngine /// /// Downloads the index buffer that contains mesh triangles data. To download data from GPU set to true and call this method from the thread other than main thread (see ). /// - /// If mesh index buffer format (see ) is then data won't be downloaded. + /// If mesh index buffer format (see ) is then data won't be downloaded. /// If set to true 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. /// The gathered data. public ushort[] DownloadIndexBufferUShort(bool forceGpu = false) diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 813836b85..425bfbec7 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -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 _cachedVertexBuffer[3]; - mutable Array _cachedIndexBuffer; - mutable int32 _cachedIndexBufferCount; public: Mesh(const Mesh& other) @@ -38,11 +28,6 @@ public: #endif } - /// - /// Finalizes an instance of the class. - /// - ~Mesh(); - public: /// /// Gets the model owning this mesh. @@ -52,37 +37,6 @@ public: return (Model*)_model; } - /// - /// Gets the index buffer. - /// - FORCE_INLINE GPUBuffer* GetIndexBuffer() const - { - return _indexBuffer; - } - - /// - /// Gets the vertex buffer. - /// - /// The index. - /// The buffer. - FORCE_INLINE GPUBuffer* GetVertexBuffer(int32 index) const - { - return _vertexBuffers[index]; - } - - /// - /// Determines whether this mesh is initialized (has vertex and index buffers initialized). - /// - FORCE_INLINE bool IsInitialized() const - { - return _vertexBuffers[0] != nullptr; - } - - /// - /// Determines whether this mesh has a vertex colors buffer. - /// - API_PROPERTY() bool HasVertexColors() const; - /// /// Determines whether this mesh contains valid lightmap texture coordinates data. /// @@ -91,16 +45,6 @@ public: return _hasLightmapUVs; } -#if USE_PRECISE_MESH_INTERSECTS - /// - /// Gets the collision proxy used by the mesh. - /// - FORCE_INLINE const CollisionProxy& GetCollisionProxy() const - { - return _collisionProxy; - } -#endif - public: /// /// Updates the model mesh (used by the virtual models created with Init rather than Load). @@ -179,38 +123,6 @@ public: /// True if failed, otherwise false. 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: - /// - /// Updates the model mesh index buffer (used by the virtual models created with Init rather than Load). - /// - /// The amount of triangles in the index buffer. - /// The index buffer. - /// True if failed, otherwise false. - FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint32* ib) - { - return UpdateTriangles(triangleCount, ib, false); - } - - /// - /// Updates the model mesh index buffer (used by the virtual models created with Init rather than Load). - /// - /// The amount of triangles in the index buffer. - /// The index buffer. - /// True if failed, otherwise false. - FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint16* ib) - { - return UpdateTriangles(triangleCount, ib, true); - } - - /// - /// Updates the model mesh index buffer (used by the virtual models created with Init rather than Load). - /// - /// The amount of triangles in the index buffer. - /// The index buffer. - /// True if index buffer uses 16-bit index buffer, otherwise 32-bit. - /// True if failed, otherwise false. - bool UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices); - public: /// /// Initializes instance of the class. @@ -237,45 +149,7 @@ public: /// True if cannot load data, otherwise false. bool Load(uint32 vertices, uint32 triangles, const void* vb0, const void* vb1, const void* vb2, const void* ib, bool use16BitIndexBuffer); - /// - /// Unloads the mesh data (vertex buffers and cache). The opposite to Load. - /// - void Unload(); - public: - /// - /// Determines if there is an intersection between the mesh and a ray in given world - /// - /// The ray to test - /// World to transform box - /// When the method completes and returns true, contains the distance of the intersection (if any valid). - /// When the method completes, contains the intersection surface normal vector (if any valid). - /// True whether the two objects intersected - bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const; - - /// - /// Determines if there is an intersection between the mesh and a ray in given world - /// - /// The ray to test - /// The instance transformation. - /// When the method completes and returns true, contains the distance of the intersection (if any valid). - /// When the method completes, contains the intersection surface normal vector (if any valid). - /// True whether the two objects intersected - bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const; - -public: - /// - /// Gets the draw call geometry for this mesh. Sets the index and vertex buffers. - /// - /// The draw call. - void GetDrawCallGeometry(DrawCall& drawCall) const; - - /// - /// Draws the mesh. Binds vertex and index buffers and invokes the draw call. - /// - /// The GPU context. - void Render(GPUContext* context) const; - /// /// Draws the mesh. /// @@ -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 }; diff --git a/Source/Engine/Graphics/Models/MeshBase.cpp b/Source/Engine/Graphics/Models/MeshBase.cpp new file mode 100644 index 000000000..c95b58acf --- /dev/null +++ b/Source/Engine/Graphics/Models/MeshBase.cpp @@ -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 + 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(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(this, triangleCount, trianglesObj); +} + +bool MeshBase::UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj) +{ + return ::UpdateTriangles(this, triangleCount, trianglesObj); +} + +#endif diff --git a/Source/Engine/Graphics/Models/MeshBase.cs b/Source/Engine/Graphics/Models/MeshBase.cs new file mode 100644 index 000000000..5156c20a6 --- /dev/null +++ b/Source/Engine/Graphics/Models/MeshBase.cs @@ -0,0 +1,96 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +using System; +using System.Collections.Generic; + +namespace FlaxEngine +{ + partial class MeshBase + { + /// + /// Gets the material slot used by this mesh during rendering. + /// + public MaterialSlot MaterialSlot => ModelBase.MaterialSlots[MaterialSlotIndex]; + + /// + /// Gets a format of the mesh index buffer. + /// + public PixelFormat IndexBufferFormat => Use16BitIndexBuffer ? PixelFormat.R16_UInt : PixelFormat.R32_UInt; + + /// + /// Updates the model mesh index buffer data. + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. + /// + /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. + 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."); + } + + /// + /// Updates the model mesh index buffer data. + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. + /// + /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. + public void UpdateTriangles(List 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."); + } + + /// + /// Updates the model mesh index buffer data. + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. + /// + /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. + 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."); + } + + /// + /// Updates the model mesh index buffer data. + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. + /// + /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. + public void UpdateTriangles(List 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."); + } + } +} diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h index 3ada38b34..309fea9e5 100644 --- a/Source/Engine/Graphics/Models/MeshBase.h +++ b/Source/Engine/Graphics/Models/MeshBase.h @@ -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; /// -/// Base class for model resources meshes. +/// Base class for mesh objects. /// 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 _cachedVertexBuffers[3]; + mutable Array _cachedIndexBuffer; + mutable int32 _cachedIndexBufferCount; + +#if USE_PRECISE_MESH_INTERSECTS + CollisionProxy _collisionProxy; +#endif + explicit MeshBase(const SpawnParams& params) : ScriptingObject(params) { } public: + ~MeshBase(); + /// /// Gets the model owning this mesh. /// @@ -107,6 +127,29 @@ public: return _use16BitIndexBuffer; } +#if USE_PRECISE_MESH_INTERSECTS + /// + /// Gets the collision proxy used by the mesh. + /// + FORCE_INLINE const CollisionProxy& GetCollisionProxy() const + { + return _collisionProxy; + } +#endif + + /// + /// Determines whether this mesh is initialized (has vertex and index buffers initialized). + /// + FORCE_INLINE bool IsInitialized() const + { + return _vertexBuffers[0] != nullptr; + } + + /// + /// Determines whether this mesh has a vertex colors buffer. + /// + API_PROPERTY() bool HasVertexColors() const; + /// /// Gets the index of the material slot to use during this mesh rendering. /// @@ -126,14 +169,91 @@ public: /// The bounding box. void SetBounds(const BoundingBox& box); + /// + /// Gets the index buffer. + /// + FORCE_INLINE GPUBuffer* GetIndexBuffer() const + { + return _indexBuffer; + } + + /// + /// Gets the vertex buffer. + /// + /// The bind slot index. + /// The buffer or null if not used. + FORCE_INLINE GPUBuffer* GetVertexBuffer(int32 index) const + { + return _vertexBuffers[index]; + } + public: /// - /// 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. + /// + void Unload(); + +public: + /// + /// Updates the model mesh index buffer. + /// + /// The amount of triangles in the index buffer. + /// The index buffer. + /// True if failed, otherwise false. + FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint32* ib) + { + return UpdateTriangles(triangleCount, ib, false); + } + + /// + /// Updates the model mesh index buffer. + /// + /// The amount of triangles in the index buffer. + /// The index buffer. + /// True if failed, otherwise false. + FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint16* ib) + { + return UpdateTriangles(triangleCount, ib, true); + } + + /// + /// Updates the model mesh index buffer. + /// + /// The amount of triangles in the index buffer. + /// The index buffer. + /// True if index buffer uses 16-bit index buffer, otherwise 32-bit. + /// True if failed, otherwise false. + bool UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices); + +public: + /// + /// Determines if there is an intersection between the mesh and a ray in given world. + /// + /// The ray to test. + /// The mesh instance transformation. + /// When the method completes and returns true, contains the distance of the intersection (if any valid). + /// When the method completes, contains the intersection surface normal vector (if any valid). + /// True whether the two objects intersected, otherwise false. + bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const; + + /// + /// Determines if there is an intersection between the mesh and a ray in given world + /// + /// The ray to test + /// The mesh instance transformation. + /// When the method completes and returns true, contains the distance of the intersection (if any valid). + /// When the method completes, contains the intersection surface normal vector (if any valid). + /// True whether the two objects intersected, otherwise false. + bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const; + +public: + /// + /// Extracts mesh buffer data from a GPU. Cannot be called from the main thread. /// /// Buffer type /// The result data /// True if failed, otherwise false - virtual bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const = 0; + bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const; /// /// Extracts mesh buffer data from GPU in the async task. @@ -141,7 +261,7 @@ public: /// Buffer type /// The result data /// Created async task used to gather the buffer data. - virtual Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const = 0; + Task* DownloadDataGPUAsync(MeshBufferType type, BytesContainer& result) const; /// /// Extract mesh buffer data from CPU. Cached internally. @@ -246,4 +366,24 @@ public: float LightmapScale = -1.0f; #endif }; + + /// + /// Gets the draw call geometry for this mesh. Sets the index and vertex buffers. + /// + /// The draw call. + void GetDrawCallGeometry(struct DrawCall& drawCall) const; + + /// + /// Draws the mesh. Binds vertex and index buffers and invokes the draw call. + /// + /// The GPU context. + 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 }; diff --git a/Source/Engine/Graphics/Model.cs b/Source/Engine/Graphics/Models/Model.cs similarity index 100% rename from Source/Engine/Graphics/Model.cs rename to Source/Engine/Graphics/Models/Model.cs diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index 1d9ab1df0..b7e2ddc6a 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -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 diff --git a/Source/Engine/Graphics/SkinnedMesh.cs b/Source/Engine/Graphics/Models/SkinnedMesh.cs similarity index 97% rename from Source/Engine/Graphics/SkinnedMesh.cs rename to Source/Engine/Graphics/Models/SkinnedMesh.cs index 0381622b2..f60305e3c 100644 --- a/Source/Engine/Graphics/SkinnedMesh.cs +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cs @@ -88,16 +88,6 @@ namespace FlaxEngine /// public SkinnedModel ParentSkinnedModel => (SkinnedModel)Internal_GetParentModel(__unmanagedPtr); - /// - /// Gets the material slot used by this mesh during rendering. - /// - public MaterialSlot MaterialSlot => ParentSkinnedModel.MaterialSlots[MaterialSlotIndex]; - - /// - /// Gets a format of the mesh index buffer. - /// - public PixelFormat IndexBufferFormat => Use16BitIndexBuffer ? PixelFormat.R16_UInt : PixelFormat.R32_UInt; - /// /// Updates the skinned model mesh vertex and index buffer data. /// Can be used only for virtual assets (see and ). @@ -313,7 +303,7 @@ namespace FlaxEngine /// /// Downloads the index buffer that contains mesh triangles data. To download data from GPU set to true and call this method from the thread other than main thread (see ). /// - /// If mesh index buffer format (see ) is then it's faster to call . + /// If mesh index buffer format (see ) is then it's faster to call . /// If set to true 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. /// The gathered data. public uint[] DownloadIndexBuffer(bool forceGpu = false) @@ -327,7 +317,7 @@ namespace FlaxEngine /// /// Downloads the index buffer that contains mesh triangles data. To download data from GPU set to true and call this method from the thread other than main thread (see ). /// - /// If mesh index buffer format (see ) is then data won't be downloaded. + /// If mesh index buffer format (see ) is then data won't be downloaded. /// If set to true 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. /// The gathered data. public ushort[] DownloadIndexBufferUShort(bool forceGpu = false) diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h index 963511671..b8dd38696 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.h +++ b/Source/Engine/Graphics/Models/SkinnedMesh.h @@ -3,7 +3,6 @@ #pragma once #include "MeshBase.h" -#include "Types.h" #include "BlendShape.h" /// @@ -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 _cachedIndexBuffer; - mutable Array _cachedVertexBuffer; - mutable int32 _cachedIndexBufferCount; public: SkinnedMesh(const SkinnedMesh& other) @@ -28,11 +21,6 @@ public: #endif } - /// - /// Finalizes an instance of the class. - /// - ~SkinnedMesh(); - public: /// /// Gets the skinned model owning this mesh. @@ -42,14 +30,6 @@ public: return (SkinnedModel*)_model; } - /// - /// Determines whether this mesh is initialized (has vertex and index buffers initialized). - /// - FORCE_INLINE bool IsInitialized() const - { - return _vertexBuffer != nullptr; - } - /// /// Blend shapes used by this mesh. /// @@ -78,11 +58,6 @@ public: /// True if cannot load data, otherwise false. bool Load(uint32 vertices, uint32 triangles, const void* vb0, const void* ib, bool use16BitIndexBuffer); - /// - /// Unloads the mesh data (vertex buffers and cache). The opposite to Load. - /// - void Unload(); - public: /// /// 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: - /// - /// Determines if there is an intersection between the mesh and a ray in given world - /// - /// The ray to test - /// World to transform box - /// When the method completes and returns true, contains the distance of the intersection (if any valid). - /// When the method completes, contains the intersection surface normal vector (if any valid). - /// True whether the two objects intersected - bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal) const; - - /// - /// Determines if there is an intersection between the mesh and a ray in given world - /// - /// The ray to test - /// Instance transformation - /// When the method completes and returns true, contains the distance of the intersection (if any valid). - /// When the method completes, contains the intersection surface normal vector (if any valid). - /// True whether the two objects intersected - bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal) const; - -public: - /// - /// Draws the mesh. Binds vertex and index buffers and invokes the draw call. - /// - /// The GPU context. - void Render(GPUContext* context) const; - /// /// Draws the mesh. /// @@ -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); diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp index 75ca25d9a..f2a2c565e 100644 --- a/Source/Engine/Graphics/RenderTools.cpp +++ b/Source/Engine/Graphics/RenderTools.cpp @@ -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); -} diff --git a/Source/Engine/Graphics/TextureBase.cs b/Source/Engine/Graphics/Textures/TextureBase.cs similarity index 100% rename from Source/Engine/Graphics/TextureBase.cs rename to Source/Engine/Graphics/Textures/TextureBase.cs