holopy3/Assets/DynamicBone/Scripts/DynamicBoneCollider.cs
2020-12-10 15:25:12 +01:00

235 lines
7.2 KiB
C#

using UnityEngine;
[AddComponentMenu("Dynamic Bone/Dynamic Bone Collider")]
public class DynamicBoneCollider : DynamicBoneColliderBase
{
#if UNITY_5_3_OR_NEWER
[Tooltip("The radius of the sphere or capsule.")]
#endif
public float m_Radius = 0.5f;
#if UNITY_5_3_OR_NEWER
[Tooltip("The height of the capsule.")]
#endif
public float m_Height = 0;
void OnValidate()
{
m_Radius = Mathf.Max(m_Radius, 0);
m_Height = Mathf.Max(m_Height, 0);
}
public override bool Collide(ref Vector3 particlePosition, float particleRadius)
{
float radius = m_Radius * Mathf.Abs(transform.lossyScale.x);
float h = m_Height * 0.5f - m_Radius;
if (h <= 0)
{
if (m_Bound == Bound.Outside)
return OutsideSphere(ref particlePosition, particleRadius, transform.TransformPoint(m_Center), radius);
else
return InsideSphere(ref particlePosition, particleRadius, transform.TransformPoint(m_Center), radius);
}
else
{
Vector3 c0 = m_Center;
Vector3 c1 = m_Center;
switch (m_Direction)
{
case Direction.X:
c0.x -= h;
c1.x += h;
break;
case Direction.Y:
c0.y -= h;
c1.y += h;
break;
case Direction.Z:
c0.z -= h;
c1.z += h;
break;
}
if (m_Bound == Bound.Outside)
return OutsideCapsule(ref particlePosition, particleRadius, transform.TransformPoint(c0), transform.TransformPoint(c1), radius);
else
return InsideCapsule(ref particlePosition, particleRadius, transform.TransformPoint(c0), transform.TransformPoint(c1), radius);
}
}
static bool OutsideSphere(ref Vector3 particlePosition, float particleRadius, Vector3 sphereCenter, float sphereRadius)
{
float r = sphereRadius + particleRadius;
float r2 = r * r;
Vector3 d = particlePosition - sphereCenter;
float len2 = d.sqrMagnitude;
// if is inside sphere, project onto sphere surface
if (len2 > 0 && len2 < r2)
{
float len = Mathf.Sqrt(len2);
particlePosition = sphereCenter + d * (r / len);
return true;
}
return false;
}
static bool InsideSphere(ref Vector3 particlePosition, float particleRadius, Vector3 sphereCenter, float sphereRadius)
{
float r = sphereRadius - particleRadius;
float r2 = r * r;
Vector3 d = particlePosition - sphereCenter;
float len2 = d.sqrMagnitude;
// if is outside sphere, project onto sphere surface
if (len2 > r2)
{
float len = Mathf.Sqrt(len2);
particlePosition = sphereCenter + d * (r / len);
return true;
}
return false;
}
static bool OutsideCapsule(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius)
{
float r = capsuleRadius + particleRadius;
float r2 = r * r;
Vector3 dir = capsuleP1 - capsuleP0;
Vector3 d = particlePosition - capsuleP0;
float t = Vector3.Dot(d, dir);
if (t <= 0)
{
// check sphere1
float len2 = d.sqrMagnitude;
if (len2 > 0 && len2 < r2)
{
float len = Mathf.Sqrt(len2);
particlePosition = capsuleP0 + d * (r / len);
return true;
}
}
else
{
float dl = dir.sqrMagnitude;
if (t >= dl)
{
// check sphere2
d = particlePosition - capsuleP1;
float len2 = d.sqrMagnitude;
if (len2 > 0 && len2 < r2)
{
float len = Mathf.Sqrt(len2);
particlePosition = capsuleP1 + d * (r / len);
return true;
}
}
else if (dl > 0)
{
// check cylinder
t /= dl;
d -= dir * t;
float len2 = d.sqrMagnitude;
if (len2 > 0 && len2 < r2)
{
float len = Mathf.Sqrt(len2);
particlePosition += d * ((r - len) / len);
return true;
}
}
}
return false;
}
static bool InsideCapsule(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius)
{
float r = capsuleRadius - particleRadius;
float r2 = r * r;
Vector3 dir = capsuleP1 - capsuleP0;
Vector3 d = particlePosition - capsuleP0;
float t = Vector3.Dot(d, dir);
if (t <= 0)
{
// check sphere1
float len2 = d.sqrMagnitude;
if (len2 > r2)
{
float len = Mathf.Sqrt(len2);
particlePosition = capsuleP0 + d * (r / len);
return true;
}
}
else
{
float dl = dir.sqrMagnitude;
if (t >= dl)
{
// check sphere2
d = particlePosition - capsuleP1;
float len2 = d.sqrMagnitude;
if (len2 > r2)
{
float len = Mathf.Sqrt(len2);
particlePosition = capsuleP1 + d * (r / len);
return true;
}
}
else if (dl > 0)
{
// check cylinder
t /= dl;
d -= dir * t;
float len2 = d.sqrMagnitude;
if (len2 > r2)
{
float len = Mathf.Sqrt(len2);
particlePosition += d * ((r - len) / len);
return true;
}
}
}
return false;
}
void OnDrawGizmosSelected()
{
if (!enabled)
return;
if (m_Bound == Bound.Outside)
Gizmos.color = Color.yellow;
else
Gizmos.color = Color.magenta;
float radius = m_Radius * Mathf.Abs(transform.lossyScale.x);
float h = m_Height * 0.5f - m_Radius;
if (h <= 0)
{
Gizmos.DrawWireSphere(transform.TransformPoint(m_Center), radius);
}
else
{
Vector3 c0 = m_Center;
Vector3 c1 = m_Center;
switch (m_Direction)
{
case Direction.X:
c0.x -= h;
c1.x += h;
break;
case Direction.Y:
c0.y -= h;
c1.y += h;
break;
case Direction.Z:
c0.z -= h;
c1.z += h;
break;
}
Gizmos.DrawWireSphere(transform.TransformPoint(c0), radius);
Gizmos.DrawWireSphere(transform.TransformPoint(c1), radius);
}
}
}