示例#1
0
        public static void ClipCameraPos( Game game, Vector3 origin, Vector3 dir, float reach, PickedPos pickedPos )
        {
            if( !game.CameraClipping ) {
                pickedPos.SetAsInvalid();
                pickedPos.IntersectPoint = origin + dir * reach;
                return;
            }
            tracer.SetRayData( origin, dir );
            World map = game.World;
            BlockInfo info = game.BlockInfo;
            float reachSquared = reach * reach;
            int iterations = 0;
            Vector3I pOrigin = Vector3I.Floor( origin );

            while( iterations < 10000 ) {
                int x = tracer.X, y = tracer.Y, z = tracer.Z;
                byte block = GetBlock( map, x, y, z, pOrigin );
                Vector3 min = new Vector3( x, y, z ) + info.MinBB[block];
                Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block];

                float dx = Math.Min( Math.Abs( origin.X - min.X ), Math.Abs( origin.X - max.X ) );
                float dy = Math.Min( Math.Abs( origin.Y - min.Y ), Math.Abs( origin.Y - max.Y ) );
                float dz = Math.Min( Math.Abs( origin.Z - min.Z ), Math.Abs( origin.Z - max.Z ) );

                if( dx * dx + dy * dy + dz * dz > reachSquared ) {
                    pickedPos.SetAsInvalid();
                    pickedPos.IntersectPoint = origin + dir * reach;
                    return;
                }

                if( info.Collide[block] == CollideType.Solid && !info.IsAir[block] ) {
                    float t0, t1;
                    const float adjust = 0.1f;
                    if( Intersection.RayIntersectsBox( origin, dir, min, max, out t0, out t1 ) ) {
                        Vector3 intersect = origin + dir * t0;
                        pickedPos.SetAsValid( x, y, z, min, max, block, intersect );

                        switch( pickedPos.BlockFace) {
                            case CpeBlockFace.XMin:
                                pickedPos.IntersectPoint.X -= adjust; break;
                            case CpeBlockFace.XMax:
                                pickedPos.IntersectPoint.X += adjust; break;
                            case CpeBlockFace.YMin:
                                pickedPos.IntersectPoint.Y -= adjust; break;
                            case CpeBlockFace.YMax:
                                pickedPos.IntersectPoint.Y += adjust; break;
                            case CpeBlockFace.ZMin:
                                pickedPos.IntersectPoint.Z -= adjust; break;
                            case CpeBlockFace.ZMax:
                                pickedPos.IntersectPoint.Z += adjust; break;
                        }
                        return;
                    }
                }
                tracer.Step();
                iterations++;
            }
            throw new InvalidOperationException( "did over 10000 iterations in ClipCameraPos(). " +
                                                "Something has gone wrong. (dir: " + dir + ")" );
        }
        bool CheckIsFree(PickedPos selected, byte newBlock)
        {
            Vector3 pos = (Vector3)selected.TranslatedPos;

            if (!CannotPassThrough(newBlock))
            {
                return(true);
            }
            if (IntersectsOtherPlayers(pos, newBlock))
            {
                return(false);
            }

            BoundingBox blockBB = new BoundingBox(pos + game.BlockInfo.MinBB[newBlock],
                                                  pos + game.BlockInfo.MaxBB[newBlock]);
            BoundingBox localBB = game.LocalPlayer.CollisionBounds;

            if (game.LocalPlayer.noClip || !localBB.Intersects(blockBB))
            {
                return(true);
            }

            if (game.LocalPlayer.PushbackPlacing)
            {
                return(PushbackPlace(selected, blockBB));
            }
            else
            {
                localBB.Min.Y += 0.25f + Entity.Adjustment;
                if (localBB.Intersects(blockBB))
                {
                    return(false);
                }

                // Push player up if they are jumping and trying to place a block underneath them.
                Vector3 p = game.LocalPlayer.Position;
                p.Y = pos.Y + game.BlockInfo.MaxBB[newBlock].Y + Entity.Adjustment;
                LocationUpdate update = LocationUpdate.MakePos(p, false);
                game.LocalPlayer.SetLocation(update, false);
                return(true);
            }
        }
        static bool PickBlock(Game game, PickedPos pos)
        {
            if (!game.CanPick(t.Block))
            {
                return(false);
            }

            // This cell falls on the path of the ray. Now perform an additional bounding box test,
            // since some blocks do not occupy a whole cell.
            float t0, t1;

            if (!Intersection.RayIntersectsBox(t.Origin, t.Dir, t.Min, t.Max, out t0, out t1))
            {
                return(false);
            }
            Vector3 I = t.Origin + t.Dir * t0;

            pos.SetAsValid(t.X, t.Y, t.Z, t.Min, t.Max, t.Block, I);
            return(true);
        }
