You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

210 lines
9.6 KiB

using System;
using System.Collections.Generic;
using Unity.Collections;
namespace UnityEngine.Rendering
{
partial class AdaptiveProbeVolumes
{
// We use this scratch memory as a way of spoofing the texture.
static DynamicArray<(float, byte)> s_ValidityLayer_locData = new DynamicArray<(float, byte)>();
static DynamicArray<int> s_ProbeIndices = new DynamicArray<int>();
internal static Vector3Int GetSampleOffset(int i)
{
return new Vector3Int(i & 1, (i >> 1) & 1, (i >> 2) & 1);
}
const float k_MinValidityForLeaking = 0.05f;
internal static uint PackValidity(float[] validity)
{
uint outputByte = 0;
for (int i = 0; i < 8; ++i)
{
uint val = (validity[i] > k_MinValidityForLeaking) ? 0u : 1u;
outputByte |= (val << i);
}
return outputByte;
}
internal static uint PackLayer(byte[] layers, int layer)
{
uint outputLayer = 0;
for (int i = 0; i < 8; ++i)
{
if ((layers[i] & (byte)(1 << layer)) != 0)
outputLayer |= (1u << i);
}
return outputLayer;
}
static void StoreScratchData(int x, int y, int z, int dataWidth, int dataHeight, float value, byte layer, int probeIndex)
{
int index = x + dataWidth * (y + dataHeight * z);
s_ValidityLayer_locData[index] = (value, layer);
s_ProbeIndices[index] = probeIndex;
}
static (float, byte) ReadValidity(int x, int y, int z, int dataWidth, int dataHeight)
{
int index = x + dataWidth * (y + dataHeight * z);
return s_ValidityLayer_locData[index];
}
static int ReadProbeIndex(int x, int y, int z, int dataWidth, int dataHeight)
{
int index = x + dataWidth * (y + dataHeight * z);
return s_ProbeIndices[index];
}
// TODO: This whole process will need optimization.
static bool NeighbourhoodIsEmptySpace(Vector3 pos, float searchDistance, Bounds boundsToCheckAgainst)
{
Vector3 halfExtents = Vector3.one * searchDistance * 0.5f;
Vector3 brickCenter = pos + halfExtents;
Collider[] colliders = Physics.OverlapBox(brickCenter, halfExtents);
if (colliders.Length > 0) return false;
// TO_VERIFY: Shall we do this check?
//foreach (var collider in colliders)
//{
// if (collider.bounds.Intersects(boundsToCheckAgainst))
// return false;
//}
return true;
}
// This is very much modeled to be as close as possible to the way bricks are loaded in the texture pool.
// Not necessarily a good thing.
static void ComputeValidityMasks(in BakingCell cell)
{
var bricks = cell.bricks;
int chunkSize = ProbeBrickPool.GetChunkSizeInBrickCount();
int brickChunksCount = (bricks.Length + chunkSize - 1) / chunkSize;
int validityLayerCount = cell.layerValidity != null ? cell.validityNeighbourMask.GetLength(0) : 1;
var probeHasEmptySpaceInGrid = new NativeArray<bool>(cell.probePositions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
int shidx = 0;
for (int chunkIndex = 0; chunkIndex < brickChunksCount; ++chunkIndex)
{
Vector3Int locSize = ProbeBrickPool.ProbeCountToDataLocSize(ProbeBrickPool.GetChunkSizeInProbeCount());
int size = locSize.x * locSize.y * locSize.z;
int count = ProbeBrickPool.GetChunkSizeInProbeCount();
int bx = 0, by = 0, bz = 0;
s_ValidityLayer_locData.Resize(size);
s_ProbeIndices.Resize(size);
HashSet<Vector3Int> probesToRestore = new HashSet<Vector3Int>();
for (int brickIdx = 0; brickIdx < count; brickIdx += ProbeBrickPool.kBrickProbeCountTotal)
{
for (int z = 0; z < ProbeBrickPool.kBrickProbeCountPerDim; z++)
{
for (int y = 0; y < ProbeBrickPool.kBrickProbeCountPerDim; y++)
{
for (int x = 0; x < ProbeBrickPool.kBrickProbeCountPerDim; x++)
{
int ix = bx + x;
int iy = by + y;
int iz = bz + z;
if (shidx >= cell.validity.Length)
{
StoreScratchData(ix, iy, iz, locSize.x, locSize.y, 1.0f, 0, shidx);
}
else
{
byte layer = validityLayerCount > 1 ? cell.layerValidity[shidx] : (byte)0xFF;
StoreScratchData(ix, iy, iz, locSize.x, locSize.y, cell.validity[shidx], layer, shidx);
// Check if we need to do some extra check on this probe.
bool hasFreeNeighbourhood = false;
Bounds invalidatingTouchupBound;
if (m_BakingBatch.forceInvalidatedProbesAndTouchupVols.TryGetValue(cell.probePositions[shidx], out invalidatingTouchupBound))
{
int actualBrickIdx = brickIdx / ProbeBrickPool.kBrickProbeCountTotal;
float brickSize = ProbeReferenceVolume.CellSize(cell.bricks[actualBrickIdx].subdivisionLevel);
Vector3 position = cell.probePositions[shidx];
probesToRestore.Add(new Vector3Int(ix, iy, iz));
var searchDistance = (brickSize * m_ProfileInfo.minBrickSize) / ProbeBrickPool.kBrickCellCount;
hasFreeNeighbourhood = NeighbourhoodIsEmptySpace(position, searchDistance, invalidatingTouchupBound);
}
probeHasEmptySpaceInGrid[shidx] = hasFreeNeighbourhood;
}
shidx++;
}
}
}
// update the pool index
bx += ProbeBrickPool.kBrickProbeCountPerDim;
if (bx >= locSize.x)
{
bx = 0;
by += ProbeBrickPool.kBrickProbeCountPerDim;
if (by >= locSize.y)
{
by = 0;
bz += ProbeBrickPool.kBrickProbeCountPerDim;
}
}
}
for (int x = 0; x < locSize.x; ++x)
{
for (int y = 0; y < locSize.y; ++y)
{
for (int z = 0; z < locSize.z; ++z)
{
int outIdx = ReadProbeIndex(x, y, z, locSize.x, locSize.y);
if (outIdx < cell.validity.Length)
{
float[] validities = new float[8];
byte[] layers = new byte[8];
bool forceAllValid = false;
for (int o = 0; o < 8; ++o)
{
Vector3Int off = GetSampleOffset(o);
Vector3Int samplePos = new Vector3Int(Mathf.Clamp(x + off.x, 0, locSize.x - 1),
Mathf.Clamp(y + off.y, 0, locSize.y - 1),
Mathf.Clamp(z + off.z, 0, ProbeBrickPool.kBrickProbeCountPerDim - 1));
if (probesToRestore.Contains(samplePos))
{
if (probeHasEmptySpaceInGrid[outIdx])
{
forceAllValid = true;
}
}
(validities[o], layers[o]) = ReadValidity(samplePos.x, samplePos.y, samplePos.z, locSize.x, locSize.y);
}
// Keeping for safety but i think this is useless
(float probeValidity, uint _) = ReadValidity(x, y, z, locSize.x, locSize.y);
cell.validity[outIdx] = probeValidity;
// Pack validity with layer mask
uint mask = forceAllValid ? 255 : PackValidity(validities);
for (int l = 0; l < validityLayerCount; l++)
{
uint layer = validityLayerCount == 1 ? 0xFF : PackLayer(layers, l);
cell.validityNeighbourMask[l, outIdx] = Convert.ToByte(mask & layer);
}
}
}
}
}
}
probeHasEmptySpaceInGrid.Dispose();
}
}
}