private static SphereColliderSerializer SphereColliderToSerializer(SpringSphereCollider sourceCollider)
 {
     return(new SphereColliderSerializer
     {
         baseInfo = TransformToColliderSerializerBaseInfo(sourceCollider.transform, SphereColliderToken),
         radius = sourceCollider.radius,
         linkedRenderer = GetComponentName(sourceCollider.linkedRenderer)
     });
 }
        public SpringBone.CollisionStatus CheckForCollisionAndReact
        (
            Vector3 moverHeadPosition,
            ref Vector3 moverPosition,
            float moverRadius,
            ref Vector3 hitNormal
        )
        {
            const float RadiusThreshold = 0.0001f;

            if ((linkedRenderer != null && !linkedRenderer.enabled) ||
                radius <= RadiusThreshold)
            {
                return(SpringBone.CollisionStatus.NoCollision);
            }

            var worldToLocal = transform.worldToLocalMatrix;
            var radiusScale  = worldToLocal.MultiplyVector(new Vector3(1f, 0f, 0f)).magnitude;

            // Lower than start cap
            var localHeadPosition  = worldToLocal.MultiplyPoint3x4(moverHeadPosition);
            var localMoverPosition = worldToLocal.MultiplyPoint3x4(moverPosition);
            var localMoverRadius   = moverRadius * radiusScale;

            var moverIsAboveTop = localMoverPosition.y >= height;
            var useSphereCheck  = (localMoverPosition.y <= 0f) | moverIsAboveTop;

            if (useSphereCheck)
            {
                var sphereOrigin   = new Vector3(0f, moverIsAboveTop ? height : 0f, 0f);
                var combinedRadius = localMoverRadius + radius;
                if ((localMoverPosition - sphereOrigin).sqrMagnitude >= combinedRadius * combinedRadius)
                {
                    // Not colliding
                    return(SpringBone.CollisionStatus.NoCollision);
                }

                var originToHead   = localHeadPosition - sphereOrigin;
                var isHeadEmbedded = originToHead.sqrMagnitude <= radius * radius;

#if UNITY_EDITOR
                RecordSphereCollision(
                    sphereOrigin,
                    localMoverPosition,
                    moverRadius,
                    isHeadEmbedded ?
                    SpringBone.CollisionStatus.HeadIsEmbedded :
                    SpringBone.CollisionStatus.TailCollision);
#endif

                if (isHeadEmbedded)
                {
                    // The head is inside the sphere, so just try to push the tail out
                    var localHitNormal = (localMoverPosition - sphereOrigin).normalized;
                    localMoverPosition = sphereOrigin + localHitNormal * combinedRadius;
                    moverPosition      = transform.TransformPoint(localMoverPosition);
                    hitNormal          = transform.TransformDirection(localHitNormal).normalized;
                    return(SpringBone.CollisionStatus.HeadIsEmbedded);
                }

                var localHeadRadius = (localMoverPosition - localHeadPosition).magnitude;
                var intersection    = new Circle3();
                if (SpringSphereCollider.ComputeIntersection(
                        localHeadPosition,
                        localHeadRadius,
                        sphereOrigin,
                        combinedRadius,
                        ref intersection))
                {
                    localMoverPosition = SpringSphereCollider.ComputeNewTailPosition(intersection, localMoverPosition);
                    moverPosition      = transform.TransformPoint(localMoverPosition);
                    var localHitNormal = (localMoverPosition - sphereOrigin).normalized;
                    hitNormal = transform.TransformDirection(localHitNormal).normalized;
                }

                return(SpringBone.CollisionStatus.TailCollision);
            }

            // Cylinder
            var collisionStatus = CheckForCylinderCollisionAndReact(
                localHeadPosition, ref moverPosition, localMoverRadius, localMoverPosition, ref hitNormal);
            return(collisionStatus);
        }