示例#4
0
        static bool RayTrace(Game game, Vector3 origin, Vector3 dir, float reach,
                             PickedPos pos, bool clipMode)
        {
            t.SetVectors(origin, dir);
            float    reachSq   = reach * reach;
            Vector3I pOrigin   = Vector3I.Floor(origin);
            bool     insideMap = game.World.IsValidPos(pOrigin);

            Vector3 coords;

            for (int i = 0; i < 10000; i++)
            {
                int x = t.X, y = t.Y, z = t.Z;
                coords.X = x; coords.Y = y; coords.Z = z;
                t.Block  = insideMap ?
                           InsideGetBlock(game.World, x, y, z) : OutsideGetBlock(game.World, x, y, z, pOrigin);

                Vector3 min = coords + BlockInfo.RenderMinBB[t.Block];
                Vector3 max = coords + BlockInfo.RenderMaxBB[t.Block];

                float dx = Math.Min(Math.Abs(origin.X - min.X), Math.Abs(origin.X - max.X));
                float dy = Math.Min(Math.Abs(origin.Y - min.Y), Math.Abs(origin.Y - max.Y));
                float dz = Math.Min(Math.Abs(origin.Z - min.Z), Math.Abs(origin.Z - max.Z));
                if (dx * dx + dy * dy + dz * dz > reachSq)
                {
                    return(false);
                }

                t.Min = min; t.Max = max;
                bool intersect = clipMode ? CameraClip(game, pos) : PickBlock(game, pos);
                if (intersect)
                {
                    return(true);
                }
                t.Step();
            }

            throw new InvalidOperationException("did over 10000 iterations in CalculatePickedBlock(). " +
                                                "Something has gone wrong. (dir: " + dir + ")");
        }
        static bool RayTrace(Game game, Vector3 origin, Vector3 dir, float reach,
                             PickedPos pos, bool clipMode)
        {
            t.SetVectors(origin, dir);
            BlockInfo info    = game.BlockInfo;
            float     reachSq = reach * reach;
            Vector3I  pOrigin = Vector3I.Floor(origin);

            for (int i = 0; i < 10000; i++)
            {
                int x = t.X, y = t.Y, z = t.Z;
                t.Block = GetBlock(game.World, x, y, z, pOrigin);
                Vector3 min = new Vector3(x, y, z) + info.MinBB[t.Block];
                Vector3 max = new Vector3(x, y, z) + info.MaxBB[t.Block];
                if (info.IsLiquid(t.Block))
                {
                    min.Y -= 1.5f / 16; max.Y -= 1.5f / 16;
                }

                float dx = Math.Min(Math.Abs(origin.X - min.X), Math.Abs(origin.X - max.X));
                float dy = Math.Min(Math.Abs(origin.Y - min.Y), Math.Abs(origin.Y - max.Y));
                float dz = Math.Min(Math.Abs(origin.Z - min.Z), Math.Abs(origin.Z - max.Z));
                if (dx * dx + dy * dy + dz * dz > reachSq)
                {
                    return(false);
                }

                t.Min = min; t.Max = max;
                bool intersect = clipMode ? CameraClip(game, pos) : PickBlock(game, pos);
                if (intersect)
                {
                    return(true);
                }
                t.Step();
            }

            throw new InvalidOperationException("did over 10000 iterations in CalculatePickedBlock(). " +
                                                "Something has gone wrong. (dir: " + dir + ")");
        }
