internal void CheckWallCollisions(Vector3D moveVector, ref float halfSizeX, ref float halfSizeY, ref float[] data) { if (moveVector.Point2.GetDistanceSquare(_Box.CenterPoint) > CheckDistanceSquare) { return; } _CheckDelegate(moveVector, ref halfSizeX, ref halfSizeY, ref data); }
/// <summary> /// Places or removes a block. /// </summary> /// <param name="place">True to place block</param> /// <param name="x">X coordinate of the placed/removed block</param> /// <param name="y">Y coordinate of the placed/removed block</param> /// <param name="z">Z coordinate of the placed/removed block</param> /// <param name="version">Current segment version</param> /// <returns>True if block has been placed/removed</returns> public bool PlaceOrRemoveBlock(bool place, out int x, out int y, out int z, out uint version) { x = -1; y = -1; z = -1; version = 0; if (!_CanPlaceBlocks) { return false; } // check closest intersection Point3D eyeLocation = new Point3D(Location, 0.0f, _EyeHeightFromCenter, 0.0f); Vector3D line = new Vector3D(eyeLocation, NewDirection); Intersection closest = new Intersection(); List<Tuple<Box, int, int>> boxes = new List<Tuple<Box, int, int>>(); // first add current segment's boxes which have 0 shift foreach (Box box in _CurrentSegment.GetBoxesSynchronized()) { boxes.Add(Tuple.Create(box, 0, 0)); } // now add other boxes and determine possible shift foreach (Segment neighbour in _CurrentSegmentNeighbours) { int shiftX = 0; if (_CurrentSegment.X - neighbour.X > WorldHelper.HalfSizeX) { shiftX = WorldHelper.SizeX; } else if (neighbour.X - _CurrentSegment.X > WorldHelper.HalfSizeX) { shiftX = -WorldHelper.SizeX; } int shiftZ = 0; if (_CurrentSegment.Z - neighbour.Z > WorldHelper.HalfSizeZ) { shiftZ = WorldHelper.SizeZ; } else if (neighbour.Z - _CurrentSegment.Z > WorldHelper.HalfSizeZ) { shiftZ = -WorldHelper.SizeZ; } foreach (Box box in neighbour.GetBoxesSynchronized()) { boxes.Add(Tuple.Create(box, shiftX, shiftZ)); } } if (eyeLocation.Y * eyeLocation.Y < BlockInteractionRange) { boxes.Add(Tuple.Create(Box.FloorBox, 0, 0)); } int reverseShiftX = 0; int reverseShiftZ = 0; foreach (Tuple<Box, int, int> box in boxes) { Intersection intersection = box.Item1.GetIntersection(line, box.Item2, box.Item3); if (intersection.Distance < closest.Distance) { closest = intersection; reverseShiftX = -box.Item2; reverseShiftZ = -box.Item3; } } // check if there is a close intersection at all if (closest.IntersectionPoint == null || closest.Distance > BlockInteractionRange) { // not close enough or no point at all return false; } // adjust the reverse shift by accounting for reverse number truncating // when below 0 if (closest.IntersectionPoint.X < 0 && closest.IntersectionPoint.X % 1 != 0) { reverseShiftX--; } if (closest.IntersectionPoint.Z < 0 && closest.IntersectionPoint.Z % 1 != 0) { reverseShiftZ--; } // calculate cube location without the shift x = Convert.ToInt32(Math.Truncate(closest.IntersectionPoint.X)) + reverseShiftX; y = Convert.ToInt32(Math.Truncate(closest.IntersectionPoint.Y)); z = Convert.ToInt32(Math.Truncate(closest.IntersectionPoint.Z)) + reverseShiftZ; if (place) { // correct the location based on intersection side switch (closest.Side) { case Sides.FrontX: x--; break; case Sides.FrontY: y--; break; case Sides.FrontZ: z--; break; } } else { // correct the location based on intersection side switch (closest.Side) { case Sides.BackX: x--; break; case Sides.BackY: y--; break; case Sides.BackZ: z--; break; } } // check for floor/ceiling limits if (y < 0 || y > byte.MaxValue) { return false; } // adjust possible shift over the edge of the world after correction if (x < 0) { x += WorldHelper.SizeX; } else if (x >= WorldHelper.SizeX) { x -= WorldHelper.SizeX; } if (z < 0) { z += WorldHelper.SizeZ; } else if (z >= WorldHelper.SizeZ) { z -= WorldHelper.SizeZ; } // check if placing the block will clip into an entity if (place) { Box box = new Box(new Cube(x, y, z, 0), null); List<Entity> entities = WorldHelper.GetEntities(box.CenterPoint, BlockEntityCollisionSelectRange); bool collides = false; foreach (Entity entity in entities) { box.CheckSuffocation(entity.Location, ref entity._HalfSizeX, ref entity._HalfSizeY, ref collides); if (collides) { // at least one entity collides with the newly placed box return false; } } } // retrieve the destination segment and place or remove the block Segment destinationSegment = WorldHelper.GetSegment(x, y, z); version = destinationSegment.PlaceOrRemoveBlockAndUpdateVersion(place, x, y, z, GetSelectedMaterial()); return true; }
/// <summary> /// Handles the entity movement in the world. /// </summary> /// <returns>True if moved</returns> public bool HandleMovement() { bool moved = false; if (!_CanMove) { return moved; } // calculate movement _CurrentTime = Environment.TickCount; if (MoveX != 0 || MoveY != 0 || MoveZ != 0) { moved = true; if (_CurrentSegment == null) { CheckSegmentChanged(); } double distance = (_CurrentTime - _LastMoveTime) * Speed.EntityBaseSpeed; Point3D move = new Point3D(MoveX * distance, MoveY * distance, MoveZ * distance); move.Rotate(ref _RotationLeft, ref _ZeroUpDownRotation); Point3D original = new Point3D(Location); Location += move; // check collisions and adjust location if needed Vector3D moveVector = new Vector3D(original, Location); List<Box> boxesForCheck = new List<Box>(_CurrentSegment.GetBoxesSynchronized()); foreach (Segment neighbour in _CurrentSegmentNeighbours) { if (neighbour != null) { boxesForCheck.AddRange(neighbour.GetBoxesSynchronized()); } } // floor/ceiling collisions _Falling = true; foreach (Box box in boxesForCheck) { Location.Y += box.CheckFloorCeilingCollisions(moveVector, ref _HalfSizeX, ref _HalfSizeY, ref _Falling); } // world floor/ceiling check if (Location.Y - _HalfSizeY < 0.0f) { Location.Y = _HalfSizeY; } else if (Location.Y + _HalfSizeY > 255.0f) { Location.Y = 255.0f - _HalfSizeY; } // assemble collision walls List<CollisionWall> walls = new List<CollisionWall>(_CurrentSegment.GetCollisionWalls()); foreach (Segment neighbour in _CurrentSegmentNeighbours) { if (neighbour != null) { walls.AddRange(neighbour.GetCollisionWalls()); } } // walls.RemoveAll(w => w.GetBoxCenterPointDistanceSquare(Location) > 4.0f); // check wall collisions float[] xData = new float[] { 512.0f, 0.0f }; float[] zData = new float[] { 512.0f, 0.0f }; foreach (CollisionWall wall in walls) { if (wall.Side == Sides.FrontX || wall.Side == Sides.BackX) { wall.CheckWallCollisions(moveVector, ref _HalfSizeX, ref _HalfSizeY, ref xData); } if (wall.Side == Sides.FrontZ || wall.Side == Sides.BackZ) { wall.CheckWallCollisions(moveVector, ref _HalfSizeX, ref _HalfSizeY, ref zData); } } // apply shift based on wall collisons Location.X += xData[1]; Location.Z += zData[1]; // check suffocation _Suffocating = false; foreach (Box box in boxesForCheck) { box.CheckSuffocation(Location, ref _HalfSizeX, ref _HalfSizeY, ref _Suffocating); } // check move over map edge if (Location.X < 0) { Location.X += WorldHelper.SizeX; } else if (Location.X >= WorldHelper.SizeX) { Location.X -= WorldHelper.SizeX; } if (Location.Z < 0) { Location.Z += WorldHelper.SizeZ; } else if (Location.Z >= WorldHelper.SizeZ) { Location.Z -= WorldHelper.SizeZ; } // check if segment changed CheckSegmentChanged(); } // calculate new direction moved |= HandleRotation(); // update last moved time _LastMoveTime = _CurrentTime; return moved; }
/// <summary> /// Gets the line fragment intersection with a triangle (if any). /// </summary> /// <param name="triangle">Triangle defined by 3 points</param> /// <param name="line">Line fragment defined by 2 points</param> /// <returns>First item = 0 --> no intersetion; first item = 1 --> one /// intersection in the second item; first item = 2 --> line is on the /// plane</returns> public static Tuple<int, Point3D> GetTriangleIntersection(Tuple<Point3D, Point3D, Point3D> triangle, Vector3D line) { #if DIAG _IntersectCounter++; #endif Point3D vU1U2 = line.Point2 - line.Point1; Point3D vU1P1 = triangle.Item2 - line.Point1; Point3D norm = GetPlanesNormal(triangle); float lenNormU1U2 = norm.DotProduct(vU1U2); float lenNormU1P1 = norm.DotProduct(vU1P1); // normala dotProduct usecka => kdyz je to 0, jsou kolme, normala je kolma na plochu, tedy usecka a plocha jsou paralelni if (lenNormU1U2.EqEps(0)) { if (lenNormU1P1.EqEps(0)) { // usecka lezi v rovine definovane trojuhelnikem.... detekuj si prunik usecky strojuhelnikem sam :-) return Tuple.Create<int, Point3D>(2, null); } else { // usecka je paralelni s rovinou, takze nic se nikdy neprotne return Tuple.Create<int, Point3D>(0, null); } } // 0 zacatek, 1 konec usecky... uzavreny interval [0;1] == na usecce :-) float position = lenNormU1P1 / lenNormU1U2; if (0 <= position && position <= 1) { // ha!, usecka protina rovinu definovanou trojuhelnikem.... ted jen zjistit jestli ten bod je v tom trojuhelniku // měla by platit rovnost pro všechny 4 body roviny: // NBx = ZjistiNormaluPlochuDefinovanouTremiBodyVole(plocha) // NBx.Item1.DotProduct( [ plocha.Item1 ...až plocha.Item3 a bodPruniku] ) == NBx.Item3 // jestli ne, mám něco blbě, což by nebylo nic zvláštního :) Point3D intersection = line.Point1 + vU1U2 * position; if (IsPointInTriangle(intersection, triangle)) { return Tuple.Create(1, intersection); } return Tuple.Create<int, Point3D>(0, null); } // usecka NEprotina plochu, ale kdybys ji prodlouzil (kdyby to byla primka), tak by rovinu protnula v bode (usecka.Item1 + vU1U2 * pozice) return Tuple.Create<int, Point3D>(0, null); }
/// <summary> /// Gets the line fragment intersection with a rectangle (if any). /// </summary> /// <param name="rectangle">Rectangle defined by 4 points (either in /// clockwise or counterclockwise direction)</param> /// <param name="line">Line fragment defined by 2 points</param> /// <returns>First item = 0 --> no intersetion; first item = 1 --> one /// intersection in the second item; first item = 2 --> line is on the /// plane</returns> public static Tuple<int, Point3D> GetRectangleIntersection(Tuple<Point3D, Point3D, Point3D, Point3D> rectangle, Vector3D line) { var result = GetTriangleIntersection(Tuple.Create(rectangle.Item1, rectangle.Item2, rectangle.Item3), line); if (result.Item1 == 0) { result = GetTriangleIntersection(Tuple.Create(rectangle.Item1, rectangle.Item4, rectangle.Item3), line); } return result; }
public void GetRectangleIntersectionTest() { Point3D pl1 = new Point3D(-5, 0, -5); Point3D pl2 = new Point3D(5, 0, -5); Point3D pl3 = new Point3D(5, 0, 5); Point3D pl4 = new Point3D(-5, 0, 5); var pl = Tuple.Create(pl1, pl2, pl3, pl4); // one intersection at [-4; 0; 4] Point3D u1 = new Point3D(-4, 2, 4); Point3D u2 = new Point3D(-4, -2, 4); var u = new Vector3D(u1, u2); var result = Point3D.GetRectangleIntersection(pl, u); Assert.IsTrue(result.Item1 == 1); Assert.AreEqual<Point3D>(result.Item2, new Point3D(-4, 0, 4)); // one intersection at [-4; 0; 4] u1 = new Point3D(-5, 2, 5); u2 = new Point3D(-3, -2, 3); u = new Vector3D(u1, u2); result = Point3D.GetRectangleIntersection(pl, u); Assert.IsTrue(result.Item1 == 1); Assert.AreEqual<Point3D>(result.Item2, new Point3D(-4, 0, 4)); // one intersection at [0; 0; 0] u1 = new Point3D(0, 2, 0); u2 = new Point3D(0, -2, 0); u = new Vector3D(u1, u2); result = Point3D.GetRectangleIntersection(pl, u); Assert.IsTrue(result.Item1 == 1); Assert.AreEqual<Point3D>(result.Item2, new Point3D(0, 0, 0)); // no intersection u1 = new Point3D(-4, -1, 4); u2 = new Point3D(-4, -2, 4); u = new Vector3D(u1, u2); result = Point3D.GetRectangleIntersection(pl, u); Assert.IsTrue(result.Item1 == 0); Assert.IsNull(result.Item2); // no intersection u1 = new Point3D(-5.5, 2, 5); u2 = new Point3D(-5, -2, 5); u = new Vector3D(u1, u2); result = Point3D.GetRectangleIntersection(pl, u); Assert.IsTrue(result.Item1 == 0); Assert.IsNull(result.Item2); // line is in the plane u1 = new Point3D(4, 0, 4); u2 = new Point3D(3, 0, 3); u = new Vector3D(u1, u2); result = Point3D.GetRectangleIntersection(pl, u); Assert.IsTrue(result.Item1 == 2); Assert.IsNull(result.Item2); }
private void CheckFrontZCollisions(Vector3D moveVector, ref float halfSizeX, ref float halfSizeY, ref float[] data) { float x1, x2, y1, y2, z1, z2; GetPoints(halfSizeX, halfSizeY, out x1, out x2, out y1, out y2, out z1, out z2); if (moveVector.Point1.Z > z1) { return; } if (_Box.CenterPoint.X - moveVector.Point1.X > WorldHelper.HalfSizeX) { x1 -= WorldHelper.SizeX; x2 -= WorldHelper.SizeX; } else if (moveVector.Point1.X - _Box.CenterPoint.X > WorldHelper.HalfSizeX) { x1 += WorldHelper.SizeX; x2 += WorldHelper.SizeX; } Tuple<int, Point3D> result = Point3D.GetRectangleIntersection(Tuple.Create( new Point3D(x1, y1, z1), new Point3D(x1, y2, z1), new Point3D(x2, y2, z1), new Point3D(x2, y1, z1)), moveVector); if (result.Item1 == 1) { float dist = result.Item2.GetDistanceSquare(moveVector.Point1); if (dist < data[0]) { float diff = moveVector.Point2.Z - result.Item2.Z; if (diff > ErrorTolerances.FloatEpsilon) { data[0] = dist; data[1] = -diff; } } } }