IVSmoke 1.0
Loading...
Searching...
No Matches
IVSmokeRenderer.h
1// Copyright (c) 2026, Team SDB. All rights reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "ScreenPass.h"
7#include "SceneTexturesConfig.h"
8#include "IVSmoke.h"
9#include "IVSmokeShaders.h"
10#include "SceneView.h"
11#include "IVSmokeSettings.h"
12#include "IVSmokeVisualMaterialPreset.h"
13
15class FRDGBuilder;
16class FSceneView;
18class UTextureRenderTargetVolume;
19struct FPostProcessMaterialInputs;
23
24//~==============================================================================
25// Internal Noise Generation Constants
26
27/**
28 * Internal noise generation configuration.
29 * These values are tuned for optimal smoke appearance and should not be exposed to users.
30 */
32{
33 static constexpr int32 Seed = 0;
34 static constexpr int32 TexSize = 128;
35 static constexpr int32 Octaves = 6;
36 static constexpr float Wrap = 0.76f;
37 static constexpr float Amplitude = 0.62f;
38 static constexpr int32 AxisCellCount = 4;
39 static constexpr int32 CellSize = 32;
40
41 /** UV multiplier for noise sampling. Fixed at 1.0; use SmokeSize to control detail frequency. */
42 static constexpr float NoiseUVMul = 1.0f;
43};
44
45//~==============================================================================
46// Render Data Structures (Thread-Safe Data Transfer)
47
48/**
49 * Packed render data for all smoke volumes.
50 * Created on Game Thread, consumed on Render Thread.
51 * Contains all data needed for rendering without accessing Volume actors.
52 *
53 * THREAD SAFETY:
54 * - Created on Game Thread in PrepareRenderData()
55 * - Transferred to Render Thread via ENQUEUE_RENDER_COMMAND
56 * - Consumed on Render Thread in Render() and RunPrePassPipeline()
57 *
58 * DATA SAFETY WARNING:
59 * Some fields are REFERENCE-BASED and tied to UObject lifetime.
60 * See comments below for dangerous vs safe fields.
61 */
62struct IVSMOKE_API FIVSmokePackedRenderData
63{
64 //~==========================================================================
65 // Safe Fields (Value-Copied)
66 // These are independent of source UObject lifetime.
67
68 /** Packed voxel birth times for all volumes (flattened by VoxelBufferOffset). */
69 TArray<float> PackedVoxelBirthTimes;
70
71 /** Packed voxel death times for all volumes (flattened by VoxelBufferOffset). */
72 TArray<float> PackedVoxelDeathTimes;
73
74 /** Per-volume GPU metadata */
75 TArray<FIVSmokeVolumeGPUData> VolumeDataArray;
76
77 /** Common resolution info */
78 FIntVector VoxelResolution = FIntVector::ZeroValue;
79 FIntVector HoleResolution = FIntVector::ZeroValue;
80 int32 VolumeCount = 0;
81
82 /** Preset parameters (copied from default preset) */
83 int32 MaxSteps = 128;
84 float GlobalAbsorption = 0.1f;
85 float SmokeSize = 128.0f;
86 float SmokeDensityFalloff = 0.2f;
87 FVector WindDirection = FVector(0.01f, 0.02f, 0.1f);
88 float VolumeRangeOffset = 0.1f;
89 float VolumeEdgeNoiseFadeOffset = 0.04f;
90 float VolumeEdgeFadeSharpness = 3.5f;
91
92 /** Scattering parameters */
93 bool bEnableScattering = true;
94 float ScatterScale = 0.5f;
95 float ScatteringAnisotropy = 0.5f;
96 FVector LightDirection = FVector(0.2f, 0.1f, 0.9f);
97 FLinearColor LightColor = FLinearColor::White;
98 float LightIntensity = 1.0f;
99
100 /** Self-shadowing parameters */
101 bool bEnableSelfShadowing = true;
102 int32 LightMarchingSteps = 6;
103 float LightMarchingDistance = 0.0f;
104 float LightMarchingExpFactor = 2.0f;
105 float ShadowAmbient = 0.2f;
106
107 /** External shadowing parameters (CSM - Cascaded Shadow Maps) */
108 /** Note: CSM is always used when external shadowing is enabled */
109 int32 NumCascades = 0;
110 TArray<FMatrix> CSMViewProjectionMatrices;
111 TArray<float> CSMSplitDistances;
112 TArray<FVector> CSMLightCameraPositions;
113 TArray<FVector> CSMLightCameraForwards;
114 float CascadeBlendRange = 0.1f;
115 float ShadowDepthBias = 1.0f;
116 float ExternalShadowAmbient = 0.3f;
117
118 /** VSM parameters */
119 bool bEnableVSM = true;
120 float VSMMinVariance = 0.0001f;
121 float VSMLightBleedingReduction = 0.2f;
122
123 /** Main camera position for CSM (must match what CSMRenderer used) */
124 FVector CSMMainCameraPosition = FVector::ZeroVector;
125
126 /** Validity flag */
127 bool bIsValid = false;
128
129 /** Game World Time */
130 float GameTime = 0.0f;
131
132 /** Rendering Info (safe - copied values) */
133 int UpSampleFilterType = 2;
134 float SharpenStrength = 0.0f;
135 float BlurStrength = 0.4f;
136
137 //~==========================================================================
138 // DANGEROUS: Reference-Based Fields
139 // WARNING: These fields hold references to resources owned by UObjects.
140 // They become INVALID when the owning UObject is destroyed.
141 // ALWAYS call FlushRenderingCommands() before destroying source UObjects.
142
143 /** Hole Texture references - owned by UIVSmokeHoleGeneratorComponent */
144 TArray<FTextureRHIRef> HoleTextures;
145 TArray<FIntVector> HoleTextureSizes;
146
147 /** CSM Texture references - owned by FIVSmokeCSMRenderer (per-world) */
148 TArray<FTextureRHIRef> CSMDepthTextures;
149 TArray<FTextureRHIRef> CSMVSMTextures;
150
151 /** Material reference - owned by UIVSmokeVisualMaterialPreset asset */
152 UMaterialInterface* SmokeVisualMaterial = nullptr;
153
154 //~==========================================================================
155
156 /** Reset to invalid state */
157 void Reset()
158 {
159 PackedVoxelBirthTimes.Empty();
160 PackedVoxelDeathTimes.Empty();
161 VolumeDataArray.Empty();
162 HoleTextures.Empty();
163 HoleTextureSizes.Empty();
164 VolumeCount = 0;
165 bIsValid = false;
166
167 // CSM
168 NumCascades = 0;
169 CSMDepthTextures.Empty();
170 CSMVSMTextures.Empty();
171 CSMViewProjectionMatrices.Empty();
172 CSMSplitDistances.Empty();
173 CSMLightCameraPositions.Empty();
174 CSMLightCameraForwards.Empty();
175
176 SmokeVisualMaterial = nullptr;
177 }
178};
179
180//~==============================================================================
181// Per-World Data Structure
182
183/**
184 * Per-World rendering data and resources.
185 * Each UWorld (Editor, PIE, Standalone) has its own instance.
186 *
187 * This enables simultaneous rendering of multiple worlds without data conflicts.
188 * Owned by FIVSmokeRenderer::WorldDataMap.
189 *
190 * LIFECYCLE:
191 * - Created on first render request for a World (lazy initialization)
192 * - Destroyed when World ends (via OnWorldBeginTearDown delegate)
193 *
194 * THREAD SAFETY:
195 * - Access must be protected by FIVSmokeRenderer::WorldDataMutex
196 */
197struct IVSMOKE_API FPerWorldData
198{
200 ~FPerWorldData(); // Defined in cpp - required for TUniquePtr with forward-declared types
201
202 // Non-copyable, non-movable (managed via TSharedPtr)
203 FPerWorldData(const FPerWorldData&) = delete;
204 FPerWorldData& operator=(const FPerWorldData&) = delete;
205 FPerWorldData(FPerWorldData&&) = delete;
206 FPerWorldData& operator=(FPerWorldData&&) = delete;
207
208 /** Cached render data for Render Thread. Set via SetCachedRenderData from ENQUEUE_RENDER_COMMAND. */
210
211 /** Cached render data for Game Thread. Used for frame deduplication when multiple ViewFamilies exist. */
213
214 /** CSM renderer for external shadowing (per-world). */
215 TUniquePtr<FIVSmokeCSMRenderer> CSMRenderer;
216
217 /** VSM processor for variance shadow mapping (per-world). */
218 TUniquePtr<FIVSmokeVSMProcessor> VSMProcessor;
219
220 /** Frame number when CSM was last updated (prevents multiple updates per frame). */
221 uint32 LastCSMUpdateFrameNumber = 0;
222
223 /** Frame number when VSM was last processed (prevents duplicate processing per view). */
224 uint32 LastVSMProcessFrameNumber = 0;
225
226 /** True if server time offset has been initialized for this World. */
227 bool bServerTimeSynced = false;
228
229 /** Server time offset for animation sync. (ServerTime = LocalTime + Offset) */
230 float ServerTimeOffset = 0.f;
231
232 /** Frame number when PrepareRenderData was last called (prevents duplicate calls per frame). */
233 uint32 LastPreparedFrameNumber = 0;
234
235 /** Reset all data to initial state. Does NOT cleanup CSM (call CleanupCSM separately). */
236 void Reset()
237 {
238 CachedRenderData.Reset();
239 LastCSMUpdateFrameNumber = 0;
240 LastVSMProcessFrameNumber = 0;
241 bServerTimeSynced = false;
242 ServerTimeOffset = 0.f;
243 LastPreparedFrameNumber = 0;
244 }
245
246 /** Cleanup CSM resources. Must be called before destruction. Defined in cpp file. */
247 void CleanupCSM();
248};
249
250//~==============================================================================
251// Renderer
252
253/**
254 * Manages registered smoke volumes and handles rendering.
255 * Owns shared rendering resources (noise volume) and reads settings from UIVSmokeSettings.
256 *
257 * ARCHITECTURE: Per-World Data
258 * - Each UWorld has its own FPerWorldData instance stored in WorldDataMap
259 * - Enables simultaneous rendering of Editor and PIE worlds
260 * - World lifecycle managed via OnWorldBeginTearDown delegate
261 *
262 * See Docs/IVSmoke_RendererArchitecture.md for detailed documentation.
263 */
264class IVSMOKE_API FIVSmokeRenderer
265{
266public:
267 static FIVSmokeRenderer& Get();
268
269 //~==============================================================================
270 // Lifecycle
271
272 /** Initialize renderer resources. Called on first use or settings change. */
273 void Initialize();
274
275 /** Release renderer resources. */
276 void Shutdown();
277
278 /** Check if renderer is initialized with valid resources. */
279 bool IsInitialized() const { return NoiseVolume != nullptr; }
280
281 //~==============================================================================
282 // Per-World Data Management
283
284 /**
285 * Get or create per-world data for a specific World.
286 * Creates new FPerWorldData if not exists and registers cleanup delegate.
287 * Must be called on Game Thread.
288 *
289 * @param World The world to get data for
290 * @return Shared pointer to world data (never null for valid World)
291 */
292 TSharedPtr<FPerWorldData> GetOrCreateWorldData(UWorld* World);
293
294 /**
295 * Get per-world data for a specific World (read-only).
296 * Returns nullptr if World is not registered.
297 * Thread-safe (uses mutex internally).
298 *
299 * @param World The world to get data for
300 * @return Shared pointer to world data, or nullptr if not found
301 */
302 TSharedPtr<FPerWorldData> GetWorldData(UWorld* World);
303
304 /**
305 * Cleanup and remove per-world data.
306 * Called automatically when World is destroyed (via delegate).
307 * IMPORTANT: Calls FlushRenderingCommands() to ensure GPU safety.
308 *
309 * @param World The world to cleanup
310 */
311 void CleanupWorldData(UWorld* World);
312
313 /** Check if server time offset was set for a World. */
314 bool bIsServerTimeSynced(UWorld* World);
315
316 /** Set server time offset for smoke wind animation for a specific World. */
317 void SetServerTimeOffset(UWorld* World, float InServerTimeOffset);
318
319 //~==============================================================================
320 // Thread-Safe Render Data (Game Thread <-> Render Thread)
321
322 /** Maximum number of volumes supported for rendering. */
323 static constexpr int32 MaxSupportedVolumes = 128;
324
325 /**
326 * Prepare render data from all registered volumes for a specific World.
327 * Must be called on Game Thread.
328 * Copies and packs all volume data for safe Render Thread access.
329 * If volume count exceeds MaxSupportedVolumes (128), filters by distance from camera.
330 * Skips processing if already called this frame (uses LastPreparedFrameNumber).
331 *
332 * @param World The world these volumes belong to
333 * @param InVolumes Array of volumes to process
334 * @param CameraPosition Camera world position for distance-based filtering
335 * @return Packed render data ready for Render Thread
336 */
337 FIVSmokePackedRenderData PrepareRenderData(UWorld* World, const TArray<AIVSmokeVoxelVolume*>& InVolumes, const FVector& CameraPosition);
338
339 /**
340 * Set cached render data for a specific World.
341 * Called from Render Thread via ENQUEUE_RENDER_COMMAND.
342 *
343 * @param World The world to set data for
344 * @param InRenderData The render data to cache
345 */
346 void SetCachedRenderData(UWorld* World, FIVSmokePackedRenderData&& InRenderData);
347
348 //~==============================================================================
349 // Rendering
350
351 /**
352 * Main render entry point called from SceneViewExtension.
353 *
354 * @param GraphBuilder RDG builder
355 * @param View Current scene view
356 * @param Inputs Post-process material inputs
357 * @return Output texture after smoke rendering
358 */
359 FScreenPassTexture Render(
360 FRDGBuilder& GraphBuilder,
361 const FSceneView& View,
362 const FPostProcessMaterialInputs& Inputs
363 );
364
365 //~==============================================================================
366 // Pre-Pass Pipeline (Public API for SceneViewExtension)
367
368 /**
369 * Execute Pre-pass pipeline: Ray March → Upscale → UpsampleFilter → Depth Write.
370 * Called from PostRenderBasePassDeferred_RenderThread BEFORE Translucent Pass.
371 * Results are cached for Post-process Visual/Composite passes.
372 *
373 * Pipeline order ensures Ray March reads only opaque depth (before smoke depth write).
374 *
375 * @param GraphBuilder RDG builder
376 * @param View Current scene view
377 * @param RenderTargets Render target slots (for depth write)
378 * @param SceneTextures Scene texture uniform buffer (for depth sampling in ray march)
379 */
380 void RunPrePassPipeline(
381 FRDGBuilder& GraphBuilder,
382 const FSceneView& View,
383 const struct FRenderTargetBindingSlots& RenderTargets,
384 TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures
385 );
386
387private:
388 FIVSmokeRenderer(); // Defined in cpp for TUniquePtr with forward-declared types
390
391 FIntVector GetAtlasTexCount(const FIntVector& TexSize, const int32 TexCount, const int32 TexturePackInterval, const int32 TexturePackMaxSize) const;
392 //~==============================================================================
393 // Resource Management
394
395 /** Create noise volume texture using settings from UIVSmokeSettings. */
396 void CreateNoiseVolume();
397
398 /** Get the effective preset for a volume (override or default). */
399 const UIVSmokeSmokePreset* GetEffectivePreset(const AIVSmokeVoxelVolume* Volume) const;
400
401 //~==============================================================================
402 // Pass Functions
403
404 /**
405 * Multi-Volume Ray Marching with Occupancy Optimization (Three-Pass Pipeline).
406 * Uses tile-based occupancy grid for efficient empty space skipping.
407 * Processes all volumes with correct Beer-Lambert integration.
408 * Outputs to Dual Render Targets (Albedo + Mask) at reduced resolution.
409 *
410 * Pipeline:
411 * Pass 0: Tile Setup - Compute per-tile depth range and quick volume mask
412 * Pass 1: Occupancy Build - Build View and Light occupancy 3D textures
413 * Pass 2: Ray March - Use occupancy for efficient skipping
414 *
415 * @param GraphBuilder RDG builder
416 * @param View Current scene view
417 * @param RenderData Pre-packed render data (created on Game Thread)
418 * @param SmokeAlbedoTex UAV texture for smoke color output
419 * @param SmokeMaskTex UAV texture for smoke opacity mask
420 * @param TexSize Size of output textures (may be reduced resolution)
421 * @param ViewportSize Size of the full viewport for depth sampling
422 * @param ViewRectMin Offset into full scene texture for depth sampling
423 */
424 /**
425 * @param SceneDepthForDependency Optional: Pass SceneDepth texture to create explicit RDG dependency.
426 * In Pre-pass, this ensures Ray March reads SceneDepth BEFORE Depth Write modifies it.
427 * Use RenderTargets.DepthStencil.GetTexture() when calling from Pre-pass.
428 */
429 void AddMultiVolumeRayMarchPass(
430 FRDGBuilder& GraphBuilder,
431 const FSceneView& View,
432 const FIVSmokePackedRenderData& RenderData,
433 FRDGTextureRef SmokeAlbedoTex,
434 FRDGTextureRef SmokeLocalPosAlpha,
435 FRDGTextureRef SmokeWorldPosDepth,
436 const FIntPoint& TexSize,
437 const FIntPoint& ViewportSize,
438 const FIntPoint& ViewRectMin,
439 FRDGTextureRef SceneDepthForDependency = nullptr
440 );
441
442 /**
443 * Copy/Resize Pass using bilinear sampling.
444 * Used for upscaling (1/2 resolution to Full) to improve quality.
445 *
446 * @param GraphBuilder RDG builder
447 * @param View Current scene view
448 * @param SourceTex Source texture to copy from
449 * @param DestSize Destination texture size (resizes via bilinear)
450 * @param TexName Debug name for the created texture
451 * @return New texture at DestSize with copied content
452 */
453 FRDGTextureRef AddCopyPass(
454 FRDGBuilder& GraphBuilder,
455 const FSceneView& View,
456 FRDGTextureRef SourceTex,
457 const FIntPoint& DestSize,
458 const TCHAR* TexName
459 );
460
461 /**
462 * Copy Pass to existing texture.
463 * Copies SourceTex to DestTex using bilinear sampling.
464 *
465 * @param GraphBuilder RDG builder
466 * @param View Current scene view
467 * @param SourceTex Source texture to copy from
468 * @param DestTex Destination texture to copy to
469 */
470 void AddCopyPass(
471 FRDGBuilder& GraphBuilder,
472 const FSceneView& View,
473 FRDGTextureRef SourceTex,
474 FRDGTextureRef DestTex
475 );
476
477 /**
478 * Filtering pass after upsampling.
479 *
480 * @param GraphBuilder RDG builder
481 * @param RenderData RenderData ref
482 * @param View Current scene view
483 * @param SceneTex SceneColor texture
484 * @param SmokeAlbedo SmokeAlbedo texture from ray marching
485 * @param SmokeLocalPosAlpha Smoke (local position, alpha) texture from ray marching
486 * @param TexSize Output texture size
487 * @param ViewRectMin ViewRect offset for UV calculation
488 */
489 FRDGTextureRef AddUpsampleFilterPass(
490 FRDGBuilder& GraphBuilder,
491 const FIVSmokePackedRenderData& RenderData,
492 const FSceneView& View,
493 FRDGTextureRef SceneTex,
494 FRDGTextureRef SmokeAlbedo,
495 FRDGTextureRef SmokeLocalPosAlpha,
496 const FIntPoint& TexSize,
497 const FIntPoint& ViewRectMin
498 );
499
500 /**
501 * Visual pass after Upsample Filtering.
502 *
503 * @param GraphBuilder RDG builder
504 * @param RenderData RenderData ref
505 * @param View Current scene view
506 * @param SmokeTex Smoke texture after Upsample filter pass
507 * @param SmokeLocalPosAlphaTex Smoke (local position, alpha) texture from ray marching
508 * @param SmokeWorldPosDepthTex Smoke (world position, linear depth) texture from ray marching
509 * @param SceneTex, Scene texture
510 * @param TexSize Output texture size
511 */
512 FRDGTextureRef AddSmokeVisualPass(FRDGBuilder& GraphBuilder, const FIVSmokePackedRenderData& RenderData, const FSceneView& View, FRDGTextureRef SmokeTex, FRDGTextureRef SmokeLocalPosAlphaTex, FRDGTextureRef SmokeWorldPosDepthTex, FRDGTextureRef SceneTex, const FIntPoint& TexSize);
513
514 /**
515 * Composite PS Pass.
516 * Blends ray marching result (Dual RT) with scene color and applies sharpening/blurring.
517 *
518 * @param GraphBuilder RDG builder
519 * @param RenderData RenderData ref
520 * @param View Current scene view
521 * @param SceneTex Scene color texture
522 * @param SmokeTex Smoke texture after Upsample filter pass
523 * @param Output Final render target
524 * @param ViewportSize Size of the viewport for UV calculation
525 */
526 void AddCompositePass(
527 FRDGBuilder& GraphBuilder,
528 const FIVSmokePackedRenderData& RenderData,
529 const FSceneView& View,
530 FRDGTextureRef SceneTex,
531 FRDGTextureRef SmokeVisualTex,
532 const FScreenPassRenderTarget& Output,
533 const FIntPoint& ViewportSize);
534
535 //~==============================================================================
536 // Pre-Pass Pipeline (Internal Helpers)
537
538 /**
539 * Check if the view is a primary game view (not a capture or reflection).
540 * Used to filter out Scene Capture, Planar Reflection, etc. for performance.
541 *
542 * @param View Scene view to check
543 * @return true if this is a primary game view that should render smoke
544 */
545 static bool IsPrimaryGameView(const FSceneView& View);
546
547 /**
548 * Execute depth write pass using current frame's ray march results.
549 * Called after ray march in Pre-pass pipeline.
550 *
551 * @param GraphBuilder RDG builder
552 * @param View Current scene view
553 * @param RenderTargets Render target slots (for depth write)
554 * @param SmokeWorldPosDepthTex World position + depth texture
555 * @param SmokeLocalPosAlphaTex Local position + alpha texture
556 * @param ViewportSize ViewRect.Size() for UV calculation
557 * @param ViewRectMin ViewRect.Min for UV calculation
558 */
559 void ExecuteDepthWrite(
560 FRDGBuilder& GraphBuilder,
561 const FSceneView& View,
562 const struct FRenderTargetBindingSlots& RenderTargets,
563 FRDGTextureRef SmokeWorldPosDepthTex,
564 FRDGTextureRef SmokeLocalPosAlphaTex,
565 const FIntPoint& ViewportSize,
566 const FIntPoint& ViewRectMin
567 );
568
569 //~==============================================================================
570 // Per-View Data (Camera-dependent, valid within single GraphBuilder)
571
572 /**
573 * Per-View rendering data for Pre-pass → Post-process data transfer.
574 * Contains ray marching results that are camera-dependent.
575 * Valid only within the same RDG builder (same ViewFamily's frame).
576 *
577 * Cleanup: Cleared when the owning ViewFamily finishes rendering.
578 */
579 struct FPerViewData
580 {
581 FRDGTextureRef SmokeTex = nullptr; // After UpsampleFilter
582 FRDGTextureRef LocalPosAlphaTex = nullptr; // Full resolution
583 FRDGTextureRef WorldPosDepthTex = nullptr; // Full resolution
584 FIntPoint ViewportSize = FIntPoint::ZeroValue;
585 FIntPoint ViewRectMin = FIntPoint::ZeroValue;
586 bool bIsValid = false;
587 };
588
589 /** View → Per-View data map. Each View has independent camera-dependent render results. */
590 TMap<const FSceneViewStateInterface*, FPerViewData> ViewDataMap;
591
592 /** Mutex for thread-safe access to ViewDataMap (protects against viewport switching races). */
593 mutable FCriticalSection ViewDataMutex;
594
595public:
596 /** Clear per-view data for a specific ViewFamily. Called at end of ViewFamily rendering. */
597 void ClearViewDataForViewFamily(const FSceneViewFamily& ViewFamily)
598 {
599 FScopeLock Lock(&ViewDataMutex);
600
601 for (const FSceneView* View : ViewFamily.Views)
602 {
603 if (View && View->State)
604 {
605 ViewDataMap.Remove(View->State);
606 }
607 }
608 }
609
610private:
611
612 //~==============================================================================
613 // State (Global - Shared across all Worlds)
614
615 /** Shared noise volume texture for all smoke rendering. Prevent GC via AddToRoot. */
616 UTextureRenderTargetVolume* NoiseVolume = nullptr;
617
618 /** Re-entry guard to prevent infinite recursion during shadow capture. */
619 bool bIsCapturingShadow = false;
620
621 //~==============================================================================
622 // Per-World Data Management
623
624 /**
625 * Map of World -> Per-World Data.
626 * Each World (Editor, PIE, Standalone) has its own rendering state.
627 * TObjectKey<UWorld> is used for safe UObject pointer comparison.
628 * TSharedPtr ensures data survives across thread boundaries.
629 *
630 * THREAD SAFETY: All access must be protected by WorldDataMutex.
631 */
632 TMap<TObjectKey<UWorld>, TSharedPtr<FPerWorldData>> WorldDataMap;
633
634 /** Mutex for thread-safe access to WorldDataMap. */
635 mutable FCriticalSection WorldDataMutex;
636
637 /** Delegate handles for World destruction callbacks. */
638 TMap<TObjectKey<UWorld>, FDelegateHandle> WorldEndPlayHandles;
639
640 //~==============================================================================
641 // CSM Helpers (Per-World)
642
643 /** Initialize CSM renderer for a specific World if needed. */
644 void InitializeCSM(TSharedPtr<FPerWorldData> WorldData, UWorld* World);
645
646 /**
647 * Find the main directional light (Atmosphere Sun Light) in the world.
648 * Uses the same logic as the engine: bAtmosphereSunLight + AtmosphereSunLightIndex.
649 *
650 * @param World World to search in
651 * @param OutDirection Direction TOWARD the light source (opposite of light travel direction)
652 * @param OutColor Light color
653 * @param OutIntensity Light intensity
654 * @return true if a directional light was found
655 */
656 bool GetMainDirectionalLight(UWorld* World, FVector& OutDirection, FLinearColor& OutColor, float& OutIntensity);
657
658 //~==============================================================================
659 // Stats Tracking
660
661 /** Last time stats were updated (for 1-second interval). */
662 double LastStatUpdateTime = 0.0;
663
664 /** Cached memory sizes for stat reporting. */
665 int64 CachedNoiseVolumeSize = 0;
666 int64 CachedCSMSize = 0;
667 int64 CachedPerFrameSize = 0;
668
669 /** Update stats if 1 second has passed since last update. */
670 void UpdateStatsIfNeeded(const FIVSmokePackedRenderData& RenderData, const FIntPoint& ViewportSize);
671
672 /** Calculate per-frame texture memory usage. */
673 int64 CalculatePerFrameTextureSize(
674 const FIntPoint& ViewportSize,
675 int32 VolumeCount,
676 const FIntVector& VoxelResolution,
677 const FIntVector& HoleResolution
678 ) const;
679
680 /** Update all stat values. */
681 void UpdateAllStats();
682};
bool IsInitialized() const
void ClearViewDataForViewFamily(const FSceneViewFamily &ViewFamily)
static constexpr float NoiseUVMul
TArray< float > PackedVoxelBirthTimes
TArray< float > PackedVoxelDeathTimes
TArray< FIVSmokeVolumeGPUData > VolumeDataArray
TArray< FTextureRHIRef > HoleTextures
TArray< FTextureRHIRef > CSMDepthTextures
FIVSmokePackedRenderData CachedRenderData
TUniquePtr< FIVSmokeVSMProcessor > VSMProcessor
TUniquePtr< FIVSmokeCSMRenderer > CSMRenderer
FIVSmokePackedRenderData GameThreadCachedRenderData