66 #ifndef LIB_ALMATH_SCENEGRAPH_GLTF_H
67 #define LIB_ALMATH_SCENEGRAPH_GLTF_H
72 #include <type_traits>
73 #include <boost/config.hpp>
74 #include <boost/lexical_cast.hpp>
75 #include <boost/optional.hpp>
76 #include <boost/archive/iterators/binary_from_base64.hpp>
77 #include <boost/archive/iterators/base64_from_binary.hpp>
78 #include <boost/archive/iterators/transform_width.hpp>
79 #include <boost/endian/buffers.hpp>
80 #include <boost/algorithm/string/join.hpp>
81 #include <boost/range/adaptor/transformed.hpp>
85 #include <Eigen/Geometry>
110 template< DataType element>
118 BOOST_STATIC_CONSTEXPR
size_t number_of_components = 1u;
119 static BOOST_CONSTEXPR
const char *
cstr() {
return "SCALAR";}
124 BOOST_STATIC_CONSTEXPR
size_t number_of_components = 3u;
125 static BOOST_CONSTEXPR
const char *
cstr() {
return "VEC3";}
130 BOOST_STATIC_CONSTEXPR
size_t number_of_components = 4u;
131 static BOOST_CONSTEXPR
const char *
cstr() {
return "VEC4";}
136 BOOST_STATIC_CONSTEXPR
size_t number_of_components = 4u;
137 static BOOST_CONSTEXPR
const char *
cstr() {
return "MAT2";}
149 static_assert(
sizeof(
char) == 1u,
150 "error: the native char type is not 1 byte wide");
156 inline void buffer_put(std::ostream &os,
unsigned char val) {
157 static_assert(
sizeof(
unsigned char) == 1u,
158 "error: the native char type is not 1 byte wide");
159 os.put(static_cast<char>(val));
165 const boost::endian::little_int16_buf_t tmp{val};
166 os.write(tmp.data(),
sizeof(tmp));
171 inline void buffer_put(std::ostream &os, std::uint16_t val) {
172 const boost::endian::little_uint16_buf_t tmp{val};
173 os.write(tmp.data(),
sizeof(tmp));
178 inline void buffer_put(std::ostream &os, std::uint32_t val) {
179 const boost::endian::little_uint32_buf_t tmp{val};
180 os.write(tmp.data(),
sizeof(tmp));
187 static_assert(std::numeric_limits<float>::is_iec559,
188 "error: the native float type is not IEEE-754-compliant");
189 static_assert(
sizeof(
float) == 4u,
190 "error: the native float type is not 4 bytes wide");
215 #ifdef BOOST_LITTLE_ENDIAN
217 os.write(reinterpret_cast<const char *>(&val), 4);
220 const boost::endian::little_uint32_buf_t tmp{
221 *
reinterpret_cast<const uint32_t*
>(&val)};
222 os.write(tmp.data,
sizeof(tmp));
229 template <
typename T,
size_t N>
238 std::fill(
min.begin(),
min.end(), std::numeric_limits<T>::max());
239 std::fill(
max.begin(),
max.end(), std::numeric_limits<T>::lowest());
242 for (
auto i = 0u; i < val.size(); ++i) {
243 min[i] = std::min(
min[i], val[i]);
244 max[i] = std::max(
max[i], val[i]);
253 inline std::string
encode64(
const std::string &val) {
254 using namespace boost::archive::iterators;
255 using It = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
256 auto tmp = std::string(It(std::begin(val)), It(std::end(val)));
257 return tmp.append((3 - val.size() % 3) % 3,
'=');
260 template <
typename T>
262 static_assert(std::is_arithmetic<T>::value,
263 "toJSON requires T to be an integer,"
264 " floating point and boolean type");
268 return boost::lexical_cast<std::string>(val);
272 template <
typename T,
typename IT>
290 auto write = [&os](
const T &value)
mutable {
295 while (++it != a._end) {
306 template<
typename T,
typename IT>
312 template<
typename T,
typename R,
typename IT=
typename R::const_iterator>
326 return Buffer{binbuffer.size(),
327 std::string(
"data:application/octet-stream;base64,") +
\
334 <<
",\"uri\":\"" << v.
uri <<
"\""
349 os <<
"{\"buffer\":" << v.
buffer
359 template <
typename T, DataType DTYPE>
365 std::array<T, data_type_traits<DTYPE>::number_of_components>
min,
max;
369 template <
typename T, DataType _type>
370 std::ostream &operator<<(std::ostream &os, const Accessor<T, _type> &v) {
390 os <<
"{\"bufferView\":" << v.bufferView
391 <<
",\"componentType\":"<<
static_cast<int>(v.componentType)
392 <<
",\"count\":" <<
toJSON(v.count)
393 <<
",\"type\":\"" << to_cstr(v.type) <<
"\""
395 << boost::join(boost::adaptors::transform(v.min, toJSON<float>),
",")
397 <<
",\"max\":" << asJSONArray<T>(v.max);
399 os <<
",\"byteOffset\":" << *v.byteOffset;
407 boost::optional<std::string>
name;
412 os <<
"{\"primitives\":["
415 os <<
",\"name\":\"" << *v.
name <<
"\"";
423 boost::optional<Eigen::Vector3f>
scale;
429 boost::optional<std::string>
name;
443 os << prefix <<
"\"children\":" << asJSONArray<size_t>(v.
children);
447 os << prefix <<
"\"name\":\"" << *v.
name <<
"\"";
451 os << prefix <<
"\"mesh\":" <<
toJSON(*v.
mesh);
460 const auto q = *(v.
matrix->rotation);
461 os << prefix <<
"\"rotation\":"
462 << asJSONArray<float>(
463 std::array<float,4>{{q.x(), q.y(), q.z(), q.w()}});
467 if (v.
matrix->translation) {
468 const auto vec3 = *(v.
matrix->translation);
469 os << prefix <<
"\"translation\":"
470 << asJSONArray<float>(
471 std::array<float,3>{{vec3[0], vec3[1], vec3[2]}});
483 boost::optional<std::string>
name;
488 os <<
"{\"nodes\":" << asJSONArray<size_t>(v.
nodes);
490 os <<
",\"name\":\"" << *v.
name <<
"\"";
537 return "translation";
559 <<
",\"target\":" << v.
target <<
"}";
570 boost::optional<std::string>
name;
594 std::vector<std::string> tmp;
596 tmp.push_back(boost::lexical_cast<std::string>(a));
598 os <<
"{\"samplers\":[" << boost::join(tmp,
",") <<
"]";
601 std::vector<std::string> tmp;
603 tmp.push_back(boost::lexical_cast<std::string>(a));
605 os <<
",\"channels\":[" << boost::join(tmp,
",") <<
"]";
613 template <
typename T>
614 size_t _add(std::vector<std::string> &vec,
const T &val) {
615 vec.push_back(boost::lexical_cast<std::string>(val));
616 return vec.size() - 1u;
633 template<
typename T, DataType DTYPE>
648 os <<
"{\"asset\": {\"version\": \"2.0\"}"
649 <<
"\n,\"buffers\":\n ["
650 << boost::join(
buffers,
"\n ,")
651 <<
"]\n,\"bufferViews\":\n ["
653 <<
"]\n,\"accessors\":\n ["
655 <<
"]\n,\"meshes\":\n ["
656 << boost::join(
meshes,
"\n ,") <<
"]\n";
658 os <<
",\"materials\":\n ["
659 << boost::join(
materials,
"\n ,") <<
"]\n";
661 os <<
",\"cameras\":\n ["
662 << boost::join(
cameras,
"\n ,") <<
"]\n";
663 }
if (!
nodes.empty()) {
664 os <<
",\"nodes\":\n ["
665 << boost::join(
nodes,
"\n ,") <<
"]\n";
667 os <<
",\"scenes\":\n ["
668 << boost::join(
scenes,
"\n ,") <<
"]\n";
672 std::vector<std::string> anims;
674 anims.push_back(boost::lexical_cast<std::string>(a));
675 os <<
",\"animations\":\n ["
676 << boost::join(anims,
"\n ,") <<
"]\n";
689 const std::vector<float> &vecs) {
690 std::ostringstream bs;
692 for (
const auto &vec: vecs) {
695 const auto binbuffer = bs.str();
696 const auto binbuffer_length = binbuffer.size();
707 const std::vector<std::array<float, 3>> &vecs) {
708 std::ostringstream bs;
710 for (
const auto &vec: vecs) {
713 const auto binbuffer = bs.str();
714 const auto binbuffer_length = binbuffer.size();
725 const std::vector<std::array<float, 3>> &verts,
726 boost::optional<std::string> node_name) {
728 std::stringstream ps;
732 ps <<
",\"attributes\":{\"POSITION\":" << a0 <<
"}"
743 const std::vector<Math::Position2D> &path,
744 boost::optional<std::string> node_name) {
745 std::vector<std::array<float, 3>> verts;
746 verts.reserve(path.size());
747 for (
const auto &p: path) {
748 verts.push_back({p.x, p.y, 0.f});
750 return add_path(doc, verts, node_name);
757 const std::vector<std::pair<float, Math::Pose2D>> &traj) {
759 std::ostringstream time_stream, translation_stream, rotation_stream;
763 for (
const auto &el: traj) {
764 time_ba.
put({el.first});
765 translation_ba.
put({el.second.x, el.second.y, 0.f});
766 Eigen::Quaternionf q;
767 q = Eigen::AngleAxisf(el.second.theta, Eigen::Vector3f::UnitZ());
768 rotation_ba.
put({q.x(), q.y(), q.z(), q.w()});
771 auto binbuffer = time_stream.str();
772 auto binbuffer_length = binbuffer.size();
775 const auto time_accessor = doc.
add(
780 binbuffer = translation_stream.str();
781 binbuffer_length = binbuffer.size();
784 const auto translation_accessor = doc.
add(
787 translation_ba.
count, translation_ba.
min, translation_ba.
max});
789 binbuffer = rotation_stream.str();
790 binbuffer_length = binbuffer.size();
793 const auto rotation_accessor = doc.
add(
796 rotation_ba.
count, rotation_ba.
min, rotation_ba.
max});
798 const auto translation_sampler =
801 translation_accessor,
808 const auto rotation_sampler =
boost::optional< size_t > byteStride
boost::optional< size_t > scene
Interpolation interpolation
std::array< T, N > value_type
static const DataType type
boost::optional< Eigen::Vector3f > translation
std::array< T, data_type_traits< DTYPE >::number_of_components > max
ComponentType componentType
std::vector< std::string > primitives
AsJSONArray< T, IT > asJSONArray(IT b, IT e)
size_t add(const Mesh &v)
std::vector< std::string > materials
boost::optional< Eigen::Quaternionf > rotation
boost::optional< size_t > camera
boost::optional< std::string > name
std::string to_string(Interpolation val)
static BOOST_CONSTEXPR const char * cstr()
void writeJSON(std::ostream &os) const
void buffer_put(std::ostream &os, char val)
size_t add(const Accessor< T, DTYPE > &v)
boost::optional< std::string > name
std::vector< size_t > children
std::vector< Channel > channels
size_t add_path(glTF::Document &doc, const std::vector< std::array< float, 3 >> &verts, boost::optional< std::string > node_name)
std::vector< Sampler > samplers
boost::optional< std::string > name
boost::optional< rst > matrix
std::vector< std::string > scenes
std::vector< std::string > accessors
std::vector< Animation > animations
boost::optional< size_t > byteOffset
static BOOST_CONSTEXPR const char * cstr()
size_t add(const Animation &v)
static BOOST_CONSTEXPR const char * cstr()
std::ostream & operator<<(std::ostream &os, const Buffer &v)
boost::optional< Eigen::Vector3f > scale
size_t add(const Node &v)
boost::optional< size_t > mesh
size_t add(const Scene &v)
std::vector< std::string > bufferViews
std::vector< std::string > cameras
boost::optional< size_t > byteOffset
size_t add_base64_bva(glTF::Document &doc, const std::vector< float > &vecs)
std::array< T, data_type_traits< DTYPE >::number_of_components > min
Buffer make_base64_Buffer(const std::string &binbuffer)
void animate(glTF::Document &doc, size_t node, size_t animation, const std::vector< std::pair< float, Math::Pose2D >> &traj)
static BOOST_CONSTEXPR const char * cstr()
size_t add(const BufferView &v)
std::vector< std::string > nodes
size_t add_sampler(const Sampler &v)
std::string toJSON(T val)
std::string encode64(const std::string &val)
size_t add_channel(const Channel &v)
boost::optional< std::string > name
size_t add(const Buffer &v)
BufferAccessor(std::ostream &os)
friend std::ostream & operator<<(std::ostream &os, const AsJSONArray &a)
std::vector< std::string > buffers
std::vector< size_t > nodes
std::vector< std::string > meshes