Beispiel #1
0
        /// <summary>
        /// Searches the recipe of the current active race for all its slots and builds a list of available bones from the slots meshData
        /// </summary>
        public void UpdateSeverablesFromActiveRecipe(string[] filters = null)
        {
            Debug.Log("UpdateSeverablesFromActiveRecipe for " + _characterAvatar.activeRace.name);
            var umaSeverablesList         = new List <UMATransform>();
            var selectedUmaSeverablesList = new List <UMATransform>();

            UMAData.UMARecipe umaRecipe = new UMAData.UMARecipe();
            _characterAvatar.activeRace.data.baseRaceRecipe.Load(umaRecipe, _characterAvatar.context);
            foreach (SlotData slot in umaRecipe.GetAllSlots())
            {
                if (slot == null || slot.asset == null || slot.asset.meshData == null || slot.asset.meshData.umaBones == null)
                {
                    continue;
                }
                //UMATransform doesn't have a System.IEquatable<UMATransform> so we have to compare by hash
                foreach (UMATransform umaTrans in slot.asset.meshData.umaBones)
                {
                    //Find out if umaSeverablesList already has this UMATransform in it
                    bool canAdd = !IsUMATransformInList(umaTrans, umaSeverablesList);
                    if (canAdd)
                    {
                        //filter out any bones we dont want
                        if (filters != null)
                        {
                            for (int fi = 0; fi < filters.Length; fi++)
                            {
                                if (!string.IsNullOrEmpty(filters[fi]) && umaTrans.name.IndexOf(filters[fi]) > -1)
                                {
                                    canAdd = false;
                                }
                            }
                        }
                        if (canAdd)
                        {
                            umaSeverablesList.Add(umaTrans);
                        }
                    }
                }
            }
            if (_cachedRaceSeverables.ContainsKey(_characterAvatar.activeRace.name))
            {
                //used the cached UMATransforms if we have them
                selectedUmaSeverablesList = new List <UMATransform>(_cachedRaceSeverables[_characterAvatar.activeRace.name]);
            }
            else
            {
                //only add any selected UMATrans that are in the new list
                for (int i = 0; i < _selectedUmaSeverables.Length; i++)
                {
                    if (IsUMATransformInList(_selectedUmaSeverables[i], umaSeverablesList))
                    {
                        selectedUmaSeverablesList.Add(_selectedUmaSeverables[i]);
                    }
                }
            }
            //can we sort _umaSeverables so it reflects the bone heirarcy? Its pretty horrible like this. Probably can since each bone contains 'parent' data
            _umaSeverables         = SortUMASeverablesList(umaSeverablesList).ToArray();
            _selectedUmaSeverables = selectedUmaSeverablesList.ToArray();
        }
Beispiel #2
0
 //generate an option list for the BaseSlots that are available to hide for each race so we can make this a mask field too
 private void GenerateBaseSlotsEnum(List <string> compatibleRaces, bool forceUpdate = false)
 {
     if (generatedBaseSlotOptions.Count == 0 || forceUpdate)
     {
         //clear the lists if we are forcing update
         if (forceUpdate)
         {
             generatedBaseSlotOptions       = new List <string>();
             generatedBaseSlotOptionsLabels = new List <string>();
         }
         List <UMARecipeBase> thisBaseRecipes = new List <UMARecipeBase>();
         Dictionary <string, List <string> > slotsRacesDict = new Dictionary <string, List <string> >();
         for (int i = 0; i < compatibleRaces.Count; i++)
         {
             if (GetCompatibleRaceData(compatibleRaces[i]) == null)
             {
                 continue;
             }
             thisBaseRecipes.Add(GetCompatibleRaceData(compatibleRaces[i]).baseRaceRecipe);
         }
         for (int i = 0; i < thisBaseRecipes.Count; i++)
         {
             if (thisBaseRecipes[i] != null)
             {
                 UMAData.UMARecipe thisBaseRecipe = thisBaseRecipes[i].GetCachedRecipe(UMAContext.Instance);
                 SlotData[]        thisSlots      = thisBaseRecipe.GetAllSlots();
                 foreach (SlotData slot in thisSlots)
                 {
                     if (slot != null)
                     {
                         if (!generatedBaseSlotOptions.Contains(slot.asset.slotName))
                         {
                             generatedBaseSlotOptions.Add(slot.asset.slotName);
                         }
                         if (!slotsRacesDict.ContainsKey(slot.asset.slotName))
                         {
                             slotsRacesDict.Add(slot.asset.slotName, new List <string>());
                         }
                         slotsRacesDict[slot.asset.slotName].Add(compatibleRaces[i]);
                     }
                 }
             }
         }
         //sort out the labels showing which race(s) the base slots are for if there is more than one compatible race
         foreach (KeyValuePair <string, List <string> > kp in slotsRacesDict)
         {
             string compatibleRaceNames = "";
             if (compatibleRaces.Count > 1)
             {
                 compatibleRaceNames = " (" + String.Join(", ", kp.Value.ToArray()) + ")";
             }
             generatedBaseSlotOptionsLabels.Add(kp.Key + compatibleRaceNames);
         }
     }
 }
