// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once #include "Engine/Core/Collections/Array.h" #include "Mesh.h" class MemoryReadStream; /// /// Represents single Level Of Detail for the model. Contains a collection of the meshes. /// API_CLASS(NoSpawn) class FLAXENGINE_API ModelLOD : public ScriptingObject { DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(ModelLOD, ScriptingObject); friend Model; friend Mesh; private: Model* _model = nullptr; int32 _lodIndex = 0; uint32 _verticesCount; public: /// /// The screen size to switch LODs. Bottom limit of the model screen size to render this LOD. /// API_FIELD() float ScreenSize = 1.0f; /// /// The meshes array. /// API_FIELD(ReadOnly) Array Meshes; /// /// Determines whether any mesh has been initialized. /// /// True if any mesh has been initialized, otherwise false. FORCE_INLINE bool HasAnyMeshInitialized() const { // Note: we initialize all meshes at once so the last one can be used to check it. return Meshes.HasItems() && Meshes.Last().IsInitialized(); } /// /// Gets the model LOD index. /// API_PROPERTY() FORCE_INLINE int32 GetLODIndex() const { return _lodIndex; } /// /// Gets the vertex count for this model LOD level. /// API_PROPERTY() FORCE_INLINE int32 GetVertexCount() const { return _verticesCount; } public: /// /// Initializes the LOD from the data stream. /// /// The stream. /// True if fails, otherwise false. bool Load(MemoryReadStream& stream); /// /// Unloads the LOD meshes data (vertex buffers and cache). It won't dispose the meshes collection. The opposite to Load. /// void Unload(); /// /// Cleanups the data. /// void Dispose(); 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); /// /// Get model bounding box in transformed world matrix. /// /// World matrix /// Bounding box BoundingBox GetBox(const Matrix& world) const; /// /// Get model bounding box in transformed world. /// /// The instance transformation. /// The meshes deformation container (optional). /// Bounding box BoundingBox GetBox(const Transform& transform, const MeshDeformation* deformation = nullptr) const; /// /// Gets the bounding box combined for all meshes in this model LOD. /// API_PROPERTY() BoundingBox GetBox() const; /// /// 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, int16 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); } };