#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 convert.h #include "acl/compression/compress.h" #include "acl/compression/track_array.h" #include "acl/core/compressed_tracks.h" #include "acl/core/error_result.h" #include "acl/core/iallocator.h" #include "acl/core/track_formats.h" #include "acl/core/impl/debug_track_writer.h" #include "acl/decompression/decompress.h" #include namespace acl { namespace acl_impl { struct raw_sampling_decompression_settings final : public decompression_settings { // Disable normalization. This is only safe if we know the input data is already normalized! static constexpr bool normalize_rotations() { return false; } }; } inline error_result convert_track_list(iallocator& allocator, const track_array& track_list, compressed_tracks*& out_compressed_tracks) { compression_settings settings = get_raw_compression_settings(); qvvf_transform_error_metric error_metric; settings.error_metric = &error_metric; // Include all the metadata settings.include_track_list_name = true; settings.include_track_names = true; settings.include_parent_track_indices = true; settings.include_track_descriptions = true; output_stats stats; return compress_track_list(allocator, track_list, settings, out_compressed_tracks, stats); } inline error_result convert_track_list(iallocator& allocator, const compressed_tracks& tracks, track_array& out_track_list) { error_result error = tracks.is_valid(false); if (error.any()) return error; const uint32_t num_tracks = tracks.get_num_tracks(); const track_type8 track_type = tracks.get_track_type(); const uint32_t num_samples = tracks.get_num_samples_per_track(); const float sample_rate = tracks.get_sample_rate(); const float duration = tracks.get_duration(); track_array result(allocator, num_tracks); result.set_name(string(allocator, tracks.get_name())); // Setup our track metadata and allocate memory bool success = true; for (uint32_t track_index = 0; track_index < num_tracks; ++track_index) { track& track_ = result[track_index]; track_desc_scalarf desc_scalar; track_desc_transformf desc_transform; bool got_description = false; switch (track_type) { case track_type8::float1f: got_description = tracks.get_track_description(track_index, desc_scalar); track_ = track_float1f::make_reserve(desc_scalar, allocator, num_samples, sample_rate); break; case track_type8::float2f: got_description = tracks.get_track_description(track_index, desc_scalar); track_ = track_float2f::make_reserve(desc_scalar, allocator, num_samples, sample_rate); break; case track_type8::float3f: got_description = tracks.get_track_description(track_index, desc_scalar); track_ = track_float3f::make_reserve(desc_scalar, allocator, num_samples, sample_rate); break; case track_type8::float4f: got_description = tracks.get_track_description(track_index, desc_scalar); track_ = track_float4f::make_reserve(desc_scalar, allocator, num_samples, sample_rate); break; case track_type8::vector4f: got_description = tracks.get_track_description(track_index, desc_scalar); track_ = track_vector4f::make_reserve(desc_scalar, allocator, num_samples, sample_rate); break; case track_type8::qvvf: got_description = tracks.get_track_description(track_index, desc_transform); track_ = track_qvvf::make_reserve(desc_transform, allocator, num_samples, sample_rate); break; default: ACL_ASSERT(false, "Unexpected track type"); break; } success &= got_description; track_.set_name(string(allocator, tracks.get_track_name(track_index))); } if (!success) return error_result("Metadata was missing from the input"); // Disable floating point exceptions since decompression assumes it scope_disable_fp_exceptions fp_off; // Decompress and populate our track data const acl_impl::tracks_header& header = acl_impl::get_tracks_header(tracks); if (header.get_rotation_format() == rotation_format8::quatf_full && header.get_translation_format() == vector_format8::vector3f_full && header.get_scale_format() == vector_format8::vector3f_full) { // Our input data uses full precision, retain it decompression_context context; context.initialize(tracks); acl_impl::debug_track_writer writer(allocator, track_type, num_tracks); for (uint32_t sample_index = 0; sample_index < num_samples; ++sample_index) { const float sample_time = rtm::scalar_min(float(sample_index) / sample_rate, duration); // Round to nearest to land directly on a sample context.seek(sample_time, sample_rounding_policy::nearest); context.decompress_tracks(writer); for (uint32_t track_index = 0; track_index < num_tracks; ++track_index) { track& track_ = result[track_index]; switch (track_type) { case track_type8::float1f: *reinterpret_cast(track_[sample_index]) = writer.read_float1(track_index); break; case track_type8::float2f: rtm::vector_store2(writer.read_float2(track_index), reinterpret_cast(track_[sample_index])); break; case track_type8::float3f: rtm::vector_store3(writer.read_float3(track_index), reinterpret_cast(track_[sample_index])); break; case track_type8::float4f: rtm::vector_store(writer.read_float4(track_index), reinterpret_cast(track_[sample_index])); break; case track_type8::vector4f: *reinterpret_cast(track_[sample_index]) = writer.read_vector4(track_index); break; case track_type8::qvvf: *reinterpret_cast(track_[sample_index]) = writer.read_qvv(track_index); break; default: ACL_ASSERT(false, "Unexpected track type"); break; } } } } else { decompression_context context; context.initialize(tracks); acl_impl::debug_track_writer writer(allocator, track_type, num_tracks); for (uint32_t sample_index = 0; sample_index < num_samples; ++sample_index) { const float sample_time = rtm::scalar_min(float(sample_index) / sample_rate, duration); // Round to nearest to land directly on a sample context.seek(sample_time, sample_rounding_policy::nearest); context.decompress_tracks(writer); for (uint32_t track_index = 0; track_index < num_tracks; ++track_index) { track& track_ = result[track_index]; switch (track_type) { case track_type8::float1f: *reinterpret_cast(track_[sample_index]) = writer.read_float1(track_index); break; case track_type8::float2f: rtm::vector_store2(writer.read_float2(track_index), reinterpret_cast(track_[sample_index])); break; case track_type8::float3f: rtm::vector_store3(writer.read_float3(track_index), reinterpret_cast(track_[sample_index])); break; case track_type8::float4f: rtm::vector_store(writer.read_float4(track_index), reinterpret_cast(track_[sample_index])); break; case track_type8::vector4f: *reinterpret_cast(track_[sample_index]) = writer.read_vector4(track_index); break; case track_type8::qvvf: *reinterpret_cast(track_[sample_index]) = writer.read_qvv(track_index); break; default: ACL_ASSERT(false, "Unexpected track type"); break; } } } } out_track_list = std::move(result); return error_result(); } }