/// <summary> Mark this as not having a selected block. </summary> public void SetAsInvalid() { Valid = false; BlockPos = TranslatedPos = Vector3I.MinusOne; BlockFace = (CpeBlockFace)255; BlockType = 0; }
public void SetRayData( Vector3 origin, Vector3 dir ) { Vector3I start = Vector3I.Floor( origin ); // Rounds the position's X, Y and Z down to the nearest integer values. // The cell in which the ray starts. X = start.X; Y = start.Y; Z = start.Z; // Determine which way we go. step.X = Math.Sign( dir.X ); step.Y = Math.Sign( dir.Y ); step.Z = 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. cellBoundary = new Vector3I( start.X + (step.X > 0 ? 1 : 0), start.Y + (step.Y > 0 ? 1 : 0), start.Z + (step.Z > 0 ? 1 : 0) ); // NOTE: we want it so if dir.x = 0, tmax.x = positive infinity // Determine how far we can travel along the ray before we hit a voxel boundary. 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. tDelta = new Vector3( step.X / dir.X, step.Y / dir.Y, step.Z / dir.Z ); 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; }
void TestAxis( float dAxis, ref float dist, Vector3I nAxis, ref Vector3I normal, CpeBlockFace fAxis) { dAxis = Math.Abs( dAxis ); if( dAxis < dist ) { dist = dAxis; normal = nAxis; BlockFace = fAxis; } }
private static void MakePlayerClick( byte button, bool buttonDown, float yaw, float pitch, byte targetEntity, Vector3I targetPos, CpeBlockFace targetFace ) { WriteUInt8( (byte)PacketId.CpePlayerClick ); WriteUInt8( button ); WriteUInt8( buttonDown ? (byte)0 : (byte)1 ); WriteInt16( (short)Utils.DegreesToPacked( yaw, 65536 ) ); WriteInt16( (short)Utils.DegreesToPacked( pitch, 65536 ) ); WriteUInt8( targetEntity ); WriteInt16( (short)targetPos.X ); WriteInt16( (short)targetPos.Y ); WriteInt16( (short)targetPos.Z ); WriteUInt8( (byte)targetFace ); }
public void SetAsValid( Vector3 min, Vector3 max, Vector3 intersect ) { Min = min; Max = max; BlockPos = Vector3I.Truncate( Min ); Valid = true; Vector3I normal = Vector3I.Zero; float dist = float.PositiveInfinity; TestAxis( intersect.X - Min.X, ref dist, -Vector3I.UnitX, ref normal, CpeBlockFace.XMin ); TestAxis( intersect.X - Max.X, ref dist, Vector3I.UnitX, ref normal, CpeBlockFace.XMax ); TestAxis( intersect.Y - Min.Y, ref dist, -Vector3I.UnitY, ref normal, CpeBlockFace.YMin ); TestAxis( intersect.Y - Max.Y, ref dist, Vector3I.UnitY, ref normal, CpeBlockFace.YMax ); TestAxis( intersect.Z - Min.Z, ref dist, -Vector3I.UnitZ, ref normal, CpeBlockFace.ZMin ); TestAxis( intersect.Z - Max.Z, ref dist, Vector3I.UnitZ, ref normal, CpeBlockFace.ZMax ); TranslatedPos = BlockPos + normal; }
/// <summary> Mark this as having a selected block, and /// calculates the closest block face of the selected position. </summary> public void SetAsValid( int x, int y, int z, Vector3 min, Vector3 max, byte block, Vector3 intersect) { Min = min; Max = max; BlockPos = new Vector3I( x, y, z ); Valid = true; BlockType = block; IntersectPoint = intersect; Vector3I normal = Vector3I.Zero; float dist = float.PositiveInfinity; TestAxis( intersect.X - Min.X, ref dist, -Vector3I.UnitX, ref normal, CpeBlockFace.XMin ); TestAxis( intersect.X - Max.X, ref dist, Vector3I.UnitX, ref normal, CpeBlockFace.XMax ); TestAxis( intersect.Y - Min.Y, ref dist, -Vector3I.UnitY, ref normal, CpeBlockFace.YMin ); TestAxis( intersect.Y - Max.Y, ref dist, Vector3I.UnitY, ref normal, CpeBlockFace.YMax ); TestAxis( intersect.Z - Min.Z, ref dist, -Vector3I.UnitZ, ref normal, CpeBlockFace.ZMin ); TestAxis( intersect.Z - Max.Z, ref dist, Vector3I.UnitZ, ref normal, CpeBlockFace.ZMax ); TranslatedPos = BlockPos + normal; }
static BlockID GetBlock(World map, int x, int y, int z, Vector3I origin) { bool sides = map.Env.SidesBlock != Block.Air; int height = Math.Max(1, map.Env.SidesHeight); bool insideMap = map.IsValidPos(origin); // handling of blocks inside the map, above, and on borders if (x >= 0 && z >= 0 && x < map.Width && z < map.Length) { if (y >= map.Height) { return(Block.Air); } if (sides && y == -1 && origin.Y > 0) { return(border); } if (sides && y == 0 && origin.Y < 0) { return(border); } if (sides && x == 0 && y >= 0 && y < height && origin.X < 0) { return(border); } if (sides && z == 0 && y >= 0 && y < height && origin.Z < 0) { return(border); } if (sides && x == (map.Width - 1) && y >= 0 && y < height && origin.X >= map.Width) { return(border); } if (sides && z == (map.Length - 1) && y >= 0 && y < height && origin.Z >= map.Length) { return(border); } if (y >= 0) { return(map.GetBlock(x, y, z)); } } // pick blocks on the map boundaries (when inside the map) if (!sides || !insideMap) { return(Block.Air); } if (y == 0 && origin.Y < 0) { return(border); } bool validX = (x == -1 || x == map.Width) && (z >= 0 && z < map.Length); bool validZ = (z == -1 || z == map.Length) && (x >= 0 && x < map.Width); if (y >= 0 && y < height && (validX || validZ)) { return(border); } return(Block.Air); }
internal void Draw() { EntityShadow mode = game.Players.ShadowMode; Vector3 Position = entity.Position; float posX = Position.X, posZ = Position.Z; int posY = Math.Min((int)Position.Y, game.Map.Height - 1); int index = 0, vb = 0; VertexPos3fTex2fCol4b[] verts = null; int posCount = 0, dataCount = 0; Vector3I * coords = stackalloc Vector3I[4]; ShadowData *data = stackalloc ShadowData[4]; for (int i = 0; i < 4; i++) { coords[i] = new Vector3I(int.MinValue); data[i] = new ShadowData(); } if (mode == EntityShadow.SnapToBlock) { vb = game.Graphics.texVb; verts = game.Graphics.texVerts; if (!GetBlocks(coords, ref posCount, posX, posZ, posY, data, ref dataCount)) { return; } float x1 = Utils.Floor(posX), z1 = Utils.Floor(posZ); DraqSquareShadow(verts, ref index, data[0].Y, 220, x1, z1); } else { vb = game.ModelCache.vb; verts = game.ModelCache.vertices; float x = posX - 7 / 16f, z = posZ - 7 / 16f; if (GetBlocks(coords, ref posCount, x, z, posY, data, ref dataCount) && data[0].A > 0) { DrawCircle(verts, ref index, data, dataCount, x, z); } x = Math.Max(posX - 7 / 16f, Utils.Floor(posX + 7 / 16f)); if (GetBlocks(coords, ref posCount, x, z, posY, data, ref dataCount) && data[0].A > 0) { DrawCircle(verts, ref index, data, dataCount, x, z); } z = Math.Max(posZ - 7 / 16f, Utils.Floor(posZ + 7 / 16f)); if (GetBlocks(coords, ref posCount, x, z, posY, data, ref dataCount) && data[0].A > 0) { DrawCircle(verts, ref index, data, dataCount, x, z); } x = posX - 7 / 16f; if (GetBlocks(coords, ref posCount, x, z, posY, data, ref dataCount) && data[0].A > 0) { DrawCircle(verts, ref index, data, dataCount, x, z); } } if (index == 0) { return; } CheckShadowTexture(game.Graphics); if (!boundShadowTex) { game.Graphics.BindTexture(shadowTex); boundShadowTex = true; } game.Graphics.UpdateDynamicIndexedVb(DrawMode.Triangles, vb, verts, index, index * 6 / 4); }
public byte GetBlock( Vector3I p ) { return mapData[( p.Y * Length + p.Z ) * Width + p.X]; }
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 + ")"); }
public bool IsLit( Vector3I p ) { if( !IsValidPos( p.X, p.Y, p.Z ) ) return true; return p.Y > heightmap[p.Z * Width + p.X]; }
bool CheckIsFree( Vector3I pos, byte newBlock ) { 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 ); for( int id = 0; id < 255; id++ ) { Player player = game.Players[id]; if( player == null ) continue; if( player.CollisionBounds.Intersects( blockBB ) ) return false; } BoundingBox localBB = game.LocalPlayer.CollisionBounds; if( localBB.Intersects( 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 + height + Entity.Adjustment; LocationUpdate update = LocationUpdate.MakePos( p, false ); game.LocalPlayer.SetLocation( update, false ); } return true; }
public void PickBlocks(bool cooldown, bool left, bool middle, bool right) { DateTime now = DateTime.UtcNow; double delta = (now - lastClick).TotalMilliseconds; if (cooldown && delta < 250) { return; // 4 times per second } lastClick = now; Inventory inv = game.Inventory; if (game.Server.UsingPlayerClick && !game.Gui.ActiveScreen.HandlesAllInput) { input.pickingId = -1; input.ButtonStateChanged(MouseButton.Left, left); input.ButtonStateChanged(MouseButton.Right, right); input.ButtonStateChanged(MouseButton.Middle, middle); } if (game.Gui.ActiveScreen.HandlesAllInput || !inv.CanPick) { return; } if (left) { if (game.Mode.PickingLeft()) { return; } Vector3I pos = game.SelectedPos.BlockPos; if (!game.SelectedPos.Valid || !game.World.IsValidPos(pos)) { return; } BlockID old = game.World.GetBlock(pos); if (BlockInfo.Draw[old] == DrawType.Gas || !BlockInfo.CanDelete[old]) { return; } game.Mode.PickLeft(old); } else if (right) { if (game.Mode.PickingRight()) { return; } Vector3I pos = game.SelectedPos.TranslatedPos; if (!game.SelectedPos.Valid || !game.World.IsValidPos(pos)) { return; } BlockID old = game.World.GetBlock(pos); BlockID block = inv.Selected; if (game.AutoRotate) { block = AutoRotate.RotateBlock(game, block); } if (game.CanPick(old) || !BlockInfo.CanPlace[block]) { return; } // air-ish blocks can only replace over other air-ish blocks if (BlockInfo.Draw[block] == DrawType.Gas && BlockInfo.Draw[old] != DrawType.Gas) { return; } if (!PickingHandler.CheckIsFree(game, block)) { return; } game.Mode.PickRight(old, block); } else if (middle) { Vector3I pos = game.SelectedPos.BlockPos; if (!game.SelectedPos.Valid || !game.World.IsValidPos(pos)) { return; } BlockID old = game.World.GetBlock(pos); game.Mode.PickMiddle(old); } }
public static Vector3I Min( Vector3I p1, Vector3I p2 ) { return new Vector3I( Math.Min( p1.X, p2.X ), Math.Min( p1.Y, p2.Y ), Math.Min( p1.Z, p2.Z ) ); }
public bool IsValidPos( Vector3I p ) { return p.X >= 0 && p.Y >= 0 && p.Z >= 0 && p.X < Width && p.Y < Height && p.Z < Length; }
protected Block GetBlock(Vector3 coords) { return((Block)game.Map.SafeGetBlock(Vector3I.Floor(coords))); }
public static Vector3 GetMaxBB(Game game, BlockID block, Vector3I coords) { #if ALPHA if (block == Block.Ladder) { Vector3 newMin = Vector3.Zero; Vector3 newMax = Vector3.One; byte data = game.World.ChunkHandler.GetDataSafe(coords.X, coords.Y, coords.Z); byte flip = 0; if (data == 0x3) { flip = 2; } if (data == 0x4) { flip = 1; } if (data == 0x5) { flip = 3; } Utils.FlipBounds(MinBB[block], MaxBB[block], out newMin, out newMax, flip); return(newMax); } else if (block == Block.Fence) { return(Vector3.One); } else if (block == Block.Torch || block == Block.RedstoneTorchOff || block == Block.RedstoneTorchOn) { Vector3 newMin = Vector3.Zero; Vector3 newMax = Vector3.One; byte data = game.World.ChunkHandler.GetDataSafe(coords.X, coords.Y, coords.Z); byte flip = 0; if (data == 0x3) { flip = 2; } if (data == 0x2) { flip = 1; } if (data == 0x1) { flip = 3; } if (data >= 0x1 && data <= 0x4) { newMin = new Vector3(5.75f / 16f, 3.25f / 16f, 0f); newMax = new Vector3(10.25f / 16f, 12.75f / 16f, 4.75f / 16f); Utils.FlipBounds(newMin, newMax, out newMin, out newMax, flip); } else { newMax = MaxBB[block]; newMax.X += (2f / 3f) / 16f; newMax.Y -= (1f / 3f) / 16f; newMax.Z += (2f / 3f) / 16f; } return(newMax); } #endif return(MaxBB[block]); }
public bool Equals(Vector3I other) { return(X == other.X && Y == other.Y && Z == other.Z); }
public static Vector3I Max(Vector3I p1, Vector3I p2) { return(new Vector3I(Math.Max(p1.X, p2.X), Math.Max(p1.Y, p2.Y), Math.Max(p1.Z, p2.Z))); }
void OnNewMap( object sender, EventArgs e ) { game.ChunkUpdates = 0; ClearChunkCache(); chunks = null; unsortedChunks = null; chunkPos = new Vector3I( int.MaxValue, int.MaxValue, int.MaxValue ); builder.OnNewMap(); }
void UpdateSortOrder() { Player p = game.LocalPlayer; Vector3 cameraPos = game.Camera.GetCameraPos( p.EyePosition ); Vector3I newChunkPos = Vector3I.Floor( cameraPos ); newChunkPos.X = (newChunkPos.X & ~0x0F) + 8; newChunkPos.Y = (newChunkPos.Y & ~0x0F) + 8; newChunkPos.Z = (newChunkPos.Z & ~0x0F) + 8; if( newChunkPos == chunkPos ) return; chunkPos = newChunkPos; for( int i = 0; i < distances.Length; i++ ) { ChunkInfo info = chunks[i]; distances[i] = Utils.DistanceSquared( info.CentreX, info.CentreY, info.CentreZ, chunkPos.X, chunkPos.Y, chunkPos.Z ); } // NOTE: Over 5x faster compared to normal comparison of IComparer<ChunkInfo>.Compare Array.Sort( distances, chunks ); Vector3I pPos = newChunkPos; for( int i = 0; i < chunks.Length; i++ ) { ChunkInfo info = chunks[i]; int dX1 = (info.CentreX - 8) - pPos.X, dX2 = (info.CentreX + 8) - pPos.X; int dY1 = (info.CentreY - 8) - pPos.Y, dY2 = (info.CentreY + 8) - pPos.Y; int dZ1 = (info.CentreZ - 8) - pPos.Z, dZ2 = (info.CentreZ + 8) - pPos.Z; // Back face culling: make sure that the chunk is definitely entirely back facing. info.DrawLeft = !(dX1 <= 0 && dX2 <= 0); info.DrawRight = !(dX1 >= 0 && dX2 >= 0); info.DrawFront = !(dZ1 <= 0 && dZ2 <= 0); info.DrawBack = !(dZ1 >= 0 && dZ2 >= 0); info.DrawBottom = !(dY1 <= 0 && dY2 <= 0); info.DrawTop = !(dY1 >= 0 && dY2 >= 0); } //SimpleOcclusionCulling(); }
public void PickBlocks(bool cooldown, bool left, bool middle, bool right) { DateTime now = DateTime.UtcNow; double delta = (now - lastClick).TotalMilliseconds; if (cooldown && delta < 250) { return; // 4 times per second } lastClick = now; Inventory inv = game.Inventory; if (game.Server.UsingPlayerClick && !game.Gui.ActiveScreen.HandlesAllInput) { input.pickingId = -1; input.ButtonStateChanged(MouseButton.Left, left); input.ButtonStateChanged(MouseButton.Right, right); input.ButtonStateChanged(MouseButton.Middle, middle); } int btns = (left ? 1 : 0) + (right ? 1 : 0) + (middle ? 1 : 0); if (btns > 1 || game.Gui.ActiveScreen.HandlesAllInput || inv.Selected == Block.Invalid) { return; } // always play delete animations, even if we aren't picking a block. if (left) { game.HeldBlockRenderer.anim.SetClickAnim(true); byte id = game.Entities.GetClosetPlayer(game.LocalPlayer); if (id != EntityList.SelfID && game.Mode.PickEntity(id)) { return; } } if (!game.SelectedPos.Valid) { return; } if (middle) { Vector3I pos = game.SelectedPos.BlockPos; if (!game.World.IsValidPos(pos)) { return; } BlockID old = game.World.GetBlock(pos); game.Mode.PickMiddle(old); } else if (left) { Vector3I pos = game.SelectedPos.BlockPos; if (!game.World.IsValidPos(pos)) { return; } BlockID old = game.World.GetBlock(pos); if (game.BlockInfo.Draw[old] == DrawType.Gas || !inv.CanDelete[old]) { return; } game.Mode.PickLeft(old); } else if (right) { Vector3I pos = game.SelectedPos.TranslatedPos; if (!game.World.IsValidPos(pos)) { return; } BlockID old = game.World.GetBlock(pos); BlockID block = inv.Selected; block = AutoRotate.RotateBlock(game, block); if (game.CanPick(old)) { return; } if (!inv.CanPlace[block] && game.SelectedPos.Block != Block.Chest1 && game.SelectedPos.Block != Block.Chest2 && game.SelectedPos.Block != Block.Chest3 && game.SelectedPos.Block != Block.Chest4) { return; } if (!PickingHandler.CheckIsFree(game, block)) { return; } game.Mode.PickRight(old, block); } }
public void Refresh() { if( chunks != null && !game.Map.IsNotLoaded ) { ClearChunkCache(); CreateChunkCache(); } chunkPos = new Vector3I( int.MaxValue, int.MaxValue, int.MaxValue ); }
// 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 + ")" ); }
void ProcessQueue( ChunkInfo src, ChunkQueue queue ) { Vector3I p = new Vector3I( src.CentreX, src.CentreY, src.CentreZ ); while( queue.Size > 0 ) { ChunkInfo chunk = queue.Dequeue(); chunk.VisibilityFlags = chunk.OcclusionFlags; int x1 = chunk.CentreX - 8, x2 = chunk.CentreX + 8; int y1 = chunk.CentreY - 8, y2 = chunk.CentreY + 8; int z1 = chunk.CentreZ - 8, z2 = chunk.CentreZ + 8; int cx = chunk.CentreX >> 4; int cy = chunk.CentreY >> 4; int cz = chunk.CentreZ >> 4; int xOffset, yOffset, zOffset; int dx = Math.Max( x1 - p.X, Math.Max( 0, p.X - x2 ) ); int dy = Math.Max( y1 - p.Y, Math.Max( 0, p.Y - y2 ) ); int dz = Math.Max( z1 - p.Z, Math.Max( 0, p.Z - z2 ) ); int distX, distY, distZ; // X axis collisions int dxLeft = Math.Abs( x1 - p.X ), dxRight = Math.Abs( x2 - p.X ); if( dxLeft < dxRight ) { distX = dxLeft * dxLeft + dy * dy + dz * dz; xOffset = -1; } else { distX = dxRight * dxRight + dy * dy + dz * dz; xOffset = 1; } // Z axis collisions int dxFront = Math.Abs( z1 - p.Z ), dxBack = Math.Abs( z2 - p.Z ); if( dxFront < dxBack ) { distZ = dx * dx + dy * dy + dxFront * dxFront; zOffset = -1; } else { distZ = dx * dx + dy * dy + dxBack * dxBack; zOffset = 1; } // Y axis collisions int dxBottom = Math.Abs( y1 - p.Y ), dxTop = Math.Abs( y2 - p.Y ); if( dxBottom < dxTop ) { distY = dx * dx + dxBottom * dxBottom + dz * dz; yOffset = -1; } else { distY = dx * dx + dxTop * dxTop + dz * dz; yOffset = 1; } int distMin = Math.Min( distX, Math.Min( distY, distZ ) ); bool occlude = true; byte flags = 0; if( distMin == distX ) OccludeX( cx, cy, cz, xOffset, ref occlude, ref flags ); if( distMin == distZ ) OccludeZ( cx, cy, cz, zOffset, ref occlude, ref flags ); if( distMin == distY ) OccludeY( cx, cy, cz, yOffset, ref occlude, ref flags ); if( occlude ) chunk.Occluded = true; chunk.VisibilityFlags = (byte)( flags | chunk.OcclusionFlags ); QueueChunk( cx - 1, cy, cz, queue ); QueueChunk( cx + 1, cy, cz, queue ); QueueChunk( cx, cy, cz - 1, queue ); QueueChunk( cx, cy, cz + 1, queue ); QueueChunk( cx, cy - 1, cz, queue ); QueueChunk( cx, cy + 1, cz, queue ); } Console.WriteLine( "======================" ); }
public bool Equals( Vector3I other ) { return X == other.X && Y == other.Y && Z == other.Z; }
void OnNewMap(object sender, EventArgs e) { heightmap = null; lastPos = new Vector3I(Int32.MaxValue); }
static byte GetBlock( World map, int x, int y, int z, Vector3I origin ) { bool sides = map.Env.SidesBlock != Block.Air; int height = Math.Max( 1, map.Env.SidesHeight ); bool insideMap = map.IsValidPos( origin ); // handling of blocks inside the map, above, and on borders if( x >= 0 && z >= 0 && x < map.Width && z < map.Length ) { if( y >= map.Height ) return 0; if( sides && y == -1 && insideMap ) return border; if( sides && y == 0 && origin.Y < 0 ) return border; if( sides && x == 0 && y >= 0 && y < height && origin.X < 0 ) return border; if( sides && z == 0 && y >= 0 && y < height && origin.Z < 0 ) return border; if( sides && x == (map.Width - 1) && y >= 0 && y < height && origin.X >= map.Width ) return border; if( sides && z == (map.Length - 1) && y >= 0 && y < height && origin.Z >= map.Length ) return border; if( y >= 0 ) return map.GetBlock( x, y, z ); } // pick blocks on the map boundaries (when inside the map) if( !sides || !insideMap ) return 0; if( y == 0 && origin.Y < 0 ) return border; bool validX = (x == -1 || x == map.Width) && (z >= 0 && z < map.Length); bool validZ = (z == -1 || z == map.Length) && (x >= 0 && x < map.Width); if( y >= 0 && y < height && (validX || validZ) ) return border; return 0; }
public void Render(double deltaTime) { Weather weather = map.Weather; if (weather == Weather.Sunny) { return; } graphics.Texturing = true; graphics.BindTexture(weather == Weather.Rainy ? game.RainTexId : game.SnowTexId); Vector3 camPos = game.CurrentCameraPos; Vector3I pos = Vector3I.Floor(camPos); bool moved = pos != lastPos; lastPos = pos; float speed = weather == Weather.Rainy ? 1f : 0.20f; vOffset = -(float)game.accumulator * speed; rainAcc += deltaTime; bool particles = weather == Weather.Rainy; int index = 0; graphics.AlphaBlending = true; graphics.DepthWrite = false; FastColour col = game.Map.Sunlight; for (int dx = -extent; dx <= extent; dx++) { for (int dz = -extent; dz <= extent; dz++) { float rainY = GetRainHeight(pos.X + dx, pos.Z + dz); float height = Math.Max(game.Map.Height, pos.Y + 64) - rainY; if (height <= 0) { continue; } if (particles && (rainAcc >= 0.25 || moved)) { game.ParticleManager.AddRainParticle(new Vector3(pos.X + dx, rainY, pos.Z + dz)); } col.A = (byte)Math.Max(0, AlphaAt(dx * dx + dz * dz)); MakeRainForSquare(pos.X + dx, rainY, height, pos.Z + dz, col, ref index); } } if (particles && (rainAcc >= 0.25 || moved)) { rainAcc = 0; } if (index > 0) { graphics.SetBatchFormat(VertexFormat.Pos3fTex2fCol4b); graphics.UpdateDynamicIndexedVb(DrawMode.Triangles, weatherVb, vertices, index, index * 6 / 4); } graphics.AlphaBlending = false; graphics.Texturing = false; graphics.DepthWrite = true; }
bool IntersectsOtherPlayers( Vector3I pos, byte newType ) { float height = game.BlockInfo.Height[newType]; BoundingBox blockBB = new BoundingBox( pos.X, pos.Y, pos.Z, pos.X + 1, pos.Y + height, pos.Z + 1 ); for( int id = 0; id < 255; id++ ) { Player player = game.Players[id]; if( player == null ) continue; if( player.CollisionBounds.Intersects( blockBB ) ) return true; } return false; }
public void SetBlock( Vector3I p, byte blockId ) { SetBlock( p.X, p.Y, p.Z, blockId ); }