public bool TestTerrainCollisionCylinder(IAABBEntity entity, out Vector3 collisionPoint) { collisionPoint = Vector3.Zero; var testBox = GetAABBVelocityBox(entity); var testCylinder = new BoundingCylinder(testBox.Min, testBox.Max, entity.BoundingBox.Min.DistanceTo(entity.BoundingBox.Max)); var collision = false; for (var x = (int)Math.Floor(testBox.Min.X); x <= (int)Math.Ceiling(testBox.Max.X); x++) { for (var z = (int)Math.Floor(testBox.Min.Z); z <= (int)Math.Ceiling(testBox.Max.Z); z++) { for (var y = (int)Math.Floor(testBox.Min.Y); y <= (int)Math.Ceiling(testBox.Max.Y); y++) { var coords = new Coordinates3D(x, y, z); if (!World.IsValidPosition(coords)) { continue; } var _box = BlockPhysicsProvider.GetBoundingBox(World, coords); if (_box == null) { continue; } var box = _box.Value.OffsetBy(coords.AsVector3()); if (testCylinder.Intersects(box)) { if (testBox.Intersects(box)) { collision = true; AdjustVelocityForCollision(entity, box); testBox = GetAABBVelocityBox(entity); testCylinder = new BoundingCylinder(testBox.Min, testBox.Max, entity.BoundingBox.Min.DistanceTo(entity.BoundingBox.Max)); collisionPoint = coords.AsVector3(); } } } } } return(collision); }
/// <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); }
public bool TestTerrainCollisionZ(IAABBEntity entity, out Vector3 collisionPoint) { // Things we need to do: // 1 - expand bounding box to include the destination and everything within // 2 - collect all blocks within that area // 3 - test bounding boxes in direction of motion collisionPoint = Vector3.Zero; if (entity.Velocity.Z == 0) { return(false); } bool negative; BoundingBox testBox; if (entity.Velocity.Z < 0) { testBox = new BoundingBox( new Vector3( entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z + entity.Velocity.Z), entity.BoundingBox.Max); negative = true; } else { testBox = new BoundingBox( entity.BoundingBox.Min, new Vector3( entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z + entity.Velocity.Z)); negative = false; } double?collisionExtent = null; for (var x = (int)Math.Floor(testBox.Min.X); x <= (int)Math.Ceiling(testBox.Max.X); x++) { for (var z = (int)Math.Floor(testBox.Min.Z); z <= (int)Math.Ceiling(testBox.Max.Z); z++) { for (var y = (int)Math.Floor(testBox.Min.Y); y <= (int)Math.Ceiling(testBox.Max.Y); y++) { var coords = new Coordinates3D(x, y, z); if (!World.IsValidPosition(coords)) { continue; } var _box = BlockPhysicsProvider.GetBoundingBox(World, coords); if (_box == null) { continue; } var box = _box.Value.OffsetBy(coords.AsVector3()); if (testBox.Intersects(box)) { if (negative) { if (collisionExtent == null || collisionExtent.Value < box.Max.Z) { collisionExtent = box.Max.Z; collisionPoint = coords.AsVector3(); } } else { if (collisionExtent == null || collisionExtent.Value > box.Min.Z) { collisionExtent = box.Min.Z; collisionPoint = coords.AsVector3(); } } } } } } if (collisionExtent != null) // Collision detected, adjust accordingly { var extent = collisionExtent.Value; double diff; if (negative) { diff = -(entity.BoundingBox.Min.Z - extent); } else { diff = extent - entity.BoundingBox.Max.Z; } entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y, (float)diff); return(true); } return(false); }