using System.Collections.Generic;
using UnityEditor;
namespace UnityEngine.Rendering
{
internal static class ProbeVolumeGizmos
{
static MeshGizmo _brickMeshGizmo;
static MeshGizmo _cellMeshGizmo;
static double _lastDrawAt = 0;
static readonly string _gizmoPath = "Packages/com.unity.render-pipelines.core/Editor/Resources/Gizmos";
static readonly string _probeAdjustmentVolumeIconPath = _gizmoPath + "/ProbeTouchupVolume.png";
static readonly string _probeVolumeIconPath = _gizmoPath + "/ProbeVolume.png";
static ProbeVolumeGizmos()
{
EditorApplication.update += Update;
}
static void Update()
{
bool resourcesAllocated = _brickMeshGizmo != null || _cellMeshGizmo != null;
if (resourcesAllocated)
{
bool shouldCleanUp = EditorApplication.timeSinceStartup - _lastDrawAt > 1.0;
if (shouldCleanUp)
{
_brickMeshGizmo?.Dispose();
_brickMeshGizmo = null;
_cellMeshGizmo?.Dispose();
_cellMeshGizmo = null;
}
}
}
[DrawGizmo(GizmoType.Active | GizmoType.Selected | GizmoType.NonSelected)]
static void DrawProbeAdjustmentVolumes(ProbeAdjustmentVolume volume, GizmoType gizmoType)
{
Gizmos.DrawIcon(volume.transform.position, _probeAdjustmentVolumeIconPath, true);
}
[DrawGizmo(GizmoType.Active | GizmoType.Selected | GizmoType.NonSelected)]
static void DrawProbeVolumeGizmos(ProbeVolume volume, GizmoType gizmoType)
{
_lastDrawAt = EditorApplication.timeSinceStartup;
Gizmos.DrawIcon(volume.transform.position, _probeVolumeIconPath, true);
var probeRefVolume = ProbeReferenceVolume.instance;
var sceneToBakingSetMap = ProbeVolumeBakingSet.SceneToBakingSet.Instance;
var allVolumes = ProbeVolume.instances;
if (!probeRefVolume.isInitialized || allVolumes.Count == 0)
return;
// Only the first PV of the available ones will draw gizmos.
if (allVolumes[0] != volume)
return;
var debugDisplay = probeRefVolume.probeVolumeDebug;
float minBrickSize = probeRefVolume.MinBrickSize();
var cellSizeInMeters = probeRefVolume.MaxBrickSize();
var probeOffset = probeRefVolume.ProbeOffset() + ProbeVolumeDebug.currentOffset;
if (debugDisplay.realtimeSubdivision)
{
var bakingSet = ProbeVolumeBakingSet.GetBakingSetForScene(volume.gameObject.scene);
if (bakingSet == null)
return;
// Overwrite settings with data from profile
minBrickSize = ProbeVolumeBakingSet.GetMinBrickSize(bakingSet.minDistanceBetweenProbes);
cellSizeInMeters = ProbeVolumeBakingSet.GetCellSizeInBricks(bakingSet.simplificationLevels) * minBrickSize;
probeOffset = bakingSet.probeOffset;
}
if (debugDisplay.drawBricks)
{
var subDivColors = probeRefVolume.subdivisionDebugColors;
if (_brickMeshGizmo == null)
_brickMeshGizmo = new MeshGizmo((int)(Mathf.Pow(3, ProbeBrickIndex.kMaxSubdivisionLevels) * MeshGizmo.vertexCountPerCube));
_brickMeshGizmo.Clear();
if (debugDisplay.realtimeSubdivision)
{
// realtime subdiv cells are already culled
foreach (var kp in probeRefVolume.realtimeSubdivisionInfo)
{
var cellVolume = kp.Key;
foreach (var brick in kp.Value)
DrawAndAddBrick(_brickMeshGizmo, brick, minBrickSize, probeOffset, subDivColors);
}
}
else
{
var cullCtx = new ProbeVolume.CellCullingContext
{
ActiveCamera = null,
FrustumPlanes = stackalloc Plane[6]
};
ProbeVolume.PrepareCellCulling(ref cullCtx);
foreach (var cell in probeRefVolume.cells.Values)
{
if (!cell.loaded)
continue;
if (volume.ShouldCullCell(cullCtx, sceneToBakingSetMap, probeRefVolume, cell.desc.position))
continue;
if (cell.data.bricks == null)
continue;
foreach (var brick in cell.data.bricks)
DrawAndAddBrick(_brickMeshGizmo, brick, minBrickSize, probeOffset, subDivColors);
}
}
_brickMeshGizmo.RenderWireframe(Matrix4x4.identity, gizmoName: "Brick Gizmo Rendering");
}
if (debugDisplay.drawCells)
{
Color s_LoadedColor = new Color(0, 1, 0.5f, 0.2f);
Color s_UnloadedColor = new Color(1, 0.0f, 0.0f, 0.2f);
Color s_StreamingColor = new Color(0.0f, 0.0f, 1.0f, 0.2f);
Color s_LowScoreColor = new Color(0, 0, 0, 0.2f);
Color s_HighScoreColor = new Color(1, 1, 0, 0.2f);
var oldGizmoMatrix = Gizmos.matrix;
Gizmos.matrix = Matrix4x4.identity;
if (_cellMeshGizmo == null)
_cellMeshGizmo = new MeshGizmo();
_cellMeshGizmo.Clear();
float minStreamingScore = probeRefVolume.minStreamingScore;
float streamingScoreRange = probeRefVolume.maxStreamingScore - probeRefVolume.minStreamingScore;
if (debugDisplay.realtimeSubdivision)
{
foreach (var kp in probeRefVolume.realtimeSubdivisionInfo)
{
DrawAndAddCell(_cellMeshGizmo, kp.Key.center, s_LoadedColor, cellSizeInMeters);
}
}
else
{
var cullCtx = new ProbeVolume.CellCullingContext
{
ActiveCamera = null,
FrustumPlanes = stackalloc Plane[6]
};
ProbeVolume.PrepareCellCulling(ref cullCtx);
foreach (var cell in probeRefVolume.cells.Values)
{
if (volume.ShouldCullCell(cullCtx, sceneToBakingSetMap, probeRefVolume, cell.desc.position))
continue;
Color color;
if (debugDisplay.displayCellStreamingScore)
{
float lerpFactor = (cell.streamingInfo.streamingScore - minStreamingScore) / streamingScoreRange;
color = Color.Lerp(s_HighScoreColor, s_LowScoreColor, lerpFactor);
}
else
{
if (cell.streamingInfo.IsStreaming())
color = s_StreamingColor;
else
color = cell.loaded ? s_LoadedColor : s_UnloadedColor;
}
var positionF = new Vector4(cell.desc.position.x, cell.desc.position.y, cell.desc.position.z, 0.0f);
var center = (Vector4)probeOffset + positionF * cellSizeInMeters + cellSizeInMeters * 0.5f * Vector4.one;
DrawAndAddCell(_cellMeshGizmo, center, color, cellSizeInMeters);
}
}
_cellMeshGizmo.RenderWireframe(Gizmos.matrix, gizmoName: "Brick Gizmo Rendering");
Gizmos.matrix = oldGizmoMatrix;
}
}
static void DrawAndAddCell(MeshGizmo meshGizmo, Vector4 center, Color color, float cellSizeInMeters)
{
Gizmos.color = color;
Gizmos.DrawCube(center, Vector3.one * cellSizeInMeters);
var wireColor = color;
wireColor.a = 1.0f;
meshGizmo.AddWireCube(center, Vector3.one * cellSizeInMeters, wireColor);
}
static void DrawAndAddBrick(MeshGizmo meshGizmo, ProbeBrickIndex.Brick brick, float minBrickSize, Vector3 probeOffset, Color[] subDivColors)
{
if (brick.subdivisionLevel < 0)
return;
float brickSize = minBrickSize * ProbeReferenceVolume.CellSize(brick.subdivisionLevel);
Vector3 scaledSize = new Vector3(brickSize, brickSize, brickSize);
Vector3 scaledPos = probeOffset + new Vector3(brick.position.x * minBrickSize, brick.position.y * minBrickSize, brick.position.z * minBrickSize) + scaledSize / 2;
meshGizmo.AddWireCube(scaledPos, scaledSize, subDivColors[brick.subdivisionLevel]);
}
}
}