libalmath  2.8.7.4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gltf.h
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Softbank Robotics. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the COPYING file.
5  */
6 
7 // glTF-related utilities
8 //
9 // See https://github.com/KhronosGroup/glTF/ for a description of glTF.
10 //
11 // In brief:
12 //
13 // A glTF asset consists of a JSON file describing the structure and
14 // composition of a scene (possibly with animations) by
15 // referring to buffer-based binary data (geometry, texture, skinning
16 // and animation data are binary).
17 //
18 // The buffers can be:
19 // * contained in the JSON, as base64-encoded strings,
20 // * stored externaly at some URIs,
21 // * combined with the JSON in a single binary glTF file.
22 //
23 // A glTF binary buffer is a sequence of bytes, using a little-endian
24 // layout.
25 // The "accessor" element is used to describe the data type
26 // (eg. 4-tuple of floats) of a buffer view.
27 //
28 // The relevant elements are:
29 // * buffer: "uri", "byteLength"
30 // * buffeView: "buffer", "byteLength", ["byteOffset", "byteStride", "target"]
31 // * accessor: "bufferView", "count", "min", "max", "componentType", "type", ["byteOffset"]
32 
33 // with componentType in
34 // * 5120 (GL_BYTE)
35 // * 5121 (GL_UNSIGNED_BYTE)
36 // * 5122 (GL_SHORT)
37 // * 5123 (GL_UNSIGNED_SHORT)
38 // * 5125 (GL_UNSIGNED_INT)
39 // * 5126 (GL_FLOAT)
40 //
41 // and type in:
42 // * "SCALAR"
43 // * "VEC2"
44 // * "VEC3"
45 // * "VEC4"
46 // * "MAT2"
47 // * "MAT3"
48 // * "MAT4"
49 //
50 // Beware, the byteStride suggests that one can interleave bufferViews,
51 // but I cound not get working.
52 //
53 // In C++, binary buffers are typically modeled as stream of char.
54 //
55 // Writing C++ types (eg. float) into a binary buffer can be done with:
56 // 1. a function writing into a ostream:
57 // void buffer_put(float, ostream &)
58 // 2. a function writing into an OutputIterator to char:
59 // template<typename OutputIterator>
60 // void buffer_put(float, OutputIterator it)
61 // 3. a custom InputIterator adaptor, converting a sequence of floats into a
62 // sequence of char.
63 //
64 // Here we use the first technique, with the buffer_put family of
65 // functions.
66 #ifndef LIB_ALMATH_SCENEGRAPH_GLTF_H
67 #define LIB_ALMATH_SCENEGRAPH_GLTF_H
68 
69 #include <cstdint>
70 #include <limits>
71 #include <sstream>
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>
82 
84 #include <almath/types/alpose2d.h>
85 #include <Eigen/Geometry>
86 
87 namespace AL {
88 namespace Math {
89 namespace glTF {
90 
91 enum struct ComponentType {
92  GL_BYTE = 5120,
93  GL_UNSIGNED_BYTE = 5121,
94  GL_SHORT = 5122,
95  GL_UNSIGNED_SHORT = 5123,
96  GL_UNSIGNED_INT = 5125,
97  GL_FLOAT = 5126
98 };
99 
100 enum struct DataType {
101  SCALAR,
102  VEC3,
103  VEC4,
104  MAT2,
105  MAT3,
106  MAT4
107 };
108 
109 
110 template< DataType element>
112  //BOOST_STATIC_CONSTEXPR size_t number_of_components;
113  //static BOOST_CONSTEXPR const char *cstr();
114 };
115 
116 template<>
118  BOOST_STATIC_CONSTEXPR size_t number_of_components = 1u;
119  static BOOST_CONSTEXPR const char *cstr() {return "SCALAR";}
120 };
121 
122 template<>
124  BOOST_STATIC_CONSTEXPR size_t number_of_components = 3u;
125  static BOOST_CONSTEXPR const char *cstr() {return "VEC3";}
126 };
127 
128 template<>
130  BOOST_STATIC_CONSTEXPR size_t number_of_components = 4u;
131  static BOOST_CONSTEXPR const char *cstr() {return "VEC4";}
132 };
133 
134 template<>
136  BOOST_STATIC_CONSTEXPR size_t number_of_components = 4u;
137  static BOOST_CONSTEXPR const char *cstr() {return "MAT2";}
138 };
139 
140 // todo: add the other specializations, MAT3, ...
141 
142 enum struct Mode {
143  LINE_STRIP = 3
144 };
145 
146 // writes a GL_BYTE (5120) value to a binary stream,
147 // in the format expected by glTF: 1 byte.
148 inline void buffer_put(std::ostream &os, char val) {
149  static_assert(sizeof(char) == 1u,
150  "error: the native char type is not 1 byte wide");
151  os.put(val);
152 }
153 
154 // writes a GL_UNSIGNED_BYTE (5121) value to a binary stream,
155 // in the format expected by glTF: 1 byte.
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));
160 }
161 
162 // writes a GL_SHORT (5122) value to a binary stream,
163 // in the format expected by glTF: 2 bytes, little-endian.
164 inline void buffer_put(std::ostream &os, std::int16_t val) {
165  const boost::endian::little_int16_buf_t tmp{val};
166  os.write(tmp.data(), sizeof(tmp));
167 }
168 
169 // writes a GL_UNSIGNED_SHORT (5123) value to a binary stream,
170 // in the format expected by glTF: 2 bytes, little-endian
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));
174 }
175 
176 // writes a GL_UNSIGNED_INT (5125) value to a binary stream,
177 // in the format expected by glTF: 4 bytes, little-endian
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));
181 }
182 
183 // writes a GL_FLOAT (5126) value to a binary stream,
184 // in the format expected by glTF: 4 bytes, little-endian, single precision
185 // IEEE-754 number.
186 inline void buffer_put(std::ostream &os, float val) {
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");
191  // Thanks to thoses assertions, we know that float is a single-precision
192  // IEEE-754 number.
193  // However we don't know
194  // * if float is little-endian or big-endian
195  // * if float endianness is the same as int endiannes
196  //
197  // Yet, we'll assume that float endianness and int endianness are
198  // identical.
199  //
200  // Some details from
201  // https://en.wikipedia.org/wiki/Endianness#Floating_point:
202  //
203  // Theoretically, this means that even standard IEEE floating-point data
204  // written by one machine might not be readable by another.
205  // However, on modern standard computers (i.e., implementing IEEE 754),
206  // one may in practice safely assume that the endianness is the same for
207  // floating-point numbers as for integers, making the conversion
208  // straightforward regardless of data type.
209  // (Small embedded systems using special floating-point formats may be
210  // another matter however).
211  //
212  // Linux implementation can be checked in:
213  // /usr/include/x86_64-linux-gnu/ieee754.h
214 
215 #ifdef BOOST_LITTLE_ENDIAN
216  // "optimized" version
217  os.write(reinterpret_cast<const char *>(&val), 4);
218 #else
219  // portable version: works for little and big endian architectures
220  const boost::endian::little_uint32_buf_t tmp{
221  *reinterpret_cast<const uint32_t*>(&val)};
222  os.write(tmp.data, sizeof(tmp));
223 #endif
224 }
225 
226 // Helper class which writes N components (of type T) at a time,
227 // while also storing the metadata that will be needed to fill the
228 // glTF accessor to the data.
229 template <typename T, size_t N>
231 public:
232  using value_type = std::array<T, N>;
235  size_t count;
236  std::ostream &os;
237  BufferAccessor(std::ostream &os) : count(0u), os(os) {
238  std::fill(min.begin(), min.end(), std::numeric_limits<T>::max());
239  std::fill(max.begin(), max.end(), std::numeric_limits<T>::lowest());
240  }
241  void put(value_type val) {
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]);
245  buffer_put(os, val[i]);
246  }
247  ++count;
248  }
249 };
250 
251 // http://www.boost.org/doc/libs/1_59_0/libs/serialization/doc/dataflow.html
252 // https://stackoverflow.com/questions/7053538/how-do-i-encode-a-string-to-base64-using-only-boost
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, '=');
258 }
259 
260 template <typename T>
261 std::string toJSON(T val) {
262  static_assert(std::is_arithmetic<T>::value,
263  "toJSON requires T to be an integer,"
264  " floating point and boolean type");
265  // TODO: support std::string, with escaping.
266  // TODO: use SFINAE rather than assertion
267  // we use boost::lexical_cast because it handles the stream precision
268  return boost::lexical_cast<std::string>(val);
269 }
270 
271 // a proxy object to stream a range as a JSON array
272 template <typename T, typename IT>
273 class AsJSONArray {
274 private:
275  const IT _begin;
276  const IT _end;
277 
278 public:
279  AsJSONArray(IT b, IT e) : _begin(b), _end(e) {}
280 
281  friend std::ostream &operator<<(std::ostream &os, const AsJSONArray &a) {
282  // Here is an alternate implementation:
283  // os << '['
284  // << boost::join(boost::adaptors::transform(v.min, toJSON<float>), ",")
285  // << ']';
286  // A better one would be to use
287  // http://en.cppreference.com/w/cpp/experimental/ostream_joiner
288  os.put('[');
289  auto it = a._begin;
290  auto write = [&os](const T &value) mutable {
291  os << toJSON(value);
292  };
293  if (it != a._end) {
294  write(*it);
295  while (++it != a._end) {
296  os.put(',');
297  write(*it);
298  }
299  }
300  os.put(']');
301  return os;
302  }
303 };
304 
305 // helper to deduce iterator type.
306 template<typename T, typename IT>
308  return AsJSONArray<T, IT>(b, e);
309 }
310 
311 // helper to deduce iterator type.
312 template<typename T, typename R, typename IT=typename R::const_iterator>
314  using std::begin;
315  using std::end;
316  return AsJSONArray<T, IT>(begin(range), end(range));
317 }
318 
319 struct Buffer {
320  size_t byteLength;
321  std::string uri;
322 };
323 
324 inline
325 Buffer make_base64_Buffer(const std::string &binbuffer) {
326  return Buffer{binbuffer.size(),
327  std::string("data:application/octet-stream;base64,") +\
328  encode64(binbuffer)};
329 }
330 
331 inline
332 std::ostream &operator<<(std::ostream &os, const Buffer &v) {
333  os << "{\"byteLength\":" << v.byteLength
334  << ",\"uri\":\"" << v.uri << "\""
335  << "}";
336  return os;
337 }
338 
339 struct BufferView {
340  size_t buffer;
341  size_t byteLength;
342  boost::optional<size_t> byteOffset;
343  boost::optional<size_t> byteStride;
344  // target
345 };
346 
347 inline
348 std::ostream &operator<<(std::ostream &os, const BufferView &v) {
349  os << "{\"buffer\":" << v.buffer
350  << ",\"byteLength\":" << v.byteLength;
351  if (v.byteOffset)
352  os << ",\"byteOffset\":" << *v.byteOffset;
353  if (v.byteStride)
354  os << ",\"byteStride\":" << *v.byteStride;
355  os << "}";
356  return os;
357 }
358 
359 template <typename T, DataType DTYPE>
360 struct Accessor {
361  size_t bufferView;
362  ComponentType componentType; // redundant with T
363  size_t count;
364  static const DataType type = DTYPE;
365  std::array<T, data_type_traits<DTYPE>::number_of_components> min, max;// TODO: shall go in a union/variant, maybe depending on DataType?
366  boost::optional<size_t> byteOffset;
367 };
368 
369 template <typename T, DataType _type>
370 std::ostream &operator<<(std::ostream &os, const Accessor<T, _type> &v) {
371 
372  auto to_cstr = [](DataType type) {
373  switch (type) {
374  case DataType::SCALAR:
375  return "SCALAR";
376  case DataType::VEC3:
377  return "VEC3";
378  case DataType::VEC4:
379  return "VEC4";
380  case DataType::MAT2:
381  return "MAT2";
382  case DataType::MAT3:
383  return "MAT3";
384  case DataType::MAT4:
385  return "MAT4";
386  }
387  std::abort();
388  return "";
389  };
390  os << "{\"bufferView\":" << v.bufferView
391  << ",\"componentType\":"<< static_cast<int>(v.componentType)
392  << ",\"count\":" << toJSON(v.count)
393  << ",\"type\":\"" << to_cstr(v.type) << "\""
394  << ",\"min\":["
395  << boost::join(boost::adaptors::transform(v.min, toJSON<float>), ",")
396  << "]"
397  << ",\"max\":" << asJSONArray<T>(v.max);
398  if (v.byteOffset)
399  os << ",\"byteOffset\":" << *v.byteOffset;
400  os << "}";
401  return os;
402 }
403 
404 struct Mesh {
405  // TODO: maybe create a struct for primitives too
406  std::vector<std::string> primitives;
407  boost::optional<std::string> name;
408 };
409 
410 inline
411 std::ostream &operator<<(std::ostream &os, const Mesh &v) {
412  os << "{\"primitives\":["
413  << boost::join(v.primitives, ",") << "]";
414  if (v.name)
415  os << ",\"name\":\"" << *v.name << "\"";
416  os << "}";
417  return os;
418 }
419 
420 struct rst {
421  // TODO: find a better way, which should support a full matrix
422  boost::optional<Eigen::Quaternionf> rotation;
423  boost::optional<Eigen::Vector3f> scale;
424  boost::optional<Eigen::Vector3f> translation;
425 };
426 
427 struct Node {
428  std::vector<size_t> children;
429  boost::optional<std::string> name;
430  boost::optional<size_t> mesh;
431  boost::optional<size_t> camera;
432  boost::optional<rst> matrix;
433  //
434  // rotation, scale, translation
435  // matrix
436 
437 };
438 
439 inline
440 std::ostream &operator<<(std::ostream &os, const Node &v) {
441  auto prefix = '{';
442  if (!v.children.empty()) {
443  os << prefix << "\"children\":" << asJSONArray<size_t>(v.children);
444  prefix = ',';
445  }
446  if (v.name) {
447  os << prefix << "\"name\":\"" << *v.name << "\"";
448  prefix = ',';
449  }
450  if (v.mesh) {
451  os << prefix << "\"mesh\":" << toJSON(*v.mesh);
452  prefix = ',';
453  }
454  if (v.camera) {
455  os << prefix << "\"camera\":" << toJSON(*v.camera);
456  prefix = ',';
457  }
458  if (v.matrix) {
459  if (v.matrix->rotation) {
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()}});
464  prefix = ',';
465  }
466  // TODO: support scale
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]}});
472  prefix = ',';
473  }
474  }
475  if (prefix == '{')
476  os << '{';
477  os << "}";
478  return os;
479 }
480 
481 struct Scene {
482  std::vector<size_t> nodes;
483  boost::optional<std::string> name;
484 };
485 
486 inline
487 std::ostream &operator<<(std::ostream &os, const Scene &v) {
488  os << "{\"nodes\":" << asJSONArray<size_t>(v.nodes);
489  if (v.name)
490  os << ",\"name\":\"" << *v.name << "\"";
491  os << "}";
492  return os;
493 }
494 
495 
496 enum struct Interpolation {
497  LINEAR
498 };
499 
500 inline std::string to_string(Interpolation val) {
501  switch (val) {
502  case (Interpolation::LINEAR):
503  return "LINEAR";
504  }
505  std::abort();
506 }
507 
508 struct Sampler {
509  size_t input;
510  size_t output;
512 };
513 
514 inline
515 std::ostream &operator<<(std::ostream &os, const Sampler &v) {
516  os << "{\"input\":" << toJSON(v.input)
517  << ",\"output\":"<< toJSON(v.output)
518  << ",\"interpolation\":\"" << to_string(v.interpolation) << "\""
519  << "}";
520  return os;
521 }
522 
523 struct Target {
524  enum struct Path {
525  translation,
526  rotation
527  };
528 
529  size_t node;
531 };
532 
533 inline
534 std::string to_string(Target::Path path) {
535  switch (path) {
537  return "translation";
539  return "rotation";
540  }
541  std::abort();
542 }
543 
544 inline
545 std::ostream &operator<<(std::ostream &os, const Target &v) {
546  os << "{\"node\":" << toJSON(v.node)
547  << ",\"path\":\"" << to_string(v.path) << "\"}";
548  return os;
549 }
550 
551 struct Channel {
552  size_t sampler;
554 };
555 
556 inline
557 std::ostream &operator<<(std::ostream &os, const Channel &v) {
558  os << "{\"sampler\":" << toJSON(v.sampler)
559  << ",\"target\":" << v.target << "}";
560  return os;
561 
562 }
563 
564 struct Animation {
565 // channel/samplers
566  struct Indexes {
567  size_t translation;
568  size_t rotation;
569  };
570  boost::optional<std::string> name;
571  std::vector<Sampler> samplers;
572  std::vector<Channel> channels;
573  size_t add_sampler(const Sampler &v) {
574  samplers.push_back(v);
575  return samplers.size() - 1u;
576  }
577  size_t add_channel(const Channel &v) {
578  channels.push_back(v);
579  return channels.size() - 1u;
580  }
581  /*
582  Indexes addSamplers(size_t time_accessor,
583  size_t translation_accessor,
584  size_t rotation_accessor);
585  Indexes addChannels(Indexes, size_t target_node);
586  void toJSON(std::ostream &os) const;
587  */ //TODO rm?
588 };
589 
590 inline
591 std::ostream &operator<<(std::ostream &os, const Animation &v) {
592 
593  {
594  std::vector<std::string> tmp;
595  for (const auto &a: v.samplers) {
596  tmp.push_back(boost::lexical_cast<std::string>(a));
597  }
598  os << "{\"samplers\":[" << boost::join(tmp, ",") << "]";
599  }
600  {
601  std::vector<std::string> tmp;
602  for (const auto &a: v.channels) {
603  tmp.push_back(boost::lexical_cast<std::string>(a));
604  }
605  os << ",\"channels\":[" << boost::join(tmp, ",") << "]";
606  }
607  os << "}";
608  return os;
609 }
610 
611 class Document {
612 private:
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;
617  }
618 public:
619  std::vector<std::string> buffers;
620  std::vector<std::string> bufferViews;
621  std::vector<std::string> accessors;
622  std::vector<std::string> materials;
623  std::vector<std::string> meshes;
624  std::vector<std::string> cameras;
625  std::vector<std::string> nodes;
626  std::vector<std::string> scenes;
627  std::vector<Animation> animations;
628  boost::optional<size_t> scene;
629  size_t add(const Buffer &v) {return _add(buffers, v);}
630 
631  size_t add(const BufferView &v) {return _add(bufferViews, v);}
632 
633  template<typename T, DataType DTYPE>
634  size_t add(const Accessor<T, DTYPE> &v) {return _add(accessors, v);}
635 
636  size_t add(const Mesh &v) {return _add(meshes, v);}
637 
638  size_t add(const Node &v) {return _add(nodes, v);}
639 
640  size_t add(const Scene &v) {return _add(scenes, v);}
641 
642  size_t add(const Animation &v) {
643  animations.push_back(v);
644  return animations.size() - 1u;
645  }
646 
647  void writeJSON(std::ostream &os) const {
648  os << "{\"asset\": {\"version\": \"2.0\"}"
649  << "\n,\"buffers\":\n ["
650  << boost::join(buffers, "\n ,")
651  << "]\n,\"bufferViews\":\n ["
652  << boost::join(bufferViews, "\n ,")
653  << "]\n,\"accessors\":\n ["
654  << boost::join(accessors, "\n ,")
655  << "]\n,\"meshes\":\n ["
656  << boost::join(meshes, "\n ,") << "]\n";
657  if (!materials.empty()) {
658  os << ",\"materials\":\n ["
659  << boost::join(materials, "\n ,") << "]\n";
660  } if (!cameras.empty()) {
661  os << ",\"cameras\":\n ["
662  << boost::join(cameras, "\n ,") << "]\n";
663  } if (!nodes.empty()) {
664  os << ",\"nodes\":\n ["
665  << boost::join(nodes, "\n ,") << "]\n";
666  } if (!scenes.empty()) {
667  os << ",\"scenes\":\n ["
668  << boost::join(scenes, "\n ,") << "]\n";
669  } if (scene) {
670  os << ",\"scene\":" << toJSON(*scene) << "\n";
671  } if (!animations.empty()) {
672  std::vector<std::string> anims;
673  for (const auto &a: animations)
674  anims.push_back(boost::lexical_cast<std::string>(a));
675  os << ",\"animations\":\n ["
676  << boost::join(anims, "\n ,") << "]\n";
677  }
678  os << "\n}";
679  }
680 };
681 
682 
683 
684 
685 // TODO: move somewhere else
686 
687 inline
689  const std::vector<float> &vecs) {
690  std::ostringstream bs;
692  for (const auto &vec: vecs) {
693  ba.put({vec});
694  }
695  const auto binbuffer = bs.str();
696  const auto binbuffer_length = binbuffer.size();
697  const auto b0 = doc.add(glTF::make_base64_Buffer(binbuffer));
698  const auto bv0 = doc.add(glTF::BufferView{b0, binbuffer_length});
701  ba.count, ba.min, ba.max});
702  return a0;
703 }
704 
705 inline
707  const std::vector<std::array<float, 3>> &vecs) {
708  std::ostringstream bs;
710  for (const auto &vec: vecs) {
711  ba.put(vec);
712  }
713  const auto binbuffer = bs.str();
714  const auto binbuffer_length = binbuffer.size();
715  const auto b0 = doc.add(glTF::make_base64_Buffer(binbuffer));
716  const auto bv0 = doc.add(glTF::BufferView{b0, binbuffer_length});
719  ba.count, ba.min, ba.max});
720  return a0;
721 }
722 
723 inline
725  const std::vector<std::array<float, 3>> &verts,
726  boost::optional<std::string> node_name) {
727  const auto a0 = add_base64_bva(doc, verts);
728  std::stringstream ps;
729  ps << "{\"mode\":" << static_cast<int>(glTF::Mode::LINE_STRIP);
730  //if (material)
731  // ps << ",\"material\":" << *material;
732  ps << ",\"attributes\":{\"POSITION\":" << a0 << "}"
733  << "}";
734  const auto m0 = doc.add(glTF::Mesh{{ps.str()}});
735  const auto n0 = doc.add(glTF::Node{{},
736  node_name,
737  m0});
738  return n0;
739 }
740 
741 inline
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});
749  }
750  return add_path(doc, verts, node_name);
751 }
752 
753 inline
755  size_t node,
756  size_t animation,
757  const std::vector<std::pair<float, Math::Pose2D>> &traj) {
758  auto &anim = doc.animations.at(animation);
759  std::ostringstream time_stream, translation_stream, rotation_stream;
760  glTF::BufferAccessor<float, 1> time_ba(time_stream);
761  glTF::BufferAccessor<float, 3> translation_ba(translation_stream);
762  glTF::BufferAccessor<float, 4> rotation_ba(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()});
769  }
770 
771  auto binbuffer = time_stream.str();
772  auto binbuffer_length = binbuffer.size();
773  auto b = doc.add(make_base64_Buffer(binbuffer));
774  auto bv = doc.add(glTF::BufferView{b, binbuffer_length});
775  const auto time_accessor = doc.add(
778  time_ba.count, time_ba.min, time_ba.max});
779 
780  binbuffer = translation_stream.str();
781  binbuffer_length = binbuffer.size();
782  b = doc.add(make_base64_Buffer(binbuffer));
783  bv = doc.add(glTF::BufferView{b, binbuffer_length});
784  const auto translation_accessor = doc.add(
787  translation_ba.count, translation_ba.min, translation_ba.max});
788 
789  binbuffer = rotation_stream.str();
790  binbuffer_length = binbuffer.size();
791  b = doc.add(make_base64_Buffer(binbuffer));
792  bv = doc.add(glTF::BufferView{b, binbuffer_length});
793  const auto rotation_accessor = doc.add(
796  rotation_ba.count, rotation_ba.min, rotation_ba.max});
797 
798  const auto translation_sampler =
799  anim.add_sampler(glTF::Sampler{
800  time_accessor,
801  translation_accessor,
803  anim.add_channel(
804  glTF::Channel{translation_sampler,
805  glTF::Target{node,
807 
808  const auto rotation_sampler =
809  anim.add_sampler(glTF::Sampler{
810  time_accessor,
811  rotation_accessor,
813  anim.add_channel(
814  glTF::Channel{rotation_sampler,
815  glTF::Target{node,
817 }
818 
819 
820 }
821 }
822 }
823 
824 #endif
boost::optional< size_t > byteStride
Definition: gltf.h:343
boost::optional< size_t > scene
Definition: gltf.h:628
Interpolation interpolation
Definition: gltf.h:511
std::array< T, N > value_type
Definition: gltf.h:232
static const DataType type
Definition: gltf.h:364
boost::optional< Eigen::Vector3f > translation
Definition: gltf.h:424
std::array< T, data_type_traits< DTYPE >::number_of_components > max
Definition: gltf.h:365
ComponentType componentType
Definition: gltf.h:362
std::vector< std::string > primitives
Definition: gltf.h:406
AsJSONArray< T, IT > asJSONArray(IT b, IT e)
Definition: gltf.h:307
size_t add(const Mesh &v)
Definition: gltf.h:636
std::vector< std::string > materials
Definition: gltf.h:622
boost::optional< Eigen::Quaternionf > rotation
Definition: gltf.h:422
boost::optional< size_t > camera
Definition: gltf.h:431
boost::optional< std::string > name
Definition: gltf.h:483
std::string to_string(Interpolation val)
Definition: gltf.h:500
static BOOST_CONSTEXPR const char * cstr()
Definition: gltf.h:137
void writeJSON(std::ostream &os) const
Definition: gltf.h:647
void buffer_put(std::ostream &os, char val)
Definition: gltf.h:148
size_t add(const Accessor< T, DTYPE > &v)
Definition: gltf.h:634
boost::optional< std::string > name
Definition: gltf.h:570
std::vector< size_t > children
Definition: gltf.h:428
std::vector< Channel > channels
Definition: gltf.h:572
size_t add_path(glTF::Document &doc, const std::vector< std::array< float, 3 >> &verts, boost::optional< std::string > node_name)
Definition: gltf.h:724
std::vector< Sampler > samplers
Definition: gltf.h:571
boost::optional< std::string > name
Definition: gltf.h:429
boost::optional< rst > matrix
Definition: gltf.h:432
std::vector< std::string > scenes
Definition: gltf.h:626
std::vector< std::string > accessors
Definition: gltf.h:621
AsJSONArray(IT b, IT e)
Definition: gltf.h:279
std::vector< Animation > animations
Definition: gltf.h:627
boost::optional< size_t > byteOffset
Definition: gltf.h:366
static BOOST_CONSTEXPR const char * cstr()
Definition: gltf.h:125
std::string uri
Definition: gltf.h:321
size_t add(const Animation &v)
Definition: gltf.h:642
static BOOST_CONSTEXPR const char * cstr()
Definition: gltf.h:131
std::ostream & operator<<(std::ostream &os, const Buffer &v)
Definition: gltf.h:332
boost::optional< Eigen::Vector3f > scale
Definition: gltf.h:423
size_t add(const Node &v)
Definition: gltf.h:638
boost::optional< size_t > mesh
Definition: gltf.h:430
size_t add(const Scene &v)
Definition: gltf.h:640
std::vector< std::string > bufferViews
Definition: gltf.h:620
std::vector< std::string > cameras
Definition: gltf.h:624
boost::optional< size_t > byteOffset
Definition: gltf.h:342
void put(value_type val)
Definition: gltf.h:241
size_t add_base64_bva(glTF::Document &doc, const std::vector< float > &vecs)
Definition: gltf.h:688
std::array< T, data_type_traits< DTYPE >::number_of_components > min
Definition: gltf.h:365
Buffer make_base64_Buffer(const std::string &binbuffer)
Definition: gltf.h:325
void animate(glTF::Document &doc, size_t node, size_t animation, const std::vector< std::pair< float, Math::Pose2D >> &traj)
Definition: gltf.h:754
static BOOST_CONSTEXPR const char * cstr()
Definition: gltf.h:119
size_t add(const BufferView &v)
Definition: gltf.h:631
std::vector< std::string > nodes
Definition: gltf.h:625
size_t add_sampler(const Sampler &v)
Definition: gltf.h:573
std::string toJSON(T val)
Definition: gltf.h:261
std::string encode64(const std::string &val)
Definition: gltf.h:253
size_t add_channel(const Channel &v)
Definition: gltf.h:577
boost::optional< std::string > name
Definition: gltf.h:407
size_t add(const Buffer &v)
Definition: gltf.h:629
BufferAccessor(std::ostream &os)
Definition: gltf.h:237
friend std::ostream & operator<<(std::ostream &os, const AsJSONArray &a)
Definition: gltf.h:281
std::vector< std::string > buffers
Definition: gltf.h:619
std::vector< size_t > nodes
Definition: gltf.h:482
std::vector< std::string > meshes
Definition: gltf.h:623