// 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; };