private void CheckWithTerrain(IAABBEntity entity, ReadOnlyWorld world) { Vector3 collisionPoint, collisionDirection; if (entity.Position.Y > 0 && entity.Position.Y <= 127) // Don't do checks outside the map { bool fireEvent = entity.Velocity != Vector3.Zero; // Do terrain collisions if (AdjustVelocityX(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) { entity.TerrainCollision(collisionPoint, collisionDirection); } } if (AdjustVelocityY(entity, world, out collisionPoint, out collisionDirection)) { entity.Velocity *= new Vector3(0.1, 1, 0.1); // TODO: More sophisticated friction if (fireEvent) { entity.TerrainCollision(collisionPoint, collisionDirection); } } if (AdjustVelocityZ(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) { entity.TerrainCollision(collisionPoint, collisionDirection); } } } }
public MultiplayerClient(TrueCraftUser user) { User = user; Client = new TcpClient(); PacketReader = new PacketReader(); PacketReader.RegisterCorePackets(); PacketHandlers = new PacketHandler[0x100]; Handlers.PacketHandlers.RegisterHandlers(this); World = new ReadOnlyWorld(); Inventory = new InventoryWindow(null); var repo = new BlockRepository(); repo.DiscoverBlockProviders(); World.World.BlockRepository = repo; World.World.ChunkProvider = new EmptyGenerator(); Physics = new PhysicsEngine(World.World, repo); SocketPool = new SocketAsyncEventArgsPool(100, 200, 65536); connected = 0; cancel = new CancellationTokenSource(); Health = 20; var crafting = new CraftingRepository(); CraftingRepository = crafting; crafting.DiscoverRecipes(); }
public PhysicsEngine(ReadOnlyWorld world, IBlockPhysicsProvider physicsProvider) { World = world; Entities = new List <IPhysicsEntity>(); EntityLock = new object(); LastUpdate = DateTime.MinValue; BlockPhysicsProvider = physicsProvider; MillisecondsBetweenUpdates = 1000 / 20; }
public PhysicsEngine(ReadOnlyWorld world, IBlockPhysicsProvider physicsProvider) { World = world; Entities = new List<IPhysicsEntity>(); EntityLock = new object(); LastUpdate = DateTime.MinValue; BlockPhysicsProvider = physicsProvider; MillisecondsBetweenUpdates = 1000 / 20; }
// Thanks to http://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game public static Tuple <Coordinates3D, BlockFace> Cast(ReadOnlyWorld world, Ray ray, IBlockRepository repository, int posmax, int negmax) { // TODO: There are more efficient ways of doing this, fwiw double min = negmax * 2; var pick = -Coordinates3D.One; var face = BlockFace.PositiveY; for (var x = -posmax; x <= posmax; x++) { for (var y = -negmax; y <= posmax; y++) { for (var z = -posmax; z <= posmax; z++) { var coords = (Coordinates3D)(new Vector3(x, y, z) + ray.Position).Round(); if (!world.IsValidPosition(coords)) { continue; } var Id = world.GetBlockId(coords); if (Id != 0) { var provider = repository.GetBlockProvider(Id); var box = provider.InteractiveBoundingBox; if (box != null) { BlockFace _face; var distance = ray.Intersects(box.Value.OffsetBy(coords.AsVector3()), out _face); if (distance != null && distance.Value < min) { min = distance.Value; pick = coords; face = _face; } } } } } } if (pick == -Coordinates3D.One) { return(null); } return(new Tuple <Coordinates3D, BlockFace>(pick, face)); }
public MultiplayerClient(TrueCraftUser user) { User = user; Client = new TcpClient(); PacketReader = new PacketReader(); PacketReader.RegisterCorePackets(); PacketHandlers = new PacketHandler[0x100]; Handlers.PacketHandlers.RegisterHandlers(this); World = new ReadOnlyWorld(); var repo = new BlockRepository(); repo.DiscoverBlockProviders(); World.World.BlockRepository = repo; World.World.ChunkProvider = new EmptyGenerator(); Physics = new PhysicsEngine(World, repo); SocketPool = new SocketAsyncEventArgsPool(100, 200, 65536); connected = 0; cancel = new CancellationTokenSource(); }
// Thanks to http://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game public static Tuple<Coordinates3D, BlockFace> Cast(ReadOnlyWorld world, Ray ray, IBlockRepository repository, int posmax, int negmax) { // TODO: There are more efficient ways of doing this, fwiw double min = negmax * 2; var pick = -Coordinates3D.One; var face = BlockFace.PositiveY; for (int x = -posmax; x <= posmax; x++) { for (int y = -negmax; y <= posmax; y++) { for (int z = -posmax; z <= posmax; z++) { var coords = (Coordinates3D)(new Vector3(x, y, z) + ray.Position).Round(); if (!world.IsValidPosition(coords)) continue; var id = world.GetBlockID(coords); if (id != 0) { var provider = repository.GetBlockProvider(id); var box = provider.InteractiveBoundingBox; if (box != null) { BlockFace _face; var distance = ray.Intersects(box.Value.OffsetBy(coords), out _face); if (distance != null && distance.Value < min) { min = distance.Value; pick = coords; face = _face; } } } } } } if (pick == -Coordinates3D.One) return null; return new Tuple<Coordinates3D, BlockFace>(pick, face); }
/// <summary> /// Performs terrain collision tests and adjusts the Z-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityZ(IAABBEntity entity, ReadOnlyWorld world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.Z == 0) { return(false); } // Do some enviornment guessing to improve speed int minX = (int)entity.Position.X - (entity.Position.X < 0 ? 1 : 0); int maxX = (int)(entity.Position.X + entity.Size.Depth) - (entity.Position.X < 0 ? 1 : 0); int minY = (int)entity.Position.Y - (entity.Position.Y < 0 ? 1 : 0); int maxY = (int)(entity.Position.Y + entity.Size.Width) - (entity.Position.Y < 0 ? 1 : 0); int minZ, maxZ; // Expand bounding box to include area to be tested if (entity.Velocity.Z < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z + entity.Velocity.Z), entity.BoundingBox.Max); maxZ = (int)(TempBoundingBox.Max.Z); minZ = (int)(TempBoundingBox.Min.Z + entity.Velocity.Z) - 1; } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min, new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z + entity.Velocity.Z) ); minZ = (int)(entity.BoundingBox.Min.Z); maxZ = (int)(entity.BoundingBox.Max.Z + entity.Velocity.Z) + 1; } // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Coordinates3D(x, y, z); var boundingBox = BlockPhysicsProvider.GetBoundingBox(world.World, position); if (boundingBox == null) { continue; } blockBox = boundingBox.Value.OffsetBy(position + new Vector3(0.5)); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.Z < 0) { if (!collisionPoint.HasValue) { collisionPoint = blockBox.Max.Z; } else if (collisionPoint.Value < blockBox.Max.Z) { collisionPoint = blockBox.Max.Z; } } else { if (!collisionPoint.HasValue) { collisionPoint = blockBox.Min.Z; } else if (collisionPoint.Value > blockBox.Min.Z) { collisionPoint = blockBox.Min.Z; } } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.Z < 0) { entity.Velocity = new Vector3( entity.Velocity.X, entity.Velocity.Y, entity.Velocity.Z - (TempBoundingBox.Min.Z - collisionPoint.Value)); collisionDirection = Vector3.Backwards; } else if (entity.Velocity.Z > 0) { entity.Velocity = new Vector3( entity.Velocity.X, entity.Velocity.Y, entity.Velocity.Z - (TempBoundingBox.Max.Z - collisionPoint.Value)); collisionDirection = Vector3.Forwards; } return(true); } return(false); }
/// <summary> /// Performs terrain collision tests and adjusts the Y-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityY(IAABBEntity entity, ReadOnlyWorld world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.Y == 0) { return(false); } // Do some enviornment guessing to improve speed int minX = (int)entity.Position.X - (entity.Position.X < 0 ? 1 : 0); int maxX = (int)(entity.Position.X + entity.Size.Width) - (entity.Position.X < 0 ? 1 : 0); int minZ = (int)entity.Position.Z - (entity.Position.Z < 0 ? 1 : 0); int maxZ = (int)(entity.Position.Z + entity.Size.Depth) - (entity.Position.Z < 0 ? 1 : 0); int minY, maxY; // Expand bounding box to include area to be tested if (entity.Velocity.Y < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y + entity.Velocity.Y, entity.BoundingBox.Min.Z), entity.BoundingBox.Max); maxY = (int)(TempBoundingBox.Max.Y); minY = (int)(TempBoundingBox.Min.Y + entity.Velocity.Y) - 1; } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min, new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y + entity.Velocity.Y, entity.BoundingBox.Max.Z)); minY = (int)(entity.BoundingBox.Min.Y); maxY = (int)(entity.BoundingBox.Max.Y + entity.Velocity.Y) + 1; } // Clamp Y into map boundaries if (minY < 0) { minY = 0; } if (minY >= TrueCraft.Core.World.World.Height) { minY = TrueCraft.Core.World.World.Height - 1; } if (maxY < 0) { maxY = 0; } if (maxY >= TrueCraft.Core.World.World.Height) { maxY = TrueCraft.Core.World.World.Height - 1; } // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Coordinates3D(x, y, z); if (!World.IsValidPosition(position)) { continue; } var boundingBox = BlockPhysicsProvider.GetBoundingBox(world.World, position); if (boundingBox == null) { continue; } blockBox = boundingBox.Value.OffsetBy(position + new Vector3(0.5)); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.Y < 0) { if (!collisionPoint.HasValue) { collisionPoint = blockBox.Max.Y; } else if (collisionPoint.Value < blockBox.Max.Y) { collisionPoint = blockBox.Max.Y; } } else { if (!collisionPoint.HasValue) { collisionPoint = blockBox.Min.Y; } else if (collisionPoint.Value > blockBox.Min.Y) { collisionPoint = blockBox.Min.Y; } } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.Y < 0) { // TODO: Do block event //var block = world.GetBlock(collision); //block.OnBlockWalkedOn(world, collision, this); entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y + (collisionPoint.Value - TempBoundingBox.Min.Y), entity.Velocity.Z); collisionDirection = Vector3.Down; } else if (entity.Velocity.Y > 0) { entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y - (TempBoundingBox.Max.Y - collisionPoint.Value), entity.Velocity.Z); collisionDirection = Vector3.Up; } return(true); } return(false); }
private void CheckWithTerrain(IAABBEntity entity, ReadOnlyWorld world) { Vector3 collisionPoint, collisionDirection; if (entity.Position.Y > 0 && entity.Position.Y <= 127) // Don't do checks outside the map { bool fireEvent = entity.Velocity != Vector3.Zero; // Do terrain collisions if (AdjustVelocityX(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) entity.TerrainCollision(collisionPoint, collisionDirection); } if (AdjustVelocityY(entity, world, out collisionPoint, out collisionDirection)) { entity.Velocity *= new Vector3(0.1, 1, 0.1); // TODO: More sophisticated friction if (fireEvent) entity.TerrainCollision(collisionPoint, collisionDirection); } if (AdjustVelocityZ(entity, world, out collisionPoint, out collisionDirection)) { if (fireEvent) entity.TerrainCollision(collisionPoint, collisionDirection); } } }
/// <summary> /// Performs terrain collision tests and adjusts the Z-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityZ(IAABBEntity entity, ReadOnlyWorld world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.Z == 0) return false; // Do some enviornment guessing to improve speed int minX = (int)entity.Position.X - (entity.Position.X < 0 ? 1 : 0); int maxX = (int)(entity.Position.X + entity.Size.Depth) - (entity.Position.X < 0 ? 1 : 0); int minY = (int)entity.Position.Y - (entity.Position.Y < 0 ? 1 : 0); int maxY = (int)(entity.Position.Y + entity.Size.Width) - (entity.Position.Y < 0 ? 1 : 0); int minZ, maxZ; // Expand bounding box to include area to be tested if (entity.Velocity.Z < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z + entity.Velocity.Z), entity.BoundingBox.Max); maxZ = (int)(TempBoundingBox.Max.Z); minZ = (int)(TempBoundingBox.Min.Z + entity.Velocity.Z) - 1; } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min, new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z + entity.Velocity.Z) ); minZ = (int)(entity.BoundingBox.Min.Z); maxZ = (int)(entity.BoundingBox.Max.Z + entity.Velocity.Z) + 1; } // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Coordinates3D(x, y, z); var boundingBox = BlockPhysicsProvider.GetBoundingBox(world.World, position); if (boundingBox == null) continue; blockBox = boundingBox.Value.OffsetBy(position + new Vector3(0.5)); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.Z < 0) { if (!collisionPoint.HasValue) collisionPoint = blockBox.Max.Z; else if (collisionPoint.Value < blockBox.Max.Z) collisionPoint = blockBox.Max.Z; } else { if (!collisionPoint.HasValue) collisionPoint = blockBox.Min.Z; else if (collisionPoint.Value > blockBox.Min.Z) collisionPoint = blockBox.Min.Z; } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.Z < 0) { entity.Velocity = new Vector3( entity.Velocity.X, entity.Velocity.Y, entity.Velocity.Z - (TempBoundingBox.Min.Z - collisionPoint.Value)); collisionDirection = Vector3.Backwards; } else if (entity.Velocity.Z > 0) { entity.Velocity = new Vector3( entity.Velocity.X, entity.Velocity.Y, entity.Velocity.Z - (TempBoundingBox.Max.Z - collisionPoint.Value)); collisionDirection = Vector3.Forwards; } return true; } return false; }
/// <summary> /// Performs terrain collision tests and adjusts the Y-axis velocity accordingly /// </summary> /// <returns>True if the entity collides with the terrain</returns> private bool AdjustVelocityY(IAABBEntity entity, ReadOnlyWorld world, out Vector3 collision, out Vector3 collisionDirection) { collision = Vector3.Zero; collisionDirection = Vector3.Zero; if (entity.Velocity.Y == 0) return false; // Do some enviornment guessing to improve speed int minX = (int)entity.Position.X - (entity.Position.X < 0 ? 1 : 0); int maxX = (int)(entity.Position.X + entity.Size.Width) - (entity.Position.X < 0 ? 1 : 0); int minZ = (int)entity.Position.Z - (entity.Position.Z < 0 ? 1 : 0); int maxZ = (int)(entity.Position.Z + entity.Size.Depth) - (entity.Position.Z < 0 ? 1 : 0); int minY, maxY; // Expand bounding box to include area to be tested if (entity.Velocity.Y < 0) { TempBoundingBox = new BoundingBox( new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y + entity.Velocity.Y, entity.BoundingBox.Min.Z), entity.BoundingBox.Max); maxY = (int)(TempBoundingBox.Max.Y); minY = (int)(TempBoundingBox.Min.Y + entity.Velocity.Y) - 1; } else { TempBoundingBox = new BoundingBox( entity.BoundingBox.Min, new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y + entity.Velocity.Y, entity.BoundingBox.Max.Z)); minY = (int)(entity.BoundingBox.Min.Y); maxY = (int)(entity.BoundingBox.Max.Y + entity.Velocity.Y) + 1; } // Clamp Y into map boundaries if (minY < 0) minY = 0; if (minY >= TrueCraft.Core.World.World.Height) minY = TrueCraft.Core.World.World.Height - 1; if (maxY < 0) maxY = 0; if (maxY >= TrueCraft.Core.World.World.Height) maxY = TrueCraft.Core.World.World.Height - 1; // Do terrain checks double? collisionPoint = null; BoundingBox blockBox; for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var position = new Coordinates3D(x, y, z); if (!World.IsValidPosition(position)) continue; var boundingBox = BlockPhysicsProvider.GetBoundingBox(world.World, position); if (boundingBox == null) continue; blockBox = boundingBox.Value.OffsetBy(position + new Vector3(0.5)); if (TempBoundingBox.Intersects(blockBox)) { if (entity.Velocity.Y < 0) { if (!collisionPoint.HasValue) collisionPoint = blockBox.Max.Y; else if (collisionPoint.Value < blockBox.Max.Y) collisionPoint = blockBox.Max.Y; } else { if (!collisionPoint.HasValue) collisionPoint = blockBox.Min.Y; else if (collisionPoint.Value > blockBox.Min.Y) collisionPoint = blockBox.Min.Y; } collision = position; } } } } if (collisionPoint != null) { if (entity.Velocity.Y < 0) { // TODO: Do block event //var block = world.GetBlock(collision); //block.OnBlockWalkedOn(world, collision, this); entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y + (collisionPoint.Value - TempBoundingBox.Min.Y), entity.Velocity.Z); collisionDirection = Vector3.Down; } else if (entity.Velocity.Y > 0) { entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y - (TempBoundingBox.Max.Y - collisionPoint.Value), entity.Velocity.Z); collisionDirection = Vector3.Up; } return true; } return false; }
// Thanks to http://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game public static Tuple<Coordinates3D, BlockFace> Cast(ReadOnlyWorld world, Ray ray, IBlockRepository repository, double max) { var origin = ray.Position.Floor(); var direction = ray.Direction; var step = new Vector3(SigNum(ray.Direction.X), SigNum(ray.Direction.Y), SigNum(ray.Direction.Z)); var tMax = new Vector3( IntBound(origin.X, direction.X), IntBound(origin.Y, direction.Y), IntBound(origin.Z, direction.Z)); var tDelta = new Vector3( step.X / direction.X, step.Y / direction.Y, step.Z / direction.Z); BlockFace face = BlockFace.PositiveY; if (ray.Direction == Vector3.Zero) return null; max /= Math.Sqrt(ray.Direction.X * ray.Direction.X + ray.Direction.Y * ray.Direction.Y + ray.Direction.Z * ray.Direction.Z); while (world.IsValidPosition((Coordinates3D)origin)) { var provider = repository.GetBlockProvider(world.GetBlockID((Coordinates3D)origin)); var _box = provider.BoundingBox; if (_box != null) { var box = _box.Value.OffsetBy((Coordinates3D)origin); if (ray.Intersects(box) != null) return new Tuple<Coordinates3D, BlockFace>((Coordinates3D)origin, face); } if (tMax.X < tMax.Y) { if (tMax.X < tMax.Z) { if (tMax.X > max) return null; // Update which cube we are now in. origin.X += step.X; // Adjust tMaxX to the next X-oriented boundary crossing. tMax.X += tDelta.X; // Record the normal vector of the cube face we entered. if (step.X < 0) face = BlockFace.PositiveX; else face = BlockFace.NegativeX; } else { if (tMax.Z > max) return null; origin.Z += step.Z; tMax.Z += tDelta.Z; if (step.Z < 0) face = BlockFace.PositiveZ; else face = BlockFace.NegativeZ; } } else { if (tMax.Y < tMax.Z) { if (tMax.Y > max) return null; origin.Y += step.Y; tMax.Y += tDelta.Y; if (step.Y < 0) face = BlockFace.PositiveY; else face = BlockFace.NegativeY; } else { // Identical to the second case, repeated for simplicity in // the conditionals. if (tMax.Z > max) break; origin.Z += step.Z; tMax.Z += tDelta.Z; if (step.Z < 0) face = BlockFace.PositiveZ; else face = BlockFace.NegativeZ; } } } return null; }