Files
necromants-tome-7d2d-3-2/HarmonySrc/HandPosePose.cs
T

615 lines
26 KiB
C#

using System.Collections.Generic;
using System.Globalization;
using System.Text;
using UnityEngine;
using UnityEngine.Scripting;
namespace NecromancerTome
{
/// <summary>
/// Свой хват рук от первого лица (BACKLOG «Заклинания с руки и жесты», 1.6.0). Хват - неподвижная
/// поза: после аниматора (LateUpdate) кости рига EModelSDCS.baseRigFP ставятся в заданные локальные
/// повороты. Аниматор игры не трогаем. Снимки necrobones 03.10: кулак браслета (FP_Block_Hold,
/// HoldType 7) - суставы пальцев повёрнуты по локальной Z на 77-92°, у пустой руки 8-38°;
/// направление руки - RightArm, RightArmRoll, RightForeArm (сгиб по Y), RightHand.
/// Пока - инструмент подбора позы (команда necropose); готовую позу потом зашить как хват.
/// </summary>
public static class HandPose
{
public static bool Enabled;
public static readonly Dictionary<string, Vector3> Overrides = new Dictionary<string, Vector3>();
static GameObject _rig;
static readonly Dictionary<string, Transform> _bones = new Dictionary<string, Transform>();
static GameObject _host;
public static void Init()
{
if (_host != null)
{
return;
}
_host = new GameObject("NecroHandPose");
Object.DontDestroyOnLoad(_host);
_host.AddComponent<HandPoseDriver>();
}
public static Transform Bone(string _name)
{
EntityPlayerLocal player = GameManager.Instance?.World?.GetPrimaryPlayer();
GameObject rig = (player?.emodel as EModelSDCS)?.baseRigFP;
if (rig == null)
{
return null;
}
if (rig != _rig)
{
_rig = rig;
_bones.Clear();
foreach (Transform t in rig.GetComponentsInChildren<Transform>(true))
{
if (!_bones.ContainsKey(t.name))
{
_bones[t.name] = t;
}
}
}
if (_bones.TryGetValue(_name, out Transform b) && b == null)
{
_rig = null; // кость уничтожена (риг пересобран) - перечитать в следующий раз
}
return b;
}
/// <summary>Кости, повёрнутые относительно КАМЕРЫ, а не родителя: ставятся после Overrides
/// (03.10: локальные углы кисти «плывут» при каждом изменении локтя - подбор вслепую).</summary>
public static readonly Dictionary<string, Quaternion> CamOverrides = new Dictionary<string, Quaternion>();
public static Transform Camera()
{
return GameManager.Instance?.World?.GetPrimaryPlayer()?.cameraTransform;
}
/// <summary>Заморозка (03.10: «при нажатии на кнопки руки деформируются, пытаясь совершить старую
/// анимацию»). Поза задаёт 20-30 костей, а удар двигает и остальные - позвоночник, ключицы, вспомогательные
/// кости предплечья и кожи (*_sup_*, *Roll*). Поэтому при включении хвата в покое снимается локальное
/// положение ВСЕХ костей под Hips, и каждый кадр они возвращаются на место, а поверх - поза.
/// Снимок только в покое аниматора (ItemActionIndex = -1), иначе застыл бы кадр удара.</summary>
public static bool Freeze = true;
static readonly List<Transform> _frozen = new List<Transform>();
static readonly List<Vector3> _frozenPos = new List<Vector3>();
static readonly List<Quaternion> _frozenRot = new List<Quaternion>();
static GameObject _frozenRig;
static readonly int ItemActionIndexHash = Animator.StringToHash("ItemActionIndex");
/// <summary>Выход из мира: всё в исходное, ссылки на кости рига отпустить (HandHolds.Update).</summary>
public static void ResetAll()
{
Enabled = false;
KnifeToLeft = false;
Overrides.Clear();
CamOverrides.Clear();
_jabStart = -1f;
Unfreeze();
_bones.Clear();
_rig = null;
}
public static void Unfreeze()
{
_frozen.Clear();
_frozenPos.Clear();
_frozenRot.Clear();
_frozenRig = null;
}
static void FreezeBones()
{
Transform hips = Bone("Hips");
if (hips == null)
{
return;
}
if (_frozenRig != _rig || _frozen.Count == 0 || _frozen[0] == null)
{
Unfreeze();
Animator anim = _rig.GetComponent<Animator>();
if (anim != null && (anim.GetInteger(ItemActionIndexHash) != -1 || anim.IsInTransition(0)))
{
return; // идёт действие - снимок позже, в покое
}
foreach (Transform t in hips.GetComponentsInChildren<Transform>(true))
{
_frozen.Add(t);
_frozenPos.Add(t.localPosition);
_frozenRot.Add(t.localRotation);
}
_frozenRig = _rig;
Debug.Log("[NecromancerTome] HandPose: заморожено костей " + _frozen.Count);
return;
}
for (int i = 0; i < _frozen.Count; i++)
{
Transform t = _frozen[i];
if (t != null)
{
t.localPosition = _frozenPos[i];
t.localRotation = _frozenRot[i];
}
}
}
public static void Apply()
{
if (!Enabled || Overrides.Count == 0 && CamOverrides.Count == 0)
{
if (_frozen.Count > 0)
{
Unfreeze();
}
return;
}
if (Freeze)
{
FreezeBones();
}
foreach (var kv in Overrides)
{
Transform b = Bone(kv.Key);
if (b != null)
{
b.localRotation = Quaternion.Euler(kv.Value);
}
}
if (KnifeToLeft)
{
ApplyKnifeToLeft();
}
Transform cam = CamOverrides.Count > 0 || _jabStart >= 0f ? Camera() : null;
ApplyJab(cam);
if (cam != null)
{
foreach (var kv in CamOverrides)
{
Transform b = Bone(kv.Key);
if (b != null)
{
b.rotation = cam.rotation * kv.Value;
}
}
}
}
/// <summary>Толчок руки к прицелу (указание 05.10, «пиф-паф» на ЛКМ: «чуть сдвигает руку в сторону
/// прицела и возвращает»). Плечо доворачивается так, чтобы кисть прошла JabAmount пути до линии
/// взгляда и вернулась - по синусу за JabTime. Ставится до CamOverrides: кисть сохраняет поворот
/// относительно камеры, сдвигается только её место.</summary>
public const float JabTime = 0.25f;
public const float JabAmount = 0.35f;
static float _jabStart = -1f;
public static void Jab()
{
_jabStart = Time.time;
}
static void ApplyJab(Transform _cam)
{
if (_jabStart < 0f)
{
return;
}
float t = (Time.time - _jabStart) / JabTime;
if (t >= 1f)
{
_jabStart = -1f;
return;
}
Transform arm = Bone("RightArm");
Transform hand = Bone("RightHand");
if (_cam == null || arm == null || hand == null)
{
return;
}
Vector3 toHand = hand.position - arm.position;
Vector3 aim = _cam.position + _cam.forward * Vector3.Dot(hand.position - _cam.position, _cam.forward);
Quaternion full = Quaternion.FromToRotation(toHand, aim - arm.position);
arm.rotation = Quaternion.Slerp(Quaternion.identity, full, Mathf.Sin(t * Mathf.PI) * JabAmount) * arm.rotation;
}
/// <summary>Кисть в осях камеры. Оси кисти правой руки (лог necrobones 03.10): пальцы - локальная +X
/// (суставы пальцев лежат по +X), ладонь - +Y (сгиб пальцев +Z ведёт +X к +Y), +Z - к мизинцу,
/// большой палец - к -Z. Нулевая поза: пальцы вверх, ладонь от игрока, тыльная сторона к камере,
/// большой палец влево. tilt - наклон пальцев влево (+) вокруг взгляда; back - кисть «от себя»
/// (+, верх пальцев уходит вперёд); turn - разворот кисти вокруг вертикали (+ ладонью вправо).</summary>
public static Quaternion PalmToCamera(float _tilt, float _back, float _turn)
{
Quaternion basis = Quaternion.LookRotation(Vector3.right, Vector3.forward);
return Quaternion.AngleAxis(_turn, Vector3.up) * Quaternion.AngleAxis(_back, Vector3.right) * Quaternion.AngleAxis(_tilt, Vector3.forward) * basis;
}
static readonly string[] Fingers = { "Index", "Middle", "Ring", "Pinky" };
/// <summary>Все суставы четырёх пальцев: сгиб по Z (curl), веер по Y у первого сустава
/// (указательный +spread, средний +spread/3, безымянный -spread/3, мизинец -spread).</summary>
public static void SetFingers(string _side, float _curl, float _spread)
{
float[] fan = { 1f, 1f / 3f, -1f / 3f, -1f };
for (int f = 0; f < Fingers.Length; f++)
{
for (int j = 1; j <= 3; j++)
{
Overrides[_side + "Hand" + Fingers[f] + j] = new Vector3(0f, j == 1 ? _spread * fan[f] : 0f, _curl);
}
}
}
/// <summary>Заготовка «раскрытая ладонь»: рука - как у браслета (снимок 03.10), пальцы прямые.</summary>
public static void PresetOpen()
{
Overrides.Clear();
Overrides["RightArm"] = new Vector3(330.8f, 34.9f, 24.3f);
Overrides["RightArmRoll"] = new Vector3(349.7f, 0f, 0f);
Overrides["RightForeArm"] = new Vector3(0f, 88.4f, 0f);
Overrides["RightHand"] = new Vector3(13.3f, 325.8f, 7.7f);
SetFingers("Right", 3f, 0f);
Overrides["RightHandThumb1"] = new Vector3(55f, 68.6f, 40.6f);
Overrides["RightHandThumb2"] = new Vector3(0f, 0f, 0f);
Overrides["RightHandThumb3"] = new Vector3(0f, 0f, 0f);
Enabled = true;
}
/// <summary>Левая рука вдоль туловища, вне кадра (общая для хватов «отрицание» и «пиф-паф», 03.10).</summary>
static void LeftArmDown()
{
Overrides["LeftArm"] = new Vector3(30f, 13.6f, 57.3f);
}
/// <summary>Хват «коза» (до 05.10 - «отрицание»; утверждён пользователем 03.10): рука вперёд, как у браслета, ниже прицела,
/// кисть в линию с предплечьем, пальцы раскрыты. 05.10: средний и безымянный поджаты, как в кулаке
/// (сгиб - как у поджатых пальцев «пиф-пафа»), указательный и мизинец прямые.</summary>
public static void PresetDeny()
{
KnifeToLeft = false;
Overrides.Clear();
CamOverrides.Clear();
Overrides["RightArm"] = new Vector3(345.8f, 34.9f, 24.3f);
Overrides["RightArmRoll"] = new Vector3(349.7f, 0f, 0f);
Overrides["RightForeArm"] = new Vector3(0f, 88.4f, 0f);
Overrides["RightHand"] = new Vector3(45f, 0f, 0f);
SetFingers("Right", 3f, 12f);
for (int j = 1; j <= 3; j++)
{
float curl = j == 3 ? 55f : 80f;
Overrides["RightHandMiddle" + j] = new Vector3(0f, 0f, curl);
Overrides["RightHandRing" + j] = new Vector3(0f, 0f, curl);
}
// Большой палец прижимает поджатые (05.10: «полноценная коза»). Подбор на глаз - уточнять necropose.
Overrides["RightHandThumb1"] = new Vector3(55f, 68.6f, 75f);
Overrides["RightHandThumb2"] = new Vector3(0f, 0f, 40f);
Overrides["RightHandThumb3"] = new Vector3(0f, 0f, 30f);
LeftArmDown();
Unfreeze();
Enabled = true;
}
/// <summary>Хват «пиф-паф» (утверждён 03.10, «Стоп. Замри»): пистолет из пальцев - указательный и средний
/// сомкнуты и смотрят к прицелу, безымянный и мизинец поджаты, большой вверх. Кисть - от камеры.</summary>
public static void PresetPifPaf()
{
KnifeToLeft = false;
Overrides.Clear();
CamOverrides.Clear();
Overrides["RightArm"] = new Vector3(330.8f, 34.9f, 24.3f);
Overrides["RightArmRoll"] = new Vector3(349.7f, 0f, 0f);
Overrides["RightForeArm"] = new Vector3(0f, 88.4f, 0f);
CamOverrides["RightHand"] = PalmToCamera(-90f, 0f, -119f);
for (int j = 1; j <= 3; j++)
{
Overrides["RightHandIndex" + j] = new Vector3(0f, j == 1 ? -3f : 0f, 3f);
Overrides["RightHandMiddle" + j] = new Vector3(0f, j == 1 ? 2f : 0f, 3f);
float curl = j == 3 ? 55f : 80f;
Overrides["RightHandRing" + j] = new Vector3(0f, 0f, curl);
Overrides["RightHandPinky" + j] = new Vector3(0f, 0f, curl);
}
Overrides["RightHandThumb1"] = new Vector3(55f, 68.6f, 20f);
Overrides["RightHandThumb2"] = new Vector3(0f, 0f, 0f);
Overrides["RightHandThumb3"] = new Vector3(0f, 0f, 0f);
LeftArmDown();
Unfreeze();
Enabled = true;
}
/// <summary>Нож в левой кисти (хват для нитей): корень модели в руке ставится каждый кадр от LeftHand. Смещение -
/// снимок хвата 68 (деревянный лук) 16.10, где нож лежал в левой руке («Нож в левой руке»).</summary>
public static bool KnifeToLeft;
static readonly Vector3 KnifeLeftPos = new Vector3(-0.0832f, -0.0354f, 0.0186f);
static readonly Quaternion KnifeLeftRot = new Quaternion(0.56471f, -0.62192f, -0.41297f, 0.35182f);
static void ApplyKnifeToLeft()
{
Transform lh = Bone("LeftHand");
Transform m = HeldTweak.Model();
if (lh != null && m != null)
{
m.SetPositionAndRotation(lh.TransformPoint(KnifeLeftPos), lh.rotation * KnifeLeftRot);
}
}
/// <summary>Хват для нитей (16.10, подбор с пользователем: «Нож в левую руку, а правую сделай как в том хвате, где
/// ты говорил что она перебирает нити»): основа - HoldType 68 (деревянный лук, items.xml); левая рука поднята и
/// отведена влево, нож в ней; правая - раскрытая ладонь хвата 64 (бинт), пересчитанная под плечо хвата 68 так,
/// чтобы кисть стояла там же относительно камеры (scratchpad ik.py).</summary>
public static void PresetThread()
{
Overrides.Clear();
CamOverrides.Clear();
Overrides["RightShoulder"] = new Vector3(0.0f, 180.0f, 180.0f);
Overrides["Right_sup_shoulder_01"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["Right_sup_shoulder_02"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["Right_sup_trap_01"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["RightArm"] = new Vector3(311.4f, 24.9f, 39.2f);
Overrides["Right_sup_arm_01"] = new Vector3(0.0f, 0.0f, 358.2f);
Overrides["RightArmRoll"] = new Vector3(357.9f, 0.0f, 0.0f);
Overrides["RightForeArm"] = new Vector3(0.0f, 79.9f, 0.0f);
Overrides["Right_sup_elbow_00"] = new Vector3(0.0f, 332.8f, 0.0f);
Overrides["RightForeArmRoll"] = new Vector3(8.7f, 0.0f, 0.0f);
Overrides["RightHand"] = new Vector3(7.2f, 346.5f, 5.4f);
Overrides["RightHandIndex0"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["Right_sup_index_00"] = new Vector3(0.0f, 0.0f, 17.3f);
Overrides["Right_sup_index_00_palm"] = new Vector3(0.0f, 0.0f, 15.6f);
Overrides["RightHandIndex1"] = new Vector3(2.6f, 2.9f, 31.3f);
Overrides["Right_sup_index_01_under"] = new Vector3(0.0f, 0.0f, 3.5f);
Overrides["RightHandIndex2"] = new Vector3(0.0f, 0.0f, 52.8f);
Overrides["Right_sup_index_02"] = new Vector3(0.0f, 0.0f, 333.6f);
Overrides["Right_sup_index_02_under"] = new Vector3(0.0f, 0.0f, 5.1f);
Overrides["RightHandIndex3"] = new Vector3(0.0f, 0.0f, 77.0f);
Overrides["Right_sup_index_03"] = new Vector3(0.0f, 0.0f, 321.5f);
Overrides["RightHandIndex4"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["RightHandMiddle0"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["Right_sup_middle_00"] = new Vector3(0.0f, 0.0f, 32.1f);
Overrides["Right_sup_middle_00_palm"] = new Vector3(0.0f, 0.0f, 28.8f);
Overrides["RightHandMiddle1"] = new Vector3(0.0f, 0.0f, 57.7f);
Overrides["Right_sup_middle_01_under"] = new Vector3(0.0f, 0.0f, 5.7f);
Overrides["RightHandMiddle2"] = new Vector3(0.0f, 0.0f, 85.0f);
Overrides["Right_sup_middle_02"] = new Vector3(0.0f, 0.0f, 317.5f);
Overrides["Right_sup_middle_02_under"] = new Vector3(0.0f, 0.0f, 5.1f);
Overrides["RightHandMiddle3"] = new Vector3(0.0f, 0.0f, 77.0f);
Overrides["Right_sup_middle_03"] = new Vector3(0.0f, 0.0f, 321.5f);
Overrides["RightHandMiddle4"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["RightHandPinky0"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["Right_sup_pinky_00"] = new Vector3(0.0f, 0.0f, 45.0f);
Overrides["Right_sup_pinky_00_palm"] = new Vector3(0.0f, 0.0f, 45.0f);
Overrides["RightHandPinky1"] = new Vector3(0.2f, 353.1f, 92.0f);
Overrides["Right_sup_pinky_01_under"] = new Vector3(0.0f, 0.0f, 5.6f);
Overrides["RightHandPinky2"] = new Vector3(0.0f, 0.0f, 84.0f);
Overrides["Right_sup_pinky_02"] = new Vector3(0.0f, 0.0f, 318.0f);
Overrides["Right_sup_pinky_02_under"] = new Vector3(0.0f, 0.0f, 5.2f);
Overrides["RightHandPinky3"] = new Vector3(0.0f, 0.0f, 78.0f);
Overrides["Right_sup_pinky_03"] = new Vector3(0.0f, 0.0f, 321.0f);
Overrides["RightHandPinky4"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["RightHandRing0"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["Right_sup_ring_00"] = new Vector3(0.0f, 0.0f, 33.0f);
Overrides["Right_sup_ring_00_palm"] = new Vector3(0.0f, 0.0f, 33.0f);
Overrides["RightHandRing1"] = new Vector3(358.6f, 355.2f, 66.1f);
Overrides["Right_sup_ring_01_under"] = new Vector3(0.0f, 0.0f, 5.5f);
Overrides["RightHandRing2"] = new Vector3(0.0f, 0.0f, 83.0f);
Overrides["Right_sup_ring_02"] = new Vector3(0.0f, 0.0f, 318.5f);
Overrides["Right_sup_ring_02_under"] = new Vector3(0.0f, 0.0f, 5.1f);
Overrides["RightHandRing3"] = new Vector3(0.0f, 0.0f, 77.0f);
Overrides["Right_sup_ring_03"] = new Vector3(0.0f, 0.0f, 321.5f);
Overrides["RightHandRing4"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["RightHandThumb1"] = new Vector3(27.9f, 75.4f, 33.2f);
Overrides["RightHandThumb2"] = new Vector3(0.0f, 0.0f, 15.8f);
Overrides["Right_sup_thumb_01"] = new Vector3(0.0f, 0.0f, 352.1f);
Overrides["Right_sup_thumb_01_under"] = new Vector3(0.0f, 0.0f, 1.2f);
Overrides["RightHandThumb3"] = new Vector3(0.0f, 0.0f, 17.9f);
Overrides["Right_sup_thumb_02"] = new Vector3(0.0f, 0.0f, 351.0f);
Overrides["RightHandThumb4"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["necroKnifePrefab(Clone)"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["boneShiv_LOD0"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["boneShiv_LOD1"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["boneShiv_LOD2"] = new Vector3(0.0f, 0.0f, 0.0f);
Overrides["RightForeArmRoll1"] = new Vector3(3.5f, 0.0f, 0.0f);
Overrides["RightForeArmRoll2"] = new Vector3(7.0f, 0.0f, 0.0f);
Overrides["RightForeArmRoll3"] = new Vector3(10.5f, 0.0f, 0.0f);
Overrides["RightForeArmRoll4"] = new Vector3(14.0f, 0.0f, 0.0f);
Overrides["Right_sup_wrist_00"] = new Vector3(0.0f, 0.0f, 357.5f);
Overrides["Right_sup_wrist_01"] = new Vector3(0.0f, 347.6f, 0.0f);
Overrides["Right_sup_wrist_02"] = new Vector3(0.0f, 0.0f, 356.8f);
Overrides["Right_sup_wrist_03"] = new Vector3(0.0f, 347.6f, 0.0f);
Overrides["LeftArm"] = new Vector3(329.6f, 42.5f, 8.5f);
KnifeToLeft = true;
Unfreeze();
Enabled = true;
}
public static string F(float _v) => _v.ToString("0.#", CultureInfo.InvariantCulture);
public static string F(Vector3 _v) => F(_v.x) + " " + F(_v.y) + " " + F(_v.z);
}
public class HandPoseDriver : MonoBehaviour
{
void LateUpdate()
{
HandHolds.Update();
HandPose.Apply();
HeldTweak.Apply();
}
}
/// <summary>
/// necropose - подбор позы рук от первого лица:
/// on | off | clear | open (заготовка: ладонь, рука как у браслета)
/// &lt;Кость&gt; x y z - локальный поворот кости (градусы)
/// add &lt;Кость&gt; dx dy dz - добавить к текущему (заданному или от аниматора)
/// fingers Right|Left сгиб [веер] - все суставы четырёх пальцев
/// show &lt;Кость&gt; - текущий поворот кости
/// save - вся поза в лог строками кода
/// </summary>
[Preserve]
public class ConsoleCmdNecroPose : ConsoleCmdAbstract
{
public override string[] getCommands() => new[] { "necropose" };
public override string getDescription() => "NecromancerTome: поза рук от первого лица - on|off|clear|deny|pifpaf|freeze [off]|open|<кость> x y z|aim <кость> наклон от_себя разворот|unaim <кость>|add <кость> dx dy dz|fingers Right|Left сгиб [веер]|show <кость>|save";
static bool Num(string _s, out float _v) => float.TryParse(_s.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out _v);
void Out(string _s)
{
SdtdConsole.Instance.Output(_s);
Debug.Log("[NecromancerTome] necropose: " + _s);
}
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo)
{
string cmd = _params.Count > 0 ? _params[0] : "";
switch (cmd)
{
case "on":
HandPose.Enabled = true;
Out("поза включена, костей " + HandPose.Overrides.Count);
return;
case "off":
HandPose.Enabled = false;
Out("поза выключена");
return;
case "clear":
HandPose.Overrides.Clear();
HandPose.CamOverrides.Clear();
Out("поза очищена");
return;
case "deny":
HandPose.PresetDeny();
Out("хват «отрицание»");
return;
case "pifpaf":
HandPose.PresetPifPaf();
Out("хват «пиф-паф»");
return;
case "freeze":
HandPose.Freeze = _params.Count < 2 || _params[1] != "off";
HandPose.Unfreeze();
Out("заморозка остальных костей " + (HandPose.Freeze ? "включена (снимок - в покое)" : "выключена"));
return;
case "open":
HandPose.PresetOpen();
Out("заготовка «ладонь» включена, костей " + HandPose.Overrides.Count);
return;
case "save":
{
var sb = new StringBuilder();
foreach (var kv in HandPose.Overrides)
{
Out("Overrides[\"" + kv.Key + "\"] = new Vector3(" + HandPose.F(kv.Value.x) + "f, " + HandPose.F(kv.Value.y) + "f, " + HandPose.F(kv.Value.z) + "f);");
}
foreach (var kv in HandPose.CamOverrides)
{
Transform cb = HandPose.Bone(kv.Key);
if (cb != null)
{
Out("Overrides[\"" + kv.Key + "\"] = new Vector3(" + HandPose.F(cb.localEulerAngles.x) + "f, " + HandPose.F(cb.localEulerAngles.y) + "f, " + HandPose.F(cb.localEulerAngles.z) + "f); // от камеры (aim)");
}
}
Out("всего костей " + HandPose.Overrides.Count + " + от камеры " + HandPose.CamOverrides.Count + ", включена " + HandPose.Enabled);
return;
}
case "show":
{
if (_params.Count < 2)
{
break;
}
Transform b = HandPose.Bone(_params[1]);
Out(b == null ? "нет кости " + _params[1] : _params[1] + " = " + HandPose.F(b.localEulerAngles) + (HandPose.Overrides.ContainsKey(_params[1]) ? " (задана)" : " (от аниматора)"));
return;
}
case "fingers":
{
if (_params.Count >= 3 && (_params[1] == "Right" || _params[1] == "Left") && Num(_params[2], out float curl))
{
float spread = 0f;
if (_params.Count >= 4)
{
Num(_params[3], out spread);
}
HandPose.SetFingers(_params[1], curl, spread);
HandPose.Enabled = true;
Out("пальцы " + _params[1] + ": сгиб " + HandPose.F(curl) + ", веер " + HandPose.F(spread));
return;
}
break;
}
case "aim":
{
// aim <Кость> [наклон] [от_себя] [разворот] - кисть в осях камеры (см. HandPose.PalmToCamera)
if (_params.Count >= 2)
{
float tilt = 0f, back = 0f, turn = 0f;
if (_params.Count >= 3) Num(_params[2], out tilt);
if (_params.Count >= 4) Num(_params[3], out back);
if (_params.Count >= 5) Num(_params[4], out turn);
if (HandPose.Bone(_params[1]) == null)
{
Out("нет кости " + _params[1]);
return;
}
HandPose.Overrides.Remove(_params[1]);
HandPose.CamOverrides[_params[1]] = HandPose.PalmToCamera(tilt, back, turn);
HandPose.Enabled = true;
Out(_params[1] + " от камеры: наклон " + HandPose.F(tilt) + ", от себя " + HandPose.F(back) + ", разворот " + HandPose.F(turn));
return;
}
break;
}
case "unaim":
{
if (_params.Count >= 2 && HandPose.CamOverrides.Remove(_params[1]))
{
Out(_params[1] + " - снова от родителя (от аниматора или заданная)");
return;
}
break;
}
case "add":
{
if (_params.Count >= 5 && Num(_params[2], out float dx) && Num(_params[3], out float dy) && Num(_params[4], out float dz))
{
string name = _params[1];
Transform b = HandPose.Bone(name);
if (b == null)
{
Out("нет кости " + name);
return;
}
Vector3 cur = HandPose.Overrides.TryGetValue(name, out Vector3 v) ? v : b.localEulerAngles;
HandPose.Overrides[name] = cur + new Vector3(dx, dy, dz);
HandPose.Enabled = true;
Out(name + " = " + HandPose.F(HandPose.Overrides[name]));
return;
}
break;
}
default:
{
if (_params.Count >= 4 && Num(_params[1], out float x) && Num(_params[2], out float y) && Num(_params[3], out float z))
{
if (HandPose.Bone(cmd) == null)
{
Out("нет кости " + cmd);
return;
}
HandPose.CamOverrides.Remove(cmd);
HandPose.Overrides[cmd] = new Vector3(x, y, z);
HandPose.Enabled = true;
Out(cmd + " = " + HandPose.F(HandPose.Overrides[cmd]));
return;
}
break;
}
}
Out(getDescription());
}
}
}