3#include "IVSmokeCollisionComponent.h"
6#include "IVSmokeGridLibrary.h"
7#include "PhysicsEngine/BodySetup.h"
13DECLARE_CYCLE_STAT(TEXT(
"Update Collision"), STAT_IVSmoke_UpdateCollision, STATGROUP_IVSmoke)
14DECLARE_CYCLE_STAT(TEXT(
"Update Collision With Octree"), STAT_IVSmoke_UpdateCollisionWithOctree, STATGROUP_IVSmoke)
15DECLARE_CYCLE_STAT(TEXT(
"Rebuild Physics Geometry"), STAT_IVSmoke_RebuildPhysicsGeometry, STATGROUP_IVSmoke)
19#pragma region Lifecycle
21UIVSmokeCollisionComponent::UIVSmokeCollisionComponent()
23 PrimaryComponentTick.bCanEverTick =
false;
25 SetGenerateOverlapEvents(
false);
27 Mobility = EComponentMobility::Movable;
29 BodyInstance.SetCollisionProfileName(UCollisionProfile::CustomCollisionProfileName);
31 BodyInstance.SetCollisionEnabled(ECollisionEnabled::QueryOnly);
33 BodyInstance.SetObjectType(ECC_WorldDynamic);
35 BodyInstance.SetResponseToAllChannels(ECR_Ignore);
37 BodyInstance.SetResponseToChannel(ECC_Visibility, ECR_Block);
40UBodySetup* UIVSmokeCollisionComponent::GetBodySetup()
44 if (GEditor && GEditor->IsPlaySessionInProgress())
46 const UWorld* World = GetWorld();
47 if (World && World->WorldType == EWorldType::Editor)
56 VoxelBodySetup = NewObject<UBodySetup>(
this, NAME_None, RF_Transient);
57 VoxelBodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;
58 VoxelBodySetup->bNeverNeedsCookedCollisionData =
true;
60 return VoxelBodySetup;
63void UIVSmokeCollisionComponent::OnCreatePhysicsState()
67 Super::OnCreatePhysicsState();
72 if (GetCollisionEnabled() == ECollisionEnabled::NoCollision)
74 if (VoxelBodySetup && VoxelBodySetup->AggGeom.BoxElems.Num() > 0)
88 int32 Diff = FMath::Abs(ActiveVoxelNum - LastActiveVoxelNum);
96 UpdateCollision(VoxelBitArray, GridResolution, VoxelSize);
98 LastSyncTime = SyncTime;
99 LastActiveVoxelNum = ActiveVoxelNum;
106#pragma region Collision
108void UIVSmokeCollisionComponent::UpdateCollision(
const TArray<uint64>& VoxelBitArray,
const FIntVector& GridResolution,
float VoxelSize)
110 SCOPE_CYCLE_COUNTER(STAT_IVSmoke_UpdateCollision);
112 TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(
"IVSmoke::UIVSmokeCollisionComponent::UpdateCollision");
114 UBodySetup* BodySetup = GetBodySetup();
120 BodySetup->AggGeom.EmptyElements();
122 TArray<uint64> TempVoxelBitArray = VoxelBitArray;
124 const int32 ResolutionY = GridResolution.Y;
125 const int32 ResolutionZ = GridResolution.Z;
127 const FIntVector CenterOffset = GridResolution / 2;
129 const float VoxelExtent = VoxelSize * 0.5f;
131 for (int32 Z = 0; Z < ResolutionZ; ++Z)
133 for (int32 Y = 0; Y < ResolutionY; ++Y)
137 uint64& CurrentRow = TempVoxelBitArray[Index];
140 const int32 BeginX = FMath::CountTrailingZeros64(CurrentRow);
142 const uint64 Shifted = CurrentRow >> BeginX;
144 const int32 Width = (Shifted == MAX_uint64) ? (64 - BeginX) : FMath::CountTrailingZeros64(~Shifted);
146 const uint64 Mask = (Width == 64) ? MAX_uint64 : ((1ULL << Width) - 1ULL) << BeginX;
149 for (int32 NextY = Y + 1; NextY < ResolutionY; ++NextY)
153 const uint64& NextRow = TempVoxelBitArray[NextIndex];
154 if ((NextRow & Mask) == Mask)
165 for (int32 NextZ = Z + 1; NextZ < ResolutionZ; ++NextZ)
167 bool bCanExpand =
true;
168 for (int32 H = 0; H < Height; ++H)
172 const uint64& NextRow = TempVoxelBitArray[NextIndex];
173 if ((NextRow & Mask) != Mask)
190 for (int32 D = 0; D < Depth; ++D)
192 for (int32 H = 0; H < Height; ++H)
196 TempVoxelBitArray[NextIndex] &= ~Mask;
202 FIntVector BeginGridPos(BeginX, Y, Z);
204 FVector CenterShift((Width - 1) * VoxelExtent, (Height - 1) * VoxelExtent, (Depth - 1) * VoxelExtent);
205 Box.Center = BeginVoxelCenter + CenterShift;
207 Box.X = Width * VoxelSize;
208 Box.Y = Height * VoxelSize;
209 Box.Z = Depth * VoxelSize;
210 Box.Rotation = FRotator::ZeroRotator;
212 BodySetup->AggGeom.BoxElems.Add(Box);
217 FinalizePhysicsUpdate();
224 VoxelBodySetup->AggGeom.EmptyElements();
225 VoxelBodySetup->InvalidatePhysicsData();
226 VoxelBodySetup->CreatePhysicsMeshes();
229 RecreatePhysicsState();
232void UIVSmokeCollisionComponent::FinalizePhysicsUpdate()
239 VoxelBodySetup->InvalidatePhysicsData();
240 VoxelBodySetup->CreatePhysicsMeshes();
242 RecreatePhysicsState();
253 UWorld* World = GetWorld();
264 FTransform ComponentTrans = GetComponentTransform();
266 const float GapScale = 0.95f;
268 for (
const FKBoxElem& Box : VoxelBodySetup->AggGeom.BoxElems)
270 FVector WorldCenter = ComponentTrans.TransformPosition(Box.Center);
272 FVector Extent(Box.X * 0.5f * GapScale, Box.Y * 0.5f * GapScale, Box.Z * 0.5f * GapScale);
274 FQuat WorldRotation = ComponentTrans.GetRotation() * Box.Rotation.Quaternion();
276 uint32 Hash = GetTypeHash(Box.Center);
277 FRandomStream StableRNG(Hash);
279 float Hue = StableRNG.FRandRange(0.0f, 360.0f);
280 FLinearColor BoxColor = FLinearColor::MakeFromHSV8(Hue, 200, 255);
287 BoxColor.ToFColor(
true),
288 false, -1.0f, 0, 1.5f
292 FVector TextPos = GetComponentLocation() + FVector(0, 0, 50.0f);
293 FString DebugMsg = FString::Printf(TEXT(
"Collision Boxes: %d"), VoxelBodySetup->AggGeom.BoxElems.Num());
294 DrawDebugString(World, TextPos, DebugMsg,
nullptr, FColor::White, 0.0f,
true);
void DrawDebugVisualization() const
void TryUpdateCollision(const TArray< uint64 > &VoxelBitArray, const FIntVector &GridResolution, float VoxelSize, int32 ActiveVoxelNum, float SyncTime, bool bForce=false)
float MinCollisionUpdateInterval
int32 MinCollisionUpdateVoxelNum
static FORCEINLINE FVector GridToLocal(const FIntVector &GridPos, float VoxelSize, const FIntVector &CenterOffset)
static FORCEINLINE int32 GridToVoxelBitIndex(const FIntVector &GridPos, const FIntVector &Resolution)