squash commits

This commit is contained in:
2025-01-07 18:54:46 +02:00
parent 855639487b
commit 62c0a21987
3632 changed files with 708443 additions and 999 deletions
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 8494881a820ea8f44b8784d1d1b8b578
folderAsset: yes
timeCreated: 1551121645
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,200 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UMA.CharacterSystem;
namespace UMA.Editors
{
[CustomEditor(typeof(DCARendererManager))]
public class DCARendererManagerEditor : Editor
{
SerializedProperty RendererElements;
SerializedProperty showHelp;
SerializedProperty State;
DynamicCharacterAvatar avatar;
UMAData.UMARecipe umaRecipe = new UMAData.UMARecipe();
List<string> wardrobeOptions = new List<string>();
List<SlotDataAsset> slotOptions = new List<SlotDataAsset>();
UMAContextBase context;
RaceData currentRaceData;
void OnEnable()
{
State = serializedObject.FindProperty("RenderersEnabled");
RendererElements = serializedObject.FindProperty("RendererElements");
showHelp = serializedObject.FindProperty("showHelp");
context = UMAContextBase.Instance;
DCARendererManager manager = target as DCARendererManager;
avatar = manager.GetComponent<DynamicCharacterAvatar>();
UpdateOptions();
}
public override void OnInspectorGUI()
{
serializedObject.Update();
GUILayout.Space(10);
State.boolValue = EditorGUILayout.Toggle("Renderers Enabled", State.boolValue);
showHelp.boolValue = EditorGUILayout.Toggle("Show Help", showHelp.boolValue);
if (showHelp.boolValue)
{
EditorGUILayout.HelpBox("This manager will create skinned mesh renderers for each Renderer Asset in each Renderer Element Set." +
"\n\n For each slot Asset listed that is found in the generating UMA, it will be separated onto each RendererAsset listed (so there could be duplicates created for various effects)." +
"\n\n This manager will also look for any slots assigned to any list Wardrobe slots and perform the same separation.", MessageType.Info);
}
if (avatar != null && avatar.activeRace != null && avatar.activeRace.data != null)
{
if (currentRaceData != avatar.activeRace.data)
{
UpdateOptions();
}
}
GUILayout.Space(10);
if(GUILayout.Button("Add New Renderer Element Set"))
{
RendererElements.arraySize++;
ClearRendererElement(RendererElements.GetArrayElementAtIndex(RendererElements.arraySize - 1));
}
GUILayout.Space(20);
for(int i = RendererElements.arraySize - 1; i >= 0; i--)
{
EditorGUILayout.BeginVertical("HelpBox");
SerializedProperty wardrobeSlots = RendererElements.GetArrayElementAtIndex(i).FindPropertyRelative("wardrobeSlots");
SerializedProperty slotAssets = RendererElements.GetArrayElementAtIndex(i).FindPropertyRelative("slotAssets");
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Renderer Element " + i.ToString());
if(GUILayout.Button("Remove"))
{
RendererElements.DeleteArrayElementAtIndex(i);
continue;
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
EditorGUI.indentLevel++;
SerializedProperty rendererAssets = RendererElements.GetArrayElementAtIndex(i).FindPropertyRelative("rendererAssets");
EditorGUILayout.PropertyField(rendererAssets, true);
GUILayout.Space(10);
bool disable = false;
if (rendererAssets.arraySize <= 0)
{
disable = true;
EditorGUILayout.HelpBox("An UMARendererAsset needs to be assigned!", MessageType.Error);
GUILayout.Space(10);
}
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginDisabledGroup(disable);
int newSlot = EditorGUILayout.Popup(0, SlotOptionsToArray(slotOptions));
if (newSlot > 0)
{
slotAssets.arraySize++;
slotAssets.GetArrayElementAtIndex(slotAssets.arraySize - 1).objectReferenceValue = slotOptions[newSlot - 1];
}
int newWardrobe = EditorGUILayout.Popup(0, wardrobeOptions.ToArray());
if (newWardrobe > 0)
{
if (!ArrayContains(wardrobeSlots, wardrobeOptions[newWardrobe]))
{
wardrobeSlots.arraySize++;
wardrobeSlots.GetArrayElementAtIndex(wardrobeSlots.arraySize - 1).stringValue = wardrobeOptions[newWardrobe];
}
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
EditorGUILayout.PropertyField(slotAssets, true);
EditorGUILayout.PropertyField(wardrobeSlots, true);
EditorGUI.indentLevel--;
bool unassigned = false;
for(int k = 0; k < rendererAssets.arraySize; k++)
{
if(rendererAssets.GetArrayElementAtIndex(k).objectReferenceValue == null)
{
unassigned = true;
break;
}
}
if(unassigned)
{
EditorGUILayout.HelpBox("There are unassigned UMARendererAssets!", MessageType.Error);
}
EditorGUILayout.EndVertical();
GUILayout.Space(10);
}
serializedObject.ApplyModifiedProperties();
}
private bool ArrayContains(SerializedProperty array, string item)
{
for(int i = 0; i < array.arraySize; i++)
{
if (array.GetArrayElementAtIndex(i).stringValue == item)
{
return true;
}
}
return false;
}
private string[] SlotOptionsToArray(List<SlotDataAsset> assets)
{
string[] options = new string[assets.Count + 1];
options[0] = "Add Base Slot";
for (int i = 0; i < assets.Count; i++)
{
options[i+1] = assets[i].slotName;
}
return options;
}
private void ClearRendererElement(SerializedProperty element)
{
element.FindPropertyRelative("rendererAssets").ClearArray();
element.FindPropertyRelative("slotAssets").ClearArray();
element.FindPropertyRelative("wardrobeSlots").ClearArray();
}
private void UpdateOptions()
{
wardrobeOptions.Clear();
slotOptions.Clear();
if (avatar != null && avatar.activeRace != null && avatar.activeRace.data != null)
{
#if UMA_ADDRESSABLES
if (avatar.AddressableBuildPending)
return;
#endif
currentRaceData = avatar.activeRace.data;
wardrobeOptions.AddRange(avatar.activeRace.data.wardrobeSlots);
wardrobeOptions.Insert(0, "Add Wardrobe Slot");
avatar.activeRace.data.baseRaceRecipe.Load(umaRecipe, context);
for (int i = 0; i < umaRecipe.slotDataList.Length; i++)
{
if (umaRecipe.slotDataList[i] != null && umaRecipe.slotDataList[i].asset != null)
{
slotOptions.Add(umaRecipe.slotDataList[i].asset);
}
}
}
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: cb41b2212ff313741b413f72213d4a94
timeCreated: 1549085612
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/Extensions/DynamicCharacterSystem/DCARendererManagerEditor.cs
uploadId: 679826
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 5df61864395acd643a2f1cff5147b8db
timeCreated: 1458326136
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/Extensions/DynamicCharacterSystem/DynamicCharacterAvatarEditor.cs
uploadId: 679826
@@ -0,0 +1,46 @@
using UnityEngine;
using UnityEditor;
namespace UMA.CharacterSystem.Editors
{
[CustomEditor(typeof(DynamicCharacterSystem))]
[CanEditMultipleObjects]
public class DynamicCharacterSystemEditor : Editor
{
private SerializedObject m_Object;
private DynamicCharacterSystem dCharacterSystem;
string recipeInBundleToFind = "";
public void OnEnable()
{
m_Object = new SerializedObject(target);
dCharacterSystem = m_Object.targetObject as DynamicCharacterSystem;
}
public override void OnInspectorGUI(){
Editor.DrawPropertiesExcluding (serializedObject, new string[] { "dynamicallyAddFromResources", "resourcesCharactersFolder", "resourcesRecipesFolder", "dynamicallyAddFromAssetBundles", "assetBundlesForCharactersToSearch", "assetBundlesForRecipesToSearch", "addAllRecipesFromDownloadedBundles" });
serializedObject.ApplyModifiedProperties ();
SerializedProperty dynamicallyAddFromResources = serializedObject.FindProperty("dynamicallyAddFromResources");
SerializedProperty dynamicallyAddFromAssetBundles = serializedObject.FindProperty("dynamicallyAddFromAssetBundles");
SerializedProperty addAllRecipesFromDownloadedBundles = serializedObject.FindProperty("addAllRecipesFromDownloadedBundles");
dynamicallyAddFromResources.boolValue = EditorGUILayout.ToggleLeft(new GUIContent(" Dynamically add from Global Library", "If true this library will dynamically add any assets you have checked on in the UMA Global Library or which you have put in a Resources folder"), dynamicallyAddFromResources.boolValue);
EditorGUILayout.PropertyField(serializedObject.FindProperty("resourcesCharactersFolder"), new GUIContent("Global Library Characters Folder Filter", "Limit the Global Library search to the following folders (no starting slash and seperate multiple entries with a comma)"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("resourcesRecipesFolder"), new GUIContent("Global Library Recipes Folder Filter", "Limit the Global Library search to the following folders (no starting slash and seperate multiple entries with a comma)"));
dynamicallyAddFromAssetBundles.boolValue = EditorGUILayout.ToggleLeft(" Dynamically add from AssetBundles", dynamicallyAddFromAssetBundles.boolValue);
EditorGUILayout.PropertyField(serializedObject.FindProperty("assetBundlesForCharactersToSearch"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("assetBundlesForRecipesToSearch"));
addAllRecipesFromDownloadedBundles.boolValue = EditorGUILayout.ToggleLeft(new GUIContent(" Add all recipes from downloaded bundles", "If true will automatically scan and add all Recipes from any downloaded bundles."), addAllRecipesFromDownloadedBundles.boolValue);
serializedObject.ApplyModifiedProperties();
if (Application.isPlaying && dynamicallyAddFromAssetBundles.boolValue) {
EditorGUILayout.Space ();
recipeInBundleToFind = EditorGUILayout.TextField (recipeInBundleToFind);
if (GUILayout.Button ("Find Recipes's AssetBundle")) {
if (recipeInBundleToFind != "")
{
dCharacterSystem.GetOriginatingAssetBundle (recipeInBundleToFind);
}
}
}
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 4d1f15a1f76dcd243bba0280610ec5eb
timeCreated: 1458877340
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/Extensions/DynamicCharacterSystem/DynamicCharacterSystemEditor.cs
uploadId: 679826
@@ -0,0 +1,57 @@
using UnityEngine;
using UnityEditor;
using UMA.Editors;
namespace UMA.CharacterSystem.Editors
{
[CustomEditor(typeof(DynamicOverlayLibrary))]
[CanEditMultipleObjects]
public class DynamicOverlayLibraryEditor : OverlayLibraryEditor
{
//extra fields for Dynamic version;
private DynamicOverlayLibrary dOverlayLibrary;
public SerializedProperty dynamicallyAddFromResources;
public SerializedProperty resourcesFolderPath;
public SerializedProperty dynamicallyAddFromAssetBundles;
public SerializedProperty assetBundleNamesToSearch;
string overlayInBundleToFind = "";
public new void OnEnable()
{
dOverlayLibrary = base.serializedObject.targetObject as DynamicOverlayLibrary;
base.OnEnable();
dynamicallyAddFromResources = serializedObject.FindProperty("dynamicallyAddFromResources");
resourcesFolderPath = serializedObject.FindProperty("resourcesFolderPath");
dynamicallyAddFromAssetBundles = serializedObject.FindProperty("dynamicallyAddFromAssetBundles");
assetBundleNamesToSearch = serializedObject.FindProperty("assetBundleNamesToSearch");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
//add our extra DynamicOverlayLibrary bits
dynamicallyAddFromResources.boolValue = GUILayout.Toggle(dynamicallyAddFromResources.boolValue ? true : false, new GUIContent(" Dynamically add from Global Library", "If true this library will dynamically add any assets you have checked on in the UMA Global Library or which you have put in a Resources folder"));
GUILayout.BeginHorizontal();
GUILayout.Label(new GUIContent("Global Library Folder Filter", "Limit the Global Library search to the following folders (no starting slash and seperate multiple entries with a comma)"), GUILayout.Width(135));
resourcesFolderPath.stringValue = GUILayout.TextField(resourcesFolderPath.stringValue);
GUILayout.EndHorizontal();
dynamicallyAddFromAssetBundles.boolValue = GUILayout.Toggle(dynamicallyAddFromAssetBundles.boolValue ? true : false, " Dynamically add from AssetBundles");
GUILayout.BeginHorizontal();
GUILayout.Label("AssetBundles to Search", GUILayout.Width(135));
assetBundleNamesToSearch.stringValue = GUILayout.TextField(assetBundleNamesToSearch.stringValue);
GUILayout.EndHorizontal();
if (Application.isPlaying && dynamicallyAddFromAssetBundles.boolValue)
{
EditorGUILayout.Space();
overlayInBundleToFind = EditorGUILayout.TextField(overlayInBundleToFind);
if (GUILayout.Button("Find Overlay's AssetBundle"))
{
if (overlayInBundleToFind != "")
dOverlayLibrary.GetOriginatingAssetBundle(overlayInBundleToFind);
}
}
serializedObject.ApplyModifiedProperties();
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 93127f4f597e2e742b3d40547e5d3551
timeCreated: 1457021374
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/Extensions/DynamicCharacterSystem/DynamicOverlayLibraryEditor.cs
uploadId: 679826
@@ -0,0 +1,51 @@
using UnityEngine;
using UnityEditor;
namespace UMA.CharacterSystem.Editors
{
[CustomEditor(typeof(DynamicRaceLibrary))]
[CanEditMultipleObjects]
public class DynamicRaceLibraryEditor : Editor
{
private SerializedObject m_Object;
private DynamicRaceLibrary raceLibrary;
string raceInBundleToFind = "";
public void OnEnable()
{
m_Object = new SerializedObject(target);
raceLibrary = m_Object.targetObject as DynamicRaceLibrary;
}
public override void OnInspectorGUI()
{
Editor.DrawPropertiesExcluding(serializedObject, new string[] { "dynamicallyAddFromResources", "resourcesFolderPath", "dynamicallyAddFromAssetBundles", "assetBundleNamesToSearch" });
serializedObject.ApplyModifiedProperties();
SerializedProperty dynamicallyAddFromResources = serializedObject.FindProperty("dynamicallyAddFromResources");
SerializedProperty dynamicallyAddFromAssetBundles = serializedObject.FindProperty("dynamicallyAddFromAssetBundles");
EditorGUI.BeginChangeCheck();
dynamicallyAddFromResources.boolValue = EditorGUILayout.ToggleLeft(new GUIContent(" Dynamically add from Global Library", "If true this library will dynamically add any assets you have checked on in the UMA Global Library or which you have put in a Resources folder"), dynamicallyAddFromResources.boolValue);
EditorGUILayout.PropertyField(serializedObject.FindProperty("resourcesFolderPath"), new GUIContent("Global Library Folder Filter", "Limit the Global Library search to the following folders (no starting slash and seperate multiple entries with a comma)"));
dynamicallyAddFromAssetBundles.boolValue = EditorGUILayout.ToggleLeft(" Dynamically Add From Asset Bundles", dynamicallyAddFromAssetBundles.boolValue);
EditorGUILayout.PropertyField(serializedObject.FindProperty("assetBundleNamesToSearch"), new GUIContent("AssetBundles to Search"));
if (EditorGUI.EndChangeCheck())
{
raceLibrary.ClearEditorAddedAssets();
serializedObject.ApplyModifiedProperties();
}
if (Application.isPlaying && dynamicallyAddFromAssetBundles.boolValue)
{
EditorGUILayout.Space();
raceInBundleToFind = EditorGUILayout.TextField(raceInBundleToFind);
if (GUILayout.Button("Find Races's AssetBundle"))
{
if (raceInBundleToFind != "")
{
raceLibrary.GetOriginatingAssetBundle(raceInBundleToFind);
}
}
}
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 8c11be64295c3464b998e092c95eb2a5
timeCreated: 1458874007
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/Extensions/DynamicCharacterSystem/DynamicRaceLibraryEditor.cs
uploadId: 679826
@@ -0,0 +1,57 @@
using UnityEngine;
using UnityEditor;
using UMA.Editors;
namespace UMA.CharacterSystem.Editors
{
[CustomEditor(typeof(DynamicSlotLibrary))]
[CanEditMultipleObjects]
public class DynamicSlotLibraryEditor : SlotLibraryEditor
{
//extra fields for Dynamic version;
private DynamicSlotLibrary dSlotLibrary;
public SerializedProperty dynamicallyAddFromResources;
public SerializedProperty resourcesFolderPath;
public SerializedProperty dynamicallyAddFromAssetBundles;
public SerializedProperty assetBundleNamesToSearch;
string slotInBundleToFind = "";
public new void OnEnable()
{
dSlotLibrary = base.serializedObject.targetObject as DynamicSlotLibrary;
base.OnEnable();
//extra for dVersion
dynamicallyAddFromResources = serializedObject.FindProperty("dynamicallyAddFromResources");
resourcesFolderPath = serializedObject.FindProperty("resourcesFolderPath");
dynamicallyAddFromAssetBundles = serializedObject.FindProperty("dynamicallyAddFromAssetBundles");
assetBundleNamesToSearch = serializedObject.FindProperty("assetBundleNamesToSearch");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
//add our extra DynamicOverlayLibrary bits
dynamicallyAddFromResources.boolValue = GUILayout.Toggle(dynamicallyAddFromResources.boolValue ? true : false, new GUIContent(" Dynamically add from Global Library", "If true this library will dynamically add any assets you have checked on in the UMA Global Library or which you have put in a Resources folder"));
GUILayout.BeginHorizontal();
GUILayout.Label(new GUIContent("Global Library Folder Filter", "Limit the Global Library search to the following folders (no starting slash and seperate multiple entries with a comma)"), GUILayout.Width(135));
resourcesFolderPath.stringValue = GUILayout.TextField(resourcesFolderPath.stringValue);
GUILayout.EndHorizontal();
dynamicallyAddFromAssetBundles.boolValue = GUILayout.Toggle(dynamicallyAddFromAssetBundles.boolValue ? true : false, " Dynamically add from AssetBundles");
GUILayout.BeginHorizontal();
GUILayout.Label("AssetBundles to Search", GUILayout.Width(135));
assetBundleNamesToSearch.stringValue = GUILayout.TextField(assetBundleNamesToSearch.stringValue);
GUILayout.EndHorizontal();
if (Application.isPlaying && dynamicallyAddFromAssetBundles.boolValue)
{
EditorGUILayout.Space();
slotInBundleToFind = EditorGUILayout.TextField(slotInBundleToFind);
if (GUILayout.Button("Find Slot's AssetBundle"))
{
if (slotInBundleToFind != "")
dSlotLibrary.GetOriginatingAssetBundle(slotInBundleToFind);
}
}
serializedObject.ApplyModifiedProperties();
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: da5436c67b6c90b4c81b356ca316913a
timeCreated: 1456951130
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/Extensions/DynamicCharacterSystem/DynamicSlotLibraryEditor.cs
uploadId: 679826
@@ -0,0 +1,522 @@
#if UNITY_EDITOR
using UnityEngine;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UMA.Editors;
namespace UMA.CharacterSystem.Editors
{
[CustomEditor(typeof(DynamicUMADnaAsset))]
public class DynamicUMADnaAssetEditor : Editor
{
//The DNAAsset field now has an 'Inspect' button, that shows the asset in a popup window. This makes it easier for users to change their dna names
//without loosing track of the behaviour they were inspecting that uses the DNA Asset (particularly helpful when customizing in playmode)
private static DynamicUMADnaAssetEditor _livePopupEditor;
private string newDNAName = "";
private bool canAddNewDNAName = false;
int DNAAssetPickerID = 0;
List<string> dnaNamesAddOpts = new List<string> { "Add", "Replace" };
int selectedAddMethod = 0;
bool editTypeHashEnabled = false;
bool _importToolsExpanded = false;
bool _helpIsExpanded = false;
private Texture2D _importIcon;
private GUIStyle _importStyle;
private GUIContent _importContent;
private Texture2D importIcon
{
get
{
if (_importIcon != null)
{
return _importIcon;
}
//Chek EditorStyles has been set up
if (EditorStyles.label == null)
{
return new Texture2D(4, 4);
}
_importIcon = EditorGUIUtility.FindTexture("CollabPull");
return _importIcon;
}
}
private GUIContent importContent
{
get
{
_importStyle= new GUIStyle();
if (_importContent != null && _importIcon != null)
{
return _importContent;
}
//Check EditorStyles has been set up
if (EditorStyles.label == null)
{
return new GUIContent("", "Show Import Names Tools");
}
_importContent = new GUIContent("", "Show Import Names Tools");
_importStyle = new GUIStyle(EditorStyles.label);
_importStyle.fixedHeight = importIcon.height + 4f;
_importStyle.contentOffset = new Vector2(-4f, -0f);
_importContent.image = _importIcon;
return _importContent;
}
}
//this is an array because the help drawer takes an array so it can draw multiple paragraphs
string[] _help = new string[]
{
"The 'Dynamic DNA Asset' defines the dna 'names' that 'DNA Converters' can use to make changes to your character.",
};
string _dnaTypehashTooltip = "The 'DNA Type Hash' is a unique identifier for this collection of names, you should only edit it if you find that you are having dna collisions that you will be notified about.";
bool initialized = false;
ReorderableList _dnaNameList;
List<int> _removeList = new List<int>();
DynamicUMADnaAsset thisDUDA;
public static DynamicUMADnaAssetEditor livePopupEditor
{
get { return _livePopupEditor; }
}
public static void SetLivePopupEditor(DynamicUMADnaAssetEditor liveDUDAEditor)
{
if (Application.isPlaying)
{
_livePopupEditor = liveDUDAEditor;
}
}
public void Init()
{
thisDUDA = target as DynamicUMADnaAsset;
//check the ID and paths
bool doUpdate = thisDUDA.SetCurrentAssetPath();
if (doUpdate)
{
serializedObject.Update();
}
initialized = true;
}
private void OnEnable()
{
_dnaNameList = new ReorderableList(serializedObject, serializedObject.FindProperty("Names"), true, true, false, false);
_dnaNameList.drawElementCallback = DrawElementCallback;
_dnaNameList.drawHeaderCallback = DrawHeaderCallback;
_dnaNameList.headerHeight = 0f;
_dnaNameList.footerHeight = (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 2);
//we really want the add names controls to go in a modified footer for the list for consistancy
_dnaNameList.drawFooterCallback = DrawFooterCallback;
}
public override void OnInspectorGUI()
{
if (!initialized)
{
Init();
}
serializedObject.Update();
GUILayout.Space(5);
var dnaHeaderRect = EditorGUILayout.GetControlRect();
GUIHelper.ToolbarStyleHeader(dnaHeaderRect, new GUIContent("DYNAMIC DNA"), _help, ref _helpIsExpanded);
var importBtnRect = new Rect(dnaHeaderRect.xMax -40f, dnaHeaderRect.yMin + 2f, 20f, EditorGUIUtility.singleLineHeight);
_importToolsExpanded = GUI.Toggle(importBtnRect, _importToolsExpanded, importContent, _importStyle);
GUIHelper.BeginVerticalPadded(3, new Color(0.75f, 0.875f, 1f, 0.3f));
SerializedProperty Names = serializedObject.FindProperty("Names");
if (_importToolsExpanded)
{
DrawImportNamesTools();
}
SerializedProperty dnaTypeHash = serializedObject.FindProperty("dnaTypeHash");
Rect hashEditorRect = GUILayoutUtility.GetRect(0.0f, EditorGUIUtility.singleLineHeight, GUILayout.ExpandWidth(true));
var hashLabelRect = hashEditorRect;
hashLabelRect.xMax = hashEditorRect.xMax / 3;
var hashBtnRect = hashEditorRect;
hashBtnRect.xMin = hashLabelRect.xMax + (EditorGUI.indentLevel * 20);
hashBtnRect.xMax = hashBtnRect.xMin + 50 + (EditorGUI.indentLevel * 20);
var hashFieldRect = hashEditorRect;
hashFieldRect.xMin = hashBtnRect.xMax - ((EditorGUI.indentLevel * 20) - 10);
if (editTypeHashEnabled)
{
EditorGUI.LabelField(hashLabelRect, new GUIContent(dnaTypeHash.displayName, _dnaTypehashTooltip));
if (GUI.Button(hashBtnRect, new GUIContent("Save", _dnaTypehashTooltip)))
{
editTypeHashEnabled = false;
}
var originalDnaTypeHash = dnaTypeHash;
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(hashFieldRect, dnaTypeHash, new GUIContent(""));
if (EditorGUI.EndChangeCheck())
{
//we MUST NOT let this have the same TypeHash as UMADnaHumanoid or UMADnaTutorial, so if people randomly choose that value- dont assign it
if (dnaTypeHash.intValue == UMAUtils.StringToHash("UMADnaHumanoid") || dnaTypeHash.intValue == UMAUtils.StringToHash("UMADnaTutorial"))
{
Debug.LogWarning("You are trying to set a DynamicDNA to the same hash as a UMADnaHumanoid or UMADnaTutorial dna- this is not allowed");
dnaTypeHash = originalDnaTypeHash;
}
else
{
serializedObject.ApplyModifiedProperties();
}
}
}
else
{
EditorGUI.LabelField(hashLabelRect, new GUIContent(dnaTypeHash.displayName, _dnaTypehashTooltip));
if (GUI.Button(hashBtnRect, new GUIContent("Edit", _dnaTypehashTooltip)))
{
if (EditorUtility.DisplayDialog("Really Change the Hash?", "If you change the DNA Assets hash, any recipes that use this DNA will need to be inspected so they update to the new value. Are you sure?", "Yes", "Cancel"))
{
editTypeHashEnabled = true;
}
}
EditorGUI.BeginDisabledGroup(true);
EditorGUI.PropertyField(hashFieldRect, dnaTypeHash, new GUIContent(""));
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.Space();
if (Names.arraySize == 0)
{
EditorGUILayout.HelpBox("Define your the names for your dna by adding them below", MessageType.Info);
}
//ACTUAL NAMES LIST
_dnaNameList.DoLayoutList();
//ADD NEW NAME BUTTON
//Moved Add controls into the Reorderable list footer for consistancy
//message that the name exists
if (!string.IsNullOrEmpty(newDNAName) && canAddNewDNAName == false)
{
EditorGUILayout.HelpBox("That name is already in use.", MessageType.Warning);
}
//Clear out indices that have been added to the remove list.
for (int i = _dnaNameList.serializedProperty.arraySize - 1; i >= 0; i--)
{
if (_removeList.Contains(i))
{
_dnaNameList.serializedProperty.DeleteArrayElementAtIndex(i);
}
}
_removeList.Clear();
serializedObject.ApplyModifiedProperties();
GUIHelper.EndVerticalPadded(3);
GUILayout.Space(5);
}
private void DrawImportNamesTools()
{
SerializedProperty Names = serializedObject.FindProperty("Names");
//drop area for importing names from other dna assets
var dropArea = GUILayoutUtility.GetRect(0.0f, 60.0f, GUILayout.ExpandWidth(true));
dropArea.xMin = dropArea.xMin + (EditorGUI.indentLevel * 15);
GUI.Box(dropArea, "Drag DynamicUMADNAAssets here to import their names. Click to pick.");
var AddMethods = new GUIContent[dnaNamesAddOpts.Count];
for (int i = 0; i < dnaNamesAddOpts.Count; i++)
{
AddMethods[i] = new GUIContent(dnaNamesAddOpts[i]);
}
Rect selectedAddMethodRect = dropArea;
selectedAddMethodRect.yMin = dropArea.yMax - EditorGUIUtility.singleLineHeight - 5;
selectedAddMethodRect.xMin = dropArea.xMin - ((EditorGUI.indentLevel * 10) - 10);
selectedAddMethodRect.xMax = dropArea.xMax - ((EditorGUI.indentLevel * 10) + 10);
selectedAddMethod = EditorGUI.Popup(selectedAddMethodRect, new GUIContent("On Import", "Choose whether to 'Add' the names to the current list, or 'Replace' the names with the new list"), selectedAddMethod, AddMethods);
var namesList = new List<string>(Names.arraySize);
for (int i = 0; i < Names.arraySize; i++)
{
namesList.Add(Names.GetArrayElementAtIndex(i).stringValue);
}
ImportDNADropArea(dropArea, namesList, selectedAddMethod);
EditorGUILayout.Space();
//Add Defaults Buttons
Rect defaultsButRect = GUILayoutUtility.GetRect(0.0f, EditorGUIUtility.singleLineHeight, GUILayout.ExpandWidth(true));
defaultsButRect.xMin = defaultsButRect.xMin + (EditorGUI.indentLevel * 15);
if (GUI.Button(defaultsButRect, new GUIContent("Import Default Names", "Adds the default names as used by UMA Human Male DNA")))
{
AddDefaultNames();
}
EditorGUILayout.Space();
}
private void ImportDNADropArea(Rect dropArea, List<string> dnaNames, int addMethod)
{
Event evt = Event.current;
//make the box clickable so that the user can select DynamicUMADnaAsset assets from the asset selection window
if (evt.type == EventType.MouseUp)
{
if (dropArea.Contains(evt.mousePosition))
{
DNAAssetPickerID = EditorGUIUtility.GetControlID(new GUIContent("DNAAssetPicker"), FocusType.Passive);
EditorGUIUtility.ShowObjectPicker<DynamicUMADnaAsset>(null, false, "", DNAAssetPickerID);
Event.current.Use();//stops the Mismatched LayoutGroup errors
return;
}
}
if (evt.commandName == "ObjectSelectorUpdated" && EditorGUIUtility.GetObjectPickerControlID() == DNAAssetPickerID)
{
DynamicUMADnaAsset tempDnaAsset = EditorGUIUtility.GetObjectPickerObject() as DynamicUMADnaAsset;
if (tempDnaAsset)
{
AddDNANames(tempDnaAsset, dnaNames, addMethod);
}
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])
{
DynamicUMADnaAsset tempDnaAsset = draggedObjects[i] as DynamicUMADnaAsset;
if (tempDnaAsset)
{
AddDNANames(tempDnaAsset, dnaNames, addMethod);
continue;
}
var path = AssetDatabase.GetAssetPath(draggedObjects[i]);
if (System.IO.Directory.Exists(path))
{
RecursiveScanFoldersForAssets(path, dnaNames, addMethod);
}
}
}
}
}
}
private void RecursiveScanFoldersForAssets(string path, List<string> dnaNames, int addMethod)
{
var assetFiles = System.IO.Directory.GetFiles(path, "*.asset");
for (int i = 0; i < assetFiles.Length; i++)
{
string assetFile = assetFiles[i];
var tempDNAAsset = AssetDatabase.LoadAssetAtPath(assetFile, typeof(DynamicUMADnaAsset)) as DynamicUMADnaAsset;
if (tempDNAAsset)
{
AddDNANames(tempDNAAsset, dnaNames, addMethod);
}
}
string[] array = System.IO.Directory.GetDirectories(path);
for (int i = 0; i < array.Length; i++)
{
string subFolder = array[i];
RecursiveScanFoldersForAssets(subFolder.Replace('\\', '/'), dnaNames, addMethod);
}
}
private void AddDNANames(DynamicUMADnaAsset tempDNAAsset, List<string> dnaNames, int addMethod)
{
List<string> newNames = addMethod == 0 ? new List<string>(dnaNames) : new List<string>();
for (int i = 0; i < tempDNAAsset.Names.Length; i++)
{
if (!newNames.Contains(tempDNAAsset.Names[i]))
{
newNames.Add(tempDNAAsset.Names[i]);
}
}
dnaNames = newNames;
(target as DynamicUMADnaAsset).Names = dnaNames.ToArray();
}
private void DrawElementCallback(Rect rect, int index, bool isActive, bool isFocused)
{
SerializedProperty element = _dnaNameList.serializedProperty.GetArrayElementAtIndex(index);
rect.y += 2;
EditorGUI.LabelField(new Rect(rect.x, rect.y, 25, EditorGUIUtility.singleLineHeight), index.ToString());
var fieldRect = new Rect(rect.x + 25, rect.y, rect.width - 50, EditorGUIUtility.singleLineHeight);
var delRect = new Rect(fieldRect.xMax + 5, rect.y, 20, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(fieldRect, element, GUIContent.none);
if (GUI.Button(delRect, "x"))
{
_removeList.Add(index);
}
}
private void DrawHeaderCallback(Rect rect)
{
//EditorGUI.LabelField(rect, "DNA Names List (" + _dnaNameList.serializedProperty.arraySize + ")", EditorStyles.helpBox);
}
private void DrawFooterCallback(Rect rect)
{
var Names = _dnaNameList.serializedProperty;
var ROLDefaults = new ReorderableList.Defaults();
var padding = 4f;
var _addBtnWidth = 100f + padding;
var _labelWidth = 68f;
Rect addRect = rect;
addRect.xMin = addRect.xMax - 420 > addRect.xMin ? addRect.xMax - 420 : addRect.xMin;
addRect.height = (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 2);
var addBtnRect = new Rect(rect.xMax - (_addBtnWidth) - (padding * 2), addRect.yMin, ((_addBtnWidth / 5) * 2) - padding, EditorGUIUtility.singleLineHeight);
var fieldRect = new Rect(addRect.xMin + (padding * 2), addRect.yMin, addRect.width - _addBtnWidth - (padding * 6), EditorGUIUtility.singleLineHeight);
var labelRect = new Rect(fieldRect.xMin, fieldRect.yMin, _labelWidth, fieldRect.height);
fieldRect.xMin = labelRect.xMax + (padding);
var clearBtnRect = new Rect(addBtnRect.xMax + padding, addRect.yMin, ((_addBtnWidth / 5) * 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;
}
EditorGUI.LabelField(labelRect, new GUIContent("Add Name:", "Add a DNA Name to the list"));
newDNAName = EditorGUI.TextField(fieldRect, newDNAName);
if (newDNAName != "")
{
canAddNewDNAName = true;
for (int ni = 0; ni < Names.arraySize; ni++)
{
if (Names.GetArrayElementAtIndex(ni).stringValue == newDNAName)
{
canAddNewDNAName = false;
}
}
}
if (GUI.Button(addBtnRect,"Add"))
{
if (newDNAName == "")
{
return;
}
if (canAddNewDNAName)
{
Names.arraySize = Names.arraySize + 1;
Names.GetArrayElementAtIndex(Names.arraySize - 1).stringValue = newDNAName;
Names.serializedObject.ApplyModifiedProperties();
newDNAName = "";
EditorGUIUtility.keyboardControl = 0;
}
}
EditorGUI.BeginDisabledGroup(Names.arraySize == 0);
if (GUI.Button(clearBtnRect, new GUIContent("Clear All", "Clears all the names from the list. Cannot be undone")))
{
if (EditorUtility.DisplayDialog("Really Clear All Names?", "This will delete all the names in the list and cannot be undone. Are you sure?", "Yes", "Cancel"))
{
Names.arraySize = 0;
Names.serializedObject.ApplyModifiedProperties();
}
}
EditorGUI.EndDisabledGroup();
}
protected void AddDefaultNames()
{
string[] defaultNames = new string[]
{
"height",
"headSize",
"headWidth",
"neckThickness",
"armLength",
"forearmLength",
"armWidth",
"forearmWidth",
"handsSize",
"feetSize",
"legSeparation",
"upperMuscle",
"lowerMuscle",
"upperWeight",
"lowerWeight",
"legsSize",
"belly",
"waist",
"gluteusSize",
"earsSize",
"earsPosition",
"earsRotation",
"noseSize",
"noseCurve",
"noseWidth",
"noseInclination",
"nosePosition",
"nosePronounced",
"noseFlatten",
"chinSize",
"chinPronounced",
"chinPosition",
"mandibleSize",
"jawsSize",
"jawsPosition",
"cheekSize",
"cheekPosition",
"lowCheekPronounced",
"lowCheekPosition",
"foreheadSize",
"foreheadPosition",
"lipsSize",
"mouthSize",
"eyeRotation",
"eyeSize",
"breastSize"
};
List<string> currentNames = new List<string>((target as DynamicUMADnaAsset).Names);
for(int i = 0; i < defaultNames.Length; i++)
{
if (!currentNames.Contains(defaultNames[i]))
{
currentNames.Add(defaultNames[i]);
}
}
(target as DynamicUMADnaAsset).Names = currentNames.ToArray();
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 6a855a1f3cd0026419ff83ce25ced6bc
timeCreated: 1466072096
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/Extensions/DynamicCharacterSystem/DynamicUMADnaAssetEditor.cs
uploadId: 679826
@@ -0,0 +1,50 @@
using UnityEngine;
using UnityEditor;
using UMA.Editors;
namespace UMA.CharacterSystem.Editors
{
[CustomPropertyDrawer(typeof(DynamicUMADnaAsset))]
public class DynamicUMADnaAssetPropertyDrawer : 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 && DynamicUMADnaAssetEditor.livePopupEditor == null)
{
var editors = InspectorUtlity.GetInspectorsEditors(inspectorPopup);
for (int i = 0; i < editors.Length; i++)
{
if (editors[i].GetType() == typeof(DynamicUMADnaAssetEditor))
{
if (editors[i].target == property.objectReferenceValue)
{
DynamicUMADnaAssetEditor.SetLivePopupEditor(editors[i] as DynamicUMADnaAssetEditor);
}
}
}
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return (EditorGUIUtility.singleLineHeight);
}
public void OnShowPopupInspector(EditorWindow newInspectorPopup)
{
if (inspectorPopup != null && inspectorPopup != newInspectorPopup)
{
inspectorPopup.Close();
DynamicUMADnaAssetEditor.SetLivePopupEditor(null);
}
inspectorPopup = newInspectorPopup;
}
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 4208e273e28e6504c846381b7fb651f3
timeCreated: 1543190403
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/Extensions/DynamicCharacterSystem/DynamicUMADnaAssetPropertyDrawer.cs
uploadId: 679826
@@ -0,0 +1,61 @@
// from http://www.sharkbombs.com/2015/02/17/unity-editor-enum-flags-as-toggle-buttons/
// Extended by Tassim
using UnityEditor;
using UnityEngine;
namespace UMA.CharacterSystem.Editors
{
[CustomPropertyDrawer(typeof(EnumFlagsAttribute))]
public class EnumFlagsAttributeDrawer : PropertyDrawer
{
public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label)
{
int namesIntModifier = 1;
//check if there is a 'none' value at the second position too because we dont want that either- not a very elegant solution...
if (prop.enumNames[1] == "none")
{
namesIntModifier = 2;
}
bool[] buttons = new bool[prop.enumNames.Length - namesIntModifier];
if (label != GUIContent.none)
{
EditorGUI.LabelField(new Rect(pos.x, pos.y, EditorGUIUtility.labelWidth, pos.height), label);
}
EditorGUI.indentLevel++;
// Handle button value
EditorGUI.BeginChangeCheck();
int buttonsValue = 0;
for (int i = 0; i < buttons.Length; i++)
{
// Check if the button is/was pressed
if ((prop.intValue & (namesIntModifier << i)) == (namesIntModifier << i))
{
buttons[i] = true;
}
buttons[i] = EditorGUILayout.ToggleLeft(prop.enumNames[i + namesIntModifier].BreakupCamelCase(), buttons[i]);
if (buttons[i])
{
buttonsValue += namesIntModifier << i;
}
}
// This is set to true if a control changed in the previous BeginChangeCheck block
if (EditorGUI.EndChangeCheck())
{
prop.intValue = buttonsValue;
}
EditorGUI.indentLevel--;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 0f;
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 892916dd7be075f42b6110b72e56c2f2
timeCreated: 1481654974
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/Extensions/DynamicCharacterSystem/EnumFlagsAttributeDrawer.cs
uploadId: 679826
@@ -0,0 +1,12 @@
using UnityEditor;
public interface IUMARecipePlugin
{
bool foldOut {
get; set;
}
string GetSectionLabel();
void OnEnable();
void OnDestroy();
void OnInspectorGUI(SerializedObject serializedObject);
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 51ab6f20ab865a3418b5118be11e7c52
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/Extensions/DynamicCharacterSystem/IUMARecipePlugin.cs
uploadId: 679826
@@ -0,0 +1,242 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using System.Collections.Generic;
namespace UMA.CharacterSystem.Editors
{
[CustomPropertyDrawer (typeof(DynamicCharacterAvatar.RaceAnimatorList))]
public class RaceAnimatorListPropertyDrawer : PropertyDrawer
{
float padding = 2f;
public DynamicCharacterAvatar thisDCA;
Texture2D warningIcon;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label){
if (warningIcon == null)
{
warningIcon = EditorGUIUtility.FindTexture("console.warnicon.sml");
}
EditorGUI.BeginProperty (position, label, property);
var r0 = new Rect (position.xMin, position.yMin, position.width, EditorGUIUtility.singleLineHeight);
SerializedProperty foldoutProp1 = property.FindPropertyRelative ("defaultAnimationController");
foldoutProp1.isExpanded = EditorGUI.Foldout (r0, foldoutProp1.isExpanded, "Race Animation Controllers");
if (foldoutProp1.isExpanded) {
EditorGUI.indentLevel++;
var valR = r0;
valR = new Rect (valR.xMin, valR.yMax + padding, valR.width, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField (valR,property.FindPropertyRelative ("defaultAnimationController"));
valR = new Rect (valR.xMin, valR.yMax + padding, valR.width, EditorGUIUtility.singleLineHeight);
SerializedProperty foldoutProp2 = property.FindPropertyRelative ("animators");
foldoutProp2.isExpanded = EditorGUI.Foldout (valR, foldoutProp2.isExpanded, "Race Animators");
//we cant delete elements in the loop so ...
List<int> willDeleteArrayElementAtIndex = new List<int> ();
if (foldoutProp2.isExpanded) {
EditorGUI.indentLevel++;
var thisAnimatorsProp = property.FindPropertyRelative ("animators");
var numAnimators = thisAnimatorsProp.arraySize;
var warningStyle = new GUIStyle(EditorStyles.label);
warningStyle.fixedHeight = warningIcon.height + 4f;
warningStyle.contentOffset = new Vector2(0, -2f);
for (int i = 0; i < numAnimators; i++) {
var thisAnimtorProp = thisAnimatorsProp.GetArrayElementAtIndex (i);
valR = new Rect (valR.xMin, valR.yMax + padding, valR.width, EditorGUIUtility.singleLineHeight);
var propsR = valR;
propsR.width = propsR.width - 20f;
var rPropR = propsR;
rPropR.width = rPropR.width / 2;
var aPropR = rPropR;
aPropR.x = propsR.x + rPropR.width;
var rLabelR = rPropR;
rLabelR.width = (float)(rLabelR.width * 0.3)+(15f * (EditorGUI.indentLevel -1));
var rFieldR = rPropR;
rFieldR.x = rFieldR.x + rLabelR.width;
rFieldR.width = rFieldR.width - rLabelR.width;
//
var aLabelR = aPropR;
aLabelR.width = (float)(aLabelR.width * 0.3);
var aFieldR = aPropR;
aFieldR.x = aFieldR.x + aLabelR.width;
aFieldR.width = aFieldR.width - aLabelR.width;
var removeR = propsR;
removeR.x = aFieldR.xMax;
removeR.width = 20f;
EditorGUI.LabelField (rLabelR, "Race");
EditorGUI.indentLevel--;
EditorGUI.indentLevel--;
if (thisAnimtorProp.FindPropertyRelative ("raceName").stringValue == "") {
//draw an object field for RaceData
EditorGUI.BeginChangeCheck();
RaceData thisRD = null;
thisRD = (RaceData)EditorGUI.ObjectField (rFieldR, thisRD, typeof(RaceData),false);
//if this gets filled set the values
if(EditorGUI.EndChangeCheck()){
if (thisRD != null) {
thisAnimatorsProp.GetArrayElementAtIndex (i).FindPropertyRelative ("raceName").stringValue = thisRD.raceName;
}
}
}
else
{
EditorGUI.BeginDisabledGroup (true);
EditorGUI.TextField (rFieldR, thisAnimtorProp.FindPropertyRelative ("raceName").stringValue);
EditorGUI.EndDisabledGroup ();
}
EditorGUI.LabelField (aLabelR, "Animator");
var thisAnimatorName = thisAnimtorProp.FindPropertyRelative("animatorControllerName").stringValue;
if (thisAnimatorName == "")
{
//draw an object field for RunTimeAnimatorController
EditorGUI.BeginChangeCheck();
RuntimeAnimatorController thisRC = null;
thisRC = (RuntimeAnimatorController)EditorGUI.ObjectField (aFieldR, thisRC, typeof(RuntimeAnimatorController), false);
//if this gets filled set the values
if(EditorGUI.EndChangeCheck()){
if (thisRC != null) {
thisAnimatorsProp.GetArrayElementAtIndex (i).FindPropertyRelative ("animatorControllerName").stringValue = thisRC.name;
}
}
}
else
{
if (!CheckAnimatorAvailability(thisAnimatorName))
{
var warningRect = new Rect((removeR.xMin - 20f), removeR.yMin, 20f, removeR.height);
aFieldR.xMax = aFieldR.xMax - 20f;
//if its in an assetbundle we need a different message (i.e "turn on 'Dynamically Add From AssetBundles"')
var warningGUIContent = new GUIContent("", thisAnimtorProp.FindPropertyRelative("animatorControllerName").stringValue + " was not Live. If the asset is in an assetBundle check 'Dynamically Add from AssetBundles' below otherwise click this button to add it to the Global Library.");
warningGUIContent.image = warningIcon;
if (GUI.Button(warningRect, warningGUIContent, warningStyle))
{
var thisAnimator = FindMissingAnimator(thisAnimatorName);
if (thisAnimator != null)
{
UMAAssetIndexer.Instance.EvilAddAsset(thisAnimator.GetType(), thisAnimator);
}
}
}
EditorGUI.BeginDisabledGroup(true);
EditorGUI.TextField(aFieldR, thisAnimtorProp.FindPropertyRelative("animatorControllerName").stringValue);
EditorGUI.EndDisabledGroup();
}
if(GUI.Button(removeR,"X")){
willDeleteArrayElementAtIndex.Add(i);
}
EditorGUI.indentLevel++;
EditorGUI.indentLevel++;
}
if (willDeleteArrayElementAtIndex.Count > 0) {
for (int i1 = 0; i1 < willDeleteArrayElementAtIndex.Count; i1++) {
int i = willDeleteArrayElementAtIndex[i1];
thisAnimatorsProp.DeleteArrayElementAtIndex (i);
}
}
thisAnimatorsProp.serializedObject.ApplyModifiedProperties();
valR = new Rect (valR.xMin, valR.yMax + padding, valR.width, EditorGUIUtility.singleLineHeight);
var butValR = valR;
//GUI doesn't know EditorGUI.indentLevel
butValR.xMin = valR.xMin + (15 * EditorGUI.indentLevel);
if(GUI.Button(butValR,"Add Race Animator")){
//add a new element to the list
thisAnimatorsProp.InsertArrayElementAtIndex(numAnimators);
thisAnimatorsProp.serializedObject.ApplyModifiedProperties();
//make sure its blank
thisAnimatorsProp.GetArrayElementAtIndex(numAnimators).FindPropertyRelative("raceName").stringValue = "";
thisAnimatorsProp.GetArrayElementAtIndex(numAnimators).FindPropertyRelative("animatorControllerName").stringValue = "";
thisAnimatorsProp.GetArrayElementAtIndex(numAnimators).FindPropertyRelative("animatorController").objectReferenceValue = null;
thisAnimatorsProp.serializedObject.ApplyModifiedProperties();
}
EditorGUI.indentLevel--;
}
valR = new Rect (valR.xMin, valR.yMax + padding, valR.width, EditorGUIUtility.singleLineHeight);
var dynamicallyAddFromResources = property.FindPropertyRelative ("dynamicallyAddFromResources").boolValue;
EditorGUI.BeginChangeCheck();
dynamicallyAddFromResources = EditorGUI.ToggleLeft(valR,"Dynamically Add from Global Library", dynamicallyAddFromResources);
if(EditorGUI.EndChangeCheck()){
property.FindPropertyRelative ("dynamicallyAddFromResources").boolValue = dynamicallyAddFromResources;
property.serializedObject.ApplyModifiedProperties ();
}
valR = new Rect (valR.xMin, valR.yMax + padding, valR.width, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField (valR,property.FindPropertyRelative ("resourcesFolderPath"), new GUIContent("Global Library Folder Filter"));
valR = new Rect (valR.xMin, valR.yMax + padding, valR.width, EditorGUIUtility.singleLineHeight);
EditorGUI.indentLevel--;
}
EditorGUI.EndProperty ();
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label){
float h = EditorGUIUtility.singleLineHeight + padding;
int extraLines = 0;
SerializedProperty foldoutProp1 = property.FindPropertyRelative ("defaultAnimationController");
SerializedProperty foldoutProp2 = property.FindPropertyRelative ("animators");
if (foldoutProp1.isExpanded) {
extraLines += 6;
if (foldoutProp2.isExpanded) {
var thisAnimatorsProp = property.FindPropertyRelative ("animators");
extraLines += thisAnimatorsProp.arraySize;
extraLines++;
}
h *= (extraLines);
h += 10 + (extraLines * padding);
}
return h;
}
/// <summary>
/// with RuntimeAnimatorControllers, DynamicCharacterAvatar ony has a direct refrence for the default animator
/// (so that other animators can be assigned that can exist in asset bundles). The only way to get the others is from DynamicAssetLoader
/// so they MUST be in an assetBundle or in GlobalIndex or there is no way of finding them, if they are not, show a warning.
/// </summary>
/// <param name="racName">RuntimeAnimatorController name.</param>
/// <returns></returns>
private bool CheckAnimatorAvailability(string racName)
{
if (Application.isPlaying)
{
return true;
}
if (UMAAssetIndexer.Instance.GetAssetDictionary(typeof(RuntimeAnimatorController)).ContainsKey(racName))
{
return true;
}
RuntimeAnimatorController defaultController = null;
if (thisDCA != null)
{
defaultController = thisDCA.raceAnimationControllers.defaultAnimationController != null ? thisDCA.raceAnimationControllers.defaultAnimationController : (thisDCA.animationController != null ? thisDCA.animationController : null);
if (defaultController != null && defaultController.name == racName)
{
return true;
}
}
return false;
}
private RuntimeAnimatorController FindMissingAnimator(string animatorName)
{
RuntimeAnimatorController foundAnimator = null;
//the following will find things like femaleHair1 if 'maleHair1' is the recipe name
var foundWardrobeGUIDS = AssetDatabase.FindAssets("t:RuntimeAnimatorController " + animatorName);
if (foundWardrobeGUIDS.Length > 0)
{
for (int i = 0; i < foundWardrobeGUIDS.Length; i++)
{
string guid = foundWardrobeGUIDS[i];
var tempAsset = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(AssetDatabase.GUIDToAssetPath(guid));
if (tempAsset != null && tempAsset.name == animatorName)
{
foundAnimator = tempAsset;
break;
}
}
}
return foundAnimator;
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 92c7c124d2234a440a32fc6388d19d92
timeCreated: 1458404195
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/Extensions/DynamicCharacterSystem/RaceAnimatorListPropertyDrawer.cs
uploadId: 679826
@@ -0,0 +1,211 @@
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using UMA.Editors;
namespace UMA.CharacterSystem.Editors
{
[CustomPropertyDrawer(typeof(DynamicCharacterAvatar.RaceSetter))]
public class RaceSetterPropertyDrawer : PropertyDrawer
{
public DynamicCharacterAvatar thisDCA;
//public DynamicRaceLibrary thisDynamicRaceLibrary;
//In the Editor when the app is NOT running this shows all the races you COULD choose- including those AssetBundles.
//When the app IS running it shows the reaces you CAN choose- i.e. the ones that are either in the build or have been downloaded.
public List<RaceData> foundRaces = new List<RaceData>();
public List<string> foundRaceNames = new List<string>();
override public float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 0;
}
public void SetRaceLists(RaceData[] raceDataArray = null)
{
foundRaces.Clear();
foundRaceNames.Clear();
foundRaces.Add(null);
foundRaceNames.Add("None Set");
if (raceDataArray == null)
{
return;
}
for (int i = 0; i < raceDataArray.Length; i++)
{
RaceData race = raceDataArray[i];
if (race != null && race.raceName != "RaceDataPlaceholder")
{
foundRaces.Add(race);
foundRaceNames.Add(race.raceName);
}
}
}
float lastTime = 0.0f;
private void CheckRaceDataLists()
{
float currentTime = Time.realtimeSinceStartup;
if (currentTime - lastTime < 5.0f)
{
lastTime = currentTime;
return;
}
if (UMAContext.Instance == null)
{
var raceDatas = UMAAssetIndexer.Instance.GetAllAssets<RaceData>();
SetRaceLists(raceDatas.ToArray());
return;
}
if (Application.isPlaying)
{
//Start will have cleared any EditorAdded Assets and we only *need* the ones in the library
var raceDatas = UMAContext.Instance.GetAllRacesBase();
SetRaceLists(raceDatas);
}
else
{
var raceDatas = UMAContext.Instance.GetAllRaces();
if ((raceDatas.Length + 1) != (foundRaces.Count))
{
SetRaceLists(raceDatas);
}
}
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
CheckRaceDataLists();
var RaceName = property.FindPropertyRelative("name");
string rn = RaceName.stringValue;
int rIndex = 0;
int newrIndex;
int converterCount = 0;
if (rn != "")
{
if (!foundRaceNames.Contains(rn))
{
foundRaceNames.Add(rn + " (Not Available)");
foundRaces.Add(null);
}
rIndex = foundRaceNames.IndexOf(rn) == -1 ? (foundRaceNames.IndexOf(rn + " (Not Available)") == -1 ? 0 : foundRaceNames.IndexOf(rn + " (Not Available)")) : foundRaceNames.IndexOf(rn);
}
// EditorGUI.BeginProperty(position, label, property);
GUIHelper.BeginVerticalPadded(5, new Color(0.75f, 0.875f, 1f));
// Rect contentPosition = EditorGUI.PrefixLabel(position, new GUIContent("Active Race"));
//Rect contentPositionP = contentPosition;
EditorGUI.BeginChangeCheck();
newrIndex = EditorGUILayout.Popup(new GUIContent("Active Race"),rIndex, foundRaceNames.ToArray());
if (EditorGUI.EndChangeCheck())
{
if (rIndex != newrIndex)
{
RaceName.stringValue = foundRaceNames[newrIndex];
//somehow if the app is playing this already works- and doing it here makes it not work
if (!EditorApplication.isPlaying)
{
property.serializedObject.ApplyModifiedProperties();
}
}
}
RaceData theRace = foundRaces[newrIndex];
if (theRace != null)
{
if (theRace.dnaConverterList != null)
{
converterCount = theRace.dnaConverterList.Length;
}
}
EditorGUILayout.LabelField("Inspector Tools", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
/*
if (GUILayout.Button("Ping Race",GUILayout.Width(90)))
{
RaceData theRace = foundRaces[newrIndex];
if (theRace != null)
{
EditorGUIUtility.PingObject(theRace);
}
}*/
if (GUILayout.Button("Race"))
{
if (theRace != null)
{
InspectorUtlity.InspectTarget(theRace);
}
}
if (GUILayout.Button("Base Recipe"))
{
if (theRace != null)
{
if (theRace.baseRaceRecipe != null)
{
InspectorUtlity.InspectTarget(theRace.baseRaceRecipe);
}
}
}
if (GUILayout.Button($"DNA Cvts ({converterCount})"))
{
if (theRace != null)
{
if (theRace.dnaConverterList != null)
{
foreach(var dna in theRace.dnaConverterList)
{
InspectorUtlity.InspectTarget(dna);
}
}
}
}
if (GUILayout.Button("BonePose"))
{
//UMABonePose firstPose = null;
if (theRace != null)
{
if (theRace.dnaConverterList != null)
{
foreach (var dna in theRace.dnaConverterList)
{
if (dna.PluginCount > 0)
{
foreach( var plugin in dna.GetPlugins())
{
if (plugin is BonePoseDNAConverterPlugin)
{
var p = plugin as BonePoseDNAConverterPlugin;
foreach (var bp in p.poseDNAConverters)
{
if (bp.poseToApply != null)
{
InspectorUtlity.InspectTarget(bp.poseToApply);
}
}
break;
}
}
}
}
}
}
}
EditorGUILayout.EndHorizontal();
GUIHelper.EndVerticalPadded(5);
//EditorGUI.EndProperty();
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 31df5d9778148214cb57a7499b94478b
timeCreated: 1457970048
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/Extensions/DynamicCharacterSystem/RaceSetterPropertyDrawer.cs
uploadId: 679826
@@ -0,0 +1,333 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UMA.Integrations;
namespace UMA.Editors
{
/// <summary>
/// Recipe editor.
/// Class is marked partial so developers can add their own functionality to edit new properties added to
/// UMATextRecipe without changing code delivered with UMA.
/// </summary>
[CanEditMultipleObjects]
[CustomEditor(typeof(UMARecipeBase), true)]
public partial class RecipeEditor : CharacterBaseEditor
{
List<GameObject> draggedObjs;
GameObject generatedContext;
EditorWindow inspectorWindow;
//for showing a warning if any of the compatible races are missing or not assigned to bundles or the index
protected Texture warningIcon;
protected GUIStyle warningStyle;
private static List<IUMARecipePlugin> plugins;
public static List<Type> GetRecipeEditorPlugins() {
List<Type> theTypes = new List<Type>();
var Assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach(var asm in Assemblies) {
try {
var Types = asm.GetTypes();
foreach(var t in Types) {
if(typeof(IUMARecipePlugin).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract) {
theTypes.Add(t);
}
}
} catch(Exception) {
// This apparently blows up on some assemblies.
}
}
return theTypes;
/* return AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
.Where(x => typeof(IUMAAddressablePlugin).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
.Select(x => x).ToList();*/
}
public virtual void OnSceneDrag(SceneView view)
{
if (Event.current.type == EventType.DragUpdated)
{
if (Event.current.mousePosition.x < 0 || Event.current.mousePosition.x >= view.position.width ||
Event.current.mousePosition.y < 0 || Event.current.mousePosition.y >= view.position.height) return;
DragAndDrop.visualMode = DragAndDropVisualMode.Copy; // show a drag-add icon on the mouse cursor
Event.current.Use();
return;
}
if (Event.current.type == EventType.DragPerform)
{
if (Event.current.mousePosition.x < 0 || Event.current.mousePosition.x >= view.position.width ||
Event.current.mousePosition.y < 0 || Event.current.mousePosition.y >= view.position.height) return;
Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
RaycastHit hit;
Vector3 position = Vector3.zero;
if (Physics.Raycast(ray, out hit))
{
position = hit.point;
}
var newSelection = new List<UnityEngine.Object>(DragAndDrop.objectReferences.Length);
foreach (var reference in DragAndDrop.objectReferences)
{
if (reference is UMARecipeBase)
{
var avatarGO = CreateAvatar(reference as UMARecipeBase);
avatarGO.GetComponent<Transform>().position = position;
position.x = position.x + 1;
newSelection.Add(avatarGO);
}
}
Selection.objects = newSelection.ToArray();
DragAndDrop.visualMode = DragAndDropVisualMode.Copy; // show a drag-add icon on the mouse cursor
Event.current.Use();
}
}
public virtual GameObject CreateAvatar(UMARecipeBase recipe)
{
var GO = new GameObject(recipe.name);
var avatar = GO.AddComponent<UMADynamicAvatar>();
avatar.umaRecipe = recipe;
avatar.loadOnStart = true;
return GO;
}
void AddPlugins() {
List<Type> PluginTypes = GetRecipeEditorPlugins();
plugins = new List<IUMARecipePlugin>();
foreach(Type t in PluginTypes) {
plugins.Add((IUMARecipePlugin)Activator.CreateInstance(t));
}
}
public override void OnEnable()
{
if(plugins == null) {
AddPlugins();
}
base.OnEnable();
foreach(IUMARecipePlugin plugin in plugins) {
plugin.OnEnable();
}
if (!NeedsReenable())
return;
_errorMessage = null;
_recipe = new UMAData.UMARecipe();
showBaseEditor = false;
try
{
var umaRecipeBase = target as UMARecipeBase;
if (umaRecipeBase != null)
{
var context = UMAContextBase.Instance;
//create a virtual UMAContextBase if we dont have one and we have DCS
// if (context == null || context.gameObject.name == "UMAEditorContext")
// {
// context = umaRecipeBase.CreateEditorContext();//will create or update an UMAEditorContext to the latest version
// generatedContext = context.gameObject.transform.parent.gameObject;//The UMAContextBase in a UMAEditorContext is that gameobject's child
// }
//legacy checks for context
if (context != null)
{
umaRecipeBase.Load(_recipe, context);
_description = umaRecipeBase.GetInfo();
}
}
}
catch (UMAResourceNotFoundException e)
{
_errorMessage = e.Message;
}
dnaEditor = new DNAMasterEditor(_recipe);
slotEditor = new SlotMasterEditor(_recipe);
_rebuildOnLayout = true;
}
public void OnDestroy()
{
if (generatedContext != null)
{
//Ensure UMAContextBase.Instance is set to null
UMAContextBase.Instance = null;
DestroyImmediate(generatedContext);
}
foreach(IUMARecipePlugin plugin in plugins) {
plugin.OnDestroy();
}
}
public override void OnInspectorGUI()
{
if (warningIcon == null)
{
warningIcon = EditorGUIUtility.FindTexture("console.warnicon.sml");
warningStyle = new GUIStyle(EditorStyles.label);
warningStyle.fixedHeight = warningIcon.height + 4f;
warningStyle.contentOffset = new Vector2(0, -2f);
}
if (_recipe == null) return;
foreach(IUMARecipePlugin plugin in plugins)
{
string label = plugin.GetSectionLabel();
plugin.foldOut = GUIHelper.FoldoutBar(plugin.foldOut, label);
if(plugin.foldOut) {
GUIHelper.BeginVerticalPadded(10, new Color(0.65f, 0.675f, 1f));
plugin.OnInspectorGUI(serializedObject);
GUIHelper.EndVerticalPadded(10);
}
}
if (UMAContext.Instance == null)
{
EditorGUILayout.HelpBox("A valid context was not found. This is required to be able to view and edit UMA recipes. You can add a Temporary context, and it will disappear when the scene or appdomain is reloaded, or you can add a permanent UMA_GLIB to the scene.", MessageType.Warning);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Add Permanent Context"))
{
var glib = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/UMA/Getting Started/UMA_GLIB.prefab");
if (glib != null)
{
glib.name = "UMA_GLIB";
var g = (GameObject)PrefabUtility.InstantiatePrefab(glib);
}
else
{
EditorUtility.DisplayDialog("error", "Unable to find UMA_GLIB. Please add context manually.", "OK");
}
}
if (GUILayout.Button("Add Temp Context"))
{
var glib = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/UMA/Getting Started/UMA_GLIB.prefab");
if (glib != null)
{
glib.name = "Temp Context (does not save)";
var g = (GameObject)PrefabUtility.InstantiatePrefab(glib);
g.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
UMAContext.Instance = g.GetComponent<UMAGlobalContext>();
}
else
{
EditorUtility.DisplayDialog("error", "Unable to find UMA_GLIB. Please add context manually.", "OK");
}
}
EditorGUILayout.EndHorizontal();
return;
}
PowerToolsGUI();
base.OnInspectorGUI();
}
protected override void DoUpdate()
{
_needsUpdate = false;
var recipeBase = (UMARecipeBase)target;
recipeBase.Save(_recipe, UMAContextBase.Instance);
EditorUtility.SetDirty(recipeBase);
AssetDatabase.SaveAssetIfDirty(recipeBase);
_rebuildOnLayout = true;
if (target is UMATextRecipe)
{
UMAUpdateProcessor.UpdateRecipe(target as UMATextRecipe);
}
}
protected override void Rebuild()
{
base.Rebuild();
var recipeBase = target as UMARecipeBase;
if (PowerToolsIntegration.HasPowerTools() && PowerToolsIntegration.HasPreview(recipeBase))
{
_needsUpdate = true;
}
}
private void PowerToolsGUI()
{
if (PowerToolsIntegration.HasPowerTools())
{
GUILayout.BeginHorizontal();
var recipeBase = target as UMARecipeBase;
if (PowerToolsIntegration.HasPreview(recipeBase))
{
if (GUILayout.Button("Hide"))
{
PowerToolsIntegration.Hide(recipeBase);
}
if (GUILayout.Button("Create Prefab"))
{
//PowerToolsIntegration.CreatePrefab(recipeBase);
}
if (GUILayout.Button("Hide All"))
{
PowerToolsIntegration.HideAll();
}
} else
{
if (GUILayout.Button("Show"))
{
PowerToolsIntegration.Show(recipeBase);
}
if (GUILayout.Button("Create Prefab"))
{
//PowerToolsIntegration.CreatePrefab(recipeBase);
}
if (GUILayout.Button("Hide All"))
{
PowerToolsIntegration.HideAll();
}
}
GUILayout.EndHorizontal();
}
}
/// <summary>
/// Checks if the given RaceData is in the globalLibrary or an assetBundle
/// </summary>
/// <param name="_raceData"></param>
/// <returns></returns>
protected bool RaceInIndex(RaceData _raceData)
{
if (UMAContextBase.Instance != null)
{
if (UMAContextBase.Instance.HasRace(_raceData.raceName) != null)
return true;
}
AssetItem ai = UMAAssetIndexer.Instance.GetAssetItem<RaceData>(_raceData.raceName);
if (ai != null)
{
return true;
}
return false;
}
}
/*public class ShowGatheringNotification : EditorWindow
{
string notification = "UMA is gathering Data";
void OnGUI() {
this.ShowNotification(new GUIContent(notification));
}
}*/
}
#endif
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 691decbfdd20d7b49940a58ab60f21de
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/Extensions/DynamicCharacterSystem/RecipeEditor.cs
uploadId: 679826
@@ -0,0 +1,527 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace UMA.CharacterSystem.Editors
{
[CustomPropertyDrawer(typeof(SkeletonModifier))]
public class SkeletonModifierPropertyDrawer : PropertyDrawer
{
float padding = 4f;
public List<string> hashNames = new List<string>();
//used when editing in playmode has the actual boneHashes from the avatars skeleton
public List<string> bonesInSkeleton = null;
public List<int> hashes = new List<int>();
public string[] dnaNames;
private bool _allowLegacyDNADrawer = false;
public bool AllowLegacyDNADrawer
{
get { return _allowLegacyDNADrawer; }
set { _allowLegacyDNADrawer = value; }
}
#pragma warning disable 618 //disable obsolete warning
//we use DNAEvaluatorList now
spValModifierPropertyDrawer thisSpValDrawer = null;
#pragma warning restore 618
Texture warningIcon;
GUIStyle warningStyle;
//Rects
Rect tabsArea;
Rect valMinMaxArea;
Rect valBox;
Rect valLabel;
Rect valVal;
Rect minBox;
Rect minLabel;
Rect minVal;
Rect maxBox;
Rect maxLabel;
Rect maxVal;
Rect modifiersArea;
Rect modifiersProps;
Rect modifiersDel;
Rect modifiersAdd;
int activeTab = 0;
string[] tabsLabels = new string[] { "ValuesX", "ValuesY", "ValuesZ" };
float valLabelWidth = 48f;
float minMaxLabelWidth = 38f;
float delButWidth = 20f;
float addButWidth = 60f;
GUIContent valueLabel = new GUIContent("X Value", "The Initial Value used at the start of the calculation. For Scale this should usually be 1, for Position or Rotation, should usually be 0");
GUIContent valueOverrideLabel = new GUIContent("Intitial X Value Override", "Editing this will affect the starting shape of ALL characters that use this modifiers converter. Consider using a Starting UMABonePose instead as these have their own tools, and the weight of the pose can be controlled by dna 'per character'");
private bool _initialized = false;
public bool Init(string[] _dnaNames = null)
{
Init(new List<string>(), new List<int>(), _dnaNames);
return _initialized;
}
#pragma warning disable 618 //disable obsolete warning
public bool Init(List<string> _hashNames, List<int> _hashes, string[] _dnaNames = null)
{
if (!_initialized)
{
hashNames = _hashNames;
hashes = _hashes;
dnaNames = _dnaNames;
//Will be removed ina future version since we use DNAEvaluatorList for these now
if (_allowLegacyDNADrawer)
{
if (thisSpValDrawer == null)
{
thisSpValDrawer = new spValModifierPropertyDrawer();
}
thisSpValDrawer.dnaNames = dnaNames;
}
if (warningIcon == null)
{
warningIcon = EditorGUIUtility.FindTexture("console.warnicon.sml");
warningStyle = new GUIStyle(EditorStyles.label);
warningStyle.fixedHeight = warningIcon.height + 4f;
warningStyle.contentOffset = new Vector2(0, -2f);
}
valueOverrideLabel.image = warningIcon;
_initialized = true;
}
else
{
if(_dnaNames != null)
{
UpdateDnaNames(_dnaNames);
}
}
return _initialized;
}
#pragma warning restore 618 //restore obsolete warning
public void UpdateHashNames(List<string> _hashNames, List<int> _hashes)
{
hashNames = _hashNames;
hashes = _hashes;
}
public void UpdateDnaNames(string[] newDnaNames)
{
if (thisSpValDrawer != null)
{
thisSpValDrawer.dnaNames = newDnaNames;
}
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (!Init(hashNames, hashes))
{
return;
}
int startingIndent = EditorGUI.indentLevel;
EditorGUI.BeginProperty(position, label, property);
string thisHashName = property.FindPropertyRelative("_hashName").stringValue;
var currRect = new Rect(position.xMin, position.yMin, position.width, EditorGUIUtility.singleLineHeight);
string betterLabel = label.text;
if (property.FindPropertyRelative("_property").enumDisplayNames[property.FindPropertyRelative("_property").enumValueIndex] != "")
{
betterLabel += " (" + property.FindPropertyRelative("_property").enumDisplayNames[property.FindPropertyRelative("_property").enumValueIndex] + ")";
}
List<string> boneNames = new List<string>();
if (bonesInSkeleton != null)
{
boneNames = new List<string>(bonesInSkeleton);
}
else
{
boneNames = new List<string>(hashNames);
}
int hashNameIndex = boneNames.IndexOf(thisHashName);
//Warn about current Bone not being available in active Avatar if we are in playMode (i.e. got sent BonesInSkeleton)
if (hashNameIndex == -1 && bonesInSkeleton != null)
{
boneNames.Insert(0, thisHashName + " (missing)");
hashNameIndex = 0;
var warningRect = new Rect((currRect.xMin), currRect.yMin, 20f, currRect.height);
var warningIconGUI = new GUIContent("", thisHashName + " was not a bone in the Avatars Skeleton. Please choose another bone for this modifier or delete it.");
warningIconGUI.image = warningIcon;
betterLabel += " (missing)";
GUI.Label(warningRect, warningIconGUI, warningStyle);
}
//Draw the foldout- toolbar-ish style here?
property.isExpanded = EditorGUI.Foldout(currRect, property.isExpanded, betterLabel, true);
if (property.isExpanded)
{
EditorGUI.indentLevel++;
//THE BONE NAME FIELD
currRect = new Rect(currRect.xMin, currRect.yMax + padding, currRect.width, EditorGUIUtility.singleLineHeight);
if (boneNames.Count > 0)
{
int newHashNameIndex = hashNameIndex;
EditorGUI.BeginChangeCheck();
newHashNameIndex = EditorGUI.Popup(currRect, "Bone Name", hashNameIndex, boneNames.ToArray());
if (EditorGUI.EndChangeCheck())
{
if (newHashNameIndex != hashNameIndex)
{
property.FindPropertyRelative("_hashName").stringValue = boneNames[newHashNameIndex];
property.FindPropertyRelative("_hash").intValue = UMAUtils.StringToHash(boneNames[newHashNameIndex]);
property.serializedObject.ApplyModifiedProperties();
}
}
}
else
{
//make sure the hash is changed if the name is edited
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(currRect, property.FindPropertyRelative("_hashName"), new GUIContent("Bone Name"));
if (EditorGUI.EndChangeCheck())
{
property.FindPropertyRelative("_hash").intValue = UMAUtils.StringToHash(property.FindPropertyRelative("_hashName").stringValue);
}
}
//THE PROPERTY FIELD
currRect = new Rect(currRect.xMin, currRect.yMax + padding, currRect.width, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(currRect, property.FindPropertyRelative("_property"));
//X/Y/Z VALUES TABS
SerializedProperty subValsToOpen = null;
var valuesX = property.FindPropertyRelative("_valuesX");
var valuesY = property.FindPropertyRelative("_valuesY");
var valuesZ = property.FindPropertyRelative("_valuesZ");
tabsArea = currRect = new Rect(currRect.xMin + (EditorGUI.indentLevel * 10f), currRect.yMax + padding, currRect.width - (EditorGUI.indentLevel * 10f), EditorGUIUtility.singleLineHeight);
//activeTab = valuesX.isExpanded ? 0 : (valuesY.isExpanded ? 1 : (valuesZ.isExpanded ? 2 : 0));
//subValsToOpen = valuesX.isExpanded ? valuesX : (valuesY.isExpanded ? valuesY : (valuesZ.isExpanded ? valuesZ : valuesX));
activeTab = 0;
subValsToOpen = valuesX;
valueLabel.text = "X Value";
valueOverrideLabel.text = "Intitial X Value Override";
if (!valuesX.isExpanded)
{
if (valuesY.isExpanded)
{
activeTab = 1;
subValsToOpen = valuesY;
valueLabel.text = "Y Value";
valueOverrideLabel.text = "Intitial Y Value Override";
}
else if (valuesZ.isExpanded)
{
activeTab = 2;
subValsToOpen = valuesZ;
valueLabel.text = "Z Value";
valueOverrideLabel.text = "Intitial Z Value Override";
}
else
{
valuesX.isExpanded = true;
}
}
EditorGUI.BeginChangeCheck();
activeTab = GUI.Toolbar(tabsArea, activeTab, tabsLabels, EditorStyles.toolbarButton);
if (EditorGUI.EndChangeCheck())
{
//make sure any focussed text areas dont prevent the tab from switching
GUI.FocusControl(null);
if (activeTab == 0)
{
valuesX.isExpanded = true;
valuesY.isExpanded = false;
valuesZ.isExpanded = false;
valueLabel.text = "X Value";
valueOverrideLabel.text = "Intitial X Value Override";
}
else if (activeTab == 1)
{
valuesX.isExpanded = false;
valuesY.isExpanded = true;
valuesZ.isExpanded = false;
subValsToOpen = valuesY;
valueLabel.text = "Y Value";
valueOverrideLabel.text = "Intitial Y Value Override";
}
else if (activeTab == 2)
{
valuesX.isExpanded = false;
valuesY.isExpanded = false;
valuesZ.isExpanded = true;
subValsToOpen = valuesZ;
valueLabel.text = "Z Value";
valueOverrideLabel.text = "Intitial Z Value Override";
}
}
//VALUES TAB CONTENT
if (subValsToOpen != null)
{
var subValuesVal = subValsToOpen.FindPropertyRelative("_val");
var subValuesMin = subValsToOpen.FindPropertyRelative("_min");
var subValuesMax = subValsToOpen.FindPropertyRelative("_max");
valMinMaxArea = currRect = new Rect(tabsArea.xMin, tabsArea.yMax, tabsArea.width, EditorGUIUtility.singleLineHeight + (padding * 4f));
valBox = new Rect(valMinMaxArea.xMin, valMinMaxArea.yMin + (padding * 2f), valMinMaxArea.width / 3f, valMinMaxArea.height - (padding * 4f));
minBox = new Rect(valBox.xMax, valBox.yMin, valBox.width, valBox.height);
maxBox = new Rect(minBox.xMax, minBox.yMin, minBox.width, minBox.height);
valLabel = new Rect(valBox.xMin + (padding * 6f), valBox.yMin, valLabelWidth, valBox.height);
valVal = new Rect(valLabel.xMax + (padding), valLabel.yMin, (valBox.width - valLabelWidth) - (padding * 10f), valLabel.height);
minLabel = new Rect(minBox.xMin + (padding * 2f), minBox.yMin, minMaxLabelWidth, minBox.height);
minVal = new Rect(minLabel.xMax, minLabel.yMin, (minBox.width - minMaxLabelWidth) - (padding * 4f), minLabel.height);
maxLabel = new Rect(maxBox.xMin + (padding * 2f), maxBox.yMin, minMaxLabelWidth, maxBox.height);
maxVal = new Rect(maxLabel.xMax, maxLabel.yMin, (maxBox.width - minMaxLabelWidth) - (padding * 4f), maxLabel.height);
if (subValuesVal.isExpanded)
{
valMinMaxArea.height += EditorGUIUtility.singleLineHeight + padding;
}
//VALUE/MIN/MAX FIELDS
EditorGUI.DrawRect(valMinMaxArea, new Color32(255, 255, 255, 100));
var prevIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
//Value Field
//Show another line here if this is expanded that lets the user change the starting value
subValuesVal.isExpanded = EditorGUI.Foldout(valLabel, subValuesVal.isExpanded, valueLabel, true);
EditorGUI.BeginDisabledGroup(true);
subValuesVal.FindPropertyRelative("_value").floatValue = EditorGUI.FloatField(valVal, subValuesVal.FindPropertyRelative("_value").floatValue);
EditorGUI.EndDisabledGroup();
if (subValuesVal.isExpanded)
{
var subValuesValRect = new Rect(tabsArea.xMin + (padding * 4f), tabsArea.yMax + (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 8), tabsArea.width - (padding * 8f), EditorGUIUtility.singleLineHeight);
subValuesVal.FindPropertyRelative("_value").floatValue = EditorGUI.FloatField(subValuesValRect, valueOverrideLabel, subValuesVal.FindPropertyRelative("_value").floatValue);
}
//Min Field
EditorGUI.LabelField(minLabel, "Min");
subValuesMin.floatValue = EditorGUI.FloatField(minVal, subValuesMin.floatValue);
//Max Field
EditorGUI.LabelField(maxLabel, "Max");
subValuesMax.floatValue = EditorGUI.FloatField(maxVal, subValuesMax.floatValue);
//EditorGUI.indentLevel = prevIndent;
//VALUE MODIFIERS AREA
var legacyModifiersProp = subValuesVal.FindPropertyRelative("_modifiers");
var legacyModifiersCount = legacyModifiersProp.arraySize;
var modifyingDNAProp = subValuesVal.FindPropertyRelative("_modifyingDNA");
var modifyingDNACount = modifyingDNAProp.FindPropertyRelative("_dnaEvaluators").arraySize;
currRect = new Rect(currRect.xMin, valMinMaxArea.yMax + 2f, currRect.width, EditorGUIUtility.singleLineHeight);
modifiersArea = new Rect(currRect.xMin, currRect.yMin, currRect.width, ((EditorGUIUtility.singleLineHeight + padding) * (legacyModifiersCount + 2)));//plus 2 for label and add button
if(modifyingDNACount != 0 || !_allowLegacyDNADrawer)
{
modifiersArea = new Rect(currRect.xMin, currRect.yMin, currRect.width, (EditorGUI.GetPropertyHeight(modifyingDNAProp) + padding));
}
EditorGUI.DrawRect(modifiersArea, new Color32(255, 255, 255, 100));
//Pad the current Rect
currRect.xMin += padding * 2f;
currRect.width -= padding * 2f;
//When modifiers get upgraded to _modifyingDNA they get cleared
//But for now
if (modifyingDNACount == 0 && _allowLegacyDNADrawer)
{
//EditorGUI.indentLevel++;
EditorGUI.LabelField(currRect, "Value Modifiers");
//EditorGUI.indentLevel--;
//Draw modifiers list
for (int i = 0; i < legacyModifiersCount; i++)
{
currRect = new Rect(currRect.xMin, currRect.yMax + padding, currRect.width, EditorGUIUtility.singleLineHeight);
modifiersProps = new Rect(currRect.xMin, currRect.yMin, currRect.width - delButWidth, EditorGUIUtility.singleLineHeight);
modifiersDel = new Rect(modifiersProps.xMax, currRect.yMin, delButWidth, EditorGUIUtility.singleLineHeight);
thisSpValDrawer.OnGUI(modifiersProps, legacyModifiersProp.GetArrayElementAtIndex(i), new GUIContent(""));
if (GUI.Button(modifiersDel, "X"))
{
legacyModifiersProp.DeleteArrayElementAtIndex(i);
}
}
//Draw the add button
modifiersAdd = new Rect(currRect.xMax - addButWidth, currRect.yMax + padding, addButWidth, EditorGUIUtility.singleLineHeight);
if (GUI.Button(modifiersAdd, "Add"))
{
legacyModifiersProp.InsertArrayElementAtIndex(legacyModifiersCount);
}
legacyModifiersProp.serializedObject.ApplyModifiedProperties();
}
else
{
var thisModifyingDNARect = new Rect(currRect.xMin, currRect.yMin, currRect.width, position.height - currRect.yMax);
EditorGUI.PropertyField(thisModifyingDNARect, modifyingDNAProp);
}
}
}
EditorGUI.indentLevel = startingIndent;
EditorGUI.EndProperty();
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float h = EditorGUIUtility.singleLineHeight + padding;
int extraLines = 1;
if (property.isExpanded)
{
extraLines = extraLines + 3;
var valuesX = property.FindPropertyRelative("_valuesX");
var valuesY = property.FindPropertyRelative("_valuesY");
var valuesZ = property.FindPropertyRelative("_valuesZ");
//valuesX is always expanded now if nothing else is
//if (valuesX.isExpanded || valuesY.isExpanded || valuesZ.isExpanded)
//{
extraLines++;
var activeValues = valuesX.isExpanded ? valuesX : (valuesY.isExpanded ? valuesY : (valuesZ.isExpanded ? valuesZ : valuesX));
var subValuesVal = activeValues.FindPropertyRelative("_val");
if (subValuesVal.isExpanded)
{
extraLines++;
}
var modifyingDNAProp = subValuesVal.FindPropertyRelative("_modifyingDNA");
var modifyingDNACount = modifyingDNAProp.FindPropertyRelative("_dnaEvaluators").arraySize;
var legacyModifiersCount = subValuesVal.FindPropertyRelative("_modifiers").arraySize;
//When modifiers get upgraded to _modifyingDNA they get cleared
//But for now
if (modifyingDNACount == 0 && _allowLegacyDNADrawer)
{
extraLines++;
extraLines += legacyModifiersCount;
//extrapix = 10f;
extraLines++;
extraLines++;
}
/*else
{
//Add _modifyingDNA (DNAEvaluators)
extraLines += modifyingDNACount + 3;
}*/
//}
h *= (extraLines);
if(modifyingDNACount != 0 || !_allowLegacyDNADrawer)
{
h += (EditorGUI.GetPropertyHeight(modifyingDNAProp) + padding) + padding *3;
}
}
return h;
}
}
#region LEGACY MODIFIERS DRAWER
[CustomPropertyDrawer(typeof(SkeletonModifier.spVal.spValValue.spValModifier))]
[System.Obsolete("Do not use. Will be removed in a future version")]
public class spValModifierPropertyDrawer : PropertyDrawer
{
public string[] dnaNames;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
int startingIndent = EditorGUI.indentLevel;
EditorGUI.BeginProperty(position, label, property);
var valR = new Rect(position.xMin, position.yMin, position.width, EditorGUIUtility.singleLineHeight);
var ddOne = valR;
var ddTwo = valR;
ddOne.width = valR.width / 2f + ((EditorGUI.indentLevel - 1) * 10);
ddTwo.width = valR.width / 2f - ((EditorGUI.indentLevel - 1) * 10);
ddTwo.x = ddTwo.x + ddOne.width;
var modifieri = property.FindPropertyRelative("_modifier").enumValueIndex;
EditorGUI.PropertyField(ddOne, property.FindPropertyRelative("_modifier"), new GUIContent(""));
EditorGUI.indentLevel = 0;
if (modifieri > 3)
{
string currentVal = property.FindPropertyRelative("_DNATypeName").stringValue;
if (dnaNames == null || dnaNames.Length == 0)
{
//If there are no names show a field with the dna name in it with a warning tooltip
//NOPE we should just show a manual field for this
/*EditorGUI.BeginDisabledGroup(true);
EditorGUI.LabelField(ddTwo, new GUIContent(property.FindPropertyRelative("_DNATypeName").stringValue, "You do not have any DNA Names set up in your DNA asset. Add some names for the Skeleton Modifiers to use."), EditorStyles.textField);
EditorGUI.EndDisabledGroup();*/
currentVal = EditorGUI.TextField(ddTwo, currentVal);
}
else
{
int selectedIndex = -1;
List<GUIContent> niceDNANames = new List<GUIContent>();
niceDNANames.Add(new GUIContent("None"));
bool missing = currentVal != "";
for (int i = 0; i < dnaNames.Length; i++)
{
niceDNANames.Add(new GUIContent(dnaNames[i]));
if (dnaNames[i] == currentVal)
{
selectedIndex = i;
missing = false;
}
}
if (missing)
{
niceDNANames[0].text = currentVal + " (missing)";
niceDNANames[0].tooltip = currentVal + " was not in the DNAAssets names list. This modifier wont do anything until you change the dna name it uses or you add this name to your DNA Asset names.";
}
int newSelectedIndex = selectedIndex == -1 ? 0 : selectedIndex + 1;
EditorGUI.BeginChangeCheck();
newSelectedIndex = EditorGUI.Popup(ddTwo, newSelectedIndex, niceDNANames.ToArray());
if (EditorGUI.EndChangeCheck())
{
//if its actually changed
if (newSelectedIndex != selectedIndex + 1)
{
if (newSelectedIndex == 0)
{
if (niceDNANames[0].text.IndexOf("(missing) ") < 0)
{
property.FindPropertyRelative("_DNATypeName").stringValue = "";
}
}
else
{
property.FindPropertyRelative("_DNATypeName").stringValue = dnaNames[newSelectedIndex - 1];
}
}
}
}
}
else
{
EditorGUI.PropertyField(ddTwo, property.FindPropertyRelative("_modifierValue"), new GUIContent(""));
}
EditorGUI.indentLevel = startingIndent;
EditorGUI.EndProperty();
}
}
#endregion
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 0ec99b5a24536c64c9d98179930675cf
timeCreated: 1458784838
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/Extensions/DynamicCharacterSystem/SkeletonModifierPropertyDrawer.cs
uploadId: 679826
@@ -0,0 +1,70 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UMA.CharacterSystem;
namespace UMA.Editors
{
[CustomEditor(typeof(UMADynamicCharacterAvatarRecipe), true)]
public partial class UMADynamicCharacterAvatarRecipeEditor : RecipeEditor
{
protected override bool PreInspectorGUI()
{
hideToolBar = false;
hideRaceField = false;//hide race field is topsyturvy its about hiding our EXTRA race field (above the toolbar)
return TextRecipeGUI();
}
/// <summary>
/// Impliment this method to output any extra GUI for any extra fields you have added to UMADynamicCharacterAvatarRecipe before the main RecipeGUI
/// </summary>
partial void PreRecipeGUI(ref bool changed);
/// <summary>
/// Impliment this method to output any extra GUI for any extra fields you have added to UMADynamicCharacterAvatarRecipe after the main RecipeGUI
/// </summary>
partial void PostRecipeGUI(ref bool changed);
protected override bool PostInspectorGUI()
{
bool changed = false;
PostRecipeGUI(ref changed);
return changed;
}
protected virtual bool TextRecipeGUI()
{
Type TargetType = target.GetType();
bool doUpdate = false;
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.Popup("Recipe Type", 0, new string[] { "DynamicCharacterAvatar" });
EditorGUI.EndDisabledGroup();
PreRecipeGUI(ref doUpdate);
//draws a button to 'Add DNA' when a new 'standard' recipe is created
if (AddDNAButtonUI())
{
hideToolBar = false;
return true;
}
//fixes dna when the recipes race has updated from UMADnaHumanoid/Tutorial to DynamicDna
if (FixDNAConverters())
{
hideToolBar = false;
return true;
}
FieldInfo ActiveWardrobeSetField = TargetType.GetField("activeWardrobeSet", BindingFlags.Public | BindingFlags.Instance);
List<WardrobeSettings> activeWardrobeSet = (List<WardrobeSettings>)ActiveWardrobeSetField.GetValue(target);
slotEditor = new WardrobeSetMasterEditor(_recipe, activeWardrobeSet);
return doUpdate;
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 9c29dc37dd2b4ef48a9c46c27b070b6a
timeCreated: 1484778203
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/Extensions/DynamicCharacterSystem/UMADynamicCharacterAvatarRecipeEditor.cs
uploadId: 679826
@@ -0,0 +1,420 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UMA.CharacterSystem;
namespace UMA.Editors
{
[CustomEditor(typeof(UMARandomizer))]
public class UMARandomizerEditor : Editor
{
UMARandomizer currentTarget = null;
public void OnEnable()
{
currentTarget = target as UMARandomizer;
List<string> Races = new List<string>();
currentTarget.raceDatas = new List<RaceData>();
currentTarget.raceDatas = UMAAssetIndexer.Instance.GetAllAssets<RaceData>();
for (int i = 0; i < currentTarget.raceDatas.Count; i++)
{
RaceData race = currentTarget.raceDatas[i];
if (race != null)
{
Races.Add(race.name);
}
}
currentTarget.races = Races.ToArray();
}
protected bool 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)
{
currentTarget.droppedItems.Clear();
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])
{
if (draggedObjects[i] is UMAWardrobeRecipe)
{
UMAWardrobeRecipe utr = draggedObjects[i] as UMAWardrobeRecipe;
currentTarget.droppedItems.Add(utr);
continue;
}
var path = AssetDatabase.GetAssetPath(draggedObjects[i]);
if (System.IO.Directory.Exists(path))
{
RecursiveScanFoldersForAssets(path);
}
}
}
}
}
return currentTarget.droppedItems.Count > 0;
}
protected void RecursiveScanFoldersForAssets(string path)
{
var assetFiles = System.IO.Directory.GetFiles(path, "*.asset");
for (int i = 0; i < assetFiles.Length; i++)
{
string assetFile = assetFiles[i];
var tempRecipe = AssetDatabase.LoadAssetAtPath(assetFile, typeof(UMAWardrobeRecipe)) as UMAWardrobeRecipe;
if (tempRecipe)
{
currentTarget.droppedItems.Add(tempRecipe);
}
}
string[] array = System.IO.Directory.GetDirectories(path);
for (int i = 0; i < array.Length; i++)
{
string subFolder = array[i];
RecursiveScanFoldersForAssets(subFolder.Replace('\\', '/'));
}
}
public bool RandomColorsGUI(RandomAvatar ra, RandomWardrobeSlot rws, RandomColors rc)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Shared Color", GUILayout.Width(80));
rc.CurrentColor = EditorGUILayout.Popup(rc.CurrentColor, rws.PossibleColors,GUILayout.Width(80));
rc.ColorName = rws.PossibleColors[rc.CurrentColor];
EditorGUILayout.LabelField("Color Table", GUILayout.Width(80));
rc.ColorTable = (SharedColorTable)EditorGUILayout.ObjectField(rc.ColorTable, typeof(SharedColorTable),false,GUILayout.ExpandWidth(true));
bool retval = GUILayout.Button("\u0078", EditorStyles.miniButton, GUILayout.ExpandWidth(false));
EditorGUILayout.EndHorizontal();
return retval;
}
public void RandomColorsGUI(RandomAvatar ra, RandomColors rc)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Shared Color", GUILayout.Width(80));
EditorGUILayout.LabelField(rc.ColorName, EditorStyles.textField, GUILayout.Width(80));
EditorGUILayout.LabelField("Color Table", GUILayout.Width(80));
rc.ColorTable = (SharedColorTable)EditorGUILayout.ObjectField(rc.ColorTable, typeof(SharedColorTable), false, GUILayout.ExpandWidth(true));
EditorGUILayout.EndHorizontal();
}
public void RandomWardrobeSlotGUI(RandomAvatar ra, RandomWardrobeSlot rws)
{
// do random colors
// show each possible item.
string name = "<null>";
if (rws.WardrobeSlot != null)
{
name = rws.WardrobeSlot.name;
}
GUIHelper.FoldoutBar(ref rws.GuiFoldout, name + " ("+rws.Chance+")", out rws.Delete);
if (rws.GuiFoldout)
{
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.75f, 0.75f));
rws.Chance = EditorGUILayout.IntSlider("Weighted Chance",rws.Chance, 1, 100);
if (rws.PossibleColors.Length > 0)
{
if (GUILayout.Button("Add Shared Color"))
{
rws.AddColorTable = true;
}
RandomColors delme = null;
for (int i = 0; i < rws.Colors.Count; i++)
{
RandomColors rc = rws.Colors[i];
if (RandomColorsGUI(ra, rws, rc))
{
delme = rc;
}
}
if (delme != null)
{
rws.Colors.Remove(delme);
EditorUtility.SetDirty(this.target);
string path = AssetDatabase.GetAssetPath(target.GetInstanceID());
AssetDatabase.ImportAsset(path);
}
}
else
{
GUILayout.Label("Wardrobe Recipe has no Shared Colors");
}
GUIHelper.EndVerticalPadded(10);
}
}
public void RandomAvatarGUI(RandomAvatar ra)
{
if (ra.raceData == null)
{
ra.raceData = UMAAssetIndexer.Instance.GetAsset<RaceData>(ra.RaceName);
}
bool del = false;
GUIHelper.FoldoutBar(ref ra.GuiFoldout, ra.RaceName, out del);
if (ra.GuiFoldout)
{
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
if (del)
{
ra.Delete = true;
}
ra.Chance = EditorGUILayout.IntSlider("Weighted Chance", ra.Chance, 1, 100);
ra.ColorsFoldout = GUIHelper.FoldoutBar(ra.ColorsFoldout, "Colors");
if (ra.ColorsFoldout)
{
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.75f, 0.75f));
if (ra.SharedColors != null && ra.SharedColors.Count > 0)
{
for (int i = 0; i < ra.SharedColors.Count; i++)
{
RandomColors rc = ra.SharedColors[i];
RandomColorsGUI(ra, rc);
}
}
else
{
EditorGUILayout.LabelField("No shared colors found on base race");
}
GUIHelper.EndVerticalPadded(10);
}
ra.DnaFoldout = GUIHelper.FoldoutBar(ra.DnaFoldout, "DNA");
if (ra.DnaFoldout)
{
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.75f, 0.75f));
// (popup with DNA names) and "Add" button.
EditorGUILayout.BeginHorizontal();
ra.SelectedDNA = EditorGUILayout.Popup("DNA", ra.SelectedDNA, ra.PossibleDNA);
bool pressed = GUILayout.Button("Add DNA", EditorStyles.miniButton);// GUIStyles.Popup?
EditorGUILayout.EndHorizontal();
if (pressed)
{
ra.DNAAdd = ra.PossibleDNA[ra.SelectedDNA];
}
if (ra.RandomDna.Count == 0)
{
EditorGUILayout.LabelField("No Random DNA has been added");
}
else
{
for (int i = 0; i < ra.RandomDna.Count; i++)
{
RandomDNA rd = ra.RandomDna[i];
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(rd.DnaName, EditorStyles.miniLabel, GUILayout.Width(100));
float lastMin = rd.MinValue;
float lastMax = rd.MaxValue;
EditorGUILayout.MinMaxSlider(ref rd.MinValue, ref rd.MaxValue, 0.0f, 1.0f);
if (rd.MinValue != lastMin || rd.MaxValue != lastMax)
{
ra.DnaChanged = true;
}
rd.Delete = GUILayout.Button("\u0078", EditorStyles.miniButton, GUILayout.ExpandWidth(false));
string vals = rd.MinValue.ToString("N3") +" - " +rd.MaxValue.ToString("N3");
EditorGUILayout.LabelField(vals, EditorStyles.miniTextField, GUILayout.Width(80));
EditorGUILayout.EndHorizontal();
}
}
GUIHelper.EndVerticalPadded(10);
}
ra.WardrobeFoldout = GUIHelper.FoldoutBar(ra.WardrobeFoldout, "Wardrobe");
if (ra.WardrobeFoldout)
{
// add a null slot for a
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Select Wardrobe Slot", GUILayout.ExpandWidth(false));
ra.currentWardrobeSlot = EditorGUILayout.Popup(ra.currentWardrobeSlot, ra.raceData.wardrobeSlots.ToArray(), GUILayout.ExpandWidth(true));
if (GUILayout.Button("Add Null",GUILayout.ExpandWidth(false)))
{
ra.RandomWardrobeSlots.Add(new RandomWardrobeSlot(null,ra.raceData.wardrobeSlots[ra.currentWardrobeSlot]));
ra.RandomWardrobeSlots.Sort((x, y) => x.SortName.CompareTo(y.SortName));
}
GUILayout.EndHorizontal();
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.75f, 0.75f));
string lastSlot = "";
for (int i = 0; i < ra.RandomWardrobeSlots.Count; i++)
{
RandomWardrobeSlot rws = ra.RandomWardrobeSlots[i];
if (rws.SlotName != lastSlot)
{
GUILayout.Label("[" + rws.SlotName + "]");
lastSlot = rws.SlotName;
}
RandomWardrobeSlotGUI(ra, rws);
}
GUIHelper.EndVerticalPadded(10);
}
GUIHelper.EndVerticalPadded(10);
}
}
public override void OnInspectorGUI()
{
if (Event.current.type == EventType.Layout)
{
UpdateObject();
}
currentTarget.currentRace = EditorGUILayout.Popup("First Select Race", currentTarget.currentRace, currentTarget.races);
GUILayout.Space(20);
Rect updateDropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
GUI.Box(updateDropArea, "Then Drag Wardrobe Recipe(s) for "+ currentTarget.races[currentTarget.currentRace] + " here");
GUILayout.Space(10);
DropAreaGUI(updateDropArea);
GUILayout.Space(10);
for (int i = 0; i < currentTarget.RandomAvatars.Count; i++)
{
RandomAvatar ra = currentTarget.RandomAvatars[i];
RandomAvatarGUI(ra);
}
if (GUI.changed)
{
EditorUtility.SetDirty(currentTarget);
string path = AssetDatabase.GetAssetPath(currentTarget.GetInstanceID());
AssetDatabase.ImportAsset(path);
}
}
private void UpdateObject()
{
// Add any dropped items.
int ChangeCount = currentTarget.droppedItems.Count;
if (currentTarget.droppedItems.Count > 0)
{
for (int i = 0; i < currentTarget.RandomAvatars.Count; i++)
{
RandomAvatar rv = currentTarget.RandomAvatars[i];
rv.GuiFoldout = false;
for (int i1 = 0; i1 < rv.RandomWardrobeSlots.Count; i1++)
{
RandomWardrobeSlot rws = rv.RandomWardrobeSlots[i1];
rws.GuiFoldout = false;
}
}
RandomAvatar ra = FindAvatar(currentTarget.raceDatas[currentTarget.currentRace]);
// Add all the wardrobe items.
for (int i = 0; i < currentTarget.droppedItems.Count; i++)
{
UMAWardrobeRecipe uwr = currentTarget.droppedItems[i];
if (RecipeCompatible(uwr, currentTarget.raceDatas[currentTarget.currentRace]))
{
RandomWardrobeSlot rws = new RandomWardrobeSlot(uwr,uwr.wardrobeSlot);
ra.GuiFoldout = true;
ra.RandomWardrobeSlots.Add(rws);
}
}
// sort the wardrobe slots
ra.RandomWardrobeSlots.Sort((x, y) => x.SortName.CompareTo(y.SortName));
currentTarget.droppedItems.Clear();
}
ChangeCount += currentTarget.RandomAvatars.RemoveAll(x => x.Delete);
foreach(RandomAvatar ra in currentTarget.RandomAvatars)
{
if (!string.IsNullOrEmpty(ra.DNAAdd))
{
ra.DnaChanged = true;
ra.RandomDna.Add(new RandomDNA(ra.DNAAdd));
ra.DNAAdd = "";
ChangeCount++;
}
int DNAChangeCount = ra.RandomDna.RemoveAll(x => x.Delete);
if (DNAChangeCount > 0)
{
ra.DnaChanged = true;
ChangeCount++;
}
ChangeCount += ra.SharedColors.RemoveAll(x => x.Delete);
ChangeCount += ra.RandomWardrobeSlots.RemoveAll(x => x.Delete);
for (int i = 0; i < ra.RandomWardrobeSlots.Count; i++)
{
RandomWardrobeSlot rws = ra.RandomWardrobeSlots[i];
ChangeCount += rws.Colors.RemoveAll(x => x.Delete);
if (rws.AddColorTable)
{
rws.Colors.Add(new RandomColors(rws));
rws.AddColorTable = false;
ChangeCount++;
}
}
}
if (ChangeCount > 0)
{
EditorUtility.SetDirty(currentTarget);
string path = AssetDatabase.GetAssetPath(currentTarget.GetInstanceID());
AssetDatabase.ImportAsset(path);
}
}
private bool RecipeCompatible(UMAWardrobeRecipe uwr, RaceData raceData)
{
// first, see if the recipe is directly compatible with the race.
for (int i = 0; i < uwr.compatibleRaces.Count; i++)
{
string s = uwr.compatibleRaces[i];
if (s == raceData.raceName)
{
return true;
}
if (raceData.IsCrossCompatibleWith(s))
{
return true;
}
}
return false;
}
private RandomAvatar FindAvatar(RaceData raceData)
{
// Is the current race defined?
for (int i = 0; i < currentTarget.RandomAvatars.Count; i++)
{
RandomAvatar ra = currentTarget.RandomAvatars[i];
if (raceData.raceName == ra.RaceName)
{
return ra;
}
}
RandomAvatar rav = new RandomAvatar(raceData);
currentTarget.RandomAvatars.Add(rav);
return rav;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 8eea85addfae5224ba70fc75bf84260f
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/Extensions/DynamicCharacterSystem/UMARandomizerEditor.cs
uploadId: 679826
@@ -0,0 +1,467 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UMA.CharacterSystem;
namespace UMA.Editors
{
[CustomEditor(typeof(UMAWardrobeCollection), true)]
public partial class UMAWardrobeCollectionEditor : RecipeEditor
{
static bool coverImagesIsExpanded = false;
protected override bool PreInspectorGUI()
{
hideToolBar = true;
hideRaceField = true;
return TextRecipeGUI();
}
/// <summary>
/// Impliment this method to output any extra GUI for any extra fields you have added to UMAWardrobeCollection before the main RecipeGUI
/// </summary>
partial void PreRecipeGUI(ref bool changed);
/// <summary>
/// Impliment this method to output any extra GUI for any extra fields you have added to UMAWardrobeCollection after the main RecipeGUI
/// </summary>
partial void PostRecipeGUI(ref bool changed);
protected override bool PostInspectorGUI()
{
bool changed = false;
PostRecipeGUI(ref changed);
return changed;
}
//draws the coverImages foldout
protected virtual bool DrawCoverImagesUI(Type TargetType)
{
bool doUpdate = false;
//FieldInfos
var CoverImagesField = TargetType.GetField("coverImages", BindingFlags.Public | BindingFlags.Instance);
//field values
List<Sprite> coverImages = (List<Sprite>)CoverImagesField.GetValue(target);
//drawUI
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
GUILayout.Space(10);
coverImagesIsExpanded = EditorGUILayout.Foldout(coverImagesIsExpanded, new GUIContent("Cover Images"));
GUILayout.EndHorizontal();
if (coverImagesIsExpanded)
{
List<Sprite> prevCoverImages = coverImages;
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
EditorGUILayout.BeginHorizontal();
for (int i = 0; i < coverImages.Count; i++)
{
EditorGUI.BeginChangeCheck();
var thisImg = EditorGUILayout.ObjectField(coverImages[i], typeof(Sprite), false, GUILayout.Width(75), GUILayout.Height(75));
if (EditorGUI.EndChangeCheck())
{
if (thisImg != coverImages[i])
{
if (thisImg == null)
{
coverImages.RemoveAt(i);
}
else
{
coverImages[i] = (Sprite)thisImg;
}
doUpdate = true;
}
}
}
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Add"))
{
coverImages.Add(null);
}
if (!AreListsEqual<Sprite>(prevCoverImages, coverImages))
{
CoverImagesField.SetValue(target, coverImages);
}
GUIHelper.EndVerticalPadded(10);
}
GUILayout.Space(-5f);
return doUpdate;
}
/// <summary>
/// An editor for a WardrobeCollection. Wardrobe collections can have Shared Colors and multiple WardrobeSets, but dont need a standard Slot or DNA Editor
/// </summary>
public class WardrobeCollectionMasterEditor : SlotMasterEditor
{
private List<string> _compatibleRaces = new List<string>();
private WardrobeCollectionList _wardrobeCollection;
private List<string> _arbitraryRecipes = new List<string>();
private bool forceGUIUpdate = false;
private static string recipesAddErrMsg = "";
//int recipePickerID = -1; This is needed if we can make the recipe drop area work with 'Click To Pick'
public WardrobeCollectionMasterEditor(UMAData.UMARecipe recipe, List<string> compatibleRaces, WardrobeCollectionList wardrobeCollection, List<string> arbitraryRecipes) : base(recipe)
{
_compatibleRaces = compatibleRaces;
_wardrobeCollection = wardrobeCollection;
_arbitraryRecipes = arbitraryRecipes;
UpdateFoldouts();
recipesAddErrMsg = "";
}
public void UpdateVals(List<string> compatibleRaces, WardrobeCollectionList wardrobeCollection, List<string> arbitraryRecipes)
{
forceGUIUpdate = false;
_wardrobeCollection = wardrobeCollection;
_compatibleRaces = compatibleRaces;
forceGUIUpdate = UpdateCollectionRaces();
UpdateFoldouts();
}
private void UpdateFoldouts()
{
if (!OpenSlots.ContainsKey("wardrobeSets"))
{
OpenSlots.Add("wardrobeSets", true);
}
if (!OpenSlots.ContainsKey("arbitraryRecipes"))
{
OpenSlots.Add("arbitraryRecipes", true);
}
for (int i = 0; i < _compatibleRaces.Count; i++)
{
bool open = i == 0 ? true : false;
if (!OpenSlots.ContainsKey(_compatibleRaces[i]))
{
OpenSlots.Add(_compatibleRaces[i], open);
}
}
}
private bool UpdateCollectionRaces()
{
bool changed = false;
if (_compatibleRaces.Count == 0 && _wardrobeCollection.sets.Count > 0)
{
_wardrobeCollection.Clear();
changed = true;
}
else
{
for(int i = 0; i < _compatibleRaces.Count; i++)
{
if (!_wardrobeCollection.Contains(_compatibleRaces[i]))
{
_wardrobeCollection.Add(_compatibleRaces[i]);
changed = true;
}
}
var collectionNames = _wardrobeCollection.GetAllRacesInCollection();
for(int i = 0; i < collectionNames.Count; i++)
{
if (!_compatibleRaces.Contains(collectionNames[i]))
{
_wardrobeCollection.Remove(collectionNames[i]);
changed = true;
}
}
}
return changed;
}
public override bool OnGUI(string targetName, ref bool _dnaDirty, ref bool _textureDirty, ref bool _meshDirty)
{
var context = UMAContextBase.Instance;
if (context == null)
{
var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContextBase.";
Debug.LogWarning(_errorMessage);
return false;
}
bool changed = forceGUIUpdate;
//Make a foldout for WardrobeSets - the UI for an individual WardrobeSet is added for each compatible race in the collection
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
GUILayout.Space(10);
bool wsfoldoutOpen = OpenSlots["wardrobeSets"];
wsfoldoutOpen = EditorGUILayout.Foldout(OpenSlots["wardrobeSets"], "Wardrobe Sets");
OpenSlots["wardrobeSets"] = wsfoldoutOpen;
GUILayout.EndHorizontal();
if (wsfoldoutOpen)
{
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
EditorGUILayout.HelpBox("Wardrobe Sets are added for each 'Compatible Race' assigned above. 'SharedColors' in this section are derived from all the recipes assigned in the set and are will be applied to the Avatar when the wardrobe sets recipes are added.", MessageType.Info);
if (_compatibleRaces.Count > 0)
{
//dont show shared colors unless there are 'FullOutfits' to apply them to
if (_sharedColorsEditor.OnGUI(_recipe))
{
changed = true;
_textureDirty = true;
}
for (int i = 0; i < _compatibleRaces.Count; i++)
{
var thisRace = context.GetRace(_compatibleRaces[i]);
if (thisRace != null)
{
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
GUILayout.Space(10);
bool foldoutOpen = OpenSlots[_compatibleRaces[i]];
foldoutOpen = EditorGUILayout.Foldout(OpenSlots[_compatibleRaces[i]], " Wardrobe Set: " + _compatibleRaces[i]);
OpenSlots[_compatibleRaces[i]] = foldoutOpen;
GUILayout.EndHorizontal();
if (foldoutOpen)
{
var thisSetEditor = new WardrobeSetEditor(thisRace, _wardrobeCollection[thisRace.raceName], _recipe, false);
if (thisSetEditor.OnGUI())
{
_wardrobeCollection[thisRace.raceName] = thisSetEditor.WardrobeSet;
changed = true;
}
}
}
else
{
//Do the foldout thing but show as 'missing'
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
GUILayout.Space(10);
bool foldoutOpen = OpenSlots[_compatibleRaces[i]];
foldoutOpen = EditorGUILayout.Foldout(OpenSlots[_compatibleRaces[i]], _compatibleRaces[i] + " Wardrobe Set (Missing)");
OpenSlots[_compatibleRaces[i]] = foldoutOpen;
GUILayout.EndHorizontal();
if (foldoutOpen)
{
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
EditorGUILayout.HelpBox("_compatibleRaces[i] could not be located by the Dynamic Race Library", MessageType.Warning);
GUIHelper.EndVerticalPadded(10);
}
}
}
}
else
{
EditorGUILayout.HelpBox("Drag in compatible races at the top of this recipe and WardrobeSets for those races will show here", MessageType.Info);
}
GUIHelper.EndVerticalPadded(10);
}
GUILayout.Space(10);
//the Arbitrary Recipes section
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
GUILayout.Space(10);
bool arbiOpen = OpenSlots["arbitraryRecipes"];
arbiOpen = EditorGUILayout.Foldout(OpenSlots["arbitraryRecipes"], "Arbitrary Recipes");
OpenSlots["arbitraryRecipes"] = arbiOpen;
Rect dropArea = new Rect();
GUILayout.EndHorizontal();
if (arbiOpen)
{
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
EditorGUILayout.HelpBox("Drop recipes in to this area to create a collection that is not a full outfit or connected to any given race, for example a 'Hair Styles' pack or 'Tattoos' pack.", MessageType.Info);
dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
GUI.Box(dropArea, "Drag WardrobeRecipes here. " + recipesAddErrMsg);
if (_arbitraryRecipes.Count > 0)
{
for (int i = 0; i < _arbitraryRecipes.Count; i++)
{
GUILayout.Space(2f);
GUI.enabled = false; //we readonly to prevent typos
Rect crfRect = GUILayoutUtility.GetRect(0.0f, EditorGUIUtility.singleLineHeight, GUILayout.ExpandWidth(true));
Rect crfDelRect = crfRect;
crfRect.width = crfRect.width - 20f - 5f;
crfDelRect.width = 20f + 2f;
crfDelRect.x = crfRect.width + 20f + 10f;
EditorGUI.TextField(crfRect, _arbitraryRecipes[i]);
GUI.enabled = true;
if (GUI.Button(crfDelRect, "X"))
{
_arbitraryRecipes.RemoveAt(i);
changed = true;
}
}
}
GUIHelper.EndVerticalPadded(10);
if (AddRecipesDropAreaGUI(ref recipesAddErrMsg, dropArea, _arbitraryRecipes))
{
changed = true;
}
}
return changed;
}
// Drop area for Arbitrary Wardrobe recipes
private bool AddRecipesDropAreaGUI(ref string errorMsg, Rect dropArea, List<string> recipes)
{
Event evt = Event.current;
bool changed = false;
//make the box clickable so that the user can select raceData assets from the asset selection window
//TODO: cant make this work without layout errors. Anyone know how to fix?
/*if (evt.type == EventType.MouseUp)
{
if (dropArea.Contains(evt.mousePosition))
{
recipePickerID = EditorGUIUtility.GetControlID(new GUIContent("recipeObjectPicker"), FocusType.Passive);
EditorGUIUtility.ShowObjectPicker<UMARecipeBase>(null, false, "", recipePickerID);
}
}
if (evt.commandName == "ObjectSelectorUpdated" && EditorGUIUtility.GetObjectPickerControlID() == recipePickerID)
{
UMARecipeBase tempRecipeAsset = EditorGUIUtility.GetObjectPickerObject() as UMARecipeBase;
if (tempRecipeAsset)
{
if (AddIfWardrobeRecipe(tempRecipeAsset, recipes))
{
changed = true;
errorMsg = "";
}
else
errorMsg = "That recipe was not a Wardrobe recipe";
}
}*/
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[];
bool allAdded = true;
for (int i = 0; i < draggedObjects.Length; i++)
{
if (draggedObjects[i])
{
UMARecipeBase tempRecipeAsset = draggedObjects[i] as UMARecipeBase;
if (tempRecipeAsset)
{
if (AddIfWardrobeRecipe(tempRecipeAsset, recipes))
{
changed = true;
}
else
{
allAdded = false;
}
continue;
}
var path = AssetDatabase.GetAssetPath(draggedObjects[i]);
if (System.IO.Directory.Exists(path))
{
RecursiveScanFoldersForAssets(path, recipes);
}
}
}
if (!allAdded)
{
errorMsg = "Some of the recipes you tried to add were not Wardrobe recipes";
}
else
{
errorMsg = "";
}
}
}
return changed;
}
private bool AddIfWardrobeRecipe(UnityEngine.Object tempRecipeAsset, List<string> recipes)
{
bool added = false;
if (!recipes.Contains(tempRecipeAsset.name))
{
if(tempRecipeAsset is UMAWardrobeRecipe) {
recipes.Add(tempRecipeAsset.name);
added = true;
}
}
return added;
}
private void RecursiveScanFoldersForAssets(string path, List<string> recipes)
{
var assetFiles = System.IO.Directory.GetFiles(path, "*.asset");
for (int i = 0; i < assetFiles.Length; i++)
{
string assetFile = assetFiles[i];
var tempRecipeAsset = AssetDatabase.LoadAssetAtPath(assetFile, typeof(UMARecipeBase)) as UMARecipeBase;
if (tempRecipeAsset)
{
AddIfWardrobeRecipe(tempRecipeAsset, recipes);
}
}
string[] array = System.IO.Directory.GetDirectories(path);
for (int i = 0; i < array.Length; i++)
{
string subFolder = array[i];
RecursiveScanFoldersForAssets(subFolder.Replace('\\', '/'), recipes);
}
}
}
protected virtual bool TextRecipeGUI()
{
Type TargetType = target.GetType();
bool doUpdate = false;
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.Popup("Recipe Type", 0, new string[] { "WardrobeCollection"});
EditorGUI.EndDisabledGroup();
PreRecipeGUI(ref doUpdate);
FieldInfo CompatibleRacesField = TargetType.GetField("compatibleRaces", BindingFlags.Public | BindingFlags.Instance);
//WardrobeCollections use the WardrobeSlot field to allow the user to define a Collection Group
FieldInfo WardrobeSlotField = TargetType.GetField("wardrobeSlot", BindingFlags.Public | BindingFlags.Instance);
string wardrobeSlot = (string)WardrobeSlotField.GetValue(target);
List<string> compatibleRaces = (List<string>)CompatibleRacesField.GetValue(target);
//FieldInfos
FieldInfo WardrobeCollectionList = TargetType.GetField("wardrobeCollection", BindingFlags.Public | BindingFlags.Instance);
FieldInfo ArbitraryRecipesList = TargetType.GetField("arbitraryRecipes", BindingFlags.Public | BindingFlags.Instance);
//field values
WardrobeCollectionList wardrobeCollection = (WardrobeCollectionList)WardrobeCollectionList.GetValue(target);
List<string> arbitraryRecipes = (List<string>)ArbitraryRecipesList.GetValue(target);
if (slotEditor == null || slotEditor.GetType() != typeof(WardrobeCollectionMasterEditor))
{
slotEditor = new WardrobeCollectionMasterEditor(_recipe, compatibleRaces, wardrobeCollection, arbitraryRecipes);
}
else
{
(slotEditor as WardrobeCollectionMasterEditor).UpdateVals(compatibleRaces, wardrobeCollection, arbitraryRecipes);
}
//wardrobe collection also has a 'cover image' field
if (DrawCoverImagesUI(TargetType))
{
doUpdate = true;
}
//CompatibleRaces drop area
if (DrawCompatibleRacesUI(TargetType))
{
doUpdate = true;
}
EditorGUILayout.Space();
//Draw the Wardrobe slot field as a WardrobeCollection Group text field.
EditorGUILayout.HelpBox("When a collection is placed on an avatar it replaces any other collections belonging to this group and unloads that collections recipes", MessageType.Info);
var newWardrobeSlot = EditorGUILayout.DelayedTextField("Collection Group", wardrobeSlot);
if(newWardrobeSlot != wardrobeSlot)
{
WardrobeSlotField.SetValue(target, newWardrobeSlot);
doUpdate = true;
}
EditorGUILayout.Space();
return doUpdate;
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 6b1d38271f518df489fe12b05984d395
timeCreated: 1484365764
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/Extensions/DynamicCharacterSystem/UMAWardrobeCollectionEditor.cs
uploadId: 679826
@@ -0,0 +1,79 @@
#if UNITY_EDITOR
using System;
using UnityEditor;
using UMA.CharacterSystem;
namespace UMA.Editors
{
[CustomEditor(typeof(UMAWardrobeRecipe), true)]
public partial class UMAWardrobeRecipeEditor : RecipeEditor
{
public static bool ShowHelp = false;
protected override bool PreInspectorGUI()
{
hideToolBar = false;
hideRaceField = false;//hide race field is topsyturvy its about hiding our EXTRA race field (above the toolbar)
return TextRecipeGUI();
}
/// <summary>
/// Impliment this method to output any extra GUI for any extra fields you have added to UMAWardrobeRecipe before the main RecipeGUI
/// </summary>
partial void PreRecipeGUI(ref bool changed);
/// <summary>
/// Impliment this method to output any extra GUI for any extra fields you have added to UMAWardrobeRecipe after the main RecipeGUI
/// </summary>
partial void PostRecipeGUI(ref bool changed);
protected override bool PostInspectorGUI()
{
bool changed = false;
PostRecipeGUI(ref changed);
return changed;
}
protected virtual bool TextRecipeGUI()
{
Type TargetType = target.GetType();
bool doUpdate = false;
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.Popup("Recipe Type", 0, new string[] { "Wardrobe" });
EditorGUI.EndDisabledGroup();
PreRecipeGUI(ref doUpdate);
hideRaceField = true;
hideToolBar = true;
//slotEditor = new WardrobeRecipeMasterEditor(_recipe, target);
ShowHelp = EditorGUILayout.Toggle("Show Help", ShowHelp);
//CompatibleRaces drop area
if (DrawCompatibleRacesUI(TargetType, ShowHelp))
{
doUpdate = true;
}
//wardrobeSlots fields
if (DrawWardrobeSlotsFields(TargetType, ShowHelp))
{
doUpdate = true;
}
if (DrawIncompatibleSlots(ShowHelp))
{
doUpdate = true;
}
//Set this up after the other so we can send the popup data with it
slotEditor = new WardrobeRecipeMasterEditor(_recipe, generatedBaseSlotOptions, generatedBaseSlotOptionsLabels);
EditorGUILayout.Space();
return doUpdate;
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: a8cc309eb11dd984494cbf213894095f
timeCreated: 1484778078
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/Extensions/DynamicCharacterSystem/UMAWardrobeRecipeEditor.cs
uploadId: 679826
@@ -0,0 +1,318 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UMA.CharacterSystem;
namespace UMA.Editors
{
/// <summary>
/// This editor tool is used for generating the bone objects on a race so that they can be referenced in the editor.
/// </summary>
public class UmaBoneBuilderWindow : EditorWindow
{
public GameObject umaObject;
public UMARecipeBase baseRecipe;
public bool removeUMAData = true;
public bool saveAvatar = false;
private UMAData _umaData;
private Animator _animator;
private int _umaBoneCount;
private UMATransform[] _umaBones;
private GameObject newUmaObj = null;
private DynamicCharacterAvatar _avatar = null;
[UnityEditor.MenuItem("GameObject/UMA/Create Rig using Bone Builder", false,10)]
[UnityEditor.MenuItem("CONTEXT/DynamicCharacterAvatar/Bone Builder")]
public static void RunBoneBuilder()
{
UmaBoneBuilderWindow window = (UmaBoneBuilderWindow)EditorWindow.GetWindow(typeof(UmaBoneBuilderWindow));
window.titleContent.text = "Bone Builder";
}
[UnityEditor.MenuItem("GameObject/UMA/Bone Builder",true, 10)]
public static bool BoneBuilderValidate()
{
GameObject go = Selection.activeGameObject;
if (go != null)
{
DynamicCharacterAvatar dca = go.GetComponent<DynamicCharacterAvatar>();
if (dca != null)
{
return true;
}
}
return false;
}
[MenuItem("UMA/Bone Builder", priority = 20)]
public static void OpenUmaTexturePrepareWindow()
{
UmaBoneBuilderWindow window = (UmaBoneBuilderWindow)EditorWindow.GetWindow(typeof(UmaBoneBuilderWindow));
window.titleContent.text = "Bone Builder";
}
void Awake()
{
GameObject go = Selection.activeGameObject;
if (go != null)
{
DynamicCharacterAvatar dca = go.GetComponent<DynamicCharacterAvatar>();
if (dca != null)
{
umaObject = go;
_avatar = go.GetComponent<DynamicCharacterAvatar>();
}
}
else
{
}
}
void OnGUI()
{
GUILayout.Label("UMA Bone Builder");
GUILayout.Space(20);
if (Application.isPlaying)
{
EditorGUILayout.HelpBox("The bone builder should not be used while the scene is running.", MessageType.Info);
return;
}
newUmaObj = EditorGUILayout.ObjectField ("UMA GameObject ", umaObject, typeof(GameObject), true) as GameObject;
//prevent being able to set set a prefab in the bone builder. It will not work.
if (newUmaObj != null && EditorUtility.IsPersistent(newUmaObj))
{
newUmaObj = null;
}
if (newUmaObj != umaObject)
{
umaObject = newUmaObj;
if(newUmaObj != null)
{
_avatar = umaObject.GetComponent<DynamicCharacterAvatar>();
}
}
if (umaObject != null && _avatar == null)
{
EditorGUILayout.HelpBox("This UMA is not a DynamicCharacterAvatar so we need to supply the base recipe.", MessageType.Info);
baseRecipe = EditorGUILayout.ObjectField("Base Recipe", baseRecipe, typeof(UMARecipeBase), false) as UMARecipeBase;
}
else
{
baseRecipe = null;
}
removeUMAData = EditorGUILayout.Toggle(new GUIContent("Remove UMAData", "A recipe and UMAData is created during the bone generation process, checking this will remove it at the end of the process. (Recommended)"), removeUMAData);
// Currently, this produces an avatar with it's arms twisted.
// saveAvatar = EditorGUILayout.Toggle(new GUIContent("Save Mecanim Avatar", "This will save the Mecanim Avatar generated as an asset."), saveAvatar);
GUILayout.Label("You can save the avatar at runtime using the option on the UMA/Runtime menu.", EditorStyles.wordWrappedMiniLabel);
if (GUILayout.Button("Generate Bones"))
{
if (umaObject == null)
{
Debug.LogWarning ("UMA GameObject not set!");
return;
}
if (_avatar != null && _avatar.activeRace.data == null)
{
Debug.LogWarning ("No recipe data found. Make sure the race is added to the library!");
return;
}
if (_avatar != null)
{
baseRecipe = _avatar.activeRace.data.baseRaceRecipe;
}
if (baseRecipe == null)
{
Debug.LogWarning("BaseRecipe not set!");
return;
}
Debug.Log("Processing...");
InitializeUMAData ();
FindBones ();
EnsureRoot ();
CreateBoneTransforms ();
InitializeAnimator ();
if( removeUMAData )
{
Cleanup();
}
Debug.Log ("Completed!");
this.Close();
}
}
private void InitializeUMAData()
{
if (umaObject == null)
{
return;
}
if (baseRecipe == null)
{
return;
}
//Adds the umaData component
if (_umaData == null)
{
_umaData = umaObject.AddComponent<UMAData>();
}
if (_umaData == null)
{
return;
}
//Create a new recipe objects
if ( _umaData.umaRecipe == null)
{
_umaData.umaRecipe = new UMAData.UMARecipe ();
}
baseRecipe.Load(_umaData.umaRecipe, UMAContextBase.Instance);
Debug.Log ("UMAData initialization successful!");
}
private void InitializeAnimator()
{
if (umaObject == null)
{
return;
}
UMAContextBase uc = UMAContextBase.Instance;
if (uc == null)
{
return;
}
UMAGeneratorBase ugb = uc.gameObject.GetComponentInChildren<UMAGeneratorBase>();
_animator = umaObject.gameObject.GetComponent<Animator> ();
if (_animator == null)
{
_animator = umaObject.gameObject.AddComponent<Animator> ();
}
var umaTransform = umaObject.transform;
var oldParent = umaTransform.parent;
var originalRot = umaTransform.localRotation;
var originalPos = umaTransform.localPosition;
umaTransform.SetParent(null, false);
umaTransform.localRotation = Quaternion.identity;
umaTransform.localPosition = Vector3.zero;
_umaData.KeepAvatar = false;
UMAGeneratorBase.SetAvatar(_umaData, _animator);
if (ugb != null)
{
ugb.UpdateAvatar(_umaData);
}
umaTransform.SetParent(oldParent, false);
umaTransform.localRotation = originalRot;
umaTransform.localPosition = originalPos;
//if (saveAvatar)
// AssetDatabase.CreateAsset(_animator.avatar, "Assets/CreatedAvatar.asset");
}
private void FindBones()
{
//get all the umaBones and umaBoneCount
Dictionary<string, UMATransform> boneDict = new Dictionary<string, UMATransform> ();
for (int i = 0; i < _umaData.umaRecipe.slotDataList.Length; i++)
{
if (_umaData.umaRecipe.slotDataList [i] != null) {
for (int j = 0; j < _umaData.umaRecipe.slotDataList [i].asset.meshData.umaBoneCount; j++) {
UMATransform bone = _umaData.umaRecipe.slotDataList [i].asset.meshData.umaBones [j];
if (!boneDict.ContainsKey (bone.name))
{
boneDict.Add (bone.name, bone);
}
}
}
}
_umaBoneCount = boneDict.Values.Count;
_umaBones = new UMATransform[_umaBoneCount];
boneDict.Values.CopyTo (_umaBones, 0);
}
private void EnsureRoot()
{
if (_umaData.umaRoot == null)
{
if (_umaData.gameObject.transform.Find ("Root") == null)
{
GameObject newRoot = new GameObject ("Root");
//make root of the UMAAvatar respect the layer setting of the UMAAvatar so cameras can just target this layer
newRoot.layer = _umaData.gameObject.layer;
newRoot.transform.parent = _umaData.transform;
newRoot.transform.localPosition = Vector3.zero;
newRoot.transform.localRotation = Quaternion.Euler (270f, 0, 0f);
newRoot.transform.localScale = Vector3.one;
_umaData.umaRoot = newRoot;
}
else
{
_umaData.umaRoot = _umaData.gameObject.transform.Find ("Root").gameObject;
}
if (_umaData.umaRoot.transform.Find ("Global") == null)
{
GameObject newGlobal = new GameObject ("Global");
newGlobal.transform.parent = _umaData.umaRoot.transform;
newGlobal.transform.localPosition = Vector3.zero;
newGlobal.transform.localRotation = Quaternion.Euler (90f, 90f, 0f);
}
}
if (_umaData.skeleton == null)
{
Transform globalTransform;
globalTransform = _umaData.umaRoot.transform.Find ("Global");
if (globalTransform != null)
{
_umaData.skeleton = new UMASkeleton (globalTransform,_umaData.umaGenerator);
}
}
}
private void CreateBoneTransforms()
{
for(int i = 0; i < _umaBoneCount; i++)
{
_umaData.skeleton.EnsureBone(_umaBones[i]);
}
_umaData.skeleton.EnsureBoneHierarchy();
}
private void Cleanup()
{
if( _umaData )
{
DestroyImmediate(_umaData);
}
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 56f88655d56761445bb80d2e9fd98a82
timeCreated: 1500258935
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/Extensions/DynamicCharacterSystem/UmaBoneBuilderWindow.cs
uploadId: 679826
@@ -0,0 +1,653 @@
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace UMA.CharacterSystem.Editors
{
[CustomPropertyDrawer(typeof(DynamicCharacterAvatar.WardrobeRecipeList))]
public class WardrobeRecipeListPropertyDrawer : PropertyDrawer
{
public List<string> recipes = new List<string>();
public List<string> recipeMenu = new List<string>();
public string LastRace = "";
public static int lastAdded = -1;
public static int selectedSlotIndex = 0;
// float padding = 2f;
// public DynamicCharacterSystem thisDCS;
public DynamicCharacterAvatar thisDCA;
public bool changed = false;
static bool defaultOpen = true;
Texture warningIcon;
int wardrobeRecipePickerID = -1;
bool recipesIndexed = false;
public static bool ShowOnlyCompatibleRecipes = false;
public static bool ShowOnlySelectedSlot = false;
public static bool ShowOnlyActive = false;
public static bool ToggleAll = false;
public void SetupDropdown(string race)
{
if (LastRace != race)
{
LastRace = race;
recipes.Clear();
recipeMenu.Clear();
if (thisDCA != null)
{
var availableRecipes = thisDCA.AvailableRecipes;
foreach (var slot in availableRecipes.Keys)
{
foreach (var recipe in availableRecipes[slot])
{
recipes.Add(recipe.name);
recipeMenu.Add(slot + "/" + recipe.name);
}
}
}
}
}
//Make a drop area for wardrobe recipes
private void DropAreaGUI(Rect dropArea, SerializedProperty thisRecipesProp)
{
var 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))
{
wardrobeRecipePickerID = EditorGUIUtility.GetControlID(new GUIContent("wrObjectPicker"), FocusType.Passive);
EditorGUIUtility.ShowObjectPicker<UMAWardrobeRecipe>(null, false, "", wardrobeRecipePickerID);
Event.current.Use();//stops the Mismatched LayoutGroup errors
return;
}
}
if (evt.commandName == "ObjectSelectorUpdated" && EditorGUIUtility.GetObjectPickerControlID() == wardrobeRecipePickerID)
{
UMAWardrobeRecipe uwr = EditorGUIUtility.GetObjectPickerObject() as UMAWardrobeRecipe;
recipesIndexed = false;
if (AddRecipe(thisRecipesProp, uwr))
{
if (recipesIndexed)
{
recipesIndexed = false;
UMAContextBase.Instance.ValidateDictionaries();
}
}
if (evt.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[];
ProcessDropeedRecipes(thisRecipesProp, draggedObjects);
}
}
}
private void ProcessDropeedRecipes(SerializedProperty thisRecipesProp, Object[] draggedObjects)
{
recipesIndexed = false;
for (int i = 0; i < draggedObjects.Length; i++)
{
if (draggedObjects[i])
{
UMATextRecipe tempRecipeAsset = draggedObjects[i] as UMATextRecipe;
if (!tempRecipeAsset)
{
var path = AssetDatabase.GetAssetPath(draggedObjects[i]);
if (System.IO.Directory.Exists(path))
{
RecursiveScanFoldersForAssets(path, thisRecipesProp);
}
}
if (tempRecipeAsset && (tempRecipeAsset.recipeType == "Wardrobe" || tempRecipeAsset.recipeType == "WardrobeCollection"))
{
AddRecipe(thisRecipesProp, tempRecipeAsset);
continue;
}
}
}
if (recipesIndexed)
{
recipesIndexed = false;
UMAContextBase.Instance.ValidateDictionaries();
}
}
private bool AddRecipe(SerializedProperty thisRecipesProp, UMATextRecipe tempRecipeAsset)
{
bool needToAddNew = true;
for (int ii = 0; ii < thisRecipesProp.arraySize; ii++)
{
SerializedProperty thisElement = thisRecipesProp.GetArrayElementAtIndex(ii);
if (thisElement.FindPropertyRelative("_recipeName").stringValue == tempRecipeAsset.name)
{
int compatibleRacesArraySize = tempRecipeAsset.compatibleRaces.Count;
thisRecipesProp.GetArrayElementAtIndex(ii).FindPropertyRelative("_compatibleRaces").arraySize = compatibleRacesArraySize;
for (int cr = 0; cr < compatibleRacesArraySize; cr++)
{
thisRecipesProp.GetArrayElementAtIndex(ii).FindPropertyRelative("_compatibleRaces").GetArrayElementAtIndex(cr).stringValue = tempRecipeAsset.compatibleRaces[cr];
}
needToAddNew = false;
}
}
if (needToAddNew)
{
if (!UMAContextBase.Instance.HasRecipe(tempRecipeAsset.name))
{
UMAContextBase.Instance.AddRecipe(tempRecipeAsset);
recipesIndexed = true;
}
int newArrayElIndex = thisRecipesProp.arraySize;
thisRecipesProp.InsertArrayElementAtIndex(newArrayElIndex);
thisRecipesProp.serializedObject.ApplyModifiedProperties();
thisRecipesProp.GetArrayElementAtIndex(newArrayElIndex).FindPropertyRelative("_recipeName").stringValue = tempRecipeAsset.name;
thisRecipesProp.GetArrayElementAtIndex(newArrayElIndex).FindPropertyRelative("_enabledInDefaultWardrobe").boolValue = true;
int compatibleRacesArraySize = tempRecipeAsset.compatibleRaces.Count;
thisRecipesProp.GetArrayElementAtIndex(newArrayElIndex).FindPropertyRelative("_compatibleRaces").arraySize = compatibleRacesArraySize;
for (int cr = 0; cr < compatibleRacesArraySize; cr++)
{
thisRecipesProp.GetArrayElementAtIndex(newArrayElIndex).FindPropertyRelative("_compatibleRaces").GetArrayElementAtIndex(cr).stringValue = tempRecipeAsset.compatibleRaces[cr];
}
thisRecipesProp.serializedObject.ApplyModifiedProperties();
GUI.changed = true;
changed = true;
return true;
}
return false;
}
protected void RecursiveScanFoldersForAssets(string path, SerializedProperty thisRecipesProp)
{
List<Object> droppedItems = new List<Object>();
var assetFiles = System.IO.Directory.GetFiles(path, "*.asset");
for (int i = 0; i < assetFiles.Length; i++)
{
string assetFile = assetFiles[i];
var tempRecipe = AssetDatabase.LoadAssetAtPath(assetFile, typeof(UMAWardrobeRecipe)) as UMAWardrobeRecipe;
if (tempRecipe)
{
droppedItems.Add(tempRecipe);
}
}
if (droppedItems.Count > 0)
{
ProcessDropeedRecipes(thisRecipesProp, droppedItems.ToArray());
}
string[] array = System.IO.Directory.GetDirectories(path);
for (int i = 0; i < array.Length; i++)
{
string subFolder = array[i];
RecursiveScanFoldersForAssets(subFolder.Replace('\\', '/'), thisRecipesProp);
}
}
/*public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float h = EditorGUIUtility.singleLineHeight + padding;
int extraLines = 0;
if (defaultOpen)
{
var thisRecipesProp = property.FindPropertyRelative("recipes");
extraLines = thisRecipesProp.arraySize;
h *= (extraLines + 3);// add space for button
h += 50f + padding;
}
return h;
} */
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 0;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
changed = false;
if (warningIcon == null)
{
warningIcon = EditorGUIUtility.FindTexture("console.warnicon.sml");
}
EditorGUI.BeginProperty(position, label, property);
//var r0 = new Rect(position.xMin, position.yMin, position.width, EditorGUIUtility.singleLineHeight);
defaultOpen = EditorGUILayout.Foldout(defaultOpen, "Default Wardrobe Recipes");
if (defaultOpen)
{
//var valR = r0;
//valR = new Rect(valR.xMin, valR.yMax, valR.width, EditorGUIUtility.singleLineHeight);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(property.FindPropertyRelative("loadDefaultRecipes"));
if (EditorGUI.EndChangeCheck())
{
property.serializedObject.ApplyModifiedProperties();
}
//Rect dropArea = new Rect(valR.xMin, (valR.yMax + padding), valR.width, 50f);
GUILayout.Box("Drag Wardrobe Recipes here or click to pick",GUILayout.Height(50),GUILayout.ExpandWidth(true));
Rect dropArea = GUILayoutUtility.GetLastRect();
//GUI.Box(dropArea, "Drag Wardrobe Recipes here or click to pick");
// menu/submenus for Slot/RecipeName.
// Example:
// [Head/DragonHelm ][Add Item]
// valR = new Rect(valR.xMin, (valR.yMin + 50f + padding), valR.width, EditorGUIUtility.singleLineHeight);
var thisRecipesProp = property.FindPropertyRelative("recipes");
//float textFieldWidth = (valR.width - 20f);
var warningStyle = new GUIStyle(EditorStyles.label);
warningStyle.fixedHeight = warningIcon.height + 4f;
warningStyle.contentOffset = new Vector2(0, -2f);
//can we make these validate to the compatible races is upto date?
thisDCA.preloadWardrobeRecipes.GetRecipesForRace();
GUILayout.BeginHorizontal();
if (GUILayout.Button("Enable All"))
{
for (int i = 0; i < thisRecipesProp.arraySize; i++)
{
SerializedProperty thisElement = thisRecipesProp.GetArrayElementAtIndex(i);
thisElement.FindPropertyRelative("_enabledInDefaultWardrobe").boolValue = true;
changed = true;
}
}
if (GUILayout.Button("Disable All"))
{
for (int i = 0; i < thisRecipesProp.arraySize; i++)
{
SerializedProperty thisElement = thisRecipesProp.GetArrayElementAtIndex(i);
thisElement.FindPropertyRelative("_enabledInDefaultWardrobe").boolValue = false;
changed = true;
}
}
if (GUILayout.Button("Add all"))
{
var availableRecipes = thisDCA.AvailableRecipes;
foreach (var slot in availableRecipes.Keys)
{
foreach (var recipe in availableRecipes[slot])
{
var recipeAsset = UMAContextBase.Instance.GetRecipe(recipe.name, false);
if (recipeAsset != null)
{
AddRecipe(thisRecipesProp, recipeAsset);
}
}
}
}
if (GUILayout.Button("Remove disabled"))
{
RemoveDisabled(thisRecipesProp);
changed = true;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (GUILayout.Toggle(ShowOnlyActive, "Active Only", GUILayout.Width(100)))
{
ShowOnlyActive = true;
}
else
{
ShowOnlyActive = false;
}
if (GUILayout.Toggle(ShowOnlyCompatibleRecipes, "Compatible Only", GUILayout.ExpandWidth(true)))
{
ShowOnlyCompatibleRecipes = true;
}
else
{
ShowOnlyCompatibleRecipes = false;
}
//if (GUILayout.Toggle(ShowOnlySelectedSlot, "Selected WardrobeSlot", GUILayout.ExpandWidth(true)))
//{
// ShowOnlySelectedSlot = true;
//}
//else
//{
// ShowOnlySelectedSlot = false;
//}
string selectedSlot = "";
if (thisDCA.activeRace == null || thisDCA.activeRace.data == null)
{
ShowOnlySelectedSlot = false;
EditorGUILayout.LabelField("Race is not set", GUILayout.Width(120));
GUILayout.EndHorizontal();
}
else
{
if (selectedSlotIndex >= thisDCA.activeRace.data.wardrobeSlots.Count)
{
selectedSlotIndex = 0;
}
GUILayout.Label("Wardrobe Slot", GUILayout.Width(85));
selectedSlotIndex = EditorGUILayout.Popup(selectedSlotIndex, thisDCA.activeRace.data.wardrobeSlots.ToArray(), GUILayout.Width(120));
if (selectedSlotIndex >= 0 && selectedSlotIndex < thisDCA.activeRace.data.wardrobeSlots.Count)
{
selectedSlot = thisDCA.activeRace.data.wardrobeSlots[selectedSlotIndex];
}
if (selectedSlotIndex == 0)
{
ShowOnlySelectedSlot = false;
}
else
{
ShowOnlySelectedSlot = true;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
SetupDropdown(thisDCA.activeRace.name);
ToggleAll = GUILayout.Toggle(ToggleAll, "Toggle", GUILayout.ExpandWidth(true));
if (GUILayout.Button("Sort by Slot", GUILayout.Width(100)))
{
SortBySlot(thisRecipesProp);
}
int added = -1;
EditorGUILayout.LabelField("Add Item", GUILayout.Width(60));
added = EditorGUILayout.Popup(added, recipeMenu.ToArray(), GUILayout.Width(150));
if (added >= 0)
{
var recipe = recipes[added];
var recipeAsset = UMAContextBase.Instance.GetRecipe(recipe, false);
if (recipeAsset != null)
{
AddRecipe(thisRecipesProp, recipeAsset);
}
}
GUILayout.EndHorizontal();
}
for (int i = 0; i < thisRecipesProp.arraySize; i++)
{
string currentSlot = "";
bool compatible = false;
// var valRBut = new Rect((textFieldWidth + 18f), (valR.yMax + padding), 20f, EditorGUIUtility.singleLineHeight);
// valR = new Rect(valR.xMin, (valR.yMax + padding), textFieldWidth, EditorGUIUtility.singleLineHeight);
SerializedProperty thisElement = thisRecipesProp.GetArrayElementAtIndex(i);
// UMAWardrobeRecipe currentRecipe = thisElement.objectReferenceValue as UMAWardrobeRecipe;
var recipeListItem = thisDCA.preloadWardrobeRecipes.recipes[i];
var currentRecipe = thisDCA.preloadWardrobeRecipes.recipes[i]._recipe;
if (ShowOnlySelectedSlot)
{
if (currentRecipe != null)
{
currentSlot = currentRecipe.wardrobeSlot;
if (currentSlot != selectedSlot)
{
continue;
}
}
}
if (ShowOnlyActive)
{
if (currentRecipe != null)
{
if (!recipeListItem._enabledInDefaultWardrobe)
{
continue;
}
}
}
int compatibleRacesArraySize = thisElement.FindPropertyRelative("_compatibleRaces").arraySize;
string compatibleRaces = "";
for (int cr = 0; cr < compatibleRacesArraySize; cr++)
{
string race = thisElement.FindPropertyRelative("_compatibleRaces").GetArrayElementAtIndex(cr).stringValue;
compatibleRaces = compatibleRaces + race;
if (thisDCA.activeRace != null)
{
if (thisDCA.activeRace.data != null)
{
if (thisDCA.activeRace.data.IsCrossCompatibleWith(race))
{
compatible = true;
}
if (race == thisDCA.activeRace.name)
{
compatible = true;
}
if (cr < compatibleRacesArraySize - 1)
{
compatibleRaces = compatibleRaces + ", ";
}
}
}
}
if (ShowOnlyCompatibleRecipes && compatible == false)
{
continue;
}
GUILayout.BeginHorizontal();
var recipeIsLive = true;
// var _recipe = thisElement.FindPropertyRelative("_recipe").objectReferenceValue;// as UMATextRecipe;
string recipeName = "";
if (recipeListItem != null)
{
string recipeslot = "unknown";
if (recipeListItem._recipe != null)
{
recipeslot = recipeListItem._recipe.wardrobeSlot;
}
recipeName = thisElement.FindPropertyRelative("_recipeName").stringValue;
if (UMAContext.Instance != null)
{
recipeIsLive = UMAContext.Instance.HasRecipe(recipeName);
}
string prequel = "";
if (recipeListItem._enabledInDefaultWardrobe)
{
EditorGUI.BeginDisabledGroup(false);
prequel = "+";
var currentWardrobe = thisDCA.WardrobeRecipes;
var values = currentWardrobe.Values;
foreach (var rcp in values)
{
if (rcp.name == recipeName)
{
prequel = "*";
break;
}
}
}
else
{
prequel = "-";
EditorGUI.BeginDisabledGroup(true);
}
EditorGUILayout.TextField($"{prequel}[{recipeslot}] { recipeName} ({ compatibleRaces} )",GUILayout.ExpandWidth(true));
}
else
{
EditorGUILayout.TextField("Recipe is null.", GUILayout.ExpandWidth(true));
}
EditorGUI.EndDisabledGroup();
if (!recipeIsLive && recipeListItem != null)
{
//var warningRect = new Rect((valRBut.xMin - 25f), valRBut.yMin, 20f, valRBut.height);
var warningGUIContent = new GUIContent("", recipeName + " was not Live. Click this button to add it to the Global Library.");
warningGUIContent.image = warningIcon;
//show a warning icon if the added recipe is not available from the global index (or assetBundles)
var foundRecipe = FindMissingRecipe(recipeName);
if (GUILayout.Button(warningGUIContent, warningStyle))
{
//the _recipe value is no longer serialized so we need to get it from AssetDatabase
if (foundRecipe != null)
{
UMAAssetIndexer.Instance.EvilAddAsset(foundRecipe.GetType(), foundRecipe);
}
}
}
if (GUILayout.Button("0/1",GUILayout.Width(30)))
{
if (recipeListItem._enabledInDefaultWardrobe)
{
recipeListItem._enabledInDefaultWardrobe = false;
}
else
{
if (ToggleAll)
{
string wardrobeSlot = recipeListItem._recipe.wardrobeSlot;
for (int j = 0; j < thisRecipesProp.arraySize; j++)
{
SerializedProperty thisElement2 = thisRecipesProp.GetArrayElementAtIndex(j);
var toggleRecipe = thisDCA.preloadWardrobeRecipes.recipes[j];
if (toggleRecipe._recipe.wardrobeSlot == wardrobeSlot)
{
toggleRecipe._enabledInDefaultWardrobe = false ;
}
}
}
recipeListItem._enabledInDefaultWardrobe = true;
}
changed = true;
thisRecipesProp.serializedObject.Update();
}
if (recipeListItem._recipe != null)
{
if (GUILayout.Button("Ping", GUILayout.Width(40)))
{
EditorGUIUtility.PingObject(recipeListItem._recipe);
}
if (GUILayout.Button("Insp", GUILayout.Width(40)))
{
InspectorUtlity.InspectTarget(recipeListItem._recipe);
}
}
if (GUILayout.Button("x", GUILayout.Width(15)))
{
changed = true;
thisRecipesProp.DeleteArrayElementAtIndex(i);
thisRecipesProp.serializedObject.ApplyModifiedProperties();
}
GUILayout.EndHorizontal();
}
DropAreaGUI(dropArea, thisRecipesProp);
}
EditorGUI.EndProperty();
}
private void SortBySlot(SerializedProperty thisRecipesProp)
{
// Sort the list by slot
List<DynamicCharacterAvatar.WardrobeRecipeListItem> sortedList = new List<DynamicCharacterAvatar.WardrobeRecipeListItem>();
for (int i = 0; i < thisRecipesProp.arraySize; i++)
{
sortedList.Add(thisDCA.preloadWardrobeRecipes.recipes[i]);
}
sortedList.Sort((x, y) => x._recipe.wardrobeSlot.CompareTo(y._recipe.wardrobeSlot));
thisDCA.preloadWardrobeRecipes.recipes = sortedList;
changed = true;
thisRecipesProp.serializedObject.Update();
}
private void RemoveDisabled(SerializedProperty thisRecipesProp)
{
// For each recipe in the list, if it is disabled, remove it.
for (int i = thisRecipesProp.arraySize - 1; i >= 0; i--)
{
SerializedProperty thisElement = thisRecipesProp.GetArrayElementAtIndex(i);
if (!thisElement.FindPropertyRelative("_enabledInDefaultWardrobe").boolValue)
{
thisRecipesProp.DeleteArrayElementAtIndex(i);
changed = true;
}
}
}
/// <summary>
/// with wardobeRecipes, DynamicCharacterSystem does not have a list of refrenced recipes like the other libraries
/// so the only way to get them is from DynamicAssetLoader (which is how DCS gets them)
/// so they MUST be in an assetBundle or in Global Index or there is no way of finding them
/// </summary>
/// <param name="recipeName"></param>
/// <returns></returns>
///
private UMARecipeBase FindMissingRecipe(string recipeName)
{
UMARecipeBase foundRecipe = null;
//the following will find things like femaleHair1 if 'maleHair1' is the recipe name
var foundWardrobeGUIDS = AssetDatabase.FindAssets("t:UMAWardrobeRecipe " + recipeName);
if (foundWardrobeGUIDS.Length > 0)
{
for (int i = 0; i < foundWardrobeGUIDS.Length; i++)
{
string guid = foundWardrobeGUIDS[i];
var tempAsset = AssetDatabase.LoadAssetAtPath<UMAWardrobeRecipe>(AssetDatabase.GUIDToAssetPath(guid));
if (tempAsset.name == recipeName)
{
foundRecipe = tempAsset;
break;
}
}
}
//try searching for WardrobeCollections
if (foundRecipe == null)
{
var foundWardrobeCollectionGUIDS = AssetDatabase.FindAssets("t:UMAWardrobeCollection " + recipeName);
if (foundWardrobeCollectionGUIDS.Length > 0)
{
for (int i = 0; i < foundWardrobeCollectionGUIDS.Length; i++)
{
string guid = foundWardrobeCollectionGUIDS[i];
var tempAsset = AssetDatabase.LoadAssetAtPath<UMAWardrobeCollection>(AssetDatabase.GUIDToAssetPath(guid));
if (tempAsset.name == recipeName)
{
foundRecipe = tempAsset;
break;
}
}
}
}
return foundRecipe;
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 65a87eed6bdb9f442ba91879a3afb996
timeCreated: 1458081930
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/Extensions/DynamicCharacterSystem/WardrobeRecipeListPropertyDrawer.cs
uploadId: 679826
@@ -0,0 +1,739 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UMA.CharacterSystem;
namespace UMA.Editors
{
//unfortunately this needs to be here if we are going to make it possible to have 'Backewards Compatible' DCA recipes (i.e. saved as 'Standard' but with a wardrobeSet)
//if we removed that functionality this could all go into UMADynamicCharacterAvatarRecipeEditor
public partial class RecipeEditor
{
/// <summary>
/// Draws a popup containing the available Wardrobe recipes for a particular race for a particular wardrobe slot
/// </summary>
public class WardrobeSlotRecipePopup
{
private string _wsRace;
private string _wsSlot;
private string _wsRecipeName;
private string _wcOverrideName;
Texture warningIcon;
public string RecipeName
{
get
{
return _wsRecipeName;
}
}
public WardrobeSlotRecipePopup(string race, string slot, string recipeName, string wcOverrideName = "")
{
_wsRace = race;
_wsSlot = slot;
_wsRecipeName = recipeName;
_wcOverrideName = wcOverrideName;
}
public bool OnGUI()
{
if (warningIcon == null)
{
warningIcon = EditorGUIUtility.FindTexture("console.warnicon.sml");
}
bool changed = false;
var context = UMAContextBase.Instance;
if (context == null)
{
var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContextBase.";
Debug.LogWarning(_errorMessage);
}
var recipesForRaceSlot = context.GetRecipeNamesForRaceSlot(_wsRace, _wsSlot);
List<string> thisPopupVals = new List<string>();
thisPopupVals.Add("None");
List<string> thisPopupLabels = new List<string>();
var noneString = "None";
if (_wcOverrideName != "")
{
noneString = "None (Set by '" + _wcOverrideName + "' Wardrobe Collection)";
}
thisPopupLabels.Add(noneString);
thisPopupVals.AddRange(recipesForRaceSlot);
thisPopupLabels.AddRange(recipesForRaceSlot);
var selected = 0;
var recipeIsLive = true;
Rect valRBut = new Rect();
var warningStyle = new GUIStyle(EditorStyles.label);
warningStyle.fixedHeight = warningIcon.height + 4f;
warningStyle.contentOffset = new Vector2(0, -2f);
if (_wsRecipeName != "")
{
recipeIsLive = context.CheckRecipeAvailability(_wsRecipeName);
selected = thisPopupVals.IndexOf(_wsRecipeName);
if (selected == -1)
{
selected = thisPopupVals.Count;
string missingOrIncompatible = "missing";
if (context.GetBaseRecipe(_wsRecipeName, false) != null)
{
missingOrIncompatible = "incompatible";
}
thisPopupLabels.Add(_wsRecipeName + " (" + missingOrIncompatible + ")");
}
}
var newSelected = selected;
if (!recipeIsLive)
{
EditorGUI.indentLevel++;
}
var label = _wsSlot == "WardrobeCollection" ? " " : _wsSlot;
EditorGUI.BeginChangeCheck();
newSelected = EditorGUILayout.Popup(label, selected, thisPopupLabels.ToArray());
if (!recipeIsLive)
{
EditorGUI.indentLevel--;
valRBut = GUILayoutUtility.GetLastRect();
}
if (EditorGUI.EndChangeCheck())
{
if (newSelected != selected)
{
changed = true;
_wsRecipeName = (thisPopupLabels[newSelected].IndexOf("(missing)") == -1 && thisPopupLabels[newSelected].IndexOf("(incompatible)") == -1) ? (thisPopupVals[newSelected] != "None" ? thisPopupVals[newSelected] : "") : _wsRecipeName;
}
}
if (!recipeIsLive)
{
var warningRect = new Rect((valRBut.xMin - 5f), valRBut.yMin, 20f, valRBut.height);
var warningGUIContent = new GUIContent("", _wsRecipeName + " was not Live. You can make it live by adding it to the UMA/UMA Global Library.");
warningGUIContent.image = warningIcon;
GUI.Button(warningRect, warningGUIContent, warningStyle);
//TODO we can probably use AssetIndexer.AddEvilAsset here so it gets added without having to go there
//Id like this to be a button that opens the window, opens the recipe section and ideally highlights the asset that needs to be made live
/*if(GUI.Button(warningRect, warningGUIContent, warningStyle))
{
UMAAssetIndexWindow.Init();
}*/
}
return changed;
}
}
/// <summary>
/// Draws an editor for a Wardrobe set which displays a list of popups listing all the possible recipes that could be set for any given wardrobe slot for the given race
/// </summary>
public class WardrobeSetEditor
{
private readonly UMAData.UMARecipe _recipe;
private readonly List<WardrobeSettings> _wardrobeSet;
private readonly RaceData _race;
private readonly bool _allowWardrobeCollectionSlot = true;
public List<WardrobeSettings> WardrobeSet
{
get
{
return _wardrobeSet;
}
}
public WardrobeSetEditor(RaceData race, List<WardrobeSettings> wardrobeSet, UMAData.UMARecipe recipe, bool allowWardrobeCollectionSlot)
{
_recipe = recipe;
_wardrobeSet = wardrobeSet;
_race = race;
_allowWardrobeCollectionSlot = allowWardrobeCollectionSlot;
}
public bool OnGUI()
{
bool changed = false;
if (_race != null)
{
if (_race.wardrobeSlots.Count > 0)
{
var context = UMAContextBase.Instance;
if (context == null)
{
var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContextBase.";
Debug.LogWarning(_errorMessage);
}
if (_wardrobeSet == null || context == null)
{
return false;
}
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
EditorGUILayout.HelpBox("Recently added recipes not showing up? Make sure you have added them to the 'UMA Global Library' and click the 'Refresh Recipes' button below.", MessageType.Info);
if (GUILayout.Button("Refresh Recipes"))
{
context.ValidateDictionaries();
return false;
}
//a dictionary of slots that are being assigned by WardrobeCollections
var slotsAssignedByWCs = new Dictionary<string, string>();
if (_allowWardrobeCollectionSlot)
{
var wcRecipesForRace = context.GetRecipesForRaceSlot(_race.raceName, "WardrobeCollection");
var wcGroupDict = new Dictionary<string, List<UMARecipeBase>>();
//I'm using reflection here to get fields and methods from the UMAWardrobeCollection type so this will still work if 'StandardAssets' is moved to 'Standard Assets'
for (int i = 0; i < wcRecipesForRace.Count; i++)
{
Type wcType = wcRecipesForRace[i].GetType();
if (wcType.ToString().Replace(wcType.Namespace+".", "") == "UMAWardrobeCollection")
{
FieldInfo wcRecipeSlotField = wcType.GetField("wardrobeSlot", BindingFlags.Public | BindingFlags.Instance);
var wcRecipeSlot = (string)wcRecipeSlotField.GetValue(wcRecipesForRace[i]);
if (!wcGroupDict.ContainsKey(wcRecipeSlot))
{
wcGroupDict.Add(wcRecipeSlot, new List<UMARecipeBase>());
}
wcGroupDict[wcRecipeSlot].Add(wcRecipesForRace[i]);
}
}
if (wcGroupDict.Count > 0)
{
MethodInfo WCGetRacesWardrobeSetMethod = null;
EditorGUILayout.LabelField("WardrobeCollections");
EditorGUI.indentLevel++;
foreach (KeyValuePair<string, List<UMARecipeBase>> kp in wcGroupDict)
{
if (WCGetRacesWardrobeSetMethod == null)
{
WCGetRacesWardrobeSetMethod = kp.Value[0].GetType().GetMethod("GetRacesWardrobeSet", new Type[] { typeof(RaceData) });
}
var selected = 0;
var prevRecipe = "";
var thisPopupVals = new List<string>();
thisPopupVals.Add("None");
for (int i = 0; i < kp.Value.Count; i++)
{
thisPopupVals.Add(kp.Value[i].name);
//check if this is selected
for (int wsi = 0; wsi < _wardrobeSet.Count; wsi++)
{
if (kp.Value[i].name == _wardrobeSet[wsi].recipe)
{
prevRecipe = _wardrobeSet[wsi].recipe;
selected = i + 1;
var thisWCWardrobeSet = (List<WardrobeSettings>)WCGetRacesWardrobeSetMethod.Invoke(kp.Value[i], new object[] { _race });
for (int wcwsi = 0; wcwsi < thisWCWardrobeSet.Count; wcwsi++)
{
if (!slotsAssignedByWCs.ContainsKey(thisWCWardrobeSet[wcwsi].slot))
{
slotsAssignedByWCs.Add(thisWCWardrobeSet[wcwsi].slot, kp.Value[i].name);
}
else
{
slotsAssignedByWCs[thisWCWardrobeSet[wcwsi].slot] = kp.Value[i].name;
}
}
break;
}
}
}
EditorGUI.BeginChangeCheck();
var newSelected = EditorGUILayout.Popup(kp.Key, selected, thisPopupVals.ToArray());
if (EditorGUI.EndChangeCheck())
{
for (int wsi = 0; wsi < _wardrobeSet.Count; wsi++)
{
if (_wardrobeSet[wsi].recipe == prevRecipe)
{
//we need to remove the wardrobeSettings that has prevRecipe as its value from _wardrobeSettings
if (newSelected == 0)
{
_wardrobeSet.RemoveAt(wsi);
}
else
{
//we need to make wardrobeSettings that has prevRecipe have the new value
_wardrobeSet[wsi].recipe = thisPopupVals[newSelected];
}
}
}
changed = true;
}
}
EditorGUI.indentLevel--;
EditorGUILayout.Space();
EditorGUILayout.LabelField("WardrobeSlots");
EditorGUI.indentLevel++;
}
}
for (int J = 0; J < _race.wardrobeSlots.Count; J++)
{
string wsl = _race.wardrobeSlots[J];
if (wsl == "None")
{
continue;
}
//Obsolete- now wardrobeCollections apply their WardrobeSet to any slots
//if (wsl == "FullOutfit" && _allowWardrobeCollectionSlot == false)
// continue;
WardrobeSlotRecipePopup thisPicker = null;
bool assignedPicker = false;
for (int wsi = 0; wsi < _wardrobeSet.Count; wsi++)
{
if (_wardrobeSet[wsi].slot == wsl)
{
thisPicker = new WardrobeSlotRecipePopup(_race.raceName, wsl, _wardrobeSet[wsi].recipe);
assignedPicker = true;
break;
}
}
if (!assignedPicker)//means there was nothing in the wardrobe set for it
{
//This may still be being assigned by a wardrobe collection though so show that
var wcOverrideName = "";
if (slotsAssignedByWCs.ContainsKey(wsl))
{
wcOverrideName = slotsAssignedByWCs[wsl];
}
thisPicker = new WardrobeSlotRecipePopup(_race.raceName, wsl, "");
}
if (thisPicker.OnGUI())
{
changed = true;
if (thisPicker.RecipeName != "None" && thisPicker.RecipeName != "")
{
bool contained = false;
for (int i = 0; i < _wardrobeSet.Count; i++)
{
if (_wardrobeSet[i].slot == wsl)
{
_wardrobeSet[i].recipe = thisPicker.RecipeName;
contained = true;
break;
}
}
if (!contained)
{
_wardrobeSet.Add(new WardrobeSettings(wsl, thisPicker.RecipeName));
}
}
else
{
for (int i = 0; i < _wardrobeSet.Count; i++)
{
if (_wardrobeSet[i].slot == wsl)
{
_wardrobeSet.RemoveAt(i);
break;
}
}
}
}
}
if (_allowWardrobeCollectionSlot)
{
EditorGUI.indentLevel--;
}
if (WardrobeSet.Count > 0)
{
EditorGUILayout.Space();
if (GUILayout.Button(new GUIContent("UpdateSharedColors", "Automatically adds any shared colors defined in the selected recipes to this recipes SharedColors")))
{
for (int i = 0; i < _wardrobeSet.Count; i++)
{
changed = AddSharedColorsFromRecipe(_wardrobeSet[i].recipe, _recipe) == true ? true : changed;
}
}
}
GUIHelper.EndVerticalPadded(10);
}
}
return changed;
}
/// <summary>
/// Adds the shared colors from a given recipe name into the target recipe
/// </summary>
/// <param name="sourceRecipeName"></param>
/// <param name="targetRecipe"></param>
protected virtual bool AddSharedColorsFromRecipe(string sourceRecipeName, UMAData.UMARecipe targetRecipe)
{
bool changed = false;
var thisUmaDataRecipe = new UMAData.UMARecipe();
var context = UMAContextBase.Instance;
if (context == null)
{
return false;
}
var thisWardrobeRecipe = context.GetBaseRecipe(sourceRecipeName,true);
if (thisWardrobeRecipe == null)
{
return false;
}
try
{
thisWardrobeRecipe.Load(thisUmaDataRecipe, context);
}
catch
{
return false;
}
if (thisUmaDataRecipe.sharedColors.Length > 0)
{
List<OverlayColorData> newSharedColors = new List<OverlayColorData>();
newSharedColors.AddRange(targetRecipe.sharedColors);
for (int i = 0; i < thisUmaDataRecipe.sharedColors.Length; i++)
{
bool existed = false;
for (int ii = 0; ii < newSharedColors.Count; ii++)
{
if (newSharedColors[ii].name == thisUmaDataRecipe.sharedColors[i].name)
{
existed = true;
break;
}
}
if (!existed)
{
newSharedColors.Add(thisUmaDataRecipe.sharedColors[i]);
changed = true;
}
}
if (changed)
{
targetRecipe.sharedColors = newSharedColors.ToArray();
}
}
return changed;
}
}
/// <summary>
/// Replaces the standard 'Slot' editor in a DynamicCharacterAvatar type of recipe with one that shows the assigned wardrobe recipes in its wardrobe set
/// </summary>
public class WardrobeSetMasterEditor : SlotMasterEditor
{
private List<WardrobeSettings> _wardrobeSet;
//private bool _foldout = true;
public WardrobeSetMasterEditor(UMAData.UMARecipe recipe, List<WardrobeSettings> wardrobeSet) : base(recipe)
{
_wardrobeSet = wardrobeSet;
}
public override bool OnGUI(string targetName, ref bool _dnaDirty, ref bool _textureDirty, ref bool _meshDirty)
{
bool changed = false;
if (!OpenSlots.ContainsKey("wardrobeSet"))
{
OpenSlots.Add("wardrobeSet", true);
}
if (_sharedColorsEditor.OnGUI(_recipe))
{
changed = true;
}
//if this is a backwards compatible DCS recipe (i.e. has SlotData AND a Wardrobe set) we need to show BOTH things
//for this to really work youd need to be able to edit the WardrobeSet and have that modify the slotDataList
//Hence the epic UpdateBackwardsCompatibleData method
if (_recipe.slotDataList.Length > 0)
{
EditorGUILayout.HelpBox("This is a 'Backwards Compatible' DynamicCharacterAvatar recipe. The slots and overlays in the 'BackwardsCompatibleData' section will update as you change the items in the WardrobeSet.", MessageType.Info);
}
if (DrawWardrobeSetUI())
{
changed = true;
if (_recipe.slotDataList.Length > 0)
{
UpdateBackwardsCompatibleData();
}
}
if (_recipe.slotDataList.Length > 0)
{
if (!OpenSlots.ContainsKey("backwardsCompatibleData"))
{
OpenSlots.Add("backwardsCompatibleData", false);
}
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
GUILayout.Space(10);
bool bcdfoldoutOpen = OpenSlots["backwardsCompatibleData"];
bcdfoldoutOpen = EditorGUILayout.Foldout(OpenSlots["backwardsCompatibleData"], "Backwards Compatible Data");
OpenSlots["backwardsCompatibleData"] = bcdfoldoutOpen;
GUILayout.EndHorizontal();
if (bcdfoldoutOpen)
{
EditorGUI.BeginDisabledGroup(true);
GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f));
EditorGUILayout.HelpBox("If you are going to use the recipe with a DynamicCharacterAvatar, it is recommended that you that you edit the 'WardrobeSet' section above and/or the base/wardrobe recipes directly. Changes you make manually below will only affect old style UMAAvatars", MessageType.Info);
for (int i = 0; i < _slotEditors.Count; i++)
{
var editor = _slotEditors[i];
if (editor == null)
{
GUILayout.Label("Empty Slot");
continue;
}
if (_slotEditors[i].Slot.isBlendShapeSource)
{
continue;
}
changed |= editor.OnGUI(ref _dnaDirty, ref _textureDirty, ref _meshDirty);
if (editor.Delete)
{
_dnaDirty = true;
_textureDirty = true;
_meshDirty = true;
_slotEditors.RemoveAt(i);
_recipe.SetSlot(editor.idx, null);
i--;
changed = true;
}
}
GUIHelper.EndVerticalPadded(10);
EditorGUI.EndDisabledGroup();
}
}
return changed;
}
private bool DrawWardrobeSetUI()
{
bool changed = false;
if (_recipe.raceData != null)
{
if (_recipe.raceData.wardrobeSlots.Count > 0)
{
GUILayout.BeginHorizontal(EditorStyles.toolbarButton);
GUILayout.Space(10);
bool wsfoldoutOpen = OpenSlots["wardrobeSet"];
wsfoldoutOpen = EditorGUILayout.Foldout(OpenSlots["wardrobeSet"], "Wardrobe Set");
OpenSlots["wardrobeSet"] = wsfoldoutOpen;
GUILayout.EndHorizontal();
if (wsfoldoutOpen)
{
if (_wardrobeSet == null)
{
return false;
}
//if this is a 'backwards compatible' recipe dont show the 'wardrobeCollections' bit since old avatars cannot wear collections
bool showWardrobeCollections = _recipe.slotDataList.Length > 0 ? false : true;
var thisWardrobeSetEditor = new WardrobeSetEditor(_recipe.raceData, _wardrobeSet, _recipe, showWardrobeCollections);
if (thisWardrobeSetEditor.OnGUI())
{
_wardrobeSet = thisWardrobeSetEditor.WardrobeSet;
changed = true;
}
}
}
}
return changed;
}
private void UpdateBackwardsCompatibleData()
{
var context = UMAContextBase.Instance;
if (context == null)
{
var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContextBase.";
Debug.LogWarning(_errorMessage);
}
//reset the recipe to the raceBase recipe
var thisBaseRecipe = _recipe.raceData.baseRaceRecipe;
thisBaseRecipe.Load(_recipe, context);
if (_wardrobeSet.Count > 0)
{
List<UMARecipeBase> Recipes = new List<UMARecipeBase>();
List<string> SuppressSlotsStrings = new List<string>();
List<string> HiddenSlots = new List<string>();
var wardrobeRecipesToRender = new Dictionary<string, UMARecipeBase>();
var activeRace = _recipe.raceData.raceName;
//Dont add the WardrobeCollection to the recipes to render- they doesn't render directly and will have already set their actual wardrobeRecipe slots SetSlot
for (int i = 0; i < _wardrobeSet.Count; i++)
{
WardrobeSettings set = _wardrobeSet[i];
var thisRecipe = UMAContext.Instance.GetBaseRecipe(set.recipe,true);
if (thisRecipe == null)
{
continue;
}
if (thisRecipe.GetType().ToString() == "UMA.UMAWardrobeCollection")
{
var TargetType = thisRecipe.GetType();
FieldInfo WardrobeCollectionField = TargetType.GetField("wardrobeCollection", BindingFlags.Public | BindingFlags.Instance);
WardrobeCollectionList wardrobeCollection = (WardrobeCollectionList)WardrobeCollectionField.GetValue(thisRecipe);
if (wardrobeCollection[activeRace] != null)
{
for (int i1 = 0; i1 < wardrobeCollection[activeRace].Count; i1++)
{
WardrobeSettings ws = wardrobeCollection[activeRace][i1];
var wsRecipe = UMAContext.Instance.GetBaseRecipe(ws.recipe,true);
if (wsRecipe != null)
{
if (wardrobeRecipesToRender.ContainsKey(ws.slot))
{
wardrobeRecipesToRender[ws.slot] = wsRecipe;
}
else
{
wardrobeRecipesToRender.Add(ws.slot, wsRecipe);
}
}
}
}
}
else
{
//_recipe.Merge(thisRecipe.GetCachedRecipe(context), true);
if (wardrobeRecipesToRender.ContainsKey(set.slot))
{
wardrobeRecipesToRender[set.slot] = thisRecipe;
}
else
{
wardrobeRecipesToRender.Add(set.slot, thisRecipe);
}
}
}
if (wardrobeRecipesToRender.Count > 0)
{
foreach (UMARecipeBase utr in wardrobeRecipesToRender.Values)
{
var TargetType = utr.GetType();
FieldInfo CompatibleRacesField = TargetType.GetField("compatibleRaces", BindingFlags.Public | BindingFlags.Instance);
FieldInfo WardrobeSlotField = TargetType.GetField("wardrobeSlot", BindingFlags.Public | BindingFlags.Instance);
FieldInfo SuppressWardrobeSlotField = TargetType.GetField("suppressWardrobeSlots", BindingFlags.Public | BindingFlags.Instance);
//field values
List<string> compatibleRaces = (List<string>)CompatibleRacesField.GetValue(utr);
string wardrobeSlot = (string)WardrobeSlotField.GetValue(utr);
List<string> suppressWardrobeSlot = (List<string>)SuppressWardrobeSlotField.GetValue(utr);
if (suppressWardrobeSlot != null)
{
if (activeRace == "" || ((compatibleRaces.Count == 0 || compatibleRaces.Contains(activeRace)) || (_recipe.raceData.IsCrossCompatibleWith(compatibleRaces) && _recipe.raceData.wardrobeSlots.Contains(wardrobeSlot))))
{
if (!SuppressSlotsStrings.Contains(wardrobeSlot))
{
for (int i = 0; i < suppressWardrobeSlot.Count; i++)
{
string suppressedSlot = suppressWardrobeSlot[i];
SuppressSlotsStrings.Add(suppressedSlot);
}
}
}
}
}
}
for (int i = 0; i < _recipe.raceData.wardrobeSlots.Count; i++)
{
string ws = _recipe.raceData.wardrobeSlots[i];
if (SuppressSlotsStrings.Contains(ws))
{
continue;
}
if (wardrobeRecipesToRender.ContainsKey(ws))
{
UMARecipeBase utr = wardrobeRecipesToRender[ws];
var TargetType = wardrobeRecipesToRender[ws].GetType();
FieldInfo CompatibleRacesField = TargetType.GetField("compatibleRaces", BindingFlags.Public | BindingFlags.Instance);
FieldInfo WardrobeSlotField = TargetType.GetField("wardrobeSlot", BindingFlags.Public | BindingFlags.Instance);
FieldInfo HidesField = TargetType.GetField("Hides", BindingFlags.Public | BindingFlags.Instance);
//field values
List<string> compatibleRaces = (List<string>)CompatibleRacesField.GetValue(utr);
string wardrobeSlot = (string)WardrobeSlotField.GetValue(utr);
List<string> hides = (List<string>)HidesField.GetValue(utr);
if (activeRace == "" || ((compatibleRaces.Count == 0 || compatibleRaces.Contains(activeRace)) || (_recipe.raceData.IsCrossCompatibleWith(compatibleRaces) && _recipe.raceData.wardrobeSlots.Contains(wardrobeSlot))))
{
Recipes.Add(utr);
if (hides.Count > 0)
{
for (int j = 0; j < hides.Count; j++)
{
string s = hides[j];
HiddenSlots.Add(s);
}
}
}
}
}
//merge them in
for (int i = 0; i < Recipes.Count; i++)
{
UMARecipeBase additionalRecipe = Recipes[i];
_recipe.Merge(additionalRecipe.GetCachedRecipe(context), true);
}
if (HiddenSlots.Count > 0)
{
List<SlotData> NewSlots = new List<SlotData>();
for (int i = 0; i < _recipe.slotDataList.Length; i++)
{
SlotData sd = _recipe.slotDataList[i];
if (sd == null)
{
continue;
}
if (!HiddenSlots.Contains(sd.asset.slotName))
{
NewSlots.Add(sd);
}
}
_recipe.slotDataList = NewSlots.ToArray();
}
ResetSlotEditors();
}
}
private void ResetSlotEditors()
{
if (_recipe.slotDataList == null)
{
_recipe.slotDataList = new SlotData[0];
}
for (int i = 0; i < _recipe.slotDataList.Length; i++)
{
var slot = _recipe.slotDataList[i];
if (slot == null)
{
continue;
}
_slotEditors.Add(new SlotEditor(_recipe, slot, i));
}
if (_slotEditors.Count > 1)
{
// Don't juggle the order - this way, they're in the order they're in the file, or dropped in.
List<SlotEditor> sortedSlots = new List<SlotEditor>(_slotEditors);
sortedSlots.Sort(SlotEditor.comparer);
var overlays1 = sortedSlots[0].GetOverlays();
var overlays2 = sortedSlots[1].GetOverlays();
for (int i = 0; i < sortedSlots.Count - 2; i++)
{
if (overlays1 == overlays2)
{
sortedSlots[i].sharedOverlays = true;
}
overlays1 = overlays2;
overlays2 = sortedSlots[i + 2].GetOverlays();
}
}
}
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 386148b403187884a9a52b417550cebf
timeCreated: 1484365720
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/Extensions/DynamicCharacterSystem/pDCSRecipeEditor.cs
uploadId: 679826
@@ -0,0 +1,356 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UMA.CharacterSystem;
namespace UMA.Editors
{
public partial class RecipeEditor
{
//if we move to having different types for the different kinds of UMATextRecipe (UMAWardrobeRecipe, UMAWardrobeCollection etc) then we will stop displaying this UI element (and just use the value when saving txt recipes)
public List<string> recipeTypeOpts = new List<string>(new string[] { "Standard", "Wardrobe" });
protected bool hideToolBar = false;
protected bool hideRaceField = true;//if true hides the extra race field that we draw *above* the toolbar
int compatibleRacePickerID = -1;
int selectedWardrobeThumb = 0;
List<string> generatedWardrobeSlotOptions = new List<string>();
List<string> generatedWardrobeSlotOptionsLabels = new List<string>();
protected List<string> generatedBaseSlotOptions = new List<string>();
protected List<string> generatedBaseSlotOptionsLabels = new List<string>();
FieldInfo ActiveWardrobeSetField = null;
List<WardrobeSettings> activeWardrobeSet = null;
protected override bool PreInspectorGUI()
{
return TextRecipeGUI();
}
protected override bool ToolbarGUI()
{
//hide the toolbar when its a recipe type that doesn't use DNA (like wardrobe or wardrobeCollection)
if (hideToolBar)
{
return slotEditor.OnGUI(target.name, ref _dnaDirty, ref _textureDirty, ref _meshDirty);
}
bool changed = false;
//the raceData field should really be ABOVE the toolbar, since it defines what the dna will be
GUILayout.Space(10);
if (!hideRaceField)
{
RaceData newRace = (RaceData)EditorGUILayout.ObjectField("RaceData", _recipe.raceData, typeof(RaceData), false);
if (_recipe.raceData != newRace)
{
_recipe.SetRace(newRace);
changed = true;
}
}
_toolbarIndex = GUILayout.Toolbar(_toolbarIndex, toolbar);
_LastToolBar = _toolbarIndex;
if (dnaEditor != null && slotEditor != null)
{
switch (_toolbarIndex)
{
case 0:
if (!dnaEditor.IsValid)
{
return false;
}
else if (dnaEditor.OnGUI(ref _dnaDirty, ref _textureDirty, ref _meshDirty))
{
return true;
}
else
{
return changed;
}
case 1:
if (slotEditor.OnGUI(target.name, ref _dnaDirty, ref _textureDirty, ref _meshDirty))
{
return true;
}
else
{
return changed;
}
}
}
return changed;
}
protected bool AreListsEqual<T>(List<T> x, List<T> y)
{
if (x == y)
{
return true;
}
if (x == null || y == null)
{
return false;
}
if (x.Count != y.Count)
{
return false;
}
for (int i = 0; i < x.Count; i++)
{
if (!x[i].Equals(y[i]))
{
return false;
}
}
return true;
}
/// <summary>
/// Adds a button for adding dna to a newly created UMATextRecipe
/// </summary>
/// <returns></returns>
protected virtual bool AddDNAButtonUI()
{
RaceData standardRaceData = null;
if (_recipe != null)
{
standardRaceData = _recipe.raceData;
}
if (standardRaceData == null)
{
return false;
}
//This enables us to create a new recipe using the Editor menu command but also add DNA to it based on the set race's converters
var currentDNA = _recipe.GetAllDna();
//we also need current slots because GetAllDna returns a zero length array if _recipe.slotdatalist == null
SlotData[] currentSlots = _recipe.GetAllSlots();
bool couldAddDNA = false;
bool DNAConvertersAdded = false;
if (currentDNA.Length == 0 && currentSlots != null)
{
var thisDNAConverterList = standardRaceData.dnaConverterList;
for (int i = 0; i < thisDNAConverterList.Length; i++)
{
IDNAConverter DnaConverter = thisDNAConverterList[i];
if (DnaConverter != null)
{
if(DnaConverter.DNATypeHash != 0)
{
couldAddDNA = true;
}
}
}
if (couldAddDNA)
{
if (GUILayout.Button("Add DNA"))
{
for (int i = 0; i < thisDNAConverterList.Length; i++)
{
IDNAConverter DnaConverter = thisDNAConverterList[i];
if (DnaConverter != null)
{
DNAConvertersAdded = true;
//the recipe already has the DNAConverter, it just doesn't have the values it requires to show the output in the DNA tab of the recipe
//_recipe.AddDNAUpdater(DnaConverter);
Type thisType = DnaConverter.DNAType;
if (DnaConverter is IDynamicDNAConverter)
{
var dna = _recipe.GetOrCreateDna(thisType, DnaConverter.DNATypeHash);
if (((IDynamicDNAConverter)DnaConverter).dnaAsset != null)
{
((DynamicUMADnaBase)dna).dnaAsset = ((IDynamicDNAConverter)DnaConverter).dnaAsset;
}
}
else
{
_recipe.GetOrCreateDna(thisType, DnaConverter.DNATypeHash);
}
}
}
}
}
}
return DNAConvertersAdded;
}
protected virtual bool FixDNAConverters()
{
RaceData standardRaceData = null;
if (_recipe != null)
{
standardRaceData = _recipe.raceData;
}
if (standardRaceData == null)
{
return false;
}
var currentDNA = _recipe.GetAllDna();
//we also need current slots because GetAllDna returns a zero length array if _recipe.slotdatalist == null
SlotData[] currentSlots = _recipe.GetAllSlots();
bool DNAConvertersModified = false;
if (currentDNA.Length > 0 && currentSlots != null)
{
//check if any DynamicDNA needs its DynamicDNAAsset updating
var thisDNAConverterList = standardRaceData.dnaConverterList;
for(int i = 0; i < thisDNAConverterList.Length; i++) /*DnaConverterBehaviour DnaConverter in thisDNAConverterList)*/
{
if(thisDNAConverterList[i] == null)
{
Debug.LogWarning(standardRaceData.raceName + " RaceData has a missing DNA Converter");
continue;
}
//'Old' UMA DNA will have a typehash based on its type name (never 0)
//DynamicDNA will only be zero if the converter does not have a DNA asset assigned (and will show a warning)
//so if the typeHash is 0 bail
if (thisDNAConverterList[i].DNATypeHash == 0)
{
Debug.LogWarning("Dynamic DNA Converter "+ thisDNAConverterList[i].name+" needs a DNA Asset assigned to it");
continue;
}
var dna = _recipe.GetOrCreateDna(thisDNAConverterList[i].DNAType, thisDNAConverterList[i].DNATypeHash);
if (thisDNAConverterList[i] is IDynamicDNAConverter)
{
var thisDnaAsset = ((IDynamicDNAConverter)thisDNAConverterList[i]).dnaAsset;
if (((DynamicUMADnaBase)dna).dnaAsset != thisDnaAsset || ((DynamicUMADnaBase)dna).didDnaAssetUpdate)
{
if (((DynamicUMADnaBase)dna).didDnaAssetUpdate)
{
Debug.Log("DynamicDNA found a missing asset");
((DynamicUMADnaBase)dna).didDnaAssetUpdate = false;
DNAConvertersModified = true;
}
else
{
//When this happens the values get lost
((DynamicUMADnaBase)dna).dnaAsset = thisDnaAsset;
//so we need to try to add any existing dna values to this dna
int imported = 0;
for (int j = 0; j < currentDNA.Length; j++)
{
if (currentDNA[j].DNATypeHash != dna.DNATypeHash)
{
imported += ((DynamicUMADnaBase)dna).ImportUMADnaValues(currentDNA[j]);
}
}
Debug.Log("Updated DNA to match DnaConverter " + thisDNAConverterList[i].name + "'s dna asset and imported "+imported+" values from previous dna");
DNAConvertersModified = true;
}
}
}
}
for (int i = 0; i < currentDNA.Length; i++)
{
//if there are no converters for the current dna
if (_recipe.raceData.GetConverters(currentDNA[i]).Length == 0)
{
int dnaToImport = currentDNA[i].Count;
int dnaImported = 0;
for (int j = 0; j < currentDNA.Length; j++)
{
if (currentDNA[j] is DynamicUMADnaBase)
{
// Keep trying to find a new home for DNA values until they have all been set
dnaImported += ((DynamicUMADnaBase)currentDNA[j]).ImportUMADnaValues(currentDNA[i]);
if (dnaImported >= dnaToImport)
{
break;
}
}
}
if (dnaImported > 0)
{
if(_recipe.GetDna(currentDNA[i].DNATypeHash) != null)
{
_recipe.RemoveDna(currentDNA[i].DNATypeHash);
}
DNAConvertersModified = true;
}
}
}
currentDNA = _recipe.GetAllDna();
//Finally if there are more DNA sets than there are converters we need to remove the dna that should not be there
if (currentDNA.Length > thisDNAConverterList.Length)
{
Debug.Log("There were more dna sets in the recipe than converters. Removing unused Dna...");
List<UMADnaBase> newCurrentDna = new List<UMADnaBase>();
for (int i = 0; i < currentDNA.Length; i++)
{
bool foundMatch = false;
for (int ii = 0; ii < thisDNAConverterList.Length; ii++)
{
if (thisDNAConverterList[ii].DNATypeHash == currentDNA[i].DNATypeHash)
{
newCurrentDna.Add(currentDNA[i]);
foundMatch = true;
}
}
if (!foundMatch)
{
if (_recipe.dnaValues.Contains(currentDNA[i]))
{
_recipe.RemoveDna(currentDNA[i].DNATypeHash);
}
}
}
currentDNA = newCurrentDna.ToArray();
DNAConvertersModified = true;
}
}
return DNAConvertersModified;
}
private bool TextRecipeGUI()
{
Type TargetType = target.GetType();//used to get the UMATextRecipe type taher than UMARecipeBase
bool doUpdate = false;
if (TargetType.ToString() == "UMA.UMATextRecipe")
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.Popup("Recipe Type", 0, new string[] { "Standard" });//other types (WardrobeRecipe, DynamicCharacterAvatarRecipe) have their own editors now so this is just for UI consistancy
EditorGUI.EndDisabledGroup();
if (ActiveWardrobeSetField == null)
{
ActiveWardrobeSetField = TargetType.GetField("activeWardrobeSet", BindingFlags.Public | BindingFlags.Instance);
}
activeWardrobeSet = (List<WardrobeSettings>)ActiveWardrobeSetField.GetValue(target);
//draws a button to 'Add DNA' when a new 'standard' recipe is created
if (AddDNAButtonUI())
{
hideToolBar = false;
return true;
}
//fixes dna when the recipes race has updated from UMADnaHumanoid/Tutorial to DynamicDna
if (FixDNAConverters())
{
hideToolBar = false;
return true;
}
//When recipes are saved from a DynamicCharacterAvatar as a 'Standard' rather than 'Optimized' recipe they are saved as 'BackwardsCompatible'
//This means they have slots/overlay data AND a wardrobeSet. In this case we need to draw the "DynamicCharacterAvatarRecipe' slot editor
//and this will show an editable Wardrobe set which will update and a slot/overlay list
if ((activeWardrobeSet.Count > 0))
{
hideRaceField = false;
slotEditor = new WardrobeSetMasterEditor(_recipe, activeWardrobeSet);
}
}
return doUpdate;
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: d6d06f87a09dc49dfad217a44ae08e5f
timeCreated: 1438996042
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/Extensions/DynamicCharacterSystem/pTextRecipeEditor.cs
uploadId: 679826
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 871246cf12effb94590dfc22c77d0c0f
timeCreated: 1484365720
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/Extensions/DynamicCharacterSystem/pWardrobeRecipeEditor.cs
uploadId: 679826
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 56ff8f562b1d64e418aa1bbd6989f539
folderAsset: yes
timeCreated: 1551121795
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,22 @@
#if UNITY_2017_1_OR_NEWER
using UnityEditor;
using UMA.Timeline;
namespace UMA.Editors
{
[CustomEditor(typeof(UmaColorTrack))]
public class UmaColorTrackEditor : Editor
{
public override void OnInspectorGUI()
{
serializedObject.Update();
SerializedProperty timeStep = serializedObject.FindProperty("timeStep");
EditorGUILayout.PropertyField(timeStep);
serializedObject.ApplyModifiedProperties();
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 945d2f76f7debd54fbc6379d0012acdd
timeCreated: 1537147148
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/Extensions/Timeline/UmaColorTrackEditor.cs
uploadId: 679826
@@ -0,0 +1,48 @@
#if UNITY_2017_1_OR_NEWER
using UnityEditor;
using UMA.Timeline;
namespace UMA.Editors
{
[CustomEditor(typeof(UmaRaceClip))]
public class UmaRaceClipEditor : Editor
{
SerializedProperty raceToChangeTo;
string[] raceOptions;
int selectedIndex = -1;
void OnEnable()
{
UMAContextBase context = UMAContextBase.Instance;
RaceData[] races = context.GetAllRaces();
raceToChangeTo = serializedObject.FindProperty("raceToChangeTo");
raceOptions = new string[races.Length];
for (int i = 0; i < races.Length; i++)
{
raceOptions[i] = races[i].raceName;
if (raceToChangeTo.stringValue == raceOptions[i])
{
selectedIndex = i;
}
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
int newIndex = EditorGUILayout.Popup("Race To Change To", selectedIndex, raceOptions);
if (newIndex != selectedIndex)
{
selectedIndex = newIndex;
raceToChangeTo.stringValue = raceOptions[selectedIndex];
}
serializedObject.ApplyModifiedProperties();
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 47dae9f7efe17514b82fd881e3e4ba66
timeCreated: 1537147225
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/Extensions/Timeline/UmaRaceClipEditor.cs
uploadId: 679826
@@ -0,0 +1,48 @@
#if UNITY_2017_1_OR_NEWER
using UnityEngine;
using UnityEditor;
using UMA.Timeline;
namespace UMA.Editors
{
[CustomPropertyDrawer(typeof(UmaWardrobeBehaviour))]
public class UmaWardrobeBehaviourEditor : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
int fieldCount = 25; // todo: get this dynamically
return fieldCount * EditorGUIUtility.singleLineHeight * EditorGUIUtility.pixelsPerPoint;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty wardrobeOption = property.FindPropertyRelative("wardrobeOption");
SerializedProperty recipesToAdd = property.FindPropertyRelative("recipesToAdd");
SerializedProperty slotsToClear = property.FindPropertyRelative("slotsToClear");
SerializedProperty rebuildImmediately = property.FindPropertyRelative("rebuildImmediately");
Rect singleFieldRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(singleFieldRect, rebuildImmediately);
singleFieldRect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(singleFieldRect, wardrobeOption);
if (wardrobeOption.enumValueIndex == (int)UmaWardrobeBehaviour.WardrobeOptions.AddRecipes)
{
singleFieldRect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(singleFieldRect, recipesToAdd, true);
}
if (wardrobeOption.enumValueIndex == (int)UmaWardrobeBehaviour.WardrobeOptions.ClearSlots)
{
singleFieldRect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(singleFieldRect, slotsToClear, true);
}
if (wardrobeOption.enumValueIndex == (int)UmaWardrobeBehaviour.WardrobeOptions.ClearAllSlots)
{
}
}
}
}
#endif
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 4f558d4d5a4c5844fa3c6306df0b2079
timeCreated: 1537147299
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/Extensions/Timeline/UmaWardrobeBehaviourEditor.cs
uploadId: 679826
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 2a1272c496047b048a3a226ec5c42336
folderAsset: yes
timeCreated: 1551121895
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
using UnityEngine;
using UnityEditor;
namespace UMA.Dynamics.Editors
{
[CustomEditor(typeof(UMAPhysicsAvatar))]
public class UMAPhysicsAvatarEditor : Editor
{
SerializedProperty ragdollLayer;
SerializedProperty playerLayer;
SerializedProperty onRagdollStarted;
SerializedProperty onRagdollEnded;
void OnEnable()
{
ragdollLayer = serializedObject.FindProperty("ragdollLayer");
playerLayer = serializedObject.FindProperty("playerLayer");
onRagdollStarted = serializedObject.FindProperty("onRagdollStarted");
onRagdollEnded = serializedObject.FindProperty("onRagdollEnded");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
UMAPhysicsAvatar avatar = (UMAPhysicsAvatar)target;
avatar.ragdolled = EditorGUILayout.Toggle(new GUIContent("Ragdolled", "Toggle to turn on/off the Ragdoll"), avatar.ragdolled);
//DrawDefaultInspector();
DrawPropertiesExcluding(serializedObject, new string[]{ "ragdollLayer", "playerLayer", "onRagdollStarted", "onRagdollEnded" });
GUILayout.Space(10);
EditorGUILayout.HelpBox("Sets layer 8 and 9 to Ragdoll and Player. If your code uses different layers do not use this defaults button", MessageType.Info);
if (GUILayout.Button("Add Default Layers"))
{
AddDefaultLayers( ragdollLayer, playerLayer);
}
EditorGUILayout.HelpBox("The Ragdoll layer needs it's collision matrix layers set to collide with only itself. Set this in Edit->Project Settings->Physics->Layer Collision Matrix", MessageType.Info);
ragdollLayer.intValue = EditorGUILayout.LayerField("Ragdoll Layer", ragdollLayer.intValue);
playerLayer.intValue = EditorGUILayout.LayerField("Player Layer", playerLayer.intValue);
GUILayout.Space(10);
EditorGUILayout.PropertyField(onRagdollStarted);
EditorGUILayout.PropertyField(onRagdollEnded);
serializedObject.ApplyModifiedProperties();
}
static public void AddDefaultLayers(SerializedProperty ragdollLayer, SerializedProperty playerLayer)
{
ragdollLayer.intValue = UMAUtils.CreateLayer("Ragdoll");
playerLayer.intValue = UMAUtils.CreateLayer("Player");
for (int i = 8; i < 32; i++)
{
if (i != ragdollLayer.intValue)
{
Physics.IgnoreLayerCollision(ragdollLayer.intValue, i, true);
}
}
Physics.IgnoreLayerCollision(ragdollLayer.intValue, ragdollLayer.intValue, false);
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 09af80291e74c324eb730e81f42aca50
timeCreated: 1489789879
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/Extensions/UMAPhysics/UMAPhysicsAvatarEditor.cs
uploadId: 679826
@@ -0,0 +1,79 @@
using UnityEditor;
namespace UMA.Dynamics.Editors
{
[CustomEditor(typeof(UMAPhysicsElement))]
public class UMAPhysicsElementEditor : Editor
{
public override void OnInspectorGUI()
{
//DrawDefaultInspector();
serializedObject.Update();
EditorGUILayout.PropertyField (serializedObject.FindProperty ("isRoot"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("boneName"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("mass"));
Show( serializedObject.FindProperty("colliders"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("parentBone"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("axis"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("swingAxis"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("lowTwistLimit"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("highTwistLimit"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("swing1Limit"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("swing2Limit"));
EditorGUILayout.PropertyField (serializedObject.FindProperty ("enablePreprocessing"));
serializedObject.ApplyModifiedProperties ();
}
private void Show(SerializedProperty list)
{
EditorGUILayout.PropertyField (list); //List Name
EditorGUI.indentLevel += 1;
if (list.isExpanded)
{
EditorGUILayout.PropertyField (list.FindPropertyRelative ("Array.size")); //List size
for (int i = 0; i < list.arraySize; i++)
{
EditorGUILayout.PropertyField (list.GetArrayElementAtIndex (i));
EditorGUI.indentLevel += 1;
if (list.GetArrayElementAtIndex (i).isExpanded)
{
EditorGUILayout.PropertyField (list.GetArrayElementAtIndex (i).FindPropertyRelative ("colliderType"));
int type = list.GetArrayElementAtIndex (i).FindPropertyRelative ("colliderType").enumValueIndex;
if( type == 0 )
{
EditorGUILayout.HelpBox("Box Colliders can not be used to affect cloth.", MessageType.Warning );
}
EditorGUILayout.PropertyField (list.GetArrayElementAtIndex (i).FindPropertyRelative ("colliderCentre"));
if (type == 0) {
//Box Collider only
EditorGUILayout.PropertyField (list.GetArrayElementAtIndex (i).FindPropertyRelative ("boxDimensions"));
}
if (type == 1) {
//Sphere Collider only
EditorGUILayout.PropertyField (list.GetArrayElementAtIndex (i).FindPropertyRelative ("sphereRadius"));
}
if (type == 2) {
//Capsule Collider only
EditorGUILayout.PropertyField (list.GetArrayElementAtIndex (i).FindPropertyRelative ("capsuleRadius"));
EditorGUILayout.PropertyField (list.GetArrayElementAtIndex (i).FindPropertyRelative ("capsuleHeight"));
EditorGUILayout.PropertyField (list.GetArrayElementAtIndex (i).FindPropertyRelative ("capsuleAlignment"));
}
}
EditorGUI.indentLevel -= 1;
}
}
EditorGUI.indentLevel -= 1;
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 83ef0212f7090be43a5b670e28cc4aac
timeCreated: 1489809717
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/Extensions/UMAPhysics/UMAPhysicsElementEditor.cs
uploadId: 679826
@@ -0,0 +1,37 @@
using UnityEngine;
using UnityEditor;
namespace UMA.Dynamics.Editors
{
[CustomEditor(typeof(UMAPhysicsSlotDefinition))]
public class UMAPhysicsSlotEditor : Editor
{
SerializedProperty ragdollLayer;
SerializedProperty playerLayer;
void OnEnable()
{
ragdollLayer = serializedObject.FindProperty("ragdollLayer");
playerLayer = serializedObject.FindProperty("playerLayer");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawDefaultInspector();
EditorGUILayout.HelpBox ("Sets layer 8 and 9 to Ragdoll and Player. If your code uses different layers do not use this defaults button", MessageType.Info);
if (GUILayout.Button ("Add Default Layers"))
{
UMAPhysicsAvatarEditor.AddDefaultLayers (ragdollLayer, playerLayer);
}
EditorGUILayout.HelpBox ("The Ragdoll layer needs it's collision matrix layers set to collide with only itself. Set this in Edit->Project Settings->Physics->Layer Collision Matrix", MessageType.Info);
ragdollLayer.intValue = EditorGUILayout.LayerField ("Ragdoll Layer", ragdollLayer.intValue);
playerLayer.intValue = EditorGUILayout.LayerField ("Player Layer", playerLayer.intValue);
serializedObject.ApplyModifiedProperties();
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 8c2ad85b3d17f204098bfbe2324a9fdb
timeCreated: 1489798887
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/Extensions/UMAPhysics/UMAPhysicsSlotEditor.cs
uploadId: 679826