3#include "IVSmokeHoleGeneratorComponent.h"
5#include "Engine/TextureRenderTargetVolume.h"
6#include "Engine/Texture2D.h"
7#include "Engine/World.h"
8#include "GameFramework/GameStateBase.h"
9#include "GlobalShader.h"
11#include "IVSmokeHoleShaders.h"
12#include "IVSmokeHolePreset.h"
13#include "IVSmokePostProcessPass.h"
14#include "IVSmokeVoxelVolume.h"
15#include "Net/UnrealNetwork.h"
16#include "RHICommandList.h"
17#include "RHIStaticStates.h"
18#include "RenderGraphBuilder.h"
19#include "RenderGraphUtils.h"
20#include "RenderingThread.h"
21#include "TextureResource.h"
23UIVSmokeHoleGeneratorComponent::UIVSmokeHoleGeneratorComponent()
24 : bHoleTextureDirty(false)
26 PrimaryComponentTick.bCanEverTick =
true;
27 SetIsReplicatedByDefault(
true);
28 SetGenerateOverlapEvents(
true);
31void UIVSmokeHoleGeneratorComponent::PostInitProperties()
33 Super::PostInitProperties();
36 SetCollisionEnabled(ECollisionEnabled::QueryOnly);
37 SetCollisionResponseToAllChannels(ECR_Overlap);
40void UIVSmokeHoleGeneratorComponent::BeginPlay()
49 if (ActiveHoles.
Num() > 0)
55 Local_InitializeHoleTexture();
59void UIVSmokeHoleGeneratorComponent::TickComponent(
const float DeltaTime,
const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
61 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
64 if (GetOwner()->HasAuthority())
66 Authority_CleanupExpiredHoles();
67 Authority_UpdateDynamicSubjectList();
74 if (ActiveHoles.
Num() > 0)
81 if (bHoleTextureDirty)
83 if (ActiveHoles.
Num() > 0)
85 Local_RebuildHoleTexture();
89 Local_ClearHoleTexture();
96void UIVSmokeHoleGeneratorComponent::EndPlay(
const EEndPlayReason::Type EndPlayReason)
98 Super::EndPlay(EndPlayReason);
101void UIVSmokeHoleGeneratorComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps)
const
103 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
116 DynamicSubjectList.Empty();
120 Local_ClearHoleTexture();
135 UE_LOG(LogIVSmoke, Warning, TEXT(
"[CreatePenetrationHole] Invalid PresetID: %d"), PresetID);
139 if (Preset->Duration <= 0.0f)
141 UE_LOG(LogIVSmoke, Warning, TEXT(
"[CreatePenetrationHole] Invalid Lifetime: %f"), Preset->Duration);
145 if (Preset->HoleType != EIVSmokeHoleType::Penetration)
147 UE_LOG(LogIVSmoke, Warning, TEXT(
"[CreatePenetrationHole] Preset is not Penetration type"));
152 FVector3f EntryPoint, ExitPoint;
153 if (!Authority_CalculatePenetrationPoints(InOrigin, InDirection, Preset->BulletThickness, EntryPoint, ExitPoint))
164 Authority_CreateHole(HoleData);
172 UE_LOG(LogIVSmoke, Warning, TEXT(
"[CreateExplosionHole] Invalid PresetID: %d"), PresetID);
176 if (Preset->Duration <= 0.0f)
178 UE_LOG(LogIVSmoke, Warning, TEXT(
"[CreateExplosionHole] Invalid Lifetime: %f"), Preset->Duration);
182 if (Preset->HoleType != EIVSmokeHoleType::Explosion)
184 UE_LOG(LogIVSmoke, Warning, TEXT(
"[CreateExplosionHole] Preset is not Explosion type"));
189 const FBox3f VolumeBox = FBox3f(Bounds.GetBox());
190 const FVector3f ExpandedMin = VolumeBox.Min - FVector3f(Preset->Radius);
191 const FVector3f ExpandedMax = VolumeBox.Max + FVector3f(Preset->Radius);
192 if (
const FBox3f ExpandedBox(ExpandedMin, ExpandedMax); !ExpandedBox.IsInside(Origin))
203 Authority_CreateHole(HoleData);
211 UE_LOG(LogIVSmoke, Warning, TEXT(
"[RegisterTrackDynamicHole] Invalid PresetID: %d"), PresetID);
215 if (Preset->Duration <= 0.0f)
217 UE_LOG(LogIVSmoke, Warning, TEXT(
"[RegisterTrackDynamicHole] Invalid Duration: %f"), Preset->Duration);
221 if (Preset->HoleType != EIVSmokeHoleType::Dynamic)
223 UE_LOG(LogIVSmoke, Warning, TEXT(
"[RegisterTrackDynamicHole] Preset is not Dynamic type"));
230 if (Tracker.TargetActor == TargetActor)
232 UE_LOG(LogIVSmoke, Warning, TEXT(
"[RegisterTrackDynamicHole] Actor already registered"));
240 NewDynamicSubject.
PresetID = PresetID;
241 NewDynamicSubject.
LastWorldPosition = FVector3f(TargetActor->GetActorLocation());
243 DynamicSubjectList.Add(NewDynamicSubject);
249#pragma region Authority Only
250void UIVSmokeHoleGeneratorComponent::Authority_CreateHole(
const FIVSmokeHoleData& HoleData)
258 int32 OldestIndex = 0;
259 float MinTime = FLT_MAX;
261 for (int32 i = 0; i < ActiveHoles.
Num(); ++i)
263 if (ActiveHoles[i].ExpirationServerTime < MinTime)
265 MinTime = ActiveHoles[i].ExpirationServerTime;
275 ActiveHoles.MarkItemDirty(Target);
281void UIVSmokeHoleGeneratorComponent::Authority_CleanupExpiredHoles()
285 for (int32 i = ActiveHoles.
Num() - 1; i >= 0; --i)
287 if (ActiveHoles[i].IsExpired(CurrentServerTime))
295bool UIVSmokeHoleGeneratorComponent::Authority_CalculatePenetrationPoints(
296 const FVector3f& Origin,
const FVector3f& Direction,
const float BulletThickness, FVector3f& OutEntry, FVector3f& OutExit)
298 FVector3f NormalizedDirection = Direction.GetSafeNormal();
299 if (NormalizedDirection.IsNearlyZero())
301 UE_LOG(LogIVSmoke, Warning, TEXT(
"[CalculatePenetrationPoints] Direction is zero"));
305 const float DistToCenter = FVector3f::Dist(Origin, FVector3f(GetComponentLocation()));
306 const float DiagonalLength = GetScaledBoxExtent().Size() * 2.0f;
307 const float MaxDistance = DistToCenter + DiagonalLength;
309 const FVector3f RayEnd = Origin + NormalizedDirection * MaxDistance;
311 FHitResult HitEntry, HitExit;
312 FCollisionQueryParams QueryParams;
313 QueryParams.bTraceComplex =
false;
316 if (!LineTraceComponent(HitEntry, FVector(Origin), FVector(RayEnd), QueryParams))
322 if (!LineTraceComponent(HitExit, FVector(RayEnd), FVector(Origin), QueryParams))
324 OutExit = FVector3f(HitEntry.Location);
328 OutExit = FVector3f(HitExit.Location);
331 OutEntry = FVector3f(HitEntry.Location);
336 TArray<FHitResult> HitResults;
337 FCollisionQueryParams WorldParams;
338 WorldParams.AddIgnoredComponent(
this);
339 const FCollisionShape SweepShape = FCollisionShape::MakeSphere(BulletThickness);
342 if (GetWorld()->SweepMultiByObjectType(
343 HitResults, FVector(OutEntry), FVector(OutExit), FQuat::Identity,
344 ObjectParams, SweepShape, WorldParams))
346 for (
const FHitResult& Hit : HitResults)
348 if (AActor* HitActor = Hit.GetActor())
350 if (!HitActor->ActorHasTag(IVSmokeVoxelVolumeTag))
352 OutExit = FVector3f(Hit.Location);
363void UIVSmokeHoleGeneratorComponent::Authority_UpdateDynamicSubjectList()
366 const FBox3f SmokeVolume = FBox3f(Bounds.GetBox());
368 for (int32 i = DynamicSubjectList.Num() - 1; i >= 0; --i)
375 DynamicSubjectList.RemoveAtSwap(i);
379 const TObjectPtr<AActor> Actor = DynamicSubject.
TargetActor.Get();
385 DynamicSubjectList.RemoveAtSwap(i);
389 const FVector3f CurrentPos = FVector3f(Actor->GetActorLocation());
392 if (!SmokeVolume.IsInside(CurrentPos))
398 if (Preset->DistanceThreshold > FVector3f::Dist(CurrentPos, LastPos))
409 Authority_CreateHole(HoleData);
420#pragma region Local Only
422void UIVSmokeHoleGeneratorComponent::Local_InitializeHoleTexture()
430 HoleTexture = NewObject<UTextureRenderTargetVolume>(
this, TEXT(
"HoleTexture"));
431 HoleTexture->bCanCreateUAV =
true;
433 HoleTexture->ClearColor = FLinearColor::White;
434 HoleTexture->SRGB =
false;
435 HoleTexture->UpdateResourceImmediate(
true);
438void UIVSmokeHoleGeneratorComponent::Local_ClearHoleTexture()
445 FTextureRenderTargetResource* RenderTargetResource = HoleTexture->GameThread_GetRenderTargetResource();
446 if (!RenderTargetResource)
451 FTextureRHIRef Texture = RenderTargetResource->GetRenderTargetTexture();
452 if (!Texture.IsValid())
457 ENQUEUE_RENDER_COMMAND(IVSmokeHoleClear)(
458 [Texture](FRHICommandListImmediate& RHICmdList)
460 FRDGBuilder GraphBuilder(RHICmdList);
462 const FRDGTextureRef RDGTexture = GraphBuilder.RegisterExternalTexture(
463 CreateRenderTarget(Texture, TEXT(
"IVSmokeHoleTextureClear"))
467 AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(RDGTexture), FVector4f(1.0f, 1.0f, 1.0f, 1.0f));
469 GraphBuilder.Execute();
474void UIVSmokeHoleGeneratorComponent::Local_RebuildHoleTexture()
476 if (IsRunningCommandlet())
487 if (SyncedTime == 0.0f)
496 Local_InitializeHoleTexture();
500 const FTextureRenderTargetResource* RenderTargetResource = HoleTexture->GameThread_GetRenderTargetResource();
501 if (!RenderTargetResource)
506 TArray<FIVSmokeHoleGPU> GPUHoles = ActiveHoles.
GetHoleGPUData(SyncedTime);
508 const TObjectPtr<AIVSmokeVoxelVolume> VoxelVolume = Cast<AIVSmokeVoxelVolume>(GetOwner());
509 if (VoxelVolume ==
nullptr)
514 FTextureRHIRef Texture = RenderTargetResource->GetRenderTargetTexture();
515 if (!Texture.IsValid())
520 const FVector3f WorldVolumeMin = FVector3f(VoxelVolume->GetVoxelWorldAABBMin());
521 const FVector3f WorldVolumeMax = FVector3f(VoxelVolume->GetVoxelWorldAABBMax());
523 const int32 NumHoles = ActiveHoles.
Num();
524 const int32 CapturedBlurStep =
BlurStep;
541 ENQUEUE_RENDER_COMMAND(IVSmokeHoleCarveFullRebuild)(
542 [Texture, GPUHoles = MoveTemp(GPUHoles), WorldVolumeMin, WorldVolumeMax, Resolution, NumHoles, CapturedBlurStep,
543 PenetrationNoiseTextureRHI, ExplosionNoiseTextureRHI, DynamicNoiseTextureRHI,
544 CapturedPenetrationNoiseStrength, CapturedPenetrationNoiseScale,
545 CapturedExplosionNoiseStrength, CapturedExplosionNoiseScale,
546 CapturedDynamicNoiseStrength, CapturedDynamicNoiseScale]
547 (FRHICommandListImmediate& RHICmdList)
549 FRDGBuilder GraphBuilder(RHICmdList);
551 const FRDGTextureRef RDGTexture = GraphBuilder.RegisterExternalTexture(
552 CreateRenderTarget(Texture, TEXT(
"IVSmokeHoleTexture"))
555 const FRDGBufferRef HoleBuffer = CreateStructuredBuffer(
557 TEXT(
"IVSmokeHoleBuffer"),
567 FIVSmokeHoleCarveCS::FParameters* CarveParameters = GraphBuilder.AllocParameters<FIVSmokeHoleCarveCS::FParameters>();
568 CarveParameters->VolumeTexture = GraphBuilder.CreateUAV(RDGTexture);
569 CarveParameters->HoleBuffer = GraphBuilder.CreateSRV(HoleBuffer);
570 CarveParameters->VolumeMin = WorldVolumeMin;
571 CarveParameters->VolumeMax = WorldVolumeMax;
572 CarveParameters->Resolution = Resolution;
573 CarveParameters->NumHoles = NumHoles;
576 CarveParameters->PenetrationNoiseTexture = PenetrationNoiseTextureRHI ? PenetrationNoiseTextureRHI : GWhiteTexture->TextureRHI;
577 CarveParameters->ExplosionNoiseTexture = ExplosionNoiseTextureRHI ? ExplosionNoiseTextureRHI : GWhiteTexture->TextureRHI;
578 CarveParameters->DynamicNoiseTexture = DynamicNoiseTextureRHI ? DynamicNoiseTextureRHI : GWhiteTexture->TextureRHI;
579 CarveParameters->NoiseSampler = TStaticSamplerState<SF_Bilinear, AM_Wrap, AM_Wrap, AM_Wrap>::GetRHI();
582 CarveParameters->PenetrationNoiseStrength = CapturedPenetrationNoiseStrength;
583 CarveParameters->PenetrationNoiseScale = CapturedPenetrationNoiseScale;
584 CarveParameters->ExplosionNoiseStrength = CapturedExplosionNoiseStrength;
585 CarveParameters->ExplosionNoiseScale = CapturedExplosionNoiseScale;
586 CarveParameters->DynamicNoiseStrength = CapturedDynamicNoiseStrength;
587 CarveParameters->DynamicNoiseScale = CapturedDynamicNoiseScale;
589 const TShaderMapRef<FIVSmokeHoleCarveCS> CarveShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
595 if (CapturedBlurStep > 0)
598 const FRDGTextureDesc BlurTexDesc = FRDGTextureDesc::Create3D(
599 FIntVector(Resolution.X, Resolution.Y, Resolution.Z),
601 FClearValueBinding::Black,
602 TexCreate_ShaderResource | TexCreate_UAV
604 const FRDGTextureRef BlurTempTexture = GraphBuilder.CreateTexture(BlurTexDesc, TEXT(
"IVSmokeHoleBlurTemp"));
606 const TShaderMapRef<FIVSmokeHoleBlurCS> BlurShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
607 FRHISamplerState* LinearClampSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
610 const FIntVector BlurDirections[3] = {
616 const FRDGTextureRef PingPong[2] = { RDGTexture, BlurTempTexture };
617 int32 CurrentInput = 0;
619 for (int32 i = 0; i < 3; ++i)
621 FIVSmokeHoleBlurCS::FParameters* BlurParameters = GraphBuilder.AllocParameters<FIVSmokeHoleBlurCS::FParameters>();
622 BlurParameters->InputTexture = GraphBuilder.CreateSRV(PingPong[CurrentInput]);
623 BlurParameters->InputSampler = LinearClampSampler;
624 BlurParameters->OutputTexture = GraphBuilder.CreateUAV(PingPong[1 - CurrentInput]);
625 BlurParameters->Resolution = Resolution;
626 BlurParameters->BlurDirection = BlurDirections[i];
627 BlurParameters->BlurStep = CapturedBlurStep;
630 GetGlobalShaderMap(GMaxRHIFeatureLevel), BlurShader, BlurParameters, Resolution);
632 CurrentInput = 1 - CurrentInput;
636 if (CurrentInput == 1)
638 AddCopyTexturePass(GraphBuilder, BlurTempTexture, RDGTexture, FRHICopyTextureInfo());
642 GraphBuilder.Execute();
654 const UWorld* World = GetWorld();
660 if (World->GetNetMode() == NM_Client)
662 if (
const AGameStateBase* GameState = World->GetGameState())
664 return GameState->GetServerWorldTimeSeconds();
668 return World->GetTimeSeconds();
674 if (
const TObjectPtr<AIVSmokeVoxelVolume> VoxelVolume = Cast<AIVSmokeVoxelVolume>(GetOwner()))
676 const FVector WorldVoxelAABBMin = VoxelVolume->GetVoxelWorldAABBMin();
677 const FVector WorldVoxelAABBMax = VoxelVolume->GetVoxelWorldAABBMax();
678 const FVector Extent = (WorldVoxelAABBMax - WorldVoxelAABBMin) * 0.5f;
679 const FVector WorldVoxelCenter = (WorldVoxelAABBMax + WorldVoxelAABBMin) * 0.5f;
681 SetWorldLocation(WorldVoxelCenter);
682 SetBoxExtent(Extent,
false);
690 if (
const FTextureRenderTargetResource* Resource = HoleTexture->GameThread_GetRenderTargetResource())
692 return Resource->GetRenderTargetTexture();
static void AddComputeShaderPass(FRDGBuilder &GraphBuilder, FGlobalShaderMap *ShaderMap, TShaderMapRef< TShaderClass > ComputeShader, typename TShaderClass::FParameters *Parameters, const FIntVector &TotalThreadSize)
Component that generates hole texture for volumetric smoke. Provides public API for penetration and e...
FIVSmokeHoleNoiseSettings ExplosionNoise
void CreateExplosionHole(const FVector3f &Origin, const uint8 PresetID)
float GetSyncedTime() const
TArray< TEnumAsByte< EObjectTypeQuery > > ObstacleObjectTypes
FTextureRHIRef GetHoleTextureRHI() const
void CreatePenetrationHole(const FVector3f &Origin, const FVector3f &Direction, const uint8 PresetID)
FIVSmokeHoleNoiseSettings DynamicNoise
FIntVector VoxelResolution
FORCEINLINE void MarkHoleTextureDirty(const bool bIsDirty=true)
void RegisterTrackDynamicHole(AActor *TargetActor, const uint8 PresetID)
FIVSmokeHoleNoiseSettings PenetrationNoise
static TObjectPtr< UIVSmokeHolePreset > FindByID(const uint8 InPresetID)
void RemoveAtSwap(const int32 Index)
void AddHole(const FIVSmokeHoleData &NewHole)
FORCEINLINE void Reserve(const int32 Number)
TArray< FIVSmokeHoleGPU > GetHoleGPUData(const float CurrentServerTime) const
TObjectPtr< UIVSmokeHoleGeneratorComponent > OwnerComponent
FORCEINLINE int32 Num() const
Network-optimized hole data structure.
float ExpirationServerTime
Dynamic hole generated type data structure.
FVector3f LastWorldPosition
TWeakObjectPtr< AActor > TargetActor
Built from FIVSmokeHoleData + UIVSmokeHolePreset at render time.
TObjectPtr< UTexture2D > Texture