Beispiel #1
0
        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));
                    }
                }
            }
        }
Beispiel #3
0
        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;
        }
Beispiel #4
0
        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);
        }
Beispiel #6
0
        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);
            }
        }
Beispiel #10
0
        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();
            }
        }
Beispiel #11
0
        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);
            }
        }
Beispiel #12
0
        // 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();
        }