public void LoadSelectedCollection(string collectionName) { var thisUWC = WardrobeCollectionLibrary.Instance.collectionIndex[collectionName]; if (thisUWC != null) { thisUWC.EnsureLocalAvailability(); } if (thisCustomizer.Avatar != null) { //is this UWC compatible with the current race of the avatar? //even if its not it should be made available to races that are? if (!thisUWC.compatibleRaces.Contains(thisCustomizer.Avatar.activeRace.name) && thisUWC.compatibleRaces.Count > 0) { //show a messagebox- but for now Debug.LogWarning("This wardrobe collection was not compatible with that avatar"); return; } //if not show a message otherwise load the recipe var thisContext = thisCustomizer.Avatar.context != null ? thisCustomizer.Avatar.context : UMAContext.FindInstance(); if (thisContext != null) { var thisDCS = (thisContext.dynamicCharacterSystem as DynamicCharacterSystem); if (thisDCS != null) { thisDCS.GetRecipe(collectionName, true); //if there is actually a 'FullOutfit' defined for the current avatar(i.e. the WardrobeSet for this race is not empty) load it if (thisUWC.wardrobeCollection[thisCustomizer.Avatar.activeRace.name].Count > 0) { thisCustomizer.Avatar.SetSlot(thisUWC); thisCustomizer.Avatar.BuildCharacter(); } } } onLoadCollection.Invoke(); //if this was not a recipe that will actually load a FullOutfit onto this race, show a message saying the assets have been added to the library if (thisUWC.wardrobeCollection[thisCustomizer.Avatar.activeRace.name].Count == 0 && thisUWC.arbitraryRecipes.Count > 0) { dialogBoxes.SetActive(true); messageBox.SetActive(true); messageHeader.text = thisUWC.name + " Loaded!"; messageBody.text = "The wardrobe recipes in " + thisUWC.name + " have been added to the DCS libraries. Compatible recipes can now be applied to your character using the 'Wardrobe' section of the UI."; } } }
public bool OnGUI() { if (warningIcon == null) { warningIcon = EditorGUIUtility.FindTexture("console.warnicon.sml"); } bool changed = false; var context = UMAContext.FindInstance(); if (context == null) { var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContext."; Debug.LogWarning(_errorMessage); } var recipesForRaceSlot = context.dynamicCharacterSystem.GetRecipeNamesForRaceSlot(_wsRace, _wsSlot); List <string> thisPopupVals = new List <string>(); thisPopupVals.Add("None"); thisPopupVals.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.dynamicCharacterSystem.CheckRecipeAvailability(_wsRecipeName); selected = thisPopupVals.IndexOf(_wsRecipeName); if (selected == -1) { selected = thisPopupVals.Count; string missingOrIncompatible = "missing"; if (context.dynamicCharacterSystem.GetBaseRecipe(_wsRecipeName, false) != null) { missingOrIncompatible = "incompatible"; } thisPopupVals.Add(_wsRecipeName + " (" + missingOrIncompatible + ")"); } } var newSelected = selected; if (!recipeIsLive) { EditorGUI.indentLevel++; } var label = _wsSlot == "WardrobeCollection" ? " " : _wsSlot; EditorGUI.BeginChangeCheck(); newSelected = EditorGUILayout.Popup(label, selected, thisPopupVals.ToArray()); if (!recipeIsLive) { EditorGUI.indentLevel--; valRBut = GUILayoutUtility.GetLastRect(); } if (EditorGUI.EndChangeCheck()) { if (newSelected != selected) { changed = true; _wsRecipeName = (thisPopupVals[newSelected].IndexOf("(missing)") == -1 && thisPopupVals[newSelected].IndexOf("(incompatible)") == -1) ? (thisPopupVals[newSelected] != "None" ? thisPopupVals[newSelected] : "") : _wsRecipeName.Replace("(missing)", "").Replace("(incompatible)", ""); } } 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); }
private void UpdateBackwardsCompatibleData() { var context = UMAContext.FindInstance(); if (context == null) { var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContext."; Debug.LogWarning(_errorMessage); } //reset the recipe to the raceBase recipe var thisBaseRecipe = _recipe.raceData.baseRaceRecipe; thisBaseRecipe.Load(_recipe, context); if (_wardrobeSet.Count > 0) { var thisDCS = context.dynamicCharacterSystem; if (thisDCS == null) { var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContext."; Debug.LogWarning(_errorMessage); } 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 foreach (WardrobeSettings set in _wardrobeSet) { var thisRecipe = thisDCS.GetBaseRecipe(set.recipe); if (thisRecipe == null) { continue; } if (thisRecipe.GetType().ToString() == "UMAWardrobeCollection") { var TargetType = thisRecipe.GetType(); FieldInfo WardrobeCollectionField = TargetType.GetField("wardrobeCollection", BindingFlags.Public | BindingFlags.Instance); WardrobeCollectionList wardrobeCollection = (WardrobeCollectionList)WardrobeCollectionField.GetValue(thisRecipe); if (wardrobeCollection[activeRace] != null) { foreach (WardrobeSettings ws in wardrobeCollection[activeRace]) { var wsRecipe = thisDCS.GetBaseRecipe(ws.recipe); 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.findBackwardsCompatibleWith(compatibleRaces) && _recipe.raceData.wardrobeSlots.Contains(wardrobeSlot)))) { if (!SuppressSlotsStrings.Contains(wardrobeSlot)) { foreach (string suppressedSlot in suppressWardrobeSlot) { SuppressSlotsStrings.Add(suppressedSlot); } } } } } } foreach (string ws in _recipe.raceData.wardrobeSlots) { 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.findBackwardsCompatibleWith(compatibleRaces) && _recipe.raceData.wardrobeSlots.Contains(wardrobeSlot)))) { Recipes.Add(utr); if (hides.Count > 0) { foreach (string s in hides) { HiddenSlots.Add(s); } } } } } //merge them in foreach (var additionalRecipe in Recipes) { _recipe.Merge(additionalRecipe.GetCachedRecipe(context), true); } if (HiddenSlots.Count > 0) { List <SlotData> NewSlots = new List <SlotData>(); foreach (SlotData sd in _recipe.slotDataList) { if (sd == null) { continue; } if (!HiddenSlots.Contains(sd.asset.slotName)) { NewSlots.Add(sd); } } _recipe.slotDataList = NewSlots.ToArray(); } ResetSlotEditors(); } }
public override bool OnGUI(string targetName, ref bool _dnaDirty, ref bool _textureDirty, ref bool _meshDirty) { bool changed = false; if (_sharedColorsEditor.OnGUI(_recipe)) { changed = true; _textureDirty = true; } GUILayout.Space(6); Rect dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true)); GUI.Box(dropArea, "Drag Slots and Overlays here. Click to pick"); if (DropAreaGUI(dropArea)) { changed |= true; _dnaDirty |= true; _textureDirty |= true; _meshDirty |= true; } GUILayout.Space(10); if (_baseSlotOptions.Count > 0) { var baseSlotsNamesList = new List <string>() { "None" }; for (int i = 0; i < _baseSlotOptionsLabels.Count; i++) { baseSlotsNamesList.Add(_baseSlotOptionsLabels[i]); } EditorGUI.BeginChangeCheck(); var baseAdded = EditorGUILayout.Popup("Add Base Slot", 0, baseSlotsNamesList.ToArray()); if (EditorGUI.EndChangeCheck()) { if (baseAdded != 0) { var slotName = _baseSlotOptions[baseAdded - 1]; LastSlot = slotName; //we know there should be one because we created a virtual one when we unpacked the recipe if it didn't exist var context = UMAContext.FindInstance(); var slotToAdd = context.InstantiateSlot(slotName); _recipe.MergeSlot(slotToAdd, false); changed |= true; _dnaDirty |= true; _textureDirty |= true; _meshDirty |= true; } } } var added = (SlotDataAsset)EditorGUILayout.ObjectField("Add Slot", null, typeof(SlotDataAsset), false); if (added != null) { LastSlot = added.slotName; var slot = new SlotData(added); _recipe.MergeSlot(slot, false); changed |= true; _dnaDirty |= true; _textureDirty |= true; _meshDirty |= true; } GUILayout.Space(20); GUILayout.BeginHorizontal(); if (GUILayout.Button("Clear Recipe")) { _recipe.slotDataList = new SlotData[0]; changed |= true; _dnaDirty |= true; _textureDirty |= true; _meshDirty |= true; } if (GUILayout.Button("Remove Nulls")) { var newList = new List <SlotData>(_recipe.slotDataList.Length); foreach (var slotData in _recipe.slotDataList) { if (slotData != null) { newList.Add(slotData); } } _recipe.slotDataList = newList.ToArray(); changed |= true; _dnaDirty |= true; _textureDirty |= true; _meshDirty |= true; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Collapse All")) { foreach (SlotEditor se in _slotEditors) { se.FoldOut = false; } } if (GUILayout.Button("Expand All")) { foreach (SlotEditor se in _slotEditors) { se.FoldOut = true; } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Select All Slots")) { SelectAllSlots(); } if (GUILayout.Button("Select All Overlays")) { SelectAllOverlays(); } GUILayout.EndHorizontal(); for (int i = 0; i < _slotEditors.Count; i++) { var editor = _slotEditors[i]; if (editor == null) { GUILayout.Label("Empty Slot"); 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; } } return(changed); }
public bool OnGUI() { bool changed = false; if (_race != null) { if (_race.wardrobeSlots.Count > 0) { var context = UMAContext.FindInstance(); if (context == null) { var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContext."; Debug.LogWarning(_errorMessage); } if (_wardrobeSet == null || context == null) { return(false); } GUIHelper.BeginVerticalPadded(10, new Color(0.75f, 0.875f, 1f)); if (_allowWardrobeCollectionSlot) { var wcRecipesForRace = context.dynamicCharacterSystem.GetRecipesForRaceSlot(_race.raceName, "WardrobeCollection"); var wcGroupDict = new Dictionary <string, List <string> >(); //for 'Standard Assets' we need to do some kind of get Types thing I think because we then need to use reflection to get the wardrobeSlot field //how can we get what we want here when WardrobeCollections dont exist in Standard Assets (if 'StandardAssets' has been moved there) for (int i = 0; i < wcRecipesForRace.Count; i++) { Type wcType = wcRecipesForRace[i].GetType(); if (wcType.ToString() == "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 <string>()); } wcGroupDict[wcRecipeSlot].Add(wcRecipesForRace[i].name); } } if (wcGroupDict.Count > 0) { EditorGUILayout.LabelField("WardrobeCollections"); EditorGUI.indentLevel++; foreach (KeyValuePair <string, List <string> > kp in wcGroupDict) { var thisPopupVals = new List <string>(); thisPopupVals.Add("None"); thisPopupVals.AddRange(kp.Value); var selected = 0; var prevRecipe = ""; //if one of the recipes in the wardrobe set is one of these then its selected for (int pvi = 0; pvi < thisPopupVals.Count; pvi++) { for (int wsi = 0; wsi < _wardrobeSet.Count; wsi++) { if (thisPopupVals[pvi] == _wardrobeSet[wsi].recipe) { prevRecipe = _wardrobeSet[wsi].recipe; selected = pvi; 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++; } } foreach (string wsl in _race.wardrobeSlots) { if (wsl == "None") { continue; } 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 { 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); }
#pragma warning restore 0414 // Use this for initialization void Start() { avatar = GetComponent <DynamicCharacterAvatar>(); avatar.CharacterBegun.AddListener(CharacterBegun); context = UMAContext.FindInstance(); }
public override bool OnGUI(string targetName, ref bool _dnaDirty, ref bool _textureDirty, ref bool _meshDirty) { var context = UMAContext.FindInstance(); if (context == null) { var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContext."; 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.raceLibrary.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); }
public static GameObject CreateEditorContext() { GameObject EditorUMAContext = null; if (UnityEditor.BuildPipeline.isBuildingPlayer) { return(null); } if (Application.isPlaying) { Debug.LogWarning("There was no UMAContext in this scene. Please add the UMA_DCS prefab to this scene before you try to generate an UMA."); return(null); } Debug.Log("UMA Recipe Editor created an UMAEditorContext to enable editing. This will auto delete once you have finished editing your recipe or you add the UMA_DCS prefab to this scene."); //if there is already an UMAEditorContext use it if (UMAContext.FindInstance() != null) { if (UMAContext.FindInstance().gameObject.name == "UMAEditorContext") { EditorUMAContext = UMAContext.FindInstance().gameObject; //if the UMAContext itself is on this game object, it means this was created and not deleted by the previous version of 'CreateEditorContext' //(The new version creates the UMAContext on a child game object called 'UMAContext' so that UMAContext.FindInstance can find it properly) //so in this case delete all the components that would have been added from the found gameObject from the previous code if (EditorUMAContext.GetComponent <UMAContext>()) { Destroy(EditorUMAContext.GetComponent <UMAContext>()); //should also make the instance null again if (EditorUMAContext.GetComponent <DynamicRaceLibrary>()) { Destroy(EditorUMAContext.GetComponent <DynamicRaceLibrary>()); } if (EditorUMAContext.GetComponent <DynamicSlotLibrary>()) { Destroy(EditorUMAContext.GetComponent <DynamicSlotLibrary>()); } if (EditorUMAContext.GetComponent <DynamicOverlayLibrary>()) { Destroy(EditorUMAContext.GetComponent <DynamicOverlayLibrary>()); } if (EditorUMAContext.GetComponent <DynamicCharacterSystem>()) { Destroy(EditorUMAContext.GetComponent <DynamicCharacterSystem>()); } if (EditorUMAContext.GetComponent <DynamicAssetLoader>()) { Destroy(EditorUMAContext.GetComponent <DynamicAssetLoader>()); } } } else if (UMAContext.FindInstance().gameObject.transform.parent.gameObject.name == "UMAEditorContext") { EditorUMAContext = UMAContext.FindInstance().gameObject.transform.parent.gameObject; } } else if (GameObject.Find("UMAEditorContext")) { EditorUMAContext = GameObject.Find("UMAEditorContext"); } else { EditorUMAContext = new GameObject(); EditorUMAContext.name = "UMAEditorContext"; } //Make this GameObject not show up in the scene or save EditorUMAContext.hideFlags = HideFlags.DontSave | HideFlags.NotEditable; //if this gameobject does not contain an UMAContext add it - we have to call it UMAContext because UMAContext.FindInstance searches for that game object var thisUMAContext = UMAContext.Instance = EditorUMAContext.GetComponentInChildren <UMAContext>(); if (UMAContext.Instance == null) { var thisUMAContextGO = new GameObject(); thisUMAContextGO.name = "UMAContext"; thisUMAContextGO.transform.parent = EditorUMAContext.transform; thisUMAContext = thisUMAContextGO.AddComponent <UMAContext>(); UMAContext.Instance = thisUMAContext; } //we need to add the libraries as components of the game object too //and then set THOSE components to the umaContext component thisUMAContext.raceLibrary = thisUMAContext.gameObject.AddComponent <DynamicRaceLibrary>(); (thisUMAContext.raceLibrary as DynamicRaceLibrary).dynamicallyAddFromResources = true; (thisUMAContext.raceLibrary as DynamicRaceLibrary).dynamicallyAddFromAssetBundles = true; thisUMAContext.overlayLibrary = thisUMAContext.gameObject.AddComponent <DynamicOverlayLibrary>(); (thisUMAContext.overlayLibrary as DynamicOverlayLibrary).dynamicallyAddFromResources = true; (thisUMAContext.overlayLibrary as DynamicOverlayLibrary).dynamicallyAddFromAssetBundles = true; thisUMAContext.slotLibrary = thisUMAContext.gameObject.AddComponent <DynamicSlotLibrary>(); (thisUMAContext.slotLibrary as DynamicSlotLibrary).dynamicallyAddFromResources = true; (thisUMAContext.slotLibrary as DynamicSlotLibrary).dynamicallyAddFromAssetBundles = true; thisUMAContext.dynamicCharacterSystem = thisUMAContext.gameObject.AddComponent <UMACharacterSystem.DynamicCharacterSystem>(); (thisUMAContext.dynamicCharacterSystem as DynamicCharacterSystem).dynamicallyAddFromResources = true; (thisUMAContext.dynamicCharacterSystem as DynamicCharacterSystem).dynamicallyAddFromAssetBundles = true; var thisDAL = thisUMAContext.gameObject.AddComponent <DynamicAssetLoader>(); DynamicAssetLoader.Instance = thisDAL; return(EditorUMAContext); }
private void AddAdditionalSlots() { umaData.AddAdditionalRecipes(additionalRecipes, UMAContext.FindInstance()); }
public CharacterSlotOverly(UMADynamicAvatar _umaDynamicAvatar) { umaDynamicAvatar = _umaDynamicAvatar; umaContext = UMAContext.FindInstance(); }
public bool AddExtraStuff() { SerializedProperty baseRaceRecipe = serializedObject.FindProperty("baseRaceRecipe"); EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(baseRaceRecipe, true); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); } if (wardrobeSlotList == null) { InitWardrobeSlotList(); } EditorGUILayout.Space(); EditorGUI.BeginChangeCheck(); wardrobeSlotList.DoLayoutList(); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); if (!race.ValidateWardrobeSlots()) { EditorUtility.SetDirty(race); } } //new CrossCompatibilitySettings //To push any old settings in RaceData.backwardsCompatibleWith into the new crossCompatibilitySettings we have to call GetCrossCompatibleRaces() directly on the target #pragma warning disable 618 if (race.backwardsCompatibleWith.Count > 0) { var cc = race.GetCrossCompatibleRaces(); if (cc.Count > 0) { serializedObject.Update(); } } #pragma warning restore 618 SerializedProperty _crossCompatibilitySettings = serializedObject.FindProperty("_crossCompatibilitySettings"); SerializedProperty _crossCompatibilitySettingsData = _crossCompatibilitySettings.FindPropertyRelative("settingsData"); //draw the new version of the crossCompatibility list that allows users to define what slots in this races base recipe equate to in the backwards compatible races base recipe _crossCompatibilitySettings.isExpanded = EditorGUILayout.Foldout(_crossCompatibilitySettings.isExpanded, "Cross Compatibility Settings"); if (_crossCompatibilitySettings.isExpanded) { //draw an info foldout EditorGUI.indentLevel++; _crossCompatibilitySettingsData.isExpanded = EditorGUILayout.Foldout(_crossCompatibilitySettingsData.isExpanded, "Help"); if (_crossCompatibilitySettingsData.isExpanded) { var helpText = "CrossCompatibilitySettings allows this race to wear wardrobe slots from another race, if this race has a wardrobe slot that the recipe is set to."; helpText += " You can further configure the compatibility settings for each compatible race to define 'equivalent' slotdatas in the races' base recipes."; helpText += " For example you could define that this races 'highpolyMaleChest' slotdata in its base recipe is equivalent to HumanMales 'MaleChest' slot data in its base recipe."; helpText += " This would mean that any recipes which hid or applied an overlay to 'MaleChest' would hide or apply an overlay to 'highPolyMaleChest' on this race."; helpText += " If 'Overlays Match' is unchecked then overlays in a recipe wont be applied."; EditorGUILayout.HelpBox(helpText, MessageType.Info); } EditorGUI.indentLevel--; if (baseRaceRecipe.objectReferenceValue != null) { Rect dropArea = new Rect(); dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true)); GUI.Box(dropArea, "Drag cross compatible Races here. Click to pick."); CompatibleRacesDropArea(dropArea, _crossCompatibilitySettingsData); EditorGUILayout.Space(); //update the foldouts list if the dropbox changes anything if (_BCFoldouts.Length != _crossCompatibilitySettingsData.arraySize) { Array.Resize <bool>(ref _BCFoldouts, _crossCompatibilitySettingsData.arraySize); } //we need an uptodate list of the slots in THIS races base recipe baseSlotsList.Clear(); baseSlotsNamesList.Clear(); //editing a race will require a context too because we need to get the base recipes and their slots if (UMAContext.FindInstance() == null) { EditorUMAContext = UMAContext.CreateEditorContext(); } UMAData.UMARecipe thisBaseRecipe = (baseRaceRecipe.objectReferenceValue as UMARecipeBase).GetCachedRecipe(UMAContext.Instance); SlotData[] thisBaseSlots = thisBaseRecipe.GetAllSlots(); foreach (SlotData slot in thisBaseSlots) { if (slot != null) { baseSlotsList.Add(slot); baseSlotsNamesList.Add(slot.slotName); } } List <int> crossCompatibleSettingsToDelete = new List <int>(); //draw a foldout area for each compatible race that will show an entry for each slot in this races base recipe //with a picker to choose the slot from the compatible race's base recipe that it equates to for (int i = 0; i < _crossCompatibilitySettingsData.arraySize; i++) { bool del = false; var thisCCSettings = _crossCompatibilitySettingsData.GetArrayElementAtIndex(i).FindPropertyRelative("ccSettings"); var ccRaceName = _crossCompatibilitySettingsData.GetArrayElementAtIndex(i).FindPropertyRelative("ccRace").stringValue; //this could be missing- we should show that var label = ccRaceName; if (GetCompatibleRaceData(ccRaceName) == null) { label += " (missing)"; } GUIHelper.FoldoutBar(ref _BCFoldouts[i], label, out del); if (del) { crossCompatibleSettingsToDelete.Add(i); } if (_BCFoldouts[i]) { DrawCCUI(ccRaceName, baseRaceRecipe, thisCCSettings); } } if (crossCompatibleSettingsToDelete.Count > 0) { foreach (int del in crossCompatibleSettingsToDelete) { _crossCompatibilitySettingsData.DeleteArrayElementAtIndex(del); serializedObject.ApplyModifiedProperties(); } } } else { EditorGUILayout.HelpBox("Please define this races baseRaceRecipe before trying to define its cross compatibility settings.", MessageType.Info); } } EditorGUILayout.Space(); EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(serializedObject.FindProperty("raceThumbnails"), true); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); } return(false); }
public override void OnInspectorGUI() { serializedObject.Update(); MeshHideAsset source = target as MeshHideAsset; bool beginSceneEditing = false; //DrawDefaultInspector(); SlotDataAsset obj = EditorGUILayout.ObjectField("SlotDataAsset", source.asset, typeof(SlotDataAsset), false) as SlotDataAsset; if (obj != source.asset) { source.asset = obj as SlotDataAsset; if (_autoInitialize) { UpdateSourceAsset(obj); } } EditorGUILayout.LabelField("Slot Name", source.AssetSlotName.ToString()); _autoInitialize = EditorGUILayout.Toggle(new GUIContent("AutoInitialize (recommended)", "Checking this will auto initialize the MeshHideAsset when a slot is added (recommended). " + "For users that are rebuilding slots that don't change the geometry, the slot reference will be lost but can be reset without losing the existing MeshHide information by unchecking this."), _autoInitialize); if (source.asset == null) { EditorGUILayout.HelpBox("No SlotDataAsset set! Begin by adding a SlotDataAsset to the object field above.", MessageType.Error); } //Race Selector here GUILayout.Space(20); selectedRaceIndex = EditorGUILayout.Popup("Select Base Slot by Race", selectedRaceIndex, foundRaceNames.ToArray()); if (selectedRaceIndex <= 0) { EditorGUILayout.HelpBox("Quick selection of base slots by race. This is not needed to create a mesh hide asset, any slot can be used.", MessageType.Info); } else { UMAData.UMARecipe baseRecipe = new UMAData.UMARecipe(); foundRaces[selectedRaceIndex].baseRaceRecipe.Load(baseRecipe, UMAContext.FindInstance()); foreach (SlotData sd in baseRecipe.slotDataList) { if (sd != null && sd.asset != null) { if (GUILayout.Button(string.Format("{0} ({1})", sd.asset.name, sd.slotName))) { if (UpdateSourceAsset(sd.asset)) { selectedRaceIndex = 0; } } } } } GUILayout.Space(20); if (source.TriangleCount > 0) { EditorGUILayout.LabelField("Triangle Indices Count: " + source.TriangleCount); EditorGUILayout.LabelField("Submesh Count: " + source.SubmeshCount); EditorGUILayout.LabelField("Hidden Triangle Count: " + source.HiddenCount); } else { EditorGUILayout.LabelField("No triangle array found"); } GUILayout.Space(20); if (!GeometrySelectorWindow.IsOpen) { EditorGUI.BeginDisabledGroup(source.asset == null); if (GUILayout.Button("Begin Editing", GUILayout.MinHeight(50))) { if (source.asset != null) { beginSceneEditing = true; } } EditorGUI.EndDisabledGroup(); GUILayout.Space(20); GUILayout.Label("Editing will be done in an empty scene."); GUILayout.Label("You will be prompted to save the scene"); GUILayout.Label("if there are any unsaved changes."); } serializedObject.ApplyModifiedProperties(); if (beginSceneEditing) { // This has to happen outside the inspector EditorApplication.delayCall += CreateSceneEditObject; } }
public void OnEnable() { if (!NeedsReenable()) { return; } _errorMessage = null; _recipe = new UMAData.UMARecipe(); showBaseEditor = false; try { var umaRecipeBase = target as UMARecipeBase; if (umaRecipeBase != null) { //if we dont have an umaContext the recipe wont actually load and we dont want people to edit it because it wont save properly //01022016 BUT we do still ned to output the inspector or else people cant make new recipes when they have a scene open with no UMAContext //TODO work out a way of editing recipes when there ISNT an UMA Context var context = UMAContext.FindInstance(); if (context == null) { context = umaRecipeBase.CreateEditorContext(); generatedContext = context.gameObject; } if (context == null) { context = umaRecipeBase.CreateEditorContext(); _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContext."; Debug.LogWarning(_errorMessage); //_recipe = null; //return; } else if (context.raceLibrary == null) { _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContext with RaceLibrary assigned."; Debug.LogWarning(_errorMessage); //_recipe = null; //return; } //when the recipe loads it can be really slow the first time can we show a notification? //this doesn't bloody show quick enough though //var editorAssembly = typeof(Editor).Assembly; //var inspectorWindowType = editorAssembly.GetType("UnityEditor.InspectorWindow"); //inspectorWindow = EditorWindow.GetWindow(inspectorWindowType); //if(inspectorWindow != null) //inspectorWindow.ShowNotification(new GUIContent("UMA is gathering Data..")); //this is where we are waiting, but the notification does not show before this starts umaRecipeBase.Load(_recipe, context); _description = umaRecipeBase.GetInfo(); //if (inspectorWindow != null) // inspectorWindow.RemoveNotification(); } } catch (UMAResourceNotFoundException e) { _errorMessage = e.Message; } dnaEditor = new DNAMasterEditor(_recipe); slotEditor = new SlotMasterEditor(_recipe); _rebuildOnLayout = true; }
public bool OnGUI() { bool changed = false; if (_race != null) { if (_race.wardrobeSlots.Count > 0) { var context = UMAContext.FindInstance(); if (context == null) { var _errorMessage = "Editing a recipe requires a loaded scene with a valid UMAContext."; 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.dynamicCharacterSystem.Refresh(false); return(false); } //a dictionary of slots that are being assigned by WardrobeCollections var slotsAssignedByWCs = new Dictionary <string, string>(); if (_allowWardrobeCollectionSlot) { var wcRecipesForRace = context.dynamicCharacterSystem.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++; } } foreach (string wsl in _race.wardrobeSlots) { 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); }