载入中...
搜索中...
未找到
VrmMotion.cpp
浏览该文件的文档.
1#include <algorithm>
2#include <cmath>
3#include <glm/gtc/matrix_transform.hpp>
4#include <glm/gtc/quaternion.hpp>
5#include <glm/gtx/quaternion.hpp>
9#include "avatar/VrmRuntime.h"
10
11namespace eve::avatar {
12namespace {
13glm::vec3 vec(const std::array<float, 3>& v) { return {v[0], v[1], v[2]}; }
14glm::mat4 matrix(const animation::AnimPose& pose, int bone) {
15 glm::mat4 m(1);
16 pose.getWorldMatrix(bone, &m[0][0]);
17 return m;
18}
19glm::quat quaternion(const animation::TransformTRS& t) { return {t.qw, t.qx, t.qy, t.qz}; }
20glm::vec3 unit(glm::vec3 v, glm::vec3 fallback = {0, 1, 0}) {
21 float n = glm::length(v);
22 return n > 1e-7f ? v / n : fallback;
23}
24void rotate(animation::AnimPose& pose, int bone, glm::quat q) {
25 q = glm::normalize(q);
26 pose.setLocalRotation(bone, q.x, q.y, q.z, q.w);
27}
28float mapped(float value, const VrmLookAt::Range& r) {
29 return std::min(std::abs(value) / std::max(r.input, .0001f), 1.f) * r.output;
30}
31} // namespace
32void VrmRuntime::gaze(AvatarInstance& avatar, animation::AnimPose& pose, const glm::mat4& modelWorld, bool enabled,
33 glm::vec3 target, float weight) {
34 for (const auto& [bone, angles] : rotations) {
35 glm::quat delta = glm::angleAxis(angles[0], glm::vec3(0, 1, 0)) *
36 glm::angleAxis(angles[1], glm::vec3(1, 0, 0)) * glm::angleAxis(angles[2], glm::vec3(0, 0, 1));
37 rotate(pose, bone, quaternion(pose.local(bone)) * delta);
38 }
39 pose.computeWorld(skeleton.get());
40 gazeWeights_.clear();
41 if (!enabled || weight <= 0 || !document.look.present || !document.humanoid.contains("head")) return;
43 float suppression = 0;
44 for (const auto& expression : document.expressions) {
45 float value = std::clamp(avatar.getParameter(expression.name), 0.f, 1.f);
46 if (expression.binary) value = value > .5f ? 1.f : 0.f;
47 if (expression.overrideLookAt == "block" && value > 0)
48 suppression = 1;
49 else if (expression.overrideLookAt == "blend")
50 suppression += value;
51 }
52 weight *= 1 - std::clamp(suppression, 0.f, 1.f);
53 }
54 const int head = nodeBones[document.humanoid.at("head")];
55 animation::AnimPose bind(skeleton->getBoneCount());
56 skeleton->applyBindPose(&bind);
57 bind.computeWorld(skeleton.get());
58 const auto& h = pose.world(head);
59 const glm::quat rest = quaternion(bind.world(head));
60 const glm::quat orientation = quaternion(h) * glm::inverse(rest);
61 glm::vec3 origin = glm::vec3(matrix(pose, head) * glm::vec4(vec(document.look.offset), 1));
62 glm::vec3 local = glm::inverse(orientation) * (glm::vec3(glm::inverse(modelWorld) * glm::vec4(target, 1)) - origin);
63 const float yaw = glm::degrees(std::atan2(local.x, local.z));
64 const float pitch = glm::degrees(std::atan2(-local.y, std::hypot(local.x, local.z)));
65 const auto& look = document.look;
66 const float vertical = mapped(pitch, pitch >= 0 ? look.down : look.up) * weight;
67 if (look.expression) {
68 gazeWeights_[yaw >= 0 ? "lookLeft" : "lookRight"] = mapped(yaw, look.outer) * weight;
69 gazeWeights_[pitch >= 0 ? "lookDown" : "lookUp"] = vertical;
70 return;
71 }
72 for (auto semantic : {"leftEye", "rightEye"}) {
73 auto it = document.humanoid.find(semantic);
74 if (it == document.humanoid.end()) continue;
75 const int bone = nodeBones[it->second];
76 const bool outer = (yaw >= 0) == (std::string_view(semantic) == "leftEye");
77 const float horizontal = mapped(yaw, outer ? look.outer : look.inner) * weight;
78 glm::quat delta = glm::angleAxis(glm::radians(std::copysign(horizontal, yaw)), glm::vec3(0, 1, 0)) *
79 glm::angleAxis(glm::radians(std::copysign(vertical, pitch)), glm::vec3(1, 0, 0));
80 // Convert the model-axis gaze into the authored eye's local rest axes.
81 const glm::quat eyeRest = quaternion(bind.world(bone));
82 const auto current = quaternion(pose.local(bone));
83 rotate(pose, bone, current * glm::inverse(eyeRest) * delta * eyeRest);
84 }
85 pose.computeWorld(skeleton.get());
86}
87void VrmRuntime::springs(animation::AnimPose& pose, float dt) {
88 if (!std::isfinite(dt) || dt <= 0) return;
89 // A long pause/teleport restarts particles; a bounded dt avoids explosive catch-up.
90 const bool reset = dt > .25f;
91 dt = std::min(dt, .05f);
92 for (size_t s = 0; s < document.springs.size(); ++s) {
93 const auto& spring = document.springs[s];
94 glm::mat4 center = spring.center >= 0 ? world * matrix(pose, nodeBones[spring.center]) : glm::mat4(1);
95 const glm::mat4 inverseCenter = glm::inverse(center);
96 for (size_t j = 0; j + 1 < spring.joints.size(); ++j) {
97 const auto& joint = spring.joints[j];
98 const int bone = nodeBones[joint.node], tailBone = nodeBones[spring.joints[j + 1].node];
99 const glm::mat4 boneWorld = world * matrix(pose, bone);
100 const glm::vec3 origin = glm::vec3(boneWorld[3]);
101 const glm::vec3 restTail = glm::vec3(world * matrix(pose, tailBone)[3]);
102 const glm::vec3 axis = unit(restTail - origin);
103 const float length = glm::length(restTail - origin);
104 if (length < 1e-7f) continue;
105 auto& particle = particles_[s][j];
106 if (!particle.initialized || reset) {
107 particle.current = particle.previous = glm::vec3(inverseCenter * glm::vec4(restTail, 1));
108 particle.initialized = true;
109 }
110 const glm::vec3 current = glm::vec3(center * glm::vec4(particle.current, 1));
111 const glm::vec3 previous = glm::vec3(center * glm::vec4(particle.previous, 1));
112 glm::vec3 next = current + (current - previous) * (1 - joint.drag) + axis * (dt * joint.stiffness) +
113 vec(joint.gravity) * (dt * joint.gravityPower);
114 next = origin + unit(next - origin, axis) * length;
115 for (int ci : spring.colliders) {
116 const auto& c = document.colliders[ci];
117 const glm::mat4 cm = world * matrix(pose, nodeBones[c.node]);
118 glm::vec3 closest = glm::vec3(cm * glm::vec4(vec(c.offset), 1));
119 if (c.capsule) {
120 glm::vec3 segment = glm::vec3(cm * glm::vec4(vec(c.tail), 1)) - closest;
121 float sq = glm::dot(segment, segment);
122 if (sq > 1e-12f) closest += segment * std::clamp(glm::dot(next - closest, segment) / sq, 0.f, 1.f);
123 }
124 float radius = c.radius + joint.radius;
125 glm::vec3 delta = next - closest;
126 if (glm::dot(delta, delta) < radius * radius) {
127 next = closest + unit(delta, axis) * radius;
128 next = origin + unit(next - origin, axis) * length;
129 }
130 }
131 particle.previous = particle.current;
132 particle.current = glm::vec3(inverseCenter * glm::vec4(next, 1));
133 const glm::vec3 localAxis = unit(glm::vec3(glm::inverse(boneWorld) * glm::vec4(restTail, 1)));
134 const glm::vec3 localTarget = unit(glm::vec3(glm::inverse(boneWorld) * glm::vec4(next, 1)), localAxis);
135 rotate(pose, bone, quaternion(pose.local(bone)) * glm::rotation(localAxis, localTarget));
136 pose.computeWorld(skeleton.get());
137 }
138 }
139}
140} // namespace eve::avatar
LogicalId target
double value
eve::EntitySpatialPose pose
const std::string & s
float length
Definition CaveMesh.cpp:94
glm::vec3 n
Definition Grass.cpp:63
std::array< double, 10 > q
double r
float v
std::int32_t c
int h
std::string local
std::uint32_t bone
graphics::Canvas * previous
float radius
float t
V3 origin
Definition RoadBake.cpp:138
double current
TacticalUnit * unit
float m[16]
uint32_t semantic
Definition WfcSimple.cpp:15
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:17
const TransformTRS & world(int boneIndex) const
World.
Definition AnimPose.cpp:367
void computeWorld(const AnimSkeleton *skeleton)
Compute world transforms from local pose + skeleton hierarchy. World values readable via getWorld* af...
Definition AnimPose.cpp:203
Unified avatar instance. Kind is a string: "image" | "live2d" | "vroid". Script-facing API avoids ove...
float getParameter(const std::string &name) const
Returns the parameter.
std::vector< int > nodeBones
Definition VrmRuntime.h:52
std::unique_ptr< animation::AnimSkeleton > skeleton
Definition VrmRuntime.h:50
void gaze(AvatarInstance &avatar, animation::AnimPose &pose, const glm::mat4 &world, bool enabled, glm::vec3 target, float weight)
Gaze.
Definition VrmMotion.cpp:32
std::map< int, std::array< float, 3 > > rotations
Definition VrmRuntime.h:53
bool enabled
std::vector< VrmSpring > springs
Definition VrmDocument.h:97
std::map< std::string, int > humanoid
Definition VrmDocument.h:93
std::vector< VrmCollider > colliders
Definition VrmDocument.h:96
std::vector< VrmExpression > expressions
Definition VrmDocument.h:94
std::array< float, 3 > offset
Definition VrmDocument.h:64