[Impeller Scene] Refactor Nodes/Meshes for simplicity and GLTF compatibility (#38180)

diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 36a9559..ee77e26 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1662,19 +1662,19 @@
 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/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/scene_entity.cc
-FILE: ../../../flutter/impeller/scene/scene_entity.h
 FILE: ../../../flutter/impeller/scene/scene_unittests.cc
 FILE: ../../../flutter/impeller/scene/shaders/geometry.vert
 FILE: ../../../flutter/impeller/scene/shaders/unlit.frag
-FILE: ../../../flutter/impeller/scene/static_mesh_entity.cc
-FILE: ../../../flutter/impeller/scene/static_mesh_entity.h
 FILE: ../../../flutter/impeller/tessellator/c/tessellator.cc
 FILE: ../../../flutter/impeller/tessellator/c/tessellator.h
 FILE: ../../../flutter/impeller/tessellator/dart/lib/tessellator.dart
diff --git a/impeller/scene/BUILD.gn b/impeller/scene/BUILD.gn
index 4a54bd7..b13a563 100644
--- a/impeller/scene/BUILD.gn
+++ b/impeller/scene/BUILD.gn
@@ -12,16 +12,16 @@
     "geometry.h",
     "material.cc",
     "material.h",
+    "mesh.cc",
+    "mesh.h",
+    "node.cc",
+    "node.h",
     "scene.cc",
     "scene.h",
     "scene_context.cc",
     "scene_context.h",
     "scene_encoder.cc",
     "scene_encoder.h",
-    "scene_entity.cc",
-    "scene_entity.h",
-    "static_mesh_entity.cc",
-    "static_mesh_entity.h",
   ]
 
   public_deps = [
diff --git a/impeller/scene/README.md b/impeller/scene/README.md
index c172992..228a0be 100644
--- a/impeller/scene/README.md
+++ b/impeller/scene/README.md
@@ -43,30 +43,28 @@
 run_action.SetWeight(0.3f);
 run_action.Play();
 
-scene.Add(
+scene.GetRoot().AddChild(
     impeller::scene::DirectionalLight(
         /* color */ impeller::Color::AntiqueWhite(),
         /* intensity */ 5,
         /* direction */ {2, 3, 4}));
 
-impeller::scene::StaticMeshEntity sphere_entity;
-sphere_entity.SetGlobalTransform(
+Node sphere_node;
+Mesh sphere_mesh;
+sphere_node.SetGlobalTransform(
     Matrix::MakeRotationEuler({kPiOver4, kPiOver4, 0}));
-sphere_entity.SetCullingMode(impeller::scene::CullingMode::kFrustum);
 
-std::unique_ptr<impeller::scene::SphereGeometry> sphere =
+auto sphere_geometry =
     impeller::scene::Geometry::MakeSphere(allocator, /* radius */ 2);
 
-sphere_entity.SetGeometry(sphere);
-
 auto material = impeller::scene::Material::MakeStandard();
-material.SetAlbedo(impeller::Color::Red());
-material.SetRoughness(0.4);
-material.SetMetallic(0.2);
+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({
+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
@@ -74,23 +72,23 @@
   impeller::BlendFactor::kOne,                  // source_alpha_factor
   impeller::BlendFactor::kOneMinusSourceAlpha,  // destination_alpha_factor
 });
-material.SetStencilConfig({
+material->SetStencilConfig({
   impeller::StencilOperation::kIncrementClamp,  // operation
   impeller::CompareFunction::kAlways,           // compare
 });
+sphere_mesh.AddPrimitive({sphere_geometry, material});
+sphere_node.SetMesh(sphere_mesh);
 
-sphere_entity->SetMaterials({material});
+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);
 
-
-impeller::scene::StaticMeshEntity cube_entity;
-cube_entity.GetGeometry(
-    impeller::scene::Geometry::MakeCube(allocator, {4, 4, 4}));
-cube_entity.SetMaterials({material});
-
-cube_entity.SetLocalTransform(Matrix::MakeTranslation({4, 0, 0}));
-
-sphere_entity->Add(sube_entity);
-scene.Add(sphere_entity);
+sphere_node.AddChild(cube_node);
+scene.GetRoot().AddChild(sphere_node);
 
 /// Post processing.
 
diff --git a/impeller/scene/geometry.cc b/impeller/scene/geometry.cc
index 8ef0f5f..2a5f74c 100644
--- a/impeller/scene/geometry.cc
+++ b/impeller/scene/geometry.cc
@@ -40,15 +40,15 @@
   return result;
 }
 
-std::shared_ptr<VertexBufferGeometry> Geometry::MakeFromFBMesh(
-    const fb::StaticMesh& mesh,
+std::shared_ptr<VertexBufferGeometry> Geometry::MakeFromFBMeshPrimitive(
+    const fb::MeshPrimitive& mesh,
     Allocator& allocator) {
   IndexType index_type;
   switch (mesh.indices()->type()) {
-    case fb::IndicesType::k16Bit:
+    case fb::IndexType::k16Bit:
       index_type = IndexType::k16bit;
       break;
-    case fb::IndicesType::k32Bit:
+    case fb::IndexType::k32Bit:
       index_type = IndexType::k32bit;
       break;
   }
diff --git a/impeller/scene/geometry.h b/impeller/scene/geometry.h
index 2c322a3..bd41589 100644
--- a/impeller/scene/geometry.h
+++ b/impeller/scene/geometry.h
@@ -28,8 +28,8 @@
   static std::shared_ptr<VertexBufferGeometry> MakeVertexBuffer(
       VertexBuffer vertex_buffer);
 
-  static std::shared_ptr<VertexBufferGeometry> MakeFromFBMesh(
-      const fb::StaticMesh& mesh,
+  static std::shared_ptr<VertexBufferGeometry> MakeFromFBMeshPrimitive(
+      const fb::MeshPrimitive& mesh,
       Allocator& allocator);
 
   virtual VertexBuffer GetVertexBuffer(Allocator& allocator) const = 0;
diff --git a/impeller/scene/importer/importer_gltf.cc b/impeller/scene/importer/importer_gltf.cc
index ff19693..74b7e57 100644
--- a/impeller/scene/importer/importer_gltf.cc
+++ b/impeller/scene/importer/importer_gltf.cc
@@ -33,9 +33,9 @@
   return index >= 0 && static_cast<size_t>(index) < size;
 }
 
-static bool ProcessStaticMesh(const tinygltf::Model& gltf,
-                              const tinygltf::Primitive& primitive,
-                              fb::StaticMeshT& static_mesh) {
+static bool ProcessMeshPrimitive(const tinygltf::Model& gltf,
+                                 const tinygltf::Primitive& primitive,
+                                 fb::MeshPrimitiveT& mesh_primitive) {
   //---------------------------------------------------------------------------
   /// Vertices.
   ///
@@ -93,7 +93,7 @@
                                      accessor.count);            // count
     }
 
-    builder.WriteFBVertices(static_mesh.vertices);
+    builder.WriteFBVertices(mesh_primitive.vertices);
   }
 
   //---------------------------------------------------------------------------
@@ -112,10 +112,10 @@
 
   switch (index_accessor.componentType) {
     case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
-      indices->type = fb::IndicesType::k16Bit;
+      indices->type = fb::IndexType::k16Bit;
       break;
     case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
-      indices->type = fb::IndicesType::k32Bit;
+      indices->type = fb::IndexType::k32Bit;
       break;
     default:
       std::cerr << "Mesh primitive has unsupported index type "
@@ -128,7 +128,7 @@
       &gltf.buffers[index_view.buffer].data[index_view.byteOffset];
   std::memcpy(indices->data.data(), index_buffer, indices->data.size());
 
-  static_mesh.indices = std::move(indices);
+  mesh_primitive.indices = std::move(indices);
 
   return true;
 }
@@ -177,11 +177,11 @@
   if (WithinRange(in_node.mesh, gltf.meshes.size())) {
     auto& mesh = gltf.meshes[in_node.mesh];
     for (const auto& primitive : mesh.primitives) {
-      auto static_mesh = std::make_unique<fb::StaticMeshT>();
-      if (!ProcessStaticMesh(gltf, primitive, *static_mesh)) {
+      auto mesh_primitive = std::make_unique<fb::MeshPrimitiveT>();
+      if (!ProcessMeshPrimitive(gltf, primitive, *mesh_primitive)) {
         continue;
       }
-      out_node.meshes.push_back(std::move(static_mesh));
+      out_node.mesh_primitives.push_back(std::move(mesh_primitive));
     }
   }
 
diff --git a/impeller/scene/importer/importer_unittests.cc b/impeller/scene/importer/importer_unittests.cc
index 68f481b..1647d09 100644
--- a/impeller/scene/importer/importer_unittests.cc
+++ b/impeller/scene/importer/importer_unittests.cc
@@ -26,8 +26,8 @@
   Matrix node_transform = ToMatrix(*node.transform);
   ASSERT_MATRIX_NEAR(node_transform, Matrix());
 
-  ASSERT_EQ(node.meshes.size(), 1u);
-  auto& mesh = *node.meshes[0];
+  ASSERT_EQ(node.mesh_primitives.size(), 1u);
+  auto& mesh = *node.mesh_primitives[0];
   ASSERT_EQ(mesh.indices->count, 918u);
 
   uint16_t first_index =
diff --git a/impeller/scene/importer/scene.fbs b/impeller/scene/importer/scene.fbs
index 9fbd4af..a0c320e 100644
--- a/impeller/scene/importer/scene.fbs
+++ b/impeller/scene/importer/scene.fbs
@@ -43,7 +43,7 @@
   color: Color;
 }
 
-enum IndicesType:byte {
+enum IndexType:byte {
   k16Bit,
   k32Bit,
 }
@@ -51,7 +51,7 @@
 table Indices {
   data: [ubyte];
   count: uint32;
-  type: IndicesType;
+  type: IndexType;
 }
 
 table Texture {
@@ -65,7 +65,7 @@
   // TODO(bdero): PBR textures.
 }
 
-table StaticMesh {
+table MeshPrimitive {
   vertices: [Vertex];
   indices: Indices;
   material: Material;
@@ -74,7 +74,7 @@
 table Node {
   children: [Node];
   transform: Matrix;
-  meshes: [StaticMesh];
+  mesh_primitives: [MeshPrimitive];
 }
 
 table Scene {
diff --git a/impeller/scene/mesh.cc b/impeller/scene/mesh.cc
new file mode 100644
index 0000000..c0a6e19
--- /dev/null
+++ b/impeller/scene/mesh.cc
@@ -0,0 +1,44 @@
+// 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 <memory>
+
+#include "impeller/base/validation.h"
+#include "impeller/scene/material.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.";
+  }
+
+  meshes_.push_back(std::move(mesh));
+}
+
+bool Mesh::Render(SceneEncoder& encoder, const Matrix& transform) const {
+  for (const auto& mesh : meshes_) {
+    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
new file mode 100644
index 0000000..a454e9d
--- /dev/null
+++ b/impeller/scene/mesh.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <memory>
+#include <type_traits>
+
+#include "flutter/fml/macros.h"
+#include "impeller/scene/geometry.h"
+#include "impeller/scene/material.h"
+#include "impeller/scene/scene_encoder.h"
+
+namespace impeller {
+namespace scene {
+
+class Mesh final {
+ public:
+  struct Primitive {
+    std::shared_ptr<Geometry> geometry_;
+    std::shared_ptr<Material> material_;
+  };
+
+  Mesh();
+  ~Mesh();
+
+  void AddPrimitive(Primitive mesh_);
+
+  bool Render(SceneEncoder& encoder, const Matrix& transform) const;
+
+ private:
+  std::vector<Primitive> meshes_;
+};
+
+}  // namespace scene
+}  // namespace impeller
diff --git a/impeller/scene/node.cc b/impeller/scene/node.cc
new file mode 100644
index 0000000..09838c5
--- /dev/null
+++ b/impeller/scene/node.cc
@@ -0,0 +1,67 @@
+// 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 <memory>
+
+#include "impeller/base/validation.h"
+#include "impeller/geometry/matrix.h"
+#include "impeller/scene/mesh.h"
+#include "impeller/scene/node.h"
+#include "impeller/scene/scene_encoder.h"
+
+namespace impeller {
+namespace scene {
+
+Node::Node() = default;
+
+Node::~Node() = default;
+
+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_;
+}
+
+void Node::AddChild(Node child) {
+  children_.push_back(child);
+  child.parent_ = this;
+}
+
+void Node::SetMesh(const Mesh& mesh) {
+  mesh_ = mesh;
+}
+
+bool Node::Render(SceneEncoder& encoder, const Matrix& parent_transform) const {
+  Matrix transform = parent_transform * local_transform_;
+
+  mesh_.Render(encoder, transform);
+
+  for (auto& child : children_) {
+    if (!child.Render(encoder, transform)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace scene
+}  // namespace impeller
diff --git a/impeller/scene/scene_entity.h b/impeller/scene/node.h
similarity index 62%
rename from impeller/scene/scene_entity.h
rename to impeller/scene/node.h
index 9c774c6..7a95cc1 100644
--- a/impeller/scene/scene_entity.h
+++ b/impeller/scene/node.h
@@ -12,19 +12,16 @@
 #include "impeller/geometry/matrix.h"
 #include "impeller/renderer/render_target.h"
 #include "impeller/scene/camera.h"
+#include "impeller/scene/mesh.h"
 #include "impeller/scene/scene_encoder.h"
 
 namespace impeller {
 namespace scene {
 
-class StaticMeshEntity;
-
-class SceneEntity {
+class Node final {
  public:
-  SceneEntity();
-  virtual ~SceneEntity();
-
-  static std::shared_ptr<StaticMeshEntity> MakeStaticMesh();
+  Node();
+  ~Node();
 
   void SetLocalTransform(Matrix transform);
   Matrix GetLocalTransform() const;
@@ -32,20 +29,19 @@
   void SetGlobalTransform(Matrix transform);
   Matrix GetGlobalTransform() const;
 
-  bool Add(const std::shared_ptr<SceneEntity>& child);
+  void AddChild(Node child);
 
-  bool Render(SceneEncoder& encoder) const;
+  void SetMesh(const Mesh& mesh);
+
+  bool Render(SceneEncoder& encoder, const Matrix& parent_transform) const;
 
  protected:
   Matrix local_transform_;
 
  private:
-  virtual bool OnRender(SceneEncoder& encoder) const;
-
-  SceneEntity* parent_ = nullptr;
-  std::vector<std::shared_ptr<SceneEntity>> children_;
-
-  FML_DISALLOW_COPY_AND_ASSIGN(SceneEntity);
+  Node* parent_ = nullptr;
+  std::vector<Node> children_;
+  Mesh mesh_;
 };
 
 }  // namespace scene
diff --git a/impeller/scene/scene.cc b/impeller/scene/scene.cc
index 3dce509..ad607cd 100644
--- a/impeller/scene/scene.cc
+++ b/impeller/scene/scene.cc
@@ -18,15 +18,15 @@
 Scene::Scene(std::shared_ptr<Context> context)
     : scene_context_(std::make_unique<SceneContext>(std::move(context))){};
 
-void Scene::Add(const std::shared_ptr<SceneEntity>& child) {
-  root_.Add(child);
+Node& Scene::GetRoot() {
+  return root_;
 }
 
 bool Scene::Render(const RenderTarget& render_target,
                    const Camera& camera) const {
   // Collect the render commands from the scene.
   SceneEncoder encoder;
-  if (!root_.Render(encoder)) {
+  if (!root_.Render(encoder, Matrix())) {
     FML_LOG(ERROR) << "Failed to render frame.";
     return false;
   }
diff --git a/impeller/scene/scene.h b/impeller/scene/scene.h
index 1e279f6..97fadc8 100644
--- a/impeller/scene/scene.h
+++ b/impeller/scene/scene.h
@@ -11,8 +11,8 @@
 
 #include "impeller/renderer/render_target.h"
 #include "impeller/scene/camera.h"
+#include "impeller/scene/node.h"
 #include "impeller/scene/scene_context.h"
-#include "impeller/scene/scene_entity.h"
 
 namespace impeller {
 namespace scene {
@@ -22,12 +22,13 @@
   Scene() = delete;
   explicit Scene(std::shared_ptr<Context> context);
 
-  void Add(const std::shared_ptr<SceneEntity>& child);
+  Node& GetRoot();
+
   bool Render(const RenderTarget& render_target, const Camera& camera) const;
 
  private:
   std::unique_ptr<SceneContext> scene_context_;
-  SceneEntity root_;
+  Node root_;
 
   FML_DISALLOW_COPY_AND_ASSIGN(Scene);
 };
diff --git a/impeller/scene/scene_entity.cc b/impeller/scene/scene_entity.cc
deleted file mode 100644
index 52d7fcd..0000000
--- a/impeller/scene/scene_entity.cc
+++ /dev/null
@@ -1,74 +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_entity.h"
-
-#include <memory>
-
-#include "impeller/base/validation.h"
-#include "impeller/geometry/matrix.h"
-#include "impeller/scene/scene_encoder.h"
-#include "impeller/scene/static_mesh_entity.h"
-
-namespace impeller {
-namespace scene {
-
-SceneEntity::SceneEntity() = default;
-
-SceneEntity::~SceneEntity() = default;
-
-std::shared_ptr<StaticMeshEntity> SceneEntity::MakeStaticMesh() {
-  return std::make_shared<StaticMeshEntity>();
-}
-
-void SceneEntity::SetLocalTransform(Matrix transform) {
-  local_transform_ = transform;
-}
-
-Matrix SceneEntity::GetLocalTransform() const {
-  return local_transform_;
-}
-
-void SceneEntity::SetGlobalTransform(Matrix transform) {
-  Matrix inverse_global_transform =
-      parent_ ? parent_->GetGlobalTransform().Invert() : Matrix();
-
-  local_transform_ = inverse_global_transform * transform;
-}
-
-Matrix SceneEntity::GetGlobalTransform() const {
-  if (parent_) {
-    return parent_->GetGlobalTransform() * local_transform_;
-  }
-  return local_transform_;
-}
-
-bool SceneEntity::Add(const std::shared_ptr<SceneEntity>& child) {
-  if (child->parent_ != nullptr) {
-    VALIDATION_LOG << "Cannot add SceneEntity as a child because it already "
-                      "has a parent assigned.";
-    return false;
-  }
-
-  children_.push_back(child);
-  child->parent_ = this;
-  return true;
-}
-
-bool SceneEntity::Render(SceneEncoder& encoder) const {
-  OnRender(encoder);
-  for (auto& child : children_) {
-    if (!child->Render(encoder)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool SceneEntity::OnRender(SceneEncoder& encoder) const {
-  return true;
-}
-
-}  // namespace scene
-}  // namespace impeller
diff --git a/impeller/scene/scene_unittests.cc b/impeller/scene/scene_unittests.cc
index b2a15ad..746d615 100644
--- a/impeller/scene/scene_unittests.cc
+++ b/impeller/scene/scene_unittests.cc
@@ -18,8 +18,8 @@
 #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 "impeller/scene/static_mesh_entity.h"
 #include "third_party/flatbuffers/include/flatbuffers/verifier.h"
 
 // #include "third_party/tinygltf/tiny_gltf.h"
@@ -37,18 +37,17 @@
     auto scene = Scene(GetContext());
 
     {
-      auto mesh = SceneEntity::MakeStaticMesh();
+      Mesh mesh;
 
       auto material = Material::MakeUnlit();
       material->SetColor(Color::Red());
-      mesh->SetMaterial(std::move(material));
 
       Vector3 size(1, 1, 0);
-      mesh->SetGeometry(Geometry::MakeCuboid(size));
+      mesh.AddPrimitive({Geometry::MakeCuboid(size), std::move(material)});
 
-      mesh->SetLocalTransform(Matrix::MakeTranslation(-size / 2));
-
-      scene.Add(mesh);
+      Node& root = scene.GetRoot();
+      root.SetLocalTransform(Matrix::MakeTranslation(-size / 2));
+      root.SetMesh(mesh);
     }
 
     // Face towards the +Z direction (+X right, +Y up).
@@ -79,10 +78,10 @@
   const auto* fb_scene = fb::GetScene(mapping->GetMapping());
   const auto fb_nodes = fb_scene->children();
   ASSERT_EQ(fb_nodes->size(), 1u);
-  const auto fb_meshes = fb_nodes->begin()->meshes();
+  const auto fb_meshes = fb_nodes->begin()->mesh_primitives();
   ASSERT_EQ(fb_meshes->size(), 1u);
   const auto* fb_mesh = fb_meshes->Get(0);
-  auto geometry = Geometry::MakeFromFBMesh(*fb_mesh, *allocator);
+  auto geometry = Geometry::MakeFromFBMeshPrimitive(*fb_mesh, *allocator);
   ASSERT_NE(geometry, nullptr);
 
   std::shared_ptr<UnlitMaterial> material = Material::MakeUnlit();
@@ -93,11 +92,11 @@
   Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
     auto scene = Scene(GetContext());
 
-    auto mesh = SceneEntity::MakeStaticMesh();
-    mesh->SetMaterial(material);
-    mesh->SetGeometry(geometry);
-    mesh->SetLocalTransform(Matrix::MakeScale({3, 3, 3}));
-    scene.Add(mesh);
+    Mesh mesh;
+    mesh.AddPrimitive({geometry, material});
+
+    scene.GetRoot().SetLocalTransform(Matrix::MakeScale({3, 3, 3}));
+    scene.GetRoot().SetMesh(mesh);
 
     Quaternion rotation({0, 1, 0}, -GetSecondsElapsed() * 0.5);
     Vector3 start_position(-1, -1.5, -5);
diff --git a/impeller/scene/static_mesh_entity.cc b/impeller/scene/static_mesh_entity.cc
deleted file mode 100644
index 53ca7fa..0000000
--- a/impeller/scene/static_mesh_entity.cc
+++ /dev/null
@@ -1,39 +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/static_mesh_entity.h"
-
-#include <memory>
-
-#include "impeller/scene/material.h"
-#include "impeller/scene/scene_encoder.h"
-
-namespace impeller {
-namespace scene {
-
-StaticMeshEntity::StaticMeshEntity() = default;
-StaticMeshEntity::~StaticMeshEntity() = default;
-
-void StaticMeshEntity::SetGeometry(std::shared_ptr<Geometry> geometry) {
-  geometry_ = std::move(geometry);
-}
-
-void StaticMeshEntity::SetMaterial(std::shared_ptr<Material> material) {
-  material_ = std::move(material);
-}
-
-// |SceneEntity|
-bool StaticMeshEntity::OnRender(SceneEncoder& encoder) const {
-  SceneCommand command = {
-      .label = "Static Mesh",
-      .transform = GetGlobalTransform(),
-      .geometry = geometry_.get(),
-      .material = material_.get(),
-  };
-  encoder.Add(command);
-  return true;
-}
-
-}  // namespace scene
-}  // namespace impeller
diff --git a/impeller/scene/static_mesh_entity.h b/impeller/scene/static_mesh_entity.h
deleted file mode 100644
index 77d3de8..0000000
--- a/impeller/scene/static_mesh_entity.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.
-
-#pragma once
-
-#include <memory>
-#include <type_traits>
-
-#include "flutter/fml/macros.h"
-#include "impeller/scene/geometry.h"
-#include "impeller/scene/material.h"
-#include "impeller/scene/scene_entity.h"
-
-namespace impeller {
-namespace scene {
-
-class StaticMeshEntity final : public SceneEntity {
- public:
-  StaticMeshEntity();
-  ~StaticMeshEntity();
-
-  void SetGeometry(std::shared_ptr<Geometry> material);
-  void SetMaterial(std::shared_ptr<Material> material);
-
- private:
-  // |SceneEntity|
-  bool OnRender(SceneEncoder& encoder) const override;
-
-  std::shared_ptr<Material> material_;
-  std::shared_ptr<Geometry> geometry_;
-
-  FML_DISALLOW_COPY_AND_ASSIGN(StaticMeshEntity);
-};
-
-}  // namespace scene
-}  // namespace impeller