示例#6
0
        /// <summary> Determines the picked block based on the given origin and direction vector.<br/>
        /// Marks pickedPos as invalid if a block could not be found due to going outside map boundaries
        /// or not being able to find a suitable candiate within the given reach distance. </summary>
        public static void CalculatePickedBlock( Game game, Vector3 origin, Vector3 dir, float reach, PickedPos pickedPos )
        {
            tracer.SetRayData( origin, dir );
            World map = game.World;
            BlockInfo info = game.BlockInfo;
            float reachSquared = reach * reach;
            int iterations = 0;
            Vector3I pOrigin = Vector3I.Floor( origin );

            while( iterations < 10000 ) {
                int x = tracer.X, y = tracer.Y, z = tracer.Z;
                byte block = GetBlock( map, x, y, z, pOrigin );
                Vector3 min = new Vector3( x, y, z ) + info.MinBB[block];
                Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block];

                float dx = Math.Min( Math.Abs( origin.X - min.X ), Math.Abs( origin.X - max.X ) );
                float dy = Math.Min( Math.Abs( origin.Y - min.Y ), Math.Abs( origin.Y - max.Y ) );
                float dz = Math.Min( Math.Abs( origin.Z - min.Z ), Math.Abs( origin.Z - max.Z ) );

                if( dx * dx + dy * dy + dz * dz > reachSquared ) {
                    pickedPos.SetAsInvalid(); return;
                }

                if( game.CanPick( block ) ) {
                    // This cell falls on the path of the ray. Now perform an additional bounding box test,
                    // since some blocks do not occupy a whole cell.
                    float t0, t1;
                    if( Intersection.RayIntersectsBox( origin, dir, min, max, out t0, out t1 ) ) {
                        Vector3 intersect = origin + dir * t0;
                        pickedPos.SetAsValid( x, y, z, min, max, block, intersect );
                        return;
                    }
                }
                tracer.Step();
                iterations++;
            }
            throw new InvalidOperationException( "did over 10000 iterations in CalculatePickedBlock(). " +
                                                "Something has gone wrong. (dir: " + dir + ")" );
        }
示例#7
0
        static bool CameraClip(Game game, PickedPos pos)
        {
            if (BlockInfo.Draw[t.Block] == DrawType.Gas || BlockInfo.Collide[t.Block] != CollideType.Solid)
            {
                return(false);
            }

            float t0, t1;

            if (!Intersection.RayIntersectsBox(t.Origin, t.Dir, t.Min, t.Max, out t0, out t1))
            {
                return(false);
            }

            // Need to collide with slightly outside block, to avoid camera clipping issues
            t.Min -= adjust; t.Max += adjust;
            Intersection.RayIntersectsBox(t.Origin, t.Dir, t.Min, t.Max, out t0, out t1);

            Vector3 I = t.Origin + t.Dir * t0;

            pos.SetAsValid(t.X, t.Y, t.Z, t.Min, t.Max, t.Block, I);
            return(true);
        }
        static bool CameraClip(Game game, PickedPos pos)
        {
            BlockInfo info = game.BlockInfo;

            if (info.IsAir[t.Block] || info.Collide[t.Block] != CollideType.Solid)
            {
                return(false);
            }

            float       t0, t1;
            const float adjust = 0.1f;

            if (!Intersection.RayIntersectsBox(t.Origin, t.Dir, t.Min, t.Max, out t0, out t1))
            {
                return(false);
            }
            Vector3 I = t.Origin + t.Dir * t0;

            pos.SetAsValid(t.X, t.Y, t.Z, t.Min, t.Max, t.Block, I);

            switch (pos.BlockFace)
            {
            case BlockFace.XMin: pos.Intersect.X -= adjust; break;

            case BlockFace.XMax: pos.Intersect.X += adjust; break;

            case BlockFace.YMin: pos.Intersect.Y -= adjust; break;

            case BlockFace.YMax: pos.Intersect.Y += adjust; break;

            case BlockFace.ZMin: pos.Intersect.Z -= adjust; break;

            case BlockFace.ZMax: pos.Intersect.Z += adjust; break;
            }
            return(true);
        }
        bool PushbackPlace( PickedPos selected, BoundingBox blockBB )
        {
            Vector3 newP = game.LocalPlayer.Position;
            Vector3 oldP = game.LocalPlayer.Position;

            // Offset position by the closest face
            if( selected.BlockFace == CpeBlockFace.XMax )
                newP.X = blockBB.Max.X + 0.5f;
            else if( selected.BlockFace == CpeBlockFace.ZMax )
                newP.Z = blockBB.Max.Z + 0.5f;
            else if( selected.BlockFace == CpeBlockFace.XMin )
                newP.X = blockBB.Min.X - 0.5f;
            else if( selected.BlockFace == CpeBlockFace.ZMin )
                newP.Z = blockBB.Min.Z - 0.5f;
            else if( selected.BlockFace == CpeBlockFace.YMax )
                newP.Y = blockBB.Min.Y + 1 + Entity.Adjustment;
            else if( selected.BlockFace == CpeBlockFace.YMin )
                newP.Y = blockBB.Min.Y - game.LocalPlayer.CollisionSize.Y - Entity.Adjustment;

            Vector3I newLoc = Vector3I.Floor( newP );
            bool validPos = newLoc.X >= 0 && newLoc.Y >= 0 && newLoc.Z >= 0 &&
                newLoc.X < game.Map.Width && newP.Z < game.Map.Length;
            if( !validPos ) return false;

            game.LocalPlayer.Position = newP;
            if( !game.LocalPlayer.noClip && game.LocalPlayer.TouchesAny( CannotPassThrough ) ) {
                game.LocalPlayer.Position = oldP;
                return false;
            }

            game.LocalPlayer.Position = oldP;
            LocationUpdate update = LocationUpdate.MakePos( newP, false );
            game.LocalPlayer.SetLocation( update, false );
            return true;
        }
