public static void Find() { var octree = new ChunkedOctree <bool>(3); octree.Update(new(0, 0, 0), (int level, ReadOnlySpan <bool> children, ref bool parent) => parent = true); octree.Update(new(-1, -1, -1), (int level, ReadOnlySpan <bool> children, ref bool parent) => parent = true); octree.Update(new(2, 2, 2), (int level, ReadOnlySpan <bool> children, ref bool parent) => parent = true); var searchFrom = new Vector3(8, 8, 8); var(px, py, pz) = searchFrom; var enumerator = octree.Find( (level, pos, state) => { if (!state) { return(null); } var(minX, minY, minZ) = pos << level << 4; var maxX = minX + (1 << 4 << level); var maxY = minY + (1 << 4 << level); var maxZ = minZ + (1 << 4 << 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); }, searchFrom.ToChunkPos()) .GetEnumerator(); Assert.True(enumerator.MoveNext()); var first = enumerator.Current; Assert.True(enumerator.MoveNext()); var second = enumerator.Current; Assert.True(enumerator.MoveNext()); var third = enumerator.Current; Assert.False(enumerator.MoveNext()); Assert.Equal(new(0, 0, 0), first.ChunkPos); Assert.Equal(0, first.Weight); Assert.True(first.Value); Assert.Equal(new(-1, -1, -1), second.ChunkPos); Assert.Equal(8 * 8 + 8 * 8 + 8 * 8, second.Weight); Assert.True(second.Value); Assert.Equal(new(2, 2, 2), third.ChunkPos); Assert.Equal(24 * 24 + 24 * 24 + 24 * 24, third.Weight); Assert.True(third.Value); }
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); } }