public static void Update() { var octree = new ChunkedOctree <bool>(3); Assert.False(octree.Get(new(0, 0, 0))); Assert.False(octree.Get(new(1, 1, 1))); Assert.False(octree.Get(new(-1, -1, -1))); octree.Update(new(0, 0, 0), (int level, ReadOnlySpan <bool> children, ref bool parent) => parent = true); Assert.True(octree.Get(new(0, 0, 0))); Assert.True(octree.Get(0, new(0, 0, 0))); Assert.True(octree.Get(1, new(0, 0, 0))); Assert.True(octree.Get(2, new(0, 0, 0))); Assert.True(octree.Get(3, new(0, 0, 0))); Assert.False(octree.Get(0, new(1, 1, 1))); Assert.False(octree.Get(1, new(2, 2, 2))); Assert.False(octree.Get(2, new(4, 4, 4))); Assert.False(octree.Get(3, new(8, 8, 8))); Assert.False(octree.Get(0, new(-1, -1, -1))); Assert.False(octree.Get(1, new(-1, -1, -1))); Assert.False(octree.Get(2, new(-1, -1, -1))); Assert.False(octree.Get(3, new(-1, -1, -1))); octree.Update(new(-1, -1, -1), (int level, ReadOnlySpan <bool> children, ref bool parent) => parent = true); Assert.True(octree.Get(new(-1, -1, -1))); Assert.True(octree.Get(0, new(-1, -1, -1))); Assert.True(octree.Get(1, new(-1, -1, -1))); Assert.True(octree.Get(2, new(-1, -1, -1))); Assert.True(octree.Get(3, new(-1, -1, -1))); }
public void Work() { var octree = new ChunkedOctree<State>(5); while (true) { var (px, py, pz) = _playerPosition; // TODO: Re-use the enumerator if position is still (roughly) the same octree wasn't modified. var enumerator = octree.Find( (level, pos, state) => { if ((state & State.GeneratedAll) == State.GeneratedAll) return null; var (minX, minY, minZ) = pos << Chunk.BIT_SHIFT << level; var maxX = minX + (1 << Chunk.BIT_SHIFT << level); var maxY = minY + (1 << Chunk.BIT_SHIFT << level); var maxZ = minZ + (1 << Chunk.BIT_SHIFT << level); var dx = (px < minX) ? minX - px : (px > maxX) ? maxX - px : 0.0F; var dy = (py < minY) ? minY - py : (py > maxY) ? maxY - py : 0.0F; var dz = (pz < minZ) ? minZ - pz : (pz > maxZ) ? maxZ - pz : 0.0F; return dx * dx + dy * dy + dz * dz; }, _playerPosition.ToChunkPos()) .Where(x => x.Weight <= DISTANCE_SQUARED) .GetEnumerator(); while (enumerator.MoveNext()) { var (chunkPos, _, _) = enumerator.Current; var chunk = (Chunk)_chunks.GetChunkOrCreate(chunkPos); // TODO: This is cobbled together. Would not work if chunks are created outside of this class. if (chunk.NumGeneratedNeighbors < 0) chunk.NumGeneratedNeighbors = Neighbors.ALL.Select(n => chunkPos + n) .Count(pos => (octree.Get(pos) & State.GeneratedAll) == State.GeneratedAll); // Find the generator that needs to be applied next. var generator = Generators .Where(g => !chunk.AppliedGenerators.Contains(g.Identifier)) .FirstOrNull(); if (generator != null) { // TODO: Instead of skipping this chunk if dependent neighboring chunks // haven't been generated, immediately start to generate that chunk. // In the same line, generate as much of the chunk as possible? if (!generator.NeighborDependencies.All( dep => _chunks.GetChunkOrNull(chunkPos + dep.Neighbor) ?.AppliedGenerators.Contains(dep.Generator) == true)) continue; // TODO: This doesn't seem safe to run in a thread? generator.Populate(chunk); chunk.AppliedGenerators.Add(generator.Identifier); break; } else { // If no generator was found, the chunk has completed generating. octree.Update(chunkPos, (int level, ReadOnlySpan<State> children, ref State parent) => { var mask = State.GeneratedAll; foreach (var childState in children) if ((childState & State.GeneratedAll) != State.GeneratedAll) { mask = State.GeneratedSome; break; } parent |= mask; }); foreach (var neighbor in chunk.Neighbors) if (++neighbor.NumGeneratedNeighbors == Neighbors.ALL.Count) _world.Schedule(() => ((ChunkManager)_chunks).InvokeChunkReady(neighbor)); break; } } Thread.Sleep(0); } }