public IEnumerable <CellCastInfo> CastCells(Vector3 origin, Vector3 dir, float maxDistance) { int currentX, currentY, currentZ; Vector3 intersectPoint, faceNormal; T startCell, endCell, currentCell; var ray = new Ray(origin, dir); GetCellIndices(origin, out currentX, out currentY, out currentZ); intersectPoint = Vector3.zero; float distance = 0f; Vector3 normal; if (IsValidCell(currentX, currentY, currentZ)) // Started inside a cell { intersectPoint = origin; startCell = cells[currentX, currentY, currentZ]; } else { if (bounds.Intersects(ray, maxDistance, out normal, out distance) == false) { //Console.Log("Does not intersect grid {0}", this); yield break; } distance = Mathf.Abs(distance); intersectPoint = origin + (dir * distance); origin = intersectPoint + (dir * 0.001f); GetCellIndices(origin, out currentX, out currentY, out currentZ); if (!IsValidCell(currentX, currentY, currentZ)) { yield break; } startCell = cells[currentX, currentY, currentZ]; } var reverseRay = new Ray(ray.origin + (dir * maxDistance), -dir); if (bounds.Intersects(ray, maxDistance, out normal, out distance) == false) { //Console.Log("Does not intersect grid reverse {0}", this); yield break; } //Console.Log("R: {0}, d: {1}", ray, distance); distance = Mathf.Abs(distance); Vector3 endPoint = reverseRay.origin + (reverseRay.direction * (distance + 0.001f)); endCell = GetCell(endPoint); faceNormal = GetCellFaceNormal(currentX, currentY, currentZ, intersectPoint); var castInfo = new CellCastInfo(currentX, currentY, currentZ, startCell, intersectPoint, faceNormal); yield return(castInfo); Vector3 current = (origin - Position) / CellSize; currentX = (int)current.x; currentY = (int)current.y; currentZ = (int)current.z; GetCellIndices(origin, out currentX, out currentY, out currentZ); int stepX, stepY, stepZ; stepX = dir.x > 0 ? 1 : (dir.x < 0 ? -1 : 0); stepY = dir.y > 0 ? 1 : (dir.y < 0 ? -1 : 0); stepZ = dir.z > 0 ? 1 : (dir.z < 0 ? -1 : 0); Vector3 rayDelta = dir * (maxDistance); Vector3 tDelta = Vector3.zero; // size of voxel in terms of t if (stepX != 0) { tDelta.x = CellSize / rayDelta.x; } else { tDelta.x = 10000000.0f; } if (stepY != 0) { tDelta.y = CellSize / rayDelta.y; } else { tDelta.y = 10000000.0f; } if (stepZ != 0) { tDelta.z = CellSize / rayDelta.z; } else { tDelta.z = 10000000.0f; } Vector3 tMax = Vector3.zero; // distance to next boundary (in terms of t) if (stepX > 0) { tMax.x = tDelta.x * MathsHelper.FracPos(current.x); } else { tMax.x = tDelta.x * MathsHelper.FracNeg(current.x); } if (stepY > 0) { tMax.y = tDelta.y * MathsHelper.FracPos(current.y); } else { tMax.y = tDelta.y * MathsHelper.FracNeg(current.y); } if (stepZ > 0) { tMax.z = tDelta.z * MathsHelper.FracPos(current.z); } else { tMax.z = tDelta.z * MathsHelper.FracNeg(current.z); } currentCell = startCell; int iters = 0; const int MaxIters = 10000; Directions movementDirection; while (IsValidCell(currentX, currentY, currentZ) && iters < MaxIters) { ++iters; #if DEBUG if (iters == MaxIters) { Console.LogWarning("Grid cell cast hit max iters"); } #endif if (Mathf.Abs(tMax.x) < Mathf.Abs(tMax.y)) { if (Mathf.Abs(tMax.x) < Mathf.Abs(tMax.z)) { if (stepX > 0) { movementDirection = Directions.Left; } else { movementDirection = Directions.Right; } currentX += stepX; tMax.x += tDelta.x; } else { if (stepZ > 0) { movementDirection = Directions.Forward; } else { movementDirection = Directions.Backward; } currentZ += stepZ; tMax.z += tDelta.z; } } else { if (Mathf.Abs(tMax.y) < Mathf.Abs(tMax.z)) { if (stepY > 0) { movementDirection = Directions.Down; } else { movementDirection = Directions.Up; } currentY += stepY; tMax.y += tDelta.y; } else { if (stepZ > 0) { movementDirection = Directions.Forward; } else { movementDirection = Directions.Backward; } currentZ += stepZ; tMax.z += tDelta.z; } } if (!IsValidCell(currentX, currentY, currentZ)) { break; } currentCell = cells[currentX, currentY, currentZ]; intersectPoint = tMax; faceNormal = GetDirectionVector(movementDirection); Bounds cellBounds = GetCellBounds(currentX, currentY, currentZ); if (cellBounds.IntersectRay(ray, out distance)) { intersectPoint = ray.origin + (ray.direction * distance); } if (distance > maxDistance) { //Console.Log("Over max distance {0}", this); yield break; } castInfo = new CellCastInfo(currentX, currentY, currentZ, currentCell, intersectPoint, faceNormal); yield return(castInfo); } }