diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a98c2cb7a7dd0..c52e212556869 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1648,6 +1648,13 @@ FILE: ../../../flutter/impeller/scene/camera.cc FILE: ../../../flutter/impeller/scene/camera.h FILE: ../../../flutter/impeller/scene/geometry.cc FILE: ../../../flutter/impeller/scene/geometry.h +FILE: ../../../flutter/impeller/scene/importer/importer.h +FILE: ../../../flutter/impeller/scene/importer/importer_gltf.cc +FILE: ../../../flutter/impeller/scene/importer/importer_main.cc +FILE: ../../../flutter/impeller/scene/importer/mesh.fbs +FILE: ../../../flutter/impeller/scene/importer/switches.cc +FILE: ../../../flutter/impeller/scene/importer/switches.h +FILE: ../../../flutter/impeller/scene/importer/types.h FILE: ../../../flutter/impeller/scene/material.cc FILE: ../../../flutter/impeller/scene/material.h FILE: ../../../flutter/impeller/scene/scene.cc diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 3098941bf611d..2a0a4ad90eabc 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -5,6 +5,24 @@ import("//flutter/impeller/tools/impeller.gni") import("//flutter/shell/version/version.gni") +impeller_component("utilities") { + # Current versions of libcxx have deprecated some of the UTF-16 string + # conversion APIs. + defines = [ "_LIBCPP_DISABLE_DEPRECATION_WARNINGS" ] + + sources = [ + "utilities.cc", + "utilities.h", + ] + + public_deps = [ + "../base", + "../geometry", + "../runtime_stage", + "//flutter/fml", + ] +} + impeller_component("compiler_lib") { include_dirs = [ "//third_party/vulkan-deps/spirv-cross/src/" ] @@ -34,11 +52,10 @@ impeller_component("compiler_lib") { "switches.h", "types.cc", "types.h", - "utilities.cc", - "utilities.h", ] public_deps = [ + ":utilities", "../base", "../geometry", "../runtime_stage", diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 30d609cd4f138..89cd974c9969f 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -33,6 +33,11 @@ impeller_shaders("shader_fixtures") { } } +scene_importer("geometry_fixtures") { + geometry = [ "flutter_logo.glb" ] + type = "gltf" +} + impellerc("runtime_stages") { shaders = [ "ink_sparkle.frag", @@ -87,6 +92,7 @@ group("fixtures") { public_deps = [ ":file_fixtures", + ":geometry_fixtures", ":shader_fixtures", ] } diff --git a/impeller/fixtures/flutter_logo.glb b/impeller/fixtures/flutter_logo.glb new file mode 100644 index 0000000000000..8a320e662e963 Binary files /dev/null and b/impeller/fixtures/flutter_logo.glb differ diff --git a/impeller/scene/importer/BUILD.gn b/impeller/scene/importer/BUILD.gn new file mode 100644 index 0000000000000..05888e1cba45b --- /dev/null +++ b/impeller/scene/importer/BUILD.gn @@ -0,0 +1,93 @@ +# 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. + +import("//flutter/impeller/tools/impeller.gni") +import("//flutter/shell/version/version.gni") +import("//third_party/flatbuffers/flatbuffers.gni") + +config("runtime_stage_config") { + configs = [ "//flutter/impeller:impeller_public_config" ] + include_dirs = [ "$root_gen_dir/flutter" ] +} + +flatbuffers("importer_flatbuffers") { + flatbuffers = [ "mesh.fbs" ] + public_configs = [ ":runtime_stage_config" ] + public_deps = [ "//third_party/flatbuffers" ] +} + +impeller_component("importer_lib") { + # Current versions of libcxx have deprecated some of the UTF-16 string + # conversion APIs. + defines = [ "_LIBCPP_DISABLE_DEPRECATION_WARNINGS" ] + + sources = [ + "importer.h", + "importer_gltf.cc", + "switches.cc", + "switches.h", + "types.h", + ] + + public_deps = [ + ":importer_flatbuffers", + "../../base", + "../../compiler:utilities", + "../../geometry", + "//flutter/fml", + + # All third_party deps must be reflected below in the scene_importer_license + # target. + # TODO(bdero): Fix tinygltf compilation warnings. + #"//third_party/tinygltf", + ] +} + +generated_file("scene_importer_license") { + source_path = rebase_path(".", "//flutter") + git_url = "https://github.com/flutter/engine/tree/$engine_version" + outputs = [ "$target_gen_dir/LICENSE.scene_importer.md" ] + contents = [ + "# scene_importer", + "", + "This tool is used by the Flutter SDK to import 3D geometry.", + "", + "Source code for this tool: [flutter/engine/$source_path]($git_url/$source_path).", + "", + "## Licenses", + "", + "### scene_importer", + "", + read_file("//flutter/sky/packages/sky_engine/LICENSE", "string"), + "", + + # These licenses are ignored by the main license checker, since they are not + # shipped to end-application binaries and only shipped as part of developer + # tooling in scene_importer. Add them here. + "## Additional open source licenses", + "", + "### tinygltf", + "", + read_file("//third_party/tinygltf/LICENSE", "string"), + ] +} + +group("importer") { + deps = [ + ":scene_importer", + ":scene_importer_license", + ] +} + +impeller_component("scene_importer") { + target_type = "executable" + + sources = [ "importer_main.cc" ] + + deps = [ ":importer_lib" ] + + metadata = { + entitlement_file_path = [ "scene_importer" ] + } +} diff --git a/impeller/scene/importer/importer.h b/impeller/scene/importer/importer.h new file mode 100644 index 0000000000000..69abcf90d510f --- /dev/null +++ b/impeller/scene/importer/importer.h @@ -0,0 +1,19 @@ +// 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 +#include + +#include "flutter/fml/mapping.h" +#include "impeller/scene/importer/mesh_flatbuffers.h" + +namespace impeller { +namespace scene { +namespace importer { + +bool ParseGLTF(const fml::Mapping& source_mapping, fb::MeshT& out_mesh); + +} +} // namespace scene +} // namespace impeller diff --git a/impeller/scene/importer/importer_gltf.cc b/impeller/scene/importer/importer_gltf.cc new file mode 100644 index 0000000000000..b39831a6eba32 --- /dev/null +++ b/impeller/scene/importer/importer_gltf.cc @@ -0,0 +1,24 @@ +// 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 +#include + +#include "impeller/scene/importer/importer.h" + +#include "flutter/fml/mapping.h" + +namespace impeller { +namespace scene { +namespace importer { + +bool ParseGLTF(const fml::Mapping& source_mapping, fb::MeshT& out_mesh) { + // TODO(bdero): Parse source_mapping and populare out_mesh with just the first + // mesh in the GLTF. + return true; +} + +} // namespace importer +} // namespace scene +} // namespace impeller diff --git a/impeller/scene/importer/importer_main.cc b/impeller/scene/importer/importer_main.cc new file mode 100644 index 0000000000000..52538d6c2585f --- /dev/null +++ b/impeller/scene/importer/importer_main.cc @@ -0,0 +1,108 @@ +// 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 +#include + +#include "flutter/fml/backtrace.h" +#include "flutter/fml/command_line.h" +#include "flutter/fml/file.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "impeller/base/strings.h" +#include "impeller/compiler/utilities.h" +#include "impeller/scene/importer/importer.h" +#include "impeller/scene/importer/switches.h" +#include "impeller/scene/importer/types.h" + +#include "third_party/flatbuffers/include/flatbuffers/flatbuffer_builder.h" + +namespace impeller { +namespace scene { +namespace importer { + +// Sets the file access mode of the file at path 'p' to 0644. +static bool SetPermissiveAccess(const std::filesystem::path& p) { + auto permissions = + std::filesystem::perms::owner_read | std::filesystem::perms::owner_write | + std::filesystem::perms::group_read | std::filesystem::perms::others_read; + std::error_code error; + std::filesystem::permissions(p, permissions, error); + if (error) { + std::cerr << "Failed to set access on file '" << p + << "': " << error.message() << std::endl; + return false; + } + return true; +} + +bool Main(const fml::CommandLine& command_line) { + fml::InstallCrashHandler(); + if (command_line.HasOption("help")) { + Switches::PrintHelp(std::cout); + return true; + } + + Switches switches(command_line); + if (!switches.AreValid(std::cerr)) { + std::cerr << "Invalid flags specified." << std::endl; + Switches::PrintHelp(std::cerr); + return false; + } + + auto source_file_mapping = + fml::FileMapping::CreateReadOnly(switches.source_file_name); + if (!source_file_mapping) { + std::cerr << "Could not open input file." << std::endl; + return false; + } + + fb::MeshT mesh; + bool success = false; + switch (switches.input_type) { + case SourceType::kGLTF: + success = ParseGLTF(*source_file_mapping, mesh); + break; + case SourceType::kUnknown: + std::cerr << "Unknown input type." << std::endl; + return false; + } + if (!success) { + std::cerr << "Failed to parse input file." << std::endl; + return false; + } + + flatbuffers::FlatBufferBuilder builder; + builder.Finish(fb::Mesh::Pack(builder, &mesh)); + + auto output_file_name = std::filesystem::absolute( + std::filesystem::current_path() / switches.output_file_name); + fml::NonOwnedMapping mapping(builder.GetCurrentBufferPointer(), + builder.GetSize()); + if (!fml::WriteAtomically(*switches.working_directory, + compiler::Utf8FromPath(output_file_name).c_str(), + mapping)) { + std::cerr << "Could not write file to " << switches.output_file_name + << std::endl; + return false; + } + + // Tools that consume the geometry data expect the access mode to be 0644. + if (!SetPermissiveAccess(output_file_name)) { + return false; + } + + return true; +} + +} // namespace importer +} // namespace scene +} // namespace impeller + +int main(int argc, char const* argv[]) { + return impeller::scene::importer::Main( + fml::CommandLineFromPlatformOrArgcArgv(argc, argv)) + ? EXIT_SUCCESS + : EXIT_FAILURE; +} diff --git a/impeller/scene/importer/mesh.fbs b/impeller/scene/importer/mesh.fbs new file mode 100644 index 0000000000000..87044f1527a0c --- /dev/null +++ b/impeller/scene/importer/mesh.fbs @@ -0,0 +1,37 @@ +// 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. + +namespace impeller.fb; + +struct Vec2 { + x: float; + y: float; +} + +struct Vec3 { + x: float; + y: float; + z: float; +} + +struct Color { + r: float; + g: float; + b: float; +} + +struct Vertex { + position: Vec3; + normal: Vec3; + tangent: Vec3; + texture_coords: Vec2; +} + +table Mesh { + vertices: [Vertex]; + indices: [uint16]; +} + +root_type Mesh; +file_identifier "IPME"; diff --git a/impeller/scene/importer/switches.cc b/impeller/scene/importer/switches.cc new file mode 100644 index 0000000000000..903c87f6ce607 --- /dev/null +++ b/impeller/scene/importer/switches.cc @@ -0,0 +1,96 @@ +// 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/switches.h" + +#include +#include +#include +#include + +#include "flutter/fml/file.h" +#include "impeller/compiler/utilities.h" +#include "impeller/scene/importer/types.h" + +namespace impeller { +namespace scene { +namespace importer { + +static const std::map kKnownSourceTypes = { + {"gltf", SourceType::kGLTF}, +}; + +void Switches::PrintHelp(std::ostream& stream) { + stream << std::endl; + stream << "Scene Importer is an offline 3D geometry file parser." + << std::endl; + stream << "---------------------------------------------------------------" + << std::endl; + stream << "Valid Argument are:" << std::endl; + stream << "--input=" << std::endl; + stream << "[optional] --input-kind={"; + for (const auto& source_type : kKnownSourceTypes) { + stream << source_type.first << ", "; + } + stream << "} (default: gltf)" << std::endl; + stream << "--output=" << std::endl; +} + +Switches::Switches() = default; + +Switches::~Switches() = default; + +static SourceType SourceTypeFromCommandLine( + const fml::CommandLine& command_line) { + auto source_type_option = + command_line.GetOptionValueWithDefault("input-type", "gltf"); + auto source_type_search = kKnownSourceTypes.find(source_type_option); + if (source_type_search == kKnownSourceTypes.end()) { + return SourceType::kUnknown; + } + return source_type_search->second; +} + +Switches::Switches(const fml::CommandLine& command_line) + : working_directory(std::make_shared(fml::OpenDirectory( + compiler::Utf8FromPath(std::filesystem::current_path()).c_str(), + false, // create if necessary, + fml::FilePermission::kRead))), + source_file_name(command_line.GetOptionValueWithDefault("input", "")), + input_type(SourceTypeFromCommandLine(command_line)), + output_file_name(command_line.GetOptionValueWithDefault("output", "")) { + if (!working_directory || !working_directory->is_valid()) { + return; + } +} + +bool Switches::AreValid(std::ostream& explain) const { + bool valid = true; + + if (input_type == SourceType::kUnknown) { + explain << "Unknown input type." << std::endl; + valid = false; + } + + if (!working_directory || !working_directory->is_valid()) { + explain << "Could not figure out working directory." << std::endl; + valid = false; + } + + if (source_file_name.empty()) { + explain << "Input file name was empty." << std::endl; + valid = false; + } + + if (output_file_name.empty()) { + explain << "Target output file name was empty." << std::endl; + valid = false; + } + + return valid; +} + +} // namespace importer +} // namespace scene +} // namespace impeller diff --git a/impeller/scene/importer/switches.h b/impeller/scene/importer/switches.h new file mode 100644 index 0000000000000..d9b298216cc48 --- /dev/null +++ b/impeller/scene/importer/switches.h @@ -0,0 +1,38 @@ +// 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. + +#pragma once + +#include +#include + +#include "flutter/fml/command_line.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/unique_fd.h" +#include "impeller/scene/importer/types.h" + +namespace impeller { +namespace scene { +namespace importer { + +struct Switches { + std::shared_ptr working_directory; + std::string source_file_name; + SourceType input_type; + std::string output_file_name; + + Switches(); + + ~Switches(); + + explicit Switches(const fml::CommandLine& command_line); + + bool AreValid(std::ostream& explain) const; + + static void PrintHelp(std::ostream& stream); +}; + +} // namespace importer +} // namespace scene +} // namespace impeller diff --git a/impeller/scene/importer/types.h b/impeller/scene/importer/types.h new file mode 100644 index 0000000000000..308527fe33e06 --- /dev/null +++ b/impeller/scene/importer/types.h @@ -0,0 +1,18 @@ +// 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. + +#pragma once + +namespace impeller { +namespace scene { +namespace importer { + +enum class SourceType { + kUnknown, + kGLTF, +}; + +} // namespace importer +} // namespace scene +} // namespace impeller diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 320efc9fcb29c..11e775cd54f28 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -24,6 +24,11 @@ declare_args() { # If it is non-empty, it should be the absolute path to impellerc. impeller_use_prebuilt_impellerc = "" + # Whether to use a prebuilt scene_importer. + # If this is the empty string, scene_importer will be built. + # If it is non-empty, it should be the absolute path to scene_importer. + impeller_use_prebuilt_scene_importer = "" + # If enabled, all OpenGL calls will be traced. Because additional trace # overhead may be substantial, this is not enabled by default. impeller_trace_all_gl_calls = false @@ -613,3 +618,47 @@ template("impeller_shaders") { } } } + +# Dispatches to the build or prebuilt scene_importer depending on the value of +# the impeller_use_prebuilt_scene_importer argument. Forwards all variables to +# compiled_action_foreach or action_foreach as appropriate. +template("_scene_importer") { + if (impeller_use_prebuilt_scene_importer == "") { + compiled_action_foreach(target_name) { + forward_variables_from(invoker, "*") + tool = "//flutter/impeller/scene/importer:scene_importer" + } + } else { + action_foreach(target_name) { + forward_variables_from(invoker, "*", [ "args" ]) + script = "//build/gn_run_binary.py" + scene_importer_path = + rebase_path(impeller_use_prebuilt_scene_importer, root_build_dir) + args = [ scene_importer_path ] + invoker.args + } + } +} + +template("scene_importer") { + assert(defined(invoker.geometry), "Geometry input files must be specified.") + assert(defined(invoker.type), + "The type of geometry to be parsed (gltf, etc..).") + + _scene_importer(target_name) { + sources = invoker.geometry + generated_dir = "$target_gen_dir" + + input_type = invoker.type + args = [ + "--input={{source}}", + "--input-type=$input_type", + ] + + output = "$generated_dir/{{source_file_part}}.ipmesh" + output_path = rebase_path(output, root_build_dir) + + args += [ "--output=$output_path" ] + + outputs = [ output ] + } +}