/// <summary> /// casts a line through the spatial hash and fills the hits array up with any colliders that the line hits /// </summary> /// <returns>the number of Colliders returned</returns> /// <param name="start">Start.</param> /// <param name="end">End.</param> /// <param name="hits">Hits.</param> /// <param name="layerMask">Layer mask.</param> public int linecast(Vector2 start, Vector2 end, RaycastHit[] hits, int layerMask) { var ray = new Ray2D(start, end); _raycastParser.start(ref ray, hits, layerMask); // get our start/end position in the same space as our grid start.X *= _inverseCellSize; start.Y *= _inverseCellSize; var endCell = cellCoords(end.X, end.Y); // TODO: check gridBounds to ensure the ray starts/ends in the grid. watch out for end cells since they report out of bounds due to int comparison // what voxel are we on var intX = Mathf.fastFloorToInt(start.X); var intY = Mathf.fastFloorToInt(start.Y); // which way we go var stepX = Math.Sign(ray.direction.X); var stepY = Math.Sign(ray.direction.Y); // Calculate cell boundaries. when the step is positive, the next cell is after this one meaning we add 1. // If negative, cell is before this one in which case dont add to boundary var boundaryX = intX + (stepX > 0 ? 1 : 0); var boundaryY = intY + (stepY > 0 ? 1 : 0); // determine the value of t at which the ray crosses the first vertical voxel boundary. same for y/horizontal. // The minimum of these two values will indicate how much we can travel along the ray and still remain in the current voxel // may be infinite for near vertical/horizontal rays var tMaxX = (boundaryX - start.X) / ray.direction.X; var tMaxY = (boundaryY - start.Y) / ray.direction.Y; if (ray.direction.X == 0f) { tMaxX = float.PositiveInfinity; } if (ray.direction.Y == 0f) { tMaxY = float.PositiveInfinity; } // how far do we have to walk before crossing a cell from a cell boundary. may be infinite for near vertical/horizontal rays var tDeltaX = stepX / ray.direction.X; var tDeltaY = stepY / ray.direction.Y; // start walking and returning the intersecting cells. var cell = cellAtPosition(intX, intY); //debugDrawCellDetails( intX, intY, cell != null ? cell.Count : 0 ); if (cell != null && _raycastParser.checkRayIntersection(intX, intY, cell)) { Array.Sort(hits, RaycastResultParser.compareRaycastHits); _raycastParser.reset(); return(_raycastParser.hitCounter); } while (intX != endCell.X || intY != endCell.Y) { if (tMaxX < tMaxY) { intX += stepX; tMaxX += tDeltaX; } else { intY += stepY; tMaxY += tDeltaY; } cell = cellAtPosition(intX, intY); if (cell != null && _raycastParser.checkRayIntersection(intX, intY, cell)) { Array.Sort(hits, RaycastResultParser.compareRaycastHits); _raycastParser.reset(); return(_raycastParser.hitCounter); } } // make sure we are reset Array.Sort(hits, RaycastResultParser.compareRaycastHits); _raycastParser.reset(); return(_raycastParser.hitCounter); }
/// <summary> /// casts a line through the spatial hash and fills the hits array up with any colliders that the line hits. /// https://github.com/francisengelmann/fast_voxel_traversal/blob/master/main.cpp /// http://www.cse.yorku.ca/~amana/research/grid.pdf /// </summary> /// <returns>the number of Colliders returned</returns> /// <param name="start">Start.</param> /// <param name="end">End.</param> /// <param name="hits">Hits.</param> /// <param name="layerMask">Layer mask.</param> public int linecast(Vector2 start, Vector2 end, RaycastHit[] hits, int layerMask) { var ray = new Ray2D(start, end); _raycastParser.start(ref ray, hits, layerMask); // get our start/end position in the same space as our grid var currentCell = cellCoords(start.X, start.Y); var lastCell = cellCoords(end.X, end.Y); // what direction are we incrementing the cell checks? var stepX = Math.Sign(ray.direction.X); var stepY = Math.Sign(ray.direction.Y); // we make sure that if we're on the same line or row we don't step in the unneeded direction if (currentCell.X == lastCell.X) { stepX = 0; } if (currentCell.Y == lastCell.Y) { stepY = 0; } // Calculate cell boundaries. when the step is positive, the next cell is after this one meaning we add 1. // If negative, cell is before this one in which case dont add to boundary var xStep = stepX < 0 ? 0f : (float)stepX; var yStep = stepY < 0 ? 0f : (float)stepY; var nextBoundaryX = ((float)currentCell.X + xStep) * _cellSize; var nextBoundaryY = ((float)currentCell.Y + yStep) * _cellSize; // determine the value of t at which the ray crosses the first vertical voxel boundary. same for y/horizontal. // The minimum of these two values will indicate how much we can travel along the ray and still remain in the current voxel // may be infinite for near vertical/horizontal rays var tMaxX = ray.direction.X != 0 ? (nextBoundaryX - ray.start.X) / ray.direction.X : float.MaxValue; var tMaxY = ray.direction.Y != 0 ? (nextBoundaryY - ray.start.Y) / ray.direction.Y : float.MaxValue; // how far do we have to walk before crossing a cell from a cell boundary. may be infinite for near vertical/horizontal rays var tDeltaX = ray.direction.X != 0 ? _cellSize / (ray.direction.X * stepX) : float.MaxValue; var tDeltaY = ray.direction.Y != 0 ? _cellSize / (ray.direction.Y * stepY) : float.MaxValue; // start walking and returning the intersecting cells. var cell = cellAtPosition(currentCell.X, currentCell.Y); // Debug.log( $"cell: {currentCell.X}, {currentCell.Y}" ); // debugDrawCellDetails( intX, intY, cell != null ? cell.Count : 10 ); if (cell != null && _raycastParser.checkRayIntersection(currentCell.X, currentCell.Y, cell)) { _raycastParser.reset(); return(_raycastParser.hitCounter); } while (currentCell.X != lastCell.X || currentCell.Y != lastCell.Y) { if (tMaxX < tMaxY) { // HACK: ensures we never overshoot our values currentCell.X = (int)Mathf.approach(currentCell.X, lastCell.X, Math.Abs(stepX)); // currentCell.X += stepX; tMaxX += tDeltaX; } else { currentCell.Y = (int)Mathf.approach(currentCell.Y, lastCell.Y, Math.Abs(stepY)); // currentCell.Y += stepY; tMaxY += tDeltaY; } // Debug.log( $"cell: {currentCell.X}, {currentCell.Y}" ); cell = cellAtPosition(currentCell.X, currentCell.Y); if (cell != null && _raycastParser.checkRayIntersection(currentCell.X, currentCell.Y, cell)) { _raycastParser.reset(); return(_raycastParser.hitCounter); } } // make sure we are reset _raycastParser.reset(); return(_raycastParser.hitCounter); }