using UnityEngine.ProBuilder.MeshOperations; namespace UnityEngine.ProBuilder.Shapes { [AddComponentMenu(""), DisallowMultipleComponent] sealed class ProBuilderShape : MonoBehaviour { [SerializeReference] Shape m_Shape = new Cube(); [SerializeField] Vector3 m_Size = Vector3.one; [SerializeField] Quaternion m_Rotation = Quaternion.identity; ProBuilderMesh m_Mesh; [SerializeField] PivotLocation m_PivotLocation; [SerializeField] Vector3 m_PivotPosition; [SerializeField] internal ushort m_UnmodifiedMeshVersion; public Shape shape { get => m_Shape; set => m_Shape = value; } public PivotLocation pivotLocation { get => m_PivotLocation; set => m_PivotLocation = value; } public Vector3 pivotLocalPosition { get => m_PivotPosition; set => m_PivotPosition = value; } public Vector3 pivotGlobalPosition { get => mesh.transform.TransformPoint(m_PivotPosition); set => pivotLocalPosition = mesh.transform.InverseTransformPoint(value); } public Vector3 size { get => m_Size; set { m_Size.x = System.Math.Abs(value.x) == 0 ? Mathf.Sign(m_Size.x) * 0.001f: value.x; m_Size.y = value.y; m_Size.z = System.Math.Abs(value.z) == 0 ? Mathf.Sign(m_Size.z) * 0.001f: value.z; } } public Quaternion rotation { get => m_Rotation; set => m_Rotation = value; } Bounds m_EditionBounds; public Bounds editionBounds { get { m_EditionBounds.center = m_ShapeBox.center; m_EditionBounds.size = m_Size; if(Mathf.Abs(m_ShapeBox.size.y) < Mathf.Epsilon) m_EditionBounds.size = new Vector3(m_Size.x, 0f, m_Size.z); return m_EditionBounds; } } [SerializeField] Bounds m_ShapeBox; public Bounds shapeBox => m_ShapeBox; public bool isEditable => m_UnmodifiedMeshVersion == mesh.versionIndex; /// /// Reference to the that this component is creating. /// public ProBuilderMesh mesh { get { if(m_Mesh == null) m_Mesh = GetComponent(); if(m_Mesh == null) m_Mesh = gameObject.AddComponent(); return m_Mesh; } } void OnValidate() { //Ensure the size in X and Z is not set to 0 otherwise PhysX //is throwing errors as it cannot create a collider m_Size.x = System.Math.Abs(m_Size.x) == 0 ? 0.001f: m_Size.x; m_Size.z = System.Math.Abs(m_Size.z) == 0 ? 0.001f: m_Size.z; } internal void UpdateComponent() { //Recenter shape ResetPivot(mesh, size, rotation); Rebuild(); } internal void UpdateBounds(Bounds bounds) { var centerLocalPos = mesh.transform.InverseTransformPoint(bounds.center); Bounds shapeBB = m_ShapeBox; shapeBB.center = centerLocalPos; m_ShapeBox = shapeBB; //Recenter shape ResetPivot(mesh, m_Size, m_Rotation); size = bounds.size; Rebuild(); } internal void Rebuild(Bounds bounds, Quaternion rotation, Vector3 cornerPivot) { var trs = transform; trs.position = bounds.center; trs.rotation = rotation; size = bounds.size; pivotGlobalPosition = pivotLocation == PivotLocation.Center ? bounds.center : cornerPivot; Rebuild(); } void Rebuild() { if(gameObject == null || gameObject.hideFlags == HideFlags.HideAndDontSave) return; m_ShapeBox = m_Shape.RebuildMesh(mesh, size, rotation); RebuildPivot(mesh, size, rotation); Bounds bounds = m_ShapeBox; bounds.size = Math.Abs(m_ShapeBox.size); MeshUtility.FitToSize(mesh, bounds, size); m_UnmodifiedMeshVersion = mesh.versionIndex; } internal void SetShape(Shape shape, PivotLocation location) { m_PivotLocation = location; m_Shape = shape; if(m_Shape is Plane || m_Shape is Sprite) { Bounds bounds = m_ShapeBox; var newCenter = bounds.center; var newSize = bounds.size; newCenter.y = 0; newSize.y = 0; bounds.center = newCenter; bounds.size = newSize; m_ShapeBox = bounds; m_Size.y = 0; } //Else if coming from a 2D-state and being back to a 3D shape //No changes is pivot is centered else if(pivotLocation == PivotLocation.FirstCorner && m_ShapeBox.size.y == 0 && size.y != 0) { Bounds bounds = m_ShapeBox; var newCenter = bounds.center; var newSize = bounds.size; newCenter.y += size.y / 2f; newSize.y = size.y; bounds.center = newCenter; bounds.size = newSize; m_ShapeBox = bounds; } ResetPivot(mesh, size, rotation); Rebuild(); } /// /// Rotates the Shape by a given quaternion while respecting the bounds /// internal void RotateInsideBounds(Quaternion deltaRotation) { ResetPivot(mesh, size, rotation); rotation = deltaRotation * rotation; Rebuild(); } void ResetPivot(ProBuilderMesh mesh, Vector3 size, Quaternion rotation) { if(mesh != null && mesh.mesh != null) { var bbCenter = mesh.transform.TransformPoint(m_ShapeBox.center); var pivotWorldPos = mesh.transform.TransformPoint(m_PivotPosition); mesh.SetPivot(bbCenter); m_PivotPosition = mesh.transform.InverseTransformPoint(pivotWorldPos); m_ShapeBox = m_Shape.UpdateBounds(mesh, size, rotation, m_ShapeBox); } } void RebuildPivot(ProBuilderMesh mesh, Vector3 size, Quaternion rotation) { if(mesh != null && mesh.mesh != null) { var bbCenter = mesh.transform.TransformPoint(m_ShapeBox.center); var pivotWorldPos = mesh.transform.TransformPoint(m_PivotPosition); mesh.SetPivot(pivotWorldPos); m_ShapeBox.center = mesh.transform.InverseTransformPoint(bbCenter); m_PivotPosition = mesh.transform.InverseTransformPoint(pivotWorldPos); m_ShapeBox = m_Shape.UpdateBounds(mesh, size, rotation, m_ShapeBox); } } } }