Files
Nathaniel Cosford f0e287b071 Mods for V1.3
2025-06-03 22:58:58 +09:30

382 lines
12 KiB
C#

#if NotEditor
#endif
using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
[AddComponentMenu("")]
public class AnimationGraphBuilder : MonoBehaviour
{
public enum ParamInWrapper
{
None,
Vanilla,
Weapon,
Attachments,
Both
}
private Animator animator;
private RuntimeAnimatorController vanillaRuntimeController;
private Avatar vanillaAvatar;
private PlayableGraph graph;
private AnimationLayerMixerPlayable mixer;
private AnimatorControllerPlayable weaponControllerPlayable;
private AnimatorControllerPlayable vanillaControllerPlayable;
private Avatar weaponAvatar;
private AvatarMask weaponMask;
private bool isFpv;
private bool isLocalPlayer = true;
private readonly List<MonoBehaviour> graphRelatedBehaviours = new List<MonoBehaviour>();
private AnimatorControllerParameter[] parameters;
private readonly Dictionary<int, ParamInWrapper> paramMapping = new Dictionary<int, ParamInWrapper>();
private readonly Dictionary<string, ParamInWrapper> paramMappingDebug = new Dictionary<string, ParamInWrapper>();
//private Animator[] childAnimators = Array.Empty<Animator>();
public bool HasWeaponOverride => graph.IsValid();
//public AnimatorControllerPlayable VanillaPlayable => vanillaControllerPlayable;
//public AnimatorControllerPlayable WeaponPlayable => weaponControllerPlayable;
public AnimatorControllerParameter[] Parameters => parameters;
private AnimationTargetsAbs CurrentTarget { get; set; }
public IAnimatorWrapper VanillaWrapper { get; private set; }
public IAnimatorWrapper WeaponWrapper { get; private set; }
public AttachmentWrapper AttachmentWrapper { get; private set; }
public static IAnimatorWrapper DummyWrapper { get; } = new AnimatorWrapper(null);
#if NotEditor
public EntityPlayer Player { get; private set; }
#endif
private void Awake()
{
isFpv = transform.name == "baseRigFP";
animator = GetComponent<Animator>();
animator.logWarnings = false;
VanillaWrapper = new AnimatorWrapper(animator);
WeaponWrapper = new AnimatorWrapper(null);
parameters = animator.parameters;
UpdateParamMapping();
vanillaAvatar = animator.avatar;
#if NotEditor
vanillaRuntimeController = isFpv ? SDCSUtils.FPAnimController : SDCSUtils.TPAnimController;
isLocalPlayer = (Player = GetComponentInParent<EntityPlayer>()) is EntityPlayerLocal;
#else
vanillaRuntimeController = animator.runtimeAnimatorController;
#endif
}
private void UpdateParamMapping()
{
paramMapping.Clear();
paramMappingDebug.Clear();
var paramList = new List<AnimatorControllerParameter>();
paramList.AddRange(animator.parameters);
for (int i = 0; i < VanillaWrapper.GetParameterCount(); i++)
{
paramMapping.Add(VanillaWrapper.GetParameter(i).nameHash, ParamInWrapper.Vanilla);
paramMappingDebug.Add(VanillaWrapper.GetParameter(i).name, ParamInWrapper.Vanilla);
}
if (WeaponWrapper != null && WeaponWrapper.IsValid)
{
for (int i = 0; i < WeaponWrapper.GetParameterCount(); i++)
{
var param = WeaponWrapper.GetParameter(i);
if (paramMapping.ContainsKey(param.nameHash))
{
paramMapping[param.nameHash] = ParamInWrapper.Both;
paramMappingDebug[param.name] = ParamInWrapper.Both;
}
else
{
paramMapping.Add(param.nameHash, ParamInWrapper.Weapon);
paramMappingDebug.Add(param.name, ParamInWrapper.Weapon);
paramList.Add(param);
}
}
}
if (AttachmentWrapper != null && AttachmentWrapper.IsValid)
{
foreach (var animator in AttachmentWrapper.animators)
{
for (int i = 0; i < animator.parameterCount; i++)
{
var param = animator.GetParameter(i);
if (!paramMapping.ContainsKey(param.nameHash))
{
paramMapping[param.nameHash] = ParamInWrapper.Attachments;
paramList.Add(param);
}
}
}
}
parameters = paramList.ToArray();
}
public ParamInWrapper GetWrapperRoleByParamHash(int nameHash)
{
if (paramMapping.TryGetValue(nameHash, out var role))
return role;
return ParamInWrapper.None;
}
public ParamInWrapper GetWrapperRoleByParamName(string name)
{
if (string.IsNullOrEmpty(name) || !paramMapping.TryGetValue(Animator.StringToHash(name), out var role))
return ParamInWrapper.None;
return role;
}
public ParamInWrapper GetWrapperRoleByParam(AnimatorControllerParameter param)
{
if (param == null || !paramMapping.TryGetValue(param.nameHash, out var role))
return ParamInWrapper.None;
return role;
}
public void SetChildFloat(int nameHash, float value)
{
if (AttachmentWrapper != null && AttachmentWrapper.IsValid)
{
AttachmentWrapper.SetFloat(nameHash, value);
}
}
public void SetChildBool(int nameHash, bool value)
{
if (AttachmentWrapper != null && AttachmentWrapper.IsValid)
{
AttachmentWrapper.SetBool(nameHash, value);
}
}
public void SetChildInteger(int nameHash, int value)
{
if (AttachmentWrapper != null && AttachmentWrapper.IsValid)
{
AttachmentWrapper.SetInteger(nameHash, value);
}
}
public void SetChildTrigger(int nameHash)
{
if (AttachmentWrapper != null && AttachmentWrapper.IsValid)
{
AttachmentWrapper.SetTrigger(nameHash);
}
}
public void ResetChildTrigger(int nameHash)
{
if (AttachmentWrapper != null && AttachmentWrapper.IsValid)
{
AttachmentWrapper.ResetTrigger(nameHash);
}
}
private void InitGraph()
{
animator.runtimeAnimatorController = null;
if (graph.IsValid())
{
return;
}
graph = PlayableGraph.Create();
mixer = AnimationLayerMixerPlayable.Create(graph, 2);
var output = AnimationPlayableOutput.Create(graph, "output", animator);
output.SetSourcePlayable(mixer);
InitVanilla();
graph.Play();
}
private void InitVanilla()
{
vanillaControllerPlayable = AnimatorControllerPlayable.Create(graph, vanillaRuntimeController);
mixer.ConnectInput(0, vanillaControllerPlayable, 0, isFpv ? 0 : 1.0f);
mixer.SetLayerAdditive(0, false);
}
public void InitWeapon(Transform weaponRoot, RuntimeAnimatorController weaponRuntimeController, AvatarMask weaponMask)
{
InitGraph();
InitBehaviours(weaponRoot);
weaponAvatar = AvatarBuilder.BuildGenericAvatar(gameObject, "Origin");
animator.avatar = weaponAvatar;
weaponControllerPlayable = AnimatorControllerPlayable.Create(graph, weaponRuntimeController);
mixer.ConnectInput(1, weaponControllerPlayable, 0, 1.0f);
mixer.SetLayerAdditive(1, false);
if (weaponMask)
{
mixer.SetLayerMaskFromAvatarMask(1, weaponMask);
}
this.weaponMask = weaponMask;
}
private void DestroyGraph()
{
CleanupBehaviours();
weaponMask = null;
if (graph.IsValid())
{
graph.Destroy();
}
if (animator)
{
animator.runtimeAnimatorController = vanillaRuntimeController;
animator.avatar = vanillaAvatar;
}
}
private void DestroyWeapon()
{
CleanupBehaviours();
weaponMask = null;
if (weaponControllerPlayable.IsValid())
{
mixer.DisconnectInput(1);
weaponControllerPlayable.Destroy();
}
animator.avatar = vanillaAvatar;
Destroy(weaponAvatar);
animator.runtimeAnimatorController = vanillaRuntimeController;
}
public void SetCurrentTarget(AnimationTargetsAbs target)
{
if (CurrentTarget == target)
{
UpdateChildAnimatorArray(target);
return;
}
var sw = new Stopwatch();
sw.Start();
#if NotEditor
bool wasCrouching = VanillaWrapper != null && VanillaWrapper.IsValid && VanillaWrapper.GetBool(AvatarController.isCrouchingHash);
#endif
//var rb = animator.transform.AddMissingComponent<RigBuilder>();
//rb.enabled = false;
if (CurrentTarget)
{
CurrentTarget.SetEnabled(false);
}
bool useGraph = target && (target is PlayGraphTargets || (target.ItemTpv && !isFpv));
if (HasWeaponOverride)
{
if (useGraph)
{
DestroyWeapon();
}
else
{
DestroyGraph();
}
}
CurrentTarget = target;
if (target)
{
target.SetEnabled(true);
}
// Log.Out($"\n#=================Rebuild Start");
//#if NotEditor
// Log.Out($"Remaining RigLayers on build:\n{string.Join("\n", rb.layers.Select(layer => layer.name))}");
//#endif
// animator.UnbindAllSceneHandles();
// animator.UnbindAllStreamHandles();
// rb.enabled = true;
// animator.Rebind();
// Log.Out($"#=================Rebuild Finish\n");
animator.WriteDefaultValues();
if (useGraph)
{
VanillaWrapper = new PlayableWrapper(vanillaControllerPlayable);
WeaponWrapper = new PlayableWrapper(weaponControllerPlayable);
}
else
{
VanillaWrapper = new AnimatorWrapper(animator);
if (target)
{
WeaponWrapper = new AnimatorWrapper(target.ItemAnimator);
}
else
{
WeaponWrapper = DummyWrapper;
}
}
UpdateChildAnimatorArray(target);
UpdateParamMapping();
#if NotEditor
animator.SetWrappedBool(AvatarController.isCrouchingHash, wasCrouching);
if (isFpv)
{
VanillaWrapper.Play("idle", 0, 0f);
VanillaWrapper.SetInteger(AvatarController.weaponHoldTypeHash, -1);
}
if (wasCrouching && !isFpv && VanillaWrapper.GetLayerCount() > 4)
{
VanillaWrapper.Play("2HGeneric", 4, 0);
}
#endif
sw.Stop();
Log.Out($"changing animation target to {(target ? target.name : "null")} took {sw.ElapsedMilliseconds}");
}
private void UpdateChildAnimatorArray(AnimationTargetsAbs target)
{
Animator[] childAnimators;
if (target && target.ItemCurrentOrDefault)
{
List<Animator> animators = new List<Animator>();
foreach (Transform trans in target.ItemCurrentOrDefault)
{
animators.AddRange(trans.GetComponentsInChildren<Animator>());
}
if (target.ItemAnimator)
{
animators.Remove(target.ItemAnimator);
}
childAnimators = animators.ToArray();
}
else
{
childAnimators = Array.Empty<Animator>();
}
AttachmentWrapper = new AttachmentWrapper(childAnimators);
}
private void InitBehaviours(Transform weaponRoot)
{
foreach (var scripts in weaponRoot.GetComponents<IPlayableGraphRelated>())
{
var behaviour = scripts.Init(transform, isLocalPlayer);
if (behaviour)
{
graphRelatedBehaviours.Add(behaviour);
}
}
}
private void CleanupBehaviours()
{
foreach (var behaviour in graphRelatedBehaviours)
{
if (behaviour)
{
(behaviour as IPlayableGraphRelated)?.Disable(transform);
}
}
graphRelatedBehaviours.Clear();
}
private void OnDisable()
{
SetCurrentTarget(null);
}
}