bdero / flutter_scene

3D library for Flutter.
MIT License
165 stars 11 forks source link

Rewrite for Flutter GPU #1

Closed bdero closed 2 months ago

bdero commented 9 months ago

Currently, most of the functionality of flutter_scene is built in C++ (as a subsystem in Impeller called Impeller Scene). In 2023, I took a step back and began building out Flutter GPU, a new low level graphics API that talks directly to Impeller's HAL from Dart.

As mentioned in the Flutter GPU doc, this renderer is undergoing a rewrite in Flutter GPU. The flutter-gpu branch has the in-progress rewrite.

The original Impeller Scene API will be surfaced as part of the rewrite. An example of the API's basic usage in C++ can be seen in impeller/scene/README.md of the Flutter Engine repository. Copied here for posterity:

std::shared_ptr<impeller::Context> 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<impeller::Surface> surface = /* Wrap the window surface */;

  renderer->Render(surface, [&scene](RenderTarget& render_target) {
    /// 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);

    return true;
  });
}
bdero commented 2 months ago

The high level widget API no longer exists, but hey, we have a renderer!