// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #pragma once #include "Config.h" #include "FoliageInstance.h" #include "FoliageCluster.h" #include "FoliageType.h" #include "Engine/Level/Actor.h" /// /// Represents a foliage actor that contains a set of instanced meshes. /// /// API_CLASS() class FLAXENGINE_API Foliage final : public Actor { DECLARE_SCENE_OBJECT(Foliage); private: bool _disableFoliageTypeEvents; int32 _sceneRenderingKey = -1; public: /// /// The allocated foliage instances. It's read-only. /// ChunkedArray Instances; #if FOLIAGE_USE_SINGLE_QUAD_TREE /// /// The root cluster. Contains all the instances and it's the starting point of the quad-tree hierarchy. Null if no foliage added. It's read-only. /// FoliageCluster* Root = nullptr; /// /// The allocated foliage clusters. It's read-only. /// ChunkedArray Clusters; #endif /// /// The foliage instances types used by the current foliage actor. It's read-only. /// API_FIELD(ReadOnly, Attributes="HideInEditor, NoSerialize") Array FoliageTypes; public: /// /// Gets the total amount of the instanced of foliage. /// /// The foliage instances count. API_PROPERTY() int32 GetInstancesCount() const; /// /// Gets the foliage instance by index. /// /// The zero-based index of the foliage instance. /// The foliage instance data. API_FUNCTION() FoliageInstance GetInstance(int32 index) const; /// /// Gets the total amount of the types of foliage. /// /// The foliage types count. API_PROPERTY() int32 GetFoliageTypesCount() const; /// /// Gets the foliage type. /// /// The zero-based index of the foliage type. /// The foliage type. API_FUNCTION() FoliageType* GetFoliageType(int32 index); /// /// Adds the type of the foliage. /// /// The model to assign. It cannot be null nor already used by the other instance type (it must be unique within the given foliage actor). API_FUNCTION() void AddFoliageType(Model* model); /// /// Removes the foliage instance type and all foliage instances using this type. /// /// The zero-based index of the foliage instance type. API_FUNCTION() void RemoveFoliageType(int32 index); /// /// Gets the total amount of the instanced that use the given foliage type. /// /// The zero-based index of the foliage type. /// The foliage type instances count. API_FUNCTION() int32 GetFoliageTypeInstancesCount(int32 index) const; /// /// Adds the new foliage instance. Ensure to always call after editing foliage to sync cached data (call it once after editing one or more instances). /// /// Input instance bounds, instance random and world matrix are ignored (recalculated). /// The instance. API_FUNCTION() void AddInstance(API_PARAM(Ref) const FoliageInstance& instance); /// /// Removes the foliage instance. Ensure to always call after editing foliage to sync cached data (call it once after editing one or more instances). /// /// The zero-based index of the instance to remove. API_FUNCTION() void RemoveInstance(int32 index) { RemoveInstance(Instances.IteratorAt(index)); } /// /// Removes the foliage instance. Ensure to always call after editing foliage to sync cached data (call it once after editing one or more instances). /// /// The iterator from foliage instances that points to the instance to remove. void RemoveInstance(ChunkedArray::Iterator i); /// /// Sets the foliage instance transformation. Ensure to always call after editing foliage to sync cached data (call it once after editing one or more instances). /// /// The zero-based index of the foliage instance. /// The value. API_FUNCTION() void SetInstanceTransform(int32 index, API_PARAM(Ref) const Transform& value); /// /// Called when foliage type model is loaded. /// /// The zero-based index of the foliage type. void OnFoliageTypeModelLoaded(int32 index); /// /// Rebuilds the foliage clusters used as internal acceleration structures (quad tree). /// API_FUNCTION() void RebuildClusters(); /// /// Updates the cull distance for all foliage instances and for created clusters. /// API_FUNCTION() void UpdateCullDistance(); public: /// /// Gets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices. /// API_PROPERTY() static float GetGlobalDensityScale(); /// /// Sets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices. /// API_PROPERTY() static void SetGlobalDensityScale(float value); private: void AddToCluster(ChunkedArray& clusters, FoliageCluster* cluster, FoliageInstance& instance); #if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING struct DrawKey { IMaterial* Mat; const Mesh* Geo; int32 Lightmap; bool operator==(const DrawKey& other) const { return Mat == other.Mat && Geo == other.Geo && Lightmap == other.Lightmap; } friend uint32 GetHash(const DrawKey& key) { uint32 hash = (uint32)((int64)(key.Mat) >> 3); hash ^= (uint32)((int64)(key.Geo) >> 3) + 0x9e3779b9 + (hash << 6) + (hash >> 2); hash ^= (uint32)key.Lightmap; return hash; } }; typedef Array> DrawCallsList; typedef Dictionary BatchedDrawCalls; void DrawInstance(RenderContext& renderContext, FoliageInstance& instance, const FoliageType& type, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const; void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const; #else void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw); #endif #if !FOLIAGE_USE_SINGLE_QUAD_TREE void DrawClusterGlobalSDF(class GlobalSignDistanceFieldPass* globalSDF, const BoundingBox& globalSDFBounds, FoliageCluster* cluster, const FoliageType& type); void DrawClusterGlobalSA(class GlobalSurfaceAtlasPass* globalSA, const Vector4& cullingPosDistance, FoliageCluster* cluster, const FoliageType& type, const BoundingBox& localBounds); void DrawFoliageJob(int32 i); RenderContextBatch* _renderContextBatch; #endif void DrawType(RenderContext& renderContext, const FoliageType& type, DrawCallsList* drawCallsLists); public: /// /// Determines if there is an intersection between the current object or any it's child and a ray. /// /// The ray 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). /// When the method completes, contains zero-based index of the foliage instance that is the closest to the ray. /// True whether the two objects intersected, otherwise false. API_FUNCTION() bool Intersects(API_PARAM(Ref) const Ray& ray, API_PARAM(Out) Real& distance, API_PARAM(Out) Vector3& normal, API_PARAM(Out) int32& instanceIndex); public: // [Actor] void Draw(RenderContext& renderContext) override; void Draw(RenderContextBatch& renderContextBatch) override; bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; void OnLayerChanged() override; protected: // [Actor] void OnEnable() override; void OnDisable() override; void OnTransformChanged() override; };