From c1a1658f31c67e2defc341799362950050050653 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Tue, 10 Dec 2024 11:46:36 -0800 Subject: [PATCH] gltfpack: Implement support for tracks with cubic spline interpolation Tracks with cubic splines have three output components for each input component; also, they require writing interpolation type properly. When serializing cubic spline tracks for rotations, the tangents are not actually quaternions; as such, they can't be encoded using quaternion filter, and in -cc mode we need to fall back to raw snorm encoding. Even snorm encoding technically might not be sufficient as tangents could be outside of -1..1 range, but for now we can probably clamp out of range tangents. --- gltf/animation.cpp | 1 + gltf/gltfpack.h | 2 +- gltf/stream.cpp | 4 ++-- gltf/write.cpp | 33 ++++++++++++++++++++++++--------- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/gltf/animation.cpp b/gltf/animation.cpp index 27381254c..d78ccb492 100644 --- a/gltf/animation.cpp +++ b/gltf/animation.cpp @@ -308,6 +308,7 @@ void processAnimation(Animation& animation, const Settings& settings) track.time.clear(); track.data.swap(result); + track.interpolation = track.interpolation == cgltf_interpolation_type_cubic_spline ? cgltf_interpolation_type_linear : track.interpolation; float tolerance = getDeltaTolerance(track.path); diff --git a/gltf/gltfpack.h b/gltf/gltfpack.h index 1510ef394..5868bf161 100644 --- a/gltf/gltfpack.h +++ b/gltf/gltfpack.h @@ -362,7 +362,7 @@ void getPositionBounds(float min[3], float max[3], const Stream& stream, const Q StreamFormat writeVertexStream(std::string& bin, const Stream& stream, const QuantizationPosition& qp, const QuantizationTexture& qt, const Settings& settings); StreamFormat writeIndexStream(std::string& bin, const std::vector& stream); StreamFormat writeTimeStream(std::string& bin, const std::vector& data); -StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector& data, const Settings& settings); +StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector& data, const Settings& settings, bool has_tangents = false); void compressVertexStream(std::string& bin, const std::string& data, size_t count, size_t stride); void compressIndexStream(std::string& bin, const std::string& data, size_t count, size_t stride); diff --git a/gltf/stream.cpp b/gltf/stream.cpp index 402c743f1..1ed7276f9 100644 --- a/gltf/stream.cpp +++ b/gltf/stream.cpp @@ -679,11 +679,11 @@ StreamFormat writeTimeStream(std::string& bin, const std::vector& data) return format; } -StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector& data, const Settings& settings) +StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector& data, const Settings& settings, bool has_tangents) { if (type == cgltf_animation_path_type_rotation) { - StreamFormat::Filter filter = settings.compressmore ? StreamFormat::Filter_Quat : StreamFormat::Filter_None; + StreamFormat::Filter filter = settings.compressmore && !has_tangents ? StreamFormat::Filter_Quat : StreamFormat::Filter_None; size_t offset = bin.size(); size_t stride = 8; diff --git a/gltf/write.cpp b/gltf/write.cpp index 3111caafa..93dc85456 100644 --- a/gltf/write.cpp +++ b/gltf/write.cpp @@ -113,13 +113,25 @@ static const char* alphaMode(cgltf_alpha_mode mode) { case cgltf_alpha_mode_opaque: return "OPAQUE"; - case cgltf_alpha_mode_mask: return "MASK"; - case cgltf_alpha_mode_blend: return "BLEND"; + default: + return ""; + } +} +static const char* interpolationType(cgltf_interpolation_type type) +{ + switch (type) + { + case cgltf_interpolation_type_linear: + return "LINEAR"; + case cgltf_interpolation_type_step: + return "STEP"; + case cgltf_interpolation_type_cubic_spline: + return "CUBICSPLINE"; default: return ""; } @@ -1403,7 +1415,6 @@ void writeAnimation(std::string& json, std::vector& views, std::stri bool needs_time = false; bool needs_pose = false; - bool needs_custom_time = false; for (size_t j = 0; j < tracks.size(); ++j) { @@ -1418,13 +1429,13 @@ void writeAnimation(std::string& json, std::vector& views, std::stri } else { - assert(track.data.size() == track.components * track.time.size()); + size_t keyframe_size = (track.interpolation == cgltf_interpolation_type_cubic_spline) ? 3 : 1; - needs_custom_time = true; + assert(track.data.size() == keyframe_size * track.components * track.time.size()); } } - bool needs_range = needs_pose && !needs_time && !needs_custom_time && animation.frames > 1; + bool needs_range = needs_pose && !needs_time && animation.frames > 1; needs_pose = needs_pose && !(needs_range && tracks.size() == 1); @@ -1454,7 +1465,7 @@ void writeAnimation(std::string& json, std::vector& views, std::stri track_time_accr = writeAnimationTime(views, json_accessors, accr_offset, track.time, settings); std::string scratch; - StreamFormat format = writeKeyframeStream(scratch, track.path, track.data, settings); + StreamFormat format = writeKeyframeStream(scratch, track.path, track.data, settings, track.interpolation == cgltf_interpolation_type_cubic_spline); if (range) { @@ -1478,8 +1489,12 @@ void writeAnimation(std::string& json, std::vector& views, std::stri append(json_samplers, range ? range_accr : (track.constant ? pose_accr : track_time_accr)); append(json_samplers, ",\"output\":"); append(json_samplers, data_accr); - if (track.interpolation == cgltf_interpolation_type_step) - append(json_samplers, ",\"interpolation\":\"STEP\""); + if (track.interpolation != cgltf_interpolation_type_linear) + { + append(json_samplers, ",\"interpolation\":\""); + append(json_samplers, interpolationType(track.interpolation)); + append(json_samplers, "\""); + } append(json_samplers, "}"); const NodeInfo& tni = nodes[track.node - data->nodes];