Beispiel #1
0
 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);
        }
Beispiel #5
0
        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
            };
        }
Beispiel #6
0
 public BlockPrototype this[BlockPosition location]
 {
     get { return this[location.X, location.Y, location.Z]; }
     set { blockIndexes[location.X, location.Y, location.Z] = prototypeMap[value]; }
 }
Beispiel #7
0
        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++;
        }
Beispiel #8
0
        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
        }
Beispiel #9
0
 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);
 }
Beispiel #10
0
 public BlockVolume(World world, BlockPosition minimum, BlockPosition maximum)
 {
     this.world = world;
     Minimum = minimum;
     Maximum = maximum;
 }
Beispiel #11
0
        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;
        }
Beispiel #12
0
 public Block(BlockPrototype prototype, BlockPosition position, byte lightLevel)
 {
     Position = position;
     LightLevel = lightLevel;
     Prototype = prototype;
 }
Beispiel #13
0
        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);
        }
Beispiel #14
0
        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;
        }