You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

397 lines
16 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
/*
* This is V2 of this script! It changes things a bit, so if you are actively using this script, you may want to revert
* or take the time to update your prefabs and code.
*/
namespace InfinityPBR
{
[RequireComponent(typeof(PrefabAndObjectManager))]
[RequireComponent(typeof(BlendShapesManager))]
[System.Serializable]
public class WardrobePrefabManager : MonoBehaviour
{
public bool autoRigWhenActivated = true;
public bool handleBlendShapes = true;
[HideInInspector] public bool showHelpBoxes = true;
[HideInInspector] public bool showFullInspector = false;
[HideInInspector] public bool showBlendShapeManagement = false;
[HideInInspector] public bool showSetup = true;
[HideInInspector] public string[] actionTypes = new string[] {"Explicit", "Less than", "Greater than"};
public List<BlendShapeGroup> blendShapeGroups = new List<BlendShapeGroup>();
public List<BlendShapeObject> blendShapeObjects = new List<BlendShapeObject>();
public BlendShapesManager BlendShapesManager => GetBlendShapesManager();
private BlendShapesManager _blendShapesManager;
public PrefabAndObjectManager PrefabAndObjectManager => GetPrefabAndObjectManager();
private PrefabAndObjectManager _prefabAndObjectManager;
private BlendShapesManager GetBlendShapesManager()
{
if (_blendShapesManager != null) return _blendShapesManager;
if (TryGetComponent(out BlendShapesManager foundManager))
_blendShapesManager = foundManager;
return _blendShapesManager;
}
private PrefabAndObjectManager GetPrefabAndObjectManager()
{
if (_prefabAndObjectManager != null) return _prefabAndObjectManager;
if (TryGetComponent(out PrefabAndObjectManager foundManager))
_prefabAndObjectManager = foundManager;
return _prefabAndObjectManager;
}
public void OnActivate(string name)
{
SetBlendShapes();
List<BlendShapeItem> blendShapeItems = GetBlendShapeItems("Activate", name);
if (blendShapeItems != null)
{
for (int i = 0; i < blendShapeItems.Count; i++)
{
var revertResults = RevertOnDeactivate(blendShapeItems[i].triggerName, name);
bool hasRevert = revertResults.Item1;
int revertIndex = revertResults.Item2;
if (hasRevert)
{
BlendShapeItem revertItem = blendShapeGroups[GetGroupIndex(name)].onDeactivate[revertIndex];
BlendShapeValue bsvalue = BlendShapesManager.GetBlendShapeValue(revertItem.objectName, revertItem.triggerName);
if (bsvalue == null)
Debug.LogWarning("Unable to get value");
else
revertItem.value = bsvalue.value;
}
float value = blendShapeItems[i].value;
float currentValue = BlendShapesManager.GetBlendShapeValue(blendShapeItems[i].objectName, blendShapeItems[i].triggerName).value;
if (blendShapeItems[i].actionType == "Less than" && currentValue <= value)
value = currentValue;
if (blendShapeItems[i].actionType == "Greater than" && currentValue >= value)
value = currentValue;
TriggerBlendShape(blendShapeItems[i].triggerName, value);
}
}
}
public void OnDeactivate(string name)
{
SetBlendShapes();
List<BlendShapeItem> blendShapeItems = GetBlendShapeItems("Deactivate", name);
if (blendShapeItems != null)
{
for (int i = 0; i < blendShapeItems.Count; i++)
{
TriggerBlendShape(blendShapeItems[i].triggerName, blendShapeItems[i].value);
}
}
}
private (bool, int) RevertOnDeactivate(string triggerName, string name)
{
bool hasRevert = false;
int revertIndex = 0;
List<BlendShapeItem> blendShapeItems = GetBlendShapeItems("Deactivate", name);
for (int i = 0; i < blendShapeItems.Count; i++)
{
if (blendShapeItems[i].triggerName == triggerName)
{
if (blendShapeItems[i].revertBack)
{
hasRevert = true;
revertIndex = i;
}
}
}
return (hasRevert, revertIndex);
}
private bool GroupIsInList(PrefabGroup group) => blendShapeGroups.FirstOrDefault(x => x.prefabGroup.uid == group.uid) != null;
private bool GroupIsInManager(PrefabGroup prefabGroup) => PrefabAndObjectManager.prefabGroups.FirstOrDefault(x => x.uid == prefabGroup.uid) != null;
private BlendShapeGroup BlendShapeGroupByName(string name) =>
blendShapeGroups.FirstOrDefault(x => x.prefabGroup.name == name);
private List<BlendShapeItem> GetBlendShapeItems(string eventType, string blendShapeGroupName)
{
var shapeGroup = BlendShapeGroupByName(blendShapeGroupName);
if (shapeGroup == null)
return null;
if (eventType == "Activate")
return shapeGroup.onActivate;
if (eventType == "Deactivate")
return shapeGroup.onDeactivate;
return null; // This should not happen
}
/*
* This will update the group list, pulling in the data from the Prefab And Object Manager component.
* It will not remove groups that already exist.
*/
public void UpdateGroupList()
{
// Add groups we don't have yet
foreach (var group in PrefabAndObjectManager.prefabGroups)
{
if (GroupIsInList(group)) continue;
var newGroup = new BlendShapeGroup
{
prefabGroup = group
};
blendShapeGroups.Add(newGroup); ;
}
// Remove orphan groups
for (int i = blendShapeGroups.Count - 1; i >= 0; i--)
{
if (GroupIsInManager(blendShapeGroups[i].prefabGroup)) continue;
blendShapeGroups.RemoveAt(i);
}
PopulateGroupBlendShapeNames();
}
public void PopulateGroupBlendShapeNames()
{
for (int g = blendShapeGroups.Count - 1; g >= 0; g--)
{
blendShapeGroups[g].actualBlendShapeNames.Clear();
blendShapeGroups[g].blendShapeNames.Clear();
blendShapeGroups[g].blendShapeObjectName.Clear();
if (string.IsNullOrEmpty(blendShapeGroups[g].prefabGroup.name))
{
Debug.LogWarning("Warning: The name of this group is empty. If you recently updated the " +
"script, you may want to try resetting the \"Wardrobe Prefab Manager\", by clicking the " +
"three vertical dots to the right of it in the Inspector. Will now return as an error would " +
"be presented.");
return;
}
foreach (var groupObject in PrefabAndObjectManager.prefabGroups[GetGroupIndex(blendShapeGroups[g].prefabGroup.name)].groupObjects)
{
SkinnedMeshRenderer[] renderers = groupObject.objectToHandle.GetComponentsInChildren<SkinnedMeshRenderer>(true);
foreach (SkinnedMeshRenderer smr in renderers)
{
for (int b = 0; b < smr.sharedMesh.blendShapeCount; b++)
{
string newName = smr.sharedMesh.GetBlendShapeName(b);
if (!newName.Contains(BlendShapesManager.blendShapePrimaryPrefix))
continue;
if (newName.Contains(BlendShapesManager.plusMinus.Item2))
continue;
blendShapeGroups[g].actualBlendShapeNames.Add(newName);
newName = newName.Replace(BlendShapesManager.plusMinus.Item1, "");
newName = newName.Replace(BlendShapesManager.blendShapePrimaryPrefix, "");
blendShapeGroups[g].blendShapeNames.Add(newName);
blendShapeGroups[g].blendShapeObjectName.Add(smr.gameObject.name);
}
}
}
}
}
public int GetGroupIndex(string groupName)
{
for (int g = 0; g < PrefabAndObjectManager.prefabGroups.Count; g++)
{
if (PrefabAndObjectManager.prefabGroups[g].name != groupName) continue;
return g;
}
Debug.LogWarning($"No group index was found for {groupName}");
return -1;
}
public void SetBlendShapes()
{
blendShapeObjects.Clear();
Transform[] gameObjects = gameObject.GetComponentsInChildren<Transform>(true);
foreach (Transform transform in gameObjects)
{
GameObject gameObject = transform.gameObject;
if (!gameObject.GetComponent<SkinnedMeshRenderer>())
continue;
SkinnedMeshRenderer smr = gameObject.GetComponent<SkinnedMeshRenderer>();
if (!smr)
continue;
if (!smr.sharedMesh)
continue;
if (smr.sharedMesh.blendShapeCount == 0)
continue;
blendShapeObjects.Add(new BlendShapeObject());
BlendShapeObject newObject = blendShapeObjects[blendShapeObjects.Count - 1];
newObject.gameObjectWithShapes = gameObject;
for (int i = 0; i < smr.sharedMesh.blendShapeCount; i++)
{
newObject.blendShapeEntries.Add(new BlendShapeEntry());
BlendShapeEntry newEntry = newObject.blendShapeEntries[newObject.blendShapeEntries.Count - 1];
newEntry.index = i;
string triggerName = BlendShapesManager.GetHumanName(smr.sharedMesh.GetBlendShapeName(i));
if (triggerName.Contains(BlendShapesManager.plusMinus.Item2))
newEntry.isMinus = true;
newEntry.triggerName = triggerName;
}
}
}
public void TriggerBlendShape(string triggerName, float shapeValue)
{
BlendShapesManager.TriggerAutoMatches(triggerName, shapeValue);
}
public BlendShapeGroup GetGroup(PrefabGroup group) => blendShapeGroups.FirstOrDefault(x => x.prefabGroup.uid == group.uid);
public void AddToList(string eventType, MatchList matchList, BlendShapeGroup group)
{
BlendShapeItem newItem = new BlendShapeItem();
BlendShapeGameObject obj = BlendShapesManager.blendShapeGameObjects[matchList.objectIndex];
BlendShapeValue value = obj.blendShapeValues[matchList.valueIndex];
string triggerName = value.triggerName;
if (eventType == "Activate")
{
// This commented-out loop was intended to ensure only one entry per item, but we now wnat to allow
// multiple due to the addition of the "actionType", so you can do less than & greater than. (Between)
for (int i = 0; i < group.onActivate.Count; i++)
{
//if (group.onActivate[i].triggerName == triggerName)
// return;
}
group.onActivate.Add(new BlendShapeItem());
newItem = group.onActivate[group.onActivate.Count - 1];
}
if (eventType == "Deactivate" || eventType == "Revert Back")
{
for (int i = 0; i < group.onDeactivate.Count; i++)
{
if (group.onDeactivate[i].triggerName == triggerName)
return;
}
group.onDeactivate.Add(new BlendShapeItem());
newItem = group.onDeactivate[group.onDeactivate.Count - 1];
if (eventType == "Revert Back")
newItem.revertBack = true;
}
newItem.min = value.min;
newItem.triggerName = triggerName;
newItem.objectName = obj.gameObjectName;
}
public void AddToList(string eventType, BlendShapeGroup group)
{
BlendShapeItem newItem = new BlendShapeItem();
if (group.actualBlendShapeNames[group.shapeChoiceIndex].Contains(BlendShapesManager.plusMinus.Item1))
newItem.min = -100f;
string triggerName = BlendShapesManager.GetHumanName(group.actualBlendShapeNames[group.shapeChoiceIndex]);
string objectName = "";
if (eventType == "Activate")
{
for (int i = 0; i < group.onActivate.Count; i++)
{
if (group.onActivate[i].triggerName == triggerName)
return;
}
group.onActivate.Add(new BlendShapeItem());
newItem = group.onActivate[group.onActivate.Count - 1];
objectName = group.blendShapeObjectName[group.shapeChoiceIndex];
}
else if (eventType == "Deactivate" || eventType == "Revert Back")
{
for (int i = 0; i < group.onDeactivate.Count; i++)
{
if (group.onDeactivate[i].triggerName == group.actualBlendShapeNames[group.shapeChoiceIndex])
return;
}
group.onDeactivate.Add(new BlendShapeItem());
newItem = group.onDeactivate[group.onDeactivate.Count - 1];
objectName = group.blendShapeObjectName[group.shapeChoiceIndex];
if (eventType == "Revert Back")
newItem.revertBack = true;
}
newItem.triggerName = triggerName;
newItem.objectName = objectName;
}
}
[System.Serializable]
public class BlendShapeGroup
{
//public string name;
//public string type;
public PrefabGroup prefabGroup;
public List<BlendShapeItem> onActivate = new List<BlendShapeItem>();
public List<BlendShapeItem> onDeactivate = new List<BlendShapeItem>();
[HideInInspector] public bool showList = false;
[HideInInspector] public List<string> blendShapeNames = new List<string>();
[HideInInspector] public List<string> actualBlendShapeNames = new List<string>();
[HideInInspector] public List<string> blendShapeObjectName = new List<string>();
[HideInInspector] public int shapeChoiceIndex = 0;
//[HideInInspector] public int deactivateChoiceIndex = 0;
}
[System.Serializable]
public class BlendShapeItem
{
public string triggerName;
public string objectName;
public float value;
public float min = 0f;
public float max = 100f;
public bool revertBack = false;
public string actionType = "Explicit";
public int actionTypeIndex = 0;
}
[System.Serializable]
public class BlendShapeObject
{
public GameObject gameObjectWithShapes;
public List<BlendShapeEntry> blendShapeEntries = new List<BlendShapeEntry>();
}
[System.Serializable]
public class BlendShapeEntry
{
public int index;
public string triggerName;
public bool isMinus = false;
}
}