This commit is contained in:
Nahuel Rocchetti 2023-08-25 09:08:16 -03:00
commit 0229014e46
3 changed files with 121 additions and 24 deletions

View file

@ -57,16 +57,16 @@ namespace OpenTS2.Components
public ScenegraphComponent Scenegraph { get; private set; } public ScenegraphComponent Scenegraph { get; private set; }
/// <summary> /// <summary>
/// This is the unity object that all the animation rigging constraints get placed into. /// This is the unity object that all the animation rigs get placed into.
/// </summary> /// </summary>
private GameObject _animationRig; private GameObject _animationRigs;
private Animator _animator;
private RigBuilder _rigBuilder; private RigBuilder _rigBuilder;
/// <summary> /// <summary>
/// Set of IK chains that have already been applied to this sim so they don't get re-applied. /// Set of IK chains that have already been applied to this sim so they don't get re-applied.
/// </summary> /// </summary>
private readonly HashSet<uint> _appliedIKChains = new HashSet<uint>(); private readonly Dictionary<uint, RigLayer> _appliedIKChains = new Dictionary<uint, RigLayer>();
/// <summary> /// <summary>
/// These are basically parent bones of IK chains, this is needed so that if an animation updates the position /// These are basically parent bones of IK chains, this is needed so that if an animation updates the position
@ -90,13 +90,13 @@ namespace OpenTS2.Components
boneRenderer.transforms = boneTransforms.ToArray(); boneRenderer.transforms = boneTransforms.ToArray();
// Add an animator component to the auskel. // Add an animator component to the auskel.
var animator = skeleton.AddComponent<Animator>(); _animator = skeleton.AddComponent<Animator>();
// Add a rig builder. // Add a rig builder.
_rigBuilder = skeleton.AddComponent<RigBuilder>(); _rigBuilder = skeleton.AddComponent<RigBuilder>();
// Add a child rig to the skeleton. // Add a child rig to the skeleton.
_animationRig = new GameObject("UnityAnimationRig", typeof(Rig)); _animationRigs = new GameObject("UnityAnimationRigs");
_animationRig.transform.SetParent(skeleton.transform, worldPositionStays: false); _animationRigs.transform.SetParent(skeleton.transform, worldPositionStays: false);
// HACK: In the auskel model, for some reason the bones that represent the IK goals for the hands are // HACK: In the auskel model, for some reason the bones that represent the IK goals for the hands are
// parented to the root_trans of the skeleton. This is done even though in animations the IK goal bones // parented to the root_trans of the skeleton. This is done even though in animations the IK goal bones
@ -120,8 +120,8 @@ namespace OpenTS2.Components
AddGizmosAroundInverseKinmaticsPositions(); AddGizmosAroundInverseKinmaticsPositions();
_rigBuilder.layers.Add(new RigLayer(_animationRig.GetComponent<Rig>())); //_rigBuilder.layers.Add(new RigLayer(_animationRig.GetComponent<Rig>()));
_rigBuilder.layers[0].Initialize(animator); //_rigBuilder.layers[0].Initialize(animator);
_rigBuilder.Build(); _rigBuilder.Build();
} }
@ -155,14 +155,35 @@ namespace OpenTS2.Components
Destroy(spherePrimitive); Destroy(spherePrimitive);
} }
public void AddInverseKinematicsFromAnimation(AnimResourceConstBlock anim) public void AdjustInverseKinematicWeightsForAnimation(AnimResourceConstBlock anim)
{ {
// TODO: This should eventually live in the animation controller for the sim. This is here for easy testing
// for now.
var auSkelTarget = anim.AnimTargets.FirstOrDefault(t => t.TagName.ToLower() == "auskel"); var auSkelTarget = anim.AnimTargets.FirstOrDefault(t => t.TagName.ToLower() == "auskel");
if (auSkelTarget == null) if (auSkelTarget == null)
{ {
return; return;
} }
AddInverseKinematicsFromAnimation(auSkelTarget);
// Disable all current rigs.
foreach (var rigLayer in _appliedIKChains.Values)
{
rigLayer.active = false;
}
// Activate the ones present in the current animation.
foreach (var chain in auSkelTarget.IKChains)
{
if (!_appliedIKChains.TryGetValue(chain.NameCrc, out var rigLayer))
{
continue;
}
rigLayer.active = true;
}
}
private void AddInverseKinematicsFromAnimation(AnimResourceConstBlock.AnimTarget auSkelTarget)
{
AddInverseKinematicsFromIKChains(auSkelTarget.IKChains); AddInverseKinematicsFromIKChains(auSkelTarget.IKChains);
} }
@ -170,10 +191,13 @@ namespace OpenTS2.Components
{ {
foreach (var chain in ikChains) foreach (var chain in ikChains)
{ {
if (_appliedIKChains.Contains(chain.NameCrc)) if (_appliedIKChains.ContainsKey(chain.NameCrc))
{ {
continue; continue;
} }
var rigObject = new GameObject($"ik-rig-{chain.NameCrc:X}", typeof(Rig));
rigObject.transform.SetParent(_animationRigs.transform);
var rig = rigObject.GetComponent<Rig>();
Debug.Assert(chain.BeginBoneCrc != 0, "ikChain without a beginning bone"); Debug.Assert(chain.BeginBoneCrc != 0, "ikChain without a beginning bone");
Debug.Assert(chain.EndBoneCrc != 0, "ikChain without an end bone"); Debug.Assert(chain.EndBoneCrc != 0, "ikChain without an end bone");
@ -222,7 +246,7 @@ namespace OpenTS2.Components
} }
ikChainObj.GetComponent<TwoBoneIKConstraint>().data = twoBoneConstraint; ikChainObj.GetComponent<TwoBoneIKConstraint>().data = twoBoneConstraint;
ikChainObj.transform.parent = _animationRig.transform; ikChainObj.transform.parent = rigObject.transform;
if (target.Rotation2Crc != 0) if (target.Rotation2Crc != 0)
{ {
@ -237,10 +261,13 @@ namespace OpenTS2.Components
space = OverrideTransformData.Space.Local, space = OverrideTransformData.Space.Local,
}; };
rotationConstraint.GetComponent<OverrideTransform>().data = rotationOverrideConstraint; rotationConstraint.GetComponent<OverrideTransform>().data = rotationOverrideConstraint;
rotationConstraint.transform.parent = _animationRig.transform; rotationConstraint.transform.parent = rigObject.transform;
} }
_appliedIKChains.Add(chain.NameCrc); var rigLayer = new RigLayer(rig);
_rigBuilder.layers.Add(rigLayer);
rigLayer.Initialize(_animator);
_appliedIKChains[chain.NameCrc] = rigLayer;
} }
_rigBuilder.Build(); _rigBuilder.Build();

