// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Math/BoundingBox.h"
#include "Engine/Core/Math/BoundingSphere.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Graphics/RenderTask.h"
#include "ModelInstanceEntry.h"
#include "Types.h"
#include "BlendShape.h"
class GPUBuffer;
///
/// Represents part of the skinned model that is made of vertices and can be rendered using custom material, transformation and skeleton bones hierarchy.
///
API_CLASS(NoSpawn) class FLAXENGINE_API SkinnedMesh : public PersistentScriptingObject
{
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(SkinnedMesh, PersistentScriptingObject);
protected:
SkinnedModel* _model;
int32 _index;
int32 _lodIndex;
int32 _materialSlotIndex;
bool _use16BitIndexBuffer;
BoundingBox _box;
BoundingSphere _sphere;
uint32 _vertices;
uint32 _triangles;
GPUBuffer* _vertexBuffer;
GPUBuffer* _indexBuffer;
mutable Array _cachedIndexBuffer;
mutable Array _cachedVertexBuffer;
public:
SkinnedMesh(const SkinnedMesh& other)
: SkinnedMesh()
{
#if !!BUILD_RELEASE
CRASH; // Not used
#endif
}
///
/// Finalizes an instance of the class.
///
~SkinnedMesh();
public:
///
/// Gets the skinned model owning this mesh.
///
/// The skinned model
FORCE_INLINE SkinnedModel* GetSkinnedModel() const
{
return _model;
}
///
/// Gets the mesh index.
///
/// The index
FORCE_INLINE int32 GetIndex() const
{
return _index;
}
///
/// Gets the material slot index.
///
/// The material slot index
API_PROPERTY() FORCE_INLINE int32 GetMaterialSlotIndex() const
{
return _materialSlotIndex;
}
///
/// Sets the index of the material slot index.
///
/// The value.
API_PROPERTY() void SetMaterialSlotIndex(int32 value);
///
/// Gets the triangle count.
///
/// The triangles
API_PROPERTY() FORCE_INLINE int32 GetTriangleCount() const
{
return _triangles;
}
///
/// Gets the vertex count.
///
/// The vertices
API_PROPERTY() FORCE_INLINE int32 GetVertexCount() const
{
return _vertices;
}
///
/// 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;
}
///
/// Determines whether this mesh is using 16 bit index buffer, otherwise it's 32 bit.
///
/// True if this mesh is using 16 bit index buffer, otherwise 32 bit index buffer.
API_PROPERTY() FORCE_INLINE bool Use16BitIndexBuffer() const
{
return _use16BitIndexBuffer;
}
///
/// Blend shapes used by this mesh.
///
Array BlendShapes;
public:
///
/// Initializes a new instance of the class.
///
/// The model.
/// The model LOD index.
/// The mesh index.
/// The material slot index to use.
/// The bounding box.
/// The bounding sphere.
void Init(SkinnedModel* model, int32 lodIndex, int32 index, int32 materialSlotIndex, const BoundingBox& box, const BoundingSphere& sphere);
///
/// Load mesh data and Initialize GPU buffers
///
/// Amount of vertices in the vertex buffer
/// Amount of triangles in the index buffer
/// Vertex buffer data
/// Index buffer data
/// True if use 16 bit indices for the index buffer (true: uint16, false: uint32).
/// True if cannot load data, otherwise false.
bool Load(uint32 vertices, uint32 triangles, void* vb0, 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).
///
/// The amount of vertices in the vertex buffer.
/// The amount of triangles in the index buffer.
/// The vertex buffer data.
/// The index buffer.
/// True if failed, otherwise false.
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, int32* ib)
{
return UpdateMesh(vertexCount, triangleCount, vb, ib, false);
}
///
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
///
/// The amount of vertices in the vertex buffer.
/// The amount of triangles in the index buffer.
/// The vertex buffer data.
/// The index buffer.
/// True if failed, otherwise false.
FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint16* ib)
{
return UpdateMesh(vertexCount, triangleCount, vb, ib, true);
}
///
/// Updates the model mesh (used by the virtual models created with Init rather than Load).
///
/// The amount of vertices in the vertex buffer.
/// The amount of triangles in the index buffer.
/// The vertex buffer data.
/// The index buffer.
/// True if index buffer uses 16-bit index buffer, otherwise 32-bit.
/// True if failed, otherwise false.
bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices);
public:
///
/// Sets the mesh bounds.
///
/// The bounding box.
void SetBounds(const BoundingBox& box)
{
_box = box;
BoundingSphere::FromBox(box, _sphere);
}
///
/// Gets the box.
///
/// The bounding box.
API_PROPERTY() FORCE_INLINE BoundingBox GetBox() const
{
return _box;
}
///
/// Gets the sphere.
///
/// The bounding sphere.
API_PROPERTY() FORCE_INLINE BoundingSphere GetSphere() const
{
return _sphere;
}
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, float& distance, Vector3& normal) const;
///
/// Retrieves the eight corners of the bounding box.
///
/// An array of points representing the eight corners of the bounding box.
FORCE_INLINE void GetCorners(Vector3 corners[8]) const
{
_box.GetCorners(corners);
}
public:
///
/// Model instance drawing packed data.
///
struct DrawInfo
{
///
/// The instance buffer to use during model rendering
///
ModelInstanceEntries* Buffer;
///
/// The skinning.
///
SkinnedMeshDrawData* Skinning;
///
/// The blend shapes.
///
BlendShapesInstance* BlendShapes;
///
/// The world transformation of the model.
///
Matrix* World;
///
/// The instance drawing state data container. Used for LOD transition handling and previous world transformation matrix updating.
///
GeometryDrawStateData* DrawState;
///
/// The object draw modes.
///
DrawPass DrawModes;
///
/// The bounds of the model (used to select a proper LOD during rendering).
///
BoundingSphere Bounds;
///
/// The per-instance random value.
///
float PerInstanceRandom;
///
/// The LOD bias value.
///
char LODBias;
///
/// The forced LOD to use. Value -1 disables this feature.
///
char ForcedLOD;
};
///
/// Draws the mesh. Binds vertex and index buffers and invokes the draw call.
///
/// The GPU context.
void Render(GPUContext* context) const;
///
/// Draws the mesh.
///
/// The rendering context.
/// The packed drawing info data.
/// The LOD transition dither factor.
void Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const;
public:
///
/// Extract mesh buffer data from the GPU (cannot be called from the main thread!).
///
/// Buffer type
/// The result data
/// True if failed, otherwise false
bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const;
///
/// Extracts mesh buffer data from the GPU in the async task.
///
/// Buffer type
/// The result data
/// Created async task used to gather the buffer data.
Task* DownloadDataAsyncGPU(MeshBufferType type, BytesContainer& result) const;
///
/// Extract mesh buffer data from the CPU.
///
/// Buffer type
/// The result data
/// True if failed, otherwise false
bool DownloadDataCPU(MeshBufferType type, BytesContainer& result) const;
private:
// Internal bindings
API_FUNCTION(NoProxy) ScriptingObject* GetParentModel();
API_FUNCTION(NoProxy) bool UpdateMeshInt(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj);
API_FUNCTION(NoProxy) bool UpdateMeshUShort(MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* blendIndicesObj, MonoArray* blendWeightsObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj);
API_FUNCTION(NoProxy) bool DownloadBuffer(bool forceGpu, MonoArray* resultObj, int32 typeI);
};