TransformInspectorWithSize

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
m (Fix class name to match filename)
(Fix for issues with multi-select and NaN values)
Line 21: Line 21:
  
 
[CanEditMultipleObjects, CustomEditor(typeof(Transform))]
 
[CanEditMultipleObjects, CustomEditor(typeof(Transform))]
public class TransformInspectorWithSize : Editor {
+
public class TransformInspector : UnityEditor.Editor {
  
 
     private const float FIELD_WIDTH = 212.0f;
 
     private const float FIELD_WIDTH = 212.0f;
Line 28: Line 28:
 
     private const float POSITION_MAX = 100000.0f;
 
     private const float POSITION_MAX = 100000.0f;
  
     private static GUIContent positionGUIContent = new GUIContent(LocalString("Position")
+
     private static GUIContent positionGUIContent = new GUIContent(LocalString("Position"), LocalString("The local position of this Game Object relative to the parent."));
                                                                ,LocalString("The local position of this Game Object relative to the parent."));
+
     private static GUIContent rotationGUIContent = new GUIContent(LocalString("Rotation"), LocalString("The local rotation of this Game Object relative to the parent."));
     private static GUIContent rotationGUIContent = new GUIContent(LocalString("Rotation")
+
     private static GUIContent scaleGUIContent    = new GUIContent(LocalString("Scale"),     LocalString("The local scaling of this Game Object relative to the parent."));
                                                                ,LocalString("The local rotation of this Game Object relative to the parent."));
+
     private static GUIContent sizeGUIContent    = new GUIContent(LocalString("Size"),     LocalString("The local scaled mesh size of this Game Object relative to the parent."));
     private static GUIContent scaleGUIContent    = new GUIContent(LocalString("Scale")
+
                                                                ,LocalString("The local scaling of this Game Object relative to the parent."));
+
     private static GUIContent sizeGUIContent    = new GUIContent(LocalString("Size")
+
                                                                ,LocalString("The local scaled mesh size of this Game Object relative to the parent."));
+
  
 
     private static string positionWarningText = LocalString("Due to floating-point precision limitations, it is recommended to bring the world coordinates of the GameObject within a smaller range.");
 
     private static string positionWarningText = LocalString("Due to floating-point precision limitations, it is recommended to bring the world coordinates of the GameObject within a smaller range.");
Line 50: Line 46:
 
         this.positionProperty = this.serializedObject.FindProperty("m_LocalPosition");
 
         this.positionProperty = this.serializedObject.FindProperty("m_LocalPosition");
 
         this.rotationProperty = this.serializedObject.FindProperty("m_LocalRotation");
 
         this.rotationProperty = this.serializedObject.FindProperty("m_LocalRotation");
         this.scaleProperty   = this.serializedObject.FindProperty("m_LocalScale");
+
         this.scaleProperty = this.serializedObject.FindProperty("m_LocalScale");
 
     }
 
     }
  
Line 60: Line 56:
  
 
         EditorGUILayout.PropertyField(this.positionProperty, positionGUIContent);
 
         EditorGUILayout.PropertyField(this.positionProperty, positionGUIContent);
         this.RotationPropertyField(   this.rotationProperty, rotationGUIContent);
+
         this.RotationPropertyField(this.rotationProperty, rotationGUIContent);
         EditorGUILayout.PropertyField(this.scaleProperty,   scaleGUIContent);
+
         EditorGUILayout.PropertyField(this.scaleProperty, scaleGUIContent);
         this.ScaledSizePropertyField( this.scaleProperty, sizeGUIContent);
