using Unity.Mathematics; using UnityEngine; namespace AutomaticDoorSystem { /// /// Pure math for baking a panel's BoxCollider into the frame the collider pool reproduces at /// runtime: a proxy GameObject parked at the panel entity's LocalToWorld position+rotation, /// at localScale 1, whose BoxCollider gets the baked center/size verbatim. /// /// The authored collider often does NOT live on the panel pivot itself - door models keep the /// BoxCollider on the mesh child (e.g. 'DoubleDoors_Door_L' under the animated CTRL node), so /// its center is child-local. Baking that raw mirrors the collider to the wrong side of the /// hinge at runtime (off by a full door width) while edit-mode physics, which uses the child's /// real transform, looks perfect. This helper folds the whole child chain - offset, rotation, /// scale, mirror - into panel-frame center/size, so the runtime proxy reproduces the authored /// box exactly. Static and UnityEngine-only so the baker, the Setup Validator and the tests /// share one definition. /// public static class DoorColliderBake { /// /// A baked box more than this factor larger (by volume) than the authored one gets a /// warning. Right-angle rotations (a right panel authored as the left one turned 180°) /// reproduce exactly and stay silent; only oblique rotations actually bloat the AABB. /// public const float BloatWarnRatio = 1.05f; /// /// How much bigger (by volume) the baked panel-frame AABB is than the authored world box. /// 1 = exact reproduction; 2 = a box rotated 45° around one axis. /// public static float BloatRatio(float3 bakedSize, Vector3 authoredWorldSize) { var authoredVolume = authoredWorldSize.x * authoredWorldSize.y * authoredWorldSize.z; if (authoredVolume <= 1e-12f) return 1f; return bakedSize.x * bakedSize.y * bakedSize.z / authoredVolume; } /// The authored box's world-space center (child transform applied). public static Vector3 WorldCenter(BoxCollider box) => box.transform.TransformPoint(box.center); /// The authored box's world-space size (|lossyScale| applied per axis). public static Vector3 WorldSize(BoxCollider box) { var s = box.transform.lossyScale; return Vector3.Scale(box.size, new Vector3(Mathf.Abs(s.x), Mathf.Abs(s.y), Mathf.Abs(s.z))); } /// /// Expresses a world-space oriented box in the panel pivot's rotation frame (scale-free, /// matching the runtime proxy). When the box is rotated relative to the panel /// ( > 0) the returned size is the enclosing /// AABB in that frame - exact for aligned boxes, conservative otherwise. /// public static void DescribeInPanelFrame( Vector3 panelPosition, Quaternion panelRotation, Vector3 boxWorldCenter, Quaternion boxWorldRotation, Vector3 boxWorldSize, out float3 center, out float3 size, out float relativeAngleDegrees) { var panelRotationInverse = Quaternion.Inverse(panelRotation); center = panelRotationInverse * (boxWorldCenter - panelPosition); var relativeRotation = panelRotationInverse * boxWorldRotation; relativeAngleDegrees = Quaternion.Angle(Quaternion.identity, relativeRotation); var m = Matrix4x4.Rotate(relativeRotation); var extents = 0.5f * boxWorldSize; size = 2f * new float3( Mathf.Abs(m.m00) * extents.x + Mathf.Abs(m.m01) * extents.y + Mathf.Abs(m.m02) * extents.z, Mathf.Abs(m.m10) * extents.x + Mathf.Abs(m.m11) * extents.y + Mathf.Abs(m.m12) * extents.z, Mathf.Abs(m.m20) * extents.x + Mathf.Abs(m.m21) * extents.y + Mathf.Abs(m.m22) * extents.z); } } }