Beispiel #3
0
 private void DrawCCUI(string ccRaceName, SerializedProperty baseRaceRecipe, SerializedProperty thisCCSettings)
 {
     GUIHelper.BeginVerticalPadded(5, new Color(0.75f, 0.875f, 1f));
     EditorGUILayout.LabelField("Equivalent Slots with " + ccRaceName, EditorStyles.centeredGreyMiniLabel);
     if (baseRaceRecipe.objectReferenceValue == null)
     {
         EditorGUILayout.HelpBox("Please set this Races 'Base Race Recipe' before trying to set equivalent Slots.", MessageType.Warning);
     }
     else
     {
         //we need to get the base raceRecipeSlots for this compatible race
         var ccRaceData = GetCompatibleRaceData(ccRaceName);
         if (ccRaceData != null)
         {
             if (ccRaceData.baseRaceRecipe == null)
             {
                 EditorGUILayout.HelpBox("Please set " + ccRaceData.raceName + " Races 'Base Race Recipe' before trying to set equivalent Slots.", MessageType.Warning);
             }
             else
             {
                 var ccSlotsList                = new List <SlotData>();
                 var ccSlotsNamesList           = new List <string>();
                 UMAData.UMARecipe ccBaseRecipe = ccRaceData.baseRaceRecipe.GetCachedRecipe(UMAContextBase.Instance);
                 SlotData[]        ccBaseSlots  = ccBaseRecipe.GetAllSlots();
                 foreach (SlotData slot in ccBaseSlots)
                 {
                     if (slot != null)
                     {
                         ccSlotsList.Add(slot);
                         ccSlotsNamesList.Add(slot.slotName);
                     }
                 }
                 //if that worked we can draw the UI for any set values and a button to add new ones
                 GUIHelper.BeginVerticalPadded(2, new Color(1f, 1f, 1f, 0.5f));
                 var headerRect             = GUILayoutUtility.GetRect(0.0f, (EditorGUIUtility.singleLineHeight * 2), GUILayout.ExpandWidth(true));
                 var slotLabelRect          = headerRect;
                 var gapRect                = headerRect;
                 var cSlotLabelRect         = headerRect;
                 var overlaysMatchLabelRect = headerRect;
                 var deleteRect             = headerRect;
                 slotLabelRect.width          = (headerRect.width - 50f - 22f - 22f) / 2;
                 gapRect.xMin                 = slotLabelRect.xMax;
                 gapRect.width                = 22f;
                 cSlotLabelRect.xMin          = gapRect.xMax;
                 cSlotLabelRect.width         = slotLabelRect.width;
                 overlaysMatchLabelRect.xMin  = cSlotLabelRect.xMax;
                 overlaysMatchLabelRect.width = 50f;
                 deleteRect.xMin              = overlaysMatchLabelRect.xMax;
                 deleteRect.width             = 22f;
                 //move this up
                 var tableHeaderStyle = EditorStyles.wordWrappedMiniLabel;
                 tableHeaderStyle.alignment = TextAnchor.MiddleCenter;
                 //we need a gui style for this that wraps the text and vertically centers it in the space
                 EditorGUI.LabelField(slotLabelRect, "This Races Slot", tableHeaderStyle);
                 EditorGUI.LabelField(gapRect, "", tableHeaderStyle);
                 EditorGUI.LabelField(cSlotLabelRect, "Compatible Races Slot", tableHeaderStyle);
                 EditorGUI.LabelField(overlaysMatchLabelRect, "Overlays Match", tableHeaderStyle);
                 GUIHelper.EndVerticalPadded(2);
                 GUIHelper.BeginVerticalPadded(2, new Color(0.75f, 0.875f, 1f));
                 if (thisCCSettings.arraySize > 0)
                 {
                     for (int ccsd = 0; ccsd < thisCCSettings.arraySize; ccsd++)
                     {
                         if (DrawCCUISetting(ccsd, thisCCSettings, ccSlotsNamesList))
                         {
                             serializedObject.ApplyModifiedProperties();
                         }
                     }
                 }
                 else
                 {
                     EditorGUILayout.LabelField("No equivalent slots defined", EditorStyles.miniLabel);
                 }
                 GUIHelper.EndVerticalPadded(2);
                 var addButtonRect = GUILayoutUtility.GetRect(0.0f, EditorGUIUtility.singleLineHeight, GUILayout.ExpandWidth(true));
                 addButtonRect.xMin  = addButtonRect.xMax - 70f;
                 addButtonRect.width = 70f;
                 if (GUI.Button(addButtonRect, "Add"))
                 {
                     thisCCSettings.InsertArrayElementAtIndex(thisCCSettings.arraySize);
                     serializedObject.ApplyModifiedProperties();
                 }
             }
         }
         else
         {
             EditorGUILayout.HelpBox("The cross compatible race " + ccRaceName + " could not be found!", MessageType.Warning);
         }
     }
     GUIHelper.EndVerticalPadded(5);
 }
