载入中...
搜索中...
未找到
MotionDatabaseLocomotion.cpp
浏览该文件的文档.
1#include <array>
2#include <cmath>
9
10namespace eve::animation {
11eve::Result<void> MotionDatabase::setLocomotionFeatures(int leftFoot, int rightFoot, int pelvis) {
12 const int count = skeleton_->getBoneCount();
13 if (baked_ || leftFoot < 0 || rightFoot < 0 || pelvis < 0 || leftFoot >= count || rightFoot >= count ||
14 pelvis >= count || leftFoot == rightFoot || leftFoot == pelvis || rightFoot == pelvis)
16 eve::DiagnosticCode::InvalidArgument, "locomotion features require three distinct valid bones before bake",
17 "featureBones", {}, "animation"));
18 std::vector<int> bones{leftFoot, rightFoot, pelvis};
19 schema_.reset();
20 featureBones_.swap(bones);
21 locomotionFeatures_ = true;
22 computeFeatureSize();
24}
25
26void MotionDatabase::extractLocomotionFeature(AnimClip* clip, float time, std::vector<float>& out, float& rootX,
27 float& rootZ, float& rootYaw, float& velX, float& velZ) const {
28 out.assign(detail::locomotionFeatureCount, 0.f);
29 const float duration = clip->getDuration();
30 const float delta = 1.f / 60.f;
31 auto world = [&](auto&& self, int bone, float t) -> TransformTRS {
32 const auto local = clip->sampleBone(bone, t, skeleton_->bindLocal(bone));
33 const int parent = skeleton_->getParent(bone);
34 return parent < 0 ? local : detail::mulTRS(self(self, parent, t), local);
35 };
36 auto poseAt = [&](int bone, float t) { return world(world, bone, clampf(clip->wrapTime(t), 0.f, duration)); };
37 // Accumulate complete root transforms at loop seams; turning cycles need
38 // rotation composition, not repeated addition of an unrotated displacement.
39 const auto start = world(world, rootBone_, 0.f), end = world(world, rootBone_, duration);
40 const float startYaw = detail::featureYaw(start);
41 const float cycleYaw = std::remainder(detail::featureYaw(end) - startYaw, 6.28318530718f);
42 auto rootAt = [&](float t) {
43 auto root = poseAt(rootBone_, t);
44 if (clip->getLoop() && duration > 1e-6f) {
45 const int cycles = static_cast<int>(std::floor(t / duration));
46 float x = root.px - start.px, z = root.pz - start.pz;
47 const float dx = end.px - start.px, dz = end.pz - start.pz;
48 const float cs = std::cos(cycleYaw), sn = std::sin(cycleYaw);
49 for (int i = 0; i < std::abs(cycles); ++i) {
50 if (cycles > 0) {
51 const float nextX = cs * x + sn * z + dx;
52 z = -sn * x + cs * z + dz;
53 x = nextX;
54 } else {
55 x -= dx;
56 z -= dz;
57 const float nextX = cs * x - sn * z;
58 z = sn * x + cs * z;
59 x = nextX;
60 }
61 }
62 root.px = start.px + x;
63 root.pz = start.pz + z;
64 root.py += cycles * (end.py - start.py);
65 const float angle = detail::featureYaw(root) + cycles * cycleYaw;
66 root.qx = root.qz = 0.f;
67 root.qy = std::sin(angle * .5f);
68 root.qw = std::cos(angle * .5f);
69 } else if (duration > 1e-6f && (t < 0.f || t > duration)) {
70 // Root trajectory extrapolation preserves velocity outside short
71 // starts/stops, instead of inventing a stationary future endpoint.
72 const float dt = std::min(delta, duration);
73 const auto a = world(world, rootBone_, t < 0.f ? 0.f : duration - dt);
74 const auto b = world(world, rootBone_, t < 0.f ? dt : duration);
75 const float extension = (t < 0.f ? t : t - duration) / dt;
76 root.px += (b.px - a.px) * extension;
77 root.py += (b.py - a.py) * extension;
78 root.pz += (b.pz - a.pz) * extension;
79 const float angle =
81 std::remainder(detail::featureYaw(b) - detail::featureYaw(a), 6.28318530718f) * extension;
82 root.qx = root.qz = 0.f;
83 root.qy = std::sin(angle * .5f);
84 root.qw = std::cos(angle * .5f);
85 }
86 return root;
87 };
88 struct Sample {
89 float x, y, z, vx, vy, vz, yaw;
90 };
91 std::array<Sample, 5> samples{};
92 const auto current = rootAt(time);
93 rootX = current.px;
94 rootZ = current.pz;
95 rootYaw = detail::featureYaw(current);
96 for (int i = 0; i < 5; ++i) {
97 const float t = time + detail::locomotionHorizons[i];
98 const auto r = rootAt(t), previous = rootAt(t - delta);
99 samples[i] = {r.px - current.px,
100 r.py - current.py,
101 r.pz - current.pz,
102 (r.px - previous.px) / delta,
103 (r.py - previous.py) / delta,
104 (r.pz - previous.pz) / delta,
106 }
107 velX = samples[1].vx;
108 velZ = samples[1].vz;
109 detail::encodeLocomotionTrajectory(std::span(out), samples, rootYaw);
110 std::array<TransformTRS, 4> now{}, past{};
111 now[0] = poseAt(rootBone_, time);
112 past[0] = poseAt(rootBone_, time - delta);
113 for (int i = 0; i < 3; ++i) {
114 now[i + 1] = poseAt(featureBones_[i], time);
115 past[i + 1] = poseAt(featureBones_[i], time - delta);
116 }
117 detail::encodeLocomotionPose(out, now, past, delta);
118}
119
120void MotionDatabase::normalizeLocomotionFeatures() {
121 featureMean_.assign(detail::locomotionFeatureCount, 0.f);
122 featureInvStd_.assign(detail::locomotionFeatureCount, 1.f);
123 // Separate channels use vector mean deviation (isotropic XZ/XYZ); the two
124 // feet share a centroid and scale, as the authored FeetVelZ group specifies.
125 const std::array<std::array<int, 3>, 12> channels{{{0, 2, 1},
126 {2, 2, 1},
127 {4, 2, 1},
128 {6, 2, 1},
129 {8, 2, 1},
130 {10, 2, 1},
131 {12, 2, 1},
132 {14, 2, 1},
133 {16, 3, 1},
134 {19, 3, 1},
135 {22, 3, 2},
136 {28, 2, 1}}};
137 for (const auto& channel : channels) {
138 const int base = channel[0], dimensions = channel[1], copies = channel[2];
139 std::array<double, 3> mean{};
140 const double count = static_cast<double>(frames_.size()) * copies;
141 for (const auto& frame : frames_)
142 for (int copy = 0; copy < copies; ++copy)
143 for (int axis = 0; axis < dimensions; ++axis)
144 mean[axis] += frame.feature[base + copy * dimensions + axis];
145 for (double& value : mean) value /= count;
146 double deviation = 0;
147 for (const auto& frame : frames_)
148 for (int copy = 0; copy < copies; ++copy) {
149 double square = 0;
150 for (int axis = 0; axis < dimensions; ++axis) {
151 const double d = frame.feature[base + copy * dimensions + axis] - mean[axis];
152 square += d * d;
153 }
154 deviation += std::sqrt(square);
155 }
156 deviation /= count;
157 const bool unitVector = base == 4 || base == 8 || base == 14 || base == 16 || base == 28;
158 const double minimum = unitVector ? 0.1 : 0.001; // UE 0.1 cm, converted to metres.
159 const float inverse = static_cast<float>(deviation > minimum ? 1.0 / deviation : (unitVector ? 1.0 : 100.0));
160 for (int copy = 0; copy < copies; ++copy)
161 for (int axis = 0; axis < dimensions; ++axis) {
162 const int i = base + copy * dimensions + axis;
163 featureMean_[i] = static_cast<float>(mean[axis]);
164 featureInvStd_[i] = inverse;
165 }
166 }
167 for (auto& frame : frames_) normalizeFeature(frame.feature);
168}
169} // namespace eve::animation
double value
Duration start
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float duration
std::map< std::string, std::vector< Key >, std::less<> > channels
int root
Definition AnimSmr.cpp:119
float minimum[3]
glm::vec4 clip
double r
std::string local
std::vector< Bone > bones
std::uint32_t bone
std::int32_t parent
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
graphics::Canvas * previous
World3D * world
float d
float t
double current
float dz
float dx
std::uint32_t count
float vz
float vy
float vx
float angle
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
Definition AnimClip.h:145
int getBoneCount() const
Returns the bone count.
int getParent(int boneIndex) const
Returns the parent.
const TransformTRS & bindLocal(int boneIndex) const
Binds local.
void normalizeFeature(std::vector< float > &feature) const
Normalize a query with statistics computed by bake().
eve::Result< void > setLocomotionFeatures(int leftFoot, int rightFoot, int pelvis)
Configure the locomotion layout before baking or creating matchers.
constexpr std::array< float, 5 > locomotionHorizons
TransformTRS mulTRS(const TransformTRS &parent, const TransformTRS &local)
Mul trs.
void encodeLocomotionPose(std::span< float > out, const std::array< TransformTRS, 4 > &now, const std::array< TransformTRS, 4 > &past, float interval)
Encode locomotion pose.
float featureYaw(const TransformTRS &t)
Feature yaw.
void encodeLocomotionTrajectory(std::span< float > out, const std::array< Sample, 5 > &samples, float yaw)
Encode locomotion trajectory.
float clampf(float v, float lo, float hi)
Clampf.
Definition AnimMath.h:34
std::string extension(std::string_view name)
Extension.
int axis(int64_t a, size_t rank)
Axis.
Local TRS used by skeletal animation (quaternion xyzw).
Definition AnimMath.h:9