+
         this.ScaledSizePropertyField(this.scaleProperty, sizeGUIContent);
  
 
         if (!ValidatePosition(((Transform) this.target).position)) {
 
         if (!ValidatePosition(((Transform) this.target).position)) {
Line 113: Line 109:
  
 
     private void ScaledSizePropertyField(SerializedProperty scaleProperty, GUIContent content) {
 
     private void ScaledSizePropertyField(SerializedProperty scaleProperty, GUIContent content) {
         Vector3 meshSize = GetMaxMeshSize((Transform) this.targets[0]);
+
        bool guiEnabled = GUI.enabled;
         foreach (UnityEngine.Object obj in this.targets) {
+
        bool enabled = GUI.enabled;
             if (meshSize != GetMaxMeshSize((Transform) obj)) {
+
         Vector3 meshSize = GetWorldMeshSize((Transform) this.target);
                EditorGUI.showMixedValue = true;
+
         if (this.targets.Length > 1) {
                break;
+
            foreach (Transform obj1 in this.targets) {
 +
                foreach (Transform obj2 in this.targets) {
 +
                    if (obj1.parent == obj2) enabled = false;
 +
                }
 +
             }
 +
            foreach (Transform obj1 in this.targets) {
 +
                if (meshSize != GetWorldMeshSize((Transform) obj1)) {
 +
                    EditorGUI.showMixedValue = true;
 +
                    break;
 +
                }
 
             }
 
             }
 
         }
 
         }
  
         EditorGUI.BeginChangeCheck();
+
         if (meshSize == Vector3.zero) return;
  
 
         EditorGUILayout.Space();
 
         EditorGUILayout.Space();
         Vector3 size = EditorGUILayout.Vector3Field(content, Vector3.Scale(meshSize, scaleProperty.vector3Value));
+
        EditorGUI.BeginChangeCheck();
 +
 
 +
        // convert to local scale
 +
        Transform t = (Transform) this.target;
 +
        Vector3 lossy2local = new Vector3(t.localScale.x / t.lossyScale.x, t.localScale.y / t.lossyScale.y, t.localScale.z / t.lossyScale.z);
 +
 
 +
        meshSize = Vector3.Scale(meshSize, lossy2local);
 +
 
 +
        enabled = enabled && !EditorGUI.showMixedValue;
 +
        GUI.enabled = enabled;
 +
         Vector3 size = EditorGUILayout.Vector3Field(content, meshSize);
 +
        GUI.enabled = guiEnabled;
 +
        if (!enabled) return;
 +
 
 +
        // don't update if any component is 0
 +
        if (size.x == 0) return;
 +
        if (size.y == 0) return;
 +
        if (size.z == 0) return;
  
 
         if (EditorGUI.EndChangeCheck()) {
 
         if (EditorGUI.EndChangeCheck()) {
 
             Undo.RecordObjects(this.targets, "Scale Changed");
 
             Undo.RecordObjects(this.targets, "Scale Changed");
 
             foreach (UnityEngine.Object obj in this.targets) {
 
             foreach (UnityEngine.Object obj in this.targets) {
                 Transform t = (Transform) obj;
+
                 t = (Transform) obj;
                 meshSize = GetMaxMeshSize(t);
+
                 meshSize = GetWorldMeshSize(t);
                 t.localScale = new Vector3(size.x / meshSize.x, size.y / meshSize.y, size.z / meshSize.z);
+
                 if (meshSize == Vector3.zero) continue;
 +
                lossy2local = new Vector3(t.localScale.x / t.lossyScale.x, t.localScale.y / t.lossyScale.y, t.localScale.z / t.lossyScale.z);
 +
                meshSize = Vector3.Scale(meshSize, lossy2local);
 +
                t.localScale = Vector3.Scale(t.localScale, new Vector3(size.x / meshSize.x, size.y / meshSize.y, size.z / meshSize.z));
 
             }
 
             }
 
             scaleProperty.serializedObject.SetIsDifferentCacheDirty();
 
             scaleProperty.serializedObject.SetIsDifferentCacheDirty();
Line 137: Line 162:
 
     }
 
     }
  
     private Vector3 GetMaxMeshSize(Transform t) {
+
 
 +
     private Vector3 GetWorldMeshSize(Transform t) {
 
         Vector3 size = Vector3.zero;
 
         Vector3 size = Vector3.zero;
 
         MeshFilter[] filters = t.GetComponentsInChildren<MeshFilter>();
 
         MeshFilter[] filters = t.GetComponentsInChildren<MeshFilter>();
 
         foreach (MeshFilter filter in filters) {
 
         foreach (MeshFilter filter in filters) {
             size = Vector3.Max(size, filter.sharedMesh.bounds.size);
+
             Vector3 meshSize = Vector3.Scale(filter.sharedMesh.bounds.size, filter.transform.lossyScale);
 +
            size = Vector3.Max(size, meshSize);
 
         }
 
         }
 
         return size;
 
         return size;

Revision as of 04:40, 1 October 2015

Description

The following script adds a Size field to the Transform inspector to show the scaled mesh size of an object.

Provides a convenient way to see the actual size of a mesh in the scene or update the scale of an object to define a precise object size after scaling.

Accounts for child objects with meshes as well, showing the total size in the parent.

The Size field can be edited and will update the Scale property in relation the objects mesh size.

Derived from TransformInspector.

Usage

Place TransformInspectorWithSize.cs into an Editor folder inside of your project's Assets folder.

TransformInspectorWithSize.cs

// Reverse engineered UnityEditor.TransformInspector
 
using UnityEngine;
using UnityEditor;
 
[CanEditMultipleObjects, CustomEditor(typeof(Transform))]
public class TransformInspector : UnityEditor.Editor {
 
    private const float FIELD_WIDTH = 212.0f;
    private const bool WIDE_MODE = true;
 
    private const float POSITION_MAX = 100000.0f;
 
    private static GUIContent positionGUIContent = new GUIContent(LocalString("Position"),  LocalString("The local position of this Game Object relative to the parent."));
    private static GUIContent rotationGUIContent = new GUIContent(LocalString("Rotation"),  LocalString("The local rotation of this Game Object relative to the parent."));
    private static GUIContent scaleGUIContent    = new GUIContent(LocalString("Scale"),     LocalString("The local scaling of this Game Object relative to the parent."));
    private static GUIContent sizeGUIContent     = new GUIContent(LocalString("Size"),      LocalString("The local scaled mesh size of this Game Object relative to the parent."));
 
    private static string positionWarningText = LocalString("Due to floating-point precision limitations, it is recommended to bring the world coordinates of the GameObject within a smaller range.");
 
    private SerializedProperty positionProperty;
    private SerializedProperty rotationProperty;
    private SerializedProperty scaleProperty;
 
    private static string LocalString(string text) {
        return LocalizationDatabase.GetLocalizedString(text);
    }
 
    public void OnEnable() {
        this.positionProperty = this.serializedObject.FindProperty("m_LocalPosition");
        this.rotationProperty = this.serializedObject.FindProperty("m_LocalRotation");
        this.scaleProperty = this.serializedObject.FindProperty("m_LocalScale");
    }
 
    public override void OnInspectorGUI() {
        EditorGUIUtility.wideMode = TransformInspector.WIDE_MODE;
        EditorGUIUtility.labelWidth = EditorGUIUtility.currentViewWidth - TransformInspector.FIELD_WIDTH; // align field to right of inspector
 
        this.serializedObject.Update();
 
        EditorGUILayout.PropertyField(this.positionProperty, positionGUIContent);
        this.RotationPropertyField(this.rotationProperty, rotationGUIContent);
        EditorGUILayout.PropertyField(this.scaleProperty, scaleGUIContent);
        this.ScaledSizePropertyField(this.scaleProperty, sizeGUIContent);
 
        if (!ValidatePosition(((Transform) this.target).position)) {
            EditorGUILayout.HelpBox(positionWarningText, MessageType.Warning);
        }
 
        this.serializedObject.ApplyModifiedProperties();
    }
 
    private bool ValidatePosition(Vector3 position) {
        if (Mathf.Abs(position.x) > TransformInspector.POSITION_MAX) return false;
        if (Mathf.Abs(position.y) > TransformInspector.POSITION_MAX) return false;
        if (Mathf.Abs(position.z) > TransformInspector.POSITION_MAX) return false;
        return true;
    }
 
    private void RotationPropertyField(SerializedProperty rotationProperty, GUIContent content) {
        Transform transform = (Transform) this.targets[0];
        Quaternion localRotation = transform.localRotation;
        foreach (UnityEngine.Object t in (UnityEngine.Object[]) this.targets) {
            if (!SameRotation(localRotation, ((Transform) t).localRotation)) {
                EditorGUI.showMixedValue = true;
                break;
            }
        }
 
        EditorGUI.BeginChangeCheck();
 
        Vector3 eulerAngles = EditorGUILayout.Vector3Field(content, localRotation.eulerAngles);
 
        if (EditorGUI.EndChangeCheck()) {
            Undo.RecordObjects(this.targets, "Rotation Changed");
            foreach (UnityEngine.Object obj in this.targets) {
                Transform t = (Transform) obj;
                t.localEulerAngles = eulerAngles;
            }
            rotationProperty.serializedObject.SetIsDifferentCacheDirty();
        }
 
        EditorGUI.showMixedValue = false;
    }
 
    private bool SameRotation(Quaternion rot1, Quaternion rot2) {
        if (rot1.x != rot2.x) return false;
        if (rot1.y != rot2.y) return false;
        if (rot1.z != rot2.z) return false;
        if (rot1.w != rot2.w) return false;
        return true;
    }
 
    private void ScaledSizePropertyField(SerializedProperty scaleProperty, GUIContent content) {
        bool guiEnabled = GUI.enabled;
        bool enabled = GUI.enabled;
        Vector3 meshSize = GetWorldMeshSize((Transform) this.target);
        if (this.targets.Length > 1) {
            foreach (Transform obj1 in this.targets) {
                foreach (Transform obj2 in this.targets) {
                    if (obj1.parent == obj2) enabled = false;
                }
            }
            foreach (Transform obj1 in this.targets) {
                if (meshSize != GetWorldMeshSize((Transform) obj1)) {
                    EditorGUI.showMixedValue = true;
                    break;
                }
            }
        }
 
        if (meshSize == Vector3.zero) return;
 
        EditorGUILayout.Space();
        EditorGUI.BeginChangeCheck();
 
        // convert to local scale
        Transform t = (Transform) this.target;
        Vector3 lossy2local = new Vector3(t.localScale.x / t.lossyScale.x, t.localScale.y / t.lossyScale.y, t.localScale.z / t.lossyScale.z);
 
        meshSize = Vector3.Scale(meshSize, lossy2local);
 
        enabled = enabled && !EditorGUI.showMixedValue;
        GUI.enabled = enabled;
        Vector3 size = EditorGUILayout.Vector3Field(content, meshSize);
        GUI.enabled = guiEnabled;
        if (!enabled) return;
 
        // don't update if any component is 0
        if (size.x == 0) return;
        if (size.y == 0) return;
        if (size.z == 0) return;
 
        if (EditorGUI.EndChangeCheck()) {
            Undo.RecordObjects(this.targets, "Scale Changed");
            foreach (UnityEngine.Object obj in this.targets) {
                t = (Transform) obj;
                meshSize = GetWorldMeshSize(t);
                if (meshSize == Vector3.zero) continue;
                lossy2local = new Vector3(t.localScale.x / t.lossyScale.x, t.localScale.y / t.lossyScale.y, t.localScale.z / t.lossyScale.z);
                meshSize = Vector3.Scale(meshSize, lossy2local);
                t.localScale = Vector3.Scale(t.localScale, new Vector3(size.x / meshSize.x, size.y / meshSize.y, size.z / meshSize.z));
            }
            scaleProperty.serializedObject.SetIsDifferentCacheDirty();
        }
    }
 
 
    private Vector3 GetWorldMeshSize(Transform t) {
        Vector3 size = Vector3.zero;
        MeshFilter[] filters = t.GetComponentsInChildren<MeshFilter>();
        foreach (MeshFilter filter in filters) {
            Vector3 meshSize = Vector3.Scale(filter.sharedMesh.bounds.size, filter.transform.lossyScale);
            size = Vector3.Max(size, meshSize);
        }
        return size;
    }
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox