Flutter Engine
The Flutter Engine
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
22namespace impeller {
23namespace scene {
24namespace importer {
25
26static const std::map<std::string, VerticesBuilder::AttributeType> kAttributes =
34
35static bool WithinRange(int index, size_t size) {
36 return index >= 0 && static_cast<size_t>(index) < size;
37}
38
39static 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
44static 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
63static 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
192static 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
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) {
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) {
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
278static 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
316static 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
450bool 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
skvx::Vec< 4, float > Vec4
GLenum type
int width() const
Definition: SkImage.h:285
int height() const
Definition: SkImage.h:291
virtual const uint8_t * GetMapping() const =0
virtual size_t GetSize() const =0
static std::unique_ptr< VerticesBuilder > MakeUnskinned()
static std::unique_ptr< VerticesBuilder > MakeSkinned()
struct MyStruct s
const uint8_t uint32_t uint32_t GError ** error
FlTexture * texture
sk_sp< const SkImage > image
Definition: SkRecords.h:269
SkMesh mesh
Definition: SkRecords.h:345
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static bool ProcessMeshPrimitive(const tinygltf::Model &gltf, const tinygltf::Primitive &primitive, fb::MeshPrimitiveT &mesh_primitive)
std::unique_ptr< fb::Matrix > ToFBMatrixUniquePtr(const Matrix &m)
Definition: conversions.cc:62
fb::Color ToFBColor(const Color c)
Definition: conversions.cc:82
static bool MeshPrimitiveIsSkinned(const tinygltf::Primitive &primitive)
Matrix ToMatrix(const std::vector< double > &m)
Definition: conversions.cc:15
static void ProcessAnimation(const tinygltf::Model &gltf, const tinygltf::Animation &in_animation, fb::AnimationT &out_animation)
static void ProcessNode(const tinygltf::Model &gltf, const tinygltf::Node &in_node, fb::NodeT &out_node)
bool ParseGLTF(const fml::Mapping &source_mapping, fb::SceneT &out_scene)
static bool WithinRange(int index, size_t size)
static void ProcessTexture(const tinygltf::Model &gltf, const tinygltf::Texture &in_texture, fb::TextureT &out_texture)
fb::Matrix ToFBMatrix(const Matrix &m)
Definition: conversions.cc:54
static void ProcessMaterial(const tinygltf::Model &gltf, const tinygltf::Material &in_material, fb::MaterialT &out_material)
static const std::map< std::string, VerticesBuilder::AttributeType > kAttributes
float Scalar
Definition: scalar.h:18
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:126