1379 lines
39 KiB
C++
1379 lines
39 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.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if defined(SJSON_CPP_PARSER)
|
|
|
|
#include "acl/io/clip_reader_error.h"
|
|
#include "acl/compression/compression_settings.h"
|
|
#include "acl/compression/track_array.h"
|
|
#include "acl/core/algorithm_types.h"
|
|
#include "acl/core/impl/compiler_utils.h"
|
|
#include "acl/core/iallocator.h"
|
|
#include "acl/core/string.h"
|
|
#include "acl/core/track_desc.h"
|
|
#include "acl/core/unique_ptr.h"
|
|
#include "acl/math/quatf.h"
|
|
|
|
#include <rtm/quatd.h>
|
|
#include <rtm/quatf.h>
|
|
#include <rtm/vector4d.h>
|
|
#include <rtm/vector4f.h>
|
|
#include <rtm/qvvd.h>
|
|
#include <rtm/qvvf.h>
|
|
|
|
#include <cstdint>
|
|
|
|
ACL_IMPL_FILE_PRAGMA_PUSH
|
|
|
|
namespace acl
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Enum to describe each type of raw content that an SJSON ACL file might contain.
|
|
enum class sjson_file_type
|
|
{
|
|
unknown,
|
|
raw_clip,
|
|
raw_track_list,
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Raw transform tracks
|
|
struct sjson_raw_clip
|
|
{
|
|
track_array_qvvf track_list;
|
|
|
|
track_array_qvvf additive_base_track_list;
|
|
additive_clip_format8 additive_format;
|
|
|
|
track_qvvf bind_pose;
|
|
|
|
bool has_settings;
|
|
compression_settings settings;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Raw scalar tracks
|
|
struct sjson_raw_track_list
|
|
{
|
|
track_array track_list;
|
|
|
|
track_qvvf bind_pose;
|
|
|
|
bool has_settings;
|
|
compression_settings settings;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// An SJSON ACL file reader.
|
|
class clip_reader
|
|
{
|
|
public:
|
|
clip_reader(iallocator& allocator, const char* sjson_input, size_t input_length)
|
|
: m_allocator(allocator)
|
|
, m_parser(sjson_input, input_length)
|
|
, m_error()
|
|
, m_version(0)
|
|
, m_num_samples(0)
|
|
, m_sample_rate(0.0F)
|
|
, m_is_binary_exact(false)
|
|
, m_bone_names(nullptr)
|
|
, m_num_bones(0)
|
|
{
|
|
}
|
|
|
|
~clip_reader()
|
|
{
|
|
deallocate_type_array(m_allocator, m_bone_names, m_num_bones);
|
|
}
|
|
|
|
sjson_file_type get_file_type()
|
|
{
|
|
reset_state();
|
|
|
|
if (!read_version())
|
|
return sjson_file_type::unknown;
|
|
|
|
if (m_parser.object_begins("clip"))
|
|
return sjson_file_type::raw_clip;
|
|
|
|
if (m_parser.object_begins("track_list"))
|
|
return sjson_file_type::raw_track_list;
|
|
|
|
return sjson_file_type::unknown;
|
|
}
|
|
|
|
bool read_raw_clip(sjson_raw_clip& out_data)
|
|
{
|
|
reset_state();
|
|
|
|
if (!read_version())
|
|
return false;
|
|
|
|
if (!read_raw_clip_header())
|
|
return false;
|
|
|
|
if (!read_settings(&out_data.has_settings, &out_data.settings))
|
|
return false;
|
|
|
|
if (!create_skeleton(out_data.track_list, out_data.bind_pose))
|
|
return false;
|
|
|
|
if (!read_tracks(out_data.track_list, out_data.additive_base_track_list))
|
|
return false;
|
|
|
|
out_data.additive_format = !out_data.additive_base_track_list.is_empty() ? m_additive_format : additive_clip_format8::none;
|
|
|
|
return nothing_follows();
|
|
}
|
|
|
|
bool read_raw_track_list(sjson_raw_track_list& out_data)
|
|
{
|
|
reset_state();
|
|
|
|
if (!read_version())
|
|
return false;
|
|
|
|
if (!read_raw_track_list_header())
|
|
return false;
|
|
|
|
if (!read_settings(&out_data.has_settings, &out_data.settings))
|
|
return false;
|
|
|
|
if (!create_track_list(out_data.track_list, out_data.bind_pose))
|
|
return false;
|
|
|
|
return nothing_follows();
|
|
}
|
|
|
|
clip_reader_error get_error() const { return m_error; }
|
|
|
|
private:
|
|
clip_reader(const clip_reader&) = delete;
|
|
clip_reader(clip_reader&&) = delete;
|
|
clip_reader& operator=(const clip_reader&) = delete;
|
|
clip_reader& operator=(clip_reader&&) = delete;
|
|
|
|
iallocator& m_allocator;
|
|
sjson::Parser m_parser;
|
|
clip_reader_error m_error;
|
|
|
|
uint32_t m_version;
|
|
uint32_t m_num_samples;
|
|
float m_sample_rate;
|
|
sjson::StringView m_clip_name;
|
|
bool m_is_binary_exact;
|
|
additive_clip_format8 m_additive_format;
|
|
sjson::StringView m_additive_base_name;
|
|
uint32_t m_additive_base_num_samples;
|
|
float m_additive_base_sample_rate;
|
|
|
|
bool m_has_settings;
|
|
float m_constant_rotation_threshold_angle;
|
|
float m_constant_translation_threshold;
|
|
float m_constant_scale_threshold;
|
|
float m_error_threshold;
|
|
|
|
sjson::StringView* m_bone_names;
|
|
uint32_t m_num_bones;
|
|
|
|
string convert_string(const sjson::StringView& str) const
|
|
{
|
|
return string(m_allocator, str.c_str(), str.size());
|
|
}
|
|
|
|
void reset_state()
|
|
{
|
|
m_parser.reset_state();
|
|
set_error(clip_reader_error::None);
|
|
}
|
|
|
|
bool read_version()
|
|
{
|
|
if (!m_parser.read("version", m_version))
|
|
{
|
|
m_error = m_parser.get_error();
|
|
return false;
|
|
}
|
|
|
|
if (m_version > 5)
|
|
{
|
|
set_error(clip_reader_error::UnsupportedVersion);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool read_raw_clip_header()
|
|
{
|
|
sjson::StringView additive_format;
|
|
|
|
if (!m_parser.object_begins("clip"))
|
|
goto error;
|
|
|
|
m_parser.try_read("name", m_clip_name, "");
|
|
|
|
double num_samples;
|
|
if (!m_parser.read("num_samples", num_samples))
|
|
goto error;
|
|
|
|
m_num_samples = static_cast<uint32_t>(num_samples);
|
|
if (static_cast<double>(m_num_samples) != num_samples)
|
|
{
|
|
set_error(clip_reader_error::UnsignedIntegerExpected);
|
|
return false;
|
|
}
|
|
|
|
double sample_rate;
|
|
if (!m_parser.read("sample_rate", sample_rate))
|
|
goto error;
|
|
|
|
m_sample_rate = static_cast<float>(sample_rate);
|
|
if (m_sample_rate <= 0.0F)
|
|
{
|
|
set_error(clip_reader_error::PositiveValueExpected);
|
|
return false;
|
|
}
|
|
|
|
// Version 1 had an error_threshold field, skip it
|
|
double error_threshold;
|
|
if (m_version == 1 && !m_parser.read("error_threshold", error_threshold))
|
|
goto error;
|
|
|
|
// Optional value
|
|
m_parser.try_read("is_binary_exact", m_is_binary_exact, false);
|
|
|
|
// Optional value
|
|
m_parser.try_read("additive_format", additive_format, "none");
|
|
if (!get_additive_clip_format(additive_format.c_str(), m_additive_format))
|
|
{
|
|
set_error(clip_reader_error::InvalidAdditiveClipFormat);
|
|
return false;
|
|
}
|
|
|
|
m_parser.try_read("additive_base_name", m_additive_base_name, "");
|
|
m_parser.try_read("additive_base_num_samples", num_samples, 1.0);
|
|
m_additive_base_num_samples = static_cast<uint32_t>(num_samples);
|
|
if (static_cast<double>(m_additive_base_num_samples) != num_samples || m_additive_base_num_samples == 0)
|
|
{
|
|
set_error(clip_reader_error::UnsignedIntegerExpected);
|
|
return false;
|
|
}
|
|
m_parser.try_read("additive_base_sample_rate", sample_rate, 30.0);
|
|
m_additive_base_sample_rate = static_cast<float>(sample_rate);
|
|
if (m_additive_base_sample_rate <= 0.0F)
|
|
{
|
|
set_error(clip_reader_error::PositiveValueExpected);
|
|
return false;
|
|
}
|
|
|
|
if (!m_parser.object_ends())
|
|
goto error;
|
|
|
|
return true;
|
|
|
|
error:
|
|
m_error = m_parser.get_error();
|
|
return false;
|
|
}
|
|
|
|
bool read_raw_track_list_header()
|
|
{
|
|
if (!m_parser.object_begins("track_list"))
|
|
goto error;
|
|
|
|
m_parser.try_read("name", m_clip_name, "");
|
|
|
|
double num_samples;
|
|
if (!m_parser.read("num_samples", num_samples))
|
|
goto error;
|
|
|
|
m_num_samples = static_cast<uint32_t>(num_samples);
|
|
if (static_cast<double>(m_num_samples) != num_samples)
|
|
{
|
|
set_error(clip_reader_error::UnsignedIntegerExpected);
|
|
return false;
|
|
}
|
|
|
|
double sample_rate;
|
|
if (!m_parser.read("sample_rate", sample_rate))
|
|
goto error;
|
|
|
|
m_sample_rate = static_cast<float>(sample_rate);
|
|
if (m_sample_rate <= 0.0F)
|
|
{
|
|
set_error(clip_reader_error::PositiveValueExpected);
|
|
return false;
|
|
}
|
|
|
|
// Optional value
|
|
m_parser.try_read("is_binary_exact", m_is_binary_exact, false);
|
|
|
|
if (!m_parser.object_ends())
|
|
goto error;
|
|
|
|
return true;
|
|
|
|
error:
|
|
m_error = m_parser.get_error();
|
|
return false;
|
|
}
|
|
|
|
bool read_settings(bool* out_has_settings, compression_settings* out_settings)
|
|
{
|
|
m_has_settings = false;
|
|
|
|
if (!m_parser.try_object_begins("settings"))
|
|
{
|
|
if (out_has_settings != nullptr)
|
|
*out_has_settings = false;
|
|
|
|
// Settings are optional, all good
|
|
return true;
|
|
}
|
|
|
|
compression_settings default_settings;
|
|
|
|
sjson::StringView algorithm_name;
|
|
sjson::StringView compression_level;
|
|
sjson::StringView rotation_format;
|
|
sjson::StringView translation_format;
|
|
sjson::StringView scale_format;
|
|
bool rotation_range_reduction;
|
|
bool translation_range_reduction;
|
|
bool scale_range_reduction;
|
|
double constant_rotation_threshold_angle;
|
|
double constant_translation_threshold;
|
|
double constant_scale_threshold;
|
|
double error_threshold;
|
|
|
|
double segmenting_ideal_num_samples = double(default_settings.segmenting.ideal_num_samples);
|
|
double segmenting_max_num_samples = double(default_settings.segmenting.max_num_samples);
|
|
|
|
m_parser.try_read("algorithm_name", algorithm_name, get_algorithm_name(algorithm_type8::uniformly_sampled));
|
|
m_parser.try_read("level", compression_level, get_compression_level_name(default_settings.level));
|
|
m_parser.try_read("rotation_format", rotation_format, get_rotation_format_name(default_settings.rotation_format));
|
|
m_parser.try_read("translation_format", translation_format, get_vector_format_name(default_settings.translation_format));
|
|
m_parser.try_read("scale_format", scale_format, get_vector_format_name(default_settings.scale_format));
|
|
m_parser.try_read("rotation_range_reduction", rotation_range_reduction, false); // Legacy, no longer used
|
|
m_parser.try_read("translation_range_reduction", translation_range_reduction, false); // Legacy, no longer used
|
|
m_parser.try_read("scale_range_reduction", scale_range_reduction, false); // Legacy, no longer used
|
|
|
|
if (m_parser.try_object_begins("segmenting"))
|
|
{
|
|
bool segmenting_enabled;
|
|
bool segmenting_rotation_range_reduction;
|
|
bool segmenting_translation_range_reduction;
|
|
bool segmenting_scale_range_reduction;
|
|
m_parser.try_read("enabled", segmenting_enabled, false); // Legacy, no longer used
|
|
m_parser.try_read("ideal_num_samples", segmenting_ideal_num_samples, double(default_settings.segmenting.ideal_num_samples));
|
|
m_parser.try_read("max_num_samples", segmenting_max_num_samples, double(default_settings.segmenting.max_num_samples));
|
|
m_parser.try_read("rotation_range_reduction", segmenting_rotation_range_reduction, false); // Legacy, no longer used
|
|
m_parser.try_read("translation_range_reduction", segmenting_translation_range_reduction, false); // Legacy, no longer used
|
|
m_parser.try_read("scale_range_reduction", segmenting_scale_range_reduction, false); // Legacy, no longer used
|
|
|
|
if (!m_parser.is_valid() || !m_parser.object_ends())
|
|
goto parsing_error;
|
|
}
|
|
|
|
// Skip deprecated values
|
|
m_parser.try_read("constant_rotation_threshold_angle", constant_rotation_threshold_angle, 0.00284714461);
|
|
m_parser.try_read("constant_translation_threshold", constant_translation_threshold, 0.001);
|
|
m_parser.try_read("constant_scale_threshold", constant_scale_threshold, 0.00001);
|
|
m_parser.try_read("error_threshold", error_threshold, 0.01);
|
|
|
|
if (!m_parser.is_valid() || !m_parser.object_ends())
|
|
goto parsing_error;
|
|
|
|
if (out_has_settings != nullptr)
|
|
{
|
|
*out_has_settings = true;
|
|
|
|
if (!get_compression_level(compression_level.c_str(), out_settings->level))
|
|
goto invalid_value_error;
|
|
|
|
if (!get_rotation_format(rotation_format.c_str(), out_settings->rotation_format))
|
|
goto invalid_value_error;
|
|
|
|
if (!get_vector_format(translation_format.c_str(), out_settings->translation_format))
|
|
goto invalid_value_error;
|
|
|
|
if (!get_vector_format(scale_format.c_str(), out_settings->scale_format))
|
|
goto invalid_value_error;
|
|
|
|
out_settings->segmenting.ideal_num_samples = uint32_t(segmenting_ideal_num_samples);
|
|
out_settings->segmenting.max_num_samples = uint32_t(segmenting_max_num_samples);
|
|
|
|
m_constant_rotation_threshold_angle = float(constant_rotation_threshold_angle);
|
|
m_constant_translation_threshold = float(constant_translation_threshold);
|
|
m_constant_scale_threshold = float(constant_scale_threshold);
|
|
m_error_threshold = float(error_threshold);
|
|
}
|
|
|
|
m_has_settings = true;
|
|
return true;
|
|
|
|
parsing_error:
|
|
m_error = m_parser.get_error();
|
|
return false;
|
|
|
|
invalid_value_error:
|
|
m_parser.get_position(m_error.line, m_error.column);
|
|
m_error.error = clip_reader_error::InvalidCompressionSetting;
|
|
return false;
|
|
}
|
|
|
|
bool create_skeleton(track_array_qvvf& track_list, track_qvvf& bind_pose)
|
|
{
|
|
sjson::ParserState before_bones = m_parser.save_state();
|
|
|
|
uint32_t num_bones;
|
|
if (!process_each_bone(nullptr, nullptr, num_bones))
|
|
return false;
|
|
|
|
m_parser.restore_state(before_bones);
|
|
|
|
m_num_bones = num_bones;
|
|
m_bone_names = allocate_type_array<sjson::StringView>(m_allocator, num_bones);
|
|
|
|
track_list = track_array_qvvf(m_allocator, num_bones);
|
|
track_list.set_name(convert_string(m_clip_name));
|
|
bind_pose = track_qvvf::make_reserve(track_desc_transformf{}, m_allocator, num_bones, 30.0F); // 1 sample per track
|
|
bind_pose.set_name(string(m_allocator, "bind pose"));
|
|
|
|
const uint32_t num_allocated_bones = num_bones;
|
|
|
|
if (!process_each_bone(&track_list, &bind_pose, num_bones))
|
|
return false;
|
|
|
|
(void)num_allocated_bones;
|
|
ACL_ASSERT(num_bones == num_allocated_bones, "Number of bones read mismatch");
|
|
|
|
return true;
|
|
}
|
|
|
|
static double hex_to_double(const sjson::StringView& value)
|
|
{
|
|
union UInt64ToDouble
|
|
{
|
|
uint64_t u64;
|
|
double dbl;
|
|
|
|
constexpr explicit UInt64ToDouble(uint64_t u64_value) : u64(u64_value) {}
|
|
};
|
|
|
|
ACL_ASSERT(value.size() <= 16, "Invalid binary exact double value");
|
|
uint64_t value_u64 = acl_impl::strtoull(value.c_str(), nullptr, 16);
|
|
return UInt64ToDouble(value_u64).dbl;
|
|
}
|
|
|
|
static float hex_to_float(const sjson::StringView& value)
|
|
{
|
|
union UInt32ToFloat
|
|
{
|
|
uint32_t u32;
|
|
float flt;
|
|
|
|
constexpr explicit UInt32ToFloat(uint32_t u32_value) : u32(u32_value) {}
|
|
};
|
|
|
|
ACL_ASSERT(value.size() <= 8, "Invalid binary exact float value");
|
|
uint32_t value_u32 = safe_static_cast<uint32_t>(std::strtoul(value.c_str(), nullptr, 16));
|
|
return UInt32ToFloat(value_u32).flt;
|
|
}
|
|
|
|
static rtm::quatd hex_to_quat(const sjson::StringView values[4])
|
|
{
|
|
return rtm::quat_set(hex_to_double(values[0]), hex_to_double(values[1]), hex_to_double(values[2]), hex_to_double(values[3]));
|
|
}
|
|
|
|
static rtm::vector4d hex_to_vector3(const sjson::StringView values[3])
|
|
{
|
|
return rtm::vector_set(hex_to_double(values[0]), hex_to_double(values[1]), hex_to_double(values[2]));
|
|
}
|
|
|
|
static rtm::float4f hex_to_float4f(const sjson::StringView values[4], uint32_t num_components)
|
|
{
|
|
ACL_ASSERT(num_components <= 4, "Invalid number of components");
|
|
|
|
rtm::float4f result = { 0.0F, 0.0F, 0.0F, 0.0F };
|
|
float* result_ptr = &result.x;
|
|
|
|
for (uint32_t component_index = 0; component_index < num_components; ++component_index)
|
|
result_ptr[component_index] = hex_to_float(values[component_index]);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool process_each_bone(track_array_qvvf* tracks, track_qvvf* bind_pose, uint32_t& num_bones)
|
|
{
|
|
bool counting = tracks == nullptr;
|
|
num_bones = 0;
|
|
|
|
if (!m_parser.array_begins("bones"))
|
|
goto error;
|
|
|
|
for (uint32_t i = 0; !m_parser.try_array_ends(); ++i)
|
|
{
|
|
if (!m_parser.object_begins())
|
|
goto error;
|
|
|
|
sjson::StringView name;
|
|
if (!m_parser.read("name", name))
|
|
goto error;
|
|
|
|
if (tracks != nullptr)
|
|
m_bone_names[i] = name;
|
|
|
|
sjson::StringView parent;
|
|
if (!m_parser.read("parent", parent))
|
|
goto error;
|
|
|
|
uint32_t parent_index = k_invalid_track_index;
|
|
if (tracks != nullptr && parent.size() != 0)
|
|
{
|
|
parent_index = find_bone(parent);
|
|
if (parent_index == k_invalid_track_index)
|
|
{
|
|
set_error(clip_reader_error::NoParentBoneWithThatName);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
float vertex_distance;
|
|
if (!m_parser.read("vertex_distance", vertex_distance))
|
|
goto error;
|
|
|
|
rtm::qvvd bind_transform = rtm::qvv_identity();
|
|
if (m_is_binary_exact)
|
|
{
|
|
sjson::StringView rotation[4];
|
|
if (m_parser.try_read("bind_rotation", rotation, 4, nullptr) && !counting)
|
|
bind_transform.rotation = hex_to_quat(rotation);
|
|
|
|
sjson::StringView translation[3];
|
|
if (m_parser.try_read("bind_translation", translation, 3, nullptr) && !counting)
|
|
bind_transform.translation = hex_to_vector3(translation);
|
|
|
|
sjson::StringView scale[3];
|
|
if (m_parser.try_read("bind_scale", scale, 3, nullptr) && !counting)
|
|
bind_transform.scale = hex_to_vector3(scale);
|
|
}
|
|
else
|
|
{
|
|
double rotation[4] = { 0.0, 0.0, 0.0, 0.0 };
|
|
if (m_parser.try_read("bind_rotation", rotation, 4, 0.0) && !counting)
|
|
bind_transform.rotation = rtm::quat_load(&rotation[0]);
|
|
|
|
double translation[3] = { 0.0, 0.0, 0.0 };
|
|
if (m_parser.try_read("bind_translation", translation, 3, 0.0) && !counting)
|
|
bind_transform.translation = rtm::vector_load3(&translation[0]);
|
|
|
|
double scale[3] = { 0.0, 0.0, 0.0 };
|
|
if (m_parser.try_read("bind_scale", scale, 3, 0.0) && !counting)
|
|
bind_transform.scale = rtm::vector_load3(&scale[0]);
|
|
}
|
|
|
|
if (tracks != nullptr)
|
|
{
|
|
track_desc_transformf desc;
|
|
desc.parent_index = parent_index;
|
|
desc.shell_distance = vertex_distance;
|
|
|
|
if (m_has_settings)
|
|
{
|
|
desc.precision = m_error_threshold;
|
|
desc.constant_rotation_threshold_angle = m_constant_rotation_threshold_angle;
|
|
desc.constant_translation_threshold = m_constant_translation_threshold;
|
|
desc.constant_scale_threshold = m_constant_scale_threshold;
|
|
}
|
|
|
|
// Create a dummy track for now to hold our arguments
|
|
(*tracks)[i] = track_qvvf::make_ref(desc, nullptr, 0, 30.0F);
|
|
(*tracks)[i].set_name(convert_string(name));
|
|
|
|
rtm::qvvf bind_transform_ = rtm::qvv_cast(bind_transform);
|
|
bind_transform_.rotation = rtm::quat_normalize(bind_transform_.rotation);
|
|
|
|
(*bind_pose)[i] = bind_transform_;
|
|
}
|
|
|
|
if (!m_parser.object_ends())
|
|
goto error;
|
|
|
|
++num_bones;
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
m_error = m_parser.get_error();
|
|
return false;
|
|
}
|
|
|
|
uint32_t find_bone(const sjson::StringView& name) const
|
|
{
|
|
for (uint32_t i = 0; i < m_num_bones; ++i)
|
|
{
|
|
if (name == m_bone_names[i])
|
|
return i;
|
|
}
|
|
|
|
return k_invalid_track_index;
|
|
}
|
|
|
|
float read_optional_float(const char* key, float default_value)
|
|
{
|
|
float value = default_value;
|
|
if (m_is_binary_exact)
|
|
{
|
|
sjson::StringView value_str;
|
|
if (m_parser.try_read(key, value_str, ""))
|
|
value = hex_to_float(value_str);
|
|
}
|
|
else
|
|
m_parser.try_read(key, value, default_value);
|
|
|
|
return value;
|
|
}
|
|
|
|
bool read_qvv_sample(rtm::qvvf& out_sample)
|
|
{
|
|
rtm::qvvf sample = rtm::qvv_identity();
|
|
sjson::StringView values_str[4];
|
|
double values[4] = { 0.0, 0.0, 0.0, 0.0 };
|
|
|
|
{
|
|
if (!m_parser.array_begins())
|
|
return false;
|
|
|
|
rtm::float4f value;
|
|
if (m_is_binary_exact)
|
|
{
|
|
if (!m_parser.read(values_str, 4))
|
|
return false;
|
|
|
|
value = hex_to_float4f(values_str, 4);
|
|
}
|
|
else
|
|
{
|
|
if (!m_parser.read(values, 4))
|
|
return false;
|
|
|
|
value = { static_cast<float>(values[0]), static_cast<float>(values[1]), static_cast<float>(values[2]), static_cast<float>(values[3])};
|
|
}
|
|
|
|
sample.rotation = rtm::quat_load(&value);
|
|
if (!rtm::quat_is_finite(sample.rotation))
|
|
return false;
|
|
|
|
if (!m_parser.array_ends())
|
|
return false;
|
|
}
|
|
|
|
if (!m_parser.read_comma())
|
|
return false;
|
|
|
|
{
|
|
if (!m_parser.array_begins())
|
|
return false;
|
|
|
|
rtm::float4f value;
|
|
if (m_is_binary_exact)
|
|
{
|
|
if (!m_parser.read(values_str, 3))
|
|
return false;
|
|
|
|
value = hex_to_float4f(values_str, 4);
|
|
}
|
|
else
|
|
{
|
|
if (!m_parser.read(values, 3))
|
|
return false;
|
|
|
|
value = { static_cast<float>(values[0]), static_cast<float>(values[1]), static_cast<float>(values[2]), 0.0F };
|
|
}
|
|
|
|
sample.translation = vector_load(&value);
|
|
if (!rtm::vector_is_finite3(sample.translation))
|
|
return false;
|
|
|
|
if (!m_parser.array_ends())
|
|
return false;
|
|
}
|
|
|
|
if (!m_parser.read_comma())
|
|
return false;
|
|
|
|
{
|
|
if (!m_parser.array_begins())
|
|
return false;
|
|
|
|
rtm::float4f value;
|
|
if (m_is_binary_exact)
|
|
{
|
|
if (!m_parser.read(values_str, 3))
|
|
return false;
|
|
|
|
value = hex_to_float4f(values_str, 4);
|
|
}
|
|
else
|
|
{
|
|
if (!m_parser.read(values, 3))
|
|
return false;
|
|
|
|
value = { static_cast<float>(values[0]), static_cast<float>(values[1]), static_cast<float>(values[2]), 0.0F };
|
|
}
|
|
|
|
sample.scale = vector_load(&value);
|
|
if (!rtm::vector_is_finite3(sample.scale))
|
|
return false;
|
|
|
|
if (!m_parser.array_ends())
|
|
return false;
|
|
}
|
|
|
|
out_sample = sample;
|
|
return true;
|
|
}
|
|
|
|
bool process_track_list(track* tracks, track_qvvf* bind_pose, uint32_t& num_tracks)
|
|
{
|
|
const bool counting = tracks == nullptr;
|
|
track dummy;
|
|
track_type8 track_list_type = track_type8::float1f;
|
|
|
|
num_tracks = 0;
|
|
|
|
if (!m_parser.array_begins("tracks"))
|
|
goto error;
|
|
|
|
for (uint32_t i = 0; !m_parser.try_array_ends(); ++i)
|
|
{
|
|
track& track_ = counting ? dummy : tracks[i];
|
|
|
|
if (!m_parser.object_begins())
|
|
goto error;
|
|
|
|
sjson::StringView name;
|
|
m_parser.try_read("name", name, "");
|
|
|
|
sjson::StringView type;
|
|
if (!m_parser.read("type", type))
|
|
goto error;
|
|
|
|
track_type8 track_type;
|
|
if (!get_track_type(type.c_str(), track_type))
|
|
{
|
|
m_error.error = clip_reader_error::InvalidTrackType;
|
|
return false;
|
|
}
|
|
|
|
if (num_tracks == 0)
|
|
track_list_type = track_type;
|
|
else if (track_type != track_list_type)
|
|
{
|
|
m_error.error = clip_reader_error::InvalidTrackType;
|
|
return false;
|
|
}
|
|
|
|
const uint32_t num_components = get_track_num_sample_elements(track_type);
|
|
ACL_ASSERT((num_components > 0 && num_components <= 4) || num_components == 12, "Must have between 1 and 4 components or 12");
|
|
|
|
track_desc_scalarf scalar_desc;
|
|
track_desc_transformf transform_desc;
|
|
|
|
float precision = read_optional_float("precision", -1.0F);
|
|
|
|
// Deprecated, no longer used
|
|
read_optional_float("constant_threshold", -1.0F);
|
|
|
|
uint32_t output_index;
|
|
m_parser.try_read("output_index", output_index, i);
|
|
|
|
m_parser.try_read("parent_index", transform_desc.parent_index, k_invalid_track_index);
|
|
|
|
transform_desc.shell_distance = read_optional_float("shell_distance", transform_desc.shell_distance);
|
|
transform_desc.constant_rotation_threshold_angle = read_optional_float("constant_rotation_threshold_angle", transform_desc.constant_rotation_threshold_angle);
|
|
transform_desc.constant_translation_threshold = read_optional_float("constant_translation_threshold", transform_desc.constant_translation_threshold);
|
|
transform_desc.constant_scale_threshold = read_optional_float("constant_scale_threshold", transform_desc.constant_scale_threshold);
|
|
|
|
scalar_desc.output_index = output_index;
|
|
transform_desc.output_index = output_index;
|
|
|
|
if (track_type == track_type8::qvvf)
|
|
transform_desc.precision = precision < 0.0F ? transform_desc.precision : precision;
|
|
else
|
|
scalar_desc.precision = precision < 0.0F ? scalar_desc.precision : precision;
|
|
|
|
rtm::qvvf bind_transform = rtm::qvv_identity();
|
|
if (m_is_binary_exact)
|
|
{
|
|
sjson::StringView rotation[4];
|
|
if (m_parser.try_read("bind_rotation", rotation, 4, nullptr) && !counting)
|
|
{
|
|
const rtm::float4f value = hex_to_float4f(rotation, 4);
|
|
bind_transform.rotation = rtm::quat_load(&value);
|
|
}
|
|
|
|
sjson::StringView translation[3];
|
|
if (m_parser.try_read("bind_translation", translation, 3, nullptr) && !counting)
|
|
{
|
|
const rtm::float4f value = hex_to_float4f(translation, 3);
|
|
bind_transform.translation = rtm::vector_load(&value);
|
|
}
|
|
|
|
sjson::StringView scale[3];
|
|
if (m_parser.try_read("bind_scale", scale, 3, nullptr) && !counting)
|
|
{
|
|
const rtm::float4f value = hex_to_float4f(scale, 3);
|
|
bind_transform.scale = rtm::vector_load(&value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double rotation[4] = { 0.0, 0.0, 0.0, 0.0 };
|
|
if (m_parser.try_read("bind_rotation", rotation, 4, 0.0) && !counting)
|
|
bind_transform.rotation = rtm::quat_normalize(rtm::quat_cast(rtm::quat_load(&rotation[0])));
|
|
|
|
double translation[3] = { 0.0, 0.0, 0.0 };
|
|
if (m_parser.try_read("bind_translation", translation, 3, 0.0) && !counting)
|
|
bind_transform.translation = rtm::vector_cast(rtm::vector_load3(&translation[0]));
|
|
|
|
double scale[3] = { 0.0, 0.0, 0.0 };
|
|
if (m_parser.try_read("bind_scale", scale, 3, 0.0) && !counting)
|
|
bind_transform.scale = rtm::vector_cast(rtm::vector_load3(&scale[0]));
|
|
}
|
|
|
|
if (bind_pose != nullptr)
|
|
(*bind_pose)[i] = bind_transform;
|
|
|
|
if (!m_parser.array_begins("data"))
|
|
goto error;
|
|
|
|
union track_samples_ptr_union
|
|
{
|
|
void* any;
|
|
float* float1f;
|
|
rtm::float2f* float2f;
|
|
rtm::float3f* float3f;
|
|
rtm::float4f* float4f;
|
|
rtm::vector4f* vector4f;
|
|
rtm::qvvf* qvvf;
|
|
};
|
|
track_samples_ptr_union track_samples_typed = { nullptr };
|
|
|
|
switch (track_type)
|
|
{
|
|
case track_type8::float1f:
|
|
track_samples_typed.float1f = allocate_type_array<float>(m_allocator, m_num_samples);
|
|
break;
|
|
case track_type8::float2f:
|
|
track_samples_typed.float2f = allocate_type_array<rtm::float2f>(m_allocator, m_num_samples);
|
|
break;
|
|
case track_type8::float3f:
|
|
track_samples_typed.float3f = allocate_type_array<rtm::float3f>(m_allocator, m_num_samples);
|
|
break;
|
|
case track_type8::float4f:
|
|
track_samples_typed.float4f = allocate_type_array<rtm::float4f>(m_allocator, m_num_samples);
|
|
break;
|
|
case track_type8::vector4f:
|
|
track_samples_typed.vector4f = allocate_type_array<rtm::vector4f>(m_allocator, m_num_samples);
|
|
break;
|
|
case track_type8::qvvf:
|
|
track_samples_typed.qvvf = allocate_type_array<rtm::qvvf>(m_allocator, m_num_samples);
|
|
break;
|
|
default:
|
|
ACL_ASSERT(false, "Unsupported track type");
|
|
break;
|
|
}
|
|
|
|
bool has_error = false;
|
|
for (uint32_t sample_index = 0; sample_index < m_num_samples; ++sample_index)
|
|
{
|
|
if (!m_parser.array_begins())
|
|
{
|
|
has_error = true;
|
|
break;
|
|
}
|
|
|
|
if (m_is_binary_exact)
|
|
{
|
|
switch (track_type)
|
|
{
|
|
case track_type8::float1f:
|
|
case track_type8::float2f:
|
|
case track_type8::float3f:
|
|
case track_type8::float4f:
|
|
case track_type8::vector4f:
|
|
{
|
|
sjson::StringView values[4];
|
|
if (m_parser.read(values, num_components))
|
|
{
|
|
const rtm::float4f value = hex_to_float4f(values, num_components);
|
|
std::memcpy(track_samples_typed.float1f + (sample_index * num_components), &value, sizeof(float) * num_components);
|
|
}
|
|
else
|
|
has_error = true;
|
|
break;
|
|
}
|
|
case track_type8::qvvf:
|
|
{
|
|
rtm::qvvf sample;
|
|
if (!read_qvv_sample(sample))
|
|
has_error = true;
|
|
else
|
|
std::memcpy(track_samples_typed.qvvf + sample_index, &sample, sizeof(rtm::qvvf));
|
|
break;
|
|
}
|
|
default:
|
|
ACL_ASSERT(false, "Unsupported track type");
|
|
break;
|
|
}
|
|
|
|
if (has_error)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
switch (track_type)
|
|
{
|
|
case track_type8::float1f:
|
|
case track_type8::float2f:
|
|
case track_type8::float3f:
|
|
case track_type8::float4f:
|
|
case track_type8::vector4f:
|
|
{
|
|
double values[4] = { 0.0, 0.0, 0.0, 0.0 };
|
|
if (m_parser.read(values, num_components))
|
|
{
|
|
const rtm::float4f value = { static_cast<float>(values[0]), static_cast<float>(values[1]), static_cast<float>(values[2]), static_cast<float>(values[3]) };
|
|
std::memcpy(track_samples_typed.float1f + (sample_index * num_components), &value, sizeof(float) * num_components);
|
|
}
|
|
else
|
|
has_error = true;
|
|
break;
|
|
}
|
|
case track_type8::qvvf:
|
|
{
|
|
rtm::qvvf sample;
|
|
if (!read_qvv_sample(sample))
|
|
has_error = true;
|
|
else
|
|
std::memcpy(track_samples_typed.qvvf + sample_index, &sample, sizeof(rtm::qvvf));
|
|
break;
|
|
}
|
|
default:
|
|
ACL_ASSERT(false, "Unsupported track type");
|
|
break;
|
|
}
|
|
|
|
if (has_error)
|
|
break;
|
|
}
|
|
|
|
if (!has_error && !m_parser.array_ends())
|
|
{
|
|
has_error = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!has_error && !m_parser.array_ends())
|
|
{
|
|
has_error = true;
|
|
break;
|
|
}
|
|
|
|
if (!has_error && !m_parser.object_ends())
|
|
{
|
|
has_error = true;
|
|
break;
|
|
}
|
|
|
|
if (has_error)
|
|
{
|
|
switch (track_type)
|
|
{
|
|
case track_type8::float1f:
|
|
deallocate_type_array<float>(m_allocator, track_samples_typed.float1f, m_num_samples);
|
|
break;
|
|
case track_type8::float2f:
|
|
deallocate_type_array<rtm::float2f>(m_allocator, track_samples_typed.float2f, m_num_samples);
|
|
break;
|
|
case track_type8::float3f:
|
|
deallocate_type_array<rtm::float3f>(m_allocator, track_samples_typed.float3f, m_num_samples);
|
|
break;
|
|
case track_type8::float4f:
|
|
deallocate_type_array<rtm::float4f>(m_allocator, track_samples_typed.float4f, m_num_samples);
|
|
break;
|
|
case track_type8::vector4f:
|
|
deallocate_type_array<rtm::vector4f>(m_allocator, track_samples_typed.vector4f, m_num_samples);
|
|
break;
|
|
case track_type8::qvvf:
|
|
deallocate_type_array<rtm::qvvf>(m_allocator, track_samples_typed.qvvf, m_num_samples);
|
|
break;
|
|
default:
|
|
ACL_ASSERT(false, "Unsupported track type");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (track_type)
|
|
{
|
|
case track_type8::float1f:
|
|
track_ = track_float1f::make_owner(scalar_desc, m_allocator, track_samples_typed.float1f, m_num_samples, m_sample_rate);
|
|
break;
|
|
case track_type8::float2f:
|
|
track_ = track_float2f::make_owner(scalar_desc, m_allocator, track_samples_typed.float2f, m_num_samples, m_sample_rate);
|
|
break;
|
|
case track_type8::float3f:
|
|
track_ = track_float3f::make_owner(scalar_desc, m_allocator, track_samples_typed.float3f, m_num_samples, m_sample_rate);
|
|
break;
|
|
case track_type8::float4f:
|
|
track_ = track_float4f::make_owner(scalar_desc, m_allocator, track_samples_typed.float4f, m_num_samples, m_sample_rate);
|
|
break;
|
|
case track_type8::vector4f:
|
|
track_ = track_vector4f::make_owner(scalar_desc, m_allocator, track_samples_typed.vector4f, m_num_samples, m_sample_rate);
|
|
break;
|
|
case track_type8::qvvf:
|
|
track_ = track_qvvf::make_owner(transform_desc, m_allocator, track_samples_typed.qvvf, m_num_samples, m_sample_rate);
|
|
break;
|
|
default:
|
|
ACL_ASSERT(false, "Unsupported track type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
track_.set_name(convert_string(name));
|
|
|
|
num_tracks++;
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
m_error = m_parser.get_error();
|
|
return false;
|
|
}
|
|
|
|
bool create_track_list(track_array& track_list, track_qvvf& bind_pose)
|
|
{
|
|
const sjson::ParserState before_tracks = m_parser.save_state();
|
|
|
|
uint32_t num_tracks;
|
|
if (!process_track_list(nullptr, nullptr, num_tracks))
|
|
return false;
|
|
|
|
m_parser.restore_state(before_tracks);
|
|
|
|
track_list = track_array(m_allocator, num_tracks);
|
|
track_list.set_name(convert_string(m_clip_name));
|
|
bind_pose = track_qvvf::make_reserve(track_desc_transformf{}, m_allocator, num_tracks, 30.0F); // 1 sample per track
|
|
bind_pose.set_name(string(m_allocator, "bind pose"));
|
|
|
|
if (!process_track_list(track_list.begin(), &bind_pose, num_tracks))
|
|
return false;
|
|
|
|
ACL_ASSERT(num_tracks == track_list.get_num_tracks(), "Number of tracks read mismatch");
|
|
|
|
return true;
|
|
}
|
|
|
|
bool read_tracks(track_array_qvvf& track_list, track_array_qvvf& additive_base_track_list)
|
|
{
|
|
const uint32_t num_transforms = track_list.get_num_tracks();
|
|
|
|
if (m_parser.try_array_begins("base_tracks"))
|
|
{
|
|
// Copy our metadata from the actual clip
|
|
additive_base_track_list = track_array_qvvf(m_allocator, num_transforms);
|
|
additive_base_track_list.set_name(string(m_allocator, "additive base"));
|
|
for (uint32_t transform_index = 0; transform_index < num_transforms; ++transform_index)
|
|
additive_base_track_list[transform_index].get_description() = track_list[transform_index].get_description();
|
|
|
|
while (!m_parser.try_array_ends())
|
|
{
|
|
if (!m_parser.object_begins())
|
|
goto error;
|
|
|
|
sjson::StringView name;
|
|
if (!m_parser.read("name", name))
|
|
goto error;
|
|
|
|
const uint32_t bone_index = find_bone(name);
|
|
if (bone_index == k_invalid_track_index)
|
|
{
|
|
set_error(clip_reader_error::NoBoneWithThatName);
|
|
return false;
|
|
}
|
|
|
|
track_desc_transformf desc = additive_base_track_list[bone_index].get_description(); // Copy our description
|
|
desc.output_index = bone_index;
|
|
|
|
track_qvvf track = track_qvvf::make_reserve(desc, m_allocator, m_additive_base_num_samples, m_additive_base_sample_rate);
|
|
track.set_name(convert_string(name));
|
|
|
|
if (m_parser.try_array_begins("rotations"))
|
|
{
|
|
if (!read_track_rotations(track, m_additive_base_num_samples) || !m_parser.array_ends())
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
for (uint32_t sample_index = 0; sample_index < m_additive_base_num_samples; ++sample_index)
|
|
track[sample_index].rotation = rtm::quat_identity();
|
|
}
|
|
|
|
if (m_parser.try_array_begins("translations"))
|
|
{
|
|
if (!read_track_translations(track, m_additive_base_num_samples) || !m_parser.array_ends())
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
for (uint32_t sample_index = 0; sample_index < m_additive_base_num_samples; ++sample_index)
|
|
track[sample_index].translation = rtm::vector_zero();
|
|
}
|
|
|
|
if (m_parser.try_array_begins("scales"))
|
|
{
|
|
if (!read_track_scales(track, m_additive_base_num_samples) || !m_parser.array_ends())
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
for (uint32_t sample_index = 0; sample_index < m_additive_base_num_samples; ++sample_index)
|
|
track[sample_index].scale = rtm::vector_set(1.0F);
|
|
}
|
|
|
|
additive_base_track_list[bone_index] = std::move(track);
|
|
|
|
if (!m_parser.object_ends())
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (!m_parser.array_begins("tracks"))
|
|
goto error;
|
|
|
|
while (!m_parser.try_array_ends())
|
|
{
|
|
if (!m_parser.object_begins())
|
|
goto error;
|
|
|
|
sjson::StringView name;
|
|
if (!m_parser.read("name", name))
|
|
goto error;
|
|
|
|
const uint32_t bone_index = find_bone(name);
|
|
if (bone_index == k_invalid_track_index)
|
|
{
|
|
set_error(clip_reader_error::NoBoneWithThatName);
|
|
return false;
|
|
}
|
|
|
|
track_desc_transformf desc = track_list[bone_index].get_description(); // Copy our description
|
|
desc.output_index = bone_index;
|
|
|
|
track_qvvf track = track_qvvf::make_reserve(desc, m_allocator, m_num_samples, m_sample_rate);
|
|
track.set_name(convert_string(name));
|
|
|
|
if (m_parser.try_array_begins("rotations"))
|
|
{
|
|
if (!read_track_rotations(track, m_num_samples) || !m_parser.array_ends())
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
for (uint32_t sample_index = 0; sample_index < m_num_samples; ++sample_index)
|
|
track[sample_index].rotation = rtm::quat_identity();
|
|
}
|
|
|
|
if (m_parser.try_array_begins("translations"))
|
|
{
|
|
if (!read_track_translations(track, m_num_samples) || !m_parser.array_ends())
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
for (uint32_t sample_index = 0; sample_index < m_num_samples; ++sample_index)
|
|
track[sample_index].translation = rtm::vector_zero();
|
|
}
|
|
|
|
if (m_parser.try_array_begins("scales"))
|
|
{
|
|
if (!read_track_scales(track, m_num_samples) || !m_parser.array_ends())
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
for (uint32_t sample_index = 0; sample_index < m_num_samples; ++sample_index)
|
|
track[sample_index].scale = rtm::vector_set(1.0F);
|
|
}
|
|
|
|
track_list[bone_index] = std::move(track);
|
|
|
|
if (!m_parser.object_ends())
|
|
goto error;
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
m_error = m_parser.get_error();
|
|
return false;
|
|
}
|
|
|
|
bool read_track_rotations(track_qvvf& track, uint32_t num_samples_expected)
|
|
{
|
|
for (uint32_t i = 0; i < num_samples_expected; ++i)
|
|
{
|
|
if (!m_parser.array_begins())
|
|
return false;
|
|
|
|
rtm::quatd rotation;
|
|
|
|
if (m_is_binary_exact)
|
|
{
|
|
sjson::StringView values[4];
|
|
if (!m_parser.read(values, 4))
|
|
return false;
|
|
|
|
rotation = hex_to_quat(values);
|
|
}
|
|
else
|
|
{
|
|
double values[4] = { 0.0, 0.0, 0.0, 0.0 };
|
|
if (!m_parser.read(values, 4))
|
|
return false;
|
|
|
|
rotation = rtm::quat_load(values);
|
|
}
|
|
|
|
if (!m_parser.array_ends())
|
|
return false;
|
|
|
|
track[i].rotation = rtm::quat_normalize(rtm::quat_cast(rotation));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool read_track_translations(track_qvvf& track, uint32_t num_samples_expected)
|
|
{
|
|
for (uint32_t i = 0; i < num_samples_expected; ++i)
|
|
{
|
|
if (!m_parser.array_begins())
|
|
return false;
|
|
|
|
rtm::vector4d translation;
|
|
|
|
if (m_is_binary_exact)
|
|
{
|
|
sjson::StringView values[3];
|
|
if (!m_parser.read(values, 3))
|
|
return false;
|
|
|
|
translation = hex_to_vector3(values);
|
|
}
|
|
else
|
|
{
|
|
double values[3];
|
|
if (!m_parser.read(values, 3))
|
|
return false;
|
|
|
|
translation = rtm::vector_load3(values);
|
|
}
|
|
|
|
if (!m_parser.array_ends())
|
|
return false;
|
|
|
|
track[i].translation = rtm::vector_cast(translation);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool read_track_scales(track_qvvf& track, uint32_t num_samples_expected)
|
|
{
|
|
for (uint32_t i = 0; i < num_samples_expected; ++i)
|
|
{
|
|
if (!m_parser.array_begins())
|
|
return false;
|
|
|
|
rtm::vector4d scale;
|
|
|
|
if (m_is_binary_exact)
|
|
{
|
|
sjson::StringView values[3];
|
|
if (!m_parser.read(values, 3))
|
|
return false;
|
|
|
|
scale = hex_to_vector3(values);
|
|
}
|
|
else
|
|
{
|
|
double values[3];
|
|
if (!m_parser.read(values, 3))
|
|
return false;
|
|
|
|
scale = rtm::vector_load3(values);
|
|
}
|
|
|
|
if (!m_parser.array_ends())
|
|
return false;
|
|
|
|
track[i].scale = rtm::vector_cast(scale);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool nothing_follows()
|
|
{
|
|
if (!m_parser.remainder_is_comments_and_whitespace())
|
|
{
|
|
m_error = m_parser.get_error();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void set_error(uint32_t reason)
|
|
{
|
|
m_parser.get_position(m_error.line, m_error.column);
|
|
m_error.error = reason;
|
|
}
|
|
};
|
|
}
|
|
|
|
ACL_IMPL_FILE_PRAGMA_POP
|
|
|
|
#endif // #if defined(SJSON_CPP_PARSER)
|