in Assets/MRTK/SDK/Features/Utilities/Solvers/SurfaceMagnetism.cs [591:781]
private void FindPlacementPlane(Vector3 origin, Vector3 direction, Vector3[] positions, Vector3[] normals, bool[] hits, float assetWidth, float maxNormalVariance, bool constrainVertical, bool useClosestDistance, out Plane plane, out float closestDistance)
{
int rayCount = positions.Length;
Vector3 originalDirection = direction;
if (constrainVertical)
{
direction.y = 0.0f;
direction = direction.normalized;
}
// Go through all the points and find the closest distance
closestDistance = float.PositiveInfinity;
int numHits = 0;
int closestPointIdx = -1;
float farthestDistance = 0f;
var averageNormal = Vector3.zero;
for (int hitIndex = 0; hitIndex < rayCount; hitIndex++)
{
if (hits[hitIndex])
{
float distance = Vector3.Dot(direction, positions[hitIndex] - origin);
if (distance < closestDistance)
{
closestPointIdx = hitIndex;
closestDistance = distance;
}
if (distance > farthestDistance)
{
farthestDistance = distance;
}
averageNormal += normals[hitIndex];
++numHits;
}
}
Vector3 closestPoint = positions[closestPointIdx];
averageNormal /= numHits;
// Calculate variance of all normals
float variance = 0;
for (int hitIndex = 0; hitIndex < rayCount; ++hitIndex)
{
if (hits[hitIndex])
{
variance += (normals[hitIndex] - averageNormal).magnitude;
}
}
variance /= numHits;
// If variance is too high, I really don't want to deal with this surface
// And if we don't even have enough rays, I'm not confident about this at all
if (variance > maxNormalVariance || numHits < rayCount * 0.25f)
{
plane = new Plane(-direction, closestPoint);
return;
}
// go through all the points and find the most orthogonal plane
var lowAngle = float.PositiveInfinity;
var highAngle = float.NegativeInfinity;
int lowIndex = -1;
int highIndex = -1;
for (int hitIndex = 0; hitIndex < rayCount; hitIndex++)
{
if (hits[hitIndex] == false || hitIndex == closestPointIdx)
{
continue;
}
Vector3 difference = positions[hitIndex] - closestPoint;
if (constrainVertical)
{
difference.y = 0.0f;
difference.Normalize();
if (difference == Vector3.zero)
{
continue;
}
}
difference.Normalize();
float angle = Vector3.Dot(direction, difference);
if (angle < lowAngle)
{
lowAngle = angle;
lowIndex = hitIndex;
}
}
if (!constrainVertical && lowIndex != -1)
{
for (int hitIndex = 0; hitIndex < rayCount; hitIndex++)
{
if (hits[hitIndex] == false || hitIndex == closestPointIdx || hitIndex == lowIndex)
{
continue;
}
float dot = Mathf.Abs(Vector3.Dot((positions[hitIndex] - closestPoint).normalized, (positions[lowIndex] - closestPoint).normalized));
if (dot > MaxDot)
{
continue;
}
float nextAngle = Mathf.Abs(Vector3.Dot(direction, Vector3.Cross(positions[lowIndex] - closestPoint, positions[hitIndex] - closestPoint).normalized));
if (nextAngle > highAngle)
{
highAngle = nextAngle;
highIndex = hitIndex;
}
}
}
Vector3 placementNormal;
if (lowIndex != -1)
{
if (debugEnabled)
{
Debug.DrawLine(closestPoint, positions[lowIndex], Color.red);
}
if (highIndex != -1)
{
if (debugEnabled)
{
Debug.DrawLine(closestPoint, positions[highIndex], Color.green);
}
placementNormal = Vector3.Cross(positions[lowIndex] - closestPoint, positions[highIndex] - closestPoint).normalized;
}
else
{
Vector3 planeUp = Vector3.Cross(positions[lowIndex] - closestPoint, direction);
placementNormal = Vector3.Cross(positions[lowIndex] - closestPoint, constrainVertical ? Vector3.up : planeUp).normalized;
}
if (debugEnabled)
{
Debug.DrawLine(closestPoint, closestPoint + placementNormal, Color.blue);
}
}
else
{
placementNormal = direction * -1.0f;
}
if (Vector3.Dot(placementNormal, direction) > 0.0f)
{
placementNormal *= -1.0f;
}
plane = new Plane(placementNormal, closestPoint);
if (debugEnabled)
{
Debug.DrawRay(closestPoint, placementNormal, Color.cyan);
}
// Figure out how far the plane should be.
if (!useClosestDistance && closestPointIdx >= 0)
{
float centerPlaneDistance;
if (plane.Raycast(new Ray(origin, originalDirection), out centerPlaneDistance) || !centerPlaneDistance.Equals(0.0f))
{
// When the plane is nearly parallel to the user, we need to clamp the distance to where the raycasts hit.
closestDistance = Mathf.Clamp(centerPlaneDistance, closestDistance, farthestDistance + assetWidth * 0.5f);
}
else
{
Debug.LogError("FindPlacementPlane: Not expected to have the center point not intersect the plane.");
}
}
}