36 #region public members 37 [Tooltip(
"LayerMask to apply Surface Magnetism to")]
38 public LayerMask MagneticSurface = 0;
40 [Tooltip(
"Max distance to check for surfaces")]
41 public float MaxDistance = 3.0f;
42 [Tooltip(
"Closest distance to bring object")]
43 public float CloseDistance = 0.5f;
45 [Tooltip(
"Offset from surface along surface normal")]
46 public float SurfaceNormalOffset = 0.5f;
47 [Tooltip(
"Offset from surface along ray cast direction")]
48 public float SurfaceRayOffset = 0;
50 [Tooltip(
"Surface raycast mode. Simple = single raycast, Complex = bbox corners")]
53 [Tooltip(
"Number of rays per edge, should be odd. Total casts is n^2")]
54 public int BoxRaysPerEdge = 3;
56 [Tooltip(
"If true, use orthographic casting for box lines instead of perspective")]
57 public bool OrthoBoxCast =
false;
59 [Tooltip(
"Align to ray cast direction if box cast hits many normals facing in varying directions")]
60 public float MaximumNormalVariance = 0.5f;
62 [Tooltip(
"Radius to use for sphere cast")]
63 public float SphereSize = 1.0f;
65 [Tooltip(
"When doing volume casts, use size override if non-zero instead of object's current scale")]
66 public float VolumeCastSizeOverride = 0;
68 [Tooltip(
"When doing volume casts, use linked AltScale instead of object's current scale")]
69 public bool UseLinkedAltScaleOverride =
false;
72 [Tooltip(
"Instead of using mesh normal, extract normal from tex coord (SR is reported to put smoothed normals in there)")]
73 bool UseTexCoordNormals =
false;
75 [Tooltip(
"Raycast direction. Can cast from head in facing dir, or cast from head to object position")]
78 [Tooltip(
"Orientation mode. None = no orienting, Vertical = Face head, but always oriented up/down, Full = Aligned to surface normal completely")]
81 [Tooltip(
"Orientation Blend Value 0.0 = All head 1.0 = All surface")]
82 public float OrientBlend = 0.65f;
88 #region private members 89 private BoxCollider m_BoxCollider;
90 private const float maxDot = 0.97f;
97 m_BoxCollider = GetComponent<BoxCollider>();
98 if (m_BoxCollider == null)
100 Debug.LogError(
"Box raycast mode requires a BoxCollider, but none was found! Defaulting to Simple raycast mode");
104 if (Application.isEditor)
110 if (Application.isEditor && UseTexCoordNormals)
112 Debug.LogWarning(
"Disabling tex coord normals while in editor mode");
113 UseTexCoordNormals =
false;
125 private static bool DefaultRaycast(Vector3 origin, Vector3 direction,
float distance, LayerMask surface, out
RaycastResultHelper result)
130 private static bool DefaultSpherecast(Vector3 origin, Vector3 direction,
float radius,
float distance, LayerMask surface, out
RaycastResultHelper result)
139 Vector3 GetRaycastOrigin()
141 if (solverHandler.TransformTarget == null)
145 return solverHandler.TransformTarget.position;
154 Vector3 GetRaycastEndPoint()
156 Vector3 ret = Vector3.forward;
157 switch (raycastDirection)
160 ret = solverHandler.TransformTarget.position + solverHandler.TransformTarget.forward;
164 ret = transform.position;
168 ret = solverHandler.GoalPosition;
178 Vector3 GetRaycastDirection()
180 Vector3 ret = Vector3.forward;
184 if (solverHandler.TransformTarget)
186 ret = solverHandler.TransformTarget.forward;
191 ret = (GetRaycastEndPoint() - GetRaycastOrigin()).normalized;
204 Quaternion CalculateMagnetismOrientation(Vector3 rayDir, Vector3 surfaceNormal)
207 Vector3 newDir = -surfaceNormal;
208 if (IsNormalVertical(newDir))
215 Quaternion surfaceRot = Quaternion.LookRotation(newDir, Vector3.up);
217 switch (orientationMode)
220 return solverHandler.GoalRotation;
226 return Quaternion.LookRotation(-surfaceNormal, Vector3.up);
229 return Quaternion.Slerp(solverHandler.GoalRotation, surfaceRot, OrientBlend);
231 return Quaternion.identity;
240 bool IsNormalVertical(Vector3 normal)
242 return 1f - Mathf.Abs(normal.y) < 0.01f;
249 float GetScaleOverride()
251 if (UseLinkedAltScaleOverride)
253 return solverHandler.AltScale.Current.magnitude;
255 return VolumeCastSizeOverride;
261 this.GoalPosition = WorkingPos;
262 this.GoalRotation = WorkingRot;
265 Ray ray =
new Ray(GetRaycastOrigin(), GetRaycastDirection());
268 if (ray.direction == Vector3.zero)
273 float ScaleOverride = GetScaleOverride();
285 bHit = DefaultRaycast(ray.origin, ray.direction, MaxDistance, MagneticSurface, out result);
289 if (UseTexCoordNormals)
291 result.OverrideNormalFromTextureCoord();
295 hitDelta = result.Point - ray.origin;
296 len = hitDelta.magnitude;
297 if (len < CloseDistance)
299 result.OverridePoint(ray.origin + ray.direction * CloseDistance);
305 GoalPosition = result.Point + SurfaceNormalOffset * result.Normal + SurfaceRayOffset * ray.direction;
306 GoalRotation = CalculateMagnetismOrientation(ray.direction, result.Normal);
312 Vector3 scale = transform.lossyScale;
313 if (ScaleOverride > 0)
315 scale = scale.normalized * ScaleOverride;
318 Quaternion orientation = orientationMode ==
OrientModeEnum.None ? Quaternion.LookRotation(ray.direction, Vector3.up) : CalculateMagnetismOrientation(ray.direction, Vector3.up);
319 Matrix4x4 targetMatrix = Matrix4x4.TRS(Vector3.zero, orientation, scale);
321 if (m_BoxCollider == null)
323 m_BoxCollider = this.GetComponent<BoxCollider>();
326 Vector3 extents = m_BoxCollider.size;
332 if (
RaycastHelper.
CastBoxExtents(extents, transform.position, targetMatrix, ray, MaxDistance, MagneticSurface, DefaultRaycast, BoxRaysPerEdge, OrthoBoxCast, out positions, out normals, out hits))
338 FindPlacementPlane(ray.origin, ray.direction, positions, normals, hits, m_BoxCollider.size.x, MaximumNormalVariance,
false, orientationMode ==
OrientModeEnum.None, out plane, out distance);
341 float verticalCorrectionOffset = 0;
342 if (IsNormalVertical(plane.normal) && !Mathf.Approximately(ray.direction.y, 0))
344 float boxSurfaceOffsetVert = targetMatrix.MultiplyVector(
new Vector3(0, extents.y / 2f, 0)).magnitude;
345 Vector3 correctionVec = boxSurfaceOffsetVert * (ray.direction / ray.direction.y);
346 verticalCorrectionOffset = -correctionVec.magnitude;
349 float boxSurfaceOffset = targetMatrix.MultiplyVector(
new Vector3(0, 0, extents.z / 2f)).magnitude;
352 GoalPosition = ray.origin + ray.direction * Mathf.Max(CloseDistance, distance + SurfaceRayOffset + boxSurfaceOffset + verticalCorrectionOffset) + plane.normal * (0 * boxSurfaceOffset + SurfaceNormalOffset);
353 GoalRotation = CalculateMagnetismOrientation(ray.direction, plane.normal);
365 float size = ScaleOverride > 0 ? ScaleOverride : transform.lossyScale.x * SphereSize;
366 bHit = DefaultSpherecast(ray.origin, ray.direction, size, MaxDistance, MagneticSurface, out result);
370 hitDelta = result.Point - ray.origin;
371 len = hitDelta.magnitude;
372 if (len < CloseDistance)
374 result.OverridePoint(ray.origin + ray.direction * CloseDistance);
380 GoalPosition = result.Point + SurfaceNormalOffset * result.Normal + SurfaceRayOffset * ray.direction;
381 GoalRotation = CalculateMagnetismOrientation(ray.direction, result.Normal);
387 UpdateWorkingPosToGoal();
388 UpdateWorkingRotToGoal();
405 private static void FindPlacementPlane(Vector3 origin, Vector3 direction, Vector3[] positions, Vector3[] normals,
bool[] hits,
float assetWidth,
float maxNormalVariance,
bool constrainVertical,
bool bUseClosestDistance, out Plane plane, out
float closestDistance)
409 int numRays = positions.Length;
411 Vector3 originalDirection = direction;
412 if (constrainVertical)
415 direction = direction.normalized;
419 int closestPoint = -1;
420 closestDistance =
float.PositiveInfinity;
421 float farthestDistance = 0f;
423 Vector3 averageNormal = Vector3.zero;
425 for (
int i = 0; i < numRays; i++)
427 if (hits[i] !=
false)
429 float dist = Vector3.Dot(direction, positions[i] - origin);
431 if (dist < closestDistance)
434 closestDistance = dist;
436 if (dist > farthestDistance)
438 farthestDistance = dist;
441 averageNormal += normals[i];
445 averageNormal /= numHits;
449 for (
int i = 0; i < numRays; ++i)
451 if (hits[i] !=
false)
453 variance += (normals[i] - averageNormal).magnitude;
460 if (variance > maxNormalVariance || numHits < numRays / 4)
462 plane =
new Plane(-direction, positions[closestPoint]);
467 float lowAngle =
float.PositiveInfinity;
469 float highAngle =
float.NegativeInfinity;
472 for (
int i = 0; i < numRays; i++)
474 if (hits[i] ==
false || i == closestPoint)
479 Vector3 diff = (positions[i] - positions[closestPoint]);
480 if (constrainVertical)
485 if (diff == Vector3.zero)
495 float angle = Vector3.Dot(direction, diff);
497 if (angle < lowAngle)
504 if (!constrainVertical && lowIndex != -1)
506 for (
int i = 0; i < numRays; i++)
508 if (hits[i] ==
false || i == closestPoint || i == lowIndex)
513 float dot = Mathf.Abs(Vector3.Dot((positions[i] - positions[closestPoint]).normalized, (positions[lowIndex] - positions[closestPoint]).normalized));
519 Vector3 normal = Vector3.Cross(positions[lowIndex] - positions[closestPoint], positions[i] - positions[closestPoint]).normalized;
521 float nextAngle = Mathf.Abs(Vector3.Dot(direction, normal));
523 if (nextAngle > highAngle)
525 highAngle = nextAngle;
531 Vector3 placementNormal;
536 Debug.DrawLine(positions[closestPoint], positions[lowIndex], Color.red);
543 Debug.DrawLine(positions[closestPoint], positions[highIndex], Color.green);
545 placementNormal = Vector3.Cross(positions[lowIndex] - positions[closestPoint], positions[highIndex] - positions[closestPoint]).normalized;
549 Vector3 planeUp = Vector3.Cross(positions[lowIndex] - positions[closestPoint], direction);
550 placementNormal = Vector3.Cross(positions[lowIndex] - positions[closestPoint], constrainVertical ? Vector3.up : planeUp).normalized;
555 Debug.DrawLine(positions[closestPoint], positions[closestPoint] + placementNormal, Color.blue);
560 placementNormal = direction * -1.0f;
563 if (Vector3.Dot(placementNormal, direction) > 0.0f)
565 placementNormal *= -1.0f;
568 plane =
new Plane(placementNormal, positions[closestPoint]);
572 Debug.DrawRay(positions[closestPoint], placementNormal, Color.cyan);
576 if (!bUseClosestDistance && closestPoint >= 0)
578 float centerPlaneDistance;
579 Ray centerPlaneRay =
new Ray(origin, originalDirection);
580 if (plane.Raycast(centerPlaneRay, out centerPlaneDistance) || centerPlaneDistance != 0)
583 closestDistance = Mathf.Clamp(centerPlaneDistance, closestDistance, farthestDistance + assetWidth * 0.5f);
587 Debug.LogError(
"FindPlacementPlane: Not expected to have the center point not intersect the plane.");