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

417 lines
16 KiB
C++

#pragma once
////////////////////////////////////////////////////////////////////////////////
// The MIT License (MIT)
//
// Copyright (c) 2020 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.
////////////////////////////////////////////////////////////////////////////////
// Included only once from track_array.h
#include "acl/core/error_result.h"
#include "acl/core/iallocator.h"
#include "acl/core/interpolation_utils.h"
#include "acl/core/track_types.h"
#include "acl/core/track_writer.h"
#include <cstdint>
namespace acl
{
inline track_array::track_array() noexcept
: m_allocator(nullptr)
, m_tracks(nullptr)
, m_num_tracks(0)
, m_name()
{}
inline track_array::track_array(iallocator& allocator, uint32_t num_tracks)
: m_allocator(&allocator)
, m_tracks(allocate_type_array<track>(allocator, num_tracks))
, m_num_tracks(num_tracks)
, m_name()
{}
inline track_array::track_array(track_array&& other) noexcept
: m_allocator(other.m_allocator)
, m_tracks(other.m_tracks)
, m_num_tracks(other.m_num_tracks)
, m_name(std::move(other.m_name))
{
other.m_allocator = nullptr; // Make sure we don't free our data since we no longer own it
}
inline track_array::~track_array()
{
if (m_allocator != nullptr)
deallocate_type_array(*m_allocator, m_tracks, m_num_tracks);
}
inline track_array& track_array::operator=(track_array&& other) noexcept
{
std::swap(m_allocator, other.m_allocator);
std::swap(m_tracks, other.m_tracks);
std::swap(m_num_tracks, other.m_num_tracks);
std::swap(m_name, other.m_name);
return *this;
}
inline track& track_array::operator[](uint32_t index)
{
ACL_ASSERT(index < m_num_tracks, "Invalid track index. %u >= %u", index, m_num_tracks);
return m_tracks[index];
}
inline const track& track_array::operator[](uint32_t index) const
{
ACL_ASSERT(index < m_num_tracks, "Invalid track index. %u >= %u", index, m_num_tracks);
return m_tracks[index];
}
inline error_result track_array::is_valid() const
{
const track_type8 type = get_track_type();
const uint32_t num_samples = get_num_samples_per_track();
const float sample_rate = get_sample_rate();
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track& track_ = m_tracks[track_index];
if (track_.get_type() != type)
return error_result("Tracks must all have the same type within an array");
if (track_.get_num_samples() != num_samples)
return error_result("Track array requires the same number of samples in every track");
if (track_.get_sample_rate() != sample_rate)
return error_result("Track array requires the same sample rate in every track");
const error_result result = track_.is_valid();
if (result.any())
return result;
if (track_.get_category() == track_category8::transformf)
{
const track_desc_transformf& desc = track_.get_description<track_desc_transformf>();
if (desc.parent_index != k_invalid_track_index && desc.parent_index >= m_num_tracks)
return error_result("Invalid parent_index. It must be 'k_invalid_track_index' or a valid track index");
}
}
// Validate output indices
uint32_t num_outputs = 0;
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track& track_ = m_tracks[track_index];
const uint32_t output_index = track_.get_output_index();
if (output_index != k_invalid_track_index && output_index >= m_num_tracks)
return error_result("The output_index must be 'k_invalid_track_index' or less than the number of bones");
if (output_index != k_invalid_track_index)
{
for (uint32_t track_index2 = track_index + 1; track_index2 < m_num_tracks; ++track_index2)
{
const track& track2_ = m_tracks[track_index2];
const uint32_t output_index2 = track2_.get_output_index();
if (output_index == output_index2)
return error_result("Duplicate output_index found");
}
num_outputs++;
}
}
for (uint32_t output_index = 0; output_index < num_outputs; ++output_index)
{
bool found = false;
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track& track_ = m_tracks[track_index];
const uint32_t output_index_ = track_.get_output_index();
if (output_index == output_index_)
{
found = true;
break;
}
}
if (!found)
return error_result("Output indices are not contiguous");
}
return error_result();
}
template<class track_writer_type>
inline void track_array::sample_tracks(float sample_time, sample_rounding_policy rounding_policy, track_writer_type& writer) const
{
static_assert(std::is_base_of<track_writer, track_writer_type>::value, "track_writer_type must derive from track_writer");
ACL_ASSERT(is_valid().empty(), "Invalid track array");
const uint32_t num_samples = get_num_samples_per_track();
const float sample_rate = get_sample_rate();
const track_type8 track_type = get_track_type();
// Clamp for safety, the caller should normally handle this but in practice, it often isn't the case
const float duration = get_duration();
sample_time = rtm::scalar_clamp(sample_time, 0.0F, duration);
uint32_t key_frame0;
uint32_t key_frame1;
float interpolation_alpha;
find_linear_interpolation_samples_with_sample_rate(num_samples, sample_rate, sample_time, rounding_policy, key_frame0, key_frame1, interpolation_alpha);
switch (track_type)
{
case track_type8::float1f:
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track_float1f& track__ = track_cast<track_float1f>(m_tracks[track_index]);
const rtm::scalarf value0 = rtm::scalar_load(&track__[key_frame0]);
const rtm::scalarf value1 = rtm::scalar_load(&track__[key_frame1]);
const rtm::scalarf value = rtm::scalar_lerp(value0, value1, rtm::scalar_set(interpolation_alpha));
writer.write_float1(track_index, value);
}
break;
case track_type8::float2f:
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track_float2f& track__ = track_cast<track_float2f>(m_tracks[track_index]);
const rtm::vector4f value0 = rtm::vector_load2(&track__[key_frame0]);
const rtm::vector4f value1 = rtm::vector_load2(&track__[key_frame1]);
const rtm::vector4f value = rtm::vector_lerp(value0, value1, interpolation_alpha);
writer.write_float2(track_index, value);
}
break;
case track_type8::float3f:
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track_float3f& track__ = track_cast<track_float3f>(m_tracks[track_index]);
const rtm::vector4f value0 = rtm::vector_load3(&track__[key_frame0]);
const rtm::vector4f value1 = rtm::vector_load3(&track__[key_frame1]);
const rtm::vector4f value = rtm::vector_lerp(value0, value1, interpolation_alpha);
writer.write_float3(track_index, value);
}
break;
case track_type8::float4f:
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track_float4f& track__ = track_cast<track_float4f>(m_tracks[track_index]);
const rtm::vector4f value0 = rtm::vector_load(&track__[key_frame0]);
const rtm::vector4f value1 = rtm::vector_load(&track__[key_frame1]);
const rtm::vector4f value = rtm::vector_lerp(value0, value1, interpolation_alpha);
writer.write_float4(track_index, value);
}
break;
case track_type8::vector4f:
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track_vector4f& track__ = track_cast<track_vector4f>(m_tracks[track_index]);
const rtm::vector4f value0 = track__[key_frame0];
const rtm::vector4f value1 = track__[key_frame1];
const rtm::vector4f value = rtm::vector_lerp(value0, value1, interpolation_alpha);
writer.write_vector4(track_index, value);
}
break;
case track_type8::qvvf:
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track_qvvf& track__ = track_cast<track_qvvf>(m_tracks[track_index]);
const rtm::qvvf& value0 = track__[key_frame0];
const rtm::qvvf& value1 = track__[key_frame1];
const rtm::quatf rotation = rtm::quat_lerp(value0.rotation, value1.rotation, interpolation_alpha);
const rtm::vector4f translation = rtm::vector_lerp(value0.translation, value1.translation, interpolation_alpha);
const rtm::vector4f scale = rtm::vector_lerp(value0.scale, value1.scale, interpolation_alpha);
writer.write_rotation(track_index, rotation);
writer.write_translation(track_index, translation);
writer.write_scale(track_index, scale);
}
break;
default:
ACL_ASSERT(false, "Invalid track type");
break;
}
}
template<class track_writer_type>
inline void track_array::sample_track(uint32_t track_index, float sample_time, sample_rounding_policy rounding_policy, track_writer_type& writer) const
{
static_assert(std::is_base_of<track_writer, track_writer_type>::value, "track_writer_type must derive from track_writer");
ACL_ASSERT(is_valid().empty(), "Invalid track array");
ACL_ASSERT(track_index < m_num_tracks, "Invalid track index");
const track& track_ = m_tracks[track_index];
const uint32_t num_samples = track_.get_num_samples();
const float sample_rate = track_.get_sample_rate();
// Clamp for safety, the caller should normally handle this but in practice, it often isn't the case
const float duration = calculate_duration(num_samples, sample_rate);
sample_time = rtm::scalar_clamp(sample_time, 0.0F, duration);
uint32_t key_frame0;
uint32_t key_frame1;
float interpolation_alpha;
find_linear_interpolation_samples_with_sample_rate(num_samples, sample_rate, sample_time, rounding_policy, key_frame0, key_frame1, interpolation_alpha);
switch (track_.get_type())
{
case track_type8::float1f:
{
const track_float1f& track__ = track_cast<track_float1f>(track_);
const rtm::scalarf value0 = rtm::scalar_load(&track__[key_frame0]);
const rtm::scalarf value1 = rtm::scalar_load(&track__[key_frame1]);
const rtm::scalarf value = rtm::scalar_lerp(value0, value1, rtm::scalar_set(interpolation_alpha));
writer.write_float1(track_index, value);
break;
}
case track_type8::float2f:
{
const track_float2f& track__ = track_cast<track_float2f>(track_);
const rtm::vector4f value0 = rtm::vector_load2(&track__[key_frame0]);
const rtm::vector4f value1 = rtm::vector_load2(&track__[key_frame1]);
const rtm::vector4f value = rtm::vector_lerp(value0, value1, interpolation_alpha);
writer.write_float2(track_index, value);
break;
}
case track_type8::float3f:
{
const track_float3f& track__ = track_cast<track_float3f>(track_);
const rtm::vector4f value0 = rtm::vector_load3(&track__[key_frame0]);
const rtm::vector4f value1 = rtm::vector_load3(&track__[key_frame1]);
const rtm::vector4f value = rtm::vector_lerp(value0, value1, interpolation_alpha);
writer.write_float3(track_index, value);
break;
}
case track_type8::float4f:
{
const track_float4f& track__ = track_cast<track_float4f>(track_);
const rtm::vector4f value0 = rtm::vector_load(&track__[key_frame0]);
const rtm::vector4f value1 = rtm::vector_load(&track__[key_frame1]);
const rtm::vector4f value = rtm::vector_lerp(value0, value1, interpolation_alpha);
writer.write_float4(track_index, value);
break;
}
case track_type8::vector4f:
{
const track_vector4f& track__ = track_cast<track_vector4f>(track_);
const rtm::vector4f value0 = track__[key_frame0];
const rtm::vector4f value1 = track__[key_frame1];
const rtm::vector4f value = rtm::vector_lerp(value0, value1, interpolation_alpha);
writer.write_vector4(track_index, value);
break;
}
case track_type8::qvvf:
{
const track_qvvf& track__ = track_cast<track_qvvf>(track_);
const rtm::qvvf& value0 = track__[key_frame0];
const rtm::qvvf& value1 = track__[key_frame1];
const rtm::quatf rotation = rtm::quat_lerp(value0.rotation, value1.rotation, interpolation_alpha);
const rtm::vector4f translation = rtm::vector_lerp(value0.translation, value1.translation, interpolation_alpha);
const rtm::vector4f scale = rtm::vector_lerp(value0.scale, value1.scale, interpolation_alpha);
writer.write_rotation(track_index, rotation);
writer.write_translation(track_index, translation);
writer.write_scale(track_index, scale);
break;
}
default:
ACL_ASSERT(false, "Invalid track type");
break;
}
}
inline uint32_t track_array::get_raw_size() const
{
const uint32_t num_samples = get_num_samples_per_track();
const track_type8 track_type = get_track_type();
uint32_t total_size = 0;
for (uint32_t track_index = 0; track_index < m_num_tracks; ++track_index)
{
const track& track_ = m_tracks[track_index];
if (track_type == track_type8::qvvf)
total_size += num_samples * 10 * sizeof(float); // 4 rotation floats, 3 translation floats, 3 scale floats
else
total_size += num_samples * track_.get_sample_size();
}
return total_size;
}
template<track_type8 track_type_>
inline typename track_array_typed<track_type_>::track_member_type& track_array_typed<track_type_>::operator[](uint32_t index)
{
ACL_ASSERT(index < m_num_tracks, "Invalid track index. %u >= %u", index, m_num_tracks);
return track_cast<track_member_type>(m_tracks[index]);
}
template<track_type8 track_type_>
inline const typename track_array_typed<track_type_>::track_member_type& track_array_typed<track_type_>::operator[](uint32_t index) const
{
ACL_ASSERT(index < m_num_tracks, "Invalid track index. %u >= %u", index, m_num_tracks);
return track_cast<track_member_type>(m_tracks[index]);
}
template<typename track_array_type>
inline track_array_type& track_array_cast(track_array& track_array_)
{
ACL_ASSERT(track_array_type::type == track_array_.get_track_type() || track_array_.is_empty(), "Unexpected track type");
return static_cast<track_array_type&>(track_array_);
}
template<typename track_array_type>
inline const track_array_type& track_array_cast(const track_array& track_array_)
{
ACL_ASSERT(track_array_type::type == track_array_.get_track_type() || track_array_.is_empty(), "Unexpected track type");
return static_cast<const track_array_type&>(track_array_);
}
template<typename track_array_type>
inline track_array_type* track_array_cast(track_array* track_array_)
{
if (track_array_ == nullptr || (track_array_type::type != track_array_->get_track_type() && !track_array_->is_empty()))
return nullptr;
return static_cast<track_array_type*>(track_array_);
}
template<typename track_array_type>
inline const track_array_type* track_array_cast(const track_array* track_array_)
{
if (track_array_ == nullptr || (track_array_type::type != track_array_->get_track_type() && !track_array_->is_empty()))
return nullptr;
return static_cast<const track_array_type*>(track_array_);
}
}