IVSmoke 1.0
Loading...
Searching...
No Matches
IVSmokeCollisionComponent.h
1// Copyright (c) 2026, Team SDB. All rights reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "Components/PrimitiveComponent.h"
7#include "Engine/CollisionProfile.h"
8#include "IVSmokeCollisionComponent.generated.h"
9
10/**
11 * A primitive component that dynamically generates collision geometry based on the voxel grid data.
12 *
13 * ## Overview
14 * Unlike standard static meshes, this component constructs a set of box colliders (AggGeom)
15 * representing the active voxels. It uses a binary greedy meshing algorithm to merge adjacent voxels into
16 * larger boxes to minimize the physics cost.
17 *
18 * ## Usage
19 * This component uses the standard Collision category in the Details panel.
20 * By default, it is configured for Query-Only interactions:
21 * - Visibility: Blocked (Blocks AI Line of Sight).
22 * - Others: Ignored (Players can walk through).
23 *
24 * You can customize these responses in the Collision Presets (set to 'Custom').
25 *
26 * @note Frequent updates to collision geometry are expensive. Use `MinCollisionUpdateInterval`
27 * and `MinCollisionUpdateVoxelNum` to throttle updates.
28 */
29UCLASS(ClassGroup = (IVSmoke), meta = (BlueprintSpawnableComponent))
30class IVSMOKE_API UIVSmokeCollisionComponent : public UPrimitiveComponent
31{
32 GENERATED_BODY()
33
34 //~==============================================================================
35 // Component Lifecycle
36#pragma region Lifecycle
37public:
39
40 virtual UBodySetup* GetBodySetup() override;
41
42protected:
43 virtual void OnCreatePhysicsState() override;
44#pragma endregion
45
46 //~==============================================================================
47 // Collision Management
48#pragma region Collision
49public:
50 /**
51 * Attempts to update the collision geometry based on the current voxel data.
52 *
53 * It checks `MinCollisionUpdateInterval` and `MinCollisionUpdateVoxelNum` to throttle updates
54 * and prevent performance spikes from frequent physics rebuilding.
55 *
56 * @param VoxelBitArray A bitmask buffer where each `uint64` element represents a row of voxels along the X-axis.
57 * @warning The Grid X-resolution must not exceed 64.
58 * @param GridResolution The resolution of the voxel grid (Width, Depth, Height).
59 * @param VoxelSize World space size of a single voxel.
60 * @param ActiveVoxelNum Current count of active voxels (used for threshold checks).
61 * @param SyncTime Current synchronized world time (used for interval checks).
62 * @param bForce If true, bypasses optimization checks and forces an immediate rebuild.
63 */
64 void TryUpdateCollision(const TArray<uint64>& VoxelBitArray, const FIntVector& GridResolution, float VoxelSize, int32 ActiveVoxelNum, float SyncTime, bool bForce = false);
65
66 /**
67 * Clears all generated physics geometry and resets the collision state.
68 * Called when the simulation is stopped or reset to ensure no "ghost" collision remains.
69 */
70 void ResetCollision();
71
72 /** Master switch for voxel collision. If false, no physics geometry will be generated, and all update requests will be ignored. */
73 UPROPERTY(EditAnywhere, Category = "IVSmoke | Config")
74 bool bCollisionEnabled = true;
75
76 /** The minimum number of voxel changes (spawned or destroyed) required to trigger a physics geometry rebuild. */
77 UPROPERTY(EditAnywhere, Category = "IVSmoke | Config", meta = (EditCondition = "bCollisionEnabled", ClampMin = "1", UIMin = "10", UIMax = "1000"))
78 int32 MinCollisionUpdateVoxelNum = 50;
79
80 /** The minimum time (in seconds) that must pass between two consecutive physics geometry rebuilds. */
81 UPROPERTY(EditAnywhere, Category = "IVSmoke | Config", meta = (EditCondition = "bCollisionEnabled", ClampMin = "0.0", UIMax = "2.0"))
82 float MinCollisionUpdateInterval = 0.25f;
83
84private:
85 /**
86 * Core algorithm that converts raw voxel data into physics geometry.
87 * Uses a greedy meshing approach to merge adjacent voxels into larger `FKBoxElem` boxes,
88 * significantly reducing the number of physics bodies required.
89 * @note This is a computationally expensive operation (O(N) on grid size).
90 */
91 void UpdateCollision(const TArray<uint64>& VoxelBitArray, const FIntVector& GridResolution, float VoxelSize);
92
93 /** Commits the new geometry to the physics engine. */
94 void FinalizePhysicsUpdate();
95
96 /** Transient BodySetup used to store the dynamic collision geometry (AggGeom). */
97 UPROPERTY(Transient)
98 TObjectPtr<UBodySetup> VoxelBodySetup;
99
100 /** Timestamp of the last successful collision update. Used for throttling. */
101 float LastSyncTime = 0.0f;
102
103 /** Voxel count at the last update. Used to detect if the shape has changed significantly. */
104 int32 LastActiveVoxelNum = 0;
105#pragma endregion
106
107 //~==============================================================================
108 // Debug
109#pragma region Debug
110public:
111 /**
112 * Renders wireframe boxes for each generated collision element (`FKBoxElem`).
113 * Useful for visualizing how the greedy meshing algorithm has optimized the voxels.
114 */
115 void DrawDebugVisualization() const;
116
117 /**
118 * If true, draws debug visualization for the collision geometry in the editor.
119 * @note Only works if `IVSmokeDebugSettings::bDebugEnabled` is also true on the main actor.
120 */
121 UPROPERTY(EditAnywhere, Category = "IVSmoke | Debug")
122 bool bDebugEnabled = false;
123
124#pragma endregion
125};