269 lines
14 KiB
C#
269 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using UniGLTF;
|
|
using UniJSON;
|
|
using UnityEngine;
|
|
using VRMShaders;
|
|
|
|
namespace UniVRM10
|
|
{
|
|
//
|
|
// extensions.VRMC_vrm_animation.extras.UNIVRM_pose
|
|
//
|
|
// no jsonscheme.
|
|
//
|
|
// extensions: {
|
|
// VRMC_vrm_animation: {
|
|
// humanoid : {
|
|
// humanBones: {}
|
|
// },
|
|
// extras: {
|
|
// UNIVRM_pose: {
|
|
// humanoid: {
|
|
// translation: [
|
|
// 0,
|
|
// 1,
|
|
// 0
|
|
// ],
|
|
// rotations: {
|
|
// hips: [
|
|
// 0,
|
|
// 0.707,
|
|
// 0,
|
|
// 0.707
|
|
// ],
|
|
// spine: [
|
|
// 0,
|
|
// 0.707,
|
|
// 0,
|
|
// 0.707
|
|
// ],
|
|
// // ...
|
|
// }
|
|
// },
|
|
// expressions: {
|
|
// preset: {
|
|
// happy: 1.0,
|
|
// },
|
|
// },
|
|
// lookAt: {
|
|
// position: [
|
|
// 4,
|
|
// 5,
|
|
// 6
|
|
// ],
|
|
// // yawPitchDegrees: [20, 30],
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
public static class Vrm10PoseLoader
|
|
{
|
|
static Vector3 ToVec3(JsonNode j)
|
|
{
|
|
return new Vector3(-j[0].GetSingle(), j[1].GetSingle(), j[2].GetSingle());
|
|
}
|
|
|
|
static Quaternion ToQuat(JsonNode j)
|
|
{
|
|
return new Quaternion(j[0].GetSingle(), -j[1].GetSingle(), -j[2].GetSingle(), j[3].GetSingle());
|
|
}
|
|
|
|
static (Vector3, Dictionary<HumanBodyBones, Quaternion>) GetPose(JsonNode humanoid)
|
|
{
|
|
Vector3 root = default;
|
|
var map = new Dictionary<HumanBodyBones, Quaternion>();
|
|
|
|
if (humanoid.TryGet("translation", out var translation))
|
|
{
|
|
root = ToVec3(translation);
|
|
}
|
|
if (humanoid.TryGet("rotations", out var rotations))
|
|
{
|
|
foreach (var kv in rotations.ObjectItems())
|
|
{
|
|
switch (kv.Key.GetString())
|
|
{
|
|
case "hips": map.Add(HumanBodyBones.Hips, ToQuat(kv.Value)); break;
|
|
case "spine": map.Add(HumanBodyBones.Spine, ToQuat(kv.Value)); break;
|
|
case "chest": map.Add(HumanBodyBones.Chest, ToQuat(kv.Value)); break;
|
|
case "upperChest": map.Add(HumanBodyBones.UpperChest, ToQuat(kv.Value)); break;
|
|
case "neck": map.Add(HumanBodyBones.Neck, ToQuat(kv.Value)); break;
|
|
case "head": map.Add(HumanBodyBones.Head, ToQuat(kv.Value)); break;
|
|
// case "leftEye": map.Add(HumanBodyBones.leftEye, ToQuat(kv.Value)); break;
|
|
// case "rightEye": map.Add(HumanBodyBones.rightEye, ToQuat(kv.Value)); break;
|
|
case "jaw": map.Add(HumanBodyBones.Jaw, ToQuat(kv.Value)); break;
|
|
case "leftShoulder": map.Add(HumanBodyBones.LeftShoulder, ToQuat(kv.Value)); break;
|
|
case "leftUpperArm": map.Add(HumanBodyBones.LeftUpperArm, ToQuat(kv.Value)); break;
|
|
case "leftLowerArm": map.Add(HumanBodyBones.LeftLowerArm, ToQuat(kv.Value)); break;
|
|
case "leftHand": map.Add(HumanBodyBones.LeftHand, ToQuat(kv.Value)); break;
|
|
case "rightShoulder": map.Add(HumanBodyBones.RightShoulder, ToQuat(kv.Value)); break;
|
|
case "rightUpperArm": map.Add(HumanBodyBones.RightUpperArm, ToQuat(kv.Value)); break;
|
|
case "rightLowerArm": map.Add(HumanBodyBones.RightLowerArm, ToQuat(kv.Value)); break;
|
|
case "rightHand": map.Add(HumanBodyBones.RightHand, ToQuat(kv.Value)); break;
|
|
case "leftUpperLeg": map.Add(HumanBodyBones.LeftUpperLeg, ToQuat(kv.Value)); break;
|
|
case "leftLowerLeg": map.Add(HumanBodyBones.LeftLowerLeg, ToQuat(kv.Value)); break;
|
|
case "leftFoot": map.Add(HumanBodyBones.LeftFoot, ToQuat(kv.Value)); break;
|
|
case "leftToes": map.Add(HumanBodyBones.LeftToes, ToQuat(kv.Value)); break;
|
|
case "rightUpperLeg": map.Add(HumanBodyBones.RightUpperLeg, ToQuat(kv.Value)); break;
|
|
case "rightLowerLeg": map.Add(HumanBodyBones.RightLowerLeg, ToQuat(kv.Value)); break;
|
|
case "rightFoot": map.Add(HumanBodyBones.RightFoot, ToQuat(kv.Value)); break;
|
|
case "rightToes": map.Add(HumanBodyBones.RightToes, ToQuat(kv.Value)); break;
|
|
case "leftThumbMetacarpal": map.Add(HumanBodyBones.LeftThumbProximal, ToQuat(kv.Value)); break;
|
|
case "leftThumbProximal": map.Add(HumanBodyBones.LeftThumbIntermediate, ToQuat(kv.Value)); break;
|
|
case "leftThumbDistal": map.Add(HumanBodyBones.LeftThumbDistal, ToQuat(kv.Value)); break;
|
|
case "leftIndexProximal": map.Add(HumanBodyBones.LeftIndexProximal, ToQuat(kv.Value)); break;
|
|
case "leftIndexIntermediate": map.Add(HumanBodyBones.LeftIndexIntermediate, ToQuat(kv.Value)); break;
|
|
case "leftIndexDistal": map.Add(HumanBodyBones.LeftIndexDistal, ToQuat(kv.Value)); break;
|
|
case "leftMiddleProximal": map.Add(HumanBodyBones.LeftMiddleProximal, ToQuat(kv.Value)); break;
|
|
case "leftMiddleIntermediate": map.Add(HumanBodyBones.LeftMiddleIntermediate, ToQuat(kv.Value)); break;
|
|
case "leftMiddleDistal": map.Add(HumanBodyBones.LeftMiddleDistal, ToQuat(kv.Value)); break;
|
|
case "leftRingProximal": map.Add(HumanBodyBones.LeftRingProximal, ToQuat(kv.Value)); break;
|
|
case "leftRingIntermediate": map.Add(HumanBodyBones.LeftRingIntermediate, ToQuat(kv.Value)); break;
|
|
case "leftRingDistal": map.Add(HumanBodyBones.LeftRingDistal, ToQuat(kv.Value)); break;
|
|
case "leftLittleProximal": map.Add(HumanBodyBones.LeftLittleProximal, ToQuat(kv.Value)); break;
|
|
case "leftLittleIntermediate": map.Add(HumanBodyBones.LeftLittleIntermediate, ToQuat(kv.Value)); break;
|
|
case "leftLittleDistal": map.Add(HumanBodyBones.LeftLittleDistal, ToQuat(kv.Value)); break;
|
|
case "rightThumbMetacarpal": map.Add(HumanBodyBones.RightThumbProximal, ToQuat(kv.Value)); break;
|
|
case "rightThumbProximal": map.Add(HumanBodyBones.RightThumbIntermediate, ToQuat(kv.Value)); break;
|
|
case "rightThumbDistal": map.Add(HumanBodyBones.RightThumbDistal, ToQuat(kv.Value)); break;
|
|
case "rightIndexProximal": map.Add(HumanBodyBones.RightIndexProximal, ToQuat(kv.Value)); break;
|
|
case "rightIndexIntermediate": map.Add(HumanBodyBones.RightIndexIntermediate, ToQuat(kv.Value)); break;
|
|
case "rightIndexDistal": map.Add(HumanBodyBones.RightIndexDistal, ToQuat(kv.Value)); break;
|
|
case "rightMiddleProximal": map.Add(HumanBodyBones.RightMiddleProximal, ToQuat(kv.Value)); break;
|
|
case "rightMiddleIntermediate": map.Add(HumanBodyBones.RightMiddleIntermediate, ToQuat(kv.Value)); break;
|
|
case "rightMiddleDistal": map.Add(HumanBodyBones.RightMiddleDistal, ToQuat(kv.Value)); break;
|
|
case "rightRingProximal": map.Add(HumanBodyBones.RightRingProximal, ToQuat(kv.Value)); break;
|
|
case "rightRingIntermediate": map.Add(HumanBodyBones.RightRingIntermediate, ToQuat(kv.Value)); break;
|
|
case "rightRingDistal": map.Add(HumanBodyBones.RightRingDistal, ToQuat(kv.Value)); break;
|
|
case "rightLittleProximal": map.Add(HumanBodyBones.RightLittleProximal, ToQuat(kv.Value)); break;
|
|
case "rightLittleIntermediate": map.Add(HumanBodyBones.RightLittleIntermediate, ToQuat(kv.Value)); break;
|
|
case "rightLittleDistal": map.Add(HumanBodyBones.RightLittleDistal, ToQuat(kv.Value)); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (root, map);
|
|
}
|
|
|
|
static void LoadHumanPose(Vrm10AnimationInstance instance,
|
|
UniJSON.JsonNode humanoid)
|
|
{
|
|
var (hips, map) = GetPose(humanoid);
|
|
|
|
var animator = instance.GetComponent<Animator>();
|
|
|
|
// update src ControlRig
|
|
animator.GetBoneTransform(HumanBodyBones.Hips).localPosition = hips;
|
|
|
|
foreach (var kv in map)
|
|
{
|
|
var t = animator.GetBoneTransform(kv.Key);
|
|
if (t != null)
|
|
{
|
|
t.localRotation = kv.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LoadExpressions(Vrm10AnimationInstance instance,
|
|
UniJSON.JsonNode expressions)
|
|
{
|
|
if (expressions.TryGet("preset", out var preset))
|
|
{
|
|
foreach (var kv in preset.ObjectItems())
|
|
{
|
|
switch (kv.Key.GetString())
|
|
{
|
|
case "happy": instance.preset_happy = kv.Value.GetSingle(); break;
|
|
case "angry": instance.preset_angry = kv.Value.GetSingle(); break;
|
|
case "sad": instance.preset_sad = kv.Value.GetSingle(); break;
|
|
case "relaxed": instance.preset_relaxed = kv.Value.GetSingle(); break;
|
|
case "surprised": instance.preset_surprised = kv.Value.GetSingle(); break;
|
|
case "aa": instance.preset_aa = kv.Value.GetSingle(); break;
|
|
case "ih": instance.preset_ih = kv.Value.GetSingle(); break;
|
|
case "ou": instance.preset_ou = kv.Value.GetSingle(); break;
|
|
case "ee": instance.preset_ee = kv.Value.GetSingle(); break;
|
|
case "oh": instance.preset_oh = kv.Value.GetSingle(); break;
|
|
case "blink": instance.preset_blink = kv.Value.GetSingle(); break;
|
|
case "blinkLeft": instance.preset_blinkleft = kv.Value.GetSingle(); break;
|
|
case "blinkRight": instance.preset_blinkright = kv.Value.GetSingle(); break;
|
|
// case "lookUp": instance.preset_lookUp = kv.Value.GetSingle(); break;
|
|
// case "lookDown": instance.preset_lookDown = kv.Value.GetSingle(); break;
|
|
// case "lookLeft": instance.preset_lookLeft = kv.Value.GetSingle(); break;
|
|
// case "lookRight": instance.preset_lookRight = kv.Value.GetSingle(); break;
|
|
case "neutral": instance.preset_neutral = kv.Value.GetSingle(); break;
|
|
}
|
|
}
|
|
}
|
|
if (expressions.TryGet("custom", out var custom))
|
|
{
|
|
foreach (var kv in preset.ObjectItems())
|
|
{
|
|
if (instance.ExpressionSetterMap.TryGetValue(ExpressionKey.CreateCustom(kv.Key.GetString()), out var setter))
|
|
{
|
|
setter(kv.Key.GetSingle());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LoadLookAt(Vrm10AnimationInstance instance,
|
|
UniJSON.JsonNode lookAt)
|
|
{
|
|
if (lookAt.TryGet("position", out var position))
|
|
{
|
|
// 注視点. 座標系?
|
|
}
|
|
}
|
|
|
|
public static async Task<Vrm10AnimationInstance> LoadVrmAnimationPose(string text)
|
|
{
|
|
using GltfData data = GlbLowLevelParser.ParseGltf(
|
|
"tmp.vrma",
|
|
text,
|
|
new List<GlbChunk>(), // .gltf file has no chunks.
|
|
new FileSystemStorage("_dummy_root_"), // .gltf file has resource path at file system.
|
|
new MigrationFlags()
|
|
);
|
|
using var loader = new VrmAnimationImporter(data);
|
|
var gltfInstance = await loader.LoadAsync(new ImmediateCaller());
|
|
var instance = gltfInstance.GetComponent<Vrm10AnimationInstance>();
|
|
|
|
if (data.GLTF.extensions is UniGLTF.glTFExtensionImport extensions)
|
|
{
|
|
foreach (var kv in extensions.ObjectItems())
|
|
{
|
|
if (kv.Key.GetString() == "VRMC_vrm_animation")
|
|
{
|
|
if (kv.Value.TryGet("extras", out var extras))
|
|
{
|
|
if (extras.TryGet("UNIVRM_pose", out var pose))
|
|
{
|
|
if (pose.TryGet("humanoid", out var humanoid))
|
|
{
|
|
LoadHumanPose(instance, humanoid);
|
|
}
|
|
|
|
if (extras.TryGet("expressions", out var expressions))
|
|
{
|
|
LoadExpressions(instance, expressions);
|
|
}
|
|
|
|
if (extras.TryGet("lookAt", out var lookAt))
|
|
{
|
|
LoadLookAt(instance, lookAt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
}
|
|
}
|