Commit 28419a2e authored by Johannes Braun's avatar Johannes Braun
Browse files

Added more streamlined application

parent eeacc6ae
#include "application.h"
#include <components/Rotator.h>
#include <components/PlayerController.h>
#include <components/FramerateCounter.h>
#include <core/audio/sound_buffer.h>
#include <core/audio/sound_source.h>
#include <core/res/resources.h>
#include <util/openal.h>
#include <util/console.h>
namespace glare
{
PathtracerApp::PathtracerApp()
: app::Application()
{
}
void PathtracerApp::onStart(const Arguments& arguments)
{
m_current_scene_root = files::asset("/meshes/scenery/");
//Create a skybox from cube map.
skybox().reset(core::Skybox::collectFilesFrom(files::asset("/textures/ryfjallet/")));
//initializeScene(m_current_scene_root / "TY_Plane.dae", 1.f);
//initializeScene(m_current_scene_root / "dragn.dae", 1.f);
//initializeScene(m_current_scene_root / "benchmark_stfd_bunny_diff.dae", 1.f);
initializeScene(m_current_scene_root / "cbox.dae", 1.f);
al::listenerf(al::ListenerParam::eGain, 0.02f);
auto audio_buffer = core::Resources::getInstance().getAudioBuffer(files::asset("/audio/env.wav"));
auto environment_source = core::state::graph_root->makeComponent<core::SoundSource>(audio_buffer->id());
environment_source->setLooping(true);
environment_source->play();
}
void PathtracerApp::onGui()
{
drawDebugWindow();
drawSceneSelector();
//drawLinespaceControls();
}
void PathtracerApp::onQuit()
{
}
void PathtracerApp::onResize(unsigned width, unsigned height)
{
}
void PathtracerApp::onKeyPress(controls::Key key, controls::KeyMods mods)
{
switch (key) {
case controls::Key::e1:
{
setRenderMode(app::RenderMode::eLines);
} break;
case controls::Key::e2:
{
setRenderMode(app::RenderMode::eRaw);
} break;
case controls::Key::e3:
{
setRenderMode(app::RenderMode::eGBuffer);
} break;
case controls::Key::e4:
{
setRenderMode(app::RenderMode::ePathtrace);
} break;
default: break;
}
}
void PathtracerApp::initializeScene(fs::path path, float scale)
{
//Initialize some scene graph from an obj file
core::state::graph_root = core::Resources::getInstance().getColladaFile(path, scale);
if (!core::state::graph_root)
{
core::state::quit();
glfwTerminate();
Log_Error << "Scene could not be loaded: " << path;
console::prompt(1);
}
std::shared_ptr<core::GraphNode> cam_node = core::state::graph_root->findFirst([](const core::GraphNode &node) {
return bool(node.getComponent<core::Camera>());
});
if (cam_node)
{
//If there are any cameras in the collada file, take the first one.
core::state::camera = cam_node->getComponent<core::Camera>();
}
else {
//Add a camera to render from
cam_node = std::make_shared<core::GraphNode>("custom_camera");
cam_node->addComponent(core::state::camera);
core::state::graph_root->attach(cam_node);
}
cam_node->makeComponent<component::PlayerController>();
core::state::graph_root->makeComponent<component::FramerateCounter>();
#define DEBUG__ADD_ROTATOR
#ifdef DEBUG__ADD_ROTATOR
auto node = core::state::graph_root->findFirstWithName("#Suzanne-mesh");
if (node) {
node->addComponent(std::make_shared<component::Rotator>());
}
#endif
}
void PathtracerApp::drawControlItem(std::string title, std::string description)
{
ImGui::Title(title.c_str());
ImGui::SameLine();
ImGui::TextWrapped(description.c_str());
}
void PathtracerApp::drawDebugWindow()
{
ImGui::Begin("Debug", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::BeginGroup();
static int render_state = 0;
render_state = int(renderMode());
ImGui::Title("Render Mode");
ImGui::PushID("id_render_mode");
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::Combo("", &render_state, { "OpenGL Wireframe", "OpenGL Raw Color", "OpenGL GBuffer", "Pathtracing" });
ImGui::PopItemWidth();
ImGui::PopID();
if (renderMode() != app::RenderMode(render_state)) {
glFinish();
setRenderMode(app::RenderMode(render_state));
}
ImGui::EndGroup();
if (renderMode() == app::RenderMode::ePathtrace) {
ImGui::Spacing();
ImGui::Title("Scene Settings");
ImGui::TextWrapped("By default, a transformation or mesh update will trigger a pathtracer sample count reset. If you want to limit this to explicit settings changes and camera movements, uncheck the following option.");
bool collector_active = pathtracer().collector()->isActive();
ImGui::Checkbox("Reset on scene update", &collector_active);
if (collector_active != pathtracer().collector()->isActive())
pathtracer().collector()->setActive(collector_active);
ImGui::Spacing();
ImGui::BeginGroup();
static int raygenstate = 0;
raygenstate = int(m_ray_gen_state);
ImGui::Title("Ray Generator");
ImGui::PushID("id_ray_generator");
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::Combo("", &raygenstate, { "GBuffer", "Default (BVH)" });
ImGui::PopItemWidth();
ImGui::PopID();
if (raygenstate != int(m_ray_gen_state)) {
m_ray_gen_state = AppRayGenState(raygenstate);
switch (m_ray_gen_state)
{
case AppRayGenState::eDefault:
pathtracer().setGenerator(std::make_shared<raytrace::RayGeneratorDefault>());
break;
case AppRayGenState::eGBuffer:
pathtracer().setGenerator(std::make_shared<raytrace::RayGeneratorGBuffer>());
break;
default:
break;
}
}
ImGui::EndGroup();
ImGui::Spacing();
ImGui::PushID("id_samples");
ImGui::Title("Samples");
ImGui::ProgressBar(pathtracer().currentSample() / float(pathtracer().getMaxSamples()), ImVec2(-1, 0), ("Current: " + std::to_string(pathtracer().currentSample()) + " of " + std::to_string(pathtracer().getMaxSamples())).c_str());
ImGui::Spacing();
int max_samples = pathtracer().getMaxSamples();
ImGui::DragInt("Maximum", &max_samples, 10, 1, 50000);
if (max_samples != pathtracer().getMaxSamples())
pathtracer().setMaxSamples(max_samples);
int spfr = pathtracer().getSamplesPerFrame();
ImGui::DragInt("per frame", &spfr, 0.1f, 1, 10);
if (spfr != pathtracer().getSamplesPerFrame())
pathtracer().setSamplesPerFrame(spfr);
ImGui::PopID();
ImGui::Spacing();
ImGui::Title("Bounces");
ImGui::PushID("id_bounces");
ImGui::TextWrapped("The global bounce limit clamps down all effect-dependant bounce limits to a unified maximum value.");
int bounces = pathtracer().getBounces();
ImGui::DragInt("Global Limit", &bounces, 0.1f, 0, 15);
if (bounces != pathtracer().getBounces())
pathtracer().setBounces(bounces);
if (ImGui::CollapsingHeader("Bounce Thresholds"))
{
drawEffectBounceControl("Diffuse", raytrace::EffectType::eDiffuse);
drawEffectBounceControl("Translucent", raytrace::EffectType::eTranslucent);
drawEffectBounceControl("Reflective", raytrace::EffectType::eReflection);
drawEffectBounceControl("Refractive", raytrace::EffectType::eRefraction);
drawEffectBounceControl("Transparence", raytrace::EffectType::eTransparent);
drawEffectBounceControl("Emissive", raytrace::EffectType::eEmissive);
}
ImGui::Spacing();
ImGui::TextWrapped("The direct clamp setting will lead to the primary ray result color being clamped down to a set maximum. That means each color component will be clamped. The same applies to the \"clamp indirect\" setting for non-primary bounces.");
float clamp_direct = pathtracer().getClampDirect();
ImGui::DragFloat("Clamp Direct", &clamp_direct, 0.1f, 0.0f, 100.f);
if (clamp_direct != pathtracer().getClampDirect())
pathtracer().setClampDirect(clamp_direct);
float clamp_indirect = pathtracer().getClampIndirect();
ImGui::DragFloat("Clamp Indirect", &clamp_indirect, 0.1f, 0.0f, 100.f);
if (clamp_indirect != pathtracer().getClampIndirect())
pathtracer().setClampIndirect(clamp_indirect);
ImGui::PopID();
auto &&style = ImGui::GetStyle();
auto col_bt = style.Colors[ImGuiCol_Button];
auto col_bt_h = style.Colors[ImGuiCol_ButtonHovered];
auto col_bt_a = style.Colors[ImGuiCol_ButtonActive];
auto color_bt = color::hex<color::rgba32f>("#4CAF50");
auto color_bt_h = color::hex<color::rgba32f>("#81C784");
auto color_bt_a = color::hex<color::rgba32f>("#2E7D32");
style.Colors[ImGuiCol_Button] = ImVec4(color_bt.r, color_bt.g, color_bt.b, color_bt.a);
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(color_bt_h.r, color_bt_h.g, color_bt_h.b, color_bt_h.a);
style.Colors[ImGuiCol_ButtonActive] = ImVec4(color_bt_a.r, color_bt_a.g, color_bt_a.b, color_bt_a.a);
ImGui::Spacing();
ImGui::Title("Save Render as...");
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / 2);
if (ImGui::Button("32bit HDR"))
{
pathtracer().saveRender(files::asset("/screenshots/render.hdr"));
}
ImGui::SameLine();
if (ImGui::Button("8bit PNG"))
{
pathtracer().saveRender(files::asset("/screenshots/render.png"));
}
ImGui::PopItemWidth();
style.Colors[ImGuiCol_Button] = col_bt;
style.Colors[ImGuiCol_ButtonHovered] = col_bt_h;
style.Colors[ImGuiCol_ButtonActive] = col_bt_a;
}
ImGui::Separator();
ImGui::Title("General Settings");
static int vsync = 0;
vsync = int(core::state::window->getVSync());
ImGui::Combo("VSync", &vsync, { "None", "60 FPS", "30 FPS", "20 FPS" });
if(vsync != int(core::state::window->getVSync()))
core::state::window->setVSync(VSync(vsync));
ImGui::Separator();
ImGui::Title("Controls");
drawControlItem("Right Click", "Grab cursor, enables looking around. Right click again to release it.");
drawControlItem("W/S", "Fly along the looking direction.");
drawControlItem("A/D", "Fly along the horizontal perpendiculat to the looking direction.");
drawControlItem("E", "Fly towards the relative up-direction.");
drawControlItem("Q", "Fly towards the relative down-direction.");
drawControlItem("1, 2, 3", "Toggle rendering mode.");
ImGui::End();
}
void PathtracerApp::drawSceneSelector()
{
ImGui::Begin("Scenes", nullptr, ImVec2(200, 300));
ImGui::Title(m_current_scene_root.string().c_str());
ImGui::DragFloat("Scale", &m_import_scale, 0.01f, 0.01f, 100.0f);
ImGui::BeginGroup();
if (ImGui::Button("..", ImVec2(ImGui::GetContentRegionAvailWidth(), 32)))
{
m_current_scene_root = m_current_scene_root.parent_path();
}
for (auto& p : fs::directory_iterator(m_current_scene_root)) {
fs::path path = p;
if (is_directory(path)) {
if (ImGui::Button(path.filename().string().c_str(), ImVec2(ImGui::GetContentRegionAvailWidth(), 40)))
{
m_current_scene_root = path;
}
}
else if (path.extension() == ".dae")
{
if (ImGui::Button(path.filename().string().c_str(), ImVec2(ImGui::GetContentRegionAvailWidth(), 32)))
{
initializeScene(path, m_import_scale);
pathtracer().reload(core::state::graph_root);
}
}
}
ImGui::EndGroup();
ImGui::End();
}
void PathtracerApp::drawLSBounceControl(const std::string &label, raytrace::EffectType effect)
{
int val = pathtracer().getLinespaceBounceThreshold(effect);
ImGui::DragInt(label.c_str(), &val, 0.1f, 0, pathtracer().getEffectBounces(effect));
if (val != pathtracer().getLinespaceBounceThreshold(effect))
{
pathtracer().setLinespaceBounceThresholds(effect, uint8_t(val));
}
}
void PathtracerApp::drawEffectBounceControl(const std::string &label, raytrace::EffectType effect)
{
int val = pathtracer().getEffectBounces(effect);
ImGui::DragInt(label.c_str(), &val, 0.1f, 0, 15);
if (val != pathtracer().getEffectBounces(effect))
{
pathtracer().setEffectBounces(effect, uint8_t(val));
}
}
void PathtracerApp::drawLinespaceControls()
{
ImGui::Begin("Linespace Settings", nullptr, ImVec2(200, 300));
ImGui::Title("PDF Thresholds");
ImGui::TextWrapped("The following threshold values will determine as of which accumulated PDF value the Line Space should be used instead of the BVH.");
{
float acc = pathtracer().getAccuracyThreshold();
ImGui::DragFloat("Direct", &acc, 0.001f, 0.0f, 1.f);
if (acc != pathtracer().getAccuracyThreshold())
pathtracer().setAccuracyThreshold(acc);
float shd = pathtracer().getShadowThreshold();
ImGui::DragFloat("Shadow", &shd, 0.001f, 0.0f, 1.f);
if (shd != pathtracer().getShadowThreshold())
pathtracer().setShadowThreshold(shd);
}
ImGui::Title("Distance Threshold");
ImGui::TextWrapped("Works like a level of detail slider. This is the maximum path length with which the BVH should be used.");
{
float dt = pathtracer().getDistanceThreshold();
ImGui::DragFloat("Distance", &dt, 0.1f, 0.0f, 10000.f);
if (dt != pathtracer().getDistanceThreshold())
pathtracer().setDistanceThreshold(dt);
}
ImGui::Title("Bounce Thresholds");
ImGui::TextWrapped("The following threshold values will determine as of which bounce the Line Space should be used instead of the BVH. This can vary for every kind of effect.");
{
drawLSBounceControl("Diffuse", raytrace::EffectType::eDiffuse);
drawLSBounceControl("Translucent", raytrace::EffectType::eTranslucent);
drawLSBounceControl("Reflective", raytrace::EffectType::eReflection);
drawLSBounceControl("Refractive", raytrace::EffectType::eRefraction);
drawLSBounceControl("Transparency", raytrace::EffectType::eTransparent);
drawLSBounceControl("Emissive", raytrace::EffectType::eEmissive);
}
ImGui::End();
}
}
#ifndef __APPLICATION_H
#define __APPLICATION_H
#include <app/application.h>
namespace glare
{
enum class AppRayGenState
{
eGBuffer = 0,
eDefault = 1
};
class PathtracerApp : public app::Application
{
public:
PathtracerApp();
void onStart(const Arguments& arguments) override;
void onGui() override;
void onQuit() override;
void onResize(unsigned width, unsigned height) override;
void onKeyPress(controls::Key key, controls::KeyMods mods) override;
private:
void initializeScene(fs::path path, float scale = 1);
void drawControlItem(std::string title, std::string description);
void drawDebugWindow();
void drawSceneSelector();
void drawLinespaceControls();
void drawLSBounceControl(const std::string &label, raytrace::EffectType effect);
void drawEffectBounceControl(const std::string &label, raytrace::EffectType effect);
//Scene
fs::path m_current_scene_root;
float m_import_scale = 1;
AppRayGenState m_ray_gen_state = AppRayGenState::eDefault;
};
}
#endif //__APPLICATION_H
#include "application.h"
int main(int argc, char* argv[])
{
glare::PathtracerApp app;
app.run(argc, argv);
return 0;
}
#include "application.h"
namespace glare
{
namespace app
{
Application::Application()
: m_skybox(std::make_shared<core::Skybox>())
{
}
void Application::run(int arg_count, char* arguments[])
{
m_arguments = std::make_unique<Arguments>(arg_count, arguments);
pugi::xml_document preferences_file;
preferences_file.load_file(files::asset("/preferences/default.xml").c_str());
// BASIC CONFIG
const auto config_child = preferences_file.child("settings").find_child_by_attribute("group", "name", "config");
m_app_config.window_width = config_child.find_child_by_attribute("item", "name", "window_size_x").attribute("value").as_uint(1280);
m_app_config.window_height = config_child.find_child_by_attribute("item", "name", "window_size_y").attribute("value").as_uint(720);
m_app_config.window_title = config_child.find_child_by_attribute("item", "name", "window_title").attribute("value").as_string("GLARE");
m_app_config.vsync = VSync(config_child.find_child_by_attribute("item", "name", "vsync").attribute("value").as_uint(0));
m_app_config.samples = config_child.find_child_by_attribute("item", "name", "msaa").attribute("value").as_uint(4);
core::state::initialize(m_app_config.window_width, m_app_config.window_height, m_app_config.window_title);
core::state::window->setVSync(m_app_config.vsync);
core::Callbacks::addKeyDownCallback("main_application", std::bind(&Application::keyDownCallback, this, std::placeholders::_1, std::placeholders::_2));
core::Callbacks::addFramebufferSizeCallback("main_application", std::bind(&Application::framebufferSizeChangeCallback, this, std::placeholders::_1, std::placeholders::_2));
gl::debugMessageCallback([&](gl::DebugSource source, gl::DebugType type, unsigned id, gl::DebugSeverity severity, std::string message) {
if (severity == gl::DebugSeverity::eHigh) {
switch (type)
{
case gl::DebugType::eError:
Log_Error << message;
break;
case gl::DebugType::eUndefinedBehavior:
case gl::DebugType::eDeprecatedBehavior:
case gl::DebugType::ePerformance:
case gl::DebugType::ePortability:
Log_Warn << message;
break;
case gl::DebugType::eMarker:
Log_Hint << message;
break;
case gl::DebugType::eOther:
Log_Debug << message;
break;
default:
break;
}
}
});
ImGui::glfw3::init(core::state::window->glfw(), false);
m_splash_screen = std::make_unique<core::SplashScreen>(files::asset("/preferences/splash_01.xml"));
m_splash_screen->run();
onStart(*m_arguments);
gl::setEnabled(gl::EnableParameter::eMultisample, true);
m_gbuffer = std::make_unique<core::GBuffer>(m_app_config.window_width, m_app_config.window_height, m_app_config.samples);
m_gbuffer->setSkybox(m_skybox);
m_texture_renderer = core::DefaultTextureRenderers::makeSimpleRenderer();
glare::core::state::window->loop([&](double delta)
{
//General state updates (camera and so on)
ImGui::glfw3::newFrame();
glare::core::state::update();
switch (m_render_mode)
{
case RenderMode::eLines:
{
gl::polygonMode(gl::Face::eFrontAndBack, gl::PolygonMode::eLine);
bool cull = gl::isEnabled(gl::EnableParameter::eCullFace);
gl::setEnabled(gl::EnableParameter::eCullFace, false);
if (glare::core::state::graph_root)
glare::core::state::graph_root->draw();
gl::setEnabled(gl::EnableParameter::eCullFace, cull);
gl::polygonMode(gl::Face::eFrontAndBack, gl::PolygonMode::eFill);
}
break;
case RenderMode::eRaw:
{
m_skybox->draw();
if (glare::core::state::graph_root)
glare::core::state::graph_root->draw();
}
break;
case RenderMode::eGBuffer:
{
m_gbuffer->activate();
m_skybox->draw();
if (glare::core::state::graph_root)
glare::core::state::graph_root->draw();
m_gbuffer->deactivate();
m_gbuffer->draw();
}
break;
case RenderMode::ePathtrace:
{
m_texture_renderer->draw(m_pathtracer->render(m_app_config.window_width, m_app_config.window_height));
}
break;
}
if (controls::keyState(controls::Key::eF) == controls::ButtonAction::ePress) {
Log_Info << "Current Frametime: " << core::Time::deltaTime();
}
onGui();
ImGui::Render();
core::LightManager::getInstance().clear();
glFinish();
});
}
Application::~Application()
{
onQuit();
core::state::quit();
ImGui::glfw3::shutdown();
glfwTerminate();
}
void Application::keyDownCallback(controls::Key key, controls::KeyMods mods)
{
onKeyPress(key, mods);
}
void Application::framebufferSizeChangeCallback(unsigned width, unsigned height)
{
Log_Debug << "Resized: " << width << ", " << height;
m_app_config.window_width = width;
m_app_config.window_height = height;
gl::viewport(0, 0, width, height);
if(core::state::window)