Skip to content

Commit

Permalink
gltfpack: Implement support for tracks with cubic spline interpolation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
zeux committed Dec 10, 2024
1 parent cd1f62b commit c1a1658
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 12 deletions.
1 change: 1 addition & 0 deletions gltf/animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion gltf/gltfpack.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned int>& stream);
StreamFormat writeTimeStream(std::string& bin, const std::vector<float>& data);
StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector<Attr>& data, const Settings& settings);
StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector<Attr>& 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);
Expand Down
4 changes: 2 additions & 2 deletions gltf/stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -679,11 +679,11 @@ StreamFormat writeTimeStream(std::string& bin, const std::vector<float>& data)
return format;
}

StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector<Attr>& data, const Settings& settings)
StreamFormat writeKeyframeStream(std::string& bin, cgltf_animation_path_type type, const std::vector<Attr>& 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;
Expand Down
33 changes: 24 additions & 9 deletions gltf/write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 "";
}
Expand Down Expand Up @@ -1403,7 +1415,6 @@ void writeAnimation(std::string& json, std::vector<BufferView>& 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)
{
Expand All @@ -1418,13 +1429,13 @@ void writeAnimation(std::string& json, std::vector<BufferView>& 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);

Expand Down Expand Up @@ -1454,7 +1465,7 @@ void writeAnimation(std::string& json, std::vector<BufferView>& 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)
{
Expand All @@ -1478,8 +1489,12 @@ void writeAnimation(std::string& json, std::vector<BufferView>& 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];
Expand Down

0 comments on commit c1a1658

Please sign in to comment.