载入中...
搜索中...
未找到
SmrFeatures.cpp
浏览该文件的文档.
2
6#include "common/Exception.h"
7
8#include <algorithm>
9#include <cctype>
10#include <cmath>
11#include <string>
12#include <utility>
13#include <vector>
14
15namespace eve::animation {
16namespace {
17
18bool partsInteract(AnimSmrBodyPart a, AnimSmrBodyPart b) {
19 if (a > b) std::swap(a, b);
20 if (a == AnimSmrBodyPart::Arm &&
22 return true;
23 if (a == AnimSmrBodyPart::Leg && b == AnimSmrBodyPart::Leg) return true;
24 if (a == AnimSmrBodyPart::Torso && b == AnimSmrBodyPart::Head) return true;
25 return false;
26}
27
28std::string normalizeToken(const std::string& name) {
29 const size_t separator = name.find_last_of(":|/");
30 const size_t begin = separator == std::string::npos ? 0 : separator + 1;
31 std::string out;
32 out.reserve(name.size() - begin);
33 for (size_t i = begin; i < name.size(); ++i) {
34 const unsigned char c = static_cast<unsigned char>(name[i]);
35 if (std::isalnum(c)) out.push_back(static_cast<char>(std::tolower(c)));
36 }
37 return out;
38}
39
40int matchJoint(const AnimSkeleton* source, const AnimSkeleton* target, int targetBone) {
41 const int exact = source->findBone(target->getBoneName(targetBone));
42 if (exact >= 0) return exact;
43 const std::string want = normalizeToken(target->getBoneName(targetBone));
44 for (int bone = 0; bone < source->getBoneCount(); ++bone) {
45 if (normalizeToken(source->getBoneName(bone)) == want) return bone;
46 }
47 return -1;
48}
49
50} // namespace
51
52void quatToRot6d(float qx, float qy, float qz, float qw, float out[6]) {
53 const float x = qx, y = qy, z = qz, w = qw;
54 const float x2 = x + x, y2 = y + y, z2 = z + z;
55 const float xx = x * x2, xy = x * y2, xz = x * z2;
56 const float yy = y * y2, yz = y * z2, zz = z * z2;
57 const float wx = w * x2, wy = w * y2, wz = w * z2;
58 out[0] = 1.f - (yy + zz);
59 out[1] = xy + wz;
60 out[2] = xz - wy;
61 out[3] = xy - wz;
62 out[4] = 1.f - (xx + zz);
63 out[5] = yz + wx;
64}
65
66void rot6dToQuat(const float in[6], float& qx, float& qy, float& qz, float& qw) {
67 float c0[3] = {in[0], in[1], in[2]};
68 float c1[3] = {in[3], in[4], in[5]};
69 auto normalize = [](float* v) {
70 const float n = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
71 if (n < 1e-8f) {
72 v[0] = 1.f;
73 v[1] = 0.f;
74 v[2] = 0.f;
75 return;
76 }
77 v[0] /= n;
78 v[1] /= n;
79 v[2] /= n;
80 };
81 normalize(c0);
82 const float dot = c0[0] * c1[0] + c0[1] * c1[1] + c0[2] * c1[2];
83 c1[0] -= dot * c0[0];
84 c1[1] -= dot * c0[1];
85 c1[2] -= dot * c0[2];
86 normalize(c1);
87 const float c2[3] = {c0[1] * c1[2] - c0[2] * c1[1], c0[2] * c1[0] - c0[0] * c1[2], c0[0] * c1[1] - c0[1] * c1[0]};
88 const float m00 = c0[0], m01 = c1[0], m02 = c2[0];
89 const float m10 = c0[1], m11 = c1[1], m12 = c2[1];
90 const float m20 = c0[2], m21 = c1[2], m22 = c2[2];
91 const float trace = m00 + m11 + m22;
92 if (trace > 0.f) {
93 const float s = 0.5f / std::sqrt(trace + 1.f);
94 qw = 0.25f / s;
95 qx = (m21 - m12) * s;
96 qy = (m02 - m20) * s;
97 qz = (m10 - m01) * s;
98 } else if (m00 > m11 && m00 > m22) {
99 const float s = 2.f * std::sqrt(1.f + m00 - m11 - m22);
100 qw = (m21 - m12) / s;
101 qx = 0.25f * s;
102 qy = (m01 + m10) / s;
103 qz = (m02 + m20) / s;
104 } else if (m11 > m22) {
105 const float s = 2.f * std::sqrt(1.f + m11 - m00 - m22);
106 qw = (m02 - m20) / s;
107 qx = (m01 + m10) / s;
108 qy = 0.25f * s;
109 qz = (m12 + m21) / s;
110 } else {
111 const float s = 2.f * std::sqrt(1.f + m22 - m00 - m11);
112 qw = (m10 - m01) / s;
113 qx = (m02 + m20) / s;
114 qy = (m12 + m21) / s;
115 qz = 0.25f * s;
116 }
117 const float n = std::sqrt(qx * qx + qy * qy + qz * qz + qw * qw);
118 if (n > 1e-8f) {
119 qx /= n;
120 qy /= n;
121 qz /= n;
122 qw /= n;
123 } else {
124 qx = qy = qz = 0.f;
125 qw = 1.f;
126 }
127}
128
129SmrFeatureBatch buildSmrFeatures(const AnimClip& sourceClip, const AnimSkeleton* sourceSkeleton,
130 const AnimSkeleton* targetSkeleton, const AnimSkin* /*sourceSkin*/,
131 const AnimSkin* /*targetSkin*/, int ringsPerBone, int pointsPerRing) {
132 if (!sourceSkeleton || !targetSkeleton) throw Exception("buildSmrFeatures: skeleton is null");
133
134 SmrFeatureBatch batch;
135 batch.joints = targetSkeleton->getBoneCount();
136 batch.jointMap.assign(static_cast<size_t>(batch.joints), -1);
137 for (int t = 0; t < batch.joints; ++t)
138 batch.jointMap[static_cast<size_t>(t)] = matchJoint(sourceSkeleton, targetSkeleton, t);
139
140 const AnimSmrSensorCloud sourceCloud =
141 AnimSmrSensorCloud::fromSkeletonDense(sourceSkeleton, ringsPerBone, pointsPerRing);
142 const AnimSmrSensorCloud targetCloud =
143 AnimSmrSensorCloud::fromSkeletonDense(targetSkeleton, ringsPerBone, pointsPerRing);
144 batch.sensors = std::min(sourceCloud.getSensorCount(), targetCloud.getSensorCount());
145 if (batch.sensors <= 0) {
146 batch.sensors = 1;
147 batch.sourceGeom.assign(7u, 0.f);
148 batch.targetGeom.assign(7u, 0.f);
149 } else {
150 batch.sourceGeom.assign(static_cast<size_t>(batch.sensors) * 7u, 0.f);
151 batch.targetGeom.assign(static_cast<size_t>(batch.sensors) * 7u, 0.f);
152 }
153
154 AnimPose sourceBind;
155 AnimPose targetBind;
156 sourceSkeleton->applyBindPose(&sourceBind);
157 targetSkeleton->applyBindPose(&targetBind);
158 sourceBind.computeWorld(sourceSkeleton);
159 targetBind.computeWorld(targetSkeleton);
160
161 if (sourceCloud.getSensorCount() > 0 && targetCloud.getSensorCount() > 0) {
162 std::vector<float> sourceRest;
163 std::vector<float> targetRest;
164 sourceCloud.evaluateWorldPositions(&sourceBind, sourceRest);
165 targetCloud.evaluateWorldPositions(&targetBind, targetRest);
166 for (int i = 0; i < batch.sensors; ++i) {
167 for (int k = 0; k < 3; ++k) {
168 batch.sourceGeom[static_cast<size_t>(i) * 7u + static_cast<size_t>(k)] =
169 sourceRest[static_cast<size_t>(i) * 3u + static_cast<size_t>(k)];
170 batch.targetGeom[static_cast<size_t>(i) * 7u + static_cast<size_t>(k)] =
171 targetRest[static_cast<size_t>(i) * 3u + static_cast<size_t>(k)];
172 }
173 batch.sourceGeom[static_cast<size_t>(i) * 7u + 3u] = static_cast<float>(sourceCloud.getSensorPart(i));
174 batch.targetGeom[static_cast<size_t>(i) * 7u + 3u] = static_cast<float>(targetCloud.getSensorPart(i));
175 batch.sourceGeom[static_cast<size_t>(i) * 7u + 4u] = static_cast<float>(sourceCloud.getSensor(i).boneIndex);
176 batch.targetGeom[static_cast<size_t>(i) * 7u + 4u] = static_cast<float>(targetCloud.getSensor(i).boneIndex);
177 }
178 }
179
180 std::vector<std::pair<int, int>> pairIdx;
181 const int sensorLimit = std::min(batch.sensors, sourceCloud.getSensorCount());
182 for (int a = 0; a < sensorLimit; ++a) {
183 for (int b = a + 1; b < sensorLimit; ++b) {
184 if (partsInteract(sourceCloud.getSensorPart(a), sourceCloud.getSensorPart(b))) pairIdx.emplace_back(a, b);
185 }
186 }
187 batch.pairs = static_cast<int>(pairIdx.size());
188
189 const float duration = sourceClip.getDuration();
190 const float rate = std::max(sourceClip.getSampleRate(), 1.f);
191 batch.frames = duration <= 0.f ? 1 : std::max(1, static_cast<int>(std::ceil(duration * rate)) + 1);
192 batch.sourceRot6d.assign(static_cast<size_t>(batch.frames) * static_cast<size_t>(batch.joints) * 6u, 0.f);
193 batch.sourceDmi.assign(static_cast<size_t>(batch.frames) * static_cast<size_t>(std::max(batch.pairs, 1)) * 10u,
194 0.f);
195
197 std::vector<float> xyz;
198 std::vector<float> prevXyz;
199 for (int frame = 0; frame < batch.frames; ++frame) {
200 const float time =
201 duration <= 0.f ? 0.f
202 : duration * static_cast<float>(frame) / static_cast<float>(std::max(batch.frames - 1, 1));
203 sourceClip.sample(time, &pose, sourceSkeleton);
204 pose.computeWorld(sourceSkeleton);
205 for (int joint = 0; joint < batch.joints; ++joint) {
206 const int sourceJoint = batch.jointMap[static_cast<size_t>(joint)];
207 float rot[6] = {1.f, 0.f, 0.f, 0.f, 1.f, 0.f};
208 if (sourceJoint >= 0) {
209 const TransformTRS& local = pose.local(sourceJoint);
210 quatToRot6d(local.qx, local.qy, local.qz, local.qw, rot);
211 }
212 const size_t base =
213 (static_cast<size_t>(frame) * static_cast<size_t>(batch.joints) + static_cast<size_t>(joint)) * 6u;
214 for (int k = 0; k < 6; ++k) batch.sourceRot6d[base + static_cast<size_t>(k)] = rot[k];
215 }
216 if (sourceCloud.getSensorCount() > 0) sourceCloud.evaluateWorldPositions(&pose, xyz);
217 for (int p = 0; p < batch.pairs; ++p) {
218 const int a = pairIdx[static_cast<size_t>(p)].first;
219 const int b = pairIdx[static_cast<size_t>(p)].second;
220 const float ax = xyz[static_cast<size_t>(a) * 3u];
221 const float ay = xyz[static_cast<size_t>(a) * 3u + 1u];
222 const float az = xyz[static_cast<size_t>(a) * 3u + 2u];
223 const float bx = xyz[static_cast<size_t>(b) * 3u];
224 const float by = xyz[static_cast<size_t>(b) * 3u + 1u];
225 const float bz = xyz[static_cast<size_t>(b) * 3u + 2u];
226 const float dx = bx - ax, dy = by - ay, dz = bz - az;
227 const float dist = std::sqrt(dx * dx + dy * dy + dz * dz);
228 float vx = 0.f, vy = 0.f, vz = 0.f;
229 if (!prevXyz.empty()) {
230 vx = (bx - prevXyz[static_cast<size_t>(b) * 3u]) - (ax - prevXyz[static_cast<size_t>(a) * 3u]);
231 vy =
232 (by - prevXyz[static_cast<size_t>(b) * 3u + 1u]) - (ay - prevXyz[static_cast<size_t>(a) * 3u + 1u]);
233 vz =
234 (bz - prevXyz[static_cast<size_t>(b) * 3u + 2u]) - (az - prevXyz[static_cast<size_t>(a) * 3u + 2u]);
235 }
236 const size_t base =
237 (static_cast<size_t>(frame) * static_cast<size_t>(batch.pairs) + static_cast<size_t>(p)) * 10u;
238 batch.sourceDmi[base + 0] = dx;
239 batch.sourceDmi[base + 1] = dy;
240 batch.sourceDmi[base + 2] = dz;
241 batch.sourceDmi[base + 3] = dist;
242 batch.sourceDmi[base + 4] = static_cast<float>(sourceCloud.getSensorPart(a));
243 batch.sourceDmi[base + 5] = static_cast<float>(sourceCloud.getSensorPart(b));
244 batch.sourceDmi[base + 6] = vx;
245 batch.sourceDmi[base + 7] = vy;
246 batch.sourceDmi[base + 8] = vz;
247 batch.sourceDmi[base + 9] = 1.f;
248 }
249 prevXyz = xyz;
250 }
251 return batch;
252}
253
254} // namespace eve::animation
LogicalId target
Trace trace
Definition Agent.cpp:49
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float duration
eve::EntitySpatialPose pose
const std::string & s
int bz
Definition CaveMesh.cpp:114
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
int az
Definition CaveMesh.cpp:113
int by
Definition CaveMesh.cpp:114
glm::vec4 p[6]
glm::vec3 n
Definition Grass.cpp:63
float v
std::int32_t second
std::int32_t c
std::int32_t first
std::string local
std::uint32_t bone
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float begin
float t
float dz
float dy
float dx
const UnitySourceAsset & source
float vz
float wz
float wx
float vy
float qy
float vx
float qx
float qw
float qz
float wy
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
Definition AnimClip.h:145
float getDuration() const
Returns the duration.
Definition AnimClip.h:163
void sample(float time, AnimPose *out, const AnimSkeleton *skeleton=nullptr) const
Sample local pose at time (seconds). If skeleton non-null, missing tracks fall back to bind pose; oth...
Definition AnimClip.cpp:575
float getSampleRate() const
Returns the sample rate.
Definition AnimClip.h:173
Evaluated local (and optional world) pose for an AnimSkeleton. Script type: AnimPose.
Definition AnimPose.h:17
void computeWorld(const AnimSkeleton *skeleton)
Compute world transforms from local pose + skeleton hierarchy. World values readable via getWorld* af...
Definition AnimPose.cpp:203
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
int getBoneCount() const
Returns the bone count.
void applyBindPose(class AnimPose *pose) const
Fill pose locals with bind pose.
CPU linear-blend skinning binding for one mesh against an AnimSkeleton.
Definition AnimSkin.h:47
Bone-attached sensor cloud for skinned-motion-retarget interaction queries. Script type: AnimSmrSenso...
Definition AnimSmr.h:53
AnimSmrBodyPart getSensorPart(int index) const
Returns the sensor part.
Definition AnimSmr.cpp:324
void evaluateWorldPositions(const AnimPose *pose, std::vector< float > &outXYZ) const
Evaluate world-space sensor positions for a pose that already has computeWorld() applied.
Definition AnimSmr.cpp:326
int getSensorCount() const
Returns the sensor count.
Definition AnimSmr.h:72
static AnimSmrSensorCloud fromSkeletonDense(const AnimSkeleton *skeleton, int ringsPerBone, int pointsPerRing)
Build denser MeshRet-style SCS rings around each bone segment.
Definition AnimSmr.cpp:228
const AnimSmrSensor & getSensor(int index) const
Returns the sensor.
Definition AnimSmr.cpp:319
AnimSmrBodyPart
Coarse body-part tag used to select MeshRet-style interaction pairs.
Definition AnimSmr.h:18
SmrFeatureBatch buildSmrFeatures(const AnimClip &sourceClip, const AnimSkeleton *sourceSkeleton, const AnimSkeleton *targetSkeleton, const AnimSkin *, const AnimSkin *, int ringsPerBone, int pointsPerRing)
Build dense SCS + DMI tensors from skeletons/clip (no tensor headers).
void rot6dToQuat(const float in[6], float &qx, float &qy, float &qz, float &qw)
Convert rotation-6D back to a unit quaternion.
void quatToRot6d(float qx, float qy, float qz, float qw, float out[6])
Convert unit quaternion to MeshRet-style rotation-6D.
WidgetDesc separator(std::string id)
Horizontal separator line.
Definition Widget.cpp:345
Packed MeshRet-style features for one retarget inference window.
Definition SmrFeatures.h:15
std::vector< float > targetGeom
[S*7]
Definition SmrFeatures.h:23
std::vector< float > sourceRot6d
[T*J*6]
Definition SmrFeatures.h:21
std::vector< float > sourceDmi
[T*P*10]
Definition SmrFeatures.h:24
std::vector< int > jointMap
target joint -> source joint (-1 if unmatched)
Definition SmrFeatures.h:25
std::vector< float > sourceGeom
[S*7]
Definition SmrFeatures.h:22
Local TRS used by skeletal animation (quaternion xyzw).
Definition AnimMath.h:9