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); 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) + BlockInfo.RenderMinBB[t.Block]; Vector3 max = new Vector3(x, y, z) + 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 + ")"); }
void FindReachableBlocks(ref int count, ref Vector3 size, out BoundingBox entityBB, out BoundingBox entityExtentBB) { Vector3 vel = Velocity; Vector3 pos = Position; entityBB = new BoundingBox( pos.X - size.X / 2, pos.Y, pos.Z - size.Z / 2, pos.X + size.X / 2, pos.Y + size.Y, pos.Z + size.Z / 2 ); // Exact maximum extent the entity can reach, and the equivalent map coordinates. entityExtentBB = new BoundingBox( vel.X < 0 ? entityBB.Min.X + vel.X : entityBB.Min.X, vel.Y < 0 ? entityBB.Min.Y + vel.Y : entityBB.Min.Y, vel.Z < 0 ? entityBB.Min.Z + vel.Z : entityBB.Min.Z, vel.X > 0 ? entityBB.Max.X + vel.X : entityBB.Max.X, vel.Y > 0 ? entityBB.Max.Y + vel.Y : entityBB.Max.Y, vel.Z > 0 ? entityBB.Max.Z + vel.Z : entityBB.Max.Z ); Vector3I min = Vector3I.Floor(entityExtentBB.Min); Vector3I max = Vector3I.Floor(entityExtentBB.Max); int elements = (max.X + 1 - min.X) * (max.Y + 1 - min.Y) * (max.Z + 1 - min.Z); if (elements > stateCache.Length) { stateCache = new State[elements]; } BoundingBox blockBB = default(BoundingBox); // Order loops so that we minimise cache misses for (int y = min.Y; y <= max.Y; y++) { for (int z = min.Z; z <= max.Z; z++) { for (int x = min.X; x <= max.X; x++) { byte blockId = GetPhysicsBlockId(x, y, z); if (!GetBoundingBox(blockId, x, y, z, ref blockBB)) { continue; } if (!entityExtentBB.Intersects(blockBB)) { continue; // necessary for non whole blocks. (slabs) } float tx = 0, ty = 0, tz = 0; CalcTime(ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz); if (tx > 1 || ty > 1 || tz > 1) { continue; } float tSquared = tx * tx + ty * ty + tz * tz; stateCache[count++] = new State(blockBB, blockId, tSquared); } } } }
/// <summary> Determines whether any of the blocks that intersect the /// given bounding box satisfy the given condition. </summary> public bool TouchesAny(BoundingBox bounds, Predicate <byte> condition) { Vector3I bbMin = Vector3I.Floor(bounds.Min); Vector3I bbMax = Vector3I.Floor(bounds.Max); // Order loops so that we minimise cache misses for (int y = bbMin.Y; y <= bbMax.Y; y++) { for (int z = bbMin.Z; z <= bbMax.Z; z++) { for (int x = bbMin.X; x <= bbMax.X; x++) { if (!game.Map.IsValidPos(x, y, z)) { continue; } byte block = game.Map.GetBlock(x, y, z); Vector3 min = new Vector3(x, y, z) + info.MinBB[block]; Vector3 max = new Vector3(x, y, z) + info.MaxBB[block]; BoundingBox blockBB = new BoundingBox(min, max); if (!blockBB.Intersects(bounds)) { continue; } if (condition(block)) { return(true); } } } } return(false); }
float LowestModifier(BoundingBox bounds, bool checkSolid) { Vector3I bbMin = Vector3I.Floor(bounds.Min); Vector3I bbMax = Vector3I.Floor(bounds.Max); float modifier = float.PositiveInfinity; for (int y = bbMin.Y; y <= bbMax.Y; y++) { for (int z = bbMin.Z; z <= bbMax.Z; z++) { for (int x = bbMin.X; x <= bbMax.X; x++) { byte block = game.Map.SafeGetBlock(x, y, z); if (block == 0) { continue; } BlockCollideType type = info.CollideType[block]; if (type == BlockCollideType.Solid && !checkSolid) { continue; } modifier = Math.Min(modifier, info.SpeedMultiplier[block]); if (block >= BlockInfo.CpeBlocksCount && type == BlockCollideType.SwimThrough) { useLiquidGravity = true; } } } } return(modifier == float.PositiveInfinity ? 1 : modifier); }
bool CanSlideThrough(ref BoundingBox adjFinalBB) { Vector3I bbMin = Vector3I.Floor(adjFinalBB.Min); Vector3I bbMax = Vector3I.Floor(adjFinalBB.Max); for (int y = bbMin.Y; y <= bbMax.Y; y++) { for (int z = bbMin.Z; z <= bbMax.Z; z++) { for (int x = bbMin.X; x <= bbMax.X; x++) { byte block = GetPhysicsBlockId(x, y, z); Vector3 min = new Vector3(x, y, z) + info.MinBB[block]; Vector3 max = new Vector3(x, y, z) + info.MaxBB[block]; BoundingBox blockBB = new BoundingBox(min, max); if (!blockBB.Intersects(adjFinalBB)) { continue; } if (info.CollideType[GetPhysicsBlockId(x, y, z)] == BlockCollideType.Solid) { return(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 + ")"); }
static bool PushbackPlace(Game game, AABB blockBB) { Vector3 newP = game.LocalPlayer.Position; Vector3 oldP = game.LocalPlayer.Position; // Offset position by the closest face PickedPos selected = game.SelectedPos; if (selected.Face == BlockFace.XMax) { newP.X = blockBB.Max.X + 0.5f; } else if (selected.Face == BlockFace.ZMax) { newP.Z = blockBB.Max.Z + 0.5f; } else if (selected.Face == BlockFace.XMin) { newP.X = blockBB.Min.X - 0.5f; } else if (selected.Face == BlockFace.ZMin) { newP.Z = blockBB.Min.Z - 0.5f; } else if (selected.Face == BlockFace.YMax) { newP.Y = blockBB.Min.Y + 1 + Entity.Adjustment; } else if (selected.Face == BlockFace.YMin) { newP.Y = blockBB.Min.Y - game.LocalPlayer.Size.Y - Entity.Adjustment; } Vector3I newLoc = Vector3I.Floor(newP); bool validPos = newLoc.X >= 0 && newLoc.Y >= 0 && newLoc.Z >= 0 && newLoc.X < game.World.Width && newP.Z < game.World.Length; if (!validPos) { return(false); } game.LocalPlayer.Position = newP; if (!game.LocalPlayer.Hacks.Noclip && game.LocalPlayer.TouchesAny(b => game.BlockInfo.Collide[b] == CollideType.Solid)) { game.LocalPlayer.Position = oldP; return(false); } game.LocalPlayer.Position = oldP; LocationUpdate update = LocationUpdate.MakePos(newP, false); game.LocalPlayer.SetLocation(update, false); return(true); }
static bool PushbackPlace(Game game, AABB blockBB) { LocalPlayer p = game.LocalPlayer; Vector3 curPos = p.Position, adjPos = p.Position; // Offset position by the closest face PickedPos selected = game.SelectedPos; if (selected.Face == BlockFace.XMax) { adjPos.X = blockBB.Max.X + 0.5f; } else if (selected.Face == BlockFace.ZMax) { adjPos.Z = blockBB.Max.Z + 0.5f; } else if (selected.Face == BlockFace.XMin) { adjPos.X = blockBB.Min.X - 0.5f; } else if (selected.Face == BlockFace.ZMin) { adjPos.Z = blockBB.Min.Z - 0.5f; } else if (selected.Face == BlockFace.YMax) { adjPos.Y = blockBB.Min.Y + 1 + Entity.Adjustment; } else if (selected.Face == BlockFace.YMin) { adjPos.Y = blockBB.Min.Y - p.Size.Y - Entity.Adjustment; } Vector3I newLoc = Vector3I.Floor(adjPos); bool validPos = newLoc.X >= 0 && newLoc.Y >= 0 && newLoc.Z >= 0 && newLoc.X < game.World.Width && adjPos.Z < game.World.Length; if (!validPos) { return(false); } p.Position = adjPos; if (!p.Hacks.Noclip && p.TouchesAny(p.Bounds, touchesAnySolid)) { p.Position = curPos; return(false); } p.Position = curPos; LocationUpdate update = LocationUpdate.MakePos(adjPos, false); p.SetLocation(update, false); return(true); }
public void SetVectors(Vector3 origin, Vector3 dir) { Origin = origin; Dir = 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. Vector3I cellBoundary; cellBoundary.X = start.X + (step.X > 0 ? 1 : 0); cellBoundary.Y = start.Y + (step.Y > 0 ? 1 : 0); cellBoundary.Z = 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; } }
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); }
void SimpleOcclusionCulling() // TODO: still broken { Vector3 p = game.LocalPlayer.EyePosition; Vector3I mapLoc = Vector3I.Floor(p); Utils.Clamp(ref mapLoc.X, 0, game.Map.Width - 1); Utils.Clamp(ref mapLoc.Y, 0, game.Map.Height - 1); Utils.Clamp(ref mapLoc.Z, 0, game.Map.Length - 1); int cx = mapLoc.X >> 4; int cy = mapLoc.Y >> 4; int cz = mapLoc.Z >> 4; ChunkInfo chunkIn = unsortedChunks[cx + chunksX * (cy + cz * chunksY)]; byte chunkInOcclusionFlags = chunkIn.OcclusionFlags; chunkIn.OcclusionFlags = 0; ChunkQueue queue = new ChunkQueue(chunksX * chunksY * chunksZ); for (int i = 0; i < chunks.Length; i++) { ChunkInfo chunk = chunks[i]; chunk.Visited = false; chunk.Occluded = false; chunk.OccludedFlags = chunk.OcclusionFlags; chunk.DistanceFlags = 0; } chunkIn.Visited = true; mapLoc = Vector3I.Floor(p); if (game.Map.IsValidPos(mapLoc)) { chunkIn.DistanceFlags = flagX | flagY | flagZ; } else { chunkIn.OccludedFlags = chunkIn.OcclusionFlags = chunkInOcclusionFlags; chunkIn.DistanceFlags |= (mapLoc.X < 0 || mapLoc.X >= game.Map.Width) ? flagX : (byte)0; chunkIn.DistanceFlags |= (mapLoc.Y < 0 || mapLoc.Y >= game.Map.Height) ? flagY : (byte)0; chunkIn.DistanceFlags |= (mapLoc.Z < 0 || mapLoc.Z >= game.Map.Length) ? flagZ : (byte)0; } Console.WriteLine("SRC {0}", cx + "," + cy + "," + cz); QueueChunk(cx - 1, cy, cz, queue); QueueChunk(cx + 1, cy, cz, queue); QueueChunk(cx, cy - 1, cz, queue); QueueChunk(cx, cy + 1, cz, queue); QueueChunk(cx, cy, cz - 1, queue); QueueChunk(cx, cy, cz + 1, queue); ProcessQueue(queue); chunkIn.OcclusionFlags = chunkInOcclusionFlags; }
static void CalcVectors(Vector3 origin, Vector3 dir, out Vector3I step, out Vector3I cellBoundary, out Vector3 tMax, out Vector3 tDelta) { Vector3I start = Vector3I.Floor(origin); // 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; } }
/// <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 + ")"); }
void UpdateSortOrder() { Vector3 cameraPos = game.CurrentCameraPos; 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 if (distances.Length > 1) { //FastSorter<ChunkInfo>.QuickSort( distances, chunks, 0, chunks.Length - 1 ); Array.Sort(distances, chunks, 0, chunks.Length - 1); } 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); } RecalcBooleans(false); //SimpleOcclusionCulling(); }
internal bool HandleKeyDown(Key key) { KeyMap keys = game.InputHandler.Keys; if (key == keys[KeyBinding.Respawn] && CanRespawn && HacksEnabled) { Vector3I p = Vector3I.Floor(SpawnPoint); if (game.Map.IsValidPos(p)) { // Spawn player at highest valid position. for (int y = p.Y; y <= game.Map.Height; y++) { byte block1 = GetPhysicsBlockId(p.X, y, p.Z); byte block2 = GetPhysicsBlockId(p.X, y + 1, p.Z); if (info.CollideType[block1] != BlockCollideType.Solid && info.CollideType[block2] != BlockCollideType.Solid) { p.Y = y; break; } } } Vector3 spawn = (Vector3)p + new Vector3(0.5f, 0.01f, 0.5f); LocationUpdate update = LocationUpdate.MakePos(spawn, false); SetLocation(update, false); } else if (key == keys[KeyBinding.SetSpawn] && CanRespawn && HacksEnabled) { SpawnPoint = Position; } else if (key == keys[KeyBinding.Fly] && CanFly && HacksEnabled) { flying = !flying; } else if (key == keys[KeyBinding.NoClip] && CanNoclip && HacksEnabled) { noClip = !noClip; } else { return(false); } return(true); }
static bool RayTrace(Game game, Vector3 origin, Vector3 dir, float reach, PickedPos pos, bool clipMode) { t.SetVectors(origin, dir); double 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); Vector3I coords2 = new Vector3I(t.X, t.Y, t.Z); //Vector3 min = coords + BlockInfo.RenderMinBB[t.Block]; //Vector3 max = coords + BlockInfo.RenderMaxBB[t.Block]; Vector3 min = coords + BlockInfo.GetMinBB(game, t.Block, coords2); Vector3 max = coords + BlockInfo.GetMaxBB(game, t.Block, coords2); double dx = Math.Min(Math.Abs(origin.X - min.X), Math.Abs(origin.X - max.X)); double dy = Math.Min(Math.Abs(origin.Y - min.Y), Math.Abs(origin.Y - max.Y)); double 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 + ")"); }
float LowestModifier(BoundingBox bounds, bool checkSolid) { Vector3I bbMin = Vector3I.Floor(bounds.Min); Vector3I bbMax = Vector3I.Floor(bounds.Max); float modifier = inf; for (int y = bbMin.Y; y <= bbMax.Y; y++) { for (int z = bbMin.Z; z <= bbMax.Z; z++) { for (int x = bbMin.X; x <= bbMax.X; x++) { byte block = game.Map.SafeGetBlock(x, y, z); if (block == 0) { continue; } BlockCollideType type = info.CollideType[block]; if (type == BlockCollideType.Solid && !checkSolid) { continue; } Vector3 min = new Vector3(x, y, z) + info.MinBB[block]; Vector3 max = new Vector3(x, y, z) + info.MaxBB[block]; BoundingBox blockBB = new BoundingBox(min, max); if (!blockBB.Intersects(bounds)) { continue; } modifier = Math.Min(modifier, info.SpeedMultiplier[block]); if (block >= BlockInfo.CpeBlocksCount && type == BlockCollideType.SwimThrough) { useLiquidGravity = true; } } } } return(modifier); }
void DrawPosition() { int index = 0; TextureRec xy = new TextureRec(2, posTexture.Y1, baseWidth, posTexture.Height); TextureRec uv = new TextureRec(0, 0, posTexture.U2, posTexture.V2); IGraphicsApi.Make2DQuad(xy, uv, game.ModelCache.vertices, ref index); Vector3I pos = Vector3I.Floor(game.LocalPlayer.Position); curX = baseWidth + 2; AddChar(13, ref index); AddInt(pos.X, ref index, true); AddInt(pos.Y, ref index, true); AddInt(pos.Z, ref index, false); AddChar(14, ref index); graphicsApi.BindTexture(posTexture.ID); graphicsApi.UpdateDynamicIndexedVb(DrawMode.Triangles, game.ModelCache.vb, game.ModelCache.vertices, index, index * 6 / 4); }
internal bool HandleKeyDown(Key key) { KeyMap keys = game.InputHandler.Keys; if (key == keys[KeyBinding.Respawn] && Hacks.CanRespawn) { Vector3I p = Vector3I.Floor(SpawnPoint); if (game.Map.IsValidPos(p)) { // Spawn player at highest valid position. for (int y = p.Y; y <= game.Map.Height; y++) { byte block1 = physics.GetPhysicsBlockId(p.X, y, p.Z); byte block2 = physics.GetPhysicsBlockId(p.X, y + 1, p.Z); if (info.CollideType[block1] != BlockCollideType.Solid && info.CollideType[block2] != BlockCollideType.Solid) { p.Y = y; break; } } } Vector3 spawn = (Vector3)p + new Vector3(0.5f, 0.01f, 0.5f); LocationUpdate update = LocationUpdate.MakePosAndOri(spawn, SpawnYaw, SpawnPitch, false); SetLocation(update, false); } else if (key == keys[KeyBinding.SetSpawn] && Hacks.CanRespawn) { SpawnPoint = Position; SpawnYaw = HeadYawDegrees; SpawnPitch = PitchDegrees; } else if (key == keys[KeyBinding.Fly] && Hacks.CanFly && Hacks.Enabled) { flying = !flying; } else if (key == keys[KeyBinding.NoClip] && Hacks.CanNoclip && Hacks.Enabled) { if (noClip) { Velocity.Y = 0; } noClip = !noClip; } else if (key == keys[KeyBinding.Jump] && !onGround && !(flying || noClip)) { if (!firstJump && Hacks.CanDoubleJump && Hacks.DoubleJump) { DoNormalJump(); firstJump = true; } else if (!secondJump && Hacks.CanDoubleJump && Hacks.DoubleJump) { DoNormalJump(); secondJump = true; } } else { return(false); } return(true); }
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 + ")"); }
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 + ")"); }
static byte GetBlock(Map map, int x, int y, int z, Vector3 origin) { bool sides = map.SidesBlock != Block.Air; int height = Math.Max(1, map.SidesHeight); bool insideMap = map.IsValidPos(Vector3I.Floor(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; } if (heightmap == null) { InitHeightmap(); } 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.AlphaTest = false; 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.AlphaTest = true; graphics.DepthWrite = true; }
protected Block GetBlock(Vector3 coords) { return((Block)game.Map.SafeGetBlock(Vector3I.Floor(coords))); }