Fix animation processing interpolation bug, crash on no merc material (#3794)

Fixes the issue with animations doing a full 180 degree rotation and
disappearing. The quaternions in the GLB export "flip", and when
interpolating animation frames in between a flip, we got bogus
quaternions.

Fixes https://github.com/open-goal/jak-project/issues/3786

Also fix a crash in `goalc` when using merc models without any
materials.

Co-authored-by: water111 <awaterford1111445@gmail.com>
This commit is contained in:
water111
2024-12-07 13:42:48 -05:00
committed by GitHub
parent 6e4e5d2bf7
commit a2f9d36332
2 changed files with 21 additions and 11 deletions
@@ -21,7 +21,8 @@ int find_max_joint(const tinygltf::Animation& anim, const std::map<int, int>& no
template <typename T>
std::vector<T> compute_keyframes(const std::vector<float>& times,
const std::vector<T>& values,
float framerate) {
float framerate,
bool quaternion_interp) {
std::vector<T> ret;
ASSERT(!times.empty());
ASSERT(times.size() == values.size());
@@ -36,8 +37,15 @@ std::vector<T> compute_keyframes(const std::vector<float>& times,
}
const float fraction = (t - times.at(i)) / (times.at(i + 1) - times.at(i));
ret.push_back(values.at(i) * (1.f - fraction) + values.at(i + 1) * fraction);
// lg::info("{} + {:.3f}, {}", i, fraction, ret.back().to_string_aligned());
if (quaternion_interp) {
float multiplier = 1;
if (values.at(i).dot(values.at(i + 1)) < 0) {
multiplier = -1;
}
ret.push_back(values.at(i) * (1.f - fraction) + values.at(i + 1) * fraction * multiplier);
} else {
ret.push_back(values.at(i) * (1.f - fraction) + values.at(i + 1) * fraction);
}
t += 1.f / framerate;
}
return ret;
@@ -47,12 +55,13 @@ template <int n>
std::vector<math::Vector<float, n>> extract_keyframed_gltf_vecn(
const tinygltf::Model& model,
const tinygltf::AnimationSampler& sampler,
float framerate) {
float framerate,
bool quaternion_interp) {
std::vector<float> times = gltf_util::extract_floats(model, sampler.input);
std::vector<math::Vector<float, n>> values =
gltf_util::extract_vec<float, n>(model, sampler.output, TINYGLTF_COMPONENT_TYPE_FLOAT);
ASSERT(times.size() == values.size());
return compute_keyframes(times, values, framerate);
return compute_keyframes(times, values, framerate, quaternion_interp);
}
} // namespace
@@ -75,13 +84,13 @@ UncompressedJointAnim extract_anim_from_gltf(const tinygltf::Model& model,
const auto& sampler = anim.samplers.at(channel.sampler);
if (channel.target_path == "translation") {
out.joints.at(channel_joint).trans_frames =
extract_keyframed_gltf_vecn<3>(model, sampler, framerate);
extract_keyframed_gltf_vecn<3>(model, sampler, framerate, false);
} else if (channel.target_path == "rotation") {
out.joints.at(channel_joint).quat_frames =
extract_keyframed_gltf_vecn<4>(model, sampler, framerate);
extract_keyframed_gltf_vecn<4>(model, sampler, framerate, true);
} else if (channel.target_path == "scale") {
out.joints.at(channel_joint).scale_frames =
extract_keyframed_gltf_vecn<3>(model, sampler, framerate);
extract_keyframed_gltf_vecn<3>(model, sampler, framerate, false);
} else {
lg::die("unknown target_path {}", channel.target_path);
}