Introduce gltf_baker tool.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ dist-*
|
||||
toolchains
|
||||
filament/docs/html/**
|
||||
.vscode
|
||||
gltf_baker.ini
|
||||
|
||||
@@ -60,6 +60,9 @@ public:
|
||||
// Helper method called after resolving fontPath; public so fonts can be added by caller.
|
||||
void createAtlasTexture(filament::Engine* engine);
|
||||
|
||||
// Return the ImGui view, useful for drawing 2D overlays.
|
||||
filament::View* getView() const { return mView; }
|
||||
|
||||
private:
|
||||
void renderDrawData(ImDrawData* imguiData);
|
||||
void createBuffers(int numRequiredBuffers);
|
||||
|
||||
@@ -48,13 +48,17 @@ namespace gltfio {
|
||||
*/
|
||||
class SimpleViewer {
|
||||
public:
|
||||
|
||||
static constexpr uint32_t FLAG_COLLAPSED = (1 << 0);
|
||||
|
||||
/**
|
||||
* Constructs a SimpleViewer that has a fixed association with the given Filament objects.
|
||||
*
|
||||
* Upon construction, the simple viewer may create some additional Filament objects (such as
|
||||
* light sources) that it owns.
|
||||
*/
|
||||
SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view);
|
||||
SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view,
|
||||
uint32_t flags = 0);
|
||||
|
||||
/**
|
||||
* Destroys the SimpleViewer and any Filament entities that it owns.
|
||||
@@ -145,6 +149,7 @@ private:
|
||||
bool mEnableMsaa = true;
|
||||
bool mEnableSsao = true;
|
||||
int mSidebarWidth = INITIAL_SIDEBAR_WIDTH;
|
||||
uint32_t mFlags;
|
||||
};
|
||||
|
||||
filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds);
|
||||
@@ -181,10 +186,12 @@ filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds) {
|
||||
return mat4f::scaling(float3(scaleFactor)) * mat4f::translation(-center);
|
||||
}
|
||||
|
||||
SimpleViewer::SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view) :
|
||||
SimpleViewer::SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view,
|
||||
uint32_t flags) :
|
||||
mEngine(engine), mScene(scene), mView(view),
|
||||
mNames(new utils::NameComponentManager(utils::EntityManager::get())),
|
||||
mSunlight(utils::EntityManager::get().create()) {
|
||||
mSunlight(utils::EntityManager::get().create()),
|
||||
mFlags(flags) {
|
||||
using namespace filament;
|
||||
LightManager::Builder(LightManager::Type::SUN)
|
||||
.color(Color::toLinear<ACCURATE>({0.98, 0.92, 0.89}))
|
||||
@@ -260,6 +267,8 @@ void SimpleViewer::applyAnimation(double currentTime) {
|
||||
void SimpleViewer::updateUserInterface() {
|
||||
using namespace filament;
|
||||
|
||||
ImGuiTreeNodeFlags headerFlags = (mFlags & FLAG_COLLAPSED) ? 0 : ImGuiTreeNodeFlags_DefaultOpen;
|
||||
|
||||
auto& tm = mEngine->getTransformManager();
|
||||
auto& rm = mEngine->getRenderableManager();
|
||||
auto& lm = mEngine->getLightManager();
|
||||
@@ -361,7 +370,7 @@ void SimpleViewer::updateUserInterface() {
|
||||
mView->setAmbientOcclusion(
|
||||
mEnableSsao ? View::AmbientOcclusion::SSAO : View::AmbientOcclusion::NONE);
|
||||
|
||||
if (ImGui::CollapsingHeader("Light", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
if (ImGui::CollapsingHeader("Light", headerFlags)) {
|
||||
ImGui::SliderFloat("IBL intensity", &mIblIntensity, 0.0f, 100000.0f);
|
||||
ImGui::SliderAngle("IBL rotation", &mIblRotation);
|
||||
ImGui::SliderFloat("Sun intensity", &mSunlightIntensity, 50000.0, 150000.0f);
|
||||
@@ -378,23 +387,25 @@ void SimpleViewer::updateUserInterface() {
|
||||
mScene->remove(mSunlight);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Model", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
if (mAnimator->getAnimationCount() > 0) {
|
||||
intptr_t animationsNodeId = -1;
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen;
|
||||
if (ImGui::TreeNodeEx((const void*) animationsNodeId, flags, "Animations")) {
|
||||
animationsTreeItem();
|
||||
ImGui::TreePop();
|
||||
if (mAsset != nullptr) {
|
||||
if (ImGui::CollapsingHeader("Model", headerFlags)) {
|
||||
if (mAnimator->getAnimationCount() > 0) {
|
||||
intptr_t animationsNodeId = -1;
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen;
|
||||
if (ImGui::TreeNodeEx((const void*) animationsNodeId, flags, "Animations")) {
|
||||
animationsTreeItem();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
ImGui::Checkbox("Wireframe", &mShowWireframe);
|
||||
treeNode(mAsset->getRoot());
|
||||
}
|
||||
ImGui::Checkbox("Wireframe", &mShowWireframe);
|
||||
treeNode(mAsset->getRoot());
|
||||
}
|
||||
|
||||
if (mShowWireframe) {
|
||||
mScene->addEntity(mAsset->getWireframe());
|
||||
} else {
|
||||
mScene->remove(mAsset->getWireframe());
|
||||
if (mShowWireframe) {
|
||||
mScene->addEntity(mAsset->getWireframe());
|
||||
} else {
|
||||
mScene->remove(mAsset->getWireframe());
|
||||
}
|
||||
}
|
||||
|
||||
mSidebarWidth = ImGui::GetWindowWidth();
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
* Creates an empty (invalid) image.
|
||||
*/
|
||||
LinearImage() : mDataRef(nullptr), mData(nullptr), mWidth(0), mHeight(0), mChannels(0) {}
|
||||
operator bool() const { return mData != nullptr; }
|
||||
|
||||
/**
|
||||
* Gets a pointer to the underlying pixel data.
|
||||
|
||||
@@ -32,6 +32,7 @@ add_mesh("assets/models/monkey/monkey.obj" "suzanne.filamesh")
|
||||
set(MATERIAL_SRCS
|
||||
materials/bakedColor.mat
|
||||
materials/bakedTexture.mat
|
||||
materials/aoPreview.mat
|
||||
materials/aiDefaultMat.mat
|
||||
materials/aiDefaultTrans.mat
|
||||
materials/depthVisualizer.mat
|
||||
@@ -256,11 +257,13 @@ if (NOT ANDROID)
|
||||
add_filamesh_demo(sample_normal_map)
|
||||
add_filamesh_demo(suzanne)
|
||||
add_filamesh_demo(gltf_viewer)
|
||||
add_filamesh_demo(gltf_baker)
|
||||
|
||||
# Sample app specific
|
||||
target_link_libraries(frame_generator PRIVATE imageio)
|
||||
target_link_libraries(suzanne PRIVATE suzanne-resources)
|
||||
target_link_libraries(gltf_viewer PRIVATE gltf-resources gltfio)
|
||||
target_link_libraries(gltf_baker PRIVATE gltf-resources gltfio)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -64,6 +64,10 @@ FilamentApp::~FilamentApp() {
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
View* FilamentApp::getGuiView() const noexcept {
|
||||
return mImGuiHelper->getView();
|
||||
}
|
||||
|
||||
void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
CleanupCallback cleanupCallback, ImGuiCallback imguiCallback,
|
||||
PreRenderCallback preRender, PostRenderCallback postRender,
|
||||
@@ -515,6 +519,8 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
|
||||
mMainCameraMan.lookAt(at + double3{ 0, 0, 4 }, at);
|
||||
mDebugCameraMan.lookAt(at + double3{ 0, 0, 4 }, at);
|
||||
mOrthoCameraMan.lookAt(at + double3{ 0, 0, 4 }, at);
|
||||
|
||||
mMainCamera->lookAt({4, 0, -4}, {0, 0, -4}, {0, 1, 0});
|
||||
}
|
||||
|
||||
FilamentApp::Window::~Window() {
|
||||
|
||||
@@ -75,6 +75,7 @@ public:
|
||||
filament::Material const* getDefaultMaterial() const noexcept { return mDefaultMaterial; }
|
||||
filament::Material const* getTransparentMaterial() const noexcept { return mTransparentMaterial; }
|
||||
IBL* getIBL() const noexcept { return mIBL.get(); }
|
||||
filament::View* getGuiView() const noexcept;
|
||||
|
||||
void close() { mClosed = true; }
|
||||
|
||||
|
||||
592
samples/gltf_baker.cpp
Normal file
592
samples/gltf_baker.cpp
Normal file
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define GLTFIO_SIMPLEVIEWER_IMPLEMENTATION
|
||||
|
||||
#include "app/Config.h"
|
||||
#include "app/FilamentApp.h"
|
||||
#include "app/IBL.h"
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/AssetPipeline.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#include <gltfio/ResourceLoader.h>
|
||||
#include <gltfio/SimpleViewer.h>
|
||||
|
||||
#include <image/LinearImage.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
|
||||
#include <getopt/getopt.h>
|
||||
|
||||
#include <utils/NameComponentManager.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "generated/resources/gltf.h"
|
||||
#include "generated/resources/resources.h"
|
||||
|
||||
using namespace filament;
|
||||
using namespace gltfio;
|
||||
using namespace utils;
|
||||
|
||||
enum AppState {
|
||||
EMPTY,
|
||||
LOADED,
|
||||
RENDERING,
|
||||
PREPPING,
|
||||
PREPPED,
|
||||
BAKING,
|
||||
BAKED,
|
||||
};
|
||||
|
||||
struct App {
|
||||
Engine* engine;
|
||||
Camera* camera;
|
||||
SimpleViewer* viewer;
|
||||
Config config;
|
||||
AssetLoader* loader;
|
||||
FilamentAsset* asset = nullptr;
|
||||
NameComponentManager* names;
|
||||
MaterialProvider* materials;
|
||||
bool actualSize = false;
|
||||
AppState state = EMPTY;
|
||||
utils::Path filename;
|
||||
image::LinearImage renderedImage;
|
||||
bool showOverlay = false;
|
||||
View* overlayView = nullptr;
|
||||
Scene* overlayScene = nullptr;
|
||||
VertexBuffer* overlayVb = nullptr;
|
||||
IndexBuffer* overlayIb = nullptr;
|
||||
Texture* overlayTexture = nullptr;
|
||||
MaterialInstance* overlayMaterial = nullptr;
|
||||
utils::Entity overlayEntity;
|
||||
AppState pushedState;
|
||||
std::atomic<bool> requestOverlayUpdate;
|
||||
std::atomic<bool> requestStatePop;
|
||||
};
|
||||
|
||||
struct OverlayVertex {
|
||||
filament::math::float2 position;
|
||||
filament::math::float2 uv;
|
||||
};
|
||||
|
||||
static OverlayVertex OVERLAY_VERTICES[4] = {
|
||||
{{0, 0}, {0, 0}},
|
||||
{{ 1000, 0}, {1, 0}},
|
||||
{{0, 1000}, {0, 1}},
|
||||
{{ 1000, 1000}, {1, 1}},
|
||||
};
|
||||
|
||||
static const char* DEFAULT_IBL = "envs/venetian_crossroads";
|
||||
|
||||
static const char* INI_FILENAME = "gltf_baker.ini";
|
||||
|
||||
static void printUsage(char* name) {
|
||||
std::string exec_name(Path(name).getName());
|
||||
std::string usage(
|
||||
"SHOWCASE can perform AO baking on the specified glTF file. If no file is specified,"
|
||||
"it loads the most recently-used glTF file.\n"
|
||||
"Usage:\n"
|
||||
" SHOWCASE [options] [gltf path]\n"
|
||||
"Options:\n"
|
||||
" --help, -h\n"
|
||||
" Prints this message\n\n"
|
||||
" --actual-size, -s\n"
|
||||
" Do not scale the model to fit into a unit cube\n\n"
|
||||
);
|
||||
const std::string from("SHOWCASE");
|
||||
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
||||
usage.replace(pos, from.length(), exec_name);
|
||||
}
|
||||
std::cout << usage;
|
||||
}
|
||||
|
||||
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
|
||||
static constexpr const char* OPTSTR = "ha:i:us";
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "actual-size", no_argument, nullptr, 's' },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
int opt;
|
||||
int option_index = 0;
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
|
||||
std::string arg(optarg ? optarg : "");
|
||||
switch (opt) {
|
||||
default:
|
||||
case 'h':
|
||||
printUsage(argv[0]);
|
||||
exit(0);
|
||||
case 's':
|
||||
app->actualSize = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return optind;
|
||||
}
|
||||
|
||||
static std::ifstream::pos_type getFileSize(const char* filename) {
|
||||
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
|
||||
return in.tellg();
|
||||
}
|
||||
|
||||
static void saveIniFile(App& app) {
|
||||
std::ofstream out(INI_FILENAME);
|
||||
out << "[recent]\n";
|
||||
out << "filename=" << app.filename.c_str() << "\n";
|
||||
}
|
||||
|
||||
static void loadIniFile(App& app) {
|
||||
utils::Path iniPath(INI_FILENAME);
|
||||
if (!app.filename.isEmpty() || !iniPath.isFile()) {
|
||||
return;
|
||||
}
|
||||
std::ifstream infile(INI_FILENAME);
|
||||
std::string line;
|
||||
while (std::getline(infile, line)) {
|
||||
size_t sep = line.find('=');
|
||||
if (sep != std::string::npos) {
|
||||
std::string lhs = line.substr(0, sep);
|
||||
std::string rhs = line.substr(sep + 1);
|
||||
if (lhs == "filename") {
|
||||
utils::Path gltf = rhs;
|
||||
if (gltf.isFile()) {
|
||||
app.filename = rhs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void updateOverlayVerts(App& app) {
|
||||
auto viewportSize = ImGui::GetIO().DisplaySize;
|
||||
viewportSize.x -= app.viewer->getSidebarWidth();
|
||||
OVERLAY_VERTICES[0].position.x = app.viewer->getSidebarWidth();
|
||||
OVERLAY_VERTICES[2].position.x = app.viewer->getSidebarWidth();
|
||||
OVERLAY_VERTICES[1].position.x = app.viewer->getSidebarWidth() + viewportSize.x;
|
||||
OVERLAY_VERTICES[3].position.x = app.viewer->getSidebarWidth() + viewportSize.x;
|
||||
OVERLAY_VERTICES[2].position.y = viewportSize.y;
|
||||
OVERLAY_VERTICES[3].position.y = viewportSize.y;
|
||||
};
|
||||
|
||||
static void updateOverlay(App& app) {
|
||||
auto& rcm = app.engine->getRenderableManager();
|
||||
auto vb = app.overlayVb;
|
||||
auto ib = app.overlayIb;
|
||||
updateOverlayVerts(app);
|
||||
vb->setBufferAt(*app.engine, 0,
|
||||
VertexBuffer::BufferDescriptor(OVERLAY_VERTICES, 64, nullptr));
|
||||
rcm.destroy(app.overlayEntity);
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{ 0, 0, 0 }, { 1000, 1000, 1 }})
|
||||
.material(0, app.overlayMaterial)
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, 6)
|
||||
.culling(false)
|
||||
.receiveShadows(false)
|
||||
.castShadows(false)
|
||||
.build(*app.engine, app.overlayEntity);
|
||||
}
|
||||
|
||||
static void updateOverlayTexture(App& app) {
|
||||
Engine& engine = *app.engine;
|
||||
int w = app.renderedImage.getWidth();
|
||||
int h = app.renderedImage.getHeight();
|
||||
void* data = app.renderedImage.getPixelRef();
|
||||
Texture::PixelBufferDescriptor buffer(data, size_t(w * h * 4),
|
||||
Texture::Format::R, Texture::Type::FLOAT);
|
||||
app.overlayTexture->setImage(engine, 0, std::move(buffer));
|
||||
}
|
||||
|
||||
static void createOverlayTexture(App& app) {
|
||||
Engine& engine = *app.engine;
|
||||
using MinFilter = TextureSampler::MinFilter;
|
||||
using MagFilter = TextureSampler::MagFilter;
|
||||
|
||||
int w = app.renderedImage.getWidth();
|
||||
int h = app.renderedImage.getHeight();
|
||||
void* data = app.renderedImage.getPixelRef();
|
||||
Texture::PixelBufferDescriptor buffer(data, size_t(w * h * 4),
|
||||
Texture::Format::R, Texture::Type::FLOAT);
|
||||
auto tex = Texture::Builder()
|
||||
.width(uint32_t(w))
|
||||
.height(uint32_t(h))
|
||||
.levels(1)
|
||||
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||
.format(Texture::InternalFormat::R8)
|
||||
.build(engine);
|
||||
tex->setImage(engine, 0, std::move(buffer));
|
||||
|
||||
TextureSampler sampler(MinFilter::LINEAR, MagFilter::LINEAR);
|
||||
app.overlayMaterial->setParameter("luma", tex, sampler);
|
||||
|
||||
engine.destroy(app.overlayTexture);
|
||||
app.overlayTexture = tex;
|
||||
}
|
||||
|
||||
static void createOverlay(App& app) {
|
||||
Engine& engine = *app.engine;
|
||||
|
||||
static constexpr uint16_t OVERLAY_INDICES[6] = { 0, 1, 2, 3, 2, 1 };
|
||||
|
||||
auto vb = VertexBuffer::Builder()
|
||||
.vertexCount(4)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 16)
|
||||
.attribute(VertexAttribute::UV0, 0, VertexBuffer::AttributeType::FLOAT2, 8, 16)
|
||||
.build(engine);
|
||||
auto ib = IndexBuffer::Builder()
|
||||
.indexCount(6)
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(engine);
|
||||
ib->setBuffer(engine,
|
||||
IndexBuffer::BufferDescriptor(OVERLAY_INDICES, 12, nullptr));
|
||||
auto mat = Material::Builder()
|
||||
.package(RESOURCES_AOPREVIEW_DATA, RESOURCES_AOPREVIEW_SIZE)
|
||||
.build(engine);
|
||||
auto matInstance = mat->createInstance();
|
||||
|
||||
app.overlayVb = vb;
|
||||
app.overlayIb = ib;
|
||||
app.overlayEntity = EntityManager::get().create();
|
||||
app.overlayMaterial = matInstance;
|
||||
}
|
||||
|
||||
static void loadAsset(App& app) {
|
||||
std::cout << "Loading " << app.filename << "..." << std::endl;
|
||||
|
||||
if (app.filename.getExtension() == "glb") {
|
||||
std::cerr << "GLB files are not yet supported." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Peek at the file size to allow pre-allocation.
|
||||
long contentSize = static_cast<long>(getFileSize(app.filename.c_str()));
|
||||
if (contentSize <= 0) {
|
||||
std::cerr << "Unable to open " << app.filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Consume the glTF file.
|
||||
std::ifstream in(app.filename.c_str(), std::ifstream::in);
|
||||
std::vector<uint8_t> buffer(static_cast<unsigned long>(contentSize));
|
||||
if (!in.read((char*) buffer.data(), contentSize)) {
|
||||
std::cerr << "Unable to read " << app.filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Parse the glTF file and create Filament entities.
|
||||
app.asset = app.loader->createAssetFromJson(buffer.data(), buffer.size());
|
||||
buffer.clear();
|
||||
buffer.shrink_to_fit();
|
||||
|
||||
if (!app.asset) {
|
||||
std::cerr << "Unable to parse " << app.filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Load external textures and buffers.
|
||||
gltfio::ResourceLoader({
|
||||
.engine = app.engine,
|
||||
.gltfPath = app.filename.getAbsolutePath(),
|
||||
.normalizeSkinningWeights = true,
|
||||
.recomputeBoundingBoxes = false
|
||||
}).loadResources(app.asset);
|
||||
|
||||
// Load animation data then free the source hierarchy.
|
||||
app.asset->getAnimator();
|
||||
app.asset->releaseSourceData();
|
||||
|
||||
// Add the renderables to the scene.
|
||||
app.viewer->setAsset(app.asset, app.names, !app.actualSize);
|
||||
|
||||
app.viewer->setIndirectLight(FilamentApp::get().getIBL()->getIndirectLight());
|
||||
|
||||
app.state = LOADED;
|
||||
}
|
||||
|
||||
static void renderAsset(App& app) {
|
||||
app.pushedState = app.state;
|
||||
app.state = RENDERING;
|
||||
|
||||
// Reload the asset from disk because we haven't bothered to retain it.
|
||||
gltfio::AssetPipeline pipeline;
|
||||
gltfio::AssetPipeline::AssetHandle asset = pipeline.load(app.filename);
|
||||
if (!asset) {
|
||||
std::cerr << "Unable to read " << app.filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Allocate the render target for the path tracer as well as a GPU texture to display it.
|
||||
auto viewportSize = ImGui::GetIO().DisplaySize;
|
||||
viewportSize.x -= app.viewer->getSidebarWidth();
|
||||
image::LinearImage image(viewportSize.x, viewportSize.y, 1);
|
||||
app.renderedImage = image;
|
||||
app.showOverlay = true;
|
||||
createOverlayTexture(app);
|
||||
|
||||
// Compute the camera paramaeters for the path tracer.
|
||||
// ---------------------------------------------------
|
||||
// The path tracer does not know about the top-level Filament transform that we use to fit the
|
||||
// model into a unit cube (see the -s option), so here we do little trick by temporarily
|
||||
// transforming the Filament camera before grabbing its lookAt vectors.
|
||||
auto& tcm = app.engine->getTransformManager();
|
||||
auto root = tcm.getInstance(app.asset->getRoot());
|
||||
auto cam = tcm.getInstance(app.camera->getEntity());
|
||||
filament::math::mat4f prev = tcm.getTransform(root);
|
||||
tcm.setTransform(root, inverse(prev));
|
||||
tcm.setParent(cam, root);
|
||||
SimpleCamera camera = {
|
||||
.aspectRatio = viewportSize.x / viewportSize.y,
|
||||
.eyePosition = app.camera->getPosition(),
|
||||
.targetPosition = app.camera->getPosition() + app.camera->getForwardVector(),
|
||||
.upVector = app.camera->getUpVector(),
|
||||
.vfovDegrees = 45, // NOTE: fov is not queryable, must match with FilamentApp
|
||||
};
|
||||
tcm.setParent(cam, {});
|
||||
tcm.setTransform(root, prev);
|
||||
|
||||
// Finally, set up some callbacks and invoke the path tracer.
|
||||
using filament::math::ushort2;
|
||||
auto onRenderTile = [](image::LinearImage, ushort2, ushort2, void* userData) {
|
||||
App* app = (App*) userData;
|
||||
app->requestOverlayUpdate = true;
|
||||
};
|
||||
auto onRenderDone = [](image::LinearImage image, void* userData) {
|
||||
App* app = (App*) userData;
|
||||
app->requestStatePop = true;
|
||||
};
|
||||
pipeline.renderAmbientOcclusion(asset, image, camera, onRenderTile, onRenderDone, &app);
|
||||
}
|
||||
|
||||
static void prepAsset(App& app) {
|
||||
app.state = PREPPING;
|
||||
|
||||
std::cout << "Prepping..." << std::endl;
|
||||
|
||||
gltfio::AssetPipeline pipeline;
|
||||
gltfio::AssetPipeline::AssetHandle asset = pipeline.load(app.filename);
|
||||
if (!asset) {
|
||||
std::cerr << "Unable to read " << app.filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
asset = pipeline.flatten(asset);
|
||||
if (!asset) {
|
||||
std::cerr << "Unable to flatten " << app.filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
asset = pipeline.parameterize(asset);
|
||||
if (!asset) {
|
||||
std::cerr << "Unable to parameterize " << app.filename << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const utils::Path folder = app.filename.getAbsolutePath().getParent();
|
||||
const utils::Path binPath = folder + "baked.bin";
|
||||
const utils::Path outPath = folder + "baked.gltf";
|
||||
|
||||
pipeline.save(asset, outPath, binPath);
|
||||
std::cout << "Generated " << outPath << " and " << binPath << std::endl;
|
||||
|
||||
app.filename = outPath;
|
||||
loadAsset(app);
|
||||
|
||||
app.state = PREPPED;
|
||||
}
|
||||
|
||||
static void bakeAsset(App& app) {
|
||||
app.state = BAKING;
|
||||
std::cout << "Baking..." << std::endl;
|
||||
|
||||
gltfio::AssetPipeline pipeline;
|
||||
gltfio::AssetPipeline::AssetHandle asset = pipeline.load(app.filename);
|
||||
|
||||
using filament::math::ushort2;
|
||||
image::LinearImage image(1024, 1024, 1);
|
||||
|
||||
auto onRenderTile = [](image::LinearImage, ushort2, ushort2, void* userData) {
|
||||
App* app = (App*) userData;
|
||||
app->requestOverlayUpdate = true;
|
||||
};
|
||||
auto onRenderDone = [](image::LinearImage image, void* userData) {
|
||||
App* app = (App*) userData;
|
||||
app->requestStatePop = true;
|
||||
};
|
||||
pipeline.bakeAmbientOcclusion(asset, image, onRenderTile, onRenderDone, &app);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
App app;
|
||||
|
||||
app.config.title = "gltf_baker";
|
||||
app.config.iblDirectory = FilamentApp::getRootPath() + DEFAULT_IBL;
|
||||
app.requestOverlayUpdate = false;
|
||||
app.requestStatePop = false;
|
||||
|
||||
int option_index = handleCommandLineArguments(argc, argv, &app);
|
||||
int num_args = argc - option_index;
|
||||
if (num_args >= 1) {
|
||||
app.filename = argv[option_index];
|
||||
if (!app.filename.exists()) {
|
||||
std::cerr << "file " << app.filename << " not found!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (app.filename.isDirectory()) {
|
||||
auto files = app.filename.listContents();
|
||||
for (auto file : files) {
|
||||
if (file.getExtension() == "gltf") {
|
||||
app.filename = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (app.filename.isDirectory()) {
|
||||
std::cerr << "no glTF file found in " << app.filename << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadIniFile(app);
|
||||
|
||||
auto setup = [&](Engine* engine, View* view, Scene* scene) {
|
||||
app.engine = engine;
|
||||
app.names = new NameComponentManager(EntityManager::get());
|
||||
app.viewer = new SimpleViewer(engine, scene, view, SimpleViewer::FLAG_COLLAPSED);
|
||||
app.materials = createMaterialGenerator(engine);
|
||||
app.loader = AssetLoader::create({engine, app.materials, app.names });
|
||||
app.camera = &view->getCamera();
|
||||
|
||||
if (!app.filename.isEmpty()) {
|
||||
loadAsset(app);
|
||||
saveIniFile(app);
|
||||
}
|
||||
|
||||
createOverlay(app);
|
||||
|
||||
app.viewer->setUiCallback([&app, scene] () {
|
||||
const ImVec4 disabled = ImGui::GetStyle().Colors[ImGuiCol_TextDisabled];
|
||||
const ImVec4 enabled = ImGui::GetStyle().Colors[ImGuiCol_Text];
|
||||
|
||||
// Prep action (flattening and parameterizing).
|
||||
const bool canPrep = app.state == LOADED;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, canPrep ? enabled : disabled);
|
||||
if (ImGui::Button("Prep", ImVec2(100, 50)) && canPrep) {
|
||||
prepAsset(app);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Flattens the asset and generates a new set of UV coordinates.");
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
|
||||
// Render action (invokes path tracer).
|
||||
const bool canRender = app.state == PREPPED;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, canRender ? enabled : disabled);
|
||||
if (ImGui::Button("Render", ImVec2(100, 50)) && canRender) {
|
||||
renderAsset(app);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Renders the asset using a pathtracer.");
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
|
||||
// Bake action (invokes path tracer).
|
||||
const bool canBake = app.state == PREPPED;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, canBake ? enabled : disabled);
|
||||
if (ImGui::Button("Bake", ImVec2(100, 50)) && canBake) {
|
||||
bakeAsset(app);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Invokes an embree-based pathtracer.");
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (app.renderedImage) {
|
||||
ImGui::Checkbox("Show embree result", &app.showOverlay);
|
||||
}
|
||||
});
|
||||
|
||||
// Leave FXAA enabled but we also enable MSAA for a nice result. The wireframe looks
|
||||
// much better with MSAA enabled.
|
||||
view->setSampleCount(4);
|
||||
};
|
||||
|
||||
auto cleanup = [&app](Engine* engine, View*, Scene*) {
|
||||
Fence::waitAndDestroy(engine->createFence());
|
||||
delete app.viewer;
|
||||
app.loader->destroyAsset(app.asset);
|
||||
app.materials->destroyMaterials();
|
||||
delete app.materials;
|
||||
AssetLoader::destroy(&app.loader);
|
||||
delete app.names;
|
||||
};
|
||||
|
||||
auto animate = [&app](Engine* engine, View* view, double now) {
|
||||
if (app.state != EMPTY) {
|
||||
app.viewer->applyAnimation(now);
|
||||
}
|
||||
if (!app.overlayScene && app.showOverlay) {
|
||||
app.overlayView = FilamentApp::get().getGuiView();
|
||||
app.overlayScene = app.overlayView->getScene();
|
||||
}
|
||||
if (app.overlayScene) {
|
||||
app.overlayScene->remove(app.overlayEntity);
|
||||
if (app.showOverlay) {
|
||||
updateOverlay(app);
|
||||
app.overlayScene->addEntity(app.overlayEntity);
|
||||
}
|
||||
}
|
||||
if (app.requestOverlayUpdate) {
|
||||
updateOverlayTexture(app);
|
||||
app.requestOverlayUpdate = false;
|
||||
}
|
||||
if (app.requestStatePop) {
|
||||
app.state = app.pushedState;
|
||||
app.pushedState = EMPTY;
|
||||
app.requestStatePop = false;
|
||||
}
|
||||
};
|
||||
|
||||
auto gui = [&app](filament::Engine* engine, filament::View* view) {
|
||||
app.viewer->updateUserInterface();
|
||||
FilamentApp::get().setSidebarWidth(app.viewer->getSidebarWidth());
|
||||
};
|
||||
|
||||
FilamentApp& filamentApp = FilamentApp::get();
|
||||
filamentApp.animate(animate);
|
||||
|
||||
filamentApp.setDropHandler([&] (std::string path) {
|
||||
app.viewer->removeAsset();
|
||||
app.loader->destroyAsset(app.asset);
|
||||
app.filename = path;
|
||||
loadAsset(app);
|
||||
saveIniFile(app);
|
||||
});
|
||||
|
||||
filamentApp.run(app.config, setup, cleanup, gui);
|
||||
|
||||
return 0;
|
||||
}
|
||||
27
samples/materials/aoPreview.mat
Normal file
27
samples/materials/aoPreview.mat
Normal file
@@ -0,0 +1,27 @@
|
||||
material {
|
||||
name : aoPreview,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2d,
|
||||
name : luma
|
||||
}
|
||||
],
|
||||
requires : [
|
||||
uv0
|
||||
],
|
||||
shadingModel : unlit,
|
||||
culling : none,
|
||||
depthCulling: false,
|
||||
blending : transparent
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
float L = texture(materialParams_luma, getUV0()).r;
|
||||
if (L == 0.0) {
|
||||
discard;
|
||||
}
|
||||
material.baseColor.rgb = vec3(L);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user