示例#10
0
        bool CheckIsFree( PickedPos selected, byte newBlock )
        {
            if( !CannotPassThrough( newBlock ) ) return true;
            if( IntersectsOtherPlayers( selected.TranslatedPos, newBlock ) ) return false;

            Vector3I pos = selected.TranslatedPos;
            float height = game.BlockInfo.Height[newBlock];
            BoundingBox blockBB = new BoundingBox( pos.X, pos.Y, pos.Z,
                                                  pos.X + 1, pos.Y + height, pos.Z + 1 );
            BoundingBox localBB = game.LocalPlayer.CollisionBounds;

            if( game.LocalPlayer.noClip || !localBB.Intersects( blockBB ) ) return true;

            if( game.LocalPlayer.PushbackBlockPlacing ) {
                return PushbackPlace( selected, blockBB );
            } else {
                localBB.Min.Y += 0.25f + Entity.Adjustment;
                if( localBB.Intersects( blockBB ) ) return false;

                // Push player up if they are jumping and trying to place a block underneath them.
                Vector3 p = game.LocalPlayer.Position;
                p.Y = pos.Y + height + Entity.Adjustment;
                LocationUpdate update = LocationUpdate.MakePos( p, false );
                game.LocalPlayer.SetLocation( update, false );
                return true;
            }
        }
示例#11
0
        // http://www.xnawiki.com/index.php/Voxel_traversal
        // https://web.archive.org/web/20120113051728/http://www.xnawiki.com/index.php?title=Voxel_traversal
        /// <summary> Determines the picked block based on the given origin and direction vector.<br/>
        /// Marks pickedPos as invalid if a block could not be found due to going outside map boundaries
        /// or not being able to find a suitable candiate within the given reach distance. </summary>
        public static void CalculatePickedBlock(Game game, Vector3 origin, Vector3 dir, float reach, PickedPos pickedPos)
        {
            // Implementation based on: "A Fast Voxel Traversal Algorithm for Ray Tracing"
            // John Amanatides, Andrew Woo
            // http://www.cse.yorku.ca/~amana/research/grid.pdf
            // http://www.devmaster.net/articles/raytracing_series/A%20faster%20voxel%20traversal%20algorithm%20for%20ray%20tracing.pdf

            // The cell in which the ray starts.
            Vector3I start = Vector3I.Floor(origin);               // Rounds the position's X, Y and Z down to the nearest integer values.
            int      x = start.X, y = start.Y, z = start.Z;
            Vector3I step, cellBoundary;
            Vector3  tMax, tDelta;

            CalcVectors(origin, dir, out step, out cellBoundary, out tMax, out tDelta);

            Map       map          = game.Map;
            BlockInfo info         = game.BlockInfo;
            float     reachSquared = reach * reach;
            int       iterations   = 0;

            // For each step, determine which distance to the next voxel boundary is lowest (i.e.
            // which voxel boundary is nearest) and walk that way.
            while (iterations < 10000)
            {
                byte    block = GetBlock(map, x, y, z, origin);
                Vector3 min   = new Vector3(x, y, z) + info.MinBB[block];
                Vector3 max   = new Vector3(x, y, z) + info.MaxBB[block];

                float dx = Math.Min(Math.Abs(origin.X - min.X), Math.Abs(origin.X - max.X));
                float dy = Math.Min(Math.Abs(origin.Y - min.Y), Math.Abs(origin.Y - max.Y));
                float dz = Math.Min(Math.Abs(origin.Z - min.Z), Math.Abs(origin.Z - max.Z));

                if (dx * dx + dy * dy + dz * dz > reachSquared)
                {
                    pickedPos.SetAsInvalid();
                    return;
                }

                if (game.CanPick(block))
                {
                    // This cell falls on the path of the ray. Now perform an additional bounding box test,
                    // since some blocks do not occupy a whole cell.
                    float t0, t1;
                    if (Intersection.RayIntersectsBox(origin, dir, min, max, out t0, out t1))
                    {
                        Vector3 intersect = origin + dir * t0;
                        pickedPos.SetAsValid(x, y, z, min, max, block, intersect);
                        return;
                    }
                }
                Step(ref tMax, ref tDelta, ref step, ref x, ref y, ref z);
                iterations++;
            }
            throw new InvalidOperationException("did over 10000 iterations in CalculatePickedBlock(). " +
                                                "Something has gone wrong. (dir: " + dir + ")");
        }
