| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "impeller/scene/importer/importer.h" |
| |
| #include <array> |
| #include <cstring> |
| #include <functional> |
| #include <iostream> |
| #include <memory> |
| #include <vector> |
| |
| #include "flutter/fml/mapping.h" |
| #include "impeller/geometry/matrix.h" |
| #include "impeller/scene/importer/conversions.h" |
| #include "impeller/scene/importer/scene_flatbuffers.h" |
| #include "impeller/scene/importer/vertices_builder.h" |
| #include "third_party/tinygltf/tiny_gltf.h" |
| |
| namespace impeller { |
| namespace scene { |
| namespace importer { |
| |
| static const std::map<std::string, VerticesBuilder::AttributeType> kAttributes = |
| {{"POSITION", VerticesBuilder::AttributeType::kPosition}, |
| {"NORMAL", VerticesBuilder::AttributeType::kNormal}, |
| {"TANGENT", VerticesBuilder::AttributeType::kTangent}, |
| {"TEXCOORD_0", VerticesBuilder::AttributeType::kTextureCoords}, |
| {"COLOR_0", VerticesBuilder::AttributeType::kColor}}; |
| |
| static bool WithinRange(int index, size_t size) { |
| return index >= 0 && static_cast<size_t>(index) < size; |
| } |
| |
| static bool ProcessMeshPrimitive(const tinygltf::Model& gltf, |
| const tinygltf::Primitive& primitive, |
| fb::MeshPrimitiveT& mesh_primitive) { |
| //--------------------------------------------------------------------------- |
| /// Vertices. |
| /// |
| |
| { |
| VerticesBuilder builder; |
| |
| for (const auto& attribute : primitive.attributes) { |
| auto attribute_type = kAttributes.find(attribute.first); |
| if (attribute_type == kAttributes.end()) { |
| std::cerr << "Vertex attribute \"" << attribute.first |
| << "\" not supported." << std::endl; |
| continue; |
| } |
| |
| const auto accessor = gltf.accessors[attribute.second]; |
| const auto view = gltf.bufferViews[accessor.bufferView]; |
| |
| const auto buffer = gltf.buffers[view.buffer]; |
| const unsigned char* source_start = &buffer.data[view.byteOffset]; |
| |
| VerticesBuilder::ComponentType type; |
| switch (accessor.componentType) { |
| case TINYGLTF_COMPONENT_TYPE_BYTE: |
| type = VerticesBuilder::ComponentType::kSignedByte; |
| break; |
| case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: |
| type = VerticesBuilder::ComponentType::kUnsignedByte; |
| break; |
| case TINYGLTF_COMPONENT_TYPE_SHORT: |
| type = VerticesBuilder::ComponentType::kSignedShort; |
| break; |
| case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: |
| type = VerticesBuilder::ComponentType::kUnsignedShort; |
| break; |
| case TINYGLTF_COMPONENT_TYPE_INT: |
| type = VerticesBuilder::ComponentType::kSignedInt; |
| break; |
| case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: |
| type = VerticesBuilder::ComponentType::kUnsignedInt; |
| break; |
| case TINYGLTF_COMPONENT_TYPE_FLOAT: |
| type = VerticesBuilder::ComponentType::kFloat; |
| break; |
| default: |
| std::cerr << "Skipping attribute \"" << attribute.first |
| << "\" due to invalid component type." << std::endl; |
| continue; |
| } |
| |
| builder.SetAttributeFromBuffer(attribute_type->second, // attribute |
| type, // component_type |
| source_start, // buffer_start |
| accessor.ByteStride(view), // stride_bytes |
| accessor.count); // count |
| } |
| |
| builder.WriteFBVertices(mesh_primitive.vertices); |
| } |
| |
| //--------------------------------------------------------------------------- |
| /// Indices. |
| /// |
| |
| if (!WithinRange(primitive.indices, gltf.accessors.size())) { |
| std::cerr << "Mesh primitive has no index buffer. Skipping." << std::endl; |
| return false; |
| } |
| |
| auto index_accessor = gltf.accessors[primitive.indices]; |
| auto index_view = gltf.bufferViews[index_accessor.bufferView]; |
| |
| auto indices = std::make_unique<fb::IndicesT>(); |
| |
| switch (index_accessor.componentType) { |
| case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: |
| indices->type = fb::IndexType::k16Bit; |
| break; |
| case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: |
| indices->type = fb::IndexType::k32Bit; |
| break; |
| default: |
| std::cerr << "Mesh primitive has unsupported index type " |
| << index_accessor.componentType << ". Skipping."; |
| return false; |
| } |
| indices->count = index_accessor.count; |
| indices->data.resize(index_view.byteLength); |
| const auto* index_buffer = |
| &gltf.buffers[index_view.buffer].data[index_view.byteOffset]; |
| std::memcpy(indices->data.data(), index_buffer, indices->data.size()); |
| |
| mesh_primitive.indices = std::move(indices); |
| |
| return true; |
| } |
| |
| static void ProcessNode(const tinygltf::Model& gltf, |
| const tinygltf::Node& in_node, |
| fb::NodeT& out_node) { |
| //--------------------------------------------------------------------------- |
| /// Transform. |
| /// |
| |
| Matrix transform; |
| if (in_node.translation.size() == 3) { |
| transform = transform * Matrix::MakeTranslation( |
| {static_cast<Scalar>(in_node.translation[0]), |
| static_cast<Scalar>(in_node.translation[0]), |
| static_cast<Scalar>(in_node.translation[0])}); |
| } |
| if (in_node.rotation.size() == 4) { |
| transform = transform * Matrix::MakeRotation(Quaternion( |
| in_node.rotation[0], in_node.rotation[1], |
| in_node.rotation[2], in_node.rotation[3])); |
| } |
| if (in_node.scale.size() == 3) { |
| transform = |
| transform * Matrix::MakeScale({static_cast<Scalar>(in_node.scale[0]), |
| static_cast<Scalar>(in_node.scale[1]), |
| static_cast<Scalar>(in_node.scale[2])}); |
| } |
| if (in_node.matrix.size() == 16) { |
| if (!transform.IsIdentity()) { |
| std::cerr << "The `matrix` attribute of node (name: " << in_node.name |
| << ") is set in addition to one or more of the " |
| "`translation/rotation/scale` attributes. Using only the " |
| "`matrix` " |
| "attribute."; |
| } |
| transform = ToMatrix(in_node.matrix); |
| } |
| out_node.transform = ToFBMatrix(transform); |
| |
| //--------------------------------------------------------------------------- |
| /// Static meshes. |
| /// |
| |
| if (WithinRange(in_node.mesh, gltf.meshes.size())) { |
| auto& mesh = gltf.meshes[in_node.mesh]; |
| for (const auto& primitive : mesh.primitives) { |
| auto mesh_primitive = std::make_unique<fb::MeshPrimitiveT>(); |
| if (!ProcessMeshPrimitive(gltf, primitive, *mesh_primitive)) { |
| continue; |
| } |
| out_node.mesh_primitives.push_back(std::move(mesh_primitive)); |
| } |
| } |
| |
| //--------------------------------------------------------------------------- |
| /// Children. |
| /// |
| |
| for (size_t node_i = 0; node_i < out_node.children.size(); node_i++) { |
| auto child = std::make_unique<fb::NodeT>(); |
| ProcessNode(gltf, gltf.nodes[in_node.children[node_i]], *child); |
| out_node.children.push_back(std::move(child)); |
| } |
| } |
| |
| bool ParseGLTF(const fml::Mapping& source_mapping, fb::SceneT& out_scene) { |
| tinygltf::Model gltf; |
| |
| { |
| tinygltf::TinyGLTF loader; |
| std::string error; |
| std::string warning; |
| bool success = loader.LoadBinaryFromMemory(&gltf, &error, &warning, |
| source_mapping.GetMapping(), |
| source_mapping.GetSize()); |
| if (!warning.empty()) { |
| std::cerr << "Warning while loading GLTF: " << warning << std::endl; |
| } |
| if (!error.empty()) { |
| std::cerr << "Error while loading GLTF: " << error << std::endl; |
| } |
| if (!success) { |
| return false; |
| } |
| } |
| |
| const tinygltf::Scene& scene = gltf.scenes[gltf.defaultScene]; |
| for (size_t node_i = 0; node_i < scene.nodes.size(); node_i++) { |
| auto node = std::make_unique<fb::NodeT>(); |
| ProcessNode(gltf, gltf.nodes[scene.nodes[node_i]], *node); |
| out_scene.children.push_back(std::move(node)); |
| } |
| |
| return true; |
| } |
| |
| } // namespace importer |
| } // namespace scene |
| } // namespace impeller |