Beispiel #4
0
        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 (UMAContextBase.Instance == null)
                    {
                        EditorUMAContextBase = UMAContextBase.CreateEditorContext();
                    }
                    UMAData.UMARecipe thisBaseRecipe = (baseRaceRecipe.objectReferenceValue as UMARecipeBase).GetCachedRecipe(UMAContextBase.Instance);
                    SlotData[]        thisBaseSlots  = thisBaseRecipe.GetAllSlots();
                    foreach (SlotData slot in thisBaseSlots)
                    {
                        if (slot != null)
                        {
                            baseSlotsList.Add(slot);
                            baseSlotsNamesList.Add(slot.slotName);
                        }
                    }
                    List <int> crossCompatibleSettingsToDelete = new List <int>();
                    //draw a foldout area for each compatible race that will show an entry for each slot in this races base recipe
                    //with a picker to choose the slot from the compatible race's base recipe that it equates to
                    for (int i = 0; i < _crossCompatibilitySettingsData.arraySize; i++)
                    {
                        bool del            = false;
                        var  thisCCSettings = _crossCompatibilitySettingsData.GetArrayElementAtIndex(i).FindPropertyRelative("ccSettings");
                        var  ccRaceName     = _crossCompatibilitySettingsData.GetArrayElementAtIndex(i).FindPropertyRelative("ccRace").stringValue;
                        //this could be missing- we should show that
                        var label = ccRaceName;
                        if (GetCompatibleRaceData(ccRaceName) == null)
                        {
                            label += " (missing)";
                        }
                        GUIHelper.FoldoutBar(ref _BCFoldouts[i], label, out del);
                        if (del)
                        {
                            crossCompatibleSettingsToDelete.Add(i);
                        }
                        if (_BCFoldouts[i])
                        {
                            DrawCCUI(ccRaceName, baseRaceRecipe, thisCCSettings);
                        }
                    }
                    if (crossCompatibleSettingsToDelete.Count > 0)
                    {
                        foreach (int del in crossCompatibleSettingsToDelete)
                        {
                            _crossCompatibilitySettingsData.DeleteArrayElementAtIndex(del);
                            serializedObject.ApplyModifiedProperties();
                        }
                    }
                }
                else
                {
                    EditorGUILayout.HelpBox("Please define this races baseRaceRecipe before trying to define its cross compatibility settings.", MessageType.Info);
                }
            }

            EditorGUILayout.Space();

            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(serializedObject.FindProperty("raceThumbnails"), true);
            if (EditorGUI.EndChangeCheck())
            {
                serializedObject.ApplyModifiedProperties();
            }
            return(false);
        }