示例#12
0
        public static void ClipCameraPos(Game game, Vector3 origin, Vector3 dir, float reach, PickedPos pickedPos)
        {
            if (!game.CameraClipping)
            {
                pickedPos.SetAsInvalid();
                pickedPos.IntersectPoint = origin + dir * reach;
                return;
            }
            tracer.SetRayData(origin, dir);
            World     map          = game.World;
            BlockInfo info         = game.BlockInfo;
            float     reachSquared = reach * reach;
            int       iterations   = 0;
            Vector3I  pOrigin      = Vector3I.Floor(origin);

            while (iterations < 10000)
            {
                int     x = tracer.X, y = tracer.Y, z = tracer.Z;
                byte    block = GetBlock(map, x, y, z, pOrigin);
                Vector3 min   = new Vector3(x, y, z) + info.MinBB[block];
                Vector3 max   = new Vector3(x, y, z) + info.MaxBB[block];
                if (info.IsLiquid[block])
                {
                    min.Y -= 1.5f / 16; max.Y -= 1.5f / 16;
                }

                float dx = Math.Min(Math.Abs(origin.X - min.X), Math.Abs(origin.X - max.X));
                float dy = Math.Min(Math.Abs(origin.Y - min.Y), Math.Abs(origin.Y - max.Y));
                float dz = Math.Min(Math.Abs(origin.Z - min.Z), Math.Abs(origin.Z - max.Z));

                if (dx * dx + dy * dy + dz * dz > reachSquared)
                {
                    pickedPos.SetAsInvalid();
                    pickedPos.IntersectPoint = origin + dir * reach;
                    return;
                }

                if (info.Collide[block] == CollideType.Solid && !info.IsAir[block])
                {
                    float       t0, t1;
                    const float adjust = 0.1f;
                    if (Intersection.RayIntersectsBox(origin, dir, min, max, out t0, out t1))
                    {
                        Vector3 intersect = origin + dir * t0;
                        pickedPos.SetAsValid(x, y, z, min, max, block, intersect);

                        switch (pickedPos.BlockFace)
                        {
                        case CpeBlockFace.XMin:
                            pickedPos.IntersectPoint.X -= adjust; break;

                        case CpeBlockFace.XMax:
                            pickedPos.IntersectPoint.X += adjust; break;

                        case CpeBlockFace.YMin:
                            pickedPos.IntersectPoint.Y -= adjust; break;

                        case CpeBlockFace.YMax:
                            pickedPos.IntersectPoint.Y += adjust; break;

                        case CpeBlockFace.ZMin:
                            pickedPos.IntersectPoint.Z -= adjust; break;

                        case CpeBlockFace.ZMax:
                            pickedPos.IntersectPoint.Z += adjust; break;
                        }
                        return;
                    }
                }
                tracer.Step();
                iterations++;
            }
            throw new InvalidOperationException("did over 10000 iterations in ClipCameraPos(). " +
                                                "Something has gone wrong. (dir: " + dir + ")");
        }
示例#13
0
 /// <summary> Calculates the picked block based on the camera's current position. </summary>
 public virtual void GetPickedBlock( PickedPos pos )
 {
 }
示例#14
0
        public override void SendPlayerClick(MouseButton button, bool buttonDown, byte targetId, PickedPos pos)
        {
            Player p = game.LocalPlayer;

            MakePlayerClick((byte)button, buttonDown, p.YawDegrees, p.PitchDegrees, targetId,
                            pos.BlockPos, pos.BlockFace);
            SendPacket();
        }
示例#15
0
 public abstract void GetPickedBlock(PickedPos pos);
