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