You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
368 lines
13 KiB
C#
368 lines
13 KiB
C#
|
3 years ago
|
using UnityEngine;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Reflection;
|
||
|
|
using System.IO;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using UnityEngine.Polybrush;
|
||
|
|
using Polybrush;
|
||
|
|
|
||
|
|
namespace UnityEditor.Polybrush
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// Helper functions for editor
|
||
|
|
/// </summary>
|
||
|
|
static class PolyEditorUtility
|
||
|
|
{
|
||
|
|
const string k_PackageDirectory = "Packages/com.unity.polybrush";
|
||
|
|
const string k_UserAssetDirectory = "Assets/Polybrush Data/";
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// True if this gameObject is in the Selection.gameObjects
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="gameObject"></param>
|
||
|
|
/// <returns></returns>
|
||
|
|
internal static bool InSelection(GameObject gameObject)
|
||
|
|
{
|
||
|
|
//null check
|
||
|
|
if(gameObject == null)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
Transform[] selectedTransform = Selection.GetTransforms(SelectionMode.Unfiltered);
|
||
|
|
|
||
|
|
return selectedTransform.Contains<Transform>(gameObject.transform);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// GetMeshGUID version without the ref parameter
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="mesh">mesh source</param>
|
||
|
|
/// <returns>ModelSource category</returns>
|
||
|
|
internal static ModelSource GetMeshGUID(Mesh mesh)
|
||
|
|
{
|
||
|
|
string temp = string.Empty;
|
||
|
|
return GetMeshGUID(mesh, ref temp);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Return the mesh source, and the guid if applicable (scene instances don't get GUIDs).
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="mesh">mesh source</param>
|
||
|
|
/// <param name="guid">guid returned</param>
|
||
|
|
/// <returns>ModelSource category</returns>
|
||
|
|
internal static ModelSource GetMeshGUID(Mesh mesh, ref string guid)
|
||
|
|
{
|
||
|
|
if(mesh == null)
|
||
|
|
{
|
||
|
|
return ModelSource.Error;
|
||
|
|
}
|
||
|
|
string path = AssetDatabase.GetAssetPath(mesh);
|
||
|
|
|
||
|
|
if(path != "")
|
||
|
|
{
|
||
|
|
AssetImporter assetImporter = AssetImporter.GetAtPath(path);
|
||
|
|
|
||
|
|
if( assetImporter != null )
|
||
|
|
{
|
||
|
|
// Only imported model (e.g. FBX) assets use the ModelImporter,
|
||
|
|
// where a saved asset will have an AssetImporter but *not* ModelImporter.
|
||
|
|
// A procedural mesh (one only existing in a scene) will not have any.
|
||
|
|
if (assetImporter is ModelImporter)
|
||
|
|
{
|
||
|
|
guid = AssetDatabase.AssetPathToGUID(path);
|
||
|
|
return ModelSource.Imported;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
guid = AssetDatabase.AssetPathToGUID(path);
|
||
|
|
return ModelSource.Asset;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return ModelSource.Scene;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return ModelSource.Scene;
|
||
|
|
}
|
||
|
|
|
||
|
|
internal const int DIALOG_OK = 0;
|
||
|
|
internal const int DIALOG_CANCEL = 1;
|
||
|
|
internal const int DIALOG_ALT = 2;
|
||
|
|
internal const string DO_NOT_SAVE = "DO_NOT_SAVE";
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Save any modifications to the EditableObject. If the mesh is a scene mesh or imported mesh, it
|
||
|
|
/// will be saved to a new asset. If the mesh was originally an asset mesh, the asset is overwritten.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="mesh">mesh to save</param>
|
||
|
|
/// <param name="meshFilter">will update the mesh filter with the new mesh if not null</param>
|
||
|
|
/// <param name="skinnedMeshRenderer">will update the skinned mesh renderer with the new mesh if not null</param>
|
||
|
|
/// <returns>return true if save was successful, false if user-canceled or otherwise failed.</returns>
|
||
|
|
internal static bool SaveMeshAsset(Mesh mesh, MeshFilter meshFilter = null, SkinnedMeshRenderer skinnedMeshRenderer = null, int overridenDialogResult = -1, string overridenPath = "")
|
||
|
|
{
|
||
|
|
if (mesh == null) return false;
|
||
|
|
|
||
|
|
string save_path = !string.IsNullOrEmpty(overridenPath) ? overridenPath : DO_NOT_SAVE;
|
||
|
|
|
||
|
|
ModelSource source = GetMeshGUID(mesh);
|
||
|
|
|
||
|
|
switch (source)
|
||
|
|
{
|
||
|
|
case ModelSource.Asset:
|
||
|
|
|
||
|
|
int saveChanges = overridenDialogResult != -1
|
||
|
|
? overridenDialogResult
|
||
|
|
: EditorUtility.DisplayDialogComplex(
|
||
|
|
"Save Changes",
|
||
|
|
"Save changes to edited mesh?",
|
||
|
|
"Save", // DIALOG_OK
|
||
|
|
"Cancel", // DIALOG_CANCEL
|
||
|
|
"Save As"); // DIALOG_ALT
|
||
|
|
|
||
|
|
if(saveChanges == DIALOG_OK)
|
||
|
|
save_path = AssetDatabase.GetAssetPath(mesh);
|
||
|
|
else if(saveChanges == DIALOG_ALT)
|
||
|
|
save_path = EditorUtility.SaveFilePanelInProject("Save Mesh As", mesh.name + ".asset", "asset", "Save edited mesh to");
|
||
|
|
else
|
||
|
|
return false;
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ModelSource.Imported:
|
||
|
|
case ModelSource.Scene:
|
||
|
|
default:
|
||
|
|
save_path = EditorUtility.SaveFilePanelInProject("Save Mesh As", mesh.name + ".asset", "asset", "Save edited mesh to");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(!save_path.Equals(DO_NOT_SAVE) && !string.IsNullOrEmpty(save_path))
|
||
|
|
{
|
||
|
|
Mesh existing = AssetDatabase.LoadAssetAtPath<Mesh>(save_path);
|
||
|
|
bool overwrite = existing != null;
|
||
|
|
Mesh dst = overwrite ? existing : new Mesh();
|
||
|
|
PolyMeshUtility.Copy(mesh, dst);
|
||
|
|
if(!overwrite)
|
||
|
|
AssetDatabase.CreateAsset(dst, save_path);
|
||
|
|
AssetDatabase.Refresh();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Save was canceled
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Load an icon
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="icon">location of the icon</param>
|
||
|
|
/// <returns>The loaded icon</returns>
|
||
|
|
internal static Texture2D LoadIcon(string icon)
|
||
|
|
{
|
||
|
|
MethodInfo loadIconMethod = typeof(EditorGUIUtility).GetMethod("LoadIcon", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
|
||
|
|
Texture2D img = (Texture2D) loadIconMethod.Invoke(null, new object[] { icon } );
|
||
|
|
return img;
|
||
|
|
}
|
||
|
|
|
||
|
|
internal static string RootFolder
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return k_PackageDirectory;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
internal static string UserAssetDirectory
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return k_UserAssetDirectory;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
internal static T LoadDefaultAsset<T>(string path) where T : UnityEngine.Object
|
||
|
|
{
|
||
|
|
return AssetDatabase.LoadAssetAtPath<T>(k_PackageDirectory + "/Content/" + path);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Fetch a default asset from path relative to the product folder. If not found, a new one is created.
|
||
|
|
/// </summary>
|
||
|
|
/// <typeparam name="T">>the type to retrieve, must inherit from ScriptableObject and implement IHasDefault and ICustomSettings</typeparam>
|
||
|
|
/// <returns>The first asset of the type T found inside the project (order set by AssetDataBase.FindAssets function. If none returns a new object T with default values set by IHasDefault.</returns>
|
||
|
|
internal static T GetFirstOrNew<T>() where T : ScriptableObject, IHasDefault, ICustomSettings
|
||
|
|
{
|
||
|
|
string[] all = AssetDatabase.FindAssets("t:" + typeof(T));
|
||
|
|
|
||
|
|
T asset = all.Length > 0 ? AssetDatabase.LoadAssetAtPath<T>(AssetDatabase.GUIDToAssetPath(all.First())) : null;
|
||
|
|
|
||
|
|
if(asset == null)
|
||
|
|
{
|
||
|
|
return CreateNew<T>();
|
||
|
|
}
|
||
|
|
|
||
|
|
return asset;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Create a new asset at path relative to the product folder.
|
||
|
|
/// </summary>
|
||
|
|
/// <typeparam name="T">>the type to create, must inherit from ScriptableObject and implement IHasDefault and ICustomSettings</typeparam>
|
||
|
|
/// <returns>A new object T with default values set by IHasDefault.</returns>
|
||
|
|
internal static T CreateNew<T>() where T : ScriptableObject, IHasDefault, ICustomSettings
|
||
|
|
{
|
||
|
|
T asset = ScriptableObject.CreateInstance<T>();
|
||
|
|
asset.SetDefaultValues();
|
||
|
|
EditorUtility.SetDirty(asset);
|
||
|
|
|
||
|
|
string folder = UserAssetDirectory;
|
||
|
|
|
||
|
|
if (!Directory.Exists(folder))
|
||
|
|
Directory.CreateDirectory(folder);
|
||
|
|
|
||
|
|
string subfolder = folder + asset.assetsFolder;
|
||
|
|
if (!Directory.Exists(subfolder))
|
||
|
|
Directory.CreateDirectory(subfolder);
|
||
|
|
|
||
|
|
string assetPath = AssetDatabase.GenerateUniqueAssetPath(subfolder + typeof(T).Name + "-Default.asset");
|
||
|
|
|
||
|
|
if (string.IsNullOrEmpty(assetPath))
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
AssetDatabase.CreateAsset(asset, assetPath);
|
||
|
|
AssetDatabase.Refresh();
|
||
|
|
|
||
|
|
return asset;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Fetch all assets of type `T`
|
||
|
|
/// </summary>
|
||
|
|
/// <typeparam name="T">the type to retrieve, must inherit from ScriptableObject and implement IHasDefault and ICustomSettings</typeparam>
|
||
|
|
/// <returns>All the assets of that type on the project</returns>
|
||
|
|
internal static List<T> GetAll<T>() where T: ScriptableObject, IHasDefault, ICustomSettings
|
||
|
|
{
|
||
|
|
var tGuids = AssetDatabase.FindAssets("t:" + typeof(T));
|
||
|
|
var Ts = new List<T>();
|
||
|
|
foreach (var guid in tGuids)
|
||
|
|
Ts.Add(AssetDatabase.LoadAssetAtPath<T>(AssetDatabase.GUIDToAssetPath(guid)));
|
||
|
|
return Ts;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Set the selected render state for an object. In Unity 5.4 and lower, this just toggles wireframe on or off.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="renderer">Renderer to change selection state</param>
|
||
|
|
/// <param name="state">State to be set</param>
|
||
|
|
internal static void SetSelectionRenderState(Renderer renderer, SelectionRenderState state)
|
||
|
|
{
|
||
|
|
#if UNITY_5_3 || UNITY_5_4
|
||
|
|
EditorUtility.SetSelectedWireframeHidden(renderer, state == 0);
|
||
|
|
#else
|
||
|
|
EditorUtility.SetSelectedRenderState(renderer, (EditorSelectedRenderState) state );
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
internal static SelectionRenderState GetSelectionRenderState()
|
||
|
|
{
|
||
|
|
|
||
|
|
#if UNITY_5_3 || UNITY_5_4
|
||
|
|
|
||
|
|
return SelectionRenderState.Wireframe;
|
||
|
|
|
||
|
|
#else
|
||
|
|
|
||
|
|
bool wireframe = false, outline = false;
|
||
|
|
|
||
|
|
try {
|
||
|
|
wireframe = (bool) ReflectionUtility.GetValue(null, "UnityEditor.AnnotationUtility", "showSelectionWire");
|
||
|
|
outline = (bool) ReflectionUtility.GetValue(null, "UnityEditor.AnnotationUtility", "showSelectionOutline");
|
||
|
|
} catch {
|
||
|
|
Debug.LogWarning("Looks like Unity changed the AnnotationUtility \"showSelectionOutline\"\nPlease email contact@procore3d.com and let Karl know!");
|
||
|
|
}
|
||
|
|
|
||
|
|
SelectionRenderState state = SelectionRenderState.None;
|
||
|
|
|
||
|
|
if(wireframe) state |= SelectionRenderState.Wireframe;
|
||
|
|
if(outline) state |= SelectionRenderState.Outline;
|
||
|
|
|
||
|
|
return state;
|
||
|
|
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns true if this object is a prefab in the Project view.
|
||
|
|
*/
|
||
|
|
internal static bool IsPrefabAsset(Object go)
|
||
|
|
{
|
||
|
|
#if UNITY_2018_3_OR_NEWER
|
||
|
|
return PrefabUtility.IsPartOfPrefabAsset(go);
|
||
|
|
#else
|
||
|
|
return PrefabUtility.GetPrefabType(go) == PrefabType.Prefab;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns true if the given array contains at least one prefab.
|
||
|
|
*/
|
||
|
|
internal static bool ContainsPrefabAssets(UnityEngine.Object[] objects)
|
||
|
|
{
|
||
|
|
for (int i = 0; i < objects.Length; ++i)
|
||
|
|
{
|
||
|
|
UnityEngine.Object obj = objects[i];
|
||
|
|
if (PolyEditorUtility.IsPrefabAsset(obj))
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Returns true if GameObject has a PolybrushMesh component.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="gameObject"></param>
|
||
|
|
/// <returns></returns>
|
||
|
|
internal static bool IsPolybrushObject(GameObject gameObject)
|
||
|
|
{
|
||
|
|
return gameObject.GetComponent<PolybrushMesh>() != null;
|
||
|
|
}
|
||
|
|
|
||
|
|
#pragma warning disable 612
|
||
|
|
/// <summary>
|
||
|
|
/// Utility to help convert objects modified with the Beta version of Polybrush (Asset Store)
|
||
|
|
/// to Polybrus 1.x.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="component"></param>
|
||
|
|
internal static PolybrushMesh ConvertGameObjectToNewFormat(z_AdditionalVertexStreams component)
|
||
|
|
{
|
||
|
|
GameObject go = component.gameObject;
|
||
|
|
PolybrushMesh newComponent = go.GetComponent<PolybrushMesh>();
|
||
|
|
MeshFilter mf = go.GetComponent<MeshFilter>();
|
||
|
|
Mesh mesh = component.m_AdditionalVertexStreamMesh;
|
||
|
|
|
||
|
|
Undo.DestroyObjectImmediate(component);
|
||
|
|
|
||
|
|
// Cancel conversion if no mesh if found on Z_AdditionalVertexStreams
|
||
|
|
if (mesh == null)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
if (newComponent == null)
|
||
|
|
{
|
||
|
|
newComponent = Undo.AddComponent<PolybrushMesh>(go);
|
||
|
|
newComponent.Initialize();
|
||
|
|
}
|
||
|
|
|
||
|
|
newComponent.mode = PolybrushMesh.Mode.AdditionalVertexStream;
|
||
|
|
newComponent.SetMesh(PolyMeshUtility.DeepCopy(mf.sharedMesh));
|
||
|
|
newComponent.SetAdditionalVertexStreams(mesh);
|
||
|
|
|
||
|
|
return newComponent;
|
||
|
|
}
|
||
|
|
#pragma warning restore 612
|
||
|
|
}
|
||
|
|
}
|