示例#16
0
        public static void ClipCameraPos(Game game, Vector3 origin, Vector3 dir, float reach, PickedPos pickedPos)
        {
            if (!game.CameraClipping)
            {
                pickedPos.SetAsInvalid();
                pickedPos.IntersectPoint = origin + dir * reach;
                return;
            }
            Vector3I start = Vector3I.Floor(origin);
            int      x = start.X, y = start.Y, z = start.Z;
            Vector3I step, cellBoundary;
            Vector3  tMax, tDelta;

            CalcVectors(origin, dir, out step, out cellBoundary, out tMax, out tDelta);

            Map       map          = game.Map;
            BlockInfo info         = game.BlockInfo;
            float     reachSquared = reach * reach;
            int       iterations   = 0;

            while (iterations < 10000)
            {
                byte    block = GetBlock(map, x, y, z, origin);
                Vector3 min   = new Vector3(x, y, z) + info.MinBB[block];
                Vector3 max   = new Vector3(x, y, z) + info.MaxBB[block];

                float dx = Math.Min(Math.Abs(origin.X - min.X), Math.Abs(origin.X - max.X));
                float dy = Math.Min(Math.Abs(origin.Y - min.Y), Math.Abs(origin.Y - max.Y));
                float dz = Math.Min(Math.Abs(origin.Z - min.Z), Math.Abs(origin.Z - max.Z));

                if (dx * dx + dy * dy + dz * dz > reachSquared)
                {
                    pickedPos.SetAsInvalid();
                    pickedPos.IntersectPoint = origin + dir * reach;
                    return;
                }

                if (info.CollideType[block] == BlockCollideType.Solid && !info.IsAir[block])
                {
                    float       t0, t1;
                    const float adjust = 0.1f;
                    if (Intersection.RayIntersectsBox(origin, dir, min, max, out t0, out t1))
                    {
                        Vector3 intersect = origin + dir * t0;
                        pickedPos.SetAsValid(x, y, z, min, max, block, intersect);

                        switch (pickedPos.BlockFace)
                        {
                        case CpeBlockFace.XMin:
                            pickedPos.IntersectPoint.X -= adjust; break;

                        case CpeBlockFace.XMax:
                            pickedPos.IntersectPoint.X += adjust; break;

                        case CpeBlockFace.YMin:
                            pickedPos.IntersectPoint.Y -= adjust; break;

                        case CpeBlockFace.YMax:
                            pickedPos.IntersectPoint.Y += adjust; break;

                        case CpeBlockFace.ZMin:
                            pickedPos.IntersectPoint.Z -= adjust; break;

                        case CpeBlockFace.ZMax:
                            pickedPos.IntersectPoint.Z += adjust; break;
                        }
                        return;
                    }
                }
                Step(ref tMax, ref tDelta, ref step, ref x, ref y, ref z);
                iterations++;
            }
            throw new InvalidOperationException("did over 10000 iterations in ClipCameraPos(). " +
                                                "Something has gone wrong. (dir: " + dir + ")");
        }
