public BlockVolume(World world, BlockPosition anchor, int xExtent, int yExtent, int zExtent) { this.world = world; int minimumX = Math.Min(anchor.X, anchor.X + xExtent); int minimumY = Math.Min(anchor.Y, anchor.Y + yExtent); int minimumZ = Math.Min(anchor.Z, anchor.Z + zExtent); int maximumX = Math.Max(anchor.X, anchor.X + xExtent); int maximumY = Math.Max(anchor.Y, anchor.Y + yExtent); int maximumZ = Math.Max(anchor.Z, anchor.Z + zExtent); Maximum = new BlockPosition(maximumX, maximumY, maximumZ); Minimum = new BlockPosition(minimumX, minimumY, minimumZ); }
void RecursivelyPropagateLight(World world, BlockPosition blockPosition, byte incomingLightValue) { TotalNumberOfRecursions++; NumberOfRecursions++; if (incomingLightValue < 1) { return; } var block = world.GetBlockAt(blockPosition); if (block.LightLevel >= incomingLightValue) { return; } if (!block.CanPropagateLight) { return; } world.SetLightLevel(blockPosition, incomingLightValue); //Trace.WriteLine(string.Format("Replaced light value {0} with {1} at {2}", block.LightLevel, incomingLightValue, blockPosition)); var outgoingLightLevel = (byte)(incomingLightValue - 1); // TODO: right now this is a depth-first fill. We really need to do a breadth-first fill so that blocks gets filled // first with the highest possible levels of light and we don't spend time overwriting lower levels from long recursion // chains that doubled back with higher levels from shorter recursion chains. // http://en.wikipedia.org/wiki/Iterative_deepening // It would probably be smart to get some unit tests going before messing with this too much. // It might also be interesting to do iterative deepening across the whole chunk so that multiple // sunlit blocks contributing to a large dark space fill it in cooperatively rather than the first // block flood-filling the whole space, then the second doing the same with slightly different values. // That would probably not be a net win but it's worth thinking about. We could do a first pass on all // sunlit blocks and make a list of just the ones that managed to propogate light somewhere, then do // iterative deepening on just those. if (outgoingLightLevel > 0) { RecursivelyPropagateLight(world, blockPosition.Left, outgoingLightLevel); RecursivelyPropagateLight(world, blockPosition.Right, outgoingLightLevel); RecursivelyPropagateLight(world, blockPosition.Up, outgoingLightLevel); RecursivelyPropagateLight(world, blockPosition.Down, outgoingLightLevel); RecursivelyPropagateLight(world, blockPosition.Front, outgoingLightLevel); RecursivelyPropagateLight(world, blockPosition.Back, outgoingLightLevel); } }
public void PropagateSunlightFromBlock(World world, BlockPosition blockPosition) { NumberOfRecursions = 0; // We assume that the block position passed to us has full sunlight and can // propogate light. const byte newLightValue = (byte)(World.MaximumLightLevel - 1); // We know we're starting with a sunlit block so we don't need to recurse up because // anything above this block is either sunlit or void. We don't need to recurse down // because anything below this block will either be sunlit or solid. RecursivelyPropagateLight(world, blockPosition.Left, newLightValue); RecursivelyPropagateLight(world, blockPosition.Right, newLightValue); RecursivelyPropagateLight(world, blockPosition.Front, newLightValue); RecursivelyPropagateLight(world, blockPosition.Back, newLightValue); }
public void PropagateLightFromBlock(World world, BlockPosition blockPosition) { NumberOfRecursions = 0; var block = world.GetBlockAt(blockPosition); if (!block.CanPropagateLight) { return; } var newLightValue = (byte)(block.LightLevel - 1); RecursivelyPropagateLight(world, blockPosition.Left, newLightValue); RecursivelyPropagateLight(world, blockPosition.Right, newLightValue); RecursivelyPropagateLight(world, blockPosition.Up, newLightValue); RecursivelyPropagateLight(world, blockPosition.Down, newLightValue); RecursivelyPropagateLight(world, blockPosition.Front, newLightValue); RecursivelyPropagateLight(world, blockPosition.Back, newLightValue); }
public Chunk(World world, ChunkPosition position, IEnvironmentGenerator environmentGenerator, IChunkRenderer renderer, BlockPrototypeMap prototypeMap) { Position = position; this.world = world; this.environmentGenerator = environmentGenerator; this.renderer = renderer; blockArray = new BlockArray(prototypeMap, XDimension, YDimension, ZDimension); lightArray = new Array3<byte>(XDimension, YDimension, ZDimension); OriginInWorld = new BlockPosition(Position, new RelativeBlockPosition(0, 0, 0)); neighborhoodPositions = new[] { Position, Position.Left, Position.Right, Position.Front, Position.Back }; }
public BlockPrototype this[BlockPosition location] { get { return this[location.X, location.Y, location.Z]; } set { blockIndexes[location.X, location.Y, location.Z] = prototypeMap[value]; } }
public void TessellateBlock(List<VertexPositionColorLighting>[] vertexLists, List<short>[] indexLists, BlockPosition worldBlockPosition, RelativeBlockPosition relativeBlockPosition) { GetRequiredBlocks(worldBlockPosition); BuildLeftQuad(vertexLists[Face.Left], indexLists[Face.Left], relativeBlockPosition); BuildRightQuad(vertexLists[Face.Right], indexLists[Face.Right], relativeBlockPosition); BuildFrontQuad(vertexLists[Face.Front], indexLists[Face.Front], relativeBlockPosition); BuildBackQuad(vertexLists[Face.Back], indexLists[Face.Back], relativeBlockPosition); BuildTopQuad(vertexLists[Face.Top], indexLists[Face.Top], relativeBlockPosition); BuildBottomQuad(vertexLists[Face.Bottom], indexLists[Face.Bottom], relativeBlockPosition); NumberOfBlocksTessellated++; }
void GetRequiredBlocks(BlockPosition worldBlockPosition) { // We're putting in some work here to try to avoid loading the same blocks // multiple times for various faces of the block we're working on. This // is only useful for blocks where we're drawing multiple faces, which isn't // many of them relatively speaking. It's just barely worth the complexity. // TODO: depending on how things work out, it might be better to just do a // marching load of blocks along the z axis. That would be 9 blocks loaded // per tessellation, and we're currently doing ~7, but that could change as // we mess with the terrain and algorithms. // TODO: We can save a little time by getting the world // positions by relative arithmetic rather than direction chains. upBlock = world.GetBlockAt(worldBlockPosition.Up); if (upBlock.CanBeSeenThrough) { upFrontBlock = world.GetBlockAt(worldBlockPosition.Up.Front); upBackBlock = world.GetBlockAt(worldBlockPosition.Up.Back); upLeftFrontBlock = world.GetBlockAt(worldBlockPosition.Up.Left.Front); upLeftBlock = world.GetBlockAt(worldBlockPosition.Up.Left); upLeftBackBlock = world.GetBlockAt(worldBlockPosition.Up.Left.Back); upRightFrontBlock = world.GetBlockAt(worldBlockPosition.Up.Right.Front); upRightBlock = world.GetBlockAt(worldBlockPosition.Up.Right); upRightBackBlock = world.GetBlockAt(worldBlockPosition.Up.Right.Back); } else { upLeftBlock = null; upBackBlock = null; upLeftBackBlock = null; upRightBlock = null; upRightBackBlock = null; upFrontBlock = null; upRightFrontBlock = null; upLeftFrontBlock = null; } leftBlock = world.GetBlockAt(worldBlockPosition.Left); if (leftBlock.CanBeSeenThrough) { leftFrontBlock = world.GetBlockAt(worldBlockPosition.Left.Front); leftBackBlock = world.GetBlockAt(worldBlockPosition.Left.Back); leftUpFrontBlock = upLeftFrontBlock ?? world.GetBlockAt(worldBlockPosition.Left.Up.Front); leftUpBlock = upLeftBlock ?? world.GetBlockAt(worldBlockPosition.Left.Up); leftUpBackBlock = upLeftBackBlock ?? world.GetBlockAt(worldBlockPosition.Left.Up.Back); leftDownFrontBlock = world.GetBlockAt(worldBlockPosition.Left.Down.Front); leftDownBlock = world.GetBlockAt(worldBlockPosition.Left.Down); leftDownBackBlock = world.GetBlockAt(worldBlockPosition.Left.Down.Back); } else { leftUpBlock = null; leftBackBlock = null; leftUpBackBlock = null; leftFrontBlock = null; leftUpFrontBlock = null; leftDownBlock = null; leftDownFrontBlock = null; leftDownBackBlock = null; } rightBlock = world.GetBlockAt(worldBlockPosition.Right); if (rightBlock.CanBeSeenThrough) { rightFrontBlock = world.GetBlockAt(worldBlockPosition.Right.Front); rightBackBlock = world.GetBlockAt(worldBlockPosition.Right.Back); rightUpFrontBlock = upRightFrontBlock ?? world.GetBlockAt(worldBlockPosition.Right.Up.Front); rightUpBlock = upRightBlock ?? world.GetBlockAt(worldBlockPosition.Right.Up); rightUpBackBlock = upRightBackBlock ?? world.GetBlockAt(worldBlockPosition.Right.Up.Back); rightDownFrontBlock = world.GetBlockAt(worldBlockPosition.Right.Down.Front); rightDownBlock = world.GetBlockAt(worldBlockPosition.Right.Down); rightDownBackBlock = world.GetBlockAt(worldBlockPosition.Right.Down.Back); } else { rightUpBlock = null; rightFrontBlock = null; rightUpFrontBlock = null; rightBackBlock = null; rightUpBackBlock = null; rightDownBlock = null; rightDownBackBlock = null; rightDownFrontBlock = null; } backBlock = world.GetBlockAt(worldBlockPosition.Back); if (backBlock.CanBeSeenThrough) { backLeftBlock = leftBackBlock ?? world.GetBlockAt(worldBlockPosition.Back.Left); backRightBlock = rightBackBlock ?? world.GetBlockAt(worldBlockPosition.Back.Right); backUpLeftBlock = upLeftBackBlock ?? leftUpBackBlock ?? world.GetBlockAt(worldBlockPosition.Back.Up.Left); backUpBlock = upBackBlock ?? world.GetBlockAt(worldBlockPosition.Back.Up); backUpRightBlock = upRightBackBlock ?? rightUpBackBlock ?? world.GetBlockAt(worldBlockPosition.Back.Up.Right); backDownLeftBlock = leftDownBackBlock ?? world.GetBlockAt(worldBlockPosition.Back.Down.Left); backDownBlock = world.GetBlockAt(worldBlockPosition.Back.Down); backDownRightBlock = rightDownBackBlock ?? world.GetBlockAt(worldBlockPosition.Back.Down.Right); } else { backUpBlock = null; backRightBlock = null; backUpRightBlock = null; backLeftBlock = null; backUpLeftBlock = null; backDownBlock = null; backDownLeftBlock = null; backDownRightBlock = null; } frontBlock = world.GetBlockAt(worldBlockPosition.Front); if (frontBlock.CanBeSeenThrough) { frontLeftBlock = leftFrontBlock ?? world.GetBlockAt(worldBlockPosition.Front.Left); frontRightBlock = rightFrontBlock ?? world.GetBlockAt(worldBlockPosition.Front.Right); frontUpLeftBlock = upLeftFrontBlock ?? leftUpFrontBlock ?? world.GetBlockAt(worldBlockPosition.Front.Up.Left); frontUpBlock = upFrontBlock ?? world.GetBlockAt(worldBlockPosition.Front.Up); frontUpRightBlock = upRightFrontBlock ?? rightUpFrontBlock ?? world.GetBlockAt(worldBlockPosition.Front.Up.Right); frontDownLeftBlock = leftDownFrontBlock ?? world.GetBlockAt(worldBlockPosition.Front.Down.Left); frontDownBlock = world.GetBlockAt(worldBlockPosition.Front.Down); frontDownRightBlock = rightDownFrontBlock ?? world.GetBlockAt(worldBlockPosition.Front.Down.Right); } else { frontUpBlock = null; frontLeftBlock = null; frontUpLeftBlock = null; frontRightBlock = null; frontUpRightBlock = null; frontDownBlock = null; frontDownRightBlock = null; frontDownLeftBlock = null; } downBlock = world.GetBlockAt(worldBlockPosition.Down); if (downBlock.CanBeSeenThrough) { downFrontBlock = frontDownBlock ?? world.GetBlockAt(worldBlockPosition.Down.Front); downBackBlock = backDownBlock ?? world.GetBlockAt(worldBlockPosition.Down.Back); downLeftFrontBlock = leftDownFrontBlock ?? frontDownLeftBlock ?? world.GetBlockAt(worldBlockPosition.Down.Left.Front); downLeftBlock = leftDownBlock ?? world.GetBlockAt(worldBlockPosition.Down.Left); downLeftBackBlock = leftDownBackBlock ?? backDownLeftBlock ?? world.GetBlockAt(worldBlockPosition.Down.Left.Back); downRightFrontBlock = rightDownFrontBlock ?? frontDownRightBlock ?? world.GetBlockAt(worldBlockPosition.Down.Right.Front); downRightBlock = rightDownBlock ?? world.GetBlockAt(worldBlockPosition.Down.Right); downRightBackBlock = rightDownBackBlock ?? backDownRightBlock ?? world.GetBlockAt(worldBlockPosition.Down.Right.Back); } // Don't need to null these out because they're not used anywhere else }
public BlockVolume(World world, BlockPosition center, int radius) { this.world = world; Maximum = new BlockPosition(center.X + radius, center.Y + radius, center.Z + radius); Minimum = new BlockPosition(center.X - radius, center.Y - radius, center.Z - radius); }
public BlockVolume(World world, BlockPosition minimum, BlockPosition maximum) { this.world = world; Minimum = minimum; Maximum = maximum; }
public static IntersectionResult Intersects(this Ray ray, World world, float maximumRayLength) { // From A Fast Voxel Traversal Algorithm for Ray Tracing: http://www.cse.yorku.ca/~amana/research/grid.pdf var currentBlockPosition = new BlockPosition(ray.Position); int stepX, stepY, stepZ; float tMaxX, tMaxY, tMaxZ; float tDeltaX, tDeltaY, tDeltaZ; if (ray.Direction.X > 0) { stepX = 1; tDeltaX = 1 / ray.Direction.X; tMaxX = (currentBlockPosition.X + 1 - ray.Position.X) * tDeltaX; } else if (ray.Direction.X < 0) { stepX = -1; tDeltaX = 1 / -ray.Direction.X; tMaxX = (ray.Position.X - currentBlockPosition.X) * tDeltaX; } else { stepX = 0; tDeltaX = 0; tMaxX = float.MaxValue; } if (ray.Direction.Y > 0) { stepY = 1; tDeltaY = 1 / ray.Direction.Y; tMaxY = (currentBlockPosition.Y + 1 - ray.Position.Y) * tDeltaY; } else if (ray.Direction.Y < 0) { stepY = -1; tDeltaY = 1 / -ray.Direction.Y; tMaxY = (ray.Position.Y - currentBlockPosition.Y) * tDeltaY; } else { stepY = 0; tDeltaY = 0; tMaxY = float.MaxValue; } if (ray.Direction.Z > 0) { stepZ = 1; tDeltaZ = 1 / ray.Direction.Z; tMaxZ = (currentBlockPosition.Z + 1 - ray.Position.Z) * tDeltaZ; } else if (ray.Direction.Z < 0) { stepZ = -1; tDeltaZ = 1 / -ray.Direction.Z; tMaxZ = (ray.Position.Z - currentBlockPosition.Z) * tDeltaZ; } else { stepZ = 0; tDeltaZ = 0; tMaxZ = float.MaxValue; } float currentRayLength; do { Face intersectedFace; if (tMaxX < tMaxY) { if (tMaxX < tMaxZ) { currentRayLength = tMaxX; intersectedFace = Face.X; currentBlockPosition.X += stepX; tMaxX = tMaxX + tDeltaX; } else { currentRayLength = tMaxZ; intersectedFace = Face.Z; currentBlockPosition.Z += stepZ; tMaxZ = tMaxZ + tDeltaZ; } } else { if (tMaxY < tMaxZ) { currentRayLength = tMaxY; intersectedFace = Face.Y; currentBlockPosition.Y += stepY; tMaxY = tMaxY + tDeltaY; } else { currentRayLength = tMaxZ; intersectedFace = Face.Z; currentBlockPosition.Z += stepZ; tMaxZ = tMaxZ + tDeltaZ; } } if (currentRayLength < maximumRayLength) { var block = world.GetBlockAt(currentBlockPosition); if (block.CanBeSelected) { var selectedFaceNormal = GetNormalForIntersectedFace(intersectedFace, stepX, stepY, stepZ); return new IntersectionResult { IntersectedBlock = block, IntersectedFaceNormal = selectedFaceNormal }; } } } while (currentRayLength < maximumRayLength); return null; }
public Block(BlockPrototype prototype, BlockPosition position, byte lightLevel) { Position = position; LightLevel = lightLevel; Prototype = prototype; }
public void Tessellate() { // TODO: I guess DX9 can be CPU-bound by the number of draw calls so we might // want to switch back to a single VB/IB per chunk. var vertexLists = new List<VertexPositionColorLighting>[6]; var indexLists = new List<short>[6]; for (int x = 0; x < 6; x++) { // You'd think that setting a largeish initial capacity would save us some time growing // the lists, but it turns out to be the exact opposite in practice. Not sure why. vertexLists[x] = new List<VertexPositionColorLighting>(); indexLists[x] = new List<short>(); } // We save iteration by only doing a slice of the chunk bounded at the top // by the highest solid block and at the bottom by the lowest non-solid block in the // neighborhood minus one. Anything above or below that isnt going to have geometry. // TODO: If we want to burn extra memory in order to optimize this even more aggressively, // we could keep track of lowest/highest for each colum in the chunk. var lowerTesselationLimit = Math.Max(lowestInvisibleBlock - 1, 0); var tessellator = new Tessellator(world); for (int x = 0; x < XDimension; x++) { for (int y = lowerTesselationLimit; y <= highestVisibleBlock; y++) { for (int z = 0; z < ZDimension; z++) { var position = new RelativeBlockPosition(x, y, z); var prototype = GetBlockPrototype(position); if (prototype.CanBeSeen) { var worldBlockPosition = new BlockPosition(Position, position); tessellator.TessellateBlock(vertexLists, indexLists, worldBlockPosition, position); } } } } // TODO: is the conversion causing extra work here? renderer.Initialize((Vector3)OriginInWorld, vertexLists, indexLists); }
public int DistanceSquared(BlockPosition otherPosition) { int deltaX = X - otherPosition.X; int deltaY = Y - otherPosition.Y; int deltaZ = Z - otherPosition.Z; return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; }