// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "../Actor.h"
#include "Engine/Core/Math/OrientedBoundingBox.h"
#include "Engine/Content/Assets/MaterialBase.h"
#include "Engine/CSG/Brush.h"
///
/// Represents a part of the CSG brush actor. Contains information about single surface.
///
API_STRUCT() struct BrushSurface : ISerializable
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(BrushSurface);
///
/// The parent brush.
///
API_FIELD(Attributes="HideInEditor")
BoxBrush* Brush = nullptr;
///
/// The surface index in the parent brush surfaces list.
///
API_FIELD(Attributes="HideInEditor")
int32 Index = -1;
///
/// The material used to render the brush surface.
///
API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"Brush\")")
AssetReference Material;
///
/// The surface texture coordinates scale.
///
API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Brush\", \"UV Scale\"), Limit(-1000, 1000, 0.01f)")
Float2 TexCoordScale = Float2::One;
///
/// The surface texture coordinates offset.
///
API_FIELD(Attributes="EditorOrder(40), EditorDisplay(\"Brush\", \"UV Offset\"), Limit(-1000, 1000, 0.01f)")
Float2 TexCoordOffset = Float2::Zero;
///
/// The surface texture coordinates rotation angle (in degrees).
///
API_FIELD(Attributes="EditorOrder(50), EditorDisplay(\"Brush\", \"UV Rotation\")")
float TexCoordRotation = 0.0f;
///
/// The scale in lightmap (per surface).
///
API_FIELD(Attributes="EditorOrder(20), EditorDisplay(\"Brush\", \"Scale In Lightmap\"), Limit(0, 10000, 0.1f)")
float ScaleInLightmap = 1.0f;
public:
// [ISerializable]
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
};
///
/// Performs CSG box brush operation that adds or removes geometry.
///
API_CLASS(Attributes="ActorContextMenu(\"New/Other/Box Brush\"), ActorToolbox(\"Other\", \"CSG Box Brush\")")
class FLAXENGINE_API BoxBrush : public Actor, public CSG::Brush
{
DECLARE_SCENE_OBJECT(BoxBrush);
private:
Vector3 _center;
Vector3 _size;
OrientedBoundingBox _bounds;
BrushMode _mode;
public:
///
/// Brush surfaces scale in lightmap
///
API_FIELD(Attributes="EditorOrder(30), DefaultValue(1.0f), EditorDisplay(\"CSG\", \"Scale In Lightmap\"), Limit(0, 1000.0f, 0.1f)")
float ScaleInLightmap = 1.0f;
///
/// Brush proxy per surface
///
BrushSurface Surfaces[6];
///
/// Gets the brush proxies per surface.
///
API_PROPERTY(Attributes="Serialize, EditorOrder(100), EditorDisplay(\"Surfaces\", EditorDisplayAttribute.InlineStyle), Collection(CanReorderItems = false, NotNullItems = true, ReadOnly = true)")
Array GetSurfaces() const;
///
/// Sets the brush proxies per surface.
///
API_PROPERTY() void SetSurfaces(const Array& value);
///
/// Gets the CSG brush mode.
///
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(BrushMode.Additive), EditorDisplay(\"CSG\")")
FORCE_INLINE BrushMode GetMode() const
{
return _mode;
}
///
/// Sets the CSG brush mode.
///
/// The value.
API_PROPERTY() void SetMode(BrushMode value);
///
/// Gets the brush center (in local space).
///
/// The value.
API_PROPERTY(Attributes="EditorOrder(21), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"CSG\")")
FORCE_INLINE Vector3 GetCenter() const
{
return _center;
}
///
/// Sets the brush center (in local space).
///
/// The value.
API_PROPERTY() void SetCenter(const Vector3& value);
///
/// Gets the brush size.
///
/// The value.
API_PROPERTY(Attributes="EditorOrder(20), EditorDisplay(\"CSG\")")
FORCE_INLINE Vector3 GetSize() const
{
return _size;
}
///
/// Sets the brush size.
///
/// The value.
API_PROPERTY() void SetSize(const Vector3& value);
///
/// Gets CSG surfaces
///
/// Surfaces
void GetSurfaces(CSG::Surface surfaces[6]);
///
/// Sets the brush surface material.
///
/// The brush surface index.
/// The material.
API_FUNCTION() void SetMaterial(int32 surfaceIndex, MaterialBase* material);
public:
///
/// Gets the volume bounding box (oriented).
///
API_PROPERTY() FORCE_INLINE OrientedBoundingBox GetOrientedBox() const
{
return _bounds;
}
///
/// Determines if there is an intersection between the brush surface and a ray.
/// If collision data is available on the CPU performs exact intersection check with the geometry.
/// Otherwise performs simple vs test.
/// For more efficient collisions detection and ray casting use physics.
///
/// The brush surface index.
/// The ray to test.
/// When the method completes and returns true, contains the distance of the intersection (if any valid).
/// When the method completes, contains the intersection surface normal vector (if any valid).
/// True if the actor is intersected by the ray, otherwise false.
API_FUNCTION() bool Intersects(int32 surfaceIndex, API_PARAM(Ref) const Ray& ray, API_PARAM(Out) Real& distance, API_PARAM(Out) Vector3& normal) const;
///
/// Gets the brush surface triangles array (group by 3 vertices).
///
/// The brush surface index.
/// The output vertices buffer with triangles or empty if no data loaded.
API_FUNCTION() void GetVertices(int32 surfaceIndex, API_PARAM(Out) Array& outputData) const;
private:
FORCE_INLINE void UpdateBounds()
{
OrientedBoundingBox::CreateCentered(_center, _size, _bounds);
_bounds.Transform(_transform);
_bounds.GetBoundingBox(_box);
BoundingSphere::FromBox(_box, _sphere);
}
public:
// [Actor]
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
#if USE_EDITOR
void OnDebugDrawSelected() override;
#endif
// [CSG::Brush]
Scene* GetBrushScene() const override;
Guid GetBrushID() const override;
bool CanUseCSG() const override;
CSG::Mode GetBrushMode() const override;
void GetSurfaces(Array& surfaces) override;
int32 GetSurfacesCount() override;
protected:
// [Actor]
void OnTransformChanged() override;
void OnActiveInTreeChanged() override;
void OnOrderInParentChanged() override;
void OnParentChanged() override;
};