Skip to content

Commit

Permalink
gltfpack: Implement support for disabling resampling when frequency is 0
Browse files Browse the repository at this point in the history
Using -af 0 now disables resampling and keeps the original time values.

When resampling is disabled, we can still mark tracks as constant and
remove them if they are not necessary, which cuts down on the animation
size. In this mode it is more difficult to support ranges, so we assume track
range will cover the relevant timespan if any tracks with time survive.

Doing this analysis for cubic splines is slightly more difficult as if
tangents are non-zero, an otherwise "constant" track will not result in
constant output, so we disable analysis in that case for simplicity.
  • Loading branch information
zeux committed Dec 10, 2024
1 parent c1a1658 commit d4759fc
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 31 deletions.
45 changes: 27 additions & 18 deletions gltf/animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,17 +207,17 @@ static void resampleKeyframes(std::vector<Attr>& data, const std::vector<float>&
}
}

static float getMaxDelta(const std::vector<Attr>& data, cgltf_animation_path_type type, int frames, const Attr* value, size_t components)
static float getMaxDelta(const std::vector<Attr>& data, cgltf_animation_path_type type, const Attr* value, size_t components)
{
assert(data.size() == frames * components);
assert(data.size() % components == 0);

float result = 0;

for (int i = 0; i < frames; ++i)
for (size_t i = 0; i < data.size(); i += components)
{
for (size_t j = 0; j < components; ++j)
{
float delta = getDelta(value[j], data[i * components + j], type);
float delta = getDelta(value[j], data[i + j], type);

result = (result < delta) ? delta : result;
}
Expand Down Expand Up @@ -287,28 +287,37 @@ void processAnimation(Animation& animation, const Settings& settings)
maxt = std::max(maxt, track.time.back());
}

mint = std::min(mint, maxt);
animation.start = mint = std::min(mint, maxt);

// round the number of frames to nearest but favor the "up" direction
// this means that at 100 Hz resampling, we will try to preserve the last frame <10ms
// but if the last frame is <2ms we favor just removing this data
int frames = 1 + int((maxt - mint) * settings.anim_freq + 0.8f);
if (settings.anim_freq)
{
// round the number of frames to nearest but favor the "up" direction
// this means that at 100 Hz resampling, we will try to preserve the last frame <10ms
// but if the last frame is <2ms we favor just removing this data
int frames = 1 + int((maxt - mint) * settings.anim_freq + 0.8f);

animation.start = mint;
animation.frames = frames;
animation.frames = frames;
}

std::vector<Attr> base;

for (size_t i = 0; i < animation.tracks.size(); ++i)
{
Track& track = animation.tracks[i];

std::vector<Attr> result;
resampleKeyframes(result, track.time, track.data, track.path, track.interpolation, track.components, frames, mint, settings.anim_freq);
if (settings.anim_freq)
{
std::vector<Attr> result;
resampleKeyframes(result, track.time, track.data, track.path, track.interpolation, track.components, animation.frames, animation.start, settings.anim_freq);

track.time.clear();
track.data.swap(result);
track.interpolation = track.interpolation == cgltf_interpolation_type_cubic_spline ? cgltf_interpolation_type_linear : track.interpolation;
}

track.time.clear();
track.data.swap(result);
track.interpolation = track.interpolation == cgltf_interpolation_type_cubic_spline ? cgltf_interpolation_type_linear : track.interpolation;
// getMaxDelta assumes linear/step interpolation for now
if (track.interpolation == cgltf_interpolation_type_cubic_spline)
continue;

float tolerance = getDeltaTolerance(track.path);

Expand All @@ -319,7 +328,7 @@ void processAnimation(Animation& animation, const Settings& settings)
tolerance /= scale == 0.f ? 1.f : scale;
}

float deviation = getMaxDelta(track.data, track.path, frames, &track.data[0], track.components);
float deviation = getMaxDelta(track.data, track.path, &track.data[0], track.components);

if (deviation <= tolerance)
{
Expand All @@ -331,7 +340,7 @@ void processAnimation(Animation& animation, const Settings& settings)
base.resize(track.components);
getBaseTransform(&base[0], track.components, track.path, track.node);

track.dummy = getMaxDelta(track.data, track.path, 1, &base[0], track.components) <= tolerance;
track.dummy = getMaxDelta(track.data, track.path, &base[0], track.components) <= tolerance;
}
}
}
4 changes: 2 additions & 2 deletions gltf/gltfpack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1331,7 +1331,7 @@ int main(int argc, char** argv)
}
else if (strcmp(arg, "-af") == 0 && i + 1 < argc && isdigit(argv[i + 1][0]))
{
settings.anim_freq = clamp(atoi(argv[++i]), 1, 100);
settings.anim_freq = clamp(atoi(argv[++i]), 0, 100);
}
else if (strcmp(arg, "-ac") == 0)
{
Expand Down Expand Up @@ -1615,7 +1615,7 @@ int main(int argc, char** argv)
fprintf(stderr, "\t-at N: use N-bit quantization for translations (default: 16; N should be between 1 and 24)\n");
fprintf(stderr, "\t-ar N: use N-bit quantization for rotations (default: 12; N should be between 4 and 16)\n");
fprintf(stderr, "\t-as N: use N-bit quantization for scale (default: 16; N should be between 1 and 24)\n");
fprintf(stderr, "\t-af N: resample animations at N Hz (default: 30)\n");
fprintf(stderr, "\t-af N: resample animations at N Hz (default: 30; use 0 to disable)\n");
fprintf(stderr, "\t-ac: keep constant animation tracks even if they don't modify the node transform\n");
fprintf(stderr, "\nScene:\n");
fprintf(stderr, "\t-kn: keep named nodes and meshes attached to named nodes so that named nodes can be transformed externally\n");
Expand Down
18 changes: 7 additions & 11 deletions gltf/write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1420,19 +1420,15 @@ void writeAnimation(std::string& json, std::vector<BufferView>& views, std::stri
{
const Track& track = *tracks[j];

if (track.time.empty())
{
assert(track.data.size() == track.components * (track.constant ? 1 : animation.frames));
#ifndef NDEBUG
size_t keyframe_size = (track.interpolation == cgltf_interpolation_type_cubic_spline) ? 3 : 1;
size_t time_size = track.constant ? 1 : (track.time.empty() ? animation.frames : track.time.size());

needs_time = needs_time || !track.constant;
needs_pose = needs_pose || track.constant;
}
else
{
size_t keyframe_size = (track.interpolation == cgltf_interpolation_type_cubic_spline) ? 3 : 1;
assert(track.data.size() == keyframe_size * track.components * time_size);
#endif

assert(track.data.size() == keyframe_size * track.components * track.time.size());
}
needs_time = needs_time || (track.time.empty() && !track.constant);
needs_pose = needs_pose || track.constant;
}

bool needs_range = needs_pose && !needs_time && animation.frames > 1;
Expand Down

0 comments on commit d4759fc

Please sign in to comment.