From 8181cf118b42c6057a722d0683e0f9d22ea37308 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 8 Aug 2024 12:12:45 -0700 Subject: [PATCH 1/4] [Impeller] remove scene3d support. --- ci/licenses_golden/excluded_files | 3 - ci/licenses_golden/licenses_flutter | 86 --- display_list/BUILD.gn | 4 - display_list/display_list.cc | 10 - display_list/display_list.h | 7 +- display_list/dl_builder.cc | 8 - display_list/dl_op_records.h | 20 - display_list/effects/dl_color_source.h | 57 -- display_list/skia/dl_sk_conversions.cc | 5 - docs/impeller/Impeller-Scene.md | 19 - impeller/BUILD.gn | 6 - impeller/README.md | 3 - impeller/aiks/BUILD.gn | 1 - impeller/aiks/aiks_unittests.cc | 45 -- impeller/aiks/canvas.cc | 6 - impeller/aiks/color_source.cc | 25 - impeller/aiks/color_source.h | 20 - impeller/display_list/BUILD.gn | 7 - impeller/display_list/dl_dispatcher.cc | 23 - impeller/display_list/dl_unittests.cc | 28 - impeller/entity/BUILD.gn | 8 - impeller/entity/contents/content_context.cc | 9 - impeller/entity/contents/content_context.h | 11 - impeller/entity/contents/scene_contents.cc | 105 ---- impeller/entity/contents/scene_contents.h | 44 -- impeller/fixtures/BUILD.gn | 19 +- impeller/fixtures/flutter_logo_baked.glb | Bin 288244 -> 0 bytes impeller/fixtures/two_triangles.glb | Bin 10048 -> 0 bytes impeller/playground/BUILD.gn | 1 - .../backend/gles/playground_impl_gles.cc | 2 - .../backend/metal/playground_impl_mtl.mm | 3 - .../backend/vulkan/playground_impl_vk.cc | 2 - impeller/scene/BUILD.gn | 60 --- impeller/scene/README.md | 136 ----- impeller/scene/animation/animation.cc | 125 ----- impeller/scene/animation/animation.h | 78 --- impeller/scene/animation/animation_clip.cc | 147 ----- impeller/scene/animation/animation_clip.h | 96 ---- impeller/scene/animation/animation_player.cc | 89 ---- impeller/scene/animation/animation_player.h | 51 -- .../scene/animation/animation_transforms.h | 21 - impeller/scene/animation/property_resolver.cc | 140 ----- impeller/scene/animation/property_resolver.h | 140 ----- impeller/scene/camera.cc | 36 -- impeller/scene/camera.h | 37 -- impeller/scene/geometry.cc | 272 ---------- impeller/scene/geometry.h | 144 ----- impeller/scene/importer/BUILD.gn | 124 ----- impeller/scene/importer/conversions.cc | 96 ---- impeller/scene/importer/conversions.h | 57 -- impeller/scene/importer/importer.h | 24 - impeller/scene/importer/importer_gltf.cc | 501 ------------------ impeller/scene/importer/importer_unittests.cc | 127 ----- impeller/scene/importer/scene.fbs | 198 ------- impeller/scene/importer/scenec_main.cc | 108 ---- impeller/scene/importer/switches.cc | 95 ---- impeller/scene/importer/switches.h | 40 -- impeller/scene/importer/types.h | 21 - impeller/scene/importer/vertices_builder.cc | 241 --------- impeller/scene/importer/vertices_builder.h | 173 ------ impeller/scene/material.cc | 228 -------- impeller/scene/material.h | 142 ----- impeller/scene/mesh.cc | 53 -- impeller/scene/mesh.h | 50 -- impeller/scene/node.cc | 417 --------------- impeller/scene/node.h | 138 ----- impeller/scene/pipeline_key.h | 45 -- impeller/scene/scene.cc | 73 --- impeller/scene/scene.h | 45 -- impeller/scene/scene_context.cc | 115 ---- impeller/scene/scene_context.h | 160 ------ impeller/scene/scene_encoder.cc | 103 ---- impeller/scene/scene_encoder.h | 52 -- impeller/scene/scene_unittests.cc | 305 ----------- impeller/scene/shaders/BUILD.gn | 19 - impeller/scene/shaders/skinned.vert | 75 --- impeller/scene/shaders/unlit.frag | 24 - impeller/scene/shaders/unskinned.vert | 32 -- impeller/scene/skin.cc | 123 ----- impeller/scene/skin.h | 44 -- impeller/tools/args.gni | 3 - lib/snapshot/BUILD.gn | 9 +- lib/snapshot/libraries_experimental.json | 17 - lib/ui/BUILD.gn | 11 - lib/ui/dart_ui.cc | 28 - lib/ui/experiments/scene.dart | 237 --------- lib/ui/experiments/setup_hooks.dart | 51 -- lib/ui/experiments/ui.dart | 51 -- shell/common/shell_test_platform_view_gl.cc | 8 - .../android/android_context_gl_impeller.cc | 8 - .../android/android_context_vk_impeller.cc | 8 - .../FlutterDarwinContextMetalImpeller.mm | 8 - .../embedder/embedder_surface_gl_impeller.cc | 8 - .../embedder_surface_metal_impeller.mm | 8 - shell/testing/tester_main.cc | 7 - tools/gn | 3 - 96 files changed, 5 insertions(+), 6667 deletions(-) delete mode 100644 docs/impeller/Impeller-Scene.md delete mode 100644 impeller/entity/contents/scene_contents.cc delete mode 100644 impeller/entity/contents/scene_contents.h delete mode 100644 impeller/fixtures/flutter_logo_baked.glb delete mode 100644 impeller/fixtures/two_triangles.glb delete mode 100644 impeller/scene/BUILD.gn delete mode 100644 impeller/scene/README.md delete mode 100644 impeller/scene/animation/animation.cc delete mode 100644 impeller/scene/animation/animation.h delete mode 100644 impeller/scene/animation/animation_clip.cc delete mode 100644 impeller/scene/animation/animation_clip.h delete mode 100644 impeller/scene/animation/animation_player.cc delete mode 100644 impeller/scene/animation/animation_player.h delete mode 100644 impeller/scene/animation/animation_transforms.h delete mode 100644 impeller/scene/animation/property_resolver.cc delete mode 100644 impeller/scene/animation/property_resolver.h delete mode 100644 impeller/scene/camera.cc delete mode 100644 impeller/scene/camera.h delete mode 100644 impeller/scene/geometry.cc delete mode 100644 impeller/scene/geometry.h delete mode 100644 impeller/scene/importer/BUILD.gn delete mode 100644 impeller/scene/importer/conversions.cc delete mode 100644 impeller/scene/importer/conversions.h delete mode 100644 impeller/scene/importer/importer.h delete mode 100644 impeller/scene/importer/importer_gltf.cc delete mode 100644 impeller/scene/importer/importer_unittests.cc delete mode 100644 impeller/scene/importer/scene.fbs delete mode 100644 impeller/scene/importer/scenec_main.cc delete mode 100644 impeller/scene/importer/switches.cc delete mode 100644 impeller/scene/importer/switches.h delete mode 100644 impeller/scene/importer/types.h delete mode 100644 impeller/scene/importer/vertices_builder.cc delete mode 100644 impeller/scene/importer/vertices_builder.h delete mode 100644 impeller/scene/material.cc delete mode 100644 impeller/scene/material.h delete mode 100644 impeller/scene/mesh.cc delete mode 100644 impeller/scene/mesh.h delete mode 100644 impeller/scene/node.cc delete mode 100644 impeller/scene/node.h delete mode 100644 impeller/scene/pipeline_key.h delete mode 100644 impeller/scene/scene.cc delete mode 100644 impeller/scene/scene.h delete mode 100644 impeller/scene/scene_context.cc delete mode 100644 impeller/scene/scene_context.h delete mode 100644 impeller/scene/scene_encoder.cc delete mode 100644 impeller/scene/scene_encoder.h delete mode 100644 impeller/scene/scene_unittests.cc delete mode 100644 impeller/scene/shaders/BUILD.gn delete mode 100644 impeller/scene/shaders/skinned.vert delete mode 100644 impeller/scene/shaders/unlit.frag delete mode 100644 impeller/scene/shaders/unskinned.vert delete mode 100644 impeller/scene/skin.cc delete mode 100644 impeller/scene/skin.h delete mode 100644 lib/snapshot/libraries_experimental.json delete mode 100644 lib/ui/experiments/scene.dart delete mode 100644 lib/ui/experiments/setup_hooks.dart delete mode 100644 lib/ui/experiments/ui.dart diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 9551f35100506..bae946a2329bf 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -207,9 +207,6 @@ ../../../flutter/impeller/renderer/renderer_unittests.cc ../../../flutter/impeller/renderer/testing ../../../flutter/impeller/runtime_stage/runtime_stage_unittests.cc -../../../flutter/impeller/scene/README.md -../../../flutter/impeller/scene/importer/importer_unittests.cc -../../../flutter/impeller/scene/scene_unittests.cc ../../../flutter/impeller/shader_archive/shader_archive_unittests.cc ../../../flutter/impeller/tessellator/dart/.dart_tool ../../../flutter/impeller/tessellator/dart/pubspec.yaml diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e94cdb0bb6732..454b4335b8f79 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -42111,8 +42111,6 @@ ORIGIN: ../../../flutter/impeller/entity/contents/radial_gradient_contents.cc + ORIGIN: ../../../flutter/impeller/entity/contents/radial_gradient_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/runtime_effect_contents.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/runtime_effect_contents.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/entity/contents/scene_contents.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/entity/contents/scene_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/solid_color_contents.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/solid_color_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/solid_rrect_blur_contents.cc + ../../../flutter/LICENSE @@ -42525,48 +42523,6 @@ ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_stage.h + ../../../flutt ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/runtime_stage/runtime_stage_types.fbs + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/animation/animation.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/animation/animation.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/animation/animation_clip.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/animation/animation_clip.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/animation/animation_player.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/animation/animation_player.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/animation/animation_transforms.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/animation/property_resolver.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/animation/property_resolver.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/camera.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/camera.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/geometry.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/geometry.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/conversions.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/conversions.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/importer.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/importer_gltf.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/scene.fbs + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/scenec_main.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/switches.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/switches.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/types.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/vertices_builder.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/importer/vertices_builder.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/material.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/material.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/mesh.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/mesh.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/node.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/node.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/pipeline_key.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/scene.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/scene.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/scene_context.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/scene_context.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/scene_encoder.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/scene_encoder.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/shaders/skinned.vert + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/shaders/unlit.frag + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/shaders/unskinned.vert + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/skin.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/scene/skin.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive.fbs + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive.h + ../../../flutter/LICENSE @@ -45411,48 +45367,6 @@ FILE: ../../../flutter/impeller/runtime_stage/runtime_stage.h FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.cc FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_playground.h FILE: ../../../flutter/impeller/runtime_stage/runtime_stage_types.fbs -FILE: ../../../flutter/impeller/scene/animation/animation.cc -FILE: ../../../flutter/impeller/scene/animation/animation.h -FILE: ../../../flutter/impeller/scene/animation/animation_clip.cc -FILE: ../../../flutter/impeller/scene/animation/animation_clip.h -FILE: ../../../flutter/impeller/scene/animation/animation_player.cc -FILE: ../../../flutter/impeller/scene/animation/animation_player.h -FILE: ../../../flutter/impeller/scene/animation/animation_transforms.h -FILE: ../../../flutter/impeller/scene/animation/property_resolver.cc -FILE: ../../../flutter/impeller/scene/animation/property_resolver.h -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/conversions.cc -FILE: ../../../flutter/impeller/scene/importer/conversions.h -FILE: ../../../flutter/impeller/scene/importer/importer.h -FILE: ../../../flutter/impeller/scene/importer/importer_gltf.cc -FILE: ../../../flutter/impeller/scene/importer/scene.fbs -FILE: ../../../flutter/impeller/scene/importer/scenec_main.cc -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/importer/vertices_builder.cc -FILE: ../../../flutter/impeller/scene/importer/vertices_builder.h -FILE: ../../../flutter/impeller/scene/material.cc -FILE: ../../../flutter/impeller/scene/material.h -FILE: ../../../flutter/impeller/scene/mesh.cc -FILE: ../../../flutter/impeller/scene/mesh.h -FILE: ../../../flutter/impeller/scene/node.cc -FILE: ../../../flutter/impeller/scene/node.h -FILE: ../../../flutter/impeller/scene/pipeline_key.h -FILE: ../../../flutter/impeller/scene/scene.cc -FILE: ../../../flutter/impeller/scene/scene.h -FILE: ../../../flutter/impeller/scene/scene_context.cc -FILE: ../../../flutter/impeller/scene/scene_context.h -FILE: ../../../flutter/impeller/scene/scene_encoder.cc -FILE: ../../../flutter/impeller/scene/scene_encoder.h -FILE: ../../../flutter/impeller/scene/shaders/skinned.vert -FILE: ../../../flutter/impeller/scene/shaders/unlit.frag -FILE: ../../../flutter/impeller/scene/shaders/unskinned.vert -FILE: ../../../flutter/impeller/scene/skin.cc -FILE: ../../../flutter/impeller/scene/skin.h FILE: ../../../flutter/impeller/shader_archive/shader_archive.cc FILE: ../../../flutter/impeller/shader_archive/shader_archive.fbs FILE: ../../../flutter/impeller/shader_archive/shader_archive.h diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index ce48a7a35990f..962a408fe8d54 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -12,10 +12,6 @@ if (is_fuchsia) { config("display_list_config") { defines = [] - - if (impeller_enable_3d) { - defines += [ "IMPELLER_ENABLE_3D" ] - } } source_set("display_list") { diff --git a/display_list/display_list.cc b/display_list/display_list.cc index ccfd97e7a963e..0b6cf7a1162be 100644 --- a/display_list/display_list.cc +++ b/display_list/display_list.cc @@ -202,10 +202,6 @@ void DisplayList::Dispatch(DlOpReceiver& receiver, break; FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH) -#ifdef IMPELLER_ENABLE_3D - DL_OP_DISPATCH(SetSceneColorSource) -#endif // IMPELLER_ENABLE_3D - #undef DL_OP_DISPATCH default: @@ -230,9 +226,6 @@ void DisplayList::DisposeOps(const uint8_t* ptr, const uint8_t* end) { break; FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPOSE) -#ifdef IMPELLER_ENABLE_3D - DL_OP_DISPOSE(SetSceneColorSource) -#endif // IMPELLER_ENABLE_3D #undef DL_OP_DISPOSE @@ -270,9 +263,6 @@ static bool CompareOps(const uint8_t* ptrA, break; FOR_EACH_DISPLAY_LIST_OP(DL_OP_EQUALS) -#ifdef IMPELLER_ENABLE_3D - DL_OP_EQUALS(SetSceneColorSource) -#endif // IMPELLER_ENABLE_3D #undef DL_OP_EQUALS diff --git a/display_list/display_list.h b/display_list/display_list.h index 5c3ef29625988..8ef90e606accf 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -140,12 +140,7 @@ namespace flutter { V(DrawShadowTransparentOccluder) #define DL_OP_TO_ENUM_VALUE(name) k##name, -enum class DisplayListOpType { - FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) -#ifdef IMPELLER_ENABLE_3D - DL_OP_TO_ENUM_VALUE(SetSceneColorSource) -#endif // IMPELLER_ENABLE_3D -}; +enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) }; #undef DL_OP_TO_ENUM_VALUE class DlOpReceiver; diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc index 14893c37c0f8c..8c3bd24af7d9a 100644 --- a/display_list/dl_builder.cc +++ b/display_list/dl_builder.cc @@ -250,14 +250,6 @@ void DisplayListBuilder::onSetColorSource(const DlColorSource* source) { Push(0, effect); break; } -#ifdef IMPELLER_ENABLE_3D - case DlColorSourceType::kScene: { - const DlSceneColorSource* scene = source->asScene(); - FML_DCHECK(scene); - Push(0, scene); - break; - } -#endif // IMPELLER_ENABLE_3D } } } diff --git a/display_list/dl_op_records.h b/display_list/dl_op_records.h index aeb0379e2371f..65391ee059fe0 100644 --- a/display_list/dl_op_records.h +++ b/display_list/dl_op_records.h @@ -275,26 +275,6 @@ struct SetRuntimeEffectColorSourceOp : DLOp { } }; -#ifdef IMPELLER_ENABLE_3D -struct SetSceneColorSourceOp : DLOp { - static constexpr auto kType = DisplayListOpType::kSetSceneColorSource; - - explicit SetSceneColorSourceOp(const DlSceneColorSource* source) - : source(source->scene_node(), source->camera_matrix()) {} - - const DlSceneColorSource source; - - void dispatch(DispatchContext& ctx) const { - ctx.receiver.setColorSource(&source); - } - - DisplayListCompare equals(const SetSceneColorSourceOp* other) const { - return (source == other->source) ? DisplayListCompare::kEqual - : DisplayListCompare::kNotEqual; - } -}; -#endif // IMPELLER_ENABLE_3D - // 4 byte header + 16 byte payload uses 24 total bytes (4 bytes unused) struct SetSharedImageFilterOp : DLOp { static constexpr auto kType = DisplayListOpType::kSetSharedImageFilter; diff --git a/display_list/effects/dl_color_source.h b/display_list/effects/dl_color_source.h index 39359d7996a06..96794f30bd977 100644 --- a/display_list/effects/dl_color_source.h +++ b/display_list/effects/dl_color_source.h @@ -20,14 +20,6 @@ #include "third_party/skia/include/core/SkShader.h" -#ifdef IMPELLER_ENABLE_3D -#include "impeller/geometry/matrix.h" // nogncheck -#include "impeller/scene/node.h" // nogncheck -namespace flutter { -class DlSceneColorSource; -} -#endif // IMPELLER_ENABLE_3D - namespace flutter { class DlColorColorSource; @@ -56,9 +48,6 @@ enum class DlColorSourceType { kConicalGradient, kSweepGradient, kRuntimeEffect, -#ifdef IMPELLER_ENABLE_3D - kScene, -#endif // IMPELLER_ENABLE_3D }; class DlColorSource : public DlAttribute { @@ -165,10 +154,6 @@ class DlColorSource : public DlAttribute { return nullptr; } -#ifdef IMPELLER_ENABLE_3D - virtual const DlSceneColorSource* asScene() const { return nullptr; } -#endif // IMPELLER_ENABLE_3D - protected: DlColorSource() = default; @@ -707,48 +692,6 @@ class DlRuntimeEffectColorSource final : public DlColorSource { FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlRuntimeEffectColorSource); }; -#ifdef IMPELLER_ENABLE_3D -class DlSceneColorSource final : public DlColorSource { - public: - DlSceneColorSource(std::shared_ptr node, - impeller::Matrix camera_matrix) - : node_(std::move(node)), camera_matrix_(camera_matrix) {} - - bool isUIThreadSafe() const override { return true; } - - const DlSceneColorSource* asScene() const override { return this; } - - std::shared_ptr shared() const override { - return std::make_shared(node_, camera_matrix_); - } - - DlColorSourceType type() const override { return DlColorSourceType::kScene; } - size_t size() const override { return sizeof(*this); } - - bool is_opaque() const override { return false; } - - std::shared_ptr scene_node() const { return node_; } - - impeller::Matrix camera_matrix() const { return camera_matrix_; } - - protected: - bool equals_(DlColorSource const& other) const override { - FML_DCHECK(other.type() == DlColorSourceType::kScene); - auto that = static_cast(&other); - if (node_ != that->node_) { - return false; - } - return true; - } - - private: - std::shared_ptr node_; - impeller::Matrix camera_matrix_; // the view-projection matrix of the scene. - - FML_DISALLOW_COPY_ASSIGN_AND_MOVE(DlSceneColorSource); -}; -#endif // IMPELLER_ENABLE_3D - } // namespace flutter #endif // FLUTTER_DISPLAY_LIST_EFFECTS_DL_COLOR_SOURCE_H_ diff --git a/display_list/skia/dl_sk_conversions.cc b/display_list/skia/dl_sk_conversions.cc index a1747cfbb26bd..ea946ed45fac8 100644 --- a/display_list/skia/dl_sk_conversions.cc +++ b/display_list/skia/dl_sk_conversions.cc @@ -165,11 +165,6 @@ sk_sp ToSk(const DlColorSource* source) { return runtime_effect->skia_runtime_effect()->makeShader( sk_uniform_data, sk_samplers.data(), sk_samplers.size()); } -#ifdef IMPELLER_ENABLE_3D - case DlColorSourceType::kScene: { - return nullptr; - } -#endif // IMPELLER_ENABLE_3D } } diff --git a/docs/impeller/Impeller-Scene.md b/docs/impeller/Impeller-Scene.md deleted file mode 100644 index dd09db8c79019..0000000000000 --- a/docs/impeller/Impeller-Scene.md +++ /dev/null @@ -1,19 +0,0 @@ -We are excited to have you tinker on [the Impeller Scene Demo presented at Flutter Forward](https://www.youtube.com/live/zKQYGKAe5W8?feature=share&t=7048). While we spend time learning the use-cases and finalizing the API, the functionality for Impeller Scene is behind a compile-time flag. During this time, there are no guarantees around API stability. - -**Compiling the Engine** - -- Configure your Mac host to compile the Flutter Engine by [following the guidance in wiki](../contributing/Setting-up-the-Engine-development-environment.md). -- Ensure that you are on the [main branch of the Flutter Engine](https://github.com/flutter/engine/tree/main). -- Ensure that you are on the [main branch of the Flutter Framework](https://github.com/flutter/flutter/tree/main). -- Configure the host build: `./flutter/tools/gn --enable-impeller-3d --no-lto` -- Configure the iOS build: `./flutter/tools/gn --enable-impeller-3d --no-lto --ios` - - Add the `--simulator --simulator-cpu=arm64` flag to the iOS build if you are going to test on the simulator. -- Build host artifacts (this will take a while): `ninja -C out/host_debug` -- Build iOS artifacts (this will take a while): `ninja -C out/ios_debug` - - If targeting the simulator: `ninja -C out/ios_debug_sim_arm64` -- Clone the demo repository: `git clone https://github.com/bdero/flutter-scene-example.git` and move into the directory. -- Plug in your device or open `Simulator.app`, then run `flutter devices` to note the device identifier. -- Run the demo application: `flutter run -d [device_id] --local-engine ios_debug --local-engine-host host_debug` (or `ios_debug_sim_arm64` if you are running on the Simulator). - - On Silicon Macs, prefer `--local-engine-host host_debug_arm64` (adjusting your `ninja` command above accordingly) - -We hope to continue evolving the API and have it available on the stable channel soon! diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 35bb8f96a4a7c..6ad39ba532e77 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -29,10 +29,6 @@ config("impeller_public_config") { if (impeller_enable_vulkan) { defines += [ "IMPELLER_ENABLE_VULKAN=1" ] } - - if (impeller_enable_3d) { - defines += [ "IMPELLER_ENABLE_3D" ] - } } group("impeller") { @@ -67,7 +63,6 @@ impeller_component("impeller_unittests") { "geometry:geometry_unittests", "renderer/backend/metal:metal_unittests", "runtime_stage:runtime_stage_unittests", - "scene/importer:importer_unittests", "shader_archive:shader_archive_unittests", "tessellator:tessellator_unittests", ] @@ -81,7 +76,6 @@ impeller_component("impeller_unittests") { "geometry:geometry_unittests", "playground", "renderer:renderer_unittests", - "scene:scene_unittests", "typographer:typographer_unittests", ] } diff --git a/impeller/README.md b/impeller/README.md index e6598ff6f6d87..16434934e47b8 100644 --- a/impeller/README.md +++ b/impeller/README.md @@ -124,9 +124,6 @@ states of completion: pre-compiled shaders themselves. Unlike Metal, backends like OpenGL ES and Vulkan don't have such a concept. For these backends, `//impeller/blobcat` is used to create a single shader library to be packaged with the engine. -* **`//impeller/scene`**: Contains an experimental 3D model renderer. This is - currently only exposed via [a special build of the Flutter - Engine](https://github.com/flutter/flutter/wiki/Impeller-Scene). ## The Offline Shader Compilation Pipeline diff --git a/impeller/aiks/BUILD.gn b/impeller/aiks/BUILD.gn index 31da5054b4961..34167b8966541 100644 --- a/impeller/aiks/BUILD.gn +++ b/impeller/aiks/BUILD.gn @@ -102,7 +102,6 @@ template("aiks_unittests_component") { "//flutter/impeller/geometry:geometry_asserts", "//flutter/impeller/golden_tests:golden_playground_test", "//flutter/impeller/playground:playground_test", - "//flutter/impeller/scene", "//flutter/impeller/typographer/backends/stb:typographer_stb_backend", "//flutter/testing:testing_lib", "//flutter/third_party/txt", diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 52ae55bb5c00f..804ed393e1905 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -859,51 +859,6 @@ TEST_P(AiksTest, SaveLayerFiltersScaleWithTransform) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } -#if IMPELLER_ENABLE_3D -TEST_P(AiksTest, SceneColorSource) { - // Load up the scene. - auto mapping = - flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene"); - ASSERT_NE(mapping, nullptr); - - std::shared_ptr gltf_scene = scene::Node::MakeFromFlatbuffer( - *mapping, *GetContext()->GetResourceAllocator()); - ASSERT_NE(gltf_scene, nullptr); - - auto callback = [&](AiksContext& renderer) -> std::optional { - Paint paint; - - static Scalar distance = 2; - static Scalar y_pos = 0; - static Scalar fov = 45; - if (AiksTest::ImGuiBegin("Controls", nullptr, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::SliderFloat("Distance", &distance, 0, 4); - ImGui::SliderFloat("Y", &y_pos, -3, 3); - ImGui::SliderFloat("FOV", &fov, 1, 180); - ImGui::End(); - } - - Scalar angle = GetSecondsElapsed(); - auto camera_position = - Vector3(distance * std::sin(angle), y_pos, -distance * std::cos(angle)); - - paint.color_source = ColorSource::MakeScene( - gltf_scene, - Matrix::MakePerspective(Degrees(fov), GetWindowSize(), 0.1, 1000) * - Matrix::MakeLookAt(camera_position, {0, 0, 0}, {0, 1, 0})); - - Canvas canvas; - canvas.DrawPaint(Paint{.color = Color::MakeRGBA8(0xf9, 0xf9, 0xf9, 0xff)}); - canvas.Scale(GetContentScale()); - canvas.DrawPaint(paint); - return canvas.EndRecordingAsPicture(); - }; - - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} -#endif // IMPELLER_ENABLE_3D - TEST_P(AiksTest, PaintWithFilters) { // validate that a paint with a color filter "HasFilters", no other filters // impact this setting. diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 917df48bd3a82..757b8c9234c6f 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -108,12 +108,6 @@ struct GetTextureColorSourceDataVisitor { std::optional operator()(const std::monostate& data) { return std::nullopt; } - -#if IMPELLER_ENABLE_3D - std::optional operator()(const SceneData& data) { - return std::nullopt; - } -#endif // IMPELLER_ENABLE_3D }; static std::optional GetImageColorSourceData( diff --git a/impeller/aiks/color_source.cc b/impeller/aiks/color_source.cc index bcb2721fc06e0..718a54fbb2022 100644 --- a/impeller/aiks/color_source.cc +++ b/impeller/aiks/color_source.cc @@ -23,11 +23,6 @@ #include "impeller/geometry/scalar.h" #include "impeller/runtime_stage/runtime_stage.h" -#if IMPELLER_ENABLE_3D -#include "impeller/entity/contents/scene_contents.h" // nogncheck -#include "impeller/scene/node.h" // nogncheck -#endif // IMPELLER_ENABLE_3D - namespace impeller { namespace { @@ -142,16 +137,6 @@ struct CreateContentsVisitor { contents->SetColor(paint.color); return contents; } - -#if IMPELLER_ENABLE_3D - std::shared_ptr operator()(const SceneData& data) { - auto contents = std::make_shared(); - contents->SetOpacityFactor(paint.color.alpha); - contents->SetNode(data.scene_node); - contents->SetCameraTransform(data.camera_transform); - return contents; - } -#endif // IMPELLER_ENABLE_3D }; } // namespace @@ -247,16 +232,6 @@ ColorSource ColorSource::MakeRuntimeEffect( return result; } -#if IMPELLER_ENABLE_3D -ColorSource ColorSource::MakeScene(std::shared_ptr scene_node, - Matrix camera_transform) { - ColorSource result; - result.type_ = Type::kScene; - result.color_source_data_ = SceneData{scene_node, camera_transform}; - return result; -} -#endif // IMPELLER_ENABLE_3D - ColorSource::Type ColorSource::GetType() const { return type_; } diff --git a/impeller/aiks/color_source.h b/impeller/aiks/color_source.h index 195f204b9c542..4b3cdf968fbd6 100644 --- a/impeller/aiks/color_source.h +++ b/impeller/aiks/color_source.h @@ -17,10 +17,6 @@ #include "impeller/geometry/point.h" #include "impeller/runtime_stage/runtime_stage.h" -#if IMPELLER_ENABLE_3D -#include "impeller/scene/node.h" // nogncheck -#endif // IMPELLER_ENABLE_3D - namespace impeller { struct Paint; @@ -78,22 +74,12 @@ struct RuntimeEffectData { std::vector texture_inputs; }; -#if IMPELLER_ENABLE_3D -struct SceneData { - std::shared_ptr scene_node; - Matrix camera_transform; -}; -#endif // IMPELLER_ENABLE_3D - using ColorSourceData = std::variant; class ColorSource { @@ -106,7 +92,6 @@ class ColorSource { kConicalGradient, kSweepGradient, kRuntimeEffect, - kScene, }; ColorSource() noexcept; @@ -157,11 +142,6 @@ class ColorSource { std::shared_ptr> uniform_data, std::vector texture_inputs); -#if IMPELLER_ENABLE_3D - static ColorSource MakeScene(std::shared_ptr scene_node, - Matrix camera_transform); -#endif // IMPELLER_ENABLE_3D - Type GetType() const; std::shared_ptr GetContents(const Paint& paint) const; diff --git a/impeller/display_list/BUILD.gn b/impeller/display_list/BUILD.gn index 879800eefea08..fd6a5fb8fa0ce 100644 --- a/impeller/display_list/BUILD.gn +++ b/impeller/display_list/BUILD.gn @@ -44,9 +44,6 @@ impeller_component("display_list") { if (!defined(defines)) { defines = [] } - if (impeller_enable_3d) { - defines += [ "IMPELLER_ENABLE_3D" ] - } } template("display_list_unittests_component") { @@ -79,9 +76,6 @@ template("display_list_unittests_component") { defines = [] } defines += [ "_USE_MATH_DEFINES" ] - if (impeller_enable_3d) { - defines += [ "IMPELLER_ENABLE_3D" ] - } sources = predefined_sources + additional_sources if (defined(invoker.deps)) { @@ -93,7 +87,6 @@ template("display_list_unittests_component") { ":display_list", "../playground:playground_test", "//flutter/impeller/golden_tests:screenshot", - "//flutter/impeller/scene", "//flutter/impeller/typographer/backends/stb:typographer_stb_backend", "//flutter/third_party/txt", ] diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index 3ecd5ef2dcaba..72c96acf8d6f0 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -30,10 +30,6 @@ #include "impeller/geometry/sigma.h" #include "impeller/typographer/font_glyph_pair.h" -#if IMPELLER_ENABLE_3D -#include "impeller/entity/contents/scene_contents.h" -#endif // IMPELLER_ENABLE_3D - namespace impeller { #define UNIMPLEMENTED \ @@ -270,10 +266,6 @@ static std::optional ToColorSourceType( return ColorSource::Type::kSweepGradient; case flutter::DlColorSourceType::kRuntimeEffect: return ColorSource::Type::kRuntimeEffect; -#ifdef IMPELLER_ENABLE_3D - case flutter::DlColorSourceType::kScene: - return ColorSource::Type::kScene; -#endif // IMPELLER_ENABLE_3D } } @@ -419,21 +411,6 @@ void DlDispatcherBase::setColorSource(const flutter::DlColorSource* source) { runtime_stage, uniform_data, texture_inputs); return; } - case ColorSource::Type::kScene: { -#ifdef IMPELLER_ENABLE_3D - const flutter::DlSceneColorSource* scene_color_source = source->asScene(); - std::shared_ptr scene_node = - scene_color_source->scene_node(); - Matrix camera_transform = scene_color_source->camera_matrix(); - - paint_.color_source = - ColorSource::MakeScene(scene_node, camera_transform); -#else // IMPELLER_ENABLE_3D - FML_LOG(ERROR) << "ColorSourceType::kScene can only be used if Impeller " - "Scene is enabled."; -#endif // IMPELLER_ENABLE_3D - return; - } } } diff --git a/impeller/display_list/dl_unittests.cc b/impeller/display_list/dl_unittests.cc index e3946c944e3ef..7869dcad5bee0 100644 --- a/impeller/display_list/dl_unittests.cc +++ b/impeller/display_list/dl_unittests.cc @@ -28,7 +28,6 @@ #include "impeller/geometry/point.h" #include "impeller/geometry/scalar.h" #include "impeller/playground/widgets.h" -#include "impeller/scene/node.h" #include "third_party/imgui/imgui.h" #include "third_party/skia/include/core/SkBlurTypes.h" #include "third_party/skia/include/core/SkClipOp.h" @@ -1602,33 +1601,6 @@ TEST(DisplayListTest, CircleBoundsComputation) { Rect::MakeLTRB(-5, -5, 5, 5)); } -#ifdef IMPELLER_ENABLE_3D -TEST_P(DisplayListTest, SceneColorSource) { - // Load up the scene. - auto mapping = - flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene"); - ASSERT_NE(mapping, nullptr); - - std::shared_ptr gltf_scene = - impeller::scene::Node::MakeFromFlatbuffer( - *mapping, *GetContext()->GetResourceAllocator()); - ASSERT_NE(gltf_scene, nullptr); - - flutter::DisplayListBuilder builder; - - auto color_source = std::make_shared( - gltf_scene, - Matrix::MakePerspective(Degrees(45), GetWindowSize(), 0.1, 1000) * - Matrix::MakeLookAt({3, 2, -5}, {0, 0, 0}, {0, 1, 0})); - - flutter::DlPaint paint = flutter::DlPaint().setColorSource(color_source); - - builder.DrawPaint(paint); - - ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); -} -#endif - TEST_P(DisplayListTest, DrawPaintIgnoresMaskFilter) { flutter::DisplayListBuilder builder; builder.DrawPaint(flutter::DlPaint().setColor(flutter::DlColor::kWhite())); diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index d91d56cdfe5fe..f0d7a369d2b00 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -212,14 +212,6 @@ impeller_component("entity") { "../typographer", ] - if (impeller_enable_3d) { - sources += [ - "contents/scene_contents.cc", - "contents/scene_contents.h", - ] - public_deps += [ "../scene" ] - } - deps = [ "//flutter/fml" ] defines = [ "_USE_MATH_DEFINES" ] } diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 8237013cbafb3..a563c2ca4d89c 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -246,9 +246,6 @@ ContentContext::ContentContext( lazy_glyph_atlas_( std::make_shared(std::move(typographer_context))), tessellator_(std::make_shared()), -#if IMPELLER_ENABLE_3D - scene_context_(std::make_shared(context_)), -#endif // IMPELLER_ENABLE_3D render_target_cache_(render_target_allocator == nullptr ? std::make_shared( context_->GetResourceAllocator()) @@ -546,12 +543,6 @@ fml::StatusOr ContentContext::MakeSubpass( return subpass_target; } -#if IMPELLER_ENABLE_3D -std::shared_ptr ContentContext::GetSceneContext() const { - return scene_context_; -} -#endif // IMPELLER_ENABLE_3D - std::shared_ptr ContentContext::GetTessellator() const { return tessellator_; } diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 8e947d2efa5a3..6814b1415397b 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -73,10 +73,6 @@ #include "impeller/entity/tiled_texture_fill_external.frag.h" #endif // IMPELLER_ENABLE_OPENGLES -#if IMPELLER_ENABLE_3D -#include "impeller/scene/scene_context.h" // nogncheck -#endif - namespace impeller { using FastGradientPipeline = @@ -378,10 +374,6 @@ class ContentContext { bool IsValid() const; -#if IMPELLER_ENABLE_3D - std::shared_ptr GetSceneContext() const; -#endif // IMPELLER_ENABLE_3D - std::shared_ptr GetTessellator() const; std::shared_ptr> GetFastGradientPipeline( @@ -1010,9 +1002,6 @@ class ContentContext { bool is_valid_ = false; std::shared_ptr tessellator_; -#if IMPELLER_ENABLE_3D - std::shared_ptr scene_context_; -#endif // IMPELLER_ENABLE_3D std::shared_ptr render_target_cache_; std::shared_ptr host_buffer_; std::shared_ptr empty_texture_; diff --git a/impeller/entity/contents/scene_contents.cc b/impeller/entity/contents/scene_contents.cc deleted file mode 100644 index 602821a6d237b..0000000000000 --- a/impeller/entity/contents/scene_contents.cc +++ /dev/null @@ -1,105 +0,0 @@ -// 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/entity/contents/scene_contents.h" - -#include "impeller/core/formats.h" -#include "impeller/entity/contents/content_context.h" -#include "impeller/entity/contents/tiled_texture_contents.h" -#include "impeller/entity/entity.h" -#include "impeller/geometry/path_builder.h" -#include "impeller/scene/camera.h" -#include "impeller/scene/scene.h" - -namespace impeller { - -SceneContents::SceneContents() = default; - -SceneContents::~SceneContents() = default; - -void SceneContents::SetCameraTransform(Matrix matrix) { - camera_transform_ = matrix; -} - -void SceneContents::SetNode(std::shared_ptr node) { - node_ = std::move(node); -} - -bool SceneContents::Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - if (!node_) { - return true; - } - - auto coverage = GetCoverage(entity); - if (!coverage.has_value()) { - return true; - } - - // This happens for CoverGeometry (DrawPaint). In this situation, - // Draw the scene to the full layer. - if (coverage.value().IsMaximum()) { - coverage = Rect::MakeSize(pass.GetRenderTargetSize()); - } - - RenderTarget subpass_target; - if (renderer.GetContext()->GetCapabilities()->SupportsOffscreenMSAA()) { - subpass_target = renderer.GetRenderTargetCache()->CreateOffscreenMSAA( - *renderer.GetContext(), // context - ISize(coverage.value().GetSize()), // size - /*mip_count=*/1, - "SceneContents", // label - RenderTarget::AttachmentConfigMSAA{ - .storage_mode = StorageMode::kDeviceTransient, - .resolve_storage_mode = StorageMode::kDevicePrivate, - .load_action = LoadAction::kClear, - .store_action = StoreAction::kMultisampleResolve, - }, // color_attachment_config - RenderTarget::AttachmentConfig{ - .storage_mode = StorageMode::kDeviceTransient, - .load_action = LoadAction::kDontCare, - .store_action = StoreAction::kDontCare, - } // stencil_attachment_config - ); - } else { - subpass_target = renderer.GetRenderTargetCache()->CreateOffscreen( - *renderer.GetContext(), // context - ISize(coverage.value().GetSize()), // size - /*mip_count=*/1, - "SceneContents", // label - RenderTarget::AttachmentConfig{ - .storage_mode = StorageMode::kDevicePrivate, - .load_action = LoadAction::kClear, - .store_action = StoreAction::kStore, - }, // color_attachment_config - RenderTarget::AttachmentConfig{ - .storage_mode = StorageMode::kDeviceTransient, - .load_action = LoadAction::kClear, - .store_action = StoreAction::kDontCare, - } // stencil_attachment_config - ); - } - - if (!subpass_target.IsValid()) { - return false; - } - - scene::Scene scene(renderer.GetSceneContext()); - scene.GetRoot().AddChild(node_); - - if (!scene.Render(subpass_target, camera_transform_)) { - return false; - } - - // Render the texture to the pass. - TiledTextureContents contents; - contents.SetGeometry(GetGeometry()); - contents.SetTexture(subpass_target.GetRenderTargetTexture()); - contents.SetEffectTransform( - Matrix::MakeScale(1 / entity.GetTransform().GetScale())); - return contents.Render(renderer, entity, pass); -} - -} // namespace impeller diff --git a/impeller/entity/contents/scene_contents.h b/impeller/entity/contents/scene_contents.h deleted file mode 100644 index e5b249baf97ff..0000000000000 --- a/impeller/entity/contents/scene_contents.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_ENTITY_CONTENTS_SCENE_CONTENTS_H_ -#define FLUTTER_IMPELLER_ENTITY_CONTENTS_SCENE_CONTENTS_H_ - -#if !IMPELLER_ENABLE_3D -static_assert(false); -#endif - -#include - -#include "impeller/entity/contents/color_source_contents.h" - -#include "impeller/geometry/matrix.h" -#include "impeller/geometry/rect.h" -#include "impeller/scene/node.h" - -namespace impeller { - -class SceneContents final : public ColorSourceContents { - public: - SceneContents(); - - ~SceneContents() override; - - void SetCameraTransform(Matrix matrix); - - void SetNode(std::shared_ptr node); - - // |Contents| - bool Render(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const override; - - private: - Matrix camera_transform_; - std::shared_ptr node_; -}; - -} // namespace impeller - -#endif // FLUTTER_IMPELLER_ENTITY_CONTENTS_SCENE_CONTENTS_H_ diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index c530b9c6c135b..3ed54fae827db 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -78,14 +78,6 @@ impeller_shaders("shader_fixtures") { } } -scenec("scene_fixtures") { - geometry = [ - "flutter_logo_baked.glb", - "two_triangles.glb", - ] - type = "gltf" -} - impellerc("runtime_stages") { shaders = [ "ink_sparkle.frag", @@ -122,7 +114,6 @@ test_fixtures("file_fixtures") { "flutter_gpu_texture.vert", "flutter_gpu_unlit.frag", "flutter_gpu_unlit.vert", - "flutter_logo_baked.glb", "kalimba.jpg", "monkey.png", "multiple_stages.hlsl", @@ -145,7 +136,6 @@ test_fixtures("file_fixtures") { "table_mountain_py.png", "table_mountain_pz.png", "test_texture.frag", - "two_triangles.glb", "types.h", "wtf.otf", "texture_lookup.frag", @@ -154,12 +144,8 @@ test_fixtures("file_fixtures") { fixtures += [ "/System/Library/Fonts/Apple Color Emoji.ttc" ] } fixtures += - filter_include(get_target_outputs(":runtime_stages"), [ "*.iplr" ]) + - filter_include(get_target_outputs(":scene_fixtures"), [ "*.ipscene" ]) - deps = [ - ":runtime_stages", - ":scene_fixtures", - ] + filter_include(get_target_outputs(":runtime_stages"), [ "*.iplr" ]) + deps = [ ":runtime_stages" ] } impellerc("flutter_gpu_shaders") { @@ -194,7 +180,6 @@ group("fixtures") { public_deps = [ ":file_fixtures", ":modern_shader_fixtures", - ":scene_fixtures", ":shader_fixtures", ] } diff --git a/impeller/fixtures/flutter_logo_baked.glb b/impeller/fixtures/flutter_logo_baked.glb deleted file mode 100644 index f786be69b36e859054ccab65c4028e4302927aa0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 288244 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�{m5||7R<_K>;(A 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

2u0dXN9wt6aM6PR5 z%zT(t%{tHXeogBLPVZ)_xbCM?RYYia%hop-EEvNyB{&Lh@5Mw$aP)k$xVhZJNy{vBpPzx>tL-@HGL zw4GPI zdlyrnP*G*vo8j&uOLzYZR54aTO~p!6zR)3<+5cP%I&5zRqe#5%9B(l%@?G}c@PgEeI02K<+TPCP^ z)!k3e85I*uQAeyQH5C@?3t+nUtJ2z8nfK53=3pjN*ZqEuv-_~!L+U{sQ0~WdE#@-! zssK`TWCd6F?MB&sQGo#3=Y84LKdp))bkk7Dq_*%kySF*r{eI2cc~n;0fErx7BF@{H z^O-WJR&(uFu_9KeY0N-X%qbXl9#+vLmcESl_@#>Xm5+yI=7U+9pwW+nkgk%&IipOg zQh-%uU{_2sTijR;6OF8jl3`fOd?fR_hnZ#f2vjP|JEYKO31P>hI??H_0*Qzs)O^km zfvKLuXQaC_=%OWM#ny@vb)(3>FX>6W-6kY6t2nyYhBk& zj^hx>=g*(VIEH%@(ObpgW1yl~Syj*u!MiH{R(*W>@af&x@BQB`jNtW~4=uf2%cya* zdu!K$dtgz;%4KSujh4>a@7z6AS_%eesi>JrNqhLDBUYXM)R%>E%!kLf_xp4AInns9 z&WxhTXqHhls#|5X9k=t{O(KFwc9uAST8Q^D~%0Ay;RC`fQpNVRBTWGFh|2AX+ia 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

!>^}mYbEv1uVI{hXi*kX}lt4HbSO# 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 V`^S7IT>m(&a5v+3)2G$={{>7S@NxhE diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index 6ac8c27b7afe5..5b611bb99a143 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -50,7 +50,6 @@ impeller_component("playground") { "../fixtures:modern_shader_fixtures", "../fixtures:shader_fixtures", "../renderer", - "../scene/shaders", "image:image_skia_backend", "imgui:imgui_impeller_backend", "//flutter/fml", diff --git a/impeller/playground/backend/gles/playground_impl_gles.cc b/impeller/playground/backend/gles/playground_impl_gles.cc index 2ac2b878885f0..dfd0ec7729af0 100644 --- a/impeller/playground/backend/gles/playground_impl_gles.cc +++ b/impeller/playground/backend/gles/playground_impl_gles.cc @@ -123,8 +123,6 @@ ShaderLibraryMappingsForPlayground() { impeller_modern_fixtures_shaders_gles_length), std::make_shared( impeller_imgui_shaders_gles_data, impeller_imgui_shaders_gles_length), - std::make_shared( - impeller_scene_shaders_gles_data, impeller_scene_shaders_gles_length), }; } diff --git a/impeller/playground/backend/metal/playground_impl_mtl.mm b/impeller/playground/backend/metal/playground_impl_mtl.mm index 98615b9e84b16..89cd4a0825328 100644 --- a/impeller/playground/backend/metal/playground_impl_mtl.mm +++ b/impeller/playground/backend/metal/playground_impl_mtl.mm @@ -25,7 +25,6 @@ #include "impeller/renderer/backend/metal/surface_mtl.h" #include "impeller/renderer/backend/metal/texture_mtl.h" #include "impeller/renderer/mtl/compute_shaders.h" -#include "impeller/scene/shaders/mtl/scene_shaders.h" namespace impeller { @@ -49,8 +48,6 @@ impeller_modern_fixtures_shaders_length), std::make_shared(impeller_imgui_shaders_data, impeller_imgui_shaders_length), - std::make_shared(impeller_scene_shaders_data, - impeller_scene_shaders_length), std::make_shared( impeller_compute_shaders_data, impeller_compute_shaders_length) diff --git a/impeller/playground/backend/vulkan/playground_impl_vk.cc b/impeller/playground/backend/vulkan/playground_impl_vk.cc index c3551eb1d016a..aaa61a77bffec 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -46,8 +46,6 @@ ShaderLibraryMappingsForPlayground() { impeller_modern_fixtures_shaders_vk_length), std::make_shared(impeller_imgui_shaders_vk_data, impeller_imgui_shaders_vk_length), - std::make_shared(impeller_scene_shaders_vk_data, - impeller_scene_shaders_vk_length), std::make_shared( impeller_compute_shaders_vk_data, impeller_compute_shaders_vk_length), }; diff --git a/impeller/scene/BUILD.gn b/impeller/scene/BUILD.gn deleted file mode 100644 index d937166b1ff21..0000000000000 --- a/impeller/scene/BUILD.gn +++ /dev/null @@ -1,60 +0,0 @@ -# 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") - -impeller_component("scene") { - sources = [ - "animation/animation.cc", - "animation/animation.h", - "animation/animation_clip.cc", - "animation/animation_clip.h", - "animation/animation_player.cc", - "animation/animation_player.h", - "animation/animation_transforms.h", - "animation/property_resolver.cc", - "animation/property_resolver.h", - "camera.cc", - "camera.h", - "geometry.cc", - "geometry.h", - "material.cc", - "material.h", - "mesh.cc", - "mesh.h", - "node.cc", - "node.h", - "pipeline_key.h", - "scene.cc", - "scene.h", - "scene_context.cc", - "scene_context.h", - "scene_encoder.cc", - "scene_encoder.h", - "skin.cc", - "skin.h", - ] - - public_deps = [ - "../renderer", - "importer:conversions", - "importer:importer_flatbuffers", - "shaders", - ] - - deps = [ "//flutter/fml" ] -} - -impeller_component("scene_unittests") { - testonly = true - - sources = [ "scene_unittests.cc" ] - - deps = [ - ":scene", - "../fixtures", - "../playground:playground_test", - "//flutter/testing:testing_lib", - ] -} diff --git a/impeller/scene/README.md b/impeller/scene/README.md deleted file mode 100644 index c622ed4766c1a..0000000000000 --- a/impeller/scene/README.md +++ /dev/null @@ -1,136 +0,0 @@ -## ⚠️ **Experimental:** Do not use in production! ⚠️ - -# Impeller Scene - -Impeller Scene is an experimental realtime 3D renderer powered by Impeller's -render layer with the following design priorities: - -* Ease of use. -* Suitability for mobile. -* Common case scalability. - -The aim is to create a familiar and flexible scene graph capable of building -complex dynamic scenes for games and beyond. - -## Example - -```cpp -std::shared_ptr context = - /* Create the backend-specific Impeller context */; - -auto allocator = context->GetResourceAllocator(); - -/// Load resources. - -auto dash_gltf = impeller::scene::LoadGLTF(allocator, "models/dash.glb"); -auto environment_hdri = - impeller::scene::LoadHDRI(allocator, "environment/table_mountain.hdr"); - -/// Construct a scene. - -auto scene = impeller::scene::Scene(context); - -scene.Add(dash_gltf.scene); - -auto& dash_player = dash_gltf.scene.CreateAnimationPlayer(); -auto& walk_action = dash_player.CreateClipAction(dash_gltf.GetClip("Walk")); -walk_action.SetLoop(impeller::scene::AnimationAction::kLoopForever); -walk_action.SetWeight(0.7f); -walk_action.Seek(0.0f); -walk_action.Play(); -auto& run_action = dash_player.CreateClipAction(dash_gltf.GetClip("Run")); -run_action.SetLoop(impeller::scene::AnimationAction::kLoopForever); -run_action.SetWeight(0.3f); -run_action.Play(); - -scene.GetRoot().AddChild( - impeller::scene::DirectionalLight( - /* color */ impeller::Color::AntiqueWhite(), - /* intensity */ 5, - /* direction */ {2, 3, 4})); - -Node sphere_node; -Mesh sphere_mesh; -sphere_node.SetGlobalTransform( - Matrix::MakeRotationEuler({kPiOver4, kPiOver4, 0})); - -auto sphere_geometry = - impeller::scene::Geometry::MakeSphere(allocator, /* radius */ 2); - -auto material = impeller::scene::Material::MakeStandard(); -material->SetAlbedo(impeller::Color::Red()); -material->SetRoughness(0.4); -material->SetMetallic(0.2); -// Common properties shared by all materials. -material->SetEnvironmentMap(environment_hdri); -material->SetFlatShaded(true); -material->SetBlendConfig({ - impeller::BlendOperation::kAdd, // color_op - impeller::BlendFactor::kOne, // source_color_factor - impeller::BlendFactor::kOneMinusSourceAlpha, // destination_color_factor - impeller::BlendOperation::kAdd, // alpha_op - impeller::BlendFactor::kOne, // source_alpha_factor - impeller::BlendFactor::kOneMinusSourceAlpha, // destination_alpha_factor -}); -material->SetStencilConfig({ - impeller::StencilOperation::kIncrementClamp, // operation - impeller::CompareFunction::kAlways, // compare -}); -sphere_mesh.AddPrimitive({sphere_geometry, material}); -sphere_node.SetMesh(sphere_mesh); - -Node cube_node; -cube_node.SetLocalTransform(Matrix::MakeTranslation({4, 0, 0})); -Mesh cube_mesh; -auto cube_geometry = impeller::scene::Geometry::MakeCuboid( - allocator, {4, 4, 4}); -cube_mesh.AddPrimitive({cube_geometry, material}); -cube_node.SetMesh(cube_mesh); - -sphere_node.AddChild(cube_node); -scene.GetRoot().AddChild(sphere_node); - -/// Post processing. - -auto dof = impeller::scene::PostProcessingEffect::MakeBokeh( - /* aperture_size */ 0.2, - /* focus_plane_distance */ 50); -scene.SetPostProcessing({dof}); - -/// Render the scene. - -auto renderer = impeller::Renderer(context); - -while(true) { - std::unique_ptr surface = /* Wrap the window surface */; - - const auto& render_target = surface->GetTargetRenderPassDescriptor(); - - /// Render a perspective view. - - auto camera = - impeller::Camera::MakePerspective( - /* fov */ kPiOver4, - /* position */ {50, -30, 50}) - .LookAt( - /* target */ impeller::Vector3::Zero, - /* up */ {0, -1, 0}); - - scene.Render(render_target, camera); - - /// Render an overhead view on the bottom right corner of the screen. - - auto size = render_target.GetRenderTargetSize(); - auto minimap_camera = - impeller::Camera::MakeOrthographic( - /* view */ Rect::MakeLTRB(-100, -100, 100, 100), - /* position */ {0, -50, 0}) - .LookAt( - /* target */ impeller::Vector3::Zero, - /* up */ {0, 0, 1}) - .WithViewport(IRect::MakeXYWH(size.width / 4, size.height / 4, - size.height / 5, size.height / 5)); - - scene.Render(render_target, minimap_camera); -} -``` diff --git a/impeller/scene/animation/animation.cc b/impeller/scene/animation/animation.cc deleted file mode 100644 index 536a3a41595f7..0000000000000 --- a/impeller/scene/animation/animation.cc +++ /dev/null @@ -1,125 +0,0 @@ -// 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/animation/animation.h" - -#include -#include -#include -#include - -#include "impeller/geometry/quaternion.h" -#include "impeller/scene/importer/scene_flatbuffers.h" -#include "impeller/scene/node.h" - -namespace impeller { -namespace scene { - -std::shared_ptr Animation::MakeFromFlatbuffer( - const fb::Animation& animation, - const std::vector>& scene_nodes) { - auto result = std::shared_ptr(new Animation()); - - result->name_ = animation.name()->str(); - for (auto channel : *animation.channels()) { - if (channel->node() < 0 || - static_cast(channel->node()) >= scene_nodes.size() || - !channel->timeline()) { - continue; - } - - Animation::Channel out_channel; - out_channel.bind_target.node_name = scene_nodes[channel->node()]->GetName(); - - auto* times = channel->timeline(); - std::vector out_times; - out_times.resize(channel->timeline()->size()); - std::copy(times->begin(), times->end(), out_times.begin()); - - // TODO(bdero): Why are the entries in the keyframe value arrays not - // contiguous in the flatbuffer? We should be able to get rid - // of the subloops below and just memcpy instead. - switch (channel->keyframes_type()) { - case fb::Keyframes::TranslationKeyframes: { - out_channel.bind_target.property = Animation::Property::kTranslation; - auto* keyframes = channel->keyframes_as_TranslationKeyframes(); - if (!keyframes->values()) { - continue; - } - std::vector out_values; - out_values.resize(keyframes->values()->size()); - for (size_t value_i = 0; value_i < keyframes->values()->size(); - value_i++) { - auto val = (*keyframes->values())[value_i]; - out_values[value_i] = Vector3(val->x(), val->y(), val->z()); - } - out_channel.resolver = PropertyResolver::MakeTranslationTimeline( - std::move(out_times), std::move(out_values)); - break; - } - case fb::Keyframes::RotationKeyframes: { - out_channel.bind_target.property = Animation::Property::kRotation; - auto* keyframes = channel->keyframes_as_RotationKeyframes(); - if (!keyframes->values()) { - continue; - } - std::vector out_values; - out_values.resize(keyframes->values()->size()); - for (size_t value_i = 0; value_i < keyframes->values()->size(); - value_i++) { - auto val = (*keyframes->values())[value_i]; - out_values[value_i] = - Quaternion(val->x(), val->y(), val->z(), val->w()); - } - out_channel.resolver = PropertyResolver::MakeRotationTimeline( - std::move(out_times), std::move(out_values)); - break; - } - case fb::Keyframes::ScaleKeyframes: { - out_channel.bind_target.property = Animation::Property::kScale; - auto* keyframes = channel->keyframes_as_ScaleKeyframes(); - if (!keyframes->values()) { - continue; - } - std::vector out_values; - out_values.resize(keyframes->values()->size()); - for (size_t value_i = 0; value_i < keyframes->values()->size(); - value_i++) { - auto val = (*keyframes->values())[value_i]; - out_values[value_i] = Vector3(val->x(), val->y(), val->z()); - } - out_channel.resolver = PropertyResolver::MakeScaleTimeline( - std::move(out_times), std::move(out_values)); - break; - } - case fb::Keyframes::NONE: - continue; - } - - result->end_time_ = - std::max(result->end_time_, out_channel.resolver->GetEndTime()); - result->channels_.push_back(std::move(out_channel)); - } - - return result; -} - -Animation::Animation() = default; - -Animation::~Animation() = default; - -const std::string& Animation::GetName() const { - return name_; -} - -const std::vector& Animation::GetChannels() const { - return channels_; -} - -SecondsF Animation::GetEndTime() const { - return end_time_; -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/animation/animation.h b/impeller/scene/animation/animation.h deleted file mode 100644 index 7087bedc284fb..0000000000000 --- a/impeller/scene/animation/animation.h +++ /dev/null @@ -1,78 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_H_ -#define FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_H_ - -#include -#include -#include - -#include "flutter/fml/hash_combine.h" -#include "impeller/base/timing.h" -#include "impeller/scene/animation/property_resolver.h" -#include "impeller/scene/importer/scene_flatbuffers.h" - -namespace impeller { -namespace scene { - -class Node; - -class Animation final { - public: - static std::shared_ptr MakeFromFlatbuffer( - const fb::Animation& animation, - const std::vector>& scene_nodes); - - enum class Property { - kTranslation, - kRotation, - kScale, - }; - - struct BindKey { - std::string node_name; - Property property = Property::kTranslation; - - struct Hash { - std::size_t operator()(const BindKey& o) const { - return fml::HashCombine(o.node_name, o.property); - } - }; - - struct Equal { - bool operator()(const BindKey& lhs, const BindKey& rhs) const { - return lhs.node_name == rhs.node_name && lhs.property == rhs.property; - } - }; - }; - - struct Channel { - BindKey bind_target; - std::unique_ptr resolver; - }; - ~Animation(); - - const std::string& GetName() const; - - const std::vector& GetChannels() const; - - SecondsF GetEndTime() const; - - private: - Animation(); - - std::string name_; - std::vector channels_; - SecondsF end_time_; - - Animation(const Animation&) = delete; - - Animation& operator=(const Animation&) = delete; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_H_ diff --git a/impeller/scene/animation/animation_clip.cc b/impeller/scene/animation/animation_clip.cc deleted file mode 100644 index 4a03bf58ba5b5..0000000000000 --- a/impeller/scene/animation/animation_clip.cc +++ /dev/null @@ -1,147 +0,0 @@ -// 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/animation/animation_clip.h" - -#include -#include -#include -#include - -#include "impeller/scene/node.h" - -namespace impeller { -namespace scene { - -AnimationClip::AnimationClip(std::shared_ptr animation, - Node* bind_target) - : animation_(std::move(animation)) { - BindToTarget(bind_target); -} - -AnimationClip::~AnimationClip() = default; - -AnimationClip::AnimationClip(AnimationClip&&) = default; -AnimationClip& AnimationClip::operator=(AnimationClip&&) = default; - -bool AnimationClip::IsPlaying() const { - return playing_; -} - -void AnimationClip::SetPlaying(bool playing) { - playing_ = playing; -} - -void AnimationClip::Play() { - SetPlaying(true); -} - -void AnimationClip::Pause() { - SetPlaying(false); -} - -void AnimationClip::Stop() { - SetPlaying(false); - Seek(SecondsF::zero()); -} - -bool AnimationClip::GetLoop() const { - return loop_; -} - -void AnimationClip::SetLoop(bool looping) { - loop_ = looping; -} - -Scalar AnimationClip::GetPlaybackTimeScale() const { - return playback_time_scale_; -} - -void AnimationClip::SetPlaybackTimeScale(Scalar playback_speed) { - playback_time_scale_ = playback_speed; -} - -Scalar AnimationClip::GetWeight() const { - return weight_; -} - -void AnimationClip::SetWeight(Scalar weight) { - weight_ = std::max(0.0f, weight); -} - -SecondsF AnimationClip::GetPlaybackTime() const { - return playback_time_; -} - -void AnimationClip::Seek(SecondsF time) { - playback_time_ = std::clamp(time, SecondsF::zero(), animation_->GetEndTime()); -} - -void AnimationClip::Advance(SecondsF delta_time) { - if (!playing_ || delta_time <= SecondsF::zero()) { - return; - } - delta_time *= playback_time_scale_; - playback_time_ += delta_time; - - /// Handle looping behavior. - - auto end_time = animation_->GetEndTime(); - if (end_time == SecondsF::zero()) { - playback_time_ = SecondsF::zero(); - return; - } - if (!loop_ && - (playback_time_ < SecondsF::zero() || playback_time_ > end_time)) { - // If looping is disabled, clamp to the end (or beginning, if playing in - // reverse) and pause. - Pause(); - playback_time_ = std::clamp(playback_time_, SecondsF::zero(), end_time); - } else if (/* loop && */ playback_time_ > end_time) { - // If looping is enabled and we ran off the end, loop to the beginning. - playback_time_ = - SecondsF(std::fmod(std::abs(playback_time_.count()), end_time.count())); - } else if (/* loop && */ playback_time_ < SecondsF::zero()) { - // If looping is enabled and we ran off the beginning, loop to the end. - playback_time_ = - end_time - - SecondsF(std::fmod(std::abs(playback_time_.count()), end_time.count())); - } -} - -void AnimationClip::ApplyToBindings( - std::unordered_map& transform_decomps, - Scalar weight_multiplier) const { - for (auto& binding : bindings_) { - auto transforms = transform_decomps.find(binding.node); - if (transforms == transform_decomps.end()) { - continue; - } - binding.channel.resolver->Apply(transforms->second, playback_time_, - weight_ * weight_multiplier); - } -} - -void AnimationClip::BindToTarget(Node* node) { - const auto& channels = animation_->GetChannels(); - bindings_.clear(); - bindings_.reserve(channels.size()); - - for (const auto& channel : channels) { - Node* channel_target; - if (channel.bind_target.node_name == node->GetName()) { - channel_target = node; - } else if (auto result = - node->FindChildByName(channel.bind_target.node_name, true)) { - channel_target = result.get(); - } else { - continue; - } - bindings_.push_back( - ChannelBinding{.channel = channel, .node = channel_target}); - } -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/animation/animation_clip.h b/impeller/scene/animation/animation_clip.h deleted file mode 100644 index 7b7c9bb99064d..0000000000000 --- a/impeller/scene/animation/animation_clip.h +++ /dev/null @@ -1,96 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_CLIP_H_ -#define FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_CLIP_H_ - -#include -#include -#include - -#include "impeller/scene/animation/animation.h" -#include "impeller/scene/animation/animation_transforms.h" - -namespace impeller { -namespace scene { - -class Node; -class AnimationPlayer; - -class AnimationClip final { - public: - AnimationClip(std::shared_ptr animation, Node* bind_target); - ~AnimationClip(); - - AnimationClip(AnimationClip&&); - AnimationClip& operator=(AnimationClip&&); - - bool IsPlaying() const; - - void SetPlaying(bool playing); - - void Play(); - - void Pause(); - - void Stop(); - - bool GetLoop() const; - - void SetLoop(bool looping); - - Scalar GetPlaybackTimeScale() const; - - /// @brief Sets the animation playback speed. Negative values make the clip - /// play in reverse. - void SetPlaybackTimeScale(Scalar playback_speed); - - Scalar GetWeight() const; - - void SetWeight(Scalar weight); - - /// @brief Get the current playback time of the animation. - SecondsF GetPlaybackTime() const; - - /// @brief Move the animation to the specified time. The given `time` is - /// clamped to the animation's playback range. - void Seek(SecondsF time); - - /// @brief Advance the animation by `delta_time` seconds. Negative - /// `delta_time` values do nothing. - void Advance(SecondsF delta_time); - - /// @brief Applies the animation to all binded properties in the scene. - void ApplyToBindings( - std::unordered_map& transform_decomps, - Scalar weight_multiplier) const; - - private: - void BindToTarget(Node* node); - - struct ChannelBinding { - const Animation::Channel& channel; - Node* node; - }; - - std::shared_ptr animation_; - std::vector bindings_; - - SecondsF playback_time_; - Scalar playback_time_scale_ = 1; // Seconds multiplier, can be negative. - Scalar weight_ = 1; - bool playing_ = false; - bool loop_ = false; - - AnimationClip(const AnimationClip&) = delete; - - AnimationClip& operator=(const AnimationClip&) = delete; - - friend AnimationPlayer; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_CLIP_H_ diff --git a/impeller/scene/animation/animation_player.cc b/impeller/scene/animation/animation_player.cc deleted file mode 100644 index 9fcf53306879f..0000000000000 --- a/impeller/scene/animation/animation_player.cc +++ /dev/null @@ -1,89 +0,0 @@ -// 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/animation/animation_player.h" - -#include -#include - -#include "flutter/fml/time/time_point.h" -#include "impeller/base/timing.h" -#include "impeller/scene/node.h" - -namespace impeller { -namespace scene { - -AnimationPlayer::AnimationPlayer() = default; -AnimationPlayer::~AnimationPlayer() = default; - -AnimationPlayer::AnimationPlayer(AnimationPlayer&&) = default; -AnimationPlayer& AnimationPlayer::operator=(AnimationPlayer&&) = default; - -AnimationClip* AnimationPlayer::AddAnimation( - const std::shared_ptr& animation, - Node* bind_target) { - if (!animation) { - VALIDATION_LOG << "Cannot add null animation."; - return nullptr; - } - - AnimationClip clip(animation, bind_target); - - // Record all of the unique default transforms that this AnimationClip - // will mutate. - for (const auto& binding : clip.bindings_) { - auto decomp = binding.node->GetLocalTransform().Decompose(); - if (!decomp.has_value()) { - continue; - } - target_transforms_.insert( - {binding.node, AnimationTransforms{.bind_pose = decomp.value()}}); - } - - auto result = clips_.insert({animation->GetName(), std::move(clip)}); - return &result.first->second; -} - -AnimationClip* AnimationPlayer::GetClip(const std::string& name) const { - auto result = clips_.find(name); - if (result == clips_.end()) { - return nullptr; - } - return const_cast(&result->second); -} - -void AnimationPlayer::Update() { - if (!previous_time_.has_value()) { - previous_time_ = Clock::now(); - } - auto new_time = Clock::now(); - auto delta_time = new_time - previous_time_.value(); - previous_time_ = new_time; - - // Reset the animated pose state. - for (auto& [node, transforms] : target_transforms_) { - transforms.animated_pose = transforms.bind_pose; - } - - // Compute a weight multiplier for normalizing the animation. - Scalar total_weight = 0; - for (auto& [_, clip] : clips_) { - total_weight += clip.GetWeight(); - } - Scalar weight_multiplier = total_weight > 1 ? 1 / total_weight : 1; - - // Update and apply all clips to the animation pose state. - for (auto& [_, clip] : clips_) { - clip.Advance(delta_time); - clip.ApplyToBindings(target_transforms_, weight_multiplier); - } - - // Apply the animated pose to the bound joints. - for (auto& [node, transforms] : target_transforms_) { - node->SetLocalTransform(Matrix(transforms.animated_pose)); - } -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/animation/animation_player.h b/impeller/scene/animation/animation_player.h deleted file mode 100644 index 78b56f2f5341e..0000000000000 --- a/impeller/scene/animation/animation_player.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_PLAYER_H_ -#define FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_PLAYER_H_ - -#include -#include -#include - -#include "impeller/base/timing.h" -#include "impeller/scene/animation/animation_clip.h" - -namespace impeller { -namespace scene { - -class Node; - -class AnimationPlayer final { - public: - AnimationPlayer(); - ~AnimationPlayer(); - - AnimationPlayer(AnimationPlayer&&); - AnimationPlayer& operator=(AnimationPlayer&&); - - AnimationClip* AddAnimation(const std::shared_ptr& animation, - Node* bind_target); - - AnimationClip* GetClip(const std::string& name) const; - - /// @brief Advanced all clips and updates animated properties in the scene. - void Update(); - - private: - std::unordered_map target_transforms_; - - std::map clips_; - - std::optional previous_time_; - - AnimationPlayer(const AnimationPlayer&) = delete; - - AnimationPlayer& operator=(const AnimationPlayer&) = delete; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_PLAYER_H_ diff --git a/impeller/scene/animation/animation_transforms.h b/impeller/scene/animation/animation_transforms.h deleted file mode 100644 index 59adb64938492..0000000000000 --- a/impeller/scene/animation/animation_transforms.h +++ /dev/null @@ -1,21 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_TRANSFORMS_H_ -#define FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_TRANSFORMS_H_ - -#include "impeller/geometry/matrix_decomposition.h" - -namespace impeller { -namespace scene { - -struct AnimationTransforms { - MatrixDecomposition bind_pose; - MatrixDecomposition animated_pose; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_ANIMATION_ANIMATION_TRANSFORMS_H_ diff --git a/impeller/scene/animation/property_resolver.cc b/impeller/scene/animation/property_resolver.cc deleted file mode 100644 index a43a308efdb75..0000000000000 --- a/impeller/scene/animation/property_resolver.cc +++ /dev/null @@ -1,140 +0,0 @@ -// 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/animation/property_resolver.h" - -#include -#include -#include - -#include "impeller/geometry/matrix_decomposition.h" -#include "impeller/geometry/point.h" -#include "impeller/scene/node.h" - -namespace impeller { -namespace scene { - -std::unique_ptr -PropertyResolver::MakeTranslationTimeline(std::vector times, - std::vector values) { - FML_DCHECK(times.size() == values.size()); - auto result = std::unique_ptr( - new TranslationTimelineResolver()); - result->times_ = std::move(times); - result->values_ = std::move(values); - return result; -} - -std::unique_ptr -PropertyResolver::MakeRotationTimeline(std::vector times, - std::vector values) { - FML_DCHECK(times.size() == values.size()); - auto result = - std::unique_ptr(new RotationTimelineResolver()); - result->times_ = std::move(times); - result->values_ = std::move(values); - return result; -} - -std::unique_ptr PropertyResolver::MakeScaleTimeline( - std::vector times, - std::vector values) { - FML_DCHECK(times.size() == values.size()); - auto result = - std::unique_ptr(new ScaleTimelineResolver()); - result->times_ = std::move(times); - result->values_ = std::move(values); - return result; -} - -PropertyResolver::~PropertyResolver() = default; - -TimelineResolver::~TimelineResolver() = default; - -SecondsF TimelineResolver::GetEndTime() { - if (times_.empty()) { - return SecondsF::zero(); - } - return SecondsF(times_.back()); -} - -TimelineResolver::TimelineKey TimelineResolver::GetTimelineKey(SecondsF time) { - if (times_.size() <= 1 || time.count() <= times_.front()) { - return {.index = 0, .lerp = 1}; - } - if (time.count() >= times_.back()) { - return {.index = times_.size() - 1, .lerp = 1}; - } - auto it = std::lower_bound(times_.begin(), times_.end(), time.count()); - size_t index = std::distance(times_.begin(), it); - - Scalar previous_time = *(it - 1); - Scalar next_time = *it; - return {.index = index, - .lerp = (time.count() - previous_time) / (next_time - previous_time)}; -} - -TranslationTimelineResolver::TranslationTimelineResolver() = default; - -TranslationTimelineResolver::~TranslationTimelineResolver() = default; - -void TranslationTimelineResolver::Apply(AnimationTransforms& target, - SecondsF time, - Scalar weight) { - if (values_.empty()) { - return; - } - auto key = GetTimelineKey(time); - auto value = values_[key.index]; - if (key.lerp < 1) { - value = values_[key.index - 1].Lerp(value, key.lerp); - } - - target.animated_pose.translation += - (value - target.bind_pose.translation) * weight; -} - -RotationTimelineResolver::RotationTimelineResolver() = default; - -RotationTimelineResolver::~RotationTimelineResolver() = default; - -void RotationTimelineResolver::Apply(AnimationTransforms& target, - SecondsF time, - Scalar weight) { - if (values_.empty()) { - return; - } - auto key = GetTimelineKey(time); - auto value = values_[key.index]; - if (key.lerp < 1) { - value = values_[key.index - 1].Slerp(value, key.lerp); - } - - target.animated_pose.rotation = - target.animated_pose.rotation * - Quaternion().Slerp(target.bind_pose.rotation.Invert() * value, weight); -} - -ScaleTimelineResolver::ScaleTimelineResolver() = default; - -ScaleTimelineResolver::~ScaleTimelineResolver() = default; - -void ScaleTimelineResolver::Apply(AnimationTransforms& target, - SecondsF time, - Scalar weight) { - if (values_.empty()) { - return; - } - auto key = GetTimelineKey(time); - auto value = values_[key.index]; - if (key.lerp < 1) { - value = values_[key.index - 1].Lerp(value, key.lerp); - } - - target.animated_pose.scale *= - Vector3(1, 1, 1).Lerp(value / target.bind_pose.scale, weight); -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/animation/property_resolver.h b/impeller/scene/animation/property_resolver.h deleted file mode 100644 index 22cada6c6c43a..0000000000000 --- a/impeller/scene/animation/property_resolver.h +++ /dev/null @@ -1,140 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_ANIMATION_PROPERTY_RESOLVER_H_ -#define FLUTTER_IMPELLER_SCENE_ANIMATION_PROPERTY_RESOLVER_H_ - -#include -#include - -#include "impeller/base/timing.h" -#include "impeller/geometry/quaternion.h" -#include "impeller/geometry/scalar.h" -#include "impeller/geometry/vector.h" -#include "impeller/scene/animation/animation_transforms.h" - -namespace impeller { -namespace scene { - -class Node; -class TranslationTimelineResolver; -class RotationTimelineResolver; -class ScaleTimelineResolver; - -class PropertyResolver { - public: - static std::unique_ptr MakeTranslationTimeline( - std::vector times, - std::vector values); - - static std::unique_ptr MakeRotationTimeline( - std::vector times, - std::vector values); - - static std::unique_ptr MakeScaleTimeline( - std::vector times, - std::vector values); - - virtual ~PropertyResolver(); - - virtual SecondsF GetEndTime() = 0; - - /// @brief Resolve and apply the property value to a target node. This - /// operation is additive; a given node property may be amended by - /// many different PropertyResolvers prior to rendering. For example, - /// an AnimationPlayer may blend multiple Animations together by - /// applying several AnimationClips. - virtual void Apply(AnimationTransforms& target, - SecondsF time, - Scalar weight) = 0; -}; - -class TimelineResolver : public PropertyResolver { - public: - virtual ~TimelineResolver(); - - // |Resolver| - SecondsF GetEndTime(); - - protected: - struct TimelineKey { - /// The index of the closest previous keyframe. - size_t index = 0; - /// Used to interpolate between the resolved values for `timeline_index - 1` - /// and `timeline_index`. The range of this value should always be `0>N>=1`. - Scalar lerp = 1; - }; - TimelineKey GetTimelineKey(SecondsF time); - - std::vector times_; -}; - -class TranslationTimelineResolver final : public TimelineResolver { - public: - ~TranslationTimelineResolver(); - - // |Resolver| - void Apply(AnimationTransforms& target, - SecondsF time, - Scalar weight) override; - - private: - TranslationTimelineResolver(); - - std::vector values_; - - TranslationTimelineResolver(const TranslationTimelineResolver&) = delete; - - TranslationTimelineResolver& operator=(const TranslationTimelineResolver&) = - delete; - - friend PropertyResolver; -}; - -class RotationTimelineResolver final : public TimelineResolver { - public: - ~RotationTimelineResolver(); - - // |Resolver| - void Apply(AnimationTransforms& target, - SecondsF time, - Scalar weight) override; - - private: - RotationTimelineResolver(); - - std::vector values_; - - RotationTimelineResolver(const RotationTimelineResolver&) = delete; - - RotationTimelineResolver& operator=(const RotationTimelineResolver&) = delete; - - friend PropertyResolver; -}; - -class ScaleTimelineResolver final : public TimelineResolver { - public: - ~ScaleTimelineResolver(); - - // |Resolver| - void Apply(AnimationTransforms& target, - SecondsF time, - Scalar weight) override; - - private: - ScaleTimelineResolver(); - - std::vector values_; - - ScaleTimelineResolver(const ScaleTimelineResolver&) = delete; - - ScaleTimelineResolver& operator=(const ScaleTimelineResolver&) = delete; - - friend PropertyResolver; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_ANIMATION_PROPERTY_RESOLVER_H_ diff --git a/impeller/scene/camera.cc b/impeller/scene/camera.cc deleted file mode 100644 index b4736177588f8..0000000000000 --- a/impeller/scene/camera.cc +++ /dev/null @@ -1,36 +0,0 @@ -// 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/camera.h" - -namespace impeller { -namespace scene { - -Camera Camera::MakePerspective(Radians fov_y, Vector3 position) { - Camera camera; - camera.fov_y_ = fov_y; - camera.position_ = position; - return camera; -} - -Camera Camera::LookAt(Vector3 target, Vector3 up) const { - Camera camera = *this; - camera.target_ = target; - camera.up_ = up; - return camera; -} - -Matrix Camera::GetTransform(ISize target_size) const { - if (transform_.has_value()) { - return transform_.value(); - } - - transform_ = Matrix::MakePerspective(fov_y_, target_size, z_near_, z_far_) * - Matrix::MakeLookAt(position_, target_, up_); - - return transform_.value(); -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/camera.h b/impeller/scene/camera.h deleted file mode 100644 index 3f833320dacb8..0000000000000 --- a/impeller/scene/camera.h +++ /dev/null @@ -1,37 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_CAMERA_H_ -#define FLUTTER_IMPELLER_SCENE_CAMERA_H_ - -#include - -#include "impeller/geometry/matrix.h" - -namespace impeller { -namespace scene { - -class Camera { - public: - static Camera MakePerspective(Radians fov_y, Vector3 position); - - Camera LookAt(Vector3 target, Vector3 up = Vector3(0, -1, 0)) const; - - Matrix GetTransform(ISize target_size) const; - - private: - Radians fov_y_ = Degrees(60); - Vector3 position_ = Vector3(); - Vector3 target_ = Vector3(0, 0, -1); - Vector3 up_ = Vector3(0, -1, 0); - Scalar z_near_ = 0.1f; - Scalar z_far_ = 1000.0f; - - mutable std::optional transform_; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_CAMERA_H_ diff --git a/impeller/scene/geometry.cc b/impeller/scene/geometry.cc deleted file mode 100644 index 00f7cfb665bb0..0000000000000 --- a/impeller/scene/geometry.cc +++ /dev/null @@ -1,272 +0,0 @@ -// 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/geometry.h" - -#include -#include - -#include "impeller/core/device_buffer_descriptor.h" -#include "impeller/core/formats.h" -#include "impeller/core/sampler_descriptor.h" -#include "impeller/core/vertex_buffer.h" -#include "impeller/geometry/point.h" -#include "impeller/geometry/vector.h" -#include "impeller/renderer/render_pass.h" -#include "impeller/renderer/vertex_buffer_builder.h" -#include "impeller/scene/importer/scene_flatbuffers.h" -#include "impeller/scene/shaders/skinned.vert.h" -#include "impeller/scene/shaders/unskinned.vert.h" - -namespace impeller { -namespace scene { - -//------------------------------------------------------------------------------ -/// Geometry -/// - -Geometry::~Geometry() = default; - -std::shared_ptr Geometry::MakeCuboid(Vector3 size) { - auto result = std::make_shared(); - result->SetSize(size); - return result; -} - -std::shared_ptr Geometry::MakeVertexBuffer(VertexBuffer vertex_buffer, - bool is_skinned) { - if (is_skinned) { - auto result = std::make_shared(); - result->SetVertexBuffer(std::move(vertex_buffer)); - return result; - } else { - auto result = std::make_shared(); - result->SetVertexBuffer(std::move(vertex_buffer)); - return result; - } -} - -std::shared_ptr Geometry::MakeFromFlatbuffer( - const fb::MeshPrimitive& mesh, - Allocator& allocator) { - IndexType index_type; - switch (mesh.indices()->type()) { - case fb::IndexType::k16Bit: - index_type = IndexType::k16bit; - break; - case fb::IndexType::k32Bit: - index_type = IndexType::k32bit; - break; - } - - const uint8_t* vertices_start; - size_t vertices_bytes; - bool is_skinned; - - switch (mesh.vertices_type()) { - case fb::VertexBuffer::UnskinnedVertexBuffer: { - const auto* vertices = - mesh.vertices_as_UnskinnedVertexBuffer()->vertices(); - vertices_start = reinterpret_cast(vertices->Get(0)); - vertices_bytes = vertices->size() * sizeof(fb::Vertex); - is_skinned = false; - break; - } - case fb::VertexBuffer::SkinnedVertexBuffer: { - const auto* vertices = mesh.vertices_as_SkinnedVertexBuffer()->vertices(); - vertices_start = reinterpret_cast(vertices->Get(0)); - vertices_bytes = vertices->size() * sizeof(fb::SkinnedVertex); - is_skinned = true; - break; - } - case fb::VertexBuffer::NONE: - VALIDATION_LOG << "Invalid vertex buffer type."; - return nullptr; - } - - const uint8_t* indices_start = - reinterpret_cast(mesh.indices()->data()->Data()); - - const size_t indices_bytes = mesh.indices()->data()->size(); - if (vertices_bytes == 0 || indices_bytes == 0) { - return nullptr; - } - - DeviceBufferDescriptor buffer_desc; - buffer_desc.size = vertices_bytes + indices_bytes; - buffer_desc.storage_mode = StorageMode::kHostVisible; - - auto buffer = allocator.CreateBuffer(buffer_desc); - buffer->SetLabel("Mesh vertices+indices"); - - if (!buffer->CopyHostBuffer(vertices_start, Range(0, vertices_bytes))) { - return nullptr; - } - if (!buffer->CopyHostBuffer(indices_start, Range(0, indices_bytes), - vertices_bytes)) { - return nullptr; - } - - VertexBuffer vertex_buffer = { - .vertex_buffer = {.buffer = buffer, .range = Range(0, vertices_bytes)}, - .index_buffer = {.buffer = buffer, - .range = Range(vertices_bytes, indices_bytes)}, - .vertex_count = mesh.indices()->count(), - .index_type = index_type, - }; - return MakeVertexBuffer(std::move(vertex_buffer), is_skinned); -} - -void Geometry::SetJointsTexture(const std::shared_ptr& texture) {} - -//------------------------------------------------------------------------------ -/// CuboidGeometry -/// - -CuboidGeometry::CuboidGeometry() = default; - -CuboidGeometry::~CuboidGeometry() = default; - -void CuboidGeometry::SetSize(Vector3 size) { - size_ = size; -} - -// |Geometry| -GeometryType CuboidGeometry::GetGeometryType() const { - return GeometryType::kUnskinned; -} - -// |Geometry| -VertexBuffer CuboidGeometry::GetVertexBuffer(Allocator& allocator) const { - VertexBufferBuilder builder; - // Layout: position, normal, tangent, uv - builder.AddVertices({ - // Front. - {Vector3(0, 0, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(0, 0), - Color::White()}, - {Vector3(1, 0, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(1, 0), - Color::White()}, - {Vector3(1, 1, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(1, 1), - Color::White()}, - {Vector3(1, 1, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(1, 1), - Color::White()}, - {Vector3(0, 1, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(0, 1), - Color::White()}, - {Vector3(0, 0, 0), Vector3(0, 0, -1), Vector3(1, 0, 0), Point(0, 0), - Color::White()}, - }); - return builder.CreateVertexBuffer(allocator); -} - -// |Geometry| -void CuboidGeometry::BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - const Matrix& transform, - RenderPass& pass) const { - pass.SetVertexBuffer( - GetVertexBuffer(*scene_context.GetContext()->GetResourceAllocator())); - - UnskinnedVertexShader::FrameInfo info; - info.mvp = transform; - UnskinnedVertexShader::BindFrameInfo(pass, buffer.EmplaceUniform(info)); -} - -//------------------------------------------------------------------------------ -/// UnskinnedVertexBufferGeometry -/// - -UnskinnedVertexBufferGeometry::UnskinnedVertexBufferGeometry() = default; - -UnskinnedVertexBufferGeometry::~UnskinnedVertexBufferGeometry() = default; - -void UnskinnedVertexBufferGeometry::SetVertexBuffer( - VertexBuffer vertex_buffer) { - vertex_buffer_ = std::move(vertex_buffer); -} - -// |Geometry| -GeometryType UnskinnedVertexBufferGeometry::GetGeometryType() const { - return GeometryType::kUnskinned; -} - -// |Geometry| -VertexBuffer UnskinnedVertexBufferGeometry::GetVertexBuffer( - Allocator& allocator) const { - return vertex_buffer_; -} - -// |Geometry| -void UnskinnedVertexBufferGeometry::BindToCommand( - const SceneContext& scene_context, - HostBuffer& buffer, - const Matrix& transform, - RenderPass& pass) const { - pass.SetVertexBuffer( - GetVertexBuffer(*scene_context.GetContext()->GetResourceAllocator())); - - UnskinnedVertexShader::FrameInfo info; - info.mvp = transform; - UnskinnedVertexShader::BindFrameInfo(pass, buffer.EmplaceUniform(info)); -} - -//------------------------------------------------------------------------------ -/// SkinnedVertexBufferGeometry -/// - -SkinnedVertexBufferGeometry::SkinnedVertexBufferGeometry() = default; - -SkinnedVertexBufferGeometry::~SkinnedVertexBufferGeometry() = default; - -void SkinnedVertexBufferGeometry::SetVertexBuffer(VertexBuffer vertex_buffer) { - vertex_buffer_ = std::move(vertex_buffer); -} - -// |Geometry| -GeometryType SkinnedVertexBufferGeometry::GetGeometryType() const { - return GeometryType::kSkinned; -} - -// |Geometry| -VertexBuffer SkinnedVertexBufferGeometry::GetVertexBuffer( - Allocator& allocator) const { - return vertex_buffer_; -} - -// |Geometry| -void SkinnedVertexBufferGeometry::BindToCommand( - const SceneContext& scene_context, - HostBuffer& buffer, - const Matrix& transform, - RenderPass& pass) const { - pass.SetVertexBuffer( - GetVertexBuffer(*scene_context.GetContext()->GetResourceAllocator())); - - SamplerDescriptor sampler_desc; - sampler_desc.min_filter = MinMagFilter::kNearest; - sampler_desc.mag_filter = MinMagFilter::kNearest; - sampler_desc.mip_filter = MipFilter::kNearest; - sampler_desc.width_address_mode = SamplerAddressMode::kRepeat; - sampler_desc.label = "NN Repeat"; - - SkinnedVertexShader::BindJointsTexture( - pass, - joints_texture_ ? joints_texture_ : scene_context.GetPlaceholderTexture(), - scene_context.GetContext()->GetSamplerLibrary()->GetSampler( - sampler_desc)); - - SkinnedVertexShader::FrameInfo info; - info.mvp = transform; - info.enable_skinning = joints_texture_ ? 1 : 0; - info.joint_texture_size = - joints_texture_ ? joints_texture_->GetSize().width : 1; - SkinnedVertexShader::BindFrameInfo(pass, buffer.EmplaceUniform(info)); -} - -// |Geometry| -void SkinnedVertexBufferGeometry::SetJointsTexture( - const std::shared_ptr& texture) { - joints_texture_ = texture; -} -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/geometry.h b/impeller/scene/geometry.h deleted file mode 100644 index 24dc76b1e8c95..0000000000000 --- a/impeller/scene/geometry.h +++ /dev/null @@ -1,144 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_GEOMETRY_H_ -#define FLUTTER_IMPELLER_SCENE_GEOMETRY_H_ - -#include - -#include "impeller/core/allocator.h" -#include "impeller/core/host_buffer.h" -#include "impeller/core/vertex_buffer.h" -#include "impeller/geometry/matrix.h" -#include "impeller/geometry/vector.h" -#include "impeller/renderer/render_pass.h" -#include "impeller/scene/importer/scene_flatbuffers.h" -#include "impeller/scene/pipeline_key.h" -#include "impeller/scene/scene_context.h" - -namespace impeller { -namespace scene { - -class CuboidGeometry; -class UnskinnedVertexBufferGeometry; - -class Geometry { - public: - virtual ~Geometry(); - - static std::shared_ptr MakeCuboid(Vector3 size); - - static std::shared_ptr MakeVertexBuffer(VertexBuffer vertex_buffer, - bool is_skinned); - - static std::shared_ptr MakeFromFlatbuffer( - const fb::MeshPrimitive& mesh, - Allocator& allocator); - - virtual GeometryType GetGeometryType() const = 0; - - virtual VertexBuffer GetVertexBuffer(Allocator& allocator) const = 0; - - virtual void BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - const Matrix& transform, - RenderPass& pass) const = 0; - - virtual void SetJointsTexture(const std::shared_ptr& texture); -}; - -class CuboidGeometry final : public Geometry { - public: - CuboidGeometry(); - - ~CuboidGeometry() override; - - void SetSize(Vector3 size); - - // |Geometry| - GeometryType GetGeometryType() const override; - - // |Geometry| - VertexBuffer GetVertexBuffer(Allocator& allocator) const override; - - // |Geometry| - void BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - const Matrix& transform, - RenderPass& pass) const override; - - private: - Vector3 size_; - - CuboidGeometry(const CuboidGeometry&) = delete; - - CuboidGeometry& operator=(const CuboidGeometry&) = delete; -}; - -class UnskinnedVertexBufferGeometry final : public Geometry { - public: - UnskinnedVertexBufferGeometry(); - - ~UnskinnedVertexBufferGeometry() override; - - void SetVertexBuffer(VertexBuffer vertex_buffer); - - // |Geometry| - GeometryType GetGeometryType() const override; - - // |Geometry| - VertexBuffer GetVertexBuffer(Allocator& allocator) const override; - - // |Geometry| - void BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - const Matrix& transform, - RenderPass& pass) const override; - - private: - VertexBuffer vertex_buffer_; - - UnskinnedVertexBufferGeometry(const UnskinnedVertexBufferGeometry&) = delete; - - UnskinnedVertexBufferGeometry& operator=( - const UnskinnedVertexBufferGeometry&) = delete; -}; - -class SkinnedVertexBufferGeometry final : public Geometry { - public: - SkinnedVertexBufferGeometry(); - - ~SkinnedVertexBufferGeometry() override; - - void SetVertexBuffer(VertexBuffer vertex_buffer); - - // |Geometry| - GeometryType GetGeometryType() const override; - - // |Geometry| - VertexBuffer GetVertexBuffer(Allocator& allocator) const override; - - // |Geometry| - void BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - const Matrix& transform, - RenderPass& pass) const override; - - // |Geometry| - void SetJointsTexture(const std::shared_ptr& texture) override; - - private: - VertexBuffer vertex_buffer_; - std::shared_ptr joints_texture_; - - SkinnedVertexBufferGeometry(const SkinnedVertexBufferGeometry&) = delete; - - SkinnedVertexBufferGeometry& operator=(const SkinnedVertexBufferGeometry&) = - delete; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_GEOMETRY_H_ diff --git a/impeller/scene/importer/BUILD.gn b/impeller/scene/importer/BUILD.gn deleted file mode 100644 index 8901df1215f51..0000000000000 --- a/impeller/scene/importer/BUILD.gn +++ /dev/null @@ -1,124 +0,0 @@ -# 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("//flutter/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 = [ "scene.fbs" ] - public_configs = [ ":runtime_stage_config" ] - public_deps = [ "//flutter/third_party/flatbuffers" ] -} - -impeller_component("conversions") { - sources = [ - "conversions.cc", - "conversions.h", - ] - - public_deps = [ - ":importer_flatbuffers", - "../../base", - "../../geometry", - "//flutter/fml", - ] -} - -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", - "vertices_builder.cc", - "vertices_builder.h", - ] - - public_deps = [ - ":conversions", - ":importer_flatbuffers", - "../../base", - "../../compiler:utilities", - "../../geometry", - "//flutter/fml", - - # All third_party deps must be reflected below in the scenec_license - # target. - "//flutter/third_party/tinygltf", - ] -} - -generated_file("scenec_license") { - source_path = rebase_path(".", "//flutter") - git_url = "https://github.com/flutter/engine/tree/$engine_version" - outputs = [ "$target_gen_dir/LICENSE.scenec.md" ] - contents = [ - "# scenec", - "", - "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", - "", - "### scenec", - "", - 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 scenec. Add them here. - "## Additional open source licenses", - "", - "### tinygltf", - "", - read_file("//flutter/third_party/tinygltf/LICENSE", "string"), - ] -} - -group("importer") { - deps = [ - ":scenec", - ":scenec_license", - ] -} - -impeller_component("scenec") { - target_type = "executable" - - sources = [ "scenec_main.cc" ] - - deps = [ ":importer_lib" ] - - metadata = { - entitlement_file_path = [ "scenec" ] - } -} - -impeller_component("importer_unittests") { - testonly = true - - output_name = "scenec_unittests" - - sources = [ "importer_unittests.cc" ] - - deps = [ - ":importer_lib", - "../../fixtures", - "../../geometry:geometry_asserts", - "//flutter/testing:testing_lib", - ] -} diff --git a/impeller/scene/importer/conversions.cc b/impeller/scene/importer/conversions.cc deleted file mode 100644 index ceef0ef187096..0000000000000 --- a/impeller/scene/importer/conversions.cc +++ /dev/null @@ -1,96 +0,0 @@ -// 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/conversions.h" - -#include - -#include "impeller/scene/importer/scene_flatbuffers.h" - -namespace impeller { -namespace scene { -namespace importer { - -Matrix ToMatrix(const std::vector& m) { - return Matrix(m[0], m[1], m[2], m[3], // - m[4], m[5], m[6], m[7], // - m[8], m[9], m[10], m[11], // - m[12], m[13], m[14], m[15]); -} - -//----------------------------------------------------------------------------- -/// Flatbuffers -> Impeller -/// - -Matrix ToMatrix(const fb::Matrix& m) { - auto& a = *m.m(); - return Matrix(a[0], a[1], a[2], a[3], // - a[4], a[5], a[6], a[7], // - a[8], a[9], a[10], a[11], // - a[12], a[13], a[14], a[15]); -} - -Vector2 ToVector2(const fb::Vec2& v) { - return Vector2(v.x(), v.y()); -} - -Vector3 ToVector3(const fb::Vec3& v) { - return Vector3(v.x(), v.y(), v.z()); -} - -Vector4 ToVector4(const fb::Vec4& v) { - return Vector4(v.x(), v.y(), v.z(), v.w()); -} - -Color ToColor(const fb::Color& c) { - return Color(c.r(), c.g(), c.b(), c.a()); -} - -//----------------------------------------------------------------------------- -/// Impeller -> Flatbuffers -/// - -fb::Matrix ToFBMatrix(const Matrix& m) { - auto array = std::array{m.m[0], m.m[1], m.m[2], m.m[3], // - m.m[4], m.m[5], m.m[6], m.m[7], // - m.m[8], m.m[9], m.m[10], m.m[11], // - m.m[12], m.m[13], m.m[14], m.m[15]}; - return fb::Matrix(array); -} - -std::unique_ptr ToFBMatrixUniquePtr(const Matrix& m) { - auto array = std::array{m.m[0], m.m[1], m.m[2], m.m[3], // - m.m[4], m.m[5], m.m[6], m.m[7], // - m.m[8], m.m[9], m.m[10], m.m[11], // - m.m[12], m.m[13], m.m[14], m.m[15]}; - return std::make_unique(array); -} - -fb::Vec2 ToFBVec2(const Vector2 v) { - return fb::Vec2(v.x, v.y); -} - -fb::Vec3 ToFBVec3(const Vector3 v) { - return fb::Vec3(v.x, v.y, v.z); -} - -fb::Vec4 ToFBVec4(const Vector4 v) { - return fb::Vec4(v.x, v.y, v.z, v.w); -} - -fb::Color ToFBColor(const Color c) { - return fb::Color(c.red, c.green, c.blue, c.alpha); -} - -std::unique_ptr ToFBColor(const std::vector& c) { - auto* color = new fb::Color(c.size() > 0 ? c[0] : 1, // - c.size() > 1 ? c[1] : 1, // - c.size() > 2 ? c[2] : 1, // - c.size() > 3 ? c[3] : 1); - return std::unique_ptr(color); -} - -} // namespace importer -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/importer/conversions.h b/impeller/scene/importer/conversions.h deleted file mode 100644 index 6c911644261f9..0000000000000 --- a/impeller/scene/importer/conversions.h +++ /dev/null @@ -1,57 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_IMPORTER_CONVERSIONS_H_ -#define FLUTTER_IMPELLER_SCENE_IMPORTER_CONVERSIONS_H_ - -#include -#include -#include - -#include "impeller/geometry/matrix.h" -#include "impeller/scene/importer/scene_flatbuffers.h" - -namespace impeller { -namespace scene { -namespace importer { - -Matrix ToMatrix(const std::vector& m); - -//----------------------------------------------------------------------------- -/// Flatbuffers -> Impeller -/// - -Matrix ToMatrix(const fb::Matrix& m); - -Vector2 ToVector2(const fb::Vec2& c); - -Vector3 ToVector3(const fb::Vec3& c); - -Vector4 ToVector4(const fb::Vec4& c); - -Color ToColor(const fb::Color& c); - -//----------------------------------------------------------------------------- -/// Impeller -> Flatbuffers -/// - -fb::Matrix ToFBMatrix(const Matrix& m); - -std::unique_ptr ToFBMatrixUniquePtr(const Matrix& m); - -fb::Vec2 ToFBVec2(const Vector2 v); - -fb::Vec3 ToFBVec3(const Vector3 v); - -fb::Vec4 ToFBVec4(const Vector4 v); - -fb::Color ToFBColor(const Color c); - -std::unique_ptr ToFBColor(const std::vector& c); - -} // namespace importer -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_IMPORTER_CONVERSIONS_H_ diff --git a/impeller/scene/importer/importer.h b/impeller/scene/importer/importer.h deleted file mode 100644 index af0934522e428..0000000000000 --- a/impeller/scene/importer/importer.h +++ /dev/null @@ -1,24 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_IMPORTER_IMPORTER_H_ -#define FLUTTER_IMPELLER_SCENE_IMPORTER_IMPORTER_H_ - -#include -#include - -#include "flutter/fml/mapping.h" -#include "impeller/scene/importer/scene_flatbuffers.h" - -namespace impeller { -namespace scene { -namespace importer { - -bool ParseGLTF(const fml::Mapping& source_mapping, fb::SceneT& out_scene); - -} -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_IMPORTER_IMPORTER_H_ diff --git a/impeller/scene/importer/importer_gltf.cc b/impeller/scene/importer/importer_gltf.cc deleted file mode 100644 index a3d9f0a767e57..0000000000000 --- a/impeller/scene/importer/importer_gltf.cc +++ /dev/null @@ -1,501 +0,0 @@ -// 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 -#include -#include -#include -#include -#include -#include - -#include "flutter/fml/mapping.h" -#include "flutter/third_party/tinygltf/tiny_gltf.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" - -namespace impeller { -namespace scene { -namespace importer { - -static const std::map kAttributes = - {{"POSITION", VerticesBuilder::AttributeType::kPosition}, - {"NORMAL", VerticesBuilder::AttributeType::kNormal}, - {"TANGENT", VerticesBuilder::AttributeType::kTangent}, - {"TEXCOORD_0", VerticesBuilder::AttributeType::kTextureCoords}, - {"COLOR_0", VerticesBuilder::AttributeType::kColor}, - {"JOINTS_0", VerticesBuilder::AttributeType::kJoints}, - {"WEIGHTS_0", VerticesBuilder::AttributeType::kWeights}}; - -static bool WithinRange(int index, size_t size) { - return index >= 0 && static_cast(index) < size; -} - -static bool MeshPrimitiveIsSkinned(const tinygltf::Primitive& primitive) { - return primitive.attributes.find("JOINTS_0") != primitive.attributes.end() && - primitive.attributes.find("WEIGHTS_0") != primitive.attributes.end(); -} - -static void ProcessMaterial(const tinygltf::Model& gltf, - const tinygltf::Material& in_material, - fb::MaterialT& out_material) { - out_material.type = fb::MaterialType::kUnlit; - out_material.base_color_factor = - ToFBColor(in_material.pbrMetallicRoughness.baseColorFactor); - bool base_color_texture_valid = - in_material.pbrMetallicRoughness.baseColorTexture.texCoord == 0 && - in_material.pbrMetallicRoughness.baseColorTexture.index >= 0 && - in_material.pbrMetallicRoughness.baseColorTexture.index < - static_cast(gltf.textures.size()); - out_material.base_color_texture = - base_color_texture_valid - // This is safe because every GLTF input texture is mapped to a - // `Scene->texture`. - ? in_material.pbrMetallicRoughness.baseColorTexture.index - : -1; -} - -static bool ProcessMeshPrimitive(const tinygltf::Model& gltf, - const tinygltf::Primitive& primitive, - fb::MeshPrimitiveT& mesh_primitive) { - //--------------------------------------------------------------------------- - /// Vertices. - /// - - { - bool is_skinned = MeshPrimitiveIsSkinned(primitive); - std::unique_ptr builder = - is_skinned ? VerticesBuilder::MakeSkinned() - : VerticesBuilder::MakeUnskinned(); - - 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; - } - if (!is_skinned && - (attribute_type->second == VerticesBuilder::AttributeType::kJoints || - attribute_type->second == - VerticesBuilder::AttributeType::kWeights)) { - // If the primitive doesn't have enough information to be skinned, skip - // skinning-related attributes. - 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); - } - - //--------------------------------------------------------------------------- - /// 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(); - - 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); - } - - //--------------------------------------------------------------------------- - /// Material. - /// - - { - auto material = std::make_unique(); - if (primitive.material >= 0 && - primitive.material < static_cast(gltf.materials.size())) { - ProcessMaterial(gltf, gltf.materials[primitive.material], *material); - } else { - material->type = fb::MaterialType::kUnlit; - } - mesh_primitive.material = std::move(material); - } - - return true; -} - -static void ProcessNode(const tinygltf::Model& gltf, - const tinygltf::Node& in_node, - fb::NodeT& out_node) { - out_node.name = in_node.name; - out_node.children = in_node.children; - - //--------------------------------------------------------------------------- - /// Transform. - /// - - Matrix transform; - if (in_node.scale.size() == 3) { - transform = - transform * Matrix::MakeScale({static_cast(in_node.scale[0]), - static_cast(in_node.scale[1]), - static_cast(in_node.scale[2])}); - } - 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.translation.size() == 3) { - transform = transform * Matrix::MakeTranslation( - {static_cast(in_node.translation[0]), - static_cast(in_node.translation[1]), - static_cast(in_node.translation[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 = ToFBMatrixUniquePtr(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(); - if (!ProcessMeshPrimitive(gltf, primitive, *mesh_primitive)) { - continue; - } - out_node.mesh_primitives.push_back(std::move(mesh_primitive)); - } - } - - //--------------------------------------------------------------------------- - /// Skin. - /// - - if (WithinRange(in_node.skin, gltf.skins.size())) { - auto& skin = gltf.skins[in_node.skin]; - - auto ipskin = std::make_unique(); - ipskin->joints = skin.joints; - { - std::vector matrices; - auto& matrix_accessor = gltf.accessors[skin.inverseBindMatrices]; - auto& matrix_view = gltf.bufferViews[matrix_accessor.bufferView]; - auto& matrix_buffer = gltf.buffers[matrix_view.buffer]; - for (size_t matrix_i = 0; matrix_i < matrix_accessor.count; matrix_i++) { - auto* s = reinterpret_cast( - matrix_buffer.data.data() + matrix_view.byteOffset + - matrix_accessor.ByteStride(matrix_view) * matrix_i); - Matrix m(s[0], s[1], s[2], s[3], // - s[4], s[5], s[6], s[7], // - s[8], s[9], s[10], s[11], // - s[12], s[13], s[14], s[15]); - matrices.push_back(ToFBMatrix(m)); - } - ipskin->inverse_bind_matrices = std::move(matrices); - } - ipskin->skeleton = skin.skeleton; - out_node.skin = std::move(ipskin); - } -} - -static void ProcessTexture(const tinygltf::Model& gltf, - const tinygltf::Texture& in_texture, - fb::TextureT& out_texture) { - if (!WithinRange(in_texture.source, gltf.images.size())) { - return; - } - auto& image = gltf.images[in_texture.source]; - - auto embedded = std::make_unique(); - embedded->bytes = image.image; - size_t bytes_per_component = 0; - switch (image.pixel_type) { - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: - embedded->component_type = fb::ComponentType::k8Bit; - bytes_per_component = 1; - break; - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: - embedded->component_type = fb::ComponentType::k16Bit; - bytes_per_component = 2; - break; - default: - std::cerr << "Texture component type " << image.pixel_type - << " not supported." << std::endl; - return; - } - if (image.image.size() != - bytes_per_component * image.component * image.width * image.height) { - std::cerr << "Decompressed texture had unexpected buffer size. Skipping." - << std::endl; - return; - } - embedded->component_count = image.component; - embedded->width = image.width; - embedded->height = image.height; - out_texture.embedded_image = std::move(embedded); - out_texture.uri = image.uri; -} - -static void ProcessAnimation(const tinygltf::Model& gltf, - const tinygltf::Animation& in_animation, - fb::AnimationT& out_animation) { - out_animation.name = in_animation.name; - - // std::vector channels; - std::vector translation_channels; - std::vector rotation_channels; - std::vector scale_channels; - for (auto& in_channel : in_animation.channels) { - auto out_channel = fb::ChannelT(); - - out_channel.node = in_channel.target_node; - auto& sampler = in_animation.samplers[in_channel.sampler]; - - /// Keyframe times. - auto& times_accessor = gltf.accessors[sampler.input]; - if (times_accessor.count <= 0) { - continue; // Nothing to record. - } - { - auto& times_bufferview = gltf.bufferViews[times_accessor.bufferView]; - auto& times_buffer = gltf.buffers[times_bufferview.buffer]; - if (times_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) { - std::cerr << "Unexpected component type \"" - << times_accessor.componentType - << "\" for animation channel times accessor. Skipping." - << std::endl; - continue; - } - if (times_accessor.type != TINYGLTF_TYPE_SCALAR) { - std::cerr << "Unexpected type \"" << times_accessor.type - << "\" for animation channel times accessor. Skipping." - << std::endl; - continue; - } - for (size_t time_i = 0; time_i < times_accessor.count; time_i++) { - const float* time_p = reinterpret_cast( - times_buffer.data.data() + times_bufferview.byteOffset + - times_accessor.ByteStride(times_bufferview) * time_i); - out_channel.timeline.push_back(*time_p); - } - } - - /// Keyframe values. - auto& values_accessor = gltf.accessors[sampler.output]; - if (values_accessor.count != times_accessor.count) { - std::cerr << "Mismatch between time and value accessors for animation " - "channel. Skipping." - << std::endl; - continue; - } - { - auto& values_bufferview = gltf.bufferViews[values_accessor.bufferView]; - auto& values_buffer = gltf.buffers[values_bufferview.buffer]; - if (values_accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) { - std::cerr << "Unexpected component type \"" - << values_accessor.componentType - << "\" for animation channel values accessor. Skipping." - << std::endl; - continue; - } - if (in_channel.target_path == "translation") { - if (values_accessor.type != TINYGLTF_TYPE_VEC3) { - std::cerr << "Unexpected type \"" << values_accessor.type - << "\" for animation channel \"translation\" accessor. " - "Skipping." - << std::endl; - continue; - } - fb::TranslationKeyframesT keyframes; - for (size_t value_i = 0; value_i < values_accessor.count; value_i++) { - const float* value_p = reinterpret_cast( - values_buffer.data.data() + values_bufferview.byteOffset + - values_accessor.ByteStride(values_bufferview) * value_i); - keyframes.values.push_back( - fb::Vec3(value_p[0], value_p[1], value_p[2])); - } - out_channel.keyframes.Set(std::move(keyframes)); - translation_channels.push_back(std::move(out_channel)); - } else if (in_channel.target_path == "rotation") { - if (values_accessor.type != TINYGLTF_TYPE_VEC4) { - std::cerr << "Unexpected type \"" << values_accessor.type - << "\" for animation channel \"rotation\" accessor. " - "Skipping." - << std::endl; - continue; - } - fb::RotationKeyframesT keyframes; - for (size_t value_i = 0; value_i < values_accessor.count; value_i++) { - const float* value_p = reinterpret_cast( - values_buffer.data.data() + values_bufferview.byteOffset + - values_accessor.ByteStride(values_bufferview) * value_i); - keyframes.values.push_back( - fb::Vec4(value_p[0], value_p[1], value_p[2], value_p[3])); - } - out_channel.keyframes.Set(std::move(keyframes)); - rotation_channels.push_back(std::move(out_channel)); - } else if (in_channel.target_path == "scale") { - if (values_accessor.type != TINYGLTF_TYPE_VEC3) { - std::cerr << "Unexpected type \"" << values_accessor.type - << "\" for animation channel \"scale\" accessor. " - "Skipping." - << std::endl; - continue; - } - fb::ScaleKeyframesT keyframes; - for (size_t value_i = 0; value_i < values_accessor.count; value_i++) { - const float* value_p = reinterpret_cast( - values_buffer.data.data() + values_bufferview.byteOffset + - values_accessor.ByteStride(values_bufferview) * value_i); - keyframes.values.push_back( - fb::Vec3(value_p[0], value_p[1], value_p[2])); - } - out_channel.keyframes.Set(std::move(keyframes)); - scale_channels.push_back(std::move(out_channel)); - } else { - std::cerr << "Unsupported animation channel target path \"" - << in_channel.target_path << "\". Skipping." << std::endl; - continue; - } - } - } - - std::vector> channels; - for (const auto& channel_list : - {translation_channels, rotation_channels, scale_channels}) { - for (const auto& channel : channel_list) { - channels.push_back(std::make_unique(channel)); - } - } - out_animation.channels = std::move(channels); -} - -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]; - out_scene.children = scene.nodes; - - out_scene.transform = - ToFBMatrixUniquePtr(Matrix::MakeScale(Vector3(1, 1, -1))); - - for (size_t texture_i = 0; texture_i < gltf.textures.size(); texture_i++) { - auto texture = std::make_unique(); - ProcessTexture(gltf, gltf.textures[texture_i], *texture); - out_scene.textures.push_back(std::move(texture)); - } - - for (size_t node_i = 0; node_i < gltf.nodes.size(); node_i++) { - auto node = std::make_unique(); - ProcessNode(gltf, gltf.nodes[node_i], *node); - out_scene.nodes.push_back(std::move(node)); - } - - for (size_t animation_i = 0; animation_i < gltf.animations.size(); - animation_i++) { - auto animation = std::make_unique(); - ProcessAnimation(gltf, gltf.animations[animation_i], *animation); - out_scene.animations.push_back(std::move(animation)); - } - - return true; -} - -} // namespace importer -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/importer/importer_unittests.cc b/impeller/scene/importer/importer_unittests.cc deleted file mode 100644 index 872db159fb4dd..0000000000000 --- a/impeller/scene/importer/importer_unittests.cc +++ /dev/null @@ -1,127 +0,0 @@ -// 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 "flutter/testing/testing.h" -#include "impeller/geometry/geometry_asserts.h" -#include "impeller/geometry/matrix.h" -#include "impeller/scene/importer/conversions.h" -#include "impeller/scene/importer/importer.h" -#include "impeller/scene/importer/scene_flatbuffers.h" - -namespace impeller { -namespace scene { -namespace importer { -namespace testing { - -TEST(ImporterTest, CanParseUnskinnedGLTF) { - auto mapping = - flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb"); - - fb::SceneT scene; - ASSERT_TRUE(ParseGLTF(*mapping, scene)); - - ASSERT_EQ(scene.children.size(), 1u); - auto& node = scene.nodes[scene.children[0]]; - - Matrix node_transform = ToMatrix(*node->transform); - ASSERT_MATRIX_NEAR(node_transform, Matrix()); - - ASSERT_EQ(node->mesh_primitives.size(), 1u); - auto& mesh = *node->mesh_primitives[0]; - ASSERT_EQ(mesh.indices->count, 918u); - - uint16_t first_index = - *reinterpret_cast(mesh.indices->data.data()); - ASSERT_EQ(first_index, 45u); - - ASSERT_EQ(mesh.vertices.type, fb::VertexBuffer::UnskinnedVertexBuffer); - auto& vertices = mesh.vertices.AsUnskinnedVertexBuffer()->vertices; - ASSERT_EQ(vertices.size(), 260u); - auto& vertex = vertices[0]; - - Vector3 position = ToVector3(vertex.position()); - ASSERT_VECTOR3_NEAR(position, Vector3(-0.0100185, -0.522907, 0.133178)); - - Vector3 normal = ToVector3(vertex.normal()); - ASSERT_VECTOR3_NEAR(normal, Vector3(0.556984, -0.810839, 0.179746)); - - Vector4 tangent = ToVector4(vertex.tangent()); - ASSERT_VECTOR4_NEAR(tangent, Vector4(0.155911, -0.110495, -0.981572, 1)); - - Vector2 texture_coords = ToVector2(vertex.texture_coords()); - ASSERT_POINT_NEAR(texture_coords, Vector2(0.727937, 0.713817)); - - Color color = ToColor(vertex.color()); - ASSERT_COLOR_NEAR(color, Color(0.0221714, 0.467781, 0.921584, 1)); -} - -TEST(ImporterTest, CanParseSkinnedGLTF) { - auto mapping = flutter::testing::OpenFixtureAsMapping("two_triangles.glb"); - - fb::SceneT scene; - ASSERT_TRUE(ParseGLTF(*mapping, scene)); - - ASSERT_EQ(scene.children.size(), 1u); - auto& node = scene.nodes[scene.children[0]]; - - Matrix node_transform = ToMatrix(*node->transform); - ASSERT_MATRIX_NEAR(node_transform, Matrix()); - - ASSERT_EQ(node->mesh_primitives.size(), 0u); - ASSERT_EQ(node->children.size(), 2u); - - // The skinned node contains both a skeleton and skinned mesh primitives that - // reference bones in the skeleton. - auto& skinned_node = scene.nodes[node->children[0]]; - ASSERT_NE(skinned_node->skin, nullptr); - - ASSERT_EQ(skinned_node->mesh_primitives.size(), 2u); - auto& bottom_triangle = *skinned_node->mesh_primitives[0]; - ASSERT_EQ(bottom_triangle.indices->count, 3u); - - ASSERT_EQ(bottom_triangle.vertices.type, - fb::VertexBuffer::SkinnedVertexBuffer); - auto& vertices = bottom_triangle.vertices.AsSkinnedVertexBuffer()->vertices; - ASSERT_EQ(vertices.size(), 3u); - auto& vertex = vertices[0]; - - Vector3 position = ToVector3(vertex.vertex().position()); - ASSERT_VECTOR3_NEAR(position, Vector3(1, 1, 0)); - - Vector3 normal = ToVector3(vertex.vertex().normal()); - ASSERT_VECTOR3_NEAR(normal, Vector3(0, 0, 1)); - - Vector4 tangent = ToVector4(vertex.vertex().tangent()); - ASSERT_VECTOR4_NEAR(tangent, Vector4(1, 0, 0, -1)); - - Vector2 texture_coords = ToVector2(vertex.vertex().texture_coords()); - ASSERT_POINT_NEAR(texture_coords, Vector2(0, 1)); - - Color color = ToColor(vertex.vertex().color()); - ASSERT_COLOR_NEAR(color, Color(1, 1, 1, 1)); - - Vector4 joints = ToVector4(vertex.joints()); - ASSERT_VECTOR4_NEAR(joints, Vector4(0, 0, 0, 0)); - - Vector4 weights = ToVector4(vertex.weights()); - ASSERT_VECTOR4_NEAR(weights, Vector4(1, 0, 0, 0)); - - ASSERT_EQ(scene.animations.size(), 2u); - ASSERT_EQ(scene.animations[0]->name, "Idle"); - ASSERT_EQ(scene.animations[1]->name, "Metronome"); - ASSERT_EQ(scene.animations[1]->channels.size(), 6u); - auto& channel = scene.animations[1]->channels[3]; - ASSERT_EQ(channel->keyframes.type, fb::Keyframes::RotationKeyframes); - auto* keyframes = channel->keyframes.AsRotationKeyframes(); - ASSERT_EQ(keyframes->values.size(), 40u); - ASSERT_VECTOR4_NEAR(ToVector4(keyframes->values[0]), - Vector4(0.653281, -0.270598, 0.270598, 0.653281)); - ASSERT_VECTOR4_NEAR(ToVector4(keyframes->values[10]), - Vector4(0.700151, 0.0989373, -0.0989373, 0.700151)); -} - -} // namespace testing -} // namespace importer -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/importer/scene.fbs b/impeller/scene/importer/scene.fbs deleted file mode 100644 index 3966e0ed395d9..0000000000000 --- a/impeller/scene/importer/scene.fbs +++ /dev/null @@ -1,198 +0,0 @@ -// 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; - -//----------------------------------------------------------------------------- -/// Materials. -/// - -struct Color { - r: float; - g: float; - b: float; - a: float; -} - -enum ComponentType:byte { - k8Bit, - k16Bit, -} - -table EmbeddedImage { - bytes: [ubyte]; - component_count: ubyte = 0; - component_type: ComponentType; - width: uint; - height: uint; -} - -/// The `bytes` field takes precedent over the `uri` field. -/// If both the `uri` and `bytes` fields are empty, a fully opaque white -/// placeholder will be used. -table Texture { - /// A Flutter asset URI for a compressed image file to import and decode. - uri: string; - /// Decompressed image bytes for uploading to the GPU. If this field is not - /// empty, it takes precedent over the `uri` field for sourcing the texture. - embedded_image: EmbeddedImage; -} - -enum MaterialType:byte { - kUnlit, - kPhysicallyBased, -} - -/// The final color of each material component is the texture color multiplied -/// by the factor of the component. -/// Texture fields are indices into the `Scene`->`textures` array. All textures -/// are optional -- a texture index value of -1 indicates no texture. -table Material { - // When the `MaterialType` is `kUnlit`, only the `base_color` fields are used. - type: MaterialType; - - base_color_factor: Color; - base_color_texture: int = -1; - - metallic_factor: float = 0; - roughness_factor: float = 0.5; - metallic_roughness_texture: int = -1; // Red=Metallic, Green=Roughness. - - normal_texture: int = -1; // Tangent space normal map. - - occlusion_texture: int = -1; -} - -//----------------------------------------------------------------------------- -/// Geometry. -/// - -struct Vec2 { - x: float; - y: float; -} - -struct Vec3 { - x: float; - y: float; - z: float; -} - -struct Vec4 { - x: float; - y: float; - z: float; - w: float; -} - -// This attribute layout is expected to be identical to that within -// `impeller/scene/shaders/geometry.vert`. -struct Vertex { - position: Vec3; - normal: Vec3; - tangent: Vec4; // The 4th component determines the handedness of the tangent. - texture_coords: Vec2; - color: Color; -} - -table UnskinnedVertexBuffer { - vertices: [Vertex]; -} - -struct SkinnedVertex { - vertex: Vertex; - /// Four joint indices corresponding to this mesh's skin transforms. These - /// are floats instead of ints because this vertex data is uploaded directly - /// to the GPU, and float attributes work for all Impeller backends. - joints: Vec4; - /// Four weight values that specify the influence of the corresponding - /// joints. - weights: Vec4; -} - -table SkinnedVertexBuffer { - vertices: [SkinnedVertex]; -} - -union VertexBuffer { UnskinnedVertexBuffer, SkinnedVertexBuffer } - -enum IndexType:byte { - k16Bit, - k32Bit, -} - -table Indices { - data: [ubyte]; - count: uint32; - type: IndexType; -} - -table MeshPrimitive { - vertices: VertexBuffer; - indices: Indices; - material: Material; -} - -//----------------------------------------------------------------------------- -/// Animations. -/// - -table TranslationKeyframes { - values: [Vec3]; -} - -table RotationKeyframes { - values: [Vec4]; -} - -table ScaleKeyframes { - values: [Vec3]; -} - -union Keyframes { TranslationKeyframes, RotationKeyframes, ScaleKeyframes } - -table Channel { - node: int; // Index into `Scene`->`nodes`. - timeline: [float]; - keyframes: Keyframes; -} - -table Animation { - name: string; - channels: [Channel]; -} - -table Skin { - joints: [int]; // Indices into `Scene`->`nodes`. - inverse_bind_matrices: [Matrix]; - /// The root joint of the skeleton. - skeleton: int; // Index into `Scene`->`nodes`. -} - -//----------------------------------------------------------------------------- -/// Scene graph. -/// - -struct Matrix { - m: [float:16]; -} - -table Node { - name: string; - children: [int]; // Indices into `Scene`->`nodes`. - transform: Matrix; - mesh_primitives: [MeshPrimitive]; - skin: Skin; -} - -table Scene { - children: [int]; // Indices into `Scene`->`nodes`. - transform: Matrix; - nodes: [Node]; - textures: [Texture]; // Textures may be reused across different materials. - animations: [Animation]; -} - -root_type Scene; -file_identifier "IPSC"; diff --git a/impeller/scene/importer/scenec_main.cc b/impeller/scene/importer/scenec_main.cc deleted file mode 100644 index 5835fdf3c16b1..0000000000000 --- a/impeller/scene/importer/scenec_main.cc +++ /dev/null @@ -1,108 +0,0 @@ -// 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/mapping.h" -#include "impeller/base/strings.h" -#include "impeller/compiler/utilities.h" -#include "impeller/scene/importer/importer.h" -#include "impeller/scene/importer/scene_flatbuffers.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::SceneT scene; - bool success = false; - switch (switches.input_type) { - case SourceType::kGLTF: - success = ParseGLTF(*source_file_mapping, scene); - 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::Scene::Pack(builder, &scene), fb::SceneIdentifier()); - - 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/switches.cc b/impeller/scene/importer/switches.cc deleted file mode 100644 index 518e04d29f3ba..0000000000000 --- a/impeller/scene/importer/switches.cc +++ /dev/null @@ -1,95 +0,0 @@ -// 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 << "SceneC 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 deleted file mode 100644 index 567cd407f2dcb..0000000000000 --- a/impeller/scene/importer/switches.h +++ /dev/null @@ -1,40 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_IMPORTER_SWITCHES_H_ -#define FLUTTER_IMPELLER_SCENE_IMPORTER_SWITCHES_H_ - -#include -#include - -#include "flutter/fml/command_line.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 - -#endif // FLUTTER_IMPELLER_SCENE_IMPORTER_SWITCHES_H_ diff --git a/impeller/scene/importer/types.h b/impeller/scene/importer/types.h deleted file mode 100644 index 0312618e5aa04..0000000000000 --- a/impeller/scene/importer/types.h +++ /dev/null @@ -1,21 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_IMPORTER_TYPES_H_ -#define FLUTTER_IMPELLER_SCENE_IMPORTER_TYPES_H_ - -namespace impeller { -namespace scene { -namespace importer { - -enum class SourceType { - kUnknown, - kGLTF, -}; - -} // namespace importer -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_IMPORTER_TYPES_H_ diff --git a/impeller/scene/importer/vertices_builder.cc b/impeller/scene/importer/vertices_builder.cc deleted file mode 100644 index 589bd0abf6714..0000000000000 --- a/impeller/scene/importer/vertices_builder.cc +++ /dev/null @@ -1,241 +0,0 @@ -// 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/vertices_builder.h" - -#include -#include -#include -#include -#include - -#include "flutter/fml/logging.h" -#include "impeller/scene/importer/conversions.h" -#include "impeller/scene/importer/scene_flatbuffers.h" - -namespace impeller { -namespace scene { -namespace importer { - -//------------------------------------------------------------------------------ -/// VerticesBuilder -/// - -std::unique_ptr VerticesBuilder::MakeUnskinned() { - return std::make_unique(); -} - -std::unique_ptr VerticesBuilder::MakeSkinned() { - return std::make_unique(); -} - -VerticesBuilder::VerticesBuilder() = default; - -VerticesBuilder::~VerticesBuilder() = default; - -/// @brief Reads a numeric component from `source` and returns a 32bit float. -/// If `normalized` is `true`, signed SourceTypes convert to a range of -/// -1 to 1, and unsigned SourceTypes convert to a range of 0 to 1. -template -static Scalar ToScalar(const void* source, size_t index, bool normalized) { - const SourceType* s = reinterpret_cast(source) + index; - Scalar result = static_cast(*s); - if (normalized) { - constexpr SourceType divisor = std::is_integral_v - ? std::numeric_limits::max() - : 1; - result = static_cast(*s) / static_cast(divisor); - } - return result; -} - -/// @brief A ComponentWriter which simply converts all of an attribute's -/// components to normalized scalar form. -static void PassthroughAttributeWriter( - Scalar* destination, - const void* source, - const VerticesBuilder::ComponentProperties& component, - const VerticesBuilder::AttributeProperties& attribute) { - FML_DCHECK(attribute.size_bytes == - attribute.component_count * sizeof(Scalar)); - for (size_t component_i = 0; component_i < attribute.component_count; - component_i++) { - *(destination + component_i) = - component.convert_proc(source, component_i, true); - } -} - -/// @brief A ComponentWriter which converts four vertex indices to scalars. -static void JointsAttributeWriter( - Scalar* destination, - const void* source, - const VerticesBuilder::ComponentProperties& component, - const VerticesBuilder::AttributeProperties& attribute) { - FML_DCHECK(attribute.component_count == 4); - for (int i = 0; i < 4; i++) { - *(destination + i) = component.convert_proc(source, i, false); - } -} - -std::map - VerticesBuilder::kAttributeTypes = { - {VerticesBuilder::AttributeType::kPosition, - {.offset_bytes = offsetof(UnskinnedVerticesBuilder::Vertex, position), - .size_bytes = sizeof(UnskinnedVerticesBuilder::Vertex::position), - .component_count = 3, - .write_proc = PassthroughAttributeWriter}}, - {VerticesBuilder::AttributeType::kNormal, - {.offset_bytes = offsetof(UnskinnedVerticesBuilder::Vertex, normal), - .size_bytes = sizeof(UnskinnedVerticesBuilder::Vertex::normal), - .component_count = 3, - .write_proc = PassthroughAttributeWriter}}, - {VerticesBuilder::AttributeType::kTangent, - {.offset_bytes = offsetof(UnskinnedVerticesBuilder::Vertex, tangent), - .size_bytes = sizeof(UnskinnedVerticesBuilder::Vertex::tangent), - .component_count = 4, - .write_proc = PassthroughAttributeWriter}}, - {VerticesBuilder::AttributeType::kTextureCoords, - {.offset_bytes = - offsetof(UnskinnedVerticesBuilder::Vertex, texture_coords), - .size_bytes = - sizeof(UnskinnedVerticesBuilder::Vertex::texture_coords), - .component_count = 2, - .write_proc = PassthroughAttributeWriter}}, - {VerticesBuilder::AttributeType::kColor, - {.offset_bytes = offsetof(UnskinnedVerticesBuilder::Vertex, color), - .size_bytes = sizeof(UnskinnedVerticesBuilder::Vertex::color), - .component_count = 4, - .write_proc = PassthroughAttributeWriter}}, - {VerticesBuilder::AttributeType::kJoints, - {.offset_bytes = offsetof(SkinnedVerticesBuilder::Vertex, joints), - .size_bytes = sizeof(SkinnedVerticesBuilder::Vertex::joints), - .component_count = 4, - .write_proc = JointsAttributeWriter}}, - {VerticesBuilder::AttributeType::kWeights, - {.offset_bytes = offsetof(SkinnedVerticesBuilder::Vertex, weights), - .size_bytes = sizeof(SkinnedVerticesBuilder::Vertex::weights), - .component_count = 4, - .write_proc = JointsAttributeWriter}}}; - -static std::map - kComponentTypes = { - {VerticesBuilder::ComponentType::kSignedByte, - {.size_bytes = sizeof(int8_t), .convert_proc = ToScalar}}, - {VerticesBuilder::ComponentType::kUnsignedByte, - {.size_bytes = sizeof(int8_t), .convert_proc = ToScalar}}, - {VerticesBuilder::ComponentType::kSignedShort, - {.size_bytes = sizeof(int16_t), .convert_proc = ToScalar}}, - {VerticesBuilder::ComponentType::kUnsignedShort, - {.size_bytes = sizeof(int16_t), .convert_proc = ToScalar}}, - {VerticesBuilder::ComponentType::kSignedInt, - {.size_bytes = sizeof(int32_t), .convert_proc = ToScalar}}, - {VerticesBuilder::ComponentType::kUnsignedInt, - {.size_bytes = sizeof(int32_t), .convert_proc = ToScalar}}, - {VerticesBuilder::ComponentType::kFloat, - {.size_bytes = sizeof(float), .convert_proc = ToScalar}}, -}; - -void VerticesBuilder::WriteAttribute(void* destination, - size_t destination_stride_bytes, - AttributeType attribute, - ComponentType component_type, - const void* source, - size_t attribute_stride_bytes, - size_t attribute_count) { - const ComponentProperties& component_props = kComponentTypes[component_type]; - const AttributeProperties& attribute_props = kAttributeTypes[attribute]; - for (size_t i = 0; i < attribute_count; i++) { - const uint8_t* src = - reinterpret_cast(source) + attribute_stride_bytes * i; - uint8_t* dst = reinterpret_cast(destination) + - i * destination_stride_bytes + attribute_props.offset_bytes; - - attribute_props.write_proc(reinterpret_cast(dst), src, - component_props, attribute_props); - } -} - -//------------------------------------------------------------------------------ -/// UnskinnedVerticesBuilder -/// - -UnskinnedVerticesBuilder::UnskinnedVerticesBuilder() = default; - -UnskinnedVerticesBuilder::~UnskinnedVerticesBuilder() = default; - -void UnskinnedVerticesBuilder::WriteFBVertices( - fb::MeshPrimitiveT& primitive) const { - auto vertex_buffer = fb::UnskinnedVertexBufferT(); - vertex_buffer.vertices.resize(0); - for (auto& v : vertices_) { - vertex_buffer.vertices.push_back(fb::Vertex( - ToFBVec3(v.position), ToFBVec3(v.normal), ToFBVec4(v.tangent), - ToFBVec2(v.texture_coords), ToFBColor(v.color))); - } - primitive.vertices.Set(std::move(vertex_buffer)); -} - -void UnskinnedVerticesBuilder::SetAttributeFromBuffer( - AttributeType attribute, - ComponentType component_type, - const void* buffer_start, - size_t attribute_stride_bytes, - size_t attribute_count) { - if (attribute_count > vertices_.size()) { - vertices_.resize(attribute_count, Vertex()); - } - WriteAttribute(vertices_.data(), // destination - sizeof(Vertex), // destination_stride_bytes - attribute, // attribute - component_type, // component_type - buffer_start, // source - attribute_stride_bytes, // attribute_stride_bytes - attribute_count); // attribute_count -} - -//------------------------------------------------------------------------------ -/// SkinnedVerticesBuilder -/// - -SkinnedVerticesBuilder::SkinnedVerticesBuilder() = default; - -SkinnedVerticesBuilder::~SkinnedVerticesBuilder() = default; - -void SkinnedVerticesBuilder::WriteFBVertices( - fb::MeshPrimitiveT& primitive) const { - auto vertex_buffer = fb::SkinnedVertexBufferT(); - vertex_buffer.vertices.resize(0); - for (auto& v : vertices_) { - auto unskinned_attributes = fb::Vertex( - ToFBVec3(v.vertex.position), ToFBVec3(v.vertex.normal), - ToFBVec4(v.vertex.tangent), ToFBVec2(v.vertex.texture_coords), - ToFBColor(v.vertex.color)); - vertex_buffer.vertices.push_back(fb::SkinnedVertex( - unskinned_attributes, ToFBVec4(v.joints), ToFBVec4(v.weights))); - } - primitive.vertices.Set(std::move(vertex_buffer)); -} - -void SkinnedVerticesBuilder::SetAttributeFromBuffer( - AttributeType attribute, - ComponentType component_type, - const void* buffer_start, - size_t attribute_stride_bytes, - size_t attribute_count) { - if (attribute_count > vertices_.size()) { - vertices_.resize(attribute_count, Vertex()); - } - WriteAttribute(vertices_.data(), // destination - sizeof(Vertex), // destination_stride_bytes - attribute, // attribute - component_type, // component_type - buffer_start, // source - attribute_stride_bytes, // attribute_stride_bytes - attribute_count); // attribute_count -} - -} // namespace importer -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/importer/vertices_builder.h b/impeller/scene/importer/vertices_builder.h deleted file mode 100644 index 799f6df9e3db2..0000000000000 --- a/impeller/scene/importer/vertices_builder.h +++ /dev/null @@ -1,173 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_IMPORTER_VERTICES_BUILDER_H_ -#define FLUTTER_IMPELLER_SCENE_IMPORTER_VERTICES_BUILDER_H_ - -#include -#include - -#include "impeller/geometry/matrix.h" -#include "impeller/scene/importer/scene_flatbuffers.h" - -namespace impeller { -namespace scene { -namespace importer { - -//------------------------------------------------------------------------------ -/// VerticesBuilder -/// - -class VerticesBuilder { - public: - static std::unique_ptr MakeUnskinned(); - - static std::unique_ptr MakeSkinned(); - - enum class ComponentType { - kSignedByte = 5120, - kUnsignedByte, - kSignedShort, - kUnsignedShort, - kSignedInt, - kUnsignedInt, - kFloat, - }; - - enum class AttributeType { - kPosition, - kNormal, - kTangent, - kTextureCoords, - kColor, - kJoints, - kWeights, - }; - - using ComponentConverter = std::function< - Scalar(const void* source, size_t byte_offset, bool normalized)>; - struct ComponentProperties { - size_t size_bytes = 0; - ComponentConverter convert_proc; - }; - - struct AttributeProperties; - using AttributeWriter = - std::function; - struct AttributeProperties { - size_t offset_bytes = 0; - size_t size_bytes = 0; - size_t component_count = 0; - AttributeWriter write_proc; - }; - - VerticesBuilder(); - - virtual ~VerticesBuilder(); - - virtual void WriteFBVertices(fb::MeshPrimitiveT& primitive) const = 0; - - virtual void SetAttributeFromBuffer(AttributeType attribute, - ComponentType component_type, - const void* buffer_start, - size_t attribute_stride_bytes, - size_t attribute_count) = 0; - - protected: - static void WriteAttribute(void* destination, - size_t destination_stride_bytes, - AttributeType attribute, - ComponentType component_type, - const void* source, - size_t attribute_stride_bytes, - size_t attribute_count); - - private: - static std::map - kAttributeTypes; - - VerticesBuilder(const VerticesBuilder&) = delete; - - VerticesBuilder& operator=(const VerticesBuilder&) = delete; -}; - -//------------------------------------------------------------------------------ -/// UnskinnedVerticesBuilder -/// - -class UnskinnedVerticesBuilder final : public VerticesBuilder { - public: - struct Vertex { - Vector3 position; - Vector3 normal; - Vector4 tangent; - Vector2 texture_coords; - Color color = Color::White(); - }; - - UnskinnedVerticesBuilder(); - - virtual ~UnskinnedVerticesBuilder() override; - - // |VerticesBuilder| - void WriteFBVertices(fb::MeshPrimitiveT& primitive) const override; - - // |VerticesBuilder| - void SetAttributeFromBuffer(AttributeType attribute, - ComponentType component_type, - const void* buffer_start, - size_t attribute_stride_bytes, - size_t attribute_count) override; - - private: - std::vector vertices_; - - UnskinnedVerticesBuilder(const UnskinnedVerticesBuilder&) = delete; - - UnskinnedVerticesBuilder& operator=(const UnskinnedVerticesBuilder&) = delete; -}; - -//------------------------------------------------------------------------------ -/// SkinnedVerticesBuilder -/// - -class SkinnedVerticesBuilder final : public VerticesBuilder { - public: - struct Vertex { - UnskinnedVerticesBuilder::Vertex vertex; - Vector4 joints; - Vector4 weights; - }; - - SkinnedVerticesBuilder(); - - virtual ~SkinnedVerticesBuilder() override; - - // |VerticesBuilder| - void WriteFBVertices(fb::MeshPrimitiveT& primitive) const override; - - // |VerticesBuilder| - void SetAttributeFromBuffer(AttributeType attribute, - ComponentType component_type, - const void* buffer_start, - size_t attribute_stride_bytes, - size_t attribute_count) override; - - private: - std::vector vertices_; - - SkinnedVerticesBuilder(const SkinnedVerticesBuilder&) = delete; - - SkinnedVerticesBuilder& operator=(const SkinnedVerticesBuilder&) = delete; -}; - -} // namespace importer -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_IMPORTER_VERTICES_BUILDER_H_ diff --git a/impeller/scene/material.cc b/impeller/scene/material.cc deleted file mode 100644 index 5794c95425148..0000000000000 --- a/impeller/scene/material.cc +++ /dev/null @@ -1,228 +0,0 @@ -// 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/material.h" -#include "impeller/base/validation.h" -#include "impeller/core/formats.h" -#include "impeller/core/sampler_descriptor.h" -#include "impeller/renderer/render_pass.h" -#include "impeller/scene/importer/conversions.h" -#include "impeller/scene/importer/scene_flatbuffers.h" -#include "impeller/scene/pipeline_key.h" -#include "impeller/scene/scene_context.h" -#include "impeller/scene/shaders/unlit.frag.h" - -#include - -namespace impeller { -namespace scene { - -//------------------------------------------------------------------------------ -/// Material -/// - -Material::~Material() = default; - -std::unique_ptr Material::MakeFromFlatbuffer( - const fb::Material& material, - const std::vector>& textures) { - switch (material.type()) { - case fb::MaterialType::kUnlit: - return UnlitMaterial::MakeFromFlatbuffer(material, textures); - case fb::MaterialType::kPhysicallyBased: - return PhysicallyBasedMaterial::MakeFromFlatbuffer(material, textures); - } -} - -std::unique_ptr Material::MakeUnlit() { - return std::make_unique(); -} - -std::unique_ptr Material::MakePhysicallyBased() { - return std::make_unique(); -} - -void Material::SetVertexColorWeight(Scalar weight) { - vertex_color_weight_ = weight; -} - -void Material::SetBlendConfig(BlendConfig blend_config) { - blend_config_ = blend_config; -} - -void Material::SetStencilConfig(StencilConfig stencil_config) { - stencil_config_ = stencil_config; -} - -void Material::SetTranslucent(bool is_translucent) { - is_translucent_ = is_translucent; -} - -SceneContextOptions Material::GetContextOptions(const RenderPass& pass) const { - // TODO(bdero): Pipeline blend and stencil config. - return {.sample_count = pass.GetRenderTarget().GetSampleCount()}; -} - -//------------------------------------------------------------------------------ -/// UnlitMaterial -/// - -std::unique_ptr UnlitMaterial::MakeFromFlatbuffer( - const fb::Material& material, - const std::vector>& textures) { - if (material.type() != fb::MaterialType::kUnlit) { - VALIDATION_LOG << "Cannot unpack unlit material because the ipscene " - "material type is not unlit."; - return nullptr; - } - - auto result = Material::MakeUnlit(); - - if (material.base_color_factor()) { - result->SetColor(importer::ToColor(*material.base_color_factor())); - } - - if (material.base_color_texture() >= 0 && - material.base_color_texture() < static_cast(textures.size())) { - result->SetColorTexture(textures[material.base_color_texture()]); - } - - return result; -} - -UnlitMaterial::~UnlitMaterial() = default; - -void UnlitMaterial::SetColor(Color color) { - color_ = color; -} - -void UnlitMaterial::SetColorTexture(std::shared_ptr color_texture) { - color_texture_ = std::move(color_texture); -} - -// |Material| -MaterialType UnlitMaterial::GetMaterialType() const { - return MaterialType::kUnlit; -} - -// |Material| -void UnlitMaterial::BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - RenderPass& pass) const { - // Uniform buffer. - UnlitFragmentShader::FragInfo info; - info.color = color_; - info.vertex_color_weight = vertex_color_weight_; - UnlitFragmentShader::BindFragInfo(pass, buffer.EmplaceUniform(info)); - - // Textures. - SamplerDescriptor sampler_descriptor; - sampler_descriptor.label = "Trilinear"; - sampler_descriptor.min_filter = MinMagFilter::kLinear; - sampler_descriptor.mag_filter = MinMagFilter::kLinear; - sampler_descriptor.mip_filter = MipFilter::kLinear; - UnlitFragmentShader::BindBaseColorTexture( - pass, - color_texture_ ? color_texture_ : scene_context.GetPlaceholderTexture(), - scene_context.GetContext()->GetSamplerLibrary()->GetSampler( - sampler_descriptor)); -} - -//------------------------------------------------------------------------------ -/// StandardMaterial -/// - -std::unique_ptr -PhysicallyBasedMaterial::MakeFromFlatbuffer( - const fb::Material& material, - const std::vector>& textures) { - if (material.type() != fb::MaterialType::kPhysicallyBased) { - VALIDATION_LOG << "Cannot unpack unlit material because the ipscene " - "material type is not unlit."; - return nullptr; - } - - auto result = Material::MakePhysicallyBased(); - - result->SetAlbedo(material.base_color_factor() - ? importer::ToColor(*material.base_color_factor()) - : Color::White()); - result->SetRoughness(material.roughness_factor()); - result->SetMetallic(material.metallic_factor()); - - if (material.base_color_texture() >= 0 && - material.base_color_texture() < static_cast(textures.size())) { - result->SetAlbedoTexture(textures[material.base_color_texture()]); - result->SetVertexColorWeight(0); - } - if (material.metallic_roughness_texture() >= 0 && - material.metallic_roughness_texture() < - static_cast(textures.size())) { - result->SetMetallicRoughnessTexture( - textures[material.metallic_roughness_texture()]); - } - if (material.normal_texture() >= 0 && - material.normal_texture() < static_cast(textures.size())) { - result->SetNormalTexture(textures[material.normal_texture()]); - } - if (material.occlusion_texture() >= 0 && - material.occlusion_texture() < static_cast(textures.size())) { - result->SetOcclusionTexture(textures[material.occlusion_texture()]); - } - - return result; -} - -PhysicallyBasedMaterial::~PhysicallyBasedMaterial() = default; - -void PhysicallyBasedMaterial::SetAlbedo(Color albedo) { - albedo_ = albedo; -} - -void PhysicallyBasedMaterial::SetRoughness(Scalar roughness) { - roughness_ = roughness; -} - -void PhysicallyBasedMaterial::SetMetallic(Scalar metallic) { - metallic_ = metallic; -} - -void PhysicallyBasedMaterial::SetAlbedoTexture( - std::shared_ptr albedo_texture) { - albedo_texture_ = std::move(albedo_texture); -} - -void PhysicallyBasedMaterial::SetMetallicRoughnessTexture( - std::shared_ptr metallic_roughness_texture) { - metallic_roughness_texture_ = std::move(metallic_roughness_texture); -} - -void PhysicallyBasedMaterial::SetNormalTexture( - std::shared_ptr normal_texture) { - normal_texture_ = std::move(normal_texture); -} - -void PhysicallyBasedMaterial::SetOcclusionTexture( - std::shared_ptr occlusion_texture) { - occlusion_texture_ = std::move(occlusion_texture); -} - -void PhysicallyBasedMaterial::SetEnvironmentMap( - std::shared_ptr environment_map) { - environment_map_ = std::move(environment_map); -} - -// |Material| -MaterialType PhysicallyBasedMaterial::GetMaterialType() const { - // TODO(bdero): Replace this once a PBR shader has landed. - return MaterialType::kUnlit; -} - -// |Material| -void PhysicallyBasedMaterial::BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - RenderPass& pass) const {} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/material.h b/impeller/scene/material.h deleted file mode 100644 index fda78807241bf..0000000000000 --- a/impeller/scene/material.h +++ /dev/null @@ -1,142 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_MATERIAL_H_ -#define FLUTTER_IMPELLER_SCENE_MATERIAL_H_ - -#include - -#include "impeller/core/formats.h" -#include "impeller/core/texture.h" -#include "impeller/geometry/scalar.h" -#include "impeller/renderer/render_pass.h" -#include "impeller/scene/importer/scene_flatbuffers.h" -#include "impeller/scene/pipeline_key.h" - -namespace impeller { -namespace scene { - -class SceneContext; -struct SceneContextOptions; -class Geometry; - -class UnlitMaterial; -class PhysicallyBasedMaterial; - -class Material { - public: - struct BlendConfig { - BlendOperation color_op = BlendOperation::kAdd; - BlendFactor source_color_factor = BlendFactor::kOne; - BlendFactor destination_color_factor = BlendFactor::kOneMinusSourceAlpha; - BlendOperation alpha_op = BlendOperation::kAdd; - BlendFactor source_alpha_factor = BlendFactor::kOne; - BlendFactor destination_alpha_factor = BlendFactor::kOneMinusSourceAlpha; - }; - - struct StencilConfig { - StencilOperation operation = StencilOperation::kKeep; - CompareFunction compare = CompareFunction::kAlways; - }; - - static std::unique_ptr MakeFromFlatbuffer( - const fb::Material& material, - const std::vector>& textures); - - static std::unique_ptr MakeUnlit(); - static std::unique_ptr MakePhysicallyBased(); - - virtual ~Material(); - - void SetVertexColorWeight(Scalar weight); - void SetBlendConfig(BlendConfig blend_config); - void SetStencilConfig(StencilConfig stencil_config); - - void SetTranslucent(bool is_translucent); - - SceneContextOptions GetContextOptions(const RenderPass& pass) const; - - virtual MaterialType GetMaterialType() const = 0; - - virtual void BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - RenderPass& pass) const = 0; - - protected: - Scalar vertex_color_weight_ = 1; - BlendConfig blend_config_; - StencilConfig stencil_config_; - bool is_translucent_ = false; -}; - -class UnlitMaterial final : public Material { - public: - static std::unique_ptr MakeFromFlatbuffer( - const fb::Material& material, - const std::vector>& textures); - - ~UnlitMaterial(); - - void SetColor(Color color); - - void SetColorTexture(std::shared_ptr color_texture); - - // |Material| - MaterialType GetMaterialType() const override; - - // |Material| - void BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - RenderPass& pass) const override; - - private: - Color color_ = Color::White(); - std::shared_ptr color_texture_; -}; - -class PhysicallyBasedMaterial final : public Material { - public: - static std::unique_ptr MakeFromFlatbuffer( - const fb::Material& material, - const std::vector>& textures); - - ~PhysicallyBasedMaterial(); - - void SetAlbedo(Color albedo); - void SetRoughness(Scalar roughness); - void SetMetallic(Scalar metallic); - - void SetAlbedoTexture(std::shared_ptr albedo_texture); - void SetMetallicRoughnessTexture( - std::shared_ptr metallic_roughness_texture); - void SetNormalTexture(std::shared_ptr normal_texture); - void SetOcclusionTexture(std::shared_ptr occlusion_texture); - - void SetEnvironmentMap(std::shared_ptr environment_map); - - // |Material| - MaterialType GetMaterialType() const override; - - // |Material| - void BindToCommand(const SceneContext& scene_context, - HostBuffer& buffer, - RenderPass& pass) const override; - - private: - Color albedo_ = Color::White(); - Scalar metallic_ = 0.5; - Scalar roughness_ = 0.5; - - std::shared_ptr albedo_texture_; - std::shared_ptr metallic_roughness_texture_; - std::shared_ptr normal_texture_; - std::shared_ptr occlusion_texture_; - - std::shared_ptr environment_map_; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_MATERIAL_H_ diff --git a/impeller/scene/mesh.cc b/impeller/scene/mesh.cc deleted file mode 100644 index bc4b02da00a80..0000000000000 --- a/impeller/scene/mesh.cc +++ /dev/null @@ -1,53 +0,0 @@ -// 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/mesh.h" - -#include -#include - -#include "impeller/base/validation.h" -#include "impeller/scene/material.h" -#include "impeller/scene/pipeline_key.h" -#include "impeller/scene/scene_encoder.h" - -namespace impeller { -namespace scene { - -Mesh::Mesh() = default; -Mesh::~Mesh() = default; - -void Mesh::AddPrimitive(Primitive mesh) { - if (mesh.geometry == nullptr) { - VALIDATION_LOG << "Mesh geometry cannot be null."; - } - if (mesh.material == nullptr) { - VALIDATION_LOG << "Mesh material cannot be null."; - } - - primitives_.push_back(std::move(mesh)); -} - -std::vector& Mesh::GetPrimitives() { - return primitives_; -} - -bool Mesh::Render(SceneEncoder& encoder, - const Matrix& transform, - const std::shared_ptr& joints) const { - for (const auto& mesh : primitives_) { - mesh.geometry->SetJointsTexture(joints); - SceneCommand command = { - .label = "Mesh Primitive", - .transform = transform, - .geometry = mesh.geometry.get(), - .material = mesh.material.get(), - }; - encoder.Add(command); - } - return true; -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/mesh.h b/impeller/scene/mesh.h deleted file mode 100644 index d48d44ae4e914..0000000000000 --- a/impeller/scene/mesh.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_MESH_H_ -#define FLUTTER_IMPELLER_SCENE_MESH_H_ - -#include - -#include "impeller/scene/geometry.h" -#include "impeller/scene/material.h" -#include "impeller/scene/scene_encoder.h" - -namespace impeller { -namespace scene { - -class Skin; - -class Mesh final { - public: - struct Primitive { - std::shared_ptr geometry; - std::shared_ptr material; - }; - - Mesh(); - ~Mesh(); - - Mesh(Mesh&& mesh); - Mesh& operator=(Mesh&& mesh); - - void AddPrimitive(Primitive mesh_); - std::vector& GetPrimitives(); - - bool Render(SceneEncoder& encoder, - const Matrix& transform, - const std::shared_ptr& joints) const; - - private: - std::vector primitives_; - - Mesh(const Mesh&) = delete; - - Mesh& operator=(const Mesh&) = delete; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_MESH_H_ diff --git a/impeller/scene/node.cc b/impeller/scene/node.cc deleted file mode 100644 index aad7fc67199e7..0000000000000 --- a/impeller/scene/node.cc +++ /dev/null @@ -1,417 +0,0 @@ -// 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/node.h" - -#include -#include -#include -#include - -#include "flutter/fml/logging.h" -#include "impeller/base/strings.h" -#include "impeller/base/thread.h" -#include "impeller/base/validation.h" -#include "impeller/geometry/matrix.h" -#include "impeller/scene/animation/animation_player.h" -#include "impeller/scene/importer/conversions.h" -#include "impeller/scene/importer/scene_flatbuffers.h" -#include "impeller/scene/mesh.h" -#include "impeller/scene/node.h" -#include "impeller/scene/scene_encoder.h" - -namespace impeller { -namespace scene { - -static std::atomic_uint64_t kNextNodeID = 0; - -void Node::MutationLog::Append(const Entry& entry) { - WriterLock lock(write_mutex_); - dirty_ = true; - entries_.push_back(entry); -} - -std::optional> -Node::MutationLog::Flush() { - WriterLock lock(write_mutex_); - if (!dirty_) { - return std::nullopt; - } - dirty_ = false; - auto result = entries_; - entries_ = {}; - return result; -} - -std::shared_ptr Node::MakeFromFlatbuffer( - const fml::Mapping& ipscene_mapping, - Allocator& allocator) { - flatbuffers::Verifier verifier(ipscene_mapping.GetMapping(), - ipscene_mapping.GetSize()); - if (!fb::VerifySceneBuffer(verifier)) { - VALIDATION_LOG << "Failed to unpack scene: Scene flatbuffer is invalid."; - return nullptr; - } - - return Node::MakeFromFlatbuffer(*fb::GetScene(ipscene_mapping.GetMapping()), - allocator); -} - -static std::shared_ptr UnpackTextureFromFlatbuffer( - const fb::Texture* iptexture, - Allocator& allocator) { - if (iptexture == nullptr || iptexture->embedded_image() == nullptr || - iptexture->embedded_image()->bytes() == nullptr) { - return nullptr; - } - - auto embedded = iptexture->embedded_image(); - - uint8_t bytes_per_component = 0; - switch (embedded->component_type()) { - case fb::ComponentType::k8Bit: - bytes_per_component = 1; - break; - case fb::ComponentType::k16Bit: - // bytes_per_component = 2; - FML_LOG(WARNING) << "16 bit textures not yet supported."; - return nullptr; - } - - switch (embedded->component_count()) { - case 4: - // RGBA. - break; - case 1: - case 3: - default: - FML_LOG(WARNING) << "Textures with " << embedded->component_count() - << " components are not supported." << std::endl; - return nullptr; - } - if (embedded->bytes()->size() != bytes_per_component * - embedded->component_count() * - embedded->width() * embedded->height()) { - FML_LOG(WARNING) << "Embedded texture has an unexpected size. Skipping." - << std::endl; - return nullptr; - } - - auto image_mapping = std::make_shared( - embedded->bytes()->Data(), embedded->bytes()->size()); - - auto texture_descriptor = TextureDescriptor{}; - texture_descriptor.storage_mode = StorageMode::kHostVisible; - texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; - texture_descriptor.size = ISize(embedded->width(), embedded->height()); - // TODO(bdero): Generate mipmaps for embedded textures. - texture_descriptor.mip_count = 1u; - - auto texture = allocator.CreateTexture(texture_descriptor); - if (!texture) { - FML_LOG(ERROR) << "Could not allocate texture."; - return nullptr; - } - - auto uploaded = texture->SetContents(image_mapping); - if (!uploaded) { - FML_LOG(ERROR) << "Could not upload texture to device memory."; - return nullptr; - } - - return texture; -} - -std::shared_ptr Node::MakeFromFlatbuffer(const fb::Scene& scene, - Allocator& allocator) { - // Unpack textures. - std::vector> textures; - if (scene.textures()) { - for (const auto iptexture : *scene.textures()) { - // The elements of the unpacked texture array must correspond exactly with - // the ipscene texture array. So if a texture is empty or invalid, a - // nullptr is inserted as a placeholder. - textures.push_back(UnpackTextureFromFlatbuffer(iptexture, allocator)); - } - } - - auto result = std::make_shared(); - result->SetLocalTransform(importer::ToMatrix(*scene.transform())); - - if (!scene.nodes() || !scene.children()) { - return result; // The scene is empty. - } - - // Initialize nodes for unpacking the entire scene. - std::vector> scene_nodes; - scene_nodes.reserve(scene.nodes()->size()); - for (size_t node_i = 0; node_i < scene.nodes()->size(); node_i++) { - scene_nodes.push_back(std::make_shared()); - } - - // Connect children to the root node. - for (int child : *scene.children()) { - if (child < 0 || static_cast(child) >= scene_nodes.size()) { - VALIDATION_LOG << "Scene child index out of range."; - continue; - } - result->AddChild(scene_nodes[child]); - } - - // Unpack each node. - for (size_t node_i = 0; node_i < scene.nodes()->size(); node_i++) { - scene_nodes[node_i]->UnpackFromFlatbuffer(*scene.nodes()->Get(node_i), - scene_nodes, textures, allocator); - } - - // Unpack animations. - if (scene.animations()) { - for (const auto animation : *scene.animations()) { - if (auto out_animation = - Animation::MakeFromFlatbuffer(*animation, scene_nodes)) { - result->animations_.push_back(out_animation); - } - } - } - - return result; -} - -void Node::UnpackFromFlatbuffer( - const fb::Node& source_node, - const std::vector>& scene_nodes, - const std::vector>& textures, - Allocator& allocator) { - name_ = source_node.name()->str(); - SetLocalTransform(importer::ToMatrix(*source_node.transform())); - - /// Meshes. - - if (source_node.mesh_primitives()) { - Mesh mesh; - for (const auto* primitives : *source_node.mesh_primitives()) { - auto geometry = Geometry::MakeFromFlatbuffer(*primitives, allocator); - auto material = - primitives->material() - ? Material::MakeFromFlatbuffer(*primitives->material(), textures) - : Material::MakeUnlit(); - mesh.AddPrimitive({std::move(geometry), std::move(material)}); - } - SetMesh(std::move(mesh)); - } - - /// Child nodes. - - if (source_node.children()) { - // Wire up graph connections. - for (int child : *source_node.children()) { - if (child < 0 || static_cast(child) >= scene_nodes.size()) { - VALIDATION_LOG << "Node child index out of range."; - continue; - } - AddChild(scene_nodes[child]); - } - } - - /// Skin. - - if (source_node.skin()) { - skin_ = Skin::MakeFromFlatbuffer(*source_node.skin(), scene_nodes); - } -} - -Node::Node() : name_(SPrintF("__node%" PRIu64, kNextNodeID++)){}; - -Node::~Node() = default; - -Mesh::Mesh(Mesh&& mesh) = default; - -Mesh& Mesh::operator=(Mesh&& mesh) = default; - -const std::string& Node::GetName() const { - return name_; -} - -void Node::SetName(const std::string& new_name) { - name_ = new_name; -} - -Node* Node::GetParent() const { - return parent_; -} - -std::shared_ptr Node::FindChildByName( - const std::string& name, - bool exclude_animation_players) const { - for (auto& child : children_) { - if (exclude_animation_players && child->animation_player_.has_value()) { - continue; - } - if (child->GetName() == name) { - return child; - } - if (auto found = child->FindChildByName(name)) { - return found; - } - } - return nullptr; -} - -std::shared_ptr Node::FindAnimationByName( - const std::string& name) const { - for (const auto& animation : animations_) { - if (animation->GetName() == name) { - return animation; - } - } - return nullptr; -} - -AnimationClip* Node::AddAnimation(const std::shared_ptr& animation) { - if (!animation_player_.has_value()) { - animation_player_ = AnimationPlayer(); - } - return animation_player_->AddAnimation(animation, this); -} - -void Node::SetLocalTransform(Matrix transform) { - local_transform_ = transform; -} - -Matrix Node::GetLocalTransform() const { - return local_transform_; -} - -void Node::SetGlobalTransform(Matrix transform) { - Matrix inverse_global_transform = - parent_ ? parent_->GetGlobalTransform().Invert() : Matrix(); - - local_transform_ = inverse_global_transform * transform; -} - -Matrix Node::GetGlobalTransform() const { - if (parent_) { - return parent_->GetGlobalTransform() * local_transform_; - } - return local_transform_; -} - -bool Node::AddChild(std::shared_ptr node) { - if (!node) { - VALIDATION_LOG << "Cannot add null child to node."; - return false; - } - - // TODO(bdero): Figure out a better paradigm/rules for nodes with multiple - // parents. We should probably disallow this, make deep - // copying of nodes cheap and easy, add mesh instancing, etc. - // Today, the parent link is only used for skin posing, and so - // it's reasonable to not have a check and allow multi-parenting. - // Even still, there should still be some kind of cycle - // prevention/detection, ideally at the protocol level. - // - // if (node->parent_ != nullptr) { - // VALIDATION_LOG - // << "Cannot add a node as a child which already has a parent."; - // return false; - // } - node->parent_ = this; - children_.push_back(std::move(node)); - - return true; -} - -std::vector>& Node::GetChildren() { - return children_; -} - -void Node::SetMesh(Mesh mesh) { - mesh_ = std::move(mesh); -} - -Mesh& Node::GetMesh() { - return mesh_; -} - -void Node::SetIsJoint(bool is_joint) { - is_joint_ = is_joint; -} - -bool Node::IsJoint() const { - return is_joint_; -} - -bool Node::Render(SceneEncoder& encoder, - Allocator& allocator, - const Matrix& parent_transform) { - std::optional> log = mutation_log_.Flush(); - if (log.has_value()) { - for (const auto& entry : log.value()) { - if (auto e = std::get_if(&entry)) { - local_transform_ = e->transform; - } else if (auto e = - std::get_if(&entry)) { - AnimationClip* clip = - animation_player_.has_value() - ? animation_player_->GetClip(e->animation_name) - : nullptr; - if (!clip) { - auto animation = FindAnimationByName(e->animation_name); - if (!animation) { - continue; - } - clip = AddAnimation(animation); - if (!clip) { - continue; - } - } - - clip->SetPlaying(e->playing); - clip->SetLoop(e->loop); - clip->SetWeight(e->weight); - clip->SetPlaybackTimeScale(e->time_scale); - } else if (auto e = - std::get_if(&entry)) { - AnimationClip* clip = - animation_player_.has_value() - ? animation_player_->GetClip(e->animation_name) - : nullptr; - if (!clip) { - auto animation = FindAnimationByName(e->animation_name); - if (!animation) { - continue; - } - clip = AddAnimation(animation); - if (!clip) { - continue; - } - } - - clip->Seek(SecondsF(e->time)); - } - } - } - - if (animation_player_.has_value()) { - animation_player_->Update(); - } - - Matrix transform = parent_transform * local_transform_; - mesh_.Render(encoder, transform, - skin_ ? skin_->GetJointsTexture(allocator) : nullptr); - - for (auto& child : children_) { - if (!child->Render(encoder, allocator, transform)) { - return false; - } - } - return true; -} - -void Node::AddMutation(const MutationLog::Entry& entry) { - mutation_log_.Append(entry); -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/node.h b/impeller/scene/node.h deleted file mode 100644 index b9c7de02928f5..0000000000000 --- a/impeller/scene/node.h +++ /dev/null @@ -1,138 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_NODE_H_ -#define FLUTTER_IMPELLER_SCENE_NODE_H_ - -#include -#include -#include - -#include "impeller/base/thread.h" -#include "impeller/base/thread_safety.h" -#include "impeller/core/texture.h" -#include "impeller/geometry/matrix.h" -#include "impeller/scene/animation/animation.h" -#include "impeller/scene/animation/animation_clip.h" -#include "impeller/scene/animation/animation_player.h" -#include "impeller/scene/mesh.h" -#include "impeller/scene/scene_encoder.h" -#include "impeller/scene/skin.h" - -namespace impeller { -namespace scene { - -class Node final { - public: - class MutationLog { - public: - struct SetTransformEntry { - Matrix transform; - }; - - struct SetAnimationStateEntry { - std::string animation_name; - bool playing = false; - bool loop = false; - Scalar weight = 0; - Scalar time_scale = 1; - }; - - struct SeekAnimationEntry { - std::string animation_name; - float time = 0; - }; - - using Entry = std:: - variant; - - void Append(const Entry& entry); - - private: - std::optional> Flush(); - - RWMutex write_mutex_; - bool dirty_ IPLR_GUARDED_BY(write_mutex_) = false; - std::vector entries_ IPLR_GUARDED_BY(write_mutex_); - - friend Node; - }; - - static std::shared_ptr MakeFromFlatbuffer( - const fml::Mapping& ipscene_mapping, - Allocator& allocator); - static std::shared_ptr MakeFromFlatbuffer(const fb::Scene& scene, - Allocator& allocator); - - Node(); - ~Node(); - - const std::string& GetName() const; - void SetName(const std::string& new_name); - - Node* GetParent() const; - - std::shared_ptr FindChildByName( - const std::string& name, - bool exclude_animation_players = false) const; - - std::shared_ptr FindAnimationByName(const std::string& name) const; - AnimationClip* AddAnimation(const std::shared_ptr& animation); - - void SetLocalTransform(Matrix transform); - Matrix GetLocalTransform() const; - - void SetGlobalTransform(Matrix transform); - Matrix GetGlobalTransform() const; - - bool AddChild(std::shared_ptr child); - std::vector>& GetChildren(); - - void SetMesh(Mesh mesh); - Mesh& GetMesh(); - - void SetIsJoint(bool is_joint); - bool IsJoint() const; - - bool Render(SceneEncoder& encoder, - Allocator& allocator, - const Matrix& parent_transform); - - void AddMutation(const MutationLog::Entry& entry); - - private: - void UnpackFromFlatbuffer( - const fb::Node& node, - const std::vector>& scene_nodes, - const std::vector>& textures, - Allocator& allocator); - - mutable MutationLog mutation_log_; - - Matrix local_transform_; - - std::string name_; - bool is_root_ = false; - bool is_joint_ = false; - Node* parent_ = nullptr; - std::vector> children_; - Mesh mesh_; - - // For convenience purposes, deserialized nodes hang onto an animation library - std::vector> animations_; - mutable std::optional animation_player_; - - std::unique_ptr skin_; - - Node(const Node&) = delete; - - Node& operator=(const Node&) = delete; - - friend Scene; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_NODE_H_ diff --git a/impeller/scene/pipeline_key.h b/impeller/scene/pipeline_key.h deleted file mode 100644 index 56f41f3658a26..0000000000000 --- a/impeller/scene/pipeline_key.h +++ /dev/null @@ -1,45 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_PIPELINE_KEY_H_ -#define FLUTTER_IMPELLER_SCENE_PIPELINE_KEY_H_ - -#include "flutter/fml/hash_combine.h" - -namespace impeller { -namespace scene { - -enum class GeometryType { - kUnskinned = 0, - kSkinned = 1, - kLastType = kSkinned, -}; -enum class MaterialType { - kUnlit = 0, - kLastType = kUnlit, -}; - -struct PipelineKey { - GeometryType geometry_type = GeometryType::kUnskinned; - MaterialType material_type = MaterialType::kUnlit; - - struct Hash { - constexpr std::size_t operator()(const PipelineKey& o) const { - return fml::HashCombine(o.geometry_type, o.material_type); - } - }; - - struct Equal { - constexpr bool operator()(const PipelineKey& lhs, - const PipelineKey& rhs) const { - return lhs.geometry_type == rhs.geometry_type && - lhs.material_type == rhs.material_type; - } - }; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_PIPELINE_KEY_H_ diff --git a/impeller/scene/scene.cc b/impeller/scene/scene.cc deleted file mode 100644 index 4e5e656120e55..0000000000000 --- a/impeller/scene/scene.cc +++ /dev/null @@ -1,73 +0,0 @@ -// 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/scene.h" - -#include -#include - -#include "flutter/fml/logging.h" -#include "fml/closure.h" -#include "impeller/renderer/render_target.h" -#include "impeller/scene/scene_context.h" -#include "impeller/scene/scene_encoder.h" - -namespace impeller { -namespace scene { - -Scene::Scene(std::shared_ptr scene_context) - : scene_context_(std::move(scene_context)) { - root_.is_root_ = true; -}; - -Scene::~Scene() { - for (auto& child : GetRoot().GetChildren()) { - child->parent_ = nullptr; - } -} - -Node& Scene::GetRoot() { - return root_; -} - -bool Scene::Render(const RenderTarget& render_target, - const Matrix& camera_transform) { - fml::ScopedCleanupClosure reset_state( - [context = scene_context_]() { context->GetTransientsBuffer().Reset(); }); - - // Collect the render commands from the scene. - SceneEncoder encoder; - if (!root_.Render(encoder, - *scene_context_->GetContext()->GetResourceAllocator(), - Matrix())) { - FML_LOG(ERROR) << "Failed to render frame."; - return false; - } - - // Encode the commands. - - std::shared_ptr command_buffer = - encoder.BuildSceneCommandBuffer(*scene_context_, camera_transform, - render_target); - - // TODO(bdero): Do post processing. - - if (!scene_context_->GetContext() - ->GetCommandQueue() - ->Submit({command_buffer}) - .ok()) { - FML_LOG(ERROR) << "Failed to submit command buffer."; - return false; - } - - return true; -} - -bool Scene::Render(const RenderTarget& render_target, const Camera& camera) { - return Render(render_target, - camera.GetTransform(render_target.GetRenderTargetSize())); -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/scene.h b/impeller/scene/scene.h deleted file mode 100644 index 1a69fe0aefb45..0000000000000 --- a/impeller/scene/scene.h +++ /dev/null @@ -1,45 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_SCENE_H_ -#define FLUTTER_IMPELLER_SCENE_SCENE_H_ - -#include - -#include "impeller/renderer/render_target.h" -#include "impeller/scene/camera.h" -#include "impeller/scene/node.h" -#include "impeller/scene/scene_context.h" - -namespace impeller { -namespace scene { - -class Scene { - public: - Scene() = delete; - - explicit Scene(std::shared_ptr scene_context); - - ~Scene(); - - Node& GetRoot(); - - bool Render(const RenderTarget& render_target, - const Matrix& camera_transform); - - bool Render(const RenderTarget& render_target, const Camera& camera); - - private: - std::shared_ptr scene_context_; - Node root_; - - Scene(const Scene&) = delete; - - Scene& operator=(const Scene&) = delete; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_SCENE_H_ diff --git a/impeller/scene/scene_context.cc b/impeller/scene/scene_context.cc deleted file mode 100644 index c4637837b3644..0000000000000 --- a/impeller/scene/scene_context.cc +++ /dev/null @@ -1,115 +0,0 @@ -// 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/scene_context.h" -#include "impeller/core/formats.h" -#include "impeller/core/host_buffer.h" -#include "impeller/scene/material.h" -#include "impeller/scene/shaders/skinned.vert.h" -#include "impeller/scene/shaders/unlit.frag.h" -#include "impeller/scene/shaders/unskinned.vert.h" - -namespace impeller { -namespace scene { - -void SceneContextOptions::ApplyToPipelineDescriptor( - const Capabilities& capabilities, - PipelineDescriptor& desc) const { - DepthAttachmentDescriptor depth; - depth.depth_compare = CompareFunction::kLess; - depth.depth_write_enabled = true; - desc.SetDepthStencilAttachmentDescriptor(depth); - desc.SetDepthPixelFormat(capabilities.GetDefaultDepthStencilFormat()); - - StencilAttachmentDescriptor stencil; - stencil.stencil_compare = CompareFunction::kAlways; - stencil.depth_stencil_pass = StencilOperation::kKeep; - desc.SetStencilAttachmentDescriptors(stencil); - desc.SetStencilPixelFormat(capabilities.GetDefaultDepthStencilFormat()); - - desc.SetSampleCount(sample_count); - desc.SetPrimitiveType(primitive_type); - - desc.SetWindingOrder(WindingOrder::kCounterClockwise); - desc.SetCullMode(CullMode::kBackFace); -} - -SceneContext::SceneContext(std::shared_ptr context) - : context_(std::move(context)) { - if (!context_ || !context_->IsValid()) { - return; - } - - auto unskinned_variant = - MakePipelineVariants( - *context_); - if (!unskinned_variant) { - FML_LOG(ERROR) << "Could not create unskinned pipeline variant."; - return; - } - pipelines_[{PipelineKey{GeometryType::kUnskinned, MaterialType::kUnlit}}] = - std::move(unskinned_variant); - - auto skinned_variant = - MakePipelineVariants(*context_); - if (!skinned_variant) { - FML_LOG(ERROR) << "Could not create skinned pipeline variant."; - return; - } - pipelines_[{PipelineKey{GeometryType::kSkinned, MaterialType::kUnlit}}] = - std::move(skinned_variant); - - { - impeller::TextureDescriptor texture_descriptor; - texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible; - texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt; - texture_descriptor.size = {1, 1}; - texture_descriptor.mip_count = 1u; - - placeholder_texture_ = - context_->GetResourceAllocator()->CreateTexture(texture_descriptor); - placeholder_texture_->SetLabel("Placeholder Texture"); - if (!placeholder_texture_) { - FML_LOG(ERROR) << "Could not create placeholder texture."; - return; - } - - uint8_t pixel[] = {0xFF, 0xFF, 0xFF, 0xFF}; - if (!placeholder_texture_->SetContents(pixel, 4)) { - FML_LOG(ERROR) << "Could not set contents of placeholder texture."; - return; - } - } - host_buffer_ = HostBuffer::Create(GetContext()->GetResourceAllocator()); - is_valid_ = true; -} - -SceneContext::~SceneContext() = default; - -std::shared_ptr> SceneContext::GetPipeline( - PipelineKey key, - SceneContextOptions opts) const { - if (!IsValid()) { - return nullptr; - } - if (auto found = pipelines_.find(key); found != pipelines_.end()) { - return found->second->GetPipeline(*context_, opts); - } - return nullptr; -} - -bool SceneContext::IsValid() const { - return is_valid_; -} - -std::shared_ptr SceneContext::GetContext() const { - return context_; -} - -std::shared_ptr SceneContext::GetPlaceholderTexture() const { - return placeholder_texture_; -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/scene_context.h b/impeller/scene/scene_context.h deleted file mode 100644 index 1a56ea1905064..0000000000000 --- a/impeller/scene/scene_context.h +++ /dev/null @@ -1,160 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_SCENE_CONTEXT_H_ -#define FLUTTER_IMPELLER_SCENE_SCENE_CONTEXT_H_ - -#include - -#include "impeller/core/host_buffer.h" -#include "impeller/renderer/context.h" -#include "impeller/renderer/pipeline.h" -#include "impeller/renderer/pipeline_descriptor.h" -#include "impeller/scene/pipeline_key.h" - -namespace impeller { -namespace scene { - -struct SceneContextOptions { - SampleCount sample_count = SampleCount::kCount1; - PrimitiveType primitive_type = PrimitiveType::kTriangle; - - struct Hash { - constexpr std::size_t operator()(const SceneContextOptions& o) const { - return fml::HashCombine(o.sample_count, o.primitive_type); - } - }; - - struct Equal { - constexpr bool operator()(const SceneContextOptions& lhs, - const SceneContextOptions& rhs) const { - return lhs.sample_count == rhs.sample_count && - lhs.primitive_type == rhs.primitive_type; - } - }; - - void ApplyToPipelineDescriptor(const Capabilities& capabilities, - PipelineDescriptor& desc) const; -}; - -class SceneContext { - public: - explicit SceneContext(std::shared_ptr context); - - ~SceneContext(); - - bool IsValid() const; - - std::shared_ptr> GetPipeline( - PipelineKey key, - SceneContextOptions opts) const; - - std::shared_ptr GetContext() const; - - std::shared_ptr GetPlaceholderTexture() const; - - HostBuffer& GetTransientsBuffer() const { return *host_buffer_; } - - private: - class PipelineVariants { - public: - virtual ~PipelineVariants() = default; - - virtual std::shared_ptr> GetPipeline( - Context& context, - SceneContextOptions opts) = 0; - }; - - template - class PipelineVariantsT final : public PipelineVariants { - public: - explicit PipelineVariantsT(Context& context) { - auto desc = PipelineT::Builder::MakeDefaultPipelineDescriptor(context); - if (!desc.has_value()) { - is_valid_ = false; - return; - } - // Apply default ContentContextOptions to the descriptor. - SceneContextOptions{}.ApplyToPipelineDescriptor( - /*capabilities=*/*context.GetCapabilities(), - /*desc=*/desc.value()); - variants_[{}] = std::make_unique(context, desc); - }; - - // |PipelineVariants| - std::shared_ptr> GetPipeline( - Context& context, - SceneContextOptions opts) { - if (auto found = variants_.find(opts); found != variants_.end()) { - return found->second->WaitAndGet(); - } - - auto prototype = variants_.find({}); - - // The prototype must always be initialized in the constructor. - FML_CHECK(prototype != variants_.end()); - - auto variant_future = prototype->second->WaitAndGet()->CreateVariant( - /*async=*/false, [&context, &opts, variants_count = variants_.size()]( - PipelineDescriptor& desc) { - opts.ApplyToPipelineDescriptor(*context.GetCapabilities(), desc); - desc.SetLabel( - SPrintF("%s V#%zu", desc.GetLabel().c_str(), variants_count)); - }); - auto variant = std::make_unique(std::move(variant_future)); - auto variant_pipeline = variant->WaitAndGet(); - variants_[opts] = std::move(variant); - return variant_pipeline; - } - - bool IsValid() const { return is_valid_; } - - private: - bool is_valid_ = true; - std::unordered_map, - SceneContextOptions::Hash, - SceneContextOptions::Equal> - variants_; - }; - - template - /// Creates a PipelineVariantsT for the given vertex and fragment shaders. - /// - /// If a pipeline could not be created, returns nullptr. - std::unique_ptr MakePipelineVariants(Context& context) { - auto pipeline = - PipelineVariantsT>( - context); - if (!pipeline.IsValid()) { - return nullptr; - } - return std::make_unique< - PipelineVariantsT>>( - std::move(pipeline)); - } - - std::unordered_map, - PipelineKey::Hash, - PipelineKey::Equal> - pipelines_; - - std::shared_ptr context_; - - bool is_valid_ = false; - // A 1x1 opaque white texture that can be used as a placeholder binding. - // Available for the lifetime of the scene context - std::shared_ptr placeholder_texture_; - std::shared_ptr host_buffer_; - - SceneContext(const SceneContext&) = delete; - - SceneContext& operator=(const SceneContext&) = delete; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_SCENE_CONTEXT_H_ diff --git a/impeller/scene/scene_encoder.cc b/impeller/scene/scene_encoder.cc deleted file mode 100644 index 7595e1eb11952..0000000000000 --- a/impeller/scene/scene_encoder.cc +++ /dev/null @@ -1,103 +0,0 @@ -// 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/scene_encoder.h" -#include "flutter/fml/logging.h" -#include "impeller/renderer/render_target.h" -#include "impeller/scene/scene_context.h" - -namespace impeller { -namespace scene { - -SceneEncoder::SceneEncoder() = default; - -void SceneEncoder::Add(const SceneCommand& command) { - // TODO(bdero): Manage multi-pass translucency ordering. - commands_.push_back(command); -} - -static void EncodeCommand(const SceneContext& scene_context, - const Matrix& view_transform, - RenderPass& render_pass, - const SceneCommand& scene_command) { - auto& host_buffer = scene_context.GetTransientsBuffer(); - - render_pass.SetCommandLabel(scene_command.label); - // TODO(bdero): Configurable stencil ref per-command. - render_pass.SetStencilReference(0); - - render_pass.SetPipeline(scene_context.GetPipeline( - PipelineKey{scene_command.geometry->GetGeometryType(), - scene_command.material->GetMaterialType()}, - scene_command.material->GetContextOptions(render_pass))); - - scene_command.geometry->BindToCommand( - scene_context, host_buffer, view_transform * scene_command.transform, - render_pass); - scene_command.material->BindToCommand(scene_context, host_buffer, - render_pass); - - render_pass.Draw(); - ; -} - -std::shared_ptr SceneEncoder::BuildSceneCommandBuffer( - const SceneContext& scene_context, - const Matrix& camera_transform, - RenderTarget render_target) const { - { - TextureDescriptor ds_texture; - ds_texture.type = TextureType::kTexture2DMultisample; - ds_texture.format = PixelFormat::kD32FloatS8UInt; - ds_texture.size = render_target.GetRenderTargetSize(); - ds_texture.usage = TextureUsage::kRenderTarget; - ds_texture.sample_count = SampleCount::kCount4; - ds_texture.storage_mode = StorageMode::kDeviceTransient; - auto texture = - scene_context.GetContext()->GetResourceAllocator()->CreateTexture( - ds_texture); - - DepthAttachment depth; - depth.load_action = LoadAction::kClear; - depth.store_action = StoreAction::kDontCare; - depth.clear_depth = 1.0; - depth.texture = texture; - render_target.SetDepthAttachment(depth); - - // The stencil and depth buffers must be the same texture for MacOS ARM - // and Vulkan. - StencilAttachment stencil; - stencil.load_action = LoadAction::kClear; - stencil.store_action = StoreAction::kDontCare; - stencil.clear_stencil = 0u; - stencil.texture = texture; - render_target.SetStencilAttachment(stencil); - } - - auto command_buffer = scene_context.GetContext()->CreateCommandBuffer(); - if (!command_buffer) { - FML_LOG(ERROR) << "Failed to create command buffer."; - return nullptr; - } - - auto render_pass = command_buffer->CreateRenderPass(render_target); - if (!render_pass) { - FML_LOG(ERROR) << "Failed to create render pass."; - return nullptr; - } - - for (auto& command : commands_) { - EncodeCommand(scene_context, camera_transform, *render_pass, command); - } - - if (!render_pass->EncodeCommands()) { - FML_LOG(ERROR) << "Failed to encode render pass commands."; - return nullptr; - } - - return command_buffer; -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/scene_encoder.h b/impeller/scene/scene_encoder.h deleted file mode 100644 index 66bc969b76352..0000000000000 --- a/impeller/scene/scene_encoder.h +++ /dev/null @@ -1,52 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_SCENE_ENCODER_H_ -#define FLUTTER_IMPELLER_SCENE_SCENE_ENCODER_H_ - -#include -#include -#include - -#include "impeller/renderer/command_buffer.h" -#include "impeller/scene/geometry.h" -#include "impeller/scene/material.h" - -namespace impeller { -namespace scene { - -class Scene; - -struct SceneCommand { - std::string label; - Matrix transform; - Geometry* geometry; - Material* material; -}; - -class SceneEncoder { - public: - void Add(const SceneCommand& command); - - private: - SceneEncoder(); - - std::shared_ptr BuildSceneCommandBuffer( - const SceneContext& scene_context, - const Matrix& camera_transform, - RenderTarget render_target) const; - - std::vector commands_; - - friend Scene; - - SceneEncoder(const SceneEncoder&) = delete; - - SceneEncoder& operator=(const SceneEncoder&) = delete; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_SCENE_ENCODER_H_ diff --git a/impeller/scene/scene_unittests.cc b/impeller/scene/scene_unittests.cc deleted file mode 100644 index 6469d8997dfa0..0000000000000 --- a/impeller/scene/scene_unittests.cc +++ /dev/null @@ -1,305 +0,0 @@ -// 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 - -#include "flutter/fml/mapping.h" -#include "flutter/testing/testing.h" -#include "impeller/core/formats.h" -#include "impeller/geometry/color.h" -#include "impeller/geometry/constants.h" -#include "impeller/geometry/matrix.h" -#include "impeller/geometry/quaternion.h" -#include "impeller/geometry/vector.h" -#include "impeller/playground/image/decompressed_image.h" -#include "impeller/playground/playground.h" -#include "impeller/playground/playground_test.h" -#include "impeller/scene/animation/animation_clip.h" -#include "impeller/scene/camera.h" -#include "impeller/scene/geometry.h" -#include "impeller/scene/importer/scene_flatbuffers.h" -#include "impeller/scene/material.h" -#include "impeller/scene/mesh.h" -#include "impeller/scene/scene.h" -#include "third_party/flatbuffers/include/flatbuffers/verifier.h" -#include "third_party/imgui/imgui.h" - -namespace impeller { -namespace scene { -namespace testing { - -using SceneTest = PlaygroundTest; -INSTANTIATE_PLAYGROUND_SUITE(SceneTest); - -TEST_P(SceneTest, CuboidUnlit) { - auto scene_context = std::make_shared(GetContext()); - - Playground::RenderCallback callback = [&](RenderTarget& render_target) { - auto allocator = GetContext()->GetResourceAllocator(); - auto scene = Scene(scene_context); - - { - Mesh mesh; - - auto material = Material::MakeUnlit(); - material->SetColor(Color::Red()); - - Vector3 size(1, 1, 0); - mesh.AddPrimitive({Geometry::MakeCuboid(size), std::move(material)}); - - Node& root = scene.GetRoot(); - root.SetLocalTransform(Matrix::MakeTranslation(-size / 2)); - root.SetMesh(std::move(mesh)); - } - - // Face towards the +Z direction (+X right, +Y up). - auto camera = Camera::MakePerspective( - /* fov */ Radians(kPiOver4), - /* position */ {2, 2, -5}) - .LookAt( - /* target */ Vector3(), - /* up */ {0, 1, 0}); - - scene.Render(render_target, camera); - return true; - }; - - OpenPlaygroundHere(callback); -} - -TEST_P(SceneTest, FlutterLogo) { - auto allocator = GetContext()->GetResourceAllocator(); - - auto mapping = - flutter::testing::OpenFixtureAsMapping("flutter_logo_baked.glb.ipscene"); - ASSERT_NE(mapping, nullptr); - - flatbuffers::Verifier verifier(mapping->GetMapping(), mapping->GetSize()); - ASSERT_TRUE(fb::VerifySceneBuffer(verifier)); - - std::shared_ptr gltf_scene = - Node::MakeFromFlatbuffer(*mapping, *allocator); - ASSERT_NE(gltf_scene, nullptr); - ASSERT_EQ(gltf_scene->GetChildren().size(), 1u); - ASSERT_EQ(gltf_scene->GetChildren()[0]->GetMesh().GetPrimitives().size(), 1u); - - auto scene_context = std::make_shared(GetContext()); - auto scene = Scene(scene_context); - scene.GetRoot().AddChild(std::move(gltf_scene)); - scene.GetRoot().SetLocalTransform(Matrix::MakeScale({3, 3, 3})); - - Playground::RenderCallback callback = [&](RenderTarget& render_target) { - Quaternion rotation({0, 1, 0}, -GetSecondsElapsed() * 0.5); - Vector3 start_position(-1, -1.5, -5); - - // Face towards the +Z direction (+X right, +Y up). - auto camera = Camera::MakePerspective( - /* fov */ Degrees(60), - /* position */ rotation * start_position) - .LookAt( - /* target */ Vector3(), - /* up */ {0, 1, 0}); - - scene.Render(render_target, camera); - return true; - }; - - OpenPlaygroundHere(callback); -} - -TEST_P(SceneTest, TwoTriangles) { - if (GetBackend() == PlaygroundBackend::kVulkan) { - GTEST_SKIP() << "Temporarily disabled."; - } - auto allocator = GetContext()->GetResourceAllocator(); - - auto mapping = - flutter::testing::OpenFixtureAsMapping("two_triangles.glb.ipscene"); - ASSERT_NE(mapping, nullptr); - - std::shared_ptr gltf_scene = - Node::MakeFromFlatbuffer(*mapping, *allocator); - ASSERT_NE(gltf_scene, nullptr); - - auto animation = gltf_scene->FindAnimationByName("Metronome"); - ASSERT_NE(animation, nullptr); - - AnimationClip* metronome_clip = gltf_scene->AddAnimation(animation); - ASSERT_NE(metronome_clip, nullptr); - metronome_clip->SetLoop(true); - metronome_clip->Play(); - - auto scene_context = std::make_shared(GetContext()); - auto scene = Scene(scene_context); - scene.GetRoot().AddChild(std::move(gltf_scene)); - - Playground::RenderCallback callback = [&](RenderTarget& render_target) { - ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize); - { - static Scalar playback_time_scale = 1; - static Scalar weight = 1; - static bool loop = true; - - ImGui::SliderFloat("Playback time scale", &playback_time_scale, -5, 5); - ImGui::SliderFloat("Weight", &weight, -2, 2); - ImGui::Checkbox("Loop", &loop); - if (ImGui::Button("Play")) { - metronome_clip->Play(); - } - if (ImGui::Button("Pause")) { - metronome_clip->Pause(); - } - if (ImGui::Button("Stop")) { - metronome_clip->Stop(); - } - - metronome_clip->SetPlaybackTimeScale(playback_time_scale); - metronome_clip->SetWeight(weight); - metronome_clip->SetLoop(loop); - } - - ImGui::End(); - Node& node = *scene.GetRoot().GetChildren()[0]; - node.SetLocalTransform(node.GetLocalTransform() * - Matrix::MakeRotation(Radians(0.02), {0, 1, 0, 0})); - - static ImVec2 mouse_pos_prev = ImGui::GetMousePos(); - ImVec2 mouse_pos = ImGui::GetMousePos(); - Vector2 mouse_diff = - Vector2(mouse_pos.x - mouse_pos_prev.x, mouse_pos.y - mouse_pos_prev.y); - - static Vector3 position(0, 1, -5); - static Vector3 cam_position = position; - auto strafe = - Vector3(ImGui::IsKeyDown(ImGuiKey_D) - ImGui::IsKeyDown(ImGuiKey_A), - ImGui::IsKeyDown(ImGuiKey_E) - ImGui::IsKeyDown(ImGuiKey_Q), - ImGui::IsKeyDown(ImGuiKey_W) - ImGui::IsKeyDown(ImGuiKey_S)); - position += strafe * 0.5; - cam_position = cam_position.Lerp(position, 0.02); - - // Face towards the +Z direction (+X right, +Y up). - auto camera = Camera::MakePerspective( - /* fov */ Degrees(60), - /* position */ cam_position) - .LookAt( - /* target */ cam_position + Vector3(0, 0, 1), - /* up */ {0, 1, 0}); - - scene.Render(render_target, camera); - return true; - }; - - OpenPlaygroundHere(callback); -} - -TEST_P(SceneTest, Dash) { - auto allocator = GetContext()->GetResourceAllocator(); - - auto mapping = flutter::testing::OpenFixtureAsMapping("dash.glb.ipscene"); - if (!mapping) { - // TODO(bdero): Just skip this playground is the dash asset isn't found. I - // haven't checked it in because it's way too big right now, - // but this is still useful to keep around for debugging - // purposes. - return; - } - ASSERT_NE(mapping, nullptr); - - std::shared_ptr gltf_scene = - Node::MakeFromFlatbuffer(*mapping, *allocator); - ASSERT_NE(gltf_scene, nullptr); - - auto walk_anim = gltf_scene->FindAnimationByName("Walk"); - ASSERT_NE(walk_anim, nullptr); - - AnimationClip* walk_clip = gltf_scene->AddAnimation(walk_anim); - ASSERT_NE(walk_clip, nullptr); - walk_clip->SetLoop(true); - walk_clip->Play(); - - auto run_anim = gltf_scene->FindAnimationByName("Run"); - ASSERT_NE(walk_anim, nullptr); - - AnimationClip* run_clip = gltf_scene->AddAnimation(run_anim); - ASSERT_NE(run_clip, nullptr); - run_clip->SetLoop(true); - run_clip->Play(); - - auto scene_context = std::make_shared(GetContext()); - auto scene = Scene(scene_context); - scene.GetRoot().AddChild(std::move(gltf_scene)); - - Playground::RenderCallback callback = [&](RenderTarget& render_target) { - ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize); - { - static Scalar playback_time_scale = 1; - static Scalar walk = 0.5; - static Scalar run = 0.5; - static bool loop = true; - - ImGui::SliderFloat("Playback time scale", &playback_time_scale, -5, 5); - ImGui::SliderFloat("Walk weight", &walk, 0, 1); - ImGui::SliderFloat("Run weight", &run, 0, 1); - ImGui::Checkbox("Loop", &loop); - if (ImGui::Button("Play")) { - walk_clip->Play(); - run_clip->Play(); - } - if (ImGui::Button("Pause")) { - walk_clip->Pause(); - run_clip->Pause(); - } - if (ImGui::Button("Stop")) { - walk_clip->Stop(); - run_clip->Stop(); - } - - walk_clip->SetPlaybackTimeScale(playback_time_scale); - walk_clip->SetWeight(walk); - walk_clip->SetLoop(loop); - - run_clip->SetPlaybackTimeScale(playback_time_scale); - run_clip->SetWeight(run); - run_clip->SetLoop(loop); - } - - ImGui::End(); - Node& node = *scene.GetRoot().GetChildren()[0]; - node.SetLocalTransform(node.GetLocalTransform() * - Matrix::MakeRotation(Radians(0.02), {0, 1, 0, 0})); - - static ImVec2 mouse_pos_prev = ImGui::GetMousePos(); - ImVec2 mouse_pos = ImGui::GetMousePos(); - Vector2 mouse_diff = - Vector2(mouse_pos.x - mouse_pos_prev.x, mouse_pos.y - mouse_pos_prev.y); - - static Vector3 position(0, 1, -5); - static Vector3 cam_position = position; - auto strafe = - Vector3(ImGui::IsKeyDown(ImGuiKey_D) - ImGui::IsKeyDown(ImGuiKey_A), - ImGui::IsKeyDown(ImGuiKey_E) - ImGui::IsKeyDown(ImGuiKey_Q), - ImGui::IsKeyDown(ImGuiKey_W) - ImGui::IsKeyDown(ImGuiKey_S)); - position += strafe * 0.5; - cam_position = cam_position.Lerp(position, 0.02); - - // Face towards the +Z direction (+X right, +Y up). - auto camera = Camera::MakePerspective( - /* fov */ Degrees(60), - /* position */ cam_position) - .LookAt( - /* target */ cam_position + Vector3(0, 0, 1), - /* up */ {0, 1, 0}); - - scene.Render(render_target, camera); - return true; - }; - - OpenPlaygroundHere(callback); -} - -} // namespace testing -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/shaders/BUILD.gn b/impeller/scene/shaders/BUILD.gn deleted file mode 100644 index 658618d407191..0000000000000 --- a/impeller/scene/shaders/BUILD.gn +++ /dev/null @@ -1,19 +0,0 @@ -# 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") - -impeller_shaders("shaders") { - name = "scene" - - if (impeller_enable_vulkan) { - vulkan_language_version = 130 - } - - shaders = [ - "skinned.vert", - "unskinned.vert", - "unlit.frag", - ] -} diff --git a/impeller/scene/shaders/skinned.vert b/impeller/scene/shaders/skinned.vert deleted file mode 100644 index 807f620b03cde..0000000000000 --- a/impeller/scene/shaders/skinned.vert +++ /dev/null @@ -1,75 +0,0 @@ -// 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. - -uniform FrameInfo { - mat4 mvp; - float enable_skinning; - float joint_texture_size; -} -frame_info; - -uniform sampler2D joints_texture; - -// This attribute layout is expected to be identical to `SkinnedVertex` within -// `impeller/scene/importer/scene.fbs`. -in vec3 position; -in vec3 normal; -in vec4 tangent; -in vec2 texture_coords; -in vec4 color; -in vec4 joints; -in vec4 weights; - -out vec3 v_position; -out mat3 v_tangent_space; -out vec2 v_texture_coords; -out vec4 v_color; - -const int kMatrixTexelStride = 4; - -mat4 GetJoint(float joint_index) { - // The size of one texel in UV space. The joint texture should always be - // square, so the answer is the same in both dimensions. - float texel_size_uv = 1 / frame_info.joint_texture_size; - - // Each joint matrix takes up 4 pixels (16 floats), so we jump 4 pixels per - // joint matrix. - float matrix_start = joint_index * kMatrixTexelStride; - - // The texture space coordinates at the start of the matrix. - float x = mod(matrix_start, frame_info.joint_texture_size); - float y = floor(matrix_start / frame_info.joint_texture_size); - - // Nearest sample the middle of each the texel by adding `0.5 * texel_size_uv` - // to both dimensions. - y = (y + 0.5) * texel_size_uv; - mat4 joint = - mat4(texture(joints_texture, vec2((x + 0.5) * texel_size_uv, y)), - texture(joints_texture, vec2((x + 1.5) * texel_size_uv, y)), - texture(joints_texture, vec2((x + 2.5) * texel_size_uv, y)), - texture(joints_texture, vec2((x + 3.5) * texel_size_uv, y))); - - return joint; -} - -void main() { - mat4 skin_matrix; - if (frame_info.enable_skinning == 1) { - skin_matrix = - GetJoint(joints.x) * weights.x + GetJoint(joints.y) * weights.y + - GetJoint(joints.z) * weights.z + GetJoint(joints.w) * weights.w; - } else { - skin_matrix = mat4(1); // Identity matrix. - } - - gl_Position = frame_info.mvp * skin_matrix * vec4(position, 1.0); - v_position = gl_Position.xyz; - - vec3 lh_tangent = (skin_matrix * vec4(tangent.xyz * tangent.w, 0.0)).xyz; - vec3 out_normal = (skin_matrix * vec4(normal, 0.0)).xyz; - v_tangent_space = mat3(frame_info.mvp) * - mat3(lh_tangent, cross(out_normal, lh_tangent), out_normal); - v_texture_coords = texture_coords; - v_color = color; -} diff --git a/impeller/scene/shaders/unlit.frag b/impeller/scene/shaders/unlit.frag deleted file mode 100644 index f9d8cd4173131..0000000000000 --- a/impeller/scene/shaders/unlit.frag +++ /dev/null @@ -1,24 +0,0 @@ -// 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. - -uniform FragInfo { - vec4 color; - float vertex_color_weight; -} -frag_info; - -uniform sampler2D base_color_texture; - -in vec3 v_position; -in mat3 v_tangent_space; -in vec2 v_texture_coords; -in vec4 v_color; - -out vec4 frag_color; - -void main() { - vec4 vertex_color = mix(vec4(1), v_color, frag_info.vertex_color_weight); - frag_color = texture(base_color_texture, v_texture_coords) * vertex_color * - frag_info.color; -} diff --git a/impeller/scene/shaders/unskinned.vert b/impeller/scene/shaders/unskinned.vert deleted file mode 100644 index c79be6fbb0a14..0000000000000 --- a/impeller/scene/shaders/unskinned.vert +++ /dev/null @@ -1,32 +0,0 @@ -// 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. - -uniform FrameInfo { - mat4 mvp; -} -frame_info; - -// This attribute layout is expected to be identical to that within -// `impeller/scene/importer/scene.fbs`. -in vec3 position; -in vec3 normal; -in vec4 tangent; -in vec2 texture_coords; -in vec4 color; - -out vec3 v_position; -out mat3 v_tangent_space; -out vec2 v_texture_coords; -out vec4 v_color; - -void main() { - gl_Position = frame_info.mvp * vec4(position, 1.0); - v_position = gl_Position.xyz; - - vec3 lh_tangent = tangent.xyz * tangent.w; - v_tangent_space = mat3(frame_info.mvp) * - mat3(lh_tangent, cross(normal, lh_tangent), normal); - v_texture_coords = texture_coords; - v_color = color; -} diff --git a/impeller/scene/skin.cc b/impeller/scene/skin.cc deleted file mode 100644 index ffabd8253b243..0000000000000 --- a/impeller/scene/skin.cc +++ /dev/null @@ -1,123 +0,0 @@ -// 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/skin.h" - -#include -#include -#include - -#include "flutter/fml/logging.h" -#include "impeller/base/allocation.h" -#include "impeller/core/allocator.h" -#include "impeller/scene/importer/conversions.h" - -namespace impeller { -namespace scene { - -std::unique_ptr Skin::MakeFromFlatbuffer( - const fb::Skin& skin, - const std::vector>& scene_nodes) { - if (!skin.joints() || !skin.inverse_bind_matrices() || - skin.joints()->size() != skin.inverse_bind_matrices()->size()) { - VALIDATION_LOG << "Skin data is missing joints or bind matrices."; - return nullptr; - } - - Skin result; - - result.joints_.reserve(skin.joints()->size()); - for (auto joint : *skin.joints()) { - if (joint < 0 || static_cast(joint) > scene_nodes.size()) { - VALIDATION_LOG << "Skin joint index out of range."; - result.joints_.push_back(nullptr); - continue; - } - if (scene_nodes[joint]) { - scene_nodes[joint]->SetIsJoint(true); - } - result.joints_.push_back(scene_nodes[joint]); - } - - result.inverse_bind_matrices_.reserve(skin.inverse_bind_matrices()->size()); - for (size_t matrix_i = 0; matrix_i < skin.inverse_bind_matrices()->size(); - matrix_i++) { - const auto* ip_matrix = skin.inverse_bind_matrices()->Get(matrix_i); - Matrix matrix = ip_matrix ? importer::ToMatrix(*ip_matrix) : Matrix(); - - result.inverse_bind_matrices_.push_back(matrix); - // Overwrite the joint transforms with the inverse bind pose. - result.joints_[matrix_i]->SetGlobalTransform(matrix.Invert()); - } - - return std::make_unique(std::move(result)); -} - -Skin::Skin() = default; - -Skin::~Skin() = default; - -Skin::Skin(Skin&&) = default; - -Skin& Skin::operator=(Skin&&) = default; - -std::shared_ptr Skin::GetJointsTexture(Allocator& allocator) { - // Each joint has a matrix. 1 matrix = 16 floats. 1 pixel = 4 floats. - // Therefore, each joint needs 4 pixels. - auto required_pixels = joints_.size() * 4; - auto dimension_size = std::max( - 2u, - Allocation::NextPowerOfTwoSize(std::ceil(std::sqrt(required_pixels)))); - - impeller::TextureDescriptor texture_descriptor; - texture_descriptor.storage_mode = impeller::StorageMode::kHostVisible; - texture_descriptor.format = PixelFormat::kR32G32B32A32Float; - texture_descriptor.size = {dimension_size, dimension_size}; - texture_descriptor.mip_count = 1u; - - auto result = allocator.CreateTexture(texture_descriptor); - result->SetLabel("Joints Texture"); - if (!result) { - FML_LOG(ERROR) << "Could not create joint texture."; - return nullptr; - } - - std::vector joints; - joints.resize(result->GetSize().Area() / 4, Matrix()); - FML_DCHECK(joints.size() >= joints_.size()); - for (size_t joint_i = 0; joint_i < joints_.size(); joint_i++) { - const Node* joint = joints_[joint_i].get(); - if (!joint) { - // When a joint is missing, just let it remain as an identity matrix. - continue; - } - - // Compute a model space matrix for the joint by walking up the bones to the - // skeleton root. - while (joint && joint->IsJoint()) { - joints[joint_i] = joint->GetLocalTransform() * joints[joint_i]; - joint = joint->GetParent(); - } - - // Get the joint transform relative to the default pose of the bone by - // incorporating the joint's inverse bind matrix. The inverse bind matrix - // transforms from model space to the default pose space of the joint. The - // result is a model space matrix that only captures the difference between - // the joint's default pose and the joint's current pose in the scene. This - // is necessary because the skinned model's vertex positions (which _define_ - // the default pose) are all in model space. - joints[joint_i] = joints[joint_i] * inverse_bind_matrices_[joint_i]; - } - - if (!result->SetContents(reinterpret_cast(joints.data()), - joints.size() * sizeof(Matrix))) { - FML_LOG(ERROR) << "Could not set contents of joint texture."; - return nullptr; - } - - return result; -} - -} // namespace scene -} // namespace impeller diff --git a/impeller/scene/skin.h b/impeller/scene/skin.h deleted file mode 100644 index 5a788c6d20b9a..0000000000000 --- a/impeller/scene/skin.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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. - -#ifndef FLUTTER_IMPELLER_SCENE_SKIN_H_ -#define FLUTTER_IMPELLER_SCENE_SKIN_H_ - -#include - -#include "impeller/core/allocator.h" -#include "impeller/core/texture.h" -#include "impeller/scene/importer/scene_flatbuffers.h" -#include "impeller/scene/node.h" - -namespace impeller { -namespace scene { - -class Skin final { - public: - static std::unique_ptr MakeFromFlatbuffer( - const fb::Skin& skin, - const std::vector>& scene_nodes); - ~Skin(); - - Skin(Skin&&); - Skin& operator=(Skin&&); - - std::shared_ptr GetJointsTexture(Allocator& allocator); - - private: - Skin(); - - std::vector> joints_; - std::vector inverse_bind_matrices_; - - Skin(const Skin&) = delete; - - Skin& operator=(const Skin&) = delete; -}; - -} // namespace scene -} // namespace impeller - -#endif // FLUTTER_IMPELLER_SCENE_SKIN_H_ diff --git a/impeller/tools/args.gni b/impeller/tools/args.gni index e40da89ba886e..adfc111ffce15 100644 --- a/impeller/tools/args.gni +++ b/impeller/tools/args.gni @@ -19,9 +19,6 @@ declare_args() { # Whether the Vulkan backend is enabled. impeller_enable_vulkan = (is_linux || is_win || is_android || is_mac || enable_unittests) && target_os != "fuchsia" - - # Enable experimental 3D scene rendering. - impeller_enable_3d = false } # Arguments that are combinations of other arguments by default but which can diff --git a/lib/snapshot/BUILD.gn b/lib/snapshot/BUILD.gn index 689bcdaddc84a..73611e45063ab 100644 --- a/lib/snapshot/BUILD.gn +++ b/lib/snapshot/BUILD.gn @@ -256,13 +256,8 @@ source_set("snapshot") { compile_platform("strong_platform") { single_root_scheme = "org-dartlang-sdk" single_root_base = rebase_path("../../../") - if (impeller_enable_3d) { - libraries_specification_uri = - "org-dartlang-sdk:///flutter/lib/snapshot/libraries_experimental.json" - } else { - libraries_specification_uri = - "org-dartlang-sdk:///flutter/lib/snapshot/libraries.json" - } + libraries_specification_uri = + "org-dartlang-sdk:///flutter/lib/snapshot/libraries.json" outputs = [ "$root_out_dir/flutter_patched_sdk/platform_strong.dill", diff --git a/lib/snapshot/libraries_experimental.json b/lib/snapshot/libraries_experimental.json deleted file mode 100644 index e064a7c0cba55..0000000000000 --- a/lib/snapshot/libraries_experimental.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "comment:0": "NOTE: THIS FILE IS GENERATED. DO NOT EDIT.", - "comment:1": "Instead modify 'flutter/lib/snapshot/libraries.yaml' and follow the instructions therein.", - "flutter": { - "include": [ - { - "path": "../../third_party/dart/sdk/lib/libraries.json", - "target": "vm_common" - } - ], - "libraries": { - "ui": { - "uri": "../../lib/ui/experiments/ui.dart" - } - } - } - } diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index e4a24a0d9630d..d831d88d15209 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -209,17 +209,6 @@ source_set("ui") { # Required for M_PI and others. defines += [ "_USE_MATH_DEFINES" ] } - if (impeller_enable_3d) { - defines += [ "IMPELLER_ENABLE_3D" ] - sources += [ - "painting/scene/scene_node.cc", - "painting/scene/scene_node.h", - "painting/scene/scene_shader.cc", - "painting/scene/scene_shader.h", - ] - - deps += [ "//flutter/impeller/scene" ] - } } if (enable_unittests) { diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 797b54f56a4bd..1e2bc7f9a2e9c 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -43,11 +43,6 @@ #include "third_party/tonic/dart_args.h" #include "third_party/tonic/logging/dart_error.h" -#ifdef IMPELLER_ENABLE_3D -#include "flutter/lib/ui/painting/scene/scene_node.h" -#include "flutter/lib/ui/painting/scene/scene_shader.h" -#endif // IMPELLER_ENABLE_3D - using tonic::ToDart; namespace flutter { @@ -308,24 +303,6 @@ typedef CanvasPath Path; V(SemanticsUpdate, dispose) \ V(Vertices, dispose) -#ifdef IMPELLER_ENABLE_3D - -#define FFI_FUNCTION_LIST_3D(V) \ - V(SceneNode::Create) \ - V(SceneShader::Create) - -#define FFI_METHOD_LIST_3D(V) \ - V(SceneNode, initFromAsset) \ - V(SceneNode, initFromTransform) \ - V(SceneNode, AddChild) \ - V(SceneNode, SetTransform) \ - V(SceneNode, SetAnimationState) \ - V(SceneNode, SeekAnimation) \ - V(SceneShader, SetCameraTransform) \ - V(SceneShader, Dispose) - -#endif // IMPELLER_ENABLE_3D - #define FFI_FUNCTION_INSERT(FUNCTION) \ g_function_dispatchers.insert(std::make_pair( \ std::string_view(#FUNCTION), \ @@ -352,11 +329,6 @@ void* ResolveFfiNativeFunction(const char* name, uintptr_t args) { void InitDispatcherMap() { FFI_FUNCTION_LIST(FFI_FUNCTION_INSERT) FFI_METHOD_LIST(FFI_METHOD_INSERT) - -#ifdef IMPELLER_ENABLE_3D - FFI_FUNCTION_LIST_3D(FFI_FUNCTION_INSERT) - FFI_METHOD_LIST_3D(FFI_METHOD_INSERT) -#endif // IMPELLER_ENABLE_3D } } // anonymous namespace diff --git a/lib/ui/experiments/scene.dart b/lib/ui/experiments/scene.dart deleted file mode 100644 index cdab6557fc98b..0000000000000 --- a/lib/ui/experiments/scene.dart +++ /dev/null @@ -1,237 +0,0 @@ -// 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. -part of dart.ui; - -/// A composable [SceneNode]. -base class SceneNode extends NativeFieldWrapperClass1 { - @pragma('vm:entry-point') - SceneNode._create() { - _constructor(); - } - - String? _debugName; - - /// Creates a scene node from the asset with key [assetKey]. - /// - /// The asset must be a file produced as the output of the `scenec` importer. - /// The constructed object should then be reused via the [shader] - /// method to create [Shader] objects that can be used by [Shader.paint]. - static SceneNodeValue fromAsset(String assetKey) { - // The flutter tool converts all asset keys with spaces into URI - // encoded paths (replacing ' ' with '%20', for example). We perform - // the same encoding here so that users can load assets with the same - // key they have written in the pubspec. - final String encodedKey = Uri(path: Uri.encodeFull(assetKey)).path; - { - final SceneNodeValue? futureSceneNode = _ipsceneRegistry[encodedKey]?.target; - if (futureSceneNode != null) { - return futureSceneNode; - } - } - - final SceneNode sceneNode = SceneNode._create(); - - final Future futureSceneNode = _futurize((_Callback callback) { - final String error = sceneNode._initFromAsset(assetKey, callback); - if (error.isNotEmpty) { - return error; - } - assert(() { - sceneNode._debugName = assetKey; - return true; - }()); - - return null; - }).then((_) => sceneNode); - - final SceneNodeValue result = SceneNodeValue.fromFuture(futureSceneNode); - _ipsceneRegistry[encodedKey] = WeakReference(result); - return result; - } - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - static SceneNodeValue fromTransform(Float64List matrix4) { - final SceneNode sceneNode = SceneNode._create(); - sceneNode._initFromTransform(matrix4); - return SceneNodeValue.fromValue(sceneNode); - } - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - void addChild(SceneNode sceneNode) { - _addChild(sceneNode); - } - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - void setTransform(Float64List matrix4) { - _setTransform(matrix4); - } - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - void setAnimationState(String animationName, bool playing, bool loop, double weight, double timeScale) { - _setAnimationState(animationName, playing, loop, weight, timeScale); - } - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - void seekAnimation(String animationName, double time) { - _seekAnimation(animationName, time); - } - - // This is a cache of ipscene-backed scene nodes that have been loaded via - // SceneNode.fromAsset. It holds weak references to the SceneNodes so that the - // case where an in-use ipscene is requested again can be fast, but scenes - // that are no longer referenced are not retained because of the cache. - static final Map> _ipsceneRegistry = - >{}; - - static Future _reinitializeScene(String assetKey) async { - final WeakReference? sceneRef = _ipsceneRegistry[assetKey]; - - // If a scene for the asset isn't already registered, then there's no - // need to reinitialize it. - if (sceneRef == null) { - return; - } - - final Future? sceneNodeFuture = sceneRef.target?.future; - if (sceneNodeFuture == null) { - return; - } - final SceneNode sceneNode = await sceneNodeFuture; - - await _futurize((_Callback callback) { - final String error = sceneNode._initFromAsset(assetKey, callback); - if (error.isNotEmpty) { - return error; - } - return null; - }); - } - - @Native(symbol: 'SceneNode::Create') - external void _constructor(); - - @Native, Handle, Handle)>(symbol: 'SceneNode::initFromAsset') - external String _initFromAsset(String assetKey, _Callback completionCallback); - - @Native, Handle)>(symbol: 'SceneNode::initFromTransform') - external void _initFromTransform(Float64List matrix4); - - @Native, Handle)>(symbol: 'SceneNode::AddChild') - external void _addChild(SceneNode sceneNode); - - @Native, Handle)>(symbol: 'SceneNode::SetTransform') - external void _setTransform(Float64List matrix4); - - @Native, Handle, Bool, Bool, Double, Double)>(symbol: 'SceneNode::SetAnimationState') - external void _setAnimationState(String animationName, bool playing, bool loop, double weight, double timeScale); - - @Native, Handle, Double)>(symbol: 'SceneNode::SeekAnimation') - external void _seekAnimation(String animationName, double time); - - /// Returns a fresh instance of [SceneShader]. - SceneShader sceneShader() => SceneShader._(this, debugName: _debugName); - -} - -// TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. -// ignore: public_member_api_docs -class SceneNodeValue { - SceneNodeValue._(this._future, this._value) { - _future?.then((SceneNode result) => _value = result); - } - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - static SceneNodeValue fromFuture(Future future) { - return SceneNodeValue._(future, null); - } - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - static SceneNodeValue fromValue(SceneNode value) { - return SceneNodeValue._(null, value); - } - - final Future? _future; - SceneNode? _value; - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - bool get isComplete { - return _value != null; - } - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - Future? get future { - return _future; - } - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - SceneNode? get value { - return _value; - } - - /// Calls `callback` when the `SceneNode` has finished initializing. If the - /// initialization is already finished, `callback` is called synchronously. - SceneNodeValue whenComplete(void Function(SceneNode) callback) { - if (_value == null && _future == null) { - return this; - } - - if (_value != null) { - callback(_value!); - return this; - } - - // _future != null - _future!.then((SceneNode node) => callback(node)); - return this; - } -} - -/// A [Shader] generated from a [SceneNode]. -/// -/// Instances of this class can be obtained from the -/// [SceneNode.sceneShader] method. -base class SceneShader extends Shader { - SceneShader._(SceneNode node, { String? debugName }) : _debugName = debugName, super._() { - _constructor(node); - } - - // ignore: unused_field - final String? _debugName; - - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. - // ignore: public_member_api_docs - void setCameraTransform(Float64List matrix4) { - _setCameraTransform(matrix4); - } - - /// Releases the native resources held by the [SceneShader]. - /// - /// After this method is called, calling methods on the shader, or attaching - /// it to a [Paint] object will fail with an exception. Calling [dispose] - /// twice will also result in an exception being thrown. - @override - void dispose() { - super.dispose(); - _dispose(); - } - - @Native(symbol: 'SceneShader::Create') - external void _constructor(SceneNode node); - - @Native, Handle)>(symbol: 'SceneShader::SetCameraTransform') - external void _setCameraTransform(Float64List matrix4); - - @Native)>(symbol: 'SceneShader::Dispose') - external void _dispose(); -} diff --git a/lib/ui/experiments/setup_hooks.dart b/lib/ui/experiments/setup_hooks.dart deleted file mode 100644 index 8d5dfdd966da8..0000000000000 --- a/lib/ui/experiments/setup_hooks.dart +++ /dev/null @@ -1,51 +0,0 @@ -// 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. - -part of dart.ui; - -Future _reinitializeScene( - String method, - Map parameters, -) async { - final String? assetKey = parameters['assetKey']; - if (assetKey != null) { - await SceneNode._reinitializeScene(assetKey); - } - - // Always succeed. - return developer.ServiceExtensionResponse.result(json.encode({ - 'type': 'Success', - })); -} - -// This is a copy of ui/setup_hooks.dart, but with reinitializeScene added for hot reloading 3D scenes. - -@pragma('vm:entry-point') -void _setupHooks() { - assert(() { - // In debug mode, register the schedule frame extension. - developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); - - // In debug mode, allow shaders to be reinitialized. - developer.registerExtension( - 'ext.ui.window.reinitializeShader', - _reinitializeShader, - ); - - // In debug mode, allow 3D scenes to be reinitialized. - developer.registerExtension( - 'ext.ui.window.reinitializeScene', - _reinitializeScene, - ); - return true; - }()); - - // In debug and profile mode, allow tools to display the current rendering backend. - if (!_kReleaseMode) { - developer.registerExtension( - 'ext.ui.window.impellerEnabled', - _getImpellerEnabled, - ); - } -} diff --git a/lib/ui/experiments/ui.dart b/lib/ui/experiments/ui.dart deleted file mode 100644 index f614b25a25e7c..0000000000000 --- a/lib/ui/experiments/ui.dart +++ /dev/null @@ -1,51 +0,0 @@ -// 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. - -/// Built-in types and core primitives for a Flutter application. -/// -/// To use, import `dart:ui`. -/// -/// This library exposes the lowest-level services that Flutter frameworks use -/// to bootstrap applications, such as classes for driving the input, graphics -/// text, layout, and rendering subsystems. -library dart.ui; - -import 'dart:async'; -import 'dart:collection' as collection; -import 'dart:convert'; -import 'dart:developer' as developer; -import 'dart:ffi'; -import 'dart:io'; -import 'dart:isolate' - show - Isolate, - IsolateSpawnException, - RawReceivePort, - RemoteError, - SendPort; -import 'dart:math' as math; -import 'dart:nativewrappers'; -import 'dart:typed_data'; - -part '../annotations.dart'; -part '../channel_buffers.dart'; -part '../compositing.dart'; -part '../geometry.dart'; -part '../hooks.dart'; -part '../isolate_name_server.dart'; -part '../key.dart'; -part '../lerp.dart'; -part '../math.dart'; -part '../natives.dart'; -part '../painting.dart'; -part '../platform_dispatcher.dart'; -part '../platform_isolate.dart'; -part '../plugins.dart'; -part '../pointer.dart'; -part '../semantics.dart'; -part 'setup_hooks.dart'; -part '../text.dart'; -part '../window.dart'; - -part 'scene.dart'; diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 3a52772d0a0f7..7c9dc06d8d854 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -11,10 +11,6 @@ #include "flutter/shell/gpu/gpu_surface_gl_skia.h" #include "impeller/entity/gles/entity_shaders_gles.h" -#if IMPELLER_ENABLE_3D -#include "impeller/scene/shaders/gles/scene_shaders_gles.h" // nogncheck -#endif - namespace flutter { namespace testing { @@ -23,10 +19,6 @@ static std::vector> ShaderLibraryMappings() { std::make_shared( impeller_entity_shaders_gles_data, impeller_entity_shaders_gles_length), -#if IMPELLER_ENABLE_3D - std::make_shared( - impeller_scene_shaders_gles_data, impeller_scene_shaders_gles_length), -#endif }; } diff --git a/shell/platform/android/android_context_gl_impeller.cc b/shell/platform/android/android_context_gl_impeller.cc index d96e55e878055..79091a108f9c1 100644 --- a/shell/platform/android/android_context_gl_impeller.cc +++ b/shell/platform/android/android_context_gl_impeller.cc @@ -12,10 +12,6 @@ #include "impeller/entity/gles/entity_shaders_gles.h" #include "impeller/entity/gles/framebuffer_blend_shaders_gles.h" -#if IMPELLER_ENABLE_3D -// This include was turned to an error since it breaks GN. -#include "impeller/scene/shaders/gles/scene_shaders_gles.h" // nogncheck -#endif // IMPELLER_ENABLE_3D namespace flutter { class AndroidContextGLImpeller::ReactorWorker final @@ -67,10 +63,6 @@ static std::shared_ptr CreateImpellerContext( std::make_shared( impeller_framebuffer_blend_shaders_gles_data, impeller_framebuffer_blend_shaders_gles_length), -#if IMPELLER_ENABLE_3D - std::make_shared( - impeller_scene_shaders_gles_data, impeller_scene_shaders_gles_length), -#endif // IMPELLER_ENABLE_3D }; auto context = impeller::ContextGLES::Create( diff --git a/shell/platform/android/android_context_vk_impeller.cc b/shell/platform/android/android_context_vk_impeller.cc index b81f9cb6b1f34..b46a3b3c5c43c 100644 --- a/shell/platform/android/android_context_vk_impeller.cc +++ b/shell/platform/android/android_context_vk_impeller.cc @@ -10,10 +10,6 @@ #include "flutter/impeller/entity/vk/modern_shaders_vk.h" #include "flutter/impeller/renderer/backend/vulkan/context_vk.h" -#if IMPELLER_ENABLE_3D -#include "flutter/impeller/scene/shaders/vk/scene_shaders_vk.h" // nogncheck -#endif // IMPELLER_ENABLE_3D - namespace flutter { static std::shared_ptr CreateImpellerContext( @@ -32,10 +28,6 @@ static std::shared_ptr CreateImpellerContext( std::make_shared( impeller_framebuffer_blend_shaders_vk_data, impeller_framebuffer_blend_shaders_vk_length), -#if IMPELLER_ENABLE_3D - std::make_shared(impeller_scene_shaders_vk_data, - impeller_scene_shaders_vk_length), -#endif std::make_shared(impeller_modern_shaders_vk_data, impeller_modern_shaders_vk_length), }; diff --git a/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm b/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm index 76f710954c7db..af73983665ff1 100644 --- a/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm +++ b/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm @@ -13,10 +13,6 @@ #include "impeller/entity/mtl/framebuffer_blend_shaders.h" #include "impeller/entity/mtl/modern_shaders.h" -#if IMPELLER_ENABLE_3D -#include "impeller/scene/shaders/mtl/scene_shaders.h" // nogncheck -#endif // IMPELLER_ENABLE_3D - FLUTTER_ASSERT_ARC static std::shared_ptr CreateImpellerContext( @@ -24,10 +20,6 @@ std::vector> shader_mappings = { std::make_shared(impeller_entity_shaders_data, impeller_entity_shaders_length), -#if IMPELLER_ENABLE_3D - std::make_shared(impeller_scene_shaders_data, - impeller_scene_shaders_length), -#endif // IMPELLER_ENABLE_3D std::make_shared(impeller_modern_shaders_data, impeller_modern_shaders_length), std::make_shared(impeller_framebuffer_blend_shaders_data, diff --git a/shell/platform/embedder/embedder_surface_gl_impeller.cc b/shell/platform/embedder/embedder_surface_gl_impeller.cc index cc5b62c8efa33..19713b02613e6 100644 --- a/shell/platform/embedder/embedder_surface_gl_impeller.cc +++ b/shell/platform/embedder/embedder_surface_gl_impeller.cc @@ -12,10 +12,6 @@ #include "impeller/renderer/backend/gles/context_gles.h" #include "impeller/renderer/backend/gles/proc_table_gles.h" -#if IMPELLER_ENABLE_3D -#include "impeller/scene/shaders/gles/scene_shaders_gles.h" // nogncheck -#endif // IMPELLER_ENABLE_3D - namespace flutter { class ReactorWorker final : public impeller::ReactorGLES::Worker { @@ -76,10 +72,6 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( std::make_shared( impeller_framebuffer_blend_shaders_gles_data, impeller_framebuffer_blend_shaders_gles_length), -#if IMPELLER_ENABLE_3D - std::make_shared( - impeller_scene_shaders_gles_data, impeller_scene_shaders_gles_length), -#endif // IMPELLER_ENABLE_3D }; auto gl = std::make_unique( gl_dispatch_table_.gl_proc_resolver); diff --git a/shell/platform/embedder/embedder_surface_metal_impeller.mm b/shell/platform/embedder/embedder_surface_metal_impeller.mm index c36199a2a3fc0..d1d1265dd99d7 100644 --- a/shell/platform/embedder/embedder_surface_metal_impeller.mm +++ b/shell/platform/embedder/embedder_surface_metal_impeller.mm @@ -17,10 +17,6 @@ #include "impeller/entity/mtl/modern_shaders.h" #include "impeller/renderer/backend/metal/context_mtl.h" -#if IMPELLER_ENABLE_3D -#include "impeller/scene/shaders/mtl/scene_shaders.h" // nogncheck -#endif // IMPELLER_ENABLE_3D - FLUTTER_ASSERT_NOT_ARC namespace flutter { @@ -36,10 +32,6 @@ std::vector> shader_mappings = { std::make_shared(impeller_entity_shaders_data, impeller_entity_shaders_length), -#if IMPELLER_ENABLE_3D - std::make_shared(impeller_scene_shaders_data, - impeller_scene_shaders_length), -#endif // IMPELLER_ENABLE_3D std::make_shared(impeller_modern_shaders_data, impeller_modern_shaders_length), std::make_shared(impeller_framebuffer_blend_shaders_data, diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index b9a8b19b88840..1d7358f0d50cf 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -43,9 +43,6 @@ #include "impeller/renderer/context.h" // nogncheck #include "impeller/renderer/vk/compute_shaders_vk.h" // nogncheck #include "shell/gpu/gpu_surface_vulkan_impeller.h" // nogncheck -#if IMPELLER_ENABLE_3D -#include "impeller/scene/shaders/vk/scene_shaders_vk.h" // nogncheck -#endif // IMPELLER_ENABLE_3D static std::vector> ShaderLibraryMappings() { return { @@ -56,10 +53,6 @@ static std::vector> ShaderLibraryMappings() { std::make_shared( impeller_framebuffer_blend_shaders_vk_data, impeller_framebuffer_blend_shaders_vk_length), -#if IMPELLER_ENABLE_3D - std::make_shared(impeller_scene_shaders_vk_data, - impeller_scene_shaders_vk_length), -#endif // IMPELLER_ENABLE_3D std::make_shared( impeller_compute_shaders_vk_data, impeller_compute_shaders_vk_length), }; diff --git a/tools/gn b/tools/gn index 6936f6ef40e34..47f6c9fce8ad9 100755 --- a/tools/gn +++ b/tools/gn @@ -710,9 +710,6 @@ def to_gn_args(args): gn_args['dart_use_mallinfo2'] = args.use_mallinfo2 # Impeller flags. - if args.enable_impeller_3d: - gn_args['impeller_enable_3d'] = True - malioc_path = args.malioc_path if not malioc_path: malioc_path = os.environ.get('MALIOC_PATH') From dc0dae111bababf12207c9dc28518a3361fe9d6f Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 8 Aug 2024 12:15:48 -0700 Subject: [PATCH 2/4] Remove remaining flags. --- ci/builders/mac_unopt.json | 1 - tools/gn | 6 ------ 2 files changed, 7 deletions(-) diff --git a/ci/builders/mac_unopt.json b/ci/builders/mac_unopt.json index 56cf4c4f82a55..bdd62e5c45a22 100644 --- a/ci/builders/mac_unopt.json +++ b/ci/builders/mac_unopt.json @@ -235,7 +235,6 @@ "--unoptimized", "--no-lto", "--prebuilt-dart-sdk", - "--enable-impeller-3d", "--rbe", "--no-goma", "--xcode-symlinks" diff --git a/tools/gn b/tools/gn index 47f6c9fce8ad9..66e03cfb261fb 100755 --- a/tools/gn +++ b/tools/gn @@ -1138,12 +1138,6 @@ def parse_args(args): ) # Impeller flags. - parser.add_argument( - '--enable-impeller-3d', - default=False, - action='store_true', - help='Enables experimental 3d support.' - ) parser.add_argument('--malioc-path', type=str, help='The path to the malioc tool.') From 9145ce4da38ef528a2ecc96c9884cc97177d643c Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 8 Aug 2024 12:32:25 -0700 Subject: [PATCH 3/4] remove includes. --- impeller/playground/backend/gles/playground_impl_gles.cc | 1 - impeller/playground/backend/vulkan/playground_impl_vk.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/impeller/playground/backend/gles/playground_impl_gles.cc b/impeller/playground/backend/gles/playground_impl_gles.cc index dfd0ec7729af0..70d630b8028a5 100644 --- a/impeller/playground/backend/gles/playground_impl_gles.cc +++ b/impeller/playground/backend/gles/playground_impl_gles.cc @@ -22,7 +22,6 @@ #include "impeller/playground/imgui/gles/imgui_shaders_gles.h" #include "impeller/renderer/backend/gles/context_gles.h" #include "impeller/renderer/backend/gles/surface_gles.h" -#include "impeller/scene/shaders/gles/scene_shaders_gles.h" namespace impeller { diff --git a/impeller/playground/backend/vulkan/playground_impl_vk.cc b/impeller/playground/backend/vulkan/playground_impl_vk.cc index aaa61a77bffec..1de431bd41365 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -24,7 +24,6 @@ #include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h" #include "impeller/renderer/backend/vulkan/texture_vk.h" #include "impeller/renderer/vk/compute_shaders_vk.h" -#include "impeller/scene/shaders/vk/scene_shaders_vk.h" namespace impeller { From 86a68786bd2ae6d687c4c5d1aadcd17eef4e51bd Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 8 Aug 2024 13:24:29 -0700 Subject: [PATCH 4/4] fix licenses. --- ci/licenses_golden/licenses_flutter | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 454b4335b8f79..156edba677040 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -42655,9 +42655,6 @@ ORIGIN: ../../../flutter/lib/ui/dart_runtime_hooks.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/dart_ui.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/dart_ui.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/dart_wrapper.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/ui/experiments/scene.dart + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/ui/experiments/setup_hooks.dart + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/lib/ui/experiments/ui.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/floating_point.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/geometry.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/hooks.dart + ../../../flutter/LICENSE @@ -44952,8 +44949,6 @@ FILE: ../../../flutter/impeller/entity/contents/radial_gradient_contents.cc FILE: ../../../flutter/impeller/entity/contents/radial_gradient_contents.h FILE: ../../../flutter/impeller/entity/contents/runtime_effect_contents.cc FILE: ../../../flutter/impeller/entity/contents/runtime_effect_contents.h -FILE: ../../../flutter/impeller/entity/contents/scene_contents.cc -FILE: ../../../flutter/impeller/entity/contents/scene_contents.h FILE: ../../../flutter/impeller/entity/contents/solid_color_contents.cc FILE: ../../../flutter/impeller/entity/contents/solid_color_contents.h FILE: ../../../flutter/impeller/entity/contents/solid_rrect_blur_contents.cc @@ -45489,7 +45484,6 @@ FILE: ../../../flutter/lib/gpu/texture.cc FILE: ../../../flutter/lib/gpu/texture.h FILE: ../../../flutter/lib/io/dart_io.cc FILE: ../../../flutter/lib/io/dart_io.h -FILE: ../../../flutter/lib/snapshot/libraries_experimental.json FILE: ../../../flutter/lib/snapshot/snapshot.h FILE: ../../../flutter/lib/ui/annotations.dart FILE: ../../../flutter/lib/ui/channel_buffers.dart @@ -45503,9 +45497,6 @@ FILE: ../../../flutter/lib/ui/dart_runtime_hooks.h FILE: ../../../flutter/lib/ui/dart_ui.cc FILE: ../../../flutter/lib/ui/dart_ui.h FILE: ../../../flutter/lib/ui/dart_wrapper.h -FILE: ../../../flutter/lib/ui/experiments/scene.dart -FILE: ../../../flutter/lib/ui/experiments/setup_hooks.dart -FILE: ../../../flutter/lib/ui/experiments/ui.dart FILE: ../../../flutter/lib/ui/floating_point.h FILE: ../../../flutter/lib/ui/geometry.dart FILE: ../../../flutter/lib/ui/hooks.dart