squash commits
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7471e7d67ca7e7498661f25021c4160
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,367 @@
|
||||
#if UMA_ADDRESSABLES
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UMA.CharacterSystem;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace UMA
|
||||
{
|
||||
public class SingleGroupGenerator : IUMAAddressablePlugin
|
||||
{
|
||||
public bool ClearMaterials = false; // should be set when generating during the build process, so the materials are cleared in the bundles
|
||||
public UMAAssetIndexer Index;
|
||||
List<UMAPackedRecipeBase> Recipes;
|
||||
Dictionary<AssetItem, List<string>> AddressableItems = new Dictionary<AssetItem, List<string>>();
|
||||
Dictionary<string, List<string>> RecipeExtraLabels = new Dictionary<string, List<string>>();
|
||||
|
||||
const string SharedGroupName = "UMA_SharedItems";
|
||||
|
||||
public string Menu
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Generate Single group (fast)";
|
||||
}
|
||||
}
|
||||
|
||||
public void LogText(string text)
|
||||
{
|
||||
#if SUPER_LOGGING
|
||||
string filePath = System.IO.Path.Combine(Application.dataPath, "Generatelog.txt");
|
||||
System.IO.File.AppendAllText(filePath, text+Environment.NewLine);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Complete()
|
||||
{
|
||||
// if the preference is turned on, then force the build flag to clear materials
|
||||
bool stripUmaMaterials = UMAEditorUtilities.StripUMAMaterials();
|
||||
if (stripUmaMaterials)
|
||||
ClearMaterials = true;
|
||||
|
||||
try
|
||||
{
|
||||
LogText("");
|
||||
LogText("****************************************************");
|
||||
LogText("Generating from recipes: " + DateTime.Now.ToString());
|
||||
LogText("****************************************************");
|
||||
LogText("");
|
||||
bool IncludeRecipes = UMAEditorUtilities.GetConfigValue(UMAEditorUtilities.ConfigToggle_IncludeRecipes, false);
|
||||
bool IncludeOthers = UMAEditorUtilities.GetConfigValue(UMAEditorUtilities.ConfigToggle_IncludeOther, false);
|
||||
string DefaultAddressableLabel = UMAEditorUtilities.GetDefaultAddressableLabel();
|
||||
|
||||
RecipeExtraLabels = new Dictionary<string, List<string>>();
|
||||
|
||||
var WardrobeCollections = UMAAssetIndexer.Instance.GetAllAssets<UMAWardrobeCollection>();
|
||||
foreach (var wc in WardrobeCollections)
|
||||
{
|
||||
if (wc == null) continue;
|
||||
string label = wc.AssignedLabel;
|
||||
List<string> recipes = wc.wardrobeCollection.GetAllRecipeNamesInCollection();
|
||||
foreach (string recipe in recipes)
|
||||
{
|
||||
if (RecipeExtraLabels.ContainsKey(recipe) == false)
|
||||
{
|
||||
RecipeExtraLabels.Add(recipe, new List<string>());
|
||||
}
|
||||
RecipeExtraLabels[recipe].Add(label);
|
||||
}
|
||||
}
|
||||
|
||||
float pos = 0.0f;
|
||||
float inc = 1.0f / Recipes.Count;
|
||||
foreach (UMAPackedRecipeBase uwr in Recipes)
|
||||
{
|
||||
List<string> ExtraLabels = new List<string>();
|
||||
|
||||
if (RecipeExtraLabels.ContainsKey(uwr.name))
|
||||
{
|
||||
ExtraLabels = RecipeExtraLabels[uwr.name];
|
||||
}
|
||||
|
||||
LogText("");
|
||||
LogText("Processing recipe: " + uwr.name + " Label: " + uwr.AssignedLabel);
|
||||
|
||||
EditorUtility.DisplayProgressBar("Generating", "processing recipe: " + uwr.name , pos);
|
||||
|
||||
|
||||
IUMAIndexOptions options = uwr as IUMAIndexOptions;
|
||||
if (options != null && options.LabelLocalFiles)
|
||||
{
|
||||
// Get the asset items for the recipe from the local directory, not the index
|
||||
// if it doesn't exist in the local directory, then get it from the index
|
||||
List<AssetItem> items = UMAAssetIndexer.Instance.GetAssetItems(uwr, false);
|
||||
foreach (AssetItem ai in items)
|
||||
{
|
||||
// Local items do not get default labels.
|
||||
// instead, they only get the label of the wardrobe recipe they are in.
|
||||
string label = uwr.AssignedLabel;
|
||||
// get the recipe path.
|
||||
// search for the asset in the recipe path, including children.
|
||||
// if it exists, then add the label to the asset.
|
||||
|
||||
string path = AssetDatabase.GetAssetPath(ai.Item.GetInstanceID());
|
||||
string filename = System.IO.Path.GetFileName(path);
|
||||
string basePath = System.IO.Path.GetDirectoryName(path);
|
||||
|
||||
Debug.Log("Looking for asset: " + filename + " in path: " + basePath);
|
||||
AssetItem ai2 = GetLocalAssetItemIfExist(basePath, filename, ai._Type.Name, ai);
|
||||
|
||||
if (ai._SerializedItem.GetInstanceID() != ai2._SerializedItem.GetInstanceID())
|
||||
{
|
||||
AddressableItems[ai].Add(uwr.AssignedLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the asset items for the recipe from the index
|
||||
List<AssetItem> items = Index.GetAssetItems(uwr, true);
|
||||
foreach (AssetItem ai in items)
|
||||
{
|
||||
if (AddressableItems.ContainsKey(ai) == false)
|
||||
{
|
||||
AddressableItems.Add(ai, new List<string>());
|
||||
AddressableItems[ai].Add(DefaultAddressableLabel);
|
||||
}
|
||||
AddressableItems[ai].Add(uwr.AssignedLabel);
|
||||
AddressableItems[ai].AddRange(ExtraLabels);
|
||||
}
|
||||
}
|
||||
|
||||
if (IncludeRecipes)
|
||||
{
|
||||
AssetItem RecipeItem = UMAAssetIndexer.Instance.GetRecipeItem(uwr);
|
||||
if (AddressableItems.ContainsKey(RecipeItem) == false)
|
||||
{
|
||||
AddressableItems.Add(RecipeItem, new List<string>());
|
||||
AddressableItems[RecipeItem].Add(DefaultAddressableLabel);
|
||||
}
|
||||
AddressableItems[RecipeItem].Add(uwr.AssignedLabel);
|
||||
AddressableItems[RecipeItem].Add("UMA_Recipes");
|
||||
AddressableItems[RecipeItem].AddRange(ExtraLabels);
|
||||
}
|
||||
pos += inc;
|
||||
}
|
||||
|
||||
|
||||
if (IncludeOthers)
|
||||
{
|
||||
AddAssetItems(typeof(RaceData), DefaultAddressableLabel);
|
||||
AddAssetItems(typeof(RuntimeAnimatorController), DefaultAddressableLabel);
|
||||
AddAssetItems(typeof(TextAsset), DefaultAddressableLabel);
|
||||
AddAssetItems(typeof(DynamicUMADnaAsset), DefaultAddressableLabel);
|
||||
}
|
||||
|
||||
|
||||
// Create the shared group that has each item packed separately.
|
||||
AddressableAssetGroup sharedGroup = AddressableUtility.AddressableSettings.FindGroup(SharedGroupName);
|
||||
if (sharedGroup == null)
|
||||
{
|
||||
sharedGroup = AddressableUtility.AddressableSettings.CreateGroup(SharedGroupName, false, false, true,AddressableUtility.AddressableSettings.DefaultGroup.Schemas);
|
||||
sharedGroup.GetSchema<BundledAssetGroupSchema>().BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackSeparately;
|
||||
}
|
||||
|
||||
pos = 0.0f;
|
||||
inc = 1.0f / AddressableItems.Count;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (AssetItem ai in AddressableItems.Keys)
|
||||
{
|
||||
if (!ai.Item)
|
||||
{
|
||||
Debug.LogError($"Asset \"{ai._Name}\" of type \"{ai._Type}\" doesn't exist anymore - did it get deleted?");
|
||||
continue;
|
||||
}
|
||||
ai.IsAddressable = true;
|
||||
ai.AddressableAddress = ""; // let the system assign it if we are generating.
|
||||
ai.AddressableGroup = sharedGroup.name;
|
||||
EditorUtility.DisplayProgressBar("Generating", "Processing Asset: " + ai.Item.name, pos);
|
||||
|
||||
sb.Clear();
|
||||
foreach (string s in AddressableItems[ai])
|
||||
{
|
||||
sb.Append(s);
|
||||
sb.Append(';');
|
||||
}
|
||||
ai.AddressableLabels = sb.ToString();
|
||||
|
||||
bool found = AssetDatabase.TryGetGUIDAndLocalFileIdentifier(ai.Item.GetInstanceID(), out string itemGUID, out long localID);
|
||||
|
||||
UMAAddressablesSupport.Instance.AddItemToSharedGroup(itemGUID, ai.AddressableAddress, AddressableItems[ai], sharedGroup);
|
||||
|
||||
if (ai._Type == typeof(SlotDataAsset) && ClearMaterials)
|
||||
{
|
||||
SlotDataAsset sda = ai.Item as SlotDataAsset;
|
||||
if (sda == null)
|
||||
{
|
||||
Debug.Log("Invalid Slotdata in recipe: " + ai._Name + ". Skipping.");
|
||||
continue;
|
||||
}
|
||||
if (sda.material != null)
|
||||
{
|
||||
if (ClearMaterials)
|
||||
{
|
||||
sda.materialName = sda.material.name;
|
||||
sda.material = null;
|
||||
EditorUtility.SetDirty(sda);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sda.material == null)
|
||||
{
|
||||
sda.material = Index.GetAsset<UMAMaterial>(sda.materialName);
|
||||
EditorUtility.SetDirty(sda);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ai._Type == typeof(OverlayDataAsset))
|
||||
{
|
||||
OverlayDataAsset od = ai.Item as OverlayDataAsset;
|
||||
if (od == null)
|
||||
{
|
||||
Debug.Log("Invalid overlay in recipe: " + ai._Name + ". Skipping.");
|
||||
continue;
|
||||
}
|
||||
if (od.material != null)
|
||||
{
|
||||
if (ClearMaterials)
|
||||
{
|
||||
od.materialName = od.material.name;
|
||||
od.material = null;
|
||||
EditorUtility.SetDirty(od);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (od.material == null)
|
||||
{
|
||||
od.material = Index.GetAsset<UMAMaterial>(od.materialName);
|
||||
EditorUtility.SetDirty(od);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear out the shaders on the UMAMaterial
|
||||
if (ai.Item is UMAMaterial um)
|
||||
{
|
||||
if (um.material != null)
|
||||
{
|
||||
um.ShaderName = um.material.shader.name;
|
||||
um.MaterialName = um.material.name;
|
||||
um.material.shader = null;
|
||||
}
|
||||
}
|
||||
#if INCL_TEXTURE2D
|
||||
foreach (Texture tex in od.textureList)
|
||||
{
|
||||
if (tex == null) continue;
|
||||
if (tex as Texture2D == null)
|
||||
{
|
||||
Debug.Log("Texture is not Texture2D!!! " + tex.name);
|
||||
continue;
|
||||
}
|
||||
string path = AssetDatabase.GetAssetPath(tex.GetInstanceID());
|
||||
string Address = "Texture2D-" + tex.name + "-" + path.GetHashCode();
|
||||
|
||||
found = AssetDatabase.TryGetGUIDAndLocalFileIdentifier(tex.GetInstanceID(), out string texGUID, out long texlocalID);
|
||||
if (found)
|
||||
{
|
||||
UMAAddressablesSupport.Instance.AddItemToSharedGroup(texGUID, AssetItem.AddressableFolder + Address, AddressableItems[ai], sharedGroup);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
pos += inc;
|
||||
}
|
||||
|
||||
UMAAddressablesSupport.Instance.AssignAddressableInformation();
|
||||
|
||||
Type[] types = Index.GetTypes();
|
||||
|
||||
foreach (Type t in types)
|
||||
{
|
||||
UMAAddressablesSupport.Instance.ReleaseReferences(t);
|
||||
}
|
||||
|
||||
UMAAddressablesSupport.Instance.CleanupAddressables(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
UMAAssetIndexer.Instance.ForceSave();
|
||||
}
|
||||
}
|
||||
|
||||
AssetItem GetLocalAssetItemIfExist(string path, string name, string typename, AssetItem defaultItem)
|
||||
{
|
||||
// We need to use the EvilName of the asset item to get the correct asset item.
|
||||
string evilName = defaultItem.EvilName;
|
||||
|
||||
var guids = AssetDatabase.FindAssets("t:" + typename, new string[] { path });
|
||||
foreach (string guid in guids)
|
||||
{
|
||||
string p = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var o = AssetDatabase.LoadAssetAtPath(p, defaultItem._Type);
|
||||
if (o != null)
|
||||
{
|
||||
var thisEvilName = AssetItem.GetEvilName(o);
|
||||
if (thisEvilName == evilName)
|
||||
{
|
||||
Debug.Log("found local asset: " + p);
|
||||
AssetItem localItem = new AssetItem(defaultItem._Type, o.name, p, o);
|
||||
return localItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultItem;
|
||||
}
|
||||
|
||||
public bool Prepare()
|
||||
{
|
||||
|
||||
Index = UMAAssetIndexer.Instance;
|
||||
UMAAddressablesSupport.Instance.CleanupAddressables(false, true);
|
||||
foreach (Type t in Index.GetTypes())
|
||||
{
|
||||
UMAAddressablesSupport.Instance.ClearAddressableFlags(t);
|
||||
}
|
||||
|
||||
Recipes = new List<UMAPackedRecipeBase>();
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<string> ProcessItem(AssetItem ai)
|
||||
{
|
||||
// This generator does not process single items.
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the recipes. This generator simply accumulates the recipes for processing
|
||||
/// once all recipes are in the list.
|
||||
/// </summary>
|
||||
/// <param name="recipe"></param>
|
||||
public void ProcessRecipe(UMAPackedRecipeBase recipe)
|
||||
{
|
||||
if (recipe.resourcesOnly)
|
||||
return;
|
||||
Recipes.Add(recipe);
|
||||
}
|
||||
|
||||
private void AddAssetItems(Type t, string DefaultLabel)
|
||||
{
|
||||
List<AssetItem> Items = UMAAssetIndexer.Instance.GetAssetItems(t);
|
||||
foreach (AssetItem item in Items)
|
||||
{
|
||||
AddressableItems.Add(item, new List<string>());
|
||||
AddressableItems[item].Add(DefaultLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcc9a449047573e438b96e098446ce81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/AddressablePlugins/SingleGroupGenerator.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,102 @@
|
||||
using System.Collections.Generic;
|
||||
using UMA;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using UMA.Editors;
|
||||
|
||||
public class AssetIndexerFilterEditor : EditorWindow
|
||||
{
|
||||
[MenuItem("UMA/Global Library Filters", priority = 99)]
|
||||
public static AssetIndexerFilterEditor GetWindow()
|
||||
{
|
||||
var window = GetWindow<AssetIndexerFilterEditor>();
|
||||
|
||||
Texture icon = AssetDatabase.LoadAssetAtPath<Texture>("Assets/UMA/InternalDataStore/UMA32.png");
|
||||
window.titleContent = new GUIContent(UmaAboutWindow.umaVersion + " Global Library Fiilters", icon);
|
||||
window.minSize = new Vector2(800, 420);
|
||||
window.maxSize = new Vector2(800, 420);
|
||||
|
||||
window.Focus();
|
||||
return window;
|
||||
}
|
||||
|
||||
private string[] typeStrings = new string[0];
|
||||
private int currentPopupType = 0;
|
||||
private string currentFilter = "<Enter Filter Here>";
|
||||
private Vector2 currentFocus = Vector2.zero;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
var keys = UMAAssetIndexer.TypeFromString.Keys;
|
||||
|
||||
List<string> typekeys = new List<string>(keys.Distinct());
|
||||
|
||||
typekeys.Remove("AnimatorController");
|
||||
typekeys.Remove("AnimatorOverrideController");
|
||||
|
||||
typeStrings = typekeys.ToArray();
|
||||
}
|
||||
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
// Display instructions.
|
||||
GUILayout.Space(8f);
|
||||
EditorGUILayout.HelpBox("Select type and enter a filter below. If any filters are added for a type, only paths that contain the filter strings will be used to search for assets of that type.", MessageType.Info);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
currentPopupType = EditorGUILayout.Popup(currentPopupType, typeStrings, GUILayout.Width(100));
|
||||
currentFilter = EditorGUILayout.TextField(currentFilter, GUILayout.ExpandWidth(true));
|
||||
if (GUILayout.Button("Browse", GUILayout.Width(80)))
|
||||
{
|
||||
string newFilter = EditorUtility.OpenFolderPanel("Browse for folder", currentFilter, "");
|
||||
var index = newFilter.ToLower().IndexOf("/assets/");
|
||||
if (index > 0)
|
||||
{
|
||||
currentFilter = newFilter.Substring(index + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "You can only select folders in your current projects asset folder", "OK");
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Add",GUILayout.Width(80)))
|
||||
{
|
||||
string typeName = typeStrings[currentPopupType];
|
||||
UMAAssetIndexer.Instance.AddSearchFolder(typeName,currentFilter);
|
||||
UMAAssetIndexer.Instance.ForceSave();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
GUILayout.Space(8);
|
||||
|
||||
GUIHelper.BeginVerticalPadded();
|
||||
currentFocus = EditorGUILayout.BeginScrollView(currentFocus,GUILayout.Width(position.width-20), GUILayout.Height(position.height-120)); // Scroll //
|
||||
|
||||
string removeKey = "";
|
||||
string removePath = "";
|
||||
foreach (var filter in UMAAssetIndexer.Instance.TypeFolderSearch)
|
||||
{
|
||||
foreach (string s in filter.Value)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(filter.Key, EditorStyles.textField, GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField(s, EditorStyles.textField,GUILayout.ExpandWidth(true));
|
||||
if (GUILayout.Button("Remove",GUILayout.Width(80)))
|
||||
{
|
||||
removeKey = filter.Key;
|
||||
removePath = s;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndScrollView();
|
||||
GUIHelper.EndVerticalPadded();
|
||||
if (!string.IsNullOrEmpty(removeKey))
|
||||
{
|
||||
UMAAssetIndexer.Instance.RemoveSearchFolder(removeKey, removePath);
|
||||
UMAAssetIndexer.Instance.ForceSave();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6c5787b75a650e42980a2ed0e0c71f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/AssetIndexerFilterEditor.cs
|
||||
uploadId: 679826
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 950f86caf7a864c4eb2ff2161f18a287
|
||||
timeCreated: 1467106475
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/AssetIndexerWindow.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
|
||||
namespace UMA.Controls
|
||||
{
|
||||
public enum Amount
|
||||
{
|
||||
NotSet = -1,
|
||||
None,
|
||||
All,
|
||||
Mixed
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class AssetTreeElement : TreeElement
|
||||
{
|
||||
|
||||
// parent item
|
||||
public Amount AmountChecked;
|
||||
public System.Type type;
|
||||
public int index;
|
||||
public int IsResourceCount;
|
||||
public int IsAddrCount;
|
||||
public int Keepcount;
|
||||
public int IgnoreCount;
|
||||
public int totalCount;
|
||||
|
||||
|
||||
// detail item
|
||||
public AssetItem ai;
|
||||
public bool Checked;
|
||||
|
||||
public void SetAmountChecked(Amount val)
|
||||
{
|
||||
AmountChecked = val;
|
||||
}
|
||||
public void SetChecked(bool val)
|
||||
{
|
||||
Checked = val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public AssetTreeElement (string name, int depth, int id) : base (name, depth, id)
|
||||
{
|
||||
AmountChecked = Amount.None;
|
||||
Checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71075f95f18b5a64b99c9bff2e55a9fd
|
||||
timeCreated: 1472024032
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/AssetTreeElement.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,244 @@
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(BaseCharacterModifier),true)]
|
||||
public class BaseCharacterModifierPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
BaseCharacterModifier _target;
|
||||
|
||||
private bool _alwaysExpanded = false;
|
||||
private bool _manuallyConfigured = false;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
public bool AlwaysExpanded
|
||||
{
|
||||
set
|
||||
{
|
||||
_alwaysExpanded = value;
|
||||
_manuallyConfigured = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (!_manuallyConfigured)
|
||||
{
|
||||
if (this.fieldInfo != null)
|
||||
{
|
||||
var attrib = this.fieldInfo.GetCustomAttributes(typeof(BaseCharacterModifier.ConfigAttribute), true).FirstOrDefault() as BaseCharacterModifier.ConfigAttribute;
|
||||
if (attrib != null)
|
||||
{
|
||||
_alwaysExpanded = attrib.alwaysExpanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
float ph = 0;
|
||||
if (!property.isExpanded && !_alwaysExpanded)
|
||||
{
|
||||
ph = (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_alwaysExpanded)
|
||||
{
|
||||
ph = ((EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) * 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
ph = ((EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) * 7);
|
||||
}
|
||||
}
|
||||
return ph;
|
||||
}
|
||||
|
||||
private void Init(SerializedProperty property)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
_target = fieldInfo.GetValue(property.serializedObject.targetObject) as BaseCharacterModifier;
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
Init(property);
|
||||
|
||||
if (!_manuallyConfigured)
|
||||
{
|
||||
if (this.fieldInfo != null)
|
||||
{
|
||||
var attrib = this.fieldInfo.GetCustomAttributes(typeof(BaseCharacterModifier.ConfigAttribute), true).FirstOrDefault() as BaseCharacterModifier.ConfigAttribute;
|
||||
if (attrib != null)
|
||||
{
|
||||
_alwaysExpanded = attrib.alwaysExpanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!_alwaysExpanded)
|
||||
{
|
||||
var foldoutRect = new Rect(position.xMin, position.yMin, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing));
|
||||
property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, label);
|
||||
}
|
||||
if (property.isExpanded || _alwaysExpanded)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
position = EditorGUI.IndentedRect(position);
|
||||
if (!_alwaysExpanded)
|
||||
{
|
||||
position.yMin = position.yMin + (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
}
|
||||
|
||||
// Don't make child fields be indented
|
||||
var indent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
var adjustScaleProp = property.FindPropertyRelative("_adjustScale");
|
||||
var adjustHeightProp = property.FindPropertyRelative("_adjustHeight");
|
||||
var adjustRadiusProp = property.FindPropertyRelative("_adjustRadius");
|
||||
var adjustMassProp = property.FindPropertyRelative("_adjustMass");
|
||||
var updateBoundsProp = property.FindPropertyRelative("_updateBounds");
|
||||
var tightenBoundsProp = property.FindPropertyRelative("_tightenBounds");
|
||||
var adjustBoundsProp = property.FindPropertyRelative("_adjustBounds");
|
||||
|
||||
var scaleAdjustProp = property.FindPropertyRelative("_scale");
|
||||
var scaleBoneProp = property.FindPropertyRelative("_bone");
|
||||
var scaleBoneHashProp = property.FindPropertyRelative("_scaleBoneHash");
|
||||
|
||||
var headRatioProp = property.FindPropertyRelative("_headRatio");
|
||||
var radiusAdjustYProp = property.FindPropertyRelative("_radiusAdjustY");
|
||||
var radiusAdjustProp = property.FindPropertyRelative("_radiusAdjust");
|
||||
var massAdjustProp = property.FindPropertyRelative("_massAdjust");
|
||||
var boundAdjustProp = property.FindPropertyRelative("_boundsAdjust");
|
||||
|
||||
//overall rects
|
||||
var scaleRect = new Rect(position.xMin, position.yMin, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing));
|
||||
var heightRect = new Rect(position.xMin, scaleRect.yMax, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing));
|
||||
var radiusRect = new Rect(position.xMin, heightRect.yMax, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing));
|
||||
var massRect = new Rect(position.xMin, radiusRect.yMax, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing));
|
||||
var boundsBoolsRect = new Rect(position.xMin, massRect.yMax, position.width, (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing));
|
||||
|
||||
//labelsRects are all 120f wide or maybe 1/3rd or whatever unity makes them by standard
|
||||
var scaleLabelRect = new Rect(position.xMin, scaleRect.yMin, (position.width / 3), scaleRect.height);
|
||||
var heightLabelRect = new Rect(position.xMin, heightRect.yMin, (position.width / 3), scaleRect.height);
|
||||
var radiusLabelRect = new Rect(position.xMin, radiusRect.yMin, (position.width / 3), scaleRect.height);
|
||||
var massLabelRect = new Rect(position.xMin, massRect.yMin, (position.width / 3), scaleRect.height);
|
||||
var boundsLabelRect = new Rect(position.xMin, boundsBoolsRect.yMin, (position.width / 3), scaleRect.height);
|
||||
var boundsLabel1Rect = new Rect(boundsLabelRect.xMax, boundsBoolsRect.yMin, (position.width / 3), scaleRect.height);
|
||||
var boundsLabel2Rect = new Rect(position.xMin, boundsBoolsRect.yMax, (position.width / 3), scaleRect.height);
|
||||
|
||||
//fieldsRects are whatever is left
|
||||
var scaleFieldsRect = new Rect(scaleLabelRect.xMax, scaleRect.yMin, (position.width / 3) * 2, scaleRect.height);
|
||||
var scaleFields1FieldRect = new Rect(scaleFieldsRect.xMin, scaleFieldsRect.yMin, (scaleFieldsRect.width / 3), EditorGUIUtility.singleLineHeight);
|
||||
var scaleFields2LabelRect = new Rect(scaleFields1FieldRect.xMax, scaleFieldsRect.yMin, 40f, EditorGUIUtility.singleLineHeight);
|
||||
var scaleFields2FieldRect = new Rect(scaleFields2LabelRect.xMax, scaleFieldsRect.yMin, ((scaleFieldsRect.width / 3) * 2) - 40f, EditorGUIUtility.singleLineHeight);
|
||||
|
||||
var heightFieldsRect = new Rect(scaleLabelRect.xMax, heightRect.yMin, (position.width / 3) * 2, scaleRect.height);
|
||||
var heightFields1FieldRect = new Rect(heightFieldsRect.xMin, heightFieldsRect.yMin, ((heightFieldsRect.width / 3) * 2), EditorGUIUtility.singleLineHeight);
|
||||
var heightFields2FieldRect = new Rect(heightFields1FieldRect.xMax, heightFieldsRect.yMin, (heightFieldsRect.width / 3), EditorGUIUtility.singleLineHeight);
|
||||
|
||||
var radiusFieldsRect = new Rect(scaleLabelRect.xMax, radiusRect.yMin, (position.width / 3) * 2, scaleRect.height);
|
||||
var radiusFields1FieldRect = new Rect(radiusFieldsRect.xMin, radiusFieldsRect.yMin, (radiusFieldsRect.width / 2), EditorGUIUtility.singleLineHeight);
|
||||
var radiusFields2FieldRect = new Rect(radiusFields1FieldRect.xMax, radiusFieldsRect.yMin, (radiusFieldsRect.width / 2), EditorGUIUtility.singleLineHeight);
|
||||
|
||||
|
||||
var massFieldsRect = new Rect(scaleLabelRect.xMax, massRect.yMin, (position.width / 3) * 2, scaleRect.height);
|
||||
var boundsFieldsRect = new Rect(scaleLabelRect.xMax, boundsBoolsRect.yMax, (position.width / 3) * 2, scaleRect.height);
|
||||
|
||||
float prevlabelWidth = EditorGUIUtility.labelWidth;
|
||||
|
||||
//you loose the tooltips when you do toggleLeft WTFF?!?
|
||||
//none of the tooltips show in play mode either!! What a fucking pain...
|
||||
var scaleLabel = EditorGUI.BeginProperty(scaleLabelRect, new GUIContent(adjustScaleProp.displayName), adjustScaleProp);
|
||||
adjustScaleProp.boolValue = EditorGUI.ToggleLeft(scaleLabelRect, scaleLabel, adjustScaleProp.boolValue);
|
||||
EditorGUI.EndProperty();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!adjustScaleProp.boolValue);
|
||||
EditorGUIUtility.labelWidth = 40f;
|
||||
if(Application.isPlaying && _target.liveScale != -1)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUI.LabelField(scaleFields1FieldRect, new GUIContent("Scale", "The live scale is being modified by a converter above. Please exit playmode if you want to edit the base scale"));
|
||||
var fieldRect = new Rect(scaleFields1FieldRect.xMin + 40f, scaleFields1FieldRect.yMin, scaleFields1FieldRect.width -40f, scaleFields1FieldRect.height);
|
||||
EditorGUI.FloatField(fieldRect, _target.liveScale);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
scaleAdjustProp.floatValue = EditorGUI.FloatField(scaleFields1FieldRect, "Scale", scaleAdjustProp.floatValue);
|
||||
|
||||
}
|
||||
EditorGUI.LabelField(scaleFields2LabelRect, " Bone");
|
||||
EditorGUI.BeginChangeCheck();
|
||||
//I want this to draw a bone selection popup when it can- i.e. when drawn inside a ConverterBehaviour and we are in play mode.
|
||||
scaleBoneProp.stringValue = EditorGUI.TextField(scaleFields2FieldRect, scaleBoneProp.stringValue);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
scaleBoneHashProp.intValue = UMAUtils.StringToHash(scaleBoneProp.stringValue);
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
var heightLabel = EditorGUI.BeginProperty(heightLabelRect, new GUIContent(adjustHeightProp.displayName), adjustHeightProp);
|
||||
adjustHeightProp.boolValue = EditorGUI.ToggleLeft(heightLabelRect, heightLabel, adjustHeightProp.boolValue);
|
||||
EditorGUI.EndProperty();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!adjustHeightProp.boolValue);
|
||||
EditorGUIUtility.labelWidth = 80f;
|
||||
headRatioProp.floatValue = EditorGUI.FloatField(heightFields1FieldRect, "Head Ratio", headRatioProp.floatValue);
|
||||
EditorGUIUtility.labelWidth = 20f;
|
||||
radiusAdjustYProp.floatValue = EditorGUI.FloatField(heightFields2FieldRect, " Y ", radiusAdjustYProp.floatValue);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
var radiusLabel = EditorGUI.BeginProperty(radiusLabelRect, new GUIContent(adjustRadiusProp.displayName), adjustRadiusProp);
|
||||
adjustRadiusProp.boolValue = EditorGUI.ToggleLeft(radiusLabelRect, radiusLabel, adjustRadiusProp.boolValue);
|
||||
EditorGUI.EndProperty();
|
||||
EditorGUI.BeginDisabledGroup(!adjustRadiusProp.boolValue);
|
||||
var radiusV2 = radiusAdjustProp.vector2Value;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
radiusV2.x = EditorGUI.FloatField(radiusFields1FieldRect, "X ", radiusV2.x);
|
||||
radiusV2.y = EditorGUI.FloatField(radiusFields2FieldRect, " Z ", radiusV2.y);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
radiusAdjustProp.vector2Value = radiusV2;
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
var massLabel = EditorGUI.BeginProperty(massLabelRect, new GUIContent(adjustMassProp.displayName), adjustMassProp);
|
||||
adjustMassProp.boolValue = EditorGUI.ToggleLeft(massLabelRect, massLabel, adjustMassProp.boolValue);
|
||||
EditorGUI.EndProperty();
|
||||
EditorGUI.BeginDisabledGroup(!adjustMassProp.boolValue);
|
||||
EditorGUI.PropertyField(massFieldsRect, massAdjustProp, GUIContent.none);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
var boundsLabel = EditorGUI.BeginProperty(boundsLabelRect, new GUIContent(updateBoundsProp.displayName), updateBoundsProp);
|
||||
updateBoundsProp.boolValue = EditorGUI.ToggleLeft(boundsLabelRect, boundsLabel, updateBoundsProp.boolValue);
|
||||
EditorGUI.EndProperty();
|
||||
var boundsLabel1 = EditorGUI.BeginProperty(boundsLabelRect, new GUIContent(tightenBoundsProp.displayName), tightenBoundsProp);
|
||||
tightenBoundsProp.boolValue = EditorGUI.ToggleLeft(boundsLabel1Rect, boundsLabel1, tightenBoundsProp.boolValue);
|
||||
EditorGUI.EndProperty();
|
||||
var boundsLabel2 = EditorGUI.BeginProperty(boundsLabel2Rect, new GUIContent(adjustBoundsProp.displayName), adjustBoundsProp);
|
||||
adjustBoundsProp.boolValue = EditorGUI.ToggleLeft(boundsLabel2Rect, boundsLabel2, adjustBoundsProp.boolValue);
|
||||
EditorGUI.EndProperty();
|
||||
EditorGUI.BeginDisabledGroup(!adjustBoundsProp.boolValue);
|
||||
EditorGUI.PropertyField(boundsFieldsRect, boundAdjustProp, GUIContent.none);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
|
||||
EditorGUIUtility.labelWidth = prevlabelWidth;
|
||||
// Set indent back to what it was
|
||||
EditorGUI.indentLevel = indent;
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ba2f1ce21bd66d4c80b37dd00aa208d
|
||||
timeCreated: 1539891448
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/BaseCharacterModifierPropertyDrawer.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,13 @@
|
||||
using UMA;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
|
||||
public class BuildPreprocessor : IPreprocessBuildWithReport
|
||||
{
|
||||
public int callbackOrder { get { return 0; } }
|
||||
|
||||
public void OnPreprocessBuild(BuildReport report)
|
||||
{
|
||||
UMAAssetIndexer.Instance.UpdateReferences();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d94befa3c5b5de04f937e868943f8f33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/BuildPreprocessor.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,119 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// A reorderable list for use in property drawers and other places where you dont want to initialize it in OnGUI every frame. Call CachedReorderableList.GetListDrawer to use
|
||||
/// </summary>
|
||||
public sealed class CachedReorderableList : ReorderableList
|
||||
{
|
||||
|
||||
public GUIContent Label;
|
||||
|
||||
private CachedReorderableList(SerializedObject serializedObj, SerializedProperty property) : base(serializedObj, property)
|
||||
{
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
private static FieldInfo _m_SerializedObject;
|
||||
private void ReInit(SerializedObject obj, SerializedProperty prop)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_m_SerializedObject == null)
|
||||
{
|
||||
_m_SerializedObject = typeof(ReorderableList).GetField("m_SerializedObject", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
}
|
||||
|
||||
_m_SerializedObject.SetValue(this, obj);
|
||||
}
|
||||
catch
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("This version of Spacepuppy Framework does not support the version of Unity it's being used with (CachedReorderableCollection).");
|
||||
}
|
||||
|
||||
this.serializedProperty = prop;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Static Factory
|
||||
|
||||
private static Dictionary<int, CachedReorderableList> _lstCache = new Dictionary<int, CachedReorderableList>();
|
||||
|
||||
public static CachedReorderableList GetListDrawer(SerializedProperty property, ReorderableList.HeaderCallbackDelegate drawHeaderCallback, ReorderableList.ElementHeightCallbackDelegate getElementHeightCallback, ReorderableList.ElementCallbackDelegate drawElementCallback,
|
||||
ReorderableList.FooterCallbackDelegate drawFooterCallback = null,
|
||||
ReorderableList.AddCallbackDelegate onAddCallback = null, ReorderableList.RemoveCallbackDelegate onRemoveCallback = null, ReorderableList.SelectCallbackDelegate onSelectCallback = null,
|
||||
ReorderableList.ChangedCallbackDelegate onChangedCallback = null, ReorderableList.ReorderCallbackDelegate onReorderCallback = null, ReorderableList.CanRemoveCallbackDelegate onCanRemoveCallback = null,
|
||||
ReorderableList.AddDropdownCallbackDelegate onAddDropdownCallback = null)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("property");
|
||||
}
|
||||
|
||||
if (!property.isArray)
|
||||
{
|
||||
throw new System.ArgumentException("SerializedProperty must be a property for an Array or List", "property");
|
||||
}
|
||||
|
||||
int hash = GetPropertyHash(property);
|
||||
CachedReorderableList lst;
|
||||
if (_lstCache.TryGetValue(hash, out lst))
|
||||
{
|
||||
lst.ReInit(property.serializedObject, property);
|
||||
}
|
||||
else
|
||||
{
|
||||
lst = new CachedReorderableList(property.serializedObject, property);
|
||||
_lstCache[hash] = lst;
|
||||
}
|
||||
lst.drawHeaderCallback = drawHeaderCallback;
|
||||
lst.elementHeightCallback = getElementHeightCallback;
|
||||
lst.drawElementCallback = drawElementCallback;
|
||||
lst.drawFooterCallback = drawFooterCallback;
|
||||
lst.onAddCallback = onAddCallback;
|
||||
lst.onRemoveCallback = onRemoveCallback;
|
||||
lst.onSelectCallback = onSelectCallback;
|
||||
lst.onChangedCallback = onChangedCallback;
|
||||
lst.onReorderCallback = onReorderCallback;
|
||||
lst.onCanRemoveCallback = onCanRemoveCallback;
|
||||
lst.onAddDropdownCallback = onAddDropdownCallback;
|
||||
|
||||
return lst;
|
||||
}
|
||||
|
||||
public static int GetPropertyHash(SerializedProperty property)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("property");
|
||||
}
|
||||
|
||||
if (property.serializedObject.targetObject == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var spath = property.propertyPath;
|
||||
|
||||
int num = property.serializedObject.targetObject.GetInstanceID() ^ spath.GetHashCode();
|
||||
if (property.propertyType == SerializedPropertyType.ObjectReference)
|
||||
{
|
||||
num ^= property.objectReferenceInstanceIDValue;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a038190d39cafa4993760e4dfe60b40
|
||||
timeCreated: 1538148631
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/CachedReorderableList.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,28 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace UMA
|
||||
{
|
||||
[Serializable]
|
||||
public class CamSaver
|
||||
{
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
|
||||
public CamSaver(Transform t)
|
||||
{
|
||||
position = t.position;
|
||||
rotation = t.localRotation;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonUtility.ToJson(this);
|
||||
}
|
||||
|
||||
public static CamSaver FromString(string s)
|
||||
{
|
||||
return JsonUtility.FromJson<CamSaver>(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a44f9e37a123d3e41936b0905cf33d80
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/CamSaver.cs
|
||||
uploadId: 679826
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f22192b22c8d1e4479a84d056207e88b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/CharacterBaseEditor.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
public class CodeGenTemplate
|
||||
{
|
||||
public String Format;
|
||||
public StringBuilder sb;
|
||||
public String Name;
|
||||
public static IFormatProvider formatter;
|
||||
public string Tag;
|
||||
public void Append(IDictionary<string, object> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
sb.AppendFormat(formatter, Format, data);
|
||||
}
|
||||
catch (FormatException fe)
|
||||
{
|
||||
Debug.LogError(fe);
|
||||
Debug.Log(Format);
|
||||
}
|
||||
}
|
||||
|
||||
public static CodeGenTemplate[] ParseTemplates(string sourceDir, string pageTemplate)
|
||||
{
|
||||
List<CodeGenTemplate> res = new List<CodeGenTemplate>();
|
||||
foreach (var line in pageTemplate.Split('\r', '\n'))
|
||||
{
|
||||
if (line.StartsWith("//#TEMPLATE "))
|
||||
{
|
||||
var parsedLine = line.Split(' ');
|
||||
if (parsedLine.Length >= 3)
|
||||
{
|
||||
var filename = Path.Combine(sourceDir, parsedLine[2]);
|
||||
var tag = parsedLine.Length > 3 ? parsedLine[3] : "";
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Debug.LogError("File not found: " + filename);
|
||||
}
|
||||
res.Add(new CodeGenTemplate() { Format = FileUtils.ReadAllText(filename), sb = new StringBuilder(), Name = parsedLine[1], Tag = tag });
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 761146c6245566642b5d708edd1c3def
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/CodeGenTemplate.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,50 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(UMAColorScheme))]
|
||||
public class ColorSchemeAssetInspector : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
UMAColorScheme colorScheme = target as UMAColorScheme;
|
||||
int previousChannelCount = colorScheme.ColorData.channelCount;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("Description"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("Icon"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("UserObject"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("ColorData"));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
if (previousChannelCount != colorScheme.ColorData.channelCount)
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
serializedObject.Update();
|
||||
Repaint();
|
||||
}
|
||||
if (GUILayout.Button("Save Now"))
|
||||
{
|
||||
Save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
EditorUtility.SetDirty(target);
|
||||
AssetDatabase.SaveAssets();
|
||||
serializedObject.Update();
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/UMA/Core/ColorScheme")]
|
||||
public static void CreateRaceMenuItem()
|
||||
{
|
||||
CustomAssetUtility.CreateAsset<UMAColorScheme>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50803ab8e75deb0458edb0202f706318
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/ColorSchemeAssetInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,68 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
public class CreateCleanAnimationMenu : MonoBehaviour
|
||||
{
|
||||
[MenuItem("UMA/Animation/Create Clean Animation")]
|
||||
static void CreateCleanAniamtionMenuItem()
|
||||
{
|
||||
foreach(var obj in Selection.objects)
|
||||
{
|
||||
var anim = obj as AnimationClip;
|
||||
if (anim != null)
|
||||
{
|
||||
var newClip = Instantiate(anim) as AnimationClip;
|
||||
newClip.ClearCurves();
|
||||
var bindings = AnimationUtility.GetCurveBindings(anim);
|
||||
foreach (var binding in bindings)
|
||||
{
|
||||
if (!binding.propertyName.StartsWith("m_LocalScale") && !binding.propertyName.StartsWith("m_LocalPosition"))
|
||||
{
|
||||
AnimationUtility.SetEditorCurve(newClip, binding, AnimationUtility.GetEditorCurve(anim, binding));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var oldPath = AssetDatabase.GetAssetPath(anim);
|
||||
var folder = System.IO.Path.GetDirectoryName(oldPath);
|
||||
|
||||
AssetDatabase.CreateAsset(newClip, AssetDatabase.GenerateUniqueAssetPath( folder + "/" + anim.name + ".anim"));
|
||||
}
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("UMA/Animation/Create Non-Scale Animation")]
|
||||
static void CreateNonScaleAniamtionMenuItem()
|
||||
{
|
||||
foreach (var obj in Selection.objects)
|
||||
{
|
||||
var anim = obj as AnimationClip;
|
||||
if (anim != null)
|
||||
{
|
||||
var newClip = Instantiate(anim) as AnimationClip;
|
||||
newClip.ClearCurves();
|
||||
var bindings = AnimationUtility.GetCurveBindings(anim);
|
||||
foreach (var binding in bindings)
|
||||
{
|
||||
if (!binding.propertyName.StartsWith("m_LocalScale"))
|
||||
{
|
||||
AnimationUtility.SetEditorCurve(newClip, binding, AnimationUtility.GetEditorCurve(anim, binding));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var oldPath = AssetDatabase.GetAssetPath(anim);
|
||||
var folder = System.IO.Path.GetDirectoryName(oldPath);
|
||||
|
||||
AssetDatabase.CreateAsset(newClip, AssetDatabase.GenerateUniqueAssetPath(folder + "/" + anim.name + ".anim"));
|
||||
}
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a34fc233d7afb134fa017cfb5282d443
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/CreateCleanAnimationMenu.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,121 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
//Draws a dummy field that will take any object that uses the IDNAConverter interface (DNAConverterBehaviours - legacy prefabs- and DNAConverterControllers- the new ScriptableObjects)
|
||||
[CustomPropertyDrawer(typeof(DNAConverterField), true)]
|
||||
public class DNAConverterFieldPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
var converterProp = property.FindPropertyRelative("_converter");
|
||||
return EditorGUI.GetPropertyHeight(converterProp, true);
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
label = EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
//we need to draw a few things to make this work. we need a background object field of type DynamicDNAConverterController so the 'pick' button shows all those
|
||||
//Then we need a TextArea that looks like a field that will show the current value and icon or 'None (DNAConverterController/Behaviour)'
|
||||
//Then over the top an invisible drop area that will accept either a DnaConverterBehaviour or a DNAConverterController
|
||||
var converterProp = property.FindPropertyRelative("_converter");
|
||||
|
||||
DrawObjectReferenceField(position, converterProp, label);
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
private void DrawObjectReferenceField(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
Vector2 iconSize = EditorGUIUtility.GetIconSize();
|
||||
EditorGUIUtility.SetIconSize(new Vector2(12f, 12f));
|
||||
DynamicDNAConverterController converterControllerObject = null;
|
||||
if(property.objectReferenceValue != null)
|
||||
{
|
||||
converterControllerObject = property.objectReferenceValue.GetType() == typeof(DynamicDNAConverterController) ? property.objectReferenceValue as DynamicDNAConverterController : null;
|
||||
}
|
||||
|
||||
var dummyFieldStyle = new GUIStyle(EditorStyles.objectField);//could be objectFieldMiniThumb
|
||||
dummyFieldStyle.normal.background = null;
|
||||
|
||||
var labelPos = new Rect(position.xMin, position.yMin, EditorGUIUtility.labelWidth, position.height);
|
||||
var fieldPos = new Rect(labelPos.xMax, position.yMin, position.width - labelPos.width, position.height);
|
||||
|
||||
//unfortunately we can use PrefixLabel because it inherits the GUI.content color of the field (which is transparent)
|
||||
//the result is the label doesn't highlight- but I think we can live with that!
|
||||
EditorGUI.LabelField(labelPos, label);
|
||||
|
||||
var prevContentColor = GUI.contentColor;
|
||||
GUI.contentColor = new Color(0, 0, 0, 0);//hide the content of the field so we can use our own label
|
||||
EditorGUI.BeginChangeCheck();
|
||||
//We use a converterController field rather than an object field so that the 'dot' button shows ConverterControllers when clicked
|
||||
converterControllerObject = (DynamicDNAConverterController)EditorGUI.ObjectField(fieldPos, "",converterControllerObject, typeof(DynamicDNAConverterController), false);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
property.objectReferenceValue = converterControllerObject;
|
||||
}
|
||||
GUI.contentColor = prevContentColor;
|
||||
|
||||
Rect dropRect = fieldPos;
|
||||
dropRect.width = dropRect.width - 18f;
|
||||
|
||||
System.Type fieldType = typeof(DynamicDNAConverterController);
|
||||
|
||||
if (property.objectReferenceValue != null)
|
||||
{
|
||||
fieldType = property.objectReferenceValue.GetType();
|
||||
}
|
||||
|
||||
GUIContent typeContent = EditorGUIUtility.ObjectContent(property.objectReferenceValue, fieldType);
|
||||
|
||||
if (property.objectReferenceValue == null)
|
||||
{
|
||||
typeContent.text = "None (DNAConverterController/Behaviour)";
|
||||
typeContent.image = null;
|
||||
}
|
||||
|
||||
GUI.Box(dropRect, typeContent, dummyFieldStyle);
|
||||
DoDropArea(dropRect, property);
|
||||
EditorGUIUtility.SetIconSize(iconSize);
|
||||
}
|
||||
private void DoDropArea(Rect dropArea, SerializedProperty property)
|
||||
{
|
||||
Event evt = Event.current;
|
||||
if (evt.type == EventType.DragUpdated)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
}
|
||||
}
|
||||
if (evt.type == EventType.DragPerform)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
UnityEngine.Object[] draggedObjects = DragAndDrop.objectReferences as UnityEngine.Object[];
|
||||
IDNAConverter IDCObj = null;
|
||||
if (draggedObjects[0] is IDNAConverter)
|
||||
{
|
||||
IDCObj = draggedObjects[0] as IDNAConverter;
|
||||
}
|
||||
else if (draggedObjects[0].GetType() == typeof(GameObject))
|
||||
{
|
||||
if ((draggedObjects[0] as GameObject).GetComponent<IDNAConverter>() != null)
|
||||
{
|
||||
IDCObj = (draggedObjects[0] as GameObject).GetComponent<IDNAConverter>();
|
||||
}
|
||||
}
|
||||
if (IDCObj != null)
|
||||
{
|
||||
property.objectReferenceValue = IDCObj as UnityEngine.Object;
|
||||
property.serializedObject.ApplyModifiedProperties();//Needed?
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a59188a454f2bf84eaedd827d55339dd
|
||||
timeCreated: 1547314085
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DNAConverterFieldPropertyDrawer.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,187 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
//Draws a 'DropList' that will accept objects that use the IDNAConverter interface (DNAConverterBehaviours - legacy prefabs- and DNAConverterControllers- the new ScriptableObjects)
|
||||
[CustomPropertyDrawer(typeof(DNAConverterList), true)]
|
||||
public class DNAConverterListPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
private float dropAreaHeight = 50f;
|
||||
private float horizPadding = 2f;
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (property.isExpanded)
|
||||
{
|
||||
var h = (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
var convertersProp = property.FindPropertyRelative("_converters");
|
||||
for (int i = 0; i < convertersProp.arraySize; i++)
|
||||
{
|
||||
h += (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
}
|
||||
|
||||
h += (EditorGUIUtility.standardVerticalSpacing);
|
||||
return h + dropAreaHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EditorGUI.GetPropertyHeight(property, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
|
||||
label = EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
//EditorGUI.PropertyField(position, property, label, true);
|
||||
|
||||
var foldoutRect = new Rect(position.xMin, position.yMin, position.width, EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, label);
|
||||
|
||||
//when this is expanded we want to draw a drop area where DNAConverterBahaviour prefabs and DnaConverterControllers can both be added
|
||||
if (property.isExpanded)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
position = EditorGUI.IndentedRect(position);
|
||||
var indexesToRemove = new List<int>();
|
||||
var convertersProp = property.FindPropertyRelative("_converters");
|
||||
//Cant use GUILayout in a property drawer!!
|
||||
Rect dropArea = new Rect(position.xMin, foldoutRect.yMax, position.width, dropAreaHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
GUI.Box(dropArea, "Drag Converter Behaviours or Converter Controllers here");
|
||||
EditorGUI.indentLevel--;
|
||||
var entryRect = new Rect(position.xMin, dropArea.yMax, position.width -20f - horizPadding, EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
var entryRemoveRect = new Rect(entryRect.xMax + horizPadding, dropArea.yMax, 20f, EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
//then draw a field for each entry with an 'x' button next to it
|
||||
for (int i = 0; i < convertersProp.arraySize; i++)
|
||||
{
|
||||
//GUILayout.BeginHorizontal();
|
||||
var fieldRect = new Rect(entryRect.xMin, entryRect.yMin + EditorGUIUtility.standardVerticalSpacing, entryRect.width, entryRect.height - EditorGUIUtility.standardVerticalSpacing);
|
||||
|
||||
//really we want the 'dot' selector here to show a popup window of DnaConverterControllers?
|
||||
//TODO We should be able to use DNAConverterFields methods to draw this
|
||||
convertersProp.GetArrayElementAtIndex(i).objectReferenceValue = EditorGUI.ObjectField(fieldRect, convertersProp.GetArrayElementAtIndex(i).objectReferenceValue, typeof(IDNAConverter), false);
|
||||
|
||||
var butRect = new Rect(entryRemoveRect.xMin, entryRemoveRect.yMin + EditorGUIUtility.standardVerticalSpacing, entryRemoveRect.width, entryRemoveRect.height - EditorGUIUtility.standardVerticalSpacing);
|
||||
if (GUI.Button(butRect, "x"))
|
||||
{
|
||||
indexesToRemove.Add(i);
|
||||
}
|
||||
|
||||
//GUILayout.EndHorizontal();
|
||||
entryRect.y += (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
entryRemoveRect.y += (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
}
|
||||
if (indexesToRemove.Count > 0)
|
||||
{
|
||||
for(int i = 0; i < indexesToRemove.Count; i++)
|
||||
{
|
||||
//DeleteArrayElementAtIndex just seems to clear the value rather than remove the entry completely
|
||||
RemoveValueAtIndex(convertersProp, indexesToRemove[i]);
|
||||
}
|
||||
|
||||
}
|
||||
DropAreaGUI(dropArea, convertersProp);
|
||||
//EditorGUI.indentLevel--;
|
||||
}
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
private void SetValueAtIndex(SerializedProperty property, int index, Object value)
|
||||
{
|
||||
property.GetArrayElementAtIndex(index).objectReferenceValue = value;
|
||||
}
|
||||
|
||||
private Object GetValueAtIndex(SerializedProperty property, int index)
|
||||
{
|
||||
return property.GetArrayElementAtIndex(index).objectReferenceValue;
|
||||
}
|
||||
|
||||
private void AddValue(SerializedProperty property, Object value)
|
||||
{
|
||||
property.arraySize++;
|
||||
SetValueAtIndex(property, property.arraySize - 1, value);
|
||||
}
|
||||
|
||||
private void RemoveValueAtIndex(SerializedProperty property, int index)
|
||||
{
|
||||
|
||||
for (int i = index; i < property.arraySize - 1; i++)
|
||||
{
|
||||
|
||||
SetValueAtIndex(property, i, GetValueAtIndex(property, i + 1));
|
||||
}
|
||||
|
||||
property.arraySize--;
|
||||
}
|
||||
|
||||
//We could probably do with a Utility here to draw this kind of thing universally since DefaultWardrobeRecipes and WardrobeCollections Arbitrary recipes,
|
||||
//and the slot/overlay libraries also have the same thing
|
||||
//draws a list like this maybe something like a 'DropList' that handles the drawing of the area, and the items in the list
|
||||
//TODO make this 'click to pick' DynamicDNAConverterControllers
|
||||
private void DropAreaGUI(Rect dropArea, SerializedProperty converterListProp)
|
||||
{
|
||||
var evt = Event.current;
|
||||
|
||||
if (evt.type == EventType.DragUpdated)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
//can we make this show 'rejected' if the object wont get added for any reason?
|
||||
}
|
||||
}
|
||||
if (evt.type == EventType.DragPerform)
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
UnityEngine.Object[] draggedObjects = DragAndDrop.objectReferences as UnityEngine.Object[];
|
||||
IDNAConverter IDCObj = null;
|
||||
if(draggedObjects[0] is IDNAConverter)
|
||||
{
|
||||
IDCObj = draggedObjects[0] as IDNAConverter;
|
||||
}
|
||||
else if(draggedObjects[0].GetType() == typeof(GameObject))
|
||||
{
|
||||
if((draggedObjects[0] as GameObject).GetComponent<IDNAConverter>() != null)
|
||||
{
|
||||
IDCObj = (draggedObjects[0] as GameObject).GetComponent<IDNAConverter>();
|
||||
}
|
||||
}
|
||||
if (IDCObj != null)
|
||||
{
|
||||
bool canAdd = true;
|
||||
for (int i = 0; i < converterListProp.arraySize; i++)
|
||||
{
|
||||
if (converterListProp.GetArrayElementAtIndex(i).objectReferenceValue == IDCObj as UnityEngine.Object)
|
||||
{
|
||||
canAdd = false;
|
||||
}
|
||||
}
|
||||
if (canAdd)
|
||||
{
|
||||
converterListProp.arraySize++;
|
||||
converterListProp.GetArrayElementAtIndex(converterListProp.arraySize - 1).objectReferenceValue = IDCObj as UnityEngine.Object;
|
||||
converterListProp.serializedObject.ApplyModifiedProperties();
|
||||
GUI.changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf71181361e9f8744924f49b521898d0
|
||||
timeCreated: 1547236610
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DNAConverterListPropertyDrawer.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,324 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(DNAEvaluationGraphPresetLibrary))]
|
||||
public class DNAEvaluationGraphPresetLibraryInspector : Editor
|
||||
{
|
||||
|
||||
DNAEvaluationGraphPresetLibrary _target;
|
||||
|
||||
DNAEvaluationGraphPropertyDrawer dnaEvalDrawer = new DNAEvaluationGraphPropertyDrawer();
|
||||
|
||||
bool extraInfoIsExpanded = false;
|
||||
|
||||
bool defaultPresetsIsExpanded = true;
|
||||
|
||||
bool customPresetsIsExpanded = true;
|
||||
|
||||
bool addPresetIsExpanded = false;
|
||||
|
||||
private float entryHeight = EditorGUIUtility.singleLineHeight * 2f;
|
||||
|
||||
private float padding = EditorGUIUtility.standardVerticalSpacing * 2f;
|
||||
|
||||
private string newGraphName = "";
|
||||
|
||||
private string newGraphTooltip = "";
|
||||
|
||||
private AnimationCurve newGraph;
|
||||
|
||||
private GUIStyle wordwrappedTextArea;
|
||||
|
||||
private bool initialized;
|
||||
|
||||
private string nameError = "";
|
||||
private string graphError = "";
|
||||
private string addSuccess = "";
|
||||
|
||||
private float swatchButWidth = 20f;
|
||||
|
||||
private List<DNAEvaluationGraph> _customPresets = new List<DNAEvaluationGraph>();
|
||||
|
||||
private List<string> _customPresetTooltips = new List<string>();
|
||||
|
||||
private DNAEvaluationGraph _updatatingPreset = null;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
//Init();//EditorStyles are not here yet on domain reload
|
||||
ResetNewGraphFields();
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
_target = serializedObject.targetObject as DNAEvaluationGraphPresetLibrary;
|
||||
if (newGraph == null)
|
||||
{
|
||||
newGraph = new AnimationCurve(DNAEvaluationGraph.Default.GraphKeys);
|
||||
}
|
||||
wordwrappedTextArea = new GUIStyle(EditorStyles.textArea);
|
||||
wordwrappedTextArea.wordWrap = true;
|
||||
_customPresets = DNAEvaluationGraphPresetLibrary.AllCustomGraphPresets;
|
||||
_customPresetTooltips = DNAEvaluationGraphPresetLibrary.AllCustomGraphTooltips;
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
Init();
|
||||
|
||||
EditorGUILayout.LabelField("DNAEvaluationGraph Preset Library", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.HelpBox("A DNAEvaluationGraph Preset Library contains presets for graphs that will be available in a DNAEvaluationGraph field's dropdown list of available graphs. This asset does not need to be included in your build", MessageType.Info);
|
||||
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
GUILayout.Space(10f);
|
||||
extraInfoIsExpanded = EditorGUILayout.Foldout(extraInfoIsExpanded, "What is a DNAEvaluationGraph?", true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (extraInfoIsExpanded)
|
||||
{
|
||||
DrawFullHelp();
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
GUILayout.Space(10f);
|
||||
defaultPresetsIsExpanded = EditorGUILayout.Foldout(defaultPresetsIsExpanded, "Default Presets", true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (defaultPresetsIsExpanded)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
EditorGUILayout.HelpBox("These are the standard evaluation graphs that ship with UMA. You can add your own in the custom section below.", MessageType.Info);
|
||||
for (int i = 0; i < DNAEvaluationGraphPresetLibrary.DefaultGraphPresets.Count; i++)
|
||||
{
|
||||
var rect = EditorGUILayout.GetControlRect(false, entryHeight + (padding * 2f));
|
||||
rect.x += padding;
|
||||
rect.width -= padding * 2f;
|
||||
Rect swatchRect = new Rect(rect.xMin, rect.yMin + padding, rect.width, rect.height - (padding * 2f));
|
||||
dnaEvalDrawer.DrawSwatch(swatchRect, DNAEvaluationGraphPresetLibrary.DefaultGraphPresets[i], DNAEvaluationGraphPresetLibrary.DefaultGraphTooltips[i], false, false, (dnaGraph) =>
|
||||
{
|
||||
newGraph = new AnimationCurve(DNAEvaluationGraphPresetLibrary.DefaultGraphPresets[i].GraphKeys);
|
||||
addPresetIsExpanded = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
GUILayout.Space(10f);
|
||||
customPresetsIsExpanded = EditorGUILayout.Foldout(customPresetsIsExpanded, "Custom Presets",true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (customPresetsIsExpanded)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
EditorGUILayout.HelpBox("Add your own graph presets to this section using the tools below.", MessageType.Info);
|
||||
List<int> custToDel = new List<int>();
|
||||
for (int i = 0; i < _customPresets.Count; i++)
|
||||
{
|
||||
var rect = EditorGUILayout.GetControlRect(false, entryHeight + (padding * 2f));
|
||||
rect.x += padding;
|
||||
rect.width -= padding * 2f;
|
||||
Rect swatchRect = new Rect(rect.xMin, rect.yMin + padding, rect.width - (swatchButWidth + padding), rect.height - (padding * 2f));
|
||||
dnaEvalDrawer.DrawSwatch(swatchRect, _customPresets[i], _customPresetTooltips[i]+" [Click To Edit]", false, false, (dnaGraph) =>
|
||||
{
|
||||
newGraphName = _customPresets[i].name;
|
||||
newGraphTooltip = _customPresetTooltips[i];
|
||||
newGraph = new AnimationCurve(_customPresets[i].GraphKeys);
|
||||
addPresetIsExpanded = true;
|
||||
_updatatingPreset = _customPresets[i];
|
||||
}
|
||||
);
|
||||
//editCallback
|
||||
|
||||
var delRect = new Rect(swatchRect.xMax, swatchRect.yMin + padding, swatchButWidth, swatchButWidth);
|
||||
if(GUI.Button(delRect, new GUIContent("X", "Delete "+ _customPresets[i].name)))
|
||||
{
|
||||
custToDel.Add(i);
|
||||
}
|
||||
}
|
||||
if(custToDel.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < custToDel.Count; i++)
|
||||
{
|
||||
DNAEvaluationGraphPresetLibrary.DeleteCustomPreset(_customPresets[custToDel[i]]);
|
||||
}
|
||||
initialized = false;
|
||||
EditorUtility.SetDirty(_target);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
DrawDNAEvaluationGraphAddBox();
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetMessages();
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void DrawDNAEvaluationGraphAddBox()
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
GUILayout.Space(10f);
|
||||
var label = _updatatingPreset ? "Update '" + newGraphName + "' Preset" : "Add a new Preset";
|
||||
addPresetIsExpanded = EditorGUILayout.Foldout(addPresetIsExpanded, label, true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (addPresetIsExpanded)
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
if (_updatatingPreset != null)
|
||||
{
|
||||
//warn the user that updating a preset wont update any fields that created their values from it
|
||||
EditorGUILayout.HelpBox("Note: Updating this preset will not update the graphs in any existing DNAEvaluationGraph fields", MessageType.Warning);
|
||||
}
|
||||
|
||||
newGraphName = EditorGUILayout.TextField("Preset Name", newGraphName);
|
||||
|
||||
var descRect = EditorGUILayout.GetControlRect(false, 45);
|
||||
newGraphTooltip = EditorGUI.TextArea(descRect, newGraphTooltip, wordwrappedTextArea);
|
||||
//I want the placeholder text in here
|
||||
if(newGraphTooltip == "")
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUI.TextArea(descRect, "Preset Tooltip", wordwrappedTextArea);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
var graphRect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight * 2f);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
ResetMessages();
|
||||
}
|
||||
|
||||
newGraph = EditorGUI.CurveField(graphRect, "Preset Graph", newGraph);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
var btnRect = EditorGUILayout.GetControlRect();
|
||||
btnRect.width = btnRect.width / 2;
|
||||
var clearBtnRect = new Rect(btnRect.xMax, btnRect.yMin, btnRect.width, btnRect.height);
|
||||
var addBtnLabel = "Add It!";
|
||||
if (_updatatingPreset != null)
|
||||
{
|
||||
addBtnLabel = "Update '" + newGraphName+"'";
|
||||
}
|
||||
if (GUI.Button(btnRect, addBtnLabel))
|
||||
{
|
||||
if (_target.AddNewPreset(newGraphName, newGraphTooltip, newGraph, ref nameError, ref graphError, _updatatingPreset))
|
||||
{
|
||||
serializedObject.Update();
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
addSuccess = _updatatingPreset ? "Updated Graph "+newGraphName+" Successfully!" : "New Custom Graph " + newGraphName + " added Successfully!";
|
||||
ResetNewGraphFields();
|
||||
initialized = false;//force the cached custom list to Update
|
||||
EditorUtility.SetDirty(_target);
|
||||
AssetDatabase.SaveAssets();
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
if(GUI.Button(clearBtnRect, "Reset Fields"))
|
||||
{
|
||||
ResetNewGraphFields();
|
||||
}
|
||||
if (graphError != "" || nameError != "")
|
||||
{
|
||||
EditorGUILayout.HelpBox("There were the following issues when trying to add your graph:", MessageType.None);
|
||||
if (nameError != "")
|
||||
{
|
||||
EditorGUILayout.HelpBox(nameError, MessageType.Error);
|
||||
}
|
||||
|
||||
if (graphError != "")
|
||||
{
|
||||
EditorGUILayout.HelpBox(graphError, MessageType.Error);
|
||||
}
|
||||
}
|
||||
else if (addSuccess != "")
|
||||
{
|
||||
EditorGUILayout.HelpBox(addSuccess, MessageType.Info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetMessages();
|
||||
}
|
||||
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
|
||||
private void ResetMessages()
|
||||
{
|
||||
addSuccess = "";
|
||||
nameError = "";
|
||||
graphError = "";
|
||||
}
|
||||
|
||||
private void ResetNewGraphFields()
|
||||
{
|
||||
newGraphName = "";
|
||||
newGraphTooltip = "";
|
||||
newGraph = new AnimationCurve(DNAEvaluationGraph.Default.GraphKeys);
|
||||
nameError = "";
|
||||
graphError = "";
|
||||
_updatatingPreset = null;
|
||||
}
|
||||
|
||||
private void DrawFullHelp()
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
|
||||
EditorGUILayout.HelpBox("A DNAEvaluationGraph is an option you can choose in a DNAEvaluator field that many UMA DNA Converters use when interpreting your avatars dna values. Its one of these:", MessageType.None);
|
||||
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
EditorGUI.indentLevel++;
|
||||
var exampleFieldProp = serializedObject.FindProperty("_exampleField");
|
||||
EditorGUILayout.PropertyField(exampleFieldProp);
|
||||
EditorGUI.indentLevel--;
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
EditorGUILayout.HelpBox("The graph is used to perform math operations on an incoming dna value. UMA comes with a set of DNAEvaluationGraphs built in. But if you need something sepcial you can add it below. Give it a go! If you add a graph in the 'CustomGraphs' area below and then click the dropdown in the field above you will see it is in there as an option. This will be the same for all DNAPlugins that use this field.", MessageType.None);
|
||||
EditorGUILayout.HelpBox("For example, below is a DNA called 'DNAValue'. Below that is a DNAEvaluator, using a graph, that will evaluate 'DNAValue'. Try changing the settings in the Evaluator and see how they affect the calculated 'Result' in the last field", MessageType.None);
|
||||
|
||||
GUIHelper.BeginVerticalPadded(5, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
EditorGUI.indentLevel++;
|
||||
var exampleEvaluatorProp = serializedObject.FindProperty("_exampleEvaluator");
|
||||
exampleEvaluatorProp.isExpanded = true;
|
||||
var exampleDNAProp = serializedObject.FindProperty("_exampleDNAValue");
|
||||
EditorGUI.BeginChangeCheck();
|
||||
exampleDNAProp.floatValue = EditorGUILayout.Slider(new GUIContent("DNAValue"), exampleDNAProp.floatValue, 0f, 1f);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(exampleEvaluatorProp);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.FloatField(new GUIContent("Evaluated Result!"), _target.EvaluateDemo());
|
||||
EditorGUI.EndDisabledGroup();
|
||||
EditorGUI.indentLevel--;
|
||||
GUIHelper.EndVerticalPadded(5);
|
||||
|
||||
EditorGUILayout.HelpBox("The Evaluation Graph field itself is a value field rather than a reference field, so once its been set to use a certain graph that graph will not change even if you change the preset that set it. Think of it like Color.red, Color.blue etc, if Unity changed the hues of those colors, any colors that you set using those defaults, would not actually change. So there is no actual need to include this preset library in your project. Its just here to help.", MessageType.None);
|
||||
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34e9beb060a36cd498de4a76c518361f
|
||||
timeCreated: 1537739310
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DNAEvaluationGraphPresetLibraryInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,146 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
//Draws the popup you see whenever a DNAEvaluationGraph field is clicked.
|
||||
//calls on DNAEvaluationGraphPresetLibrary to get the tooltips so these dont have to be stored along with the field
|
||||
public class DNAEvaluationGraphPopupContent : PopupWindowContent
|
||||
{
|
||||
|
||||
private DNAEvaluationGraph _selectedPreset;
|
||||
|
||||
public SerializedProperty property;
|
||||
|
||||
public delegate void OnSelectedDelegate(DNAEvaluationGraph selectedGraph, SerializedProperty property);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a selection is made from the popup
|
||||
/// </summary>
|
||||
public OnSelectedDelegate OnSelected;
|
||||
|
||||
DNAEvaluationGraphPropertyDrawer dnaEvalDrawer = new DNAEvaluationGraphPropertyDrawer();
|
||||
|
||||
public float width = 200f;
|
||||
|
||||
private float minWidth = 200f;
|
||||
private float minHeight = 150f;
|
||||
//entries show in a scrollRect if the height is bigger than this
|
||||
private float maxHeight = 400f;
|
||||
|
||||
private float entryHeight = EditorGUIUtility.singleLineHeight * 2f;
|
||||
|
||||
private float padding = EditorGUIUtility.standardVerticalSpacing * 2f;
|
||||
|
||||
private Vector2 scrollPosition;
|
||||
private int _selectedIndex = -1;
|
||||
public int _hoveredIndex = -1;
|
||||
|
||||
public DNAEvaluationGraph selectedPreset
|
||||
{
|
||||
get { return _selectedPreset; }
|
||||
set
|
||||
{
|
||||
_hoveredIndex = -1;
|
||||
_selectedPreset = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override Vector2 GetWindowSize()
|
||||
{
|
||||
var entrysHeightCalc = 0f;
|
||||
if (DNAEvaluationGraphPresetLibrary.AllGraphPresets.Count > 0)
|
||||
{
|
||||
entrysHeightCalc = (entryHeight * DNAEvaluationGraphPresetLibrary.AllGraphPresets.Count) + (padding * 2f);
|
||||
entrysHeightCalc += (EditorGUIUtility.singleLineHeight * 2f) + (padding * 2f);
|
||||
if (entrysHeightCalc > maxHeight)
|
||||
{
|
||||
entrysHeightCalc = maxHeight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entrysHeightCalc = minHeight;
|
||||
}
|
||||
return new Vector2((width > minWidth ? width : minWidth), entrysHeightCalc);
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position)
|
||||
{
|
||||
GUI.Label(position, GUIContent.none, GUI.skin.box);
|
||||
Event current = Event.current;
|
||||
|
||||
//handle the user editing a field whose graph is not longer in any preset libraries
|
||||
var allPresets = DNAEvaluationGraphPresetLibrary.AllGraphPresets;
|
||||
var allPresetTooltips = DNAEvaluationGraphPresetLibrary.AllGraphTooltips;
|
||||
if (_selectedPreset != null && !allPresets.Contains(_selectedPreset))
|
||||
{
|
||||
allPresets.Insert(0, _selectedPreset);
|
||||
allPresetTooltips.Insert(0, _selectedPreset.name + " (Not in Presets Library)");
|
||||
}
|
||||
scrollPosition = GUILayout.BeginScrollView(scrollPosition);
|
||||
if (DNAEvaluationGraphPresetLibrary.AllGraphPresets.Count > 0)
|
||||
{
|
||||
for (int index = 0; index < allPresets.Count; index++)
|
||||
{
|
||||
Rect rect = GUILayoutUtility.GetRect(16f, entryHeight + (padding * 2f), new GUILayoutOption[1]
|
||||
{
|
||||
GUILayout.ExpandWidth(true)
|
||||
});
|
||||
rect.x += padding;
|
||||
rect.width -= padding * 2f;
|
||||
Rect swatchRect = new Rect(rect.xMin, rect.yMin + padding, rect.width, rect.height - (padding * 2f));
|
||||
bool selectedFlag = false;
|
||||
bool hoveredFlag = false;
|
||||
if (current.type == EventType.MouseDown && rect.Contains(current.mousePosition))
|
||||
{
|
||||
_selectedIndex = index;
|
||||
_selectedPreset = allPresets[index];
|
||||
this.editorWindow.Close();
|
||||
}
|
||||
else if (current.type != EventType.MouseDown && (_selectedPreset != null && allPresets[index] == _selectedPreset))
|
||||
{
|
||||
_selectedIndex = index;
|
||||
}
|
||||
if (current.type == EventType.MouseMove && rect.Contains(current.mousePosition))
|
||||
{
|
||||
_hoveredIndex = index;
|
||||
this.editorWindow.Repaint();
|
||||
}
|
||||
if (index == _selectedIndex)
|
||||
{
|
||||
selectedFlag = true;
|
||||
}
|
||||
if (index == _hoveredIndex)
|
||||
{
|
||||
hoveredFlag = true;
|
||||
}
|
||||
if (current.type == EventType.Repaint)
|
||||
{
|
||||
dnaEvalDrawer.DrawSwatch(swatchRect, allPresets[index], allPresetTooltips[index], hoveredFlag, selectedFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("There were no DNAEvaluationGraph presets.", MessageType.Warning);
|
||||
}
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
GUILayout.Space(10f);
|
||||
}
|
||||
|
||||
public override void OnOpen()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public override void OnClose()
|
||||
{
|
||||
if (OnSelected != null)
|
||||
{
|
||||
OnSelected(_selectedPreset, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c797e668b7b6ed40b0cfbd7c8335266
|
||||
timeCreated: 1537923396
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DNAEvaluationGraphPresetsPopupContent.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,239 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(DNAEvaluationGraph), true)]
|
||||
public class DNAEvaluationGraphPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
|
||||
private DNAEvaluationGraph.EditorHelper _helper = new DNAEvaluationGraph.EditorHelper();
|
||||
|
||||
private DNAEvaluationGraph.EditorHelper _drawhelper = new DNAEvaluationGraph.EditorHelper();
|
||||
|
||||
private bool initialized = false;
|
||||
|
||||
private static GUIStyle graphLabelStyle;
|
||||
|
||||
private string cachedTooltip = "";
|
||||
|
||||
private float popupIconSize = 20f;
|
||||
|
||||
private static Color _bgColor = new Color(0.337f, 0.337f, 0.337f, 1f);
|
||||
|
||||
private static Color _bgColorHovered = new Color(0.337f, 0.45f, 0.337f, 1f);
|
||||
|
||||
private static Color _bgColorSelected = new Color(0.337f, 0.55f, 0.337f, 1f);
|
||||
|
||||
private static DNAEvaluationGraphPopupContent _popupContent;
|
||||
|
||||
//Could be used to make all swatches show a -1f -> +1f range- isn't right now
|
||||
private Rect rangesRect;
|
||||
|
||||
private bool changed = false;
|
||||
|
||||
private void Init()
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (graphLabelStyle == null)
|
||||
{
|
||||
graphLabelStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel);
|
||||
graphLabelStyle.normal.textColor = Color.white;
|
||||
graphLabelStyle.active.textColor = Color.white;
|
||||
graphLabelStyle.hover.textColor = Color.white;
|
||||
graphLabelStyle.fontStyle = FontStyle.Bold;
|
||||
}
|
||||
initialized = true;
|
||||
rangesRect = new Rect();
|
||||
rangesRect.xMin = 0f;
|
||||
rangesRect.xMax = 1f;
|
||||
rangesRect.yMin = -1f;
|
||||
rangesRect.yMax = 1f;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
Init();
|
||||
|
||||
label = EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
CopyValuesToHelper(property, _helper);
|
||||
|
||||
if ((_helper._graph == null || _helper._graph.keys.Length == 0) && string.IsNullOrEmpty(_helper._name))
|
||||
{
|
||||
AddDefaultValues();
|
||||
}
|
||||
|
||||
//Dont think we can cache the tooltip so always do this
|
||||
UpdateCachedToolTip(_helper.Target);
|
||||
|
||||
var fieldRect = EditorGUI.PrefixLabel(position, label);
|
||||
|
||||
GUI.SetNextControlName("DNAEvaluationGraph");
|
||||
|
||||
var prevIndent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
DrawPopup(fieldRect, property);
|
||||
|
||||
EditorGUI.indentLevel = prevIndent;
|
||||
|
||||
CopyValuesFromHelper(property, _helper);
|
||||
|
||||
if (changed)
|
||||
{
|
||||
GUI.changed = true;
|
||||
changed = false;
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
private void UpdateCachedToolTip(DNAEvaluationGraph graph)
|
||||
{
|
||||
cachedTooltip = DNAEvaluationGraphPresetLibrary.GetTooltipFor(graph);
|
||||
}
|
||||
|
||||
private void DrawPopup(Rect position, SerializedProperty property)
|
||||
{
|
||||
var popupIconRect = new Rect((position.xMax - popupIconSize) + 1f, position.yMin, popupIconSize - 2f, position.height + 1f);
|
||||
var contentRect = new Rect(position.xMin + 1f, position.yMin + 0.5f, position.width - popupIconSize +3f, position.height - 2f);
|
||||
var overlayRect = new Rect(position.xMin, position.yMin - 1f, position.width, position.height);
|
||||
|
||||
//make this look like a popup that has this graph swatch in
|
||||
//draw a popup style background-this makes the field look highlighted (in blue)
|
||||
EditorGUI.LabelField(position, GUIContent.none, EditorStyles.miniButton);
|
||||
|
||||
//draw a little popup icon aswell
|
||||
EditorGUI.LabelField(popupIconRect, GUIContent.none, EditorStyles.popup);
|
||||
|
||||
//now draw the graph swatch in the center of that (there is an optional rangesRect param that we could add here if we always wanted to draw the swatch with a vertical axis going fro -1f -> 1f)
|
||||
EditorGUIUtility.DrawCurveSwatch(contentRect, _helper._graph, null, new Color(1f, 1f, 0f, 0.7f), new Color(0.337f, 0.337f, 0.337f, 1f));
|
||||
|
||||
//now draw a button over the whole thing, with a label for the selected swatch, that will open up the swatch selector when its clicked
|
||||
EditorGUI.DropShadowLabel(overlayRect, new GUIContent(_helper._name), graphLabelStyle);
|
||||
if (GUI.Button(overlayRect, new GUIContent(_helper._name, cachedTooltip), graphLabelStyle))
|
||||
{
|
||||
GUI.FocusControl("DNAEvaluationGraph");
|
||||
|
||||
//Deal with missing settings
|
||||
//If this field contains a graph that is no longer in any preset libraries
|
||||
//they wont be able to get that graph back unless they add it to a library
|
||||
//so show a warning dialog giving them the option of doing that
|
||||
if (_helper.Target != null && !DNAEvaluationGraphPresetLibrary.AllGraphPresets.Contains(_helper.Target))
|
||||
{
|
||||
var _missingGraphChoice = EditorUtility.DisplayDialogComplex("Missing Preset", "The graph " + _helper.Target.name + " was not in any preset libraries in the project. If you change the graph this field is using you wont be able to select " + _helper.Target.name + " again. What would you like to do?", "Change Anyway", "Store and Change", "Cancel");
|
||||
if (_missingGraphChoice == 1)
|
||||
{
|
||||
Debug.Log("_missingGraphChoice == 1");
|
||||
//store and change
|
||||
//add to the first found lib and then carry on
|
||||
DNAEvaluationGraphPresetLibrary.AddNewPreset(_helper.Target.name, new AnimationCurve(_helper.Target.GraphKeys), _helper.Target.name);
|
||||
}
|
||||
else if(_missingGraphChoice == 2)
|
||||
{
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
//WHY is the content drawing disabled in play mode??!!??
|
||||
var prevEnabled = GUI.enabled;
|
||||
GUI.enabled = true;
|
||||
if (_popupContent == null)
|
||||
{
|
||||
_popupContent = new DNAEvaluationGraphPopupContent();
|
||||
}
|
||||
|
||||
_popupContent.width = overlayRect.width;
|
||||
_popupContent.selectedPreset = new DNAEvaluationGraph(_helper.Target);
|
||||
_popupContent.property = property;
|
||||
_popupContent.OnSelected = PopupCallback;
|
||||
|
||||
PopupWindow.Show(overlayRect, _popupContent);
|
||||
GUI.enabled = prevEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
private void PopupCallback(DNAEvaluationGraph selectedGraph, SerializedProperty property)
|
||||
{
|
||||
_helper = new DNAEvaluationGraph.EditorHelper(selectedGraph);
|
||||
CopyValuesFromHelper(property, _helper);
|
||||
UpdateCachedToolTip(_helper.Target);
|
||||
GUI.changed = true;
|
||||
//store the change for the next update
|
||||
changed = true;
|
||||
GUI.FocusControl("DNAEvaluationGraph");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Draws a swatch for the given DNAEvaluationGraph at the given position.
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="dnaGraph"></param>
|
||||
/// <param name="tooltip">An optional tooltip for when the swatch is hovered</param>
|
||||
/// <param name="hovered">Draw the swatch is hovered</param>
|
||||
/// <param name="selected">Draw the swatch is selected</param>
|
||||
/// <param name="callback">An optional callback to trigger when the swatch is clicked</param>
|
||||
public void DrawSwatch(Rect position, DNAEvaluationGraph dnaGraph, string tooltip = "", bool hovered = false, bool selected = false, System.Action<DNAEvaluationGraph> callback = null)
|
||||
{
|
||||
Init();
|
||||
|
||||
_drawhelper = new DNAEvaluationGraph.EditorHelper(dnaGraph);
|
||||
|
||||
var thisBgColor = hovered ? _bgColorHovered : (selected ? _bgColorSelected : _bgColor);
|
||||
EditorGUIUtility.DrawCurveSwatch(position, _drawhelper._graph, null, new Color(1f, 1f, 0f, 0.7f), thisBgColor);
|
||||
var overlayRect = new Rect(position.xMin, position.yMin - 1f, position.width, position.height);
|
||||
|
||||
EditorGUI.DropShadowLabel(overlayRect, new GUIContent(dnaGraph.name), graphLabelStyle);
|
||||
|
||||
if (GUI.Button(overlayRect, new GUIContent(dnaGraph.name, tooltip), graphLabelStyle))
|
||||
{
|
||||
if (callback != null)
|
||||
{
|
||||
callback(dnaGraph);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDefaultValues()
|
||||
{
|
||||
_helper = new DNAEvaluationGraph.EditorHelper(DNAEvaluationGraph.Default);
|
||||
}
|
||||
|
||||
private void AddDefaultValues(SerializedProperty property)
|
||||
{
|
||||
_helper = new DNAEvaluationGraph.EditorHelper(DNAEvaluationGraph.Default);
|
||||
|
||||
CopyValuesFromHelper(property, _helper);
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
return EditorGUIUtility.singleLineHeight /*+ (EditorGUIUtility.standardVerticalSpacing *2f)*/;
|
||||
}
|
||||
|
||||
#region Static Utils
|
||||
|
||||
//DNAEvaluationGraph is simpler now so the _helper isn't really needed- its just for setting the graph directly really
|
||||
private static void CopyValuesToHelper(SerializedProperty property, DNAEvaluationGraph.EditorHelper helper)
|
||||
{
|
||||
helper._name = property.FindPropertyRelative("_name").stringValue;
|
||||
helper._graph = property.FindPropertyRelative("_graph").animationCurveValue;
|
||||
}
|
||||
|
||||
private static void CopyValuesFromHelper(SerializedProperty property, DNAEvaluationGraph.EditorHelper helper)
|
||||
{
|
||||
property.FindPropertyRelative("_name").stringValue = helper._name;
|
||||
property.FindPropertyRelative("_graph").animationCurveValue = helper._graph;
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
//property.serializedObject.Update();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb3f1200ce0e23448b08365bba5ff16f
|
||||
timeCreated: 1537559014
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DNAEvaluationGraphPropertyDrawer.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,290 @@
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(DNAEvaluatorList), true)]
|
||||
public class DNAEvaluatorListPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
|
||||
private const string DNAEVALUATORSPROPERTY = "_dnaEvaluators";
|
||||
private const string AGGREGATIONMETHODPROPERTY = "_aggregationMethod";
|
||||
|
||||
private SerializedProperty _property;
|
||||
|
||||
private GUIContent _propertyLabel;
|
||||
|
||||
private float _padding = EditorGUIUtility.standardVerticalSpacing;
|
||||
|
||||
private ReorderableList _dnaEvaluatorList;
|
||||
|
||||
private DNAEvaluatorPropertyDrawer _dnaEvaluatorDrawer = new DNAEvaluatorPropertyDrawer();
|
||||
|
||||
//private bool _drawAsReorderableList = true;
|
||||
|
||||
private DNAEvaluatorList.ConfigAttribute.LabelOptions _labelOption = DNAEvaluatorList.ConfigAttribute.LabelOptions.drawLabelAsFoldout;
|
||||
|
||||
private DNAEvaluationGraph _defaultGraph = null;
|
||||
|
||||
private bool _manuallyConfigured = false;
|
||||
|
||||
private GUIStyle _aggregationLabelStyle;
|
||||
|
||||
ReorderableList.Defaults ROLDefaults;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
//If the list aggregationMode is 'Cumulative' draw the calc options
|
||||
private bool drawCalcOption = false;
|
||||
|
||||
public DNAEvaluatorList.ConfigAttribute.LabelOptions LabelOption
|
||||
{
|
||||
get { return _labelOption; }
|
||||
set {
|
||||
_labelOption = value;
|
||||
_manuallyConfigured = true;
|
||||
}
|
||||
}
|
||||
//TODO Impliment this
|
||||
/// <summary>
|
||||
/// Not Implimented
|
||||
/// </summary>
|
||||
public DNAEvaluationGraph DefaultGraph
|
||||
{
|
||||
get { return _defaultGraph; }
|
||||
set {
|
||||
_defaultGraph = value;
|
||||
if (_defaultGraph != null)
|
||||
{
|
||||
_manuallyConfigured = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_manuallyConfigured)
|
||||
{
|
||||
if (this.fieldInfo != null)
|
||||
{
|
||||
var attrib = this.fieldInfo.GetCustomAttributes(typeof(DNAEvaluatorList.ConfigAttribute), true).FirstOrDefault() as DNAEvaluatorList.ConfigAttribute;
|
||||
if (attrib != null)
|
||||
{
|
||||
_labelOption = attrib.labelOption;
|
||||
_defaultGraph = attrib.defaultGraph;
|
||||
}
|
||||
}
|
||||
}
|
||||
_aggregationLabelStyle = new GUIStyle(EditorStyles.label);
|
||||
_aggregationLabelStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel);
|
||||
_aggregationLabelStyle.alignment = TextAnchor.MiddleLeft;
|
||||
|
||||
if (ROLDefaults == null)
|
||||
{
|
||||
ROLDefaults = new ReorderableList.Defaults();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
Init();
|
||||
|
||||
if (property.isExpanded
|
||||
|| _labelOption == DNAEvaluatorList.ConfigAttribute.LabelOptions.drawExpandedNoLabel
|
||||
|| _labelOption == DNAEvaluatorList.ConfigAttribute.LabelOptions.drawExpandedWithLabel)
|
||||
{
|
||||
var dnaEvalListProp = property.FindPropertyRelative(DNAEVALUATORSPROPERTY);
|
||||
float h = 0f;
|
||||
|
||||
if(_labelOption == DNAEvaluatorList.ConfigAttribute.LabelOptions.drawExpandedWithLabel
|
||||
|| _labelOption == DNAEvaluatorList.ConfigAttribute.LabelOptions.drawLabelAsFoldout)
|
||||
{
|
||||
h = (EditorGUIUtility.singleLineHeight + (_padding)) * 3;
|
||||
}
|
||||
|
||||
if (dnaEvalListProp.arraySize > 0)
|
||||
{
|
||||
//we only show the aggregation method if there is more than one and that makes the footer higher
|
||||
if(dnaEvalListProp.arraySize > 1)
|
||||
{
|
||||
h += _padding * 3;
|
||||
}
|
||||
for (int i = 0; i < dnaEvalListProp.arraySize; i++)
|
||||
{
|
||||
h += EditorGUIUtility.singleLineHeight + (_padding * 2)+1f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
h += EditorGUIUtility.singleLineHeight + (_padding);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EditorGUI.GetPropertyHeight(property, true);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
|
||||
_propertyLabel = label = EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
_property = property;
|
||||
|
||||
Init();
|
||||
|
||||
var aggregationProp = property.FindPropertyRelative(AGGREGATIONMETHODPROPERTY);
|
||||
if (aggregationProp.enumValueIndex == 1)//Cumulative
|
||||
{
|
||||
drawCalcOption = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
drawCalcOption = false;
|
||||
}
|
||||
|
||||
DrawReorderableList(position, property, label);
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
private void DrawReorderableList(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
var labelRect = new Rect(position.xMin, position.yMin, position.width, 0f);
|
||||
if (_labelOption == DNAEvaluatorList.ConfigAttribute.LabelOptions.drawLabelAsFoldout)
|
||||
{
|
||||
labelRect = new Rect(position.xMin, position.yMin, position.width, EditorGUIUtility.singleLineHeight);
|
||||
property.isExpanded = EditorGUI.Foldout(labelRect, property.isExpanded, label);
|
||||
}
|
||||
else if(_labelOption == DNAEvaluatorList.ConfigAttribute.LabelOptions.drawExpandedWithLabel)
|
||||
{
|
||||
labelRect = new Rect(position.xMin, position.yMin, position.width, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.LabelField(labelRect, label);
|
||||
}
|
||||
if (property.isExpanded || _labelOption == DNAEvaluatorList.ConfigAttribute.LabelOptions.drawExpandedWithLabel || _labelOption == DNAEvaluatorList.ConfigAttribute.LabelOptions.drawExpandedNoLabel)
|
||||
{
|
||||
var contentRect = EditorGUI.IndentedRect(position);
|
||||
contentRect.yMin = labelRect.yMax + _padding;
|
||||
contentRect.height = contentRect.height - labelRect.height;
|
||||
|
||||
var dnaEvalListProp = property.FindPropertyRelative(DNAEVALUATORSPROPERTY);
|
||||
|
||||
_dnaEvaluatorList = CachedReorderableList.GetListDrawer(dnaEvalListProp, DrawHeaderCallback, null, DrawElementCallback, DrawFooterCallback);
|
||||
|
||||
_dnaEvaluatorList.DoList(contentRect);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawHeaderCallback(Rect rect)
|
||||
{
|
||||
_dnaEvaluatorDrawer.DrawLabels = false;
|
||||
_dnaEvaluatorDrawer.DrawCalcOption = _dnaEvaluatorList.count > 1 ? drawCalcOption : false;
|
||||
if (_labelOption == DNAEvaluatorList.ConfigAttribute.LabelOptions.drawExpandedNoLabel)
|
||||
{
|
||||
_dnaEvaluatorDrawer.DoLabelsInline(rect, _propertyLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dnaEvaluatorDrawer.DoLabelsInline(rect);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawFooterCallback(Rect rect)
|
||||
{
|
||||
float xMax = rect.xMax;
|
||||
float availWidth = xMax - 60f;
|
||||
//if the view is wide this only needs to be XMax - 300 else its Xmin
|
||||
float bgXmin = rect.xMax - 300f > rect.xMin ? rect.xMax - 300f : rect.xMin;
|
||||
Rect bgRect = new Rect(bgXmin, rect.yMin, rect.width, rect.height);
|
||||
bgRect.xMax = rect.xMax;
|
||||
float agWidthMod = 0f;
|
||||
//If the list count is greater than 1 we need to draw the background for the aggregation controls
|
||||
if (_dnaEvaluatorList.count > 1)
|
||||
{
|
||||
agWidthMod = 4f;
|
||||
}
|
||||
rect = new Rect(availWidth, rect.y, xMax - availWidth - agWidthMod, rect.height);
|
||||
Rect rect2 = new Rect(availWidth + 4f, rect.y - 3f, 25f, 13f);
|
||||
Rect rect3 = new Rect(xMax - 33f, rect.y - 3f, 25f, 13f);
|
||||
Rect agRect = new Rect(bgRect.xMin + 8f, bgRect.yMin, (bgRect.width - rect.width) -16f, bgRect.height);
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
{
|
||||
var prevFooterFixedHeight = ROLDefaults.footerBackground.fixedHeight;
|
||||
var addMinusHeight = prevFooterFixedHeight;
|
||||
//If the list count is greater than 1 we need to draw the background for the aggregation controls
|
||||
if (_dnaEvaluatorList.count > 1)
|
||||
{
|
||||
//usually 13f but we need it higher to hold aggregation controls
|
||||
ROLDefaults.footerBackground.fixedHeight = 13f + (EditorGUIUtility.standardVerticalSpacing * 3);
|
||||
ROLDefaults.footerBackground.Draw(bgRect, false, false, false, false);
|
||||
addMinusHeight += 2f;
|
||||
}
|
||||
//now draw the standard background for the +/- controls
|
||||
ROLDefaults.footerBackground.fixedHeight = addMinusHeight;
|
||||
ROLDefaults.footerBackground.Draw(rect, false, false, false, false);
|
||||
ROLDefaults.footerBackground.fixedHeight = prevFooterFixedHeight;
|
||||
}
|
||||
if (GUI.Button(rect2, ROLDefaults.iconToolbarPlus, ROLDefaults.preButton))
|
||||
{
|
||||
ROLDefaults.DoAddButton(_dnaEvaluatorList);
|
||||
}
|
||||
using (new EditorGUI.DisabledScope(_dnaEvaluatorList.index < 0 || _dnaEvaluatorList.index >= _dnaEvaluatorList.count))
|
||||
{
|
||||
if (GUI.Button(rect3, ROLDefaults.iconToolbarMinus, ROLDefaults.preButton))
|
||||
{
|
||||
ROLDefaults.DoRemoveButton(_dnaEvaluatorList);
|
||||
}
|
||||
}
|
||||
var prevIndentLevel = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
//If the list count is greater than 1 we need to draw the background for the aggregation controls
|
||||
if (_dnaEvaluatorList.count > 1)
|
||||
{
|
||||
DrawAggregationMethod(agRect, _property);
|
||||
}
|
||||
EditorGUI.indentLevel = prevIndentLevel;
|
||||
}
|
||||
|
||||
//GetArrayElementAtIndex is slow, we need to cache the results the same way DNAPluginsDrawerer does
|
||||
private void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
_dnaEvaluatorDrawer.DrawLabels = false;
|
||||
var dnaEvalListProp = _dnaEvaluatorList.serializedProperty;
|
||||
var entryRect = new Rect(rect.xMin, rect.yMin + _padding, rect.width, rect.height );
|
||||
_dnaEvaluatorDrawer.DrawCalcOption = _dnaEvaluatorList.count > 1 ? drawCalcOption : false;
|
||||
_dnaEvaluatorDrawer.DoFieldsInline(entryRect, dnaEvalListProp.GetArrayElementAtIndex(index));
|
||||
}
|
||||
|
||||
private Rect DrawAggregationMethod(Rect position, SerializedProperty property)
|
||||
{
|
||||
var labelRectMinWidth = 110f;
|
||||
var popupMinWidth = 60f;
|
||||
var aggregationProp = property.FindPropertyRelative(AGGREGATIONMETHODPROPERTY);
|
||||
var aggregationRect = new Rect(position.xMin, position.yMin, position.width, EditorGUIUtility.singleLineHeight);
|
||||
var labelRectWidth = aggregationRect.width - labelRectMinWidth < popupMinWidth ? aggregationRect.width / 2f : labelRectMinWidth;
|
||||
var popupRectWidth = aggregationRect.width - labelRectMinWidth < popupMinWidth ? aggregationRect.width / 2f : aggregationRect.width - labelRectWidth;
|
||||
var labelRect = new Rect(aggregationRect.xMin, aggregationRect.yMin, labelRectWidth, aggregationRect.height);
|
||||
var popupRect = new Rect(labelRect.xMax, aggregationRect.yMin, popupRectWidth, aggregationRect.height);
|
||||
|
||||
var label = EditorGUI.BeginProperty(labelRect, new GUIContent(aggregationProp.displayName), aggregationProp);
|
||||
EditorGUI.LabelField(labelRect, label, _aggregationLabelStyle);
|
||||
EditorGUI.PropertyField(popupRect, aggregationProp, GUIContent.none);
|
||||
EditorGUI.EndProperty();
|
||||
|
||||
var retRect = new Rect(position.xMin, aggregationRect.yMax + _padding, position.width, position.height - (EditorGUIUtility.singleLineHeight + _padding));
|
||||
return retRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b2bb9b5689baff449ad4173ade43ec2
|
||||
timeCreated: 1538094534
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DNAEvaluatorListPropertyDrawer.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,346 @@
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(DNAEvaluator), true)]
|
||||
public class DNAEvaluatorPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
private const string CALCOPTIONPROPERTY = "_calcOption";
|
||||
private const string DNANAMEPROPERTY = "_dnaName";
|
||||
private const string DNANAMEHASHPROPERTY = "_dnaNameHash";
|
||||
private const string EVALUATORPROPERTY = "_evaluator";
|
||||
private const string MULTIPLIERPROPERTY = "_multiplier";
|
||||
|
||||
private const string DNANAMELABEL = "DNA Name";
|
||||
private const string EVALUATORLABEL = "Evaluator";
|
||||
private const string MULTIPLIERLABEL = "Multiplier";
|
||||
private const string CALCOPTIONMINILABEL = "\u01A9";
|
||||
|
||||
private DNAEvaluator _target;
|
||||
|
||||
private bool _drawLabels = true;
|
||||
private bool _alwaysExpanded = false;
|
||||
private bool _drawCalcOption = false;
|
||||
|
||||
private float _calcOptionWidth = 25f;
|
||||
private GUIContent _calcOptionHeaderLabel = new GUIContent("\u03A3", "Define how the evaluated value will be combined with the previous Evaluator in the list.");
|
||||
private GUIContent[] _calcOptionMiniLabels = new GUIContent[] { new GUIContent("+", "Add"), new GUIContent("-", "Subtract"), new GUIContent("\u0078", "Multiply"), new GUIContent("\u00F7", "Divide") };
|
||||
|
||||
private float _multiplierLabelWidth = 55f;
|
||||
private Vector2 _dnaToEvaluatorRatio = new Vector2(2f, 3f);
|
||||
private float _padding = 2f;
|
||||
private bool _manuallyConfigured = false;
|
||||
|
||||
//if this is drawn in a DynamicDNAPlugin it should give us dna names to choose from
|
||||
private DynamicDNAPlugin _dynamicDNAPlugin;
|
||||
|
||||
private bool initialized = false;
|
||||
|
||||
public bool DrawLabels
|
||||
{
|
||||
set
|
||||
{
|
||||
_drawLabels = value;
|
||||
_manuallyConfigured = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AlwaysExpanded
|
||||
{
|
||||
set
|
||||
{
|
||||
_alwaysExpanded = value;
|
||||
_manuallyConfigured = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DrawCalcOption
|
||||
{
|
||||
set
|
||||
{
|
||||
_drawCalcOption = value;
|
||||
_manuallyConfigured = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_manuallyConfigured)
|
||||
{
|
||||
if (this.fieldInfo != null)
|
||||
{
|
||||
var attrib = this.fieldInfo.GetCustomAttributes(typeof(DNAEvaluator.ConfigAttribute), true).FirstOrDefault() as DNAEvaluator.ConfigAttribute;
|
||||
if (attrib != null)
|
||||
{
|
||||
_drawLabels = attrib.drawLabels;
|
||||
_alwaysExpanded = attrib.alwaysExpanded;
|
||||
_drawCalcOption = attrib.drawCalcOption;
|
||||
}
|
||||
}
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
Event current = Event.current;
|
||||
label = EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
//Try and get a DNAAsset from the serializedObject- this is used for showing a popup in the dna field rather than a text field
|
||||
CheckDynamicDNAPlugin(property);
|
||||
|
||||
Init();
|
||||
|
||||
if (!_alwaysExpanded)
|
||||
{
|
||||
var foldoutPos = new Rect(position.xMin, position.yMin, position.width, EditorGUIUtility.singleLineHeight);
|
||||
property.isExpanded = EditorGUI.Foldout(foldoutPos, property.isExpanded, label);
|
||||
}
|
||||
var reorderableListDefaults = new ReorderableList.Defaults();
|
||||
|
||||
if (property.isExpanded || _alwaysExpanded)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
position = EditorGUI.IndentedRect(position);
|
||||
if (!_alwaysExpanded)
|
||||
{
|
||||
position.yMin = position.yMin + EditorGUIUtility.singleLineHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
position.yMin += 2f;
|
||||
}
|
||||
|
||||
position.xMin -= 15f;//make it the same width as a reorderable list
|
||||
if (_drawLabels)
|
||||
{
|
||||
//can we draw this so it looks like the header of a reorderable List?
|
||||
if (current.type == EventType.Repaint)
|
||||
{
|
||||
reorderableListDefaults.headerBackground.Draw(position, GUIContent.none, false, false, false, false);
|
||||
}
|
||||
|
||||
var rect1 = new Rect(position.xMin + 6f, position.yMin + 1f, position.width - 12f, position.height);
|
||||
if (_alwaysExpanded)
|
||||
{
|
||||
position = DoLabelsInline(rect1, label);
|
||||
}
|
||||
else
|
||||
{
|
||||
position = DoLabelsInline(rect1, DNANAMELABEL);
|
||||
}
|
||||
|
||||
position.xMin -= 6f;
|
||||
position.width += 6f;
|
||||
position.yMin -= 1f;
|
||||
position.height -= 3f;
|
||||
}
|
||||
if (current.type == EventType.Repaint)
|
||||
{
|
||||
reorderableListDefaults.boxBackground.Draw(position, GUIContent.none, false, false, false, false);
|
||||
}
|
||||
|
||||
var rect2 = new Rect(position.xMin + 6f, position.yMin + 3f, position.width - 12f, position.height);
|
||||
DoFieldsInline(rect2, property);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
public Rect DoLabelsInline(Rect position, string label1 = DNANAMELABEL, string label2 = EVALUATORLABEL, string label3 = MULTIPLIERLABEL)
|
||||
{
|
||||
return DoLabelsInline(position, new GUIContent(label1, GetChildTooltip(DNANAMEPROPERTY)), new GUIContent(label2, GetChildTooltip(EVALUATORPROPERTY)), new GUIContent(label3, GetChildTooltip(MULTIPLIERPROPERTY)));
|
||||
}
|
||||
|
||||
public Rect DoLabelsInline(Rect position, GUIContent label1, GUIContent label2 = null, GUIContent label3 = null)
|
||||
{
|
||||
if (label2 == null)
|
||||
{
|
||||
label2 = new GUIContent(EVALUATORLABEL, GetChildTooltip(EVALUATORPROPERTY));
|
||||
}
|
||||
|
||||
if (label3 == null)
|
||||
{
|
||||
label3 = new GUIContent(MULTIPLIERLABEL, GetChildTooltip(MULTIPLIERPROPERTY));
|
||||
}
|
||||
|
||||
var prevIndent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
Rect calcOptionRect = Rect.zero;
|
||||
if (_drawCalcOption)
|
||||
{
|
||||
calcOptionRect = new Rect(position.xMin+15f, position.yMin, _calcOptionWidth, EditorGUIUtility.singleLineHeight);
|
||||
position.xMin = calcOptionRect.xMax;
|
||||
}
|
||||
var fieldbaseRatio = (position.width - _multiplierLabelWidth) / (_dnaToEvaluatorRatio.x + _dnaToEvaluatorRatio.y);
|
||||
var dnafieldWidth = fieldbaseRatio * _dnaToEvaluatorRatio.x;
|
||||
var evaluatorFieldWidth = fieldbaseRatio * _dnaToEvaluatorRatio.y;
|
||||
|
||||
var dnaNameLabelRect = new Rect(position.xMin, position.yMin, dnafieldWidth, EditorGUIUtility.singleLineHeight);
|
||||
var evaluatorLabelRect = new Rect(dnaNameLabelRect.xMax, position.yMin, evaluatorFieldWidth, EditorGUIUtility.singleLineHeight);
|
||||
var multiplierLabelRect = new Rect(evaluatorLabelRect.xMax, position.yMin, _multiplierLabelWidth, EditorGUIUtility.singleLineHeight);
|
||||
if (_drawCalcOption)
|
||||
{
|
||||
EditorGUI.LabelField(calcOptionRect, _calcOptionHeaderLabel, EditorStyles.centeredGreyMiniLabel);
|
||||
}
|
||||
EditorGUI.LabelField(dnaNameLabelRect, label1, EditorStyles.centeredGreyMiniLabel);
|
||||
EditorGUI.LabelField(evaluatorLabelRect, label2, EditorStyles.centeredGreyMiniLabel);
|
||||
EditorGUI.LabelField(multiplierLabelRect, label3, EditorStyles.centeredGreyMiniLabel);
|
||||
position.yMin = dnaNameLabelRect.yMax + 2f;
|
||||
|
||||
EditorGUI.indentLevel = prevIndent;
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a DNAEvaluator with inline styling
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="property"></param>
|
||||
/// <param name="label"></param>
|
||||
public void DoFieldsInline(Rect position, SerializedProperty property)
|
||||
{
|
||||
CheckDynamicDNAPlugin(property);
|
||||
|
||||
Init();
|
||||
|
||||
var prevIndent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
Rect calcOptionRect = Rect.zero;
|
||||
if (_drawCalcOption)
|
||||
{
|
||||
calcOptionRect = new Rect(position.xMin, position.yMin, _calcOptionWidth, EditorGUIUtility.singleLineHeight);
|
||||
position.xMin = calcOptionRect.xMax;
|
||||
}
|
||||
var calcOptionProp = property.FindPropertyRelative(CALCOPTIONPROPERTY);
|
||||
var dnaNameProp = property.FindPropertyRelative(DNANAMEPROPERTY);
|
||||
var dnaNameHashProp = property.FindPropertyRelative(DNANAMEHASHPROPERTY);
|
||||
var evaluatorProp = property.FindPropertyRelative(EVALUATORPROPERTY);
|
||||
var intensityProp = property.FindPropertyRelative(MULTIPLIERPROPERTY);
|
||||
var fieldbaseRatio = (position.width - _multiplierLabelWidth) / (_dnaToEvaluatorRatio.x + _dnaToEvaluatorRatio.y);
|
||||
var dnafieldWidth = fieldbaseRatio * _dnaToEvaluatorRatio.x;
|
||||
var evaluatorFieldWidth = fieldbaseRatio * _dnaToEvaluatorRatio.y;
|
||||
|
||||
position.height = EditorGUIUtility.singleLineHeight;//theres a space at the bottom so cut that off
|
||||
|
||||
var dnaNameRect = new Rect(position.xMin + _padding, position.yMin, dnafieldWidth - (_padding * 2), position.height);
|
||||
var evaluatorRect = new Rect(dnaNameRect.xMax + (_padding * 2), position.yMin, evaluatorFieldWidth - (_padding * 2), position.height);
|
||||
var multiplierRect = new Rect(evaluatorRect.xMax + (_padding * 2), position.yMin, _multiplierLabelWidth - (_padding * 2), position.height);
|
||||
|
||||
if (_drawCalcOption)
|
||||
{
|
||||
calcOptionProp.enumValueIndex = EditorGUI.Popup(calcOptionRect, calcOptionProp.enumValueIndex, _calcOptionMiniLabels);
|
||||
}
|
||||
EditorGUI.BeginChangeCheck();
|
||||
if (_dynamicDNAPlugin == null)
|
||||
{
|
||||
EditorGUI.PropertyField(dnaNameRect, dnaNameProp, GUIContent.none);
|
||||
}
|
||||
else
|
||||
{
|
||||
DynamicDNAConverterControllerInspector.DNANamesPopup(dnaNameRect, dnaNameProp, dnaNameProp.stringValue, _dynamicDNAPlugin.converterController.DNAAsset);
|
||||
}
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dnaNameProp.stringValue))
|
||||
{
|
||||
dnaNameHashProp.intValue = UMAUtils.StringToHash(dnaNameProp.stringValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
dnaNameHashProp.intValue = -1;
|
||||
}
|
||||
}
|
||||
EditorGUI.BeginDisabledGroup(string.IsNullOrEmpty(dnaNameProp.stringValue));
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUI.PropertyField(evaluatorRect, evaluatorProp, GUIContent.none);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
InspectorUtlity.RepaintAllInspectors();
|
||||
GUI.changed = true;
|
||||
}
|
||||
EditorGUI.PropertyField(multiplierRect, intensityProp, GUIContent.none);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
EditorGUI.indentLevel = prevIndent;
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (!_manuallyConfigured)
|
||||
{
|
||||
if (this.fieldInfo != null)
|
||||
{
|
||||
var attrib = this.fieldInfo.GetCustomAttributes(typeof(DNAEvaluator.ConfigAttribute), true).FirstOrDefault() as DNAEvaluator.ConfigAttribute;
|
||||
if (attrib != null)
|
||||
{
|
||||
_drawLabels = attrib.drawLabels;
|
||||
_alwaysExpanded = attrib.alwaysExpanded;
|
||||
_drawCalcOption = attrib.drawCalcOption;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (property.isExpanded || _alwaysExpanded)
|
||||
{
|
||||
if (_drawLabels)
|
||||
{
|
||||
return EditorGUIUtility.singleLineHeight * (_alwaysExpanded ? 2f : 3f) + (_padding * 4f) + 6f;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EditorGUIUtility.singleLineHeight * (_alwaysExpanded ? 1f : 2f) + (_padding * 4f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return EditorGUI.GetPropertyHeight(property, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// if this property is in a DynamicDNAPlugin this will find it so we can use its DNAAsset etc
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
private void CheckDynamicDNAPlugin(SerializedProperty property)
|
||||
{
|
||||
if (typeof(DynamicDNAPlugin).IsAssignableFrom((property.serializedObject.targetObject).GetType()))
|
||||
{
|
||||
_dynamicDNAPlugin = (DynamicDNAPlugin)property.serializedObject.targetObject;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tooltip from the 'Tooltip' attribute defined in the type class (if set)
|
||||
/// </summary>
|
||||
private string GetChildTooltip(SerializedProperty property)
|
||||
{
|
||||
|
||||
return GetChildTooltip(property.name);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the tooltip from the 'Tooltip' attribute defined in the type class (if set)
|
||||
/// </summary>
|
||||
private string GetChildTooltip(string propertyName)
|
||||
{
|
||||
|
||||
TooltipAttribute[] attributes = typeof(DNAEvaluator).GetField(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).GetCustomAttributes(typeof(TooltipAttribute), true) as TooltipAttribute[];
|
||||
|
||||
return attributes.Length > 0 ? attributes[0].tooltip : "";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cf0293b13cc72a479ff5bc818575523
|
||||
timeCreated: 1538075214
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DNAEvaluatorPropertyDrawer.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,221 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(DNARangeAsset))]
|
||||
public class DNARangeInspector : Editor
|
||||
{
|
||||
[MenuItem("Assets/Create/UMA/DNA/Legacy/DNA Range Asset")]
|
||||
public static void CreateOverlayMenuItem()
|
||||
{
|
||||
CustomAssetUtility.CreateAsset<DNARangeAsset>();
|
||||
}
|
||||
|
||||
private DNARangeAsset dnaRange;
|
||||
private UMADnaBase dnaSource;
|
||||
|
||||
private int entryCount = 0;
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
dnaRange = target as DNARangeAsset;
|
||||
GetEntryCount();
|
||||
}
|
||||
//UMA 2.8 FixDNAPrefabs:multiUse function for getting this
|
||||
private void GetEntryCount()
|
||||
{
|
||||
if (dnaRange.dnaConverter != null)
|
||||
{
|
||||
if (dnaRange.dnaConverter.DNAType == typeof(DynamicUMADna))
|
||||
{
|
||||
entryCount = ((IDynamicDNAConverter)dnaRange.dnaConverter).dnaAsset.Names.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
dnaSource = dnaRange.dnaConverter.DNAType.GetConstructor(System.Type.EmptyTypes).Invoke(null) as UMADnaBase;
|
||||
if (dnaSource != null)
|
||||
{
|
||||
|
||||
entryCount = dnaSource.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entryCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds any names in the given replacing converter, that match ones in the original converter
|
||||
/// </summary>
|
||||
/// <param name="originalConverter"></param>
|
||||
/// <param name="replacingConverter"></param>
|
||||
/// <returns>returns a dictionary of matching indexes, where the entry's index is the index in the replacing converter's dna and the entry's value is the corresponding index in the original converter's dna </returns>
|
||||
private Dictionary<int, int> GetMatchingIndexes(IDNAConverter originalConverter, IDNAConverter replacingConverter)
|
||||
{
|
||||
List<string> originalNames = new List<string>();
|
||||
List<string> replacingNames = new List<string>();
|
||||
UMADnaBase originalDNA;
|
||||
UMADnaBase replacingDNA;
|
||||
//original
|
||||
if (originalConverter.DNAType == typeof(DynamicUMADna))
|
||||
{
|
||||
originalNames.AddRange(((IDynamicDNAConverter)originalConverter).dnaAsset.Names);
|
||||
}
|
||||
else
|
||||
{
|
||||
originalDNA = originalConverter.DNAType.GetConstructor(System.Type.EmptyTypes).Invoke(null) as UMADnaBase;
|
||||
if (originalDNA != null)
|
||||
{
|
||||
originalNames.AddRange(originalDNA.Names);
|
||||
}
|
||||
}
|
||||
//replacing
|
||||
if (replacingConverter.DNAType == typeof(DynamicUMADna))
|
||||
{
|
||||
replacingNames.AddRange(((IDynamicDNAConverter)replacingConverter).dnaAsset.Names);
|
||||
}
|
||||
else
|
||||
{
|
||||
replacingDNA = replacingConverter.DNAType.GetConstructor(System.Type.EmptyTypes).Invoke(null) as UMADnaBase;
|
||||
if (replacingDNA != null)
|
||||
{
|
||||
replacingNames.AddRange(replacingDNA.Names);
|
||||
}
|
||||
}
|
||||
Dictionary<int, int> matchingIndexes = new Dictionary<int, int>();
|
||||
for(int i = 0; i < originalNames.Count; i++)
|
||||
{
|
||||
if (replacingNames.Contains(originalNames[i]))
|
||||
{
|
||||
matchingIndexes.Add(i, replacingNames.IndexOf(originalNames[i]));
|
||||
}
|
||||
}
|
||||
return matchingIndexes;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
bool dirty = false;
|
||||
|
||||
var currentSource = dnaRange.dnaConverter;
|
||||
IDNAConverter newSource = currentSource;
|
||||
|
||||
var converterProp = serializedObject.FindProperty("_dnaConverter");
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(converterProp);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
var converterFieldProp = converterProp.FindPropertyRelative("_converter");
|
||||
if (converterFieldProp.objectReferenceValue != null)
|
||||
{
|
||||
newSource = converterFieldProp.objectReferenceValue as IDNAConverter;
|
||||
dnaRange.dnaConverter = newSource;
|
||||
}
|
||||
GetEntryCount();
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
//UMA 2.8 FixDNAPrefabs:Use the propertyField
|
||||
//newSource = EditorGUILayout.ObjectField("DNA Converter", dnaRange.dnaConverter, typeof(DnaConverterBehaviour), true) as DnaConverterBehaviour;
|
||||
|
||||
if (currentSource != newSource)
|
||||
{
|
||||
dnaRange.dnaConverter = newSource;
|
||||
dnaSource = null;
|
||||
//UMA 2.8 FixDNAPrefabs: We want to preserve the settings if we can
|
||||
var matchingIndexes = GetMatchingIndexes(currentSource, newSource);
|
||||
|
||||
var newMeans = new float[entryCount];
|
||||
var newDeviations = new float[entryCount];
|
||||
var newSpreads = new float[entryCount];
|
||||
for (int i = 0; i < entryCount; i++)
|
||||
{
|
||||
if (matchingIndexes.ContainsKey(i))
|
||||
{
|
||||
newMeans[i] = dnaRange.means[matchingIndexes[i]];
|
||||
newDeviations[i] = dnaRange.deviations[matchingIndexes[i]];
|
||||
newSpreads[i] = dnaRange.spreads[matchingIndexes[i]];
|
||||
}
|
||||
else
|
||||
{
|
||||
newMeans[i] = 0.5f;
|
||||
newDeviations[i] = 0.16f;
|
||||
newSpreads[i] = 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
/*dnaRange.means = new float[entryCount];
|
||||
dnaRange.deviations = new float[entryCount];
|
||||
dnaRange.spreads = new float[entryCount];
|
||||
for (int i = 0; i < entryCount; i++)
|
||||
{
|
||||
dnaRange.means[i] = 0.5f;
|
||||
dnaRange.deviations[i] = 0.16f;
|
||||
dnaRange.spreads[i] = 0.5f;
|
||||
}*/
|
||||
|
||||
dnaRange.means = newMeans;
|
||||
dnaRange.deviations = newDeviations;
|
||||
dnaRange.spreads = newSpreads;
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));
|
||||
|
||||
if (dnaRange.dnaConverter != null)
|
||||
{
|
||||
GUILayout.Space(2f);
|
||||
GUIStyle headerStyle = new GUIStyle();
|
||||
headerStyle.alignment = TextAnchor.MiddleCenter;
|
||||
headerStyle.normal.textColor = Color.white;
|
||||
headerStyle.fontSize = 12;
|
||||
EditorGUILayout.LabelField(dnaRange.dnaConverter.DNAType.Name, headerStyle);
|
||||
|
||||
string[] dnaNames;
|
||||
if (dnaRange.dnaConverter.DNAType == typeof(DynamicUMADna))
|
||||
{
|
||||
dnaNames = ((IDynamicDNAConverter)dnaRange.dnaConverter).dnaAsset.Names;
|
||||
}
|
||||
else
|
||||
{
|
||||
dnaNames = dnaSource.Names;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entryCount; i++)
|
||||
{
|
||||
if (i > dnaRange.means.Length -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
float currentMin = dnaRange.means[i] - dnaRange.spreads[i];
|
||||
float currentMax = dnaRange.means[i] + dnaRange.spreads[i];
|
||||
float min = currentMin;
|
||||
float max = currentMax;
|
||||
EditorGUILayout.PrefixLabel(dnaNames[i]);
|
||||
EditorGUILayout.MinMaxSlider(ref min, ref max, 0f, 1f);
|
||||
if ((min != currentMin) || (max != currentMax))
|
||||
{
|
||||
dnaRange.means[i] = (min + max) / 2f;
|
||||
dnaRange.spreads[i] = (max - min) / 2f;
|
||||
dnaRange.deviations[i] = dnaRange.spreads[i] / 3f;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
EditorUtility.SetDirty(dnaRange);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2057943a68754cd2960911a7b53b1f9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DNARangeInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,62 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
public sealed class DictionaryCustomFormatter : IFormatProvider, ICustomFormatter
|
||||
{
|
||||
#region IFormatProvider Members
|
||||
|
||||
public object GetFormat(Type formatType)
|
||||
{
|
||||
if (typeof(ICustomFormatter).Equals(formatType))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICustomFormatter Members
|
||||
|
||||
public string Format(string format, object arg, IFormatProvider formatProvider)
|
||||
{
|
||||
if (arg == null)
|
||||
{
|
||||
throw new ArgumentNullException("arg");
|
||||
}
|
||||
|
||||
var dict = arg as Dictionary<string, object>;
|
||||
if (format != null && dict != null)
|
||||
{
|
||||
object o;
|
||||
if (dict.TryGetValue(format.Trim(), out o))
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
Debug.LogError("data was null: " + format.Trim());
|
||||
}
|
||||
var template = o as CodeGenTemplate;
|
||||
if (template != null)
|
||||
{
|
||||
return template.sb.ToString();
|
||||
}
|
||||
return o.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
if (arg is IFormattable)
|
||||
{
|
||||
return ((IFormattable)arg).ToString(format, formatProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
return arg.ToString();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aaca09c00d404ff4a88766ca53441eef
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DictionaryCustomFormatter.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// DisplayListWindow is a generic dialog window that can be used to display a list of strings.
|
||||
/// To Use, call DisplayListWindow.ShowDialog("title", Pos, StringList);
|
||||
/// </summary>
|
||||
public class DisplayListWindow : EditorWindow
|
||||
{
|
||||
static Vector2 winsize = new Vector2(200, 300);
|
||||
public static void ShowDialog(string Title, Rect parentPos, List<string> Items)
|
||||
{
|
||||
Rect Pos = new Rect(parentPos.center - (winsize / 2), winsize);
|
||||
|
||||
DisplayListWindow window = ScriptableObject.CreateInstance(typeof(DisplayListWindow)) as DisplayListWindow;
|
||||
window.Initialize(Items);
|
||||
window.titleContent = new GUIContent(Title);
|
||||
window.ShowUtility();
|
||||
window.position = Pos;
|
||||
}
|
||||
|
||||
const int itemHeight = 18;
|
||||
public List<string> displayItems = new List<string>();
|
||||
Vector2 scrollPosition = new Vector2(0.0f, 0.0f);
|
||||
|
||||
Rect virtualBox
|
||||
{
|
||||
get { return new Rect(0, 0, position.width - 20, itemHeight * displayItems.Count); }
|
||||
}
|
||||
|
||||
Rect listBox
|
||||
{
|
||||
get { return new Rect(10, 10, position.width - 20, position.height - 40); }
|
||||
}
|
||||
|
||||
Rect buttonBar
|
||||
{
|
||||
get { return new Rect(10, position.height - 30, position.width - 20, 20); }
|
||||
}
|
||||
|
||||
|
||||
public void Initialize(List<string> Items)
|
||||
{
|
||||
displayItems = Items;
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
Rect ItemRect = new Rect(listBox);
|
||||
|
||||
ItemRect.x = 10;
|
||||
ItemRect.width -= 20;
|
||||
ItemRect.y = 0;
|
||||
ItemRect.height = itemHeight;
|
||||
|
||||
// An absolute-positioned example: We make a scrollview that has a really large client
|
||||
// rect and put it in a small rect on the screen.
|
||||
scrollPosition = GUI.BeginScrollView(listBox, scrollPosition, virtualBox);
|
||||
foreach (string s in displayItems)
|
||||
{
|
||||
EditorGUI.LabelField(ItemRect, s);
|
||||
ItemRect.y += itemHeight;
|
||||
}
|
||||
GUI.EndScrollView();
|
||||
|
||||
Rect B = buttonBar;
|
||||
B.x = B.width - 80;
|
||||
B.width = 80;
|
||||
if (GUI.Button(B,"Close"))
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6283c3fd82f017a498e466e16e3ce420
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DisplayListWindow.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,758 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
|
||||
[CustomEditor(typeof(DynamicDNAConverterController),true)]
|
||||
public class DynamicDNAConverterControllerInspector : Editor
|
||||
{
|
||||
|
||||
[MenuItem("Assets/Create/UMA/DNA/Dynamic DNA Converter Controller")]
|
||||
public static void CreateDynamicDNAConverterController()
|
||||
{
|
||||
DynamicDNAConverterController.CreateDynamicDNAConverterControllerAsset();
|
||||
}
|
||||
|
||||
#region FIELDS
|
||||
|
||||
private static DynamicDNAConverterControllerInspector _livePopupEditor;
|
||||
|
||||
public static UnityEvent OnLivePopupEditorChange = new UnityEvent();
|
||||
|
||||
DynamicDNAConverterController _target;
|
||||
|
||||
//if set will be sent to the plugins so they can draw a popup of dnaNames rather than a string field for dna selection if they wish
|
||||
private DynamicUMADnaAsset _dnaAsset;
|
||||
|
||||
//the converter plugins that are available
|
||||
private List<System.Type> _availablePlugins = new List<Type>();
|
||||
|
||||
//The editors for each of the converter plugins, users can make their own editors of they like so long as they descend from DynamicDNAPluginInspector
|
||||
private Dictionary<DynamicDNAPlugin, DynamicDNAPluginInspector> _pluginsEditors = new Dictionary<DynamicDNAPlugin, DynamicDNAPluginInspector>();
|
||||
|
||||
// the type of converter plugin to add (set from the _availablePlugins above)
|
||||
private Type _pluginToAdd;
|
||||
|
||||
private bool _dnaAssetExpanded = true;
|
||||
|
||||
private bool _dnaAssetHelpExpanded = false;
|
||||
|
||||
private bool _convertersExpanded = true;
|
||||
|
||||
private bool _convertersHelpExpanded = false;
|
||||
|
||||
private bool _overallModifiersExpanded = true;
|
||||
|
||||
private bool _overallModifiersHelpExpanded = false;
|
||||
|
||||
//if true 'view by dna name' otherwise 'view by converter type'
|
||||
private bool _view;
|
||||
|
||||
//maintains a dictionary of the dna names that are expanded when in 'view by dna' mode
|
||||
private Dictionary<string, bool> _expandedDNANames = new Dictionary<string, bool>();
|
||||
|
||||
private SerializedProperty _convertersListProp;
|
||||
|
||||
private ReorderableList _convertersROL;
|
||||
|
||||
//stores the search string (if any) when in 'view by dna' mode
|
||||
private string _DNASearchString = "";
|
||||
|
||||
//styles we use
|
||||
private GUIStyle _subHeaderStyle;
|
||||
private Texture _helpIcon;
|
||||
private GUIStyle _helpStyle;
|
||||
private GUIStyle _foldoutTipStyle;
|
||||
private GUIStyle _pluginChooserAreaStyle;
|
||||
private SearchField _dnaSearchField;
|
||||
private GUIStyle _pluginsByDNAAreaStyle;
|
||||
|
||||
//GUIContent Defaults
|
||||
private string _dnaConvertersLabel = "DNA Converters";
|
||||
|
||||
private string[] _viewTabsLabels = new string[] { "By Converter Type", "By DNA Name" };
|
||||
|
||||
//default button sizes
|
||||
private float _addPluginBtnWidth = 50f;
|
||||
|
||||
private bool _initialized = false;
|
||||
|
||||
private string[] _help = new string[]
|
||||
{
|
||||
"DNA Converters convert dna values into modifications to your character. Different converters apply the dna in different ways. For example a Skeleton DNA Converter will take a dna value and convert it into transforms that are applied to the skeleton bones. A Blendshape DNA Converter will convert a dna value into the power value for a blendshape.",
|
||||
"Normally DNA Converters only do anything when the dna value is changed from its starting value, but some converters allow you to define a 'Starting' value and this can used to apply a modification by default. A 'Starting Pose' is a good example of this.",
|
||||
"Converters are applied to the character from top to bottom, you can change the order by dragging the handle next to the converter entries header in the 'View By Converter Type' view.",
|
||||
"Also in the 'View By Converter Type' view you can click the 'Cog' icon to rename or delete a converter instance. Click the 'Import' button to show the import area for the plugin, which allows you to import settings from another instance in various ways",
|
||||
"The 'View By DNA Name' tab lists all the dna names the converters can use. Expanding a dna name shows you all the converters that use that dna name in any way."
|
||||
};
|
||||
|
||||
private bool changed = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PUBLIC PROPERTIES
|
||||
|
||||
public DynamicUMADnaAsset DNAAsset
|
||||
{
|
||||
set { _dnaAsset = value; }
|
||||
}
|
||||
|
||||
public static DynamicDNAConverterControllerInspector livePopupEditor
|
||||
{
|
||||
get { return _livePopupEditor; }
|
||||
}
|
||||
|
||||
public static bool livePopupEditorChanged
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_livePopupEditor != null)
|
||||
{
|
||||
return _livePopupEditor.changed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INIT
|
||||
|
||||
private bool Init()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
bool stylesSet = false;
|
||||
|
||||
if (EditorStyles.helpBox == null || EditorStyles.foldout == null || EditorStyles.label == null)
|
||||
{
|
||||
//Dont set any styles
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//Style for subHeaders
|
||||
_subHeaderStyle = new GUIStyle(EditorStyles.helpBox);
|
||||
_subHeaderStyle.margin = new RectOffset(_subHeaderStyle.margin.left, _subHeaderStyle.margin.right, _subHeaderStyle.margin.top, 0);
|
||||
|
||||
//Style for Tips
|
||||
_foldoutTipStyle = new GUIStyle(EditorStyles.foldout);
|
||||
_foldoutTipStyle.fontStyle = FontStyle.Bold;
|
||||
|
||||
//Help Icon & style
|
||||
_helpIcon = EditorGUIUtility.FindTexture("_Help");
|
||||
|
||||
_helpStyle = new GUIStyle(EditorStyles.label);
|
||||
_helpStyle.fixedHeight = _helpIcon.height + 4f;
|
||||
_helpStyle.contentOffset = new Vector2(-4f, 0f);
|
||||
|
||||
//Styles for the Add Converter area
|
||||
var reorderableListDefaults = new ReorderableList.Defaults();
|
||||
_pluginChooserAreaStyle = new GUIStyle(reorderableListDefaults.boxBackground);
|
||||
_pluginChooserAreaStyle.margin = new RectOffset(4, 4, 2, 2);
|
||||
_pluginChooserAreaStyle.stretchHeight = false;
|
||||
_pluginChooserAreaStyle.padding = new RectOffset(8, 8, 4, 8);
|
||||
|
||||
_pluginsByDNAAreaStyle = new GUIStyle(EditorStyles.textField);
|
||||
_pluginsByDNAAreaStyle.margin = new RectOffset(0, 0, 0, 0);
|
||||
_pluginsByDNAAreaStyle.padding = new RectOffset(4,4,4,4);
|
||||
|
||||
stylesSet = true;
|
||||
}
|
||||
|
||||
_initialized = stylesSet;
|
||||
|
||||
_target = target as DynamicDNAConverterController;
|
||||
|
||||
_dnaAsset = _target.DNAAsset;
|
||||
|
||||
InitPlugins();
|
||||
}
|
||||
return _initialized;
|
||||
}
|
||||
|
||||
private void InitPlugins()
|
||||
{
|
||||
_target.ValidatePlugins();
|
||||
|
||||
_pluginsEditors.Clear();
|
||||
|
||||
//initialize the editors for the existing plugins
|
||||
for (int i = 0; i < _target.PluginCount; i++)
|
||||
{
|
||||
var pluginEditor = Editor.CreateEditor(_target.GetPlugin(i)) as DynamicDNAPluginInspector;
|
||||
if (pluginEditor != null)
|
||||
{
|
||||
pluginEditor.DNAAsset = _dnaAsset;
|
||||
pluginEditor.Converter = _target;
|
||||
}
|
||||
_pluginsEditors.Add(_target.GetPlugin(i), pluginEditor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UNITY METHODS
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
if (!Init())
|
||||
{
|
||||
EditorGUILayout.HelpBox("Dynamic DNA Converter Asset failed to initialize GUI. Please try selecting it again.", MessageType.Error);
|
||||
Debug.LogError("FAILED TO INITIALIZE. Bailing...");
|
||||
return;
|
||||
}
|
||||
|
||||
var displayValueProp = serializedObject.FindProperty("_displayValue");
|
||||
|
||||
EditorGUILayout.PropertyField(displayValueProp);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
DrawDNAAssetField();
|
||||
|
||||
//Draw the header and help as defined in the scope
|
||||
var controllerHeaderRect = EditorGUILayout.GetControlRect();
|
||||
DrawControllersHeader(controllerHeaderRect, _help, ref _convertersExpanded, ref _convertersHelpExpanded);
|
||||
|
||||
if (_convertersExpanded)
|
||||
{
|
||||
//Draw the view tabs for viewing by Modifier or dna name
|
||||
DrawControllersViewTabs();
|
||||
|
||||
//Draw the GUI for each initialized plugin depending on whether the 'By Plugin' view or the 'By DNA View' was selected
|
||||
EditorGUI.BeginChangeCheck();
|
||||
if (_view == false)
|
||||
{
|
||||
DrawConverters();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawConvertersByDNA();
|
||||
}
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
changed = true;
|
||||
if (_livePopupEditor != null && _livePopupEditor == this)
|
||||
{
|
||||
OnLivePopupEditorChange.Invoke();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
DrawOverallModifiers();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GUI DRAWING METHODS
|
||||
|
||||
private void DrawDNAAssetField()
|
||||
{
|
||||
var dnaAssetProp = serializedObject.FindProperty("_dnaAsset");
|
||||
var dnaAssetFoldoutRect = EditorGUILayout.GetControlRect();
|
||||
dnaAssetFoldoutRect.height = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
var dnaAssetLabel = EditorGUI.BeginProperty(dnaAssetFoldoutRect, new GUIContent(dnaAssetProp.displayName), dnaAssetProp);
|
||||
|
||||
dnaAssetLabel.text = dnaAssetLabel.text.ToUpper();
|
||||
GUIHelper.ToolbarStyleFoldout(dnaAssetFoldoutRect, dnaAssetLabel.text.ToUpper(), new string[] { dnaAssetLabel.tooltip }, ref _dnaAssetExpanded, ref _dnaAssetHelpExpanded);
|
||||
|
||||
if (_dnaAssetExpanded)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
GUILayout.Space(5);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(dnaAssetProp);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (dnaAssetProp.objectReferenceValue != null)
|
||||
{
|
||||
_dnaAsset = dnaAssetProp.objectReferenceValue as DynamicUMADnaAsset;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dnaAsset = null;
|
||||
}
|
||||
//TODO in the ConverterBehaviour editor we cleared the DNA on the avatar.umaData (if we are in play mode and inspecting using customizer)
|
||||
//we could probably do with doing the same here
|
||||
/*
|
||||
//force the Avatar to update its dna and dnaconverter dictionaries
|
||||
umaData.umaRecipe.ClearDna();
|
||||
umaData.umaRecipe.ClearDNAConverters();
|
||||
*/
|
||||
}
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
private void DrawControllersHeader(Rect rect, string[] help, ref bool _isExpanded, ref bool _helpExpanded)
|
||||
{
|
||||
//GUIHelper.ToolbarStyleHeader(rect, new GUIContent(_dnaConvertersLabel.ToUpper()), _help, ref _helpExpanded);
|
||||
GUIHelper.ToolbarStyleFoldout(rect, new GUIContent(_dnaConvertersLabel.ToUpper()), _help, ref _isExpanded, ref _helpExpanded);
|
||||
//_isExpanded = true;
|
||||
}
|
||||
|
||||
private void DrawHelp(string[] help)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
for(int i = 0; i < help.Length; i++)
|
||||
{
|
||||
EditorGUILayout.HelpBox(help[i], MessageType.None);
|
||||
}
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
|
||||
//Draws the 'View' tabs allowing the user to switch between viewing data 'By Plugin' or 'By DNA'
|
||||
private void DrawControllersViewTabs()
|
||||
{
|
||||
//Tabs for viewing by modifier or by dna
|
||||
var tabsRect = EditorGUILayout.GetControlRect();
|
||||
var tabsLabel = new Rect(tabsRect.xMin, tabsRect.yMin, 60f, tabsRect.height);
|
||||
var tabsButRect = new Rect(tabsLabel.xMax, tabsRect.yMin, (tabsRect.width - tabsLabel.width), tabsRect.height);
|
||||
|
||||
EditorGUI.LabelField(tabsLabel, "View:", EditorStyles.toolbarButton);
|
||||
|
||||
var scopeViewInt = (_view ? 1 : 0);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
scopeViewInt = GUI.Toolbar(tabsButRect, scopeViewInt, _viewTabsLabels, EditorStyles.toolbarButton);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
_view = scopeViewInt == 0 ? false : true;
|
||||
//refresh the used dnaNames
|
||||
_target.GetUsedDNANames(true);
|
||||
}
|
||||
}
|
||||
|
||||
//Draws the plugins in 'By Converter' view
|
||||
private void DrawConverters()
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
|
||||
if (_target.PluginCount == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No Converters have been added yet. Use the 'Add' tool below to add some", MessageType.Info);
|
||||
}
|
||||
_convertersListProp = serializedObject.FindProperty("_plugins");
|
||||
|
||||
_convertersROL = CachedReorderableList.GetListDrawer(_convertersListProp, DrawConverterListHeaderCallback, GetConverterListEntryHeightCallback, DrawConverterListEntryCallback, DrawConverterListFooterCallback);
|
||||
_convertersROL.headerHeight = 0f;
|
||||
_convertersROL.footerHeight = (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 2);
|
||||
_convertersROL.DoLayoutList();
|
||||
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
|
||||
//Draws the converters in the 'By DNA' view
|
||||
private void DrawConvertersByDNA()
|
||||
{
|
||||
var inUseNames = _target.GetUsedDNANames();
|
||||
List<string> namesToDraw;
|
||||
|
||||
//if we have a dnanames asset we will loop through those names
|
||||
if (_dnaAsset != null && _dnaAsset.Names.Length > 0)
|
||||
{
|
||||
namesToDraw = new List<string>(_dnaAsset.Names);
|
||||
}
|
||||
//otherwise we will used the inUseNames from the plugins
|
||||
else
|
||||
{
|
||||
namesToDraw = inUseNames;
|
||||
}
|
||||
|
||||
//Draw the output
|
||||
if (namesToDraw.Count == 0)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
if (_target.PluginCount == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No plugins have been added yet. Use the 'Add' tool below to add some", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("No plugins have been set up to use any dnaNames yet. Switch to the other view to add them", MessageType.Info);
|
||||
}
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
|
||||
//Draw the search field
|
||||
var activeNamesToDraw = DrawDNASearchArea(EditorGUILayout.GetControlRect(), namesToDraw);
|
||||
|
||||
DynamicDNAPlugin plugin;
|
||||
|
||||
for (int i = 0; i < activeNamesToDraw.Count; i++)
|
||||
{
|
||||
if (!_expandedDNANames.ContainsKey(activeNamesToDraw[i]))
|
||||
{
|
||||
_expandedDNANames.Add(activeNamesToDraw[i], false);
|
||||
}
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
EditorGUI.indentLevel++;
|
||||
_expandedDNANames[activeNamesToDraw[i]] = EditorGUILayout.Foldout(_expandedDNANames[activeNamesToDraw[i]], activeNamesToDraw[i]);
|
||||
EditorGUI.indentLevel--;
|
||||
GUILayout.EndHorizontal();
|
||||
if (_expandedDNANames[activeNamesToDraw[i]])
|
||||
{
|
||||
GUI.color = new Color(0.75f, 0.875f, 1f, 0.3f);
|
||||
GUILayout.BeginVertical(_pluginsByDNAAreaStyle);
|
||||
GUI.color = Color.white;
|
||||
|
||||
//these would be a reorderable list in the other view so draw a reorderable list box around this whole list so it looks the same
|
||||
//(plugins are not reorderable in this view because that would sort of suggest you can have plugins output in a different order
|
||||
//depending on the dna name- which you cant)
|
||||
GUILayout.BeginVertical(_pluginChooserAreaStyle);
|
||||
|
||||
for (int pi = 0; pi < _target.PluginCount; pi++)
|
||||
{
|
||||
plugin = _target.GetPlugin(pi);
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//make a space like the other view
|
||||
if (pi > 0)
|
||||
{
|
||||
GUILayout.Space(EditorGUIUtility.standardVerticalSpacing *2);
|
||||
}
|
||||
|
||||
//tell the plugin to draw its entry for this dna name, plugins might use more than one dna name so its up to their drawers to sort out what to draw
|
||||
//the general idea is that if this dna name appears anywhere in the plugin, then it should draw the relevant entry
|
||||
_pluginsEditors[plugin].OnInspectorForDNAGUI(activeNamesToDraw[i]);
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
//TODO after we have drawn all the namesToDraw if there are any inUseNames that have not been drawn, draw those too
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawOverallModifiers()
|
||||
{
|
||||
var overallModifiersProp = serializedObject.FindProperty("_overallModifiers");
|
||||
var overallModsFoldoutRect = EditorGUILayout.GetControlRect();
|
||||
overallModsFoldoutRect.height = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
var overallModsLabel = EditorGUI.BeginProperty(overallModsFoldoutRect, new GUIContent(overallModifiersProp.displayName), overallModifiersProp);
|
||||
|
||||
overallModsLabel.text = overallModsLabel.text.ToUpper();
|
||||
//TODO Id actually like an import settings thing here now, so that you can import overallModifiers from another converterController or converterBehaviour
|
||||
GUIHelper.ToolbarStyleFoldout(overallModsFoldoutRect, overallModsLabel.text.ToUpper(), new string[] { overallModsLabel.tooltip }, ref _overallModifiersExpanded, ref _overallModifiersHelpExpanded);
|
||||
|
||||
if (_overallModifiersExpanded)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
GUILayout.Space(5);
|
||||
EditorGUILayout.PropertyField(overallModifiersProp);
|
||||
GUIHelper.EndVerticalPadded(3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region REORDERABLE LIST CALLBACKS
|
||||
|
||||
private void DrawConverterListHeaderCallback(Rect rect)
|
||||
{
|
||||
//Hide the header unless needed
|
||||
_convertersROL.headerHeight = 0f;
|
||||
}
|
||||
|
||||
private float GetConverterListEntryHeightCallback(int index)
|
||||
{
|
||||
var plugin = _target.GetPlugin(index);
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
return _pluginsEditors[plugin].GetInspectorHeight();
|
||||
}
|
||||
|
||||
private void DrawConverterListEntryCallback(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
var plugin = _target.GetPlugin(index);
|
||||
|
||||
if (plugin == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var prevIndent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
_pluginsEditors[plugin].DrawInspectorGUI(rect);
|
||||
|
||||
EditorGUI.indentLevel = prevIndent;
|
||||
}
|
||||
|
||||
private void DrawConverterListFooterCallback(Rect rect)
|
||||
{
|
||||
//Draw the Add Converter popup instead of the +/- buttons
|
||||
DrawAddConverterPopup(rect);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GUI UTILS
|
||||
|
||||
//Draws a popup showing the available plugins for the project
|
||||
private void DrawAddConverterPopup(Rect position)
|
||||
{
|
||||
var ROLDefaults = new ReorderableList.Defaults();
|
||||
var padding = 4f;
|
||||
_availablePlugins = DynamicDNAPlugin.GetAvailablePluginTypes();
|
||||
|
||||
Rect addRect = Rect.zero;
|
||||
|
||||
if (position == Rect.zero)
|
||||
{
|
||||
GUILayout.BeginVertical(_pluginChooserAreaStyle);
|
||||
addRect = EditorGUILayout.GetControlRect();
|
||||
}
|
||||
else
|
||||
{
|
||||
addRect = position;
|
||||
}
|
||||
addRect.xMin = addRect.xMax - 190 > addRect.xMin ? addRect.xMax - 190 : addRect.xMin;
|
||||
var labelRect = new Rect(addRect.xMin + (padding * 2), addRect.yMin, addRect.width - (padding * 2), 0);
|
||||
var addPopupRect = new Rect(addRect.xMin + (padding * 2), labelRect.yMax, addRect.width - _addPluginBtnWidth - (padding * 2), EditorGUIUtility.singleLineHeight);
|
||||
var addBtnRect = new Rect(addPopupRect.xMax + padding, labelRect.yMax, _addPluginBtnWidth - (padding * 3), EditorGUIUtility.singleLineHeight);
|
||||
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
{
|
||||
var prevFooterFixedHeight = ROLDefaults.footerBackground.fixedHeight;
|
||||
ROLDefaults.footerBackground.fixedHeight = addRect.height;
|
||||
ROLDefaults.footerBackground.Draw(addRect, false, false, false, false);
|
||||
ROLDefaults.footerBackground.fixedHeight = prevFooterFixedHeight;
|
||||
}
|
||||
|
||||
var dropdownLabel = _pluginToAdd != null ? _pluginToAdd.Name : "Add Converters...";
|
||||
if (EditorGUI.DropdownButton(addPopupRect, new GUIContent(dropdownLabel, "Add converters of the selected type to the " + _dnaConvertersLabel + " list"), FocusType.Keyboard))
|
||||
{
|
||||
// create the menu and add items to it
|
||||
GenericMenu popupMenu = new GenericMenu();
|
||||
|
||||
//add the choose entry- clears the selection
|
||||
AddMenuItemForAddConvertersPopup(popupMenu, null);
|
||||
|
||||
//add the actual entries
|
||||
for (int i = 0; i < _availablePlugins.Count; i++)
|
||||
{
|
||||
AddMenuItemForAddConvertersPopup(popupMenu, _availablePlugins[i]);
|
||||
}
|
||||
|
||||
// display the menu
|
||||
popupMenu.DropDown(addPopupRect);
|
||||
}
|
||||
|
||||
EditorGUI.BeginDisabledGroup(_pluginToAdd == null);
|
||||
if (GUI.Button(addBtnRect, new GUIContent("Add", (_pluginToAdd == null ? "Choose converters to add first" : ""))))
|
||||
{
|
||||
//do it!
|
||||
_target.AddPlugin(_pluginToAdd);
|
||||
//reset the choice
|
||||
_pluginToAdd = null;
|
||||
//reInit the plugins
|
||||
InitPlugins();
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
if (position == Rect.zero)
|
||||
{
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a menu item to the custom popup we draw for selecting a plugin to add
|
||||
/// </summary>
|
||||
private void AddMenuItemForAddConvertersPopup(GenericMenu menu, Type pluginType)
|
||||
{
|
||||
if (pluginType == null)//the choose Plugin Entry
|
||||
{
|
||||
var cbObj = new ConverterToChoose(pluginType);
|
||||
var selected = _pluginToAdd == null;
|
||||
menu.AddItem(new GUIContent("Add Converters..."), selected, OnAddConvertersPopupItemSelected, cbObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cbObj = new ConverterToChoose(pluginType);
|
||||
var selected = (_pluginToAdd != null && _pluginToAdd.Equals(pluginType)) ? true : false;
|
||||
menu.AddItem(new GUIContent(pluginType.Name.Replace("Plugin", "")+"s"), selected, OnAddConvertersPopupItemSelected, cbObj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback for the custom menu items in the popup we draw for selecting a plugin to add
|
||||
/// </summary>
|
||||
private void OnAddConvertersPopupItemSelected(object pluginToChoose)
|
||||
{
|
||||
_pluginToAdd = ((ConverterToChoose)pluginToChoose).converterType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a search area for the dna view
|
||||
/// </summary>
|
||||
/// <returns>returns a list of names with the search filter applied</returns>
|
||||
private List<string> DrawDNASearchArea(Rect position, List<string> namesList)
|
||||
{
|
||||
if (_dnaSearchField == null)
|
||||
{
|
||||
_dnaSearchField = new UnityEditor.IMGUI.Controls.SearchField();
|
||||
}
|
||||
|
||||
_DNASearchString = _dnaSearchField.OnToolbarGUI(position, _DNASearchString);
|
||||
|
||||
if (String.IsNullOrEmpty(_DNASearchString))
|
||||
{
|
||||
return namesList;
|
||||
}
|
||||
|
||||
List<string> filteredNames = new List<string>();
|
||||
//loop backwards over the list so we can remove stuff without out of range shiz
|
||||
for (int i = namesList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (namesList[i].IndexOf(_DNASearchString, StringComparison.CurrentCultureIgnoreCase) > -1)
|
||||
{
|
||||
filteredNames.Add(namesList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredNames;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region STATIC UTILS
|
||||
|
||||
public static void SetLivePopupEditor(DynamicDNAConverterControllerInspector liveDDCCEditor)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
_livePopupEditor = liveDDCCEditor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Id really like this to show 'Choose DNA Name' in the field and 'None' as the 'Un-Choose' option in the list
|
||||
//but for that we need a generic menu
|
||||
//I also want a customOption that shows the textfield until you press return and then asks you if you want to add the name to the dna asset
|
||||
/// <summary>
|
||||
/// Draws a popup for selecting a dna name from the converters DynamicDNAAsset (if set) otherwise draws a text field
|
||||
/// </summary>
|
||||
public static void DNANamesPopup(Rect position, SerializedProperty property, string selected, DynamicUMADnaAsset DNAAsset)
|
||||
{
|
||||
if (DNAAsset == null)
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
property.stringValue = EditorGUI.TextField(position, selected);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int selectedIndex = -1;
|
||||
var names = GetDNANamesForPopup(DNAAsset);
|
||||
selectedIndex = names.IndexOf(selected);
|
||||
if (selectedIndex == -1)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(selected))
|
||||
{
|
||||
names.Insert(1, selected);
|
||||
selectedIndex = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedIndex = 0;
|
||||
}
|
||||
}
|
||||
EditorGUI.BeginChangeCheck();
|
||||
selectedIndex = EditorGUI.Popup(position, selectedIndex, names.ToArray());
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (selectedIndex != 0)
|
||||
{
|
||||
property.stringValue = names[selectedIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
property.stringValue = "";
|
||||
}
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//gets the names for the above popup, keeps missing names in the list too so that users can reselect them
|
||||
private static List<string> GetDNANamesForPopup(DynamicUMADnaAsset DNAAsset)
|
||||
{
|
||||
var _dnaNamesForPopup = new List<string>();
|
||||
for (int i = 0; i < DNAAsset.Names.Length; i++)
|
||||
{
|
||||
_dnaNamesForPopup.Add(DNAAsset.Names[i]);
|
||||
}
|
||||
_dnaNamesForPopup.Insert(0, "Choose DNA Name");
|
||||
return _dnaNamesForPopup;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SPECIAL TYPES
|
||||
|
||||
/// <summary>
|
||||
/// a special type to hold the data for choosing given plugin type. Used by plugins popup chooser
|
||||
/// </summary>
|
||||
private class ConverterToChoose
|
||||
{
|
||||
public Type converterType;
|
||||
|
||||
public ConverterToChoose() { }
|
||||
|
||||
public ConverterToChoose(Type pt)
|
||||
{
|
||||
converterType = pt;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df62ebdbb9eee9645b5022ff4db3a829
|
||||
timeCreated: 1538168684
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DynamicDNAConverterControllerInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,51 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UMA.Editors;
|
||||
|
||||
namespace UMA
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(DynamicDNAConverterController), true)]
|
||||
public class DynamicDNAConverterControllerPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
static EditorWindow inspectorPopup;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
GUIHelper.InspectableObjectField(position, property, label, OnShowPopupInspector);
|
||||
|
||||
//We need to check this a few times because when the inspector window is created by InspectorUtility.InspectTarget
|
||||
//when the user clicks the inspect button next to the field that is drawn above,
|
||||
//GetInspectorsEditors doesnt return the actual editors correctly until the popup window repaints
|
||||
if (inspectorPopup != null && DynamicDNAConverterControllerInspector.livePopupEditor == null)
|
||||
{
|
||||
var editors = InspectorUtlity.GetInspectorsEditors(inspectorPopup);
|
||||
for (int i = 0; i < editors.Length; i++)
|
||||
{
|
||||
if (editors[i].GetType() == typeof(DynamicDNAConverterControllerInspector))
|
||||
{
|
||||
if (editors[i].target == property.objectReferenceValue)
|
||||
{
|
||||
DynamicDNAConverterControllerInspector.SetLivePopupEditor(editors[i] as DynamicDNAConverterControllerInspector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
return (EditorGUIUtility.singleLineHeight);
|
||||
}
|
||||
|
||||
public void OnShowPopupInspector(EditorWindow newInspectorPopup)
|
||||
{
|
||||
if (inspectorPopup != null && inspectorPopup != newInspectorPopup)
|
||||
{
|
||||
inspectorPopup.Close();
|
||||
DynamicDNAConverterControllerInspector.SetLivePopupEditor(null);
|
||||
}
|
||||
inspectorPopup = newInspectorPopup;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9b2f96bc6066c84c9e7f1a3e7eec875
|
||||
timeCreated: 1543188165
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DynamicDNAConverterControllerPropertyDrawer.cs
|
||||
uploadId: 679826
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac740e993146b9d438873fa300c5f87c
|
||||
timeCreated: 1538743041
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DynamicDNAPluginInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,134 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(DynamicDNAPlugin.MasterWeight),true)]
|
||||
public class DynamicDNAPluginMasterWeightDrawer : PropertyDrawer
|
||||
{
|
||||
|
||||
private DNAEvaluationGraph dummyEvaluator = new DNAEvaluationGraph();
|
||||
private GUIStyle italicLabel;
|
||||
private float typePopupWidth = 110f;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (italicLabel == null)
|
||||
{
|
||||
italicLabel = new GUIStyle(EditorStyles.label);
|
||||
italicLabel.clipping = TextClipping.Clip;
|
||||
italicLabel.fontStyle = FontStyle.Italic;
|
||||
}
|
||||
|
||||
var foldoutRect = new Rect(position.xMin + 6f, position.yMin, position.width - 6f, EditorGUIUtility.singleLineHeight + (EditorGUIUtility.standardVerticalSpacing * 2));
|
||||
|
||||
var foldoutLabel = EditorGUI.BeginProperty(foldoutRect, new GUIContent(property.displayName), property);
|
||||
property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, foldoutLabel, true);
|
||||
EditorGUI.EndProperty();
|
||||
|
||||
if (property.isExpanded)
|
||||
{
|
||||
var masterWeightTypeProp = property.FindPropertyRelative("_masterWeightType");
|
||||
var globalWeightProp = property.FindPropertyRelative("_globalWeight");
|
||||
var dnaForWeightProp = property.FindPropertyRelative("_DNAForWeight");
|
||||
|
||||
var rect = foldoutRect;
|
||||
var typeRect = new Rect(rect.xMin, foldoutRect.yMax + EditorGUIUtility.standardVerticalSpacing, typePopupWidth, EditorGUIUtility.singleLineHeight);
|
||||
|
||||
EditorGUI.PropertyField(typeRect, masterWeightTypeProp, GUIContent.none);
|
||||
|
||||
if (masterWeightTypeProp.enumValueIndex == 0)//useGlobalWeight
|
||||
{
|
||||
var field1Rect = new Rect(typeRect.xMax + 20f, foldoutRect.yMax + EditorGUIUtility.standardVerticalSpacing, rect.width - typeRect.width - 20f, EditorGUI.GetPropertyHeight(globalWeightProp));
|
||||
EditorGUI.PropertyField(field1Rect, globalWeightProp, GUIContent.none);
|
||||
}
|
||||
else //use DNA Value
|
||||
{
|
||||
var field2Rect = new Rect(typeRect.xMax + 5f, foldoutRect.yMax, rect.width - typeRect.width, EditorGUI.GetPropertyHeight(dnaForWeightProp));
|
||||
EditorGUI.PropertyField(field2Rect, dnaForWeightProp);
|
||||
}
|
||||
}
|
||||
|
||||
DrawCurrentSettingInfo(property, foldoutRect, position);
|
||||
|
||||
}
|
||||
|
||||
private void DrawCurrentSettingInfo(SerializedProperty property, Rect foldoutRect, Rect position)
|
||||
{
|
||||
var foldoutLabelNameRect = new Rect(foldoutRect.xMin + 9f, foldoutRect.yMin, 70f, foldoutRect.height);
|
||||
var foldoutInfoRect = new Rect(foldoutLabelNameRect.xMax + 4f, foldoutRect.yMin, 170f, foldoutRect.height);
|
||||
var foldoutFieldRect = new Rect(foldoutInfoRect.xMax + 4f, foldoutRect.yMin, 40f, EditorGUIUtility.singleLineHeight);
|
||||
|
||||
var masterWeightTypeProp = property.FindPropertyRelative("_masterWeightType");
|
||||
var globalWeightProp = property.FindPropertyRelative("_globalWeight");
|
||||
var dnaForWeightProp = property.FindPropertyRelative("_DNAForWeight");
|
||||
var dnaNameForWeightProp = dnaForWeightProp.FindPropertyRelative("_dnaName");
|
||||
|
||||
GUIContent infoText = masterWeightTypeProp.enumValueIndex == 0 ? new GUIContent("Using Global Value : ") : new GUIContent("Using DNA Value '" + dnaNameForWeightProp.stringValue + "' : ");
|
||||
|
||||
//recalc widths based on that
|
||||
var labelWidth = (EditorStyles.foldout.CalcSize(new GUIContent(property.displayName)).x - 15f);
|
||||
foldoutLabelNameRect.width = labelWidth;
|
||||
foldoutInfoRect.xMin = foldoutLabelNameRect.xMax;
|
||||
foldoutInfoRect.width = italicLabel.CalcSize(infoText).x;
|
||||
foldoutFieldRect.xMin = foldoutInfoRect.xMax + 4f;
|
||||
foldoutFieldRect.width = 50f;
|
||||
//now fix anything that overflows!
|
||||
var xMax = position.xMax;
|
||||
if (foldoutFieldRect.xMax > xMax)
|
||||
{
|
||||
foldoutFieldRect.xMin = xMax - 50f;
|
||||
foldoutInfoRect.width = xMax - labelWidth - 50f - 15f - 4f - 4f - 6f - 6f;
|
||||
}
|
||||
foldoutFieldRect.width = 40f;
|
||||
float fieldValue = masterWeightTypeProp.enumValueIndex == 0 ? globalWeightProp.floatValue : 0.5f;
|
||||
//I want to evaluate the dna so users can see how different evaluators affect the value
|
||||
if (masterWeightTypeProp.enumValueIndex == 1 && Application.isPlaying)
|
||||
{
|
||||
//this really has to get the current dna value else its too confusing
|
||||
//fieldValue = EvalauateValue(fieldValue, dnaForWeightProp);
|
||||
}
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUI.LabelField(foldoutInfoRect, infoText, italicLabel);
|
||||
if (masterWeightTypeProp.enumValueIndex == 0)
|
||||
{
|
||||
EditorGUI.LabelField(foldoutFieldRect, "[" + fieldValue.ToString("0.00") + "]");
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
var height = EditorGUIUtility.singleLineHeight;
|
||||
if (property.isExpanded)
|
||||
{
|
||||
if(property.FindPropertyRelative("_masterWeightType").enumValueIndex == 0)//use GlobalValue
|
||||
{
|
||||
height += (EditorGUIUtility.singleLineHeight + (EditorGUIUtility.standardVerticalSpacing * 2));
|
||||
height += 4f;
|
||||
}
|
||||
else //use dna Value
|
||||
{
|
||||
height += (EditorGUIUtility.singleLineHeight + (EditorGUIUtility.standardVerticalSpacing * 2)) * 2f;
|
||||
height += 8f;
|
||||
}
|
||||
}
|
||||
return height;
|
||||
}
|
||||
|
||||
//I'd really like this if its inspected at runtime to show the actual DNA value thats chosen being evaluated
|
||||
//but for that I'd need some kind of context thing like UMABonePose has
|
||||
//I'll get to it...TODO
|
||||
protected float EvalauateValue(float value, SerializedProperty evaluatorProp)
|
||||
{
|
||||
//maybe I can get the active dna off the plugin using property.serializedObject.targetObject like dnaEvalutaor does?
|
||||
var multiplierProp = evaluatorProp.FindPropertyRelative("_multiplier");
|
||||
var graphProp = evaluatorProp.FindPropertyRelative("_evaluator");
|
||||
var graphNameProp = graphProp.FindPropertyRelative("_name");
|
||||
var graphCurveProp = graphProp.FindPropertyRelative("_graph");
|
||||
dummyEvaluator = new DNAEvaluationGraph(graphNameProp.stringValue, graphCurveProp.animationCurveValue);
|
||||
return (dummyEvaluator.Evaluate(value) * multiplierProp.floatValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8fae1066aa910be4e8242e8f4ea30298
|
||||
timeCreated: 1539708358
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/DynamicDNAPluginMasterWeightDrawer.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,120 @@
|
||||
// ============================================================
|
||||
// Name: ExpressionPlayerInspector
|
||||
// Author: Eli Curtz
|
||||
// Copyright: (c) 2014 Eli Curtz
|
||||
// ============================================================
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.PoseTools
|
||||
{
|
||||
[CustomEditor(typeof(ExpressionPlayer), true)]
|
||||
public class ExpressionPlayerInspector : Editor
|
||||
{
|
||||
private ExpressionPlayer player;
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
player = target as ExpressionPlayer;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
|
||||
if (GUILayout.Button("Reset Expression"))
|
||||
{
|
||||
float[] zeroes = new float[player.Values.Length];
|
||||
player.Values = zeroes;
|
||||
EditorUtility.SetDirty(player);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Save To Clip"))
|
||||
{
|
||||
string assetPath = EditorUtility.SaveFilePanelInProject("Save Expression Clip", "Expression", "anim", null);
|
||||
player.SaveExpressionClip(assetPath);
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("UMA/Pose Tools/Set Clip Generic", true, priority = 1)]
|
||||
static bool ValidateSetClipGeneric()
|
||||
{
|
||||
|
||||
Object[] objs = Selection.objects;
|
||||
if ((objs == null) || (objs.Length < 1)) return false;
|
||||
|
||||
bool hasLegacyClip = false;
|
||||
foreach (Object obj in objs)
|
||||
{
|
||||
AnimationClip clip = obj as AnimationClip;
|
||||
if (clip != null)
|
||||
{
|
||||
if (clip.legacy)
|
||||
{
|
||||
hasLegacyClip = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasLegacyClip;
|
||||
}
|
||||
|
||||
[MenuItem("UMA/Pose Tools/Set Clip Generic",priority =1)]
|
||||
static void SetClipGeneric()
|
||||
{
|
||||
Object[] objs = Selection.objects;
|
||||
if (objs == null) return;
|
||||
|
||||
foreach (Object obj in objs)
|
||||
{
|
||||
AnimationClip clip = obj as AnimationClip;
|
||||
if (clip != null)
|
||||
{
|
||||
clip.legacy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("UMA/Pose Tools/Set Clip Legacy", true, priority = 1)]
|
||||
static bool ValidateSetClipLegacy()
|
||||
{
|
||||
|
||||
Object[] objs = Selection.objects;
|
||||
if ((objs == null) || (objs.Length < 1)) return false;
|
||||
|
||||
bool hasGenericClip = false;
|
||||
foreach (Object obj in objs)
|
||||
{
|
||||
AnimationClip clip = obj as AnimationClip;
|
||||
if (clip != null)
|
||||
{
|
||||
if (!clip.legacy && !clip.humanMotion)
|
||||
{
|
||||
hasGenericClip = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasGenericClip;
|
||||
}
|
||||
|
||||
[MenuItem("UMA/Pose Tools/Set Clip Legacy", priority = 1)]
|
||||
static void SetClipLegacy()
|
||||
{
|
||||
Object[] objs = Selection.objects;
|
||||
if (objs == null) return;
|
||||
|
||||
foreach (Object obj in objs)
|
||||
{
|
||||
AnimationClip clip = obj as AnimationClip;
|
||||
if (clip != null)
|
||||
{
|
||||
clip.legacy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63d9437a3891c43438d1f854d4332ab9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/ExpressionPlayerInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,788 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Events;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
public static class GUIHelper
|
||||
{
|
||||
|
||||
private static Texture _helpIcon;
|
||||
|
||||
private static GUIStyle _iconLabel;
|
||||
|
||||
private static Texture _inspectIcon;
|
||||
private static GUIContent _inspectContent;
|
||||
private static GUIStyle _inspectStyle;
|
||||
private static float inspectBtnWidth = 25f;
|
||||
|
||||
private static Texture helpIcon
|
||||
{
|
||||
get {
|
||||
if (_helpIcon != null)
|
||||
{
|
||||
return _helpIcon;
|
||||
}
|
||||
//Sometimes editor styles is not set up when we ask for this
|
||||
if (EditorStyles.label == null)
|
||||
{
|
||||
return new Texture2D(16,16);
|
||||
}
|
||||
|
||||
_helpIcon = EditorGUIUtility.FindTexture("_Help");
|
||||
return _helpIcon;
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIStyle iconLabel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
return _iconLabel;
|
||||
}
|
||||
|
||||
if (EditorStyles.label == null)
|
||||
{
|
||||
return new GUIStyle();
|
||||
}
|
||||
|
||||
_iconLabel = new GUIStyle(EditorStyles.label);
|
||||
_iconLabel.fixedHeight = 18f;
|
||||
_iconLabel.contentOffset = new Vector2(-4.0f, 0f);
|
||||
return _iconLabel;
|
||||
}
|
||||
}
|
||||
|
||||
private static Texture inspectIcon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_inspectIcon != null)
|
||||
{
|
||||
return _inspectIcon;
|
||||
}
|
||||
//Check EditorStyles has been set up
|
||||
if (EditorStyles.label == null)
|
||||
{
|
||||
return new Texture2D(16, 16);
|
||||
}
|
||||
|
||||
_inspectIcon = EditorGUIUtility.FindTexture("ViewToolOrbit");
|
||||
return _inspectIcon;
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIContent inspectContent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_inspectContent != null && _inspectIcon != null)
|
||||
{
|
||||
return _inspectContent;
|
||||
}
|
||||
|
||||
_inspectContent = new GUIContent("", "Inspect");
|
||||
_inspectContent.image = inspectIcon;
|
||||
return _inspectContent;
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIStyle inspectStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_inspectStyle != null)
|
||||
{
|
||||
return _inspectStyle;
|
||||
}
|
||||
//Check EditorStyles is set up
|
||||
if (EditorStyles.miniButton == null)
|
||||
{
|
||||
return new GUIStyle();
|
||||
}
|
||||
|
||||
_inspectStyle = new GUIStyle(EditorStyles.miniButton);
|
||||
_inspectStyle.contentOffset = new Vector2(0f, 0f);
|
||||
_inspectStyle.padding = new RectOffset(0, 0, 0, 0);
|
||||
_inspectStyle.margin = new RectOffset(0, 0, 0, 0);
|
||||
return _inspectStyle;
|
||||
}
|
||||
}
|
||||
|
||||
public static void BeginVerticalPadded()
|
||||
{
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(10, new Color(1.3f, 1.4f, 1.5f));
|
||||
}
|
||||
else
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EndVerticalPadded()
|
||||
{
|
||||
GUIHelper.EndVerticalPadded(10);
|
||||
}
|
||||
|
||||
public static void BeginVerticalPadded(float padding, Color backgroundColor, GUIStyle theStyle = null)
|
||||
{
|
||||
if (theStyle == null)
|
||||
{
|
||||
theStyle = EditorStyles.textField;
|
||||
}
|
||||
|
||||
GUI.color = backgroundColor;
|
||||
GUILayout.BeginHorizontal(theStyle);
|
||||
GUI.color = Color.white;
|
||||
|
||||
GUILayout.Space(padding);
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.Space(padding);
|
||||
}
|
||||
|
||||
public static void EndVerticalPadded(float padding)
|
||||
{
|
||||
GUILayout.Space(padding);
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.Space(padding);
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static void BeginVerticalIndented(float indentation, Color backgroundColor)
|
||||
{
|
||||
GUI.color = backgroundColor;
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(indentation);
|
||||
GUI.color = Color.white;
|
||||
GUILayout.BeginVertical();
|
||||
}
|
||||
|
||||
public static void EndVerticalIndented()
|
||||
{
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static void BeginHorizontalPadded(float padding, Color backgroundColor)
|
||||
{
|
||||
GUI.color = backgroundColor;
|
||||
GUILayout.BeginVertical(EditorStyles.textField);
|
||||
GUI.color = Color.white;
|
||||
|
||||
GUILayout.Space(padding);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(padding);
|
||||
}
|
||||
|
||||
public static void EndHorizontalPadded(float padding)
|
||||
{
|
||||
GUILayout.Space(padding);
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.Space(padding);
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
public static void Separator()
|
||||
{
|
||||
GUILayout.BeginHorizontal(EditorStyles.textField);
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static void BeginCollapsableGroupPartStart(ref bool show, string text, string boneName, ref bool selected)
|
||||
{
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
GUI.SetNextControlName(boneName);
|
||||
show = EditorGUILayout.Foldout(show, text);
|
||||
var control = GUI.GetNameOfFocusedControl();
|
||||
selected = control == boneName;
|
||||
//GUI.color = selected ? Color.yellow : Color.white;
|
||||
//if (GUILayout.Button(text, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)))
|
||||
//{
|
||||
// selected = true;
|
||||
//}
|
||||
//GUI.color = Color.white;
|
||||
}
|
||||
|
||||
public static void BeginCollapsableGroupPartMiddle(ref bool show, string text, ref bool selected)
|
||||
{
|
||||
GUILayout.Label("", EditorStyles.toolbarButton);
|
||||
}
|
||||
|
||||
public static bool BeginCollapsableGroupPartEnd(ref bool show, string text, ref bool selected)
|
||||
{
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (show)
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
}
|
||||
return show;
|
||||
}
|
||||
|
||||
|
||||
public static bool BeginCollapsableGroup(ref bool show, string text)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
show = GUILayout.Toggle(show, show ? "\u002d" : "\u002b", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false));
|
||||
GUILayout.Label(text, EditorStyles.toolbarButton, GUILayout.ExpandWidth(false));
|
||||
GUILayout.Label("", EditorStyles.toolbarButton);
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (show)
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
}
|
||||
return show;
|
||||
}
|
||||
|
||||
public static void EndCollapsableGroup(ref bool show)
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
EndCollapsableGroup();
|
||||
}
|
||||
}
|
||||
|
||||
public static void EndCollapsableGroup()
|
||||
{
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
public static void BeginObject(string label, int minWidth)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(label, EditorStyles.boldLabel, GUILayout.ExpandWidth(false), GUILayout.MinWidth(minWidth));
|
||||
}
|
||||
|
||||
public static void EndObject()
|
||||
{
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static void FoldoutBar(ref bool foldout, string content, out bool delete)
|
||||
{
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
GUILayout.Space(10);
|
||||
foldout = EditorGUILayout.Foldout(foldout, content,true);
|
||||
delete = GUILayout.Button("\u0078", EditorStyles.miniButton, GUILayout.ExpandWidth(false));
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static bool FoldoutBar(bool foldout, string content)
|
||||
{
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
GUILayout.Space(10);
|
||||
bool nfoldout = EditorGUILayout.Foldout(foldout, content,true);
|
||||
GUILayout.EndHorizontal();
|
||||
return nfoldout;
|
||||
}
|
||||
public static void FoldoutBarButton(ref bool foldout, string content, string button, out bool pressed, out bool delete)
|
||||
{
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
GUILayout.Space(10);
|
||||
foldout = EditorGUILayout.Foldout(foldout, content,true);
|
||||
pressed = GUILayout.Button(button, EditorStyles.miniButton, GUILayout.ExpandWidth(false));
|
||||
delete = GUILayout.Button("\u0078", EditorStyles.miniButton, GUILayout.ExpandWidth(false));
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static void FoldoutBarButton(ref bool foldout, string content, List<string> buttons, out List<bool> pressed, out int move, out bool delete)
|
||||
{
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
GUILayout.Space(10);
|
||||
foldout = EditorGUILayout.Foldout(foldout, content, true);
|
||||
|
||||
pressed = new List<bool>();
|
||||
for (int i = 0; i < buttons.Count; i++)
|
||||
{
|
||||
pressed.Add(GUILayout.Button(buttons[i], EditorStyles.miniButton, GUILayout.ExpandWidth(false)));
|
||||
}
|
||||
// pressed = GUILayout.Button(button, EditorStyles.miniButton, GUILayout.ExpandWidth(false));
|
||||
// pressed2 = GUILayout.Button(button2, EditorStyles.miniButton, GUILayout.ExpandWidth(false));
|
||||
|
||||
move = 0;
|
||||
if (GUILayout.Button("\u25B2", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
move--;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("\u25BC", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
move++;
|
||||
}
|
||||
|
||||
delete = GUILayout.Button("\u0078", EditorStyles.miniButton, GUILayout.ExpandWidth(false));
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static void FoldoutBarButton(ref bool foldout, string content, string button, out bool pressed, out int move, out bool delete)
|
||||
{
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
GUILayout.Space(10);
|
||||
foldout = EditorGUILayout.Foldout(foldout, content, true);
|
||||
|
||||
move = 0;
|
||||
if (GUILayout.Button("\u25B2", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
move--;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("\u25BC", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
move++;
|
||||
}
|
||||
|
||||
pressed = GUILayout.Button(button, EditorStyles.miniButton, GUILayout.ExpandWidth(false));
|
||||
delete = GUILayout.Button("\u0078", EditorStyles.miniButton, GUILayout.ExpandWidth(false));
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static void FoldoutBar(ref bool foldout, string content, out int move, out bool delete)
|
||||
{
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
GUILayout.Space(10);
|
||||
foldout = EditorGUILayout.Foldout(foldout, content,true);
|
||||
|
||||
move = 0;
|
||||
if (GUILayout.Button("\u25B2", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
move--;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("\u25BC", EditorStyles.miniButton, GUILayout.ExpandWidth(false)))
|
||||
{
|
||||
move++;
|
||||
}
|
||||
|
||||
delete = GUILayout.Button("\u0078", EditorStyles.miniButton, GUILayout.ExpandWidth(false));
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public static void ToolbarStyleFoldout(Rect rect, string label, ref bool isExpanded, GUIStyle toolbarStyleOverride = null, GUIStyle labelStyleOverride = null)
|
||||
{
|
||||
bool dummyHelpExpanded = false;
|
||||
ToolbarStyleFoldout(rect, new GUIContent(label), new string[0], ref isExpanded, ref dummyHelpExpanded, toolbarStyleOverride, labelStyleOverride);
|
||||
}
|
||||
|
||||
public static void ToolbarStyleFoldout(Rect rect, GUIContent label, ref bool isExpanded, GUIStyle toolbarStyleOverride = null, GUIStyle labelStyleOverride = null)
|
||||
{
|
||||
bool dummyHelpExpanded = false;
|
||||
ToolbarStyleFoldout(rect, label, new string[0], ref isExpanded, ref dummyHelpExpanded, toolbarStyleOverride, labelStyleOverride);
|
||||
}
|
||||
|
||||
public static void ToolbarStyleFoldout(Rect rect, string label, string[] help, ref bool isExpanded, ref bool helpExpanded, GUIStyle toolbarStyleOverride = null, GUIStyle labelStyleOverride = null)
|
||||
{
|
||||
ToolbarStyleFoldout(rect, new GUIContent(label), help, ref isExpanded, ref helpExpanded, toolbarStyleOverride, labelStyleOverride);
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws a ToolBar style foldout with a centered foldout label. Optionally draws a help icon that will show the selected array of 'help' paragraphs
|
||||
/// </summary>
|
||||
/// <param name="rect"></param>
|
||||
/// <param name="label"></param>
|
||||
/// <param name="help">An array of help paragpahs. If supplied the help Icon will be shown</param>
|
||||
/// <param name="isExpanded"></param>
|
||||
/// <param name="helpExpanded"></param>
|
||||
/// <param name="toolbarStyleOverride">Overrides EdiorStyles.toolbar as the background</param>
|
||||
/// <param name="labelStyleOverride">Overrides EditorStyles.folodout as the label style</param>
|
||||
public static void ToolbarStyleFoldout(Rect rect, GUIContent label, string[] help, ref bool isExpanded, ref bool helpExpanded, GUIStyle toolbarStyleOverride = null, GUIStyle labelStyleOverride = null)
|
||||
{
|
||||
GUIStyle toolbarStyle = toolbarStyleOverride;
|
||||
GUIStyle labelStyle = labelStyleOverride;
|
||||
if (toolbarStyle == null)
|
||||
{
|
||||
toolbarStyle = EditorStyles.toolbar;
|
||||
}
|
||||
|
||||
if (labelStyle == null)
|
||||
{
|
||||
labelStyle = EditorStyles.foldout;
|
||||
}
|
||||
|
||||
var helpIconRect = new Rect(rect.xMax - 20f, rect.yMin, 20f, rect.height);
|
||||
var helpGUI= new GUIContent("", "Show Help");
|
||||
helpGUI.image = helpIcon;
|
||||
Event current = Event.current;
|
||||
if (current.type == EventType.Repaint)
|
||||
{
|
||||
toolbarStyle.Draw(rect, GUIContent.none, false, false, false, false);
|
||||
}
|
||||
var labelWidth = labelStyle.CalcSize(label);
|
||||
labelWidth.x += 15f;//add the foldout arrow
|
||||
var toolbarFoldoutRect = new Rect((rect.xMax / 2f) - (labelWidth.x / 2f) + 30f, rect.yMin, ((rect.width / 2) + (labelWidth.x / 2f)) - 20f - 30f, rect.height);
|
||||
isExpanded = EditorGUI.Foldout(toolbarFoldoutRect, isExpanded, label, true, labelStyle);
|
||||
if (help.Length > 0)
|
||||
{
|
||||
helpExpanded = GUI.Toggle(helpIconRect, helpExpanded, helpGUI, iconLabel);
|
||||
if (helpExpanded)
|
||||
{
|
||||
ToolbarStyleHelp(help);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ToolbarStyleHeader(Rect rect, GUIContent label, string[] help, ref bool helpExpanded, GUIStyle toolbarStyleOverride = null, GUIStyle labelStyleOverride = null)
|
||||
{
|
||||
var toolbarStyle = toolbarStyleOverride != null ? toolbarStyleOverride : EditorStyles.toolbar;
|
||||
var labelStyle = labelStyleOverride != null ? labelStyleOverride : EditorStyles.label;
|
||||
var helpIconRect = new Rect(rect.xMax - 20f, rect.yMin, 20f, rect.height);
|
||||
var helpGUI = new GUIContent("", "Show Help");
|
||||
helpGUI.image = helpIcon;
|
||||
Event current = Event.current;
|
||||
if (current.type == EventType.Repaint)
|
||||
{
|
||||
toolbarStyle.Draw(rect, GUIContent.none, false, false, false, false);
|
||||
}
|
||||
var labelWidth = labelStyle.CalcSize(label);
|
||||
var toolbarFoldoutRect = new Rect((rect.xMax / 2f) - (labelWidth.x / 2f) + 30f, rect.yMin, ((rect.width / 2) + (labelWidth.x / 2f)) - 20f - 30f, rect.height);
|
||||
EditorGUI.LabelField(toolbarFoldoutRect, label, labelStyle);
|
||||
if (help.Length > 0)
|
||||
{
|
||||
helpExpanded = GUI.Toggle(helpIconRect, helpExpanded, helpGUI, iconLabel);
|
||||
if (helpExpanded)
|
||||
{
|
||||
ToolbarStyleHelp(help);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ToolbarStyleHelp(string[] help)
|
||||
{
|
||||
BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
|
||||
for (int i = 0; i < help.Length; i++)
|
||||
{
|
||||
EditorGUILayout.HelpBox(help[i], MessageType.None);
|
||||
}
|
||||
EndVerticalPadded(3);
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws an object field with an 'inspect' button next to it which opens up the editor for the assigned object in a popup window
|
||||
/// </summary>
|
||||
public static void InspectableObjectField(Rect position, SerializedProperty property, GUIContent label, UnityAction<EditorWindow> onCreateWindowCallback = null)
|
||||
{
|
||||
label = EditorGUI.BeginProperty(position, label, property);
|
||||
var objectFieldRect = position;
|
||||
var inspectBtnRect = Rect.zero;
|
||||
if (property.objectReferenceValue)
|
||||
{
|
||||
objectFieldRect = new Rect(position.xMin, position.yMin, position.width - inspectBtnWidth - 4f, position.height);
|
||||
inspectBtnRect = new Rect(objectFieldRect.xMax + 4f, objectFieldRect.yMin, inspectBtnWidth, objectFieldRect.height);
|
||||
}
|
||||
EditorGUI.ObjectField(objectFieldRect, property, label);
|
||||
if (property.objectReferenceValue)
|
||||
{
|
||||
if (GUI.Button(inspectBtnRect, inspectContent, inspectStyle))
|
||||
{
|
||||
var inspectorWindow = InspectorUtlity.InspectTarget(property.objectReferenceValue);
|
||||
if (onCreateWindowCallback != null)
|
||||
{
|
||||
onCreateWindowCallback(inspectorWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
private static string[] defaultTags;
|
||||
private static string[] basetagnames;
|
||||
private static int selectedTag = 0;
|
||||
|
||||
private static void GetTags()
|
||||
{
|
||||
if (defaultTags == null)
|
||||
{
|
||||
defaultTags = UMAEditorUtilities.GetDefaultTags();
|
||||
basetagnames = UMAEditorUtilities.GetDefaultBaseTags();
|
||||
}
|
||||
}
|
||||
|
||||
public static UnityEditorInternal.ReorderableList InitGenericTagsList(List<string> tags)
|
||||
{
|
||||
GetTags();
|
||||
|
||||
var tagsList = new UnityEditorInternal.ReorderableList(tags, typeof(string), true, true, false, false);
|
||||
tagsList.drawHeaderCallback = (Rect rect) =>
|
||||
{
|
||||
float width1 = 56;
|
||||
float width3 = 44; // Add button
|
||||
float width4 = 20; // + button
|
||||
float width5 = 44; // Clear button
|
||||
float width6 = 44; // load button
|
||||
float width7 = 44; // save button
|
||||
float width2 = rect.width - (width1 + width3 + width4 + width5 + width6 + width7);
|
||||
|
||||
float pos1 = 0;
|
||||
float pos2 = pos1 + width1;
|
||||
float pos3 = pos2 + width2;
|
||||
float pos4 = pos3 + width3;
|
||||
float pos5 = pos4 + width4;
|
||||
float pos6 = pos5 + width5;
|
||||
float pos7 = pos6 + width6;
|
||||
|
||||
Rect ctrl = new Rect(rect.x, rect.y, rect.width - 20, rect.height);
|
||||
|
||||
Rect labelrect = new Rect(rect.x + pos1, rect.y, width1, rect.height);
|
||||
Rect dropdownrect = new Rect(rect.x + pos2, rect.y, width2, rect.height);
|
||||
Rect adddropdownrect = new Rect(rect.x + pos3, rect.y, width3, rect.height);
|
||||
Rect addBlankrect = new Rect(rect.x + pos4, rect.y, width4, rect.height);
|
||||
Rect clearRect = new Rect(rect.x + pos5, rect.y, width5, rect.height);
|
||||
Rect loadRect = new Rect(rect.x + pos6, rect.y, width6, rect.height);
|
||||
Rect saveRect = new Rect(rect.x + pos7, rect.y, width7, rect.height);
|
||||
|
||||
EditorGUI.LabelField(labelrect, "Select");
|
||||
|
||||
if (defaultTags.Length > 0)
|
||||
{
|
||||
selectedTag = Mathf.Clamp(selectedTag, 0, defaultTags.Length - 1);
|
||||
selectedTag = EditorGUI.Popup(dropdownrect, selectedTag, defaultTags);
|
||||
|
||||
if (GUI.Button(adddropdownrect, "Add", EditorStyles.miniButton))
|
||||
{
|
||||
string newTag = basetagnames[selectedTag];
|
||||
bool found = false;
|
||||
for (int i = 0; i < tagsList.list.Count; i++)
|
||||
{
|
||||
if ((string)tagsList.list[i] == newTag)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
tagsList.list.Add(newTag);
|
||||
}
|
||||
}
|
||||
|
||||
if (GUI.Button(clearRect, "Clear", EditorStyles.miniButton))
|
||||
{
|
||||
tagsList.list.Clear();
|
||||
}
|
||||
|
||||
if (GUI.Button(loadRect, "Load", EditorStyles.miniButton))
|
||||
{
|
||||
string fname = EditorUtility.OpenFilePanel("Load", "", "txt");
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fname))
|
||||
{
|
||||
var tags = File.ReadAllLines(fname);
|
||||
tagsList.list.Clear();
|
||||
for (int i = 0; i < tags.Length; i++)
|
||||
{
|
||||
tagsList.list.Add(tags[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
GUIUtility.ExitGUI(); // This is necessary to prevent a crash in Unity
|
||||
}
|
||||
if (GUI.Button(saveRect, "Save", EditorStyles.miniButton))
|
||||
{
|
||||
string fname = EditorUtility.SaveFilePanel("Save", "", "Tags", "txt");
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fname))
|
||||
{
|
||||
string[] tags = new string[tagsList.list.Count];
|
||||
for (int i = 0; i < tagsList.list.Count; i++)
|
||||
{
|
||||
tags[i] = tagsList.list[i].ToString();
|
||||
}
|
||||
File.WriteAllLines(fname, tags);
|
||||
}
|
||||
}
|
||||
GUIUtility.ExitGUI(); // This is necessary to prevent a crash in Unity
|
||||
}
|
||||
if (GUI.Button(addBlankrect, "+", EditorStyles.miniButton))
|
||||
{
|
||||
tagsList.list.Add("");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.Label(ctrl, "Add defaults in prefs");
|
||||
if (GUI.Button(addBlankrect, "+", EditorStyles.miniButton))
|
||||
{
|
||||
tagsList.list.Add("");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tagsList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
if (tagsList.list.Count <= index)
|
||||
return;
|
||||
var element = tagsList.list[index].ToString();
|
||||
rect.y += 2;
|
||||
tagsList.list[index]= EditorGUI.DelayedTextField(new Rect(rect.x, rect.y, rect.width - 22, EditorGUIUtility.singleLineHeight), element);
|
||||
// draw the remove button
|
||||
if (GUI.Button(new Rect(rect.x + rect.width - 20, rect.y, 20, EditorGUIUtility.singleLineHeight), "x"))
|
||||
{
|
||||
tagsList.list.RemoveAt(index);
|
||||
}
|
||||
};
|
||||
tagsList.footerHeight = 0;
|
||||
return tagsList;
|
||||
}
|
||||
|
||||
|
||||
public static UnityEditorInternal.ReorderableList InitTagsList(string fieldName, SerializedObject serializedObject)
|
||||
{
|
||||
GetTags();
|
||||
|
||||
var arrayProperty = serializedObject.FindProperty(fieldName);
|
||||
//var HideTagsProperty = serializedObject.FindProperty("HideTags");
|
||||
var tagsList = new UnityEditorInternal.ReorderableList(serializedObject, arrayProperty, true, true, false, false);
|
||||
tagsList.drawHeaderCallback = (Rect rect) =>
|
||||
{
|
||||
// You know what would be nice?
|
||||
// if GUILayout.BeginArea worked here. It doesn't, and while reported and acknowledged,
|
||||
// it's not going to be fixed.
|
||||
// So we have to do this the hard way.
|
||||
|
||||
float width1 = 56;
|
||||
float width3 = 44; // Add button
|
||||
float width4 = 20; // + button
|
||||
float width5 = 44; // Clear button
|
||||
float width6 = 44; // load button
|
||||
float width7 = 44; // save button
|
||||
float width2 = rect.width - (width1 + width3 + width4 + width5 + width6 + width7);
|
||||
|
||||
float pos1 = 0;
|
||||
float pos2 = pos1 + width1;
|
||||
float pos3 = pos2 + width2;
|
||||
float pos4 = pos3 + width3;
|
||||
float pos5 = pos4 + width4;
|
||||
float pos6 = pos5 + width5;
|
||||
float pos7 = pos6 + width6;
|
||||
|
||||
Rect ctrl = new Rect(rect.x, rect.y, rect.width - 20, rect.height);
|
||||
|
||||
Rect labelrect = new Rect(rect.x + pos1, rect.y, width1, rect.height);
|
||||
Rect dropdownrect = new Rect(rect.x + pos2, rect.y, width2, rect.height);
|
||||
Rect adddropdownrect = new Rect(rect.x + pos3, rect.y, width3, rect.height);
|
||||
Rect addBlankrect = new Rect(rect.x + pos4, rect.y, width4, rect.height);
|
||||
Rect clearRect = new Rect(rect.x + pos5, rect.y, width5, rect.height);
|
||||
Rect loadRect = new Rect(rect.x + pos6, rect.y, width6, rect.height);
|
||||
Rect saveRect = new Rect(rect.x + pos7, rect.y, width7, rect.height);
|
||||
|
||||
EditorGUI.LabelField(labelrect, "Select");
|
||||
//ctrl.x += rect.width - 20;
|
||||
//ctrl.width = 20;
|
||||
|
||||
if (defaultTags.Length > 0)
|
||||
{
|
||||
selectedTag = Mathf.Clamp(selectedTag, 0, defaultTags.Length - 1);
|
||||
selectedTag = EditorGUI.Popup(dropdownrect, selectedTag, defaultTags);
|
||||
|
||||
if (GUI.Button(adddropdownrect, "Add", EditorStyles.miniButton))
|
||||
{
|
||||
string currentTag = basetagnames[selectedTag];
|
||||
var arraySizeProp = arrayProperty.FindPropertyRelative("Array.size");
|
||||
|
||||
bool found = false;
|
||||
for (var i = 0; i < arraySizeProp.intValue; i++)
|
||||
{
|
||||
string val = arrayProperty.GetArrayElementAtIndex(i).stringValue;
|
||||
if (val == currentTag)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the tag to the list if it's not already there
|
||||
if (!found)
|
||||
{
|
||||
//
|
||||
arrayProperty.InsertArrayElementAtIndex(arrayProperty.arraySize);
|
||||
arrayProperty.GetArrayElementAtIndex(arrayProperty.arraySize - 1).stringValue = currentTag;
|
||||
}
|
||||
}
|
||||
|
||||
if (GUI.Button(clearRect, "Clear", EditorStyles.miniButton))
|
||||
{
|
||||
arrayProperty.ClearArray();
|
||||
}
|
||||
if (GUI.Button(loadRect, "Load", EditorStyles.miniButton))
|
||||
{
|
||||
string fname = EditorUtility.OpenFilePanel("Load", "", "txt");
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fname))
|
||||
{
|
||||
var tags = File.ReadAllLines(fname);
|
||||
arrayProperty.ClearArray();
|
||||
for (int i = 0; i < tags.Length; i++)
|
||||
{
|
||||
arrayProperty.InsertArrayElementAtIndex(i);
|
||||
arrayProperty.GetArrayElementAtIndex(i).stringValue = tags[i];
|
||||
}
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
GUIUtility.ExitGUI(); // This is necessary to prevent a crash in Unity
|
||||
}
|
||||
if (GUI.Button(saveRect, "Save", EditorStyles.miniButton))
|
||||
{
|
||||
string fname = EditorUtility.SaveFilePanel("Save", "", "Tags", "txt");
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fname))
|
||||
{
|
||||
string[] tags = new string[arrayProperty.arraySize];
|
||||
for (int i = 0; i < arrayProperty.arraySize; i++)
|
||||
{
|
||||
tags[i] = arrayProperty.GetArrayElementAtIndex(i).stringValue;
|
||||
}
|
||||
File.WriteAllLines(fname, tags);
|
||||
}
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
GUIUtility.ExitGUI(); // This is necessary to prevent a crash in Unity
|
||||
}
|
||||
if (GUI.Button(addBlankrect, "+", EditorStyles.miniButton))
|
||||
{
|
||||
arrayProperty.InsertArrayElementAtIndex(arrayProperty.arraySize);
|
||||
// Sometimes, Unity will set the value to the last added element, so we need to set it to an empty string
|
||||
arrayProperty.GetArrayElementAtIndex(arrayProperty.arraySize - 1).stringValue = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.Label(ctrl, "Add defaults in prefs");
|
||||
if (GUI.Button(addBlankrect, "+", EditorStyles.miniButton))
|
||||
{
|
||||
arrayProperty.InsertArrayElementAtIndex(arrayProperty.arraySize);
|
||||
// Sometimes, Unity will set the value to the last added element, so we need to set it to an empty string
|
||||
arrayProperty.GetArrayElementAtIndex(arrayProperty.arraySize - 1).stringValue = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tagsList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
if (tagsList.serializedProperty.arraySize <= index)
|
||||
return;
|
||||
var element = tagsList.serializedProperty.GetArrayElementAtIndex(index);
|
||||
if (element == null)
|
||||
return;
|
||||
rect.y += 2;
|
||||
element.stringValue = EditorGUI.DelayedTextField(new Rect(rect.x, rect.y, rect.width - 22, EditorGUIUtility.singleLineHeight), element.stringValue);
|
||||
// draw the remove button
|
||||
if (GUI.Button(new Rect(rect.x + rect.width - 20, rect.y, 20, EditorGUIUtility.singleLineHeight), "x"))
|
||||
{
|
||||
tagsList.serializedProperty.DeleteArrayElementAtIndex(index);
|
||||
}
|
||||
};
|
||||
tagsList.footerHeight = 0;
|
||||
return tagsList;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7bcbdb6fc3cad6a4186ff6e931b5b19b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/GUIHelper.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,947 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(GeometrySelector))]
|
||||
public class GeometrySelectorWindow : Editor
|
||||
{
|
||||
private GeometrySelector _Source;
|
||||
private SlotDataAsset _OccluderSlotData = null;
|
||||
private MeshHideAsset _OccluderMeshHide = null;
|
||||
|
||||
private float _occluderOffset = 0;
|
||||
private Vector3 _occluderPosition = Vector3.zero;
|
||||
private Vector3 _occluderRotation = new Vector3(270.0f, 0.0f, 0.0f);
|
||||
private Vector3 _occluderScale = Vector3.one;
|
||||
private bool bothDirections = false;
|
||||
|
||||
private bool isMirroring = false;
|
||||
private bool doneEditing = false; //set to true to end editing this objects
|
||||
private bool showWireframe = true; //whether to switch to wireframe mode or not
|
||||
private bool backfaceCull = true;
|
||||
private bool isSelecting = false; //is the user actively selecting
|
||||
private bool setSelectedOn = true; //whether to set the triangles to selected or unselection when using selection box
|
||||
private bool cancelSave = false; // Set this to true to cancel save;
|
||||
private Vector2 startMousePos;
|
||||
private Texture2D textureMap;
|
||||
|
||||
private const float drawTolerance = 10.0f; //in pixels
|
||||
private Color selectionColor = new Color(0.8f, 0.8f, 0.95f, 0.15f);
|
||||
private List<GeometrySelector.SceneInfo> restoreScenes;
|
||||
private Vector2 scrollPosition = Vector2.zero;
|
||||
private int selectionSelected = 0;
|
||||
private static string[] selectionOptions = new string[] { "Select", "UnSelect" };
|
||||
private Rect infoRect = new Rect(10, 30, 400, 30);
|
||||
private GUIStyle whiteLabels;
|
||||
private GUIStyle blackLabels;
|
||||
private bool disposed;
|
||||
|
||||
|
||||
public static GeometrySelectorWindow Instance { get; private set; }
|
||||
public static bool IsOpen
|
||||
{
|
||||
get { return Instance != null; }
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
disposed = false;
|
||||
|
||||
_Source = target as GeometrySelector;
|
||||
if (_Source != null)
|
||||
{
|
||||
restoreScenes = _Source.restoreScenes;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("GeometrySelector not found!");
|
||||
}
|
||||
|
||||
Instance = this;
|
||||
EditorApplication.update += GeometryUpdate;
|
||||
UpdateShadingMode(showWireframe);
|
||||
|
||||
whiteLabels = new GUIStyle(EditorStyles.boldLabel);
|
||||
blackLabels = new GUIStyle(EditorStyles.boldLabel);
|
||||
whiteLabels.normal.textColor = Color.white;
|
||||
blackLabels.normal.textColor = Color.black;
|
||||
|
||||
|
||||
// Tools.current = Tool.None;
|
||||
// Tools.hidden = true;
|
||||
EditorApplication.LockReloadAssemblies();
|
||||
SceneView.duringSceneGui += this.DoSceneGUI;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SceneView.duringSceneGui -= this.DoSceneGUI;
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
private void CleanUp()
|
||||
{
|
||||
// Guard against Unity calling this via update multiple times even after
|
||||
// it's been removed from the event. Only happens on Mac.
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
|
||||
Instance = null;
|
||||
EditorApplication.update -= GeometryUpdate;
|
||||
Tools.hidden = false;
|
||||
DestroySceneEditObject();
|
||||
EditorApplication.UnlockReloadAssemblies();
|
||||
|
||||
if (restoreScenes != null)
|
||||
{
|
||||
foreach (GeometrySelector.SceneInfo s in restoreScenes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s.path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
EditorSceneManager.OpenScene(s.path, s.mode);
|
||||
}
|
||||
if (_Source.currentSceneView != null)
|
||||
{
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
_Source.currentSceneView.sceneLighting = _Source.SceneviewLightingState;
|
||||
#else
|
||||
_Source.currentSceneView.m_SceneLighting = _Source.SceneviewLightingState;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Mesh Selector Utilities", EditorStyles.largeLabel, GUILayout.MaxHeight(25) );
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
GUIHelper.BeginVerticalPadded(10, new Color(0.55f, 0.25f, 0.25f));
|
||||
SceneWindow(0);
|
||||
GUIHelper.EndVerticalPadded(10);
|
||||
#endif
|
||||
scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUIStyle.none);
|
||||
GUILayout.Space(20);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
bool newNormals = EditorGUILayout.Toggle("Visualize Normals", _Source.visualizeNormals);
|
||||
if ( newNormals != _Source.visualizeNormals )
|
||||
{
|
||||
_Source.visualizeNormals = newNormals;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
Color32 newNormalColor = EditorGUILayout.ColorField(_Source.normalsColor);
|
||||
if (!newNormalColor.Equals(_Source.normalsColor))
|
||||
{
|
||||
_Source.normalsColor = newNormalColor;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
float newNormalLength = EditorGUILayout.Slider("Normals Length", _Source.normalsLength, 0.01f, 1.5f);
|
||||
if( newNormalLength != _Source.normalsLength )
|
||||
{
|
||||
_Source.normalsLength = newNormalLength;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
GUILayout.Space(20);
|
||||
EditorGUILayout.LabelField(new GUIContent("Occlusion Slot (Optional)","Use this mesh to attempt to automatically detect occluded triangles"));
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
SlotDataAsset newOccluderSlotData = (SlotDataAsset) EditorGUILayout.ObjectField(_OccluderSlotData, typeof(SlotDataAsset), false);
|
||||
MeshHideAsset newOccluderMeshHide = (MeshHideAsset)EditorGUILayout.ObjectField(_OccluderMeshHide, typeof(MeshHideAsset), false);
|
||||
if(GUILayout.Button("Clear", GUILayout.MaxWidth(60)))
|
||||
{
|
||||
_OccluderSlotData = null;
|
||||
newOccluderSlotData = null;
|
||||
_OccluderMeshHide = null;
|
||||
newOccluderMeshHide = null;
|
||||
_Source.occlusionMesh = null;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
if (newOccluderSlotData != _OccluderSlotData)
|
||||
{
|
||||
_OccluderSlotData = newOccluderSlotData;
|
||||
_OccluderMeshHide = null;
|
||||
newOccluderMeshHide = null;
|
||||
if (_OccluderSlotData != null)
|
||||
{
|
||||
_Source.UpdateOcclusionMesh(_OccluderSlotData.meshData, _occluderOffset, _occluderPosition, _occluderRotation, _occluderScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
_Source.occlusionMesh = null;
|
||||
}
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
if (newOccluderMeshHide != _OccluderMeshHide)
|
||||
{
|
||||
if (newOccluderMeshHide == _Source.meshAsset)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "Can not select the same MeshHideAsset currently being edited!", "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
_OccluderMeshHide = newOccluderMeshHide;
|
||||
_OccluderSlotData = null;
|
||||
newOccluderSlotData = null;
|
||||
if (_OccluderMeshHide != null && _OccluderMeshHide != _Source.meshAsset)
|
||||
{
|
||||
_Source.UpdateOcclusionMesh(_OccluderMeshHide, _occluderOffset, _occluderPosition, _occluderRotation, _occluderScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
_Source.occlusionMesh = null;
|
||||
}
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(_Source.occlusionMesh == null);
|
||||
bool changed = false;
|
||||
|
||||
Color32 newOcclusionColor = EditorGUILayout.ColorField("Occlusion Mesh Color",_Source.occlusionColor);
|
||||
if( !newOcclusionColor.Equals(_Source.occlusionColor))
|
||||
{
|
||||
_Source.occlusionColor = newOcclusionColor;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
bool newWireframe = EditorGUILayout.Toggle("Occlusion Mesh Wireframe", _Source.occlusionWireframe);
|
||||
if( newWireframe != _Source.occlusionWireframe )
|
||||
{
|
||||
_Source.occlusionWireframe = newWireframe;
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
float newOffset = EditorGUILayout.Slider(new GUIContent("Normal Offset", "Distance along the normal to offset each vertex of the occlusion mesh"), _occluderOffset, -0.1f, 0.25f);
|
||||
if (!Mathf.Approximately(newOffset,_occluderOffset))
|
||||
{
|
||||
_occluderOffset = newOffset;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
Vector3 newPosition = EditorGUILayout.Vector3Field(new GUIContent("Position", "Offset the position of the occluder"), _occluderPosition);
|
||||
if( newPosition != _occluderPosition)
|
||||
{
|
||||
_occluderPosition = newPosition;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
Vector3 newRotation = EditorGUILayout.Vector3Field(new GUIContent("Rotation", "Offset the rotation (degrees) of the occluder"), _occluderRotation);
|
||||
if (newRotation != _occluderRotation)
|
||||
{
|
||||
_occluderRotation = newRotation;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
Vector3 newScale = EditorGUILayout.Vector3Field(new GUIContent("Scale", "Offset the scale of the occluder"), _occluderScale);
|
||||
if (newScale != _occluderScale)
|
||||
{
|
||||
_occluderScale = newScale;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
bothDirections = EditorGUILayout.Toggle( new GUIContent("RayCast Both Directions",
|
||||
"Determines whether to raycast only outward along the normal from the source mesh or in both directions. Both directions can be helpful if the occlusion slot is close to the surface of the source mesh or even slightly under it."),
|
||||
bothDirections);
|
||||
|
||||
if (changed)
|
||||
{
|
||||
if(_OccluderSlotData)
|
||||
{
|
||||
_Source.UpdateOcclusionMesh( _OccluderSlotData.meshData, _occluderOffset, _occluderPosition, _occluderRotation, _occluderScale);
|
||||
}
|
||||
|
||||
if (_OccluderMeshHide)
|
||||
{
|
||||
_Source.UpdateOcclusionMesh( _OccluderMeshHide, _occluderOffset, _occluderPosition, _occluderRotation, _occluderScale);
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button(new GUIContent("Raycast Hidden Faces", "Warning! This will clear the current selection.")))
|
||||
{
|
||||
RaycastHide(bothDirections);
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
GUILayout.Space(20);
|
||||
if (GUILayout.Button("Export Occlusion Map"))
|
||||
{
|
||||
GeometryUVEditorWindow.Init(_Source, true);
|
||||
}
|
||||
EditorGUILayout.LabelField(new GUIContent("Occlusion Map (Optional)", "Use this texture to attempt to automatically detect occluded triangles"));
|
||||
textureMap = EditorGUILayout.ObjectField("Set From Occluaion Map", textureMap, typeof(Texture2D), false) as Texture2D;
|
||||
if (GUILayout.Button("Add to Occlusion from texture."))
|
||||
{
|
||||
if (_Source != null)
|
||||
{
|
||||
if (ValidateTexture(textureMap))
|
||||
{
|
||||
_Source.UpdateFromTexture(textureMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Set Occlusion from texture (clears existing)"))
|
||||
{
|
||||
if (_Source != null)
|
||||
{
|
||||
if (ValidateTexture(textureMap))
|
||||
{
|
||||
ClearAll();
|
||||
_Source.UpdateFromTexture(textureMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.Space(20);
|
||||
if (GUILayout.Button(new GUIContent("View UV Layout", "Brings up a window displaying the uv layout of the currently selected object and export to texture options.")))
|
||||
{
|
||||
GeometryUVEditorWindow.Init(_Source,false);
|
||||
}
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
public bool ValidateTexture(Texture2D textureMap)
|
||||
{
|
||||
if (textureMap == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Warning", "A readable texture must be selected before processing.", "OK");
|
||||
return false;
|
||||
}
|
||||
else if (!textureMap.isReadable)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Warning", "Texture is not readable. Please set the read/write flag on the texture import settings.", "OK");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateShadingMode(bool wireframeOn)
|
||||
{
|
||||
if (SceneView.lastActiveSceneView == null)
|
||||
{
|
||||
Debug.LogWarning("currentDrawingSceneView is null");
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_2018_1_OR_NEWER
|
||||
SceneView.CameraMode newMode = SceneView.lastActiveSceneView.cameraMode;
|
||||
if (wireframeOn)
|
||||
{
|
||||
newMode.drawMode = DrawCameraMode.TexturedWire;
|
||||
}
|
||||
else
|
||||
{
|
||||
newMode.drawMode = DrawCameraMode.Textured;
|
||||
}
|
||||
#else
|
||||
if (wireframeOn)
|
||||
SceneView.lastActiveSceneView.renderMode = DrawCameraMode.TexturedWire;
|
||||
else
|
||||
SceneView.lastActiveSceneView.renderMode = DrawCameraMode.Textured;
|
||||
#endif
|
||||
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
|
||||
private void ClearAll()
|
||||
{
|
||||
if (_Source != null)
|
||||
{
|
||||
_Source.ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectAll()
|
||||
{
|
||||
if (_Source != null)
|
||||
{
|
||||
_Source.SelectAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void Invert()
|
||||
{
|
||||
if (_Source != null)
|
||||
{
|
||||
_Source.Invert();
|
||||
}
|
||||
}
|
||||
|
||||
private void GeometryUpdate()
|
||||
{
|
||||
if (doneEditing)
|
||||
{
|
||||
SaveSelection(_Source.selectedTriangles);
|
||||
CleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetLabelStart()
|
||||
{
|
||||
infoRect = new Rect(10, 20, 400, 30);
|
||||
}
|
||||
|
||||
private void MoveToNextMessage(float xoffset, float yoffset)
|
||||
{
|
||||
infoRect.x += xoffset;
|
||||
infoRect.y += yoffset;
|
||||
}
|
||||
|
||||
private string SelectionString(bool selectionMode)
|
||||
{
|
||||
return selectionMode ? "Selection Mode: Add" : "Selection Mode: Remove";
|
||||
}
|
||||
|
||||
public void SaveSelection(BitArray selection)
|
||||
{
|
||||
if (cancelSave)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_Source.meshAsset.SaveSelection(selection);
|
||||
}
|
||||
|
||||
private void DrawNextLabel(string lbl)
|
||||
{
|
||||
// Frame the text so it's visible everywhere
|
||||
MoveToNextMessage(-1, -1);
|
||||
GUI.Label(infoRect, lbl, blackLabels);
|
||||
MoveToNextMessage(2, 0);
|
||||
GUI.Label(infoRect, lbl, blackLabels);
|
||||
MoveToNextMessage(0, 2);
|
||||
GUI.Label(infoRect, lbl, blackLabels);
|
||||
MoveToNextMessage(-2, 0);
|
||||
GUI.Label(infoRect, lbl, blackLabels);
|
||||
MoveToNextMessage(1, -1);
|
||||
GUI.Label(infoRect, lbl, whiteLabels);
|
||||
MoveToNextMessage(0, 20);
|
||||
}
|
||||
|
||||
|
||||
void SceneWindow(int WindowID)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Selection", GUILayout.Width(100));
|
||||
if (GUILayout.Button("Clear"))
|
||||
{
|
||||
ClearAll();
|
||||
}
|
||||
if (GUILayout.Button("Select All"))
|
||||
{
|
||||
SelectAll();
|
||||
}
|
||||
if (GUILayout.Button("Invert"))
|
||||
{
|
||||
Invert();
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Selection Mode", GUILayout.Width(100));
|
||||
selectionSelected = GUILayout.SelectionGrid(selectionSelected, selectionOptions, selectionOptions.Length);
|
||||
if (selectionSelected == 0)
|
||||
{
|
||||
setSelectedOn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
setSelectedOn = false;
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label("Options", GUILayout.Width(100));
|
||||
bool toggled = GUILayout.Toggle(showWireframe, new GUIContent("Wireframe", "Toggle showing the Wireframe"), "Button");
|
||||
if (toggled != showWireframe)
|
||||
{
|
||||
UpdateShadingMode(toggled);
|
||||
}
|
||||
showWireframe = toggled;
|
||||
backfaceCull = GUILayout.Toggle(backfaceCull, new GUIContent("Backface Cull", "Toggle whether to select back faces"), "Button");
|
||||
isMirroring = GUILayout.Toggle(isMirroring, new GUIContent("X Symmetry", "Mirror Selection on X axis"), "Button");
|
||||
GUILayout.EndHorizontal();
|
||||
if (!isMirroring)
|
||||
{
|
||||
GUILayout.Space(18);
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.Label("Symmetry not supported in area select");
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Focus Mesh"))
|
||||
{
|
||||
Selection.activeGameObject = _Source.gameObject;
|
||||
EditorApplication.delayCall += ForceFrame;
|
||||
}
|
||||
GUILayout.Space(100);
|
||||
if (GUILayout.Button("Save & Return"))
|
||||
{
|
||||
doneEditing = true;
|
||||
/* save location and orientation of camera */
|
||||
string CamKey = _Source.meshAsset.name + "_MHA_Cam";
|
||||
CamSaver cs = new CamSaver(_Source.currentSceneView.camera.transform);
|
||||
EditorPrefs.SetString(CamKey, cs.ToString());
|
||||
}
|
||||
if (GUILayout.Button("Cancel Edits"))
|
||||
{
|
||||
doneEditing = true;
|
||||
cancelSave = true;
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void ForceFrame()
|
||||
{
|
||||
SceneView.FrameLastActiveSceneView();
|
||||
}
|
||||
|
||||
void DoSceneGUI(SceneView scene)
|
||||
{
|
||||
ResetLabelStart();
|
||||
|
||||
Handles.BeginGUI();
|
||||
|
||||
DrawNextLabel("Left click and drag to area select");
|
||||
DrawNextLabel("Hold SHIFT while dragging to paint");
|
||||
DrawNextLabel("Hold CTRL while dragging to paint inverse");
|
||||
DrawNextLabel("Hold ALT while dragging to orbit");
|
||||
DrawNextLabel("Return to original scene by pressing \"Save and Return\"");
|
||||
Handles.EndGUI();
|
||||
|
||||
if (_Source == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSelecting && Event.current.alt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Rect selectionRect = new Rect();
|
||||
|
||||
if (Event.current.type == EventType.Layout)
|
||||
{
|
||||
HandleUtility.AddDefaultControl(GUIUtility.GetControlID(GetHashCode(), FocusType.Passive));
|
||||
}
|
||||
|
||||
if (isSelecting)
|
||||
{
|
||||
Vector2 selectionSize = (Event.current.mousePosition - startMousePos);
|
||||
Vector2 correctedPos = startMousePos;
|
||||
if (selectionSize.x < 0)
|
||||
{
|
||||
selectionSize.x = Mathf.Abs(selectionSize.x);
|
||||
correctedPos.x = startMousePos.x - selectionSize.x;
|
||||
}
|
||||
if (selectionSize.y < 0)
|
||||
{
|
||||
selectionSize.y = Mathf.Abs(selectionSize.y);
|
||||
correctedPos.y = startMousePos.y - selectionSize.y;
|
||||
}
|
||||
|
||||
if (Event.current.shift || Event.current.control)
|
||||
{
|
||||
bool selVal = setSelectedOn;
|
||||
if (Event.current.control)
|
||||
{
|
||||
selVal = !selVal;
|
||||
}
|
||||
|
||||
startMousePos = Event.current.mousePosition;
|
||||
int mirrorHit = -1;
|
||||
int triangleHit = RayPick(isMirroring, out mirrorHit);
|
||||
if (triangleHit >= 0)
|
||||
{
|
||||
if (_Source.selectedTriangles[triangleHit] != selVal)
|
||||
{
|
||||
// avoid constant rebuild.
|
||||
_Source.selectedTriangles[triangleHit] = selVal;
|
||||
if (isMirroring && mirrorHit != -1)
|
||||
{
|
||||
_Source.selectedTriangles[mirrorHit] = selVal;
|
||||
}
|
||||
_Source.UpdateSelectionMesh();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (selectionSize.x > drawTolerance || selectionSize.y > drawTolerance)
|
||||
{
|
||||
Handles.BeginGUI();
|
||||
selectionRect = new Rect(correctedPos, selectionSize);
|
||||
Handles.DrawSolidRectangleWithOutline(selectionRect, selectionColor, Color.black);
|
||||
Handles.EndGUI();
|
||||
HandleUtility.Repaint();
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.MouseDrag)
|
||||
{
|
||||
SceneView.RepaintAll();
|
||||
Event.current.Use();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Single mouse click
|
||||
if (Event.current != null && Event.current.type == EventType.MouseDown && Event.current.button == 0)
|
||||
{
|
||||
isSelecting = true;
|
||||
startMousePos = Event.current.mousePosition;
|
||||
int mirrorHit = -1;
|
||||
|
||||
int triangleHit = RayPick(isMirroring,out mirrorHit);
|
||||
|
||||
if (triangleHit >= 0)
|
||||
{
|
||||
_Source.selectedTriangles[triangleHit] = !_Source.selectedTriangles[triangleHit];
|
||||
if (isMirroring && mirrorHit != -1)
|
||||
{
|
||||
// Mirror triangle should be the same as the hit triangle regardless of previous selection.
|
||||
_Source.selectedTriangles[mirrorHit] = _Source.selectedTriangles[triangleHit];
|
||||
}
|
||||
_Source.UpdateSelectionMesh();
|
||||
}
|
||||
}
|
||||
|
||||
if (Event.current != null && Event.current.type == EventType.MouseUp && Event.current.button == 0)
|
||||
{
|
||||
if (isSelecting)
|
||||
{
|
||||
isSelecting = false;
|
||||
Rect screenSelectionRect = new Rect();
|
||||
screenSelectionRect.min = HandleUtility.GUIPointToScreenPixelCoordinate(new Vector2(selectionRect.xMin, selectionRect.yMax));
|
||||
screenSelectionRect.max = HandleUtility.GUIPointToScreenPixelCoordinate(new Vector2(selectionRect.xMax, selectionRect.yMin));
|
||||
|
||||
|
||||
int[] triangles = _Source.meshAsset.asset.meshData.submeshes[_Source.meshAsset.asset.subMeshIndex].nativeTriangles.ToArray();
|
||||
for (int i = 0; i < triangles.Length; i+=3 )
|
||||
{
|
||||
bool found = false;
|
||||
Vector3 center = new Vector3();
|
||||
Vector3 centerNormal = new Vector3();
|
||||
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
Vector3 vertex = _Source.meshAsset.asset.meshData.vertices[triangles[i+k]];
|
||||
vertex = _Source.transform.localToWorldMatrix.MultiplyVector(vertex);
|
||||
|
||||
Vector3 normal = _Source.meshAsset.asset.meshData.normals[triangles[i+k]];
|
||||
normal = _Source.transform.localToWorldMatrix.MultiplyVector(normal);
|
||||
|
||||
center += vertex;
|
||||
centerNormal += normal;
|
||||
|
||||
vertex = SceneView.currentDrawingSceneView.camera.WorldToScreenPoint(vertex);
|
||||
|
||||
if (screenSelectionRect.Contains( vertex ))
|
||||
{
|
||||
if (backfaceCull)
|
||||
{
|
||||
if (Vector3.Dot(normal, SceneView.currentDrawingSceneView.camera.transform.forward) < -0.0f)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
center = center / 3;
|
||||
centerNormal = centerNormal / 3;
|
||||
center = SceneView.currentDrawingSceneView.camera.WorldToScreenPoint(center);
|
||||
if (screenSelectionRect.Contains(center))
|
||||
{
|
||||
if (backfaceCull)
|
||||
{
|
||||
if (Vector3.Dot(centerNormal, SceneView.currentDrawingSceneView.camera.transform.forward) < -0.0f)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
_Source.selectedTriangles[(i / 3)] = setSelectedOn;
|
||||
}
|
||||
}
|
||||
|
||||
_Source.UpdateSelectionMesh();
|
||||
}
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.MouseMove)
|
||||
{
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
}
|
||||
|
||||
private int RayPick(bool Mirror, out int MirrorTriangle)
|
||||
{
|
||||
MirrorTriangle = -1;
|
||||
if (Camera.current == null)
|
||||
{
|
||||
Debug.LogWarning("Camera is null!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
|
||||
RaycastHit hit;
|
||||
if (!Physics.Raycast(ray, out hit))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
MeshCollider meshCollider = hit.collider as MeshCollider;
|
||||
if (meshCollider == null || meshCollider.sharedMesh == null || meshCollider != _Source.meshCollider)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Mirror)
|
||||
{
|
||||
RaycastHit MirrorHit;
|
||||
|
||||
// this only works because the model is at 0,0
|
||||
Vector3 MirrorHitPt = hit.point;
|
||||
Vector3 MirrorNormal = hit.normal;
|
||||
|
||||
MirrorHitPt.x = -MirrorHitPt.x;
|
||||
MirrorNormal.x = -MirrorNormal.x;
|
||||
|
||||
Vector3 NewSource = MirrorHitPt + Vector3.Scale(MirrorNormal, new Vector3(0.1f,0.1f,0.1f));
|
||||
Vector3 NewNormal = Vector3.Scale(MirrorNormal,new Vector3(-1, -1, -1));
|
||||
Ray NewRay = new Ray(NewSource, NewNormal);
|
||||
if (Physics.Raycast(NewRay, out MirrorHit))
|
||||
{
|
||||
MirrorTriangle = MirrorHit.triangleIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return hit.triangleIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified ray hits the triangle descibed by p1, p2 and p3.
|
||||
/// Möller–Trumbore ray-triangle intersection algorithm implementation.
|
||||
/// Source from Unity Answers
|
||||
/// </summary>
|
||||
/// <param name="ray">The ray to test hit for.</param>
|
||||
/// <param name="p1">Vertex 1 of the triangle.</param>
|
||||
/// <param name="p2">Vertex 2 of the triangle.</param>
|
||||
/// <param name="p3">Vertex 3 of the triangle.</param>
|
||||
/// <returns><c>true</c> when the ray hits the triangle, otherwise <c>false</c></returns>
|
||||
public static bool RayTriIntersect(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, out float dist)
|
||||
{
|
||||
// Vectors from p1 to p2/p3 (edges)
|
||||
Vector3 e1, e2;
|
||||
|
||||
Vector3 p, q, t;
|
||||
float det, invDet, u, v;
|
||||
dist = Mathf.Infinity;
|
||||
|
||||
//Find vectors for edges sharing vertex/point p1
|
||||
e1 = p2 - p1;
|
||||
e2 = p3 - p1;
|
||||
|
||||
// Calculate determinant
|
||||
p = Vector3.Cross(ray.direction, e2);
|
||||
|
||||
//Calculate determinat
|
||||
det = Vector3.Dot(e1, p);
|
||||
|
||||
//if determinant is near zero, ray lies in plane of triangle otherwise not
|
||||
if (det > -Mathf.Epsilon && det < Mathf.Epsilon) { return false; }
|
||||
invDet = 1.0f / det;
|
||||
|
||||
//calculate distance from p1 to ray origin
|
||||
t = ray.origin - p1;
|
||||
|
||||
//Calculate u parameter
|
||||
u = Vector3.Dot(t, p) * invDet;
|
||||
|
||||
//Check for ray hit
|
||||
if (u < 0 || u > 1) { return false; }
|
||||
|
||||
//Prepare to test v parameter
|
||||
q = Vector3.Cross(t, e1);
|
||||
|
||||
//Calculate v parameter
|
||||
v = Vector3.Dot(ray.direction, q) * invDet;
|
||||
|
||||
//Check for ray hit
|
||||
if (v < 0 || u + v > 1) { return false; }
|
||||
|
||||
dist = Vector3.Dot(e2, q) * invDet;
|
||||
|
||||
if ((Vector3.Dot(e2, q) * invDet) > Mathf.Epsilon)
|
||||
{
|
||||
//ray does intersect
|
||||
return true;
|
||||
}
|
||||
|
||||
// No hit at all
|
||||
return false;
|
||||
}
|
||||
|
||||
private void RaycastHide(bool bothDirections = false)
|
||||
{
|
||||
if (_Source == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Mesh targetMesh = _Source.sharedMesh;
|
||||
if (targetMesh == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Mesh occlusionMesh = _Source.occlusionMesh;
|
||||
if (occlusionMesh == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3[] targetVerts = targetMesh.vertices;
|
||||
Vector3[] targetNorms = targetMesh.normals;
|
||||
if (targetNorms.Length != targetVerts.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Matrix4x4 m = _Source.gameObject.transform.localToWorldMatrix;
|
||||
for (int i = 0; i < targetVerts.Length; i++)
|
||||
{
|
||||
targetVerts[i] = m.MultiplyPoint3x4(targetVerts[i]);
|
||||
targetNorms[i] = m.MultiplyPoint3x4(targetNorms[i]);
|
||||
}
|
||||
|
||||
Vector3[] occlusionVerts = occlusionMesh.vertices;
|
||||
List<int[]> occlusionTriangles = new List<int[]>();
|
||||
for (int i = 0; i < occlusionMesh.subMeshCount; i++)
|
||||
{
|
||||
occlusionTriangles.Add(occlusionMesh.GetTriangles(i));
|
||||
}
|
||||
|
||||
BitArray vertexOccluded = new BitArray(targetVerts.Length);
|
||||
for (int i = 0; i < targetVerts.Length; i++)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Progress", "calculating...", ((float)i / (float)targetVerts.Length));
|
||||
|
||||
Ray testRay = new Ray(targetVerts[i], targetNorms[i] );
|
||||
Ray oppositeTestRay = new Ray(targetVerts[i], -targetNorms[i]);
|
||||
for (int j = 0; j < occlusionTriangles.Count; j++)
|
||||
{
|
||||
int[] triVerts = occlusionTriangles[j];
|
||||
for (int k = 0; k < triVerts.Length; k+= 3)
|
||||
{
|
||||
float dist = Mathf.Infinity;
|
||||
if (RayTriIntersect(testRay,
|
||||
occlusionVerts[triVerts[k + 0]],
|
||||
occlusionVerts[triVerts[k + 1]],
|
||||
occlusionVerts[triVerts[k + 2]],
|
||||
out dist))
|
||||
{
|
||||
if (dist <= _Source.normalsLength)
|
||||
{
|
||||
vertexOccluded[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(bothDirections)
|
||||
{
|
||||
if (RayTriIntersect(oppositeTestRay,
|
||||
occlusionVerts[triVerts[k + 0]],
|
||||
occlusionVerts[triVerts[k + 1]],
|
||||
occlusionVerts[triVerts[k + 2]],
|
||||
out dist))
|
||||
{
|
||||
if (dist <= _Source.normalsLength)
|
||||
{
|
||||
vertexOccluded[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vertexOccluded[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
_Source.selectedTriangles.SetAll(false);
|
||||
|
||||
for (int i = 0; i < /*_Source.sharedMesh.subMeshCount*/ 1; i++)
|
||||
{
|
||||
int[] triVerts = _Source.sharedMesh.GetTriangles(i);
|
||||
for (int j = 0; j < triVerts.Length; j += 3)
|
||||
{
|
||||
if (vertexOccluded[triVerts[j + 0]] ||
|
||||
vertexOccluded[triVerts[j + 1]] ||
|
||||
vertexOccluded[triVerts[j + 2]])
|
||||
{
|
||||
_Source.selectedTriangles[(j / 3)] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_Source.UpdateSelectionMesh();
|
||||
}
|
||||
|
||||
private void DestroySceneEditObject()
|
||||
{
|
||||
if (_Source != null)
|
||||
{
|
||||
UpdateShadingMode(false);
|
||||
if (_Source.meshAsset != null)
|
||||
{
|
||||
Selection.activeObject = _Source.meshAsset;
|
||||
}
|
||||
|
||||
DestroyImmediate(_Source.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0bf9409304b6f540b700b526f5d3c34
|
||||
timeCreated: 1500614165
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/GeometrySelectorWindow.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,633 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
public class GeometryUVEditorWindow : EditorWindow
|
||||
{
|
||||
private GeometrySelector geometrySelector;
|
||||
|
||||
private Vector2 startPosition = new Vector2(0, 0);
|
||||
private static float dimension = 512;
|
||||
private Vector2 offset;
|
||||
private Vector2 drag;
|
||||
|
||||
private Vector2[] box = { new Vector2(0,0), new Vector2(0,1), new Vector2(1,0), new Vector2(1,1) };
|
||||
private bool occlusionMapMode = false;
|
||||
private Texture2D occlusionMap = null;
|
||||
private Color32[] straightWhiteLine = new Color32[(int)dimension];
|
||||
|
||||
|
||||
public static void Init(GeometrySelector obj, bool OcclusionMapMode)
|
||||
{
|
||||
// Get existing open window or if none, make a new one:
|
||||
GeometryUVEditorWindow window = (GeometryUVEditorWindow)EditorWindow.GetWindow(typeof(GeometryUVEditorWindow));
|
||||
window.titleContent = new GUIContent("UV Layout");
|
||||
window.minSize = new Vector2(dimension, dimension);
|
||||
window.geometrySelector = obj;
|
||||
window.occlusionMapMode = OcclusionMapMode;
|
||||
if (OcclusionMapMode)
|
||||
{
|
||||
window.GenerateOcclusionMap();
|
||||
}
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (occlusionMap != null)
|
||||
{
|
||||
DestroyImmediate(occlusionMap);
|
||||
}
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (occlusionMap != null)
|
||||
{
|
||||
GUI.color = Color.white;
|
||||
GUI.DrawTexture(new Rect(startPosition.x, startPosition.y, occlusionMap.width, occlusionMap.height), occlusionMap);//, ScaleMode.ScaleToFit, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawGrid(20, 0.2f, Color.gray);
|
||||
DrawGrid(100, 0.4f, Color.gray);
|
||||
DrawUVLines();
|
||||
}
|
||||
|
||||
if(GUILayout.Button("Export", GUILayout.MaxWidth(100)))
|
||||
{
|
||||
SaveTexture();
|
||||
}
|
||||
|
||||
ProcessEvents(Event.current);
|
||||
|
||||
if (GUI.changed)
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessEvents(Event e)
|
||||
{
|
||||
drag = Vector2.zero;
|
||||
|
||||
switch(e.type)
|
||||
{
|
||||
case EventType.MouseDrag:
|
||||
if (e.button == 0)
|
||||
{
|
||||
OnDrag(e.delta);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrag(Vector2 delta)
|
||||
{
|
||||
drag = delta;
|
||||
|
||||
startPosition += delta;
|
||||
|
||||
GUI.changed = true;
|
||||
}
|
||||
|
||||
private void DrawUVLines()
|
||||
{
|
||||
if (geometrySelector == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BitArray selectedTris = geometrySelector.selectedTriangles;
|
||||
Mesh mesh = geometrySelector.sharedMesh;
|
||||
Vector2[] uvs = mesh.uv;
|
||||
int[] triangles = mesh.triangles;
|
||||
|
||||
Handles.BeginGUI();
|
||||
|
||||
//Draw UV Space box
|
||||
Handles.color = Color.white;
|
||||
Handles.DrawLine((box[0] * dimension) + startPosition, (box[1] * dimension) + startPosition);
|
||||
Handles.DrawLine((box[0] * dimension) + startPosition, (box[2] * dimension) + startPosition);
|
||||
Handles.DrawLine((box[3] * dimension) + startPosition, (box[1] * dimension) + startPosition);
|
||||
Handles.DrawLine((box[3] * dimension) + startPosition, (box[2] * dimension) + startPosition);
|
||||
|
||||
Vector2 uv0;
|
||||
Vector2 uv1;
|
||||
Vector2 uv2;
|
||||
|
||||
for(int i = 0; i < selectedTris.Length; i++)
|
||||
{
|
||||
if (selectedTris[i])
|
||||
{
|
||||
Handles.color = Color.red;
|
||||
}
|
||||
else
|
||||
{
|
||||
Handles.color = Color.white;
|
||||
}
|
||||
|
||||
int triIndex = i * 3;
|
||||
|
||||
uv0 = uvs[triangles[triIndex]];
|
||||
uv1 = uvs[triangles[triIndex + 1]];
|
||||
uv2 = uvs[triangles[triIndex + 2]];
|
||||
|
||||
uv0.y = 1f - uv0.y;
|
||||
uv1.y = 1f - uv1.y;
|
||||
uv2.y = 1f - uv2.y;
|
||||
|
||||
uv0 = (uv0 * dimension) + startPosition;
|
||||
uv1 = (uv1 * dimension) + startPosition;
|
||||
uv2 = (uv2 * dimension) + startPosition;
|
||||
|
||||
Handles.DrawLine( uv0, uv1 );
|
||||
Handles.DrawLine( uv1, uv2 );
|
||||
Handles.DrawLine( uv0, uv2 );
|
||||
}
|
||||
|
||||
Handles.EndGUI();
|
||||
}
|
||||
|
||||
private void DrawGrid(float gridSpacing, float gridOpacity, Color gridColor)
|
||||
{
|
||||
int widthDivs = Mathf.CeilToInt(position.width / gridSpacing);
|
||||
int heightDivs = Mathf.CeilToInt(position.height / gridSpacing);
|
||||
|
||||
Handles.BeginGUI();
|
||||
Handles.color = new Color(gridColor.r, gridColor.g, gridColor.b, gridOpacity);
|
||||
|
||||
offset += drag * 0.5f;
|
||||
Vector3 newOffset = new Vector3(offset.x % gridSpacing, offset.y % gridSpacing, 0);
|
||||
|
||||
for (int i = 0; i < widthDivs; i++)
|
||||
{
|
||||
Handles.DrawLine(new Vector3(gridSpacing * i, -gridSpacing, 0) + newOffset, new Vector3(gridSpacing * i, position.height, 0f) + newOffset);
|
||||
}
|
||||
|
||||
for (int j = 0; j < heightDivs; j++)
|
||||
{
|
||||
Handles.DrawLine(new Vector3(-gridSpacing, gridSpacing * j, 0) + newOffset, new Vector3(position.width, gridSpacing * j, 0f) + newOffset);
|
||||
}
|
||||
|
||||
Handles.color = Color.white;
|
||||
Handles.EndGUI();
|
||||
}
|
||||
|
||||
|
||||
private void GenerateOcclusionMap()
|
||||
{
|
||||
BitArray selectedTris = geometrySelector.selectedTriangles;
|
||||
Mesh mesh = geometrySelector.sharedMesh;
|
||||
Vector2[] uvs = mesh.uv;
|
||||
int[] triangles = mesh.triangles;
|
||||
|
||||
Vector2 uv0;
|
||||
Vector2 uv1;
|
||||
Vector2 uv2;
|
||||
Color currentColor = Color.white;
|
||||
|
||||
|
||||
for (int i = 0; i < straightWhiteLine.Length; i++)
|
||||
{
|
||||
straightWhiteLine[i] = Color.white;
|
||||
}
|
||||
|
||||
int length = (int)dimension * (int)dimension;
|
||||
Color32[] colors = new Color32[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
colors[i] = Color.black;
|
||||
}
|
||||
|
||||
if (occlusionMap != null)
|
||||
{
|
||||
DestroyImmediate(occlusionMap);
|
||||
}
|
||||
occlusionMap = new Texture2D((int)dimension, (int)dimension, TextureFormat.ARGB32, false);
|
||||
//Set the background to black
|
||||
|
||||
occlusionMap.SetPixels32(0, 0, (int)dimension - 1, (int)dimension - 1, colors);
|
||||
for (int i = 0; i < selectedTris.Length; i++)
|
||||
{
|
||||
if (selectedTris[i])
|
||||
{
|
||||
int triIndex = i * 3;
|
||||
|
||||
uv0 = uvs[triangles[triIndex]];
|
||||
uv1 = uvs[triangles[triIndex + 1]];
|
||||
uv2 = uvs[triangles[triIndex + 2]];
|
||||
|
||||
uv0.x = Mathf.RoundToInt(uv0.x * dimension);
|
||||
uv0.y = Mathf.RoundToInt(uv0.y * dimension);
|
||||
uv1.x = Mathf.RoundToInt(uv1.x * dimension);
|
||||
uv1.y = Mathf.RoundToInt(uv1.y * dimension);
|
||||
uv2.x = Mathf.RoundToInt(uv2.x * dimension);
|
||||
uv2.y = Mathf.RoundToInt(uv2.y * dimension);
|
||||
|
||||
|
||||
DrawTriangle(occlusionMap, uv0, uv1, uv2);
|
||||
}
|
||||
}
|
||||
occlusionMap.Apply();
|
||||
}
|
||||
|
||||
public static bool LineIntersection(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, ref Vector2 intersection)
|
||||
{
|
||||
float Ax, Bx, Cx, Ay, By, Cy, d, e, f, num/*,offset*/;
|
||||
float x1lo, x1hi, y1lo, y1hi;
|
||||
|
||||
Ax = p2.x - p1.x;
|
||||
Bx = p3.x - p4.x;
|
||||
|
||||
// X bound box test/
|
||||
|
||||
if (Ax < 0)
|
||||
{
|
||||
|
||||
x1lo = p2.x; x1hi = p1.x;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
x1hi = p2.x; x1lo = p1.x;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (Bx > 0)
|
||||
{
|
||||
|
||||
if (x1hi < p4.x || p3.x < x1lo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (x1hi < p3.x || p4.x < x1lo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Ay = p2.y - p1.y;
|
||||
By = p3.y - p4.y;
|
||||
|
||||
// Y bound box test//
|
||||
if (Ay < 0)
|
||||
{
|
||||
|
||||
y1lo = p2.y; y1hi = p1.y;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
y1hi = p2.y; y1lo = p1.y;
|
||||
}
|
||||
|
||||
if (By > 0)
|
||||
{
|
||||
if (y1hi < p4.y || p3.y < y1lo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (y1hi < p3.y || p4.y < y1lo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Cx = p1.x - p3.x;
|
||||
Cy = p1.y - p3.y;
|
||||
d = By * Cx - Bx * Cy; // alpha numerator//
|
||||
f = Ay * Bx - Ax * By; // both denominator//
|
||||
|
||||
// alpha tests//
|
||||
if (f > 0)
|
||||
{
|
||||
if (d < 0 || d > f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (d > 0 || d < f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
e = Ax * Cy - Ay * Cx; // beta numerator//
|
||||
|
||||
// beta tests //
|
||||
if (f > 0)
|
||||
{
|
||||
if (e < 0 || e > f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e > 0 || e < f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check if they are parallel
|
||||
if (f == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute intersection coordinates //
|
||||
|
||||
num = d * Ax; // numerator //
|
||||
intersection.x = p1.x + num / f;
|
||||
num = d * Ay;
|
||||
intersection.y = p1.y + num / f;
|
||||
return true;
|
||||
}
|
||||
private void DrawTriangle(Texture2D tex, Vector2 v1, Vector2 v2, Vector2 v3)
|
||||
{
|
||||
Vector2[] vertices = { v1, v2, v3 };
|
||||
|
||||
//Sort the vertices by y
|
||||
Array.Sort(vertices, (x, y) => x.y.CompareTo(y.y));
|
||||
|
||||
v1 = vertices[0];
|
||||
v2 = vertices[1];
|
||||
v3 = vertices[2];
|
||||
|
||||
// Draw a square around each vertex, to make sure we don't miss any pixels
|
||||
DrawSquare(tex, v1, Color.white, dimension /256);
|
||||
DrawSquare(tex, v2, Color.white, dimension / 256);
|
||||
DrawSquare(tex, v3, Color.white, dimension / 256);
|
||||
|
||||
/* here we know that v1.y <= v2.y <= v3.y */
|
||||
/* check for trivial case of bottom-flat triangle */
|
||||
if ((int)v2.y == (int)v3.y)
|
||||
{
|
||||
fillBottomFlatTriangle(tex, v1, v2, v3);
|
||||
}
|
||||
else if ((int)v1.y == (int)v2.y)
|
||||
{
|
||||
fillTopFlatTriangle(tex, v1, v2, v3);
|
||||
}
|
||||
else
|
||||
{
|
||||
// general case - split the triangle in a topflat and bottom-flat one
|
||||
|
||||
Vector2 temp1;
|
||||
Vector2 temp2;
|
||||
|
||||
Vector2 intersection = Vector2.zero;
|
||||
|
||||
temp1.x = 0;
|
||||
temp1.y = v2.y;
|
||||
temp2.x = dimension;
|
||||
temp2.y = v2.y;
|
||||
|
||||
float invslopev1v3 = (v3.x - v1.x) / (v3.y - v1.y);
|
||||
float xintersect = v1.x + invslopev1v3 * (v2.y - v1.y);
|
||||
|
||||
Vector2 v4 = new Vector2(xintersect, v2.y);
|
||||
fillBottomFlatTriangle(tex, v1, v2, v4);
|
||||
fillTopFlatTriangle(tex, v2, v4, v3);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSquare(Texture2D tex, Vector2 v3, Color white, float v)
|
||||
{
|
||||
int x = (int)(v3.x - v);
|
||||
int y = (int)(v3.y - v);
|
||||
int height = (int)v * 2;
|
||||
|
||||
for(int i=0; i<height; i++)
|
||||
{
|
||||
DrawStraightLine(tex, x, y + i, x+height);
|
||||
}
|
||||
}
|
||||
|
||||
private void fillBottomFlatTriangle(Texture2D tex, Vector2 v1, Vector2 v2, Vector2 v3)
|
||||
{
|
||||
float invslope1 = (v2.x - v1.x) / (v2.y - v1.y);
|
||||
float invslope2 = (v3.x - v1.x) / (v3.y - v1.y);
|
||||
|
||||
float curx1 = v1.x;
|
||||
float curx2 = v1.x;
|
||||
|
||||
for (int scanlineY = (int)v1.y; scanlineY <= (int)v2.y; scanlineY++)
|
||||
{
|
||||
DrawStraightLine(tex, (int)curx1, scanlineY, (int)curx2);
|
||||
curx1 += invslope1;
|
||||
curx2 += invslope2;
|
||||
}
|
||||
}
|
||||
|
||||
private void fillTopFlatTriangle(Texture2D tex, Vector2 v1, Vector2 v2, Vector2 v3)
|
||||
{
|
||||
float invslope1 = (v3.x - v1.x) / (v3.y - v1.y);
|
||||
float invslope2 = (v3.x - v2.x) / (v3.y - v2.y);
|
||||
|
||||
float curx1 = v3.x;
|
||||
float curx2 = v3.x;
|
||||
|
||||
for (int scanlineY = (int)v3.y; scanlineY > (int)v1.y; scanlineY--)
|
||||
{
|
||||
DrawStraightLine(tex, (int)curx1, scanlineY, (int)curx2);
|
||||
curx1 -= invslope1;
|
||||
curx2 -= invslope2;
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveTexture()
|
||||
{
|
||||
string filename = geometrySelector.meshAsset.name;
|
||||
if (occlusionMap != null)
|
||||
{
|
||||
filename += "_MHA_OcclusionMap.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
filename += "_MHA_UV.png";
|
||||
}
|
||||
|
||||
string path = EditorUtility.SaveFilePanelInProject("Save Texture(s)", filename, "png", "Base Filename to save PNG files to.");
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
bool freemap = false;
|
||||
Texture2D tex;
|
||||
|
||||
if (occlusionMapMode)
|
||||
{
|
||||
tex = occlusionMap;
|
||||
}
|
||||
else
|
||||
{
|
||||
freemap = true;
|
||||
tex = new Texture2D((int)dimension, (int)dimension, TextureFormat.ARGB32, false);
|
||||
BitArray selectedTris = geometrySelector.selectedTriangles;
|
||||
Mesh mesh = geometrySelector.sharedMesh;
|
||||
Vector2[] uvs = mesh.uv;
|
||||
int[] triangles = mesh.triangles;
|
||||
|
||||
Vector2 uv0;
|
||||
Vector2 uv1;
|
||||
Vector2 uv2;
|
||||
Color currentColor = Color.white;
|
||||
|
||||
int length = (int)dimension * (int)dimension;
|
||||
Color32[] colors = new Color32[length];
|
||||
for(int i = 0; i < length; i++ )
|
||||
{
|
||||
colors[i] = Color.black;
|
||||
}
|
||||
|
||||
//Set the background to black
|
||||
tex.SetPixels32(0, 0, (int)dimension-1, (int)dimension-1, colors );
|
||||
|
||||
for (int i = 0; i < selectedTris.Length; i++)
|
||||
{
|
||||
if (selectedTris[i])
|
||||
{
|
||||
currentColor = Color.red;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentColor = Color.white;
|
||||
}
|
||||
|
||||
int triIndex = i * 3;
|
||||
|
||||
uv0 = uvs[triangles[triIndex]];
|
||||
uv1 = uvs[triangles[triIndex + 1]];
|
||||
uv2 = uvs[triangles[triIndex + 2]];
|
||||
|
||||
uv0 = (uv0 * dimension);
|
||||
uv1 = (uv1 * dimension);
|
||||
uv2 = (uv2 * dimension);
|
||||
|
||||
//Handles.DrawLine(uv0, uv1);
|
||||
DrawLine(tex, (int)uv0.x, (int)uv0.y, (int)uv1.x, (int)uv1.y, currentColor);
|
||||
//Handles.DrawLine(uv1, uv2);
|
||||
DrawLine(tex, (int)uv1.x, (int)uv1.y, (int)uv2.x, (int)uv2.y, currentColor);
|
||||
//Handles.DrawLine(uv0, uv2);
|
||||
DrawLine(tex, (int)uv0.x, (int)uv0.y, (int)uv2.x, (int)uv2.y, currentColor);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] data = tex.EncodeToPNG();
|
||||
System.IO.File.WriteAllBytes(path, data);
|
||||
if (freemap)
|
||||
{
|
||||
DestroyImmediate(tex);
|
||||
}
|
||||
AssetDatabase.Refresh();
|
||||
TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
|
||||
textureImporter.textureType = TextureImporterType.Default;
|
||||
textureImporter.isReadable = true;
|
||||
textureImporter.mipmapEnabled = false;
|
||||
AssetDatabase.ImportAsset(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawStraightLine(Texture2D tex, int x0, int y0, int x1)
|
||||
{
|
||||
if (x0 > x1)
|
||||
{
|
||||
int tmp = x0;
|
||||
x0 = x1;
|
||||
x1 = tmp;
|
||||
}
|
||||
if (x1 >= dimension)
|
||||
{
|
||||
x1 = (int)dimension-1;
|
||||
}
|
||||
int width = (x1 - x0) + 1;
|
||||
|
||||
if (width < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (width > 0)
|
||||
{
|
||||
tex.SetPixels32(x0, y0, width, 1, straightWhiteLine);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From wiki at http://wiki.unity3d.com/index.php/TextureDrawLine
|
||||
/// </summary>
|
||||
/// <param name="tex"></param>
|
||||
/// <param name="x1"></param>
|
||||
/// <param name="y1"></param>
|
||||
/// <param name="x2"></param>
|
||||
/// <param name="y2"></param>
|
||||
/// <param name="col"></param>
|
||||
private void DrawLine(Texture2D tex, int x0, int y0, int x1, int y1, Color col)
|
||||
{
|
||||
int dy = (int)(y1 - y0);
|
||||
int dx = (int)(x1 - x0);
|
||||
int stepx, stepy;
|
||||
|
||||
if (dy < 0) { dy = -dy; stepy = -1; }
|
||||
else { stepy = 1; }
|
||||
if (dx < 0) { dx = -dx; stepx = -1; }
|
||||
else { stepx = 1; }
|
||||
dy <<= 1;
|
||||
dx <<= 1;
|
||||
|
||||
float fraction = 0;
|
||||
|
||||
tex.SetPixel(x0, y0, col);
|
||||
if (dx > dy)
|
||||
{
|
||||
fraction = dy - (dx >> 1);
|
||||
while (Mathf.Abs(x0 - x1) > 1)
|
||||
{
|
||||
if (fraction >= 0)
|
||||
{
|
||||
y0 += stepy;
|
||||
fraction -= dx;
|
||||
}
|
||||
x0 += stepx;
|
||||
fraction += dy;
|
||||
tex.SetPixel(x0, y0, col);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fraction = dx - (dy >> 1);
|
||||
while (Mathf.Abs(y0 - y1) > 1)
|
||||
{
|
||||
if (fraction >= 0)
|
||||
{
|
||||
x0 += stepx;
|
||||
fraction -= dy;
|
||||
}
|
||||
y0 += stepy;
|
||||
fraction += dx;
|
||||
tex.SetPixel(x0, y0, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fc933e39ebd5b14d9d493fbe96eebb6
|
||||
timeCreated: 1534354341
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/GeometryUVEditorWindow.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using UMA;
|
||||
|
||||
public interface IUMAAddressablePlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Menu is the Menu name for the generator. It will appear under the "Addressable Generators" menu item in the Asset Index window
|
||||
/// </summary>
|
||||
string Menu { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This begins the process.
|
||||
/// </summary>
|
||||
/// <returns>True - continue processing. False - abort.</returns>
|
||||
bool Prepare();
|
||||
|
||||
/// <summary>
|
||||
/// This is called once for every recipe. Every recipe is processed before
|
||||
/// any items are processed. If you process recipes instead of processing items,
|
||||
/// then you are responsible for adding the items to groups, labelling them, etc.
|
||||
/// For an example, The SingleGroupGenerator class processes recipes and manually adds items to groups.
|
||||
/// </summary>
|
||||
/// <param name="recipe"></param>
|
||||
void ProcessRecipe(UMAPackedRecipeBase recipe);
|
||||
|
||||
/// <summary>
|
||||
/// This is called once for every item in the index. It is called AFTER every recipe has been processed.
|
||||
/// If you process the items, and return a list of labels for that item, then the generator will label the item
|
||||
/// for you and add it to the shared group. Any overlay will also add it's textures to the shared group.
|
||||
/// </summary>
|
||||
/// <param name="ai"></param>
|
||||
/// <returns></returns>
|
||||
List<string> ProcessItem(AssetItem ai);
|
||||
|
||||
/// <summary>
|
||||
/// Finalize is called after every recipe and every item has been processed.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
void Complete();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b286c08ac7598494a921e0213b613382
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/IUMAAddressablePlugin.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UMA
|
||||
{
|
||||
public class ImportProcessor : AssetPostprocessor
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
string UMAVER = "UMA " + UmaAboutWindow.umaVersion;
|
||||
if (BuildPipeline.isBuildingPlayer || UnityEditorInternal.InternalEditorUtility.inBatchMode || Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EditorPrefs.GetString("UMA_VERSION", "0") != UmaAboutWindow.umaVersion)
|
||||
{
|
||||
EnsureTags();
|
||||
EnsureUMAIndicators();
|
||||
UMAAssetIndexer UAI = UMAAssetIndexer.Instance;
|
||||
if (UAI == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int chosen = EditorUtility.DisplayDialogComplex("UMA " + UmaAboutWindow.umaVersion, "New UMA version imported. The global index should be rebuilt or restored (if you made a backup). (If you don't know what this means, choose 'Rebuild Index')", "Rebuild Index", "Restore from backup", "Do nothing");
|
||||
|
||||
switch (chosen)
|
||||
{
|
||||
case 0:
|
||||
UAI.SaveKeeps();
|
||||
UAI.Clear();
|
||||
UAI.BuildStringTypes();
|
||||
UAI.AddEverything(false);
|
||||
UAI.RestoreKeeps();
|
||||
UAI.ForceSave();
|
||||
Resources.UnloadUnusedAssets();
|
||||
EditorUtility.DisplayDialog(UMAVER, "Index rebuild complete", "OK");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
string filename = EditorUtility.OpenFilePanel("Restore", "", "bak");
|
||||
if (!string.IsNullOrEmpty(filename))
|
||||
{
|
||||
try
|
||||
{
|
||||
string backup = System.IO.File.ReadAllText(filename);
|
||||
EditorUtility.DisplayProgressBar(UMAVER, "Restoring index", 0);
|
||||
if (!UAI.Restore(backup))
|
||||
{
|
||||
EditorUtility.DisplayDialog(UMAVER, "Error: Unable to restore index. Please review the console for more information.", "OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog(UMAVER, "Restore successful.", "OK");
|
||||
}
|
||||
backup = "";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
EditorUtility.DisplayDialog("Error", "Error reading backup: " + ex.Message, "OK");
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
EditorUtility.DisplayDialog("UMA " + UmaAboutWindow.umaVersion, "You can rebuild or restore the library from the Global Library window accessable from the UMA menu above.", "OK");
|
||||
break;
|
||||
}
|
||||
EditorPrefs.SetString("UMA_VERSION", UmaAboutWindow.umaVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureUMAIndicators()
|
||||
{
|
||||
var defineSymbols = new HashSet<string>(PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup).Split(';'));
|
||||
if (!defineSymbols.Contains("UMA_INSTALLED"))
|
||||
{
|
||||
defineSymbols.Add("UMA_INSTALLED");
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, string.Join(";", defineSymbols));
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureTags()
|
||||
{
|
||||
// Open tag manager
|
||||
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
|
||||
EnsureTag("UMAIgnore", tagManager);
|
||||
EnsureTag("UMAKeepChain", tagManager);
|
||||
}
|
||||
|
||||
private static void EnsureTag(string s, SerializedObject tagManager)
|
||||
{
|
||||
SerializedProperty tagsProp = tagManager.FindProperty("tags");
|
||||
|
||||
|
||||
// First check if it is not already present
|
||||
for (int i = 0; i < tagsProp.arraySize; i++)
|
||||
{
|
||||
SerializedProperty t = tagsProp.GetArrayElementAtIndex(i);
|
||||
if (t.stringValue.Equals(s))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tagsProp.InsertArrayElementAtIndex(0);
|
||||
SerializedProperty n = tagsProp.GetArrayElementAtIndex(0);
|
||||
n.stringValue = s;
|
||||
|
||||
// and to save the changes
|
||||
tagManager.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d3ea9f1b6c126f4296c6f63985d5a9e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/ImportProcessor.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,90 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Reflection;
|
||||
|
||||
public static class InspectorUtlity
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new inspector window instance and locks it to inspect the specified target
|
||||
/// </summary>
|
||||
/// <param name="target">The target object to inspect</param>
|
||||
/// <param name="revertProjectSelection">If true reverts the object selected in the project to the original selection. Otherwise selects the target object</param>
|
||||
public static EditorWindow InspectTarget(Object target, bool revertProjectSelection = false)
|
||||
{
|
||||
var prevSelection = Selection.activeObject;
|
||||
|
||||
// Get a reference to the `InspectorWindow` type object
|
||||
var inspectorType = typeof(Editor).Assembly.GetType("UnityEditor.InspectorWindow");
|
||||
// Create an InspectorWindow instance
|
||||
var inspectorInstance = ScriptableObject.CreateInstance(inspectorType) as EditorWindow;
|
||||
// Get a ref to the "locked" property, which will lock the state of the inspector to the current inspected target
|
||||
var isLocked = inspectorType.GetProperty("isLocked", BindingFlags.Instance | BindingFlags.Public);
|
||||
// Invoke `isLocked` setter method passing 'false' to UNlock the inspector
|
||||
isLocked.GetSetMethod().Invoke(inspectorInstance, new object[] { false });
|
||||
// We display it - currently, it will inspect whatever gameObject is currently selected
|
||||
// So we need to find a way to let it inspect/aim at our target GO that we passed
|
||||
// For that we do a simple trick:
|
||||
// 1- Cache the current selected gameObject
|
||||
// 2- Set the current selection to our target GO (so now all inspectors are targeting it)
|
||||
// 3- Lock our created inspector to that target
|
||||
// 4- Fallback to our previous selection
|
||||
inspectorInstance.Show();
|
||||
|
||||
// Set the selection to GO we want to inspect
|
||||
// Selection.activeGameObject = target;
|
||||
Selection.instanceIDs = new int[] { target.GetInstanceID() };
|
||||
|
||||
// Invoke `isLocked` setter method passing 'true' to lock the inspector
|
||||
isLocked.GetSetMethod().Invoke(inspectorInstance, new object[] { true });
|
||||
// Finally revert back to the previous selection so that other inspectors continue to inspect whatever they were inspecting...
|
||||
Selection.activeObject = prevSelection;
|
||||
if (revertProjectSelection)
|
||||
{
|
||||
EditorGUIUtility.PingObject(prevSelection);
|
||||
}
|
||||
|
||||
return inspectorInstance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array of editors for the specified inspectorWindow.
|
||||
/// CAUTION: This will now return the correct array straight after InspectTarget is called.
|
||||
/// You need to wait for the inspector windows to repaint, and/or keep checking this array until it contains the expected editor for the expected target
|
||||
/// </summary>
|
||||
/// <param name="inspectorWindow"></param>
|
||||
/// <returns></returns>
|
||||
public static Editor[] GetInspectorsEditors(EditorWindow inspectorWindow)
|
||||
{
|
||||
Editor[] editors = new Editor[0];
|
||||
var inspectorType = typeof(Editor).Assembly.GetType("UnityEditor.InspectorWindow");
|
||||
if (inspectorWindow.GetType() != inspectorType)
|
||||
{
|
||||
Debug.LogWarning("The supplied window was not an InspectorWindow");
|
||||
return null;
|
||||
}
|
||||
var activeEditorTrackerPInfo = inspectorType.GetProperty("tracker", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
//Unity 2018.3 changed the get method to private so pass true along with the request for it
|
||||
var activeEditorTracker = activeEditorTrackerPInfo.GetGetMethod(true).Invoke(inspectorWindow, new object[0]);
|
||||
if (((ActiveEditorTracker)activeEditorTracker) != null)
|
||||
{
|
||||
editors = ((ActiveEditorTracker)activeEditorTracker).activeEditors;
|
||||
}
|
||||
return editors;
|
||||
}
|
||||
|
||||
private static System.Reflection.MethodInfo m_RepaintInspectors = null;
|
||||
|
||||
/// <summary>
|
||||
/// Repaints all Inspector Windows. I some circumstances popup windows dont repaint immediately. Calling this forces them to do so
|
||||
/// </summary>
|
||||
public static void RepaintAllInspectors()
|
||||
{
|
||||
if (m_RepaintInspectors == null)
|
||||
{
|
||||
var inspWin = typeof(Editor).Assembly.GetType("UnityEditor.InspectorWindow");
|
||||
m_RepaintInspectors = inspWin.GetMethod("RepaintAllInspectors", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
|
||||
}
|
||||
m_RepaintInspectors.Invoke(null, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98cfa8b421aac0a4f8a65957b074e07b
|
||||
timeCreated: 1518147781
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/InspectorUtlity.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,114 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
public class MassChangeImporterSettings : EditorWindow
|
||||
{
|
||||
int MaxSize = 1024;
|
||||
int selectedFormat = 0;
|
||||
|
||||
string[] ImportFormats = System.Enum.GetNames(typeof(TextureImporterFormat));
|
||||
|
||||
bool updateSize;
|
||||
bool updateFormats;
|
||||
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
GUILayout.Label("UMA Texture Format Updater");
|
||||
GUILayout.Space(10);
|
||||
|
||||
EditorGUILayout.LabelField("Select textures in project view");
|
||||
updateSize = EditorGUILayout.Toggle("Update Size", updateSize);
|
||||
if (updateSize)
|
||||
{
|
||||
MaxSize = EditorGUILayout.IntField("Enter Max Size", MaxSize);
|
||||
}
|
||||
updateFormats = EditorGUILayout.Toggle("Update Format", updateFormats);
|
||||
if (updateFormats)
|
||||
{
|
||||
selectedFormat = EditorGUILayout.Popup("Select Format", selectedFormat, ImportFormats);
|
||||
}
|
||||
|
||||
if (updateFormats | updateSize)
|
||||
{
|
||||
if (GUILayout.Button("Update all selected textures "))
|
||||
{
|
||||
int numChanges = 0;
|
||||
TextureImporterFormat destFmt;
|
||||
if (Enum.TryParse<TextureImporterFormat>(ImportFormats[selectedFormat], false, out destFmt))
|
||||
{
|
||||
var textures = Selection.GetFiltered(typeof(Texture2D), SelectionMode.Assets);
|
||||
int processedTextures = 1;
|
||||
int totalTextures = textures.Length;
|
||||
|
||||
foreach (var o in textures)
|
||||
{
|
||||
if (EditorUtility.DisplayCancelableProgressBar("Processing textures", $"{processedTextures} of {totalTextures}", processedTextures / (float)totalTextures))
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
break;
|
||||
}
|
||||
string path = AssetDatabase.GetAssetPath(o);
|
||||
TextureImporter importer = (TextureImporter)TextureImporter.GetAtPath(path);
|
||||
if (importer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var def = importer.GetDefaultPlatformTextureSettings();
|
||||
var changed = false;
|
||||
|
||||
|
||||
Action<TextureImporterPlatformSettings> maybeChange = (platSettings) =>
|
||||
{
|
||||
if (updateSize && platSettings.maxTextureSize != MaxSize)
|
||||
{
|
||||
platSettings.maxTextureSize = MaxSize;
|
||||
changed = true;
|
||||
}
|
||||
if (updateFormats && platSettings.format != destFmt)
|
||||
{
|
||||
platSettings.format = destFmt;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed == true)
|
||||
{
|
||||
platSettings.overridden = true;
|
||||
importer.SetPlatformTextureSettings(platSettings);
|
||||
}
|
||||
};
|
||||
|
||||
maybeChange(importer.GetPlatformTextureSettings("iPhone"));
|
||||
// Uncomment if you use Android
|
||||
//maybeChange(importer.GetPlatformTextureSettings("Android"));
|
||||
|
||||
if (changed)
|
||||
{
|
||||
importer.SaveAndReimport();
|
||||
++numChanges;
|
||||
}
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("Nothing to update!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MenuItem("UMA/Texture Format Updater")]
|
||||
public static void OpenUmaTexturePrepareWindow()
|
||||
{
|
||||
MassChangeImporterSettings window = (MassChangeImporterSettings)EditorWindow.GetWindow(typeof(MassChangeImporterSettings));
|
||||
|
||||
window.titleContent.text = "Mass Set Importer Settings";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: caa05268b5612664bbb5b95447ec67bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/MassChangeImporterSettings.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,561 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEditor.SceneManagement;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using static UMA.UMAUtils;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
//OnPreviewGUI
|
||||
//http://timaksu.com/post/126337219047/spruce-up-your-custom-unity-inspectors-with-a
|
||||
//
|
||||
[CustomEditor(typeof(MeshHideAsset))]
|
||||
public class MeshHideInspector : Editor
|
||||
{
|
||||
private Mesh _meshPreview;
|
||||
private UMAMeshData _meshData;
|
||||
private PreviewRenderUtility _previewRenderUtility;
|
||||
private Vector2 _drag;
|
||||
private Material _material;
|
||||
private bool _autoInitialize = true;
|
||||
|
||||
private int selectedRaceIndex = 0;
|
||||
private List<RaceData> foundRaces = new List<RaceData>();
|
||||
private List<string> foundRaceNames = new List<string>();
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
MeshHideAsset source = target as MeshHideAsset;
|
||||
|
||||
SetRaceLists();
|
||||
|
||||
if (source.asset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( _material == null )
|
||||
{
|
||||
_material = AssetDatabase.GetBuiltinExtraResource<Material>("Default-Diffuse.mat");
|
||||
}
|
||||
|
||||
if (_meshPreview == null)
|
||||
{
|
||||
UpdateMeshPreview();
|
||||
}
|
||||
|
||||
if (_previewRenderUtility == null)
|
||||
{
|
||||
_previewRenderUtility = new PreviewRenderUtility();
|
||||
ResetPreviewCamera();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
MeshHideAsset source = target as MeshHideAsset;
|
||||
bool beginSceneEditing = false;
|
||||
|
||||
//DrawDefaultInspector();
|
||||
SlotDataAsset obj = EditorGUILayout.ObjectField("SlotDataAsset", source.asset, typeof(SlotDataAsset), false) as SlotDataAsset;
|
||||
if (obj != source.asset)
|
||||
{
|
||||
source.asset = obj as SlotDataAsset;
|
||||
if (_autoInitialize)
|
||||
{
|
||||
UpdateSourceAsset(obj);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.LabelField("Slot Name", source.AssetSlotName.ToString());
|
||||
if (source.HasReference)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Warning: This Mesh Hide Asset contains a reference. It should be freed so the referenced asset is not included in the build.", MessageType.Warning);
|
||||
if (GUILayout.Button("Free Reference"))
|
||||
{
|
||||
source.FreeReference();
|
||||
EditorUtility.SetDirty(source);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
|
||||
_autoInitialize = EditorGUILayout.Toggle(new GUIContent("AutoInitialize (recommended)", "Checking this will auto initialize the MeshHideAsset when a slot is added (recommended). " +
|
||||
"For users that are rebuilding slots that don't change the geometry, the slot reference will be lost but can be reset without losing the existing MeshHide information by unchecking this." ),_autoInitialize);
|
||||
|
||||
if (source.asset == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No SlotDataAsset set! Begin by adding a SlotDataAsset to the object field above.", MessageType.Error);
|
||||
}
|
||||
|
||||
|
||||
//Race Selector here
|
||||
GUILayout.Space(20);
|
||||
selectedRaceIndex = EditorGUILayout.Popup("Select Base Slot by Race", selectedRaceIndex, foundRaceNames.ToArray());
|
||||
if( selectedRaceIndex <= 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Quick selection of base slots by race. This is not needed to create a mesh hide asset, any slot can be used.", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
UMAData.UMARecipe baseRecipe = new UMAData.UMARecipe();
|
||||
foundRaces[selectedRaceIndex].baseRaceRecipe.Load(baseRecipe, UMAContextBase.Instance);
|
||||
|
||||
foreach(SlotData sd in baseRecipe.slotDataList)
|
||||
{
|
||||
if (sd != null && sd.asset != null)
|
||||
{
|
||||
if( GUILayout.Button(string.Format("{0} ({1})", sd.asset.name, sd.slotName)))
|
||||
{
|
||||
if( UpdateSourceAsset(sd.asset))
|
||||
{
|
||||
selectedRaceIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.Space(20);
|
||||
if (source.TriangleCount > 0)
|
||||
{
|
||||
EditorGUILayout.LabelField("Triangle Indices Count: " + source.TriangleCount);
|
||||
EditorGUILayout.LabelField("Submesh Count: " + source.SubmeshCount);
|
||||
if (source.asset != null)
|
||||
{
|
||||
EditorGUILayout.LabelField("Current Submesh: " + source.asset.subMeshIndex);
|
||||
}
|
||||
EditorGUILayout.LabelField("Hidden Triangle Count: " + source.HiddenCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("No triangle array found");
|
||||
}
|
||||
|
||||
GUILayout.Space(20);
|
||||
if (!GeometrySelectorWindow.IsOpen)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(source.asset == null);
|
||||
if (GUILayout.Button("Begin Editing", GUILayout.MinHeight(50)))
|
||||
{
|
||||
if (source.asset != null)
|
||||
{
|
||||
beginSceneEditing = true;
|
||||
}
|
||||
}
|
||||
EditorGUI.EndDisabledGroup();
|
||||
GUILayout.Space(20);
|
||||
GUILayout.Label("Editing will be done in an empty scene.");
|
||||
GUILayout.Label("You will be prompted to save the scene");
|
||||
GUILayout.Label("if there are any unsaved changes.");
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (beginSceneEditing)
|
||||
{
|
||||
// This has to happen outside the inspector
|
||||
EditorApplication.delayCall += CreateSceneEditObject;
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpdateSourceAsset( SlotDataAsset newObj )
|
||||
{
|
||||
MeshHideAsset source = target as MeshHideAsset;
|
||||
bool update = false;
|
||||
|
||||
if (source.asset != null)
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Warning", "Setting a new source slot will clear the existing data on this asset!", "OK", "Cancel"))
|
||||
{
|
||||
update = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
update = true;
|
||||
}
|
||||
|
||||
if(update)
|
||||
{
|
||||
source.AssetSlotName = newObj.slotName;
|
||||
source.Initialize();
|
||||
UpdateMeshPreview();
|
||||
EditorUtility.SetDirty(target);
|
||||
AssetDatabase.SaveAssets();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UpdateMeshPreview()
|
||||
{
|
||||
MeshHideAsset source = target as MeshHideAsset;
|
||||
if (source.asset == null)
|
||||
{
|
||||
_meshPreview = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_meshPreview == null)
|
||||
{
|
||||
_meshPreview = new Mesh();
|
||||
#if UMA_32BITBUFFERS
|
||||
_meshPreview.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
#endif
|
||||
}
|
||||
|
||||
UpdateMeshData( source.triangleFlags);
|
||||
|
||||
_meshPreview.Clear();
|
||||
_meshPreview.vertices = _meshData.vertices;
|
||||
_meshPreview.subMeshCount = _meshData.subMeshCount;
|
||||
|
||||
for (int i = 0; i < _meshData.subMeshCount; i++)
|
||||
{
|
||||
var tris = _meshData.submeshes[i].getBaseTriangles();
|
||||
_meshPreview.SetIndices(tris, MeshTopology.Triangles, i);
|
||||
}
|
||||
|
||||
ResetPreviewCamera();
|
||||
}
|
||||
|
||||
public void UpdateMeshData( BitArray[] triangleFlags )
|
||||
{
|
||||
if (triangleFlags == null)
|
||||
{
|
||||
Debug.LogWarning("UpdateMeshData: triangleFlags are null!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_meshData == null )
|
||||
{
|
||||
_meshData = new UMAMeshData();
|
||||
}
|
||||
|
||||
MeshHideAsset source = target as MeshHideAsset;
|
||||
|
||||
UMAMeshData sourceData = source.asset.meshData;
|
||||
|
||||
_meshData.submeshes = new SubMeshTriangles[sourceData.subMeshCount];
|
||||
_meshData.subMeshCount = sourceData.subMeshCount;
|
||||
|
||||
bool has_normals = (sourceData.normals != null && sourceData.normals.Length != 0);
|
||||
|
||||
_meshData.vertices = new Vector3[sourceData.vertexCount];
|
||||
sourceData.vertices.CopyTo(_meshData.vertices, 0);
|
||||
|
||||
if(has_normals)
|
||||
{
|
||||
_meshData.normals = new Vector3[sourceData.vertexCount];
|
||||
sourceData.normals.CopyTo(_meshData.normals, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < sourceData.subMeshCount; i++)
|
||||
{
|
||||
NativeArray<int> subMeshTriangles = sourceData.submeshes[i].GetTriangles();
|
||||
|
||||
List<int> newTriangles = new List<int>();
|
||||
for (int j = 0; j < triangleFlags[i].Count; j++)
|
||||
{
|
||||
if (!triangleFlags[i][j])
|
||||
{
|
||||
newTriangles.Add(subMeshTriangles[(j*3) + 0]);
|
||||
newTriangles.Add(subMeshTriangles[(j*3) + 1]);
|
||||
newTriangles.Add(subMeshTriangles[(j*3) + 2]);
|
||||
}
|
||||
}
|
||||
_meshData.submeshes[i] = new SubMeshTriangles();
|
||||
_meshData.submeshes[i].SetTriangles(new int[newTriangles.Count]);
|
||||
// .triangles = new int[newTriangles.Count];
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateSceneEditObject()
|
||||
{
|
||||
bool focusObject = true;
|
||||
MeshHideAsset source = target as MeshHideAsset;
|
||||
if (source.asset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GeometrySelectorWindow.IsOpen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GeometrySelectorExists())
|
||||
{
|
||||
GameObject.DestroyImmediate(GameObject.Find("GeometrySelector").gameObject);
|
||||
}
|
||||
|
||||
bool hasDirtyScenes = false;
|
||||
|
||||
for (int i = 0; i < EditorSceneManager.sceneCount; i++)
|
||||
{
|
||||
Scene sc = EditorSceneManager.GetSceneAt(i);
|
||||
if (sc.isDirty)
|
||||
{
|
||||
hasDirtyScenes = true;
|
||||
}
|
||||
}
|
||||
if (hasDirtyScenes)
|
||||
{
|
||||
int saveChoice = EditorUtility.DisplayDialogComplex("Modified scenes detected", "Opening the Mesh Hide Editor will close all scenes and create a new blank scene. Any current scene changes will be lost unless saved.", "Save and Continue", "Continue without saving", "Cancel");
|
||||
|
||||
switch (saveChoice)
|
||||
{
|
||||
case 0: // Save and continue
|
||||
{
|
||||
if (!EditorSceneManager.SaveOpenScenes())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 1: // don't save and continue
|
||||
break;
|
||||
case 2: // cancel
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SceneView sceneView = SceneView.lastActiveSceneView;
|
||||
|
||||
if (sceneView == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "A Scene View must be open and active", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
SceneView.lastActiveSceneView.Focus();
|
||||
|
||||
List<GeometrySelector.SceneInfo> currentscenes = new List<GeometrySelector.SceneInfo>();
|
||||
|
||||
for (int i = 0; i < EditorSceneManager.sceneCount; i++)
|
||||
{
|
||||
Scene sc = EditorSceneManager.GetSceneAt(i);
|
||||
GeometrySelector.SceneInfo si = new GeometrySelector.SceneInfo();
|
||||
si.path = sc.path;
|
||||
si.name = sc.name;
|
||||
if (i == 0)
|
||||
{
|
||||
// first scene should clear the temp scene.
|
||||
si.mode = OpenSceneMode.Single;
|
||||
}
|
||||
else
|
||||
{
|
||||
si.mode = sc.isLoaded ? OpenSceneMode.Additive : OpenSceneMode.AdditiveWithoutLoading;
|
||||
}
|
||||
currentscenes.Add(si);
|
||||
}
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
Scene s = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
#else
|
||||
Scene s = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
|
||||
#endif
|
||||
EditorSceneManager.SetActiveScene(s);
|
||||
GameObject obj = EditorUtility.CreateGameObjectWithHideFlags("GeometrySelector", HideFlags.DontSaveInEditor);
|
||||
GeometrySelector geometry = obj.AddComponent<GeometrySelector>();
|
||||
|
||||
// Restore the camera position.
|
||||
string CamKey = source.name + "_MHA_Cam";
|
||||
if (EditorPrefs.HasKey(CamKey))
|
||||
{
|
||||
string xform = EditorPrefs.GetString(CamKey);
|
||||
CamSaver cs = CamSaver.FromString(xform);
|
||||
sceneView.camera.transform.position = cs.position;
|
||||
sceneView.camera.transform.localRotation = cs.rotation;
|
||||
focusObject = false;
|
||||
}
|
||||
|
||||
if (geometry != null)
|
||||
{
|
||||
Selection.activeGameObject = obj;
|
||||
//InspectorUtlity.InspectTarget(obj);
|
||||
if (focusObject)
|
||||
{
|
||||
SceneView.lastActiveSceneView.FrameSelected(true);
|
||||
}
|
||||
|
||||
// sceneView.camera.transform;
|
||||
geometry.meshAsset = source;
|
||||
geometry.restoreScenes = currentscenes;
|
||||
geometry.currentSceneView = sceneView;
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
geometry.SceneviewLightingState = sceneView.sceneLighting;
|
||||
sceneView.sceneLighting = false;
|
||||
#else
|
||||
geometry.SceneviewLightingState = sceneView.m_SceneLighting;
|
||||
sceneView.m_SceneLighting = false;
|
||||
#endif
|
||||
geometry.InitializeFromMeshData(source.asset.meshData);
|
||||
|
||||
|
||||
geometry.selectedTriangles = new BitArray(source.triangleFlags[source.asset.subMeshIndex]);
|
||||
|
||||
geometry.UpdateSelectionMesh();
|
||||
if (focusObject)
|
||||
{
|
||||
SceneView.FrameLastActiveSceneView();
|
||||
SceneView.lastActiveSceneView.FrameSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool GeometrySelectorExists()
|
||||
{
|
||||
if (GameObject.Find("GeometrySelector") != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool HasPreviewGUI()
|
||||
{
|
||||
MeshHideAsset source = target as MeshHideAsset;
|
||||
if (source.asset == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnPreviewGUI(Rect r, GUIStyle background)
|
||||
{
|
||||
if (_meshPreview == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_material == null)
|
||||
{
|
||||
_material = GetDefaultDiffuseMaterial();
|
||||
}
|
||||
|
||||
_drag = Drag2D(_drag, r);
|
||||
|
||||
if (_previewRenderUtility == null)
|
||||
{
|
||||
_previewRenderUtility = new PreviewRenderUtility();
|
||||
ResetPreviewCamera();
|
||||
}
|
||||
|
||||
if( Event.current.type == EventType.Repaint )
|
||||
{
|
||||
_previewRenderUtility.BeginPreview(r, background);
|
||||
_previewRenderUtility.DrawMesh(_meshPreview, Vector3.zero, Quaternion.identity, _material, 0);
|
||||
|
||||
_previewRenderUtility.camera.transform.position = Vector2.zero;
|
||||
_previewRenderUtility.camera.transform.rotation = Quaternion.Euler(new Vector3(-_drag.y, -_drag.x, 0));
|
||||
_previewRenderUtility.camera.transform.position = _previewRenderUtility.camera.transform.forward * -6f;
|
||||
_previewRenderUtility.camera.transform.position += _meshPreview.bounds.center;
|
||||
|
||||
_previewRenderUtility.camera.Render();
|
||||
|
||||
Texture resultRender = _previewRenderUtility.EndPreview();
|
||||
|
||||
GUI.DrawTexture(r, resultRender, ScaleMode.StretchToFill, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetPreviewCamera()
|
||||
{
|
||||
if (_previewRenderUtility == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MeshHideAsset source = target as MeshHideAsset;
|
||||
|
||||
_drag = Vector2.zero;
|
||||
if( source.asset.meshData.rootBoneHash == UMAUtils.StringToHash("Global"))
|
||||
{
|
||||
_drag.y = -90;
|
||||
}
|
||||
|
||||
_previewRenderUtility.camera.transform.position = new Vector3(0, 0, -6);
|
||||
_previewRenderUtility.camera.transform.rotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
public override void OnPreviewSettings()
|
||||
{
|
||||
if (GUILayout.Button("Refresh", EditorStyles.whiteMiniLabel))
|
||||
{
|
||||
UpdateMeshPreview();
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if( _previewRenderUtility != null )
|
||||
{
|
||||
_previewRenderUtility.Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector2 Drag2D(Vector2 scrollPosition, Rect position)
|
||||
{
|
||||
int controlID = GUIUtility.GetControlID("Slider".GetHashCode(), FocusType.Passive);
|
||||
Event current = Event.current;
|
||||
switch (current.GetTypeForControl(controlID))
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
if (position.Contains(current.mousePosition) && position.width > 50f)
|
||||
{
|
||||
GUIUtility.hotControl = controlID;
|
||||
current.Use();
|
||||
EditorGUIUtility.SetWantsMouseJumping(1);
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == controlID)
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
}
|
||||
EditorGUIUtility.SetWantsMouseJumping(0);
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == controlID)
|
||||
{
|
||||
scrollPosition -= current.delta * (float)((!current.shift) ? 1 : 3) / Mathf.Min(position.width, position.height) * 140f;
|
||||
scrollPosition.y = Mathf.Clamp(scrollPosition.y, -90f, 90f);
|
||||
current.Use();
|
||||
GUI.changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return scrollPosition;
|
||||
}
|
||||
|
||||
public void SetRaceLists()
|
||||
{
|
||||
UMAContextBase ubc = UMAContext.Instance;
|
||||
if (ubc != null)
|
||||
{
|
||||
RaceData[] raceDataArray = ubc.GetAllRaces();
|
||||
foundRaces.Clear();
|
||||
foundRaceNames.Clear();
|
||||
foundRaces.Add(null);
|
||||
foundRaceNames.Add("None Set");
|
||||
foreach (RaceData race in raceDataArray)
|
||||
{
|
||||
if (race != null && race.raceName != "RaceDataPlaceholder")
|
||||
{
|
||||
foundRaces.Add(race);
|
||||
foundRaceNames.Add(race.raceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3b2ec35b48414448a4090528987bd80
|
||||
timeCreated: 1497131403
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/MeshHideInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,206 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UMA.CharacterSystem;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(OverlayColorData),true)]
|
||||
public class OverlayColorDataPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
GUIContent Modulate = new GUIContent("Multiplier");
|
||||
GUIContent Additive = new GUIContent("Additive");
|
||||
GUIContent Channels = new GUIContent("Channel Count");
|
||||
|
||||
|
||||
public static object GetDeepPropertyValue(object src, string propName)
|
||||
{
|
||||
if (propName.Contains('.'))
|
||||
{
|
||||
string[] Split = propName.Split('.');
|
||||
string RemainingProperty = propName.Substring(propName.IndexOf('.') + 1);
|
||||
return GetDeepPropertyValue(src.GetType().GetProperty(Split[0]).GetValue(src, null), RemainingProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
return src.GetType().GetProperty(propName).GetValue(src, null);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
OverlayColorData ocd = null;
|
||||
DynamicCharacterAvatar dca = property.serializedObject.targetObject as DynamicCharacterAvatar;
|
||||
|
||||
|
||||
ocd = property.GetValue<OverlayColorData>();
|
||||
if (ocd == null && dca != null)
|
||||
{
|
||||
string Name = property.FindPropertyRelative("name").stringValue;
|
||||
foreach( OverlayColorData o in dca.characterColors._colors)
|
||||
{
|
||||
if (o.name == Name)
|
||||
{
|
||||
ocd = o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
var name = property.FindPropertyRelative("name");
|
||||
var mask = property.FindPropertyRelative("channelMask");
|
||||
var additive = property.FindPropertyRelative("channelAdditiveMask");
|
||||
var propblock = property.FindPropertyRelative("propertyBlock");
|
||||
var displayColor = property.FindPropertyRelative("displayColor");
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
name.isExpanded = EditorGUILayout.Foldout(name.isExpanded, label);
|
||||
if (!name.isExpanded)
|
||||
{
|
||||
//name.stringValue = EditorGUILayout.TextField(new GUIContent(""), name.stringValue);
|
||||
if (mask.arraySize > 0)
|
||||
{
|
||||
SerializedProperty colProp = mask.GetArrayElementAtIndex(0);
|
||||
//Color c = colProp.colorValue;
|
||||
//EditorGUILayout.ColorField(c, GUILayout.Width(120));
|
||||
EditorGUILayout.PropertyField(colProp);
|
||||
//if (colProp.colorValue != c)
|
||||
//{
|
||||
// colProp.colorValue = c;
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.ColorField(Color.white, GUILayout.Width(120));
|
||||
}
|
||||
bool delete = GUILayout.Button("X", GUILayout.Width(20));
|
||||
if (delete)
|
||||
{
|
||||
property.FindPropertyRelative("deleteThis").boolValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (name.isExpanded)
|
||||
{
|
||||
EditorGUILayout.PropertyField(property.FindPropertyRelative("name"));
|
||||
EditorGUILayout.PropertyField(property.FindPropertyRelative("isBaseColor"));
|
||||
EditorGUILayout.PropertyField(displayColor);
|
||||
|
||||
if (ocd != null)
|
||||
{
|
||||
string Name = property.FindPropertyRelative("name").stringValue;
|
||||
int ChannelCount = EditorGUILayout.IntSlider(Channels, ocd.channelCount, 0, 16);
|
||||
if (ChannelCount != ocd.channelCount)
|
||||
{
|
||||
ocd.SetChannels(ChannelCount);
|
||||
if (dca != null)
|
||||
{
|
||||
EditorUtility.SetDirty(dca);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SerializedProperty showAdvancedProperty = property.FindPropertyRelative("showAdvanced");
|
||||
EditorGUILayout.PropertyField(showAdvancedProperty);
|
||||
//showAdvanced = EditorGUILayout.Toggle("Show Extended Ranges", showAdvanced);
|
||||
|
||||
GUILayout.Space(5);
|
||||
|
||||
|
||||
for (int i = 0; i < mask.arraySize; i++)
|
||||
{
|
||||
if (showAdvancedProperty.boolValue)
|
||||
{
|
||||
var channelMask = mask.GetArrayElementAtIndex(i);
|
||||
var channelColor = ToVector4(channelMask.colorValue);
|
||||
var newchannelColor = EditorGUILayout.Vector4Field("Multiplier (" + i + ")", channelColor);
|
||||
if (channelColor != newchannelColor)
|
||||
{
|
||||
channelMask.colorValue = ToColor(newchannelColor);
|
||||
}
|
||||
|
||||
var AdditiveMask = additive.GetArrayElementAtIndex(i);
|
||||
var AdditiveColor = ToVector4(AdditiveMask.colorValue);
|
||||
var newAdditiveColor = EditorGUILayout.Vector4Field("Additive (" + i + ")", AdditiveColor);
|
||||
if (newAdditiveColor != AdditiveColor)
|
||||
{
|
||||
AdditiveMask.colorValue = ToColor(newAdditiveColor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Modulate.text = "Multiplier (" + i + ")";
|
||||
EditorGUILayout.PropertyField(mask.GetArrayElementAtIndex(i), Modulate);
|
||||
Additive.text = "Additive (" + i + ")";
|
||||
EditorGUILayout.PropertyField(additive.GetArrayElementAtIndex(i), Additive);
|
||||
}
|
||||
GUILayout.Space(5);
|
||||
}
|
||||
if (ocd != null)
|
||||
{
|
||||
if (ocd.PropertyBlock != null)
|
||||
{
|
||||
if (UMAMaterialPropertyBlockDrawer.OnGUI(ocd.PropertyBlock))
|
||||
{
|
||||
if (dca != null)
|
||||
{
|
||||
EditorUtility.SetDirty(dca);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GUILayout.Button("Add Properties Block"))
|
||||
{
|
||||
ocd.PropertyBlock = new UMAMaterialPropertyBlock();
|
||||
EditorUtility.SetDirty(dca);
|
||||
AssetDatabase.SaveAssets();
|
||||
property.serializedObject.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if (mask.arraySize > 0)
|
||||
// {
|
||||
// EditorGUILayout.PropertyField(mask.GetArrayElementAtIndex(0), new GUIContent("BaseColor"));
|
||||
// }
|
||||
// EditorGUILayout.PropertyField(displayColor);
|
||||
|
||||
// if (ocd.HasProperties)
|
||||
// {
|
||||
// EditorGUILayout.LabelField("Has Properties");
|
||||
// }
|
||||
}
|
||||
//EditorGUILayout.Space();
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
return -2f;
|
||||
}
|
||||
|
||||
|
||||
private Color ToColor(Vector4 colorVector)
|
||||
{
|
||||
return new Color(colorVector.x, colorVector.y, colorVector.z, colorVector.w);
|
||||
}
|
||||
|
||||
private Vector4 ToVector4(Color color)
|
||||
{
|
||||
return new Vector4(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
}
|
||||
public class PropertyDrawerUtility
|
||||
{
|
||||
public static OverlayColorData GetOverlayDataAsset(System.Reflection.FieldInfo fieldInfo, SerializedProperty property)
|
||||
{
|
||||
DynamicCharacterAvatar dca = property.serializedObject.targetObject as DynamicCharacterAvatar;
|
||||
return new OverlayColorData();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a771d36b4dbcbe943ac7ff53c0e0c80f
|
||||
timeCreated: 1427499314
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/OverlayColorDataPropertyDrawer.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,188 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(OverlayDataAsset))]
|
||||
[CanEditMultipleObjects]
|
||||
public class OverlayDataAssetInspector : Editor
|
||||
{
|
||||
//DelayedFields ony trigger GUI.changed when the user selects another field. This means if the user changes a value but never changes the selected field it does not ever save.
|
||||
//Instead add a short delay on saving so that the asset doesn't save while the user is typing in a field
|
||||
private SerializedProperty _overlayName;
|
||||
private SerializedProperty _overlayType;
|
||||
private SerializedProperty _umaMaterial;
|
||||
private SerializedProperty _textureList;
|
||||
private SerializedProperty _blendList;
|
||||
private SerializedProperty _channels;
|
||||
private SerializedProperty _rect;
|
||||
private SerializedProperty _alphaMask;
|
||||
private SerializedProperty _tags;
|
||||
private SerializedProperty _occlusionEntries;
|
||||
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
_overlayName = serializedObject.FindProperty("overlayName");
|
||||
_overlayType = serializedObject.FindProperty("overlayType");
|
||||
_umaMaterial = serializedObject.FindProperty("material");
|
||||
_textureList = serializedObject.FindProperty("textureList");
|
||||
_blendList = serializedObject.FindProperty("overlayBlend");
|
||||
_rect = serializedObject.FindProperty("rect");
|
||||
_alphaMask = serializedObject.FindProperty("alphaMask");
|
||||
_tags = serializedObject.FindProperty("tags");
|
||||
_occlusionEntries = serializedObject.FindProperty("OcclusionEntries");
|
||||
(target as OverlayDataAsset).tagsList = GUIHelper.InitTagsList("tags",serializedObject);
|
||||
|
||||
EditorApplication.update += DoDelayedSave;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
EditorApplication.update -= DoDelayedSave;
|
||||
}
|
||||
|
||||
void DoDelayedSave()
|
||||
{
|
||||
OverlayDataAsset od = target as OverlayDataAsset;
|
||||
|
||||
if (od.doSave && Time.realtimeSinceStartup > (od.lastActionTime + 0.5f))
|
||||
{
|
||||
od.doSave = false;
|
||||
od.lastActionTime = Time.realtimeSinceStartup;
|
||||
EditorUtility.SetDirty(target);
|
||||
//AssetDatabase.SaveAssets();
|
||||
UMAUpdateProcessor.UpdateOverlay(target as OverlayDataAsset);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
OverlayDataAsset od = target as OverlayDataAsset;
|
||||
if (od.lastActionTime == 0)
|
||||
{
|
||||
od.lastActionTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
od.ValidateBlendList();
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.PropertyField(_overlayName);
|
||||
EditorGUILayout.PropertyField(_overlayType);
|
||||
EditorGUILayout.PropertyField(_rect);
|
||||
EditorGUILayout.LabelField("Note: It is recommended to use UV coordinates (0.0 -> 1.0) in 2.10+ for rect fields.", EditorStyles.helpBox);
|
||||
|
||||
EditorGUILayout.PropertyField(_umaMaterial);
|
||||
|
||||
if (_umaMaterial != null && _umaMaterial.objectReferenceValue != null)
|
||||
{
|
||||
int textureChannelCount = 0;
|
||||
SerializedObject tempObj = new SerializedObject(_umaMaterial.objectReferenceValue);
|
||||
_channels = tempObj.FindProperty("channels");
|
||||
|
||||
if (_channels == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Channels not found!", MessageType.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
textureChannelCount = _channels.arraySize;
|
||||
}
|
||||
|
||||
od.textureFoldout = GUIHelper.FoldoutBar(od.textureFoldout, $"Texture Channels ({textureChannelCount}) Material Channels ({_textureList.arraySize})");
|
||||
|
||||
if (od.textureFoldout)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
|
||||
EditorGUILayout.PropertyField(_textureList.FindPropertyRelative("Array.size"));
|
||||
_blendList.arraySize = _textureList.arraySize;
|
||||
for (int i = 0; i < _textureList.arraySize; i++)
|
||||
{
|
||||
SerializedProperty textureElement = _textureList.GetArrayElementAtIndex(i);
|
||||
SerializedProperty blendElement = _blendList.GetArrayElementAtIndex(i);
|
||||
string materialName = "Unknown";
|
||||
|
||||
if (i < _channels.arraySize)
|
||||
{
|
||||
SerializedProperty channel = _channels.GetArrayElementAtIndex(i);
|
||||
if (channel != null)
|
||||
{
|
||||
SerializedProperty materialPropertyName = channel.FindPropertyRelative("materialPropertyName");
|
||||
if (materialPropertyName != null)
|
||||
{
|
||||
materialName = materialPropertyName.stringValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
GUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(textureElement, new GUIContent(materialName), GUILayout.ExpandWidth(true));
|
||||
EditorGUILayout.PropertyField(blendElement, new GUIContent(""), GUILayout.Width(110));
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
GUIHelper.EndVerticalPadded(10);
|
||||
}
|
||||
|
||||
if ( _textureList.arraySize != textureChannelCount)
|
||||
{
|
||||
EditorGUILayout.HelpBox($"Overlay Texture count {_textureList.arraySize} and UMA Material channel count {textureChannelCount} don't match!", MessageType.Error);
|
||||
}
|
||||
|
||||
if (!_textureList.hasMultipleDifferentValues)
|
||||
{
|
||||
bool allValid = true;
|
||||
for (int i = 0; i < _textureList.arraySize; i++)
|
||||
{
|
||||
if (_textureList.GetArrayElementAtIndex(i).objectReferenceValue == null)
|
||||
{
|
||||
allValid = false;
|
||||
}
|
||||
}
|
||||
if (!allValid)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Not all textures in Texture List set. This overlay will only work as an additional overlay in a recipe", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("No UMA Material selected!", MessageType.Warning);
|
||||
}
|
||||
|
||||
od.additionalFoldout = GUIHelper.FoldoutBar(od.additionalFoldout, "Alpha mask Parameters");
|
||||
if (od.additionalFoldout)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
|
||||
EditorGUILayout.HelpBox("The alpha mask is optional. If it is not set the texture[0].alpha is used instead.", MessageType.Info);
|
||||
EditorGUILayout.PropertyField(_alphaMask);
|
||||
GUIHelper.EndVerticalPadded(10);
|
||||
}
|
||||
|
||||
od.tagsFoldout = GUIHelper.FoldoutBar(od.tagsFoldout, "Tags");
|
||||
if (od.tagsFoldout)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
|
||||
// EditorGUILayout.PropertyField(_tags, true);
|
||||
(target as OverlayDataAsset).tagsList.DoLayoutList();
|
||||
GUIHelper.EndVerticalPadded(10);
|
||||
}
|
||||
|
||||
od.occlusionFoldout = GUIHelper.FoldoutBar(od.occlusionFoldout, "Occlusion");
|
||||
if (od.occlusionFoldout)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
|
||||
EditorGUILayout.PropertyField(_occlusionEntries, true);
|
||||
GUIHelper.EndVerticalPadded(10);
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
od.lastActionTime = Time.realtimeSinceStartup;
|
||||
od.doSave = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76e2305b140f90e48b6936fe8ad69978
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/OverlayDataAssetInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,27 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(OverlayData))]
|
||||
public class OverlayInspector : Editor
|
||||
{
|
||||
#if UMA_HOTKEYS
|
||||
[MenuItem("Assets/Create/UMA/Core/Overlay Asset %#o")]
|
||||
#else
|
||||
[MenuItem("Assets/Create/UMA/Core/Overlay Asset")]
|
||||
#endif
|
||||
public static void CreateOverlayMenuItem()
|
||||
{
|
||||
var ovl = CustomAssetUtility.CreateAsset<OverlayDataAsset>();
|
||||
}
|
||||
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66462a7c8fca9e247a0d8d5d4168d4fd
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/OverlayInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,382 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(OverlayLibrary))]
|
||||
[CanEditMultipleObjects]
|
||||
public class OverlayLibraryEditor : Editor
|
||||
{
|
||||
private SerializedObject m_Object;
|
||||
private OverlayLibrary overlayLibrary;
|
||||
private SerializedProperty m_OverlayDataCount;
|
||||
|
||||
private const string kArraySizePath = "overlayElementList.Array.size";
|
||||
private const string kArrayData = "overlayElementList.Array.data[{0}]";
|
||||
|
||||
private bool canUpdate;
|
||||
private bool isDirty;
|
||||
|
||||
|
||||
public SerializedProperty scaleAdjust;
|
||||
public SerializedProperty readWrite;
|
||||
public SerializedProperty compress;
|
||||
|
||||
public void OnEnable(){
|
||||
|
||||
m_Object = new SerializedObject(target);
|
||||
overlayLibrary = m_Object.targetObject as OverlayLibrary;
|
||||
m_OverlayDataCount = m_Object.FindProperty(kArraySizePath);
|
||||
scaleAdjust = serializedObject.FindProperty ("scaleAdjust");
|
||||
readWrite = serializedObject.FindProperty ("readWrite");
|
||||
compress = serializedObject.FindProperty ("compress");
|
||||
}
|
||||
|
||||
|
||||
private OverlayDataAsset[] GetOverlayDataArray()
|
||||
{
|
||||
|
||||
int arrayCount = m_OverlayDataCount.intValue;
|
||||
OverlayDataAsset[] OverlayDataArray = new OverlayDataAsset[arrayCount];
|
||||
|
||||
for(int i = 0; i < arrayCount; i++){
|
||||
|
||||
OverlayDataArray[i] = m_Object.FindProperty(string.Format(kArrayData,i)).objectReferenceValue as OverlayDataAsset;
|
||||
|
||||
}
|
||||
return OverlayDataArray;
|
||||
|
||||
}
|
||||
|
||||
private void SetOverlayData(int index, OverlayDataAsset overlayElement)
|
||||
{
|
||||
m_Object.FindProperty(string.Format(kArrayData,index)).objectReferenceValue = overlayElement;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
private OverlayDataAsset GetOverlayDataAtIndex(int index)
|
||||
{
|
||||
return m_Object.FindProperty(string.Format(kArrayData, index)).objectReferenceValue as OverlayDataAsset;
|
||||
}
|
||||
|
||||
private void AddOverlayData(OverlayDataAsset overlayElement)
|
||||
{
|
||||
m_OverlayDataCount.intValue ++;
|
||||
SetOverlayData(m_OverlayDataCount.intValue - 1, overlayElement);
|
||||
}
|
||||
|
||||
|
||||
private void RemoveOverlayDataAtIndex(int index){
|
||||
|
||||
for(int i = index; i < m_OverlayDataCount.intValue - 1; i++){
|
||||
|
||||
SetOverlayData(i, GetOverlayDataAtIndex(i + 1));
|
||||
}
|
||||
|
||||
m_OverlayDataCount.intValue --;
|
||||
|
||||
}
|
||||
|
||||
private void ScaleDownTextures(){
|
||||
|
||||
OverlayDataAsset[] overlayElementList = GetOverlayDataArray();
|
||||
string path;
|
||||
|
||||
|
||||
for(int i = 0; i < overlayElementList.Length; i++){
|
||||
if(overlayElementList[i] != null){
|
||||
Rect tempRect = overlayElementList[i].rect;
|
||||
overlayElementList[i].rect = new Rect(tempRect.x*0.5f,tempRect.y*0.5f,tempRect.width*0.5f,tempRect.height*0.5f);
|
||||
|
||||
EditorUtility.SetDirty(overlayElementList[i]);
|
||||
|
||||
for(int textureID = 0; textureID < overlayElementList[i].textureList.Length; textureID++){
|
||||
if(overlayElementList[i].textureList[textureID]){
|
||||
path = AssetDatabase.GetAssetPath(overlayElementList[i].textureList[textureID]);
|
||||
TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
|
||||
|
||||
textureImporter.maxTextureSize = (int)(textureImporter.maxTextureSize*0.5f);
|
||||
|
||||
AssetDatabase.WriteImportSettingsIfDirty (path);
|
||||
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private void ScaleUpTextures(){
|
||||
|
||||
OverlayDataAsset[] overlayElementList = GetOverlayDataArray();
|
||||
string path;
|
||||
|
||||
|
||||
for(int i = 0; i < overlayElementList.Length; i++){
|
||||
if(overlayElementList[i] != null){
|
||||
|
||||
Rect tempRect = overlayElementList[i].rect;
|
||||
overlayElementList[i].rect = new Rect(tempRect.x*2,tempRect.y*2,tempRect.width*2,tempRect.height*2);
|
||||
|
||||
EditorUtility.SetDirty(overlayElementList[i]);
|
||||
|
||||
for(int textureID = 0; textureID < overlayElementList[i].textureList.Length; textureID++){
|
||||
if(overlayElementList[i].textureList[textureID]){
|
||||
path = AssetDatabase.GetAssetPath(overlayElementList[i].textureList[textureID]);
|
||||
TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
|
||||
|
||||
textureImporter.maxTextureSize = (int)(textureImporter.maxTextureSize*2);
|
||||
|
||||
AssetDatabase.WriteImportSettingsIfDirty (path);
|
||||
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
|
||||
private void ConfigureTextures(){
|
||||
|
||||
OverlayDataAsset[] overlayElementList = GetOverlayDataArray();
|
||||
string path;
|
||||
|
||||
|
||||
for(int i = 0; i < overlayElementList.Length; i++){
|
||||
if(overlayElementList[i] != null){
|
||||
|
||||
for(int textureID = 0; textureID < overlayElementList[i].textureList.Length; textureID++){
|
||||
if(overlayElementList[i].textureList[textureID]){
|
||||
path = AssetDatabase.GetAssetPath(overlayElementList[i].textureList[textureID]);
|
||||
TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
|
||||
|
||||
textureImporter.isReadable = readWrite.boolValue;
|
||||
|
||||
if(compress.boolValue){
|
||||
textureImporter.textureCompression = TextureImporterCompression.CompressedHQ;
|
||||
textureImporter.compressionQuality = 100;
|
||||
}else{
|
||||
textureImporter.textureCompression = TextureImporterCompression.Uncompressed;
|
||||
}
|
||||
|
||||
AssetDatabase.WriteImportSettingsIfDirty (path);
|
||||
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
|
||||
Debug.Log(overlayElementList[i].textureList[textureID].name + " isReadable set to " + readWrite.boolValue + " and compression set to " + compress.boolValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
private void DropAreaGUI(Rect dropArea){
|
||||
|
||||
var evt = Event.current;
|
||||
|
||||
if(evt.type == EventType.DragUpdated){
|
||||
if(dropArea.Contains(evt.mousePosition)){
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
}
|
||||
}
|
||||
|
||||
if(evt.type == EventType.DragPerform){
|
||||
if(dropArea.Contains(evt.mousePosition)){
|
||||
DragAndDrop.AcceptDrag();
|
||||
UnityEngine.Object[] draggedObjects = DragAndDrop.objectReferences;
|
||||
for(int i = 0; i < draggedObjects.Length; i++){
|
||||
if (draggedObjects[i])
|
||||
{
|
||||
OverlayDataAsset tempOverlayData = draggedObjects[i] as OverlayDataAsset;
|
||||
if (tempOverlayData)
|
||||
{
|
||||
AddOverlayData(tempOverlayData);
|
||||
continue;
|
||||
}
|
||||
var path = AssetDatabase.GetAssetPath(draggedObjects[i]);
|
||||
if (System.IO.Directory.Exists(path))
|
||||
{
|
||||
RecursiveScanFoldersForAssets(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RecursiveScanFoldersForAssets(string path)
|
||||
{
|
||||
var assetFiles = System.IO.Directory.GetFiles(path, "*.asset");
|
||||
foreach (var assetFile in assetFiles)
|
||||
{
|
||||
var tempOverlayData = AssetDatabase.LoadAssetAtPath(assetFile, typeof(OverlayDataAsset)) as OverlayDataAsset;
|
||||
if (tempOverlayData)
|
||||
{
|
||||
AddOverlayData(tempOverlayData);
|
||||
}
|
||||
}
|
||||
foreach (var subFolder in System.IO.Directory.GetDirectories(path))
|
||||
{
|
||||
RecursiveScanFoldersForAssets(subFolder.Replace('\\', '/'));
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI(){
|
||||
m_Object.Update();
|
||||
serializedObject.Update();
|
||||
|
||||
GUILayout.Label ("overlayList", EditorStyles.boldLabel);
|
||||
|
||||
|
||||
OverlayDataAsset[] overlayElementList = GetOverlayDataArray();
|
||||
GUILayout.Space(30);
|
||||
GUILayout.Label ("Overlays reduced " + scaleAdjust.intValue +" time(s)");
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
if(scaleAdjust.intValue > 0){
|
||||
if(GUILayout.Button("Resolution +")){
|
||||
ScaleUpTextures();
|
||||
|
||||
isDirty = true;
|
||||
canUpdate = false;
|
||||
scaleAdjust.intValue --;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(GUILayout.Button("Resolution -")){
|
||||
ScaleDownTextures();
|
||||
|
||||
isDirty = true;
|
||||
canUpdate = false;
|
||||
scaleAdjust.intValue ++;
|
||||
}
|
||||
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.Space(20);
|
||||
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
compress.boolValue = GUILayout.Toggle (compress.boolValue ? true : false," Compress Textures");
|
||||
|
||||
readWrite.boolValue = GUILayout.Toggle (readWrite.boolValue ? true : false," Read/Write");
|
||||
|
||||
if(GUILayout.Button(" Apply")){
|
||||
ConfigureTextures();
|
||||
|
||||
isDirty = true;
|
||||
canUpdate = false;
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.Space(20);
|
||||
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
if(GUILayout.Button("Order by Name")){
|
||||
canUpdate = false;
|
||||
|
||||
List<OverlayDataAsset> OverlayDataTemp = new List<OverlayDataAsset>();
|
||||
OverlayDataTemp.AddRange(overlayElementList);
|
||||
|
||||
//Make sure there's no invalid data
|
||||
for(int i = 0; i < OverlayDataTemp.Count; i++){
|
||||
if(OverlayDataTemp[i] == null){
|
||||
OverlayDataTemp.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
OverlayDataTemp.Sort((x,y) => x.name.CompareTo(y.name));
|
||||
|
||||
for(int i = 0; i < OverlayDataTemp.Count; i++){
|
||||
SetOverlayData(i,OverlayDataTemp[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(GUILayout.Button("Update List")){
|
||||
isDirty = true;
|
||||
canUpdate = false;
|
||||
}
|
||||
if (GUILayout.Button("Remove Duplicates"))
|
||||
{
|
||||
HashSet<OverlayDataAsset> Overlays = new HashSet<OverlayDataAsset>();
|
||||
|
||||
foreach(OverlayDataAsset oda in overlayElementList)
|
||||
{
|
||||
Overlays.Add(oda);
|
||||
}
|
||||
|
||||
m_OverlayDataCount.intValue = Overlays.Count;
|
||||
|
||||
List<OverlayDataAsset> od = new List<OverlayDataAsset>();
|
||||
od.AddRange(Overlays);
|
||||
|
||||
for(int i=0;i<od.Count;i++)
|
||||
{
|
||||
SetOverlayData(i,od[i]);
|
||||
}
|
||||
isDirty = true;
|
||||
canUpdate = false;
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.Space(20);
|
||||
Rect dropArea = GUILayoutUtility.GetRect(0.0f,50.0f, GUILayout.ExpandWidth(true));
|
||||
GUI.Box(dropArea,"Drag Overlays here");
|
||||
GUILayout.Space(20);
|
||||
|
||||
|
||||
for(int i = 0; i < m_OverlayDataCount.intValue; i ++){
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
var result = EditorGUILayout.ObjectField(overlayElementList[i], typeof(OverlayDataAsset), true) as OverlayDataAsset;
|
||||
|
||||
if(GUI.changed && canUpdate){
|
||||
SetOverlayData(i,result);
|
||||
}
|
||||
|
||||
if(GUILayout.Button("-", GUILayout.Width(20.0f))){
|
||||
canUpdate = false;
|
||||
RemoveOverlayDataAtIndex(i);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if(i == m_OverlayDataCount.intValue -1){
|
||||
canUpdate = true;
|
||||
|
||||
if(isDirty){
|
||||
overlayLibrary.UpdateDictionary();
|
||||
isDirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropAreaGUI(dropArea);
|
||||
|
||||
if(GUILayout.Button("Add OverlayData")){
|
||||
AddOverlayData(null);
|
||||
}
|
||||
|
||||
if(GUILayout.Button("Clear List")){
|
||||
m_OverlayDataCount.intValue = 0;
|
||||
}
|
||||
|
||||
|
||||
m_Object.ApplyModifiedProperties();
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 936cf90bea11b69439b5c47612fecccb
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/OverlayLibraryEditor.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,602 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(RaceData))]
|
||||
public class RaceInspector : Editor
|
||||
{
|
||||
[MenuItem("Assets/Create/UMA/Core/RaceData")]
|
||||
public static void CreateRaceMenuItem()
|
||||
{
|
||||
CustomAssetUtility.CreateAsset<RaceData>();
|
||||
}
|
||||
|
||||
protected RaceData race;
|
||||
protected bool _needsUpdate;
|
||||
protected string _errorMessage;
|
||||
//we dont really want to use delayedFields because if the user does not change focus from the field in the inspector but instead selects another asset in their projects their changes dont save
|
||||
//Instead what we really want to do is set a short delay on saving so that the asset doesn't save while the user is typing in a field
|
||||
private float lastActionTime = 0;
|
||||
private bool doSave = false;
|
||||
//pRaceInspector needs to get unpacked UMATextRecipes so we might need a virtual UMAContextBase
|
||||
GameObject EditorUMAContextBase;
|
||||
|
||||
#region DCS variables
|
||||
private ReorderableList wardrobeSlotList;
|
||||
private bool wardrobeSlotListInitialized = false;
|
||||
|
||||
private int compatibleRacePickerID;
|
||||
static bool[] _BCFoldouts = new bool[0];
|
||||
List<SlotData> baseSlotsList = new List<SlotData>();
|
||||
List<string> baseSlotsNamesList = new List<string>();
|
||||
#endregion
|
||||
|
||||
public void OnEnable() {
|
||||
race = target as RaceData;
|
||||
EditorApplication.update += DoDelayedSave;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
EditorApplication.update -= DoDelayedSave;
|
||||
}
|
||||
|
||||
void DoDelayedSave()
|
||||
{
|
||||
if (doSave && Time.realtimeSinceStartup > (lastActionTime + 0.5f))
|
||||
{
|
||||
doSave = false;
|
||||
lastActionTime = Time.realtimeSinceStartup;
|
||||
EditorUtility.SetDirty(race);
|
||||
string path = AssetDatabase.GetAssetPath(race.GetInstanceID());
|
||||
AssetDatabase.ImportAsset(path);
|
||||
UMAUpdateProcessor.UpdateRace(race);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (lastActionTime == 0)
|
||||
{
|
||||
lastActionTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
race.raceName = EditorGUILayout.TextField("Race Name", race.raceName);
|
||||
race.umaTarget = (UMA.RaceData.UMATarget)EditorGUILayout.EnumPopup(new GUIContent("UMA Target", "The Mecanim animation rig type."), race.umaTarget);
|
||||
race.genericRootMotionTransformName = EditorGUILayout.TextField("Root Motion Transform", race.genericRootMotionTransformName);
|
||||
race.TPose = EditorGUILayout.ObjectField(new GUIContent("T-Pose", "The UMA T-Pose asset can be created by selecting the race fbx and choosing the Extract T-Pose dropdown. Only needs to be done once per race."), race.TPose, typeof(UmaTPose), false) as UmaTPose;
|
||||
race.expressionSet = EditorGUILayout.ObjectField(new GUIContent("Expression Set", "The Expression Set asset is used by the Expression player."), race.expressionSet, typeof(UMA.PoseTools.UMAExpressionSet), false) as UMA.PoseTools.UMAExpressionSet;
|
||||
EditorGUILayout.HelpBox("Fixup Rotations should be true for Blender FBX slots", MessageType.Info);
|
||||
race.FixupRotations = EditorGUILayout.Toggle("Fixup Rotations",race.FixupRotations);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
SerializedProperty dnaConverterListprop = serializedObject.FindProperty("_dnaConverterList");
|
||||
EditorGUILayout.PropertyField(dnaConverterListprop, true);
|
||||
|
||||
SerializedProperty dnaRanges = serializedObject.FindProperty("dnaRanges");
|
||||
EditorGUILayout.PropertyField(dnaRanges, true);
|
||||
/* tags GUI */
|
||||
SerializedProperty tags = serializedObject.FindProperty("tags");
|
||||
EditorGUILayout.PropertyField(tags, true);
|
||||
if(EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
_needsUpdate = true;
|
||||
}
|
||||
|
||||
foreach (var field in race.GetType().GetFields())
|
||||
{
|
||||
foreach (var attribute in System.Attribute.GetCustomAttributes(field))
|
||||
{
|
||||
if (attribute is UMAAssetFieldVisible)
|
||||
{
|
||||
SerializedProperty serializedProp = serializedObject.FindProperty(field.Name);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(serializedProp);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
_needsUpdate = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
PreInspectorGUI(ref _needsUpdate);
|
||||
if(_needsUpdate == true){
|
||||
_needsUpdate = false;
|
||||
DoUpdate();
|
||||
}
|
||||
}catch (UMAResourceNotFoundException e){
|
||||
_errorMessage = e.Message;
|
||||
}
|
||||
|
||||
if (GUI.changed)
|
||||
{
|
||||
doSave = true;
|
||||
lastActionTime = Time.realtimeSinceStartup;
|
||||
UMAAssetIndexer.RebuildUMAS(SceneManager.GetActiveScene());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to this method in extender editors if you need to do anything extra when updating the data.
|
||||
/// </summary>
|
||||
protected virtual void DoUpdate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#region DCS functions
|
||||
// Drop area for Backwards Compatible Races
|
||||
private void CompatibleRacesDropArea(Rect dropArea, SerializedProperty crossCompatibilitySettingsData)
|
||||
{
|
||||
Event evt = Event.current;
|
||||
//make the box clickable so that the user can select raceData assets from the asset selection window
|
||||
if (evt.type == EventType.MouseUp)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
compatibleRacePickerID = EditorGUIUtility.GetControlID(new GUIContent("crfObjectPicker"), FocusType.Passive);
|
||||
EditorGUIUtility.ShowObjectPicker<RaceData>(null, false, "", compatibleRacePickerID);
|
||||
Event.current.Use();//stops the Mismatched LayoutGroup errors
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (evt.commandName == "ObjectSelectorUpdated" && EditorGUIUtility.GetObjectPickerControlID() == compatibleRacePickerID)
|
||||
{
|
||||
RaceData tempRaceDataAsset = EditorGUIUtility.GetObjectPickerObject() as RaceData;
|
||||
if (tempRaceDataAsset)
|
||||
{
|
||||
AddRaceDataAsset(tempRaceDataAsset, crossCompatibilitySettingsData);
|
||||
}
|
||||
if (Event.current.type != EventType.Layout)
|
||||
{
|
||||
Event.current.Use();//stops the Mismatched LayoutGroup errors
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (evt.type == EventType.DragUpdated)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
}
|
||||
}
|
||||
if (evt.type == EventType.DragPerform)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
|
||||
UnityEngine.Object[] draggedObjects = DragAndDrop.objectReferences as UnityEngine.Object[];
|
||||
for (int i = 0; i < draggedObjects.Length; i++)
|
||||
{
|
||||
if (draggedObjects[i])
|
||||
{
|
||||
RaceData tempRaceDataAsset = draggedObjects[i] as RaceData;
|
||||
if (tempRaceDataAsset)
|
||||
{
|
||||
AddRaceDataAsset(tempRaceDataAsset, crossCompatibilitySettingsData);
|
||||
continue;
|
||||
}
|
||||
|
||||
var path = AssetDatabase.GetAssetPath(draggedObjects[i]);
|
||||
if (System.IO.Directory.Exists(path))
|
||||
{
|
||||
RecursiveScanFoldersForAssets(path, crossCompatibilitySettingsData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RecursiveScanFoldersForAssets(string path, SerializedProperty crossCompatibilitySettingsData)
|
||||
{
|
||||
var assetFiles = System.IO.Directory.GetFiles(path, "*.asset");
|
||||
foreach (var assetFile in assetFiles)
|
||||
{
|
||||
var tempRaceDataAsset = AssetDatabase.LoadAssetAtPath(assetFile, typeof(RaceData)) as RaceData;
|
||||
if (tempRaceDataAsset)
|
||||
{
|
||||
AddRaceDataAsset(tempRaceDataAsset, crossCompatibilitySettingsData);
|
||||
}
|
||||
}
|
||||
foreach (var subFolder in System.IO.Directory.GetDirectories(path))
|
||||
{
|
||||
RecursiveScanFoldersForAssets(subFolder.Replace('\\', '/'), crossCompatibilitySettingsData);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRaceDataAsset(RaceData raceDataAsset, SerializedProperty crossCompatibilitySettingsData)
|
||||
{
|
||||
if (raceDataAsset.raceName == serializedObject.FindProperty("raceName").stringValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < crossCompatibilitySettingsData.arraySize; i++)
|
||||
{
|
||||
var ccRaceName = crossCompatibilitySettingsData.GetArrayElementAtIndex(i).FindPropertyRelative("ccRace").stringValue;
|
||||
if (ccRaceName == raceDataAsset.raceName)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
crossCompatibilitySettingsData.InsertArrayElementAtIndex(crossCompatibilitySettingsData.arraySize);
|
||||
crossCompatibilitySettingsData.GetArrayElementAtIndex(crossCompatibilitySettingsData.arraySize - 1).FindPropertyRelative("ccRace").stringValue = raceDataAsset.raceName;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
//if (!compatibleRaces.Contains(raceDataAsset.raceName))
|
||||
// compatibleRaces.Add(raceDataAsset.raceName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add to PreInspectorGUI in any derived editors to allow editing of new properties added to races.
|
||||
/// </summary>
|
||||
//partial void PreInspectorGUI(ref bool result);
|
||||
protected virtual void PreInspectorGUI(ref bool result)
|
||||
{
|
||||
if (!wardrobeSlotListInitialized)
|
||||
{
|
||||
InitWardrobeSlotList();
|
||||
}
|
||||
result = AddExtraStuff();
|
||||
}
|
||||
|
||||
private void InitWardrobeSlotList()
|
||||
{
|
||||
var thisWardrobeSlotList = serializedObject.FindProperty("wardrobeSlots");
|
||||
if (thisWardrobeSlotList.arraySize == 0)
|
||||
{
|
||||
race.ValidateWardrobeSlots(true);
|
||||
thisWardrobeSlotList = serializedObject.FindProperty("wardrobeSlots");
|
||||
}
|
||||
wardrobeSlotList = new ReorderableList(serializedObject, thisWardrobeSlotList, true, true, true, true);
|
||||
wardrobeSlotList.drawHeaderCallback = (Rect rect) => {
|
||||
EditorGUI.LabelField(rect, "Wardrobe Slots");
|
||||
};
|
||||
wardrobeSlotList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => {
|
||||
var element = wardrobeSlotList.serializedProperty.GetArrayElementAtIndex(index);
|
||||
rect.y += 2;
|
||||
element.stringValue = EditorGUI.TextField(new Rect(rect.x + 10, rect.y, rect.width - 10, EditorGUIUtility.singleLineHeight), element.stringValue);
|
||||
};
|
||||
wardrobeSlotListInitialized = true;
|
||||
}
|
||||
|
||||
public bool AddExtraStuff()
|
||||
{
|
||||
SerializedProperty baseRaceRecipe = serializedObject.FindProperty("baseRaceRecipe");
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(baseRaceRecipe, true);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
if (wardrobeSlotList == null)
|
||||
{
|
||||
InitWardrobeSlotList();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
wardrobeSlotList.DoLayoutList();
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
if (!race.ValidateWardrobeSlots())
|
||||
{
|
||||
EditorUtility.SetDirty(race);
|
||||
}
|
||||
}
|
||||
//new CrossCompatibilitySettings
|
||||
//To push any old settings in RaceData.backwardsCompatibleWith into the new crossCompatibilitySettings we have to call GetCrossCompatibleRaces() directly on the target
|
||||
#pragma warning disable 618
|
||||
if (race.backwardsCompatibleWith.Count > 0)
|
||||
{
|
||||
var cc = race.GetCrossCompatibleRaces();
|
||||
if (cc.Count > 0)
|
||||
{
|
||||
serializedObject.Update();
|
||||
}
|
||||
}
|
||||
#pragma warning restore 618
|
||||
SerializedProperty _crossCompatibilitySettings = serializedObject.FindProperty("_crossCompatibilitySettings");
|
||||
SerializedProperty _crossCompatibilitySettingsData = _crossCompatibilitySettings.FindPropertyRelative("settingsData");
|
||||
//draw the new version of the crossCompatibility list that allows users to define what slots in this races base recipe equate to in the backwards compatible races base recipe
|
||||
_crossCompatibilitySettings.isExpanded = EditorGUILayout.Foldout(_crossCompatibilitySettings.isExpanded, "Cross Compatibility Settings");
|
||||
if (_crossCompatibilitySettings.isExpanded)
|
||||
{
|
||||
//draw an info foldout
|
||||
EditorGUI.indentLevel++;
|
||||
_crossCompatibilitySettingsData.isExpanded = EditorGUILayout.Foldout(_crossCompatibilitySettingsData.isExpanded, "Help");
|
||||
if (_crossCompatibilitySettingsData.isExpanded)
|
||||
{
|
||||
var helpText = "CrossCompatibilitySettings allows this race to wear wardrobe slots from another race, if this race has a wardrobe slot that the recipe is set to.";
|
||||
helpText += " You can further configure the compatibility settings for each compatible race to define 'equivalent' slotdatas in the races' base recipes.";
|
||||
helpText += " For example you could define that this races 'highpolyMaleChest' slotdata in its base recipe is equivalent to HumanMales 'MaleChest' slot data in its base recipe.";
|
||||
helpText += " This would mean that any recipes which hid or applied an overlay to 'MaleChest' would hide or apply an overlay to 'highPolyMaleChest' on this race.";
|
||||
helpText += " If 'Overlays Match' is unchecked then overlays in a recipe wont be applied.";
|
||||
EditorGUILayout.HelpBox(helpText, MessageType.Info);
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
if (baseRaceRecipe.objectReferenceValue != null)
|
||||
{
|
||||
Rect dropArea = new Rect();
|
||||
dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
|
||||
GUI.Box(dropArea, "Drag cross compatible Races here. Click to pick.");
|
||||
CompatibleRacesDropArea(dropArea, _crossCompatibilitySettingsData);
|
||||
EditorGUILayout.Space();
|
||||
//update the foldouts list if the dropbox changes anything
|
||||
if (_BCFoldouts.Length != _crossCompatibilitySettingsData.arraySize)
|
||||
{
|
||||
Array.Resize<bool>(ref _BCFoldouts, _crossCompatibilitySettingsData.arraySize);
|
||||
}
|
||||
//we need an uptodate list of the slots in THIS races base recipe
|
||||
baseSlotsList.Clear();
|
||||
baseSlotsNamesList.Clear();
|
||||
|
||||
UMAData.UMARecipe thisBaseRecipe = (baseRaceRecipe.objectReferenceValue as UMARecipeBase).GetCachedRecipe(UMAContextBase.Instance);
|
||||
SlotData[] thisBaseSlots = thisBaseRecipe.GetAllSlots();
|
||||
foreach (SlotData slot in thisBaseSlots)
|
||||
{
|
||||
if (slot != null)
|
||||
{
|
||||
baseSlotsList.Add(slot);
|
||||
baseSlotsNamesList.Add(slot.slotName);
|
||||
}
|
||||
}
|
||||
List<int> crossCompatibleSettingsToDelete = new List<int>();
|
||||
//draw a foldout area for each compatible race that will show an entry for each slot in this races base recipe
|
||||
//with a picker to choose the slot from the compatible race's base recipe that it equates to
|
||||
for (int i = 0; i < _crossCompatibilitySettingsData.arraySize; i++)
|
||||
{
|
||||
bool del = false;
|
||||
var thisCCSettings = _crossCompatibilitySettingsData.GetArrayElementAtIndex(i).FindPropertyRelative("ccSettings");
|
||||
var ccRaceName = _crossCompatibilitySettingsData.GetArrayElementAtIndex(i).FindPropertyRelative("ccRace").stringValue;
|
||||
//this could be missing- we should show that
|
||||
var label = ccRaceName;
|
||||
if (GetCompatibleRaceData(ccRaceName) == null)
|
||||
{
|
||||
label += " (missing)";
|
||||
}
|
||||
|
||||
GUIHelper.FoldoutBar(ref _BCFoldouts[i], label, out del);
|
||||
if (del)
|
||||
{
|
||||
crossCompatibleSettingsToDelete.Add(i);
|
||||
}
|
||||
if (_BCFoldouts[i])
|
||||
{
|
||||
DrawCCUI(ccRaceName, baseRaceRecipe, thisCCSettings);
|
||||
}
|
||||
}
|
||||
if (crossCompatibleSettingsToDelete.Count > 0)
|
||||
{
|
||||
foreach (int del in crossCompatibleSettingsToDelete)
|
||||
{
|
||||
_crossCompatibilitySettingsData.DeleteArrayElementAtIndex(del);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Please define this races baseRaceRecipe before trying to define its cross compatibility settings.", MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("raceThumbnails"), true);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private RaceData GetCompatibleRaceData(string raceName)
|
||||
{
|
||||
RaceData foundRace = null;
|
||||
string[] foundRacesStrings = AssetDatabase.FindAssets("t:RaceData");
|
||||
for (int i = 0; i < foundRacesStrings.Length; i++)
|
||||
{
|
||||
RaceData thisFoundRace = AssetDatabase.LoadAssetAtPath<RaceData>(AssetDatabase.GUIDToAssetPath(foundRacesStrings[i]));
|
||||
if (thisFoundRace.raceName == raceName)
|
||||
{
|
||||
foundRace = thisFoundRace;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return foundRace;
|
||||
}
|
||||
|
||||
private void DrawCCUI(string ccRaceName, SerializedProperty baseRaceRecipe, SerializedProperty thisCCSettings)
|
||||
{
|
||||
GUIHelper.BeginVerticalPadded(5, new Color(0.75f, 0.875f, 1f));
|
||||
EditorGUILayout.LabelField("Equivalent Slots with " + ccRaceName, EditorStyles.centeredGreyMiniLabel);
|
||||
if (baseRaceRecipe.objectReferenceValue == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Please set this Races 'Base Race Recipe' before trying to set equivalent Slots.", MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
//we need to get the base raceRecipeSlots for this compatible race
|
||||
var ccRaceData = GetCompatibleRaceData(ccRaceName);
|
||||
if (ccRaceData != null)
|
||||
{
|
||||
if (ccRaceData.baseRaceRecipe == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Please set " + ccRaceData.raceName + " Races 'Base Race Recipe' before trying to set equivalent Slots.", MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ccSlotsList = new List<SlotData>();
|
||||
var ccSlotsNamesList = new List<string>();
|
||||
UMAData.UMARecipe ccBaseRecipe = ccRaceData.baseRaceRecipe.GetCachedRecipe(UMAContextBase.Instance);
|
||||
SlotData[] ccBaseSlots = ccBaseRecipe.GetAllSlots();
|
||||
foreach (SlotData slot in ccBaseSlots)
|
||||
{
|
||||
if (slot != null)
|
||||
{
|
||||
ccSlotsList.Add(slot);
|
||||
ccSlotsNamesList.Add(slot.slotName);
|
||||
}
|
||||
}
|
||||
//if that worked we can draw the UI for any set values and a button to add new ones
|
||||
GUIHelper.BeginVerticalPadded(2, new Color(1f, 1f, 1f, 0.5f));
|
||||
var headerRect = GUILayoutUtility.GetRect(0.0f, (EditorGUIUtility.singleLineHeight * 2), GUILayout.ExpandWidth(true));
|
||||
var slotLabelRect = headerRect;
|
||||
var gapRect = headerRect;
|
||||
var cSlotLabelRect = headerRect;
|
||||
var overlaysMatchLabelRect = headerRect;
|
||||
var deleteRect = headerRect;
|
||||
slotLabelRect.width = (headerRect.width - 50f - 22f - 22f) / 2;
|
||||
gapRect.xMin = slotLabelRect.xMax;
|
||||
gapRect.width = 22f;
|
||||
cSlotLabelRect.xMin = gapRect.xMax;
|
||||
cSlotLabelRect.width = slotLabelRect.width;
|
||||
overlaysMatchLabelRect.xMin = cSlotLabelRect.xMax;
|
||||
overlaysMatchLabelRect.width = 50f;
|
||||
deleteRect.xMin = overlaysMatchLabelRect.xMax;
|
||||
deleteRect.width = 22f;
|
||||
//move this up
|
||||
var tableHeaderStyle = EditorStyles.wordWrappedMiniLabel;
|
||||
tableHeaderStyle.alignment = TextAnchor.MiddleCenter;
|
||||
//we need a gui style for this that wraps the text and vertically centers it in the space
|
||||
EditorGUI.LabelField(slotLabelRect, "This Races Slot", tableHeaderStyle);
|
||||
EditorGUI.LabelField(gapRect, "", tableHeaderStyle);
|
||||
EditorGUI.LabelField(cSlotLabelRect, "Compatible Races Slot", tableHeaderStyle);
|
||||
EditorGUI.LabelField(overlaysMatchLabelRect, "Overlays Match", tableHeaderStyle);
|
||||
GUIHelper.EndVerticalPadded(2);
|
||||
GUIHelper.BeginVerticalPadded(2, new Color(0.75f, 0.875f, 1f));
|
||||
if (thisCCSettings.arraySize > 0)
|
||||
{
|
||||
for (int ccsd = 0; ccsd < thisCCSettings.arraySize; ccsd++)
|
||||
{
|
||||
if (DrawCCUISetting(ccsd, thisCCSettings, ccSlotsNamesList))
|
||||
{
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("No equivalent slots defined", EditorStyles.miniLabel);
|
||||
}
|
||||
GUIHelper.EndVerticalPadded(2);
|
||||
var addButtonRect = GUILayoutUtility.GetRect(0.0f, EditorGUIUtility.singleLineHeight, GUILayout.ExpandWidth(true));
|
||||
addButtonRect.xMin = addButtonRect.xMax - 70f;
|
||||
addButtonRect.width = 70f;
|
||||
if (GUI.Button(addButtonRect, "Add"))
|
||||
{
|
||||
thisCCSettings.InsertArrayElementAtIndex(thisCCSettings.arraySize);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("The cross compatible race " + ccRaceName + " could not be found!", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
GUIHelper.EndVerticalPadded(5);
|
||||
}
|
||||
|
||||
private bool DrawCCUISetting(int ccsd, SerializedProperty thisCCSettings, List<string> ccSlotsNamesList)
|
||||
{
|
||||
var changed = false;
|
||||
var startingRect = GUILayoutUtility.GetRect(0.0f, EditorGUIUtility.singleLineHeight, GUILayout.ExpandWidth(true));
|
||||
var thisSlot = thisCCSettings.GetArrayElementAtIndex(ccsd).FindPropertyRelative("raceSlot").stringValue;
|
||||
var thisSlotIndex = baseSlotsNamesList.IndexOf(thisSlot);
|
||||
var thisCompatibleSlot = thisCCSettings.GetArrayElementAtIndex(ccsd).FindPropertyRelative("compatibleRaceSlot").stringValue;
|
||||
var thisCompatibleSlotIndex = ccSlotsNamesList.IndexOf(thisCompatibleSlot);
|
||||
var thisOverlaysMatch = thisCCSettings.GetArrayElementAtIndex(ccsd).FindPropertyRelative("overlaysMatch").boolValue;
|
||||
var thisSlotRect = startingRect;
|
||||
var thisEqualsLabelRect = startingRect;
|
||||
var thisCompatibleSlotRect = startingRect;
|
||||
//var thisOverlaysLabelRect = startingRect;
|
||||
var thisOverlaysMatchRect = startingRect;
|
||||
var thisDeleteRect = startingRect;
|
||||
thisSlotRect.width = (startingRect.width - 50f - 22f - 22f) / 2;
|
||||
thisEqualsLabelRect.xMin = thisSlotRect.xMax;
|
||||
thisEqualsLabelRect.width = 22f;
|
||||
thisCompatibleSlotRect.xMin = thisEqualsLabelRect.xMax;
|
||||
thisCompatibleSlotRect.width = thisSlotRect.width;
|
||||
thisOverlaysMatchRect.xMin = thisCompatibleSlotRect.xMax + 22f;
|
||||
thisOverlaysMatchRect.width = 50f - 22f;
|
||||
thisDeleteRect.xMin = thisOverlaysMatchRect.xMax;
|
||||
thisDeleteRect.width = 22f;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var newSlotIndex = EditorGUI.Popup(thisSlotRect, "", thisSlotIndex, baseSlotsNamesList.ToArray());
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (newSlotIndex != thisSlotIndex)
|
||||
{
|
||||
thisCCSettings.GetArrayElementAtIndex(ccsd).FindPropertyRelative("raceSlot").stringValue = baseSlotsNamesList[newSlotIndex];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
EditorGUI.LabelField(thisEqualsLabelRect, "==");
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var newCompatibleSlotIndex = EditorGUI.Popup(thisCompatibleSlotRect, "", thisCompatibleSlotIndex, ccSlotsNamesList.ToArray());
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (newCompatibleSlotIndex != thisCompatibleSlotIndex)
|
||||
{
|
||||
thisCCSettings.GetArrayElementAtIndex(ccsd).FindPropertyRelative("compatibleRaceSlot").stringValue = ccSlotsNamesList[newCompatibleSlotIndex];
|
||||
/*var ccSlotsOverlays = ccSlotsList[newCompatibleSlotIndex].GetOverlayList();
|
||||
thisCCSettings.GetArrayElementAtIndex(ccsd).FindPropertyRelative("compatibleRaceSlotOverlays").arraySize = ccSlotsOverlays.Count;
|
||||
for (int ccai = 0; ccai < ccSlotsOverlays.Count; ccai++)
|
||||
thisCCSettings.GetArrayElementAtIndex(ccsd).FindPropertyRelative("compatibleRaceSlotOverlays").GetArrayElementAtIndex(ccai).stringValue = ccSlotsOverlays[ccai].overlayName;*/
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
//we need a gui style for this that centers this horizontally
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var newOverlaysMatch = EditorGUI.ToggleLeft(thisOverlaysMatchRect, " ", thisOverlaysMatch);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (newOverlaysMatch != thisOverlaysMatch)
|
||||
{
|
||||
thisCCSettings.GetArrayElementAtIndex(ccsd).FindPropertyRelative("overlaysMatch").boolValue = newOverlaysMatch;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (GUI.Button(thisDeleteRect, "X", EditorStyles.miniButton))
|
||||
{
|
||||
thisCCSettings.DeleteArrayElementAtIndex(ccsd);
|
||||
changed = true;
|
||||
}
|
||||
//******NEEDS TO BE IN THE RETURN***//
|
||||
//if (changed)
|
||||
// serializedObject.ApplyModifiedProperties();
|
||||
//GUILayout.EndHorizontal();
|
||||
GUILayout.Space(2f);
|
||||
return changed;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df45baff2c1c17c488ecc88585a3a19f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/RaceInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,132 @@
|
||||
// <author>
|
||||
// douduck08: https://github.com/douduck08
|
||||
// Use Reflection to get instance of Unity's SerializedProperty in Custom Editor.
|
||||
// Modified codes from 'Unity Answers', in order to apply on nested List<T> or Array.
|
||||
//
|
||||
// Original author: HiddenMonk & Johannes Deml
|
||||
// Ref: http://answers.unity3d.com/questions/627090/convert-serializedproperty-to-custom-class.html
|
||||
// </author>
|
||||
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA
|
||||
{
|
||||
public static class SerializedPropertyExtensions
|
||||
{
|
||||
|
||||
public static T GetValue<T>(this SerializedProperty property) where T : class
|
||||
{
|
||||
object obj = property.serializedObject.targetObject;
|
||||
string path = property.propertyPath.Replace(".Array.data", "");
|
||||
string[] fieldStructure = path.Split('.');
|
||||
Regex rgx = new Regex(@"\[\d+\]");
|
||||
for (int i = 0; i < fieldStructure.Length; i++)
|
||||
{
|
||||
if (fieldStructure[i].Contains("["))
|
||||
{
|
||||
int index = System.Convert.ToInt32(new string(fieldStructure[i].Where(c => char.IsDigit(c)).ToArray()));
|
||||
obj = GetFieldValueWithIndex(rgx.Replace(fieldStructure[i], ""), obj, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj = GetFieldValue(fieldStructure[i], obj);
|
||||
}
|
||||
}
|
||||
return (T)obj;
|
||||
}
|
||||
|
||||
public static bool SetValue<T>(this SerializedProperty property, T value) where T : class
|
||||
{
|
||||
object obj = property.serializedObject.targetObject;
|
||||
string path = property.propertyPath.Replace(".Array.data", "");
|
||||
string[] fieldStructure = path.Split('.');
|
||||
Regex rgx = new Regex(@"\[\d+\]");
|
||||
for (int i = 0; i < fieldStructure.Length - 1; i++)
|
||||
{
|
||||
if (fieldStructure[i].Contains("["))
|
||||
{
|
||||
int index = System.Convert.ToInt32(new string(fieldStructure[i].Where(c => char.IsDigit(c)).ToArray()));
|
||||
obj = GetFieldValueWithIndex(rgx.Replace(fieldStructure[i], ""), obj, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj = GetFieldValue(fieldStructure[i], obj);
|
||||
}
|
||||
}
|
||||
|
||||
string fieldName = fieldStructure.Last();
|
||||
if (fieldName.Contains("["))
|
||||
{
|
||||
int index = System.Convert.ToInt32(new string(fieldName.Where(c => char.IsDigit(c)).ToArray()));
|
||||
return SetFieldValueWithIndex(rgx.Replace(fieldName, ""), obj, index, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SetFieldValue(fieldName, obj, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static object GetFieldValue(string fieldName, object obj, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
{
|
||||
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
|
||||
if (field != null)
|
||||
{
|
||||
return field.GetValue(obj);
|
||||
}
|
||||
return default(object);
|
||||
}
|
||||
|
||||
private static object GetFieldValueWithIndex(string fieldName, object obj, int index, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
{
|
||||
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
|
||||
if (field != null)
|
||||
{
|
||||
object list = field.GetValue(obj);
|
||||
if (list.GetType().IsArray)
|
||||
{
|
||||
return ((object[])list)[index];
|
||||
}
|
||||
else if (list is IEnumerable)
|
||||
{
|
||||
return ((IList)list)[index];
|
||||
}
|
||||
}
|
||||
return default(object);
|
||||
}
|
||||
|
||||
public static bool SetFieldValue(string fieldName, object obj, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
{
|
||||
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
|
||||
if (field != null)
|
||||
{
|
||||
field.SetValue(obj, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool SetFieldValueWithIndex(string fieldName, object obj, int index, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
{
|
||||
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
|
||||
if (field != null)
|
||||
{
|
||||
object list = field.GetValue(obj);
|
||||
if (list.GetType().IsArray)
|
||||
{
|
||||
((object[])list)[index] = value;
|
||||
return true;
|
||||
}
|
||||
else if (value is IEnumerable)
|
||||
{
|
||||
((IList)list)[index] = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 761953b54f7c8854d9e4f0ff8024bb96
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/SerializedPropertyExtensions.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,10 @@
|
||||
using UnityEditor;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
// [CustomEditor(typeof(SharedColorTable))]
|
||||
public class SharedColorTableEditor : Editor
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86e6e15da58a0e845af3456db52f3d48
|
||||
timeCreated: 1427500797
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/SharedColorTableEditor.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
public struct SlotBuilderParameters
|
||||
{
|
||||
public bool udimAdjustment;
|
||||
public bool calculateNormals;
|
||||
public bool calculateTangents;
|
||||
public bool binarySerialization;
|
||||
public bool useRootFolder;
|
||||
public bool nameByMaterial;
|
||||
public bool keepAllBones;
|
||||
|
||||
public string stripBones;
|
||||
public string rootBone;
|
||||
public string assetName;
|
||||
public string slotName;
|
||||
public string assetFolder;
|
||||
public string slotFolder;
|
||||
|
||||
public List<string> keepList;
|
||||
public SkinnedMeshRenderer slotMesh;
|
||||
public SkinnedMeshRenderer seamsMesh;
|
||||
public UMAMaterial material;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 068e321f2da046b42b4dd5c60af04e8d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/SlotBuilderParameters.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,668 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(SlotDataAsset))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SlotDataAssetInspector : Editor
|
||||
{
|
||||
enum SlotPreviewMode { ThisSlot, WeldSlot, BothSlots };
|
||||
|
||||
static string[] RegularSlotFields = new string[] { "slotName", "CharacterBegun", "SlotAtlassed", "SlotProcessed", "SlotBeginProcessing", "DNAApplied", "CharacterCompleted", "_slotDNALegacy", "tags", "isWildCardSlot", "Races", "smooshOffset", "smooshExpand", "Welds" };
|
||||
static string[] WildcardSlotFields = new string[] { "slotName", "CharacterBegun", "SlotAtlassed", "SlotProcessed", "SlotBeginProcessing", "DNAApplied", "CharacterCompleted", "_slotDNALegacy", "tags", "isWildCardSlot", "Races", "_rendererAsset", "maxLOD", "useAtlasOverlay", "overlayScale", "animatedBoneNames", "_slotDNA", "meshData", "subMeshIndex", "Welds" };
|
||||
SerializedProperty slotName;
|
||||
SerializedProperty CharacterBegun;
|
||||
SerializedProperty SlotAtlassed;
|
||||
SerializedProperty SlotProcessed;
|
||||
SerializedProperty SlotBeginProcessing;
|
||||
SerializedProperty DNAApplied;
|
||||
SerializedProperty CharacterCompleted;
|
||||
SerializedProperty MaxLOD;
|
||||
SerializedProperty isClippingPlane;
|
||||
SerializedProperty smooshOffset;
|
||||
SerializedProperty smooshExpand;
|
||||
SlotDataAsset slot;
|
||||
SlotDataAsset.Welding lastWeld = null;
|
||||
SlotDataAsset WeldToSlot = null;
|
||||
|
||||
bool CopyNormals;
|
||||
bool CopyBoneWeights;
|
||||
UMA.SlotDataAsset.BlendshapeCopyMode blendshapeCopyMode;
|
||||
bool AverageNormals;
|
||||
float weldDistance = 0.0001f;
|
||||
bool reConfigurePreview = false;
|
||||
|
||||
private int selectedRaceIndex = -1;
|
||||
private List<RaceData> foundRaces = new List<RaceData>();
|
||||
private List<string> foundRaceNames = new List<string>();
|
||||
|
||||
public override bool HasPreviewGUI() => true;
|
||||
MeshPreview MeshPreview;
|
||||
Mesh meshToPreview;
|
||||
static Vector3 previewRotation = Vector3.zero;
|
||||
SlotPreviewMode previewMode = SlotPreviewMode.ThisSlot;
|
||||
int previewVertex = -1;
|
||||
|
||||
[MenuItem("Assets/Create/UMA/Core/Custom Slot Asset")]
|
||||
public static void CreateCustomSlotAssetMenuItem()
|
||||
{
|
||||
CustomAssetUtility.CreateAsset<SlotDataAsset>("", true, "Custom");
|
||||
}
|
||||
|
||||
[MenuItem("Assets/Create/UMA/Core/Wildcard Slot Asset")]
|
||||
public static void CreateWildcardSlotAssetMenuItem()
|
||||
{
|
||||
SlotDataAsset wildcard = CustomAssetUtility.CreateAsset<SlotDataAsset>("", true, "Wildcard", true);
|
||||
wildcard.isWildCardSlot = true;
|
||||
wildcard.slotName = "WildCard";
|
||||
EditorUtility.SetDirty(wildcard);
|
||||
string path = AssetDatabase.GetAssetPath(wildcard.GetInstanceID());
|
||||
AssetDatabase.ImportAsset(path);
|
||||
EditorUtility.DisplayDialog("UMA", "Wildcard slot created. You should first change the SlotName in the inspector, and then add it to the global library or to a scene library", "OK");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// clean up
|
||||
if (meshToPreview != null)
|
||||
{
|
||||
DestroyImmediate(meshToPreview);
|
||||
}
|
||||
meshToPreview = null;
|
||||
if (MeshPreview != null)
|
||||
{
|
||||
MeshPreview.Dispose();
|
||||
MeshPreview = null;
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
slotName = serializedObject.FindProperty("slotName");
|
||||
CharacterBegun = serializedObject.FindProperty("CharacterBegun");
|
||||
SlotAtlassed = serializedObject.FindProperty("SlotAtlassed");
|
||||
DNAApplied = serializedObject.FindProperty("DNAApplied");
|
||||
SlotProcessed = serializedObject.FindProperty("SlotProcessed");
|
||||
SlotBeginProcessing = serializedObject.FindProperty("SlotBeginProcessing");
|
||||
CharacterCompleted = serializedObject.FindProperty("CharacterCompleted");
|
||||
MaxLOD = serializedObject.FindProperty("maxLOD");
|
||||
isClippingPlane = serializedObject.FindProperty("isClippingPlane");
|
||||
smooshExpand = serializedObject.FindProperty("smooshExpand");
|
||||
smooshOffset = serializedObject.FindProperty("smooshOffset");
|
||||
slot = (target as SlotDataAsset);
|
||||
SetRaceLists();
|
||||
|
||||
slot.backingTags = new List<string>(slot.tags);
|
||||
slot.tagList = GUIHelper.InitGenericTagsList(slot.backingTags);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (meshToPreview != null)
|
||||
{
|
||||
DestroyImmediate(meshToPreview);
|
||||
}
|
||||
meshToPreview = null;
|
||||
if (MeshPreview != null)
|
||||
{
|
||||
MeshPreview.Dispose();
|
||||
MeshPreview = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private void InitTagList(SlotDataAsset _slotDataAsset)
|
||||
{
|
||||
|
||||
var HideTagsProperty = serializedObject.FindProperty("tags");
|
||||
slot.tagList = new ReorderableList(serializedObject, HideTagsProperty, true, true, true, true);
|
||||
slot.tagList.drawHeaderCallback = (Rect rect) =>
|
||||
{
|
||||
if (_slotDataAsset.isWildCardSlot)
|
||||
{
|
||||
EditorGUI.LabelField(rect, "Match the following tags:");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUI.LabelField(rect, "Tags");
|
||||
}
|
||||
};
|
||||
slot.tagList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
var element = (target as SlotDataAsset).tagList.serializedProperty.GetArrayElementAtIndex(index);
|
||||
rect.y += 2;
|
||||
element.stringValue = EditorGUI.TextField(new Rect(rect.x + 10, rect.y, rect.width - 10, EditorGUIUtility.singleLineHeight), element.stringValue);
|
||||
};
|
||||
} */
|
||||
|
||||
public void SetRaceLists()
|
||||
{
|
||||
UMAContextBase ubc = UMAContext.Instance;
|
||||
if (ubc != null)
|
||||
{
|
||||
RaceData[] raceDataArray = ubc.GetAllRaces();
|
||||
foundRaces.Clear();
|
||||
foundRaceNames.Clear();
|
||||
foundRaces.Add(null);
|
||||
foundRaceNames.Add("None Set");
|
||||
foreach (RaceData race in raceDataArray)
|
||||
{
|
||||
if (race != null && race.raceName != "RaceDataPlaceholder")
|
||||
{
|
||||
foundRaces.Add(race);
|
||||
foundRaceNames.Add(race.raceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSourceAsset(SlotDataAsset sda)
|
||||
{
|
||||
if (sda != null)
|
||||
{
|
||||
lastWeld = slot.CalculateWelds(sda, CopyNormals, CopyBoneWeights, AverageNormals, Vector3.kEpsilon, SlotDataAsset.BlendshapeCopyMode.None);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (slot == null)
|
||||
{
|
||||
OnEnable();
|
||||
}
|
||||
bool forceUpdate = false;
|
||||
SlotDataAsset targetAsset = target as SlotDataAsset;
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
GUILayout.BeginHorizontal();
|
||||
EditorGUILayout.DelayedTextField(slotName);
|
||||
if (GUILayout.Button("Use Obj Name", GUILayout.Width(90)))
|
||||
{
|
||||
foreach (var t in targets)
|
||||
{
|
||||
var slotDataAsset = t as SlotDataAsset;
|
||||
slotDataAsset.slotName = slotDataAsset.name;
|
||||
EditorUtility.SetDirty(slotDataAsset);
|
||||
GUI.changed = true;
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Validate"))
|
||||
{
|
||||
foreach (var t in targets)
|
||||
{
|
||||
var slotDataAsset = t as SlotDataAsset;
|
||||
if (slotDataAsset != null)
|
||||
{
|
||||
slotDataAsset.ValidateMeshData();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GUILayout.Button("Clear Errors"))
|
||||
{
|
||||
foreach (var t in targets)
|
||||
{
|
||||
var slotDataAsset = t as SlotDataAsset;
|
||||
if (slotDataAsset != null)
|
||||
{
|
||||
slotDataAsset.Errors = "";
|
||||
EditorUtility.SetDirty(slotDataAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
if (!string.IsNullOrEmpty(targetAsset.Errors))
|
||||
{
|
||||
EditorGUILayout.HelpBox($"Errors: {targetAsset.Errors}", MessageType.Error);
|
||||
}
|
||||
if ((target as SlotDataAsset).isWildCardSlot)
|
||||
{
|
||||
EditorGUILayout.HelpBox("This is a wildcard slot", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField($"UtilitySlot: " + targetAsset.isUtilitySlot);
|
||||
|
||||
if (slot.isWildCardSlot)
|
||||
{
|
||||
Editor.DrawPropertiesExcluding(serializedObject, WildcardSlotFields);
|
||||
}
|
||||
else
|
||||
{
|
||||
Editor.DrawPropertiesExcluding(serializedObject, RegularSlotFields);
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
slot.smooshFoldout = EditorGUILayout.Foldout(slot.smooshFoldout, "Smooshing");
|
||||
GUILayout.EndHorizontal();
|
||||
if (slot.smooshFoldout)
|
||||
{
|
||||
GUILayout.Space(10);
|
||||
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
|
||||
EditorGUILayout.HelpBox("Smooshing is a feature that conforms one slot to another using a clipping plane. Smoosh Offset is used to adjust the offset of the conforming vertexes to help assist conforming and fitting. Smoosh Expand expands scales the vertexes. ", MessageType.Info);
|
||||
|
||||
var currentTarget = target as SlotDataAsset;
|
||||
|
||||
forceUpdate = EditorGUI.EndChangeCheck();
|
||||
EditorGUILayout.PropertyField(smooshOffset);
|
||||
EditorGUILayout.PropertyField(smooshExpand);
|
||||
|
||||
if (GUILayout.Button("Save and Test Smoosh"))
|
||||
{
|
||||
UMAUpdateProcessor.UpdateSlot(target as SlotDataAsset, false);
|
||||
EditorUtility.SetDirty(target);
|
||||
AssetDatabase.SaveAssetIfDirty(target);
|
||||
string path = AssetDatabase.GetAssetPath(target.GetInstanceID());
|
||||
AssetDatabase.ImportAsset(path);
|
||||
forceUpdate = true;
|
||||
}
|
||||
GUIHelper.EndVerticalPadded(10);
|
||||
}
|
||||
|
||||
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
slot.tagsFoldout = EditorGUILayout.Foldout(slot.tagsFoldout, "Tags");
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (slot.tagsFoldout)
|
||||
{
|
||||
GUILayout.Space(10);
|
||||
slot.tagList.DoLayoutList();
|
||||
if (GUI.changed)
|
||||
{
|
||||
slot.tags = slot.backingTags.ToArray();
|
||||
EditorUtility.SetDirty(slot);
|
||||
forceUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
(target as SlotDataAsset).eventsFoldout = EditorGUILayout.Foldout((target as SlotDataAsset).eventsFoldout, "Slot Events");
|
||||
GUILayout.EndHorizontal();
|
||||
if ((target as SlotDataAsset).eventsFoldout)
|
||||
{
|
||||
EditorGUILayout.PropertyField(CharacterBegun);
|
||||
if (!slot.isWildCardSlot)
|
||||
{
|
||||
EditorGUILayout.PropertyField(SlotAtlassed);
|
||||
EditorGUILayout.PropertyField(DNAApplied);
|
||||
EditorGUILayout.PropertyField(SlotBeginProcessing);
|
||||
EditorGUILayout.PropertyField(SlotProcessed);
|
||||
}
|
||||
EditorGUILayout.PropertyField(CharacterCompleted);
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
|
||||
slot.welldingFoldout = EditorGUILayout.Foldout(slot.welldingFoldout, "Welding");
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (slot.welldingFoldout)
|
||||
{
|
||||
#region WELDS
|
||||
|
||||
|
||||
selectedRaceIndex = EditorGUILayout.Popup("Select Base Slot by Race", selectedRaceIndex, foundRaceNames.ToArray());
|
||||
if (selectedRaceIndex <= 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Select a slot by race quickly, or use manual selection below", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
UMAData.UMARecipe baseRecipe = new UMAData.UMARecipe();
|
||||
foundRaces[selectedRaceIndex].baseRaceRecipe.Load(baseRecipe, UMAContextBase.Instance);
|
||||
|
||||
foreach (SlotData sd in baseRecipe.slotDataList)
|
||||
{
|
||||
if (sd != null && sd.asset != null)
|
||||
{
|
||||
if (GUILayout.Button(string.Format("{0} ({1})", sd.asset.name, sd.slotName)))
|
||||
{
|
||||
// UpdateSourceAsset(sd.asset);
|
||||
WeldToSlot = sd.asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.Space(12);
|
||||
|
||||
|
||||
|
||||
WeldToSlot = EditorGUILayout.ObjectField("Drop slot here to create weld", WeldToSlot, typeof(SlotDataAsset), false) as SlotDataAsset;
|
||||
|
||||
weldDistance = EditorGUILayout.FloatField("Weld Distance", weldDistance);
|
||||
CopyBoneWeights = EditorGUILayout.Toggle("Copy Boneweights", CopyBoneWeights);
|
||||
CopyNormals = EditorGUILayout.Toggle("Copy Normals", CopyNormals);
|
||||
AverageNormals = EditorGUILayout.Toggle("Average Normals", AverageNormals);
|
||||
blendshapeCopyMode = (UMA.SlotDataAsset.BlendshapeCopyMode)EditorGUILayout.EnumPopup("Blendshape Copy Mode", blendshapeCopyMode);
|
||||
|
||||
|
||||
GUILayout.Box("Warning! averaging normals will update both slots!", GUILayout.ExpandWidth(true));
|
||||
|
||||
SlotPreviewMode newPreviewMode = (SlotPreviewMode)EditorGUILayout.EnumPopup("Preview Mode", previewMode);
|
||||
if (meshToPreview != null)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Preview Vert", GUILayout.Width(100));
|
||||
int newpreviewVertex = EditorGUILayout.IntSlider(previewVertex, -1, meshToPreview.vertexCount - 1);
|
||||
if (newpreviewVertex != previewVertex)
|
||||
{
|
||||
previewVertex = newpreviewVertex;
|
||||
reConfigurePreview = true;
|
||||
}
|
||||
if (GUILayout.Button("Dump Vert", GUILayout.Width(50)))
|
||||
{
|
||||
ShowDebugVertInfo(previewVertex);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
Vector3 savedPreviewRotation = previewRotation;
|
||||
previewRotation = EditorGUILayout.Vector3Field("Preview Rotation", previewRotation);
|
||||
if (savedPreviewRotation != previewRotation)
|
||||
{
|
||||
reConfigurePreview = true;
|
||||
}
|
||||
if (newPreviewMode != previewMode)
|
||||
{
|
||||
reConfigurePreview = true;
|
||||
previewMode = newPreviewMode;
|
||||
}
|
||||
if (reConfigurePreview)
|
||||
{
|
||||
reConfigurePreview = false;
|
||||
if (MeshPreview != null)
|
||||
{
|
||||
MeshPreview.Dispose();
|
||||
MeshPreview = null;
|
||||
}
|
||||
if (meshToPreview != null)
|
||||
{
|
||||
DestroyImmediate(meshToPreview);
|
||||
meshToPreview = null;
|
||||
}
|
||||
meshToPreview = GetPreviewMesh();
|
||||
if (meshToPreview != null)
|
||||
{
|
||||
MeshPreview = new MeshPreview(meshToPreview);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MeshPreview != null)
|
||||
{
|
||||
MeshPreview.Dispose();
|
||||
MeshPreview = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WeldToSlot == null)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
}
|
||||
if (GUILayout.Button("Perform Weld"))
|
||||
{
|
||||
lastWeld = slot.CalculateWelds(WeldToSlot, CopyNormals, CopyBoneWeights, AverageNormals, weldDistance, blendshapeCopyMode);
|
||||
forceUpdate = true;
|
||||
}
|
||||
if (WeldToSlot == null)
|
||||
{
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
int lastWeldCount = 0;
|
||||
int lastWeldMismatch = 0;
|
||||
if (lastWeld != null)
|
||||
{
|
||||
lastWeldCount = lastWeld.WeldPoints.Count;
|
||||
lastWeldMismatch = lastWeld.MisMatchCount;
|
||||
}
|
||||
|
||||
if (lastWeld != null)
|
||||
{
|
||||
GUILayout.Label($"Last Weld: {lastWeldCount} points, {lastWeld.MisMatchCount} mismatches", GUILayout.ExpandWidth(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayout.Label($"Last Weld: None", GUILayout.ExpandWidth(true));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
foreach (var t in targets)
|
||||
{
|
||||
var slotDataAsset = t as SlotDataAsset;
|
||||
if (slotDataAsset != null)
|
||||
{
|
||||
if (slotDataAsset.animatedBoneHashes.Length != slotDataAsset.animatedBoneNames.Length)
|
||||
{
|
||||
slotDataAsset.animatedBoneHashes = new int[slotDataAsset.animatedBoneNames.Length];
|
||||
for (int i = 0; i < slotDataAsset.animatedBoneNames.Length; i++)
|
||||
{
|
||||
slotDataAsset.animatedBoneHashes[i] = UMASkeleton.StringToHash(slotDataAsset.animatedBoneNames[i]);
|
||||
}
|
||||
GUI.changed = true;
|
||||
EditorUtility.SetDirty(slotDataAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!slot.isWildCardSlot)
|
||||
{
|
||||
GUILayout.Space(20);
|
||||
Rect updateDropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
|
||||
GUI.Box(updateDropArea, "Drag SkinnedMeshRenderers here to update the slot meshData.");
|
||||
GUILayout.Space(10);
|
||||
UpdateSlotDropAreaGUI(updateDropArea);
|
||||
|
||||
GUILayout.Space(10);
|
||||
Rect boneDropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
|
||||
GUI.Box(boneDropArea, "Drag Bone Transforms here to add their names to the Animated Bone Names.\nSo the power tools will preserve them!");
|
||||
GUILayout.Space(10);
|
||||
AnimatedBoneDropAreaGUI(boneDropArea);
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (EditorGUI.EndChangeCheck() || forceUpdate)
|
||||
{
|
||||
EditorUtility.SetDirty(target);
|
||||
AssetDatabase.SaveAssetIfDirty(target);
|
||||
string path = AssetDatabase.GetAssetPath(target.GetInstanceID());
|
||||
AssetDatabase.ImportAsset(path);
|
||||
UMAUpdateProcessor.UpdateSlot(target as SlotDataAsset, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowDebugVertInfo(int previewVertex)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
slot.BuildVertexLookups(WeldToSlot);
|
||||
slot.BuildOurAndTheirBoneWeights(WeldToSlot);
|
||||
slot.BuildBoneLookups(WeldToSlot);
|
||||
|
||||
foreach (var bw in slot.OurBoneWeights[previewVertex])
|
||||
{
|
||||
string boneName = slot.meshData.umaBones[bw.boneIndex].name;
|
||||
sb.Append($"Bone {boneName}({bw.boneIndex}): Weight {bw.weight}");
|
||||
sb.Append(Environment.NewLine);
|
||||
}
|
||||
Debug.Log("Our vertex " + previewVertex + Environment.NewLine + sb.ToString());
|
||||
|
||||
int theirVertex = slot.OurVertextoTheirVertex[previewVertex];
|
||||
foreach (var bw in slot.TheirBoneWeights[theirVertex])
|
||||
{
|
||||
string boneName = WeldToSlot.meshData.umaBones[bw.boneIndex].name;
|
||||
sb.Append($"Bone {boneName}({bw.boneIndex}): Weight {bw.weight}");
|
||||
sb.Append(Environment.NewLine);
|
||||
}
|
||||
Debug.Log("Their vertex " + theirVertex + Environment.NewLine + sb.ToString());
|
||||
|
||||
}
|
||||
|
||||
public override void OnPreviewSettings()
|
||||
{
|
||||
if (MeshPreview == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
MeshPreview.OnPreviewSettings();
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Mesh GetPreviewMesh()
|
||||
{
|
||||
Quaternion pRot = Quaternion.Euler(previewRotation);
|
||||
if (previewMode == SlotPreviewMode.ThisSlot)
|
||||
{
|
||||
return SlotToMesh.ConvertSlotToMesh((target as SlotDataAsset),pRot, previewVertex);
|
||||
}
|
||||
if (previewMode == SlotPreviewMode.WeldSlot)
|
||||
{
|
||||
if (WeldToSlot != null)
|
||||
{
|
||||
return SlotToMesh.ConvertSlotToMesh(WeldToSlot, pRot, previewVertex);
|
||||
}
|
||||
}
|
||||
if (previewMode == SlotPreviewMode.BothSlots)
|
||||
{
|
||||
Mesh mesh = SlotToMesh.ConvertSlotToMesh((target as SlotDataAsset), pRot, previewVertex);
|
||||
if (WeldToSlot != null)
|
||||
{
|
||||
Mesh weldMesh = SlotToMesh.ConvertSlotToMesh(WeldToSlot, pRot, previewVertex);
|
||||
if (weldMesh != null)
|
||||
{
|
||||
CombineInstance[] combine = new CombineInstance[2];
|
||||
combine[0].mesh = mesh;
|
||||
combine[1].mesh = weldMesh;
|
||||
Mesh combinedMesh = new Mesh();
|
||||
combinedMesh.CombineMeshes(combine,false,false,false);
|
||||
DestroyImmediate(mesh);
|
||||
DestroyImmediate(weldMesh);
|
||||
return combinedMesh;
|
||||
}
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void OnInteractivePreviewGUI(Rect r, GUIStyle background)
|
||||
{
|
||||
if (meshToPreview == null)
|
||||
{
|
||||
meshToPreview = GetPreviewMesh();
|
||||
if (meshToPreview != null)
|
||||
{
|
||||
MeshPreview = new MeshPreview(meshToPreview);
|
||||
}
|
||||
}
|
||||
if (meshToPreview != null && MeshPreview != null)
|
||||
{
|
||||
MeshPreview.OnPreviewGUI(r, background);
|
||||
GUI.Label(r, MeshPreview.GetInfoString(meshToPreview));
|
||||
}
|
||||
}
|
||||
|
||||
private void AnimatedBoneDropAreaGUI(Rect dropArea)
|
||||
{
|
||||
GameObject obj = DropAreaGUI(dropArea);
|
||||
if (obj != null)
|
||||
{
|
||||
AddAnimatedBone(obj.name);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSlotDropAreaGUI(Rect dropArea)
|
||||
{
|
||||
GameObject obj = DropAreaGUI(dropArea);
|
||||
if (obj != null)
|
||||
{
|
||||
SkinnedMeshRenderer skinnedMesh = obj.GetComponent<SkinnedMeshRenderer>();
|
||||
if (skinnedMesh != null)
|
||||
{
|
||||
UpdateSlotData(slot.normalReferenceMesh, skinnedMesh);
|
||||
GUI.changed = true;
|
||||
EditorUtility.DisplayDialog("Complete", "Update completed","OK");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error", "No skinned mesh renderer found!", "Ok");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private GameObject DropAreaGUI(Rect dropArea)
|
||||
{
|
||||
var evt = Event.current;
|
||||
|
||||
if (evt.type == EventType.DragUpdated)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
}
|
||||
}
|
||||
|
||||
if (evt.type == EventType.DragPerform)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
UnityEngine.Object[] draggedObjects = DragAndDrop.objectReferences;
|
||||
for (int i = 0; i < draggedObjects.Length; i++)
|
||||
{
|
||||
if (draggedObjects[i])
|
||||
{
|
||||
var go = draggedObjects[i] as GameObject;
|
||||
if (go != null)
|
||||
{
|
||||
return go;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private void AddAnimatedBone(string animatedBone)
|
||||
{
|
||||
var hash = UMASkeleton.StringToHash(animatedBone);
|
||||
foreach (var t in targets)
|
||||
{
|
||||
var slotDataAsset = t as SlotDataAsset;
|
||||
if (slotDataAsset != null)
|
||||
{
|
||||
ArrayUtility.Add(ref slotDataAsset.animatedBoneNames, animatedBone);
|
||||
ArrayUtility.Add(ref slotDataAsset.animatedBoneHashes, hash);
|
||||
EditorUtility.SetDirty(slotDataAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void UpdateSlotData(SkinnedMeshRenderer seamsMesh, SkinnedMeshRenderer skinnedMesh)
|
||||
{
|
||||
SlotDataAsset slot = target as SlotDataAsset;
|
||||
|
||||
string existingRootBone = slot.meshData.RootBoneName;
|
||||
|
||||
UMASlotProcessingUtil.UpdateSlotData(slot, skinnedMesh, slot.material, seamsMesh, existingRootBone, true);
|
||||
string path = AssetDatabase.GetAssetPath(target.GetInstanceID());
|
||||
AssetDatabase.ImportAsset(path);
|
||||
UMAUpdateProcessor.UpdateSlot(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa545c66d8a0ebd41a74808abdf87f1a
|
||||
timeCreated: 1436106234
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/SlotDataAssetInspector.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,267 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
//using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
[CustomEditor(typeof(SlotLibrary))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SlotLibraryEditor : Editor
|
||||
{
|
||||
private SerializedObject m_Object;
|
||||
private SlotLibrary slotLibrary;
|
||||
private SerializedProperty m_SlotDataAssetCount;
|
||||
|
||||
private const string kArraySizePath = "slotElementList.Array.size";
|
||||
private const string kArrayData = "slotElementList.Array.data[{0}]";
|
||||
|
||||
private bool canUpdate;
|
||||
private bool isDirty;
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
m_Object = new SerializedObject(target);
|
||||
slotLibrary = m_Object.targetObject as SlotLibrary;
|
||||
m_SlotDataAssetCount = m_Object.FindProperty(kArraySizePath);
|
||||
}
|
||||
|
||||
|
||||
private SlotDataAsset[] GetSlotDataAssetArray()
|
||||
{
|
||||
|
||||
int arrayCount = m_SlotDataAssetCount.intValue;
|
||||
SlotDataAsset[] SlotDataAssetArray = new SlotDataAsset[arrayCount];
|
||||
|
||||
for (int i = 0; i < arrayCount; i++)
|
||||
{
|
||||
|
||||
SlotDataAssetArray[i] = m_Object.FindProperty(string.Format(kArrayData, i)).objectReferenceValue as SlotDataAsset;
|
||||
|
||||
}
|
||||
return SlotDataAssetArray;
|
||||
|
||||
}
|
||||
|
||||
private void SetSlotDataAsset(int index, SlotDataAsset slotElement)
|
||||
{
|
||||
m_Object.FindProperty(string.Format(kArrayData, index)).objectReferenceValue = slotElement;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
private SlotDataAsset GetSlotDataAssetAtIndex(int index)
|
||||
{
|
||||
return m_Object.FindProperty(string.Format(kArrayData, index)).objectReferenceValue as SlotDataAsset;
|
||||
}
|
||||
|
||||
private void AddSlotDataAsset(SlotDataAsset slotElement)
|
||||
{
|
||||
m_SlotDataAssetCount.intValue++;
|
||||
SetSlotDataAsset(m_SlotDataAssetCount.intValue - 1, slotElement);
|
||||
}
|
||||
|
||||
|
||||
private void RemoveSlotDataAssetAtIndex(int index)
|
||||
{
|
||||
|
||||
for (int i = index; i < m_SlotDataAssetCount.intValue - 1; i++)
|
||||
{
|
||||
|
||||
SetSlotDataAsset(i, GetSlotDataAssetAtIndex(i + 1));
|
||||
}
|
||||
|
||||
m_SlotDataAssetCount.intValue--;
|
||||
|
||||
}
|
||||
|
||||
private void DropAreaGUI(Rect dropArea)
|
||||
{
|
||||
|
||||
var evt = Event.current;
|
||||
|
||||
if (evt.type == EventType.DragUpdated)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||||
}
|
||||
}
|
||||
|
||||
if (evt.type == EventType.DragPerform)
|
||||
{
|
||||
if (dropArea.Contains(evt.mousePosition))
|
||||
{
|
||||
DragAndDrop.AcceptDrag();
|
||||
|
||||
UnityEngine.Object[] draggedObjects = DragAndDrop.objectReferences as UnityEngine.Object[];
|
||||
for (int i = 0; i < draggedObjects.Length; i++)
|
||||
{
|
||||
if (draggedObjects[i])
|
||||
{
|
||||
SlotDataAsset tempSlotDataAsset = draggedObjects[i] as SlotDataAsset;
|
||||
if (tempSlotDataAsset)
|
||||
{
|
||||
AddSlotDataAsset(tempSlotDataAsset);
|
||||
continue;
|
||||
}
|
||||
|
||||
var path = AssetDatabase.GetAssetPath(draggedObjects[i]);
|
||||
if (System.IO.Directory.Exists(path))
|
||||
{
|
||||
RecursiveScanFoldersForAssets(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RecursiveScanFoldersForAssets(string path)
|
||||
{
|
||||
var assetFiles = System.IO.Directory.GetFiles(path, "*.asset");
|
||||
foreach (var assetFile in assetFiles)
|
||||
{
|
||||
var tempSlotDataAsset = AssetDatabase.LoadAssetAtPath(assetFile, typeof(SlotDataAsset)) as SlotDataAsset;
|
||||
if (tempSlotDataAsset)
|
||||
{
|
||||
AddSlotDataAsset(tempSlotDataAsset);
|
||||
}
|
||||
}
|
||||
foreach (var subFolder in System.IO.Directory.GetDirectories(path))
|
||||
{
|
||||
RecursiveScanFoldersForAssets(subFolder.Replace('\\', '/'));
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
m_Object.Update();
|
||||
|
||||
GUILayout.Label("slotElementList", EditorStyles.boldLabel);
|
||||
|
||||
SlotDataAsset[] slotElementList = GetSlotDataAssetArray();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("Order by Name"))
|
||||
{
|
||||
canUpdate = false;
|
||||
|
||||
List<SlotDataAsset> SlotDataAssetTemp = new List<SlotDataAsset>();
|
||||
SlotDataAssetTemp.AddRange(slotElementList);
|
||||
|
||||
//Make sure there's no invalid data
|
||||
for (int i = 0; i < SlotDataAssetTemp.Count; i++)
|
||||
{
|
||||
if (SlotDataAssetTemp[i] == null)
|
||||
{
|
||||
SlotDataAssetTemp.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
SlotDataAssetTemp.Sort((x, y) => x.name.CompareTo(y.name));
|
||||
|
||||
for (int i = 0; i < SlotDataAssetTemp.Count; i++)
|
||||
{
|
||||
SetSlotDataAsset(i, SlotDataAssetTemp[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Update List"))
|
||||
{
|
||||
isDirty = true;
|
||||
canUpdate = false;
|
||||
}
|
||||
if (GUILayout.Button("Remove Duplicates"))
|
||||
{
|
||||
HashSet<SlotDataAsset> Slots = new HashSet<SlotDataAsset>();
|
||||
|
||||
foreach(SlotDataAsset osa in slotElementList)
|
||||
{
|
||||
Slots.Add(osa);
|
||||
}
|
||||
|
||||
List<SlotDataAsset> sd = new List<SlotDataAsset>();
|
||||
sd.AddRange(Slots);
|
||||
|
||||
m_SlotDataAssetCount.intValue = Slots.Count;
|
||||
for(int i=0;i<sd.Count;i++)
|
||||
{
|
||||
SetSlotDataAsset(i,sd[i]);
|
||||
}
|
||||
isDirty = true;
|
||||
canUpdate = false;
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.Space(20);
|
||||
Rect dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
|
||||
GUI.Box(dropArea, "Drag Slots here");
|
||||
GUILayout.Space(20);
|
||||
|
||||
|
||||
for (int i = 0; i < m_SlotDataAssetCount.intValue; i++)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
SlotDataAsset result = EditorGUILayout.ObjectField(slotElementList[i], typeof(SlotDataAsset), true) as SlotDataAsset;
|
||||
|
||||
if (GUI.changed && canUpdate)
|
||||
{
|
||||
SetSlotDataAsset(i, result);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("-", GUILayout.Width(20.0f)))
|
||||
{
|
||||
canUpdate = false;
|
||||
RemoveSlotDataAssetAtIndex(i);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
if (i == m_SlotDataAssetCount.intValue - 1)
|
||||
{
|
||||
canUpdate = true;
|
||||
|
||||
if (isDirty)
|
||||
{
|
||||
slotLibrary.UpdateDictionary();
|
||||
isDirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropAreaGUI(dropArea);
|
||||
|
||||
if (GUILayout.Button("Add SlotDataAsset"))
|
||||
{
|
||||
AddSlotDataAsset(null);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Clear List"))
|
||||
{
|
||||
m_SlotDataAssetCount.intValue = 0;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Remove Invalid Slot Data"))
|
||||
{
|
||||
RemoveInvalidSlotDataAsset(slotElementList);
|
||||
}
|
||||
|
||||
m_Object.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void RemoveInvalidSlotDataAsset(SlotDataAsset[] slotElementList)
|
||||
{
|
||||
for (int i = m_SlotDataAssetCount.intValue - 1; i >= 0; i--)
|
||||
{
|
||||
if (slotElementList[i] == null)
|
||||
{
|
||||
RemoveSlotDataAssetAtIndex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f21073907ba14d542bbf2979b8c0b1fb
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/SlotLibraryEditor.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,103 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
namespace UMA.Editors
|
||||
{
|
||||
public static class TPoseExtracter
|
||||
{
|
||||
[MenuItem("UMA/Extract T-Pose", priority = 30)]
|
||||
static void ExtractTPose()
|
||||
{
|
||||
var selectedObjects = Selection.objects;
|
||||
if (selectedObjects.Length > 0)
|
||||
{
|
||||
bool extracted = false;
|
||||
foreach (var selectedObject in selectedObjects)
|
||||
{
|
||||
var assetPath = AssetDatabase.GetAssetPath(selectedObject);
|
||||
|
||||
if (!string.IsNullOrEmpty(assetPath))
|
||||
{
|
||||
// Get asset path directory
|
||||
var assetDirectory = new FileInfo(assetPath).Directory.FullName + Path.DirectorySeparatorChar + "TPoses";
|
||||
|
||||
// Trim off the path at "Assets" to get the relative path to the assets directory
|
||||
assetDirectory = assetDirectory.Substring(assetDirectory.IndexOf("Assets"));
|
||||
|
||||
var modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter;
|
||||
if( modelImporter != null )
|
||||
{
|
||||
var asset = UmaTPose.CreateInstance<UMA.UmaTPose>();
|
||||
asset.ReadFromHumanDescription(modelImporter.humanDescription);
|
||||
var name = selectedObject.name;
|
||||
if (name.EndsWith("(Clone)"))
|
||||
{
|
||||
name = name.Substring(0, name.Length - 7);
|
||||
asset.boneInfo[0].name = name;
|
||||
asset.Serialize();
|
||||
}
|
||||
if (!Directory.Exists(assetDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(assetDirectory);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
AssetDatabase.CreateAsset(asset, assetDirectory + Path.DirectorySeparatorChar + name + "_TPose.asset");
|
||||
}
|
||||
catch (UnityException e)
|
||||
{
|
||||
Debug.Log(e.ToString());
|
||||
}
|
||||
extracted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (extracted)
|
||||
{
|
||||
AssetDatabase.SaveAssets();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var animator in Transform.FindObjectsOfType(typeof(Animator)) as Animator[])
|
||||
{
|
||||
var asset = UmaTPose.CreateInstance<UmaTPose>();
|
||||
asset.ReadFromTransform(animator);
|
||||
var name = animator.name;
|
||||
if (name.EndsWith("(Clone)"))
|
||||
{
|
||||
name = name.Substring(0, name.Length - 7);
|
||||
asset.boneInfo[0].name = name;
|
||||
asset.Serialize();
|
||||
}
|
||||
|
||||
// Default path
|
||||
string path = "Assets/UMA/Content/Generated/TPoses";
|
||||
|
||||
string[] inds = AssetDatabase.FindAssets("AssetIndexer t:umaassetindexer");
|
||||
if (inds.Length > 0)
|
||||
{
|
||||
// If UMA has moved, then move the pose path also.
|
||||
string tpath = AssetDatabase.GUIDToAssetPath(inds[0]);
|
||||
int pos = tpath.IndexOf("UMA/InternalDataStore", System.StringComparison.OrdinalIgnoreCase);
|
||||
string UMABase = tpath.Substring(0, pos) + "/UMA";
|
||||
path = UMABase + "Content/Generated/TPoses";
|
||||
}
|
||||
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
AssetDatabase.CreateAsset(asset, path+"/" + name + "_TPose.asset");
|
||||
EditorUtility.SetDirty(asset);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e54447f4162094e4db82ba88138aef85
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/TPoseExtracter.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,132 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using TypePair = System.Collections.Generic.KeyValuePair<System.Type, string>;
|
||||
using System.Diagnostics;
|
||||
|
||||
// Simple Editor Script that fills a bar in the given seconds.
|
||||
namespace UMA
|
||||
{
|
||||
public class TagsEditor : EditorWindow
|
||||
{
|
||||
public class ProcessItem
|
||||
{
|
||||
public delegate void Processor(ProcessItem p);
|
||||
public UnityEngine.Object oObj;
|
||||
public Processor pFunc;
|
||||
public TypePair typePair;
|
||||
|
||||
public ProcessItem(TypePair t, UnityEngine.Object o, Processor pFunc)
|
||||
{
|
||||
this.pFunc = pFunc;
|
||||
oObj = o;
|
||||
typePair = t;
|
||||
}
|
||||
|
||||
public void Process()
|
||||
{
|
||||
pFunc(this);
|
||||
}
|
||||
}
|
||||
|
||||
Queue<ProcessItem> Items = new Queue<ProcessItem>();
|
||||
float TotalItems;
|
||||
bool Cleanup = false;
|
||||
Stopwatch stopWatch = new Stopwatch();
|
||||
|
||||
[MenuItem("UMA/Tags Editor")]
|
||||
static void Init()
|
||||
{
|
||||
UnityEditor.EditorWindow window = GetWindow(typeof(TagsEditor), true);
|
||||
window.CenterOnMainWin();
|
||||
window.Show();
|
||||
}
|
||||
|
||||
public void LoadItemList(ProcessItem.Processor pFunc)
|
||||
{
|
||||
foreach(TypePair t in UMAEditorUtilities.FriendlyNames)
|
||||
{
|
||||
var objs = Resources.FindObjectsOfTypeAll(t.Key);
|
||||
if (objs == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var o in objs)
|
||||
{
|
||||
Items.Enqueue(new ProcessItem(t, o, pFunc));
|
||||
}
|
||||
}
|
||||
TotalItems = Items.Count;
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
GUILayout.Label("Warning: This can take a few minutes.");
|
||||
GUILayout.Label("The AssetDatabase will be refreshed");
|
||||
GUILayout.Label("when complete.");
|
||||
|
||||
if (GUILayout.Button("Set UMA Tags") && Items.Count == 0)
|
||||
{
|
||||
LoadItemList(SetItems);
|
||||
}
|
||||
if (GUILayout.Button("Clear UMA Tags") & Items.Count == 0)
|
||||
{
|
||||
LoadItemList(ClearItems);
|
||||
}
|
||||
if (Items.Count > 0)
|
||||
{
|
||||
stopWatch.Reset();
|
||||
stopWatch.Start();
|
||||
while (Items.Count > 0)
|
||||
{
|
||||
ProcessItem p = Items.Dequeue();
|
||||
p.Process();
|
||||
if (stopWatch.ElapsedMilliseconds > 100)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
stopWatch.Stop();
|
||||
if (Items.Count == 0)
|
||||
{
|
||||
Cleanup = true;
|
||||
EditorUtility.DisplayProgressBar("UMA Tags","Refreshing AssetDatabase" , 1.0f);
|
||||
}
|
||||
}
|
||||
if (Event.current.type == EventType.Layout && Cleanup)
|
||||
{
|
||||
Cleanup = false;
|
||||
AssetDatabase.SaveAssets();
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetItems(ProcessItem p)
|
||||
{
|
||||
string tagName = "UMA_" + p.typePair.Value;
|
||||
string[] currentLabels = AssetDatabase.GetLabels(p.oObj);
|
||||
ArrayUtility.Add<string>(ref currentLabels, tagName);
|
||||
AssetDatabase.SetLabels(p.oObj, currentLabels);
|
||||
EditorUtility.SetDirty(p.oObj);
|
||||
float progress = (TotalItems - Items.Count) / TotalItems;
|
||||
EditorUtility.DisplayProgressBar("UMA Tags", "Setting tag " +tagName+ " (" + Items.Count + ") "+p.oObj.name, progress);
|
||||
}
|
||||
|
||||
private void ClearItems(ProcessItem p)
|
||||
{
|
||||
string tagName = "UMA_" + p.typePair.Value;
|
||||
string[] currentLabels = AssetDatabase.GetLabels(p.oObj);
|
||||
ArrayUtility.Remove<string>(ref currentLabels, tagName);
|
||||
AssetDatabase.SetLabels(p.oObj, currentLabels);
|
||||
EditorUtility.SetDirty(p.oObj);
|
||||
float progress = (TotalItems-Items.Count) / TotalItems;
|
||||
EditorUtility.DisplayProgressBar("UMA Tags", "Clearing tag " + tagName + " (" + Items.Count + ") " + p.oObj.name, progress);
|
||||
}
|
||||
|
||||
void OnInspectorUpdate()
|
||||
{
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b68aa1e905736ca4bbe77d3ac0148c00
|
||||
labels:
|
||||
- UMA_Text
|
||||
timeCreated: 1528225562
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/TagsEditor.cs
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c12b0dbfb9e898346b7e8d3a214822b9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1 @@
|
||||
public System.Byte {0:FieldName};
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 282142572957fd948ade3bb2d1f2594f
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/Templates/UmaDnaChild_Byte_Fields_Fragment.cs.txt
|
||||
uploadId: 679826
|
||||
@@ -0,0 +1 @@
|
||||
res.{0:FieldName} = (System.Byte)(dna.{0:FieldName} * 255f+0.5f);
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 276fb4664da6b9843b88f11b50fca750
|
||||
TextScriptImporter:
|
||||
userData:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 35611
|
||||
packageName: UMA 2
|
||||
packageVersion: 2.13
|
||||
assetPath: Assets/UMA/Core/Editor/Scripts/Templates/UmaDnaChild_Byte_FromDna_Fragment.cs.txt
|
||||
uploadId: 679826
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user