Skip to content

Commit

Permalink
Merge pull request #819 from zeux/gltf-animfreq
Browse files Browse the repository at this point in the history
gltfpack: Disable animation resampling when frequency is 0
  • Loading branch information
zeux authored Dec 11, 2024
2 parents d95c7fe + 1ab8735 commit 28549cb
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 38 deletions.
45 changes: 28 additions & 17 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,27 +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);
// getMaxDelta assumes linear/step interpolation for now
if (track.interpolation == cgltf_interpolation_type_cubic_spline)
continue;

float tolerance = getDeltaTolerance(track.path);

Expand All @@ -318,19 +328,20 @@ 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)
{
// track is constant (equal to first keyframe), we only need the first keyframe
track.constant = true;
track.time.clear();
track.data.resize(track.components);

// track.dummy is true iff track redundantly sets up the value to be equal to default node transform
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 @@ -1333,7 +1333,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 @@ -1617,7 +1617,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
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
74 changes: 58 additions & 16 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 @@ -1079,13 +1091,8 @@ void writeMeshGeometry(std::string& json, std::vector<BufferView>& views, std::s
}
}

static size_t writeAnimationTime(std::vector<BufferView>& views, std::string& json_accessors, size_t& accr_offset, float mint, int frames, float period, const Settings& settings)
static size_t writeAnimationTime(std::vector<BufferView>& views, std::string& json_accessors, size_t& accr_offset, const std::vector<float>& time, const Settings& settings)
{
std::vector<float> time(frames);

for (int j = 0; j < frames; ++j)
time[j] = mint + float(j) * period;

std::string scratch;
StreamFormat format = writeTimeStream(scratch, time);
BufferView::Compression compression = settings.compress ? BufferView::Compression_Attribute : BufferView::Compression_None;
Expand All @@ -1095,13 +1102,23 @@ static size_t writeAnimationTime(std::vector<BufferView>& views, std::string& js
views[view].data += scratch;

comma(json_accessors);
writeAccessor(json_accessors, view, offset, cgltf_type_scalar, format.component_type, format.normalized, frames, &time.front(), &time.back(), 1);
writeAccessor(json_accessors, view, offset, cgltf_type_scalar, format.component_type, format.normalized, time.size(), &time.front(), &time.back(), 1);

size_t time_accr = accr_offset++;

return time_accr;
}

static size_t writeAnimationTime(std::vector<BufferView>& views, std::string& json_accessors, size_t& accr_offset, float mint, int frames, float period, const Settings& settings)
{
std::vector<float> time(frames);

for (int j = 0; j < frames; ++j)
time[j] = mint + float(j) * period;

return writeAnimationTime(views, json_accessors, accr_offset, time, settings);
}

size_t writeJointBindMatrices(std::vector<BufferView>& views, std::string& json_accessors, size_t& accr_offset, const cgltf_skin& skin, const QuantizationPosition& qp, const Settings& settings)
{
std::string scratch;
Expand Down Expand Up @@ -1403,10 +1420,14 @@ void writeAnimation(std::string& json, std::vector<BufferView>& views, std::stri
{
const Track& track = *tracks[j];

assert(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());

assert(track.data.size() == keyframe_size * track.components * time_size);
#endif

needs_time = needs_time || !track.constant;
needs_time = needs_time || (track.time.empty() && !track.constant);
needs_pose = needs_pose || track.constant;
}

Expand All @@ -1428,15 +1449,32 @@ void writeAnimation(std::string& json, std::vector<BufferView>& views, std::stri

size_t track_offset = 0;

size_t last_track_time_accr = 0;
const Track* last_track_time = NULL;

for (size_t j = 0; j < tracks.size(); ++j)
{
const Track& track = *tracks[j];

bool range = needs_range && j == 0;
int range_size = range ? 2 : 1;

size_t track_time_accr = time_accr;
if (!track.time.empty())
{
// reuse time accessors between consecutive tracks if possible
if (last_track_time && track.time == last_track_time->time)
track_time_accr = last_track_time_accr;
else
{
track_time_accr = writeAnimationTime(views, json_accessors, accr_offset, track.time, settings);
last_track_time_accr = track_time_accr;
last_track_time = &track;
}
}

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 @@ -1457,11 +1495,15 @@ void writeAnimation(std::string& json, std::vector<BufferView>& views, std::stri

comma(json_samplers);
append(json_samplers, "{\"input\":");
append(json_samplers, range ? range_accr : (track.constant ? pose_accr : time_accr));
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 28549cb

Please sign in to comment.