mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-01-01 20:25:07 +08:00
ddbc3b164b
* chore: rearrange package structure to have the package at the root * ci: update CI workflows * ci: fixing workflow bugs * ci: recurse building .zip package * ci: more fixes * ci: add back in the nadena.dev VPM repo * ci: fix tests
192 lines
5.1 KiB
C#
192 lines
5.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace nadena.dev.modular_avatar.core.armature_lock
|
|
{
|
|
internal class ArmatureLockController : IDisposable
|
|
{
|
|
private static long lastMovedFrame = 0;
|
|
public static bool MovedThisFrame => Time.frameCount == lastMovedFrame;
|
|
|
|
// Undo operations can reinitialize the MAMA component, which destroys critical lock controller state.
|
|
// Avoid this issue by keeping a static reference to the controller for each MAMA component.
|
|
private static Dictionary<ModularAvatarMergeArmature, ArmatureLockController>
|
|
_controllers = new Dictionary<ModularAvatarMergeArmature, ArmatureLockController>();
|
|
|
|
public delegate IReadOnlyList<(Transform, Transform)> GetTransformsDelegate();
|
|
|
|
private readonly ModularAvatarMergeArmature _mama;
|
|
private readonly GetTransformsDelegate _getTransforms;
|
|
private IArmatureLock _lock;
|
|
|
|
private bool _updateActive;
|
|
|
|
private bool UpdateActive
|
|
{
|
|
get => _updateActive;
|
|
set
|
|
{
|
|
if (UpdateActive == value) return;
|
|
#if UNITY_EDITOR
|
|
if (value)
|
|
{
|
|
UpdateLoopController.OnArmatureLockUpdate += VoidUpdate;
|
|
}
|
|
else
|
|
{
|
|
UpdateLoopController.OnArmatureLockUpdate -= VoidUpdate;
|
|
}
|
|
|
|
_updateActive = value;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private ArmatureLockMode _curMode, _mode;
|
|
|
|
public ArmatureLockMode Mode
|
|
{
|
|
get => _mode;
|
|
set
|
|
{
|
|
if (value == _mode) return;
|
|
|
|
_mode = value;
|
|
|
|
UpdateActive = true;
|
|
}
|
|
}
|
|
|
|
private bool _enabled;
|
|
|
|
public bool Enabled
|
|
{
|
|
get => _enabled;
|
|
set
|
|
{
|
|
if (Enabled == value) return;
|
|
_enabled = value;
|
|
if (_enabled) UpdateActive = true;
|
|
}
|
|
}
|
|
|
|
public ArmatureLockController(ModularAvatarMergeArmature mama, GetTransformsDelegate getTransforms)
|
|
{
|
|
#if UNITY_EDITOR
|
|
AssemblyReloadEvents.beforeAssemblyReload += Dispose;
|
|
#endif
|
|
|
|
this._mama = mama;
|
|
this._getTransforms = getTransforms;
|
|
}
|
|
|
|
public static ArmatureLockController ForMerge(ModularAvatarMergeArmature mama,
|
|
GetTransformsDelegate getTransforms)
|
|
{
|
|
if (!_controllers.TryGetValue(mama, out var controller))
|
|
{
|
|
controller = new ArmatureLockController(mama, getTransforms);
|
|
_controllers[mama] = controller;
|
|
}
|
|
|
|
return controller;
|
|
}
|
|
|
|
public bool IsStable()
|
|
{
|
|
if (Mode == ArmatureLockMode.NotLocked) return true;
|
|
|
|
if (_curMode == _mode && _lock?.IsStable() == true) return true;
|
|
return RebuildLock() && (_lock?.IsStable() ?? false);
|
|
}
|
|
|
|
private void VoidUpdate()
|
|
{
|
|
Update();
|
|
}
|
|
|
|
internal bool Update()
|
|
{
|
|
LockResult result;
|
|
if (!Enabled)
|
|
{
|
|
UpdateActive = false;
|
|
_lock?.Dispose();
|
|
_lock = null;
|
|
return true;
|
|
}
|
|
|
|
if (_curMode == _mode)
|
|
{
|
|
result = _lock?.Execute() ?? LockResult.Failed;
|
|
if (result == LockResult.Success)
|
|
{
|
|
lastMovedFrame = Time.frameCount;
|
|
}
|
|
|
|
if (result != LockResult.Failed) return true;
|
|
}
|
|
|
|
if (!RebuildLock()) return false;
|
|
|
|
result = (_lock?.Execute() ?? LockResult.Failed);
|
|
|
|
return result != LockResult.Failed;
|
|
}
|
|
|
|
private bool RebuildLock()
|
|
{
|
|
_lock?.Dispose();
|
|
_lock = null;
|
|
|
|
var xforms = _getTransforms();
|
|
if (xforms == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
switch (Mode)
|
|
{
|
|
case ArmatureLockMode.BidirectionalExact:
|
|
_lock = new BidirectionalArmatureLock(_getTransforms());
|
|
break;
|
|
case ArmatureLockMode.BaseToMerge:
|
|
_lock = new OnewayArmatureLock(_getTransforms());
|
|
break;
|
|
default:
|
|
UpdateActive = false;
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
_lock = null;
|
|
return false;
|
|
}
|
|
|
|
_curMode = _mode;
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_lock?.Dispose();
|
|
_lock = null;
|
|
|
|
#if UNITY_EDITOR
|
|
AssemblyReloadEvents.beforeAssemblyReload -= Dispose;
|
|
#endif
|
|
|
|
_controllers.Remove(_mama);
|
|
UpdateActive = false;
|
|
}
|
|
}
|
|
} |