using UnityEngine; using System; using System.Reflection; using System.Collections.Generic; namespace UnityEditor.Polybrush { internal static class ProBuilderBridge { /// /// Defines what objects are selectable for the scene tool. /// [System.Flags] public enum SelectMode { /// /// No selection mode defined. /// None = 0 << 0, /// /// Objects are selectable. /// Object = 1 << 0, /// /// Vertices are selectable. /// Vertex = 1 << 1, /// /// Edges are selectable. /// Edge = 1 << 2, /// /// Faces are selectable. /// Face = 1 << 3, /// /// Texture coordinates are selectable. /// TextureFace = 1 << 4, /// /// Texture coordinates are selectable. /// TextureEdge = 1 << 5, /// /// Texture coordinates are selectable. /// TextureVertex = 1 << 6, /// /// Other input tool (Poly Shape editor, Bezier editor, etc) /// InputTool = 1 << 7, /// /// Match any value. /// Any = 0xFFFF } /// /// Selectively rebuild and apply mesh attributes to the UnityEngine.Mesh asset. /// /// [System.Flags] public enum RefreshMask { /// /// Textures channel will be rebuilt. /// UV = 0x1, /// /// Colors will be rebuilt. /// Colors = 0x2, /// /// Normals will be recalculated and applied. /// Normals = 0x4, /// /// Tangents will be recalculated and applied. /// Tangents = 0x8, /// /// Re-assign the MeshCollider sharedMesh. /// Collisions = 0x10, /// /// Refresh all optional mesh attributes. /// All = UV | Colors | Normals | Tangents | Collisions }; const string k_ProBuilderName = "UnityEditor.ProBuilder.ProBuilderEditor, Unity.ProBuilder.Editor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; const string k_ProBuilderMeshName = "UnityEngine.ProBuilder.ProBuilderMesh, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; const string k_RefreshMaskName = "UnityEngine.ProBuilder.RefreshMask, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; const string k_EditorUtilityName = "UnityEditor.ProBuilder.EditorMeshUtility, Unity.ProBuilder.Editor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; static Type s_ProBuilderType = null; static Type s_ProBuilderMeshType = null; static Type s_RefreshMaskType = null; static Type s_EditorUtilityType = null; static MethodInfo m_PolybrushOnSelectModeListenerMethodInfo; static MethodInfo m_ProBuilderRefreshMethodInfo = null; static EventInfo m_ProBuilderOnSelectModeChanged = null; static PropertyInfo m_ProBuilderSelectModePropertyInfo = null; static MethodInfo m_ProBuilderMeshToMeshMethodInfo = null; static MethodInfo m_ProBuilderMeshRefreshMethodInfo = null; static MethodInfo m_ProBuilderMeshOptimizeMethodInfo = null; static MethodInfo m_ProBuilderMeshSetUVMethodInfo = null; static PropertyInfo m_ProBuilderMeshPositionsPropertyInfo = null; static PropertyInfo m_ProBuilderMeshTangentsPropertyInfo = null; static PropertyInfo m_ProBuilderMeshColorsPropertyInfo = null; static PropertyInfo m_ProBuilderMeshVertexCountPropertyInfo = null; internal static bool ProBuilderExists() { if (GetProBuilderType() != null && GetProBuilderMeshType() != null && GetRefreshMaskType() != null && GetEditorUtilityType() != null) return true; return false; } static Type GetProBuilderType() { if (s_ProBuilderType == null) s_ProBuilderType = Type.GetType(k_ProBuilderName); return s_ProBuilderType; } static Type GetProBuilderMeshType() { try { if (s_ProBuilderMeshType == null) s_ProBuilderMeshType = Type.GetType(k_ProBuilderMeshName); } catch (Exception e) { Debug.Log(e.Message); } return s_ProBuilderMeshType; } static Type GetRefreshMaskType() { if (s_RefreshMaskType == null) s_RefreshMaskType = Type.GetType(k_RefreshMaskName); return s_RefreshMaskType; } static Type GetEditorUtilityType() { if (s_EditorUtilityType == null) s_EditorUtilityType = Type.GetType(k_EditorUtilityName); return s_EditorUtilityType; } /// /// Get ProBuilderMesh component from given GameObject. /// /// /// internal static UnityEngine.Object GetProBuilderComponent(GameObject obj) { return obj.GetComponent(GetProBuilderMeshType()); } static MethodInfo PolybrushOnSelectModeListenerMethodInfo { get { if (m_PolybrushOnSelectModeListenerMethodInfo == null) { m_PolybrushOnSelectModeListenerMethodInfo = typeof(PolybrushEditor).GetMethod("OnProBuilderSelectModeChanged", BindingFlags.NonPublic | BindingFlags.Instance); } return m_PolybrushOnSelectModeListenerMethodInfo; } } static MethodInfo ProBuilderRefreshMethodInfo { get { if (m_ProBuilderRefreshMethodInfo == null) { m_ProBuilderRefreshMethodInfo = GetProBuilderType().GetMethod("Refresh", BindingFlags.Public | BindingFlags.Static, null, new Type[] {typeof(bool)}, null); } return m_ProBuilderRefreshMethodInfo; } } static EventInfo ProBuilderOnSelectModeChanged { get { if (m_ProBuilderOnSelectModeChanged == null) { m_ProBuilderOnSelectModeChanged = GetProBuilderType().GetEvent("selectModeChanged", BindingFlags.Public | BindingFlags.Static); } return m_ProBuilderOnSelectModeChanged; } } static PropertyInfo ProBuilderSelectModePropertyInfo { get { if (m_ProBuilderSelectModePropertyInfo == null) { m_ProBuilderSelectModePropertyInfo = GetProBuilderType().GetProperty("selectMode", BindingFlags.Public | BindingFlags.Static); } return m_ProBuilderSelectModePropertyInfo; } } static MethodInfo ProBuilderMeshToMeshMethodInfo { get { if (m_ProBuilderMeshToMeshMethodInfo == null) { m_ProBuilderMeshToMeshMethodInfo = GetProBuilderMeshType().GetMethod("ToMesh", BindingFlags.Public | BindingFlags.Instance, null, new Type[] {typeof(MeshTopology)}, null); } return m_ProBuilderMeshToMeshMethodInfo; } } static MethodInfo ProBuilderMeshRefreshMethodInfo { get { if (m_ProBuilderMeshRefreshMethodInfo == null) { m_ProBuilderMeshRefreshMethodInfo = GetProBuilderMeshType().GetMethod("Refresh", BindingFlags.Public | BindingFlags.Instance, null, new Type[] {GetRefreshMaskType()}, null); } return m_ProBuilderMeshRefreshMethodInfo; } } static MethodInfo ProBuilderMeshOptimizeMethodInfo { get { if (m_ProBuilderMeshOptimizeMethodInfo == null) { m_ProBuilderMeshOptimizeMethodInfo = GetEditorUtilityType().GetMethod("Optimize", BindingFlags.Public | BindingFlags.Static, null, new Type[] {GetProBuilderMeshType(), typeof(bool)}, null); } return m_ProBuilderMeshOptimizeMethodInfo; } } static MethodInfo ProBuilderMeshSetUVMethodInfo { get { if (m_ProBuilderMeshSetUVMethodInfo == null) { m_ProBuilderMeshSetUVMethodInfo = GetProBuilderMeshType().GetMethod("SetUVs", BindingFlags.Public | BindingFlags.Instance, null, new Type[] {typeof(int), typeof(List)}, null); } return m_ProBuilderMeshSetUVMethodInfo; } } static PropertyInfo ProBuilderMeshPositionsPropertyInfo { get { if (m_ProBuilderMeshPositionsPropertyInfo == null) { m_ProBuilderMeshPositionsPropertyInfo = GetProBuilderMeshType().GetProperty("positions", BindingFlags.Instance | BindingFlags.Public); } return m_ProBuilderMeshPositionsPropertyInfo; } } static PropertyInfo ProBuilderMeshTangentsPropertyInfo { get { if (m_ProBuilderMeshTangentsPropertyInfo == null) { m_ProBuilderMeshTangentsPropertyInfo = GetProBuilderMeshType().GetProperty("tangents", BindingFlags.Instance | BindingFlags.Public); } return m_ProBuilderMeshTangentsPropertyInfo; } } static PropertyInfo ProBuilderMeshColorsPropertyInfo { get { if (m_ProBuilderMeshColorsPropertyInfo == null) { m_ProBuilderMeshColorsPropertyInfo = GetProBuilderMeshType().GetProperty("colors", BindingFlags.Instance | BindingFlags.Public); } return m_ProBuilderMeshColorsPropertyInfo; } } static PropertyInfo ProBuilderMeshVertexCountPropertyInfo { get { if (m_ProBuilderMeshVertexCountPropertyInfo == null) { m_ProBuilderMeshVertexCountPropertyInfo = GetProBuilderMeshType().GetProperty("vertexCount", BindingFlags.Instance | BindingFlags.Public); } return m_ProBuilderMeshVertexCountPropertyInfo; } } /// /// Return true if given GameObject has a ProBuilderMesh component. /// /// /// internal static bool IsValidProBuilderMesh(GameObject obj) { if (ProBuilderExists()) { Component comp = obj.GetComponent(GetProBuilderMeshType()); return comp != null; } return false; } /// /// Call ProBuilderEditor.Refresh(). /// /// internal static void RefreshEditor(bool vertexCountChanged) { if (ProBuilderRefreshMethodInfo == null) { Debug.LogWarning( "ProBuilderBridge.RefreshEditor() failed to find an appropriate `Refresh` method on `ProBuilderEditor` type"); return; } ProBuilderRefreshMethodInfo.Invoke(null, new object[] {vertexCountChanged}); } /// /// Set ProBuilderEditor.selectMode. /// /// internal static void SetSelectMode(SelectMode mode) { if (ProBuilderSelectModePropertyInfo == null) { Debug.LogWarning( "ProBuilderBridge.ProBuilderSelectMode() failed to find an appropriate `selectMode` property on `ProBuilder` type"); return; } ProBuilderSelectModePropertyInfo.SetValue(null, (int) mode); } /// /// Return ProBuilderMesh.vertexCount value. /// /// GameObject with instance of ProBuilderMesh. /// internal static int GetVertexCount(GameObject obj) { if (ProBuilderMeshVertexCountPropertyInfo == null) { Debug.LogWarning( "ProBuilderBridge.ProBuilderPosition() failed to find an appropriate `tangents` property on `ProBuilderMesh` type"); return 0; } object comp = obj.GetComponent(GetProBuilderMeshType()); return (int) ProBuilderMeshVertexCountPropertyInfo.GetValue(comp); } /// /// Assign vertices information to ProBuilderMesh.position property. /// /// GameObject with instance of ProBuilderMesh. /// internal static void SetPositions(GameObject obj, IList position) { if (ProBuilderMeshPositionsPropertyInfo == null) { Debug.LogWarning( "ProBuilderBridge.ProBuilderPosition() failed to find an appropriate `positions` property on `ProBuilderMesh` type"); return; } object comp = obj.GetComponent(GetProBuilderMeshType()); ProBuilderMeshPositionsPropertyInfo.SetValue(comp, position); } /// /// Assign tangents information to ProBuilderMesh.tangents property. /// /// GameObject with instance of ProBuilderMesh. /// internal static void SetTangents(GameObject obj, IList tangents) { if (ProBuilderMeshTangentsPropertyInfo == null) { Debug.LogWarning( "ProBuilderBridge.ProBuilderPosition() failed to find an appropriate `tangents` property on `ProBuilderMesh` type"); return; } object comp = obj.GetComponent(GetProBuilderMeshType()); ProBuilderMeshTangentsPropertyInfo.SetValue(comp, tangents); } /// /// Assign colors information to ProBuilderMesh.colors property. /// /// GameObject with instance of ProBuilderMesh. /// internal static void SetColors(GameObject obj, IList colors) { if (ProBuilderMeshColorsPropertyInfo == null) { Debug.LogWarning( "ProBuilderBridge.ProBuilderColors() failed to find an appropriate `colors` property on `ProBuilderMesh` type"); return; } object comp = obj.GetComponent(GetProBuilderMeshType()); ProBuilderMeshColorsPropertyInfo.SetValue(comp, colors); } /// /// Call ProBuilderMesh.SetUVs(). /// /// GameObject with instance of ProBuilderMesh. /// /// internal static void SetUVs(GameObject obj, int index, List uv) { if (ProBuilderMeshSetUVMethodInfo == null) { Debug.LogWarning( "ProBuilderBridge.ProBuilderMeshSetUV() failed to find an appropriate `SetUV` method on `ProBuilderMesh` type"); return; } object comp = obj.GetComponent(GetProBuilderMeshType()); ProBuilderMeshSetUVMethodInfo.Invoke(comp, new object[] {index, uv}); } /// /// Call ProBuilder.ToMesh(). /// /// GameObject with instance of ProBuilderMesh. internal static void ToMesh(GameObject obj) { if (ProBuilderMeshToMeshMethodInfo == null) { Debug.LogWarning( "ProBuilderBridge.ToMesh() failed to find an appropriate `ToMesh` method on `ProBuilderMesh` type"); return; } object comp = obj.GetComponent(GetProBuilderMeshType()); ProBuilderMeshToMeshMethodInfo.Invoke(comp, new object[] {MeshTopology.Triangles}); } /// /// Call ProBuilderMesh.Refresh(). /// /// GameObject with instance of ProBuilderMesh. /// internal static void Refresh(GameObject obj, RefreshMask mask = RefreshMask.All) { if (ProBuilderMeshRefreshMethodInfo == null) { Debug.LogWarning( "ProBuilderBridge.Refresh() failed to find an appropriate `Refresh` method on `ProBuilderMesh` type"); return; } obj.GetComponent(GetProBuilderMeshType()); object comp = obj.GetComponent(GetProBuilderMeshType()); ProBuilderMeshRefreshMethodInfo.Invoke(comp, new object[] {(int) mask}); } /// /// Call ProBuilderMesh.Optimize(). /// /// GameObject with instance of ProBuilderMesh. internal static void Optimize(GameObject obj) { if (ProBuilderMeshOptimizeMethodInfo == null) { Debug.LogWarning( "ProBuilderBridge.Optimize() failed to find an appropriate `Optimize` method on `ProBuilderMesh` type"); return; } object comp = obj.GetComponent(GetProBuilderMeshType()); ProBuilderMeshOptimizeMethodInfo.Invoke(null, new object[]{comp, false}); } /// /// Subscribe to ProBuilderEditor.selectModeChanged event. /// /// internal static void SubscribeToSelectModeChanged(Action listener) { if (ProBuilderOnSelectModeChanged == null) { Debug.LogWarning( "ProBuilderBridge.ProBuilderSubscribeToSelectModeChanged() failed to find an appropriate `selectModeChanged` event on `ProBuilderEditor` type"); return; } Type tDelegate = ProBuilderOnSelectModeChanged.EventHandlerType; Delegate d = Delegate.CreateDelegate(tDelegate, PolybrushEditor.instance, PolybrushOnSelectModeListenerMethodInfo); MethodInfo addMethod = ProBuilderOnSelectModeChanged.GetAddMethod(); addMethod.Invoke(null, new object[] { d }); } /// /// Unsubscribe from ProBuilderEditor.selectModeChanged event. /// /// internal static void UnsubscribeToSelectModeChanged(Action listener) { if (ProBuilderOnSelectModeChanged == null) { Debug.LogWarning( "ProBuilderBridge.ProBuilderUnsubscribeToSelectModeChanged() failed to find an appropriate `selectModeChanged` event on `ProBuilderEditor` type"); return; } Type tDelegate = ProBuilderOnSelectModeChanged.EventHandlerType; Delegate d = Delegate.CreateDelegate(tDelegate, PolybrushEditor.instance, PolybrushOnSelectModeListenerMethodInfo); MethodInfo removeMethod = ProBuilderOnSelectModeChanged.GetRemoveMethod(); removeMethod.Invoke(null, new object[] { d }); } /// /// Tests utility class for ProBuilderBridge class. /// Primarily used in Tests Runner. /// internal class TestsUtility { /// /// Validate that all MethodInfo fields in ProBuilderBridge are defined /// when ProBuilder is imported in the project. /// If one is not defined, most likely ProBuilder API changed. /// internal static void ValidateIntegration() { ValidatePropertyInfo(ProBuilderMeshPositionsPropertyInfo); ValidatePropertyInfo(ProBuilderMeshTangentsPropertyInfo); ValidatePropertyInfo(ProBuilderMeshColorsPropertyInfo); ValidatePropertyInfo(ProBuilderMeshVertexCountPropertyInfo); ValidatePropertyInfo(ProBuilderSelectModePropertyInfo); ValidateMethodInfo(ProBuilderRefreshMethodInfo); ValidateMethodInfo(ProBuilderMeshToMeshMethodInfo); ValidateMethodInfo(ProBuilderMeshRefreshMethodInfo); ValidateMethodInfo(ProBuilderMeshOptimizeMethodInfo); ValidateMethodInfo(ProBuilderMeshSetUVMethodInfo); ValidateEventInfo(ProBuilderOnSelectModeChanged); } /// /// Validate Polybrush has defined a proper event listener to use with ProBuilder. /// It ensures that we don't rename our listener without updating the ProBuilder integration. /// internal static void ValidatePolybrushListeners() { ValidateMethodInfo(PolybrushOnSelectModeListenerMethodInfo); } static void ValidateMethodInfo(MethodInfo method) { if (method == null) { throw new NullReferenceException(string.Format("{0} not found.", method.Name)); } } static void ValidateEventInfo(EventInfo evt) { if (evt == null) { throw new NullReferenceException(string.Format("{0} not found.", evt.Name)); } } static void ValidatePropertyInfo(PropertyInfo property) { if (property == null) { throw new NullReferenceException(string.Format("{0} not found.", property.Name)); } } } } }