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 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);
        }
Beispiel #3
0
	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);
		}
	}