示例#17
0
        // http://www.xnawiki.com/index.php/Voxel_traversal
        public static void GetPickedBlockPos( Game window, Vector3 origin, Vector3 dir, float reach, PickedPos pickedPos )
        {
            // Implementation is based on:
            // "A Fast Voxel Traversal Algorithm for Ray Tracing"
            // John Amanatides, Andrew Woo
            // http://www.cse.yorku.ca/~amana/research/grid.pdf
            // http://www.devmaster.net/articles/raytracing_series/A%20faster%20voxel%20traversal%20algorithm%20for%20ray%20tracing.pdf

            // NOTES:
            // * This code assumes that the ray's position and direction are in 'cell coordinates', which means
            //   that one unit equals one cell in all directions.
            // * When the ray doesn't start within the voxel grid, calculate the first position at which the
            //   ray could enter the grid. If it never enters the grid, there is nothing more to do here.
            // * Also, it is important to test when the ray exits the voxel grid when the grid isn't infinite.
            // * The Point3D structure is a simple structure having three integer fields (X, Y and Z).

            // The cell in which the ray starts.
            Vector3I start = Vector3I.Floor( origin ); // Rounds the position's X, Y and Z down to the nearest integer values.
            int x = start.X;
            int y = start.Y;
            int z = start.Z;

            // Determine which way we go.
            int stepX = Math.Sign( dir.X );
            int stepY = Math.Sign( dir.Y );
            int stepZ = Math.Sign( dir.Z );

            // Calculate cell boundaries. When the step (i.e. direction sign) is positive,
            // the next boundary is AFTER our current position, meaning that we have to add 1.
            // Otherwise, it is BEFORE our current position, in which case we add nothing.
            Vector3I cellBoundary = new Vector3I(
                x + ( stepX > 0 ? 1 : 0 ),
                y + ( stepY > 0 ? 1 : 0 ),
                z + ( stepZ > 0 ? 1 : 0 ) );

            // NOTE: For the following calculations, the result will be Single.PositiveInfinity
            // when ray.Direction.X, Y or Z equals zero, which is OK. However, when the left-hand
            // value of the division also equals zero, the result is Single.NaN, which is not OK.

            // Determine how far we can travel along the ray before we hit a voxel boundary.
            Vector3 tMax = new Vector3(
                ( cellBoundary.X - origin.X ) / dir.X,    // Boundary is a plane on the YZ axis.
                ( cellBoundary.Y - origin.Y ) / dir.Y,    // Boundary is a plane on the XZ axis.
                ( cellBoundary.Z - origin.Z ) / dir.Z );  // Boundary is a plane on the XY axis.
            if( Single.IsNaN( tMax.X ) || Single.IsInfinity( tMax.X ) ) tMax.X = Single.PositiveInfinity;
            if( Single.IsNaN( tMax.Y ) || Single.IsInfinity( tMax.Y ) ) tMax.Y = Single.PositiveInfinity;
            if( Single.IsNaN( tMax.Z ) || Single.IsInfinity( tMax.Z ) ) tMax.Z = Single.PositiveInfinity;

            // Determine how far we must travel along the ray before we have crossed a gridcell.
            Vector3 tDelta = new Vector3(
                stepX / dir.X,     // Crossing the width of a cell.
                stepY / dir.Y,     // Crossing the height of a cell.
                stepZ / dir.Z );   // Crossing the depth of a cell.
            if( Single.IsNaN( tDelta.X ) ) tDelta.X = Single.PositiveInfinity;
            if( Single.IsNaN( tDelta.Y ) ) tDelta.Y = Single.PositiveInfinity;
            if( Single.IsNaN( tDelta.Z ) ) tDelta.Z = Single.PositiveInfinity;

            Map map = window.Map;
            BlockInfo info = window.BlockInfo;
            float reachSquared = reach * reach;
            int iterations = 0;

            // For each step, determine which distance to the next voxel boundary is lowest (i.e.
            // which voxel boundary is nearest) and walk that way.
            while( iterations < 10000 ) {
                byte block = GetBlock( map, x, y, z, origin );
                Vector3 min = new Vector3( x, y, z ) + info.MinBB[block];
                Vector3 max = new Vector3( x, y, z ) + info.MaxBB[block];

                float dx = Math.Min( Math.Abs( origin.X - min.X ), Math.Abs( origin.X - max.X ) );
                float dy = Math.Min( Math.Abs( origin.Y - min.Y ), Math.Abs( origin.Y - max.Y ) );
                float dz = Math.Min( Math.Abs( origin.Z - min.Z ), Math.Abs( origin.Z - max.Z ) );

                if( dx * dx + dy * dy + dz * dz > reachSquared ) {
                    pickedPos.SetAsInvalid();
                    return;
                }

                if( window.CanPick( block ) ) {
                    // This cell falls on the path of the ray. Now perform an additional bounding box test,
                    // since some blocks do not occupy a whole cell.
                    float t0, t1;
                    if( Intersection.RayIntersectsBox( origin, dir, min, max, out t0, out t1 ) ) {
                        Vector3 intersect = origin + dir * t0;
                        pickedPos.SetAsValid( min, max, block, intersect );
                        return;
                    }
                }

                if( tMax.X < tMax.Y && tMax.X < tMax.Z ) {
                    // tMax.X is the lowest, an YZ cell boundary plane is nearest.
                    x += stepX;
                    tMax.X += tDelta.X;
                } else if( tMax.Y < tMax.Z ) {
                    // tMax.Y is the lowest, an XZ cell boundary plane is nearest.
                    y += stepY;
                    tMax.Y += tDelta.Y;
                } else {
                    // tMax.Z is the lowest, an XY cell boundary plane is nearest.
                    z += stepZ;
                    tMax.Z += tDelta.Z;
                }
                iterations++;
            }
            throw new InvalidOperationException( "did over 10000 iterations in GetPickedBlockPos(). " +
                                                "Something has gone wrong. (dir: " + dir + ")" );
        }
        bool CheckIsFree( PickedPos selected, byte newBlock )
        {
            Vector3 pos = (Vector3)selected.TranslatedPos;
            if( !CannotPassThrough( newBlock ) ) return true;
            if( IntersectsOtherPlayers( pos, newBlock ) ) return false;

            BoundingBox blockBB = new BoundingBox( pos + game.BlockInfo.MinBB[newBlock],
                                                  pos + game.BlockInfo.MaxBB[newBlock] );
            BoundingBox localBB = game.LocalPlayer.CollisionBounds;

            if( game.LocalPlayer.Hacks.Noclip || !localBB.Intersects( blockBB ) ) return true;
            HacksComponent hacks = game.LocalPlayer.Hacks;
            if( hacks.CanPushbackBlocks && hacks.PushbackPlacing && hacks.Enabled )
                return PushbackPlace( selected, blockBB );

            localBB.Min.Y += 0.25f + Entity.Adjustment;
            if( localBB.Intersects( blockBB ) ) return false;

            // Push player up if they are jumping and trying to place a block underneath them.
            Vector3 p = game.LocalPlayer.Position;
            p.Y = pos.Y + game.BlockInfo.MaxBB[newBlock].Y + Entity.Adjustment;
            LocationUpdate update = LocationUpdate.MakePos( p, false );
            game.LocalPlayer.SetLocation( update, false );
            return true;
        }
