Flutter Impeller
importer_gltf.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <array>
8 #include <cstring>
9 #include <functional>
10 #include <iostream>
11 #include <iterator>
12 #include <memory>
13 #include <vector>
14 
15 #include "flutter/fml/mapping.h"
16 #include "flutter/third_party/tinygltf/tiny_gltf.h"
19 #include "impeller/scene/importer/scene_flatbuffers.h"
21 
22 namespace impeller {
23 namespace scene {
24 namespace importer {
25 
26 static const std::map<std::string, VerticesBuilder::AttributeType> kAttributes =
34 
35 static bool WithinRange(int index, size_t size) {
36  return index >= 0 && static_cast<size_t>(index) < size;
37 }
38 
39 static bool MeshPrimitiveIsSkinned(const tinygltf::Primitive& primitive) {
40  return primitive.attributes.find("JOINTS_0") != primitive.attributes.end() &&
41  primitive.attributes.find("WEIGHTS_0") != primitive.attributes.end();
42 }
43 
44 static void ProcessMaterial(const tinygltf::Model& gltf,
45  const tinygltf::Material& in_material,
46  fb::MaterialT& out_material) {
47  out_material.type = fb::MaterialType::kUnlit;
48  out_material.base_color_factor =
49  ToFBColor(in_material.pbrMetallicRoughness.baseColorFactor);
50  bool base_color_texture_valid =
51  in_material.pbrMetallicRoughness.baseColorTexture.texCoord == 0 &&
52  in_material.pbrMetallicRoughness.baseColorTexture.index >= 0 &&
53  in_material.pbrMetallicRoughness.baseColorTexture.index <
54  static_cast<int32_t>(gltf.textures.size());
55  out_material.base_color_texture =
56  base_color_texture_valid
57  // This is safe because every GLTF input texture is mapped to a
58  // `Scene->texture`.
59  ? in_material.pbrMetallicRoughness.baseColorTexture.index
60  : -1;
61 }
62 
63 static bool ProcessMeshPrimitive(const tinygltf::Model& gltf,
64  const tinygltf::Primitive& primitive,
65  fb::MeshPrimitiveT& mesh_primitive) {
66  //---------------------------------------------------------------------------
67  /// Vertices.
68  ///
69 
70  {
71  bool is_skinned = MeshPrimitiveIsSkinned(primitive);
72  std::unique_ptr<VerticesBuilder> builder =
73  is_skinned ? VerticesBuilder::MakeSkinned()
75 
76  for (const auto& attribute : primitive.attributes) {
77  auto attribute_type = kAttributes.find(attribute.first);
78  if (attribute_type == kAttributes.end()) {
79  std::cerr << "Vertex attribute \"" << attribute.first
80  << "\" not supported." << std::endl;
81  continue;
82  }
83  if (!is_skinned &&
84  (attribute_type->second == VerticesBuilder::AttributeType::kJoints ||
85  attribute_type->second ==
87  // If the primitive doesn't have enough information to be skinned, skip
88  // skinning-related attributes.
89  continue;
90  }
91 
92  const auto accessor = gltf.accessors[attribute.second];
93  const auto view = gltf.bufferViews[accessor.bufferView];
94 
95  const auto buffer = gltf.buffers[view.buffer];
96  const unsigned char* source_start = &buffer.data[view.byteOffset];
97 
99  switch (accessor.componentType) {
100  case TINYGLTF_COMPONENT_TYPE_BYTE:
102  break;
103  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
105  break;
106  case TINYGLTF_COMPONENT_TYPE_SHORT:
108  break;
109  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
111  break;
112  case TINYGLTF_COMPONENT_TYPE_INT:
114  break;
115  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
117  break;
118  case TINYGLTF_COMPONENT_TYPE_FLOAT:
120  break;
121  default:
122  std::cerr << "Skipping attribute \"" << attribute.first
123  << "\" due to invalid component type." << std::endl;
124  continue;
125  }
126 
127  builder->SetAttributeFromBuffer(
128  attribute_type->second, // attribute
129  type, // component_type
130  source_start, // buffer_start
131  accessor.ByteStride(view), // stride_bytes
132  accessor.count); // count
133  }
134 
135  builder->WriteFBVertices(mesh_primitive);
136  }
137 
138  //---------------------------------------------------------------------------
139  /// Indices.
140  ///
141 
142  {
143  if (!WithinRange(primitive.indices, gltf.accessors.size())) {
144  std::cerr << "Mesh primitive has no index buffer. Skipping." << std::endl;
145  return false;
146  }
147 
148  auto index_accessor = gltf.accessors[primitive.indices];
149  auto index_view = gltf.bufferViews[index_accessor.bufferView];
150 
151  auto indices = std::make_unique<fb::IndicesT>();
152 
153  switch (index_accessor.componentType) {
154  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
155  indices->type = fb::IndexType::k16Bit;
156  break;
157  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
158  indices->type = fb::IndexType::k32Bit;
159  break;
160  default:
161  std::cerr << "Mesh primitive has unsupported index type "
162  << index_accessor.componentType << ". Skipping.";
163  return false;
164  }
165  indices->count = index_accessor.count;
166  indices->data.resize(index_view.byteLength);
167  const auto* index_buffer =
168  &gltf.buffers[index_view.buffer].data[index_view.byteOffset];
169  std::memcpy(indices->data.data(), index_buffer, indices->data.size());
170 
171  mesh_primitive.indices = std::move(indices);
172  }
173 
174  //---------------------------------------------------------------------------
175  /// Material.
176  ///
177 
178  {
179  auto material = std::make_unique<fb::MaterialT>();
180  if (primitive.material >= 0 &&
181  primitive.material < static_cast<int>(gltf.materials.size())) {
182  ProcessMaterial(gltf, gltf.materials[primitive.material], *material);
183  } else {
184  material->type = fb::MaterialType::kUnlit;
185  }
186  mesh_primitive.material = std::move(material);
187  }
188 
189  return true;
190 }
191 
192 static void ProcessNode(const tinygltf::Model& gltf,
193  const tinygltf::Node& in_node,
194  fb::NodeT& out_node) {
195  out_node.name = in_node.name;
196  out_node.children = in_node.children;
197 
198  //---------------------------------------------------------------------------
199  /// Transform.
200  ///
201 
202  Matrix transform;
203  if (in_node.scale.size() == 3) {
204  transform =
205  transform * Matrix::MakeScale({static_cast<Scalar>(in_node.scale[0]),
206  static_cast<Scalar>(in_node.scale[1]),
207  static_cast<Scalar>(in_node.scale[2])});
208  }
209  if (in_node.rotation.size() == 4) {
210  transform = transform * Matrix::MakeRotation(Quaternion(
211  in_node.rotation[0], in_node.rotation[1],
212  in_node.rotation[2], in_node.rotation[3]));
213  }
214  if (in_node.translation.size() == 3) {
215  transform = transform * Matrix::MakeTranslation(
216  {static_cast<Scalar>(in_node.translation[0]),
217  static_cast<Scalar>(in_node.translation[1]),
218  static_cast<Scalar>(in_node.translation[2])});
219  }
220  if (in_node.matrix.size() == 16) {
221  if (!transform.IsIdentity()) {
222  std::cerr << "The `matrix` attribute of node (name: " << in_node.name
223  << ") is set in addition to one or more of the "
224  "`translation/rotation/scale` attributes. Using only the "
225  "`matrix` "
226  "attribute.";
227  }
228  transform = ToMatrix(in_node.matrix);
229  }
230  out_node.transform = ToFBMatrixUniquePtr(transform);
231 
232  //---------------------------------------------------------------------------
233  /// Static meshes.
234  ///
235 
236  if (WithinRange(in_node.mesh, gltf.meshes.size())) {
237  auto& mesh = gltf.meshes[in_node.mesh];
238  for (const auto& primitive : mesh.primitives) {
239  auto mesh_primitive = std::make_unique<fb::MeshPrimitiveT>();
240  if (!ProcessMeshPrimitive(gltf, primitive, *mesh_primitive)) {
241  continue;
242  }
243  out_node.mesh_primitives.push_back(std::move(mesh_primitive));
244  }
245  }
246 
247  //---------------------------------------------------------------------------
248  /// Skin.
249  ///
250 
251  if (WithinRange(in_node.skin, gltf.skins.size())) {
252  auto& skin = gltf.skins[in_node.skin];
253 
254  auto ipskin = std::make_unique<fb::SkinT>();
255  ipskin->joints = skin.joints;
256  {
257  std::vector<fb::Matrix> matrices;
258  auto& matrix_accessor = gltf.accessors[skin.inverseBindMatrices];
259  auto& matrix_view = gltf.bufferViews[matrix_accessor.bufferView];
260  auto& matrix_buffer = gltf.buffers[matrix_view.buffer];
261  for (size_t matrix_i = 0; matrix_i < matrix_accessor.count; matrix_i++) {
262  auto* s = reinterpret_cast<const float*>(
263  matrix_buffer.data.data() + matrix_view.byteOffset +
264  matrix_accessor.ByteStride(matrix_view) * matrix_i);
265  Matrix m(s[0], s[1], s[2], s[3], //
266  s[4], s[5], s[6], s[7], //
267  s[8], s[9], s[10], s[11], //
268  s[12], s[13], s[14], s[15]);
269  matrices.push_back(ToFBMatrix(m));
270  }
271  ipskin->inverse_bind_matrices = std::move(matrices);
272  }
273  ipskin->skeleton = skin.skeleton;
274  out_node.skin = std::move(ipskin);
275  }
276 }
277 
278 static void ProcessTexture(const tinygltf::Model& gltf,
279  const tinygltf::Texture& in_texture,
280  fb::TextureT& out_texture) {
281  if (!WithinRange(in_texture.source, gltf.images.size())) {
282  return;
283  }
284  auto& image = gltf.images[in_texture.source];
285 
286  auto embedded = std::make_unique<fb::EmbeddedImageT>();
287  embedded->bytes = image.image;
288  size_t bytes_per_component = 0;
289  switch (image.pixel_type) {
290  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
291  embedded->component_type = fb::ComponentType::k8Bit;
292  bytes_per_component = 1;
293  break;
294  case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
295  embedded->component_type = fb::ComponentType::k16Bit;
296  bytes_per_component = 2;
297  break;
298  default:
299  std::cerr << "Texture component type " << image.pixel_type
300  << " not supported." << std::endl;
301  return;
302  }
303  if (image.image.size() !=
304  bytes_per_component * image.component * image.width * image.height) {
305  std::cerr << "Decompressed texture had unexpected buffer size. Skipping."
306  << std::endl;
307  return;
308  }
309  embedded->component_count = image.component;
310  embedded->width = image.width;
311  embedded->height = image.height;
312  out_texture.embedded_image = std::move(embedded);
313  out_texture.uri = image.uri;
314 }
315 
316 static void ProcessAnimation(const tinygltf::Model& gltf,
317  const tinygltf::Animation& in_animation,
318  fb::AnimationT& out_animation) {
319  out_animation.name = in_animation.name;
320 
321  // std::vector<impeller::fb::ChannelT> channels;
322  std::vector<impeller::fb::ChannelT> translation_channels;
323  std::vector<impeller::fb::ChannelT> rotation_channels;
324  std::vector<impeller::fb::ChannelT> scale_channels;
325  for (auto& in_channel : in_animation.channels) {
326  auto out_channel = fb::ChannelT();
327 
328  out_channel.node = in_channel.target_node;
329  auto& sampler = in_animation.samplers[in_channel.sampler];
330 
331  /// Keyframe times.
332  auto& times_accessor = gltf.accessors[sampler.input];
333  if (times_accessor.count <= 0) {
334  continue; // Nothing to record.
335  }
336  {
337  auto& times_bufferview = gltf.bufferViews[times_accessor.bufferView];
338  auto& times_buffer = gltf.buffers[times_bufferview.buffer];
339  if (times_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
340  std::cerr << "Unexpected component type \""
341  << times_accessor.componentType
342  << "\" for animation channel times accessor. Skipping."
343  << std::endl;
344  continue;
345  }
346  if (times_accessor.type != TINYGLTF_TYPE_SCALAR) {
347  std::cerr << "Unexpected type \"" << times_accessor.type
348  << "\" for animation channel times accessor. Skipping."
349  << std::endl;
350  continue;
351  }
352  for (size_t time_i = 0; time_i < times_accessor.count; time_i++) {
353  const float* time_p = reinterpret_cast<const float*>(
354  times_buffer.data.data() + times_bufferview.byteOffset +
355  times_accessor.ByteStride(times_bufferview) * time_i);
356  out_channel.timeline.push_back(*time_p);
357  }
358  }
359 
360  /// Keyframe values.
361  auto& values_accessor = gltf.accessors[sampler.output];
362  if (values_accessor.count != times_accessor.count) {
363  std::cerr << "Mismatch between time and value accessors for animation "
364  "channel. Skipping."
365  << std::endl;
366  continue;
367  }
368  {
369  auto& values_bufferview = gltf.bufferViews[values_accessor.bufferView];
370  auto& values_buffer = gltf.buffers[values_bufferview.buffer];
371  if (values_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
372  std::cerr << "Unexpected component type \""
373  << values_accessor.componentType
374  << "\" for animation channel values accessor. Skipping."
375  << std::endl;
376  continue;
377  }
378  if (in_channel.target_path == "translation") {
379  if (values_accessor.type != TINYGLTF_TYPE_VEC3) {
380  std::cerr << "Unexpected type \"" << values_accessor.type
381  << "\" for animation channel \"translation\" accessor. "
382  "Skipping."
383  << std::endl;
384  continue;
385  }
386  fb::TranslationKeyframesT keyframes;
387  for (size_t value_i = 0; value_i < values_accessor.count; value_i++) {
388  const float* value_p = reinterpret_cast<const float*>(
389  values_buffer.data.data() + values_bufferview.byteOffset +
390  values_accessor.ByteStride(values_bufferview) * value_i);
391  keyframes.values.push_back(
392  fb::Vec3(value_p[0], value_p[1], value_p[2]));
393  }
394  out_channel.keyframes.Set(std::move(keyframes));
395  translation_channels.push_back(std::move(out_channel));
396  } else if (in_channel.target_path == "rotation") {
397  if (values_accessor.type != TINYGLTF_TYPE_VEC4) {
398  std::cerr << "Unexpected type \"" << values_accessor.type
399  << "\" for animation channel \"rotation\" accessor. "
400  "Skipping."
401  << std::endl;
402  continue;
403  }
404  fb::RotationKeyframesT keyframes;
405  for (size_t value_i = 0; value_i < values_accessor.count; value_i++) {
406  const float* value_p = reinterpret_cast<const float*>(
407  values_buffer.data.data() + values_bufferview.byteOffset +
408  values_accessor.ByteStride(values_bufferview) * value_i);
409  keyframes.values.push_back(
410  fb::Vec4(value_p[0], value_p[1], value_p[2], value_p[3]));
411  }
412  out_channel.keyframes.Set(std::move(keyframes));
413  rotation_channels.push_back(std::move(out_channel));
414  } else if (in_channel.target_path == "scale") {
415  if (values_accessor.type != TINYGLTF_TYPE_VEC3) {
416  std::cerr << "Unexpected type \"" << values_accessor.type
417  << "\" for animation channel \"scale\" accessor. "
418  "Skipping."
419  << std::endl;
420  continue;
421  }
422  fb::ScaleKeyframesT keyframes;
423  for (size_t value_i = 0; value_i < values_accessor.count; value_i++) {
424  const float* value_p = reinterpret_cast<const float*>(
425  values_buffer.data.data() + values_bufferview.byteOffset +
426  values_accessor.ByteStride(values_bufferview) * value_i);
427  keyframes.values.push_back(
428  fb::Vec3(value_p[0], value_p[1], value_p[2]));
429  }
430  out_channel.keyframes.Set(std::move(keyframes));
431  scale_channels.push_back(std::move(out_channel));
432  } else {
433  std::cerr << "Unsupported animation channel target path \""
434  << in_channel.target_path << "\". Skipping." << std::endl;
435  continue;
436  }
437  }
438  }
439 
440  std::vector<std::unique_ptr<impeller::fb::ChannelT>> channels;
441  for (const auto& channel_list :
442  {translation_channels, rotation_channels, scale_channels}) {
443  for (const auto& channel : channel_list) {
444  channels.push_back(std::make_unique<fb::ChannelT>(channel));
445  }
446  }
447  out_animation.channels = std::move(channels);
448 }
449 
450 bool ParseGLTF(const fml::Mapping& source_mapping, fb::SceneT& out_scene) {
451  tinygltf::Model gltf;
452 
453  {
454  tinygltf::TinyGLTF loader;
455  std::string error;
456  std::string warning;
457  bool success = loader.LoadBinaryFromMemory(&gltf, &error, &warning,
458  source_mapping.GetMapping(),
459  source_mapping.GetSize());
460  if (!warning.empty()) {
461  std::cerr << "Warning while loading GLTF: " << warning << std::endl;
462  }
463  if (!error.empty()) {
464  std::cerr << "Error while loading GLTF: " << error << std::endl;
465  }
466  if (!success) {
467  return false;
468  }
469  }
470 
471  const tinygltf::Scene& scene = gltf.scenes[gltf.defaultScene];
472  out_scene.children = scene.nodes;
473 
474  out_scene.transform =
476 
477  for (size_t texture_i = 0; texture_i < gltf.textures.size(); texture_i++) {
478  auto texture = std::make_unique<fb::TextureT>();
479  ProcessTexture(gltf, gltf.textures[texture_i], *texture);
480  out_scene.textures.push_back(std::move(texture));
481  }
482 
483  for (size_t node_i = 0; node_i < gltf.nodes.size(); node_i++) {
484  auto node = std::make_unique<fb::NodeT>();
485  ProcessNode(gltf, gltf.nodes[node_i], *node);
486  out_scene.nodes.push_back(std::move(node));
487  }
488 
489  for (size_t animation_i = 0; animation_i < gltf.animations.size();
490  animation_i++) {
491  auto animation = std::make_unique<fb::AnimationT>();
492  ProcessAnimation(gltf, gltf.animations[animation_i], *animation);
493  out_scene.animations.push_back(std::move(animation));
494  }
495 
496  return true;
497 }
498 
499 } // namespace importer
500 } // namespace scene
501 } // namespace impeller
impeller::scene::importer::VerticesBuilder::AttributeType::kNormal
@ kNormal
impeller::scene::importer::VerticesBuilder::AttributeType::kJoints
@ kJoints
impeller::scene::importer::ParseGLTF
bool ParseGLTF(const fml::Mapping &source_mapping, fb::SceneT &out_scene)
Definition: importer_gltf.cc:450
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::scene::importer::ProcessMaterial
static void ProcessMaterial(const tinygltf::Model &gltf, const tinygltf::Material &in_material, fb::MaterialT &out_material)
Definition: importer_gltf.cc:44
impeller::scene::importer::VerticesBuilder::ComponentType::kUnsignedByte
@ kUnsignedByte
impeller::scene::importer::ProcessMeshPrimitive
static bool ProcessMeshPrimitive(const tinygltf::Model &gltf, const tinygltf::Primitive &primitive, fb::MeshPrimitiveT &mesh_primitive)
Definition: importer_gltf.cc:63
impeller::Matrix::MakeRotation
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:126
importer.h
impeller::scene::importer::VerticesBuilder::AttributeType::kWeights
@ kWeights
impeller::scene::importer::VerticesBuilder::ComponentType::kUnsignedShort
@ kUnsignedShort
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
conversions.h
impeller::scene::importer::VerticesBuilder::ComponentType::kSignedInt
@ kSignedInt
impeller::scene::importer::ProcessNode
static void ProcessNode(const tinygltf::Model &gltf, const tinygltf::Node &in_node, fb::NodeT &out_node)
Definition: importer_gltf.cc:192
matrix.h
impeller::scene::importer::ToFBMatrix
fb::Matrix ToFBMatrix(const Matrix &m)
Definition: conversions.cc:54
impeller::scene::importer::VerticesBuilder::AttributeType::kPosition
@ kPosition
impeller::Quaternion
Definition: quaternion.h:14
impeller::scene::importer::VerticesBuilder::AttributeType::kTangent
@ kTangent
impeller::scene::importer::ProcessTexture
static void ProcessTexture(const tinygltf::Model &gltf, const tinygltf::Texture &in_texture, fb::TextureT &out_texture)
Definition: importer_gltf.cc:278
impeller::scene::importer::VerticesBuilder::MakeSkinned
static std::unique_ptr< VerticesBuilder > MakeSkinned()
Definition: vertices_builder.cc:29
impeller::scene::importer::VerticesBuilder::ComponentType::kUnsignedInt
@ kUnsignedInt
impeller::scene::importer::MeshPrimitiveIsSkinned
static bool MeshPrimitiveIsSkinned(const tinygltf::Primitive &primitive)
Definition: importer_gltf.cc:39
impeller::Matrix::IsIdentity
constexpr bool IsIdentity() const
Definition: matrix.h:350
impeller::scene::importer::ToFBColor
fb::Color ToFBColor(const Color c)
Definition: conversions.cc:82
impeller::scene::importer::VerticesBuilder::ComponentType::kSignedShort
@ kSignedShort
impeller::scene::importer::ToFBMatrixUniquePtr
std::unique_ptr< fb::Matrix > ToFBMatrixUniquePtr(const Matrix &m)
Definition: conversions.cc:62
impeller::scene::importer::ProcessAnimation
static void ProcessAnimation(const tinygltf::Model &gltf, const tinygltf::Animation &in_animation, fb::AnimationT &out_animation)
Definition: importer_gltf.cc:316
impeller::scene::importer::VerticesBuilder::MakeUnskinned
static std::unique_ptr< VerticesBuilder > MakeUnskinned()
Definition: vertices_builder.cc:25
impeller::scene::importer::VerticesBuilder::ComponentType::kSignedByte
@ kSignedByte
vertices_builder.h
impeller::scene::importer::VerticesBuilder::AttributeType::kColor
@ kColor
impeller::scene::importer::VerticesBuilder::ComponentType::kFloat
@ kFloat
impeller::scene::importer::VerticesBuilder::ComponentType
ComponentType
Definition: vertices_builder.h:29
impeller
Definition: aiks_blur_unittests.cc:20
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Vector3
Definition: vector.h:20
impeller::scene::importer::ToMatrix
Matrix ToMatrix(const std::vector< double > &m)
Definition: conversions.cc:15
impeller::scene::importer::VerticesBuilder::AttributeType::kTextureCoords
@ kTextureCoords
impeller::scene::importer::WithinRange
static bool WithinRange(int index, size_t size)
Definition: importer_gltf.cc:35
impeller::scene::importer::kAttributes
static const std::map< std::string, VerticesBuilder::AttributeType > kAttributes
Definition: importer_gltf.cc:26