A game about forced loneliness, made by TACStudios
1using System.Collections.Generic; 2using UnityEngine.SceneManagement; 3 4namespace UnityEngine.Rendering 5{ 6 /// <summary> 7 /// A marker to determine what area of the scene is considered by the Probe Volumes system 8 /// </summary> 9 [CoreRPHelpURL("probevolumes-settings#probe-volume-properties", "com.unity.render-pipelines.high-definition")] 10 [ExecuteAlways] 11 [AddComponentMenu("Rendering/Adaptive Probe Volume")] 12 public partial class ProbeVolume : MonoBehaviour 13 { 14 /// <summary>Indicates which renderers should be considerer for the Probe Volume bounds when baking</summary> 15 public enum Mode 16 { 17 /// <summary>Encapsulate all renderers in the baking set.</summary> 18 Global, 19 /// <summary>Encapsulate all renderers in the scene.</summary> 20 Scene, 21 /// <summary>Encapsulate all renderers in the bounding box.</summary> 22 Local 23 } 24 25 /// <summary> 26 /// If is a global bolume 27 /// </summary> 28 [Tooltip("When set to Global this Probe Volume considers all renderers with Contribute Global Illumination enabled. Local only considers renderers in the scene.\nThis list updates every time the Scene is saved or the lighting is baked.")] 29 public Mode mode = Mode.Local; 30 31 /// <summary> 32 /// The size 33 /// </summary> 34 public Vector3 size = new Vector3(10, 10, 10); 35 36 /// <summary> 37 /// Override the renderer filters. 38 /// </summary> 39 [HideInInspector, Min(0)] 40 public bool overrideRendererFilters = false; 41 42 /// <summary> 43 /// The minimum renderer bounding box volume size. This value is used to discard small renderers when the overrideMinRendererVolumeSize is enabled. 44 /// </summary> 45 [HideInInspector, Min(0)] 46 public float minRendererVolumeSize = 0.1f; 47 48 /// <summary> 49 /// The <see cref="LayerMask"/> 50 /// </summary> 51 public LayerMask objectLayerMask = -1; 52 53 /// <summary> 54 /// The lowest subdivision level override 55 /// </summary> 56 [HideInInspector] 57 public int lowestSubdivLevelOverride = 0; 58 59 /// <summary> 60 /// The highest subdivision level override 61 /// </summary> 62 [HideInInspector] 63 public int highestSubdivLevelOverride = ProbeBrickIndex.kMaxSubdivisionLevels; 64 65 /// <summary> 66 /// If the subdivision levels need to be overriden 67 /// </summary> 68 [HideInInspector] 69 public bool overridesSubdivLevels = false; 70 71 [SerializeField] internal bool mightNeedRebaking = false; 72 73 [SerializeField] internal Matrix4x4 cachedTransform; 74 [SerializeField] internal int cachedHashCode; 75 76 /// <summary>Whether spaces with no renderers need to be filled with bricks at highest subdivision level.</summary> 77 [HideInInspector] 78 [Tooltip("Whether Unity should fill empty space between renderers with bricks at the highest subdivision level.")] 79 public bool fillEmptySpaces = false; 80 81#if UNITY_EDITOR 82 /// <summary> 83 /// Returns the extents of the volume. 84 /// </summary> 85 /// <returns>The extents of the ProbeVolume.</returns> 86 public Vector3 GetExtents() 87 { 88 return size; 89 } 90 91 public Matrix4x4 GetVolume() 92 { 93 return Matrix4x4.TRS(transform.position, transform.rotation, GetExtents()); 94 } 95 96 internal Bounds ComputeBounds(GIContributors.ContributorFilter filter, Scene? scene = null) 97 { 98 Bounds bounds = new Bounds(); 99 bool foundABound = false; 100 101 void ExpandBounds(Bounds bound) 102 { 103 if (!foundABound) 104 { 105 bounds = bound; 106 foundABound = true; 107 } 108 else 109 { 110 bounds.Encapsulate(bound); 111 } 112 } 113 114 var contributors = GIContributors.Find(filter, scene); 115 foreach (var renderer in contributors.renderers) 116 ExpandBounds(renderer.component.bounds); 117 foreach (var terrain in contributors.terrains) 118 ExpandBounds(terrain.boundsWithTrees); 119 120 return bounds; 121 } 122 123 internal void UpdateGlobalVolume(GIContributors.ContributorFilter filter) 124 { 125 float minBrickSize = ProbeReferenceVolume.instance.MinBrickSize(); 126 var bounds = ComputeBounds(filter, gameObject.scene); 127 transform.position = bounds.center; 128 size = Vector3.Max(bounds.size + new Vector3(minBrickSize, minBrickSize, minBrickSize), Vector3.zero); 129 } 130 131 internal void OnLightingDataAssetCleared() 132 { 133 mightNeedRebaking = true; 134 } 135 136 internal void OnBakeCompleted() 137 { 138 // We cache the data of last bake completed. 139 cachedTransform = gameObject.transform.worldToLocalMatrix; 140 cachedHashCode = GetHashCode(); 141 mightNeedRebaking = false; 142 } 143 144 public override int GetHashCode() 145 { 146 int hash = 17; 147 148 unchecked 149 { 150 hash = hash * 23 + size.GetHashCode(); 151 hash = hash * 23 + gameObject.transform.worldToLocalMatrix.GetHashCode(); 152 hash = hash * 23 + overridesSubdivLevels.GetHashCode(); 153 hash = hash * 23 + highestSubdivLevelOverride.GetHashCode(); 154 hash = hash * 23 + lowestSubdivLevelOverride.GetHashCode(); 155 hash = hash * 23 + overrideRendererFilters.GetHashCode(); 156 if (overrideRendererFilters) 157 { 158 hash = hash * 23 + minRendererVolumeSize.GetHashCode(); 159 hash = hash * 23 + objectLayerMask.value.GetHashCode(); 160 } 161 hash = hash * 23 + fillEmptySpaces.GetHashCode(); 162 } 163 164 return hash; 165 } 166 167 internal void GetSubdivisionOverride(int maxSubdivisionLevel, out int minLevel, out int maxLevel) 168 { 169 if (overridesSubdivLevels) 170 { 171 maxLevel = Mathf.Min(highestSubdivLevelOverride, maxSubdivisionLevel); 172 minLevel = Mathf.Min(lowestSubdivLevelOverride, maxLevel); 173 } 174 else 175 { 176 maxLevel = maxSubdivisionLevel; 177 minLevel = 0; 178 } 179 } 180 181 // Momentarily moving the gizmo rendering for bricks and cells to Probe Volume itself, 182 // only the first probe volume in the scene will render them. The reason is that we dont have any 183 // other non-hidden component related to APV. 184 #region APVGizmo 185 186 internal static List<ProbeVolume> instances = new(); 187 188 MeshGizmo brickGizmos; 189 MeshGizmo cellGizmo; 190 191 void DisposeGizmos() 192 { 193 brickGizmos?.Dispose(); 194 brickGizmos = null; 195 cellGizmo?.Dispose(); 196 cellGizmo = null; 197 } 198 199 void OnEnable() 200 { 201 instances.Add(this); 202 } 203 204 void OnDisable() 205 { 206 instances.Remove(this); 207 DisposeGizmos(); 208 } 209 210 // Only the first PV of the available ones will draw gizmos. 211 bool IsResponsibleToDrawGizmo() => instances.Count > 0 && instances[0] == this; 212 213 internal bool ShouldCullCell(Vector3 cellPosition) 214 { 215 var cellSizeInMeters = ProbeReferenceVolume.instance.MaxBrickSize(); 216 var probeOffset = ProbeReferenceVolume.instance.ProbeOffset() + ProbeVolumeDebug.currentOffset; 217 var debugDisplay = ProbeReferenceVolume.instance.probeVolumeDebug; 218 if (debugDisplay.realtimeSubdivision) 219 { 220 var bakingSet = ProbeVolumeBakingSet.GetBakingSetForScene(gameObject.scene); 221 if (bakingSet == null) 222 return true; 223 224 // Use the non-backed data to display real-time info 225 cellSizeInMeters = ProbeVolumeBakingSet.GetMinBrickSize(bakingSet.minDistanceBetweenProbes) * ProbeVolumeBakingSet.GetCellSizeInBricks(bakingSet.simplificationLevels); 226 probeOffset = bakingSet.probeOffset + ProbeVolumeDebug.currentOffset; 227 } 228 Camera activeCamera = Camera.current; 229#if UNITY_EDITOR 230 if (activeCamera == null) 231 activeCamera = UnityEditor.SceneView.lastActiveSceneView.camera; 232#endif 233 234 if (activeCamera == null) 235 return true; 236 237 var cameraTransform = activeCamera.transform; 238 239 Vector3 cellCenterWS = probeOffset + cellPosition * cellSizeInMeters + Vector3.one * (cellSizeInMeters / 2.0f); 240 241 // Round down to cell size distance 242 float roundedDownDist = Mathf.Floor(Vector3.Distance(cameraTransform.position, cellCenterWS) / cellSizeInMeters) * cellSizeInMeters; 243 244 if (roundedDownDist > ProbeReferenceVolume.instance.probeVolumeDebug.subdivisionViewCullingDistance) 245 return true; 246 247 var frustumPlanes = GeometryUtility.CalculateFrustumPlanes(activeCamera); 248 var volumeAABB = new Bounds(cellCenterWS, cellSizeInMeters * Vector3.one); 249 250 return !GeometryUtility.TestPlanesAABB(frustumPlanes, volumeAABB); 251 } 252 253 254 struct CellDebugData 255 { 256 public Vector4 center; 257 public Color color; 258 } 259 260 // Path to the gizmos location 261 internal readonly static string s_gizmosLocationPath = "Packages/com.unity.render-pipelines.core/Editor/Resources/Gizmos/"; 262 263 // TODO: We need to get rid of Handles.DrawWireCube to be able to have those at runtime as well. 264 void OnDrawGizmos() 265 { 266 Gizmos.DrawIcon(transform.position, s_gizmosLocationPath + "ProbeVolume.png", true); 267 268 if (!ProbeReferenceVolume.instance.isInitialized || !IsResponsibleToDrawGizmo()) 269 return; 270 271 var debugDisplay = ProbeReferenceVolume.instance.probeVolumeDebug; 272 273 float minBrickSize = ProbeReferenceVolume.instance.MinBrickSize(); 274 var cellSizeInMeters = ProbeReferenceVolume.instance.MaxBrickSize(); 275 var probeOffset = ProbeReferenceVolume.instance.ProbeOffset() + ProbeVolumeDebug.currentOffset; 276 if (debugDisplay.realtimeSubdivision) 277 { 278 var bakingSet = ProbeVolumeBakingSet.GetBakingSetForScene(gameObject.scene); 279 if (bakingSet == null) 280 return; 281 282 // Overwrite settings with data from profile 283 minBrickSize = ProbeVolumeBakingSet.GetMinBrickSize(bakingSet.minDistanceBetweenProbes); 284 cellSizeInMeters = ProbeVolumeBakingSet.GetCellSizeInBricks(bakingSet.simplificationLevels) * minBrickSize; 285 probeOffset = bakingSet.probeOffset; 286 } 287 288 if (debugDisplay.drawBricks) 289 { 290 var subdivColors = ProbeReferenceVolume.instance.subdivisionDebugColors; 291 292 IEnumerable<ProbeBrickIndex.Brick> GetVisibleBricks() 293 { 294 if (debugDisplay.realtimeSubdivision) 295 { 296 // realtime subdiv cells are already culled 297 foreach (var kp in ProbeReferenceVolume.instance.realtimeSubdivisionInfo) 298 { 299 var cellVolume = kp.Key; 300 301 foreach (var brick in kp.Value) 302 { 303 yield return brick; 304 } 305 } 306 } 307 else 308 { 309 foreach (var cell in ProbeReferenceVolume.instance.cells.Values) 310 { 311 if (!cell.loaded) 312 continue; 313 314 if (ShouldCullCell(cell.desc.position)) 315 continue; 316 317 if (cell.data.bricks == null) 318 continue; 319 320 foreach (var brick in cell.data.bricks) 321 yield return brick; 322 } 323 } 324 } 325 326 if (brickGizmos == null) 327 brickGizmos = new MeshGizmo((int)(Mathf.Pow(3, ProbeBrickIndex.kMaxSubdivisionLevels) * MeshGizmo.vertexCountPerCube)); 328 329 brickGizmos.Clear(); 330 foreach (var brick in GetVisibleBricks()) 331 { 332 if (brick.subdivisionLevel < 0) 333 continue; 334 335 float brickSize = minBrickSize * ProbeReferenceVolume.CellSize(brick.subdivisionLevel); 336 Vector3 scaledSize = new Vector3(brickSize, brickSize, brickSize); 337 Vector3 scaledPos = probeOffset + new Vector3(brick.position.x * minBrickSize, brick.position.y * minBrickSize, brick.position.z * minBrickSize) + scaledSize / 2; 338 brickGizmos.AddWireCube(scaledPos, scaledSize, subdivColors[brick.subdivisionLevel]); 339 } 340 341 brickGizmos.RenderWireframe(Matrix4x4.identity, gizmoName: "Brick Gizmo Rendering"); 342 } 343 344 if (debugDisplay.drawCells) 345 { 346 IEnumerable<CellDebugData> GetVisibleCellDebugData() 347 { 348 Color s_LoadedColor = new Color(0, 1, 0.5f, 0.2f); 349 Color s_UnloadedColor = new Color(1, 0.0f, 0.0f, 0.2f); 350 Color s_StreamingColor = new Color(0.0f, 0.0f, 1.0f, 0.2f); 351 Color s_LowScoreColor = new Color(0, 0, 0, 0.2f); 352 Color s_HighScoreColor = new Color(1, 1, 0, 0.2f); 353 354 var prv = ProbeReferenceVolume.instance; 355 356 float minStreamingScore = prv.minStreamingScore; 357 float streamingScoreRange = prv.maxStreamingScore - prv.minStreamingScore; 358 359 if (debugDisplay.realtimeSubdivision) 360 { 361 foreach (var kp in prv.realtimeSubdivisionInfo) 362 { 363 var center = kp.Key.center; 364 yield return new CellDebugData { center = center, color = s_LoadedColor }; 365 } 366 } 367 else 368 { 369 foreach (var cell in ProbeReferenceVolume.instance.cells.Values) 370 { 371 if (ShouldCullCell(cell.desc.position)) 372 continue; 373 374 var positionF = new Vector4(cell.desc.position.x, cell.desc.position.y, cell.desc.position.z, 0.0f); 375 var output = new CellDebugData(); 376 output.center = (Vector4)probeOffset + positionF * cellSizeInMeters + cellSizeInMeters * 0.5f * Vector4.one; 377 if (debugDisplay.displayCellStreamingScore) 378 { 379 float lerpFactor = (cell.streamingInfo.streamingScore - minStreamingScore) / streamingScoreRange; 380 output.color = Color.Lerp(s_HighScoreColor, s_LowScoreColor, lerpFactor); 381 } 382 else 383 { 384 if (cell.streamingInfo.IsStreaming()) 385 output.color = s_StreamingColor; 386 else 387 output.color = cell.loaded ? s_LoadedColor : s_UnloadedColor; 388 } 389 yield return output; 390 } 391 } 392 } 393 394 var oldGizmoMatrix = Gizmos.matrix; 395 396 if (cellGizmo == null) 397 cellGizmo = new MeshGizmo(); 398 cellGizmo.Clear(); 399 foreach (var cell in GetVisibleCellDebugData()) 400 { 401 Gizmos.color = cell.color; 402 Gizmos.matrix = Matrix4x4.identity; 403 404 Gizmos.DrawCube(cell.center, Vector3.one * cellSizeInMeters); 405 var wireColor = cell.color; 406 wireColor.a = 1.0f; 407 cellGizmo.AddWireCube(cell.center, Vector3.one * cellSizeInMeters, wireColor); 408 } 409 cellGizmo.RenderWireframe(Gizmos.matrix, gizmoName: "Brick Gizmo Rendering"); 410 Gizmos.matrix = oldGizmoMatrix; 411 } 412 } 413 #endregion 414 415#endif // UNITY_EDITOR 416 } 417} // UnityEngine.Rendering.HDPipeline