Files
DeviantMobile-Rohan/Assets/Plugins/PanettoneGames/ColliderX/Editor/ColliderX.cs

276 lines
9.8 KiB
C#

using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(Transform), true)]
public class ColliderX : UnityEditor.Editor
{
#region private members
private bool _enforceMeshColliders = false;
private bool _useConvex = true;
private bool _keep = false;
private Transform _target;
private GUISkin skin;
//Unity's built-in editor
Editor defaultEditor;
private bool showInfo;
private int _vertCount;
private static string myPubID = "46749";
[Tooltip("Number of Vertices below which a Box Collider will be used")]
private int vertLimit = 200;
//[Range(0.1f, 1f)]
private float blockinessThreshold = 0.6f;
private bool isBox;
private string meshType;
private ColliderLimits colliderLimits;
private bool settingsSection;
#endregion
void OnEnable()
{
//When this inspector is created, also create the built-in inspector
defaultEditor = Editor.CreateEditor(targets, Type.GetType("UnityEditor.TransformInspector, UnityEditor"));
//initialization
showInfo = true;
skin = Resources.Load<GUISkin>("guiStyles/Default");
colliderLimits = Resources.Load<ColliderLimits>("settings/ColliderLimits");//there's already one scriptable object asset provided and you don't actually need to create another one, just find it and change its variables
vertLimit = colliderLimits.VertLimit;
blockinessThreshold = colliderLimits.BlockinessThreshold;
}
void OnDisable()
{
//When OnDisable is called, the default editor we created should be destroyed to avoid memory leakage.
//Also, make sure to call any required methods like OnDisable
MethodInfo disableMethod = defaultEditor.GetType().GetMethod("OnDisable", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (disableMethod != null)
disableMethod.Invoke(defaultEditor, null);
DestroyImmediate(defaultEditor);
}
/// <summary>
/// Draws UI. Called every time the mouse hovers over the editor
/// </summary>
public override void OnInspectorGUI()
{
//Drawing UI
EditorGUILayout.LabelField("Local Space", EditorStyles.boldLabel);
defaultEditor.OnInspectorGUI();
//Uncomment to show world space position, rotation and scale
//ShowWorldSpace();
GUI.enabled = true;
DrawXColliderInspector();
}
/// <summary>
/// Reveals WorldSpace data
/// </summary>
private void ShowWorldSpace()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("World Space", EditorStyles.boldLabel);
GUI.enabled = false;
Vector3 localPosition = _target.localPosition;
_target.localPosition = _target.position;
Quaternion localRotation = _target.localRotation;
_target.localRotation = _target.rotation;
Vector3 localScale = _target.localScale;
_target.localScale = _target.lossyScale;
defaultEditor.OnInspectorGUI();
_target.localPosition = localPosition;
_target.localRotation = localRotation;
_target.localScale = localScale;
}
/// <summary>
/// Draws the Editor UI
/// </summary>
private void DrawXColliderInspector()
{
_target = (Transform)target;
int _meshCount = _target.GetComponentsInChildren<MeshFilter>().Length;
int _colliderCount = _target.GetComponentsInChildren<Collider>().Length;
int _boxColliderCount = _target.GetComponentsInChildren<BoxCollider>().Length;
int _meshColliderCount = _target.GetComponentsInChildren<MeshCollider>().Length;
_vertCount = 0;
MeshFilter _item = _target.GetComponent<MeshFilter>();
if (_meshCount > 0)
{
if (_target.GetComponent<MeshFilter>() != null)
_vertCount = _target.GetComponent<MeshFilter>().sharedMesh.vertexCount;
EditorGUILayout.Space(20);
//GUILayout.Space(20);
showInfo = EditorGUILayout.BeginFoldoutHeaderGroup(showInfo, "ColliderX", skin.GetStyle("PanHeaderDefault"));
if (showInfo)
{
EditorGUILayout.Space(5);
//GUILayout.Label($"Colliders");
if (_item != null)
{
GUILayout.BeginHorizontal();
isBox = (IsBlocky(_item) || _item.sharedMesh.vertexCount < vertLimit);
//meshType = isBox ? $"Box Colliders for blocky objects under {vertLimit}" : $"Mesh Colliders for blocky objects under {vertLimit}";
meshType = isBox ? "Box" : "Mesh";
GUILayout.Label($"Is Blocky? {IsBlocky(_item)}, Is LowPoly? { _item.sharedMesh.vertexCount < vertLimit}");
GUILayout.Label($"{_item.name} Verts: {_vertCount}");
GUILayout.EndHorizontal();
GUILayout.Label(meshType, isBox ? skin.GetStyle("GreenText") : skin.GetStyle("RedText"));
EditorGUILayout.Space(5);
}
GUILayout.BeginHorizontal();
_keep = GUILayout.Toggle(_keep, "Keep Existing");
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
_enforceMeshColliders = GUILayout.Toggle(_enforceMeshColliders, "Enforce Mesh Colliders");
_useConvex = GUILayout.Toggle(_useConvex, "Use Convex");
GUILayout.EndHorizontal();
EditorGUILayout.Space(5);
if (GUILayout.Button($"Generate Colliders for {_meshCount} objects"))
{
colliderLimits.VertLimit = vertLimit;
colliderLimits.BlockinessThreshold = blockinessThreshold;
AddColliders(_enforceMeshColliders, _useConvex);
}
if (_colliderCount > 0)
{
var msg = (_colliderCount > 1) ?
$"Remove {_boxColliderCount} Box & {_meshColliderCount} Mesh Colliders" :
$"Remove {_colliderCount} Colliders";
if (GUILayout.Button(msg))
RemoveAllColliders();
}
}
EditorGUILayout.EndFoldoutHeaderGroup();
GUILayout.Space(15);
settingsSection = EditorGUILayout.BeginFoldoutHeaderGroup(settingsSection, "Detection Settings");//, skin.GetStyle("H2"));
if (settingsSection)
{
vertLimit = EditorGUILayout.IntField("Verts Limit:", Mathf.Clamp(vertLimit, 3, 10000));
blockinessThreshold = EditorGUILayout.FloatField("Block Thresh Limit:", Mathf.Clamp(blockinessThreshold, 0.01f, 1.5f));
}
EditorGUILayout.EndFoldoutHeaderGroup();
GUILayout.Space(10);
if (GUILayout.Button("More cool tools...", skin.GetStyle("PanStoreLink")))
{
Application.OpenURL($"https://assetstore.unity.com/publishers/" + myPubID);
Application.OpenURL($"https://panettonegames.com/");
}
GUILayout.Space(5);
}
}
public int GetCollidersCount()
{
return _target.GetComponentsInChildren<MeshFilter>().Length;
}
/// <summary>
/// Generates Colliders to all child transforms
/// </summary>
/// <param name="enfoceMeshCollider">Always Creates mesh colliders regardless of vertics count and GameObject Dimensions/Blockiness</param>
/// <param name="useConvex">Enforces Convex colliders</param>
public void AddColliders(bool enfoceMeshCollider, bool useConvex)
{
var items = _target.GetComponentsInChildren<MeshFilter>();
foreach (var item in items)
{
if (!_keep)
RemoveColliders(item);
if (enfoceMeshCollider)
{
if (item.gameObject.GetComponent<Collider>() == null)
item.gameObject.AddComponent<MeshCollider>().convex = useConvex;
}
else
{
if ((IsBlocky(item) || item.sharedMesh.vertexCount < vertLimit))
{
if (item.gameObject.GetComponent<Collider>() == null)
item.gameObject.AddComponent<BoxCollider>();
}
else
{
if (item.gameObject.GetComponent<Collider>() == null)
item.gameObject.AddComponent<MeshCollider>().convex = useConvex;
}
}
}
}
/// <summary>
/// Decides whether or not the GameObject is blocky based on the BlockinessThreshold settings in the ColliderLimits Scriptable Objects
/// </summary>
/// <param name="item">The Target GameObject Mesh</param>
/// <returns></returns>
private bool IsBlocky(MeshFilter item)
{
return
Mathf.Abs(item.sharedMesh.bounds.size.x - item.sharedMesh.bounds.size.y) < blockinessThreshold ||
Mathf.Abs(item.sharedMesh.bounds.size.x - item.sharedMesh.bounds.size.z) < blockinessThreshold ||
Mathf.Abs(item.sharedMesh.bounds.size.y - item.sharedMesh.bounds.size.z) < blockinessThreshold;
}
/// <summary>
/// Clears colliders for a particular GameObject with a MeshFilter
/// </summary>
/// <param name="item">Child GameObject</param>
private void RemoveColliders(MeshFilter item)
{
foreach (var xCollider in item.gameObject.GetComponents<Collider>())
DestroyImmediate(xCollider);
}
/// <summary>
/// Clears colliders from all children with MeshFilter
/// </summary>
public void RemoveAllColliders()
{
var items = _target.GetComponentsInChildren<MeshFilter>();
foreach (var item in items)
RemoveColliders(item);
}
}