blob: 26d2055d9cea2544e38a6b73e77e02ca1efd5834 [file] [log] [blame] [edit]
// 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 "mesh_example.h"
#include <array>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include "imgui/imgui.h"
#include "impeller/core/allocator.h"
#include "impeller/core/buffer_view.h"
#include "impeller/core/device_buffer_descriptor.h"
#include "impeller/core/formats.h"
#include "impeller/core/host_buffer.h"
#include "impeller/core/vertex_buffer.h"
#include "impeller/geometry/matrix.h"
#include "impeller/geometry/scalar.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/pipeline_library.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/render_target.h"
#include "impeller/renderer/sampler_library.h"
#include "impeller/tessellator/tessellator.h"
#include "examples/assets.h"
#include "generated/importer/mesh_flatbuffers.h"
#include "generated/shaders/mesh_example.frag.h"
#include "generated/shaders/mesh_example.vert.h"
namespace example {
ExampleBase::Info MeshExample::GetInfo() {
return {
.name = "Mesh Example",
.description =
"An FBX with texture coordinates and normals/tangents imported ahead "
"of time. The importer tool is located under `src/importer`.",
};
}
bool MeshExample::Setup(impeller::Context& context) {
transients_buffer_ =
impeller::HostBuffer::Create(context.GetResourceAllocator());
//----------------------------------------------------------------------------
/// Load/unpack the model.
///
std::ifstream in;
const char* filename = "assets/flutter_logo.model";
in.open(filename, std::ios::binary | std::ios::in);
in.seekg(0, std::ios::end);
auto size = in.tellg();
in.seekg(0, std::ios::beg);
std::vector<char> data(size);
in.read(data.data(), size);
in.close();
if (!in) {
std::cerr << "Failed to read file: " << filename << std::endl;
return false;
}
fb::MeshT mesh;
fb::GetMesh(data.data())->UnPackTo(&mesh);
//----------------------------------------------------------------------------
/// Load textures.
///
const auto asset_path = std::filesystem::current_path() / "assets/";
base_color_texture_ =
example::LoadTexture(asset_path / "flutter_logo_BaseColor.png",
*context.GetResourceAllocator());
if (!base_color_texture_) {
std::cerr << "Failed to load base color texture." << std::endl;
}
normal_texture_ = example::LoadTexture(asset_path / "flutter_logo_Normal.png",
*context.GetResourceAllocator());
if (!normal_texture_) {
std::cerr << "Failed to load normal texture." << std::endl;
}
occlusion_roughness_metallic_texture_ = example::LoadTexture(
asset_path / "flutter_logo_OcclusionRoughnessMetallic.png",
*context.GetResourceAllocator());
if (!occlusion_roughness_metallic_texture_) {
std::cerr << "Failed to load occlusion/roughness/metallic texture."
<< std::endl;
}
//----------------------------------------------------------------------------
/// Upload vertices/indices to the device.
///
auto vertices_size = sizeof(fb::Vertex) * mesh.vertices.size();
auto indices_size = sizeof(uint16_t) * mesh.indices.size();
auto device_buffer = context.GetResourceAllocator()->CreateBuffer({
.storage_mode = impeller::StorageMode::kHostVisible,
.size = vertices_size + indices_size,
});
if (!device_buffer) {
std::cerr << "Failed to create device buffer." << std::endl;
return false;
}
if (!device_buffer->CopyHostBuffer(
reinterpret_cast<uint8_t*>(mesh.vertices.data()),
impeller::Range{0, vertices_size}, 0)) {
std::cerr << "Failed to upload vertices to device buffer." << std::endl;
return false;
}
if (!device_buffer->CopyHostBuffer(
reinterpret_cast<uint8_t*>(mesh.indices.data()),
impeller::Range{0, indices_size}, vertices_size)) {
std::cerr << "Failed to upload indices to device buffer." << std::endl;
return false;
}
vertex_buffer_ = {
.vertex_buffer =
impeller::BufferView{.buffer = device_buffer,
.range = impeller::Range{0, vertices_size}},
.index_buffer =
impeller::BufferView{
.buffer = device_buffer,
.range = impeller::Range{vertices_size, indices_size}},
.vertex_count = mesh.indices.size(),
.index_type = impeller::IndexType::k16bit,
};
//----------------------------------------------------------------------------
/// Build the pipeline.
///
auto pipeline_desc =
impeller::PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(context);
pipeline_desc->SetSampleCount(impeller::SampleCount::kCount4);
pipeline_desc->SetWindingOrder(impeller::WindingOrder::kClockwise);
pipeline_desc->SetCullMode(impeller::CullMode::kBackFace);
pipeline_desc->SetDepthStencilAttachmentDescriptor(
std::make_optional<impeller::DepthAttachmentDescriptor>({
.depth_compare = impeller::CompareFunction::kLess,
.depth_write_enabled = true,
}));
pipeline_ = context.GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
if (!pipeline_ || !pipeline_->IsValid()) {
std::cerr << "Failed to initialize pipeline for mesh example.";
return false;
}
return true;
}
bool MeshExample::Render(impeller::Context& context,
const impeller::RenderTarget& render_target,
impeller::CommandBuffer& command_buffer) {
clock_.Tick();
transients_buffer_->Reset();
static float exposure = 5;
ImGui::SliderFloat("Exposure", &exposure, 0, 15);
auto pass = command_buffer.CreateRenderPass(render_target);
if (!pass) {
return false;
}
pass->SetCommandLabel("Mesh Example");
pass->SetPipeline(pipeline_);
pass->SetVertexBuffer(vertex_buffer_);
auto time = clock_.GetTime();
VS::VertInfo vs_uniform;
vs_uniform.mvp =
impeller::Matrix::MakePerspective(
impeller::Degrees{60}, pass->GetRenderTargetSize(), 0.1, 1000) *
impeller::Matrix::MakeLookAt({0, 0, -50}, {0, 0, 0}, {0, 1, 0}) *
impeller::Matrix::MakeScale({0.3, 0.3, 0.3}) *
impeller::Matrix::MakeRotationY(impeller::Radians{-0.4f * time}) *
impeller::Matrix::MakeRotationZ(
impeller::Radians{std::sin(time * 0.43f) / 5}) *
impeller::Matrix::MakeRotationX(
impeller::Radians{std::cos(time * 0.27f) / 4});
VS::BindVertInfo(*pass, transients_buffer_->EmplaceUniform(vs_uniform));
FS::FragInfo fs_uniform;
fs_uniform.exposure = exposure;
fs_uniform.camera_position = {0, 0, -50};
FS::BindFragInfo(*pass, transients_buffer_->EmplaceUniform(fs_uniform));
impeller::SamplerDescriptor sampler_desc;
sampler_desc.min_filter = impeller::MinMagFilter::kLinear;
sampler_desc.mag_filter = impeller::MinMagFilter::kLinear;
const auto& sampler = context.GetSamplerLibrary()->GetSampler(sampler_desc);
FS::BindBaseColorTexture(*pass, base_color_texture_, sampler);
FS::BindNormalTexture(*pass, normal_texture_, sampler);
FS::BindOcclusionRoughnessMetallicTexture(
*pass, occlusion_roughness_metallic_texture_, sampler);
if (!pass->Draw().ok()) {
return false;
}
if (!pass->EncodeCommands()) {
return false;
}
return true;
}
} // namespace example