/// <summary> /// /// </summary> /// <param name="ray"></param> /// <param name="distanceLimit"></param> /// <returns></returns> public RayResult RayIntersects(Ray ray, float distanceLimit) { long curr_x = 0, curr_z = 0; ConvertWorldPositionToTerrainSlot(ray.Origin, out curr_x, out curr_z); TerrainSlot slot = GetTerrainSlot(curr_x, curr_z); RayResult result = new RayResult(false, null, Vector3.Zero); Vector3 tmp, localRayDir, centreOrigin, offset; tmp = localRayDir = centreOrigin = offset = Vector3.Zero; ConvertTerrainSlotToWorldPosition(curr_x, curr_z, out centreOrigin); offset = ray.Origin - centreOrigin; localRayDir = ray.Direction; switch (Alignment) { case Alignment.Align_X_Y: float t1 = localRayDir.x, t2 = localRayDir.z; Swap(t1, t2); localRayDir = new Vector3(t1, localRayDir.y, t2); t1 = offset.x; t2 = offset.z; Swap(t1, t2); localRayDir = new Vector3(t1, offset.y, t2); break; case Alignment.Align_Y_Z: // x = z, z = y, y = -x tmp.x = localRayDir.z; tmp.z = localRayDir.y; tmp.y = -localRayDir.x; localRayDir = tmp; tmp.x = offset.z; tmp.z = offset.y; tmp.y = -offset.x; offset = tmp; break; case Alignment.Align_X_Z: // already in X/Z but values increase in -Z localRayDir.z = -localRayDir.z; offset.z = -offset.z; break; } offset /= _terrainWorldSize; offset = new Vector3(offset.x +0.5f,offset.y + 0.5f, offset.z +0.5f); Vector3 inc = new Vector3(Math.Utility.Abs(localRayDir.x), Math.Utility.Abs(localRayDir.y), Math.Utility.Abs(localRayDir.z)); long xdir = localRayDir.x > 0.0f ? 1 : -1; long zdir = localRayDir.z > 0.0f ? 1 : -1; // We're always counting from 0 to 1 regardless of what direction we're heading if (xdir < 0) offset.x = 1.0f - offset.x; if (zdir < 0) offset.z = 1.0f - offset.z; // find next slot bool keepSearching = true; int numGaps = 0; while (keepSearching) { if (Math.Utility.RealEqual(inc.x, 0.0f) && Math.Utility.RealEqual(inc.z, 0.0f)) keepSearching = false; while ((slot != null || slot.Instance != null) && keepSearching) { ++numGaps; /// if we don't find any filled slot in 6 traversals, give up if (numGaps > 6) { keepSearching = false; break; } // find next slot Vector3 oldoffset = offset; while (offset.x < 1.0f && offset.z < 1.0f) offset += inc; if (offset.x >= 1.0f && offset.z >= 1.0f) { // We crossed a corner, need to figure out which we passed first Real diffz = 1.0f - oldoffset.z; Real diffx = 1.0f - oldoffset.x; Real distz = diffz / inc.z; Real distx = diffx / inc.x; if (distx < distz) { curr_x += xdir; offset.x -= 1.0f; } else { curr_z += zdir; offset.z -= 1.0f; } } else if (offset.x >= 1.0f) { curr_x += xdir; offset.x -= 1.0f; } else if (offset.z >= 1.0f) { curr_z += zdir; offset.z -= 1.0f; } if (distanceLimit > 0) { Vector3 worldPos; ConvertTerrainSlotToWorldPosition(curr_x, curr_z, out worldPos); if (ray.Origin.Distance(worldPos) > distanceLimit) { keepSearching = false; break; } } slot = GetTerrainSlot(curr_x, curr_z); } if (slot != null && slot.Instance != null) { numGaps = 0; // don't cascade into neighbours KeyValuePair<bool, Vector3> raypair = slot.Instance.RayIntersects(ray, false, distanceLimit); if (raypair.Key) { keepSearching = false; result.Hit = true; result.Terrain = slot.Instance; result.Position = raypair.Value; break; } else { // not this one, trigger search for another slot slot = null; } } } return result; }