View file

@ -1,4 +1,7 @@
using OpenTS2.Common; using System.Collections;
using System.Collections.Generic;
using System.Linq;
using OpenTS2.Common;
using OpenTS2.Components; using OpenTS2.Components;
using OpenTS2.Content; using OpenTS2.Content;
using OpenTS2.Content.DBPF.Scenegraph; using OpenTS2.Content.DBPF.Scenegraph;
@ -10,6 +13,15 @@ namespace OpenTS2.Engine.Tests
{ {
public class SimAnimationTest : MonoBehaviour public class SimAnimationTest : MonoBehaviour
{ {
public string animationName = "";
[TextArea(minLines: 10, maxLines: 10)]
public string matchedNames = "";
private readonly Dictionary<string, ScenegraphAnimationAsset> _animations = new Dictionary<string, ScenegraphAnimationAsset>();
private Animation _animationObj;
private SimCharacterComponent _sim;
private void Start() private void Start()
{ {
var contentProvider = ContentProvider.Get(); var contentProvider = ContentProvider.Get();
@ -19,24 +31,78 @@ namespace OpenTS2.Engine.Tests
Filesystem.GetPackagesInDirectory(Filesystem.GetDataPathForProduct(ProductFlags.BaseGame) + Filesystem.GetPackagesInDirectory(Filesystem.GetDataPathForProduct(ProductFlags.BaseGame) +
"/Res/Sims3D")); "/Res/Sims3D"));
var sim = SimCharacterComponent.CreateNakedBaseSim(); // Load all animations involving auskel and put them in the dictionary.
foreach (var animationAsset in contentProvider.GetAssetsOfType<ScenegraphAnimationAsset>(TypeIDs.SCENEGRAPH_ANIM))
{
var auSkelTarget = animationAsset.AnimResource.AnimTargets.FirstOrDefault(t => t.TagName.ToLower() == "auskel");
if (auSkelTarget == null)
{
continue;
}
_animations[animationAsset.AnimResource.ScenegraphResource.ResourceName] = animationAsset;
}
UpdateAnimationsListAndGetSelection();
_sim = SimCharacterComponent.CreateNakedBaseSim();
const string animationName = "a-pose-neutral-stand_anim"; const string animationName = "a-pose-neutral-stand_anim";
var anim = contentProvider.GetAsset<ScenegraphAnimationAsset>( var anim = contentProvider.GetAsset<ScenegraphAnimationAsset>(
new ResourceKey(animationName, GroupIDs.Scenegraph, TypeIDs.SCENEGRAPH_ANIM)); new ResourceKey(animationName, GroupIDs.Scenegraph, TypeIDs.SCENEGRAPH_ANIM));
sim.AddInverseKinematicsFromAnimation(anim.AnimResource); _sim.AdjustInverseKinematicWeightsForAnimation(anim.AnimResource);
// Add the test animation. // Add the test animation.
var animationObj = sim.GetComponentInChildren<Animation>(); _animationObj = _sim.GetComponentInChildren<Animation>();
var clip = anim.CreateClipFromResource(sim.Scenegraph.BoneNamesToRelativePaths, sim.Scenegraph.BlendNamesToRelativePaths); var clip = anim.CreateClipFromResource(_sim.Scenegraph.BoneNamesToRelativePaths, _sim.Scenegraph.BlendNamesToRelativePaths);
animationObj.AddClip(clip, animationName); _animationObj.AddClip(clip, animationName);
//const string testAnimation = "a-blowHorn_anim"; //const string testAnimation = "a-blowHorn_anim";
const string testAnimation = "a2o-punchingBag-punch-start_anim"; const string testAnimation = "a2o-punchingBag-punch-start_anim";
anim = contentProvider.GetAsset<ScenegraphAnimationAsset>( anim = contentProvider.GetAsset<ScenegraphAnimationAsset>(
new ResourceKey(testAnimation, GroupIDs.Scenegraph, TypeIDs.SCENEGRAPH_ANIM)); new ResourceKey(testAnimation, GroupIDs.Scenegraph, TypeIDs.SCENEGRAPH_ANIM));
clip = anim.CreateClipFromResource(sim.Scenegraph.BoneNamesToRelativePaths, sim.Scenegraph.BlendNamesToRelativePaths); clip = anim.CreateClipFromResource(_sim.Scenegraph.BoneNamesToRelativePaths, _sim.Scenegraph.BlendNamesToRelativePaths);
animationObj.AddClip(clip, testAnimation); _animationObj.AddClip(clip, testAnimation);
_sim.AdjustInverseKinematicWeightsForAnimation(anim.AnimResource);
}
private void OnValidate()
{
// Check what new animations match.
var anim = UpdateAnimationsListAndGetSelection();
if (anim == null)
{
return;
}
// Play the neutral animation for one second to reset positions to default.
_animationObj.Play("a-pose-neutral-stand_anim");
var animName = anim.AnimResource.ScenegraphResource.ResourceName;
if (_animationObj.GetClip(animName) == null)
{
var clip = anim.CreateClipFromResource(_sim.Scenegraph.BoneNamesToRelativePaths,
_sim.Scenegraph.BlendNamesToRelativePaths);
_animationObj.AddClip(clip, animName);
}
StartCoroutine(PlayClipFrameDelayed(animName, anim));
}
IEnumerator PlayClipFrameDelayed(string animName, ScenegraphAnimationAsset anim)
{
yield return new WaitForFixedUpdate();
_sim.AdjustInverseKinematicWeightsForAnimation(anim.AnimResource);
_animationObj.Play(animName);
}
private ScenegraphAnimationAsset UpdateAnimationsListAndGetSelection()
{
var matchedAnimations = _animations.Where(pair => pair.Key.StartsWith(animationName));
var tenMatchedAnimations = matchedAnimations.Take(10).ToArray();
// Display 10 matching animations.
matchedNames = string.Join("\n", tenMatchedAnimations.Select(pair => pair.Key));
return tenMatchedAnimations.Length == 0 ? null : tenMatchedAnimations[0].Value;
} }
} }
} }

View file

@ -22,10 +22,14 @@ namespace OpenTS2.Files.Formats.DBPF.Scenegraph.Block
/// </summary> /// </summary>
public const double FramesPerTick = 0.03; public const double FramesPerTick = 0.03;
public ScenegraphResource ScenegraphResource;
public AnimTarget[] AnimTargets; public AnimTarget[] AnimTargets;
public AnimResourceConstBlock(PersistTypeInfo blockTypeInfo, AnimTarget[] animTargets) : base(blockTypeInfo) public AnimResourceConstBlock(PersistTypeInfo blockTypeInfo, ScenegraphResource scenegraphResource,
AnimTarget[] animTargets) : base(blockTypeInfo)
{ {
ScenegraphResource = scenegraphResource;
AnimTargets = animTargets; AnimTargets = animTargets;
} }
@ -639,7 +643,7 @@ namespace OpenTS2.Files.Formats.DBPF.Scenegraph.Block
} }
*/ */
return new AnimResourceConstBlock(blockTypeInfo, animTargets); return new AnimResourceConstBlock(blockTypeInfo, resource, animTargets);
} }
// For some reason this format pads to 4-byte boundaries sometimes...we deal with that here. // For some reason this format pads to 4-byte boundaries sometimes...we deal with that here.