void ClipToIndexSpace(Vector3Int pos, int subdiv, out Vector3Int outMinpos, out Vector3Int outMaxpos, CellIndexUpdateInfo cellInfo) { // to relative coordinates int cellSize = ProbeReferenceVolume.CellSize(subdiv); // The position here is in global space, however we want to constraint this voxel update to the valid cell area var minValidPosition = cellInfo.cellPositionInBricksAtMaxRes + cellInfo.minValidBrickIndexForCellAtMaxRes; var maxValidPosition = cellInfo.cellPositionInBricksAtMaxRes + cellInfo.maxValidBrickIndexForCellAtMaxResPlusOne - Vector3Int.one; int minpos_x = pos.x - m_CenterRS.x; int minpos_y = pos.y; int minpos_z = pos.z - m_CenterRS.z; int maxpos_x = minpos_x + cellSize; int maxpos_y = minpos_y + cellSize; int maxpos_z = minpos_z + cellSize; // clip to valid region minpos_x = Mathf.Max(minpos_x, minValidPosition.x); minpos_y = Mathf.Max(minpos_y, minValidPosition.y); minpos_z = Mathf.Max(minpos_z, minValidPosition.z); maxpos_x = Mathf.Min(maxpos_x, maxValidPosition.x); maxpos_y = Mathf.Min(maxpos_y, maxValidPosition.y); maxpos_z = Mathf.Min(maxpos_z, maxValidPosition.z); outMinpos = new Vector3Int(minpos_x, minpos_y, minpos_z); outMaxpos = new Vector3Int(maxpos_x, maxpos_y, maxpos_z); }
private void MapBrickToVoxels(ProbeBrickIndex.Brick brick, HashSet <Vector3Int> voxels) { // create a list of all voxels this brick will touch int brick_subdiv = brick.size; int voxels_touched_cnt = (int)Mathf.Pow(3, Mathf.Max(0, brick_subdiv - m_VoxelSubdivLevel)); Vector3Int ipos = brick.position; int brick_size = ProbeReferenceVolume.CellSize(brick.size); int voxel_size = ProbeReferenceVolume.CellSize(m_VoxelSubdivLevel); if (voxels_touched_cnt <= 1) { Vector3 pos = brick.position; pos = pos * (1.0f / voxel_size); ipos = new Vector3Int(Mathf.FloorToInt(pos.x) * voxel_size, Mathf.FloorToInt(pos.y) * voxel_size, Mathf.FloorToInt(pos.z) * voxel_size); } for (int z = ipos.z; z < ipos.z + brick_size; z += voxel_size) { for (int y = ipos.y; y < ipos.y + brick_size; y += voxel_size) { for (int x = ipos.x; x < ipos.x + brick_size; x += voxel_size) { voxels.Add(new Vector3Int(x, y, z)); } } } }
void UpdatePhysicalIndex(Vector3Int brickMin, Vector3Int brickMax, int value, CellIndexUpdateInfo cellInfo) { // We need to do our calculations in local space to the cell, so we move the brick to local space as a first step. // Reminder that at this point we are still operating at highest resolution possible, not necessarily the one that will be // the final resolution for the chunk. brickMin = brickMin - cellInfo.cellPositionInBricksAtMaxRes; brickMax = brickMax - cellInfo.cellPositionInBricksAtMaxRes; // Since the index is spurious (not same resolution, but varying per cell) we need to bring to the output resolution the brick coordinates // Before finding the locations inside the Index for the current cell/chunk. brickMin /= ProbeReferenceVolume.CellSize(cellInfo.minSubdivInCell); brickMax /= ProbeReferenceVolume.CellSize(cellInfo.minSubdivInCell); // Verify we are actually in local space now. int maxCellSizeInOutputRes = ProbeReferenceVolume.CellSize(ProbeReferenceVolume.instance.GetMaxSubdivision() - 1 - cellInfo.minSubdivInCell); Debug.Assert(brickMin.x >= 0 && brickMin.y >= 0 && brickMin.z >= 0 && brickMax.x >= 0 && brickMax.y >= 0 && brickMax.z >= 0); Debug.Assert(brickMin.x < maxCellSizeInOutputRes && brickMin.y < maxCellSizeInOutputRes && brickMin.z < maxCellSizeInOutputRes && brickMax.x <= maxCellSizeInOutputRes && brickMax.y <= maxCellSizeInOutputRes && brickMax.z <= maxCellSizeInOutputRes); // We are now in the right resolution, but still not considering the valid area, so we need to still normalize against that. // To do so first let's move back the limits to the desired resolution var cellMinIndex = cellInfo.minValidBrickIndexForCellAtMaxRes / ProbeReferenceVolume.CellSize(cellInfo.minSubdivInCell); var cellMaxIndex = cellInfo.maxValidBrickIndexForCellAtMaxResPlusOne / ProbeReferenceVolume.CellSize(cellInfo.minSubdivInCell); // Then perform the rescale of the local indices for min and max. brickMin -= cellMinIndex; brickMax -= cellMinIndex; // In theory now we are all positive since we clipped during the voxel stage. Keeping assert for debugging, but can go later. Debug.Assert(brickMin.x >= 0 && brickMin.y >= 0 && brickMin.z >= 0 && brickMax.x >= 0 && brickMax.y >= 0 && brickMax.z >= 0); // Compute the span of the valid part var size = (cellMaxIndex - cellMinIndex); // Analytically compute min and max because doing it in the inner loop with Math.Min/Max is costly (not inlined) int chunkStart = cellInfo.firstChunkIndex * kIndexChunkSize; int newMin = chunkStart + brickMin.z * (size.x * size.y) + brickMin.x * size.y + brickMin.y; int newMax = chunkStart + Math.Max(0, (brickMax.z - 1)) * (size.x * size.y) + Math.Max(0, (brickMax.x - 1)) * size.y + Math.Max(0, (brickMax.y - 1)); m_UpdateMinIndex = Math.Min(m_UpdateMinIndex, newMin); m_UpdateMaxIndex = Math.Max(m_UpdateMaxIndex, newMax); // Loop through all touched indices for (int x = brickMin.x; x < brickMax.x; ++x) { for (int z = brickMin.z; z < brickMax.z; ++z) { for (int y = brickMin.y; y < brickMax.y; ++y) { int localFlatIdx = z * (size.x * size.y) + x * size.y + y; int actualIdx = chunkStart + localFlatIdx; m_PhysicalIndexBufferData[actualIdx] = value; } } } m_NeedUpdateIndexComputeBuffer = true; }
public static void Subdivide(Vector3Int cellPosGridSpace, ProbeReferenceVolume refVol, float cellSize, Vector3 translation, Quaternion rotation, List <ProbeReferenceVolume.Volume> influencerVolumes, ref Vector3[] positions, ref List <ProbeBrickIndex.Brick> bricks) { //TODO: This per-cell volume is calculated 2 times during probe placement. We should calculate it once and reuse it. ProbeReferenceVolume.Volume cellVolume = new ProbeReferenceVolume.Volume(); cellVolume.corner = new Vector3(cellPosGridSpace.x * cellSize, cellPosGridSpace.y * cellSize, cellPosGridSpace.z * cellSize); cellVolume.X = new Vector3(cellSize, 0, 0); cellVolume.Y = new Vector3(0, cellSize, 0); cellVolume.Z = new Vector3(0, 0, cellSize); cellVolume.Transform(Matrix4x4.TRS(translation, rotation, Vector3.one)); // TODO move out var indicatorVolumes = new List <ProbeReferenceVolume.Volume>(); foreach (ProbeVolume pv in UnityEngine.Object.FindObjectsOfType <ProbeVolume>()) { if (!pv.enabled) { continue; } indicatorVolumes.Add(new ProbeReferenceVolume.Volume(Matrix4x4.TRS(pv.transform.position, pv.transform.rotation, pv.GetExtents()))); } ProbeReferenceVolume.SubdivisionDel subdivDel = (RefTrans refTrans, List <Brick> inBricks, List <Flags> outFlags) => { SubdivisionAlgorithm(cellVolume, indicatorVolumes, influencerVolumes, refTrans, inBricks, outFlags); }; bricks = new List <ProbeBrickIndex.Brick>(); // get a list of bricks for this volume int numProbes; refVol.CreateBricks(new List <ProbeReferenceVolume.Volume>() { cellVolume }, subdivDel, bricks, out numProbes); positions = new Vector3[numProbes]; refVol.ConvertBricks(bricks, positions); }
private void ClipToIndexSpace(Vector3Int pos, int subdiv, out Vector3Int outMinpos, out Vector3Int outMaxpos) { // to relative coordinates int cellSize = ProbeReferenceVolume.CellSize(subdiv); int minpos_x = pos.x - m_CenterRS.x; int minpos_y = pos.y; int minpos_z = pos.z - m_CenterRS.z; int maxpos_x = minpos_x + cellSize; int maxpos_y = minpos_y + cellSize; int maxpos_z = minpos_z + cellSize; // clip to index region minpos_x = Mathf.Max(minpos_x, -m_IndexDim.x / 2); minpos_z = Mathf.Max(minpos_z, -m_IndexDim.z / 2); maxpos_x = Mathf.Min(maxpos_x, m_IndexDim.x / 2); maxpos_z = Mathf.Min(maxpos_z, m_IndexDim.z / 2); outMinpos = new Vector3Int(minpos_x, minpos_y, minpos_z); outMaxpos = new Vector3Int(maxpos_x, maxpos_y, maxpos_z); }
void UpdateIndexForVoxel(Vector3Int voxel, List <ReservedBrick> bricks, List <ushort> indices, CellIndexUpdateInfo cellInfo) { // clip voxel to index space Vector3Int vx_min, vx_max; ClipToIndexSpace(voxel, GetVoxelSubdivLevel(), out vx_min, out vx_max, cellInfo); foreach (var rbrick in bricks) { // clip brick to clipped voxel int brick_cell_size = ProbeReferenceVolume.CellSize(rbrick.brick.subdivisionLevel); Vector3Int brick_min = rbrick.brick.position; Vector3Int brick_max = rbrick.brick.position + Vector3Int.one * brick_cell_size; brick_min.x = Mathf.Max(vx_min.x, brick_min.x - m_CenterRS.x); brick_min.y = Mathf.Max(vx_min.y, brick_min.y); brick_min.z = Mathf.Max(vx_min.z, brick_min.z - m_CenterRS.z); brick_max.x = Mathf.Min(vx_max.x, brick_max.x - m_CenterRS.x); brick_max.y = Mathf.Min(vx_max.y, brick_max.y); brick_max.z = Mathf.Min(vx_max.z, brick_max.z - m_CenterRS.z); UpdatePhysicalIndex(brick_min, brick_max, rbrick.flattenedIdx, cellInfo); } }
private void UpdateIndex(Vector3Int voxel, List <ReservedBrick> bricks, List <ushort> indices) { int base_offset = kAPVConstantsSize + m_IndexDim.x * m_IndexDim.z; // clip voxel to index space Vector3Int vx_min, vx_max; ClipToIndexSpace(voxel, m_VoxelSubdivLevel, out vx_min, out vx_max); foreach (var rbrick in bricks) { // clip brick to clipped voxel int brick_cell_size = ProbeReferenceVolume.CellSize(rbrick.brick.size); Vector3Int brick_min = rbrick.brick.position; Vector3Int brick_max = rbrick.brick.position + Vector3Int.one * brick_cell_size; brick_min.x = Mathf.Max(vx_min.x, brick_min.x - m_CenterRS.x); brick_min.y = Mathf.Max(vx_min.y, brick_min.y); brick_min.z = Mathf.Max(vx_min.z, brick_min.z - m_CenterRS.z); brick_max.x = Mathf.Min(vx_max.x, brick_max.x - m_CenterRS.x); brick_max.y = Mathf.Min(vx_max.y, brick_max.y); brick_max.z = Mathf.Min(vx_max.z, brick_max.z - m_CenterRS.z); int bsize_x = brick_max.x - brick_min.x; int bsize_z = brick_max.z - brick_min.z; if (bsize_x <= 0 || bsize_z <= 0) { continue; } for (int idx = 0; idx < brick_cell_size; idx++) { m_TmpUpdater[idx] = rbrick.flattenedIdx; } int posIS_x = m_CenterIS.x + brick_min.x; int posIS_z = m_CenterIS.z + brick_min.z; // iterate over z then x, as y needs special handling for updating the base offset for (int z = 0; z < bsize_z; z++) { for (int x = 0; x < bsize_x; x++) { int mx = (posIS_x + x) % m_IndexDim.x; int mz = (posIS_z + z) % m_IndexDim.z; int hoff_idx = mz * m_IndexDim.x + mx; HeightRange hr = m_HeightRanges[hoff_idx]; if (hr.min == -1) // untouched column { hr.min = brick_min.y; hr.cnt = Mathf.Min(brick_cell_size, m_IndexDim.y); m_IndexBuffer.SetData(m_TmpUpdater, 0, base_offset + TranslateIndex(new Vector3Int(mx, 0, mz)), hr.cnt); } else { // shift entire column upwards, but without pushing out existing indices int lowest_limit = hr.min - (m_IndexDim.y - hr.cnt); lowest_limit = Mathf.Max(brick_min.y, lowest_limit); int shift_cnt = Mathf.Max(0, hr.min - lowest_limit); int highest_limit = hr.min + m_IndexDim.y; if (shift_cnt == 0) { hr.cnt = Mathf.Min(m_IndexDim.y, brick_min.y + brick_cell_size - hr.min); m_IndexBuffer.SetData(m_TmpUpdater, 0, base_offset + TranslateIndex(new Vector3Int(mx, brick_min.y - hr.min, mz)), Mathf.Min(brick_cell_size, highest_limit - brick_min.y)); } else { m_IndexBuffer.GetData(m_TmpUpdater, shift_cnt, base_offset + TranslateIndex(new Vector3Int(mx, 0, mz)), hr.cnt); hr.min = lowest_limit; hr.cnt += shift_cnt; m_IndexBuffer.SetData(m_TmpUpdater, 0, base_offset + TranslateIndex(new Vector3Int(mx, 0, mz)), hr.cnt); // restore pool idx array for (int cidx = shift_cnt; cidx < brick_cell_size; cidx++) { m_TmpUpdater[cidx] = rbrick.flattenedIdx; } } } // update the column offset m_HeightRanges[hoff_idx] = hr; m_TmpUpdater[m_TmpUpdater.Length - 1] = hr.min; m_IndexBuffer.SetData(m_TmpUpdater, m_TmpUpdater.Length - 1, kAPVConstantsSize + hoff_idx, 1); } } } }
private void ClearVoxel(Vector3Int pos) { // clip voxel to index space Vector3Int volMin, volMax; ClipToIndexSpace(pos, m_VoxelSubdivLevel, out volMin, out volMax); int base_offset = kAPVConstantsSize + m_IndexDim.x * m_IndexDim.z; int volCellSize = ProbeReferenceVolume.CellSize(m_VoxelSubdivLevel); int bsize_x = volMax.x - volMin.x; int bsize_z = volMax.z - volMin.z; if (bsize_x <= 0 || bsize_z <= 0) { return; } for (int idx = 0; idx < volCellSize; idx++) { m_TmpUpdater[idx] = -1; } int posIS_x = m_CenterIS.x + volMin.x; int posIS_z = m_CenterIS.z + volMin.z; // iterate over z then x, as y needs special handling for updating the base offset for (int z = 0; z < bsize_z; z++) { for (int x = 0; x < bsize_x; x++) { int mx = (posIS_x + x) % m_IndexDim.x; int mz = (posIS_z + z) % m_IndexDim.z; int hoff_idx = mz * m_IndexDim.x + mx; HeightRange hr = m_HeightRanges[hoff_idx]; if (hr.min == -1) { continue; } m_IndexBuffer.GetData(m_TmpUpdater, 0, base_offset + TranslateIndex(new Vector3Int(mx, 0, mz)), hr.cnt); int start = volMin.y - hr.min; int end = Mathf.Min(start + volCellSize, m_IndexDim.y); start = Mathf.Max(start, 0); for (int i = start; i < end; i++) { m_TmpUpdater[i] = -1; } int hmin = m_IndexDim.y, hmax = -1; for (int i = 0; i < m_IndexDim.y; i++) { if (m_TmpUpdater[i] != -1) { hmin = Mathf.Min(hmin, i); hmax = Mathf.Max(hmax, i); } } bool all_cleared = hmin == m_IndexDim.y; if (all_cleared) { hr.min = -1; hr.cnt = 0; m_IndexBuffer.SetData(m_TmpUpdater, 0, base_offset + TranslateIndex(new Vector3Int(mx, 0, mz)), m_IndexDim.y); } else { hr.min += hmin; hr.cnt = hmax - hmin; m_IndexBuffer.SetData(m_TmpUpdater, hmin, base_offset + TranslateIndex(new Vector3Int(mx, 0, mz)), m_IndexDim.y - hmin); m_IndexBuffer.SetData(m_TmpUpdater, 0, base_offset + TranslateIndex(new Vector3Int(mx, 0, mz)), hmin); } // update the column offset m_HeightRanges[hoff_idx] = hr; m_TmpUpdater[m_TmpUpdater.Length - 1] = hr.min; m_IndexBuffer.SetData(m_TmpUpdater, m_TmpUpdater.Length - 1, kAPVConstantsSize + hoff_idx, 1); } } }
public void AddBricks(RegId id, List <Brick> bricks, List <Chunk> allocations, int allocationSize, int poolWidth, int poolHeight) { Debug.Assert(bricks.Count <= ushort.MaxValue, "Cannot add more than 65K bricks per RegId."); int largest_cell = ProbeReferenceVolume.CellSize(15); // create a new copy BrickMeta bm = new BrickMeta(); bm.voxels = new HashSet <Vector3Int>(); bm.bricks = new List <ReservedBrick>(bricks.Count); m_BricksToVoxels.Add(id, bm); int brick_idx = 0; // find all voxels each brick will touch for (int i = 0; i < allocations.Count; i++) { Chunk alloc = allocations[i]; int cnt = Mathf.Min(allocationSize, bricks.Count - brick_idx); for (int j = 0; j < cnt; j++, brick_idx++, alloc.x += ProbeBrickPool.kBrickProbeCountPerDim) { Brick brick = bricks[brick_idx]; int cellSize = ProbeReferenceVolume.CellSize(brick.size); Debug.Assert(cellSize <= largest_cell, "Cell sizes are not correctly sorted."); largest_cell = Mathf.Min(largest_cell, cellSize); MapBrickToVoxels(brick, bm.voxels); ReservedBrick rbrick = new ReservedBrick(); rbrick.brick = brick; rbrick.flattenedIdx = MergeIndex(alloc.flattenIndex(poolWidth, poolHeight), brick.size); bm.bricks.Add(rbrick); foreach (var v in bm.voxels) { List <VoxelMeta> vm_list; if (!m_VoxelToBricks.TryGetValue(v, out vm_list)) // first time the voxel is touched { vm_list = new List <VoxelMeta>(1); m_VoxelToBricks.Add(v, vm_list); } VoxelMeta vm; int vm_idx = vm_list.FindIndex((VoxelMeta lhs) => lhs.id == id); if (vm_idx == -1) // first time a brick from this id has touched this voxel { vm.id = id; vm.brickIndices = new List <ushort>(4); vm_list.Add(vm); } else { vm = vm_list[vm_idx]; } // add this brick to the voxel under its regId vm.brickIndices.Add((ushort)brick_idx); } } } foreach (var voxel in bm.voxels) { UpdateIndex(voxel); } }
static void DoApplyVirtualOffsets(Vector3[] probePositions, out Vector3[] probeOffsets, VirtualOffsetSettings voSettings) { // Limit memory usage based on ray cast / hit structures (of which there are lots per position) int maxPositionsPerBatch; { var rayCastBytesPerPosition = UnsafeUtility.SizeOf <RaycastCommand>() * kRayDirectionsPerPosition; var rayHitBytesPerPosition = UnsafeUtility.SizeOf <RaycastHit>() * kRayDirectionsPerPosition * voSettings.maxHitsPerRay; var rayDataBytesPerPosition = rayCastBytesPerPosition + rayHitBytesPerPosition; maxPositionsPerBatch = (kMaxMemoryUsage / 2) / rayDataBytesPerPosition; #if VERBOSE Debug.Log($"Running virtual offset over {(probePositions.Length + maxPositionsPerBatch - 1)/maxPositionsPerBatch} batches."); #endif } // This data is shared across all jobs var positions = new NativeArray <Vector3>(probePositions, Allocator.TempJob); var offsets = new NativeArray <Vector3>(probePositions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); var searchDistanceForPosition = new NativeArray <float>(positions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); var positionHasColliders = new NativeArray <bool>(positions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); // Allocate ray cast/hit data var raycastCommands = new[] { new NativeArray <RaycastCommand>(maxPositionsPerBatch * kRayDirectionsPerPosition, Allocator.TempJob, NativeArrayOptions.UninitializedMemory), new NativeArray <RaycastCommand>(maxPositionsPerBatch * kRayDirectionsPerPosition, Allocator.TempJob, NativeArrayOptions.UninitializedMemory) }; { // We need to set a known per-ray maxHits up-front since raycast command schedule reads this at schedule time. This is a bit annoying but it's a // price we'll have to pay right now to be able to create commands from a job. QueryParameters queryParams = new QueryParameters(); queryParams.hitBackfaces = true; queryParams.layerMask = 0; var defaultRaycastCommand = new RaycastCommand(Vector3.zero, Vector3.zero, queryParams, 0f); for (var i = 0; i < maxPositionsPerBatch * kRayDirectionsPerPosition; ++i) { raycastCommands[0][i] = raycastCommands[1][i] = defaultRaycastCommand; } } var raycastHits = new[] { new NativeArray <RaycastHit>(maxPositionsPerBatch * kRayDirectionsPerPosition * voSettings.maxHitsPerRay, Allocator.TempJob, NativeArrayOptions.UninitializedMemory), new NativeArray <RaycastHit>(maxPositionsPerBatch * kRayDirectionsPerPosition * voSettings.maxHitsPerRay, Allocator.TempJob, NativeArrayOptions.UninitializedMemory) }; // Create job data var createRayCastCommandsJob = new CreateRayCastCommandsJob { voSettings = voSettings, positions = positions, positionHasColliders = positionHasColliders, searchDistanceForPosition = searchDistanceForPosition }; var pushOutGeometryJob = new PushOutGeometryJob { voSettings = voSettings, positions = positions, offsets = offsets, positionHasColliders = positionHasColliders, }; var jobHandles = new JobHandle[2]; try { #if VERBOSE var positionsWithColliders = 0; #endif for (int globalPosIdx = 0, nextBatchIdx = -1; globalPosIdx < positions.Length; globalPosIdx += maxPositionsPerBatch) { // Run a quick overlap check for each search box before setting up rays for the position var batchPosStart = globalPosIdx; var batchPosEnd = Mathf.Min(positions.Length, batchPosStart + maxPositionsPerBatch); for (var batchPosIdx = batchPosStart; batchPosIdx < batchPosEnd; ++batchPosIdx) { m_BakingBatch.uniqueBrickSubdiv.TryGetValue(positions[batchPosIdx], out var subdivLevel); var brickSize = ProbeReferenceVolume.CellSize(subdivLevel); var searchDistance = (brickSize * m_BakingProfile.minBrickSize) / ProbeBrickPool.kBrickCellCount; var scaleForSearchDist = voSettings.searchMultiplier; var distanceSearch = scaleForSearchDist * searchDistance; var positionHasCollider = Physics.CheckBox(positions[batchPosIdx], new Vector3(distanceSearch, distanceSearch, distanceSearch), Quaternion.identity, voSettings.collisionMask); #if VERBOSE if (positionHasCollider) { ++positionsWithColliders; } #endif searchDistanceForPosition[batchPosIdx] = distanceSearch; positionHasColliders[batchPosIdx] = positionHasCollider; } // Swap buffers and sync any already running job at that slot nextBatchIdx = (nextBatchIdx + 1) % 2; jobHandles[nextBatchIdx].Complete(); // Assign ranges and ray/hit arrays createRayCastCommandsJob.startIdx = batchPosStart; createRayCastCommandsJob.endIdx = batchPosEnd; createRayCastCommandsJob.raycastCommands = raycastCommands[nextBatchIdx]; pushOutGeometryJob.startIdx = batchPosStart; pushOutGeometryJob.endIdx = batchPosEnd; pushOutGeometryJob.raycastCommands = raycastCommands[nextBatchIdx]; pushOutGeometryJob.raycastHits = raycastHits[nextBatchIdx]; #if VERBOSE Debug.Log($"Dispatching batch {batchPosStart/maxPositionsPerBatch} {batchPosStart} - {batchPosEnd} using index {nextBatchIdx} (accumulated colliders {positionsWithColliders}"); #endif #if USE_JOBS // Kick off jobs immediately var createRayCastCommandsJobHandle = createRayCastCommandsJob.Schedule(); var raycastCommandsJobHandle = RaycastCommand.ScheduleBatch(raycastCommands[nextBatchIdx], raycastHits[nextBatchIdx], kMinCommandsPerJob, voSettings.maxHitsPerRay, createRayCastCommandsJobHandle); jobHandles[nextBatchIdx] = pushOutGeometryJob.Schedule(raycastCommandsJobHandle); JobHandle.ScheduleBatchedJobs(); #else // Run jobs in-place for easier debugging createRayCastCommandsJob.Run(); RaycastCommand.ScheduleBatch(raycastCommands[nextBatchIdx], raycastHits[nextBatchIdx], voSettings.maxHitsPerRay, kMinCommandsPerJob).Complete(); pushOutGeometryJob.Run(); #endif } // Sync any in-flight jobs (order doesn't matter) JobHandle.CompleteAll(ref jobHandles[0], ref jobHandles[1]); // Copy out result data positions.CopyTo(probePositions); probeOffsets = offsets.ToArray(); #if VERBOSE Debug.Log($"Earlied out {positions.Length - positionsWithColliders}/{positions.Length} probe positions from virtual offset."); Debug.Log($"Working memory used: {(raycastCommands[0].Length * UnsafeUtility.SizeOf<RaycastCommand>() * 2 + raycastHits[0].Length * UnsafeUtility.SizeOf<RaycastHit>() * 2) / 1024 / 1024} MB"); #endif } catch (System.Exception e) { Debug.LogException(e); JobHandle.CompleteAll(ref jobHandles[0], ref jobHandles[1]); probeOffsets = null; } finally { positions.Dispose(); offsets.Dispose(); searchDistanceForPosition.Dispose(); positionHasColliders.Dispose(); raycastCommands[0].Dispose(); raycastCommands[1].Dispose(); raycastHits[0].Dispose(); raycastHits[1].Dispose(); } }
public void AddBricks(Cell cell, NativeArray <Brick> bricks, List <Chunk> allocations, int allocationSize, int poolWidth, int poolHeight, CellIndexUpdateInfo cellInfo) { Debug.Assert(bricks.Length <= ushort.MaxValue, "Cannot add more than 65K bricks per RegId."); int largest_cell = ProbeReferenceVolume.CellSize(kMaxSubdivisionLevels); g_Cell = cell; // create a new copy BrickMeta bm = m_BrickMetaPool.Get(); m_BricksToVoxels.Add(cell, bm); int brick_idx = 0; // find all voxels each brick will touch for (int i = 0; i < allocations.Count; i++) { Chunk alloc = allocations[i]; int cnt = Mathf.Min(allocationSize, bricks.Length - brick_idx); for (int j = 0; j < cnt; j++, brick_idx++, alloc.x += ProbeBrickPool.kBrickProbeCountPerDim) { Brick brick = bricks[brick_idx]; int cellSize = ProbeReferenceVolume.CellSize(brick.subdivisionLevel); Debug.Assert(cellSize <= largest_cell, "Cell sizes are not correctly sorted."); largest_cell = Mathf.Min(largest_cell, cellSize); MapBrickToVoxels(brick, bm.voxels); ReservedBrick rbrick = new ReservedBrick(); rbrick.brick = brick; rbrick.flattenedIdx = MergeIndex(alloc.flattenIndex(poolWidth, poolHeight), brick.subdivisionLevel); bm.bricks.Add(rbrick); foreach (var v in bm.voxels) { List <VoxelMeta> vm_list; if (!m_VoxelToBricks.TryGetValue(v, out vm_list)) // first time the voxel is touched { vm_list = m_VoxelMetaListPool.Get(); m_VoxelToBricks.Add(v, vm_list); } VoxelMeta vm = null; int vm_idx = vm_list.FindIndex((VoxelMeta lhs) => lhs.cell == g_Cell); if (vm_idx == -1) // first time a brick from this id has touched this voxel { vm = m_VoxelMetaPool.Get(); vm.cell = cell; vm_list.Add(vm); } else { vm = vm_list[vm_idx]; } // add this brick to the voxel under its regId vm.brickIndices.Add((ushort)brick_idx); } } } foreach (var voxel in bm.voxels) { UpdateIndexForVoxel(voxel, cellInfo); } }
// 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(BakingCell bakingCell) { var bricks = bakingCell.bricks; var cell = bakingCell; int chunkSize = ProbeBrickPool.GetChunkSizeInBrickCount(); int brickChunksCount = (bricks.Length + chunkSize - 1) / chunkSize; var probeHasEmptySpaceInGrid = new NativeArray <bool>(bakingCell.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_Validity_locData.Resize(size); s_ProbeIndices.Resize(size); Dictionary <Vector3Int, (float, Vector3, Bounds)> probesToRestoreInfo = new Dictionary <Vector3Int, (float, Vector3, Bounds)>(); 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, shidx); } else { StoreScratchData(ix, iy, iz, locSize.x, locSize.y, cell.validity[shidx], shidx); // Check if we need to do some extra check on this probe. bool hasFreeNeighbourhood = false; Bounds invalidatingTouchupBound; if (s_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_BakingProfile.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); float probeValidity = ReadValidity(x, y, z, locSize.x, locSize.y); if (outIdx < cell.validity.Length) { float[] validities = new float[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] = ReadValidity(samplePos.x, samplePos.y, samplePos.z, locSize.x, locSize.y); } byte mask = forceAllValid ? (byte)255 : Convert.ToByte(PackValidity(validities)); float validity = probeValidity; cell.validity[outIdx] = validity; cell.validityNeighbourMask[outIdx] = mask; } } } } } probeHasEmptySpaceInGrid.Dispose(); }