// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #pragma once #include "Engine/Content/BinaryAsset.h" #include "Engine/Core/Math/BoundingBox.h" class Model; class ModelBase; class ModelData; class MeshBase; /// /// A storage data type. /// API_ENUM() enum class CollisionDataType { /// /// Nothing. /// None = 0, /// /// A convex polyhedron represented as a set of vertices and polygonal faces. The number of vertices and faces of a convex mesh is limited to 255. /// ConvexMesh = 1, /// /// A collision triangle mesh consists of a collection of vertices and the triangle indices. /// TriangleMesh = 2, }; /// /// Set of flags used to generate model convex mesh. Allows to customize process. /// API_ENUM(Attributes="Flags") enum class ConvexMeshGenerationFlags { /// /// Nothing. /// None = 0, /// /// Disables the convex mesh validation to speed-up hull creation. Creating a convex mesh with invalid input data without prior validation may result in undefined behavior. /// SkipValidation = 1, /// /// Enables plane shifting vertex limit algorithm. /// /// Plane shifting is an alternative algorithm for the case when the computed hull has more vertices /// than the specified vertex limit. /// /// The default algorithm computes the full hull, and an OBB around the input vertices. This OBB is then sliced /// with the hull planes until the vertex limit is reached. The default algorithm requires the vertex limit /// to be set to at least 8, and typically produces results that are much better quality than are produced /// by plane shifting. /// /// When plane shifting is enabled, the hull computation stops when vertex limit is reached.The hull planes /// are then shifted to contain all input vertices, and the new plane intersection points are then used to /// generate the final hull with the given vertex limit.Plane shifting may produce sharp edges to vertices /// very far away from the input cloud, and does not guarantee that all input vertices are inside the resulting /// hull. However, it can be used with a vertex limit as low as 4. /// UsePlaneShifting = 2, /// /// Inertia tensor computation is faster using SIMD code, but the precision is lower, which may result in incorrect inertia for very thin hulls. /// UseFastInteriaComputation = 4, /// /// Convex hull input vertices are shifted to be around origin to provide better computation stability. /// It is recommended to provide input vertices around the origin, otherwise use this flag to improve numerical stability. /// ShiftVertices = 8, /// /// If checked, the face remap table is not created. This saves a significant amount of memory, but disabled ability to remap the cooked collision geometry into original mesh using raycast hit info. /// SuppressFaceRemapTable = 16, /// /// The combination of flags that improve the collision data cooking performance at the cost of quality and features. Recommend for runtime dynamic or deformable objects that need quick collision updates. /// FastCook = SkipValidation | UseFastInteriaComputation | SuppressFaceRemapTable, }; DECLARE_ENUM_OPERATORS(ConvexMeshGenerationFlags); /// /// The collision data asset cooking options. /// API_STRUCT() struct CollisionDataOptions { DECLARE_SCRIPTING_TYPE_NO_SPAWN(CollisionDataOptions); /// /// The data type. /// API_FIELD() CollisionDataType Type = CollisionDataType::None; /// /// The source model asset id. /// API_FIELD() Guid Model = Guid::Empty; /// /// The source model LOD index. /// API_FIELD() int32 ModelLodIndex = 0; /// /// The cooked collision bounds. /// API_FIELD() BoundingBox Box = BoundingBox::Zero; /// /// The convex generation flags. /// API_FIELD() ConvexMeshGenerationFlags ConvexFlags = ConvexMeshGenerationFlags::None; /// /// The convex vertices limit (maximum amount). /// API_FIELD() int32 ConvexVertexLimit = 0; /// /// The source model material slots mask. One bit per-slot. Can be used to exclude particular material slots from collision cooking. /// API_FIELD() uint32 MaterialSlotsMask = MAX_uint32; }; /// /// Represents a physics mesh that can be used with a MeshCollider. Physics mesh can be a generic triangle mesh or a convex mesh. /// API_CLASS(NoSpawn) class FLAXENGINE_API CollisionData : public BinaryAsset { DECLARE_BINARY_ASSET_HEADER(CollisionData, 1); public: /// /// A raw structure stored in the binary asset. It has fixed size so it's easier to add new parameters to it. It's loaded and changed into Options structure used at runtime. /// struct SerializedOptions { CollisionDataType Type; Guid Model; int32 ModelLodIndex; ConvexMeshGenerationFlags ConvexFlags; int32 ConvexVertexLimit; uint32 MaterialSlotsMask; byte Padding[92]; }; static_assert(sizeof(SerializedOptions) == 128, "Invalid collision data options size. Change the padding."); private: CollisionDataOptions _options; void* _convexMesh; void* _triangleMesh; public: /// /// Gets the options. /// API_PROPERTY() FORCE_INLINE const CollisionDataOptions& GetOptions() const { return _options; } /// /// Gets the convex mesh object (valid only if asset is loaded and has cooked convex data). /// FORCE_INLINE void* GetConvex() const { return _convexMesh; } /// /// Gets the triangle mesh object (valid only if asset is loaded and has cooked triangle data). /// FORCE_INLINE void* GetTriangle() const { return _triangleMesh; } public: #if COMPILE_WITH_PHYSICS_COOKING /// /// Cooks the mesh collision data and updates the virtual asset. /// /// /// Can be used only for virtual assets (see and ). /// /// The collision data type. /// The source model. If model is virtual then this method cannot be called from the main thread. /// The source model LOD index. /// The source model material slots mask. One bit per-slot. Can be used to exclude particular material slots from collision cooking. /// The convex mesh generation flags. /// The convex mesh vertex limit. Use values in range [8;255] /// True if failed, otherwise false. API_FUNCTION() bool CookCollision(CollisionDataType type, ModelBase* model, int32 modelLodIndex = 0, uint32 materialSlotsMask = MAX_uint32, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags::None, int32 convexVertexLimit = 255); /// /// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread. /// /// /// Can be used only for virtual assets (see and ). /// /// The collision data type. /// The source geometry vertex buffer with vertices positions. Cannot be empty. /// The source data index buffer (triangles list). Uses 32-bit stride buffer. Cannot be empty. Length must be multiple of 3 (as 3 vertices build a triangle). /// The convex mesh generation flags. /// The convex mesh vertex limit. Use values in range [8;255] /// True if failed, otherwise false. API_FUNCTION() bool CookCollision(CollisionDataType type, const Span& vertices, const Span& triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags::None, int32 convexVertexLimit = 255); /// /// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread. /// /// /// Can be used only for virtual assets (see and ). /// /// The collision data type. /// The source geometry vertex buffer with vertices positions. Cannot be empty. /// The source data index buffer (triangles list). Uses 32-bit stride buffer. Cannot be empty. Length must be multiple of 3 (as 3 vertices build a triangle). /// The convex mesh generation flags. /// The convex mesh vertex limit. Use values in range [8;255] /// True if failed, otherwise false. API_FUNCTION() bool CookCollision(CollisionDataType type, const Span& vertices, const Span& triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags::None, int32 convexVertexLimit = 255); /// /// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread. /// /// /// Can be used only for virtual assets (see and ). /// /// The collision data type. /// The custom geometry. /// The convex mesh generation flags. /// The convex mesh vertex limit. Use values in range [8;255] /// True if failed, otherwise false. bool CookCollision(CollisionDataType type, ModelData* modelData, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit); #endif /// /// Extracts the triangle index of the original mesh data used for cooking this collision data. Can be used to get vertex attributes of the triangle mesh hit by the raycast. /// /// Supported only for collision data built as triangle mesh and without flag set. /// The face index of the collision mesh. /// The result source mesh used to build this collision data (can be null if collision data was cooked using custom geometry without source Model set). /// The result triangle index of the source geometry used to build this collision data. /// True if got a valid triangle index, otherwise false. API_FUNCTION() bool GetModelTriangle(uint32 faceIndex, API_PARAM(Out) MeshBase*& mesh, API_PARAM(Out) uint32& meshTriangleIndex) const; /// /// Extracts the collision data geometry into list of triangles. /// /// The output vertex buffer. /// The output index buffer. API_FUNCTION() void ExtractGeometry(API_PARAM(Out) Array& vertexBuffer, API_PARAM(Out) Array& indexBuffer) const; public: // MeshCollider is drawing debug view of the collision data, allow to share it across instances #if USE_EDITOR private: bool _hasMissingDebugLines = true; Array _debugLines; Array _debugVertexBuffer; Array _debugIndexBuffer; public: const Array& GetDebugLines(); void GetDebugTriangles(Array*& vertexBuffer, Array*& indexBuffer); #endif private: LoadResult load(const SerializedOptions* options, byte* dataPtr, int32 dataSize); protected: // [BinaryAsset] LoadResult load() override; void unload(bool isReloading) override; AssetChunksFlag getChunksToPreload() const override; };