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); }
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 + ")"); }
/// <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 + ")" ); }
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; }
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; } }
// 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 + ")"); }
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 + ")"); }
/// <summary> Calculates the picked block based on the camera's current position. </summary> public virtual void GetPickedBlock( PickedPos pos ) { }
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(); }
public abstract void GetPickedBlock(PickedPos pos);
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 + ")"); }
// 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; }
/// <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 );
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 ); }
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 ); }
/// <summary> Calculates the picked block based on the camera's current state. </summary> public virtual void GetPickedBlock(PickedPos pos) { }
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);
/// <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 + ")"); }