载入中...
搜索中...
未找到
MotionMatcherSchema.cpp
浏览该文件的文档.
1#include <algorithm>
6#include "common/Exception.h"
7
8namespace eve::animation {
10 float dt, std::span<const MotionFeatureTrajectorySample> samples,
11 std::span<const MotionFeatureCurveSample> curves) {
12 auto fail = [](const char* message) {
14 eve::DiagnosticCode::InvalidArgument, message, "featureQuery", {}, "animation"));
15 };
16 if (!database_->schema_ || !database_->isBaked() || !std::isfinite(dt) || dt <= 0.f || dt > 1.f ||
17 current.getBoneCount() != skeleton_->getBoneCount() || previous.getBoneCount() != skeleton_->getBoneCount() || samples.size() > 256 || curves.size() > 256)
18 return fail("feature query requires a baked variable layout, matching poses and finite dt in (0,1]");
19 for (const auto* pose : {&current, &previous}) for (int i = 0; i < pose->getBoneCount(); ++i) {
20 const auto& t = pose->local(i);
21 for (float v : {t.px,t.py,t.pz,t.qx,t.qy,t.qz,t.qw,t.sx,t.sy,t.sz})
22 if (!std::isfinite(v)) return fail("feature query poses must be finite");
23 }
24 for (std::size_t i = 0; i < samples.size(); ++i) {
25 const auto& s = samples[i];
26 for (float v : {s.seconds,s.x,s.y,s.z,s.vx,s.vy,s.vz,s.yaw})
27 if (!std::isfinite(v)) return fail("trajectory samples must be finite");
28 for (std::size_t j = 0; j < i; ++j)
29 if (std::abs(s.seconds-samples[j].seconds) < 1e-6f) return fail("trajectory sample times must be unique");
30 }
31 const auto& schema = *database_->schema_;
32 for (std::size_t i = 0; i < curves.size(); ++i) {
33 const auto& c = curves[i];
34 if (c.curve.empty() || c.curve.size() > 4096 || !std::isfinite(c.seconds) || !std::isfinite(c.value))
35 return fail("curve queries require bounded names and finite times/values");
36 for (std::size_t j = 0; j < i; ++j)
37 if (c.curve == curves[j].curve && std::abs(c.seconds-curves[j].seconds) < 1e-6f)
38 return fail("curve query names and times must be unique");
39 }
40 std::vector<float> scalarValues(schema.layout.channels.size(), 0.f);
41 for (std::size_t i = 0; i < schema.layout.channels.size(); ++i) {
42 const auto& c = schema.layout.channels[i];
43 if (c.kind != MotionFeatureKind::Curve) continue;
44 auto sample = std::find_if(curves.begin(), curves.end(), [&](const auto& s) {
45 return s.curve == c.curve && std::abs(s.seconds-c.sampleTime) < 1e-6f;
46 });
47 if (sample == curves.end()) return fail("curve query does not cover a configured channel/time");
48 scalarValues[i] = sample->value;
49 }
50 std::vector<const MotionFeatureTrajectorySample*> selected;
51 for (const auto& c : schema.layout.channels) {
52 const MotionFeatureTrajectorySample* sample = nullptr;
53 if (c.source == MotionFeatureSource::Trajectory) {
54 for (const auto& s : samples) if (std::abs(s.seconds-c.sampleTime) < 1e-6f) { sample = &s; break; }
55 if (!sample) return fail("trajectory query does not cover a configured sample time");
56 }
57 selected.push_back(sample);
58 }
59 AnimPose now, past;
60 now.copyFrom(&current); past.copyFrom(&previous);
61 now.computeWorld(skeleton_); past.computeWorld(skeleton_);
62 const auto& root = now.world(database_->getRootBone());
63 const auto& previousRoot = past.world(database_->getRootBone());
64 TransformTRS character;
65 character.qy = std::sin(desiredYaw_ * .5f); character.qw = std::cos(desiredYaw_ * .5f);
66 std::vector<float> query(schema.dimension, 0.f);
67 for (std::size_t i = 0; i < schema.layout.channels.size(); ++i) {
68 const auto& c = schema.layout.channels[i];
69 std::array<float, 3> value{};
70 if (c.kind == MotionFeatureKind::Curve) value[0] = scalarValues[i];
71 else if (const auto* sample = selected[i]) {
72 if (c.kind == MotionFeatureKind::Position) value = detail::schemaRotate({sample->x,sample->y,sample->z}, character, true);
73 else if (c.kind == MotionFeatureKind::Velocity) value = detail::schemaRotate({sample->vx,sample->vy,sample->vz}, character, true);
74 else {
76 rotation.qy = std::sin(sample->yaw * .5f); rotation.qw = std::cos(sample->yaw * .5f);
77 value = detail::schemaHeading(rotation, character, c.headingAxis);
78 }
79 } else {
80 const auto& bone = now.world(c.bone);
82 else if (c.kind == MotionFeatureKind::Heading) value = detail::schemaHeading(bone, root, c.headingAxis);
83 else if (c.characterSpaceVelocity) {
85 const auto old = detail::schemaDifference(past.world(c.bone), past.world(c.origin), previousRoot);
86 for (int axis = 0; axis < 3; ++axis) value[axis] = (value[axis] - old[axis]) / dt;
87 } else {
89 for (float& v : value) v /= dt;
90 }
91 }
92 detail::schemaEncode(std::span(query).subspan(schema.offsets[i]), c, value, schema.layout.normalizationLengthScale);
93 }
94 for (float v : query) if (!std::isfinite(v)) return fail("encoded feature query exceeds finite range");
95 const float queryTrajectorySpeed = database_->schemaTrajectorySpeed(query);
96 database_->normalizeFeature(query);
97 for (float v : query) if (!std::isfinite(v)) return fail("normalized feature query exceeds finite range");
98 schemaQuery_.swap(query);
99 queryTrajectorySpeed_ = queryTrajectorySpeed;
101}
102
103void MotionMatcher::buildSchemaQuery(std::vector<float>& query) const {
104 if (schemaQuery_.empty()) throw Exception("MotionMatcher: configure a feature query before searching");
105 query = schemaQuery_;
106}
107} // namespace eve::animation
double value
int root
Definition AnimSmr.cpp:119
eve::EntitySpatialPose pose
const std::string & s
std::string message
float v
std::int32_t c
std::array< float, 4 > rotation
std::uint32_t bone
graphics::Canvas * previous
float t
double current
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
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
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
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 copyFrom(const AnimPose *other)
Copies from.
Definition AnimPose.cpp:91
void computeWorld(const AnimSkeleton *skeleton)
Compute world transforms from local pose + skeleton hierarchy. World values readable via getWorld* af...
Definition AnimPose.cpp:203
int getBoneCount() const
Returns the bone count.
void normalizeFeature(std::vector< float > &feature) const
Normalize a query with statistics computed by bake().
bool isBaked() const
True when baked.
int getRootBone() const
Returns the root bone.
eve::Result< void > setFeatureQuery(const AnimPose &current, const AnimPose &previous, float elapsedSeconds, std::span< const MotionFeatureTrajectorySample > samples, std::span< const MotionFeatureCurveSample > curves={})
Atomically copy a query for the database's variable feature layout.
std::array< float, 3 > schemaDifference(const TransformTRS &a, const TransformTRS &b, const TransformTRS &root)
std::array< float, 3 > schemaHeading(const TransformTRS &bone, const TransformTRS &root, int axis)
std::array< float, 3 > schemaRotate(const std::array< float, 3 > &p, const TransformTRS &q, bool inverse=false)
void schemaEncode(std::span< float > output, const MotionFeatureChannel &channel, std::array< float, 3 > value, float lengthScale)
Schema encode.
Copied trajectory sample at an explicit offset from the current frame.
Local TRS used by skeletal animation (quaternion xyzw).
Definition AnimMath.h:9