Exemple #1
0
 public BlockID this[Vector3I pos]
 {
     get { return(this[pos.X, pos.Y, pos.Z]); }
     set { this[pos.X, pos.Y, pos.Z] = value; }
 }
        /// <summary>
        /// Gets the cells which intersect with the specified <see cref="Ray"/>.
        /// </summary>
        /// <param name="ray">The cell-relative <see cref="Ray"/>.</param>
        /// <param name="maxDepth">The maximum search depth, which equals the maximum number of
        /// returned points.</param>
        /// <returns>An enumerable list of points, starting with the cell closest to the starting
        /// point of the ray.</returns>
        /// <remarks>
        /// <para>The position and direction of the <paramref name="ray"/> must be in the cell
        /// coordinate system.</para>
        /// <para>The first cell which is returned refers to the cell in which the ray starts.</para>
        /// </remarks>
        public IEnumerable <Vector3I> GetCellsOnRay(Ray ray, int maxDepth)
        {
            // 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).

            if (float.IsNaN(ray.Position.X) || float.IsNaN(ray.Position.Y) || float.IsNaN(ray.Position.Z))
            {
                yield break;
            }

            int x = FastFloor(ray.Position.X);
            int y = FastFloor(ray.Position.Y);
            int z = FastFloor(ray.Position.Z);

            // Determine which way we go.
            int stepX = Math.Sign(ray.Direction.X);
            int stepY = Math.Sign(ray.Direction.Y);
            int stepZ = Math.Sign(ray.Direction.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.
            //Point3D cellBoundary = new Point3D(
            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 - ray.Position.X) / ray.Direction.X,    // Boundary is a plane on the YZ axis.
                (cellBoundary.Y - ray.Position.Y) / ray.Direction.Y,    // Boundary is a plane on the XZ axis.
                (cellBoundary.Z - ray.Position.Z) / ray.Direction.Z);   // Boundary is a plane on the XY axis.

            if (Single.IsNaN(tMax.X))
            {
                tMax.X = Single.PositiveInfinity;
            }
            if (Single.IsNaN(tMax.Y))
            {
                tMax.Y = Single.PositiveInfinity;
            }
            if (Single.IsNaN(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 / ray.Direction.X,                    // Crossing the width of a cell.
                stepY / ray.Direction.Y,                    // Crossing the height of a cell.
                stepZ / ray.Direction.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;
            }

            // For each step, determine which distance to the next voxel boundary is lowest (i.e.
            // which voxel boundary is nearest) and walk that way.
            for (int i = 0; i < maxDepth; i++)
            {
                // Return it.
                yield return(new Vector3(x, y, z).ToBlockCoords());

                // Do the next step.
                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;
                }
            }
        }
Exemple #3
0
 public bool InBounds(Vector3I pos)
 {
     return(InBounds(pos.X, pos.Y, pos.Z));
 }
        public void Draw2D(object sender, Draw2DEventArgs e)
        {
            frameCounter++;

            e.SpriteBatch.Begin();

            if (!Client.Paused)
            {
                // Crosshair
                e.SpriteBatch.Draw(Client.CrosshairTexture,
                                   Client.WindowCenter,                                                            // Center of screen
                                   null,                                                                           // Source rectangle
                                   Color.White,                                                                    // Color
                                   0f,                                                                             // Rotation
                                   new Vector2(Client.CrosshairTexture.Width, Client.CrosshairTexture.Height) / 2, // Image center
                                   1f,                                                                             // Scale
                                   SpriteEffects.None,
                                   0f                                                                              // Depth
                                   );
                e.SpriteBatch.Draw(Client.EmptyTexture, Client.WindowCenter, Color.Red);
                string fps = string.Format("FPS: {0}", frameRate);
                e.SpriteBatch.DrawString(Client.Font, fps, Vector2.Zero, Color.White);

                MouseState m          = Mouse.GetState();
                int        mouseX     = m.X;
                int        mouseY     = m.Y;
                Vector3    nearsource = new Vector3((float)mouseX, (float)mouseY, 0f);
                Vector3    farsource  = new Vector3((float)mouseX, (float)mouseY, 1f);

                Vector3 nearPoint = Client.Viewport.Unproject(nearsource,
                                                              Client.MainPlayer.Camera.ProjectionMatrix,
                                                              Client.MainPlayer.Camera.ViewMatrix,
                                                              Matrix.Identity);

                Vector3 farPoint = Client.Viewport.Unproject(farsource,
                                                             Client.MainPlayer.Camera.ProjectionMatrix,
                                                             Client.MainPlayer.Camera.ViewMatrix,
                                                             Matrix.Identity);

                Vector3 direction = farPoint - nearPoint;
                direction.Normalize();

                //Matrix rotationMatrix = Matrix.CreateRotationX(Client.MainPlayer.Camera.Rotation.X) *
                //                        Matrix.CreateRotationY(Client.MainPlayer.Camera.Rotation.Y);
                //distance = Vector3.Transform(distance, rotationMatrix);

                Ray r = new Ray(nearPoint.Center(), direction);
                foreach (Vector3I coord in GetCellsOnRay(r, 15))
                {
                    BlockID id        = Client.MainWorld[coord];
                    Vector3 renderPos = coord.ToRenderCoords();
                    Vector3 min       = new Vector3(renderPos.X - 1f, renderPos.Y - 1f, renderPos.Z - 1f);
                    Vector3 max       = new Vector3(renderPos.X + 1f, renderPos.Y + 1f, renderPos.Z + 1f);

                    float i = Vector3.Distance(Client.MainPlayer.Camera.Position, renderPos);
                    if (i < 2)
                    {
                        continue;
                    }
                    if (id != BlockID.None && id != BlockID.Air)
                    {
                        e.SpriteBatch.DrawString(Client.Font, "Looking: " + id + " - " + i, new Vector2(0, 80), Color.White);
                        BoundingBoxRenderer.Render(new BoundingBox(min, max),
                                                   Client.Device,
                                                   Client.MainPlayer.Camera.ViewMatrix,
                                                   Client.MainPlayer.Camera.ProjectionMatrix,
                                                   Color.Red);
                        Selected = coord;
                        break;
                    }
                    else
                    {
                        BoundingBoxRenderer.Render(new BoundingBox(min, max),
                                                   Client.Device,
                                                   Client.MainPlayer.Camera.ViewMatrix,
                                                   Client.MainPlayer.Camera.ProjectionMatrix,
                                                   Color.White);
                    }
                }

                if (Mouse.GetState().LeftButton == ButtonState.Pressed &&
                    Client.MainWorld[Selected] != BlockID.None &&
                    Client.MainWorld[Selected] != BlockID.Air)
                {
                    Client.MainWorld[Selected] = BlockID.Air;
                }
            }
            else
            {
                AnchoredText t = new AnchoredText(Client.Font, "PAUSED: " + Client.Version, Client.WindowCenter, TextAnchor.MiddleCenter);
                t.Draw(e.SpriteBatch);
            }
            e.SpriteBatch.End();
        }