cocos-engine-external/sources/acl/compression/impl/segment_streams.h

188 lines
8.8 KiB
C++

#pragma once
////////////////////////////////////////////////////////////////////////////////
// The MIT License (MIT)
//
// Copyright (c) 2017 Nicholas Frechette & Animation Compression Library contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#include "acl/core/iallocator.h"
#include "acl/core/impl/compiler_utils.h"
#include "acl/core/error.h"
#include "acl/compression/compression_settings.h"
#include "acl/compression/impl/clip_context.h"
#include <cstdint>
ACL_IMPL_FILE_PRAGMA_PUSH
namespace acl
{
namespace acl_impl
{
inline void segment_streams(iallocator& allocator, clip_context& clip, const segmenting_settings& settings)
{
ACL_ASSERT(clip.num_segments == 1, "clip_context must have a single segment.");
ACL_ASSERT(settings.ideal_num_samples <= settings.max_num_samples, "Invalid num samples for segmenting settings. %u > %u", settings.ideal_num_samples, settings.max_num_samples);
if (clip.num_samples <= settings.max_num_samples)
return;
//////////////////////////////////////////////////////////////////////////
// This algorithm is simple in nature. Its primary aim is to avoid having
// the last segment being partial if multiple segments are present.
// The extra samples from the last segment will be redistributed evenly
// starting with the first segment.
// As such, in order to quickly find which segment contains a particular sample
// you can simply divide the number of samples by the number of segments to get
// the floored value of number of samples per segment. This guarantees an accurate estimate.
// You can then query the segment start index by dividing the desired sample index
// with the floored value. If the sample isn't in the current segment, it will live in one of its neighbors.
// TODO: Can we provide a tighter guarantee?
//////////////////////////////////////////////////////////////////////////
uint32_t num_segments = (clip.num_samples + settings.ideal_num_samples - 1) / settings.ideal_num_samples;
const uint32_t max_num_samples = num_segments * settings.ideal_num_samples;
const uint32_t original_num_segments = num_segments;
uint32_t* num_samples_per_segment = allocate_type_array<uint32_t>(allocator, num_segments);
std::fill(num_samples_per_segment, num_samples_per_segment + num_segments, settings.ideal_num_samples);
const uint32_t num_leftover_samples = settings.ideal_num_samples - (max_num_samples - clip.num_samples);
if (num_leftover_samples != 0)
num_samples_per_segment[num_segments - 1] = num_leftover_samples;
const uint32_t slack = settings.max_num_samples - settings.ideal_num_samples;
if ((num_segments - 1) * slack >= num_leftover_samples)
{
// Enough segments to distribute the leftover samples of the last segment
while (num_samples_per_segment[num_segments - 1] != 0)
{
for (uint32_t segment_index = 0; segment_index < num_segments - 1 && num_samples_per_segment[num_segments - 1] != 0; ++segment_index)
{
num_samples_per_segment[segment_index]++;
num_samples_per_segment[num_segments - 1]--;
}
}
num_segments--;
}
ACL_ASSERT(num_segments != 1, "Expected a number of segments greater than 1.");
SegmentContext* clip_segment = clip.segments;
clip.segments = allocate_type_array<SegmentContext>(allocator, num_segments);
clip.num_segments = num_segments;
uint32_t clip_sample_index = 0;
for (uint32_t segment_index = 0; segment_index < num_segments; ++segment_index)
{
const uint32_t num_samples_in_segment = num_samples_per_segment[segment_index];
SegmentContext& segment = clip.segments[segment_index];
segment.clip = &clip;
segment.bone_streams = allocate_type_array<BoneStreams>(allocator, clip.num_bones);
segment.ranges = nullptr;
segment.num_bones = clip.num_bones;
segment.num_samples = num_samples_in_segment;
segment.clip_sample_offset = clip_sample_index;
segment.segment_index = segment_index;
segment.distribution = SampleDistribution8::Uniform;
segment.are_rotations_normalized = false;
segment.are_translations_normalized = false;
segment.are_scales_normalized = false;
segment.animated_pose_rotation_bit_size = 0;
segment.animated_pose_translation_bit_size = 0;
segment.animated_pose_scale_bit_size = 0;
segment.animated_pose_bit_size = 0;
segment.animated_data_size = 0;
segment.range_data_size = 0;
segment.total_header_size = 0;
for (uint32_t bone_index = 0; bone_index < clip.num_bones; ++bone_index)
{
const BoneStreams& clip_bone_stream = clip_segment->bone_streams[bone_index];
BoneStreams& segment_bone_stream = segment.bone_streams[bone_index];
segment_bone_stream.segment = &segment;
segment_bone_stream.bone_index = bone_index;
segment_bone_stream.parent_bone_index = clip_bone_stream.parent_bone_index;
segment_bone_stream.output_index = clip_bone_stream.output_index;
if (clip_bone_stream.is_rotation_constant)
{
segment_bone_stream.rotations = clip_bone_stream.rotations.duplicate();
}
else
{
const uint32_t sample_size = clip_bone_stream.rotations.get_sample_size();
RotationTrackStream rotations(allocator, num_samples_in_segment, sample_size, clip_bone_stream.rotations.get_sample_rate(), clip_bone_stream.rotations.get_rotation_format(), clip_bone_stream.rotations.get_bit_rate());
std::memcpy(rotations.get_raw_sample_ptr(0), clip_bone_stream.rotations.get_raw_sample_ptr(clip_sample_index), size_t(num_samples_in_segment) * sample_size);
segment_bone_stream.rotations = std::move(rotations);
}
if (clip_bone_stream.is_translation_constant)
{
segment_bone_stream.translations = clip_bone_stream.translations.duplicate();
}
else
{
const uint32_t sample_size = clip_bone_stream.translations.get_sample_size();
TranslationTrackStream translations(allocator, num_samples_in_segment, sample_size, clip_bone_stream.translations.get_sample_rate(), clip_bone_stream.translations.get_vector_format(), clip_bone_stream.translations.get_bit_rate());
std::memcpy(translations.get_raw_sample_ptr(0), clip_bone_stream.translations.get_raw_sample_ptr(clip_sample_index), size_t(num_samples_in_segment) * sample_size);
segment_bone_stream.translations = std::move(translations);
}
if (clip_bone_stream.is_scale_constant)
{
segment_bone_stream.scales = clip_bone_stream.scales.duplicate();
}
else
{
const uint32_t sample_size = clip_bone_stream.scales.get_sample_size();
ScaleTrackStream scales(allocator, num_samples_in_segment, sample_size, clip_bone_stream.scales.get_sample_rate(), clip_bone_stream.scales.get_vector_format(), clip_bone_stream.scales.get_bit_rate());
std::memcpy(scales.get_raw_sample_ptr(0), clip_bone_stream.scales.get_raw_sample_ptr(clip_sample_index), size_t(num_samples_in_segment) * sample_size);
segment_bone_stream.scales = std::move(scales);
}
segment_bone_stream.is_rotation_constant = clip_bone_stream.is_rotation_constant;
segment_bone_stream.is_rotation_default = clip_bone_stream.is_rotation_default;
segment_bone_stream.is_translation_constant = clip_bone_stream.is_translation_constant;
segment_bone_stream.is_translation_default = clip_bone_stream.is_translation_default;
segment_bone_stream.is_scale_constant = clip_bone_stream.is_scale_constant;
segment_bone_stream.is_scale_default = clip_bone_stream.is_scale_default;
}
clip_sample_index += num_samples_in_segment;
}
deallocate_type_array(allocator, num_samples_per_segment, original_num_segments);
destroy_segment_context(allocator, *clip_segment);
deallocate_type_array(allocator, clip_segment, 1);
}
}
}
ACL_IMPL_FILE_PRAGMA_POP