Netcode Bootstrap
This commit is contained in:
+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
|
||||
Reference in New Issue
Block a user