#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/core/track_formats.h" #include "acl/core/track_types.h" #include "acl/core/utils.h" #include "acl/core/variable_bit_rates.h" #include "acl/math/quat_packing.h" #include "acl/math/vector4_packing.h" #include #include #include ACL_IMPL_FILE_PRAGMA_PUSH namespace acl { namespace acl_impl { class TrackStream { public: uint8_t* get_raw_sample_ptr(uint32_t sample_index) { ACL_ASSERT(sample_index < m_num_samples, "Invalid sample index. %u >= %u", sample_index, m_num_samples); uint32_t offset = sample_index * m_sample_size; return m_samples + offset; } const uint8_t* get_raw_sample_ptr(uint32_t sample_index) const { ACL_ASSERT(sample_index < m_num_samples, "Invalid sample index. %u >= %u", sample_index, m_num_samples); uint32_t offset = sample_index * m_sample_size; return m_samples + offset; } template SampleType RTM_SIMD_CALL get_raw_sample(uint32_t sample_index) const { const uint8_t* ptr = get_raw_sample_ptr(sample_index); return *safe_ptr_cast(ptr); } #if defined(RTM_NO_INTRINSICS) template void RTM_SIMD_CALL set_raw_sample(uint32_t sample_index, const SampleType& sample) #else template void RTM_SIMD_CALL set_raw_sample(uint32_t sample_index, SampleType sample) #endif { ACL_ASSERT(m_sample_size == sizeof(SampleType), "Unexpected sample size. %u != %zu", m_sample_size, sizeof(SampleType)); uint8_t* ptr = get_raw_sample_ptr(sample_index); *safe_ptr_cast(ptr) = sample; } uint32_t get_num_samples() const { return m_num_samples; } uint32_t get_sample_size() const { return m_sample_size; } float get_sample_rate() const { return m_sample_rate; } animation_track_type8 get_track_type() const { return m_type; } uint8_t get_bit_rate() const { return m_bit_rate; } bool is_bit_rate_variable() const { return m_bit_rate != k_invalid_bit_rate; } float get_duration() const { return calculate_duration(m_num_samples, m_sample_rate); } uint32_t get_packed_sample_size() const { if (m_type == animation_track_type8::rotation) return get_packed_rotation_size(m_format.rotation); else return get_packed_vector_size(m_format.vector); } protected: TrackStream(animation_track_type8 type, track_format8 format) noexcept : m_allocator(nullptr), m_samples(nullptr), m_num_samples(0), m_sample_size(0), m_sample_rate(0.0F), m_type(type), m_format(format), m_bit_rate(0) {} TrackStream(iallocator& allocator, uint32_t num_samples, uint32_t sample_size, float sample_rate, animation_track_type8 type, track_format8 format, uint8_t bit_rate) : m_allocator(&allocator) , m_samples(reinterpret_cast(allocator.allocate(sample_size * num_samples + k_padding, 16))) , m_num_samples(num_samples) , m_sample_size(sample_size) , m_sample_rate(sample_rate) , m_type(type) , m_format(format) , m_bit_rate(bit_rate) {} TrackStream(const TrackStream&) = delete; TrackStream(TrackStream&& other) noexcept : m_allocator(other.m_allocator) , m_samples(other.m_samples) , m_num_samples(other.m_num_samples) , m_sample_size(other.m_sample_size) , m_sample_rate(other.m_sample_rate) , m_type(other.m_type) , m_format(other.m_format) , m_bit_rate(other.m_bit_rate) { new(&other) TrackStream(other.m_type, other.m_format); } ~TrackStream() { if (m_allocator != nullptr && m_num_samples != 0) m_allocator->deallocate(m_samples, m_sample_size * m_num_samples + k_padding); } TrackStream& operator=(const TrackStream&) = delete; TrackStream& operator=(TrackStream&& rhs) noexcept { std::swap(m_allocator, rhs.m_allocator); std::swap(m_samples, rhs.m_samples); std::swap(m_num_samples, rhs.m_num_samples); std::swap(m_sample_size, rhs.m_sample_size); std::swap(m_sample_rate, rhs.m_sample_rate); std::swap(m_type, rhs.m_type); std::swap(m_format, rhs.m_format); std::swap(m_bit_rate, rhs.m_bit_rate); return *this; } void duplicate(TrackStream& copy) const { ACL_ASSERT(copy.m_type == m_type, "Attempting to duplicate streams with incompatible types!"); if (m_allocator != nullptr) { copy.m_allocator = m_allocator; copy.m_samples = reinterpret_cast(m_allocator->allocate(m_sample_size * m_num_samples + k_padding, 16)); copy.m_num_samples = m_num_samples; copy.m_sample_size = m_sample_size; copy.m_sample_rate = m_sample_rate; copy.m_format = m_format; copy.m_bit_rate = m_bit_rate; std::memcpy(copy.m_samples, m_samples, (size_t)m_sample_size * m_num_samples); } } // In order to guarantee the safety of unaligned SIMD loads of every byte, we add some padding static constexpr uint32_t k_padding = 15; iallocator* m_allocator; uint8_t* m_samples; uint32_t m_num_samples; uint32_t m_sample_size; float m_sample_rate; animation_track_type8 m_type; track_format8 m_format; uint8_t m_bit_rate; }; class RotationTrackStream final : public TrackStream { public: RotationTrackStream() noexcept : TrackStream(animation_track_type8::rotation, track_format8(rotation_format8::quatf_full)) {} RotationTrackStream(iallocator& allocator, uint32_t num_samples, uint32_t sample_size, float sample_rate, rotation_format8 format, uint8_t bit_rate = k_invalid_bit_rate) : TrackStream(allocator, num_samples, sample_size, sample_rate, animation_track_type8::rotation, track_format8(format), bit_rate) {} RotationTrackStream(const RotationTrackStream&) = delete; RotationTrackStream(RotationTrackStream&& other) noexcept : TrackStream(static_cast(other)) {} ~RotationTrackStream() = default; RotationTrackStream& operator=(const RotationTrackStream&) = delete; RotationTrackStream& operator=(RotationTrackStream&& rhs) noexcept { TrackStream::operator=(static_cast(rhs)); return *this; } RotationTrackStream duplicate() const { RotationTrackStream copy; TrackStream::duplicate(copy); return copy; } rotation_format8 get_rotation_format() const { return m_format.rotation; } }; class TranslationTrackStream final : public TrackStream { public: TranslationTrackStream() noexcept : TrackStream(animation_track_type8::translation, track_format8(vector_format8::vector3f_full)) {} TranslationTrackStream(iallocator& allocator, uint32_t num_samples, uint32_t sample_size, float sample_rate, vector_format8 format, uint8_t bit_rate = k_invalid_bit_rate) : TrackStream(allocator, num_samples, sample_size, sample_rate, animation_track_type8::translation, track_format8(format), bit_rate) {} TranslationTrackStream(const TranslationTrackStream&) = delete; TranslationTrackStream(TranslationTrackStream&& other) noexcept : TrackStream(static_cast(other)) {} ~TranslationTrackStream() = default; TranslationTrackStream& operator=(const TranslationTrackStream&) = delete; TranslationTrackStream& operator=(TranslationTrackStream&& rhs) noexcept { TrackStream::operator=(static_cast(rhs)); return *this; } TranslationTrackStream duplicate() const { TranslationTrackStream copy; TrackStream::duplicate(copy); return copy; } vector_format8 get_vector_format() const { return m_format.vector; } }; class ScaleTrackStream final : public TrackStream { public: ScaleTrackStream() noexcept : TrackStream(animation_track_type8::scale, track_format8(vector_format8::vector3f_full)) {} ScaleTrackStream(iallocator& allocator, uint32_t num_samples, uint32_t sample_size, float sample_rate, vector_format8 format, uint8_t bit_rate = k_invalid_bit_rate) : TrackStream(allocator, num_samples, sample_size, sample_rate, animation_track_type8::scale, track_format8(format), bit_rate) {} ScaleTrackStream(const ScaleTrackStream&) = delete; ScaleTrackStream(ScaleTrackStream&& other) noexcept : TrackStream(static_cast(other)) {} ~ScaleTrackStream() = default; ScaleTrackStream& operator=(const ScaleTrackStream&) = delete; ScaleTrackStream& operator=(ScaleTrackStream&& rhs) noexcept { TrackStream::operator=(static_cast(rhs)); return *this; } ScaleTrackStream duplicate() const { ScaleTrackStream copy; TrackStream::duplicate(copy); return copy; } vector_format8 get_vector_format() const { return m_format.vector; } }; // For a rotation track, the extent only tells us if the track is constant or not // since the min/max we maintain aren't valid rotations. // Similarly, the center isn't a valid rotation and is meaningless. class TrackStreamRange { public: static TrackStreamRange RTM_SIMD_CALL from_min_max(rtm::vector4f_arg0 min, rtm::vector4f_arg1 max) { return TrackStreamRange(min, rtm::vector_sub(max, min)); } static TrackStreamRange RTM_SIMD_CALL from_min_extent(rtm::vector4f_arg0 min, rtm::vector4f_arg1 extent) { return TrackStreamRange(min, extent); } TrackStreamRange() : m_min(rtm::vector_zero()) , m_extent(rtm::vector_zero()) {} rtm::vector4f RTM_SIMD_CALL get_min() const { return m_min; } rtm::vector4f RTM_SIMD_CALL get_max() const { return rtm::vector_add(m_min, m_extent); } rtm::vector4f RTM_SIMD_CALL get_center() const { return rtm::vector_add(m_min, rtm::vector_mul(m_extent, 0.5F)); } rtm::vector4f RTM_SIMD_CALL get_extent() const { return m_extent; } bool is_constant(float threshold) const { return rtm::vector_all_less_than(rtm::vector_abs(m_extent), rtm::vector_set(threshold)); } private: TrackStreamRange(rtm::vector4f_arg0 min, rtm::vector4f_arg1 extent) : m_min(min) , m_extent(extent) {} rtm::vector4f m_min; rtm::vector4f m_extent; }; struct BoneRanges { TrackStreamRange rotation; TrackStreamRange translation; TrackStreamRange scale; }; struct SegmentContext; struct BoneStreams { SegmentContext* segment; uint32_t bone_index; uint32_t parent_bone_index; uint32_t output_index; RotationTrackStream rotations; TranslationTrackStream translations; ScaleTrackStream scales; bool is_rotation_constant; bool is_rotation_default; bool is_translation_constant; bool is_translation_default; bool is_scale_constant; bool is_scale_default; bool is_stripped_from_output() const { return output_index == k_invalid_track_index; } BoneStreams duplicate() const { BoneStreams copy; copy.segment = segment; copy.bone_index = bone_index; copy.parent_bone_index = parent_bone_index; copy.output_index = output_index; copy.rotations = rotations.duplicate(); copy.translations = translations.duplicate(); copy.scales = scales.duplicate(); copy.is_rotation_constant = is_rotation_constant; copy.is_rotation_default = is_rotation_default; copy.is_translation_constant = is_translation_constant; copy.is_translation_default = is_translation_default; copy.is_scale_constant = is_scale_constant; copy.is_scale_default = is_scale_default; return copy; } }; } } ACL_IMPL_FILE_PRAGMA_POP