Netcode Bootstrap
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f076b37cd5c8ec45a7f28f5f437e9a7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,795 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using Rukhanka.Hybrid.RTP;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Assertions;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using Unity.Entities;
|
||||
using AnimationClip = UnityEngine.AnimationClip;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
[BurstCompile]
|
||||
public partial class AnimationClipBaker
|
||||
{
|
||||
// To reduce huge memory consumption during baking of many animations, I will reuse temporarty buffers
|
||||
NativeList<KeyFrame> keyFramesList = new (Allocator.Temp);
|
||||
NativeList<Track> trackList = new (Allocator.Temp);
|
||||
NativeList<uint> trackGroupHashes = new (Allocator.Temp);
|
||||
NativeList<uint> trackGroupOffsets = new (Allocator.Temp);
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
NativeList<FixedString128Bytes> trackGroupNames = new (Allocator.Temp);
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ParsedCurveBinding
|
||||
{
|
||||
public BindingType bindingType;
|
||||
public uint channelIndex;
|
||||
public string boneName;
|
||||
public string channelName;
|
||||
|
||||
public bool IsValid() => boneName.Length > 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
(string, string) SplitPath(string path)
|
||||
{
|
||||
var arr = path.Split('/');
|
||||
Assert.IsTrue(arr.Length > 0);
|
||||
var rv = (arr.Last(), arr.Length > 1 ? arr[^2] : "");
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BindingType PickGenericBindingTypeByString(string bindingString) => bindingString switch
|
||||
{
|
||||
"m_LocalPosition" => BindingType.Translation,
|
||||
"m_LocalRotation" => BindingType.Quaternion,
|
||||
"localEulerAngles" => BindingType.EulerAngles,
|
||||
"localEulerAnglesRaw" => BindingType.EulerAngles,
|
||||
"m_LocalScale" => BindingType.Scale,
|
||||
_ => BindingType.Unknown
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint ChannelIndexFromString(string c) => c switch
|
||||
{
|
||||
"x" => 0,
|
||||
"y" => 1,
|
||||
"z" => 2,
|
||||
"w" => 3,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string ConstructBoneClipName(string name, string path, Type animatedObjectType)
|
||||
{
|
||||
if (animatedObjectType == typeof(UnityEngine.Animator))
|
||||
return SpecialBones.AnimatorTypeName;
|
||||
|
||||
var rv = name;
|
||||
if (animatedObjectType != typeof(UnityEngine.SkinnedMeshRenderer))
|
||||
{
|
||||
// Empty name string is unnamed root bone
|
||||
if (name.Length == 0 && path.Length == 0)
|
||||
{
|
||||
rv = SpecialBones.UnnamedRootBoneName;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int2 CopyKeyFrames(Keyframe[] keysArr, ref NativeList<KeyFrame> outKeyFrames)
|
||||
{
|
||||
var rv = new int2(outKeyFrames.Length, keysArr.Length);
|
||||
foreach (var k in keysArr)
|
||||
{
|
||||
var kf = new KeyFrame()
|
||||
{
|
||||
time = k.time,
|
||||
inTan = math.select(0, k.inTangent, math.isfinite(k.inTangent)),
|
||||
outTan = math.select(0, k.outTangent, math.isfinite(k.outTangent)),
|
||||
v = k.value
|
||||
};
|
||||
outKeyFrames.Add(kf);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Track CreateTrackData(int2 keyFrameRange, ParsedCurveBinding pb)
|
||||
{
|
||||
var rv = new Track();
|
||||
rv.keyFrameRange = keyFrameRange;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
rv.name = $"{pb.boneName}.{pb.channelName}";
|
||||
#endif
|
||||
// For unknown binding types treat props as channel name hash
|
||||
if (pb.bindingType == BindingType.Unknown)
|
||||
{
|
||||
rv.props = Track.CalculateHash(pb.channelName);
|
||||
}
|
||||
else
|
||||
{
|
||||
rv.bindingType = pb.bindingType;
|
||||
rv.channelIndex = pb.channelIndex;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void InsertTrackIntoGroup(Track t, string trackName)
|
||||
{
|
||||
// Tracks with same path hash will go into same track group. This is needed to speedup access of bone data
|
||||
// (no need to query each attribute hash)
|
||||
|
||||
var nameHash = trackName.CalculateHash32();
|
||||
// Search for name in already existing groups
|
||||
var i = trackGroupHashes.IndexOf(nameHash);
|
||||
|
||||
if (i < 0)
|
||||
{
|
||||
// Create new track group and add track into it
|
||||
trackGroupHashes.Add(nameHash);
|
||||
trackGroupOffsets.Add((uint)trackList.Length);
|
||||
trackList.Add(t);
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
trackGroupNames.Add(trackName);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insert track into existing group
|
||||
var startIndex = (int)trackGroupOffsets[i];
|
||||
trackList.InsertRange(startIndex, 1);
|
||||
trackList[startIndex] = t;
|
||||
|
||||
// Shift all other groups to the right
|
||||
for (var l = 0; l < trackGroupOffsets.Length; ++l)
|
||||
{
|
||||
ref var tgo = ref trackGroupOffsets.ElementAt(l);
|
||||
if (tgo > startIndex)
|
||||
tgo += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ParsedCurveBinding ParseGenericCurveBinding(EditorCurveBinding b)
|
||||
{
|
||||
var rv = new ParsedCurveBinding();
|
||||
|
||||
var t = b.propertyName.Split('.');
|
||||
var propName = t[0];
|
||||
var channel = t.Length > 1 ? t[1] : "";
|
||||
|
||||
rv.channelIndex = ChannelIndexFromString(channel);
|
||||
rv.bindingType = PickGenericBindingTypeByString(propName);
|
||||
rv.channelName = b.propertyName;
|
||||
var nameAndPath = SplitPath(b.path);
|
||||
rv.boneName = ConstructBoneClipName(nameAndPath.Item1, nameAndPath.Item2, b.type);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int GetHumanBoneIndexForHumanName(in HumanDescription hd, FixedStringName humanBoneName)
|
||||
{
|
||||
var humanBoneIndexInAvatar = Array.FindIndex(hd.human, x => x.humanName == humanBoneName);
|
||||
return humanBoneIndexInAvatar;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ParsedCurveBinding ParseHumanoidCurveBinding(EditorCurveBinding b, Avatar avatar)
|
||||
{
|
||||
if (!humanoidMappingTable.TryGetValue(b.propertyName, out var rv))
|
||||
return ParseGenericCurveBinding(b);
|
||||
|
||||
var hd = avatar.humanDescription;
|
||||
var humanBoneIndexInAvatar = GetHumanBoneIndexForHumanName(hd, rv.boneName);
|
||||
if (humanBoneIndexInAvatar < 0)
|
||||
return rv;
|
||||
|
||||
if (rv.bindingType == BindingType.HumanMuscle)
|
||||
{
|
||||
var humanBoneDef = hd.human[humanBoneIndexInAvatar];
|
||||
rv.boneName = humanBoneDef.boneName;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ParsedCurveBinding ParseCurveBinding(AnimationClip ac, EditorCurveBinding b, Avatar avatar)
|
||||
{
|
||||
var rv = ac.isHumanMotion ?
|
||||
ParseHumanoidCurveBinding(b, avatar) :
|
||||
ParseGenericCurveBinding(b);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
KeyFrame AddKeyFrameFromFloatValue(float2 key, float v)
|
||||
{
|
||||
var kf = new KeyFrame()
|
||||
{
|
||||
time = key.x,
|
||||
inTan = key.y,
|
||||
outTan = key.y,
|
||||
v = v
|
||||
};
|
||||
return kf;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
void ComputeTangents(in Span<KeyFrame> keyFrames)
|
||||
{
|
||||
for (int i = 0; i < keyFrames.Length; ++i)
|
||||
{
|
||||
var p0 = i == 0 ? keyFrames[0] : keyFrames[i - 1];
|
||||
var p1 = keyFrames[i];
|
||||
var p2 = i == keyFrames.Length - 1 ? keyFrames[i] : keyFrames[i + 1];
|
||||
|
||||
var outV = math.normalizesafe(new float2(p2.time, p2.v) - new float2(p1.time, p1.v));
|
||||
var outTan = outV.x > 0.0001f ? outV.y / outV.x : 0;
|
||||
|
||||
var inV = math.normalizesafe(new float2(p1.time, p1.v) - new float2(p0.time, p0.v));
|
||||
var inTan = inV.x > 0.0001f ? inV.y / inV.x : 0;
|
||||
|
||||
var dt = math.abs(inTan) + math.abs(outTan);
|
||||
var f = dt > 0 ? math.abs(inTan) / dt : 0;
|
||||
|
||||
var avgTan = math.lerp(inTan, outTan, f);
|
||||
|
||||
var k = keyFrames[i];
|
||||
k.outTan = avgTan;
|
||||
k.inTan = avgTan;
|
||||
keyFrames[i] = k;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NativeList<float> CreateKeyframeTimes(float animationLength, float dt, float frameTime)
|
||||
{
|
||||
var numFrames = (int)math.ceil(animationLength / dt) + 1;
|
||||
var rv = new NativeList<float>(numFrames, Allocator.Temp);
|
||||
|
||||
if (frameTime >= 0)
|
||||
{
|
||||
rv.Add(frameTime);
|
||||
return rv;
|
||||
}
|
||||
|
||||
var curTime = 0.0f;
|
||||
for (var i = 0; i < numFrames; ++i)
|
||||
{
|
||||
rv.Add(curTime);
|
||||
curTime += dt;
|
||||
}
|
||||
|
||||
if (rv.Length > 0)
|
||||
rv[^1] = math.min(animationLength, rv[^1]);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ReadCurvesFromTransform(Transform tr, in NativeArray<Track> trackSpan, int keyIndex, float time)
|
||||
{
|
||||
quaternion q = tr.localRotation;
|
||||
float3 t = tr.localPosition;
|
||||
|
||||
Span<float> vArr = stackalloc float[7];
|
||||
vArr[0] = t.x;
|
||||
vArr[1] = t.y;
|
||||
vArr[2] = t.z;
|
||||
vArr[3] = q.value.x;
|
||||
vArr[4] = q.value.y;
|
||||
vArr[5] = q.value.z;
|
||||
vArr[6] = q.value.w;
|
||||
|
||||
for (int l = 0; l < vArr.Length; ++l)
|
||||
{
|
||||
var kfIndex = trackSpan[l].keyFrameRange.x + keyIndex;
|
||||
var kf = AddKeyFrameFromFloatValue(time, vArr[l]);
|
||||
keyFramesList[kfIndex] = kf;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void SampleUnityAnimation(AnimationClip ac, Animator anm, ValueTuple<Transform, uint>[] trs, bool applyRootMotion, float frameTime)
|
||||
{
|
||||
if (trs.Length == 0)
|
||||
return;
|
||||
|
||||
var sampleAnimationFrameTime = 1 / 60.0f;
|
||||
var keysList = CreateKeyframeTimes(ac.length, sampleAnimationFrameTime, frameTime);
|
||||
|
||||
var tracks = new []
|
||||
{
|
||||
new Track(BindingType.Translation, 0),
|
||||
new Track(BindingType.Translation, 1),
|
||||
new Track(BindingType.Translation, 2),
|
||||
new Track(BindingType.Quaternion, 0),
|
||||
new Track(BindingType.Quaternion, 1),
|
||||
new Track(BindingType.Quaternion, 2),
|
||||
new Track(BindingType.Quaternion, 3),
|
||||
};
|
||||
|
||||
var rac = anm.runtimeAnimatorController;
|
||||
var origPos = anm.transform.position;
|
||||
var origRot = anm.transform.rotation;
|
||||
var origRootMotion = anm.applyRootMotion;
|
||||
var prevAnmCulling = anm.cullingMode;
|
||||
|
||||
anm.runtimeAnimatorController = null;
|
||||
anm.cullingMode = AnimatorCullingMode.AlwaysAnimate;
|
||||
anm.applyRootMotion = true;
|
||||
anm.transform.position = Vector3.zero;
|
||||
anm.transform.rotation = quaternion.identity;
|
||||
|
||||
var newTracks = new NativeArray<Track>(tracks.Length * trs.Length, Allocator.Temp);
|
||||
for (int k = 0; k < newTracks.Length; ++k)
|
||||
{
|
||||
var nt = tracks[k % tracks.Length];
|
||||
nt.keyFrameRange = new int2(keyFramesList.Length, keysList.Length);
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
nt.name = $"{trs[k / tracks.Length].Item1.name}.{nt.bindingType}.{nt.channelIndex}";
|
||||
#endif
|
||||
keyFramesList.Resize(keyFramesList.Length + keysList.Length, NativeArrayOptions.ClearMemory);
|
||||
newTracks[k] = nt;
|
||||
}
|
||||
|
||||
for (int i = 0; i < keysList.Length; ++i)
|
||||
{
|
||||
var time = keysList[i];
|
||||
ac.SampleAnimation(anm.gameObject, time);
|
||||
|
||||
for (int l = 0; l < trs.Length; ++l)
|
||||
{
|
||||
var tr = trs[l].Item1;
|
||||
var trackSpan = newTracks.GetSubArray(l * tracks.Length, tracks.Length);
|
||||
ReadCurvesFromTransform(tr, trackSpan, i, time);
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = 0; l < trs.Length; ++l)
|
||||
{
|
||||
var transformHash = trs[l].Item2;
|
||||
var idx = trackGroupHashes.IndexOf(transformHash);
|
||||
if (idx < 0)
|
||||
{
|
||||
trackGroupHashes.Add(transformHash);
|
||||
trackGroupOffsets.Add((uint)trackList.Length);
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
trackGroupNames.Add(trs[l].Item1.name);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
trackGroupOffsets[idx] = (uint)trackList.Length;
|
||||
}
|
||||
|
||||
trackList.AddRange(newTracks.GetSubArray(l * tracks.Length, tracks.Length));
|
||||
}
|
||||
|
||||
for (int m = 0; m < newTracks.Length; ++m)
|
||||
{
|
||||
var nt = newTracks[m];
|
||||
var trackKeyFrames = new Span<KeyFrame>(keyFramesList.GetUnsafePtr() + nt.keyFrameRange.x, nt.keyFrameRange.y);
|
||||
ComputeTangents(trackKeyFrames);
|
||||
}
|
||||
|
||||
anm.cullingMode = prevAnmCulling;
|
||||
anm.runtimeAnimatorController = rac;
|
||||
anm.transform.position = origPos;
|
||||
anm.transform.rotation = origRot;
|
||||
anm.applyRootMotion = origRootMotion;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
(Transform, uint) GetRootBoneTransform(Animator anm)
|
||||
{
|
||||
if (anm.avatar.isHuman)
|
||||
{
|
||||
var hipsTransform = anm.GetBoneTransform(HumanBodyBones.Hips);
|
||||
var hd = anm.avatar.humanDescription;
|
||||
var humanBoneIndexInDesc = GetHumanBoneIndexForHumanName(hd, "Hips");
|
||||
var rigHipsBoneName = new FixedStringName(hd.human[humanBoneIndexInDesc].boneName).CalculateHash32();
|
||||
return (hipsTransform, rigHipsBoneName);
|
||||
}
|
||||
|
||||
var rootBoneName = anm.avatar.GetRootMotionNodeName();
|
||||
var rootBoneNameHash = new FixedStringName(rootBoneName).CalculateHash32();
|
||||
var rootBoneTransform = Rukhanka.Toolbox.TransformUtils.FindChildRecursively(anm.transform, rootBoneName);
|
||||
return (rootBoneTransform, rootBoneNameHash);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SampleMissingCurves(AnimationClip ac, Animator anm, float frameTime)
|
||||
{
|
||||
var trs = new List<ValueTuple<Transform, uint>>();
|
||||
var entityRootTransform = anm.transform;
|
||||
var rootBoneTransformData = GetRootBoneTransform(anm);
|
||||
|
||||
if (anm.isHuman)
|
||||
trs.Add(rootBoneTransformData);
|
||||
|
||||
// Sample curves for non-rootmotion animations
|
||||
SampleUnityAnimation(ac, anm, trs.ToArray(), false, frameTime);
|
||||
|
||||
// Sample root motion curves
|
||||
trs.Clear();
|
||||
|
||||
var entityRootHash = SpecialBones.UnnamedRootBoneName.CalculateHash32();
|
||||
entityRootHash = AnimationProcessSystem.ComputeBoneAnimationJob.ModifyBoneHashForRootMotion(entityRootHash);
|
||||
trs.Add((entityRootTransform, entityRootHash));
|
||||
|
||||
// Modify bone hash to separate root motion tracks and ordinary tracks
|
||||
rootBoneTransformData.Item2 = AnimationProcessSystem.ComputeBoneAnimationJob.ModifyBoneHashForRootMotion(rootBoneTransformData.Item2);
|
||||
trs.Add(rootBoneTransformData);
|
||||
|
||||
SampleUnityAnimation(ac, anm, trs.ToArray(), true, frameTime);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void BakeAnimationEvents(BlobBuilder bb, ref AnimationClipBlob acb, AnimationClip ac)
|
||||
{
|
||||
if (ac.events.Length == 0)
|
||||
return;
|
||||
|
||||
var eventsArr = bb.Allocate(ref acb.events, ac.events.Length);
|
||||
for (var i = 0; i < eventsArr.Length; ++i)
|
||||
{
|
||||
var ae = ac.events[i];
|
||||
ref var bakedEvent = ref eventsArr[i];
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
if (ae.functionName.Length > 0)
|
||||
bb.AllocateString(ref bakedEvent.name, ae.functionName);
|
||||
if (ae.stringParameter.Length > 0)
|
||||
bb.AllocateString(ref bakedEvent.stringParam, ae.stringParameter);
|
||||
#endif
|
||||
bakedEvent.nameHash = new FixedStringName(ae.functionName).CalculateHash32();
|
||||
bakedEvent.time = ae.time / ac.length;
|
||||
bakedEvent.floatParam = ae.floatParameter;
|
||||
bakedEvent.intParam = ae.intParameter;
|
||||
bakedEvent.stringParamHash = new FixedStringName(ae.stringParameter).CalculateHash32();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AnimationClip[] Deduplicate(AnimationClip[] animationClips)
|
||||
{
|
||||
var dedupList = new List<AnimationClip>();
|
||||
var dupSet = new NativeHashSet<ulong>(animationClips.Length, Allocator.Temp);
|
||||
|
||||
foreach (var a in animationClips)
|
||||
{
|
||||
if (a != null &&
|
||||
#if UNITY_6000_4_OR_NEWER
|
||||
!dupSet.Add(a.GetEntityId().GetRawData())
|
||||
#else
|
||||
!dupSet.Add((ulong)a.GetInstanceID())
|
||||
#endif
|
||||
)
|
||||
continue;
|
||||
|
||||
dedupList.Add(a);
|
||||
}
|
||||
return dedupList.ToArray();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int BuildAnimationBakeList(IBaker baker, AnimationClip[] animationClips, Avatar avatar, out NativeArray<BlobAssetReference<AnimationClipBlob>> alreadyBakedList)
|
||||
{
|
||||
alreadyBakedList = new (animationClips.Length, Allocator.Temp);
|
||||
var rv = 0;
|
||||
for (var i = 0; i < animationClips.Length; ++i)
|
||||
{
|
||||
var ac = animationClips[i];
|
||||
if (ac == null)
|
||||
continue;
|
||||
|
||||
// Check for blob asset store first
|
||||
var animationHash = BakingUtils.ComputeAnimationHash(ac, avatar);
|
||||
var isAnimationExists = baker.TryGetBlobAssetReference<AnimationClipBlob>(animationHash, out var acb);
|
||||
if (!isAnimationExists)
|
||||
{
|
||||
// Try cached baked animation
|
||||
acb = BlobCache.LoadBakedAnimationFromCache(ac, avatar);
|
||||
if (acb == BlobAssetReference<AnimationClipBlob>.Null)
|
||||
{
|
||||
rv += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't forget to add loaded animation to blob asset store
|
||||
baker.AddBlobAssetWithCustomHash(ref acb, animationHash);
|
||||
}
|
||||
}
|
||||
|
||||
alreadyBakedList[i] = acb;
|
||||
}
|
||||
|
||||
// Return count of animations need to perform full bake
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public NativeArray<BlobAssetReference<AnimationClipBlob>> BakeAnimations(IBaker baker, AnimationClip[] animationClips, Avatar avatar, GameObject animatedObjectRoot)
|
||||
{
|
||||
if (animationClips == null || animationClips.Length == 0)
|
||||
return default;
|
||||
|
||||
animationClips = Deduplicate(animationClips);
|
||||
|
||||
// Firstly create list of animations that need to be baked (not present in cache and in not in blob asset store)
|
||||
var numClipsToBake = BuildAnimationBakeList(baker, animationClips, avatar, out var alreadyBakedAnimations);
|
||||
|
||||
// If nothing to bake, just return already baked list
|
||||
if (numClipsToBake == 0)
|
||||
return alreadyBakedAnimations;
|
||||
|
||||
// Now bake animations that require full rebake
|
||||
// Need to make instance of object because when we will sample animations object placement can be modified.
|
||||
// Also prefabs will not update its transforms
|
||||
GameObject objectCopy = null;
|
||||
Animator animatorCopy = null;
|
||||
if (avatar != null)
|
||||
{
|
||||
objectCopy = GameObject.Instantiate(animatedObjectRoot);
|
||||
objectCopy.hideFlags = HideFlags.HideAndDontSave;
|
||||
animatorCopy = objectCopy.GetComponent<Animator>();
|
||||
if (animatorCopy == null)
|
||||
animatorCopy = objectCopy.AddComponent<Animator>();
|
||||
animatorCopy.avatar = avatar;
|
||||
}
|
||||
|
||||
for (var i = 0; i < animationClips.Length; ++i)
|
||||
{
|
||||
var clipBlob = alreadyBakedAnimations[i];
|
||||
var a = animationClips[i];
|
||||
if (clipBlob != BlobAssetReference<AnimationClipBlob>.Null)
|
||||
continue;
|
||||
|
||||
var animationHash = BakingUtils.ComputeAnimationHash(a, avatar);
|
||||
var isAnimationExists = baker.TryGetBlobAssetReference(animationHash, out clipBlob);
|
||||
if (!isAnimationExists)
|
||||
{
|
||||
clipBlob = CreateAnimationBlobAsset(a, animatorCopy, animationHash);
|
||||
baker.AddBlobAssetWithCustomHash(ref clipBlob, animationHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Animation '{a.name}' is duplicate!");
|
||||
}
|
||||
|
||||
alreadyBakedAnimations[i] = clipBlob;
|
||||
baker.DependsOn(a);
|
||||
}
|
||||
|
||||
if (objectCopy != null)
|
||||
GameObject.DestroyImmediate(objectCopy);
|
||||
|
||||
return alreadyBakedAnimations;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BlobAssetReference<AnimationClipBlob> CreateAnimationBlobAsset(AnimationClip ac, Animator animator, Hash128 animationHash)
|
||||
{
|
||||
var avatar = animator?.avatar;
|
||||
var acSettings = AnimationUtility.GetAnimationClipSettings(ac);
|
||||
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var rv = ref bb.ConstructRoot<AnimationClipBlob>();
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var startTimeMarker = Time.realtimeSinceStartupAsDouble;
|
||||
if (ac.name.Length > 0)
|
||||
bb.AllocateString(ref rv.name, ac.name);
|
||||
#endif
|
||||
|
||||
rv.length = ac.length;
|
||||
rv.looped = ac.isLooping;
|
||||
rv.hash = animationHash;
|
||||
rv.loopPoseBlend = acSettings.loopBlend;
|
||||
rv.cycleOffset = acSettings.cycleOffset;
|
||||
|
||||
BakeAnimationEvents(bb, ref rv, ac);
|
||||
BakeTrackSet(bb, ref rv.clipTracks, out var maxTrackKeyframeLength, -1, ac, animator, avatar);
|
||||
if (acSettings.additiveReferencePoseClip != null)
|
||||
BakeTrackSet(bb, ref rv.additiveReferencePoseFrame, out _, acSettings.additiveReferencePoseTime, acSettings.additiveReferencePoseClip, animator, avatar);
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var dt = Time.realtimeSinceStartupAsDouble - startTimeMarker;
|
||||
rv.bakingTime = (float)dt;
|
||||
#endif
|
||||
|
||||
rv.maxTrackKeyframeLength = maxTrackKeyframeLength;
|
||||
var bar = bb.CreateBlobAssetReference<AnimationClipBlob>(Allocator.Persistent);
|
||||
|
||||
// Save baked animation into cache
|
||||
BlobCache.SaveBakedAnimationToCache(ac, avatar, bar);
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Keyframe[] GetKeyFramesArray(UnityEngine.AnimationCurve animationCurve, float frameTime)
|
||||
{
|
||||
if (frameTime < 0)
|
||||
return animationCurve.keys;
|
||||
|
||||
var oneFrameAnimation = new Keyframe[]
|
||||
{
|
||||
new (0, animationCurve.Evaluate(frameTime))
|
||||
};
|
||||
return oneFrameAnimation;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void BakeTrackSet(BlobBuilder bb, ref TrackSet outTrackSet, out uint maxTrackKeyframeLength, float frameTime, AnimationClip ac, Animator animator, Avatar avatar)
|
||||
{
|
||||
var bindings = AnimationUtility.GetCurveBindings(ac);
|
||||
|
||||
keyFramesList.Clear();
|
||||
trackList.Clear();
|
||||
trackGroupHashes.Clear();
|
||||
trackGroupOffsets.Clear();
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
trackGroupNames.Clear();
|
||||
#endif
|
||||
|
||||
maxTrackKeyframeLength = 0u;
|
||||
foreach (var b in bindings)
|
||||
{
|
||||
var ec = AnimationUtility.GetEditorCurve(ac, b);
|
||||
var pb = ParseCurveBinding(ac, b, animator?.avatar);
|
||||
var inKeyframes = GetKeyFramesArray(ec, frameTime);
|
||||
var keyFramesRange = CopyKeyFrames(inKeyframes, ref keyFramesList);
|
||||
var trackData = CreateTrackData(keyFramesRange, pb);
|
||||
InsertTrackIntoGroup(trackData, pb.boneName);
|
||||
|
||||
maxTrackKeyframeLength = math.max(maxTrackKeyframeLength, (uint)keyFramesRange.y);
|
||||
}
|
||||
|
||||
if (avatar != null)
|
||||
{
|
||||
// Sample root and hips curves and from unity animations. Maybe sometime I will figure out all RootT/RootQ and body pose generation formulas and this step could be replaced with generation.
|
||||
SampleMissingCurves(ac, animator, frameTime);
|
||||
}
|
||||
|
||||
// Create special end empty track group because track group tracks count is calculated as trackGroup[i + 1].trackCount - trackGroup[i].trackCount
|
||||
trackGroupHashes.Add(0xffffffff);
|
||||
trackGroupOffsets.Add((uint)trackList.Length);
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
trackGroupNames.Add("RUKHANKA_TRAILING_TRACK");
|
||||
#endif
|
||||
|
||||
CreateTrackSetBlob(ref bb, ref outTrackSet, ac.name);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void CreateTrackSetBlob(ref BlobBuilder bb, ref TrackSet outTrackSet, string animationName)
|
||||
{
|
||||
var keyFramesBlobArray = bb.Allocate(ref outTrackSet.keyframes, keyFramesList.Length);
|
||||
UnsafeUtility.MemCpy(keyFramesBlobArray.GetUnsafePtr(), keyFramesList.GetUnsafeReadOnlyPtr(), keyFramesList.Length * UnsafeUtility.SizeOf<KeyFrame>());
|
||||
|
||||
var tracksBlobArray = bb.Allocate(ref outTrackSet.tracks, trackList.Length);
|
||||
UnsafeUtility.MemCpy(tracksBlobArray.GetUnsafePtr(), trackList.GetUnsafeReadOnlyPtr(), trackList.Length * UnsafeUtility.SizeOf<Track>());
|
||||
|
||||
var trackGroupsBlobArray = bb.Allocate(ref outTrackSet.trackGroups, trackGroupOffsets.Length);
|
||||
UnsafeUtility.MemCpy(trackGroupsBlobArray.GetUnsafePtr(), trackGroupOffsets.GetUnsafeReadOnlyPtr(), trackGroupOffsets.Length * UnsafeUtility.SizeOf<uint>());
|
||||
|
||||
// Make a hash table from track group hashes
|
||||
var phtIsCreated = Perfect2HashTable.Build(trackGroupHashes.AsArray(), out var pht, out var seed);
|
||||
Assert.IsTrue(phtIsCreated, $"Cannot create track perfect hash table for animation {animationName}");
|
||||
|
||||
var phtBlobArray = bb.Allocate(ref outTrackSet.trackGroupPHT.pht, pht.Length);
|
||||
UnsafeUtility.MemCpy(phtBlobArray.GetUnsafePtr(), pht.GetUnsafeReadOnlyPtr(), pht.Length * UnsafeUtility.SizeOf<uint2>());
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var trackGroupDebugInfoArr = bb.Allocate(ref outTrackSet.trackGroupDebugInfo, trackGroupHashes.Length);
|
||||
for (var i = 0; i < trackGroupHashes.Length; ++i)
|
||||
{
|
||||
var trackGroupInfoBlob = new TrackGroupInfo()
|
||||
{
|
||||
hash = trackGroupHashes[i],
|
||||
name = trackGroupNames[i]
|
||||
};
|
||||
trackGroupDebugInfoArr[i] = trackGroupInfoBlob;
|
||||
}
|
||||
#endif
|
||||
|
||||
outTrackSet.trackGroupPHT.seed = seed;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MakeAdditiveReferencePoseFrame(ref NativeList<BoneClip> bc, AnimationClipSettings acs, Avatar avatar)
|
||||
{
|
||||
/*
|
||||
if (acs.additiveReferencePoseClip == null)
|
||||
return;
|
||||
|
||||
for (var k = 0; k < bc.Length; ++k)
|
||||
{
|
||||
var bcc = bc[k];
|
||||
for (var m = 0; m < bcc.animationCurves.Length; ++m)
|
||||
{
|
||||
ref var ac = ref bcc.animationCurves.ElementAt(m);
|
||||
}
|
||||
}
|
||||
|
||||
var clipPose = SampleAnimation(acs.additiveReferencePoseClip, avatar, acs.additiveReferencePoseTime);
|
||||
foreach (var c in clipPose)
|
||||
{
|
||||
for (var k = 0; k < bc.Length; ++k)
|
||||
{
|
||||
var bcc = bc[k];
|
||||
if (c.pb.boneName != bcc.name)
|
||||
continue;
|
||||
|
||||
for (var m = 0; m < bcc.animationCurves.Length; ++m)
|
||||
{
|
||||
ref var ac = ref bcc.animationCurves.ElementAt(m);
|
||||
|
||||
if (ac.bindingType == BindingType.Scale || (ac.bindingType == BindingType.Quaternion && ac.channelIndex == 3))
|
||||
ac.additiveReferenceValue = 1;
|
||||
|
||||
if (ac.bindingType == c.pb.bindingType && ac.channelIndex == c.pb.channelIndex)
|
||||
{
|
||||
ac.additiveReferenceValue = c.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aedde7e2d280f234882a7bb61985b6f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AnimationClip/AnimationClipBaker.cs
|
||||
uploadId: 897522
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
// RTP - Ready to process
|
||||
namespace RTP
|
||||
{
|
||||
public struct AnimationClip: IDisposable
|
||||
{
|
||||
public FixedStringName name;
|
||||
public UnsafeList<BoneClip> bones;
|
||||
public UnsafeList<BoneClip> curves;
|
||||
public UnsafeList<AnimationEvent> events;
|
||||
public bool looped;
|
||||
public bool loopPoseBlend;
|
||||
public float cycleOffset;
|
||||
public float length;
|
||||
public float additiveReferencePoseTime;
|
||||
public bool hasRootMotionCurves;
|
||||
public Hash128 hash;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var a in bones) a.Dispose();
|
||||
foreach (var a in curves) a.Dispose();
|
||||
|
||||
bones.Dispose();
|
||||
curves.Dispose();
|
||||
events.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct BoneClip: IEquatable<Hash128>, IDisposable
|
||||
{
|
||||
public FixedStringName name;
|
||||
public Hash128 nameHash;
|
||||
public bool isHumanMuscleClip;
|
||||
public UnsafeList<AnimationCurve> animationCurves;
|
||||
|
||||
public bool Equals(Hash128 o) => o == nameHash;
|
||||
|
||||
public void SetName(string n)
|
||||
{
|
||||
name = n;
|
||||
nameHash = name.CalculateHash128();
|
||||
}
|
||||
|
||||
public void DisposeCurves()
|
||||
{
|
||||
foreach (var a in animationCurves) a.Dispose();
|
||||
animationCurves.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeCurves();
|
||||
animationCurves.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimationCurve: IDisposable
|
||||
{
|
||||
public BindingType bindingType;
|
||||
public short channelIndex; // 0, 1, 2, 3 -> x, y, z, w
|
||||
public UnsafeList<KeyFrame> keyFrames;
|
||||
|
||||
public void Dispose() => keyFrames.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimationEvent
|
||||
{
|
||||
public FixedStringName name;
|
||||
public float time;
|
||||
public float floatParam;
|
||||
public int intParam;
|
||||
public FixedStringName stringParam;
|
||||
}
|
||||
|
||||
} // RTP
|
||||
}
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a14aafd10469a274986298f1cfa7e496
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AnimationClip/AnimationClipBakerData.cs
|
||||
uploadId: 897522
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public partial class AnimationClipBaker
|
||||
{
|
||||
static Dictionary<string, ParsedCurveBinding> humanoidMappingTable;
|
||||
static Dictionary<string, string> humanoidMuscleNameFromCurveProperty;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AnimationClipBaker()
|
||||
{
|
||||
humanoidMappingTable = new ()
|
||||
{
|
||||
// --- Head ---
|
||||
// Neck
|
||||
{ "Neck Nod Down-Up", new ParsedCurveBinding() { boneName = "Neck", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Neck Tilt Left-Right", new ParsedCurveBinding() { boneName = "Neck", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Neck Turn Left-Right", new ParsedCurveBinding() { boneName = "Neck", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// Head
|
||||
{ "Head Nod Down-Up", new ParsedCurveBinding() { boneName = "Head", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Head Tilt Left-Right", new ParsedCurveBinding() { boneName = "Head", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Head Turn Left-Right", new ParsedCurveBinding() { boneName = "Head", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// Left Eye
|
||||
{ "Left Eye Down-Up", new ParsedCurveBinding() { boneName = "LeftEye", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Eye In-Out", new ParsedCurveBinding() { boneName = "LeftEye", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Right Eye
|
||||
{ "Right Eye Down-Up", new ParsedCurveBinding() { boneName = "RightEye", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Eye In-Out", new ParsedCurveBinding() { boneName = "RightEye", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Jaw
|
||||
{ "Jaw Close", new ParsedCurveBinding() { boneName = "Jaw", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Jaw Left-Right", new ParsedCurveBinding() { boneName = "Jaw", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
|
||||
// --- Body ---
|
||||
// Spine
|
||||
{ "Spine Front-Back", new ParsedCurveBinding() { boneName = "Spine", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Spine Left-Right", new ParsedCurveBinding() { boneName = "Spine", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Spine Twist Left-Right", new ParsedCurveBinding() { boneName = "Spine", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// Chest
|
||||
{ "Chest Front-Back", new ParsedCurveBinding() { boneName = "Chest", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Chest Left-Right", new ParsedCurveBinding() { boneName = "Chest", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Chest Twist Left-Right", new ParsedCurveBinding() { boneName = "Chest", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// UpperChest
|
||||
{ "UpperChest Front-Back", new ParsedCurveBinding() { boneName = "UpperChest", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "UpperChest Left-Right", new ParsedCurveBinding() { boneName = "UpperChest", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
{ "UpperChest Twist Left-Right", new ParsedCurveBinding() { boneName = "UpperChest", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
|
||||
// --- Left Arm ---
|
||||
// LeftShoulder
|
||||
{ "Left Shoulder Down-Up", new ParsedCurveBinding() { boneName = "LeftShoulder", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Shoulder Front-Back", new ParsedCurveBinding() { boneName = "LeftShoulder", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// LeftUpperArm
|
||||
{ "Left Arm Down-Up", new ParsedCurveBinding() { boneName = "LeftUpperArm", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Arm Front-Back", new ParsedCurveBinding() { boneName = "LeftUpperArm", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Arm Twist In-Out", new ParsedCurveBinding() { boneName = "LeftUpperArm", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// LeftLowerArm
|
||||
{ "Left Forearm Stretch", new ParsedCurveBinding() { boneName = "LeftLowerArm", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Forearm Twist In-Out", new ParsedCurveBinding() { boneName = "LeftLowerArm", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// LeftHand
|
||||
{ "Left Hand Down-Up", new ParsedCurveBinding() { boneName = "LeftHand", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Hand In-Out", new ParsedCurveBinding() { boneName = "LeftHand", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
|
||||
// --- Left Hand ---
|
||||
// Thumb 1
|
||||
{ "LeftHand.Thumb.1 Stretched", new ParsedCurveBinding() { boneName = "Left Thumb Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "LeftHand.Thumb.Spread", new ParsedCurveBinding() { boneName = "Left Thumb Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Thumb 2
|
||||
{ "LeftHand.Thumb.2 Stretched", new ParsedCurveBinding() { boneName = "Left Thumb Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Thumb 3
|
||||
{ "LeftHand.Thumb.3 Stretched", new ParsedCurveBinding() { boneName = "Left Thumb Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Index 1
|
||||
{ "LeftHand.Index.1 Stretched", new ParsedCurveBinding() { boneName = "Left Index Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "LeftHand.Index.Spread", new ParsedCurveBinding() { boneName = "Left Index Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Index 2
|
||||
{ "LeftHand.Index.2 Stretched", new ParsedCurveBinding() { boneName = "Left Index Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Index 3
|
||||
{ "LeftHand.Index.3 Stretched", new ParsedCurveBinding() { boneName = "Left Index Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Middle 1
|
||||
{ "LeftHand.Middle.1 Stretched", new ParsedCurveBinding() { boneName = "Left Middle Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "LeftHand.Middle.Spread", new ParsedCurveBinding() { boneName = "Left Middle Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Middle 2
|
||||
{ "LeftHand.Middle.2 Stretched", new ParsedCurveBinding() { boneName = "Left Middle Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Middle 3
|
||||
{ "LeftHand.Middle.3 Stretched", new ParsedCurveBinding() { boneName = "Left Middle Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Ring 1
|
||||
{ "LeftHand.Ring.1 Stretched", new ParsedCurveBinding() { boneName = "Left Ring Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "LeftHand.Ring.Spread", new ParsedCurveBinding() { boneName = "Left Ring Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Ring 2
|
||||
{ "LeftHand.Ring.2 Stretched", new ParsedCurveBinding() { boneName = "Left Ring Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Ring 3
|
||||
{ "LeftHand.Ring.3 Stretched", new ParsedCurveBinding() { boneName = "Left Ring Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Little 1
|
||||
{ "LeftHand.Little.1 Stretched", new ParsedCurveBinding() { boneName = "Left Little Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "LeftHand.Little.Spread", new ParsedCurveBinding() { boneName = "Left Little Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Little 2
|
||||
{ "LeftHand.Little.2 Stretched", new ParsedCurveBinding() { boneName = "Left Little Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Little 3
|
||||
{ "LeftHand.Little.3 Stretched", new ParsedCurveBinding() { boneName = "Left Little Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
|
||||
// --- Right Arm ---
|
||||
// RightShoulder
|
||||
{ "Right Shoulder Down-Up", new ParsedCurveBinding() { boneName = "RightShoulder", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Shoulder Front-Back", new ParsedCurveBinding() { boneName = "RightShoulder", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// RightUpperArm
|
||||
{ "Right Arm Down-Up", new ParsedCurveBinding() { boneName = "RightUpperArm", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Arm Front-Back", new ParsedCurveBinding() { boneName = "RightUpperArm", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Arm Twist In-Out", new ParsedCurveBinding() { boneName = "RightUpperArm", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// RightLowerArm
|
||||
{ "Right Forearm Stretch", new ParsedCurveBinding() { boneName = "RightLowerArm", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Forearm Twist In-Out", new ParsedCurveBinding() { boneName = "RightLowerArm", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// RightHand
|
||||
{ "Right Hand Down-Up", new ParsedCurveBinding() { boneName = "RightHand", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Hand In-Out", new ParsedCurveBinding() { boneName = "RightHand", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
|
||||
// --- Right Hand ---
|
||||
// Thumb 1
|
||||
{ "RightHand.Thumb.1 Stretched", new ParsedCurveBinding() { boneName = "Right Thumb Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "RightHand.Thumb.Spread", new ParsedCurveBinding() { boneName = "Right Thumb Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Thumb 2
|
||||
{ "RightHand.Thumb.2 Stretched", new ParsedCurveBinding() { boneName = "Right Thumb Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Thumb 3
|
||||
{ "RightHand.Thumb.3 Stretched", new ParsedCurveBinding() { boneName = "Right Thumb Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Index 1
|
||||
{ "RightHand.Index.1 Stretched", new ParsedCurveBinding() { boneName = "Right Index Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "RightHand.Index.Spread", new ParsedCurveBinding() { boneName = "Right Index Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Index 2
|
||||
{ "RightHand.Index.2 Stretched", new ParsedCurveBinding() { boneName = "Right Index Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Index 3
|
||||
{ "RightHand.Index.3 Stretched", new ParsedCurveBinding() { boneName = "Right Index Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Middle 1
|
||||
{ "RightHand.Middle.1 Stretched", new ParsedCurveBinding() { boneName = "Right Middle Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "RightHand.Middle.Spread", new ParsedCurveBinding() { boneName = "Right Middle Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Middle 2
|
||||
{ "RightHand.Middle.2 Stretched", new ParsedCurveBinding() { boneName = "Right Middle Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Middle 3
|
||||
{ "RightHand.Middle.3 Stretched", new ParsedCurveBinding() { boneName = "Right Middle Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Ring 1
|
||||
{ "RightHand.Ring.1 Stretched", new ParsedCurveBinding() { boneName = "Right Ring Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "RightHand.Ring.Spread", new ParsedCurveBinding() { boneName = "Right Ring Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Ring 2
|
||||
{ "RightHand.Ring.2 Stretched", new ParsedCurveBinding() { boneName = "Right Ring Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Ring 3
|
||||
{ "RightHand.Ring.3 Stretched", new ParsedCurveBinding() { boneName = "Right Ring Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Little 1
|
||||
{ "RightHand.Little.1 Stretched", new ParsedCurveBinding() { boneName = "Right Little Proximal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "RightHand.Little.Spread", new ParsedCurveBinding() { boneName = "Right Little Proximal", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// Little 2
|
||||
{ "RightHand.Little.2 Stretched", new ParsedCurveBinding() { boneName = "Right Little Intermediate", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
// Little 3
|
||||
{ "RightHand.Little.3 Stretched", new ParsedCurveBinding() { boneName = "Right Little Distal", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
|
||||
// --- Left Leg ---
|
||||
// LeftUpperLeg
|
||||
{ "Left Upper Leg Front-Back", new ParsedCurveBinding() { boneName = "LeftUpperLeg", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Upper Leg In-Out", new ParsedCurveBinding() { boneName = "LeftUpperLeg", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Upper Leg Twist In-Out", new ParsedCurveBinding() { boneName = "LeftUpperLeg", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// LeftLowerLeg
|
||||
{ "Left Lower Leg Stretch", new ParsedCurveBinding() { boneName = "LeftLowerLeg", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Lower Leg Twist In-Out", new ParsedCurveBinding() { boneName = "LeftLowerLeg", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// LeftFoot
|
||||
{ "Left Foot Up-Down", new ParsedCurveBinding() { boneName = "LeftFoot", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Left Foot Twist In-Out", new ParsedCurveBinding() { boneName = "LeftFoot", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// LeftToes
|
||||
{ "Left Toes Up-Down", new ParsedCurveBinding() { boneName = "LeftToes", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
|
||||
// --- Right Leg ---
|
||||
// RightUpperLeg
|
||||
{ "Right Upper Leg Front-Back", new ParsedCurveBinding() { boneName = "RightUpperLeg", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Upper Leg In-Out", new ParsedCurveBinding() { boneName = "RightUpperLeg", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Upper Leg Twist In-Out", new ParsedCurveBinding() { boneName = "RightUpperLeg", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// RightLowerLeg
|
||||
{ "Right Lower Leg Stretch", new ParsedCurveBinding() { boneName = "RightLowerLeg", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Lower Leg Twist In-Out", new ParsedCurveBinding() { boneName = "RightLowerLeg", channelIndex = 0, bindingType = BindingType.HumanMuscle }},
|
||||
// RightFoot
|
||||
{ "Right Foot Up-Down", new ParsedCurveBinding() { boneName = "RightFoot", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
{ "Right Foot Twist In-Out", new ParsedCurveBinding() { boneName = "RightFoot", channelIndex = 1, bindingType = BindingType.HumanMuscle }},
|
||||
// RightToes
|
||||
{ "Right Toes Up-Down", new ParsedCurveBinding() { boneName = "RightToes", channelIndex = 2, bindingType = BindingType.HumanMuscle }},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0bc7b37fdee74b4e8e6e52530590fd9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AnimationClip/AnimationClipHumanoidMapTable.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,39 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public partial class AnimationClipBaker
|
||||
{
|
||||
struct SampledCurve
|
||||
{
|
||||
public ParsedCurveBinding pb;
|
||||
public float value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
List<SampledCurve> SampleAnimation(AnimationClip ac, Avatar avatar, float time)
|
||||
{
|
||||
var rv = new List<SampledCurve>();
|
||||
var bindings = AnimationUtility.GetCurveBindings(ac);
|
||||
foreach (var cb in bindings)
|
||||
{
|
||||
var ec = AnimationUtility.GetEditorCurve(ac, cb);
|
||||
var v = new SampledCurve();
|
||||
v.pb = ParseCurveBinding(ac, cb, avatar);
|
||||
v.value = ec.Evaluate(time);
|
||||
rv.Add(v);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7296fa1c2965e841a8a623e67ae1ed9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AnimationClip/AnimationSampler.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 582a9521397c99440bc508317369da89
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public partial class AnimatorControllerBaker: Baker<Animator>
|
||||
{
|
||||
public override void Bake(Animator a)
|
||||
{
|
||||
// Skip animators without rig definition
|
||||
var rd = a.GetComponent<RigDefinitionAuthoring>();
|
||||
if (rd == null)
|
||||
return;
|
||||
|
||||
if (a.runtimeAnimatorController == null)
|
||||
{
|
||||
Debug.LogWarning($"There is no controller attached to '{a.name}' animator. Skipping this object");
|
||||
return;
|
||||
}
|
||||
|
||||
var e = GetEntity(TransformUsageFlags.Dynamic);
|
||||
|
||||
var rac = GetRuntimeAnimatorController(a);
|
||||
var ac = GetAnimatorControllerFromRuntime(rac);
|
||||
var controllerBlob = BuildControllerBlob(ac, rd);
|
||||
var controllerAnimationHashesBlob = BuildControllerAnimationHashesBlob(ac, a.avatar);
|
||||
|
||||
var animationsFromOverrideController = GetAnimationsFromOverrideController(a);
|
||||
var allClips = new AnimationClip[animationsFromOverrideController.Length + ac.animationClips.Length];
|
||||
animationsFromOverrideController.CopyTo(allClips, 0);
|
||||
ac.animationClips.CopyTo(allClips, animationsFromOverrideController.Length);
|
||||
BakeAllControllerAnimations(e, a.avatar, allClips, a.gameObject);
|
||||
BakeAllUsedAvatarMasks(e, ac, rd);
|
||||
|
||||
CreateControllerEntityComponents(rd, e, controllerBlob, controllerAnimationHashesBlob);
|
||||
CreateOverrideAnimationsBuffer(e, a, ac);
|
||||
|
||||
DependsOn(ac);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RuntimeAnimatorController GetRuntimeAnimatorController(Animator a)
|
||||
{
|
||||
var rv = a.runtimeAnimatorController;
|
||||
// Check for animator override controller
|
||||
var aoc = rv as AnimatorOverrideController;
|
||||
if (aoc != null)
|
||||
{
|
||||
rv = aoc.runtimeAnimatorController;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AnimatorController GetAnimatorControllerFromRuntime(RuntimeAnimatorController rac)
|
||||
{
|
||||
if (rac == null)
|
||||
return null;
|
||||
|
||||
var acPath = AssetDatabase.GetAssetPath(rac);
|
||||
var controller = AssetDatabase.LoadAssetAtPath<AnimatorController>(acPath);
|
||||
return controller;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AnimationClip[] GetAnimationsFromOverrideController(Animator animator)
|
||||
{
|
||||
var aoc = GetOverrideController(animator);
|
||||
if (aoc == null)
|
||||
return Array.Empty<AnimationClip>();
|
||||
|
||||
var overrides = new List<KeyValuePair<AnimationClip, AnimationClip>>();
|
||||
aoc.GetOverrides(overrides);
|
||||
|
||||
var rv = new List<AnimationClip>();
|
||||
for (var i = 0; i < overrides.Count; ++i)
|
||||
{
|
||||
var dstAnm = overrides[i].Value;
|
||||
if (dstAnm != null)
|
||||
rv.Add(dstAnm);
|
||||
}
|
||||
return rv.ToArray();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void BakeAllUsedAvatarMasks(Entity e, AnimatorController controller, RigDefinitionAuthoring rd)
|
||||
{
|
||||
var bakedAvatarMasks = new NativeList<AvatarMaskBakingData>(Allocator.Temp);
|
||||
for (int i = 0; i < controller.layers.Length; ++i)
|
||||
{
|
||||
var l = controller.layers[i];
|
||||
if (l.avatarMask != null)
|
||||
{
|
||||
var amb = new AvatarMaskBaker();
|
||||
var avatarMaskBlobAsset = amb.CreateAvatarMaskBlob(this, l.avatarMask, rd);
|
||||
var newAvatarMaskBlob = new AvatarMaskBakingData()
|
||||
{
|
||||
rigEntity = e,
|
||||
dataBlob = avatarMaskBlobAsset
|
||||
};
|
||||
bakedAvatarMasks.Add(newAvatarMaskBlob);
|
||||
}
|
||||
}
|
||||
|
||||
if (bakedAvatarMasks.Length > 0)
|
||||
{
|
||||
var buf = AddBuffer<AvatarMaskBakingData>(e);
|
||||
buf.AddRange(bakedAvatarMasks.AsArray());
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void BakeAllControllerAnimations(Entity e, Avatar avatar, AnimationClip[] animationClips, GameObject go)
|
||||
{
|
||||
var animationBaker = new AnimationClipBaker();
|
||||
var bakedClipBlobs = animationBaker.BakeAnimations(this, animationClips, avatar, go);
|
||||
var newAnimationClips = AddBuffer<NewBlobAssetDatabaseRecord<AnimationClipBlob>>(e);
|
||||
foreach (var bcb in bakedClipBlobs)
|
||||
{
|
||||
if (!bcb.IsCreated)
|
||||
continue;
|
||||
|
||||
newAnimationClips.Add(new ()
|
||||
{
|
||||
value = bcb,
|
||||
hash = bcb.Value.hash
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateControllerParametersComponents(Entity e, BlobAssetReference<ControllerBlob> cb)
|
||||
{
|
||||
ref var parameters = ref cb.Value.parameters;
|
||||
if (parameters.Length == 0)
|
||||
return;
|
||||
|
||||
// Add dynamic parameters
|
||||
var paramArray = AddBuffer<AnimatorControllerParameterComponent>(e);
|
||||
for (int p = 0; p < parameters.Length; ++p)
|
||||
{
|
||||
ref var pm = ref parameters[p];
|
||||
var acpc = new AnimatorControllerParameterComponent()
|
||||
{
|
||||
value = pm.defaultValue,
|
||||
hash = pm.hash,
|
||||
type = pm.type,
|
||||
};
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
pm.name.CopyToWithTruncate(ref acpc.name);
|
||||
#endif
|
||||
|
||||
paramArray.Add(acpc);
|
||||
}
|
||||
|
||||
// Add perfect hash table used to fast runtime parameter value lookup
|
||||
var parametersPerfectHashTableBlob = CreateParametersPerfectHashTableBlob(cb);
|
||||
var pht = new AnimatorControllerParameterIndexTableComponent()
|
||||
{
|
||||
value = parametersPerfectHashTableBlob
|
||||
};
|
||||
AddComponent(e, pht);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AnimatorOverrideController GetOverrideController(Animator animator)
|
||||
{
|
||||
var rac = animator.runtimeAnimatorController;
|
||||
var aoc = rac as AnimatorOverrideController;
|
||||
return aoc;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateOverrideAnimationsBuffer(Entity e, Animator animator, AnimatorController ac)
|
||||
{
|
||||
var aoc = GetOverrideController(animator);
|
||||
if (aoc == null)
|
||||
return;
|
||||
|
||||
var overrides = new List<KeyValuePair<AnimationClip, AnimationClip>>();
|
||||
aoc.GetOverrides(overrides);
|
||||
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var cab = ref bb.ConstructRoot<ControllerAnimationsBlob>();
|
||||
var animsArr = bb.Allocate(ref cab.animations, ac.animationClips.Length);
|
||||
for (var i = 0; i < overrides.Count; ++i)
|
||||
{
|
||||
var kv = overrides[i];
|
||||
if (kv.Value == null)
|
||||
continue;
|
||||
|
||||
var replacedIndex = Array.IndexOf(ac.animationClips, kv.Key);
|
||||
if (replacedIndex >= 0)
|
||||
animsArr[replacedIndex] = BakingUtils.ComputeAnimationHash(kv.Value, animator.avatar);
|
||||
}
|
||||
|
||||
var blobAsset = bb.CreateBlobAssetReference<ControllerAnimationsBlob>(Allocator.Persistent);
|
||||
AddBlobAsset(ref blobAsset, out _);
|
||||
|
||||
AddComponent(e, new AnimatorOverrideAnimations() { value = blobAsset });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateControllerEntityComponents
|
||||
(
|
||||
RigDefinitionAuthoring rda,
|
||||
Entity e,
|
||||
BlobAssetReference<ControllerBlob> controllerBlob,
|
||||
BlobAssetReference<ControllerAnimationsBlob> controllerAnimationsBlob
|
||||
)
|
||||
{
|
||||
var acc = new AnimatorControllerLayerComponent();
|
||||
acc.rtd = RuntimeAnimatorData.MakeDefault();
|
||||
acc.controller = controllerBlob;
|
||||
acc.animations = controllerAnimationsBlob;
|
||||
acc.speed = 1;
|
||||
|
||||
var buf = AddBuffer<AnimatorControllerLayerComponent>(e);
|
||||
ref var cb = ref controllerBlob.Value;
|
||||
for (int k = 0; k < cb.layers.Length; ++k)
|
||||
{
|
||||
acc.layerIndex = k;
|
||||
acc.weight = cb.layers[k].initialWeight;
|
||||
buf.Add(acc);
|
||||
}
|
||||
|
||||
if (rda.hasAnimatorControllerEvents)
|
||||
AddBuffer<AnimatorControllerEventComponent>(e);
|
||||
|
||||
CreateControllerParametersComponents(e, controllerBlob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6093d93334269749906cd60c9fe0cd3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AnimatorController/AnimatorControllerBaker.cs
|
||||
uploadId: 897522
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
// RTP - Ready to process
|
||||
namespace RTP
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct State: IEquatable<int>
|
||||
{
|
||||
public int hashCode;
|
||||
public FixedStringName name;
|
||||
public FixedStringName tag;
|
||||
public float speed;
|
||||
public FixedStringName speedMultiplierParameter;
|
||||
public UnsafeList<Transition> transitions;
|
||||
public FixedStringName timeParameter;
|
||||
public float cycleOffset;
|
||||
public FixedStringName cycleOffsetParameter;
|
||||
public Motion motion;
|
||||
|
||||
public bool Equals(int o) => o == hashCode;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct ChildMotion
|
||||
{
|
||||
public Motion motion;
|
||||
public float threshold;
|
||||
public float timeScale;
|
||||
public bool mirror;
|
||||
public FixedStringName directBlendParameterName;
|
||||
public float2 position2D;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct Motion
|
||||
{
|
||||
public FixedStringName name;
|
||||
public MotionBlob.Type type;
|
||||
public int animationIndex;
|
||||
public BlendTree blendTree;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct BlendTree
|
||||
{
|
||||
public FixedStringName name;
|
||||
public FixedStringName blendParameterName;
|
||||
public FixedStringName blendParameterYName;
|
||||
public bool normalizeBlendValues;
|
||||
public UnsafeList<ChildMotion> motions;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct Transition
|
||||
{
|
||||
public FixedStringName name;
|
||||
public float duration;
|
||||
public float exitTime;
|
||||
public float offset;
|
||||
public bool hasExitTime;
|
||||
public bool hasFixedDuration;
|
||||
public bool soloFlag;
|
||||
public bool muteFlag;
|
||||
public bool canTransitionToSelf;
|
||||
public int targetStateHash;
|
||||
public TransitionBlob.InterruptionSource interruptionSource;
|
||||
public bool orderedInterruption;
|
||||
public UnsafeList<Condition> conditions;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct Condition
|
||||
{
|
||||
public FixedStringName name;
|
||||
public FixedStringName paramName;
|
||||
public ParameterValue threshold;
|
||||
public AnimatorConditionMode conditionMode;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct Layer
|
||||
{
|
||||
public FixedStringName name;
|
||||
public int defaultStateIndex;
|
||||
public float weight;
|
||||
public int syncedLayerIndex;
|
||||
public bool syncedTiming;
|
||||
public AnimationBlendingMode blendMode;
|
||||
public UnsafeList<Transition> anyStateTransitions;
|
||||
public UnsafeList<State> states;
|
||||
public Hash128 avatarMaskBlobHash;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct Parameter: IEquatable<FixedStringName>
|
||||
{
|
||||
public FixedStringName name;
|
||||
public ParameterValue defaultValue;
|
||||
public ControllerParameterType type;
|
||||
|
||||
public bool Equals(FixedStringName o) => o == name;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct Controller
|
||||
{
|
||||
public FixedStringName name;
|
||||
public Hash128 hash;
|
||||
public UnsafeList<Layer> layers;
|
||||
public UnsafeList<Parameter> parameters;
|
||||
}
|
||||
|
||||
} // RTP
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c046381eed1e32844b8f999dd574ffe2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AnimatorController/AnimatorControllerBakerData.cs
|
||||
uploadId: 897522
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Mathematics;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
[BurstCompile]
|
||||
public partial class AnimatorControllerBaker
|
||||
{
|
||||
uint motionCounter;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void AddTransitionBlob(RTP.Transition t, UnsafeList<RTP.State> allStates, UnsafeList<RTP.Parameter> allParams, ref BlobBuilder bb, ref TransitionBlob tb)
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref tb.name, ref t.name);
|
||||
#endif
|
||||
|
||||
var bbc = bb.Allocate(ref tb.conditions, t.conditions.Length);
|
||||
for (int ci = 0; ci < t.conditions.Length; ++ci)
|
||||
{
|
||||
ref var cb = ref bbc[ci];
|
||||
var src = t.conditions[ci];
|
||||
cb.conditionMode = src.conditionMode;
|
||||
cb.paramIdx = allParams.IndexOf(src.paramName);
|
||||
cb.threshold = src.threshold;
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref cb.name, ref src.name);
|
||||
#endif
|
||||
}
|
||||
|
||||
tb.hash = t.name.CalculateHash32();
|
||||
tb.duration = t.duration;
|
||||
tb.exitTime = t.exitTime;
|
||||
tb.hasExitTime = t.hasExitTime;
|
||||
tb.offset = t.offset;
|
||||
tb.hasFixedDuration = t.hasFixedDuration;
|
||||
tb.targetStateId = allStates.IndexOf(t.targetStateHash);
|
||||
tb.interruptionSource = t.interruptionSource;
|
||||
tb.orderedInterruption = t.orderedInterruption;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void AddChildMotionBlob(RTP.ChildMotion cm, ref BlobBuilder bb, ref ChildMotionBlob cmb, in UnsafeList<RTP.Parameter> allParams)
|
||||
{
|
||||
cmb.threshold = cm.threshold;
|
||||
cmb.timeScale = cm.timeScale;
|
||||
cmb.mirror = cm.mirror;
|
||||
cmb.position2D = cm.position2D;
|
||||
cmb.directBlendParameterIndex = allParams.IndexOf(cm.directBlendParameterName);
|
||||
AddMotionBlob(cm.motion, ref bb, ref cmb.motion, allParams);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void AddMotionBlob(RTP.Motion m, ref BlobBuilder bb, ref MotionBlob mb, in UnsafeList<RTP.Parameter> allParams)
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref mb.name, ref m.name);
|
||||
#endif
|
||||
|
||||
mb.type = m.type;
|
||||
mb.hash = (uint)m.name.GetHashCode();
|
||||
mb.animationIndex = m.animationIndex;
|
||||
|
||||
if (m.type != MotionBlob.Type.None && m.type != MotionBlob.Type.AnimationClip)
|
||||
{
|
||||
ref var bt = ref mb.blendTree;
|
||||
var bbm = bb.Allocate(ref bt.motions, m.blendTree.motions.Length);
|
||||
for (int i = 0; i < bbm.Length; ++i)
|
||||
{
|
||||
AddChildMotionBlob(m.blendTree.motions[i], ref bb, ref bbm[i], allParams);
|
||||
}
|
||||
bt.blendParameterIndex = allParams.IndexOf(m.blendTree.blendParameterName);
|
||||
bt.blendParameterYIndex = allParams.IndexOf(m.blendTree.blendParameterYName);
|
||||
bt.normalizeBlendValues = m.blendTree.normalizeBlendValues;
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref bt.name, ref m.blendTree.name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void AddStateBlob
|
||||
(
|
||||
RTP.State s,
|
||||
ref BlobBuilder bb,
|
||||
ref StateBlob sb,
|
||||
UnsafeList<RTP.Transition> anyStateTransitions,
|
||||
UnsafeList<RTP.State> allStates,
|
||||
UnsafeList<RTP.Parameter> allParams
|
||||
)
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref sb.name, ref s.name);
|
||||
#endif
|
||||
|
||||
sb.hash = s.name.CalculateHash32();
|
||||
sb.speed = s.speed;
|
||||
sb.speedMultiplierParameterIndex = allParams.IndexOf(s.speedMultiplierParameter);
|
||||
sb.timeParameterIndex = allParams.IndexOf(s.timeParameter);
|
||||
sb.cycleOffset = s.cycleOffset;
|
||||
sb.cycleOffsetParameterIndex = allParams.IndexOf(s.cycleOffsetParameter);
|
||||
|
||||
if (!s.tag.IsEmpty)
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref sb.tag, ref s.tag);
|
||||
#endif
|
||||
sb.tagHash = s.tag.CalculateHash32();
|
||||
}
|
||||
|
||||
var bbt = bb.Allocate(ref sb.transitions, s.transitions.Length + anyStateTransitions.Length);
|
||||
|
||||
// Any state transitions are first priority
|
||||
for (int ti = 0; ti < anyStateTransitions.Length; ++ti)
|
||||
{
|
||||
var ast = anyStateTransitions[ti];
|
||||
// Do not add transitions to self according to flag
|
||||
if (ast.canTransitionToSelf || ast.targetStateHash != s.hashCode)
|
||||
AddTransitionBlob(ast, allStates, allParams, ref bb, ref bbt[ti]);
|
||||
}
|
||||
|
||||
for (int ti = 0; ti < s.transitions.Length; ++ti)
|
||||
{
|
||||
var src = s.transitions[ti];
|
||||
AddTransitionBlob(src, allStates, allParams, ref bb, ref bbt[ti + anyStateTransitions.Length]);
|
||||
}
|
||||
|
||||
// Add motion
|
||||
AddMotionBlob(s.motion, ref bb, ref sb.motion, allParams);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void AddAllLayers(BlobBuilder bb, ref ControllerBlob c, RTP.Controller data)
|
||||
{
|
||||
var bbl = bb.Allocate(ref c.layers, data.layers.Length);
|
||||
|
||||
// Clear synced timing value first
|
||||
for (int li = 0; li < data.layers.Length; ++li)
|
||||
{
|
||||
ref var l = ref bbl[li];
|
||||
l.syncedTiming = -1;
|
||||
}
|
||||
|
||||
for (int li = 0; li < data.layers.Length; ++li)
|
||||
{
|
||||
var src = data.layers[li];
|
||||
ref var l = ref bbl[li];
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref l.name, ref src.name);
|
||||
#endif
|
||||
|
||||
l.defaultStateIndex = src.defaultStateIndex;
|
||||
l.initialWeight = src.weight;
|
||||
l.blendingMode = src.blendMode;
|
||||
|
||||
// States
|
||||
var bbs = bb.Allocate(ref l.states, src.states.Length);
|
||||
for (int si = 0; si < src.states.Length; ++si)
|
||||
{
|
||||
var s = src.states[si];
|
||||
AddStateBlob(s, ref bb, ref bbs[si], src.anyStateTransitions, src.states, data.parameters);
|
||||
}
|
||||
|
||||
l.avatarMaskBlobHash = src.avatarMaskBlobHash;
|
||||
l.syncedLayerIndex = src.syncedLayerIndex;
|
||||
|
||||
if (src.syncedTiming)
|
||||
{
|
||||
ref var bl = ref bbl[l.syncedLayerIndex];
|
||||
// Set synced timing index for base layer
|
||||
bl.syncedTiming = li;
|
||||
l.syncedTiming = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void AddParameters(BlobBuilder bb, ref ControllerBlob c, in RTP.Controller data)
|
||||
{
|
||||
// Now place parameters in its original places as in authoring animator
|
||||
var bba = bb.Allocate(ref c.parameters, data.parameters.Length);
|
||||
for (int pi = 0; pi < data.parameters.Length; ++pi)
|
||||
{
|
||||
var src = data.parameters[pi];
|
||||
ref var p = ref bba[pi];
|
||||
p.defaultValue = src.defaultValue;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref p.name, ref src.name);
|
||||
#endif
|
||||
p.hash = data.parameters[pi].name.CalculateHash32();
|
||||
p.type = src.type;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static unsafe BlobAssetReference<PerfectHashTableBlob> CreateParametersPerfectHashTableBlobInternal(in NativeArray<uint> hashesArr)
|
||||
{
|
||||
if (!Perfect2HashTable.Build(hashesArr, out var pht, out var seed))
|
||||
return default;
|
||||
|
||||
using var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var ppb = ref bb.ConstructRoot<PerfectHashTableBlob>();
|
||||
ppb.seed = seed;
|
||||
var bbh = bb.Allocate(ref ppb.pht, pht.Length);
|
||||
UnsafeUtility.MemCpy(bbh.GetUnsafePtr(), pht.GetUnsafeReadOnlyPtr(), pht.Length * UnsafeUtility.SizeOf<uint2>());
|
||||
|
||||
pht.Dispose();
|
||||
|
||||
var rv = bb.CreateBlobAssetReference<PerfectHashTableBlob>(Allocator.Persistent);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe BlobAssetReference<PerfectHashTableBlob> CreateParametersPerfectHashTableBlob(BlobAssetReference<ControllerBlob> cb)
|
||||
{
|
||||
ref var parameters = ref cb.Value.parameters;
|
||||
|
||||
// Create blob asset for perfect hash table, but only if number of parameters is big enough
|
||||
if (parameters.Length <= 0)
|
||||
return default;
|
||||
|
||||
var hashesArr = new NativeArray<uint>(parameters.Length, Allocator.Temp);
|
||||
for (int l = 0; l < parameters.Length; ++l)
|
||||
{
|
||||
hashesArr[l] = parameters[l].hash;
|
||||
}
|
||||
|
||||
var hasher = new xxHash3.StreamingState();
|
||||
hasher.Update(hashesArr.GetUnsafeReadOnlyPtr(), hashesArr.Length * sizeof(uint));
|
||||
var phtBlobHash = new Hash128(hasher.DigestHash128());
|
||||
|
||||
var blobAlreadyExists = TryGetBlobAssetReference<PerfectHashTableBlob>(phtBlobHash, out var phtBlob);
|
||||
if (blobAlreadyExists)
|
||||
return phtBlob;
|
||||
|
||||
var rv = CreateParametersPerfectHashTableBlobInternal(hashesArr);
|
||||
AddBlobAssetWithCustomHash(ref rv, phtBlobHash);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
static void BuildControllerBlobInternal(ref RTP.Controller controllerData, ref BlobBuilder bb, ref ControllerBlob c, in Hash128 hash)
|
||||
{
|
||||
c.hash = hash;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref c.name, ref controllerData.name);
|
||||
#endif
|
||||
AddParameters(bb, ref c, controllerData);
|
||||
AddAllLayers(bb, ref c, controllerData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BlobAssetReference<ControllerBlob> BuildControllerBlob(AnimatorController controller, RigDefinitionAuthoring rd)
|
||||
{
|
||||
var controllerHash = BakingUtils.ComputeControllerHash(controller);
|
||||
|
||||
// Try blob asset store first
|
||||
var isControllerAlreadyBaked = TryGetBlobAssetReference<ControllerBlob>(controllerHash, out var controllerBlob);
|
||||
if (isControllerAlreadyBaked)
|
||||
return controllerBlob;
|
||||
|
||||
// Try file cache
|
||||
controllerBlob = BlobCache.LoadBakedControllerFromCache(controller);
|
||||
if (controllerBlob != BlobAssetReference<ControllerBlob>.Null)
|
||||
{
|
||||
// Don't forget to add loaded controller to blob asset store
|
||||
AddBlobAssetWithCustomHash(ref controllerBlob, controllerHash);
|
||||
return controllerBlob;
|
||||
}
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var startTimeMarker = Time.realtimeSinceStartupAsDouble;
|
||||
#endif
|
||||
|
||||
// Only after failed caches build controller from scratch
|
||||
var controllerDataCollector = new AnimatorControllerDataCollector(controller, rd);
|
||||
var controllerData = controllerDataCollector.Collect();
|
||||
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var c = ref bb.ConstructRoot<ControllerBlob>();
|
||||
BuildControllerBlobInternal(ref controllerData, ref bb, ref c, controllerHash);
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var dt = Time.realtimeSinceStartupAsDouble - startTimeMarker;
|
||||
c.bakingTime = (float)dt;
|
||||
#endif
|
||||
|
||||
var rv = bb.CreateBlobAssetReference<ControllerBlob>(Allocator.Persistent);
|
||||
AddBlobAssetWithCustomHash(ref rv, controllerData.hash);
|
||||
|
||||
// Save created controller to cache
|
||||
BlobCache.SaveBakedControllerToCache(controller, rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BlobAssetReference<ControllerAnimationsBlob> BuildControllerAnimationHashesBlob(AnimatorController ac, Avatar avatar)
|
||||
{
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var cab = ref bb.ConstructRoot<ControllerAnimationsBlob>();
|
||||
var animsBlobArr = bb.Allocate(ref cab.animations, ac.animationClips.Length);
|
||||
for (var i = 0; i < animsBlobArr.Length; ++i)
|
||||
{
|
||||
var anim = ac.animationClips[i];
|
||||
animsBlobArr[i] = BakingUtils.ComputeAnimationHash(anim, avatar);
|
||||
}
|
||||
var rv = bb.CreateBlobAssetReference<ControllerAnimationsBlob>(Allocator.Persistent);
|
||||
AddBlobAsset(ref rv, out _);
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e300e50212f089e4880a8ddb09cbcbc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AnimatorController/AnimatorControllerBaker_BlobBuilder.cs
|
||||
uploadId: 897522
|
||||
+561
@@ -0,0 +1,561 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
internal class AnimatorControllerDataCollector
|
||||
{
|
||||
struct TransitionPrototype
|
||||
{
|
||||
public AnimatorState destinationState;
|
||||
public AnimatorStateMachine destinationStateMachine;
|
||||
public float duration;
|
||||
public float exitTime;
|
||||
public bool hasExitTime;
|
||||
public bool hasFixedDuration;
|
||||
public float offset;
|
||||
public bool muted;
|
||||
public bool solo;
|
||||
public bool canTransitionToSelf;
|
||||
public string ownStateName;
|
||||
public string name;
|
||||
public TransitionBlob.InterruptionSource interruptionSource;
|
||||
public bool orderedInterruption;
|
||||
public AnimatorCondition[] conditions;
|
||||
|
||||
public TransitionPrototype(AnimatorStateTransition t, string ownStateName)
|
||||
{
|
||||
duration = t.duration;
|
||||
exitTime = t.exitTime;
|
||||
hasExitTime = t.hasExitTime;
|
||||
hasFixedDuration = t.hasFixedDuration;
|
||||
offset = t.offset;
|
||||
solo = t.solo;
|
||||
muted = t.mute;
|
||||
canTransitionToSelf = t.canTransitionToSelf;
|
||||
destinationState = t.destinationState;
|
||||
conditions = t.conditions;
|
||||
destinationStateMachine = t.destinationStateMachine;
|
||||
this.ownStateName = ownStateName;
|
||||
name = t.name;
|
||||
interruptionSource = (TransitionBlob.InterruptionSource)t.interruptionSource;
|
||||
orderedInterruption = t.orderedInterruption;
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<AnimatorStateMachine, AnimatorStateMachine> stateMachineParents;
|
||||
readonly AnimatorController ac;
|
||||
readonly RigDefinitionAuthoring rigDefinition;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AnimatorControllerDataCollector(AnimatorController ac, RigDefinitionAuthoring rd)
|
||||
{
|
||||
this.ac = ac;
|
||||
this.rigDefinition = rd;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public RTP.Controller Collect()
|
||||
{
|
||||
stateMachineParents = CreateParentsStateMachineDictionary(ac);
|
||||
|
||||
var rv = new RTP.Controller();
|
||||
rv.name = ac.name;
|
||||
rv.hash = BakingUtils.ComputeControllerHash(ac);
|
||||
rv.parameters = GenerateControllerParametersComputationData(ac.parameters);
|
||||
|
||||
rv.layers = new UnsafeList<RTP.Layer>(ac.layers.Length, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < ac.layers.Length; ++i)
|
||||
{
|
||||
var l = ac.layers[i];
|
||||
var layerData = GenerateControllerLayerComputationData(l, i, rv.parameters);
|
||||
if (!layerData.states.IsEmpty)
|
||||
rv.layers.Add(layerData);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UnsafeList<RTP.Parameter> GenerateControllerParametersComputationData(AnimatorControllerParameter[] aps)
|
||||
{
|
||||
var parameters = new UnsafeList<RTP.Parameter>(aps.Length, Allocator.Temp);
|
||||
for (int i = 0; i < aps.Length; ++i)
|
||||
{
|
||||
var sourceParam = aps[i];
|
||||
var outParam = new RTP.Parameter();
|
||||
|
||||
switch (sourceParam.type)
|
||||
{
|
||||
case AnimatorControllerParameterType.Float:
|
||||
outParam.type = ControllerParameterType.Float;
|
||||
outParam.defaultValue.floatValue = sourceParam.defaultFloat;
|
||||
break;
|
||||
case AnimatorControllerParameterType.Int:
|
||||
outParam.type = ControllerParameterType.Int;
|
||||
outParam.defaultValue.intValue = sourceParam.defaultInt;
|
||||
break;
|
||||
case AnimatorControllerParameterType.Bool:
|
||||
outParam.type = ControllerParameterType.Bool;
|
||||
outParam.defaultValue.boolValue = sourceParam.defaultBool;
|
||||
break;
|
||||
case AnimatorControllerParameterType.Trigger:
|
||||
outParam.type = ControllerParameterType.Trigger;
|
||||
outParam.defaultValue.boolValue = sourceParam.defaultBool;
|
||||
break;
|
||||
};
|
||||
|
||||
outParam.name = sourceParam.name;
|
||||
parameters.Add(outParam);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RTP.Layer GenerateControllerLayerComputationData
|
||||
(
|
||||
AnimatorControllerLayer acl,
|
||||
int layerIndex,
|
||||
in UnsafeList<RTP.Parameter> allParams
|
||||
)
|
||||
{
|
||||
var l = new RTP.Layer();
|
||||
l.name = acl.name;
|
||||
|
||||
var stateList = new UnsafeList<RTP.State>(128, Allocator.Temp);
|
||||
var anyStateTransitions = new UnsafeList<RTP.Transition>(128, Allocator.Temp);
|
||||
|
||||
// If current layer syncs with other, use other layer states as baking source
|
||||
var stateMachine = acl.syncedLayerIndex >= 0 ? ac.layers[acl.syncedLayerIndex].stateMachine : acl.stateMachine;
|
||||
GenerateControllerStateMachineComputationData(stateMachine, acl, ref stateList, ref anyStateTransitions, allParams);
|
||||
|
||||
l.syncedLayerIndex = acl.syncedLayerIndex;
|
||||
l.syncedTiming = acl.syncedLayerAffectsTiming;
|
||||
l.avatarMaskBlobHash = BakingUtils.ComputeAvatarMaskHash(acl.avatarMask, rigDefinition);
|
||||
l.states = stateList;
|
||||
|
||||
var defaultState = stateMachine.defaultState;
|
||||
|
||||
l.defaultStateIndex = defaultState == null ? -1 : stateList.IndexOf(defaultState.GetHashCode());
|
||||
l.anyStateTransitions = anyStateTransitions;
|
||||
l.weight = layerIndex == 0 ? 1 : acl.defaultWeight;
|
||||
l.blendMode = (AnimationBlendingMode)acl.blendingMode;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RTP.Condition GenerateControllerConditionComputationData(AnimatorCondition c, in UnsafeList<RTP.Parameter> allParams)
|
||||
{
|
||||
var rv = new RTP.Condition();
|
||||
rv.paramName = c.parameter;
|
||||
|
||||
var paramIdx = allParams.IndexOf(rv.paramName);
|
||||
if (paramIdx < 0)
|
||||
return default;
|
||||
|
||||
var p = allParams[paramIdx];
|
||||
|
||||
switch (p.type)
|
||||
{
|
||||
case ControllerParameterType.Int:
|
||||
rv.threshold.intValue = (int)c.threshold;
|
||||
break;
|
||||
case ControllerParameterType.Float:
|
||||
rv.threshold.floatValue = c.threshold;
|
||||
break;
|
||||
case ControllerParameterType.Bool:
|
||||
case ControllerParameterType.Trigger:
|
||||
rv.threshold.boolValue = c.threshold > 0;
|
||||
break;
|
||||
}
|
||||
rv.conditionMode = (AnimatorConditionMode)c.mode;
|
||||
rv.name = $"{rv.paramName} {rv.conditionMode} {rv.threshold}";
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RTP.Transition GenerateTransitionDataBetweenStates(in TransitionPrototype t, in UnsafeList<RTP.Parameter> allParams)
|
||||
{
|
||||
var rv = new RTP.Transition();
|
||||
|
||||
rv.duration = t.duration;
|
||||
rv.exitTime = t.exitTime;
|
||||
rv.hasExitTime = t.hasExitTime;
|
||||
rv.hasFixedDuration = t.hasFixedDuration;
|
||||
rv.offset = t.offset;
|
||||
rv.targetStateHash = t.destinationState.GetHashCode();
|
||||
rv.conditions = new UnsafeList<RTP.Condition>(t.conditions.Length, Allocator.Temp);
|
||||
rv.soloFlag = t.solo;
|
||||
rv.muteFlag = t.muted;
|
||||
rv.canTransitionToSelf = t.canTransitionToSelf;
|
||||
rv.interruptionSource = t.interruptionSource;
|
||||
rv.orderedInterruption = t.orderedInterruption;
|
||||
|
||||
if (t.name != "")
|
||||
rv.name = t.name;
|
||||
else
|
||||
rv.name = $"{t.ownStateName} -> {t.destinationState.name}";
|
||||
|
||||
for (int i = 0; i < t.conditions.Length; ++i)
|
||||
{
|
||||
var c = t.conditions[i];
|
||||
var createdCondition = GenerateControllerConditionComputationData(c, allParams);
|
||||
if (!createdCondition.paramName.IsEmpty)
|
||||
rv.conditions.Add(createdCondition);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AnimatorCondition[] MergeConditions(AnimatorCondition[] a, AnimatorCondition[] b)
|
||||
{
|
||||
var rv = new AnimatorCondition[a.Length + b.Length];
|
||||
Array.Copy(a, rv, a.Length);
|
||||
Array.Copy(b, 0, rv, a.Length, b.Length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NativeArray<RTP.Transition> GenerateTransitionsToDestinationStateMachine(TransitionPrototype t, AnimatorStateMachine dstSM, in UnsafeList<RTP.Parameter> allParams)
|
||||
{
|
||||
// Generate transitions to every state connected with entry state
|
||||
var rv = new NativeList<RTP.Transition>(Allocator.Temp);
|
||||
|
||||
for (var i = 0; i < dstSM.entryTransitions.Length; ++i)
|
||||
{
|
||||
var e = dstSM.entryTransitions[i];
|
||||
var conditionsArr = MergeConditions(t.conditions, e.conditions);
|
||||
var modT = t;
|
||||
modT.solo = e.solo;
|
||||
modT.muted = e.mute;
|
||||
modT.destinationStateMachine = e.destinationStateMachine;
|
||||
modT.destinationState = e.destinationState;
|
||||
modT.conditions = conditionsArr;
|
||||
|
||||
var entryTransitions = GenerateControllerTransitionComputationData(modT, dstSM, allParams);
|
||||
rv.AddRange(entryTransitions.AsArray());
|
||||
}
|
||||
|
||||
// Add transition to the default state of target state machine with lowest priority
|
||||
t.destinationState = dstSM.defaultState;
|
||||
var outT = GenerateTransitionDataBetweenStates(t, allParams);
|
||||
rv.Add(outT);
|
||||
|
||||
return rv.AsArray();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NativeArray<RTP.Transition> GenerateTransitionsToExitState(TransitionPrototype t, AnimatorStateMachine stateMachine, in UnsafeList<RTP.Parameter> allParams)
|
||||
{
|
||||
var rv = new NativeList<RTP.Transition>(Allocator.Temp);
|
||||
|
||||
var parentStateMachine = stateMachineParents[stateMachine];
|
||||
var smTransitions = parentStateMachine.GetStateMachineTransitions(stateMachine);
|
||||
for (var i = 0; i < smTransitions.Length; ++i)
|
||||
{
|
||||
var at = smTransitions[i];
|
||||
var conditionsArr = MergeConditions(t.conditions, at.conditions);
|
||||
|
||||
var modT = t;
|
||||
modT.conditions = conditionsArr;
|
||||
modT.destinationState = at.destinationState;
|
||||
modT.destinationStateMachine = at.destinationStateMachine;
|
||||
modT.muted = at.mute;
|
||||
modT.solo = at.solo;
|
||||
modT.name = at.name;
|
||||
|
||||
var outT = GenerateControllerTransitionComputationData(modT, parentStateMachine, allParams);
|
||||
rv.AddRange(outT.AsArray());
|
||||
}
|
||||
|
||||
// Add transition to the default state of target state machine with lowest priority
|
||||
var targetState = parentStateMachine == null ? stateMachine.defaultState : parentStateMachine.defaultState;
|
||||
t.destinationState = targetState;
|
||||
var outToParentSM = GenerateTransitionDataBetweenStates(t, allParams);
|
||||
rv.Add(outToParentSM);
|
||||
|
||||
return rv.AsArray();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NativeList<RTP.Transition> GenerateControllerTransitionComputationData(TransitionPrototype t, AnimatorStateMachine stateMachine, in UnsafeList<RTP.Parameter> allParams)
|
||||
{
|
||||
// Because exit and enter states of substatemachines can have several transitions with different conditions this function can generate several transitions
|
||||
var rv = new NativeList<RTP.Transition>(Allocator.Temp);
|
||||
if (t.destinationState != null)
|
||||
{
|
||||
var outT = GenerateTransitionDataBetweenStates(t, allParams);
|
||||
rv.Add(outT);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t.destinationStateMachine == null)
|
||||
{
|
||||
// This is exit state transition.
|
||||
// If parent state machine is null, behavior exactly the same as destination state machine transition.
|
||||
var parentStateMachine = stateMachineParents[stateMachine];
|
||||
if (parentStateMachine == null)
|
||||
{
|
||||
var dstSMTransitions = GenerateTransitionsToDestinationStateMachine(t, stateMachine, allParams);
|
||||
rv.AddRange(dstSMTransitions);
|
||||
}
|
||||
// Otherwise for parent state machine transitions separate "StateMachineTransitions" should be considered.
|
||||
else
|
||||
{
|
||||
var exitStateTransitions = GenerateTransitionsToExitState(t, stateMachine, allParams);
|
||||
rv.AddRange(exitStateTransitions);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var dstSMTransitions = GenerateTransitionsToDestinationStateMachine(t, t.destinationStateMachine, allParams);
|
||||
rv.AddRange(dstSMTransitions);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RTP.ChildMotion GenerateChildMotionComputationData(ChildMotion cm)
|
||||
{
|
||||
var rv = new RTP.ChildMotion();
|
||||
rv.threshold = cm.threshold;
|
||||
rv.timeScale = cm.timeScale;
|
||||
rv.mirror = cm.mirror;
|
||||
rv.directBlendParameterName = cm.directBlendParameter;
|
||||
// Data for 2D blend trees
|
||||
rv.position2D = cm.position;
|
||||
rv.motion = GenerateMotionComputationData(cm.motion);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RTP.Motion GenerateMotionComputationData(Motion m)
|
||||
{
|
||||
var rv = new RTP.Motion();
|
||||
rv.animationIndex = -1;
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
rv.name = "NULL_MOTION";
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv.name = m.name;
|
||||
|
||||
var anm = m as AnimationClip;
|
||||
if (anm)
|
||||
{
|
||||
rv.animationIndex = Array.IndexOf(ac.animationClips, anm);
|
||||
rv.type = MotionBlob.Type.AnimationClip;
|
||||
}
|
||||
|
||||
var bt = m as BlendTree;
|
||||
if (bt)
|
||||
{
|
||||
rv.type = bt.blendType switch
|
||||
{
|
||||
BlendTreeType.Simple1D => MotionBlob.Type.BlendTree1D,
|
||||
BlendTreeType.Direct => MotionBlob.Type.BlendTreeDirect,
|
||||
BlendTreeType.SimpleDirectional2D => MotionBlob.Type.BlendTree2DSimpleDirectional,
|
||||
BlendTreeType.FreeformDirectional2D => MotionBlob.Type.BlendTree2DFreeformDirectional,
|
||||
BlendTreeType.FreeformCartesian2D => MotionBlob.Type.BlendTree2DFreeformCartesian,
|
||||
_ => MotionBlob.Type.None
|
||||
};
|
||||
rv.blendTree = new RTP.BlendTree();
|
||||
rv.blendTree.name = bt.name;
|
||||
rv.blendTree.motions = new UnsafeList<RTP.ChildMotion>(bt.children.Length, Allocator.Temp);
|
||||
rv.blendTree.blendParameterName = bt.blendParameter;
|
||||
rv.blendTree.blendParameterYName = bt.blendParameterY;
|
||||
rv.blendTree.normalizeBlendValues = GetNormalizedBlendValuesProp(bt);
|
||||
for (int i = 0; i < bt.children.Length; ++i)
|
||||
{
|
||||
var c = bt.children[i];
|
||||
var childMotion = GenerateChildMotionComputationData(c);
|
||||
rv.blendTree.motions.Add(childMotion);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GetNormalizedBlendValuesProp(BlendTree bt)
|
||||
{
|
||||
// Hacky way to extract "Normalized Blend Values" prop
|
||||
var rv = false;
|
||||
using (var so = new SerializedObject(bt))
|
||||
{
|
||||
var p = so.FindProperty("m_NormalizedBlendValues");
|
||||
if (p != null)
|
||||
rv = p.boolValue;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RTP.State GenerateControllerStateComputationData
|
||||
(
|
||||
AnimatorState state,
|
||||
AnimatorStateMachine stateMachine,
|
||||
AnimatorControllerLayer acl,
|
||||
in UnsafeList<RTP.Parameter> allParams
|
||||
)
|
||||
{
|
||||
var rv = new RTP.State();
|
||||
rv.name = state.name;
|
||||
rv.tag = state.tag;
|
||||
rv.hashCode = state.GetHashCode();
|
||||
|
||||
rv.speed = state.speed;
|
||||
rv.speedMultiplierParameter = state.speedParameterActive ? state.speedParameter : "";
|
||||
rv.transitions = new UnsafeList<RTP.Transition>(state.transitions.Length, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < state.transitions.Length; ++i)
|
||||
{
|
||||
var at = state.transitions[i];
|
||||
var t = new TransitionPrototype(at, state.name);
|
||||
var generatedTransitions = GenerateControllerTransitionComputationData(t, stateMachine, allParams);
|
||||
foreach (var gt in generatedTransitions)
|
||||
rv.transitions.Add(gt);
|
||||
}
|
||||
|
||||
FilterSoloAndMuteTransitions(ref rv.transitions);
|
||||
|
||||
// Handle controller layer sync feature
|
||||
var overrideMotion = acl.GetOverrideMotion(state);
|
||||
var motion = overrideMotion != null ? overrideMotion : state.motion;
|
||||
|
||||
rv.motion = GenerateMotionComputationData(motion);
|
||||
if (state.timeParameterActive)
|
||||
rv.timeParameter = state.timeParameter;
|
||||
|
||||
rv.cycleOffset = state.cycleOffset;
|
||||
if (state.cycleOffsetParameterActive)
|
||||
rv.cycleOffsetParameter = state.cycleOffsetParameter;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FilterSoloAndMuteTransitions(ref UnsafeList<RTP.Transition> transitions)
|
||||
{
|
||||
var hasSoloTransitions = false;
|
||||
var l = transitions.Length;
|
||||
for (int i = 0; i < l && !hasSoloTransitions; ++i)
|
||||
{
|
||||
hasSoloTransitions = transitions[i].soloFlag;
|
||||
}
|
||||
|
||||
for (int i = 0; i < l;)
|
||||
{
|
||||
var t = transitions[i];
|
||||
// According to documentation mute flag has precedence
|
||||
if (t.muteFlag)
|
||||
{
|
||||
transitions.RemoveAtSwapBack(i);
|
||||
--l;
|
||||
}
|
||||
else if (!t.soloFlag && hasSoloTransitions)
|
||||
{
|
||||
transitions.RemoveAtSwapBack(i);
|
||||
--l;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Dictionary<AnimatorStateMachine, AnimatorStateMachine> CreateParentsStateMachineDictionary(AnimatorController ac)
|
||||
{
|
||||
var rv = new Dictionary<AnimatorStateMachine, AnimatorStateMachine>();
|
||||
foreach (var al in ac.layers)
|
||||
{
|
||||
FillParentsStateMachineDictionaryRecursively(al.stateMachine, null, ref rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FillParentsStateMachineDictionaryRecursively(AnimatorStateMachine sm, AnimatorStateMachine parent, ref Dictionary<AnimatorStateMachine, AnimatorStateMachine> outDict)
|
||||
{
|
||||
if (sm == null)
|
||||
return;
|
||||
|
||||
outDict.Add(sm, parent);
|
||||
foreach (var csm in sm.stateMachines)
|
||||
{
|
||||
FillParentsStateMachineDictionaryRecursively(csm.stateMachine, sm, ref outDict);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void GenerateControllerStateMachineComputationData
|
||||
(
|
||||
AnimatorStateMachine asm,
|
||||
AnimatorControllerLayer acl,
|
||||
ref UnsafeList<RTP.State> sl,
|
||||
ref UnsafeList<RTP.Transition> anyStateTransitions,
|
||||
in UnsafeList<RTP.Parameter> allParams
|
||||
)
|
||||
{
|
||||
for (int k = 0; k < asm.anyStateTransitions.Length; ++k)
|
||||
{
|
||||
var ast = asm.anyStateTransitions[k];
|
||||
var t = new TransitionPrototype(ast, "Any State");
|
||||
var generatedTransitions = GenerateControllerTransitionComputationData(t, asm, allParams);
|
||||
foreach (var gt in generatedTransitions)
|
||||
anyStateTransitions.Add(gt);
|
||||
}
|
||||
|
||||
FilterSoloAndMuteTransitions(ref anyStateTransitions);
|
||||
|
||||
for (int i = 0; i < asm.states.Length; ++i)
|
||||
{
|
||||
var s = asm.states[i];
|
||||
var generatedState = GenerateControllerStateComputationData(s.state, asm, acl, allParams);
|
||||
sl.Add(generatedState);
|
||||
}
|
||||
|
||||
for (int j = 0; j < asm.stateMachines.Length; ++j)
|
||||
{
|
||||
var sm = asm.stateMachines[j];
|
||||
GenerateControllerStateMachineComputationData(sm.stateMachine, acl, ref sl, ref anyStateTransitions, allParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a446f88f5e2d8d247968ee69e3bbf726
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AnimatorController/AnimatorControllerDataCollector.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcacb11a91364d04c981419806fce3f2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,84 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class AvatarMaskBaker
|
||||
{
|
||||
internal BlobAssetReference<AvatarMaskBakingDataBlob> CreateAvatarMaskBlob(IBaker baker, AvatarMask am, RigDefinitionAuthoring rd)
|
||||
{
|
||||
if (am == null)
|
||||
return default;
|
||||
|
||||
var blobHash = BakingUtils.ComputeAvatarMaskHash(am, rd);
|
||||
var blobExists = baker.TryGetBlobAssetReference<AvatarMaskBakingDataBlob>(blobHash, out var avatarMaskBlob);
|
||||
if (blobExists)
|
||||
return avatarMaskBlob;
|
||||
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var amb = ref bb.ConstructRoot<AvatarMaskBakingDataBlob>();
|
||||
amb.hash = blobHash;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
if (am.name.Length > 0)
|
||||
bb.AllocateString(ref amb.name, am.name);
|
||||
var startTimeMarker = Time.realtimeSinceStartup;
|
||||
#endif
|
||||
|
||||
// Generic avatar mask
|
||||
var avatarMaskIncludedBones = new List<string>();
|
||||
for (int i = 0; i < am.transformCount; ++i)
|
||||
{
|
||||
var bonePath = am.GetTransformPath(i);
|
||||
var boneActive = am.GetTransformActive(i);
|
||||
if (bonePath.Length == 0 || !boneActive) continue;
|
||||
var boneNames = bonePath.Split('/');
|
||||
var leafBoneName = boneNames[^1];
|
||||
avatarMaskIncludedBones.Add(leafBoneName);
|
||||
}
|
||||
|
||||
var includedBoneHashes = bb.Allocate(ref amb.includedBoneHashes, avatarMaskIncludedBones.Count);
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var includedBonePaths = bb.Allocate(ref amb.includedBoneNames, avatarMaskIncludedBones.Count);
|
||||
#endif
|
||||
|
||||
for (var i = 0; i < includedBoneHashes.Length; ++i)
|
||||
{
|
||||
var leafBoneName = avatarMaskIncludedBones[i];
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
bb.AllocateString(ref includedBonePaths[i], leafBoneName);
|
||||
#endif
|
||||
includedBoneHashes[i] = new FixedStringName(leafBoneName).CalculateHash32();
|
||||
}
|
||||
|
||||
// Humanoid avatar mask
|
||||
var humanBodyPartsCount = (int)AvatarMaskBodyPart.LastBodyPart;
|
||||
amb.humanBodyPartsAvatarMask = 0;
|
||||
for (int i = 0; i < humanBodyPartsCount; ++i)
|
||||
{
|
||||
var ambp = (AvatarMaskBodyPart)i;
|
||||
if (am.GetHumanoidBodyPartActive(ambp))
|
||||
amb.humanBodyPartsAvatarMask |= 1u << i;
|
||||
}
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var dt = Time.realtimeSinceStartupAsDouble - startTimeMarker;
|
||||
amb.bakingTime = (float)dt;
|
||||
#endif
|
||||
|
||||
var rv = bb.CreateBlobAssetReference<AvatarMaskBakingDataBlob>(Allocator.Persistent);
|
||||
baker.AddBlobAssetWithCustomHash(ref rv, blobHash);
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8815f346c0ffcd4a91d7954749ca057
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AvatarMask/AvatarMaskBaker.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,131 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
|
||||
internal struct AvatarMaskBakingDataBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
public BlobArray<BlobString> includedBoneNames;
|
||||
public float bakingTime;
|
||||
#endif
|
||||
public Hash128 hash;
|
||||
public BlobArray<uint> includedBoneHashes;
|
||||
public uint humanBodyPartsAvatarMask;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[TemporaryBakingType]
|
||||
internal struct AvatarMaskBakingData: IBufferElementData
|
||||
{
|
||||
public Entity rigEntity;
|
||||
public BlobAssetReference<AvatarMaskBakingDataBlob> dataBlob;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
|
||||
[RequireMatchingQueriesForUpdate]
|
||||
partial class AvatarMaskBakingSystem: SystemBase
|
||||
{
|
||||
BakingSystem bakingSystem;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnCreate()
|
||||
{
|
||||
bakingSystem = World.GetExistingSystemManaged<BakingSystem>();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
DynamicBuffer<NewBlobAssetDatabaseRecord<AvatarMaskBlob>> newBlobAssetRecords = default;
|
||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||
|
||||
foreach (var (avatarMaskDataArr, e) in SystemAPI.Query<DynamicBuffer<AvatarMaskBakingData>>()
|
||||
.WithEntityAccess().WithOptions(EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabledEntities))
|
||||
{
|
||||
foreach (var am in avatarMaskDataArr)
|
||||
{
|
||||
if (!EntityManager.HasComponent<RigDefinitionComponent>(am.rigEntity))
|
||||
continue;
|
||||
|
||||
var rigDef = EntityManager.GetComponentData<RigDefinitionComponent>(am.rigEntity);
|
||||
var amb = MakeMaskForAvatar(rigDef.rigBlob, am.dataBlob);
|
||||
|
||||
if (!newBlobAssetRecords.IsCreated)
|
||||
{
|
||||
newBlobAssetRecords = ecb.AddBuffer<NewBlobAssetDatabaseRecord<AvatarMaskBlob>>(e);
|
||||
}
|
||||
var newAvatarMaskBlob = new NewBlobAssetDatabaseRecord<AvatarMaskBlob>()
|
||||
{
|
||||
hash = amb.Value.hash,
|
||||
value = amb
|
||||
};
|
||||
newBlobAssetRecords.Add(newAvatarMaskBlob);
|
||||
}
|
||||
}
|
||||
ecb.Playback(EntityManager);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BlobAssetReference<AvatarMaskBlob> MakeMaskForAvatar(BlobAssetReference<RigDefinitionBlob> rdb, BlobAssetReference<AvatarMaskBakingDataBlob> am)
|
||||
{
|
||||
var l = rdb.Value.bones.Length;
|
||||
var avatarMaskContainerLength = (int)math.ceil(l / 32.0f);
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var ambBuilder = ref bb.ConstructRoot<AvatarMaskBlob>();
|
||||
ambBuilder.hash = am.Value.hash;
|
||||
ambBuilder.humanBodyPartsAvatarMask = am.Value.humanBodyPartsAvatarMask;
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
if (am.Value.name.Length > 0)
|
||||
bb.AllocateString(ref ambBuilder.name, am.Value.name.ToString());
|
||||
var includedBoneNames = bb.Allocate(ref ambBuilder.includedBoneNames, am.Value.includedBoneNames.Length);
|
||||
for (var i = 0; i < includedBoneNames.Length; ++i)
|
||||
{
|
||||
bb.AllocateString(ref includedBoneNames[i], am.Value.includedBoneNames[i].ToString());
|
||||
}
|
||||
ambBuilder.bakingTime = am.Value.bakingTime;
|
||||
#endif
|
||||
|
||||
var maskArr = bb.Allocate(ref ambBuilder.includedBoneMask, avatarMaskContainerLength);
|
||||
|
||||
for (var i = 0; i < l; ++i)
|
||||
{
|
||||
ref var rigBone = ref rdb.Value.bones[i];
|
||||
var maskEntriesCount = am.Value.includedBoneHashes.Length;
|
||||
var j = 0;
|
||||
for (; j < maskEntriesCount; ++j)
|
||||
{
|
||||
var maskBoneHash = am.Value.includedBoneHashes[j];
|
||||
if (maskBoneHash == rigBone.hash)
|
||||
break;
|
||||
}
|
||||
|
||||
if (j < maskEntriesCount)
|
||||
{
|
||||
var (uintIndex, mask) = AvatarMaskBlob.GetUintIndexAndMask(i);
|
||||
var avatarMaskValue = maskArr[uintIndex];
|
||||
avatarMaskValue |= mask;
|
||||
maskArr[uintIndex] = avatarMaskValue;
|
||||
}
|
||||
}
|
||||
|
||||
var amb = bb.CreateBlobAssetReference<AvatarMaskBlob>(Allocator.Persistent);
|
||||
bakingSystem.BlobAssetStore.TryAdd(am.Value.hash, ref amb);
|
||||
return amb;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ee83ceaf7c5a9e4a9c8192105c07930
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/AvatarMask/AvatarMaskBakingSystem.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,80 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public static class BakingUtils
|
||||
{
|
||||
public static Hash128 ComputeAnimationHash(uint2 animationAssetID, uint2 avatarAssetID)
|
||||
{
|
||||
var rv = new Hash128(avatarAssetID.x, avatarAssetID.y, animationAssetID.x, animationAssetID.y);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static uint2 GetAssetID(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return 0;
|
||||
|
||||
if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guidString, out long fileID))
|
||||
{
|
||||
// In case of no backed file, use InstanceID
|
||||
#if UNITY_6000_4_OR_NEWER
|
||||
ulong entityID = obj.GetEntityId().GetRawData();
|
||||
#else
|
||||
ulong entityID = (ulong)obj.GetInstanceID();
|
||||
#endif
|
||||
return new uint2((uint)entityID, (uint)(entityID >> 32));
|
||||
}
|
||||
|
||||
var guid = new GUID(guidString);
|
||||
|
||||
var hashBuilder = new xxHash3.StreamingState(true, 121212);
|
||||
hashBuilder.Update(guid);
|
||||
hashBuilder.Update(fileID);
|
||||
var rv = hashBuilder.DigestHash64();
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Hash128 ComputeAnimationHash(AnimationClip animation, Avatar avatar)
|
||||
{
|
||||
var animationAssetID = GetAssetID(animation);
|
||||
var avatarAssetID = GetAssetID(avatar);
|
||||
var rv = ComputeAnimationHash(animationAssetID, avatarAssetID);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Hash128 ComputeControllerHash(AnimatorController controller)
|
||||
{
|
||||
var assetID = GetAssetID(controller);
|
||||
var rv = new Hash128(assetID.x, assetID.y, 0, 0);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Hash128 ComputeAvatarMaskHash(AvatarMask avatarMask, RigDefinitionAuthoring rigDefinition)
|
||||
{
|
||||
var assetID = GetAssetID(avatarMask);
|
||||
var rigHash = rigDefinition.CalculateRigHash();
|
||||
var rv = new Hash128(assetID.x, assetID.y, rigHash.Value.x, rigHash.Value.y);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d735c8faad4bcc240b69f24bafbfad5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/BakingUtils.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,181 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Unity.Entities;
|
||||
using Unity.Entities.Serialization;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class BlobCache: AssetPostprocessor
|
||||
{
|
||||
// Need to have two separate versions because blobs are different depending on RUKHANKA_DEBUG_INFO
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
static readonly int BLOB_VERSION = 6;
|
||||
#else
|
||||
static readonly int BLOB_VERSION = 7;
|
||||
#endif
|
||||
public static string GetBlobCacheDirPath() => $"{Environment.CurrentDirectory.Replace('\\', '/')}/Library/Rukhanka.Animation";
|
||||
public static string GetAnimationBlobCacheDirPath() => GetBlobCacheDirPath() + "/AnimationCache";
|
||||
public static string GetControllerBlobCacheDirPath() => GetBlobCacheDirPath() + "/ControllerCache";
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static string ConstructCachedAnimationBlobPath(AnimationClip animationClip, Avatar avatar)
|
||||
{
|
||||
var animationHash = BakingUtils.ComputeAnimationHash(animationClip, avatar);
|
||||
var avatarName = avatar != null ? avatar.name : "NO_AVATAR";
|
||||
var cacheFileName = $"{animationClip.name}_{avatarName}_{animationHash}.blob";
|
||||
cacheFileName = string.Join("_", cacheFileName.Split(Path.GetInvalidFileNameChars()));
|
||||
var cacheFilePath = $"{GetAnimationBlobCacheDirPath()}/{cacheFileName}";
|
||||
return cacheFilePath;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BlobAssetReference<AnimationClipBlob> LoadBakedAnimationFromCache(AnimationClip animationClip, Avatar avatar)
|
||||
{
|
||||
#if RUKHANKA_NO_BLOB_CACHE
|
||||
var dummy = BLOB_VERSION;
|
||||
return default;
|
||||
#else
|
||||
var cachedBlobPath = ConstructCachedAnimationBlobPath(animationClip, avatar);
|
||||
if (!File.Exists(cachedBlobPath))
|
||||
return default;
|
||||
|
||||
BlobAssetReference<AnimationClipBlob>.TryRead(cachedBlobPath, BLOB_VERSION, out var rv);
|
||||
return rv;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void SaveBakedAnimationToCache(AnimationClip animationClip, Avatar avatar, BlobAssetReference<AnimationClipBlob> bar)
|
||||
{
|
||||
#if !RUKHANKA_NO_BLOB_CACHE
|
||||
var cachedBlobPath = ConstructCachedAnimationBlobPath(animationClip, avatar);
|
||||
Directory.CreateDirectory(GetAnimationBlobCacheDirPath());
|
||||
using (var writer = new StreamBinaryWriter(cachedBlobPath))
|
||||
{
|
||||
writer.Write(BLOB_VERSION);
|
||||
writer.Write(bar);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static string ConstructCachedControllerBlobPath(AnimatorController controller)
|
||||
{
|
||||
var controllerHash = BakingUtils.ComputeControllerHash(controller);
|
||||
var cacheFilePath = $"{GetControllerBlobCacheDirPath()}/{controller.name}_{controllerHash}.blob";
|
||||
return cacheFilePath;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BlobAssetReference<ControllerBlob> LoadBakedControllerFromCache(AnimatorController controller)
|
||||
{
|
||||
#if RUKHANKA_NO_BLOB_CACHE
|
||||
return default;
|
||||
#else
|
||||
var cachedBlobPath = ConstructCachedControllerBlobPath(controller);
|
||||
if (!File.Exists(cachedBlobPath))
|
||||
return default;
|
||||
|
||||
BlobAssetReference<ControllerBlob>.TryRead(cachedBlobPath, BLOB_VERSION, out var rv);
|
||||
return rv;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void SaveBakedControllerToCache(AnimatorController controller, BlobAssetReference<ControllerBlob> bar)
|
||||
{
|
||||
#if !RUKHANKA_NO_BLOB_CACHE
|
||||
var cachedBlobPath = ConstructCachedControllerBlobPath(controller);
|
||||
Directory.CreateDirectory(GetControllerBlobCacheDirPath());
|
||||
using (var writer = new StreamBinaryWriter(cachedBlobPath))
|
||||
{
|
||||
writer.Write(BLOB_VERSION);
|
||||
writer.Write(bar);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static UnityEngine.Object[] LoadAllAssetsAtPath(string assetPath)
|
||||
{
|
||||
if (Directory.Exists(assetPath))
|
||||
{
|
||||
// Return an empty array if the path points to a directory
|
||||
return Array.Empty<UnityEngine.Object>();
|
||||
}
|
||||
|
||||
var rv = typeof(SceneAsset).Equals(AssetDatabase.GetMainAssetTypeAtPath(assetPath)) ?
|
||||
// "Do not use ReadObjectThreaded on scene objects!" Fix
|
||||
new[] { AssetDatabase.LoadMainAssetAtPath(assetPath) } :
|
||||
AssetDatabase.LoadAllAssetsAtPath(assetPath);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
#if !RUKHANKA_NO_BLOB_CACHE
|
||||
// Delete cached file in case of source has changed
|
||||
var controllerBlobCachePath = GetControllerBlobCacheDirPath();
|
||||
Directory.CreateDirectory(controllerBlobCachePath);
|
||||
var cachedControllerBlobPaths = Directory.GetFiles(controllerBlobCachePath);
|
||||
|
||||
var animationBlobCachePath = GetAnimationBlobCacheDirPath();
|
||||
Directory.CreateDirectory(animationBlobCachePath);
|
||||
var cachedAnimationBlobPaths = Directory.GetFiles(animationBlobCachePath);
|
||||
|
||||
foreach (var ia in importedAssets)
|
||||
{
|
||||
var assets = LoadAllAssetsAtPath(ia);
|
||||
foreach (var o in assets)
|
||||
{
|
||||
// Clips cache
|
||||
if (o as AnimationClip)
|
||||
{
|
||||
var animationID = BakingUtils.GetAssetID(o);
|
||||
var animationHash = BakingUtils.ComputeAnimationHash(animationID, 0);
|
||||
var cachedFileEnding = $"{animationHash}.blob".Substring(16);
|
||||
foreach (var p in cachedAnimationBlobPaths)
|
||||
{
|
||||
if (p.EndsWith(cachedFileEnding))
|
||||
File.Delete(p);
|
||||
}
|
||||
}
|
||||
// Controller cache
|
||||
if (o as AnimatorController)
|
||||
{
|
||||
var controllerHashHash = BakingUtils.ComputeControllerHash(o as AnimatorController);
|
||||
var cachedFileEnding = $"{controllerHashHash}.blob";
|
||||
foreach (var p in cachedControllerBlobPaths)
|
||||
{
|
||||
if (p.EndsWith(cachedFileEnding))
|
||||
{
|
||||
File.Delete(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24b5e18cc1868594bb4f0a763c53e343
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/BlobCache.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5cb8aa72157d03548b77bcd1cb7295d2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class BoneVisualizationAuthoring: MonoBehaviour
|
||||
{
|
||||
public float tripodSize;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class BoneVisualizationBaker: Baker<BoneVisualizationAuthoring>
|
||||
{
|
||||
public override void Bake(BoneVisualizationAuthoring a)
|
||||
{
|
||||
#if !UNITY_SERVER
|
||||
var e = GetEntity(TransformUsageFlags.Dynamic);
|
||||
var bvc = new BoneVisualizationComponent()
|
||||
{
|
||||
tripodSize = a.tripodSize
|
||||
};
|
||||
AddComponent(e, bvc);
|
||||
#endif
|
||||
|
||||
#if (RUKHANKA_NO_DEBUG_DRAWER && RUKHANKA_DEBUG_INFO)
|
||||
Debug.LogWarning($"'{a.name}' rig visualization was requested, but DebugDrawer is compiled out via RUKHANKA_NO_DEBUG_DRAWER script symbol. No visualization is available.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7815096ff0c4dc8438b8349dd733f220
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/DebugAndVisualization/BoneVisualizationAuthoring.cs
|
||||
uploadId: 897522
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Entities.Hybrid.Baking;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
|
||||
[UpdateAfter(typeof(BakingOnlyEntityAuthoringBakingSystem))]
|
||||
partial class BoneVisualizationConversionSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
if (!SystemAPI.TryGetSingleton<DebugConfigurationComponent>(out var dcc) || !dcc.visualizeAllRigs)
|
||||
return;
|
||||
|
||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||
|
||||
foreach (var (_, e) in SystemAPI.Query<RigDefinitionComponent>()
|
||||
.WithEntityAccess()
|
||||
.WithOptions(EntityQueryOptions.IncludePrefab)
|
||||
.WithNone<BoneVisualizationComponent>())
|
||||
{
|
||||
// Add BoneVisualizationComponent to every animated entity
|
||||
ecb.AddComponent<BoneVisualizationComponent>(e);
|
||||
}
|
||||
|
||||
ecb.Playback(EntityManager);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ab9d0065e6372b44a2f943adf816075
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/DebugAndVisualization/BoneVisualizationConversionSystem.cs
|
||||
uploadId: 897522
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class RukhankaDebugConfiguration: MonoBehaviour
|
||||
{
|
||||
[Header("Bone Visualization")]
|
||||
public bool visualizeAllRigs;
|
||||
public Color boneColorCPURig = new
|
||||
(
|
||||
DebugConfigurationComponent.CPU_RIG_COLOR.x,
|
||||
DebugConfigurationComponent.CPU_RIG_COLOR.y,
|
||||
DebugConfigurationComponent.CPU_RIG_COLOR.z,
|
||||
DebugConfigurationComponent.CPU_RIG_COLOR.w
|
||||
);
|
||||
public Color boneColorGPURig = new
|
||||
(
|
||||
DebugConfigurationComponent.GPU_RIG_COLOR.x,
|
||||
DebugConfigurationComponent.GPU_RIG_COLOR.y,
|
||||
DebugConfigurationComponent.GPU_RIG_COLOR.z,
|
||||
DebugConfigurationComponent.GPU_RIG_COLOR.w
|
||||
);
|
||||
|
||||
[Header("Skinned Mesh Bounds")]
|
||||
public bool visualizeSkinnedMeshBounds;
|
||||
public Color staticBoundsColor = new
|
||||
(
|
||||
DebugConfigurationComponent.STATIC_BOUNDS_COLOR.x,
|
||||
DebugConfigurationComponent.STATIC_BOUNDS_COLOR.y,
|
||||
DebugConfigurationComponent.STATIC_BOUNDS_COLOR.z,
|
||||
DebugConfigurationComponent.STATIC_BOUNDS_COLOR.w
|
||||
);
|
||||
public Color dynamicBoundsColor = new
|
||||
(
|
||||
DebugConfigurationComponent.DYNAMIC_BOUNDS_COLOR.x,
|
||||
DebugConfigurationComponent.DYNAMIC_BOUNDS_COLOR.y,
|
||||
DebugConfigurationComponent.DYNAMIC_BOUNDS_COLOR.z,
|
||||
DebugConfigurationComponent.DYNAMIC_BOUNDS_COLOR.w
|
||||
);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public class DebugConfigurationBaker: Baker<RukhankaDebugConfiguration>
|
||||
{
|
||||
public override void Bake(RukhankaDebugConfiguration a)
|
||||
{
|
||||
var dcc = DebugConfigurationComponent.Default();
|
||||
|
||||
dcc.visualizeAllRigs = a.visualizeAllRigs;
|
||||
dcc.cpuRigColor = new float4(a.boneColorCPURig.r, a.boneColorCPURig.g, a.boneColorCPURig.b, a.boneColorCPURig.a);
|
||||
dcc.gpuRigColor = new float4(a.boneColorGPURig.r, a.boneColorGPURig.g, a.boneColorGPURig.b, a.boneColorGPURig.a);
|
||||
dcc.visualizeMeshBounds = a.visualizeSkinnedMeshBounds;
|
||||
dcc.staticMeshBoundsColor = new float4(a.staticBoundsColor.r, a.staticBoundsColor.g, a.staticBoundsColor.b, a.staticBoundsColor.a);
|
||||
dcc.dynamicMeshBoundsColor = new float4(a.dynamicBoundsColor.r, a.dynamicBoundsColor.g, a.dynamicBoundsColor.b, a.dynamicBoundsColor.a);
|
||||
|
||||
var e = GetEntity(TransformUsageFlags.None);
|
||||
AddComponent(e, dcc);
|
||||
|
||||
#if (RUKHANKA_NO_DEBUG_DRAWER && RUKHANKA_DEBUG_INFO)
|
||||
if (a.visualizeAllRigs)
|
||||
Debug.LogWarning("All rigs visualization was requested, but DebugDrawer is compiled out via RUKHANKA_NO_DEBUG_DRAWER script symbol. No visualization is available.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 050c017b0a715da438609e4964d1226d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/DebugAndVisualization/RukhankaDebugConfiguration.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 409451e1e1a1ac840b57f45d01094156
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Unity.Entities;
|
||||
using Unity.Rendering;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
|
||||
[UpdateInGroup(typeof(PreBakingSystemGroup))]
|
||||
public partial class DisableEntitiesGraphicsSkinnedMeshBakerSystem: SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var varName = "_IndexToBakerInstances";
|
||||
var bakerDataUtilityType = typeof(BakerDataUtility);
|
||||
var fieldInfo = bakerDataUtilityType.GetField(varName, BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (fieldInfo == null)
|
||||
{
|
||||
throw new NullReferenceException($"Cannot find '{varName}' in {bakerDataUtilityType.Name}. Disabling Entities.Graphics skinned mesh baking is failed.");
|
||||
}
|
||||
|
||||
var typeIndexToBakerInstancesMap = fieldInfo.GetValue(null) as Dictionary<TypeIndex, BakerDataUtility.BakerData[]>;
|
||||
if (typeIndexToBakerInstancesMap == null)
|
||||
{
|
||||
throw new NullReferenceException($"Cannot cast '{varName}' to proper dictionary type. Disabling Entities.Graphics skinned mesh baking is failed.");
|
||||
}
|
||||
|
||||
var entitiesGraphicsSystemType = typeof(EntitiesGraphicsSystem);
|
||||
var skinnedMeshRendererBakerType = entitiesGraphicsSystemType.Assembly.GetType("Unity.Rendering.SkinnedMeshRendererBaker");
|
||||
var skinnedMeshRendererIndex = TypeManager.GetTypeIndex<SkinnedMeshRenderer>();
|
||||
var smrBakers = typeIndexToBakerInstancesMap[skinnedMeshRendererIndex].ToList();
|
||||
for (var i = 0; i < smrBakers.Count; i++)
|
||||
{
|
||||
var smb = smrBakers[i];
|
||||
if (smb.Baker.GetType() == skinnedMeshRendererBakerType)
|
||||
{
|
||||
smrBakers.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
typeIndexToBakerInstancesMap[skinnedMeshRendererIndex] = smrBakers.ToArray();
|
||||
fieldInfo.SetValue(null, typeIndexToBakerInstancesMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb22abbe32349cb4fb8cd0d223f40cdf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Deformation/DisableEntitiesGraphicsSkinnedMeshBakerSystem.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31cce8a5235ed58488c78a677f913f62
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21b5d5642dffd3643a90516d85552963
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: InternalsVisibleTo("Rukhanka.Hybrid")]
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 317cc264b3b7c934dbdef1b95982c881
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/EntitiesInternalsAccess/Unity.Entities.Graphics/EntitiesGraphicsAssemblyVisibility.cs
|
||||
uploadId: 897522
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"reference": "GUID:7a450cf7ca9694b5a8bfa3fd83ec635a"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f991b4da7267da441920f12bbb8e6266
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/EntitiesInternalsAccess/Unity.Entities.Graphics/EntitiesGraphicsReference.asmref
|
||||
uploadId: 897522
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c34ad7afe80ff874c9bd8c794b622133
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: InternalsVisibleTo("Rukhanka.Hybrid")]
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb8acdb259e71194e862f82c2a6fb4d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/EntitiesInternalsAccess/Unity.Entities.Hybrid/EntitiesAssemblyVisibility.cs
|
||||
uploadId: 897522
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"reference": "GUID:734d92eba21c94caba915361bd5ac177"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d5433aae8c2411478ccb26afc3b8ea7
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/EntitiesInternalsAccess/Unity.Entities.Hybrid/EntitiesReference.asmref
|
||||
uploadId: 897522
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88d35a20b54745a4d958e8c6930acba4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: InternalsVisibleTo("Rukhanka.Hybrid")]
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ba0ce6a968739741beb19c617e8d804
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/EntitiesInternalsAccess/Unity.Entities/EntitiesAssemblyVisibility.cs
|
||||
uploadId: 897522
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"reference": "GUID:8819f35a0fc84499b990e90a4ca1911f"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bc7a0016f7762c4bb45bc09b2b281c6
|
||||
AssemblyDefinitionReferenceImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/EntitiesInternalsAccess/Unity.Entities/EntitiesReference.asmref
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f4bb93f989c55f4ab1f6618b03dc07e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
[RequireComponent(typeof(Renderer))]
|
||||
public class GPUAttachmentAuthoring: MonoBehaviour
|
||||
{
|
||||
public int attachedBoneIndex = -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class GPUAttachmentBaker: Baker<GPUAttachmentAuthoring>
|
||||
{
|
||||
public override void Bake(GPUAttachmentAuthoring a)
|
||||
{
|
||||
var e = GetEntity(a, TransformUsageFlags.Dynamic);
|
||||
|
||||
var ga = new GPUAttachmentComponent()
|
||||
{
|
||||
attachedBoneIndex = GetComponentInParent<RigDefinitionAuthoring>(a) != null ? -1 : a.attachedBoneIndex
|
||||
};
|
||||
AddComponent(e, ga);
|
||||
AddComponent<GPUAttachmentBoneIndexMPComponent>(e);
|
||||
AddComponent<GPUAttachmentToBoneTransformMPComponent>(e);
|
||||
AddComponent<GPURigEntityLocalToWorldMPComponent>(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14b34ef51d48dc546b4e80ec8bf8d424
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/GPUAnimationEngine/GPUAttachmentAuthoring.cs
|
||||
uploadId: 897522
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
using Unity.Entities;
|
||||
using Unity.Entities.Hybrid.Baking;
|
||||
using Unity.Rendering;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
|
||||
[RequireMatchingQueriesForUpdate]
|
||||
public partial class GPUAttachmentMeshBakingSystem: SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var ecb = new EntityCommandBuffer(CheckedStateRef.WorldUpdateAllocator);
|
||||
|
||||
// GPU attachments support for submesh render entities
|
||||
foreach(var (aeb, gac0, gac1, gac2, e) in SystemAPI.Query
|
||||
<DynamicBuffer<AdditionalEntitiesBakingData>,
|
||||
GPUAttachmentBoneIndexMPComponent,
|
||||
GPUAttachmentToBoneTransformMPComponent,
|
||||
GPURigEntityLocalToWorldMPComponent>()
|
||||
.WithNone<MaterialMeshInfo>()
|
||||
.WithEntityAccess()
|
||||
.WithOptions(EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabledEntities))
|
||||
{
|
||||
// This is submesh render entity. Propagate GPU attachment components from parent to the children
|
||||
for (var i = 0; i < aeb.Length; ++i)
|
||||
{
|
||||
var childRenderEntity = aeb[i].Value;
|
||||
ecb.AddComponent(childRenderEntity, gac0);
|
||||
ecb.AddComponent(childRenderEntity, gac1);
|
||||
ecb.AddComponent(childRenderEntity, gac2);
|
||||
}
|
||||
ecb.RemoveComponent<GPUAttachmentBoneIndexMPComponent>(e);
|
||||
ecb.RemoveComponent<GPUAttachmentToBoneTransformMPComponent>(e);
|
||||
ecb.RemoveComponent<GPURigEntityLocalToWorldMPComponent>(e);
|
||||
}
|
||||
|
||||
ecb.Playback(EntityManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5f84bde5c6c372488dead3e80bcee4e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/GPUAnimationEngine/GPUAttachmentMeshBakingSystem.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9d7599d3cebe0b44881ce997a6c5b02
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,22 @@
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class AimIKAuthoring: MonoBehaviour
|
||||
{
|
||||
public Transform target;
|
||||
public float angleLimitMin;
|
||||
public float angleLimitMax;
|
||||
public Vector3 forwardVector = Vector3.forward;
|
||||
[Range(0, 1)]
|
||||
public float weight = 1;
|
||||
|
||||
public WeightedTransform[] affectedBones;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void OnEnable() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e4319be77ca1d84a993b9434a363e0b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/AimIKAuthoring.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,45 @@
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class AimIKBaker: Baker<AimIKAuthoring>
|
||||
{
|
||||
public override void Bake(AimIKAuthoring a)
|
||||
{
|
||||
var e = GetEntity(a, TransformUsageFlags.None);
|
||||
|
||||
var aik = new AimIKComponent()
|
||||
{
|
||||
angleLimits = math.radians(new float2(a.angleLimitMin, a.angleLimitMax)),
|
||||
target = GetEntity(a.target, TransformUsageFlags.Dynamic),
|
||||
forwardVector = math.normalize(a.forwardVector),
|
||||
weight = a.weight,
|
||||
};
|
||||
|
||||
AddComponent(e, aik);
|
||||
var aikAffectedBones = AddBuffer<AimIKAffectedBoneComponent>(e);
|
||||
|
||||
for (var i = 0; i < a.affectedBones.Length; ++i)
|
||||
{
|
||||
var ab = a.affectedBones[i];
|
||||
if (ab.bone == null)
|
||||
{
|
||||
Debug.LogWarning($"'{a.name}': Affected bone list contains empty bone reference at index {i}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var aimedBone = new AimIKAffectedBoneComponent()
|
||||
{
|
||||
weight = ab.weight,
|
||||
boneEntity = GetEntity(ab.bone, TransformUsageFlags.Dynamic)
|
||||
};
|
||||
|
||||
aikAffectedBones.Add(aimedBone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8666b9be5fee4f74d9eda80824a22de7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/AimIKBaker.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class DynamicBoneChainAuthoring: MonoBehaviour
|
||||
{
|
||||
public Transform tip;
|
||||
[Range(0, 1)]
|
||||
public float inertia = 1;
|
||||
[Range(0, 1)]
|
||||
public float damping = 0.1f;
|
||||
[Range(0, 1)]
|
||||
public float elasticity = 0.1f;
|
||||
[Range(0, 1)]
|
||||
public float stiffness = 0.1f;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void OnEnable() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed30767c708802945837aff39e198384
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/DynamicBoneChainAuthoring.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class DynamicBoneChainBaker: Baker<DynamicBoneChainAuthoring>
|
||||
{
|
||||
public override void Bake(DynamicBoneChainAuthoring a)
|
||||
{
|
||||
if (a.tip == null)
|
||||
{
|
||||
Debug.LogError($"DynamicBoneChainAuthoring '{a.name}': No tip transform defined!");
|
||||
return;
|
||||
}
|
||||
|
||||
var e = GetEntity(a, TransformUsageFlags.None);
|
||||
|
||||
var dbcc = new DynamicBoneChainComponent()
|
||||
{
|
||||
inertia = a.inertia,
|
||||
damping = a.damping,
|
||||
elasticity = a.elasticity,
|
||||
stiffness = a.stiffness,
|
||||
timeAccumulator = 0,
|
||||
prevPosition = a.transform.position
|
||||
};
|
||||
|
||||
AddComponent(e, dbcc);
|
||||
var nodeList = AddBuffer<DynamicBoneChainNode>(e);
|
||||
|
||||
var allBones = GatherChainTransforms(a.transform, a.tip);
|
||||
|
||||
for (var i = 0; i < allBones.Count; ++i)
|
||||
{
|
||||
var currentTransform = allBones[i];
|
||||
var refLocalPos = new BoneTransform()
|
||||
{
|
||||
pos = currentTransform.localPosition,
|
||||
rot = currentTransform.localRotation,
|
||||
scale = currentTransform.localScale
|
||||
};
|
||||
|
||||
var be = new DynamicBoneChainNode()
|
||||
{
|
||||
boneEntity = GetEntity(currentTransform, TransformUsageFlags.Dynamic),
|
||||
prevPosition = currentTransform.position,
|
||||
position = currentTransform.position,
|
||||
referenceLocalPose = refLocalPos,
|
||||
parentIndex = allBones.FindIndex(0, allBones.Count, x => x == currentTransform.parent)
|
||||
};
|
||||
nodeList.Add(be);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
List<Transform> GatherChainTransforms(Transform root, Transform tip)
|
||||
{
|
||||
var rv = new List<Transform>();
|
||||
if (tip == null)
|
||||
return rv;
|
||||
|
||||
var curNode = tip;
|
||||
do
|
||||
{
|
||||
rv.Add(curNode);
|
||||
curNode = curNode.parent;
|
||||
}
|
||||
while (curNode != root && curNode.parent != null);
|
||||
|
||||
rv.Reverse();
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c11627453db5f94b9ab9c20f001b93e
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/DynamicBoneChainBaker.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,18 @@
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class FABRIKAuthoring: MonoBehaviour
|
||||
{
|
||||
[Range(0, 1)]
|
||||
public float weight = 1;
|
||||
public Transform tip;
|
||||
public Transform target;
|
||||
public int numIterations = 15;
|
||||
public float threshold = 0.00001f;
|
||||
|
||||
void OnEnable() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4dc285f22ddcadd458e6484e02e2e7d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/FABRIKAuthoring.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,71 @@
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class FABRIKBaker: Baker<FABRIKAuthoring>
|
||||
{
|
||||
public override void Bake(FABRIKAuthoring a)
|
||||
{
|
||||
if (a.target == null)
|
||||
{
|
||||
Debug.LogError($"'{a.name}': IK target is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (a.tip == null)
|
||||
{
|
||||
Debug.LogError($"'{a.name}': IK tip (end of chain) is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (a.tip == a.target)
|
||||
{
|
||||
Debug.LogError($"'{a.name}': IK tip and target must not be same transform.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (a.tip == a.transform)
|
||||
{
|
||||
Debug.LogError($"'{a.name}': IK tip and root must not be same transform.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckReachability(a.transform, a.tip))
|
||||
{
|
||||
Debug.LogError($"'{a.name}' transform cannot be reached from '{a.tip}'. Check your skeleton hierarchy.");
|
||||
return;
|
||||
}
|
||||
|
||||
var ikComponent = new FABRIKComponent()
|
||||
{
|
||||
target = GetEntity(a.target, TransformUsageFlags.Dynamic),
|
||||
tip = GetEntity(a.tip, TransformUsageFlags.Dynamic),
|
||||
numIterations = a.numIterations,
|
||||
threshold = a.threshold,
|
||||
weight = a.weight
|
||||
};
|
||||
|
||||
var e = GetEntity(TransformUsageFlags.Dynamic);
|
||||
AddComponent(e, ikComponent);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckReachability(Transform root, Transform tip)
|
||||
{
|
||||
var isReachable = false;
|
||||
var curNode = tip;
|
||||
|
||||
while (!isReachable && curNode != null)
|
||||
{
|
||||
isReachable = curNode == root;
|
||||
curNode = curNode.parent;
|
||||
}
|
||||
|
||||
return isReachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1611d67705c6034397e6d8c21a02d78
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/FABRIKBaker.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,19 @@
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class OverrideTransformIKAuthoring: MonoBehaviour
|
||||
{
|
||||
public Transform target;
|
||||
[Range(0, 1)]
|
||||
public float positionWeight = 1;
|
||||
[Range(0, 1)]
|
||||
public float rotationWeight = 1;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void OnEnable() { }
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9371b93ced6f67428c1e5e306fb5aae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/OverrideTransformIKAuthoring.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,22 @@
|
||||
using Unity.Entities;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class OverrideTransformIKBaker: Baker<OverrideTransformIKAuthoring>
|
||||
{
|
||||
public override void Bake(OverrideTransformIKAuthoring a)
|
||||
{
|
||||
var e = GetEntity(a, TransformUsageFlags.None);
|
||||
var otik = new OverrideTransformIKComponent()
|
||||
{
|
||||
target = GetEntity(a.target, TransformUsageFlags.Dynamic),
|
||||
positionWeight = a.positionWeight,
|
||||
rotationWeight = a.rotationWeight,
|
||||
};
|
||||
|
||||
AddComponent(e, otik);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 911a5c8a94d20ab418845f033516babe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/OverrideTransformIKBaker.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,18 @@
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class TwoBoneIKAuthoring: MonoBehaviour
|
||||
{
|
||||
[Range(0, 1)]
|
||||
public float weight = 1;
|
||||
public Transform mid;
|
||||
public Transform tip;
|
||||
public Transform target;
|
||||
public Transform midBentHint;
|
||||
|
||||
void OnEnable() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1cd4368386b54241b1b5121ce9ddf54
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/TwoBoneIKAuthoring.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,24 @@
|
||||
using Unity.Entities;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class TwoBoneIKBaker: Baker<TwoBoneIKAuthoring>
|
||||
{
|
||||
public override void Bake(TwoBoneIKAuthoring a)
|
||||
{
|
||||
var tbik = new TwoBoneIKComponent()
|
||||
{
|
||||
mid = GetEntity(a.mid, TransformUsageFlags.Dynamic),
|
||||
target = GetEntity(a.target, TransformUsageFlags.Dynamic),
|
||||
tip = GetEntity(a.tip, TransformUsageFlags.Dynamic),
|
||||
midBentHint = GetEntity(a.midBentHint, TransformUsageFlags.Dynamic),
|
||||
weight = a.weight
|
||||
};
|
||||
|
||||
var e = GetEntity(TransformUsageFlags.Dynamic);
|
||||
AddComponent(e, tbik);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b231f09e5866af40a601d3663e3bb13
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/TwoBoneIKBaker.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
[Serializable]
|
||||
public class WeightedTransform
|
||||
{
|
||||
public Transform bone;
|
||||
[Range(0, 1)]
|
||||
public float weight = 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 935cc76966b6ca24991aaa2e3001e5db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/IK/WeightedTransform.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f246c219cbcb7d94bb1353720a755008
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
|
||||
public static class AvatarExtensions
|
||||
{
|
||||
static readonly MethodInfo getPreRotationFn = typeof(Avatar).GetMethod("GetPreRotation", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly MethodInfo getPostRotationFn = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly MethodInfo getLimitSignFn = typeof(Avatar).GetMethod("GetLimitSign", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly MethodInfo getZYPostQFn = typeof(Avatar).GetMethod("GetZYPostQ", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly MethodInfo getZYRollFn = typeof(Avatar).GetMethod("GetZYRoll", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
static readonly MethodInfo getAxisLengthFn = typeof(Avatar).GetMethod("GetAxisLength", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Quaternion GetZYPostQ(this Avatar a, int humanId, Quaternion parentQ, Quaternion q)
|
||||
{
|
||||
return (Quaternion)getZYPostQFn.Invoke(a, new object[] {humanId, parentQ, q});
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Quaternion GetZYRoll(this Avatar a, int humanId, Vector3 uvw)
|
||||
{
|
||||
return (Quaternion)getZYRollFn.Invoke(a, new object[] {humanId, uvw});
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static float GetAxisLength(this Avatar a, int humanId)
|
||||
{
|
||||
return (float)getAxisLengthFn.Invoke(a, new object[] {humanId});
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Quaternion GetPreRotation(this Avatar a, int humanId)
|
||||
{
|
||||
return (Quaternion)getPreRotationFn.Invoke(a, new object[] {humanId});
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Quaternion GetPostRotation(this Avatar a, int humanId)
|
||||
{
|
||||
return (Quaternion)getPostRotationFn.Invoke(a, new object[] {humanId});
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Vector3 GetLimitSign(this Avatar a, int humanId)
|
||||
{
|
||||
return (Vector3)getLimitSignFn.Invoke(a, new object[] {humanId});
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static string GetRootMotionNodeName(this Avatar a)
|
||||
{
|
||||
if (a == null) return "";
|
||||
|
||||
var fi = typeof(HumanDescription).GetField("m_RootMotionBoneName", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
return fi == null ? "" : (string)fi.GetValue(a.humanDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32c3a7f3e2e8b3047b37fc8615a558b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Rig/AvatarExtensions.cs
|
||||
uploadId: 897522
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Entities.Hybrid.Baking;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
|
||||
[UpdateInGroup(typeof(BakingSystemGroup))]
|
||||
[UpdateBefore(typeof(BakingOnlyEntityAuthoringBakingSystem))]
|
||||
partial class FlatHierarchyStripBoneEntitiesBakingSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||
foreach (var boneEntitiesToRemove in SystemAPI.Query<DynamicBuffer<BoneEntitiesToRemove>>().WithOptions(EntityQueryOptions.IncludePrefab))
|
||||
{
|
||||
for (int i = 0; i < boneEntitiesToRemove.Length; ++i)
|
||||
{
|
||||
var e = boneEntitiesToRemove[i].boneEntity;
|
||||
if (EntityManager.Exists(e))
|
||||
ecb.AddComponent<BakingOnlyEntity>(e);
|
||||
}
|
||||
}
|
||||
|
||||
ecb.Playback(EntityManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb83eb059c52e0f4db966da19bc1668b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Rig/FlatHierarchyStripBoneEntitiesBakingSystem.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public class HumanBodyPartOverrideAuthoring: MonoBehaviour
|
||||
{
|
||||
public AvatarMaskBodyPart humanBodyPart;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e4b4300f9d9ae3429077a8035ffb6fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Rig/HumanBodyPartOverrideAuthoring.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
[HelpURL("https://docs.rukhanka.com/getting_started#rig-definition")]
|
||||
[Icon(iconPath)]
|
||||
public class RigDefinitionAuthoring: MonoBehaviour
|
||||
{
|
||||
public const string iconPath = "Packages/com.rukhanka.animation/Rukhanka.Editor/Editor Default Resources/Icons/Icon@64.png";
|
||||
|
||||
public enum BoneEntityStrippingMode
|
||||
{
|
||||
None,
|
||||
Automatic,
|
||||
Manual
|
||||
}
|
||||
|
||||
public enum RigConfigSource
|
||||
{
|
||||
FromAnimator,
|
||||
UserDefined
|
||||
}
|
||||
|
||||
public enum AnimationEngine
|
||||
{
|
||||
CPU,
|
||||
GPU
|
||||
}
|
||||
|
||||
public enum RootMotionMode
|
||||
{
|
||||
Normal,
|
||||
DisableBuiltinMovement
|
||||
}
|
||||
|
||||
public RigConfigSource rigConfigSource;
|
||||
public Avatar avatar;
|
||||
public AvatarMask avatarOptimizationMask;
|
||||
public bool applyRootMotion;
|
||||
public bool animationCulling;
|
||||
|
||||
[Tooltip("<color=Cyan><b>None</b></color> - keep all skeleton bone entities.\n<color=Cyan><b>Automatic</b></color> - automatically strip unreferenced bone entities.\n<color=Cyan><b>Manual</b></color> - included and stripped bone entities will be taken from specified avatar mask. This mode will make 'flat' bone hierarchy.")]
|
||||
public BoneEntityStrippingMode boneEntityStrippingMode;
|
||||
[Tooltip("<color=Cyan><b>Normal</b></color> - Compute root motion movement from animations and apply it to the animated entity pose.\n<color=Cyan><b>Disable Builtin Movement</b></color> - Compute root motion movement but don't apply it to the entity pose. 'RootMotionVelocityComponent' can be used to get movement velocity.")]
|
||||
public RootMotionMode rootMotionMode;
|
||||
public AvatarMask boneStrippingMask;
|
||||
public bool hasAnimationEvents;
|
||||
public bool hasAnimatorControllerEvents;
|
||||
public AnimationEngine animationEngine;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Avatar GetAvatar()
|
||||
{
|
||||
var rv = avatar;
|
||||
if (rigConfigSource == RigConfigSource.FromAnimator)
|
||||
{
|
||||
var anm = GetComponent<Animator>();
|
||||
if (anm)
|
||||
rv = anm.avatar;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Hash128 CalculateRigHash()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var a = GetAvatar();
|
||||
Object obj = a == null ? gameObject : a;
|
||||
var h = BakingUtils.GetAssetID(obj);
|
||||
var rv = new Hash128(h.x, h.y, 0, 0);
|
||||
Assert.IsTrue(!math.all(rv.Value == default), $"Rig hash error '{name}'");
|
||||
#else
|
||||
var rv = new Hash128();
|
||||
#endif
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fb757bd46edfe349a52b4ea886d67e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Rig/RigDefinitionAuthoring.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,600 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Unity.Assertions;
|
||||
using Unity.Mathematics;
|
||||
using Rukhanka.Toolbox;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[TemporaryBakingType]
|
||||
internal struct BoneEntitiesToRemove : IBufferElementData
|
||||
{
|
||||
public Entity boneEntity;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[TemporaryBakingType]
|
||||
internal struct BoneEntityRef: IBufferElementData
|
||||
{
|
||||
public Entity boneEntity;
|
||||
public int rigBoneIndex;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal class InternalSkeletonBone
|
||||
{
|
||||
public Transform boneTransform;
|
||||
public string name;
|
||||
public string parentName;
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
public Vector3 scale;
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
public partial class RigDefinitionBaker: Baker<RigDefinitionAuthoring>
|
||||
{
|
||||
static FieldInfo parentBoneNameField;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static RigDefinitionBaker()
|
||||
{
|
||||
parentBoneNameField = typeof(SkeletonBone).GetField("parentName", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public override void Bake(RigDefinitionAuthoring a)
|
||||
{
|
||||
var e = GetEntity(TransformUsageFlags.Dynamic);
|
||||
|
||||
var animator = GetComponent<Animator>();
|
||||
if (a.rigConfigSource == RigDefinitionAuthoring.RigConfigSource.FromAnimator)
|
||||
{
|
||||
Assert.IsNotNull(animator, "Rig is configured to use parameters from Unity.Animator, but no one found. Please switch to manual configuration mode, or attach Animator to the authoring GameObject.");
|
||||
a.avatar = animator.avatar;
|
||||
a.animationCulling = animator.cullingMode != AnimatorCullingMode.AlwaysAnimate;
|
||||
a.applyRootMotion = animator.applyRootMotion;
|
||||
}
|
||||
|
||||
DependsOn(a.avatar);
|
||||
|
||||
AddBuffer<AnimationToProcessComponent>(e);
|
||||
AddComponent<GPURigFrameOffsetsComponent>(e);
|
||||
CreateRigDefinitionFromRigAuthoring(e, a);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
InternalSkeletonBone CreateSkeletonBoneFromTransform(Transform t, string parentName)
|
||||
{
|
||||
var bone = new InternalSkeletonBone();
|
||||
bone.boneTransform = t;
|
||||
bone.name = t.name;
|
||||
bone.position = t.localPosition;
|
||||
bone.rotation = t.localRotation;
|
||||
bone.scale = t.localScale;
|
||||
bone.parentName = parentName;
|
||||
return bone;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TransformHierarchyWalk(Transform parent, List<InternalSkeletonBone> sb)
|
||||
{
|
||||
for (int i = 0; i < parent.childCount; ++i)
|
||||
{
|
||||
var c = parent.GetChild(i);
|
||||
var ct = c.transform;
|
||||
var bone = CreateSkeletonBoneFromTransform(ct, parent.name);
|
||||
sb.Add(bone);
|
||||
|
||||
TransformHierarchyWalk(ct, sb);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
List<InternalSkeletonBone> CreateAvatarFromObjectHierarchy(GameObject root)
|
||||
{
|
||||
// Manually fill all bone transforms
|
||||
var sb = new List<InternalSkeletonBone>();
|
||||
var rootBone = CreateSkeletonBoneFromTransform(root.transform, "");
|
||||
sb.Add(rootBone);
|
||||
|
||||
TransformHierarchyWalk(root.transform, sb);
|
||||
return sb;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int GetRigRootBoneIndex(Avatar avatar, List<InternalSkeletonBone> rigBones)
|
||||
{
|
||||
if (avatar == null)
|
||||
return 0;
|
||||
|
||||
var rootBoneName = avatar.GetRootMotionNodeName();
|
||||
if (avatar.isHuman)
|
||||
{
|
||||
var hd = avatar.humanDescription;
|
||||
var humanBoneIndexInDesc = Array.FindIndex(hd.human, x => x.humanName == "Hips");
|
||||
rootBoneName = hd.human[humanBoneIndexInDesc].boneName;
|
||||
}
|
||||
var rv = rigBones.FindIndex(x => x.name == rootBoneName);
|
||||
return math.max(rv, 0);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int DeepestHierarchyBoneCount(in BlobBuilderArray<RigBoneInfo> rigBones)
|
||||
{
|
||||
var rv = 0;
|
||||
for (var i = 0; i < rigBones.Length; ++i)
|
||||
{
|
||||
var numBonesInHierarchy = 1;
|
||||
var curBoneIndex = rigBones[i].parentBoneIndex;
|
||||
while (curBoneIndex >= 0)
|
||||
{
|
||||
curBoneIndex = rigBones[curBoneIndex].parentBoneIndex;
|
||||
++numBonesInHierarchy;
|
||||
}
|
||||
rv = math.max(rv, numBonesInHierarchy);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool IsBoneInOptimizationMask(string skeletonBonesPath, AvatarMask mask)
|
||||
{
|
||||
if (mask == null)
|
||||
return true;
|
||||
|
||||
for (var i = 0; i < mask.transformCount; ++i)
|
||||
{
|
||||
var maskPath = mask.GetTransformPath(i);
|
||||
var pathActive = mask.GetTransformActive(i);
|
||||
if (maskPath == skeletonBonesPath)
|
||||
return pathActive;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string[] MakeSkeletonBonesFullPaths(SkeletonBone[] skeletonBones)
|
||||
{
|
||||
var rv = new string[skeletonBones.Length];
|
||||
if (skeletonBones.Length == 0)
|
||||
return rv;
|
||||
|
||||
// First bone need to be empty, because it contains root transform name, but avatar masks have it empty string
|
||||
rv[0] = "";
|
||||
for (var i = 1; i < skeletonBones.Length; ++i)
|
||||
{
|
||||
var sb = skeletonBones[i];
|
||||
var parentName = (string)parentBoneNameField.GetValue(sb);
|
||||
var parentIndex = Array.FindIndex(skeletonBones, 0, i, x => x.name == parentName);
|
||||
var fullName = sb.name;
|
||||
if (parentIndex > 0)
|
||||
fullName = rv[parentIndex] + "/" + sb.name;
|
||||
rv[i] = fullName;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
List<InternalSkeletonBone> CreateInternalRigRepresentation(Avatar avatar, RigDefinitionAuthoring rigDef)
|
||||
{
|
||||
if (avatar == null)
|
||||
{
|
||||
return CreateAvatarFromObjectHierarchy(rigDef.gameObject);
|
||||
}
|
||||
|
||||
var skeleton = avatar.humanDescription.skeleton;
|
||||
|
||||
// Validate avatar optimization mask
|
||||
var rigOptimizationMask = rigDef.avatarOptimizationMask;
|
||||
if (rigOptimizationMask != null)
|
||||
{
|
||||
if (rigOptimizationMask.transformCount != skeleton.Length)
|
||||
{
|
||||
Debug.LogWarning($"'{rigOptimizationMask.name}' bone count ({rigOptimizationMask.transformCount}) does not match rig avatar '{avatar.name}' bone count ({skeleton.Length}). Avatar mask was created for different avatar and ignored.");
|
||||
rigOptimizationMask = null;
|
||||
}
|
||||
}
|
||||
|
||||
var skeletonBonesFullPaths = MakeSkeletonBonesFullPaths(skeleton);
|
||||
|
||||
var rv = new List<InternalSkeletonBone>();
|
||||
for (var i = 0; i < skeleton.Length; ++i)
|
||||
{
|
||||
var boneIsObjectRoot = i == 0;
|
||||
var sb = skeleton[i];
|
||||
|
||||
if (!IsBoneInOptimizationMask(skeletonBonesFullPaths[i], rigOptimizationMask))
|
||||
continue;
|
||||
|
||||
var isb = new InternalSkeletonBone()
|
||||
{
|
||||
boneTransform = boneIsObjectRoot ? rigDef.transform : TransformUtils.FindChildRecursively(rigDef.transform, sb.name),
|
||||
name = sb.name,
|
||||
position = sb.position,
|
||||
rotation = sb.rotation,
|
||||
scale = sb.scale,
|
||||
parentName = (string)parentBoneNameField.GetValue(sb)
|
||||
};
|
||||
rv.Add(isb);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateRigDefinitionFromRigAuthoring(Entity rigEntity, RigDefinitionAuthoring rigDef)
|
||||
{
|
||||
var avatar = rigDef.avatar;
|
||||
|
||||
var skeletonBones = CreateInternalRigRepresentation(avatar, rigDef);
|
||||
if (skeletonBones.Count == 0)
|
||||
{
|
||||
Debug.LogError($"Unity avatar '{avatar.name}' setup is incorrect. Follow <a href=\"https://docs.rukhanka.com/getting_started#rig-definition\">documentation</a> about avatar setup process please.");
|
||||
return;
|
||||
}
|
||||
|
||||
var rv = new RigDefinitionComponent();
|
||||
rv.applyRootMotion = rigDef.applyRootMotion;
|
||||
|
||||
var rigBlobHash = rigDef.CalculateRigHash();
|
||||
var rigBlobExist = TryGetBlobAssetReference<RigDefinitionBlob>(rigBlobHash, out var rigBlob);
|
||||
if (!rigBlobExist)
|
||||
{
|
||||
rigBlob = CreateRigBlob(avatar, rigDef, skeletonBones, rigBlobHash);
|
||||
AddBlobAssetWithCustomHash(ref rigBlob, rigBlobHash);
|
||||
}
|
||||
|
||||
rv.rigBlob = rigBlob;
|
||||
AddComponent(rigEntity, rv);
|
||||
AddBuffer<RootMotionAnimationStateComponent>(rigEntity);
|
||||
var rmvc = new RootMotionVelocityComponent()
|
||||
{
|
||||
worldVelocity = float3.zero,
|
||||
deltaPos = float3.zero,
|
||||
deltaRot = quaternion.identity,
|
||||
removeBuiltinEntityMovement = rigDef.rootMotionMode == RigDefinitionAuthoring.RootMotionMode.DisableBuiltinMovement
|
||||
};
|
||||
AddComponent(rigEntity, rmvc);
|
||||
|
||||
if (rigDef.hasAnimationEvents)
|
||||
{
|
||||
AddBuffer<AnimationEventComponent>(rigEntity);
|
||||
AddBuffer<PreviousProcessedAnimationComponent>(rigEntity);
|
||||
}
|
||||
|
||||
if (rigDef.animationCulling)
|
||||
{
|
||||
AddComponent<CullAnimationsTag>(rigEntity);
|
||||
}
|
||||
|
||||
var isGPUAnimator = rigDef.animationEngine == RigDefinitionAuthoring.AnimationEngine.GPU;
|
||||
AddComponent<GPUAnimationEngineTag>(rigEntity);
|
||||
SetComponentEnabled<GPUAnimationEngineTag>(rigEntity, isGPUAnimator);
|
||||
|
||||
ProcessBoneStrippingMask(rigEntity, rigDef, skeletonBones);
|
||||
CreateBoneEntityRefs(rigEntity, skeletonBones, rigDef);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateBoneEntityRefs(Entity e, List<InternalSkeletonBone> skeletonBones, RigDefinitionAuthoring rigDef)
|
||||
{
|
||||
var transformFlags = TransformUsageFlags.Dynamic;
|
||||
var manualBoneStripping = rigDef.boneEntityStrippingMode == RigDefinitionAuthoring.BoneEntityStrippingMode.Manual;
|
||||
|
||||
var boneEntityRefArr = AddBuffer<BoneEntityRef>(e);
|
||||
for (var i = 0; i < skeletonBones.Count; ++i)
|
||||
{
|
||||
var boneTransformFlags = transformFlags | (manualBoneStripping && i != 0 ? TransformUsageFlags.WorldSpace : 0);
|
||||
var skeletonBone = skeletonBones[i];
|
||||
var boneEntity = GetEntityForBone(skeletonBone.boneTransform, boneTransformFlags, rigDef);
|
||||
boneEntityRefArr.Add(new BoneEntityRef() {boneEntity = boneEntity, rigBoneIndex = i});
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CheckForDuplicatedBones(ref RigDefinitionBlob rdb)
|
||||
{
|
||||
var duplicateBoneChecker = new NativeHashSet<uint>(rdb.bones.Length, Allocator.Temp);
|
||||
for (var i = 0; i < rdb.bones.Length; ++i)
|
||||
{
|
||||
ref var bone = ref rdb.bones[i];
|
||||
if (!duplicateBoneChecker.Add(bone.hash))
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
Debug.LogError($"RigDefinitionBaker: Duplicate bone with name '{bone.name.ToString()}' in rig '{rdb.name.ToString()}'! This is not allowed!");
|
||||
#else
|
||||
Debug.LogError($"RigDefinitionBaker: Duplicate bone with hash '{bone.hash}' in rig '{rdb.hash}'! This is not allowed! Enable 'RUKHANKA_DEBUG_INFO' to see bone names.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BlobAssetReference<RigDefinitionBlob> CreateRigBlob(Avatar avatar, RigDefinitionAuthoring rigDef, List<InternalSkeletonBone> skeletonBones, Hash128 rigHash)
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var startTimeMarker = Time.realtimeSinceStartupAsDouble;
|
||||
#endif
|
||||
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var c = ref bb.ConstructRoot<RigDefinitionBlob>();
|
||||
c.hash = rigHash;
|
||||
c.rootBoneIndex = GetRigRootBoneIndex(avatar, skeletonBones);
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var rigName = rigDef.gameObject.name;
|
||||
if (rigName.Length > 0)
|
||||
bb.AllocateString(ref c.name, rigName);
|
||||
#endif
|
||||
|
||||
var bonesArrayBlob = bb.Allocate(ref c.bones, skeletonBones.Count);
|
||||
for (int i = 0; i < skeletonBones.Count; ++i)
|
||||
{
|
||||
ref var boneBlob = ref bonesArrayBlob[i];
|
||||
CreateRigBoneBlob(bb, ref boneBlob, skeletonBones, i);
|
||||
}
|
||||
|
||||
var rigIsHuman = avatar != null && avatar.isHuman;
|
||||
if (rigIsHuman)
|
||||
{
|
||||
CreateHumanoidData(bb, ref c, bonesArrayBlob, avatar, skeletonBones);
|
||||
}
|
||||
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
var dt = Time.realtimeSinceStartupAsDouble - startTimeMarker;
|
||||
c.bakingTime = (float)dt;
|
||||
#endif
|
||||
|
||||
var rv = bb.CreateBlobAssetReference<RigDefinitionBlob>(Allocator.Persistent);
|
||||
CheckForDuplicatedBones(ref rv.Value);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateHumanoidData(BlobBuilder bb, ref RigDefinitionBlob rdb, BlobBuilderArray<RigBoneInfo> bonesArr, Avatar avatar, List<InternalSkeletonBone> skeletonBones)
|
||||
{
|
||||
ref var hdb = ref bb.Allocate(ref rdb.humanData);
|
||||
var humanToRigArr = bb.Allocate(ref hdb.humanBoneToSkeletonBoneIndices, (int)HumanBodyBones.LastBone);
|
||||
var humanRotArr = bb.Allocate(ref hdb.humanRotData, skeletonBones.Count);
|
||||
//var mirroredIndicesArr = bb.Allocate(ref hdb.mirroredBoneIndices, skeletonBones.Count);
|
||||
|
||||
for (int j = 0; j < humanToRigArr.Length; ++j)
|
||||
humanToRigArr[j] = -1;
|
||||
|
||||
for (int l = 0; l < humanRotArr.Length; ++l)
|
||||
{
|
||||
ref var hrd = ref humanRotArr[l];
|
||||
|
||||
var humanRigIndex = CreateHumanoidBoneRotationData(ref hrd, avatar, skeletonBones[l].name);
|
||||
if (humanRigIndex >= 0)
|
||||
{
|
||||
humanToRigArr[humanRigIndex] = l;
|
||||
// Make muscle neutral ref pose
|
||||
ref var rbi = ref bonesArr[l];
|
||||
rbi.refPose.rot = math.mul(hrd.preRot, hrd.postRot);
|
||||
// Set human body part for this bone
|
||||
rbi.humanBodyPart = humanPartToAvatarMaskPartRemapTable[humanRigIndex];
|
||||
}
|
||||
}
|
||||
|
||||
//CreateHumanoidMirrorData(humanToRigArr, mirroredIndicesArr);
|
||||
SetHumanBodyBodyPartForNonAssignedBones(bonesArr, skeletonBones);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AvatarMaskBodyPart GetAvatarMaskBodyPartFromParent(int boneIndex, BlobBuilderArray<RigBoneInfo> bonesArr)
|
||||
{
|
||||
if (boneIndex < 0)
|
||||
return (AvatarMaskBodyPart)(-1);
|
||||
|
||||
ref var rb = ref bonesArr[boneIndex];
|
||||
if (rb.humanBodyPart >= 0)
|
||||
return rb.humanBodyPart;
|
||||
|
||||
return GetAvatarMaskBodyPartFromParent(rb.parentBoneIndex, bonesArr);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateHumanoidMirrorData(BlobBuilderArray<int> humanToRigArr, BlobBuilderArray<int> mirroredIndicesArr)
|
||||
{
|
||||
for (var i = 0; i < mirroredIndicesArr.Length; ++i)
|
||||
mirroredIndicesArr[i] = i;
|
||||
|
||||
for (var i = 0; i < humanToRigArr.Length; ++i)
|
||||
{
|
||||
ref var v = ref humanToRigArr[i];
|
||||
var mirroredPart = humanoidMirrorTable[i];
|
||||
mirroredIndicesArr[v] = humanToRigArr[(int)mirroredPart];
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SetHumanBodyBodyPartForNonAssignedBones(BlobBuilderArray<RigBoneInfo> bonesArr, List<InternalSkeletonBone> skeletonBones)
|
||||
{
|
||||
// Root bone is a special case
|
||||
bonesArr[0].humanBodyPart = AvatarMaskBodyPart.Root;
|
||||
|
||||
// For other bones search for parent with body part is set and set it to the same value
|
||||
for (int i = 1; i < bonesArr.Length; ++i)
|
||||
{
|
||||
// Override human body part if explicitly specified
|
||||
var t = skeletonBones[i].boneTransform;
|
||||
HumanBodyPartOverrideAuthoring hbpo = null;
|
||||
if (t != null)
|
||||
hbpo = t.GetComponent<HumanBodyPartOverrideAuthoring>();
|
||||
var humanBodyPart = hbpo != null ? hbpo.humanBodyPart : GetAvatarMaskBodyPartFromParent(i, bonesArr);
|
||||
bonesArr[i].humanBodyPart = humanBodyPart;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int CreateHumanoidBoneRotationData(ref HumanRotationData hrd, Avatar a, string boneName)
|
||||
{
|
||||
hrd = HumanRotationData.Identity();
|
||||
|
||||
var hd = a.humanDescription;
|
||||
var humanBoneInSkeletonIndex = Array.FindIndex(hd.human, x => x.boneName == boneName);
|
||||
if (humanBoneInSkeletonIndex < 0)
|
||||
return -1;
|
||||
|
||||
var humanBones = HumanTrait.BoneName;
|
||||
var humanBoneDef = hd.human[humanBoneInSkeletonIndex];
|
||||
var humanBoneId = Array.FindIndex(humanBones, x => x == humanBoneDef.humanName);
|
||||
Debug.Assert(humanBoneId >= 0);
|
||||
|
||||
hrd.preRot = a.GetPreRotation(humanBoneId);
|
||||
hrd.postRot = math.inverse(a.GetPostRotation(humanBoneId));
|
||||
hrd.sign = a.GetLimitSign(humanBoneId);
|
||||
|
||||
var minA = humanBoneDef.limit.min;
|
||||
var maxA = humanBoneDef.limit.max;
|
||||
if (humanBoneDef.limit.useDefaultValues)
|
||||
{
|
||||
minA.x = HumanTrait.GetMuscleDefaultMin(HumanTrait.MuscleFromBone(humanBoneId, 0));
|
||||
minA.y = HumanTrait.GetMuscleDefaultMin(HumanTrait.MuscleFromBone(humanBoneId, 1));
|
||||
minA.z = HumanTrait.GetMuscleDefaultMin(HumanTrait.MuscleFromBone(humanBoneId, 2));
|
||||
|
||||
maxA.x = HumanTrait.GetMuscleDefaultMax(HumanTrait.MuscleFromBone(humanBoneId, 0));
|
||||
maxA.y = HumanTrait.GetMuscleDefaultMax(HumanTrait.MuscleFromBone(humanBoneId, 1));
|
||||
maxA.z = HumanTrait.GetMuscleDefaultMax(HumanTrait.MuscleFromBone(humanBoneId, 2));
|
||||
}
|
||||
hrd.minMuscleAngles = math.radians(minA);
|
||||
hrd.maxMuscleAngles = math.radians(maxA);
|
||||
|
||||
return humanBoneId;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe Entity GetEntityForBone(Transform t, TransformUsageFlags boneFlags, RigDefinitionAuthoring rigDef)
|
||||
{
|
||||
// Hierarchy root should be always included
|
||||
if (t == rigDef.transform)
|
||||
return GetEntity(t, boneFlags);
|
||||
|
||||
if (t == null || t.GetComponent<SkinnedMeshRenderer>() != null)
|
||||
return Entity.Null;
|
||||
|
||||
var automaticBoneStripping = rigDef.boneEntityStrippingMode == RigDefinitionAuthoring.BoneEntityStrippingMode.Automatic;
|
||||
var rv = automaticBoneStripping ? _State.BakedEntityData->GetEntity(t) : GetEntity(t, boneFlags);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateRigBoneBlob(BlobBuilder bb, ref RigBoneInfo rbi, List<InternalSkeletonBone> skeletonBones, int boneIndex)
|
||||
{
|
||||
var boneIsObjectRoot = boneIndex == 0;
|
||||
var skeletonBone = skeletonBones[boneIndex];
|
||||
|
||||
var name = skeletonBone.name;
|
||||
// Special handling of hierarchy root
|
||||
if (boneIsObjectRoot)
|
||||
{
|
||||
name = SpecialBones.UnnamedRootBoneName;
|
||||
}
|
||||
|
||||
var boneName = new FixedStringName(name);
|
||||
var boneHash = boneName.CalculateHash32();
|
||||
rbi.hash = boneHash;
|
||||
rbi.refPose = CreateBoneTransformFromSkeletonBone(skeletonBone);
|
||||
rbi.humanBodyPart = (AvatarMaskBodyPart)(-1);
|
||||
var parentBoneIndex = skeletonBones.FindIndex(x => x.name == skeletonBone.parentName);
|
||||
rbi.parentBoneIndex = parentBoneIndex;
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
if (name.Length > 0)
|
||||
bb.AllocateString(ref rbi.name, name);
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BoneTransform CreateBoneTransformFromSkeletonBone(InternalSkeletonBone skeletonBone)
|
||||
{
|
||||
var pose = new BoneTransform()
|
||||
{
|
||||
pos = skeletonBone.position,
|
||||
rot = skeletonBone.rotation,
|
||||
scale = skeletonBone.scale,
|
||||
};
|
||||
|
||||
if (skeletonBone.boneTransform != null)
|
||||
{
|
||||
pose = new BoneTransform()
|
||||
{
|
||||
pos = skeletonBone.boneTransform.localPosition,
|
||||
rot = skeletonBone.boneTransform.localRotation,
|
||||
scale = skeletonBone.boneTransform.localScale,
|
||||
};
|
||||
}
|
||||
return pose;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ProcessBoneStrippingMask(Entity rigEntity, RigDefinitionAuthoring rda, List<InternalSkeletonBone> rigBones)
|
||||
{
|
||||
// Bone stripping mask is processed only in "Manual" stripping mode
|
||||
if (rda.boneEntityStrippingMode != RigDefinitionAuthoring.BoneEntityStrippingMode.Manual || rda.boneStrippingMask == null)
|
||||
return;
|
||||
|
||||
var m = rda.boneStrippingMask;
|
||||
var bonesToRemove = AddBuffer<BoneEntitiesToRemove>(rigEntity);
|
||||
|
||||
for (int i = 0; i < m.transformCount; ++i)
|
||||
{
|
||||
var isActive = m.GetTransformActive(i);
|
||||
if (isActive) continue;
|
||||
|
||||
var path = m.GetTransformPath(i);
|
||||
var boneIndex = 0;
|
||||
for (; boneIndex < rigBones.Count && !path.EndsWith(rigBones[boneIndex].name); ++boneIndex) { }
|
||||
|
||||
if (boneIndex < rigBones.Count)
|
||||
{
|
||||
var boneEntity = GetEntity(rigBones[boneIndex].boneTransform, TransformUsageFlags.None);
|
||||
bonesToRemove.Add(new BoneEntitiesToRemove() { boneEntity = boneEntity });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f120dd0f19b2524c97ce89535ad94f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Rig/RigDefinitionBaker.cs
|
||||
uploadId: 897522
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public partial class RigDefinitionBaker
|
||||
{
|
||||
public static readonly AvatarMaskBodyPart[] humanPartToAvatarMaskPartRemapTable =
|
||||
{
|
||||
// Hips = 0,
|
||||
AvatarMaskBodyPart.Root,
|
||||
// LeftUpperLeg = 1,
|
||||
AvatarMaskBodyPart.LeftLeg,
|
||||
// RightUpperLeg = 2,
|
||||
AvatarMaskBodyPart.RightLeg,
|
||||
// LeftLowerLeg = 3,
|
||||
AvatarMaskBodyPart.LeftLeg,
|
||||
// RightLowerLeg = 4,
|
||||
AvatarMaskBodyPart.RightLeg,
|
||||
// LeftFoot = 5,
|
||||
AvatarMaskBodyPart.LeftLeg,
|
||||
// RightFoot = 6,
|
||||
AvatarMaskBodyPart.RightLeg,
|
||||
// Spine = 7,
|
||||
AvatarMaskBodyPart.Body,
|
||||
// Chest = 8,
|
||||
AvatarMaskBodyPart.Body,
|
||||
// Neck = 9,
|
||||
AvatarMaskBodyPart.Head,
|
||||
// Head = 10,
|
||||
AvatarMaskBodyPart.Head,
|
||||
// LeftShoulder = 11,
|
||||
AvatarMaskBodyPart.LeftArm,
|
||||
// RightShoulder = 12,
|
||||
AvatarMaskBodyPart.RightArm,
|
||||
// LeftUpperArm = 13,
|
||||
AvatarMaskBodyPart.LeftArm,
|
||||
// RightUpperArm = 14,
|
||||
AvatarMaskBodyPart.RightArm,
|
||||
// LeftLowerArm = 15,
|
||||
AvatarMaskBodyPart.LeftArm,
|
||||
// RightLowerArm = 16,
|
||||
AvatarMaskBodyPart.RightArm,
|
||||
// LeftHand = 17,
|
||||
AvatarMaskBodyPart.LeftArm,
|
||||
// RightHand = 18,
|
||||
AvatarMaskBodyPart.RightArm,
|
||||
// LeftToes = 19,
|
||||
AvatarMaskBodyPart.LeftLeg,
|
||||
// RightToes = 20,
|
||||
AvatarMaskBodyPart.RightLeg,
|
||||
// LeftEye = 21,
|
||||
AvatarMaskBodyPart.Head,
|
||||
// RightEye = 22,
|
||||
AvatarMaskBodyPart.Head,
|
||||
// Jaw = 23,
|
||||
AvatarMaskBodyPart.Head,
|
||||
// LeftThumbProximal = 24,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftThumbIntermediate = 25,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftThumbDistal = 26,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftIndexProximal = 27,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftIndexIntermediate = 28,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftIndexDistal = 29,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftMiddleProximal = 30,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftMiddleIntermediate = 31,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftMiddleDistal = 32,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftRingProximal = 33,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftRingIntermediate = 34,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftRingDistal = 35,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftLittleProximal = 36,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftLittleIntermediate = 37,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// LeftLittleDistal = 38,
|
||||
AvatarMaskBodyPart.LeftFingers,
|
||||
// RightThumbProximal = 39,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightThumbIntermediate = 40,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightThumbDistal = 41,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightIndexProximal = 42,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightIndexIntermediate = 43,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightIndexDistal = 44,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightMiddleProximal = 45,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightMiddleIntermediate = 46,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightMiddleDistal = 47,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightRingProximal = 48,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightRingIntermediate = 49,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightRingDistal = 50,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightLittleProximal = 51,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightLittleIntermediate = 52,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// RightLittleDistal = 53,
|
||||
AvatarMaskBodyPart.RightFingers,
|
||||
// UpperChest = 54,
|
||||
AvatarMaskBodyPart.Body,
|
||||
};
|
||||
|
||||
// Humanoid mirror remap array
|
||||
public static readonly HumanBodyBones[] humanoidMirrorTable =
|
||||
{
|
||||
HumanBodyBones.Hips,
|
||||
HumanBodyBones.RightUpperLeg,
|
||||
HumanBodyBones.LeftUpperLeg,
|
||||
HumanBodyBones.RightLowerLeg,
|
||||
HumanBodyBones.LeftLowerLeg,
|
||||
HumanBodyBones.RightFoot,
|
||||
HumanBodyBones.LeftFoot,
|
||||
HumanBodyBones.Spine,
|
||||
HumanBodyBones.Chest,
|
||||
HumanBodyBones.Neck,
|
||||
HumanBodyBones.Head,
|
||||
HumanBodyBones.RightShoulder,
|
||||
HumanBodyBones.LeftShoulder,
|
||||
HumanBodyBones.RightUpperArm,
|
||||
HumanBodyBones.LeftUpperArm,
|
||||
HumanBodyBones.RightLowerArm,
|
||||
HumanBodyBones.LeftLowerArm,
|
||||
HumanBodyBones.RightHand,
|
||||
HumanBodyBones.LeftHand,
|
||||
HumanBodyBones.RightToes,
|
||||
HumanBodyBones.LeftToes,
|
||||
HumanBodyBones.RightEye,
|
||||
HumanBodyBones.LeftEye,
|
||||
HumanBodyBones.Jaw,
|
||||
HumanBodyBones.RightThumbProximal,
|
||||
HumanBodyBones.RightThumbIntermediate,
|
||||
HumanBodyBones.RightThumbDistal,
|
||||
HumanBodyBones.RightIndexProximal,
|
||||
HumanBodyBones.RightIndexIntermediate,
|
||||
HumanBodyBones.RightIndexDistal,
|
||||
HumanBodyBones.RightMiddleProximal,
|
||||
HumanBodyBones.RightMiddleIntermediate,
|
||||
HumanBodyBones.RightMiddleDistal,
|
||||
HumanBodyBones.RightRingProximal,
|
||||
HumanBodyBones.RightRingIntermediate,
|
||||
HumanBodyBones.RightRingDistal,
|
||||
HumanBodyBones.RightLittleProximal,
|
||||
HumanBodyBones.RightLittleIntermediate,
|
||||
HumanBodyBones.RightLittleDistal,
|
||||
HumanBodyBones.LeftThumbProximal,
|
||||
HumanBodyBones.LeftThumbIntermediate,
|
||||
HumanBodyBones.LeftThumbDistal,
|
||||
HumanBodyBones.LeftIndexProximal,
|
||||
HumanBodyBones.LeftIndexIntermediate,
|
||||
HumanBodyBones.LeftIndexDistal,
|
||||
HumanBodyBones.LeftMiddleProximal,
|
||||
HumanBodyBones.LeftMiddleIntermediate,
|
||||
HumanBodyBones.LeftMiddleDistal,
|
||||
HumanBodyBones.LeftRingProximal,
|
||||
HumanBodyBones.LeftRingIntermediate,
|
||||
HumanBodyBones.LeftRingDistal,
|
||||
HumanBodyBones.LeftLittleProximal,
|
||||
HumanBodyBones.LeftLittleIntermediate,
|
||||
HumanBodyBones.LeftLittleDistal,
|
||||
HumanBodyBones.UpperChest,
|
||||
HumanBodyBones.LastBone,
|
||||
};
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2658459aaed9ec24d8ba4418614dc0d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Rig/RigDefinitionBaker_HumanConversionData.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,32 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Entities.Hybrid.Baking;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.BakingSystem)]
|
||||
[RequireMatchingQueriesForUpdate]
|
||||
[UpdateAfter(typeof(BakingOnlyEntityAuthoringBakingSystem))]
|
||||
public partial class RigDefinitionConversionSystem : SystemBase
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
using var ecb = new EntityCommandBuffer(Allocator.TempJob);
|
||||
var bakingOnlyEntityLookup = SystemAPI.GetComponentLookup<BakingOnlyEntity>(true);
|
||||
|
||||
var createComponentDatasJob = new CreateComponentDatasJob()
|
||||
{
|
||||
ecb = ecb.AsParallelWriter(),
|
||||
bakingOnlyLookup = bakingOnlyEntityLookup
|
||||
};
|
||||
|
||||
createComponentDatasJob.ScheduleParallel();
|
||||
Dependency.Complete();
|
||||
|
||||
ecb.Playback(EntityManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c918fab6ea7b35644bbaa4a816b9d131
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Rig/RigDefinitionConversionSystem.cs
|
||||
uploadId: 897522
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.Hybrid
|
||||
{
|
||||
public partial class RigDefinitionConversionSystem
|
||||
{
|
||||
[BurstCompile]
|
||||
[WithOptions(EntityQueryOptions.IncludePrefab)]
|
||||
partial struct CreateComponentDatasJob: IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<BakingOnlyEntity> bakingOnlyLookup;
|
||||
|
||||
public EntityCommandBuffer.ParallelWriter ecb;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(Entity e, [ChunkIndexInQuery] int chunkIndex, ref RigDefinitionComponent rdc, DynamicBuffer<BoneEntityRef> boneEntityRefs)
|
||||
{
|
||||
for (int l = 0; l < boneEntityRefs.Length; ++l)
|
||||
{
|
||||
var boneEntityRef = boneEntityRefs[l];
|
||||
var boneEntity = boneEntityRef.boneEntity;
|
||||
if (boneEntity != Entity.Null && !bakingOnlyLookup.HasComponent(boneEntity))
|
||||
{
|
||||
var animatorEntityRefComponent = new AnimatorEntityRefComponent()
|
||||
{
|
||||
animatorEntity = e,
|
||||
boneIndexInAnimationRig = boneEntityRef.rigBoneIndex
|
||||
};
|
||||
ecb.AddComponent(chunkIndex, boneEntity, animatorEntityRefComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5515884a3d61aa8469ca13156921cf8e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Rig/RigDefinitionConversionSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "Rukhanka.Hybrid",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:01e5f7c036964124aa192bf5e2d021ba",
|
||||
"GUID:69653bcfe095d1841bfa14a815f86dad",
|
||||
"GUID:734d92eba21c94caba915361bd5ac177",
|
||||
"GUID:7a450cf7ca9694b5a8bfa3fd83ec635a",
|
||||
"GUID:8819f35a0fc84499b990e90a4ca1911f",
|
||||
"GUID:2665a8d13d1b3f18800f46e256720795",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:e04e6c86a9f3947eb95fded39f9e60cc",
|
||||
"GUID:64dd21a71f05646a89405b0ade11289b",
|
||||
"GUID:e0cd26848372d4e5c891c569017e11f1",
|
||||
"GUID:a5baed0c9693541a5bd947d336ec7659"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3bd69b8470189e543a7984590f770554
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Hybrid/Rukhanka.Hybrid.asmdef
|
||||
uploadId: 897522
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user