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,134 @@
#if UNITY_EDITOR
#if UMA_ADDRESSABLES
#if UMA_NOASMDEF
using UnityEditor;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
using UnityEditor.VersionControl;
using UnityEngine;
namespace UMA
{
public class AddressableInfo
{
public string AddressableAddress;
public string AddressableGroup;
public string AddressableLabels;
public AddressableInfo(string addressableAddress, string addressableGroup, string addressableLabels)
{
AddressableAddress = addressableAddress;
AddressableGroup = addressableGroup;
AddressableLabels = addressableLabels;
}
};
public class AddressableUtility
{
private static readonly AddressableUtility addressableUtility = new AddressableUtility();
private static AddressableAssetSettings _AddressableSettings;
public static AddressableAssetSettings AddressableSettings
{
get
{
if (_AddressableSettings == null)
{
string[] Settings = AssetDatabase.FindAssets("AddressableAssetSettings");
string path = AssetDatabase.GUIDToAssetPath(Settings[0]);
_AddressableSettings = AssetDatabase.LoadAssetAtPath<AddressableAssetSettings>(path);
}
return _AddressableSettings;
}
}
public static bool DoesAddressExist(string label)
{
List<AddressableAssetEntry> allEntries = new List<AddressableAssetEntry>();
AddressableUtility.AddressableSettings.GetAllAssets(allEntries, false);
foreach (AddressableAssetEntry entry in allEntries)
{
if (entry.labels.Contains(label))
return true;
}
return false;
}
public static AddressableAssetEntry GetAddressableAssetEntry(string assetPath, out AddressableAssetGroup assetgroup)
{
assetgroup = null;
if (AddressableSettings == null)
{
return null;
}
foreach (var group in AddressableSettings.groups)
{
if (group.HasSchema<PlayerDataGroupSchema>())
continue;
foreach (AddressableAssetEntry e in group.entries)
{
if (e.AssetPath == assetPath)
{
assetgroup = group;
return e;
}
}
}
// Not found
return null;
}
public static AddressableAssetEntry GetAddressableAssetEntry(string AssetPath)
{
if (AddressableSettings == null)
{
return null;
}
foreach (var group in AddressableSettings.groups)
{
if (group.HasSchema<PlayerDataGroupSchema>())
continue;
foreach (AddressableAssetEntry e in group.entries)
{
if (e.AssetPath == AssetPath)
{
return e;
}
}
}
// Not found
return null;
}
public static string GetAddressableLabels(AddressableAssetEntry ae)
{
string retval = "";
foreach (string s in ae.labels)
{
retval += s + ";";
}
return retval;
}
public static AddressableInfo GetAddressableInfo(string assetPath)
{
AddressableAssetEntry ae = GetAddressableAssetEntry(assetPath);
if (ae != null)
{
return new AddressableInfo(ae.address, ae.parentGroup.Name, GetAddressableLabels(ae));
}
return null;
}
}
}
#endif
#endif
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b8f30b5810c88e24eb81272cfc351d1b
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/Scripts/AddressableUtility.cs
uploadId: 679826
+440
View File
@@ -0,0 +1,440 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UMA
{
[System.Serializable]
public class AssetItem
#if UNITY_EDITOR
: System.IEquatable<AssetItem>, System.IComparable<AssetItem>, ISerializationCallbackReceiver
#endif
{
#region Fields
public const string AddressableFolder = "UMA/";
private System.Type _TheType;
public string _BaseTypeName;
public string _Name;
public Object _SerializedItem;
public string _Path;
public string _Guid;
public string _Address;
public bool IsResource;
public bool IsAssetBundle;
public bool IsAddressable;
public bool IsAlwaysLoaded;
public bool Ignore; // does not go into adressables or resources.
public string AddressableGroup;
public string AddressableAddress
{
get
{
if (IsAddressable && string.IsNullOrEmpty(_Address))
{
return AddressableFolder + _Type.Name + "-" + EvilName;
}
return _Address;
}
set
{
_Address = value;
}
}
public string AddressableLabels;
public int ReferenceCount;
#endregion
#region Properties
public System.Type _Type
{
get
{
if (_TheType != null && _TheType != typeof(UnityEngine.Object))
{
return _TheType;
}
if (!UMAAssetIndexer.TypeFromString.ContainsKey(_BaseTypeName))
{
if (_BaseTypeName.Contains("SlotData"))
{
_TheType = typeof(SlotDataAsset);
}
else if (_BaseTypeName.Contains("OverlayData"))
{
_TheType = typeof(OverlayDataAsset);
}
else if (_BaseTypeName.Contains("Animator")) // for some reason the animatorcontrollers were blowing up in 2019.3
{
_TheType = typeof(RuntimeAnimatorController);
}
else if (_BaseTypeName.Contains("RaceData"))
{
_TheType = typeof(RaceData);
}
}
else
{
_TheType = UMAAssetIndexer.TypeFromString[_BaseTypeName];
}
return _TheType;
}
}
public AssetItem CreateSerializedItem(bool ForceItemSave)
{
if (ForceItemSave)
{
// If this flag is set, then we must serialize the item also (this is used when building the executable).
return new AssetItem(this._Type, this._Name, this._Path, this.Item);
}
else
{
return new AssetItem(this._Type, this._Name, this._Path, null);
}
}
public Object Item
{
get
{
#if UNITY_EDITOR
if (_SerializedItem != null)
{
return _SerializedItem;
}
// Items that are addressable should not be cached.
// but the editors still need them, so we'll load them from
// the assetdatabase as needed.
#if !UMA_ALWAYSGETADDR_NO_PROD
if (IsAddressable)
{
if (Application.isPlaying)
{
return null;
}
else
{
return GetItem();
}
}
#endif
CacheSerializedItem();
return _SerializedItem;
#else
return _SerializedItem;
#endif
}
}
public string _AssetBaseName
{
get
{
return System.IO.Path.GetFileNameWithoutExtension(_Path);
}
}
private Object GetItem()
{
#if UNITY_EDITOR
Object itemObject = AssetDatabase.LoadAssetAtPath(_Path, _Type);
if (itemObject == null)
{
// uhoh. It's gone.
if (!string.IsNullOrEmpty(_Guid))
{
// at least we have a guid. Let's try to find it from that...
_Path = AssetDatabase.GUIDToAssetPath(_Guid);
if (!string.IsNullOrEmpty(_Path))
{
itemObject = AssetDatabase.LoadAssetAtPath(_Path, _Type);
}
}
// No guid, or couldn't even find by GUID.
// Let's search for it?
if (itemObject == null)
{
string s = _Type.Name;
string[] guids = AssetDatabase.FindAssets(_Name + " t:" + s);
if (guids.Length > 0)
{
_Guid = guids[0];
_Path = AssetDatabase.GUIDToAssetPath(_Guid);
itemObject = AssetDatabase.LoadAssetAtPath(_Path, _Type);
}
}
}
#else
Object itemObject = null;
#endif
return itemObject;
}
public Object CacheSerializedItem()
{
#if UNITY_EDITOR
if (_SerializedItem != null) return _SerializedItem;
#if SUPER_LOGGING
Debug.Log("Loading item in AssetItem: " + _Name);
#endif
//if (IsAddressable) return;
_SerializedItem = GetItem();
return _SerializedItem;
#else
// This function does nothing in a build.
return null;
#endif
}
public static string TranslatedName(string Name)
{
#if UMA_INDEX_LC
return Name.ToLower();
#else
return Name;
#endif
}
public static string GetEvilName(Object o)
{
if (!o)
{
return "<Not Found!>";
}
if (o is SlotDataAsset)
{
SlotDataAsset sd = o as SlotDataAsset;
if (!string.IsNullOrEmpty(sd.slotName))
{
return TranslatedName(sd.slotName);
}
}
if (o is OverlayDataAsset)
{
OverlayDataAsset od = o as OverlayDataAsset;
if (!string.IsNullOrEmpty(od.overlayName))
{
return TranslatedName(od.overlayName);
}
}
if (o is RaceData)
{
RaceData rd = o as RaceData;
if (!string.IsNullOrEmpty(rd.raceName))
{
return TranslatedName(rd.raceName);
}
}
return TranslatedName(o.name);
}
public string EvilName
{
get
{
Object o = Item;
return GetEvilName(o);
}
}
#endregion
public void AddReference()
{
ReferenceCount++;
}
public void FreeReference()
{
ReferenceCount = 0;
_SerializedItem = null;
}
public void ReleaseItem()
{
if (IsAddressable)
{
ReferenceCount--;
if (ReferenceCount < 1)
{
if (ReferenceCount < 0)
{
Debug.LogError("Reference count is negative on AssetItem " + this._Name + " of Type " + this._TheType+". This should not happen.");
return;
}
FreeReference();
}
}
else
{
FreeReference();
}
}
public bool IsLoaded
{
get
{
if (IsAddressable)
{
return _SerializedItem != null;
}
return true;
}
}
public bool IsOverlayDataAsset
{
get
{
return _Type == typeof(OverlayDataAsset);
}
}
public bool IsSlotDataAsset
{
get
{
return _Type == typeof(SlotDataAsset);
}
}
#region Methods (edit time)
#if UNITY_EDITOR
public string ToString(string SortOrder)
{
if (SortOrder == "AssetName")
{
return _AssetBaseName;
}
if (SortOrder == "FilePath")
{
return _Path;
}
return _Name;
}
public bool Equals(AssetItem other)
{
if (other == null)
{
return false;
}
if (UMAAssetIndexer.SortOrder == "AssetName")
{
if (this._AssetBaseName == other._AssetBaseName)
{
return true;
}
else
{
return false;
}
}
if (UMAAssetIndexer.SortOrder == "FilePath")
{
if (this._Path == other._Path)
{
return true;
}
else
{
return false;
}
}
if (this._Name == other._Name)
{
return true;
}
return false;
}
public int CompareTo(AssetItem other)
{
// A null value means that this object is greater.
if (other == null)
{
return 1;
}
if (UMAAssetIndexer.SortOrder == "AssetName")
{
return (this._AssetBaseName.CompareTo(other._AssetBaseName));
}
if (UMAAssetIndexer.SortOrder == "FilePath")
{
return this._Path.CompareTo(other._Path);
}
return this._Name.CompareTo(other._Name);
}
public void OnBeforeSerialize()
{
if (IsAddressable)
{
_SerializedItem = null;
}
}
public void OnAfterDeserialize()
{
if (IsAddressable)
{
_SerializedItem = null;
}
}
#endif
#endregion
#region Constructors
public AssetItem(System.Type Type, string Name, string Path, Object Item)
{
if (Type == null)
{
return;
}
_TheType = Type;
_BaseTypeName = Type.Name;
_Name = Name;
_SerializedItem = Item;
_Path = Path;
#if UNITY_EDITOR
_Guid = AssetDatabase.AssetPathToGUID(_Path);
#endif
}
public AssetItem(System.Type Type, Object Item)
{
if (Type == null)
{
return;
}
#if UNITY_EDITOR
_Path = AssetDatabase.GetAssetPath(Item.GetInstanceID());
_Guid = AssetDatabase.AssetPathToGUID(_Path);
#endif
_TheType = Type;
_BaseTypeName = Type.Name;
_SerializedItem = Item;
_Name = EvilName;
}
#endregion
}
}
+19
View File
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 30f73fd70e24ce449b4f09400865ef6b
timeCreated: 1491331883
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/Scripts/AssetItem.cs
uploadId: 679826
@@ -0,0 +1,626 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UMA
{
/// <summary>
/// Deals with base modifications to the character, user can enable scale, height, radius, mass and bounds modifications.
/// </summary>
//This replaces the fields and methods in DynamicDNAConverterBehaviour pre-DynamicDNAPlugins.
//The available modifications are the same but each kind can be enabled or disabled for more granular control
//The method for getting the character height is now based solely on the resulting bone structure of the mechanim bones (for humanoid characters)
//so it no longer assumes certain dna or bones are available
//the adjustment numbers should now be considered to be more like 'padding' since none of them (apart from 'Head Ratio') are actually required in order to perform any calculations
[System.Serializable]
public class BaseCharacterModifier
{
#region FIELDS
[Tooltip("If true the characters 'base scale' will be adjusted using the given scale value on the given bone. For rigs based on the standard UMA Rig, this is usually the 'Position' bone.")]
[SerializeField]
private bool _adjustScale;
[Tooltip("Adjust the height calculation for the character. Head Ratio is how many 'heads high' the character is (classically proportioned character's total height = 7.5 * head height). Bigger heads have a smaller ratio. You can make further manual adjustments using the 'Y' setting. The playmode 'Height Debug' tools in the Converter Customiser scene will help you.")]
[SerializeField]
private bool _adjustHeight;
[Tooltip("Manually adds an X and Z amount when calculating the characters radius.")]
[SerializeField]
private bool _adjustRadius;
//I think the x value here is like 'base weight' and the y and z values are how much any extra height or width should influence this
//but I'm not sure- in the scripted UMA converters Human Male ends up weighing less than HumanFemale, so its never been right
[SerializeField]
private bool _adjustMass;
[Tooltip("Should the bounds be updated when the dna changes. Turn this on if you are permitting large dna changes on your character.")]
[SerializeField]
private bool _updateBounds;
[Tooltip("Checking this will make the bounds tight to the characters head/feet. You can manually adjust the bounds futher using 'Adjust Bounds' below.")]
[SerializeField]
private bool _tightenBounds;
[Tooltip("Manually adds extra padding to the characters bounds")]
[SerializeField]
private bool _adjustBounds;
[SerializeField]
private float _scale = 1f;
//This is the field thats assigned in the editor, but we use the hash
#pragma warning disable 0414
[SerializeField]
private string _bone = "Position";
#pragma warning restore 0414
[SerializeField]
private int _scaleBoneHash = -1084586333;//hash of the Position Bone
[SerializeField]
private float _headRatio = 7.5f;
//when importing this will be the Y value of the radius
[SerializeField]
private float _radiusAdjustY = 0.01f;
//when importing this will be the x value of radius adjust (there never has been a z but there should be)
[SerializeField]
private Vector2 _radiusAdjust = new Vector2(0.23f, 0);
[Tooltip("This is used to adjust the characters mass.")]
[SerializeField]
private Vector3 _massAdjust = new Vector3(46f, 26f, 26f);
[SerializeField]
private Vector3 _boundsAdjust = Vector3.zero;
#endregion
#region NON-SERIALIZED FIELDS
//I know private fields are not supposed to be serialized but somehow setting these values at playtime still means they are here after
//(not that flagging as [Non-Serialized] helps I now discover...
[System.NonSerialized]
private Dictionary<string, int> _mechanimBoneDict = new Dictionary<string, int>();
[System.NonSerialized]
private string _lastRace = null;
[System.NonSerialized]
private bool boundsAdjustmentApplied = false;
[System.NonSerialized]
private float _liveScale = -1f;
#endregion
#region PUBLIC PROPERTIES
//do we need 'sets' for these in order to be backwards compatible?
public bool adjustScale
{
get { return _adjustScale; }
}
public bool adjustHeight
{
get { return _adjustHeight; }
}
public bool adjustRadius
{
get { return _adjustRadius; }
}
public bool adjustMass
{
get { return _adjustMass; }
}
public bool updateBounds
{
get { return _updateBounds; }
}
public bool tightenBounds
{
get { return _tightenBounds; }
}
public bool adjustBounds
{
get { return _adjustBounds; }
}
public float scale
{
get { return _scale; }
}
public int scaleBoneHash
{
get { return _scaleBoneHash; }
}
public float headRatio
{
get { return _headRatio; }
}
public float radiusAdjustY
{
get { return _radiusAdjustY; }
}
public Vector2 radiusAdjust
{
get { return _radiusAdjust; }
}
public Vector3 massAdjust
{
get { return _massAdjust; }
}
public Vector3 boundsAdjust
{
get { return _boundsAdjust; }
}
/// <summary>
/// Changes the characters base scale at runtime
/// </summary>
public float liveScale
{
get { return _liveScale; }
set { _liveScale = value; }
}
#endregion
#region CTOR
public BaseCharacterModifier() { }
//an epic constructor for backwards compatibility when re-deserializing
public BaseCharacterModifier(bool overallModifiersEnabled, float overallScale,
string overallScaleBone, int overallScaleBoneHash,
bool tightenBounds, Vector3 boundsAdjust,
Vector2 radiusAdjust, Vector3 massModifiers)
{
if (!overallModifiersEnabled)
{
this._adjustScale = false;
this._adjustHeight = false;
this._adjustRadius = false;
this._adjustMass = false;
this._updateBounds = false;
this._tightenBounds = false;
this._adjustBounds = false;
}
else
{
this._adjustScale = (!string.IsNullOrEmpty(overallScaleBone) && overallScaleBoneHash != -1 && overallScale != 1f) ? true : false;
this._adjustHeight = true;
this._adjustRadius = true;
this._adjustMass = true;
this._updateBounds = true;
this._tightenBounds = tightenBounds;
this._adjustBounds = boundsAdjust != Vector3.zero ? true : false;
}
this._scale = overallScale;
this._bone = overallScaleBone;
this._scaleBoneHash = overallScaleBoneHash;
this._radiusAdjustY = radiusAdjust.y;
this._radiusAdjust = new Vector2(radiusAdjust.x, 0f);
this._massAdjust = new Vector3(massModifiers.x, massModifiers.y, massModifiers.z);
this._boundsAdjust = new Vector3(boundsAdjust.x, boundsAdjust.y, boundsAdjust.z);
}
#endregion
#region PUBLIC METHODS
#if UNITY_EDITOR
public void ImportSettings(BaseCharacterModifier other)
{
this._adjustBounds = other._adjustBounds;
this._adjustHeight = other._adjustHeight;
this._adjustMass = other._adjustMass;
this._adjustRadius = other._adjustRadius;
this._adjustScale = other.adjustScale;
this._bone = other._bone;
this._boundsAdjust = other._boundsAdjust;
this._headRatio = other._headRatio;
this._massAdjust = other._massAdjust;
this._radiusAdjust = other._radiusAdjust;
this._radiusAdjustY = other._radiusAdjustY;
this._scale = other._scale;
this._scaleBoneHash = other._scaleBoneHash;
this._tightenBounds = other._tightenBounds;
this._updateBounds = other._updateBounds;
}
#endif
public void AdjustScale(UMASkeleton skeleton)
{
if (_adjustScale)
{
if (skeleton.HasBone(scaleBoneHash))
{
var liveScaleResult = _liveScale != -1f ? _liveScale : _scale;
float finalOverallScale = skeleton.GetScale(_scaleBoneHash).x * liveScaleResult;//hmm why does this work- its supposed to be +
skeleton.SetScale(_scaleBoneHash, new Vector3(finalOverallScale, finalOverallScale, finalOverallScale));
}
}
}
public void UpdateCharacterHeightMassRadius(UMAData umaData, UMASkeleton skeleton)
{
if (_adjustHeight || _adjustMass || _adjustRadius || _adjustBounds)
{
var baseRenderer = GetBaseRenderer(umaData);
if (baseRenderer == null)
{
return;
}
var newBounds = DoBoundsModifications(baseRenderer, umaData);
UpdateCharacterHeightMassRadius(umaData, skeleton, newBounds);
}
}
public void UpdateCharacter(UMAData umaData, UMASkeleton skeleton, bool asReset)
{
AdjustScale(skeleton);
UpdateCharacterHeightMassRadius(umaData, skeleton);
}
#endregion
#region PRIVATE METHODS
/// <summary>
/// Performs the bounds adjustment operations (updateBounds, tightenBounds, AdjustBounds) if enabled.
/// </summary>
/// <param name="targetRenderer">The renderer to perform the adjustments on.</param>
/// <param name="umaData"></param>
/// <returns>The updated bounds</returns>
private Bounds DoBoundsModifications(SkinnedMeshRenderer targetRenderer, UMAData umaData)
{
//we cant tighten or adjust bounds unless we also update
//otherwise they end up in the center of the world
var umaTransform = umaData.transform;
var originalParent = umaData.transform.parent;
var originalRot = umaData.transform.localRotation;
var originalPos = umaData.transform.localPosition;
Collider umaCollider = null;
bool prevColliderEnabled = false;
bool prevUpdateWhenOffScreen = false;
Bounds newBounds = targetRenderer.bounds;
var saveRoot = targetRenderer.rootBone;
targetRenderer.rootBone = null;
if (_updateBounds)
{
umaCollider = umaData.gameObject.GetComponent<Collider>();
prevColliderEnabled = umaCollider != null ? umaCollider.enabled : false;
prevUpdateWhenOffScreen = targetRenderer.updateWhenOffscreen;
//if there is a collider, disable it before we move anything
if (umaCollider)
{
umaCollider.enabled = false;
}
//Move UMA into the root, we do this because it might be inside a scaled object
umaTransform.SetParent(null, false);
umaTransform.localRotation = Quaternion.identity;
umaTransform.localPosition = Vector3.zero;
//Get the new bounds from the renderer set to always update (updateWhenOffScreen)
targetRenderer.updateWhenOffscreen = true;
newBounds = new Bounds(targetRenderer.localBounds.center, targetRenderer.localBounds.size);
targetRenderer.updateWhenOffscreen = prevUpdateWhenOffScreen;
boundsAdjustmentApplied = false;
}
//somehow the bounds end up beneath the floor i.e. newBounds.center.y - newBounds.extents.y is actually a minus number
//tighten bounds fixes this
if (_tightenBounds)
{
Vector3 newCenter = new Vector3(newBounds.center.x, newBounds.center.y, newBounds.center.z);
Vector3 newSize = new Vector3(newBounds.size.x, newBounds.size.y, newBounds.size.z);
if (newBounds.center.y - newBounds.extents.y < 0)
{
var underAmount = newBounds.center.y - newBounds.extents.y;
newSize.y = (newBounds.center.y * 2) - underAmount;
newCenter.y = newSize.y / 2;
}
if (umaData.umaRoot != null)
{
newCenter.x = umaData.umaRoot.transform.position.x;
newCenter.z = umaData.umaRoot.transform.position.z;
Bounds modifiedBounds = new Bounds(newCenter, newSize);
newBounds = modifiedBounds;
}
}
//the user has tools for expanding the bounds aswell so do those here too if they havent been done already (to this race?)
if (_adjustBounds && !boundsAdjustmentApplied)
{
newBounds.Expand(_boundsAdjust);
boundsAdjustmentApplied = true;
}
//if we moved the character move it back again
if (_updateBounds)
{
umaTransform.SetParent(originalParent, false);
umaTransform.localRotation = originalRot;
umaTransform.localPosition = originalPos;
//set any collider to its original setting
if (umaCollider)
{
umaCollider.enabled = prevColliderEnabled;
}
}
targetRenderer.localBounds = newBounds;
targetRenderer.rootBone = saveRoot;
return newBounds;
}
// UMAs can have lots of renderers, but this should return the one that we should use when calculating umaData.characterHeight/Radius/Mass- will that always be umaData.GetRenderer(0)?
private SkinnedMeshRenderer GetBaseRenderer(UMAData umaData, int rendererToGet = 0)
{
if (umaData.rendererCount == 0 || umaData.GetRenderer(rendererToGet) == null)
{
return null;
}
return umaData.GetRenderer(rendererToGet);
}
/// <summary>
/// Builds a dictionary of mechanim bones that can be used when doing the characterHeight/Radius/Mass calculations
/// </summary>
private void UpdateMechanimBoneDict(UMAData umaData, UMASkeleton skeleton)
{
//"Head" is obligatory for mechanim so we can check that too to tell us when we have switched back to this race and the skeleton was rebuilt but this Behaviour hadnt been garbage collected yet
if ((_lastRace == null || umaData.umaRecipe.raceData.raceName != _lastRace) || (!_mechanimBoneDict.ContainsKey("Head") || !skeleton.BoneExists(_mechanimBoneDict["Head"])) && umaData.umaRecipe.raceData.umaTarget == RaceData.UMATarget.Humanoid)
{
_lastRace = umaData.umaRecipe.raceData.raceName;
_mechanimBoneDict.Clear();
//var umaTPose = umaData.umaRecipe.raceData.TPose;
var umaTPose = umaData.GetTPose();
if (umaTPose == null)
{
_lastRace = null;
return;
}
_mechanimBoneDict.Add("Head", UMAUtils.StringToHash((Array.Find(umaTPose.humanInfo, entry => entry.humanName == "Head").boneName)));
_mechanimBoneDict.Add("LeftEye", UMAUtils.StringToHash((Array.Find(umaTPose.humanInfo, entry => entry.humanName == "LeftEye").boneName)));//optionalBone
_mechanimBoneDict.Add("RightEye", UMAUtils.StringToHash((Array.Find(umaTPose.humanInfo, entry => entry.humanName == "RightEye").boneName)));//optionalBone
_mechanimBoneDict.Add("Hips", UMAUtils.StringToHash((Array.Find(umaTPose.humanInfo, entry => entry.humanName == "Hips").boneName)));
_mechanimBoneDict.Add("Neck", UMAUtils.StringToHash((Array.Find(umaTPose.humanInfo, entry => entry.humanName == "Neck").boneName)));//OptionalBone
_mechanimBoneDict.Add("LeftUpperArm", UMAUtils.StringToHash((Array.Find(umaTPose.humanInfo, entry => entry.humanName == "LeftUpperArm").boneName)));
_mechanimBoneDict.Add("RightUpperArm", UMAUtils.StringToHash((Array.Find(umaTPose.humanInfo, entry => entry.humanName == "RightUpperArm").boneName)));
_mechanimBoneDict.Add("LeftUpperLeg", UMAUtils.StringToHash((Array.Find(umaTPose.humanInfo, entry => entry.humanName == "LeftUpperLeg").boneName)));
_mechanimBoneDict.Add("RightUpperLeg", UMAUtils.StringToHash((Array.Find(umaTPose.humanInfo, entry => entry.humanName == "RightUpperLeg").boneName)));
}
}
/// <summary>
/// Updates the characterHeightMassRadius after all other changes have been made by the converters
/// </summary>
private void UpdateCharacterHeightMassRadius(UMAData umaData, UMASkeleton skeleton, Bounds newBounds)
{
float charHeight = newBounds.size.y;
float charWidth = newBounds.size.x;
if (umaData.umaRecipe.raceData.umaTarget == RaceData.UMATarget.Humanoid)
{
//adjusting is only about tweaking the result
if (_adjustHeight || _adjustRadius)
{
float chinHeight = 0f;
float headHeight = 0f;
float headWidth = 0f;
UpdateMechanimBoneDict(umaData, skeleton);
//character needs to be moved into the root again with no rotation applied
var umaTransform = umaData.transform;
var originalParent = umaData.transform.parent;
var originalRot = umaData.transform.localRotation;
var originalPos = umaData.transform.localPosition;
var umaCollider = umaData.gameObject.GetComponent<Collider>();
bool prevColliderEnabled = umaCollider != null ? umaCollider.enabled : false;
//if there is a collider, disable it before we move anything
if (umaCollider)
{
umaCollider.enabled = false;
}
umaTransform.SetParent(null, false);
umaTransform.localRotation = Quaternion.identity;
umaTransform.localPosition = Vector3.zero;
if (_adjustHeight)
{
//Classically a human is apprx 7.5 heads tall, but we only know the height to the base of the head bone
//usually head bone is actually usually in line with the lips, making the base of the chin, 1/3rd down the neck
//so if we have the optional neck bone use that to estimate the base of the chin
int boneHash = _mechanimBoneDict["Head"];
var pos = skeleton.GetRelativePosition(boneHash);
chinHeight = pos.y;
if (skeleton.BoneExists(_mechanimBoneDict["Neck"]))
{
chinHeight = skeleton.GetRelativePosition(_mechanimBoneDict["Neck"]).y + (((skeleton.GetRelativePosition(_mechanimBoneDict["Head"]).y - skeleton.GetRelativePosition(_mechanimBoneDict["Neck"]).y) / 3f) * 2f);
}
//apply the headRatio (by default this is 7.5)
chinHeight = (chinHeight / 6.5f) * (_headRatio - 1);
//so classically chinbase is 6.5 headHeights from the floor so headHeight is..
headHeight = (chinHeight / (_headRatio - 1));
//but bobble headed charcaters (toons), children or dwarves etc have bigger heads proportionally
//so their overall height will greater than (chinHeight / 6.5) * 7.5
//If we have the eyes we can use those to calculate the size of the head better because classically the distance from the chin to the eyes will be half the head height
if (skeleton.BoneExists(_mechanimBoneDict["LeftEye"]) || skeleton.BoneExists(_mechanimBoneDict["RightEye"]))
{
var eyeHeight = 0f;
//if we have both eyes get the average
if (skeleton.BoneExists(_mechanimBoneDict["LeftEye"]) && skeleton.BoneExists(_mechanimBoneDict["RightEye"]))
{
eyeHeight = (skeleton.GetRelativePosition(_mechanimBoneDict["LeftEye"]).y + skeleton.GetRelativePosition(_mechanimBoneDict["RightEye"]).y) / 2f;
}
else if (skeleton.BoneExists(_mechanimBoneDict["LeftEye"]))
{
eyeHeight = skeleton.GetRelativePosition(_mechanimBoneDict["LeftEye"]).y;
}
else if (skeleton.BoneExists(_mechanimBoneDict["RightEye"]))
{
eyeHeight = skeleton.GetRelativePosition(_mechanimBoneDict["RightEye"]).y;
}
headHeight = ((eyeHeight - chinHeight) * 2f);
//because we do this the actual headRatio doesnt *feel* right
//i.e. with toon the head ratio is more like 3, but the correct value for calcs is 6.5
//I'd prefer it if these calcs made 3 deliver the correct result
}
//So finally
//Debug.Log("chinHeight was " + chinHeight+" headHeight was "+headHeight);
//Debug.Log("Classical Height from Head = " + (headHeight * 7.5f));
//Debug.Log("Classical Height from Chin = " + ((chinHeight / 6.5f) * 7.5f));
charHeight = chinHeight + headHeight;
}
if (_adjustRadius)
{
if (skeleton.BoneExists(_mechanimBoneDict["LeftUpperArm"]) && skeleton.BoneExists(_mechanimBoneDict["RightUpperArm"]) &&
skeleton.BoneExists(_mechanimBoneDict["LeftUpperLeg"]) && skeleton.BoneExists(_mechanimBoneDict["RightUpperLeg"])
)
{
float shouldersWidth = Mathf.Abs(skeleton.GetRelativePosition(_mechanimBoneDict["LeftUpperArm"]).x - skeleton.GetRelativePosition(_mechanimBoneDict["RightUpperArm"]).x);
//Also female charcaters tend to have hips wider than their shoulders, so check that
float hipsWidth = Mathf.Abs(skeleton.GetRelativePosition(_mechanimBoneDict["LeftUpperLeg"]).x - skeleton.GetRelativePosition(_mechanimBoneDict["RightUpperLeg"]).x);
//the outerWidth of the hips is larger than this because the thigh muscles are so big so make this 1/3rd bigger
hipsWidth = (hipsWidth / 2) * 3;
//classically the width of the body is 3* headwidth for a strong character so headwidth will be
headWidth = shouldersWidth / 2.75f;
//but bobble headed charcaters (toons), children or dwarves etc have bigger heads proportionally and the head can be wider than the shoulders
//so if we have eye bones use them to calculate head with
if (skeleton.BoneExists(_mechanimBoneDict["LeftEye"]) && skeleton.BoneExists(_mechanimBoneDict["RightEye"]))
{
//clasically a face is 5* the width of the eyes where the distance between the pupils is 2 * eye width
var eyeWidth = Mathf.Abs(skeleton.GetRelativePosition(_mechanimBoneDict["LeftEye"]).x - skeleton.GetRelativePosition(_mechanimBoneDict["RightEye"]).x) / 2;
headWidth = eyeWidth * 5f;
}
charWidth = (shouldersWidth > headWidth || hipsWidth > headWidth) ? (shouldersWidth > hipsWidth ? shouldersWidth : hipsWidth) : headWidth;
}
//we might also want to take into account the z depth between the hips and the head,
//because if the character has been made into a horse or something it might be on all fours (and still be mechanim.Humanoid [if that even works!])
//capsule colliders break down in this scenario though (switch race to SkyCar to see what I mean), so would we want to change the orientation of the collider or the type?
//does the collider orientation have any impact on physics?
}
//Set the UMA back
umaTransform.SetParent(originalParent, false);
umaTransform.localRotation = originalRot;
umaTransform.localPosition = originalPos;
//set any collider to its original setting
if (umaCollider)
{
umaCollider.enabled = prevColliderEnabled;
}
}
}
else //if its a car or a castle or whatever use the bounds
{
charHeight = newBounds.size.y;
charWidth = newBounds.size.x;
}
//get the scale of the overall scale bone if set
float overallScale = 1f;
if (skeleton.GetBoneTransform(_scaleBoneHash) != null)
{
overallScale = (skeleton.GetScale(_scaleBoneHash)).x;
}
if (_adjustHeight)
{
//characterHeight is what we calculated plus any radius y the user has added
umaData.characterHeight = charHeight * (1 + (_radiusAdjustY * 2));
}
else
{
umaData.characterHeight = charHeight;
}
if (_adjustRadius)
{
//characterRadius is the average of the width we calculated plus the z-depth from the bounds / 2
//we need to include the new _radiusAdjust.y (which is now an adjust for the z axis as radiusAdjustY is now its own field (used for heightAdjust)
umaData.characterRadius = (charWidth + newBounds.size.z * (_radiusAdjust.x * 2)) / 2;
//Debug.Log("BCM umaData.characterRadius[" + umaData.characterRadius + "] = (charWidth[" + charWidth + "] + newBounds.size.z[" + newBounds.size.z + "] * (radiusAdjust.x[" + _radiusAdjust.x + "] * 2)) / 2;");
}
else
{
umaData.characterRadius = (charWidth + newBounds.size.z) / 2;
}
if (_adjustMass)
{
//characterMass is... what? still dont understand this quite
var massZModified = (umaData.characterRadius * 2) * overallScale;
var massYModified = ((1 + _radiusAdjustY) * 0.5f) * overallScale;
umaData.characterMass = (_massAdjust.x * overallScale) + _massAdjust.y * massYModified + _massAdjust.z * massZModified;
}
else
{
//HUMANMALE umaData.characterMass[66.68236] = raceMass[50] * overallScale[0.907451] + 28f * umaDna.upperWeight[0.5019608] + 22f * umaDna.lowerWeight[0.3176471]
umaData.characterMass = 66.68236f;//? Thats the result
}
//in the fixed UMA converters HumanMales mass is actually less than HumanFemales, with this thats solved
//but the characters are lighter than stock by a about 5 when at they standard size and heaver than stock by about 7 when at max height
//I dont know how much this matters? It can easily be fixed by tweaking the calcs, I just dont know what the numbers mean again...
//Debug.Log(umaData.transform.gameObject.name + " umaData.characterMass was "+ umaData.characterMass+" characterHeight was " + umaData.characterHeight + " characterRadius was " + umaData.characterRadius);
#if UNITY_EDITOR
/*if (_heightDebugToolsEnabled)
AddDebugBoxes(umaData, chinHeight, headHeight, headWidth);
else
RemoveDebugBoxes(umaData);*/
#endif
}
#endregion
#region ATTRIBUTES
[System.AttributeUsage(System.AttributeTargets.Field)]
public class ConfigAttribute : System.Attribute
{
public bool alwaysExpanded = false;
public ConfigAttribute(bool alwaysExpanded)
{
this.alwaysExpanded = alwaysExpanded;
}
}
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 4fcb90e54929ef049b963199d3d865e4
timeCreated: 1539889447
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/Scripts/BaseCharacterModifier.cs
uploadId: 679826
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7760dd96186d62949a648952ae1e3260
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,364 @@
//Adapted to Jobs/Burst by Kenamis
/*
* The following code was adapted from: https://schemingdeveloper.com
*
* Visit our game studio website: http://stopthegnomes.com
*
* License: You may use this code however you see fit, as long as you include this notice
* without any modifications.
*
* You may not publish a paid asset on Unity store if its main function is based on
* the following code, but you may publish a paid asset that uses this code.
*
* If you intend to use this in a Unity store asset or a commercial project, it would
* be appreciated, but not required, if you let me know with a link to the asset. If I
* don't get back to you just go ahead and use it anyway!
*/
#if UMA_BURSTCOMPILE
using Unity.Collections;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Burst;
using UnityEngine;
public static class MeshUtilities
{
public static JobHandle BakeOneFramePositionBlendShape(NativeArray<Vector3> vertices, NativeArray<Vector3> deltaVertices, float weight, JobHandle dependsOn = default)
{
var job = new BakeBlendShape
{
weight = weight,
vertices = vertices,
deltaVertices = deltaVertices
};
return job.Schedule(vertices.Length, 32, dependsOn);
}
public struct BakeBlendShape : IJobParallelFor
{
public float weight;
public NativeArray<Vector3> vertices;
[ReadOnly][DeallocateOnJobCompletion] public NativeArray<Vector3> deltaVertices;
public void Execute(int index)
{
vertices[index] += (deltaVertices[index] * weight);
}
}
public static void RecalculateNormalsTangentsJobified(this Mesh mesh, float angle)
{
//THIS IS JUST AN EXAMPLE
//Inefficient getter api. It'd be better to pass in existing arrays or use MeshData to get the data off the mesh.
NativeArray<Vector3> vertices = new NativeArray<Vector3>(mesh.vertices, Allocator.TempJob);
NativeArray<Vector3> normals = new NativeArray<Vector3>(mesh.normals, Allocator.TempJob);
NativeArray<Vector2> uvs = new NativeArray<Vector2>(mesh.uv, Allocator.TempJob);
NativeArray<Vector4> tangents = new NativeArray<Vector4>(mesh.tangents, Allocator.TempJob);
NativeArray<Vector3> tan1 = new NativeArray<Vector3>(mesh.vertexCount, Allocator.TempJob);
NativeArray<Vector3> tan2 = new NativeArray<Vector3>(mesh.vertexCount, Allocator.TempJob);
NativeArray<int> triangles = new NativeArray<int>(mesh.triangles, Allocator.TempJob);
NativeArray<Vector3> triNormals = new NativeArray<Vector3>(triangles.Length, Allocator.TempJob);
NativeParallelMultiHashMap<int, int2> map = new NativeParallelMultiHashMap<int, int2>(mesh.vertexCount * 10, Allocator.TempJob);
NativeList<int> keys = new NativeList<int>(mesh.vertexCount, Allocator.TempJob);
var normalsFirstJob = new RecalculateNormalsFirstJob
{
triangles = triangles.Reinterpret<int, int3>(),
vertices = vertices,
triNormals = triNormals,
map = map.AsParallelWriter(),
};
var normalsFirstHandle = normalsFirstJob.Schedule(triangles.Length / 3, 32);
var normalsSecondJob = new RecalculateNormalsSecondJob
{
keys = keys,
map = map
};
var normalsSecondHandle = normalsSecondJob.Schedule(normalsFirstHandle);
var normalsLastJob = new RecalculateNormalsLastJob
{
keys = keys.AsDeferredJobArray(),
map = map,
triNormals = triNormals,
normals = normals,
cosineThreshold = math.cos(angle * Mathf.Deg2Rad)
};
var normalsLastHandle = normalsLastJob.Schedule(keys, 8, normalsSecondHandle);
var tangentsFirstJob = new RecalculateTangentsFirstJob
{
triangles = triangles.Reinterpret<int, int3>(),
vertices = vertices,
normals = normals,
uvs = uvs,
tan1 = tan1,
tan2 = tan2
};
var tangentsFirstHandle = tangentsFirstJob.Schedule(triangles.Length / 3, 32, normalsLastHandle);
var tangentsLastJob = new RecalculateTangentsLastJob
{
normals = normals,
tan1 = tan1,
tan2 = tan2,
tangents = tangents
};
tangentsLastJob.Schedule(uvs.Length, 32, tangentsFirstHandle).Complete();
mesh.SetNormals(normals);
vertices.Dispose();
normals.Dispose();
uvs.Dispose();
tangents.Dispose();
tan1.Dispose();
tan2.Dispose();
keys.Dispose();
map.Dispose();
triangles.Dispose();
triNormals.Dispose();
}
public static JobHandle RecalculateNormalsTangentsJobified(NativeArray<Vector3> vertices, NativeArray<Vector3> normals, NativeArray<Vector2> uvs, NativeArray<Vector4> tangents, NativeArray<int> triangles, float angle, JobHandle dependsOn = default)
{
NativeArray<Vector3> tan1 = new NativeArray<Vector3>(vertices.Length, Allocator.TempJob);
NativeArray<Vector3> tan2 = new NativeArray<Vector3>(vertices.Length, Allocator.TempJob);
NativeArray<Vector3> triNormals = new NativeArray<Vector3>(triangles.Length, Allocator.TempJob);
NativeParallelMultiHashMap<int, int2> map = new NativeParallelMultiHashMap<int, int2>(vertices.Length * 10, Allocator.TempJob);
NativeList<int> keys = new NativeList<int>(vertices.Length, Allocator.TempJob);
var normalsFirstJob = new RecalculateNormalsFirstJob
{
triangles = triangles.Reinterpret<int, int3>(),
vertices = vertices,
triNormals = triNormals,
map = map.AsParallelWriter(),
};
var normalsFirstHandle = normalsFirstJob.Schedule(triangles.Length / 3, 32, dependsOn);
var normalsSecondJob = new RecalculateNormalsSecondJob
{
keys = keys,
map = map
};
var normalsSecondHandle = normalsSecondJob.Schedule(normalsFirstHandle);
var normalsLastJob = new RecalculateNormalsLastJob
{
keys = keys.AsDeferredJobArray(),
map = map,
triNormals = triNormals,
normals = normals,
cosineThreshold = math.cos(angle * Mathf.Deg2Rad)
};
var normalsLastHandle = normalsLastJob.Schedule(keys, 8, normalsSecondHandle);
var tangentsFirstJob = new RecalculateTangentsFirstJob
{
triangles = triangles.Reinterpret<int, int3>(),
vertices = vertices,
normals = normals,
uvs = uvs,
tan1 = tan1,
tan2 = tan2
};
var tangentsFirstHandle = tangentsFirstJob.Schedule(triangles.Length / 3, 32, normalsLastHandle);
var tangentsLastJob = new RecalculateTangentsLastJob
{
normals = normals,
tan1 = tan1,
tan2 = tan2,
tangents = tangents
};
keys.Dispose(normalsLastHandle);
map.Dispose(normalsLastHandle);
return tangentsLastJob.Schedule(uvs.Length, 32, tangentsFirstHandle);
}
// Change this if you require a different precision.
private const int Tolerance = 100000;
// Magic FNV values. Do not change these.
private const long FNV32Init = 0x811c9dc5;
private const long FNV32Prime = 0x01000193;
public static int Vector3Hash(Vector3 vector)
{
long rv = FNV32Init;
rv ^= (long)math.round(vector.x * Tolerance);
rv *= FNV32Prime;
rv ^= (long)math.round(vector.y * Tolerance);
rv *= FNV32Prime;
rv ^= (long)math.round(vector.z * Tolerance);
rv *= FNV32Prime;
return rv.GetHashCode();
}
[BurstCompile(CompileSynchronously = true)]
public struct RecalculateNormalsFirstJob : IJobParallelFor
{
[ReadOnly] public NativeArray<int3> triangles;
[ReadOnly] public NativeArray<Vector3> vertices;
public NativeArray<Vector3> triNormals;
public NativeParallelMultiHashMap<int, int2>.ParallelWriter map;
public void Execute(int triIndex)
{
int3 tri = triangles[triIndex];
float3 p1 = vertices[tri.y] - vertices[tri.x];
float3 p2 = vertices[tri.z] - vertices[tri.x];
float3 normal = math.cross(p1, p2);
float magnitude = math.length(normal);
if (magnitude > 0)
{
normal /= magnitude;
}
triNormals[triIndex] = normal;
int hash0 = Vector3Hash(vertices[tri.x]);
int hash1 = Vector3Hash(vertices[tri.y]);
int hash2 = Vector3Hash(vertices[tri.z]);
map.Add(hash0, new int2 { x = tri.x, y = triIndex });
map.Add(hash1, new int2 { x = tri.y, y = triIndex });
map.Add(hash2, new int2 { x = tri.z, y = triIndex });
}
}
[BurstCompile(CompileSynchronously = true)]
public struct RecalculateNormalsSecondJob : IJob
{
public NativeList<int> keys;
[ReadOnly] public NativeParallelMultiHashMap<int, int2> map;
public void Execute()
{
keys.AddRange(map.GetKeyArray(Allocator.Temp));
}
}
[BurstCompile(CompileSynchronously = true)]
public struct RecalculateNormalsLastJob : IJobParallelForDefer
{
[ReadOnly] public NativeArray<int> keys;
[ReadOnly] public NativeParallelMultiHashMap<int, int2> map;
[ReadOnly][DeallocateOnJobCompletion] public NativeArray<Vector3> triNormals;
[NativeDisableParallelForRestriction] public NativeArray<Vector3> normals;
public float cosineThreshold;
public void Execute(int index)
{
var values = map.GetValuesForKey(keys[index]);
foreach (int2 lhsEntry in values)
{
Vector3 sum = new Vector3();
foreach (int2 rhsEntry in values)
{
if (lhsEntry.x == rhsEntry.x)
{
sum += triNormals[rhsEntry.y];
}
else
{
// The dot product is the cosine of the angle between the two triangles.
// A larger cosine means a smaller angle.
float dot = math.dot(triNormals[lhsEntry.y], triNormals[rhsEntry.y]);
if (dot >= cosineThreshold)
{
sum += triNormals[rhsEntry.y];
}
}
}
normals[lhsEntry.x] = sum.normalized;
}
}
}
[BurstCompile(CompileSynchronously = true)]
public struct RecalculateTangentsFirstJob : IJobParallelFor
{
[ReadOnly] public NativeArray<int3> triangles;
[ReadOnly] public NativeArray<Vector3> vertices;
[ReadOnly] public NativeArray<Vector3> normals;
[ReadOnly] public NativeArray<Vector2> uvs;
[NativeDisableParallelForRestriction] public NativeArray<Vector3> tan1;
[NativeDisableParallelForRestriction] public NativeArray<Vector3> tan2;
public void Execute(int index)
{
int3 tri = triangles[index];
int i1 = tri.x;
int i2 = tri.y;
int i3 = tri.z;
Vector3 v1 = vertices[i1];
Vector3 v2 = vertices[i2];
Vector3 v3 = vertices[i3];
Vector2 w1 = uvs[i1];
Vector2 w2 = uvs[i2];
Vector2 w3 = uvs[i3];
float x1 = v2.x - v1.x;
float x2 = v3.x - v1.x;
float y1 = v2.y - v1.y;
float y2 = v3.y - v1.y;
float z1 = v2.z - v1.z;
float z2 = v3.z - v1.z;
float s1 = w2.x - w1.x;
float s2 = w3.x - w1.x;
float t1 = w2.y - w1.y;
float t2 = w3.y - w1.y;
float div = s1 * t2 - s2 * t1;
float r = div == 0.0f ? 0.0f : 1.0f / div;
Vector3 sDir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
Vector3 tDir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
tan1[i1] += sDir;
tan1[i2] += sDir;
tan1[i3] += sDir;
tan2[i1] += tDir;
tan2[i2] += tDir;
tan2[i3] += tDir;
}
}
[BurstCompile(CompileSynchronously = true)]
public struct RecalculateTangentsLastJob : IJobParallelFor
{
[ReadOnly] public NativeArray<Vector3> normals;
[ReadOnly] [DeallocateOnJobCompletion] public NativeArray<Vector3> tan1;
[ReadOnly] [DeallocateOnJobCompletion] public NativeArray<Vector3> tan2;
public NativeArray<Vector4> tangents;
public void Execute(int index)
{
Vector3 n = normals[index];
Vector3 t = tan1[index];
Vector3.OrthoNormalize(ref n, ref t);
Vector4 tangent = new Vector4();
tangent.x = t.x;
tangent.y = t.y;
tangent.z = t.z;
tangent.w = (math.dot(math.cross(n, t), tan2[index]) < 0.0f) ? -1.0f : 1.0f;
tangents[index] = tangent;
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 359df18f5c4ca05408523ab44aba8c35
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/Scripts/Burst/RecalculateNormalsJobified.cs
uploadId: 679826
@@ -0,0 +1,72 @@
using UMA;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
namespace UMA
{
public class RecalculationSlot : MonoBehaviour
{
public float angle = 60f;
public string blendShapeNameStartsWith = "";
public void Recalculate(UMAData umaDATA)
{
#if UMA_BURSTCOMPILE
Transform theRenderer = umaDATA.gameObject.transform.Find("UMARenderer");
GameObject rendererGameObject = theRenderer.gameObject;
SkinnedMeshRenderer smr = rendererGameObject.GetComponent<SkinnedMeshRenderer>();
if (smr != null)
{
NativeArray<Vector3> vertices = new NativeArray<Vector3>(smr.sharedMesh.vertices, Allocator.TempJob);
NativeArray<Vector3> normals = new NativeArray<Vector3>(smr.sharedMesh.normals, Allocator.TempJob);
NativeArray<Vector2> uvs = new NativeArray<Vector2>(smr.sharedMesh.uv, Allocator.TempJob);
NativeArray<Vector4> tangents = new NativeArray<Vector4>(smr.sharedMesh.tangents, Allocator.TempJob);
NativeArray<int> triangles = new NativeArray<int>(smr.sharedMesh.triangles, Allocator.TempJob);
Vector3[] deltaVertices = new Vector3[vertices.Length];
JobHandle handle = default;
int blendShapeCount = smr.sharedMesh.blendShapeCount;
for (int shapeIndex = 0; shapeIndex < blendShapeCount; shapeIndex++)
{
float weight = smr.GetBlendShapeWeight(shapeIndex);
if (weight > 0f)
{
weight /= 100f; //bring the weight into 0-1 range.
string blendShapeName = smr.sharedMesh.GetBlendShapeName(shapeIndex);
if (blendShapeName.StartsWith(blendShapeNameStartsWith))
{
smr.sharedMesh.GetBlendShapeFrameVertices(shapeIndex, 0, deltaVertices, null, null);
NativeArray<Vector3> blendShapeDeltaVertices = new NativeArray<Vector3>(deltaVertices, Allocator.TempJob);
handle = MeshUtilities.BakeOneFramePositionBlendShape(vertices, blendShapeDeltaVertices, weight, handle);
}
}
}
handle = MeshUtilities.RecalculateNormalsTangentsJobified(vertices, normals, uvs, tangents, triangles, angle, handle);
handle.Complete();
//We don't need to do this if we know our mesh is a single instance!
//Mesh mesh = Instantiate<Mesh>(smr.sharedMesh);
Mesh mesh = smr.sharedMesh;
//mesh.SetVertices(vertices); //don't update the vertices if the blendshapes are going to continue...
mesh.SetNormals(normals);
mesh.SetUVs(0, uvs);
mesh.SetTangents(tangents);
smr.sharedMesh = mesh;
vertices.Dispose();
normals.Dispose();
uvs.Dispose();
tangents.Dispose();
triangles.Dispose();
}
#endif
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 8f4a2b4c41e32534e9524d5b61e4aa93
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/Scripts/Burst/RecalculationSlot.cs
uploadId: 679826
Binary file not shown.
@@ -0,0 +1,17 @@
fileFormatVersion: 2
guid: cea561855e83c6a479aac5acdafb601e
labels:
- UMA_Slot
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Scripts/Custom SlotDataAsset.asset
uploadId: 679826
+9
View File
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: ebe44e89a6928c343ba652465ba25935
folderAsset: yes
timeCreated: 1486749628
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
+10
View File
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 8ffcaa36b7824384aafad9735d992338
folderAsset: yes
timeCreated: 1547936894
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,172 @@
namespace UMA
{
/// <summary>
/// Base class for Humanoid DNA converters.
/// </summary>
/// <remarks>
/// Holds hash values for all the bones used in the default UMA humanoid rig.
/// </remarks>
public class HumanoidDNAConverterBehaviour : DnaConverterBehaviour
{
static bool builtHashes = false;
static protected int headAdjustHash;
static protected int neckAdjustHash;
static protected int leftOuterBreastHash;
static protected int rightOuterBreastHash;
static protected int leftEyeHash;
static protected int rightEyeHash;
static protected int leftEyeAdjustHash;
static protected int rightEyeAdjustHash;
static protected int spine1AdjustHash;
static protected int spineAdjustHash;
static protected int lowerBackBellyHash;
static protected int lowerBackAdjustHash;
static protected int leftTrapeziusHash;
static protected int rightTrapeziusHash;
static protected int leftArmAdjustHash;
static protected int rightArmAdjustHash;
static protected int leftForeArmAdjustHash;
static protected int rightForeArmAdjustHash;
static protected int leftForeArmTwistAdjustHash;
static protected int rightForeArmTwistAdjustHash;
static protected int leftShoulderAdjustHash;
static protected int rightShoulderAdjustHash;
static protected int leftUpLegAdjustHash;
static protected int rightUpLegAdjustHash;
static protected int leftLegAdjustHash;
static protected int rightLegAdjustHash;
static protected int leftGluteusHash;
static protected int rightGluteusHash;
static protected int leftEarAdjustHash;
static protected int rightEarAdjustHash;
static protected int noseBaseAdjustHash;
static protected int noseMiddleAdjustHash;
static protected int leftNoseAdjustHash;
static protected int rightNoseAdjustHash;
static protected int upperLipsAdjustHash;
static protected int mandibleAdjustHash;
static protected int leftLowMaxilarAdjustHash;
static protected int rightLowMaxilarAdjustHash;
static protected int leftCheekAdjustHash;
static protected int rightCheekAdjustHash;
static protected int leftLowCheekAdjustHash;
static protected int rightLowCheekAdjustHash;
static protected int noseTopAdjustHash;
static protected int leftEyebrowLowAdjustHash;
static protected int rightEyebrowLowAdjustHash;
static protected int leftEyebrowMiddleAdjustHash;
static protected int rightEyebrowMiddleAdjustHash;
static protected int leftEyebrowUpAdjustHash;
static protected int rightEyebrowUpAdjustHash;
static protected int lipsSuperiorAdjustHash;
static protected int lipsInferiorAdjustHash;
static protected int leftLipsSuperiorMiddleAdjustHash;
static protected int rightLipsSuperiorMiddleAdjustHash;
static protected int leftLipsInferiorAdjustHash;
static protected int rightLipsInferiorAdjustHash;
static protected int leftLipsAdjustHash;
static protected int rightLipsAdjustHash;
static protected int globalHash;
static protected int positionHash;
static protected int lowerBackHash;
static protected int headHash;
static protected int leftArmHash;
static protected int rightArmHash;
static protected int leftForeArmHash;
static protected int rightForeArmHash;
static protected int leftHandHash;
static protected int rightHandHash;
static protected int leftFootHash;
static protected int rightFootHash;
static protected int leftUpLegHash;
static protected int rightUpLegHash;
static protected int leftShoulderHash;
static protected int rightShoulderHash;
static protected int mandibleHash;
public override void Prepare()
{
if (builtHashes)
{
return;
}
headAdjustHash = UMAUtils.StringToHash("HeadAdjust");
neckAdjustHash = UMAUtils.StringToHash("NeckAdjust");
leftOuterBreastHash = UMAUtils.StringToHash("LeftOuterBreast");
rightOuterBreastHash = UMAUtils.StringToHash("RightOuterBreast");
leftEyeHash = UMAUtils.StringToHash("LeftEye");
rightEyeHash = UMAUtils.StringToHash("RightEye");
leftEyeAdjustHash = UMAUtils.StringToHash("LeftEyeAdjust");
rightEyeAdjustHash = UMAUtils.StringToHash("RightEyeAdjust");
spine1AdjustHash = UMAUtils.StringToHash("Spine1Adjust");
spineAdjustHash = UMAUtils.StringToHash("SpineAdjust");
lowerBackBellyHash = UMAUtils.StringToHash("LowerBackBelly");
lowerBackAdjustHash = UMAUtils.StringToHash("LowerBackAdjust");
leftTrapeziusHash = UMAUtils.StringToHash("LeftTrapezius");
rightTrapeziusHash = UMAUtils.StringToHash("RightTrapezius");
leftArmAdjustHash = UMAUtils.StringToHash("LeftArmAdjust");
rightArmAdjustHash = UMAUtils.StringToHash("RightArmAdjust");
leftForeArmAdjustHash = UMAUtils.StringToHash("LeftForeArmAdjust");
rightForeArmAdjustHash = UMAUtils.StringToHash("RightForeArmAdjust");
leftForeArmTwistAdjustHash = UMAUtils.StringToHash("LeftForeArmTwistAdjust");
rightForeArmTwistAdjustHash = UMAUtils.StringToHash("RightForeArmTwistAdjust");
leftShoulderAdjustHash = UMAUtils.StringToHash("LeftShoulderAdjust");
rightShoulderAdjustHash = UMAUtils.StringToHash("RightShoulderAdjust");
leftUpLegAdjustHash = UMAUtils.StringToHash("LeftUpLegAdjust");
rightUpLegAdjustHash = UMAUtils.StringToHash("RightUpLegAdjust");
leftLegAdjustHash = UMAUtils.StringToHash("LeftLegAdjust");
rightLegAdjustHash = UMAUtils.StringToHash("RightLegAdjust");
leftGluteusHash = UMAUtils.StringToHash("LeftGluteus");
rightGluteusHash = UMAUtils.StringToHash("RightGluteus");
leftEarAdjustHash = UMAUtils.StringToHash("LeftEarAdjust");
rightEarAdjustHash = UMAUtils.StringToHash("RightEarAdjust");
noseBaseAdjustHash = UMAUtils.StringToHash("NoseBaseAdjust");
noseMiddleAdjustHash = UMAUtils.StringToHash("NoseMiddleAdjust");
leftNoseAdjustHash = UMAUtils.StringToHash("LeftNoseAdjust");
rightNoseAdjustHash = UMAUtils.StringToHash("RightNoseAdjust");
upperLipsAdjustHash = UMAUtils.StringToHash("UpperLipsAdjust");
mandibleAdjustHash = UMAUtils.StringToHash("MandibleAdjust");
leftLowMaxilarAdjustHash = UMAUtils.StringToHash("LeftLowMaxilarAdjust");
rightLowMaxilarAdjustHash = UMAUtils.StringToHash("RightLowMaxilarAdjust");
leftCheekAdjustHash = UMAUtils.StringToHash("LeftCheekAdjust");
rightCheekAdjustHash = UMAUtils.StringToHash("RightCheekAdjust");
leftLowCheekAdjustHash = UMAUtils.StringToHash("LeftLowCheekAdjust");
rightLowCheekAdjustHash = UMAUtils.StringToHash("RightLowCheekAdjust");
noseTopAdjustHash = UMAUtils.StringToHash("NoseTopAdjust");
leftEyebrowLowAdjustHash = UMAUtils.StringToHash("LeftEyebrowLowAdjust");
rightEyebrowLowAdjustHash = UMAUtils.StringToHash("RightEyebrowLowAdjust");
leftEyebrowMiddleAdjustHash = UMAUtils.StringToHash("LeftEyebrowMiddleAdjust");
rightEyebrowMiddleAdjustHash = UMAUtils.StringToHash("RightEyebrowMiddleAdjust");
leftEyebrowUpAdjustHash = UMAUtils.StringToHash("LeftEyebrowUpAdjust");
rightEyebrowUpAdjustHash = UMAUtils.StringToHash("RightEyebrowUpAdjust");
lipsSuperiorAdjustHash = UMAUtils.StringToHash("LipsSuperiorAdjust");
lipsInferiorAdjustHash = UMAUtils.StringToHash("LipsInferiorAdjust");
leftLipsSuperiorMiddleAdjustHash = UMAUtils.StringToHash("LeftLipsSuperiorMiddleAdjust");
rightLipsSuperiorMiddleAdjustHash = UMAUtils.StringToHash("RightLipsSuperiorMiddleAdjust");
leftLipsInferiorAdjustHash = UMAUtils.StringToHash("LeftLipsInferiorAdjust");
rightLipsInferiorAdjustHash = UMAUtils.StringToHash("RightLipsInferiorAdjust");
leftLipsAdjustHash = UMAUtils.StringToHash("LeftLipsAdjust");
rightLipsAdjustHash = UMAUtils.StringToHash("RightLipsAdjust");
globalHash = UMAUtils.StringToHash("Global");
positionHash = UMAUtils.StringToHash("Position");
lowerBackHash = UMAUtils.StringToHash("LowerBack");
headHash = UMAUtils.StringToHash("Head");
leftArmHash = UMAUtils.StringToHash("LeftArm");
rightArmHash = UMAUtils.StringToHash("RightArm");
leftForeArmHash = UMAUtils.StringToHash("LeftForeArm");
rightForeArmHash = UMAUtils.StringToHash("RightForeArm");
leftHandHash = UMAUtils.StringToHash("LeftHand");
rightHandHash = UMAUtils.StringToHash("RightHand");
leftFootHash = UMAUtils.StringToHash("LeftFoot");
rightFootHash = UMAUtils.StringToHash("RightFoot");
leftUpLegHash = UMAUtils.StringToHash("LeftUpLeg");
rightUpLegHash = UMAUtils.StringToHash("RightUpLeg");
leftShoulderHash = UMAUtils.StringToHash("LeftShoulder");
rightShoulderHash = UMAUtils.StringToHash("RightShoulder");
mandibleHash = UMAUtils.StringToHash("Mandible");
builtHashes = true;
}
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 4dddfeb7c1d47498c82430e5960d4cce
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/Scripts/DNA/Legacy/HumanoidDNAConverterBehaviour.cs
uploadId: 679826
@@ -0,0 +1,31 @@
using UnityEngine;
namespace UMA
{
/// <summary>
/// Example DNA converter behaviour. Only adjusts distance between eyes.
/// </summary>
public class TutorialDNAConverterBehaviour : DnaConverterBehaviour
{
public TutorialDNAConverterBehaviour()
{
this.ApplyDnaAction = UpdateTutorialBones;
this.DNAType = typeof(UMADnaTutorial);
}
/// <summary>
/// Apply the DNA information about eye spacing to a skeleton.
/// </summary>
/// <param name="umaData">The character data.</param>
/// <param name="skeleton">Skeleton.</param>
public static void UpdateTutorialBones(UMAData umaData, UMASkeleton skeleton)
{
var umaDna = umaData.GetDna<UMADnaTutorial>();
float spacing = (umaDna.eyeSpacing - 0.5f) * 0.01f;
skeleton.SetPositionRelative(UMAUtils.StringToHash("LeftEye"), new Vector3(0f, -spacing, 0f));
skeleton.SetPositionRelative(UMAUtils.StringToHash("RightEye"), new Vector3(0f, spacing, 0f));
}
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 461a6e020632b416b879aa3a48c5638e
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/Scripts/DNA/Legacy/TutorialDNAConverterBehaviour.cs
uploadId: 679826
@@ -0,0 +1,55 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &100000
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 400000}
- component: {fileID: 11400000}
m_Layer: 0
m_Name: TutorialDNAConverterBehaviour
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &400000
Transform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 100000}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0.2752999, y: 1.682059, z: -0.43053064}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 100000}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 461a6e020632b416b879aa3a48c5638e, type: 3}
m_Name:
m_EditorClassIdentifier:
_displayValue:
dnaTypeHash: -1679007774
--- !u!1001 &100100000
Prefab:
m_ObjectHideFlags: 1
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications: []
m_RemovedComponents: []
m_ParentPrefab: {fileID: 0}
m_RootGameObject: {fileID: 100000}
m_IsPrefabParent: 1
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a5c36ccd1f394ff3b7aac62b5d1e308
NativeFormatImporter:
userData:
AssetOrigin:
serializedVersion: 1
productId: 35611
packageName: UMA 2
packageVersion: 2.13
assetPath: Assets/UMA/Core/Scripts/DNA/Legacy/TutorialDNAConverterBehaviour.prefab
uploadId: 679826
@@ -0,0 +1,7 @@
namespace UMA
{
public abstract partial class UMADna : UMADnaBase
{
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 874a34061c7cbe64fbd33c61faaefd26
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/Scripts/DNA/Legacy/UMADna.cs
uploadId: 679826
@@ -0,0 +1,69 @@
namespace UMA
{
/// <summary>
/// Humanoid DNA.
/// </summary>
/// <remarks>
/// Contains a large number of variables for the ways that the
/// basic UMA human models can be adjusted to create different
/// body shapes and sizes.
/// </remarks>
[System.Serializable]
public partial class UMADnaHumanoid : UMADna
{
public float height = 0.5f;
public float headSize = 0.5f;
public float headWidth = 0.5f;
public float neckThickness = 0.5f;
public float armLength = 0.5f;
public float forearmLength = 0.5f;
public float armWidth = 0.5f;
public float forearmWidth = 0.5f;
public float handsSize = 0.5f;
public float feetSize = 0.5f;
public float legSeparation = 0.5f;
public float upperMuscle = 0.5f;
public float lowerMuscle = 0.5f;
public float upperWeight = 0.5f;
public float lowerWeight = 0.5f;
public float legsSize = 0.5f;
public float belly = 0.5f;
public float waist = 0.5f;
public float gluteusSize = 0.5f;
public float earsSize = 0.5f;
public float earsPosition = 0.5f;
public float earsRotation = 0.5f;
public float noseSize = 0.5f;
public float noseCurve = 0.5f;
public float noseWidth = 0.5f;
public float noseInclination = 0.5f;
public float nosePosition = 0.5f;
public float nosePronounced = 0.5f;
public float noseFlatten = 0.5f;
public float chinSize = 0.5f;
public float chinPronounced = 0.5f;
public float chinPosition = 0.5f;
public float mandibleSize = 0.5f;
public float jawsSize = 0.5f;
public float jawsPosition = 0.5f;
public float cheekSize = 0.5f;
public float cheekPosition = 0.5f;
public float lowCheekPronounced = 0.5f;
public float lowCheekPosition = 0.5f;
public float foreheadSize = 0.5f;
public float foreheadPosition = 0.5f;
public float lipsSize = 0.5f;
public float mouthSize = 0.5f;
public float eyeRotation = 0.5f;
public float eyeSize = 0.5f;
public float breastSize = 0.5f;
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 1928e74356386b64795aeafad7902c3d
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/Scripts/DNA/Legacy/UMADnaHumanoid.cs
uploadId: 679826
@@ -0,0 +1,466 @@
// UMA Auto genered code, DO NOT MODIFY!!!
// All changes to this file will be destroyed without warning or confirmation!
// Use double { to escape a single curly bracket
//
// template junk executed per dna Field , the accumulated content is available through the {0:ID} tag
//
//#TEMPLATE GetValues UmaDnaChild_GetIndex_Fragment.cs.txt
//#TEMPLATE SetValues UmaDnaChild_SetIndex_Fragment.cs.txt
//#TEMPLATE GetValue UmaDnaChild_GetValue_Fragment.cs.txt
//#TEMPLATE SetValue UmaDnaChild_SetValue_Fragment.cs.txt
//#TEMPLATE GetNames UmaDnaChild_GetNames_Fragment.cs.txt
//
// Byte Serialization Handling
//
//#TEMPLATE Byte_Fields UmaDnaChild_Byte_Fields_Fragment.cs.txt
//#TEMPLATE Byte_ToDna UmaDnaChild_Byte_ToDna_Fragment.cs.txt
//#TEMPLATE Byte_FromDna UmaDnaChild_Byte_FromDna_Fragment.cs.txt
//
namespace UMA
{
public partial class UMADnaHumanoid
{
public override int Count { get { return 46; } }
public override float[] Values
{
get
{
return new float[]
{
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,
};
}
set
{
height = value[0];
headSize = value[1];
headWidth = value[2];
neckThickness = value[3];
armLength = value[4];
forearmLength = value[5];
armWidth = value[6];
forearmWidth = value[7];
handsSize = value[8];
feetSize = value[9];
legSeparation = value[10];
upperMuscle = value[11];
lowerMuscle = value[12];
upperWeight = value[13];
lowerWeight = value[14];
legsSize = value[15];
belly = value[16];
waist = value[17];
gluteusSize = value[18];
earsSize = value[19];
earsPosition = value[20];
earsRotation = value[21];
noseSize = value[22];
noseCurve = value[23];
noseWidth = value[24];
noseInclination = value[25];
nosePosition = value[26];
nosePronounced = value[27];
noseFlatten = value[28];
chinSize = value[29];
chinPronounced = value[30];
chinPosition = value[31];
mandibleSize = value[32];
jawsSize = value[33];
jawsPosition = value[34];
cheekSize = value[35];
cheekPosition = value[36];
lowCheekPronounced = value[37];
lowCheekPosition = value[38];
foreheadSize = value[39];
foreheadPosition = value[40];
lipsSize = value[41];
mouthSize = value[42];
eyeRotation = value[43];
eyeSize = value[44];
breastSize = value[45];
}
}
public override float GetValue(int idx)
{
switch(idx)
{
case 0: return height;
case 1: return headSize;
case 2: return headWidth;
case 3: return neckThickness;
case 4: return armLength;
case 5: return forearmLength;
case 6: return armWidth;
case 7: return forearmWidth;
case 8: return handsSize;
case 9: return feetSize;
case 10: return legSeparation;
case 11: return upperMuscle;
case 12: return lowerMuscle;
case 13: return upperWeight;
case 14: return lowerWeight;
case 15: return legsSize;
case 16: return belly;
case 17: return waist;
case 18: return gluteusSize;
case 19: return earsSize;
case 20: return earsPosition;
case 21: return earsRotation;
case 22: return noseSize;
case 23: return noseCurve;
case 24: return noseWidth;
case 25: return noseInclination;
case 26: return nosePosition;
case 27: return nosePronounced;
case 28: return noseFlatten;
case 29: return chinSize;
case 30: return chinPronounced;
case 31: return chinPosition;
case 32: return mandibleSize;
case 33: return jawsSize;
case 34: return jawsPosition;
case 35: return cheekSize;
case 36: return cheekPosition;
case 37: return lowCheekPronounced;
case 38: return lowCheekPosition;
case 39: return foreheadSize;
case 40: return foreheadPosition;
case 41: return lipsSize;
case 42: return mouthSize;
case 43: return eyeRotation;
case 44: return eyeSize;
case 45: return breastSize;
}
throw new System.ArgumentOutOfRangeException();
}
public override void SetValue(int idx, float value)
{
switch(idx)
{
case 0: height = value; return;
case 1: headSize = value; return;
case 2: headWidth = value; return;
case 3: neckThickness = value; return;
case 4: armLength = value; return;
case 5: forearmLength = value; return;
case 6: armWidth = value; return;
case 7: forearmWidth = value; return;
case 8: handsSize = value; return;
case 9: feetSize = value; return;
case 10: legSeparation = value; return;
case 11: upperMuscle = value; return;
case 12: lowerMuscle = value; return;
case 13: upperWeight = value; return;
case 14: lowerWeight = value; return;
case 15: legsSize = value; return;
case 16: belly = value; return;
case 17: waist = value; return;
case 18: gluteusSize = value; return;
case 19: earsSize = value; return;
case 20: earsPosition = value; return;
case 21: earsRotation = value; return;
case 22: noseSize = value; return;
case 23: noseCurve = value; return;
case 24: noseWidth = value; return;
case 25: noseInclination = value; return;
case 26: nosePosition = value; return;
case 27: nosePronounced = value; return;
case 28: noseFlatten = value; return;
case 29: chinSize = value; return;
case 30: chinPronounced = value; return;
case 31: chinPosition = value; return;
case 32: mandibleSize = value; return;
case 33: jawsSize = value; return;
case 34: jawsPosition = value; return;
case 35: cheekSize = value; return;
case 36: cheekPosition = value; return;
case 37: lowCheekPronounced = value; return;
case 38: lowCheekPosition = value; return;
case 39: foreheadSize = value; return;
case 40: foreheadPosition = value; return;
case 41: lipsSize = value; return;
case 42: mouthSize = value; return;
case 43: eyeRotation = value; return;
case 44: eyeSize = value; return;
case 45: breastSize = value; return;
}
throw new System.ArgumentOutOfRangeException();
}
public static string[] GetNames()
{
return 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",
};
}
public override string[] Names
{
get
{
return GetNames();
}
}
public static UMADnaHumanoid LoadInstance(string data)
{
return UnityEngine.JsonUtility.FromJson<UMADnaHumanoid_Byte>(data).ToDna();
}
public static string SaveInstance(UMADnaHumanoid instance)
{
return UnityEngine.JsonUtility.ToJson(UMADnaHumanoid_Byte.FromDna(instance));
}
}
[System.Serializable]
public class UMADnaHumanoid_Byte
{
public System.Byte height;
public System.Byte headSize;
public System.Byte headWidth;
public System.Byte neckThickness;
public System.Byte armLength;
public System.Byte forearmLength;
public System.Byte armWidth;
public System.Byte forearmWidth;
public System.Byte handsSize;
public System.Byte feetSize;
public System.Byte legSeparation;
public System.Byte upperMuscle;
public System.Byte lowerMuscle;
public System.Byte upperWeight;
public System.Byte lowerWeight;
public System.Byte legsSize;
public System.Byte belly;
public System.Byte waist;
public System.Byte gluteusSize;
public System.Byte earsSize;
public System.Byte earsPosition;
public System.Byte earsRotation;
public System.Byte noseSize;
public System.Byte noseCurve;
public System.Byte noseWidth;
public System.Byte noseInclination;
public System.Byte nosePosition;
public System.Byte nosePronounced;
public System.Byte noseFlatten;
public System.Byte chinSize;
public System.Byte chinPronounced;
public System.Byte chinPosition;
public System.Byte mandibleSize;
public System.Byte jawsSize;
public System.Byte jawsPosition;
public System.Byte cheekSize;
public System.Byte cheekPosition;
public System.Byte lowCheekPronounced;
public System.Byte lowCheekPosition;
public System.Byte foreheadSize;
public System.Byte foreheadPosition;
public System.Byte lipsSize;
public System.Byte mouthSize;
public System.Byte eyeRotation;
public System.Byte eyeSize;
public System.Byte breastSize;
public UMADnaHumanoid ToDna()
{
var res = new UMADnaHumanoid();
res.height = height * (1f / 255f);
res.headSize = headSize * (1f / 255f);
res.headWidth = headWidth * (1f / 255f);
res.neckThickness = neckThickness * (1f / 255f);
res.armLength = armLength * (1f / 255f);
res.forearmLength = forearmLength * (1f / 255f);
res.armWidth = armWidth * (1f / 255f);
res.forearmWidth = forearmWidth * (1f / 255f);
res.handsSize = handsSize * (1f / 255f);
res.feetSize = feetSize * (1f / 255f);
res.legSeparation = legSeparation * (1f / 255f);
res.upperMuscle = upperMuscle * (1f / 255f);
res.lowerMuscle = lowerMuscle * (1f / 255f);
res.upperWeight = upperWeight * (1f / 255f);
res.lowerWeight = lowerWeight * (1f / 255f);
res.legsSize = legsSize * (1f / 255f);
res.belly = belly * (1f / 255f);
res.waist = waist * (1f / 255f);
res.gluteusSize = gluteusSize * (1f / 255f);
res.earsSize = earsSize * (1f / 255f);
res.earsPosition = earsPosition * (1f / 255f);
res.earsRotation = earsRotation * (1f / 255f);
res.noseSize = noseSize * (1f / 255f);
res.noseCurve = noseCurve * (1f / 255f);
res.noseWidth = noseWidth * (1f / 255f);
res.noseInclination = noseInclination * (1f / 255f);
res.nosePosition = nosePosition * (1f / 255f);
res.nosePronounced = nosePronounced * (1f / 255f);
res.noseFlatten = noseFlatten * (1f / 255f);
res.chinSize = chinSize * (1f / 255f);
res.chinPronounced = chinPronounced * (1f / 255f);
res.chinPosition = chinPosition * (1f / 255f);
res.mandibleSize = mandibleSize * (1f / 255f);
res.jawsSize = jawsSize * (1f / 255f);
res.jawsPosition = jawsPosition * (1f / 255f);
res.cheekSize = cheekSize * (1f / 255f);
res.cheekPosition = cheekPosition * (1f / 255f);
res.lowCheekPronounced = lowCheekPronounced * (1f / 255f);
res.lowCheekPosition = lowCheekPosition * (1f / 255f);
res.foreheadSize = foreheadSize * (1f / 255f);
res.foreheadPosition = foreheadPosition * (1f / 255f);
res.lipsSize = lipsSize * (1f / 255f);
res.mouthSize = mouthSize * (1f / 255f);
res.eyeRotation = eyeRotation * (1f / 255f);
res.eyeSize = eyeSize * (1f / 255f);
res.breastSize = breastSize * (1f / 255f);
return res;
}
public static UMADnaHumanoid_Byte FromDna(UMADnaHumanoid dna)
{
var res = new UMADnaHumanoid_Byte();
res.height = (System.Byte)(dna.height * 255f+0.5f);
res.headSize = (System.Byte)(dna.headSize * 255f+0.5f);
res.headWidth = (System.Byte)(dna.headWidth * 255f+0.5f);
res.neckThickness = (System.Byte)(dna.neckThickness * 255f+0.5f);
res.armLength = (System.Byte)(dna.armLength * 255f+0.5f);
res.forearmLength = (System.Byte)(dna.forearmLength * 255f+0.5f);
res.armWidth = (System.Byte)(dna.armWidth * 255f+0.5f);
res.forearmWidth = (System.Byte)(dna.forearmWidth * 255f+0.5f);
res.handsSize = (System.Byte)(dna.handsSize * 255f+0.5f);
res.feetSize = (System.Byte)(dna.feetSize * 255f+0.5f);
res.legSeparation = (System.Byte)(dna.legSeparation * 255f+0.5f);
res.upperMuscle = (System.Byte)(dna.upperMuscle * 255f+0.5f);
res.lowerMuscle = (System.Byte)(dna.lowerMuscle * 255f+0.5f);
res.upperWeight = (System.Byte)(dna.upperWeight * 255f+0.5f);
res.lowerWeight = (System.Byte)(dna.lowerWeight * 255f+0.5f);
res.legsSize = (System.Byte)(dna.legsSize * 255f+0.5f);
res.belly = (System.Byte)(dna.belly * 255f+0.5f);
res.waist = (System.Byte)(dna.waist * 255f+0.5f);
res.gluteusSize = (System.Byte)(dna.gluteusSize * 255f+0.5f);
res.earsSize = (System.Byte)(dna.earsSize * 255f+0.5f);
res.earsPosition = (System.Byte)(dna.earsPosition * 255f+0.5f);
res.earsRotation = (System.Byte)(dna.earsRotation * 255f+0.5f);
res.noseSize = (System.Byte)(dna.noseSize * 255f+0.5f);
res.noseCurve = (System.Byte)(dna.noseCurve * 255f+0.5f);
res.noseWidth = (System.Byte)(dna.noseWidth * 255f+0.5f);
res.noseInclination = (System.Byte)(dna.noseInclination * 255f+0.5f);
res.nosePosition = (System.Byte)(dna.nosePosition * 255f+0.5f);
res.nosePronounced = (System.Byte)(dna.nosePronounced * 255f+0.5f);
res.noseFlatten = (System.Byte)(dna.noseFlatten * 255f+0.5f);
res.chinSize = (System.Byte)(dna.chinSize * 255f+0.5f);
res.chinPronounced = (System.Byte)(dna.chinPronounced * 255f+0.5f);
res.chinPosition = (System.Byte)(dna.chinPosition * 255f+0.5f);
res.mandibleSize = (System.Byte)(dna.mandibleSize * 255f+0.5f);
res.jawsSize = (System.Byte)(dna.jawsSize * 255f+0.5f);
res.jawsPosition = (System.Byte)(dna.jawsPosition * 255f+0.5f);
res.cheekSize = (System.Byte)(dna.cheekSize * 255f+0.5f);
res.cheekPosition = (System.Byte)(dna.cheekPosition * 255f+0.5f);
res.lowCheekPronounced = (System.Byte)(dna.lowCheekPronounced * 255f+0.5f);
res.lowCheekPosition = (System.Byte)(dna.lowCheekPosition * 255f+0.5f);
res.foreheadSize = (System.Byte)(dna.foreheadSize * 255f+0.5f);
res.foreheadPosition = (System.Byte)(dna.foreheadPosition * 255f+0.5f);
res.lipsSize = (System.Byte)(dna.lipsSize * 255f+0.5f);
res.mouthSize = (System.Byte)(dna.mouthSize * 255f+0.5f);
res.eyeRotation = (System.Byte)(dna.eyeRotation * 255f+0.5f);
res.eyeSize = (System.Byte)(dna.eyeSize * 255f+0.5f);
res.breastSize = (System.Byte)(dna.breastSize * 255f+0.5f);
return res;
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 92db5a2bef85c8a49bd095e03118735e
timeCreated: 1426456705
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/Scripts/DNA/Legacy/UMADnaHumanoid_Generated.cs
uploadId: 679826
@@ -0,0 +1,8 @@
namespace UMA
{
[System.Serializable]
public partial class UMADnaTutorial : UMADna
{
public float eyeSpacing = 0.5f;
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: eaf5b852437ea4db0a37bba17017d480
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/Scripts/DNA/Legacy/UMADnaTutorial.cs
uploadId: 679826
@@ -0,0 +1,106 @@
// UMA Auto genered code, DO NOT MODIFY!!!
// All changes to this file will be destroyed without warning or confirmation!
// Use double { to escape a single curly bracket
//
// template junk executed per dna Field , the accumulated content is available through the {0:ID} tag
//
//#TEMPLATE GetValues UmaDnaChild_GetIndex_Fragment.cs.txt
//#TEMPLATE SetValues UmaDnaChild_SetIndex_Fragment.cs.txt
//#TEMPLATE GetValue UmaDnaChild_GetValue_Fragment.cs.txt
//#TEMPLATE SetValue UmaDnaChild_SetValue_Fragment.cs.txt
//#TEMPLATE GetNames UmaDnaChild_GetNames_Fragment.cs.txt
//
// Byte Serialization Handling
//
//#TEMPLATE Byte_Fields UmaDnaChild_Byte_Fields_Fragment.cs.txt
//#TEMPLATE Byte_ToDna UmaDnaChild_Byte_ToDna_Fragment.cs.txt
//#TEMPLATE Byte_FromDna UmaDnaChild_Byte_FromDna_Fragment.cs.txt
//
namespace UMA
{
public partial class UMADnaTutorial
{
public override int Count { get { return 1; } }
public override float[] Values
{
get
{
return new float[]
{
eyeSpacing,
};
}
set
{
eyeSpacing = value[0];
}
}
public override float GetValue(int idx)
{
switch(idx)
{
case 0: return eyeSpacing;
}
throw new System.ArgumentOutOfRangeException();
}
public override void SetValue(int idx, float value)
{
switch(idx)
{
case 0: eyeSpacing = value; return;
}
throw new System.ArgumentOutOfRangeException();
}
public static string[] GetNames()
{
return new string[]
{
"eyeSpacing",
};
}
public override string[] Names
{
get
{
return GetNames();
}
}
public static UMADnaTutorial LoadInstance(string data)
{
return UnityEngine.JsonUtility.FromJson<UMADnaTutorial_Byte>(data).ToDna();
}
public static string SaveInstance(UMADnaTutorial instance)
{
return UnityEngine.JsonUtility.ToJson(UMADnaTutorial_Byte.FromDna(instance));
}
}
[System.Serializable]
public class UMADnaTutorial_Byte
{
public System.Byte eyeSpacing;
public UMADnaTutorial ToDna()
{
var res = new UMADnaTutorial();
res.eyeSpacing = eyeSpacing * (1f / 255f);
return res;
}
public static UMADnaTutorial_Byte FromDna(UMADnaTutorial dna)
{
var res = new UMADnaTutorial_Byte();
res.eyeSpacing = (System.Byte)(dna.eyeSpacing * 255f+0.5f);
return res;
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: acb2973fae9fe3945b0f810cf4ba6269
timeCreated: 1426456705
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/Scripts/DNA/Legacy/UMADnaTutorial_Generated.cs
uploadId: 679826
@@ -0,0 +1,117 @@
// UMA Auto genered code, DO NOT MODIFY!!!
// All changes to this file will be destroyed without warning or confirmation!
// Use double { to escape a single curly bracket
//
// template junk executed per UMADna derived sub class, the accumulated content is available through the {0:ID} tag
//
//#TEMPLATE GetNames UmaDna_GetNames_Fragment.cs.txt
//#TEMPLATE GetType UmaDna_GetType_Fragment.cs.txt
//#TEMPLATE GetTypes UmaDna_GetTypes_Fragment.cs.txt
//#TEMPLATE Load UmaDna_Load_Fragment.cs.txt
//#TEMPLATE Save UmaDna_Save_Fragment.cs.txt
//
namespace UMA
{
public abstract partial class UMADna
{
public static string[] GetNames(System.Type dnaType)
{
if( dnaType == typeof(UMADnaHumanoid) )
{
return UMADnaHumanoid.GetNames();
}
if ( dnaType == typeof(UMADnaTutorial) )
{
return UMADnaTutorial.GetNames();
}
if ( dnaType == typeof(DynamicUMADna) )
{
return DynamicUMADna.GetNames();
}
return new string[0];
}
public static System.Type GetType(System.String className)
{
if( "UMADnaHumanoid" == className )
{
return typeof(UMADnaHumanoid);
}
if ( "UMADnaTutorial" == className )
{
return typeof(UMADnaTutorial);
}
if ( "DynamicUMADna" == className )
{
return typeof(DynamicUMADna);
}
return null;
}
public static System.Type[] GetTypes()
{
return new System.Type[]
{
typeof(UMADnaHumanoid),
typeof(UMADnaTutorial),
typeof(DynamicUMADna),
};
}
public static UMADnaBase LoadInstance(System.Type dnaType, System.String data)
{
if( dnaType == typeof(UMADnaHumanoid))
{
return UMADnaHumanoid.LoadInstance(data);
}
if ( dnaType == typeof(UMADnaTutorial))
{
return UMADnaTutorial.LoadInstance(data);
}
if ( dnaType == typeof(DynamicUMADna))
{
return DynamicUMADna.LoadInstance(data);
}
return null;
}
public static System.String SaveInstance(UMADnaBase instance)
{
System.Type dnaType = instance.GetType();
if( dnaType == typeof(UMADnaHumanoid))
{
return UMADnaHumanoid.SaveInstance(instance as UMADnaHumanoid);
}
if ( dnaType == typeof(UMADnaTutorial))
{
return UMADnaTutorial.SaveInstance(instance as UMADnaTutorial);
}
if ( dnaType == typeof(DynamicUMADna))
{
return DynamicUMADna.SaveInstance(instance as DynamicUMADna);
}
return null;
}
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 0f206c530b2ebc34cb647c71a68a825c
timeCreated: 1426456705
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/Scripts/DNA/Legacy/UMADna_Generated.cs
uploadId: 679826
@@ -0,0 +1,517 @@
using System.Collections.Generic;
using UnityEngine;
namespace UMA
{
//A dna evaluaton graph is used so that we dont need to hard code things like the math calcs for PoseOne and PoseZero in a MorphDNASet
//we can use a linear animation graph and that can do that evaluation for us based on the incoming dna value.
//Basically there is no need to hard code other behaviours now just because you need the dna interpreted differently.
//The incoming dna value is the horizontal axis on the graph and what it returns is the value on the vertical axis at that point.
//this class defines the graph class and provides lots of handy defaults in the same way Color does with Color.red, Color.blue etc
//Theres loads of help in the instance of DNAEvaluationGraphPresetLibrary in the project that hopefully makes it really clear.
[System.Serializable]
public sealed class DNAEvaluationGraph : System.IEquatable<DNAEvaluationGraph>
{
[SerializeField]
private string _name;
[SerializeField]
private AnimationCurve _graph;
public DNAEvaluationGraph() { }
public DNAEvaluationGraph(string name, AnimationCurve graph)
{
this._name = name;
this._graph = new AnimationCurve(graph.keys);
}
public DNAEvaluationGraph(DNAEvaluationGraph other)
{
this._name = other.name;
this._graph = new AnimationCurve(other._graph.keys);
}
public string name
{
get { return _name; }
}
public Keyframe[] GraphKeys
{
get { return _graph.keys; }
}
/// <summary>
/// <para>Evaluate the dnaValue using the graph.</para>
/// </summary>
/// <param name="dnaValue">The dnaValue within the graph you want to evaluate (the horizontal axis in the graph).</param>
/// <returns>
/// <para>The value of the graph, at the point specified.</para>
/// </returns>
public float Evaluate(float dnaValue)
{
return _graph.Evaluate(dnaValue);
}
/// <summary>
/// Returns true if this DNAEvaluationGraphs graph is the same as another ones graph
/// </summary>
/// <param name="other">The other DNAEvaluationGraph to check</param>
/// <returns></returns>
public bool GraphMatches(DNAEvaluationGraph other)
{
return GraphMatches(other._graph);
}
/// <summary>
/// Returns true if this DNAEvaluationGraphs graph is the same as the given AnimationCurve
/// </summary>
/// <param name="animCurve">The AnimationCurve to compare</param>
/// <returns></returns>
public bool GraphMatches(AnimationCurve animCurve)
{
//I *think* == on an animationCurve only tells us
//if the two instance are the same as one another
//rather than whether two curves have the same keys so...
if (this._graph == null && animCurve == null)
{
return true;
}
if (this._graph == null && animCurve != null)
{
return false;
}
if (this._graph != null && animCurve == null)
{
return false;
}
if (this._graph.keys.Length == animCurve.keys.Length)
{
if (this._graph.keys.Length == 0 && animCurve.keys.Length == 0)
{
return true;
}
int matchingKeys = 0;
for (int i = 0; i < this._graph.keys.Length; i++)
{
if (this._graph.keys[i].time == animCurve.keys[i].time &&
this._graph.keys[i].value == animCurve.keys[i].value &&
this._graph.keys[i].inTangent == animCurve.keys[i].inTangent &&
this._graph.keys[i].outTangent == animCurve.keys[i].outTangent /*&&
this._graph.keys[i].tangentMode == animCurve.keys[i].tangentMode*/)//tangentMode is obsolete outside the editor as of Unity2018
{
matchingKeys++;
}
}
if (matchingKeys == this._graph.keys.Length)
{
return true;
}
}
return false;
}
#region IEquatableInterface
public static implicit operator bool(DNAEvaluationGraph obj)
{
return ((System.Object)obj) != null;
}
public bool Equals(DNAEvaluationGraph other)
{
return (this == other);
}
public override bool Equals(object other)
{
return Equals(other as DNAEvaluationGraph);
}
public static bool operator ==(DNAEvaluationGraph cd1, DNAEvaluationGraph cd2)
{
return (Compare(cd1, cd2)) > 0;
}
public static bool operator !=(DNAEvaluationGraph cd1, DNAEvaluationGraph cd2)
{
return (Compare(cd1, cd2)) == 0;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
#endregion
#region IComparerInterface
/// <summary>
/// Compares the given DNAEvaluationGraphs to see if they are equal to each other
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns>Returns 0 if they are NOT the same returns 1 if they are the same (and if they are both null)</returns>
private static int Compare(object x, object y)
{
if (((System.Object)x) == null && ((System.Object)y) == null)
{
return 1;
}
if (((System.Object)x) == null && ((System.Object)y) != null)
{
return 0;
}
if (((System.Object)x) != null && ((System.Object)y) == null)
{
return 0;
}
var xo = (x as DNAEvaluationGraph);
var yo = (y as DNAEvaluationGraph);
if (xo.name == yo.name)
{
if (xo._graph != null && yo._graph != null)
{
if (xo._graph.keys.Length == yo._graph.keys.Length)
{
if (xo._graph.keys.Length == 0 && yo._graph.keys.Length == 0)
{
return 1;
}
int matchingKeys = 0;
for (int i = 0; i < xo._graph.keys.Length; i++)
{
if (xo._graph.keys[i].time == yo._graph.keys[i].time &&
xo._graph.keys[i].value == yo._graph.keys[i].value &&
xo._graph.keys[i].inTangent == yo._graph.keys[i].inTangent &&
xo._graph.keys[i].outTangent == yo._graph.keys[i].outTangent /*&&
xo._graph.keys[i].tangentMode == yo._graph.keys[i].tangentMode*/)//tangentMode is obsolete outside the editor as of Unity2018
{
matchingKeys++;
}
}
if (matchingKeys == xo._graph.keys.Length)
{
return 1;
}
return 0;
}
return 0;
}
else if (xo._graph == null && yo._graph == null)
{
return 1;
}
return 0;
}
return 0;
}
#endregion
#region DEFAULTS
//The following are like Color.blue or Color.red etc
//Should the 'Default' graph be the calculation that we *always* do (i.e. ((value - 0.5f) *2) which would be a graph that went was 0 to 1 horizontally and was -1 to +1 vertically)
//I think so, so I have made another graph that just returns the incoming dna value and called it 'Raw'
//I have also made a whole Presets thing aswell thats the equivalent to preset colors in the color chooser or preset animation curves with animation curve
//Check out DNAEvaluationGraphPresetLibrary for this, or an instance of it in the inspector- its like an animationCurve preset library on crack.
/// <summary>
/// Performs the default math calculation on the incoming dna value, subtracting 0.5f making its range -0.5f -> 0.5f
/// </summary>
public static DNAEvaluationGraph Default
{
get
{
return new DNAEvaluationGraph(
"Default",
new AnimationCurve(new Keyframe(0f, -0.5f, 0f, 1f), new Keyframe(0.5f, 0f, 1f, 1f), new Keyframe(1f, 0.5f, 1f, 2f))
);
}
}
/// <summary>
/// Performs the default math calculation on the incoming dna value and inverts it, extending its range from 0f -> 1f to 1f -> -1f
/// </summary>
public static DNAEvaluationGraph DefaultInverted
{
get
{
return new DNAEvaluationGraph(
"DefaultInverted",
new AnimationCurve(new Keyframe(0f, 0.5f, 0f, -1f), new Keyframe(0.5f, 0f, -1f, -1f), new Keyframe(1f, -0.5f, -1f, 2f))
);
}
}
/// <summary>
/// Performs the default math calculation on the incoming dna value, and extends its range from 0f -> 1f to -1f -> 1f
/// </summary>
public static DNAEvaluationGraph DefaultOne
{
get
{
return new DNAEvaluationGraph(
"DefaultOne",
new AnimationCurve(new Keyframe(0f, -1f, 0f, 2f), new Keyframe(0.5f, 0f, 2f, 2f), new Keyframe(1f, 1f, 2f, 1f))
);
}
}
/// <summary>
/// Performs the default math calculation on the incoming dna value and inverts it, extending its range from 0f -> 1f to 1f -> -1f
/// </summary>
public static DNAEvaluationGraph DefaultOneInverted
{
get
{
return new DNAEvaluationGraph(
"DefaultOneInverted",
new AnimationCurve(new Keyframe(0f, 1f, 0f, -2f), new Keyframe(0.5f, 0f, -2f, -2f), new Keyframe(1f, -1f, -2f, 1f))
);
}
}
/// <summary>
/// The returned value remains at zero until it is greater than 0.5 when it increases as the incoming value approaches 1f
/// </summary>
public static DNAEvaluationGraph ZeroZeroOne
{
get
{
return new DNAEvaluationGraph(
"ZeroZeroOne",
new AnimationCurve(new Keyframe(0f, 0f, 0f, 0f), new Keyframe(0.5f, 0f, 0f, 2f), new Keyframe(1f, 1f, 2f, 0f))
);
}
}
/// <summary>
/// Returns 1f when the dna value is zero and decreases as the dna approahes 0.5f, thereafter returns zero
/// </summary>
public static DNAEvaluationGraph OneZeroZero
{
get
{
return new DNAEvaluationGraph(
"OneZeroZero",
new AnimationCurve(new Keyframe(0f, 1f, 0f, -2f), new Keyframe(0.5f, 0f, -2f, 0f), new Keyframe(1f, 0f, 0f, 0f))
);
}
}
/// <summary>
/// Returns 0f when the dna value is zero, 1f when dna value is 0.5f, and zero when dna value is 1f
/// </summary>
public static DNAEvaluationGraph ZeroOneZero
{
get
{
return new DNAEvaluationGraph(
"ZeroOneZero",
new AnimationCurve(new Keyframe(0f, 0f, 0f, 2f), new Keyframe(0.5f, 1f, 2f, -2f), new Keyframe(1f, 0f, -2f, 1f))
);
}
}
/// <summary>
/// Returns 1f when the dna value is zero, 0f when dna value is 0.5f, and 1f when dna value is 1f
/// </summary>
public static DNAEvaluationGraph OneZeroOne
{
get
{
return new DNAEvaluationGraph(
"OneZeroOne",
new AnimationCurve(new Keyframe(0f, 1f, 0f, -2f), new Keyframe(0.5f, 0f, -2f, 2f), new Keyframe(1f, 1f, 2f, 1f))
);
}
}
/// <summary>
/// Returns 0f when the dna value is zero, fading to 1f when dna value is 0.5f, and 1f thereafter
public static DNAEvaluationGraph ZeroOneOne
{
get
{
return new DNAEvaluationGraph(
"ZeroOneOne",
new AnimationCurve(new Keyframe(0f, 0f, 0f, 2f), new Keyframe(0.5f, 1f, 2f, 0f), new Keyframe(1f, 1f, -0f, 1f))
);
}
}
/// <summary>
/// Returns 1f when the dna value is zero, 1f when dna value is 0.5f, fading to zero thereafter
public static DNAEvaluationGraph OneOneZero
{
get
{
return new DNAEvaluationGraph(
"OneOneZero",
new AnimationCurve(new Keyframe(0f, 1f, 0f, 0f), new Keyframe(0.5f, 1f, -0f, -2f), new Keyframe(1f, 0f, -2f, 1f))
);
}
}
/// <summary>
/// Simply returns the raw dna value which will be somewhere between 0 and 1
/// </summary>
public static DNAEvaluationGraph Raw
{
get
{
return new DNAEvaluationGraph(
"Raw",
new AnimationCurve(new Keyframe(0f, 0f, 0f, 1f), new Keyframe(0.5f, 0.5f, 1f, 1f), new Keyframe(1f, 1f, 1f, 0f))
);
}
}
/// <summary>
/// Simply returns the raw dna value but inverted, a incoming value of 1 will be 0 and vice versa
/// </summary>
public static DNAEvaluationGraph RawInverted
{
get
{
return new DNAEvaluationGraph(
"RawInverted",
new AnimationCurve(new Keyframe(0f, 1f, 0f, -1f), new Keyframe(0.5f, 0.5f, -1f, -1f), new Keyframe(1f, 0f, -1f, 0f))
);
}
}
public static string DefaultToolTip
{
get { return "The Default DNA Calculation. Subtracts 0.5f from the incoming value, making its range -0.5f -> 0.5f and making 0.5f = 0f"; }
}
public static string DefaultInvertedToolTip
{
get { return "The Default DNA Calculation but inverted. Subtracts 0.5f from the incoming value and multiplies by -1f, making its range 0.5f -> -0.5f and making 0.5f = 0f"; }
}
public static string DefaultOneToolTip
{
get { return "Subtracts 0.5f from the incoming value and doubles the result, extending its range from 0f -> 1f to -1f -> 1f"; }
}
public static string DefaultOneInvertedToolTip
{
get { return "Subtracts 0.5f from the incoming value, doubles the result, and inverts it, extending its range from 0f -> 1f to 1f -> -1f"; }
}
public static string ZeroZeroOneToolTip
{
get { return "The returned value remains at zero until it is greater than 0.5 when it increases as the incoming value approaches 1f"; }
}
public static string OneZeroZeroToolTip
{
get { return "Returns 1f when the dna value is zero and decreases as the dna approahes 0.5f, thereafter returns zero"; }
}
public static string ZeroOneZeroToolTip
{
get { return "Returns 0f when the dna value is zero, 1f when dna value is 0.5f, and zero when dna value is 1f"; }
}
public static string OneZeroOneToolTip
{
get { return "Returns 1f when the dna value is zero, 0f when dna value is 0.5f, and 1f when dna value is 1f"; }
}
public static string ZeroOneOneToolTip
{
get { return "Returns 0f when the dna value is zero, fading to 1f when dna value is 0.5f, and 1f thereafter"; }
}
public static string OneOneZeroToolTip
{
get { return "Returns 1f when the dna value is zero, 1f when dna value is 0.5f, fading to zero thereafter"; }
}
public static string RawToolTip
{
get { return "Simply returns the raw dna value which will be somewhere between 0 and 1"; }
}
public static string RawInvertedToolTip
{
get { return "Simply returns the raw dna value but inverted, a incoming value of 1 will be 0 and vice versa"; }
}
/// <summary>
/// Returns a dictionary of all the default DNAEvaluationGraphs and their corresponding tooltips
/// </summary>
/// <returns></returns>
public static Dictionary<DNAEvaluationGraph, string> Defaults
{
get
{
var dict = new Dictionary<DNAEvaluationGraph, string>
{
{ Default, DefaultToolTip },
{ DefaultInverted, DefaultInvertedToolTip },
{ DefaultOne, DefaultOneToolTip },
{ DefaultOneInverted, DefaultOneInvertedToolTip },
{ ZeroZeroOne, ZeroZeroOneToolTip },
{ OneZeroZero, OneZeroZeroToolTip },
{ ZeroOneZero, ZeroOneZeroToolTip },
{ OneZeroOne, OneZeroOneToolTip },
{ OneOneZero, OneOneZeroToolTip },
{ ZeroOneOne, ZeroOneOneToolTip },
{ Raw, RawToolTip },
{ RawInverted, RawInvertedToolTip },
};
return dict;
}
}
#endregion
#region Special Types
public class EditorHelper
{
private DNAEvaluationGraph _evaluationGraph;
public EditorHelper()
{
_evaluationGraph = new DNAEvaluationGraph();
}
public EditorHelper(AnimationCurve graph, string name, string description)
{
_evaluationGraph = new DNAEvaluationGraph(name, graph);
}
public EditorHelper(DNAEvaluationGraph dnaEasingCurve)
{
_evaluationGraph = new DNAEvaluationGraph(dnaEasingCurve);
}
public DNAEvaluationGraph Target
{
get { return _evaluationGraph; }
}
public string _name
{
get { return _evaluationGraph._name; }
set { _evaluationGraph._name = value; }
}
public AnimationCurve _graph
{
get { return _evaluationGraph._graph; }
set {
if (value == null)
{
_evaluationGraph._graph = value;
}
else
{
_evaluationGraph._graph = new AnimationCurve(value.keys);
}
}
}
}
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 774c8aecab9a17241814ee689de5d91d
timeCreated: 1537557934
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/Scripts/DNAEvaluationGraph.cs
uploadId: 679826
@@ -0,0 +1,429 @@
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using System.IO;
#endif
namespace UMA
{
//This is an editor only asset that stores a list of preset DNAEvaluationGraphs, its similar to an AnimationCurve Preset Library.
//A DNAEvaluationGraph field works kind of like an enum of animationCurves and in its popup it shows the presets defined here,
//rather like the 'Options' cog in an AnimationCurve editor. This is to make it possible for the user to easily set 'sane' values for their DNAEvaluationGraphs.
//
//I really wanted the options that show in the dropdown of choices to also have tooltips to explain what they do, but I didn't want the
//actual field to contain that data, so this handles that too
//
//The values from the items in this library are used to set the keyframes in the DNAEvaluationGraph's graph value in the same way that an
//a curve in an AnimationCurve preset library, is used to set the keyframes for an AnimationCurve, the actual presets are not referenced.
//This is so that this script/asset does not need to be in the build for a DNAEvaluationGraph field to work, under the hood DNAEvaluationGraph is just a name (string) and graph(AnimationCurve)
//There is a slight downside to this in that 'editing' a preset here wont change the values of any field that appears to be using it, we could make that happen though if its deemed necessary.
//
//I decided to make it possible to create more than one of these libraries because its better if the user doesn't edit one that might in future come with UMA
//because if they update UMA it will get overridden and they'd loose any of their custom graphs.
//I chose not to just have an animationCurve field because its really not very easy to understand how the curve works on the incoming dna value
[System.Serializable]
public sealed class DNAEvaluationGraphPresetLibrary : ScriptableObject
{
[SerializeField]
private List<DNAEvaluationGraph> _customGraphPresets = new List<DNAEvaluationGraph>();
[SerializeField]
private List<string> _customGraphTooltips = new List<string>();
#if UNITY_EDITOR
private static List<DNAEvaluationGraph> _cachedGlobalList = new List<DNAEvaluationGraph>();
private static List<string> _cachedGlobalTooltips = new List<string>();
#endif
/// <summary>
/// Returns the default list of graph presets shipped with UMA
/// </summary>
public static List<DNAEvaluationGraph> DefaultGraphPresets
{
get { return new List<DNAEvaluationGraph>(DNAEvaluationGraph.Defaults.Keys); }
}
public static List<string> DefaultGraphTooltips
{
get { return new List<string>(DNAEvaluationGraph.Defaults.Values); }
}
/// <summary>
/// Returns the list of graph presets stored in this asset
/// </summary>
public List<DNAEvaluationGraph> CustomGraphPresets
{
get { return _customGraphPresets; }
}
/// <summary>
/// Returns the list of graph presets tooltips stored in this asset
/// </summary>
public List<string> CustomGraphTooltips
{
get { return _customGraphTooltips; }
}
#if UNITY_EDITOR
/// <summary>
/// Returns all the custom graph presets from all the DNAEvaluatorPresetLibrarys in the project
/// </summary>
public static List<DNAEvaluationGraph> AllCustomGraphPresets
{
get
{
var presetLibs = GetAllInstances();
var _customsList = new List<DNAEvaluationGraph>();
for (int i = 0; i < presetLibs.Length; i++)
{
if (presetLibs[i]._customGraphPresets.Count > 0)
{
_customsList.AddRange(presetLibs[i]._customGraphPresets);
}
}
return _customsList;
}
}
/// <summary>
/// Returns all the custom graph tooltips from all the DNAEvaluatorPresetLibrarys in the project
/// </summary>
public static List<string> AllCustomGraphTooltips
{
get
{
var presetLibs = GetAllInstances();
var _customsTooltips = new List<string>();
for (int i = 0; i < presetLibs.Length; i++)
{
if (presetLibs[i]._customGraphTooltips.Count > 0)
{
_customsTooltips.AddRange(presetLibs[i]._customGraphTooltips);
}
}
return _customsTooltips;
}
}
/// <summary>
/// Returns a list of all the graph presets in the project (Default and Custom)
/// </summary>
public static List<DNAEvaluationGraph> AllGraphPresets
{
get
{
if (_cachedGlobalList.Count == 0)
{
_cachedGlobalList.AddRange(DefaultGraphPresets);
_cachedGlobalList.AddRange(AllCustomGraphPresets);
}
return _cachedGlobalList;
}
}
/// <summary>
/// Returns a list of the tooltips for all the graph presets in the project (Default and Custom)
/// </summary>
public static List<string> AllGraphTooltips
{
get {
if (_cachedGlobalTooltips.Count == 0)
{
_cachedGlobalTooltips.AddRange(DefaultGraphTooltips);
_cachedGlobalTooltips.AddRange(AllCustomGraphTooltips);
}
return _cachedGlobalTooltips;
}
}
/// <summary>
/// Attempts to find the tooltip for the given evaluation graph from all the graph presets in the project (Default and Custom)
/// </summary>
public static string GetTooltipFor(DNAEvaluationGraph graph)
{
var ret = graph.name;
foreach(KeyValuePair<DNAEvaluationGraph, string> kp in DNAEvaluationGraph.Defaults)
{
if (kp.Key.GraphMatches(graph))
{
return kp.Value;
}
}
var _allCustomPresets = AllCustomGraphPresets;
for (int i = 0; i < _allCustomPresets.Count; i++)
{
if (_allCustomPresets[i].GraphMatches(graph))
{
return AllCustomGraphTooltips[i];
}
}
return ret;
}
/// <summary>
/// Adds a new preset to the first found DNAEvaluationGraphPresets library
/// </summary>
public static void AddNewPreset(string name, AnimationCurve graph, string tooltip)
{
var presetLibs = GetAllInstances();
for (int i = 0; i < presetLibs.Length; i++)
{
if (presetLibs[i].AddNewPreset(graph, name, tooltip))
{
break;
}
}
}
/// <summary>
/// Add a new preset to this assets DNAEvaluatorPresets list
/// </summary>
public bool AddNewPreset(AnimationCurve graph, string name, string tooltip)
{
var nameError = "";
var graphError = "";
return AddNewPreset(name, tooltip, graph, ref nameError, ref graphError, null);
}
/// <summary>
/// Add a new preset to this assets DNAEvaluatorPresets list
/// </summary>
/// <returns>Returns true if the preset was added. False if another preset already existed with the same name or graph</returns>
public bool AddNewPreset(string name, string tooltip, AnimationCurve graph, ref string nameError, ref string graphError, DNAEvaluationGraph existingGraph = null)
{
nameError = "";
graphError = "";
bool added = true;
if (graph == null)
{
graphError = "No graph Provided";
Debug.LogWarning(graphError);
return false;
}
if (string.IsNullOrEmpty(name))
{
nameError = "No name provided";
Debug.LogWarning(nameError);
return false;
}
if (graph.keys.Length < 2)
{
graphError = "The new graph must have at least two keys";
Debug.LogWarning(graphError);
return false;
}
//if we are not updating an existing one
if (existingGraph == null)
{
//make sure there is not already an existing one
if (!CheckForExistingPreset(name, graph, ref nameError, ref graphError))
{
Debug.LogWarning("Graph could not be added");
if (!string.IsNullOrEmpty(nameError))
{
Debug.LogWarning(nameError);
}
if (!string.IsNullOrEmpty(graphError))
{
Debug.LogWarning(graphError);
}
return false;
}
_customGraphPresets.Add(new DNAEvaluationGraph(name, graph));
_customGraphTooltips.Add(tooltip);
}
else
{
//get the existing one (which might reside in another asset) and update that
var presetLibs = GetAllInstances();
int foundIndex = -1;
for (int i = 0; i < presetLibs.Length; i++)
{
foundIndex = -1;
for (int ci = 0; ci < presetLibs[i]._customGraphPresets.Count; ci++)
{
if(presetLibs[i]._customGraphPresets[ci] == existingGraph)
{
foundIndex = ci;
break;
}
}
if(foundIndex >= 0)
{
presetLibs[i]._customGraphPresets[foundIndex] = new DNAEvaluationGraph(name, graph);
presetLibs[i]._customGraphTooltips[foundIndex] = tooltip;
added = true;
EditorUtility.SetDirty(presetLibs[i]);
//AssetDatabase.SaveAssets();
break;
}
}
if (!added)
{
nameError = graphError ="Could not find existsing graph to update";
}
}
if (added)
{
//force the global caches to refresh
_cachedGlobalList.Clear();
_cachedGlobalTooltips.Clear();
}
return added;
}
/// <summary>
/// Returns true if no existing default or custom presets have the same name or graph as the given name and graph
/// </summary>
/// <param name="name">The name of the preset to be added</param>
/// <param name="graph">The graph of the preset to be added</param>
/// <param name="nameError">Returns an error with the name of the library asset that an existing DNAEvaluationGraph with the same name has been added to</param>
/// <param name="graphError">Returns an error with the name of the library asset that an existing DNAEvaluationGraph with the same graph has been added to</param>
/// <returns></returns>
private bool CheckForExistingPreset(string name, AnimationCurve graph, ref string nameError, ref string graphError)
{
nameError = "";
graphError = "";
//check the default presets
for (int di = 0; di < DefaultGraphPresets.Count; di++)
{
if (DefaultGraphPresets[di].name == name)
{
nameError = "There was already a Default Preset with the name you are attempting to add";
return false;
}
if (DefaultGraphPresets[di].GraphMatches(graph))
{
graphError = "Default preset " + DefaultGraphPresets[di].name + " has the same graph as the one you are attempting to add";
return false;
}
}
var presetLibs = GetAllInstances();
for (int i = 0; i < presetLibs.Length; i++)
{
if (presetLibs[i]._customGraphPresets.Count > 0)
{
for (int ci = 0; ci < presetLibs[i]._customGraphPresets.Count; ci++)
{
if (presetLibs[i].CustomGraphPresets[ci].name == name)
{
nameError = "Custom preset " + presetLibs[i]._customGraphPresets[ci].name + " in preset library named " + presetLibs[i].name + " has the same name as the one you are attempting to add";
return false;
}
if (presetLibs[i]._customGraphPresets[ci].GraphMatches(graph))
{
graphError = "Custom preset " + presetLibs[i]._customGraphPresets[ci].name + " in preset library named "+ presetLibs[i].name +" has the same graph as the one you are attempting to add";
return false;
}
}
}
}
return true;
}
/// <summary>
/// Deletes any customPresets using the given graph from all preset libraries in the project
/// </summary>
public static void DeleteCustomPreset(DNAEvaluationGraph graphToDelete)
{
var presetLibs = GetAllInstances();
int indexToDel = -1;
for (int i = 0; i < presetLibs.Length; i++)
{
if (presetLibs[i]._customGraphPresets.Count > 0)
{
for (int ci = 0; ci < presetLibs[i]._customGraphPresets.Count; ci++)
{
if(presetLibs[i]._customGraphPresets[ci] == graphToDelete)
{
Debug.Log("Found Delete Target " + graphToDelete.name+" in "+presetLibs[i].name);
indexToDel = ci;
break;
}
}
if (indexToDel > -1)
{
Debug.Log(graphToDelete.name+" Deleted from list in "+presetLibs[i].name);
presetLibs[i]._customGraphPresets.RemoveAt(indexToDel);
try
{
presetLibs[i]._customGraphTooltips.RemoveAt(indexToDel);
}
catch { }
}
}
if (indexToDel > -1)
{
//force the global caches to refresh
_cachedGlobalList.Clear();
_cachedGlobalTooltips.Clear();
EditorUtility.SetDirty(presetLibs[i]);
AssetDatabase.SaveAssets();
break;
}
}
}
private static DNAEvaluationGraphPresetLibrary[] GetAllInstances()
{
string[] guids = AssetDatabase.FindAssets("t:" + typeof(DNAEvaluationGraphPresetLibrary).Name);
DNAEvaluationGraphPresetLibrary[] a = new DNAEvaluationGraphPresetLibrary[guids.Length];
for (int i = 0; i < guids.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
a[i] = AssetDatabase.LoadAssetAtPath<DNAEvaluationGraphPresetLibrary>(path);
}
return a;
}
#region DEMO FIELDS AND MEHODS
[SerializeField]
private DNAEvaluationGraph _exampleField;
[SerializeField]
[Tooltip("To make the example work, make sure the dna name is set to 'DNAValue', or not, up to you :)")]
private DNAEvaluator _exampleEvaluator = new DNAEvaluator("DNAValue", DNAEvaluationGraph.Default, 1f);
[SerializeField]
[Range(0f, 1f)]
private float _exampleDNAValue = 0.5f;
public float EvaluateDemo()
{
var dna = new DynamicUMADna();
dna._names = new string[1] { "DNAValue" };
dna._values = new float[1] { _exampleDNAValue };
return _exampleEvaluator.Evaluate(dna);
}
#endregion
[UnityEditor.MenuItem("UMA/Create DNAEvaluationGraph Presets Library")]
public static void DNAEvaluatorPresetLibraryAsset()
{
//we want to create this in an editor folder
var path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (path == "")
{
path = "Assets/Editor";
}
else if (File.Exists(path)) // modified this line, folders can have extensions.
{
path = path.Replace("/" + Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
}
if (path.IndexOf("/Editor") < 0)
{
path += "/Editor";
}
path += "/New DNAEvaluatorPresetLibrary.asset";
UMA.CustomAssetUtility.CreateAsset<DNAEvaluationGraphPresetLibrary>(path);
}
#endif
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 4d44c25e00b6ded4fb89fab1646d01ee
timeCreated: 1537725240
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/Scripts/DNAEvaluationGraphPresetLibrary.cs
uploadId: 679826
+208
View File
@@ -0,0 +1,208 @@
using UnityEngine;
namespace UMA
{
//A DNAEvaluator performs an math evaluation on an incoming dna value.
//All over the place in our code we change the incoming dna value from its standard 0 -> 1 range to a -0.5 -> 0.5 range
//but then in Morphset new code was needed in order to change the value into a value that could be used for 'PoseOne' or 'PoseZero'
//The whole idea of DNAEvaluator (and its corresoponding DNAEvaluationGraph) is to rid us of the need to make new code every time
//we need to perform a different math calculation on a dna value (and to give this flexibility to users as well)
[System.Serializable]
public sealed class DNAEvaluator : ISerializationCallbackReceiver
{
//This is used with the Cumulative option in DNAEvaluatorList. Each line can be added/subtracted etc from the previous one
public enum CalcOption
{
Add,
Subtract,
Multiply,
Divide
};
[SerializeField]
[Tooltip("Define how the evaluated value will be combined with the previous Evaluator in the list.")]
private CalcOption _calcOption = CalcOption.Add;
[SerializeField]
[Tooltip("The DNA entry name to evaluate")]
private string _dnaName;
//this could be used to search dnaNames by hash- I did some tests but could not see any speed improvement
[SerializeField]
private int _dnaNameHash = -1;
[SerializeField]
[Tooltip("Evaluates the incoming dna value using the given graph. Hover the options for info")]
private DNAEvaluationGraph _evaluator;
[SerializeField]
[Tooltip("The evaluated value will be multiplied by this value before it is returned.")]
private float _multiplier = 1.0f;
[SerializeField]
[HideInInspector]
private bool _initialized = false;
//used at runtime to speed up finding dna values. The first time the evaluator evaluates it uses the dnaName, if the request successfully
//returns an index, thereafter the index is used
//this has a minor impact on speed for lookups
[System.NonSerialized]
private int _lastIndex = -1;
//if the evaluator fails to evaluate for any reason it returns this value
//TODO Double check this doesn't need to return the raw value (i.e. 0.5f)
public static readonly float defaultDNAValue = 0f;
public CalcOption calcOption
{
get { return _calcOption; }
set { _calcOption = value; }
}
public string dnaName
{
get { return _dnaName; }
set
{
_dnaName = value;
_dnaNameHash = UMAUtils.StringToHash(_dnaName);
}
}
public int dnaNameHash
{
get { return _dnaNameHash; }
}
public DNAEvaluationGraph evaluator
{
get { return _evaluator; }
set { _evaluator = new DNAEvaluationGraph(value); }
}
public float multiplier
{
get { return _multiplier; }
set { _multiplier = value; }
}
public DNAEvaluator() { }
public DNAEvaluator(string dnaName, DNAEvaluationGraph evaluator = null, float multiplier = 1f, CalcOption calcOption = CalcOption.Add)
{
_calcOption = calcOption;
_dnaName = dnaName;
_dnaNameHash = UMAUtils.StringToHash(_dnaName);
_evaluator = evaluator == null ? DNAEvaluationGraph.Default : new DNAEvaluationGraph(evaluator);
_multiplier = multiplier;
_initialized = true;
}
public DNAEvaluator(DNAEvaluator other)
{
_calcOption = other.calcOption;
_dnaName = other.dnaName;
_dnaNameHash = UMAUtils.StringToHash(_dnaName);
_evaluator = other.evaluator == null ? DNAEvaluationGraph.Default : new DNAEvaluationGraph(other.evaluator);
_multiplier = other.multiplier;
_initialized = true;
}
/// <summary>
/// Applies the evaluator and the multiplier to the given dna value.
/// </summary>
/// <param name="dnaValue"></param>
/// <returns>The evaluated value</returns>
public float Evaluate(float dnaValue)
{
float val = dnaValue;
return (_evaluator.Evaluate(val)) * _multiplier;
}
/// <summary>
/// Finds the set dna name in the supplied dna list and applies the evaluator and multiplier to it.
/// Tip: if you are already looping through the dna to find the name/value just use Evaluate(float dnaValue) instead for efficiency
/// </summary>
/// <param name="dna">The dna to search</param>
/// <returns>The evaluated value</returns>
public float Evaluate(UMADnaBase dna)
{
//using lastIndex building dna takes apprx 00:00:00.0027695
//Using lastIndex modifying dna takes apprx 00:00:00.0004993
//notusing lastIndex building dna takes apprx 00:00:00.0035695
//notusing lastIndex modifying dna takes apprx 00:00:00.0008447
//using lastIndex is about 1/3 faster at the cost of being less robust because the dnaNames could in theory be changed at runtime and lastIndex would then fail
//could make the difference between being able to use things like color dna for wrinkle maps etc?
if (_lastIndex != -1)
{
return Evaluate(dna.GetValue(_lastIndex));
}
else
{
if (!string.IsNullOrEmpty(_dnaName) && dna.Names != null)
{
_lastIndex = System.Array.IndexOf(dna.Names, _dnaName);
if (_lastIndex > -1)
{
return Evaluate(dna.GetValue(_lastIndex));
}
}
}
return defaultDNAValue;
}
#region ISerializationCallbackReceiver Members
public void OnAfterDeserialize()
{
}
//Fix to ensure that when instances of this class are added to a list in the inspector the default values are set properly
//GRR I hate this solution but I dont want users to have to set every new one of these to have a multiplier of 1 and if the dnaName is empty the hash needs to be -1
public void OnBeforeSerialize()
{
if (!_initialized)
{
if (!string.IsNullOrEmpty(_dnaName))
{
_dnaNameHash = UMAUtils.StringToHash(_dnaName);
}
else
{
_dnaNameHash = -1;
}
_multiplier = 1f;
_initialized = true;
}
}
#endregion
#region ATTRIBUTES
//TODO Ditch these I always want this drawn the same way
[System.AttributeUsage(System.AttributeTargets.Field)]
public class ConfigAttribute : System.Attribute
{
public bool drawLabels = true;
//the calc option is usually only needed with a list of Evaluators whose results will be added/subtracted etc from each other
public bool drawCalcOption = false;
public bool alwaysExpanded = false;
public ConfigAttribute(bool drawLabels)
{
this.drawLabels = drawLabels;
}
public ConfigAttribute(bool drawLabels, bool alwaysExpanded)
{
this.drawLabels = drawLabels;
this.alwaysExpanded = alwaysExpanded;
}
public ConfigAttribute( bool drawLabels, bool alwaysExpanded, bool drawCalcOption)
{
this.drawLabels = drawLabels;
this.alwaysExpanded = alwaysExpanded;
this.drawCalcOption = drawCalcOption;
}
}
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 3428d329f54c1d44fb1be5929ddf43d2
timeCreated: 1537642906
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/Scripts/DNAEvaluator.cs
uploadId: 679826
+340
View File
@@ -0,0 +1,340 @@
using System.Collections.Generic;
using UnityEngine;
namespace UMA
{
//A dna evaluator list evaluates multiple dna at the same time according to each entrys evaluation graph.
//the results are then combined together according to the 'aggregationMethod' option.
//This provides huge flexibility when determining how different dna values interract with each other when they are affecting
//a blendshape or bone pose for example.
//You can generally treat this like a list of evaluators, but you can call Evaluate on it directly to get the aggregated result
[System.Serializable]
public class DNAEvaluatorList
{
public enum AggregationMethodOpts
{
Average,
Cumulative,
Minimum,
Maximum
};
[SerializeField]
private List<DNAEvaluator> _dnaEvaluators = new List<DNAEvaluator>();
[SerializeField]
[Tooltip("How the evaluated results of each entry are combined and returned. When 'Cumulative' is selected you can choose how each line will be combined with the preceeding one.")]
private AggregationMethodOpts _aggregationMethod = AggregationMethodOpts.Average;
public AggregationMethodOpts aggregationMethod
{
get { return _aggregationMethod; }
set { _aggregationMethod = value; }
}
/// <summary>
/// Returns a list of used dna names- any entries that dont have a name assigned are discarded. Beware that the list will include names that may not be in the current dna and these will return 0.5f
/// </summary>
public List<string> UsedDNANames
{
get
{
var ret = new List<string>();
for(int i = 0; i < _dnaEvaluators.Count; i++)
{
if (!string.IsNullOrEmpty(_dnaEvaluators[i].dnaName))
{
ret.Add(_dnaEvaluators[i].dnaName);
}
}
return ret;
}
}
#region CONSTRUCTOR
public DNAEvaluatorList()
{
}
public DNAEvaluatorList(DNAEvaluatorList other)
{
_aggregationMethod = other._aggregationMethod;
for(int i = 0; i < other._dnaEvaluators.Count; i++)
{
_dnaEvaluators.Add(new DNAEvaluator(other._dnaEvaluators[i]));
}
}
public DNAEvaluatorList(List<DNAEvaluator> evaluators, AggregationMethodOpts aggregationMethod = AggregationMethodOpts.Average)
{
_aggregationMethod = aggregationMethod;
for (int i = 0; i < evaluators.Count; i++)
{
_dnaEvaluators.Add(new DNAEvaluator(evaluators[i]));
}
}
public DNAEvaluatorList(AggregationMethodOpts aggregationMethod)
{
_aggregationMethod = aggregationMethod;
}
#endregion
#region METHODS
/// <summary>
/// For each evaluator in the list, finds the set dna name in the supplied dna list and applies the evaluator and multiplier to it. The resulting values are then aggregated together using the method defined in the 'AggregationMethod' field
/// </summary>
/// <param name="dna">The dna to search</param>
/// <returns>The evaluated value</returns>
public float Evaluate(UMADnaBase dna)
{
if(_dnaEvaluators.Count > 0)
{
return GetAggregateValueNew(dna);
}
else
{
return DNAEvaluator.defaultDNAValue;
}
}
public float ApplyDNAToValue(UMADnaBase umaDna, float startingValue)
{
if (_dnaEvaluators.Count > 0)
{
return GetAggregateValueNew(umaDna, startingValue);
}
else
{
return startingValue;
}
}
private float GetAggregateValueNew(UMADnaBase dna, float result = 0f)
{
float tempResult = 0f;
if (_aggregationMethod == AggregationMethodOpts.Average)
{
var aveCount = result != 0f ? 1 : 0;
for (int i = 0; i < _dnaEvaluators.Count; i++)
{
if (!string.IsNullOrEmpty(_dnaEvaluators[i].dnaName))
{
aveCount++;
result += _dnaEvaluators[i].Evaluate(dna);
}
}
if (aveCount > 0)
{
result = result / aveCount;
}
}
else if (_aggregationMethod == AggregationMethodOpts.Maximum)
{
for (int i = 0; i < _dnaEvaluators.Count; i++)
{
tempResult = 0f;
if (!string.IsNullOrEmpty(_dnaEvaluators[i].dnaName))
{
tempResult = _dnaEvaluators[i].Evaluate(dna);
if (tempResult > result)
{
result = tempResult;
}
}
}
}
else if (_aggregationMethod == AggregationMethodOpts.Minimum)
{
for (int i = 0; i < _dnaEvaluators.Count; i++)
{
tempResult = 0f;
if (!string.IsNullOrEmpty(_dnaEvaluators[i].dnaName))
{
tempResult = _dnaEvaluators[i].Evaluate(dna);
if (tempResult < result)
{
result = tempResult;
}
}
}
}
else if (aggregationMethod == AggregationMethodOpts.Cumulative)
{
for (int i = 0; i < _dnaEvaluators.Count; i++)
{
tempResult = 0f;
if (!string.IsNullOrEmpty(_dnaEvaluators[i].dnaName))
{
tempResult = _dnaEvaluators[i].Evaluate(dna);
if (_dnaEvaluators[i].calcOption == DNAEvaluator.CalcOption.Add)
{
result += tempResult;
}
else if (_dnaEvaluators[i].calcOption == DNAEvaluator.CalcOption.Subtract)
{
result -= tempResult;
}
else if (_dnaEvaluators[i].calcOption == DNAEvaluator.CalcOption.Multiply)
{
result *= tempResult;
}
else if (_dnaEvaluators[i].calcOption == DNAEvaluator.CalcOption.Divide && tempResult != 0)
{
result /= tempResult;
}
}
}
}
return result;
}
private float GetAggregateValue(List<float> vals)
{
float result = 0f;
if(_aggregationMethod == AggregationMethodOpts.Average)
{
if (vals.Count > 0)
{
for (int i = 0; i < vals.Count; i++)
{
result += vals[i];
}
result = result / vals.Count;
}
}
if(_aggregationMethod == AggregationMethodOpts.Cumulative)
{
for (int i = 0; i < vals.Count; i++)
{
result += vals[i];
}
}
if(_aggregationMethod == AggregationMethodOpts.Minimum)
{
if (vals.Count > 0)
{
result = vals[0];
for (int i = 0; i < vals.Count; i++)
{
if (vals[i] < result)
{
result = vals[i];
}
}
}
}
if(_aggregationMethod == AggregationMethodOpts.Maximum)
{
if (vals.Count > 0)
{
result = vals[0];
for (int i = 0; i < vals.Count; i++)
{
if (vals[i] > result)
{
result = vals[i];
}
}
}
}
return result;
}
#endregion
#region PASSTHRU LIST METHODS
public DNAEvaluator this[int key]
{
get { return _dnaEvaluators[key]; }
set { _dnaEvaluators[key] = value; }
}
public int Count
{
get { return _dnaEvaluators.Count; }
}
public void Add(DNAEvaluator evaluator)
{
_dnaEvaluators.Add(evaluator);
}
public void AddRange(IEnumerable<DNAEvaluator> evaluators)
{
_dnaEvaluators.AddRange(evaluators);
}
public bool Contains(DNAEvaluator evaluator)
{
return _dnaEvaluators.Contains(evaluator);
}
public void Clear()
{
_dnaEvaluators.Clear();
}
public int IndexOf(DNAEvaluator evaluator)
{
return _dnaEvaluators.IndexOf(evaluator);
}
public void Insert(int index, DNAEvaluator evaluator)
{
_dnaEvaluators.Insert(index, evaluator);
}
public void InsertRange(int index, IEnumerable<DNAEvaluator> evaluators)
{
_dnaEvaluators.InsertRange(index, evaluators);
}
public void Remove(DNAEvaluator evaluator)
{
_dnaEvaluators.Remove(evaluator);
}
public void RemoveAt(int index)
{
_dnaEvaluators.RemoveAt(index);
}
public void RemoveRange(int index, int count)
{
_dnaEvaluators.RemoveRange(index, count);
}
public DNAEvaluator[] ToArray()
{
return _dnaEvaluators.ToArray();
}
#endregion
#region ATTRIBUTES
[System.AttributeUsage(System.AttributeTargets.Field)]
public class ConfigAttribute : System.Attribute
{
public enum LabelOptions
{
drawLabelAsFoldout,
drawExpandedWithLabel,
drawExpandedNoLabel
}
//if drawExpandedNoLabel the label for the list is shown as the heading for the dnaName field
public LabelOptions labelOption = LabelOptions.drawLabelAsFoldout;
//if set, when a new entry is added to the list using the UI it will be of this type
public DNAEvaluationGraph defaultGraph = DNAEvaluationGraph.Default;
/// <param name="labelOption">How to show the label for the list. If 'drawExpandedNoLabel' the label for the list is shown as the heading for the dnaName field</param>
public ConfigAttribute(LabelOptions labelOption)
{
this.labelOption = labelOption;
}
}
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 355e702fa1ebb78499ee5a4bb2ebd821
timeCreated: 1538092557
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/Scripts/DNAEvaluatorList.cs
uploadId: 679826
+10
View File
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 700d4ac68ef1e87469914cf2a9fac3c8
folderAsset: yes
timeCreated: 1538883441
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,282 @@
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UMA
{
[System.Serializable]
public class BlendshapeDNAConverterPlugin : DynamicDNAPlugin
{
#region FIELDS
[SerializeField]
private List<BlendshapeDNAConverter> _blendshapeDNAConverters = new List<BlendshapeDNAConverter>();
#endregion
#region PUBLIC PROPERTIES
public List<BlendshapeDNAConverter> blendshapeDNAConverters
{
get { return _blendshapeDNAConverters; }
set { _blendshapeDNAConverters = value; }
}
#endregion
#region DYNAMICDNAPLUGIN PROPERTIES
#if UNITY_EDITOR
public override string PluginHelp
{
get { return "Blendshape DNA Converters convert the set dna names into weight settings for Blendshape on the character. You can use the 'Starting Shape Weight' to force this shape on for all characters that use this converter at the start. Or you can hook up to a modifying dna so that the shape is only applied based on a characters dna value."; }
}
#endif
#endregion
#region REQUIRED DYNAMICDNAPLUGIN METHODS
/// <summary>
/// Returns a dictionary of all the dna names in use by the plugin and the entries in its converter list that reference them
/// </summary>
/// <returns></returns>
public override Dictionary<string, List<int>> IndexesForDnaNames
{
get
{
var dict = new Dictionary<string, List<int>>();
for (int i = 0; i < _blendshapeDNAConverters.Count; i++)
{
for (int ci = 0; ci < _blendshapeDNAConverters[i].UsedDNANames.Count; ci++)
{
if (!dict.ContainsKey(_blendshapeDNAConverters[i].UsedDNANames[ci]))
{
dict.Add(_blendshapeDNAConverters[i].UsedDNANames[ci], new List<int>());
}
dict[_blendshapeDNAConverters[i].UsedDNANames[ci]].Add(i);
}
}
return dict;
}
}
public override ApplyPassOpts ApplyPass
{
get
{
return ApplyPassOpts.PostPass;
}
}
/// <summary>
/// Apply the blendshape modifications according to the given dna (determined by the dnaTypeHash)
/// </summary>
/// <param name="umaData"></param>
/// <param name="skeleton"></param>
/// <param name="dnaTypeHash"></param>
public override void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash)
{
var umaDna = umaData.GetDna(dnaTypeHash);
var masterWeightCalc = masterWeight.GetWeight(umaDna);
for (int i = 0; i < _blendshapeDNAConverters.Count; i++)
{
_blendshapeDNAConverters[i].ApplyDNA(umaData, skeleton, umaDna, masterWeightCalc);
}
}
#endregion
#region INSPECTOR GUI OVERRIDES
#if UNITY_EDITOR
public override string[] ImportSettingsMethods
{
get
{
return new string[]
{
"Add",
"Replace"
};
}
}
public override bool ImportSettings(UnityEngine.Object pluginToImport, int importMethod)
{
//TODO Deal with Morphset?
var importPlug = pluginToImport as BlendshapeDNAConverterPlugin;
if (importPlug == null)
{
Debug.LogWarning("The plugin you are trying to import was not a PoseDNAConverterPlugin!");
return false;
}
if (importPlug._blendshapeDNAConverters.Count == 0)
{
Debug.LogWarning("The plugin you are trying to import had no settings!");
return false;
}
//Method Replace
if (importMethod == 1)
{
_blendshapeDNAConverters.Clear();
}
for (int i = 0; i < importPlug._blendshapeDNAConverters.Count; i++)
{
_blendshapeDNAConverters.Add(new BlendshapeDNAConverter(importPlug._blendshapeDNAConverters[i]));
}
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
return true;
}
#endif
#endregion
#region SPECIAL TYPES
[System.Serializable]
public class BlendshapeDNAConverter
{
#region FIELDS
[Tooltip("The Blendshape to apply to the character.")]
[SerializeField]
private string _blendshapeToApply;
[SerializeField]
[Tooltip("Make the default weight 1 to apply the blendshape on start to *all* characters that use this converter or set to 0 so the shape is only applied by 'Modifying DNA' below. If you want to affect this 'per character' use 'Modifying DNA' instead")]
[Range(0f, 1f)]
private float _startingShapeWeight = 0f;
[SerializeField]
[Tooltip("Add dna(s) here that will change the amount that this blendshape is applied depending on their evaluated value.")]
private DNAEvaluatorList _modifyingDNA = new DNAEvaluatorList();
#endregion
#region PRIVATE VARS
private float _liveShapeWeight = 0f;
private DynamicUMADnaBase _activeDNA;
#endregion
#region PUBLIC PROPERTIES
public string blendshapeToApply
{
get { return _blendshapeToApply; }
set { _blendshapeToApply = value; }
}
public float startingShapeWeight
{
get { return _startingShapeWeight; }
set { _startingShapeWeight = value; }
}
public DNAEvaluatorList modifyingDNA
{
get { return _modifyingDNA; }
set { _modifyingDNA = new DNAEvaluatorList(value); }
}
//TODO Timeline properties for screwing with the modifying dnas?
public List<string> UsedDNANames
{
get
{
//Do we include masterDNA in this?
//It will make a dna entry where every entry in any plugin that uses the name shows up
//could be more annoying/confusing than useful
return _modifyingDNA.UsedDNANames;
}
}
#endregion
#region CONSTRUCTOR
public BlendshapeDNAConverter() { }
public BlendshapeDNAConverter(string blendshapeToApply, float startingShapeWeight, DNAEvaluatorList modifyingDnas)
{
this._blendshapeToApply = blendshapeToApply;
this._startingShapeWeight = startingShapeWeight;
if(modifyingDnas != null)
{
this._modifyingDNA = new DNAEvaluatorList(modifyingDnas);
}
}
public BlendshapeDNAConverter(string shapeToApply, float startingShapeWeight = 0f, List<DNAEvaluator> modifyingDnas = null)
{
this._blendshapeToApply = shapeToApply;
this._startingShapeWeight = startingShapeWeight;
if(modifyingDnas != null)
{
this._modifyingDNA = new DNAEvaluatorList(modifyingDnas);
}
}
public BlendshapeDNAConverter(BlendshapeDNAConverter other)
{
this._blendshapeToApply = other._blendshapeToApply;
this._startingShapeWeight = other._startingShapeWeight;
this._modifyingDNA = new DNAEvaluatorList(other._modifyingDNA);
}
#endregion
#region METHODS
//TODO methods for screwing with the reducer dnas? Better if we can use fancy properties with indexers
public void ApplyDNA(UMAData umaData, UMASkeleton skeleton, UMADnaBase activeDNA, float masterWeight = 1f)
{
_liveShapeWeight = _startingShapeWeight;
//dna weight superceeds startingWeight if it exists
if (_modifyingDNA.UsedDNANames.Count > 0)
{
_liveShapeWeight = _modifyingDNA.Evaluate(activeDNA);
}
_liveShapeWeight = _liveShapeWeight * masterWeight;
//In Unity 2018.3+ blendshapes can have negative values too, so in that case allow the value to go to -1
#if !UNITY_2018_3_OR_NEWER
_liveShapeWeight = Mathf.Clamp(_liveShapeWeight, 0f, 1f);
#endif
umaData.SetBlendShape(_blendshapeToApply, _liveShapeWeight);
}
public void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash, float masterWeight = 1f)
{
_liveShapeWeight = _startingShapeWeight;
//dna weight superceeds startingWeight if it exists
if (_modifyingDNA.UsedDNANames.Count > 0)
{
_activeDNA = (DynamicUMADnaBase)umaData.GetDna(dnaTypeHash);
_liveShapeWeight = _modifyingDNA.Evaluate(_activeDNA);
}
_liveShapeWeight = _liveShapeWeight * masterWeight;
_liveShapeWeight = Mathf.Clamp(_liveShapeWeight, 0f, 1f);
umaData.SetBlendShape(_blendshapeToApply, _liveShapeWeight);
}
#endregion
}
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 00fb5c7be33cb104bb2a880b52b840d3
timeCreated: 1538887663
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/Scripts/DNAPlugins/BlendshapeDNAConverterPlugin.cs
uploadId: 679826
@@ -0,0 +1,361 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UMA.PoseTools;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UMA
{
[System.Serializable]
public class BonePoseDNAConverterPlugin : DynamicDNAPlugin
{
[SerializeField]
private List<BonePoseDNAConverter> _poseDNAConverters = new List<BonePoseDNAConverter>();
public List<BonePoseDNAConverter> poseDNAConverters
{
get { return _poseDNAConverters; }
set { _poseDNAConverters = value; }
}
#region BACKWARDS COMPATIBILITY
//the following are backwards compatible methods for DynamicDNAConverterBehaviour.StartingPose
//TODO can we make this work again?
//backwards compatibility
public UMABonePose StartingPose
{
get {
/*if(_poseDNAConverters.Count > 0)
{
for(int i = 0; i < _poseDNAConverters.Count; i++)
{
if (_poseDNAConverters[i].poseToApply != null && (_poseDNAConverters[i].startingPoseWeight.defaultWeight == 1 > 0f)
return _poseDNAConverters[i].poseToApply;
}
}*/
return null;
}
//this is not bulletproof but it will be obsolete anyway
set {
/*if(_poseDNAConverters.Count > 0)
{
int foundIndex = -1;
for(int i = 0; i < _poseDNAConverters.Count; i++)
{
if (_poseDNAConverters[i].poseToApply == value)
{
foundIndex = i;
break;
}
}
if(foundIndex >= 0 && foundIndex != 0)
{
//move the existing entry to 0
var current = new BonePoseDNAConverter(_poseDNAConverters[foundIndex]);
_poseDNAConverters.RemoveAt(foundIndex);
_poseDNAConverters.Insert(0, current);
return;
}
}
_poseDNAConverters.Insert(0, new BonePoseDNAConverter(value, 1f));*/
}
}
//backwards compatibility
public float StartingPoseWeight
{
get
{
/*if (_poseDNAConverters.Count > 0)
{
for (int i = 0; i < _poseDNAConverters.Count; i++)
{
if (_poseDNAConverters[i].poseToApply != null && _poseDNAConverters[i].defaultPoseWeight > 0f)
return _poseDNAConverters[i].defaultPoseWeight;
}
}*/
return 0f;
}
set {
/*if (_poseDNAConverters.Count > 0)
{
_poseDNAConverters[0].defaultPoseWeight = value;
}*/
}
}
#endregion
#region DYNAMICDNAPLUGIN PROPERTIES
#if UNITY_EDITOR
public override string PluginHelp
{
get { return "Bone Pose DNA Converters convert the set dna names into weight settings for UMA Bone Pose that will be applied to a character. You can use the 'Starting Pose Weight' to force this pose on all characters that use this converter at the start. Or you can hook up to a modifying dna so that the pose is only applied based on a characters dna value."; }
}
#endif
#endregion
#region REQUIRED DYNAMICDNAPLUGIN METHODS
/// <summary>
/// Returns a dictionary of all the dna names in use by the plugin and the entries in its converter list that reference them
/// </summary>
/// <returns></returns>
public override Dictionary<string, List<int>> IndexesForDnaNames
{
get
{
var dict = new Dictionary<string, List<int>>();
for (int i = 0; i < _poseDNAConverters.Count; i++)
{
for (int ci = 0; ci < _poseDNAConverters[i].UsedDNANames.Count; ci++)
{
if (!dict.ContainsKey(_poseDNAConverters[i].UsedDNANames[ci]))
{
dict.Add(_poseDNAConverters[i].UsedDNANames[ci], new List<int>());
}
dict[_poseDNAConverters[i].UsedDNANames[ci]].Add(i);
}
}
return dict;
}
}
/// <summary>
/// Apply the boneposes according to the given dna (determined by the dnaTypeHash)
/// </summary>
/// <param name="umaData"></param>
/// <param name="skeleton"></param>
/// <param name="dnaTypeHash"></param>
public override void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash)
{
var umaDna = umaData.GetDna(dnaTypeHash);
var masterWeightCalc = masterWeight.GetWeight(umaDna);
for (int i = 0; i < _poseDNAConverters.Count; i++)
{
_poseDNAConverters[i].ApplyDNA(umaData, skeleton, dnaTypeHash, masterWeightCalc);
}
}
#endregion
#region INSPECTOR GUI OVERRIDES
#if UNITY_EDITOR
public override string[] ImportSettingsMethods
{
get
{
return new string[]
{
"Add",
"Replace"
};
}
}
public override GUIContent GetPluginEntryLabel(SerializedProperty entry, SerializedObject pluginSO, int entryIndex)
{
if (_poseDNAConverters[entryIndex].poseToApply != null)
{
return new GUIContent(_poseDNAConverters[entryIndex].poseToApply.name);
}
return base.GetPluginEntryLabel(entry, pluginSO, entryIndex);
}
public override bool ImportSettings(UnityEngine.Object pluginToImport, int importMethod)
{
//TODO Deal with Morphset?
var importPlug = pluginToImport as BonePoseDNAConverterPlugin;
if (importPlug == null)
{
Debug.LogWarning("The plugin you are trying to import was not a PoseDNAConverterPlugin!");
return false;
}
if (importPlug._poseDNAConverters.Count == 0)
{
Debug.LogWarning("The plugin you are trying to import had no settings!");
return false;
}
//Method Replace
if (importMethod == 1)
{
_poseDNAConverters.Clear();
}
for (int i = 0; i < importPlug._poseDNAConverters.Count; i++)
{
_poseDNAConverters.Add(new BonePoseDNAConverter(importPlug._poseDNAConverters[i]));
}
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
return true;
}
#endif
#endregion
#region SPECIAL TYPES
[System.Serializable]
public class BonePoseDNAConverter
{
#region FIELDS
[Tooltip("The UMABonePose to apply to the character. This will effectively 'morph' the character into a different shape (using bone deformation so clothes will still fit).")]
[SerializeField]
private UMABonePose _poseToApply;
[SerializeField]
[Tooltip("Make the default weight 1 to apply the pose on start to *all* characters that use this converter or set to 0 so the pose is only applied by 'Modifying DNA' below. If you want to affect this 'per character' use 'Modifying DNA' instead")]
[Range(0f, 1f)]
private float _startingPoseWeight = 0f;
[SerializeField]
[Tooltip("Add dna(s) here that will change the amount that this Pose is applied depending on their evaluated value.")]
private DNAEvaluatorList _modifyingDNA = new DNAEvaluatorList();
#endregion
#region PRIVATE VARS
private float _livePoseWeight = 0f;
//private int _dnaIndex = -1;
private DynamicUMADnaBase _activeDNA;
#endregion
#region PUBLIC PROPERTIES
public UMABonePose poseToApply
{
get { return _poseToApply; }
set { _poseToApply = value; }
}
public float startingPoseWeight
{
get { return _startingPoseWeight; }
set { _startingPoseWeight = value; }
}
public DNAEvaluatorList modifyingDNA
{
get { return _modifyingDNA; }
set { _modifyingDNA = new DNAEvaluatorList(value); }
}
//TODO Timeline properties for screwing with the modifying dnas?
public List<string> UsedDNANames
{
get
{
//Do we include masterDNA in this?
//It will make a dna entry where every entry in any plugin that uses the name shows up
//could be more annoying/confusing than useful
return _modifyingDNA.UsedDNANames;
}
}
#endregion
#region CONSTRUCTOR
public BonePoseDNAConverter() { }
public BonePoseDNAConverter(UMABonePose poseToApply, float startingPoseWeight, DNAEvaluatorList modifyingDnas)
{
this._poseToApply = poseToApply;
this._startingPoseWeight = startingPoseWeight;
if (modifyingDnas != null)
{
this._modifyingDNA = new DNAEvaluatorList(modifyingDnas);
}
}
public BonePoseDNAConverter(UMABonePose poseToApply, float startingPoseWeight = 0f, List<DNAEvaluator> modifyingDnas = null)
{
this._poseToApply = poseToApply;
this._startingPoseWeight = startingPoseWeight;
if(modifyingDnas != null)
{
this._modifyingDNA = new DNAEvaluatorList(modifyingDnas);
}
}
public BonePoseDNAConverter(BonePoseDNAConverter other)
{
this._poseToApply = other._poseToApply;
this.startingPoseWeight = other._startingPoseWeight;
this._modifyingDNA = new DNAEvaluatorList(other._modifyingDNA);
}
#endregion
#region METHODS
public void ApplyDNA(UMAData umaData, UMASkeleton skeleton, UMADnaBase activeDNA, float masterWeight = 1f)
{
if (_poseToApply == null)
{
if (Debug.isDebugBuild)
{
Debug.LogWarning(umaData.gameObject.name + " had an invalid or empty pose set in its BonePoseDNAConverters in its DNAConverterController");
}
return;
}
_livePoseWeight = _startingPoseWeight;
//dna weight superceeds startingWeight if it exists
if (_modifyingDNA.UsedDNANames.Count > 0)
{
_livePoseWeight = _modifyingDNA.Evaluate(activeDNA);
}
_livePoseWeight = _livePoseWeight * masterWeight;
_livePoseWeight = Mathf.Clamp(_livePoseWeight, 0f, 1f);
_poseToApply.ApplyPose(skeleton, _livePoseWeight);
}
public void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash, float masterWeight = 1f)
{
if (_poseToApply == null)
{
if (Debug.isDebugBuild)
{
Debug.LogWarning(umaData.gameObject.name + " had an invalid or empty pose set in its BonePoseDNAConverters in its DNAConverterController");
}
return;
}
_livePoseWeight = _startingPoseWeight;
//dna weight superceeds startingWeight if it exists
if (_modifyingDNA.UsedDNANames.Count > 0)
{
_activeDNA = (DynamicUMADnaBase)umaData.GetDna(dnaTypeHash);
_livePoseWeight = _modifyingDNA.Evaluate(_activeDNA);
}
_livePoseWeight = _livePoseWeight * masterWeight;
_livePoseWeight = Mathf.Clamp(_livePoseWeight, 0f, 1f);
_poseToApply.ApplyPose(skeleton, _livePoseWeight);
}
#endregion
}
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: c3ac2aa42c9f4de41a32fbd9228196b5
timeCreated: 1537475097
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/Scripts/DNAPlugins/BonePoseDNAConverterPlugin.cs
uploadId: 679826
@@ -0,0 +1,517 @@
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.Serialization;
using AdjustmentType = UMA.OverlayData.ColorComponentAdjuster.AdjustmentType;
namespace UMA
{
public class ColorDNAConverterPlugin : DynamicDNAPlugin
{
#region FIELDS
[FormerlySerializedAs("colorSets")]
[SerializeField]
private DNAColorSet[] _colorSets = new DNAColorSet[0];
#endregion
#region PRIVATE FIELDS
//has dna been applied this cycle
[System.NonSerialized]
private List<GameObject> _dnaAppliedTo = new List<GameObject>();
//have we added the extra listeners required by ColorDNA?
//private lists in ScriptableObjects seem to need to be explicitly set as non serialized for some reason
[System.NonSerialized]
private List<GameObject> _listenersAddedTo = new List<GameObject>();
#endregion
#region PUBLIC PROPERTIES
public DNAColorSet[] colorSets
{
get { return _colorSets; }
set { _colorSets = value; }
}
public override ApplyPassOpts ApplyPass
{
get
{
return ApplyPassOpts.PrePass;
}
}
public override Dictionary<string, List<int>> IndexesForDnaNames
{
get
{
var dict = new Dictionary<string, List<int>>();
for (int i = 0; i < _colorSets.Length; i++)
{
for (int ci = 0; ci < _colorSets[i].UsedDNANames.Count; ci++)
{
if (!dict.ContainsKey(_colorSets[i].UsedDNANames[ci]))
{
dict.Add(_colorSets[i].UsedDNANames[ci], new List<int>());
}
dict[_colorSets[i].UsedDNANames[ci]].Add(i);
}
}
return dict;
}
}
#endregion
#region PRIVATE METHODS
public void ResetOnCharaterUpdated(UMAData umaData)
{
_dnaAppliedTo.Remove(umaData.gameObject);
}
#endregion
#region REQUIRED DYNAMICDNAPLUGIN METHODS
public override void Reset()
{
_dnaAppliedTo.Clear();
base.Reset();
}
public override void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash)
{
//Add the reset listeners if we havent already
//we need this because if 'fastGeneration' is false we may still get another loop
//and we should not do this again if _dnaAppliedTo contains umaData.gameObject
if (!_listenersAddedTo.Contains(umaData.gameObject))
{
umaData.CharacterUpdated.AddListener(ResetOnCharaterUpdated);
_listenersAddedTo.Add(umaData.gameObject);
}
if(_dnaAppliedTo.Contains(umaData.gameObject))
{
return;
}
UMADnaBase activeDNA = umaData.GetDna(dnaTypeHash);
if (activeDNA == null)
{
Debug.LogError("Could not get DNA values for: " + this.name);
return;
}
var masterWeightCalc = masterWeight.GetWeight(activeDNA);
if (masterWeightCalc == 0f)
{
return;
}
bool needsUpdate = false;
for(int i = 0; i < _colorSets.Length; i++)
{
if (_colorSets[i].modifyingDNA.UsedDNANames.Count == 0 || string.IsNullOrEmpty(_colorSets[i].targetName))
{
continue;
}
var targetOverlays = new List<OverlayData>();
for (int si = 0; si < umaData.umaRecipe.slotDataList.Length; si++)
{
var overlays = umaData.umaRecipe.slotDataList[si].GetOverlayList();
for (int oi = 0; oi < overlays.Count; oi++)
{
if (overlays[oi] != null)
{
//we can target specific Overlays or SharedColors now
if ((overlays[oi].colorData.IsASharedColor && overlays[oi].colorData.name == _colorSets[i].targetName) || overlays[oi].overlayName == _colorSets[i].targetName)
{
if(!targetOverlays.Contains(overlays[oi]))
{
targetOverlays.Add(overlays[oi]);
}
}
}
}
}
if (targetOverlays.Count == 0)
{
continue;
}
if (_colorSets[i].EvaluateAndApplyAdjustments(activeDNA, masterWeightCalc, targetOverlays))
{
needsUpdate = true;
}
}
if (needsUpdate)
{
umaData.isTextureDirty = true;
//pretty sure this doesn't affect the atlas
//umaData.isAtlasDirty = true;
}
_dnaAppliedTo.Add(umaData.gameObject);
}
#endregion
#region DYNAMICDNAPLUGIN EDITOR OVERRIDES
#if UNITY_EDITOR
//this could be runtime in DynamicDNAPlugin if it was ever needed
public override bool ImportSettings(Object pluginToImport, int importMethod)
{
if (pluginToImport.GetType() == typeof(ColorDNAConverterPlugin))
{
List<DNAColorSet> thisColorSets = importMethod == 0 ? new List<DNAColorSet>(_colorSets) : new List<DNAColorSet>();
for (int i = 0; i < ((ColorDNAConverterPlugin)pluginToImport)._colorSets.Length; i++)
{
thisColorSets.Add(new DNAColorSet(((ColorDNAConverterPlugin)pluginToImport)._colorSets[i]));
}
_colorSets = thisColorSets.ToArray();
return true;
}
return false;
}
public override GUIContent GetPluginEntryLabel(SerializedProperty entry, SerializedObject pluginSO, int entryIndex)
{
if (entry != null)
{
List<string> usedColorProps = new List<string>();
if (entry.FindPropertyRelative("colorModifier").FindPropertyRelative("R").FindPropertyRelative("enable").boolValue == true)
{
usedColorProps.Add("R");
}
if (entry.FindPropertyRelative("colorModifier").FindPropertyRelative("G").FindPropertyRelative("enable").boolValue == true)
{
usedColorProps.Add("G");
}
if (entry.FindPropertyRelative("colorModifier").FindPropertyRelative("B").FindPropertyRelative("enable").boolValue == true)
{
usedColorProps.Add("B");
}
if (entry.FindPropertyRelative("colorModifier").FindPropertyRelative("A").FindPropertyRelative("enable").boolValue == true)
{
usedColorProps.Add("A");
}
var usedColorComponents = string.Join(", ", usedColorProps.ToArray());
usedColorComponents = string.IsNullOrEmpty(usedColorComponents) ? "" : "Components: [" + usedColorComponents + "]";
return new GUIContent("("+ entry.FindPropertyRelative("mode").enumNames[entry.FindPropertyRelative("mode").enumValueIndex] + ") "+ entry.FindPropertyRelative("targetName").stringValue + " - Texture: [" + entry.FindPropertyRelative("textureChannel").intValue + "] "+ usedColorComponents);
}
return GUIContent.none;
}
public override string PluginHelp
{
get
{
return "ColorDNA Converters convert DNA values into color changes on an overlay texture. You can define which texture on the overlay you wish to affect to achieve things like changing the diffuse color, fading normal maps in and out, making a character more or less metallic and so forth. The changes do not change 'Shared Colors' but are applied to them at the dna stage.";
}
}
#endif
#endregion
#region SPECIAL TYPES
[System.Serializable]
public class DNAColorSet
{
public enum Mode
{
Overlay,
SharedColor
}
[Tooltip("A Color DNA Converter can target a specific overlay or a sharedColor")]
public Mode mode = Mode.Overlay;
[Tooltip("The name of the overlay or shared color to target")]
[FormerlySerializedAs("overlayEntryName")]
public string targetName;
[Tooltip("Texture Channel: For example PBR, 0 = Albedo, 1 = Normal, 2 = Metallic")]
[FormerlySerializedAs("colorChannel")]
public int textureChannel = 0;
[Tooltip("Define the dna that influence these changes. Note: If no dna is defined nothing will happen!")]
public DNAEvaluatorList modifyingDNA = new DNAEvaluatorList();
[Tooltip("Define how you want to change the colors used on this overlay")]
public DNAColorModifier colorModifier = new DNAColorModifier();
public List<string> UsedDNANames
{
get
{
return modifyingDNA.UsedDNANames;
}
}
public DNAColorSet() { }
public DNAColorSet(DNAColorSet other)
{
mode = other.mode;
targetName = other.targetName;
textureChannel = other.textureChannel;
colorModifier = new DNAColorModifier(other.colorModifier);
modifyingDNA = new DNAEvaluatorList(other.modifyingDNA);
}
public bool EvaluateAndApplyAdjustments(UMADnaBase activeDNA, float masterWeight, List<OverlayData> targetOverlays)
{
var dnaVal = modifyingDNA.Evaluate(activeDNA);
float rAdj = 0f;
float gAdj = 0f;
float bAdj = 0f;
float aAdj = 0f;
float rCurr = 0f;
float gCurr = 0f;
float bCurr = 0f;
float aCurr = 0f;
OverlayColorData ocd;
//Color modifiers can be costly so only return true if anything actually changed
bool adjusted = false;
for (int oi = 0; oi < targetOverlays.Count; oi++)
{
ocd = targetOverlays[oi].colorData;
if (colorModifier.R.enable)
{
rCurr = colorModifier.R.Additive ? ocd.channelAdditiveMask[textureChannel].r : ocd.channelMask[textureChannel].r;
rAdj = colorModifier.R.EvaluateAdjustment(dnaVal, rCurr);
if (colorModifier.R.Absolute)
{
rAdj = Mathf.Lerp(rCurr, rAdj, masterWeight);
}
else
{
rAdj = rAdj * masterWeight;
}
if ((colorModifier.R.Absolute && rAdj != 0 && rAdj != rCurr) || (!colorModifier.R.Absolute && rAdj != rCurr))
{
targetOverlays[oi].colorComponentAdjusters.Add(new OverlayData.ColorComponentAdjuster(textureChannel, 0, rAdj, colorModifier.R.adjustmentType));
adjusted = true;
}
}
if (colorModifier.G.enable)
{
gCurr = colorModifier.G.Additive ? ocd.channelAdditiveMask[textureChannel].g : ocd.channelMask[textureChannel].g;
gAdj = colorModifier.G.EvaluateAdjustment(dnaVal, gCurr);
if (colorModifier.G.Absolute)
{
gAdj = Mathf.Lerp(gCurr, gAdj, masterWeight);
}
else
{
gAdj = gAdj * masterWeight;
}
if ((colorModifier.G.Absolute && gAdj != 0 && gAdj != gCurr) || (!colorModifier.G.Absolute && gAdj != gCurr))
{
targetOverlays[oi].colorComponentAdjusters.Add(new OverlayData.ColorComponentAdjuster(textureChannel, 1, gAdj, colorModifier.G.adjustmentType));
adjusted = true;
}
}
if (colorModifier.B.enable)
{
bCurr = colorModifier.B.Additive ? ocd.channelAdditiveMask[textureChannel].b : ocd.channelMask[textureChannel].b;
bAdj = colorModifier.B.EvaluateAdjustment(dnaVal, bCurr);
if (colorModifier.B.Absolute)
{
bAdj = Mathf.Lerp(bCurr, bAdj, masterWeight);
}
else
{
bAdj = bAdj * masterWeight;
}
if ((colorModifier.B.Absolute && bAdj != 0 && bAdj != bCurr) || (!colorModifier.B.Absolute && bAdj != bCurr))
{
targetOverlays[oi].colorComponentAdjusters.Add(new OverlayData.ColorComponentAdjuster(textureChannel, 2, bAdj, colorModifier.B.adjustmentType));
adjusted = true;
}
}
if (colorModifier.A.enable)
{
aCurr = colorModifier.A.Additive ? ocd.channelAdditiveMask[textureChannel].a : ocd.channelMask[textureChannel].a;
aAdj = colorModifier.A.EvaluateAdjustment(dnaVal, aCurr);
if (colorModifier.A.Absolute)
{
aAdj = Mathf.Lerp(aCurr, aAdj, masterWeight);
}
else if(colorModifier.A.adjustmentType == AdjustmentType.BlendFactor)//BlendFactor is different and only used on the alpha channel
{
aAdj = Mathf.Lerp(aCurr, aAdj * masterWeight, masterWeight);
}
else
{
aAdj = aAdj * masterWeight;
}
if ((colorModifier.A.Absolute && aAdj != 0 && aAdj != aCurr) || (!colorModifier.A.Absolute && aAdj != aCurr))
{
targetOverlays[oi].colorComponentAdjusters.Add(new OverlayData.ColorComponentAdjuster(textureChannel, 3, aAdj, colorModifier.A.adjustmentType));
adjusted = true;
}
}
}
return adjusted;
}
[System.Serializable]
public class DNAColorComponent
{
[Tooltip("Change this component of the color")]
public bool enable = true;
[Tooltip("If Absolute the setting overrides the value of the component of the color. If Adjust, the setting is added to the value of the component of the color. Use BlendFactor to completely fade a texture in and out")]
public AdjustmentType adjustmentType = AdjustmentType.Absolute;
[Tooltip("If true the evaluated DNA value will be used when setting the color value for this component of the color")]
public bool useDNAValue = false;
[Tooltip("The value for this component of the color")]
[Range(0f, 1f)]
public float value;
[Tooltip("The amount to adjust this component of the color by. This can be negative, for example value of -0.5 on the red component would turn an incoming color of (1,1,1,1) into (0.5f,1,1,1)")]
[Range(-1f, 1f)]
public float adjustValue;
[Tooltip("A multiplier to apply to the evaluated dnaValue. This allows you to use the same dna to affect components of the color by different amounts")]
public float multiplier = 1f;
public bool Additive
{
get { return adjustmentType == AdjustmentType.AbsoluteAdditive || adjustmentType == AdjustmentType.AdjustAdditive; }
}
public bool Absolute
{
get { return adjustmentType == AdjustmentType.Absolute || adjustmentType == AdjustmentType.AbsoluteAdditive; }
}
public DNAColorComponent() { }
public DNAColorComponent(DNAColorComponent other)
{
enable = other.enable;
adjustmentType = other.adjustmentType;
useDNAValue = other.useDNAValue;
value = other.value;
adjustValue = other.adjustValue;
multiplier = other.multiplier;
}
public float Evaluate(float dnaValue, float current)
{
if (!enable)
{
return current;
}
float newVal = current;
if (useDNAValue)
{
if (adjustmentType == AdjustmentType.Absolute || adjustmentType == AdjustmentType.AbsoluteAdditive)
{
newVal = dnaValue * multiplier;
}
else //AdjustmentType.Adjust
{
newVal = Mathf.Clamp(current + (dnaValue * multiplier), 0, 1f);
}
}
else
{
if (adjustmentType == AdjustmentType.Absolute || adjustmentType == AdjustmentType.AbsoluteAdditive)
{
newVal = value;
}
else //AdjustmentType.Adjust
{
newVal = Mathf.Clamp(current + adjustValue, 0, 1f);
}
}
return Mathf.Lerp(current, newVal, dnaValue);
}
public float EvaluateAdjustment(float dnaValue, float currentColor)
{
if (!enable)
{
return 0f;
}
if (useDNAValue)
{
if (Absolute)
{
return Mathf.Lerp(currentColor, Mathf.Clamp(dnaValue * multiplier, 0f, 1f), Mathf.Clamp(dnaValue, 0f, 1f));
}
else if (adjustmentType == AdjustmentType.BlendFactor)
{
return Mathf.Clamp(dnaValue * multiplier, 0f, 1f);
}
else
{
return Mathf.Lerp(0f, dnaValue * multiplier, Mathf.Abs(dnaValue));
}
}
else
{
if (Absolute)
{
return Mathf.Lerp(currentColor, value, Mathf.Clamp(dnaValue, 0f, 1f));
}
else if (adjustmentType == AdjustmentType.BlendFactor)
{
return Mathf.Lerp(0f, value, Mathf.Abs(dnaValue));
}
else
{
return Mathf.Lerp(0f, adjustValue, Mathf.Abs(dnaValue));
}
}
}
}
[System.Serializable]
public class DNAColorModifier
{
public DNAColorComponent R = new DNAColorComponent();
public DNAColorComponent G = new DNAColorComponent();
public DNAColorComponent B = new DNAColorComponent();
public DNAColorComponent A = new DNAColorComponent();
#pragma warning disable 0414
//used in the editor for the preview tools
[SerializeField]
private float _testDNAVal = 0f;
#pragma warning restore 0414
public DNAColorModifier() { }
public DNAColorModifier(DNAColorModifier other)
{
R = new DNAColorComponent(other.R);
G = new DNAColorComponent(other.G);
B = new DNAColorComponent(other.B);
A = new DNAColorComponent(other.A);
}
}
}
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 4952de3305cd7194bb51aafa1b220607
timeCreated: 1541029365
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/Scripts/DNAPlugins/ColorDNAConverterPlugin.cs
uploadId: 679826
@@ -0,0 +1,166 @@
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace UMA
{
[System.Serializable]
public class OverallScaleDNAConverterPlugin : DynamicDNAPlugin
{
#region FIELDS
[SerializeField]
private List<OverallScaleModifier> _overallScaleModifiers = new List<OverallScaleModifier>();
#endregion
#region PUBLIC PROPERTIES
public List<OverallScaleModifier> overallScaleModifiers
{
get { return _overallScaleModifiers; }
set { _overallScaleModifiers = value; }
}
#endregion
#region REQUIRED DYNAMICDNAPLUGIN METHODS PROPERTIES
/// <summary>
/// Returns a dictionary of all the dna names in use by the plugin and the entries in its converter list that reference them
/// </summary>
/// <returns></returns>
public override Dictionary<string, List<int>> IndexesForDnaNames
{
get
{
var dict = new Dictionary<string, List<int>>();
for (int i = 0; i < _overallScaleModifiers.Count; i++)
{
for (int ci = 0; ci < _overallScaleModifiers[i].UsedDNANames.Count; ci++)
{
if (!dict.ContainsKey(_overallScaleModifiers[i].UsedDNANames[ci]))
{
dict.Add(_overallScaleModifiers[i].UsedDNANames[ci], new List<int>());
}
dict[_overallScaleModifiers[i].UsedDNANames[ci]].Add(i);
}
}
return dict;
}
}
/// <summary>
/// Apply the overall scale modifications according to the given dna (determined by the dnaTypeHash)
/// </summary>
/// <param name="umaData"></param>
/// <param name="skeleton"></param>
/// <param name="dnaTypeHash"></param>
public override void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash)
{
if (this.converterController == null /*|| this.converterController.converterBehaviour == null*/ || _overallScaleModifiers.Count == 0)
{
return;
}
var umaDna = (DynamicUMADnaBase)umaData.GetDna(dnaTypeHash);
//master weight determines how much we modify the converters base scale to our new value, 1 its fully overridden, 0 its left as it is
var masterWeightCalc = masterWeight.GetWeight(umaDna);
if (masterWeightCalc == 0f)
{
return;
}
float baseScale = this.converterController.baseScale;
//Each modifier wants to change the base scale to its overall scale value depending on how stronly its dna(s) are applied
//so we need to accumulate the differences each one wants to make rather than the full value
float evaluatedScale = 0f;
float evaluatedDiff = 0f;
for (int i = 0; i < _overallScaleModifiers.Count; i++)
{
evaluatedDiff += ((_overallScaleModifiers[i].overallScale - baseScale) * _overallScaleModifiers[i].GetEvaluatedDNA(umaDna));
}
//add the combined differences to the base scale
evaluatedScale = baseScale + evaluatedDiff;
//lerp to that result based on the masterWeightCalc
float newScale = Mathf.Lerp(baseScale, evaluatedScale, masterWeightCalc);
this.converterController.liveScale = newScale;
}
#endregion
#region DYNAMICDNAPLUGIN EDITOR OVERRIDES
#if UNITY_EDITOR
public override string PluginHelp
{
get { return "Changes the overall scale value on this plugins converter behaviour based on dna. Each entry will be evaluated according to evaluated weight of its dna entry and the weigted avaerage result of all the entries will be sent to the converter behaviour to use for its 'overall scale' calculation"; }
}
public override void OnAddEntryCallback(SerializedObject pluginSO, int entryIndex)
{
var thismodifier = pluginSO.FindProperty("_overallScaleModifiers").GetArrayElementAtIndex(entryIndex);
if (thismodifier.FindPropertyRelative("_overallScale").floatValue == 0f)
{
thismodifier.FindPropertyRelative("_overallScale").floatValue = 0.88f;//the default value for humanMale as defined in OverallScaleModifier
}
}
#endif
#endregion
#region SPECIAL TYPES
[System.Serializable]
public class OverallScaleModifier
{
//Just to help with organising in the inspector
[SerializeField]
[Tooltip("This is just a label for helping organise entries in the UI")]
private string _label;
[SerializeField]
[Tooltip("If no modifying dna is specified below this scale will be fully applied to the character.")]
private float _overallScale = 0.88f;
[SerializeField]
[Tooltip("Modify how much the overallScale above is applied to the character based on dna value(s) you specify here")]
private DNAEvaluatorList _modifyingDNA = new DNAEvaluatorList();
public float overallScale
{
get { return _overallScale; }
}
public List<string> UsedDNANames
{
get
{
var usedNames = new List<string>();
for (int i = 0; i < _modifyingDNA.Count; i++)
{
if (!string.IsNullOrEmpty(_modifyingDNA[i].dnaName))
{
usedNames.Add(_modifyingDNA[i].dnaName);
}
}
return usedNames;
}
}
public float GetEvaluatedDNA(UMADnaBase umaDNA)
{
if (_modifyingDNA.Count > 0)
{
return _modifyingDNA.Evaluate(umaDNA);
}
return 1f;//if there is no modifying dna assume the overall scale is fully applied
}
}
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: d15b594bf85dc8e4fbf81314a1fc0702
timeCreated: 1540519219
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/Scripts/DNAPlugins/OverallScaleDNAConverterPlugin.cs
uploadId: 679826
@@ -0,0 +1,489 @@
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UMA.CharacterSystem;
namespace UMA
{
public class SkeletonDNAConverterPlugin : DynamicDNAPlugin
{
#region FIELDS
[SerializeField]
private List<SkeletonModifier> _skeletonModifiers = new List<SkeletonModifier>();
#endregion
#region PUBLIC PROPERTIES
public List<SkeletonModifier> skeletonModifiers
{
get { return _skeletonModifiers; }
set { _skeletonModifiers = value; }
}
#endregion
#region PUBLIC METHODS
public void AddModifier(SkeletonModifier modifier)
{
_skeletonModifiers.Add(modifier);
}
#endregion
#region REQUIRED DYNAMICDNAPLUGIN METHODS PROPERTIES
/// <summary>
/// Returns a dictionary of all the dna names in use by the plugin and the entries in its converter list that reference them
/// </summary>
/// <returns></returns>
public override Dictionary<string, List<int>> IndexesForDnaNames
{
get
{
var dict = new Dictionary<string, List<int>>();
for (int i = 0; i < _skeletonModifiers.Count; i++)
{
var skelModsUsedNames = SkeletonModifierUsedDNANames(_skeletonModifiers[i]);
for (int ci = 0; ci < skelModsUsedNames.Count; ci++)
{
if (!dict.ContainsKey(skelModsUsedNames[ci]))
{
dict.Add(skelModsUsedNames[ci], new List<int>());
}
dict[skelModsUsedNames[ci]].Add(i);
}
}
return dict;
}
}
/// <summary>
/// Apply the modifiers using the given dna (determined by the typehash)
/// </summary>
/// <param name="umaData"></param>
/// <param name="skeleton"></param>
/// <param name="dnaTypeHash"></param>
public override void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash)
{
var umaDna = umaData.GetDna(dnaTypeHash);
if (umaDna == null)
{
return;
}
var masterWeightCalc = masterWeight.GetWeight(umaDna);
if (masterWeightCalc == 0f)
{
return;
}
for (int i = 0; i < _skeletonModifiers.Count; i++)
{
_skeletonModifiers[i].umaDNA = umaDna;
var thisHash = (_skeletonModifiers[i].hash != 0) ? _skeletonModifiers[i].hash : UMAUtils.StringToHash(_skeletonModifiers[i].hashName);
//check skeleton has the bone we want to change
if (!skeleton.HasBone(thisHash))
{
//Debug.LogWarning("You were trying to apply skeleton modifications to a bone that didn't exist (" + _skeletonModifiers[i].hashName + ") on " + umaData.gameObject.name);
continue;
}
//With these ValueX.x is the calculated value and ValueX.y is min and ValueX.z is max
var thisValueX = _skeletonModifiers[i].CalculateValueX(umaDna);
var thisValueY = _skeletonModifiers[i].CalculateValueY(umaDna);
var thisValueZ = _skeletonModifiers[i].CalculateValueZ(umaDna);
if (_skeletonModifiers[i].property == SkeletonModifier.SkeletonPropType.Position)
{
skeleton.SetPositionRelative(thisHash,
new Vector3(
Mathf.Clamp(thisValueX.x, thisValueX.y, thisValueX.z),
Mathf.Clamp(thisValueY.x, thisValueY.y, thisValueY.z),
Mathf.Clamp(thisValueZ.x, thisValueZ.y, thisValueZ.z)), masterWeightCalc);
}
else if (_skeletonModifiers[i].property == SkeletonModifier.SkeletonPropType.Rotation)
{
skeleton.SetRotationRelative(thisHash,
Quaternion.Euler(new Vector3(
Mathf.Clamp(thisValueX.x, thisValueX.y, thisValueX.z),
Mathf.Clamp(thisValueY.x, thisValueY.y, thisValueY.z),
Mathf.Clamp(thisValueZ.x, thisValueZ.y, thisValueZ.z))), masterWeightCalc);
}
else if (_skeletonModifiers[i].property == SkeletonModifier.SkeletonPropType.Scale)
{
//If there are two sets of skeletonModifiers and both are at 50% it needs to apply them both but the result should be cumulative
//so we need to work out the difference this one is making, weight that and add it to the current scale of the bone
var scale = new Vector3(
Mathf.Clamp(thisValueX.x, thisValueX.y, thisValueX.z),
Mathf.Clamp(thisValueY.x, thisValueY.y, thisValueY.z),
Mathf.Clamp(thisValueZ.x, thisValueZ.y, thisValueZ.z));
//we cant use val.value here because the initial values always need to be applied
var defaultVal = SkeletonModifier.skelAddDefaults[SkeletonModifier.SkeletonPropType.Scale].x;
var scaleDiff = new Vector3(scale.x - defaultVal,
scale.y - defaultVal,
scale.z - defaultVal);
var weightedScaleDiff = scaleDiff * masterWeightCalc;
var fullScale = skeleton.GetScale(_skeletonModifiers[i].hash) + weightedScaleDiff;
skeleton.SetScale(thisHash, fullScale);
}
}
}
#endregion
#region DYNAMICDNAPLUGIN EDITOR OVERRIDES
#if UNITY_EDITOR
public override string PluginHelp
{
get { return "Skeleton DNA Converters use dna values to transform the bones in an avatars skeleton."; }
}
public override string[] ImportSettingsMethods
{
get
{
return new string[]
{
"Add",
"Replace",
"Overwrite",
"AddOverwrite"
};
}
}
public override float GetListFooterHeight
{
get
{
return 16f + (/*EditorGUIUtility.singleLineHeight +*/ (EditorGUIUtility.standardVerticalSpacing * 2));
}
}
#pragma warning disable 618 //disable obsolete warning
/// <summary>
/// Imports SkeletomModifiers from another object into this SkeletonModifiersDNAConverterPlugin
/// </summary>
/// <param name="pluginToImport">You can import another SkeletonModifiersDNAConverterPlugin or settings from a legacy DynamicDNAConverterBehaviour prefab</param>
/// <param name="importMethod">Use 0 to Add to the existing list, 1 to Replace the existing list, 2 to only Overwrite anything in the existing list with matching modifiers in the incoming list, or 3 to Overwrite any existing entries and then Add any entries that were not already in the existing list</param>
/// <returns>True if any settings were imported successfully, otherwise false</returns>
public override bool ImportSettings(Object pluginToImport, int importMethod)
{
List<SkeletonModifier> importedSkeletonModifiers = new List<SkeletonModifier>();
bool isLegacy = false;
if (pluginToImport.GetType() == this.GetType())
{
importedSkeletonModifiers = (pluginToImport as SkeletonDNAConverterPlugin)._skeletonModifiers;
}
else if(pluginToImport.GetType().IsAssignableFrom(typeof(DynamicDNAConverterController)))
{
var skelModPlugs = (pluginToImport as DynamicDNAConverterController).GetPlugins(typeof(SkeletonDNAConverterPlugin));
if(skelModPlugs.Count > 0)
{
importedSkeletonModifiers = (skelModPlugs[0] as SkeletonDNAConverterPlugin)._skeletonModifiers;
}
}
if(importedSkeletonModifiers != null)
{
// add the modifiers- if the import method is Replace this is a new list
var currentModifiers = importMethod == 1 ? new List<SkeletonModifier>() : _skeletonModifiers;
var incomingModifiers = importedSkeletonModifiers;
List<string> existingDNANames = new List<string>();
if (DNAAsset != null)
{
existingDNANames.AddRange(DNAAsset.Names);
}
List<string> missingDNANames = new List<string>();
//If any dnanames are misisng give the user the option to only overwrite matching dna names
//or add the missing dna names and continue
//or cancel
//string nameToCheck = "";
bool existed = false;
//if the names in the dnaAsset get changed during this process we need to save that too
bool updateDNAAsset = false;
for (int i = 0; i < incomingModifiers.Count; i++)
{
//if the Add method is OverwriteAndAdd we need to check dnanames in all the incoming converters
//otherwise we only need to check names for incoming converters that have a matching one in the current list
//was Add = 0 Replace = 1 Overwrite = 2 AddOverwrite = 3
if (importMethod == 2)
{
existed = false;
for (int ci = 0; ci < currentModifiers.Count; ci++)
{
if ((currentModifiers[ci].hash == incomingModifiers[i].hash) && currentModifiers[ci].property == incomingModifiers[i].property)
{
existed = true;
break;
}
}
if (!existed)
{
continue;
}
}
var usedDNANames = SkeletonModifierUsedDNANames(incomingModifiers[i], isLegacy);
for(int nc = 0; nc < usedDNANames.Count; nc++)
{
if (!existingDNANames.Contains(usedDNANames[nc]) && !missingDNANames.Contains(usedDNANames[nc]))
{
missingDNANames.Add(usedDNANames[nc]);
}
}
}
if (missingDNANames.Count > 0 && DNAAsset != null)
{
string missingDNAMsg = "";
if (missingDNANames.Count > 10)
{
missingDNAMsg = "There were over 10 missing dna names in this converter compared to the one you want to overwrite from";
}
else
{
missingDNAMsg = "The following dna names were missing in this converter compared to the one you want to overwrite from: ";
missingDNAMsg += string.Join(", ", missingDNANames.ToArray());
}
missingDNAMsg += ". Please choose how you would like to proceed.";
//options: "Only Overwrite Existing DNA" "Add Missing DNA" "Cancel"
var missingDNAOption = EditorUtility.DisplayDialogComplex("Missing DNA in Current Converter", missingDNAMsg, "Only Overwrite Existing DNA", "Add Missing DNA", "Cancel");
if (missingDNAOption == 2)
{
return false;
}
else if (missingDNAOption == 1)
{
//add the missing names
var assetNames = new List<string>(DNAAsset.Names);
assetNames.AddRange(missingDNANames);
DNAAsset.Names = assetNames.ToArray();
existingDNANames.AddRange(missingDNANames);
updateDNAAsset = true;
}
//now we just add any settings for DNANames that exist, since the ones we need will have either been added or the user knows they will be skipped
}
else if (DNAAsset == null)
{
//the inspector will sort this out later
}
//if the method is add or overwriteAdd we need to add any missing ones (if the method is replace the list will be empty so everything will get added here)
for (int i = 0; i < incomingModifiers.Count; i++)
{
existed = false;
for (int ci = 0; ci < currentModifiers.Count; ci++)
{
if ((currentModifiers[ci].hash == incomingModifiers[i].hash) && currentModifiers[ci].property == incomingModifiers[i].property)
{
existed = true;
if (importMethod == 2 || importMethod == 3)
{
//handle the overwrites
currentModifiers[ci].valuesX.min = incomingModifiers[i].valuesX.min;
currentModifiers[ci].valuesX.max = incomingModifiers[i].valuesX.max;
currentModifiers[ci].valuesX.val.value = incomingModifiers[i].valuesX.val.value;
//now currentModifiers should only ever have modifyingDNA but incomingModifiers might contain data in legacy 'modifiers' OR in 'modifyingDNA'
if (isLegacy)
{
ProcessSkelModOverwrites(currentModifiers[ci].valuesX.val.modifyingDNA, incomingModifiers[i].valuesX.val.modifiers, existingDNANames);
}
else
{
ProcessSkelModOverwrites(currentModifiers[ci].valuesX.val.modifyingDNA, incomingModifiers[i].valuesX.val.modifyingDNA, existingDNANames);
}
currentModifiers[ci].valuesY.min = incomingModifiers[i].valuesY.min;
currentModifiers[ci].valuesY.max = incomingModifiers[i].valuesY.max;
currentModifiers[ci].valuesY.val.value = incomingModifiers[i].valuesY.val.value;
if (isLegacy)
{
ProcessSkelModOverwrites(currentModifiers[ci].valuesY.val.modifyingDNA, incomingModifiers[i].valuesY.val.modifiers, existingDNANames);
}
else
{
ProcessSkelModOverwrites(currentModifiers[ci].valuesY.val.modifyingDNA, incomingModifiers[i].valuesY.val.modifyingDNA, existingDNANames);
}
currentModifiers[ci].valuesZ.min = incomingModifiers[i].valuesZ.min;
currentModifiers[ci].valuesZ.max = incomingModifiers[i].valuesZ.max;
currentModifiers[ci].valuesZ.val.value = incomingModifiers[i].valuesZ.val.value;
if (isLegacy)
{
ProcessSkelModOverwrites(currentModifiers[ci].valuesZ.val.modifyingDNA, incomingModifiers[i].valuesZ.val.modifiers, existingDNANames);
}
else
{
ProcessSkelModOverwrites(currentModifiers[ci].valuesZ.val.modifyingDNA, incomingModifiers[i].valuesZ.val.modifyingDNA, existingDNANames);
}
}
break;
}
}
if (!existed && importMethod != 2)//if the method is anything other overwrite add the missing modifier
{
currentModifiers.Add(new SkeletonModifier(incomingModifiers[i], true));
}
}
_skeletonModifiers = currentModifiers;
EditorUtility.SetDirty(this);
if (updateDNAAsset)
{
EditorUtility.SetDirty(DNAAsset);
}
AssetDatabase.SaveAssets();
return true;
}
else
{
return false;
}
}
#pragma warning restore 618
#pragma warning disable 618 //disable obsolete warning
/// <summary>
/// Converts the legacy incoming values into DNAEvaluators and then overwrites any DNAEvaluators in the current settings with the settings from the incoming DNAEvaluators if the dnaName matches, otherwise adds a new evaluator for the dnaName
/// </summary>
/// <param name="existingDNANames">If the dnaName used by a DNAEvaluator in the incoming settings is not found in this list it will not be processed</param>
private void ProcessSkelModOverwrites(DNAEvaluatorList currentMods, List<SkeletonModifier.spVal.spValValue.spValModifier> incomingMods, List<string> existingDNANames)
{
SkeletonModifier.spVal.spValValue tempValValue = new SkeletonModifier.spVal.spValValue();
tempValValue.modifiers = new List<SkeletonModifier.spVal.spValValue.spValModifier>(incomingMods);
tempValValue.ConvertToDNAEvaluators();
ProcessSkelModOverwrites(currentMods, tempValValue.modifyingDNA, existingDNANames);
}
#pragma warning restore 618
/// <summary>
/// overwrites any DNAEvaluators in the current settings with the settings from the incoming DNAEvaluators if the dnaName matches, otherwise adds a new evaluator for the dnaName
/// </summary>
/// <param name="existingDNANames">If the dnaName used by a DNAEvaluator in the incoming settings is not found in this list it will not be processed</param>
private void ProcessSkelModOverwrites(DNAEvaluatorList currentMods, DNAEvaluatorList incomingMods, List<string> existingDNANames)
{
for (int i = 0; i < incomingMods.Count; i++)
{
if (!existingDNANames.Contains(incomingMods[i].dnaName))
{
continue;
}
var foundInCurrent = false;
for (int ci = 0; ci < currentMods.Count; ci++)
{
if (currentMods[ci].dnaName == incomingMods[i].dnaName)
{
currentMods[ci].calcOption = incomingMods[i].calcOption;
currentMods[ci].evaluator = new DNAEvaluationGraph(incomingMods[i].evaluator);
currentMods[ci].multiplier = incomingMods[i].multiplier;
foundInCurrent = true;
}
}
if (!foundInCurrent)
{
currentMods.Add(new DNAEvaluator(incomingMods[i].dnaName, incomingMods[i].evaluator, incomingMods[i].multiplier, incomingMods[i].calcOption));
}
}
}
#endif
#endregion
#region PRIVATE METHODS
#pragma warning disable 618 //disable obsolete warning
/// <summary>
/// Returns the DNANames used by the given skeleton modifier,
/// optionally filtering by a given name, in which case the returned list count will only be greater than zero if the modifier used the name.
/// This can be used to query if any modifiers were using the given name
/// </summary>
/// <returns></returns>
private List<string> SkeletonModifierUsedDNANames(SkeletonModifier skeletonModifier, bool searchLegacy = false, string dnaName = "")
{
List<string> usedNames = new List<string>();
//names from new _modifyingDNA in the modifiers
var xNames = skeletonModifier.valuesX.val.modifyingDNA.UsedDNANames;
var yNames = skeletonModifier.valuesY.val.modifyingDNA.UsedDNANames;
var zNames = skeletonModifier.valuesZ.val.modifyingDNA.UsedDNANames;
for (int i = 0; i < xNames.Count; i++)
{
if (!usedNames.Contains(xNames[i]) && (dnaName == "" || (!string.IsNullOrEmpty(dnaName) && xNames[i] == dnaName)))
{
usedNames.Add(xNames[i]);
}
}
for (int i = 0; i < yNames.Count; i++)
{
if (!usedNames.Contains(yNames[i]) && (dnaName == "" || (!string.IsNullOrEmpty(dnaName) && yNames[i] == dnaName)))
{
usedNames.Add(yNames[i]);
}
}
for (int i = 0; i < zNames.Count; i++)
{
if (!usedNames.Contains(zNames[i]) && (dnaName == "" || (!string.IsNullOrEmpty(dnaName) && zNames[i] == dnaName)))
{
usedNames.Add(zNames[i]);
}
}
if (searchLegacy)
{
//legacy names
for (int xi = 0; xi < skeletonModifier.valuesX.val.modifiers.Count; xi++)
{
if (!string.IsNullOrEmpty(skeletonModifier.valuesX.val.modifiers[xi].DNATypeName) &&
dnaName == "" || (!string.IsNullOrEmpty(dnaName) && skeletonModifier.valuesX.val.modifiers[xi].DNATypeName == dnaName))
{
if (!usedNames.Contains(skeletonModifier.valuesX.val.modifiers[xi].DNATypeName))
{
usedNames.Add(skeletonModifier.valuesX.val.modifiers[xi].DNATypeName);
}
}
}
for (int yi = 0; yi < skeletonModifier.valuesY.val.modifiers.Count; yi++)
{
if (!string.IsNullOrEmpty(skeletonModifier.valuesY.val.modifiers[yi].DNATypeName) &&
dnaName == "" || (!string.IsNullOrEmpty(dnaName) && skeletonModifier.valuesY.val.modifiers[yi].DNATypeName == dnaName))
{
if (!usedNames.Contains(skeletonModifier.valuesY.val.modifiers[yi].DNATypeName))
{
usedNames.Add(skeletonModifier.valuesY.val.modifiers[yi].DNATypeName);
}
}
}
for (int zi = 0; zi < skeletonModifier.valuesZ.val.modifiers.Count; zi++)
{
if (!string.IsNullOrEmpty(skeletonModifier.valuesZ.val.modifiers[zi].DNATypeName) &&
dnaName == "" || (!string.IsNullOrEmpty(dnaName) && skeletonModifier.valuesZ.val.modifiers[zi].DNATypeName == dnaName))
{
if (!usedNames.Contains(skeletonModifier.valuesZ.val.modifiers[zi].DNATypeName))
{
usedNames.Add(skeletonModifier.valuesZ.val.modifiers[zi].DNATypeName);
}
}
}
}
return usedNames;
}
#pragma warning restore 618 //restore obsolete warning
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 0bb2689319b4d5a418829cf43a059e3a
timeCreated: 1539127269
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/Scripts/DNAPlugins/SkeletonDNAConverterPlugin.cs
uploadId: 679826
+68
View File
@@ -0,0 +1,68 @@
namespace UMA
{
#region DNASETTER
/// <summary>
/// A DnaSetter is used to set a specific piece of DNA on the avatar
/// that it is pulled from.
/// </summary>
public class DnaSetter
{
public string Name; // The name of the DNA.
public float Value; // Current value of the DNA.
public string Category;
public int OwnerIndex
{
// position of DNA in index, created at initialization
get;
private set;
}
protected UMADnaBase Owner; // owning DNA class. Used to set the DNA by index
/// <summary>
/// Construct a DnaSetter
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="ownerIndex"></param>
/// <param name="owner"></param>
/// <param name="category"></param>
public DnaSetter(string name, float value, int ownerIndex, UMADnaBase owner, string category)
{
Name = name;
Value = value;
OwnerIndex = ownerIndex;
Owner = owner;
Category = category;
}
/// <summary>
/// Set the current DNA value. You will need to rebuild the character to see
/// the results change.
/// </summary>
public void Set(float val)
{
Value = val;
Owner.SetValue(OwnerIndex, val);
}
/// <summary>
/// Set the current DNA value. You will need to rebuild the character to see
/// the results change.
/// </summary>
public void Set()
{
Owner.SetValue(OwnerIndex, Value);
}
/// <summary>
/// Gets the current DNA value.
/// </summary>
public float Get()
{
return Owner.GetValue(OwnerIndex);
}
}
}
#endregion
+18
View File
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 46d21cdaeb46f974a8ff5efc9521f32c
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/Scripts/DNASetter.cs
uploadId: 679826
@@ -0,0 +1,657 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UMA
{
//The DynamicDNAConverterController manages the list Converters (aka DynamicDNAPlugins) the user has decided to use.
//It is a Scriptable Object, and as Converters are added to it, it creates instances of those and stores them inside itself
//this is so all the assets this needs are packaged up with it UMA3 style.
//This asset reploaces DynamicDNAConverterBehaviour and applies the converters to the avatar
[System.Serializable]
public class DynamicDNAConverterController : ScriptableObject, IDNAConverter, IDynamicDNAConverter
{
[SerializeField]
[Tooltip("A DNA Asset defines the names that will be available to the DNA Converters when modifying the Avatar. Often displayed in the UI as 'sliders'. Click the 'Inspect' button to view the assigned asset")]
private DynamicUMADnaAsset _dnaAsset;
/// <summary>
/// The List of all the plugins (converters) assigned to this ConverterController
/// </summary>
[SerializeField]
private List<DynamicDNAPlugin> _plugins = new List<DynamicDNAPlugin>();
[SerializeField]
[BaseCharacterModifier.Config(true)]//does this stop it drawing the foldout? if so we dont want that here
[Tooltip("Overall Modifiers apply to ALL characters that use this converter. You use this to make a Female race shorter than a Male race for example. They can change an entire races base scale, height and radius (used for fitting the collider), its mass, and update its bounds. Its elements can selectively be enabled and are calculated after all other DNA Converters have made changes to the avatar. Usually you only use these once per race, on the base 'Converter Controller' for the race.")]
private BaseCharacterModifier _overallModifiers = new BaseCharacterModifier();
/// <summary>
/// Contains a list of all the dna names used by all the plugins (converters) assigned to this ConverterController
/// </summary>
private List<string> _usedDNANames = new List<string>();
#pragma warning disable 649
//only set in the editor
[SerializeField]
[Tooltip("A 'nice name' to use when Categorizing DNASetters in the UI")]
private string _displayValue;
#pragma warning restore 649
[System.NonSerialized]
private List<DynamicDNAPlugin> _applyDNAPostpassPlugins = new List<DynamicDNAPlugin>();
[System.NonSerialized]
private List<DynamicDNAPlugin> _applyDNAPrepassPlugins = new List<DynamicDNAPlugin>();
[System.NonSerialized]
private List<DynamicDNAPlugin> _applyDNAPlugins = new List<DynamicDNAPlugin>();
[System.NonSerialized]
private bool _prepared = false;
private Dictionary<string, List<UnityAction<string, float>>> _dnaCallbackDelegates = new Dictionary<string, List<UnityAction<string, float>>>();
#region IDNAConverter IMPLIMENTATION
public string DisplayValue
{
get { return _displayValue; }
}
public System.Type DNAType
{
get { return typeof(DynamicUMADna); }
}
/// <summary>
/// Returns the dnaTypeHash from the assigned dnaAsset or 0 if no dnaAsset is set
/// </summary>
/// <returns></returns>
public int DNATypeHash
{
get
{
if (_dnaAsset != null)
{
return _dnaAsset.dnaTypeHash;
}
else
{
Debug.LogWarning(this.name + " did not have a DNA Asset assigned. This is required for DynamicDnaConverterControllers.");
}
return 0;
}
}
public DNAConvertDelegate PreApplyDnaAction
{
get { return ApplyDNAPrepass; }
}
public DNAConvertDelegate PostApplyDnaAction
{
get { return ApplyDNAPostpass; }
}
public DNAConvertDelegate ApplyDnaAction
{
get { return ApplyDNA; }
}
//Prepare should be here too
#endregion
#region IDynamicDNAConverter IMPLIMENTATION
public DynamicUMADnaAsset dnaAsset
{
get { return DNAAsset; }
}
#endregion
public DynamicUMADnaAsset DNAAsset
{
get
{
if (_dnaAsset != null)
{
return _dnaAsset;
}
else
{
return null;
}
}
set
{
_dnaAsset = value;
}
}
/// <summary>
/// Returns the number of plugins assigned to this ConverterController Asset
/// </summary>
public int PluginCount
{
get { return _plugins.Count; }
}
public BaseCharacterModifier overallModifiers
{
get { return _overallModifiers; }
}
/// <summary>
/// Changes the characters base scale at runtime. This is reset per character everyime dna is applied, so its not shared like everything else in overallModifiers is.
/// It should only be used by the 'OverallScaleDNAConverterPlugin' or similar
/// </summary>
public float liveScale
{
get { return _overallModifiers.liveScale; }
set { _overallModifiers.liveScale = value; }
}
/// <summary>
/// Gets the base scale as set in the 'overall modifiers' section of this converter
/// </summary>
public float baseScale
{
get { return _overallModifiers.scale; }
}
public void Prepare()
{
if (!_prepared)
{
for (int i = 0; i < _plugins.Count; i++)
{
if (_plugins[i].ApplyPass == DynamicDNAPlugin.ApplyPassOpts.Standard)
{
if (!_applyDNAPlugins.Contains(_plugins[i]))
{
_applyDNAPlugins.Add(_plugins[i]);
}
}
else if (_plugins[i].ApplyPass == DynamicDNAPlugin.ApplyPassOpts.PrePass)
{
if (!_applyDNAPrepassPlugins.Contains(_plugins[i]))
{
_applyDNAPrepassPlugins.Add(_plugins[i]);
}
}
else if (_plugins[i].ApplyPass == DynamicDNAPlugin.ApplyPassOpts.PostPass)
{
if (!_applyDNAPostpassPlugins.Contains(_plugins[i]))
{
_applyDNAPostpassPlugins.Add(_plugins[i]);
}
}
}
_prepared = true;
}
}
public bool AddDnaCallbackDelegate(UnityAction<string, float> callback, string targetDnaName)
{
bool added = false;
if (!_dnaCallbackDelegates.ContainsKey(targetDnaName))
{
_dnaCallbackDelegates.Add(targetDnaName, new List<UnityAction<string, float>>());
}
if (!_dnaCallbackDelegates[targetDnaName].Contains(callback))
{
_dnaCallbackDelegates[targetDnaName].Add(callback);
added = true;
}
return added;
}
public bool RemoveDnaCallbackDelegate(UnityAction<string, float> callback, string targetDnaName)
{
bool removed = false;
if (!_dnaCallbackDelegates.ContainsKey(targetDnaName))
{
removed = true;
}
else
{
if (_dnaCallbackDelegates[targetDnaName].Contains(callback))
{
_dnaCallbackDelegates[targetDnaName].Remove(callback);
removed = true;
}
if (_dnaCallbackDelegates[targetDnaName].Count == 0)
{
_dnaCallbackDelegates.Remove(targetDnaName);
}
}
return removed;
}
/// <summary>
/// Calls ApplyDNA on all this convertersControllers plugins (aka converters) that apply dna during the pre-pass
/// </summary>
/// <param name="umaData">The umaData on the avatar</param>
/// <param name="skeleton">The avatars skeleton</param>
/// <param name="dnaTypeHash">The dnaTypeHash that this converters behaviour is using</param>
public void ApplyDNAPrepass(UMAData umaData, UMASkeleton skeleton)
{
if (!_prepared)
{
Prepare();
}
UMADnaBase umaDna = umaData.GetDna(DNATypeHash);
//Make the DNAAssets match if they dont already, can happen when some parts are in bundles and others arent
if (umaDna is DynamicUMADnaBase)
{
if (((DynamicUMADnaBase)umaDna).dnaAsset != DNAAsset && DNAAsset != null)
{
((DynamicUMADnaBase)umaDna).dnaAsset = DNAAsset;
}
}
if (_applyDNAPrepassPlugins.Count > 0)
{
for (int i = 0; i < _applyDNAPrepassPlugins.Count; i++)
{
_applyDNAPrepassPlugins[i].ApplyDNA(umaData, skeleton, DNATypeHash);
}
}
}
/// <summary>
/// Calls ApplyDNA on all this convertersControllers plugins (aka converters) that apply dna during the pre-pass
/// </summary>
/// <param name="umaData">The umaData on the avatar</param>
/// <param name="skeleton">The avatars skeleton</param>
/// <param name="dnaTypeHash">The dnaTypeHash that this converters behaviour is using</param>
public void ApplyDNAPostpass(UMAData umaData, UMASkeleton skeleton)
{
if (!_prepared)
{
Prepare();
}
UMADnaBase umaDna = umaData.GetDna(DNATypeHash);
//Make the DNAAssets match if they dont already, can happen when some parts are in bundles and others arent
if (umaDna is DynamicUMADnaBase)
{
if (((DynamicUMADnaBase)umaDna).dnaAsset != DNAAsset && DNAAsset != null)
{
((DynamicUMADnaBase)umaDna).dnaAsset = DNAAsset;
}
}
if (_applyDNAPostpassPlugins.Count > 0)
{
for (int i = 0; i < _applyDNAPostpassPlugins.Count; i++)
{
try
{
_applyDNAPostpassPlugins[i].ApplyDNA(umaData, skeleton, DNATypeHash);
}
catch(System.Exception ex)
{
Debug.LogException(ex);
}
}
}
}
/// <summary>
/// Calls ApplyDNA on all this convertersControllers plugins (aka converters) that apply dna at the standard time
/// </summary>
/// <param name="umaData">The umaData on the avatar</param>
/// <param name="skeleton">The avatars skeleton</param>
/// <param name="dnaTypeHash">The dnaTypeHash that this converters behaviour is using</param>
public void ApplyDNA(UMAData umaData, UMASkeleton skeleton)
{
if (!_prepared)
{
Prepare();
}
UMADnaBase umaDna = null;
//reset the live scale on the overallModifiers ready for any adjustments any plugins might make
liveScale = -1;
//Add this ApplyHeightMassRadius method to this umaDatas CharacterUpdated event so that HeightMassRadius and bounds BaseCharacterModifiers get applied after all ConverterControllers on this character
umaData.OnCharacterBeforeUpdated += ApplyHeightMassRadius;
//Add this ApplyAdjustScale method to this umaDatas DnaUpdated event so that we adjust the global scale just after all other dna adjustments
umaData.OnCharacterBeforeDnaUpdated += ApplyAdjustScale;
//fixDNAPrefabs- do we need to deal with 'reset' as dnaconverterBehaviour used to do? If so wouldn't we just apply all the plugins with MasterWeight set to 0?
//if (!asReset)
//{
umaDna = umaData.GetDna(DNATypeHash);
//Make the DNAAssets match if they dont already, can happen when some parts are in bundles and others arent
if (umaDna is DynamicUMADnaBase)
{
if (((DynamicUMADnaBase)umaDna).dnaAsset != DNAAsset)
{
((DynamicUMADnaBase)umaDna).dnaAsset = DNAAsset;
}
}
//}
for (int i = 0; i < _applyDNAPlugins.Count; i++)
{
_applyDNAPlugins[i].ApplyDNA(umaData, skeleton, DNATypeHash);
}
//_overallModifiers.UpdateCharacter(umaData, skeleton, false);
ApplyDnaCallbackDelegates(umaData);
}
/// <summary>
/// Applies OverallModifiers after all other dna changes have completed
/// </summary>
/// <param name="umaData"></param>
public void ApplyAdjustScale(UMAData umaData)
{
_overallModifiers.AdjustScale(umaData.skeleton);
//remove this listener from this umaData
umaData.OnCharacterBeforeDnaUpdated -= ApplyAdjustScale;
}
/// <summary>
/// Applies ApplyHeightMassRadius after all other dna changes have completed
/// </summary>
/// <param name="umaData"></param>
public void ApplyHeightMassRadius(UMAData umaData)
{
_overallModifiers.UpdateCharacterHeightMassRadius(umaData, umaData.skeleton);
//remove this listener from this umaData
umaData.OnCharacterBeforeUpdated -= ApplyHeightMassRadius;
}
public void ApplyDnaCallbackDelegates(UMAData umaData)
{
if (_dnaCallbackDelegates.Count == 0)
{
return;
}
UMADnaBase umaDna;
//need to use the typehash
umaDna = umaData.GetDna(DNATypeHash);
if (umaDna.Count == 0)
{
return;
}
foreach (KeyValuePair<string, List<UnityAction<string, float>>> kp in _dnaCallbackDelegates)
{
for (int i = 0; i < kp.Value.Count; i++)
{
kp.Value[i].Invoke(kp.Key, (umaDna as DynamicUMADna).GetValue(kp.Key, true));
}
}
}
/// <summary>
/// Gets all the used dna names from all the plugins (aka converters). This can be used to speed up searching the dna for names by string
/// </summary>
/// <param name="forceRefresh">Set this to true if you know the dna names used by any of the plugins has been changed at runtime</param>
/// <returns></returns>
public List<string> GetUsedDNANames(bool forceRefresh = false)
{
if (_usedDNANames.Count == 0 || forceRefresh)
{
CompileUsedDNANamesList();
}
return _usedDNANames;
}
/// <summary>
/// Gets a plugin from the list of plugins assigned to this converterController by index
/// </summary>
public DynamicDNAPlugin GetPlugin(int index)
{
if (_plugins.Count > index)
{
return _plugins[index];
}
return null;
}
/// <summary>
/// Gets a plugin from the list of plugins assigned to this converterController by name
/// </summary>
public DynamicDNAPlugin GetPlugin(string name)
{
for(int i = 0; i < _plugins.Count; i++)
{
if (_plugins[i].name == name)
{
return _plugins[i];
}
}
return null;
}
/// <summary>
/// Gets all plugins assigned to this converterController that are of the given type
/// </summary>
public List<DynamicDNAPlugin> GetPlugins()
{
return _plugins;
}
/// <summary>
/// Gets all plugins assigned to this converterController that are of the given type
/// </summary>
public List<DynamicDNAPlugin> GetPlugins(System.Type pluginType)
{
var pluginsOfType = new List<DynamicDNAPlugin>();
for (int i = 0; i < _plugins.Count; i++)
{
if (pluginType.IsAssignableFrom(_plugins[i].GetType()))
{
pluginsOfType.Add(_plugins[i]);
}
}
return pluginsOfType;
}
/// <summary>
/// Creates a plugin of the given type (must descend from DynamicDNAPlugin), adds it to this converterControllers plugins list, and stores its asset in the given DynamicDNAConverterController asset
/// </summary>
/// <param name="pluginType">The type of dna plugin to create (must descend from DynamicDNAPlugin)</param>
/// <returns>Returns the created plugin</returns>
//This can happen at runtime but no asset is created or stored, it just exists in memory
public DynamicDNAPlugin AddPlugin(System.Type pluginType)
{
DynamicDNAPlugin plugin = null;
plugin = CreatePlugin(pluginType, this);
if (plugin != null)
{
_prepared = false;
_plugins.Add(plugin);
#if UNITY_EDITOR
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
#endif
//ensure the new plugin is added to the _applyPlugins lists
if(Application.isPlaying)
{
Prepare();
}
return plugin;
}
return null;
}
/// <summary>
/// Removes the given plugin from this converterController, and deletes its asset (in the Editor)
/// </summary>
/// <param name="pluginToDelete"></param>
/// <returns></returns>
public bool DeletePlugin(DynamicDNAPlugin pluginToDelete)
{
//check if the given plugin is indeed inside this asset
//if it is DestroyImmediate
if (_plugins.Contains(pluginToDelete))
{
_prepared = false;
_plugins.Remove(pluginToDelete);
Debug.Log(pluginToDelete.name + " successfully deleted from " + this.name);
#if UNITY_EDITOR
DestroyImmediate(pluginToDelete, true);
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
#endif
}
//then Validate the list
ValidatePlugins();
return false;
}
/// <summary>
/// At run time this simply clears the plugins list of any empty entries, or null entries, and assigns itself as the converterController for the plugin
/// At edit time all instantiated plugins inside the given converterController are checked to see if they belong in this list and if they are they get added
/// This can happen when a plugin script is deleted but then restored again (like when working on different branches in sourceControl)
/// </summary>
public void ValidatePlugins()
{
#if UNITY_EDITOR
bool changed = false;
#endif
var cleanList = new List<DynamicDNAPlugin>();
for (int i = 0; i < _plugins.Count; i++)
{
if (_plugins[i] != null)
{
if (DynamicDNAPlugin.IsValidPlugin(_plugins[i]))
{
cleanList.Add(_plugins[i]);
if (_plugins[i].converterController != this)
{
_plugins[i].converterController = this;
#if UNITY_EDITOR
EditorUtility.SetDirty(_plugins[i]);
changed = true;
#endif
}
}
}
}
_plugins = cleanList;
#if UNITY_EDITOR
//if we are in the editor get all the assets inside the given converterController asset and check if any of those should be in this list
var thisAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(this));
for (int i = 0; i < thisAssets.Length; i++)
{
if (thisAssets[i] == this)
{
continue;
}
if (!DynamicDNAPlugin.IsValidPlugin(thisAssets[i]))
{
continue;
}
if (!_plugins.Contains(thisAssets[i] as DynamicDNAPlugin))
{
_plugins.Add(thisAssets[i] as DynamicDNAPlugin);
changed = true;
}
}
if (changed)
{
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}
#endif
CompileUsedDNANamesList();
}
/// <summary>
/// Compiles the used names cache. This can be used to speed up searching the dna for names by string
/// </summary>
private void CompileUsedDNANamesList()
{
_usedDNANames.Clear();
for (int i = 0; i < _plugins.Count; i++)
{
foreach(KeyValuePair<string, List<int>> kp in _plugins[i].IndexesForDnaNames)
{
if (!_usedDNANames.Contains(kp.Key) && !string.IsNullOrEmpty(kp.Key))
{
_usedDNANames.Add(kp.Key);
}
}
}
}
/// <summary>
/// Creates a new plugin of the given type and stores it inside the given converterController asset
/// </summary>
/// <returns>Returns the created asset</returns>
private static DynamicDNAPlugin CreatePlugin(System.Type pluginType, DynamicDNAConverterController converter)
{
//Checks and warnings
if (pluginType == null)
{
Debug.LogWarning("Could not create plugin because the plugin type was null");
return null;
}
if (converter == null)
{
Debug.LogWarning("Could not create plugin because no converterController was provided to add it to");
return null;
}
if (!DynamicDNAPlugin.IsValidPluginType(pluginType))
{
Debug.LogWarning("Could not create plugin because it did not descend from DynamicDNAPlugin");
return null;
}
DynamicDNAPlugin asset = ScriptableObject.CreateInstance(pluginType) as DynamicDNAPlugin;
asset.name = converter.GetUniquePluginName(pluginType.Name.Replace("Plugin","") + "s");
#if UNITY_EDITOR
Debug.Log(pluginType + " created successfully! Its asset '" + asset.name + "' has been stored in " + converter.name);
AssetDatabase.AddObjectToAsset(asset, converter);
#endif
return asset;
}
/// <summary>
/// Gets a unique name for a plugin relative to this converterController
/// </summary>
/// <param name="desiredName">The name you'd like</param>
public string GetUniquePluginName(string desiredName, DynamicDNAPlugin existingPlugin = null)
{
var intSuffix = 0;
for (int i = 0; i < _plugins.Count; i++)
{
if (_plugins[i].name == desiredName && (existingPlugin == null || (existingPlugin != null && existingPlugin != _plugins[i])))
{
intSuffix++;
}
}
return desiredName + (intSuffix != 0 ? intSuffix.ToString() : "");
}
#if UNITY_EDITOR
[UnityEditor.MenuItem("UMA/Create Dynamic DNA Converter Controller")]
public static DynamicDNAConverterController CreateDynamicDNAConverterControllerAsset()
{
return UMA.CustomAssetUtility.CreateAsset<DynamicDNAConverterController>();
}
public static DynamicDNAConverterController CreateDynamicDNAConverterControllerAsset(string newAssetPath, bool selectCreatedAsset = true, string baseName = "New")
{
return UMA.CustomAssetUtility.CreateAsset<DynamicDNAConverterController>(newAssetPath, selectCreatedAsset, baseName);
}
#endif
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 56c6edb17218abd469257d07a2bc5559
timeCreated: 1538164723
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/Scripts/DynamicDNAConverterController.cs
uploadId: 679826
+536
View File
@@ -0,0 +1,536 @@
using System;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UMA
{
[System.Serializable]
public abstract class DynamicDNAPlugin : ScriptableObject
{
//=====================================================================//
//A DynamicDNAPlugin is always a LIST of a type of 'DNA Converter' (an abstract concept- it can be any type)
//A 'DNA Converter' converts dna values into modifications to the character
//For example in UMA currently we have a SkeletonModifier that converts dna values into modifications to the skeletons bones
//Or a DNAMorphset converts dna values into weights for UMABonePoses and Blendshapes
//So those are examples of a 'DNA Converter' and a DynamicDNAPlugin is most basically a list of one of those kinds of things,
//along with an ApplyDNA method to apply them
//The plugin concept places no restrictions on what those 'DNA Converters' might be or do.
//But it does expect that a DynamicDNAPlugin contain a list of them and requires one method and one property in order to integrate them into the system.
//DynamicDNAPlugins only have to derive from DynamicDNAPlugin and they do not need an associated inspector.
//Any new plugins are automatically found and are available to add to the DynamicDNAConverterControllerAsset via its inspector
//DynamicDNAPlugins also have a MasterWeight. Setting this to zero disables the plugin, but the master weight can itself be hooked up to a dna value.
//By doing this different characters can control how much a set of Skeleton Modifiers or MorphSets should apply to them in their current state.
//For example a charcaters 'Claws' dna might do nothing while its human, but alot when it is a 'werewolf' (i.e. its 'werewolf' dna is turned up)
//The system also introduces DNAEvaluator, which is a super flexible field that performs math calculations on a dna value using a customizable animation curve.
//This is so that there is no need to have any extra behaviours or code in order to interpret dna values in a certain way.
//So the user is not overwhelmed by the complexity of using animation curves for math, DNAEvaluator uses DNAEvaluationGraphs which have preset curves
//with nice friendly names and tool tips. As coders we can set DNAEvaluationGraph fields to use one of our predefined defaults.
//DynamicDNAPlugins are assigned to a DynamicDNAConverterControllerAsset which calls ApplyDNA on each plugin in turn at runtime,
//and which at edit time looks after the creation of the Plugin Assets and its own Plugins list
//A DynamicDNAConverterControllerAsset is assigned to a DynamicDNAConverterBehaviour which triggers the ApplyDNA action on the DynamicDNAConverterControllerAsset
/// <summary>
/// Does this plugin get applied during the Standard ApplyDNA pass or in the 'Pre Pass'
/// </summary>
public enum ApplyPassOpts
{
PrePass,
Standard,
PostPass
}
#region ABSTRACT PROPERTIES AND METHODS
//It is REQUIRED that all DynamicDNAPlugins have these two Propeties and Methods (and thats it!)
/// <summary>
/// Returns a dictionary of all the dna names in use by the plugin and the indexes of the entries in its converter list that reference them
/// </summary>
public abstract Dictionary<string, List<int>> IndexesForDnaNames { get; }
/// <summary>
/// Called by the converter this plugin is assigned to. Applys the plugins list of converters to the character
/// </summary>
public abstract void ApplyDNA(UMAData umaData, UMASkeleton skeleton, int dnaTypeHash);
#endregion
#region PUBLIC MEMBERS
/// <summary>
/// All DynamicDNAPlugins have a 'MasterWeight' this makes it possible to disable them completely, or only enable them when certain dna conditions are met
/// </summary>
[Tooltip("The master weight controls how much all the converters in this group are applied. You can disable a set of converters by making the master weight zero. Or you can hook the master weight up to a characters dna so the converters only apply when that dna has a certain value.")]
[SerializeField]
public MasterWeight masterWeight = new MasterWeight();
#endregion
#region PRIVATE MEMBERS
[SerializeField]
private DynamicDNAConverterController _converterController;
#endregion
#region PUPLIC PROPERTIES
/// <summary>
/// The converterController asset this plugin has been assigned to. This property is set by the converterBehaviour when it is inspected or starts
/// </summary>
public DynamicDNAConverterController converterController
{
get { return _converterController; }
set { _converterController = value; }
}
public DynamicUMADnaAsset DNAAsset
{
get
{
if (_converterController != null)
{
return _converterController.DNAAsset;
}
return null;
}
}
#endregion
#region VIRTUAL PROPERTIES
//Its is OPTIONAL for any DynamicDNAPlugin to override these properties / methods
/// <summary>
/// Does this plugin get applied during the Standard ApplyDNA pass or in the 'Pre Pass'
/// </summary>
public virtual ApplyPassOpts ApplyPass { get { return ApplyPassOpts.Standard; } }
#if UNITY_EDITOR
public virtual string PluginHelp { get { return ""; } }
public virtual float GetListHeaderHeight
{
get
{
return 0f;
}
}
public virtual float GetListFooterHeight
{
get
{
return 13f;
}
}
/// <summary>
/// Gets the height of the 'Help' info that will be drawn by DrawPluginHelp.
/// </summary>
public virtual float GetPluginHelpHeight
{
get
{
//by default this will return the height of a help box that contains the PluginHelp string
return EditorStyles.helpBox.CalcHeight(new GUIContent(PluginHelp, EditorGUIUtility.FindTexture("console.infoicon")), Screen.width - EditorGUIUtility.FindTexture("console.infoicon").width) + (EditorGUIUtility.singleLineHeight / 2);
}
}
//This is a string array because different plugins might want to make different import methods.
//the plugins ImportSettings method will just be sent the index of the choice, then its up to the method what to do
/// <summary>
/// Standard ImportSettingsMethods are [0]Add [1]Replace
/// Override this if your plugins ImportSettings method uses different options
/// </summary>
public virtual string[] ImportSettingsMethods
{
get
{
return new string[]
{
"Add",
"Replace"
};
}
}
#endif
#endregion
#region VIRTUAL METHODS
public virtual void Reset()
{
}
//these all show when anything else gets a DynamicDNAPlugin - I'd prefer them to be protaected but also available to the pluginDrawer..How??
#if UNITY_EDITOR
/// <summary>
/// Override this method if DynamicDNAPluginInspector is not finding your list of converters automatically
/// </summary>
public virtual SerializedProperty GetConvertersListProperty(SerializedObject pluginSO)
{
//if overidden you should do something like
//return pluginSO.FindPropertyRelative("nameOfMyConverterList");
//By default gets the first kind of valid array in the plugin.
//Since plugins should always be a list of converters first and foremost this should usually work
SerializedProperty it = pluginSO.GetIterator();
it.Next(true);
while (it.Next(false))
{
if (it.propertyType != SerializedPropertyType.String && it.isArray && it.name != "Array" && it.name != "_masterWeight")
{
return it;
}
}
Debug.LogWarning("Could not find the Converters list for " + this.name + ". Please override 'GetConvertersListProperty' in your plugin");
return null;
}
/// <summary>
/// Draws the plugins 'Help' info using the value from the plugins 'PluginHelp' property. If you override this method you will also need to override the GetPluginHeight method
/// </summary>
public virtual void DrawPluginHelp(Rect position)
{
//by default this will draw a helpbox that contains the PluginHelp string
EditorGUI.HelpBox(position, PluginHelp, MessageType.Info);
}
/// <summary>
/// Override this to draw your own content in the Elements list header
/// Use pluginSO for to find properties to pass to standard EditorGUI.Property methods etc
/// You may also want to override GetListHeaderHeight if you need more lines
/// </summary>
/// <param name="rect">The full height of the header, override GetListHeaderHeight if you need more lines sent here</param>
/// <param name="pluginSO">The ScriptableObject representation of the plugin</param>
/// <returns>True if you want the default elements search bar drawn, false otherwise</returns>
public virtual bool DrawElementsListHeaderContent(Rect rect, SerializedObject pluginSO)
{
return true;
}
/// <summary>
/// Gets an label for an entry from this plugins list of converters
/// </summary>
/// <param name="pluginSO">The SerializedObject representation of this plugin</param>
/// <param name="entryIndex">The index from this plugins list of converters to draw</param>
public virtual GUIContent GetPluginEntryLabel(SerializedProperty entry, SerializedObject pluginSO, int entryIndex)
{
if (entry != null)
{
return new GUIContent(entry.displayName);
}
return GUIContent.none;
}
/// <summary>
/// Gets the height for an entry from this plugins list of converters.
/// </summary>
/// <param name="pluginSO">The SerializedObject representation of this plugin</param>
/// <param name="entryIndex">The index from this plugins list of converters to draw</param>
public virtual float GetPluginEntryHeight(SerializedObject pluginSO, int entryIndex, SerializedProperty entry)
{
if(entry != null)
{
if (entry.isExpanded)
{
return EditorGUI.GetPropertyHeight(entry, true);
}
else
{
return EditorGUIUtility.singleLineHeight;
}
}
return EditorGUIUtility.singleLineHeight;
}
/// <summary>
/// Draws an entry from this plugins list of converters in the UI. If you override this you may also need to override GetPluginEntryHeight.
/// </summary>
/// <param name="pluginSO">The SerializedObject representation of this plugin</param>
/// <param name="entryIndex">The index from this plugins list of converters to draw</param>
/// <param name="isExpanded">Whether the entry is currently expanded</param>
/// <returns>whether the entry is still expanded</returns>
public virtual bool DrawPluginEntry(Rect rect, SerializedObject pluginSO, int entryIndex, bool isExpanded, SerializedProperty entry)
{
if (entry != null)
{
EditorGUI.PropertyField(rect, entry, GetPluginEntryLabel(entry, pluginSO, entryIndex), true);
return entry.isExpanded;
}
return false;
}
/// <summary>
/// A callback that is called *after* a new entry is added to the plugins list of converters
/// </summary>
/// <param name="pluginSO">The SerializedObject representation of this plugin</param>
/// <param name="entryIndex">The index from this plugins list of converters to that was added</param>
public virtual void OnAddEntryCallback(SerializedObject pluginSO, int entryIndex)
{
//do nothing
}
/// <summary>
/// A callback that is called *before* an entry will be deleted from the plugins list of converters
/// </summary>
/// <param name="pluginSO">The SerializedObject representation of this plugin</param>
/// <param name="entryIndex">The index from this plugins list of converters that will be deleted/param>
/// <returns>Returns true if the entry can safely be deleted</returns>
public virtual bool OnRemoveEntryCallback(SerializedObject pluginSO, int entryIndex)
{
return true;
}
/// <summary>
/// Override this to draw your own content in the Elements list footer
/// Use pluginSO for to find properties to pass to standard EditorGUI.Property methods etc
/// You may also want to override GetListFooterHeight if you need more lines
/// </summary>
/// <param name="rect">The full height of the footer, override GetListFooterHeight if you need more lines sent here</param>
/// <param name="pluginSO">The ScriptableObject representation of the plugin</param>
/// <returns>True if you want the default elements '+/-' add/remove controls drawn, false otherwise</returns>
public virtual bool DrawElementsListFooterContent(Rect rect, SerializedObject pluginSO)
{
return true;
}
/// <summary>
/// Import settings from another plugin. You need to override this method to enable this functionality in your plugin
/// </summary>
/// <param name="pluginToImport">The sent UnityEngine.Object. Your plugin script should first check that this plugin is the correct type</param>
/// <returns>True if the settings imported successfully</returns>
public virtual bool ImportSettings(UnityEngine.Object pluginToImport, int importMethod)
{
Debug.LogWarning("Import Settings was not implimented for this plugin");
return false;
}
#endif
#endregion
#region PRIVATE STATIC FIELDS
private static readonly Type baseDynamicDNAPluginType = typeof(DynamicDNAPlugin);
private static List<Type> _pluginTypes;
#endregion
#region PUBLIC STATIC METHODS
public static List<Type> GetAvailablePluginTypes()
{
if (_pluginTypes == null)
{
CompilePluginTypesList();
}
return _pluginTypes;
}
public static bool IsValidPluginType(Type type)
{
return PluginDerivesFromBase(type);
}
public static bool IsValidPlugin(UnityEngine.Object asset)
{
try
{
return PluginDerivesFromBase(asset.GetType());
}
catch
{
return false;
}
}
#endregion
#region PRIVATE STATIC METHODS
private static bool PluginDerivesFromBase(Type type)
{
if (type == baseDynamicDNAPluginType)
{
return false;
}
else
{
return baseDynamicDNAPluginType.IsAssignableFrom(type);
}
}
private static void CompilePluginTypesList()
{
var list = new List<Type>();
System.Reflection.Assembly[] array = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < array.Length; i++)
{
System.Reflection.Assembly assembly = array[i];
try
{
if (assembly != null)
{
Type[] array1 = assembly.GetTypes();
for (int i1 = 0; i1 < array1.Length; i1++)
{
Type type = array1[i1];
if (type.IsAbstract)
{
continue;
}
if (PluginDerivesFromBase(type))
{
list.Add(type);
}
}
}
}
catch
{
Debug.Log("An exception occurred loading assemblies. this can happen when an invalid assembly is present in the project and it cannot be loaded.");
}
}
_pluginTypes = list;
}
#endregion
#region SPECIAL TYPES
[System.Serializable]
public class MasterWeight
{
public enum MasterWeightType
{
UseGlobalValue,
UseDNAValue
}
[Tooltip("Choose whether to use a global value for all characters that use this converter, or a dnaValue that characters can change.")]
[SerializeField]
private MasterWeightType _masterWeightType = MasterWeightType.UseGlobalValue;
[Tooltip("The global weight to use for this set of converters. Applies to all characters that use the converter behaviour this resides in. Override this with DNAForWeight for 'per character' control")]
[Range(0f, 1f)]
[SerializeField]
private float _globalWeight = 1f;
[Tooltip("If set, the weight value will be controlled by the given dna on the character.")]
[SerializeField]
[DNAEvaluator.Config(true, true)]
private DNAEvaluator _DNAForWeight = new DNAEvaluator("", DNAEvaluationGraph.Raw, 1);
public MasterWeightType masterWeightType
{
get { return _masterWeightType; }
set { _masterWeightType = value; }
}
public float globalWeight
{
get { return _globalWeight; }
set { _globalWeight = value; }
}
public string dnaName
{
get { return _DNAForWeight.dnaName; }
set { _DNAForWeight.dnaName = value; }
}
public DNAEvaluationGraph dnaEvaluationGraph
{
get { return _DNAForWeight.evaluator; }
set { _DNAForWeight.evaluator = value; }
}
public float dnaMultiplier
{
get { return _DNAForWeight.multiplier; }
set { _DNAForWeight.multiplier = value; }
}
public MasterWeight()
{
_masterWeightType = MasterWeightType.UseGlobalValue;
_globalWeight = 1f;
}
public MasterWeight(MasterWeight other)
{
_masterWeightType = other._masterWeightType;
_globalWeight = other._globalWeight;
_DNAForWeight = new DNAEvaluator(other._DNAForWeight);
}
public MasterWeight(MasterWeightType masterWeightType = MasterWeightType.UseGlobalValue, float defaultWeight = 1f, string dnaForWeightName = "", DNAEvaluationGraph dnaForWeightGraph = null, float dnaForWeightMultiplier = 1f)
{
_masterWeightType = masterWeightType;
_globalWeight = defaultWeight;
if (!string.IsNullOrEmpty(dnaForWeightName))
{
_DNAForWeight = new DNAEvaluator(dnaForWeightName, dnaForWeightGraph, dnaForWeightMultiplier);
}
else
{
_DNAForWeight = new DNAEvaluator("", DNAEvaluationGraph.Raw, 1);
}
}
public float GetWeight(UMADnaBase umaDna = null)
{
if (_masterWeightType == MasterWeightType.UseDNAValue)
{
return _DNAForWeight.Evaluate(umaDna);
}
else
{
return _globalWeight;
}
}
//TODO check if this still screws up the incoming dnas values
public UMADnaBase GetWeightedDNA(UMADnaBase incomingDna)
{
if (_masterWeightType == MasterWeightType.UseGlobalValue)
{
return incomingDna;
}
var masterWeight = GetWeight(incomingDna);
var weightedDNA = new DynamicUMADna();
if (masterWeight > 0)
{
weightedDNA._names = new string[incomingDna.Names.Length];
Array.Copy(incomingDna.Names, weightedDNA._names, incomingDna.Names.Length);
weightedDNA._values = new float[incomingDna.Values.Length];
Array.Copy(incomingDna.Values, weightedDNA._values, incomingDna.Values.Length);
for (int i = 0; i < incomingDna.Count; i++)
{
weightedDNA.SetValue(i, weightedDNA.GetValue(i) * masterWeight);
}
}
return weightedDNA;
}
}
#endregion
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: f7cf4f6fc39293d4ca907620ae84f2c6
timeCreated: 1537470577
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/Scripts/DynamicDNAPlugin.cs
uploadId: 679826
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6ce85e543a13a9c4f8e8cae097820de2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,6 @@
namespace Assets.UMA.Core.Scripts.DynamicExpressions
{
internal class DynamicExpressionBonePose
{
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5038fa450b0832144af1bfb32d84d3fc
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/Scripts/DynamicExpressions/DynamicExpressionBonePose.cs
uploadId: 679826
@@ -0,0 +1,6 @@
namespace UMA
{
public class DynamicExpressionList
{
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9e6e7b33f9ccca3409b6cf1ad7bc9be5
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/Scripts/DynamicExpressions/DynamicExpressionList.cs
uploadId: 679826
@@ -0,0 +1,9 @@
namespace UMA
{
public interface IDynamicExpression
{
public abstract void Initialize(UMAData umadata);
public abstract void PreProcess(UMAData umadata);
public abstract void Process(UMAData umadata);
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: bb0e685d9cf1c144da1ba84dfd338cae
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/Scripts/DynamicExpressions/IDynamicExpression.cs
uploadId: 679826
+67
View File
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class FPSCounter : MonoBehaviour
{
public Text Text;
private Dictionary<int, string> CachedNumberStrings = new();
private int[] _frameRateSamples;
private int _cacheNumbersAmount = 1000;
private int _averageFromAmount = 30;
private int _averageCounter = 0;
private int _currentAveraged;
public float updateRate = 0.5f;
public float updateTime = 0.0f;
void Awake()
{
// Cache strings and create array
{
for (int i = 0; i < _cacheNumbersAmount; i++)
{
CachedNumberStrings[i] = i.ToString();
}
_frameRateSamples = new int[_averageFromAmount];
}
}
void Update()
{
// Sample
{
var currentFrame = (int)Math.Round(1f / Time.smoothDeltaTime); // If your game modifies Time.timeScale, use unscaledDeltaTime and smooth manually (or not).
_frameRateSamples[_averageCounter] = currentFrame;
}
// Average
{
var average = 0f;
foreach (var frameRate in _frameRateSamples)
{
average += frameRate;
}
_currentAveraged = (int)Math.Round(average / _averageFromAmount);
_averageCounter = (_averageCounter + 1) % _averageFromAmount;
}
updateTime -= Time.unscaledDeltaTime;
if (updateTime <= 0.0f)
// Assign to UI
{
Text.text = _currentAveraged switch
{
var x when x >= 0 && x < _cacheNumbersAmount => CachedNumberStrings[x],
var x when x >= _cacheNumbersAmount => $"> {_cacheNumbersAmount}",
var x when x < 0 => "< 0",
_ => "?"
};
updateTime = updateRate;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b19a6b26dd4b48845b0237118b8cfb36
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/Scripts/FPSCounter.cs
uploadId: 679826
+401
View File
@@ -0,0 +1,401 @@
using System.Collections.Generic;
using UMA;
using UMA.CharacterSystem;
using UnityEngine;
using UnityEngine.SceneManagement;
public class HairSmoosher : MonoBehaviour
{
// These are temporary until we can integrate this
// into the workflow.
private SlotDataAsset HairToSmoosh;
private SlotDataAsset HairPlane;
private SlotDataAsset HeadSlot;
public bool invertX;
public bool invertY;
public bool invertZ;
public bool invertDist;
#if UNITY_EDITOR
[InspectorButton("OnButtonClicked")]
#endif
public bool forceRebuild;
public enum SmooshMode { ToCenter, Raycast, Physics };
public float SmooshDistance = 0.01f;
public float OverSmoosh = 0.02f;
public bool enableCaching = false;
public string LastSmoosh;
public SmooshMode Mode;
[Range(0, 5000)]
public int ShowVertex = -1;
public Vector3 theShownVert = new Vector3();
public static Dictionary<string, Vector3[]> cachedSmooshes = new Dictionary<string, Vector3[]>();
/// <summary>
/// Forces the character to rebuild and re-smoosh the hair.
/// Ignore the zero references. It really is referenced, in the attribute above.
/// </summary>
private void OnButtonClicked()
{
DynamicCharacterAvatar dca = this.gameObject.GetComponent<DynamicCharacterAvatar>();
if (dca != null)
{
dca.GenerateNow();
}
}
public System.Diagnostics.Stopwatch StartTimer()
{
Debug.Log("Timer started at " + Time.realtimeSinceStartup + " Sec");
System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
st.Start();
return st;
}
public void StopTimer(System.Diagnostics.Stopwatch st, string Status)
{
st.Stop();
LastSmoosh = Status + " Completed " + st.ElapsedMilliseconds + "ms";
//Debug.Log(Status + " Completed " + st.ElapsedMilliseconds + "ms");
return;
}
public void BeforeBuild(UMAData umaData)
{
// find the Hair slot in the SlotDataList.
// process all verts in the SlotDataList.
// Set override vertexes.
Debug.Log("Smoosher called");
SlotDataAsset hair = null;
SlotDataAsset clip = HairPlane;
int slotCount = umaData.GetSlotArraySize();
for (int i = 0; i < slotCount; i++)
{
var slot = umaData.GetSlot(i);
if (slot.HasTag("SmooshTarget"))
{
HeadSlot = slot.asset;
}
if (HairToSmoosh != null && slot != null && slot.slotName == HairToSmoosh.slotName)
{
hair = slot.asset;
}
else if (slot.HasTag("smooshable"))
{
hair = slot.asset;
}
else if (slot.HasTag("smooshclip"))
{
clip = slot.asset;
}
}
if (hair != null)
{
HairToSmoosh = hair;
Debug.Log("Smooshing selected slot: " + hair.slotName);
var st = StartTimer();
if (Mode == SmooshMode.Physics)
{
SmooshSlotPhysics(umaData, hair, clip, HeadSlot);
}
else
{
SmooshSlot(umaData, hair, clip, HeadSlot);
}
StopTimer(st, "Smooshing slot complete");
}
}
private bool PointInTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 p)
{
Vector3 d, e;
double w1, w2;
d = b - a;
e = c - a;
if (Mathf.Approximately(e.y, 0))
{
e.y = 0.0001f;
}
w1 = ((e.x * (a.y - p.y)) + (e.y * (p.x - a.x))) / ((d.x * e.y) - (d.y * e.x));
w2 = (p.y - a.y - (w1 * d.y)) / e.y;
return (w1 >= 0f) && (w2 >= 0.0) && ((w1 + w2) <= 1.0);
}
public Vector3 GetDestVert(Vector3 vertex, Vector3 center, float PlaneDist, SlotDataAsset SmooshTarget)
{
Vector3 result = new Vector3();
if (Mode == SmooshMode.ToCenter || HeadSlot == null)
{
return center;
}
result.Set(vertex.x, vertex.y, vertex.z);
float distance = Vector3.Distance(center, vertex);
SubMeshTriangles smt = SmooshTarget.meshData.submeshes[HeadSlot.subMeshIndex];
int[] tris = smt.getBaseTriangles();
Vector3[] verts = SmooshTarget.meshData.vertices;
int tricount = tris.Length / 3;
Plane p = new Plane();
Vector3 direction = center - vertex;
direction.Normalize();
Ray r = new Ray(vertex, direction);
for (int tri = 0; tri < tricount; tri++)
{
int baseTri = tri * 3;
Vector3 v1 = verts[tris[baseTri]];
Vector3 v2 = verts[tris[baseTri + 1]];
Vector3 v3 = verts[tris[baseTri + 2]];
try
{
p.Set3Points(v1, v2, v3);
}
catch (System.Exception ex)
{
Debug.Log("Exception: " + ex.Message);
}
//Initialise the enter variable
float enter = 0.0f;
if (p.Raycast(r, out enter))
{
//Get the point that is clicked
Vector3 hitPoint = r.GetPoint(enter);
if (PointInTriangle(v1, v2, v3, hitPoint))
{
float vertdistance = (hitPoint - vertex).magnitude;
if (vertdistance < distance)
{
distance = vertdistance;
float newSmooshDistance = SmooshDistance;
// Smooth smooshing
if (distance > 0.0f)
{
newSmooshDistance = SmooshDistance + (SmooshDistance * (distance / OverSmoosh));
}
Vector3 newLocation = hitPoint + (direction * (0 - newSmooshDistance));
result.Set(newLocation.x, newLocation.y, newLocation.z);
}
}
}
#if false
Vector3 v = p.ClosestPointOnPlane(vertex);
#endif
}
return result;
}
public Vector3 GetDestVertPhys(Vector3 vertex, Vector3 center, float PlaneDist, SlotDataAsset SmooshTarget, PhysicsScene ps, int vertindex)
{
Vector3 result = new Vector3();
if (Mode == SmooshMode.ToCenter || HeadSlot == null)
{
return center;
}
Vector3 AlternateCenter = new Vector3(center.x, center.y + 0.001f, center.z);
result.Set(vertex.x, vertex.y, vertex.z);
float distance = Vector3.Distance(center, vertex);
Vector3 direction = (center - vertex).normalized;
if (ps.Raycast(vertex, direction, out RaycastHit hit, 5.0f, -5, QueryTriggerInteraction.Ignore))
{
// todo: offset by smoosh distance
float vertdistance = (hit.point - vertex).magnitude;
if (vertdistance < distance)
{
distance = vertdistance;
float newSmooshDistance = SmooshDistance;
// Smooth smooshing
if (distance > 0.0f)
{
newSmooshDistance = SmooshDistance + (SmooshDistance * (distance / OverSmoosh));
}
Vector3 newLocation = hit.point + (direction * (0 - newSmooshDistance));
result.Set(newLocation.x, newLocation.y, newLocation.z);
}
else
{
result.Set(hit.point.x, hit.point.y, hit.point.z);
}
}
else
{
result.Set(center.x, center.y, center.z);
}
return result;
}
public void SmooshSlotPhysics(UMAData umaData, SlotDataAsset SmooshMe, SlotDataAsset SmooshPlane, SlotDataAsset SmooshTarget)
{
if (SmooshMe == null || SmooshPlane == null || SmooshTarget == null)
{
return;
}
Mesh m = new Mesh();
if (umaData.VertexOverrides.ContainsKey(SmooshTarget.slotName))
{
m.SetVertices(umaData.VertexOverrides[SmooshTarget.slotName]);
}
else
{
m.SetVertices(SmooshTarget.meshData.vertices);
}
m.SetTriangles(SmooshTarget.meshData.submeshes[0].getBaseTriangles(), 0);
CreateSceneParameters csp = new CreateSceneParameters(LocalPhysicsMode.Physics3D);
Scene S = SceneManager.CreateScene("SmooshScene", csp);
GameObject go = new GameObject();
try
{
var collider = go.AddComponent<MeshCollider>();
collider.sharedMesh = m;
SceneManager.MoveGameObjectToScene(go, S);
PhysicsScene physicsScene = S.GetPhysicsScene();
var a = SmooshPlane.meshData.vertices[0];
var b = SmooshPlane.meshData.vertices[1];
var c = SmooshPlane.meshData.vertices[2];
if (invertY)
{
a.y = -a.y;
b.y = -b.y;
c.y = -c.y;
}
if (invertX)
{
a.x = -a.x;
b.x = -b.x;
c.x = -c.x;
}
Plane p = new Plane(a, b, c);
Vector3 center = (a + b + c) / 3;
Vector3[] newVerts = new Vector3[SmooshMe.meshData.vertices.Length];
Vector3[] sourceVertexes = SmooshMe.meshData.vertices;
if (umaData.VertexOverrides.ContainsKey(SmooshMe.slotName))
{
sourceVertexes = umaData.VertexOverrides[SmooshMe.slotName];
}
for (int i = 0; i < newVerts.Length; i++)
{
Vector3 currentVert = sourceVertexes[i];
float dist = p.GetDistanceToPoint(currentVert);
if (invertDist)
{
dist *= -1;
}
if (dist > -OverSmoosh)
{
newVerts[i] = GetDestVertPhys(currentVert, center, dist, SmooshTarget, physicsScene, i);
}
else
{
newVerts[i] = currentVert;
}
}
umaData.AddVertexOverride(SmooshMe, newVerts);
}
finally
{
// Cleanup
SceneManager.UnloadSceneAsync(S);
GameObject.Destroy(m);
}
}
public void SmooshSlot(UMAData umaData, SlotDataAsset SmooshMe, SlotDataAsset SmooshPlane, SlotDataAsset SmooshTarget)
{
string key = SmooshMe.slotName + "*" + SmooshPlane.slotName;
if (enableCaching)
{
if (cachedSmooshes.ContainsKey(key))
{
Debug.Log("Used cached smoosh");
umaData.AddVertexOverride(SmooshMe, cachedSmooshes[key]);
return;
}
}
var a = SmooshPlane.meshData.vertices[0];
var b = SmooshPlane.meshData.vertices[1];
var c = SmooshPlane.meshData.vertices[2];
Vector3[] newVerts = new Vector3[SmooshMe.meshData.vertices.Length];
if (invertY)
{
a.y = -a.y;
b.y = -b.y;
c.y = -c.y;
}
if (invertX)
{
a.x = -a.x;
b.x = -b.x;
c.x = -c.x;
}
Plane p = new Plane(a, b, c);
Vector3 center = (a + b + c) / 3;
for (int i = 0; i < newVerts.Length; i++)
{
Vector3 currentVert = SmooshMe.meshData.vertices[i];
// if (invertX) currentVert.x = -currentVert.x;
// if (invertY) currentVert.y = -currentVert.y;
// if (invertZ) currentVert.z = -currentVert.z;
float dist = p.GetDistanceToPoint(currentVert);
if (invertDist)
{
dist *= -1;
}
if (dist > -OverSmoosh)
{
newVerts[i] = GetDestVert(currentVert, center, dist, SmooshTarget);
}
else
{
newVerts[i] = currentVert;
}
}
umaData.AddVertexOverride(SmooshMe, newVerts);
if (enableCaching)
{
if (cachedSmooshes.ContainsKey(key) == false)
{
cachedSmooshes.Add(key, newVerts);
}
else
{
cachedSmooshes[key] = newVerts;
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: de88f2c64c43b954dbecacbb68d58405
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/Scripts/HairSmoosher.cs
uploadId: 679826
+23
View File
@@ -0,0 +1,23 @@
using UnityEngine;
public class MirrorCam : MonoBehaviour
{
public Transform playerTarget;
public Transform mirror;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 localPlayer = mirror.transform.InverseTransformPoint(playerTarget.position);
Vector3 lookatMirror = mirror.TransformPoint(new Vector3(localPlayer.x, localPlayer.y, localPlayer.z));
transform.LookAt(lookatMirror, mirror.up);
}
}
+18
View File
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 883cb758cd4e0f84e98d048d0ab89731
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/Scripts/MirrorCam.cs
uploadId: 679826
+161
View File
@@ -0,0 +1,161 @@
using UnityEngine;
using System.Collections.Generic;
using System;
namespace UMA
{
public class OverlayLibrary : OverlayLibraryBase
{
[SerializeField]
protected OverlayDataAsset[] overlayElementList = new OverlayDataAsset[0];
[NonSerialized]
private Dictionary<int, OverlayDataAsset> overlayDictionary;
public int scaleAdjust = 1;
public bool readWrite = false;
public bool compress = false;
void Awake()
{
ValidateDictionary();
}
#pragma warning disable 618
override public void UpdateDictionary()
{
ValidateDictionary();
overlayDictionary.Clear();
for (int i = 0; i < overlayElementList.Length; i++)
{
if (overlayElementList[i])
{
var hash = UMAUtils.StringToHash(overlayElementList[i].overlayName);
if (!overlayDictionary.ContainsKey(hash))
{
overlayDictionary.Add(hash, overlayElementList[i]);
}
}
}
}
public override bool HasOverlay(string Name)
{
ValidateDictionary();
var hash = UMAUtils.StringToHash(Name);
return overlayDictionary.ContainsKey(hash);
}
public override bool HasOverlay(int NameHash)
{
ValidateDictionary();
return overlayDictionary.ContainsKey(NameHash);
}
public override void AddOverlayAsset(OverlayDataAsset overlay)
{
ValidateDictionary();
var hash = UMAUtils.StringToHash(overlay.overlayName);
if (overlayDictionary.ContainsKey(hash))
{
for (int i = 0; i < overlayElementList.Length; i++)
{
if (overlayElementList[i].overlayName == overlay.overlayName)
{
overlayElementList[i] = overlay;
break;
}
}
}
else
{
var list = new OverlayDataAsset[overlayElementList.Length + 1];
for (int i = 0; i < overlayElementList.Length; i++)
{
list[i] = overlayElementList[i];
}
list[list.Length - 1] = overlay;
overlayElementList = list;
}
overlayDictionary[hash] = overlay;
}
#pragma warning restore 618
public override void ValidateDictionary()
{
if (overlayDictionary == null)
{
overlayDictionary = new Dictionary<int, OverlayDataAsset>();
UpdateDictionary();
}
}
public override OverlayData InstantiateOverlay(string name)
{
#if SUPER_LOGGING
Debug.Log("Instantiating overlay: " + name);
#endif
var res = Internal_InstantiateOverlay(UMAUtils.StringToHash(name));
if (res == null)
{
throw new UMAResourceNotFoundException("OverlayLibrary: Unable to find: " + name);
}
return res;
}
public override OverlayData InstantiateOverlay(int nameHash)
{
var res = Internal_InstantiateOverlay(nameHash);
if (res == null)
{
throw new UMAResourceNotFoundException("OverlayLibrary: Unable to find hash: " + nameHash);
}
return res;
}
public override OverlayData InstantiateOverlay(string name, Color color)
{
#if SUPER_LOGGING
Debug.Log("Instantiating overlay: " + name);
#endif
var res = Internal_InstantiateOverlay(UMAUtils.StringToHash(name));
if (res == null)
{
throw new UMAResourceNotFoundException("OverlayLibrary: Unable to find: " + name);
}
res.colorData.color = color;
return res;
}
public override OverlayData InstantiateOverlay(int nameHash, Color color)
{
var res = Internal_InstantiateOverlay(nameHash);
if (res == null)
{
throw new UMAResourceNotFoundException("OverlayLibrary: Unable to find hash: " + nameHash);
}
res.colorData.color = color;
return res;
}
private OverlayData Internal_InstantiateOverlay(int nameHash)
{
ValidateDictionary();
OverlayDataAsset source;
if (!overlayDictionary.TryGetValue(nameHash, out source))
{
return null;
}
else
{
return new OverlayData(source);
}
}
public override OverlayDataAsset[] GetAllOverlayAssets()
{
#pragma warning disable 618
return overlayElementList;
#pragma warning restore 618
}
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: fa857c0e4a44bcf42abca9c1cecaa495
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/Scripts/OverlayLibrary.cs
uploadId: 679826
+55
View File
@@ -0,0 +1,55 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
#endif
namespace UMA
{
/// <summary>
/// This class is used by the OverlayEditor to set parameters for viewing overlays in the scene view.
/// See the OverlayAligner scene for an example of both.
/// </summary>
[ExecuteInEditMode]
public class OverlayViewer : MonoBehaviour
{
public TextureMerge TextureMergePrefab;
public SlotDataAsset SlotDataAsset;
public OverlayDataAsset BaseOverlay;
public List<OverlayDataAsset> Overlays = new List<OverlayDataAsset>();
public RawImage ImageViewer;
public GameObject AnnoyingPanel;
#if UNITY_EDITOR
private PopUpAssetInspector inspector;
// Start is called before the first frame update
void Start()
{
CheckInspector();
}
private void OnDestroy()
{
if (inspector != null)
{
inspector.Close();
inspector = null;
}
}
// Update is called once per frame
void Update()
{
CheckInspector();
}
void CheckInspector()
{
if (inspector == null)
{
inspector = PopUpAssetInspector.Create(this);
}
}
#endif
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 426656b7005c7fb41bd0c6a7605df6d2
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/Scripts/OverlayViewer.cs
uploadId: 679826
@@ -0,0 +1,51 @@
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System.Linq;
namespace UMA
{
/// <summary>
/// PopupAssetInspector lets you popup an inspector to view a specific instance of a scriptableobject or monobehavior.
/// This has to live with the base code (not editor code) so the monobehavior can popup the inspector when it's initialized.
/// </summary>
public class PopUpAssetInspector : EditorWindow
{
private Object asset;
private Editor assetEditor;
public static PopUpAssetInspector Create(Object asset)
{
EditorWindow[] windows = Resources.FindObjectsOfTypeAll<EditorWindow>();
var gameWindow = windows.FirstOrDefault(e => e.titleContent.text.Contains("Inspector"));
PopUpAssetInspector window = null;
if (gameWindow != null)
{
window = CreateWindow<PopUpAssetInspector>($"{ObjectNames.NicifyVariableName(asset.name)} ({asset.GetType().Name})",gameWindow.GetType());
}
else
{
window = CreateWindow<PopUpAssetInspector>($"{ObjectNames.NicifyVariableName(asset.name)} ({asset.GetType().Name})");
}
window.asset = asset;
window.assetEditor = Editor.CreateEditor(asset);
return window;
}
private void OnGUI()
{
if (assetEditor == null)
{
assetEditor = Editor.CreateEditor(asset);
}
GUI.enabled = false;
asset = EditorGUILayout.ObjectField("Asset", asset, asset.GetType(), false);
GUI.enabled = true;
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
assetEditor.OnInspectorGUI();
EditorGUILayout.EndVertical();
}
}
}
#endif
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 4a4f870debb12224fb42cd5160d5c90d
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/Scripts/PopupAssetInspector.cs
uploadId: 679826
+146
View File
@@ -0,0 +1,146 @@
using UnityEngine;
using System.Collections.Generic;
using System;
namespace UMA
{
public class RaceLibrary : RaceLibraryBase
{
[SerializeField]
protected RaceData[] raceElementList = new RaceData[0];
private Dictionary<string, RaceData> raceDictionary;
void Awake(){
ValidateDictionary();
}
public override void ValidateDictionary()
{
if (raceDictionary == null)
{
raceDictionary = new Dictionary<string, RaceData>();
UpdateDictionary();
}
}
#pragma warning disable 618
override public void UpdateDictionary()
{
ValidateDictionary();
raceDictionary.Clear();
for (int i = 0; i < raceElementList.Length; i++){
if (raceElementList[i]){
raceElementList[i].UpdateDictionary();
if (!raceDictionary.ContainsKey(raceElementList[i].raceName)){
raceDictionary.Add(raceElementList[i].raceName, raceElementList[i]);
}
}
}
}
override public void AddRace(RaceData race)
{
if (race == null)
{
return;
}
ValidateDictionary();
for (int i = 0; i < raceElementList.Length; i++)
{
if (raceElementList[i].raceName == race.raceName)
{
raceElementList[i] = race;
return;
}
}
var list = new RaceData[raceElementList.Length + 1];
Array.Copy(raceElementList, list, raceElementList.Length );
list[raceElementList.Length] = race;
raceElementList = list;
raceDictionary.Add(race.raceName, race);
}
#pragma warning restore 618
public override RaceData HasRace(string raceName)
{
if ((raceName == null) || (raceName.Length == 0))
{
return null;
}
ValidateDictionary();
RaceData res;
if (!raceDictionary.TryGetValue(raceName, out res))
{
return null;
}
return res;
}
public override RaceData HasRace(int raceHash)
{
if (raceHash == 0)
{
return null;
}
ValidateDictionary();
foreach (string name in raceDictionary.Keys)
{
int hash = UMAUtils.StringToHash(name);
if (hash == raceHash)
{
return raceDictionary[name];
}
}
return null;
}
override public RaceData GetRace(string raceName)
{
if ((raceName == null) || (raceName.Length == 0))
{
return null;
}
ValidateDictionary();
RaceData res;
if (!raceDictionary.TryGetValue(raceName, out res))
{
return null;
}
return res;
}
override public RaceData GetRace(int raceHash)
{
if (raceHash == 0)
{
return null;
}
ValidateDictionary();
foreach (string name in raceDictionary.Keys) {
int hash = UMAUtils.StringToHash(name);
if (hash == raceHash) {
return raceDictionary[name];
}
}
return null;
}
public override RaceData[] GetAllRaces()
{
#pragma warning disable 618
return raceElementList;
#pragma warning restore 618
}
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 3a3f628e56879a34cae8c04646690c27
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/Scripts/RaceLibrary.cs
uploadId: 679826
@@ -0,0 +1,48 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UMA
{
[System.Serializable]
public class SharedColorTable : ScriptableObject, ISerializationCallbackReceiver
{
#if UNITY_EDITOR
[MenuItem("Assets/Create/UMA/Core/Shared Color List")]
public static void CreateSharedColor()
{
UMA.CustomAssetUtility.CreateAsset<SharedColorTable>();
}
#endif
public int channelCount;
[Tooltip("If true, all colors will have the same name, copied from sharedColorName")]
public bool copyColorName = true;
public string sharedColorName;
public OverlayColorData[] colors;
#region ISerializationCallbackReceiver Members
public void OnAfterDeserialize()
{
}
public void OnBeforeSerialize()
{
if (colors != null)
{
for (int i = 0; i < colors.Length; i++)
{
OverlayColorData color = colors[i];
color.EnsureChannelsExact(channelCount);
if (copyColorName && !string.IsNullOrEmpty(sharedColorName))
{
color.name = sharedColorName;
}
}
}
}
#endregion
}
}
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 534edf300d8929243a0d291e413e2f0b
timeCreated: 1427501728
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/Scripts/SharedColorTable.cs
uploadId: 679826
+518
View File
@@ -0,0 +1,518 @@
using UnityEngine;
using System;
using System.Collections.Generic;
using UnityEngine.Serialization;
namespace UMA.CharacterSystem
{
//To enable us to change how this works in the future I just went the whole hog and changed all the public fields to private with public property get/setters
[Serializable]
public class SkeletonModifier
{
public enum SkeletonPropType { Position, Rotation, Scale }
[FormerlySerializedAs("hashName")]
[SerializeField]
private string _hashName;
[FormerlySerializedAs("hash")]
[SerializeField]
private int _hash;
[FormerlySerializedAs("property")]
[SerializeField]
private SkeletonPropType _property = SkeletonPropType.Position;
[FormerlySerializedAs("valuesX")]
[SerializeField]
private spVal _valuesX;
[FormerlySerializedAs("valuesY")]
[SerializeField]
private spVal _valuesY;
[FormerlySerializedAs("valuesZ")]
[SerializeField]
private spVal _valuesZ;
[FormerlySerializedAs("umaDNA")]
[SerializeField]
private UMADnaBase _umaDNA;
public string hashName
{
get { return _hashName; }
set { _hashName = value; }
}
public int hash
{
get { return _hash; }
set { hash = value; }
}
public SkeletonPropType property
{
get { return _property; }
set { _property = value; }
}
public spVal valuesX
{
get { return _valuesX; }
set { _valuesX = new spVal(value); }
}
public spVal valuesY
{
get { return _valuesY; }
set { _valuesY = new spVal(value); }
}
public spVal valuesZ
{
get { return _valuesZ; }
set { _valuesZ = new spVal(value); }
}
//this only needs setting at runtime- confirm
public UMADnaBase umaDNA
{
get { return _umaDNA; }
set { _umaDNA = value; }
}
/// <summary>
/// A dictionary of skeletonModifier default values by SkeletonPropType where the x value is the default value for the property the y value is the default clamp min and the z value is the default clamp max
/// </summary>
public static Dictionary<SkeletonPropType, Vector3> skelAddDefaults = new Dictionary<SkeletonPropType, Vector3>
{
{SkeletonPropType.Position, new Vector3(0f,-0.1f, 0.1f) },
{SkeletonPropType.Rotation, new Vector3(0f,-360f, 360f) },
{SkeletonPropType.Scale, new Vector3(1f,0f, 5f) }
};
[Obsolete("Please use CalculateValueX((UMADnaBase umaDNA) instead")]
public Vector3 ValueX
{
get { return _valuesX.CalculateValue(_umaDNA); }
}
[Obsolete("Please use CalculateValueY((UMADnaBase umaDNA) instead")]
public Vector3 ValueY
{
get { return _valuesY.CalculateValue(_umaDNA); }
}
[Obsolete("Please use CalculateValueZ((UMADnaBase umaDNA) instead")]
public Vector3 ValueZ
{
get { return _valuesZ.CalculateValue(_umaDNA); }
}
public SkeletonModifier() { }
public SkeletonModifier(string _hashName, int _hash, SkeletonPropType _propType)
{
this._hashName = _hashName;
this._hash = _hash;
this._property = _propType;
this._valuesX = new spVal(skelAddDefaults[_propType]);
this._valuesY = new spVal(skelAddDefaults[_propType]);
this._valuesZ = new spVal(skelAddDefaults[_propType]);
}
public SkeletonModifier(SkeletonModifier importedModifier, bool doUpgrade = false)
{
this._hashName = importedModifier._hashName;
this._hash = importedModifier._hash;
this._property = importedModifier._property;
this._valuesX = new spVal(importedModifier._valuesX);
this._valuesY = new spVal(importedModifier._valuesY);
this._valuesZ = new spVal(importedModifier._valuesZ);
this._umaDNA = importedModifier._umaDNA;//dont think this should have ever been serialized
if (doUpgrade)
{
UpgradeToDNAEvaluators();
}
}
//When happy get rid of this and do it via ISerializationCallbacks instead
public void UpgradeToDNAEvaluators()
{
_valuesX.val.ConvertToDNAEvaluators();
_valuesY.val.ConvertToDNAEvaluators();
_valuesZ.val.ConvertToDNAEvaluators();
}
public Vector3 CalculateValueX(UMADnaBase umaDNA)
{
var resVal = _valuesX.CalculateValue(_umaDNA);
return resVal;
}
public Vector3 CalculateValueY(UMADnaBase umaDNA)
{
var resVal = _valuesY.CalculateValue(_umaDNA);
return resVal;
}
public Vector3 CalculateValueZ(UMADnaBase umaDNA)
{
var resVal = _valuesZ.CalculateValue(_umaDNA);
return resVal;
}
//Skeleton Modifier Special Types
[Serializable]
public class spVal
{
[FormerlySerializedAs("val")]
[SerializeField]
private spValValue _val;
[FormerlySerializedAs("min")]
[SerializeField]
private float _min = 1;
[FormerlySerializedAs("max")]
[SerializeField]
private float _max = 1;
public spValValue val
{
get { return _val; }
set { _val = value; }
}
public float min
{
get { return _min; }
set { _min = value; }
}
public float max
{
get { return _max; }
set { _max = value; }
}
public spVal() { }
public spVal(Vector3 startingVals)
{
_val = new spValValue();
_val.value = startingVals.x;
_min = startingVals.y;
_max = startingVals.z;
}
#pragma warning disable 618 //disable obsolete warning
public spVal(spVal importedSpVal)
{
_val = new spValValue();
_val.value = importedSpVal._val.value;
_val.modifiers = new List<spValValue.spValModifier>(importedSpVal._val.modifiers);
_val.modifyingDNA = new DNAEvaluatorList(importedSpVal._val.modifyingDNA);
_min = importedSpVal._min;
_max = importedSpVal._max;
}
#pragma warning restore 618 //disable obsolete warning
public Vector3 CalculateValue(UMADnaBase umaDNA)
{
var thisVal = new Vector3();
//val
thisVal.x = _val.CalculateValue(umaDNA);
//valmin
thisVal.y = _min;
//max
thisVal.z = _max;
return thisVal;
}
//spVal Special Types
[Serializable]
public class spValValue
{
[FormerlySerializedAs("value")]
[SerializeField]
private float _value = 0f;
//Mark as Obsolete
[FormerlySerializedAs("modifiers")]
[SerializeField]
private List<spValModifier> _modifiers = new List<spValModifier>();
[SerializeField]
[DNAEvaluatorList.Config(DNAEvaluatorList.ConfigAttribute.LabelOptions.drawExpandedWithLabel)]
[Tooltip("A list of dna that will be used to modify the bone on this axis. Usually you use 'Cumulative' so that the initial value for the axis is modified by each line here in turn.")]
private DNAEvaluatorList _modifyingDNA = new DNAEvaluatorList(DNAEvaluatorList.AggregationMethodOpts.Cumulative);
public float value
{
get { return _value; }
set { _value = value; }
}
[System.Obsolete("Will be removed in future version. Please use 'modifyingDNA' instead")]
public List<spValModifier> modifiers
{
get { return _modifiers; }
set { _modifiers = value; }
}
public DNAEvaluatorList modifyingDNA
{
get { return _modifyingDNA; }
set { _modifyingDNA = new DNAEvaluatorList(value); }
}
public float CalculateValue(UMADnaBase umaDNA)
{
float thisVal = _value;
if (_modifiers.Count > 0 && _modifyingDNA.Count == 0)
{
thisVal = CalculateLegacyModifiers(thisVal, _modifiers, umaDNA);
}
if(_modifyingDNA.Count > 0)
{
var modDNAResult = _modifyingDNA.ApplyDNAToValue(umaDNA, _value);
thisVal = modDNAResult;
}
return thisVal;
}
private float CalculateLegacyModifiers(float startingVal, List<spValModifier> _modifiers, UMADnaBase umaDNA)
{
float modifierVal = 0;
float tempModifierVal = 0;
string dnaCombineMethod = "";
bool inModifierPair = false;
for (int i = 0; i < _modifiers.Count; i++)
{
if (_modifiers[i].DNATypeName != "None" && (_modifiers[i].modifier == spValModifier.spValModifierType.AddDNA ||
_modifiers[i].modifier == spValModifier.spValModifierType.DivideDNA ||
_modifiers[i].modifier == spValModifier.spValModifierType.MultiplyDNA ||
_modifiers[i].modifier == spValModifier.spValModifierType.SubtractDNA))
{
tempModifierVal = GetUmaDNAValue(_modifiers[i].DNATypeName, umaDNA);
tempModifierVal -= 0.5f;
inModifierPair = true;
if (_modifiers[i].modifier == spValModifier.spValModifierType.AddDNA)
{
dnaCombineMethod = "Add";
}
else if (_modifiers[i].modifier == spValModifier.spValModifierType.DivideDNA)
{
dnaCombineMethod = "Divide";
}
else if (_modifiers[i].modifier == spValModifier.spValModifierType.MultiplyDNA)
{
dnaCombineMethod = "Multiply";
}
else if (_modifiers[i].modifier == spValModifier.spValModifierType.SubtractDNA)
{
dnaCombineMethod = "Subtract";
}
}
else
{
if (_modifiers[i].modifier == spValModifier.spValModifierType.Add)
{
modifierVal += (tempModifierVal + _modifiers[i].modifierValue);
tempModifierVal = 0;
inModifierPair = false;
}
else if (_modifiers[i].modifier == spValModifier.spValModifierType.Divide)
{
modifierVal += (tempModifierVal / _modifiers[i].modifierValue);
tempModifierVal = 0;
inModifierPair = false;
}
else if (_modifiers[i].modifier == spValModifier.spValModifierType.Multiply)
{
modifierVal += (tempModifierVal * _modifiers[i].modifierValue);
tempModifierVal = 0;
inModifierPair = false;
}
else if (_modifiers[i].modifier == spValModifier.spValModifierType.Subtract)
{
modifierVal += (tempModifierVal - _modifiers[i].modifierValue);
tempModifierVal = 0;
inModifierPair = false;
}
}
if (modifierVal != 0 && inModifierPair == false)
{
if (dnaCombineMethod == "Add")
{
startingVal += modifierVal;
}
if (dnaCombineMethod == "Subtract")
{
startingVal -= modifierVal;
}
if (dnaCombineMethod == "Multiply")
{
startingVal *= modifierVal;
}
if (dnaCombineMethod == "Divide")
{
startingVal /= modifierVal;
}
modifierVal = 0;
dnaCombineMethod = "";
}
}
//in the case of left/Right(Up)LegAdjust the umadna is subtracted from the result without being multiplied by anything
//this accounts for the scenario where umaDna is left trailing with no correcponding add/subtract/multiply/divide multiplier
if (tempModifierVal != 0 && inModifierPair != false)
{
if (dnaCombineMethod == "Add")
{
startingVal += tempModifierVal;
}
if (dnaCombineMethod == "Subtract")
{
startingVal -= tempModifierVal;
}
if (dnaCombineMethod == "Multiply")
{
startingVal *= tempModifierVal;
}
if (dnaCombineMethod == "Divide")
{
startingVal /= tempModifierVal;
}
dnaCombineMethod = "";
modifierVal = 0;
tempModifierVal = 0;
inModifierPair = false;
}
return startingVal;
}
//Obsolete
public float GetUmaDNAValue(string DNATypeName, UMADnaBase umaDnaIn)
{
if (umaDnaIn == null)
{
return 0.5f;
}
DynamicUMADnaBase umaDna = (DynamicUMADnaBase)umaDnaIn;
float val = 0.5f;
if (DNATypeName == "None" || umaDna == null)
{
return val;
}
val = umaDna.GetValue(DNATypeName, true);//implimented a 'failSilently' option here because recipes may have dna in that the dna asset no longer has
return val;
}
#pragma warning disable 618 //disable obsolete warning
//make this staic?
public void ConvertToDNAEvaluators()
{
spValModifier accessoryMod = null;
DNAEvaluator.CalcOption calcOption = DNAEvaluator.CalcOption.Add;
float multiplier = 1f;
if (_modifiers.Count > 0 && _modifyingDNA.Count == 0)
{
_modifyingDNA.Clear();
for (int i = 0; i < modifiers.Count; i++)
{
accessoryMod = null;
multiplier = 1f;
if (!string.IsNullOrEmpty(modifiers[i].DNATypeName) && modifiers[i].modifier.ToString().IndexOf("DNA") > -1)
{
if ((i + 1) < modifiers.Count && modifiers[i + 1].modifier.ToString().IndexOf("DNA") < 0)
{
accessoryMod = modifiers[i + 1];
}
if (modifiers[i].modifier == spValModifier.spValModifierType.AddDNA)
{
calcOption = DNAEvaluator.CalcOption.Add;
}
else if (modifiers[i].modifier == spValModifier.spValModifierType.DivideDNA)
{
calcOption = DNAEvaluator.CalcOption.Divide;
}
else if (modifiers[i].modifier == spValModifier.spValModifierType.MultiplyDNA)
{
calcOption = DNAEvaluator.CalcOption.Multiply;
}
else if (modifiers[i].modifier == spValModifier.spValModifierType.SubtractDNA)
{
calcOption = DNAEvaluator.CalcOption.Subtract;
}
if (accessoryMod != null)
{
if (accessoryMod.modifier == spValModifier.spValModifierType.Multiply)
{
multiplier = accessoryMod.modifierValue;
}
else if (accessoryMod.modifier == spValModifier.spValModifierType.Divide)
{
multiplier = (1f / accessoryMod.modifierValue);
}
//otherwise we are stuffed- you can do add/subtract by using a different evaluator but I'm not gonna do that here
else
{
multiplier = 1f;
}
}
_modifyingDNA.Add(new DNAEvaluator(modifiers[i].DNATypeName, DNAEvaluationGraph.Default, multiplier, calcOption));
}
if (accessoryMod != null)
{
i++;
}
}
}
_modifyingDNA.aggregationMethod = DNAEvaluatorList.AggregationMethodOpts.Cumulative;
_modifiers.Clear();
}
#pragma warning restore 618 //restore obsolete warning
//Mark as obsolete
//spValValue Special Types
//The aim is to replace these with DNAEvaluators
[Serializable]
public class spValModifier
{
public enum spValModifierType { Add, Subtract, Multiply, Divide, AddDNA, SubtractDNA, MultiplyDNA, DivideDNA }
[FormerlySerializedAs("modifier")]
[SerializeField]
private spValModifierType _modifier = spValModifierType.Add;
[FormerlySerializedAs("DNATypeName")]
[SerializeField]
private string _DNATypeName = "";
[FormerlySerializedAs("modifierValue")]
[SerializeField]
private float _modifierValue = 0f;
public spValModifierType modifier
{
get { return _modifier; }
set { _modifier = value; }
}
public string DNATypeName
{
get{return _DNATypeName;}
set{_DNATypeName = value;}
}
public float modifierValue
{
get { return _modifierValue; }
set { _modifierValue = value; }
}
}
}
}
}
}
@@ -0,0 +1,20 @@
fileFormatVersion: 2
guid: 09916f0b87dd9e444920d6daa26130bc
timeCreated: 1539887645
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/Scripts/SkeletonModifier.cs
uploadId: 679826
+205
View File
@@ -0,0 +1,205 @@
using UnityEngine;
namespace UMA
{
public static class SkeletonTools
{
#if UNITY_EDITOR
[UnityEditor.MenuItem("UMA/Verify Slot Mesh")]
static void Start()
{
var transforms = UnityEditor.Selection.GetTransforms(UnityEditor.SelectionMode.Editable);
if (transforms.Length != 2)
{
if(Debug.isDebugBuild)
{
Debug.LogError("To Compare Skeletons you need to select two characters in your hierarchy.");
}
return;
}
var root1 = LocateRoot(transforms[0]);
var root2 = LocateRoot(transforms[1]);
int failure = 0;
CompareSkeletonRecursive(root1, root2, ref failure);
}
#endif
private static void CompareRootBone(Transform raceRoot, Transform slotRoot, ref int failure)
{
var rootIterator = slotRoot;
while (rootIterator.parent != null)
{
rootIterator = rootIterator.parent;
}
if (RecursiveFindBone(rootIterator, raceRoot) == null)
{
if (Debug.isDebugBuild)
{
Debug.LogError("Race root: " + raceRoot.name + " not found in the slot hierarchy");
}
failure++;
}
}
private static Transform RecursiveFindBone(Transform bone, Transform raceRoot)
{
if (bone.name == raceRoot.name)
{
return bone;
}
for (int i = 0; i < bone.childCount; i++)
{
var result = RecursiveFindBone(bone.GetChild(i), raceRoot);
if (result != null)
{
return result;
}
}
return null;
}
public static Transform RecursiveFindBone(Transform bone, string Name)
{
if (bone.name == Name)
{
return bone;
}
for (int i = 0; i < bone.childCount; i++)
{
var result = RecursiveFindBone(bone.GetChild(i), Name);
if (result != null)
{
return result;
}
}
return null;
}
private static void CompareSkeletonRecursive(Transform race, Transform slot, ref int failure)
{
if ((race.localScale - slot.localScale).sqrMagnitude > 0.0001f)
{
failure++;
if (Debug.isDebugBuild)
{
Debug.LogError("Scale on " + race.name + " differs by " + (race.localScale - slot.localScale), slot);
}
}
if ((race.localPosition - slot.localPosition).sqrMagnitude > 0.0001f)
{
failure++;
if (Debug.isDebugBuild)
{
Debug.LogError("Position on " + race.name + " differs by " + (race.localPosition - slot.localPosition), slot);
}
}
if (race.localRotation != slot.localRotation)
{
failure++;
if (Debug.isDebugBuild)
{
Debug.LogError("Rotation on " + race.name + " differs by " + Quaternion.Angle(race.localRotation, slot.localRotation) + " degrees", slot);
}
}
for (int i = 0; i < race.childCount; i++)
{
var raceChild = race.GetChild(i);
var slotChild = slot.Find(raceChild.name);
if (slotChild != null)
{
CompareSkeletonRecursive(raceChild, slotChild, ref failure);
}
else
{
failure++;
if (Debug.isDebugBuild)
{
Debug.LogError("Bone is missing: " + raceChild.name + " on bone: " + slot.name, slot);
}
}
if (failure >= 50)
{
return;
}
}
}
public static Transform LocateRoot(Transform parent)
{
for (int i = 0; i < parent.childCount; i++)
{
var child = parent.GetChild(i);
if (child.childCount == 0)
{
continue;
}
return child;
}
return null;
}
public enum ValidateResult
{
Ok,
InvalidScale,
SkeletonProblem,
}
public static ValidateResult ValidateSlot(SkinnedMeshRenderer RaceSMR, SkinnedMeshRenderer SlotSMR, out string description)
{
var slotMesh = new Mesh();
#if UMA_32BITBUFFERS
slotMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#endif
SlotSMR.BakeMesh(slotMesh);
var bounds = slotMesh.bounds;
if (bounds.max.y < 0.05f)
{
description = "Scale Factor on the Model Import Settings seems to be wrong!";
return ValidateResult.InvalidScale;
}
int failure = 0;
CompareSkeletonRecursive(LocateRoot(RaceSMR.transform.parent), LocateRoot(SlotSMR.transform.parent), ref failure);
CompareRootBone(RaceSMR.rootBone, SlotSMR.rootBone, ref failure);
if (failure > 0)
{
description = "The Skeleton Hierarchy seems off, check the log for more info.";
return ValidateResult.SkeletonProblem;
}
description = "Everything seems fine.";
return ValidateResult.Ok;
}
public static void ForceSkeleton(SkinnedMeshRenderer SourceSMR, SkinnedMeshRenderer DestSMR)
{
ForceSkeletonRecursive(LocateRoot(SourceSMR.transform.parent), LocateRoot(DestSMR.transform.parent));
}
private static void ForceSkeletonRecursive(Transform source, Transform dest)
{
dest.localScale = source.localScale;
dest.localPosition = source.localPosition;
dest.localRotation = source.localRotation;
for (int i = 0; i < source.childCount; i++)
{
var raceChild = source.GetChild(i);
var slotChild = dest.Find(raceChild.name);
if (slotChild != null)
{
ForceSkeletonRecursive(raceChild, slotChild);
}
}
}
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 792d50105c7839542a5bf34d2b8f4e39
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/Scripts/SkeletonTools.cs
uploadId: 679826
+174
View File
@@ -0,0 +1,174 @@
using UnityEngine;
using System.Collections.Generic;
using System;
namespace UMA
{
public class SlotLibrary : SlotLibraryBase
{
[SerializeField]
protected SlotDataAsset[] slotElementList = new SlotDataAsset[0];
[NonSerialized]
private Dictionary<int, SlotDataAsset> slotDictionary;
void Awake()
{
ValidateDictionary();
}
#pragma warning disable 618
override public void UpdateDictionary()
{
ValidateDictionary();
slotDictionary.Clear();
for (int i = 0; i < slotElementList.Length; i++)
{
if (slotElementList[i])
{
var hash = slotElementList[i].nameHash;
if (!slotDictionary.ContainsKey(hash))
{
slotDictionary.Add(hash, slotElementList[i]);
}
}
}
}
public override void ValidateDictionary()
{
if (slotDictionary == null)
{
slotDictionary = new Dictionary<int, SlotDataAsset>();
UpdateDictionary();
}
}
public override void AddSlotAsset(SlotDataAsset slot)
{
ValidateDictionary();
if (slotDictionary.ContainsKey(slot.nameHash))
{
for (int i = 0; i < slotElementList.Length; i++)
{
if (slotElementList[i].slotName == slot.slotName)
{
slotElementList[i] = slot;
break;
}
}
}
else
{
var list = new SlotDataAsset[slotElementList.Length + 1];
for (int i = 0; i < slotElementList.Length; i++)
{
list[i] = slotElementList[i];
}
list[list.Length - 1] = slot;
slotElementList = list;
}
slotDictionary[slot.nameHash] = slot;
}
#pragma warning restore 618
public override bool HasSlot(string name)
{
ValidateDictionary();
return slotDictionary.ContainsKey(UMAUtils.StringToHash(name));
}
public override bool HasSlot(int nameHash)
{
ValidateDictionary();
return slotDictionary.ContainsKey(nameHash);
}
public override SlotData InstantiateSlot(string name)
{
#if SUPER_LOGGING
Debug.Log("Instantiating slot: " + name);
#endif
var res = Internal_InstantiateSlot(UMAUtils.StringToHash(name));
if (res == null)
{
throw new UMAResourceNotFoundException("SlotLibrary: Unable to find: " + name);
}
return res;
}
public override SlotData InstantiateSlot(int nameHash)
{
var res = Internal_InstantiateSlot(nameHash);
if (res == null)
{
throw new UMAResourceNotFoundException("SlotLibrary: Unable to find hash: " + nameHash);
}
return res;
}
public override SlotData InstantiateSlot(string name, List<OverlayData> overlayList)
{
#if SUPER_LOGGING
Debug.Log("Instantiating slot: " + name);
#endif
var res = Internal_InstantiateSlot(UMAUtils.StringToHash(name));
if (res == null)
{
throw new UMAResourceNotFoundException("SlotLibrary: Unable to find: " + name);
}
res.SetOverlayList(overlayList);
return res;
}
public override SlotData InstantiateSlot(int nameHash, List<OverlayData> overlayList)
{
var res = Internal_InstantiateSlot(nameHash);
if (res == null)
{
#if UNITY_EDITOR
foreach (var path in UnityEditor.AssetDatabase.GetAllAssetPaths())
{
if (!path.EndsWith(".asset"))
{
continue;
}
var slot = UnityEditor.AssetDatabase.LoadAssetAtPath(path, typeof(SlotDataAsset)) as SlotDataAsset;
if (slot == null)
{
continue;
}
if (slot.nameHash == nameHash)
{
throw new UMAResourceNotFoundException("SlotLibrary: Unable to find: " + slot.slotName);
}
}
#endif
throw new UMAResourceNotFoundException("SlotLibrary: Unable to find hash: " + nameHash);
}
res.SetOverlayList(overlayList);
return res;
}
private SlotData Internal_InstantiateSlot(int nameHash)
{
ValidateDictionary();
SlotDataAsset source;
if (!slotDictionary.TryGetValue(nameHash, out source))
{
return null;
}
else
{
return new SlotData(source);
}
}
public override SlotDataAsset[] GetAllSlotAssets()
{
#pragma warning disable 618
return slotElementList;
#pragma warning restore 618
}
}
}
@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: b0a16fef7b92feb47836d6fc3f49ca0d
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/Scripts/SlotLibrary.cs
uploadId: 679826
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: d395228424b033644afbc403aecfe647
timeCreated: 1489175242
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/Scripts/UMAAssetIndexer.cs
uploadId: 679826
+71
View File
@@ -0,0 +1,71 @@
using UnityEngine;
namespace UMA.CharacterSystem
{
public class UMAColorFader : MonoBehaviour
{
public enum FadeType
{
FadeIn,
FadeOut
};
public DynamicCharacterAvatar DCA;
private OverlayColorData Color = new OverlayColorData(3);
public FadeType Fade = FadeType.FadeOut;
public string ColorName = "Fade";
public float time = 3.0f;
// Start is called before the first frame update
void Start()
{
DCA = GetComponent<DynamicCharacterAvatar>();
if (DCA != null)
{
Color = DCA.GetColor(ColorName);
if (Color == null)
{
Color = new OverlayColorData(3);
}
DCA.SetColor(ColorName, Color, false);
}
}
// Update is called once per frame
void Update()
{
if (DCA == null)
{
return;
}
bool done = false;
float FadeVal = Time.deltaTime / time;
if (Fade == FadeType.FadeIn)
{
Color.channelMask[0].a += FadeVal;
if (Color.channelMask[0].a >= 1.0f)
{
done = true;
}
DCA.SetColor(ColorName,Color, true);
}
else
{
Color.channelMask[0].a -= FadeVal;
if (Color.channelMask[0].a <= 0.0f)
{
done = true;
}
DCA.SetColor(ColorName, Color, true);
}
if (done)
{
Destroy(this);
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0917f921f021f0e479750d7ef65832d7
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/Scripts/UMAColorFader.cs
uploadId: 679826
+10
View File
@@ -0,0 +1,10 @@
using UnityEngine;
using UMA;
public class UMAColorScheme : ScriptableObject
{
public string Description;
public Sprite Icon;
public Object UserObject;
public OverlayColorData ColorData;
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 47cec62de2e861542b419580d661003d
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/Scripts/UMAColorScheme.cs
uploadId: 679826
+41
View File
@@ -0,0 +1,41 @@
using UMA.CharacterSystem;
using UnityEngine;
namespace UMA
{
public class UMALODLogger : MonoBehaviour
{
public DynamicCharacterAvatar avatar;
public string slotID;
public string lastLOD;
// Update is called once per frame
void Update()
{
if (avatar != null)
{
if (avatar.umaData != null && avatar.umaData.umaRecipe != null)
{
var slots = avatar.umaData.umaRecipe.slotDataList;
for (int i = 0; i < slots.Length; i++)
{
SlotData slot = slots[i];
if (slot != null)
{
if (slot.slotName != null)
{
if (slot.slotName.ToLower().Contains(slotID.ToLower()))
{
lastLOD = slot.slotName;
}
}
}
}
}
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6f0e35f1d60ea8945be21e1137a570b5
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/Scripts/UMALODLogger.cs
uploadId: 679826
+191
View File
@@ -0,0 +1,191 @@
using UMA.CharacterSystem;
using UMA;
using UnityEngine;
using UnityEngine.Events;
[ExecuteInEditMode]
public class UMAMountedItem : MonoBehaviour
{
[Tooltip("The name of the bone. Case must match.")]
public string BoneName;
[Tooltip("Unique ID for this object. Example: 'RightHandMount")]
public string ID;
public Vector3 Position;
public Quaternion Orientation;
public string IgnoreTag = "UMAIgnore";
[Tooltip("If true the object will scale to bone DNA")]
public bool setScale = true;
[Tooltip("Mount this item in startup. Useful when instantiating prefabs.")]
public bool MountOnStart;
private int BoneHash;
private DynamicCharacterAvatar avatar;
private Transform MountPoint; // This is the mount point we create/update.
private UMAData lastUmaData;
// Start is called before the first frame update
void Start()
{
Initialize();
gameObject.tag = IgnoreTag;
}
private bool Initialize()
{
avatar = GetComponentInParent<DynamicCharacterAvatar>();
if (avatar == null)
{
return false;
}
avatar.CharacterUpdated.AddListener(new UnityAction<UMAData>(CharacterUpdated));
#if UNITY_EDITOR
if (!Application.isPlaying || MountOnStart)
{
MountPoint = EditorFindOrCreateMountpoint();
SetMountTransform();
}
#endif
return true;
}
// Used when mounting manually.
public bool MountItem()
{
if (avatar == null)
{
Initialize();
if (avatar == null)
{
return false;
}
}
MountPoint = FindOrCreateMountpoint(avatar.umaData);
SetMountTransform();
return true;
}
#if UNITY_EDITOR
public Transform EditorFindOrCreateMountpoint()
{
Transform BoneTransform = SkeletonTools.RecursiveFindBone(avatar.gameObject.transform, BoneName);
if (BoneTransform == null)
{
return null;
}
foreach (Transform child in BoneTransform)
{
if (child.name == ID)
{
return child;
}
}
return CreateMountpoint(BoneTransform, avatar.gameObject.layer);
}
#endif
public void ResetMountPoint()
{
MountPoint = FindOrCreateMountpoint(avatar.umaData);
SetMountTransform();
}
public Transform FindOrCreateMountpoint(UMAData umaData)
{
if (string.IsNullOrEmpty(BoneName))
{
return null;
}
if (umaData == null || umaData.skeleton == null)
{
return null;
}
BoneHash = UMAUtils.StringToHash(BoneName);
Transform BoneTransform = umaData.skeleton.GetBoneTransform(BoneHash);
if (BoneTransform == null)
{
return null;
}
foreach (Transform child in BoneTransform)
{
if (child.name == ID)
{
UpdateMountPoint(child);
return child;
}
}
return CreateMountpoint(BoneTransform, umaData.gameObject.layer);
}
private void UpdateMountPoint(Transform newRoot)
{
newRoot.transform.localPosition = Position;
newRoot.transform.localRotation = Orientation;
newRoot.transform.localScale = Vector3.one;
}
private Transform CreateMountpoint(Transform BoneTransform, int Layer)
{
GameObject newRoot = new GameObject(ID);
newRoot.layer = Layer;
newRoot.transform.parent = BoneTransform;
newRoot.transform.localPosition = Position;
newRoot.transform.localRotation = Orientation;
newRoot.transform.localScale = Vector3.one;
return newRoot.transform;
}
public void CharacterUpdated(UMAData umaData)
{
// Debug.Log("Getting bone info");
MountPoint = FindOrCreateMountpoint(umaData);
lastUmaData = umaData;
}
void LateUpdate()
{
if (avatar == null)
{
if (!Initialize())
{
return;
}
}
if (MountPoint != null)
{
// get the worldpos/orientation of the mounted object.
// copy to this object.
SetMountTransform();
}
else
{
if (lastUmaData != null)
{
FindOrCreateMountpoint(lastUmaData);
SetMountTransform();
}
}
}
private void SetMountTransform()
{
// Debug.Log("Setting mount transform");
if (MountPoint != null)
{
Vector3 globalScale = avatar.gameObject.transform.lossyScale;
transform.position = MountPoint.position;
transform.rotation = MountPoint.rotation;
if (setScale == true)
{
MountPoint.localScale = MountPoint.parent.localScale;
transform.localScale = MountPoint.localScale;
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f2a1a3efd8e885446a13fd602376e584
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/Scripts/UMAMountedItem.cs
uploadId: 679826
+108
View File
@@ -0,0 +1,108 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class UMANormalViewer : MonoBehaviour
{
#if UNITY_EDITOR
[SerializeField]
private SkinnedMeshRenderer _skinnedMesh = null;
[SerializeField]
private bool _displayWireframe = false;
[SerializeField]
private NormalsDrawData _vertexNormals = new NormalsDrawData(new Color32(200, 0, 0, 240), false);
[System.Serializable]
private class NormalsDrawData
{
[SerializeField]
protected DrawType _draw = DrawType.Selected;
protected enum DrawType { Never, Selected, Always }
[SerializeField]
protected float _length = 0.035f;
[SerializeField]
protected Color _normalColor;
private Color _baseColor = new Color32(255, 133, 0, 255);
public float vertexCircumference = 0.0125f;
public bool showVertexes = false;
public NormalsDrawData(Color normalColor, bool draw)
{
_normalColor = normalColor;
_draw = draw ? DrawType.Selected : DrawType.Never;
}
public bool CanDraw(bool isSelected)
{
return (_draw == DrawType.Always) || (_draw == DrawType.Selected && isSelected);
}
public void Draw(Vector3 from, Vector3 direction)
{
if (Camera.current.transform.InverseTransformDirection(direction).z < 0f)
{
if (showVertexes)
{
Gizmos.color = _baseColor;
Gizmos.DrawWireSphere(from, vertexCircumference);
}
Gizmos.color = _normalColor;
Gizmos.DrawRay(from, direction * _length);
}
}
}
void OnDrawGizmosSelected()
{
#pragma warning disable CS0618 // Type or member is obsolete
EditorUtility.SetSelectedWireframeHidden(GetComponent<Renderer>(), !_displayWireframe);
#pragma warning restore CS0618 // Type or member is obsolete
OnDrawNormals(true);
}
void OnDrawGizmos()
{
if (!Selection.Contains(this))
{
OnDrawNormals(false);
}
}
public Mesh mesh;
private void OnDrawNormals(bool isSelected)
{
if (_skinnedMesh == null)
{
_skinnedMesh = GetComponent<SkinnedMeshRenderer>();
if (_skinnedMesh == null)
{
return;
}
}
if (mesh == null && _skinnedMesh != null)
{
mesh = new Mesh();
_skinnedMesh.BakeMesh(mesh, true);
}
if (mesh == null)
{ return; }
//Draw Vertex Normals
if (_vertexNormals.CanDraw(isSelected))
{
Vector3[] vertices = mesh.vertices;
Vector3[] normals = mesh.normals;
for (int i = 0; i < vertices.Length; i++)
{
_vertexNormals.Draw(transform.TransformPoint(vertices[i]), transform.TransformVector(normals[i]));
}
}
}
#endif
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a70a8321cb89f0646b48c7402623b381
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/Scripts/UMANormalViewer.cs
uploadId: 679826
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More