// Copyright (c) Wojciech Figat. All rights reserved.
#pragma once
#include "ModelBase.h"
#include "Engine/Graphics/Models/Mesh.h"
///
/// Represents single Level Of Detail for the model. Contains a collection of the meshes.
///
API_CLASS(NoSpawn) class FLAXENGINE_API ModelLOD : public ModelLODBase
{
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(ModelLOD, ModelLODBase);
friend Model;
friend Mesh;
private:
uint32 _verticesCount = 0;
Model* _model = nullptr;
void Link(Model* model, int32 lodIndex)
{
_model = model;
_lodIndex = lodIndex;
_verticesCount = 0;
}
public:
///
/// The meshes array.
///
API_FIELD(ReadOnly) Array Meshes;
///
/// Gets the vertex count for this model LOD level.
///
API_PROPERTY() FORCE_INLINE int32 GetVertexCount() const
{
return _verticesCount;
}
public:
///
/// Determines if there is an intersection between the Model and a Ray in given world using given instance
///
/// The ray to test
/// World to test
/// When the method completes, contains the distance of the intersection (if any valid).
/// When the method completes, contains the intersection surface normal vector (if any valid).
/// Mesh, or null
/// True whether the two objects intersected
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh);
///
/// Determines if there is an intersection between the Model and a Ray in given world using given instance
///
/// The ray to test
/// The instance transformation.
/// When the method completes, contains the distance of the intersection (if any valid).
/// When the method completes, contains the intersection surface normal vector (if any valid).
/// Mesh, or null
/// True whether the two objects intersected
bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, Mesh** mesh);
///
/// Draws the meshes. Binds vertex and index buffers and invokes the draw calls.
///
/// The GPU context to draw with.
FORCE_INLINE void Render(GPUContext* context)
{
for (int32 i = 0; i < Meshes.Count(); i++)
Meshes.Get()[i].Render(context);
}
///
/// Draws the meshes from the model LOD.
///
/// The rendering context.
/// The material to use for rendering.
/// The world transformation of the model.
/// The object static flags.
/// True if rendered geometry can receive decals, otherwise false.
/// The draw passes to use for rendering this object.
/// The random per-instance value (normalized to range 0-1).
/// Object sorting key.
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int8 sortOrder = 0) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
Meshes.Get()[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom, sortOrder);
}
///
/// Draws all the meshes from the model LOD.
///
/// The rendering context.
/// The packed drawing info data.
/// The LOD transition dither factor.
FORCE_INLINE void Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info, float lodDitherFactor) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
Meshes.Get()[i].Draw(renderContext, info, lodDitherFactor);
}
///
/// Draws all the meshes from the model LOD.
///
/// The rendering context batch.
/// The packed drawing info data.
/// The LOD transition dither factor.
FORCE_INLINE void Draw(const RenderContextBatch& renderContextBatch, const Mesh::DrawInfo& info, float lodDitherFactor) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
Meshes.Get()[i].Draw(renderContextBatch, info, lodDitherFactor);
}
public:
// [ModelLODBase]
int32 GetMeshesCount() const override;
const MeshBase* GetMesh(int32 index) const override;
MeshBase* GetMesh(int32 index) override;
void GetMeshes(Array& meshes) override;
void GetMeshes(Array& meshes) const override;
};
///
/// Model asset that contains model object made of meshes which can rendered on the GPU.
///
API_CLASS(NoSpawn) class FLAXENGINE_API Model : public ModelBase
{
DECLARE_BINARY_ASSET_HEADER(Model, 30);
friend Mesh;
public:
///
/// Model level of details. The first entry is the highest quality LOD0 followed by more optimized versions.
///
API_FIELD(ReadOnly) Array> LODs;
///
/// The generated Sign Distant Field (SDF) for this model (merged all meshes). Use GenerateSDF to update it.
///
API_FIELD(ReadOnly) SDFData SDF;
public:
///
/// Determines whether any LOD has been initialized.
///
bool HasAnyLODInitialized() const;
public:
///
/// Determines if there is an intersection between the Model and a Ray in given world using given instance.
///
/// The ray to test
/// World to test
/// When the method completes, contains the distance of the intersection (if any valid).
/// When the method completes, contains the intersection surface normal vector (if any valid).
/// Mesh, or null
/// Level Of Detail index
/// True whether the two objects intersected
bool Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, Mesh** mesh, int32 lodIndex = 0);
///
/// Determines if there is an intersection between the Model and a Ray in given world using given instance.
///
/// The ray to test
/// The instance transformation.
/// When the method completes, contains the distance of the intersection (if any valid).
/// When the method completes, contains the intersection surface normal vector (if any valid).
/// Mesh, or null
/// Level Of Detail index
/// True whether the two objects intersected
bool Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, Mesh** mesh, int32 lodIndex = 0);
///
/// Gets the model bounding box in custom matrix world space.
///
/// The transformation matrix.
/// The Level Of Detail index.
/// The bounding box.
API_FUNCTION() BoundingBox GetBox(const Matrix& world, int32 lodIndex = 0) const;
///
/// Gets the model bounding box in custom transformation.
///
/// The instance transformation.
/// The Level Of Detail index.
/// The bounding box.
API_FUNCTION() BoundingBox GetBox(const Transform& transform, int32 lodIndex = 0) const
{
return LODs[lodIndex].GetBox(transform);
}
///
/// Gets the model bounding box in local space.
///
/// The Level Of Detail index.
/// The bounding box.
API_FUNCTION() BoundingBox GetBox(int32 lodIndex = 0) const;
public:
///
/// Draws the meshes. Binds vertex and index buffers and invokes the draw calls.
///
/// GPU context to draw with.
/// The Level Of Detail index.
void Render(GPUContext* context, int32 lodIndex = 0)
{
LODs[lodIndex].Render(context);
}
///
/// Draws the model.
///
/// The rendering context.
/// The material to use for rendering.
/// The world transformation of the model.
/// The object static flags.
/// True if rendered geometry can receive decals, otherwise false.
/// Object sorting key.
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, int8 sortOrder = 0) const;
///
/// Draws the model.
///
/// The rendering context.
/// The packed drawing info data.
void Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info);
///
/// Draws the model.
///
/// The rendering context batch.
/// The packed drawing info data.
void Draw(const RenderContextBatch& renderContextBatch, const Mesh::DrawInfo& info);
public:
///
/// Setups the model LODs collection including meshes creation.
///
/// The meshes count per lod array (amount of meshes per LOD).
/// True if failed, otherwise false.
API_FUNCTION() bool SetupLODs(const Span& meshesCountPerLod);
///
/// Generates the Sign Distant Field for this model.
///
/// Can be called in async in case of SDF generation on a CPU (assuming model is not during rendering).
/// The SDF texture resolution scale. Use higher values for more precise data but with significant performance and memory overhead.
/// The index of the LOD to use for the SDF building.
/// If true, the generated SDF texture data will be cached on CPU (in asset chunk storage) to allow saving it later, otherwise it will be runtime for GPU-only. Ignored for virtual assets or in build.
/// Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh.
/// Enables using GPU for SDF generation, otherwise CPU will be used (async via Job System).
/// True if failed, otherwise false.
API_FUNCTION() bool GenerateSDF(float resolutionScale = 1.0f, int32 lodIndex = 6, bool cacheData = true, float backfacesThreshold = 0.6f, bool useGPU = true);
///
/// Sets set SDF data (releases the current one).
///
API_FUNCTION() void SetSDF(const SDFData& sdf);
private:
///
/// Initializes this model to an empty collection of LODs with meshes.
///
/// The meshes count per lod array (amount of meshes per LOD).
/// True if failed, otherwise false.
bool Init(const Span& meshesCountPerLod);
// [ModelBase]
bool LoadHeader(ReadStream& stream, byte& headerVersion);
#if USE_EDITOR
friend class ImportModel;
bool SaveHeader(WriteStream& stream) const override;
static bool SaveHeader(WriteStream& stream, const ModelData& modelData);
bool Save(bool withMeshDataFromGpu, Function& getChunk) const override;
#endif
public:
// [ModelBase]
void SetupMaterialSlots(int32 slotsCount) override;
int32 GetLODsCount() const override;
const ModelLODBase* GetLOD(int32 lodIndex) const override;
ModelLODBase* GetLOD(int32 lodIndex) override;
const MeshBase* GetMesh(int32 meshIndex, int32 lodIndex = 0) const override;
MeshBase* GetMesh(int32 meshIndex, int32 lodIndex = 0) override;
void GetMeshes(Array& meshes, int32 lodIndex = 0) const override;
void GetMeshes(Array& meshes, int32 lodIndex = 0) override;
void InitAsVirtual() override;
// [StreamableResource]
int32 GetMaxResidency() const override;
int32 GetAllocatedResidency() const override;
protected:
// [ModelBase]
LoadResult load() override;
void unload(bool isReloading) override;
AssetChunksFlag getChunksToPreload() const override;
};