示例#19
0
 /// <summary> Informs the server that using the given mouse button,
 /// the client clicked on a particular block or entity. </summary>
 public abstract void SendPlayerClick( MouseButton button, bool buttonDown, byte targetId, PickedPos pos );
示例#20
0
 public override void GetPickedBlock( PickedPos pos )
 {
     Vector3 dir = Utils.GetDirVector( player.HeadYawRadians,
                                      AdjustPitch( player.PitchDegrees ) );
     Vector3 eyePos = player.EyePosition;
     float reach = game.LocalPlayer.ReachDistance;
     Picking.CalculatePickedBlock( game, eyePos, dir, reach, pos );
 }
示例#21
0
 public override void SendPlayerClick( MouseButton button, bool buttonDown, byte targetId, PickedPos pos )
 {
     Player p = game.LocalPlayer;
     MakePlayerClick( (byte)button, buttonDown, p.YawDegrees, p.PitchDegrees, targetId,
                     pos.BlockPos, pos.BlockFace );
 }
示例#22
0
 /// <summary> Calculates the picked block based on the camera's current state. </summary>
 public virtual void GetPickedBlock(PickedPos pos)
 {
 }
示例#23
0
 public override void GetPickedBlock( PickedPos pos )
 {
     Vector3 dir = Utils.GetDirVector( player.YawRadians, player.PitchRadians );
     Vector3 eyePos = player.EyePosition;
     float reach = game.LocalPlayer.ReachDistance;
     Picking.GetPickedBlockPos( game, eyePos, dir, reach, pos );
 }
 /// <summary> Informs the server that using the given mouse button,
 /// the client clicked on a particular block or entity. </summary>
 public abstract void SendPlayerClick(MouseButton button, bool buttonDown, byte targetId, PickedPos pos);
示例#25
0
        /// <summary> Determines the picked block based on the given origin and direction vector.<br/>
        /// Marks pickedPos as invalid if a block could not be found due to going outside map boundaries
        /// or not being able to find a suitable candiate within the given reach distance. </summary>
        public static void CalculatePickedBlock(Game game, Vector3 origin, Vector3 dir, float reach, PickedPos pickedPos)
        {
            tracer.SetRayData(origin, dir);
            World     map          = game.World;
            BlockInfo info         = game.BlockInfo;
            float     reachSquared = reach * reach;
            int       iterations   = 0;
            Vector3I  pOrigin      = Vector3I.Floor(origin);

            while (iterations < 10000)
            {
                int     x = tracer.X, y = tracer.Y, z = tracer.Z;
                byte    block = GetBlock(map, x, y, z, pOrigin);
                Vector3 min   = new Vector3(x, y, z) + info.MinBB[block];
                Vector3 max   = new Vector3(x, y, z) + info.MaxBB[block];
                if (info.IsLiquid[block])
                {
                    min.Y -= 1.5f / 16; max.Y -= 1.5f / 16;
                }

                float dx = Math.Min(Math.Abs(origin.X - min.X), Math.Abs(origin.X - max.X));
                float dy = Math.Min(Math.Abs(origin.Y - min.Y), Math.Abs(origin.Y - max.Y));
                float dz = Math.Min(Math.Abs(origin.Z - min.Z), Math.Abs(origin.Z - max.Z));

                if (dx * dx + dy * dy + dz * dz > reachSquared)
                {
                    pickedPos.SetAsInvalid(); return;
                }

                if (game.CanPick(block))
                {
                    // This cell falls on the path of the ray. Now perform an additional bounding box test,
                    // since some blocks do not occupy a whole cell.
                    float t0, t1;
                    if (Intersection.RayIntersectsBox(origin, dir, min, max, out t0, out t1))
                    {
                        Vector3 intersect = origin + dir * t0;
                        pickedPos.SetAsValid(x, y, z, min, max, block, intersect);
                        return;
                    }
                }
                tracer.Step();
                iterations++;
            }
            throw new InvalidOperationException("did over 10000 iterations in CalculatePickedBlock(). " +
                                                "Something has gone wrong. (dir: " + dir + ")");
        }