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;
}
/// <summary>