Skip to content

Commit e5a7d11

Browse files
authored
Config, Events, PathSources (#60)
* Add Config * Add Scene::pipeline_state - Enable toggling between filled and wireframe pipelines in editor (through frame stats). * Add Events - Refine load hooks, config updates, etc: wire through events. - Only update browse path in `Config` if changed. - Refactor `BrowseFile` to support above. - Prepare for unifying interface for all load path sources. * Add PathSource - Rename main_menu/ to gui/. - Move `BrowseGltf`, `OpenRecent`, `DropFile` to gltf_sources. - Consolidate events in gui/events.hpp. * Remove events/common * Move gltf_sources constructors to cpp files - Make them classes with only constructors publicly accessible. * Store weak_ptr<Events> in BrowseGltf
1 parent 861826e commit e5a7d11

File tree

22 files changed

+528
-97
lines changed

22 files changed

+528
-97
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ wiki
1313
imgui.ini
1414

1515
facade.log*
16+
facade.conf*

CMakeLists.txt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,15 @@ if(FACADE_BUILD_EXE)
5353
add_executable(${PROJECT_NAME})
5454

5555
target_sources(${PROJECT_NAME} PRIVATE
56-
src/main_menu/main_menu.cpp
57-
src/main_menu/main_menu.hpp
56+
src/config/config.cpp
57+
src/config/config.hpp
58+
src/events/events.hpp
59+
src/gui/events.hpp
60+
src/gui/gltf_sources.hpp
61+
src/gui/gltf_sources.cpp
62+
src/gui/main_menu.cpp
63+
src/gui/main_menu.hpp
64+
src/gui/path_source.hpp
5865

5966
src/main.cpp
6067

lib/engine/include/facade/engine/editor/browse_file.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44

55
namespace facade::editor {
66
struct BrowseFile {
7+
struct Result {
8+
std::string selected{};
9+
bool dir_changed{};
10+
};
11+
712
env::DirEntries& out_entries;
813
env::MatchList extensions{};
914
float parent_indent{5.0f};
1015

11-
std::string operator()(NotClosed<Popup> popup, std::string& out_path);
16+
Result operator()(NotClosed<Popup> popup, std::string& out_path);
1217
};
1318
} // namespace facade::editor

lib/engine/include/facade/engine/engine.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class Engine {
9191
///
9292
LoadStatus load_status() const;
9393

94+
glm::ivec2 window_position() const;
9495
glm::uvec2 window_extent() const;
9596
glm::uvec2 framebuffer_extent() const;
9697

lib/engine/src/editor/browse_file.cpp

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,24 @@ std::string truncate(std::string_view in, std::size_t limit) {
2525
}
2626
} // namespace
2727

28-
std::string BrowseFile::operator()(NotClosed<Popup> popup, std::string& out_path) {
29-
auto const fs_path = [&] {
30-
auto ret = fs::absolute(out_path);
31-
if (!fs::is_directory(ret)) {
32-
ret = fs::current_path();
33-
out_path = ret.generic_string();
28+
auto BrowseFile::operator()(NotClosed<Popup> popup, std::string& out_path) -> Result {
29+
auto ret = Result{};
30+
auto fs_path = [&] {
31+
auto path = fs::path{};
32+
if (!fs::is_directory(out_path)) {
33+
path = fs::current_path();
34+
out_path = path.generic_string();
35+
ret.dir_changed = true;
36+
} else {
37+
path = fs::absolute(out_path);
3438
}
35-
return ret;
39+
return path;
3640
}();
41+
auto cd = [&](fs::path path) {
42+
fs_path = std::move(path);
43+
out_path = fs_path.generic_string();
44+
ret.dir_changed = true;
45+
};
3746
auto label = out_path;
3847
auto const text_size = ImGui::CalcTextSize(label.c_str());
3948
ImGui::PushItemWidth(-50.0f);
@@ -49,39 +58,37 @@ std::string BrowseFile::operator()(NotClosed<Popup> popup, std::string& out_path
4958
auto name = p.filename().generic_string();
5059
name += "/";
5160
if (ImGui::Selectable(name.c_str(), false)) {
52-
out_path = p.generic_string();
61+
cd(std::move(p));
5362
break;
5463
}
5564
if (p.parent_path() == p) { break; }
5665
}
5766
ImGui::EndCombo();
5867
}
59-
if (fs_path.has_parent_path() && ImGui::Button("Up")) { out_path = fs_path.parent_path().generic_string(); }
68+
if (fs_path.has_parent_path() && ImGui::Button("Up")) { cd(fs_path.parent_path()); }
6069
if (auto documents = env::documents_path(); !documents.empty()) {
6170
ImGui::SameLine();
62-
if (ImGui::Button("Documents")) { out_path = std::move(documents); }
71+
if (ImGui::Button("Documents")) { cd(std::move(documents)); }
6372
}
6473
if (auto downloads = env::downloads_path(); !downloads.empty()) {
6574
ImGui::SameLine();
66-
if (ImGui::Button("Downloads")) { out_path = std::move(downloads); }
75+
if (ImGui::Button("Downloads")) { cd(std::move(downloads)); }
6776
}
6877

6978
ImGui::Separator();
7079
if (auto window = editor::Window{popup, "File Tree"}) {
7180
env::GetDirEntries{.extensions = extensions}(out_entries, out_path.c_str());
72-
if (fs_path.has_parent_path() && ImGui::Selectable("..", false, ImGuiSelectableFlags_DontClosePopups)) {
73-
out_path = fs_path.parent_path().generic_string();
74-
}
81+
if (fs_path.has_parent_path() && ImGui::Selectable("..", false, ImGuiSelectableFlags_DontClosePopups)) { cd(fs_path.parent_path()); }
7582
for (auto const& dir : out_entries.dirs) {
7683
auto filename = env::to_filename(dir);
7784
filename += "/";
78-
if (ImGui::Selectable(filename.c_str(), false, ImGuiSelectableFlags_DontClosePopups)) { out_path = dir; }
85+
if (ImGui::Selectable(filename.c_str(), false, ImGuiSelectableFlags_DontClosePopups)) { cd(dir); }
7986
}
8087
for (auto const& file : out_entries.files) {
81-
if (ImGui::Selectable(env::to_filename(file).c_str(), false, ImGuiSelectableFlags_DontClosePopups)) { return file; }
88+
if (ImGui::Selectable(env::to_filename(file).c_str(), false, ImGuiSelectableFlags_DontClosePopups)) { ret.selected = file; }
8289
}
8390
}
8491

85-
return {};
92+
return ret;
8693
}
8794
} // namespace facade::editor

lib/engine/src/engine.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ void Engine::render() {
259259

260260
void Engine::request_stop() { glfwSetWindowShouldClose(window(), GLFW_TRUE); }
261261

262+
glm::ivec2 Engine::window_position() const { return m_impl->window.window.get().position(); }
262263
glm::uvec2 Engine::window_extent() const { return m_impl->window.window.get().window_extent(); }
263264
glm::uvec2 Engine::framebuffer_extent() const { return m_impl->window.window.get().framebuffer_extent(); }
264265

lib/engine/src/scene_renderer.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ SceneRenderer::SceneRenderer(Gfx const& gfx)
4646
void SceneRenderer::render(Scene const& scene, Renderer& renderer, vk::CommandBuffer cb) {
4747
m_scene = &scene;
4848
write_view(renderer.framebuffer_extent());
49-
for (auto const& node : m_scene->m_tree.roots) { render(renderer, cb, node); }
49+
for (auto const& node : m_scene->roots()) { render(renderer, cb, node); }
5050
}
5151

5252
void SceneRenderer::write_view(glm::vec2 const extent) {
5353
auto const& cam_node = m_scene->camera();
5454
auto const* cam_id = cam_node.find<Id<Camera>>();
5555
assert(cam_id);
56-
auto const& cam = m_scene->m_storage.cameras[*cam_id];
56+
auto const& cam = m_scene->resources().cameras[*cam_id];
5757
struct {
5858
glm::mat4x4 mat_v;
5959
glm::mat4x4 mat_p;
@@ -89,19 +89,20 @@ std::span<glm::mat4x4 const> SceneRenderer::make_instances(Node const& node, glm
8989
}
9090

9191
void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 const& parent) {
92+
auto const resources = m_scene->resources();
9293
if (auto const* mesh_id = node.find<Id<Mesh>>()) {
9394
static auto const s_default_material = LitMaterial{};
94-
auto const& mesh = m_scene->m_storage.meshes.at(*mesh_id);
95+
auto const& mesh = resources.meshes[*mesh_id];
9596
for (auto const& primitive : mesh.primitives) {
96-
auto const& material =
97-
primitive.material ? *m_scene->m_storage.materials.at(primitive.material->value()) : static_cast<Material const&>(s_default_material);
98-
auto pipeline = renderer.bind_pipeline(cb, {}, material.shader_id());
97+
auto const& material = primitive.material ? *resources.materials[primitive.material->value()] : static_cast<Material const&>(s_default_material);
98+
auto pipeline = renderer.bind_pipeline(cb, m_scene->pipeline_state, material.shader_id());
99+
pipeline.set_line_width(m_scene->pipeline_state.line_width);
99100

100101
update_view(pipeline);
101-
auto const store = TextureStore{m_scene->m_storage.textures, m_white};
102+
auto const store = TextureStore{resources.textures, m_white};
102103
material.write_sets(pipeline, store);
103104

104-
auto const& static_mesh = m_scene->m_storage.static_meshes.at(primitive.static_mesh);
105+
auto const& static_mesh = resources.static_meshes[primitive.static_mesh];
105106
auto const instances = make_instances(node, parent);
106107
renderer.draw(pipeline, static_mesh, instances);
107108
}

lib/glfw/include/facade/glfw/glfw.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,25 @@ struct Glfw::Window {
8989
///
9090
static UniqueWin make();
9191

92+
///
93+
/// \brief Obtain the window position in screen coordinates.
94+
/// \returns Window position in screen coordinates
95+
///
96+
glm::ivec2 position() const;
9297
///
9398
/// \brief Obtain the extent (size) of the window.
99+
/// \returns Extent (size) of the window
94100
///
95101
glm::uvec2 window_extent() const;
96102
///
97103
/// \brief Obtain the extent (size) of the framebuffer.
104+
/// \returns Extent (size) of the framebuffer
98105
///
99106
glm::uvec2 framebuffer_extent() const;
100107

101108
///
102109
/// \brief Obtain the state since the last poll.
110+
/// \returns State since the last poll
103111
///
104112
State const& state() const;
105113

lib/glfw/src/glfw.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ std::vector<char const*> Glfw::vk_extensions() const {
9393
return ret;
9494
}
9595

96+
glm::ivec2 Glfw::Window::position() const {
97+
auto ret = glm::ivec2{};
98+
glfwGetWindowPos(win, &ret.x, &ret.y);
99+
return ret;
100+
}
101+
96102
glm::uvec2 Glfw::Window::window_extent() const {
97103
auto ret = glm::ivec2{};
98104
glfwGetWindowSize(win, &ret.x, &ret.y);

lib/scene/include/facade/scene/scene.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <facade/util/ptr.hpp>
1313
#include <facade/util/transform.hpp>
1414
#include <facade/vk/buffer.hpp>
15+
#include <facade/vk/pipeline.hpp>
1516
#include <facade/vk/static_mesh.hpp>
1617
#include <facade/vk/texture.hpp>
1718
#include <atomic>
@@ -247,6 +248,11 @@ class Scene {
247248
///
248249
Lights lights{};
249250

251+
///
252+
/// \brief Global pipeline state.
253+
///
254+
Pipeline::State pipeline_state{};
255+
250256
private:
251257
struct TreeBuilder;
252258

@@ -294,7 +300,5 @@ class Scene {
294300
Storage m_storage{};
295301
std::string m_name{};
296302
TreeImpl m_tree{};
297-
298-
friend class SceneRenderer;
299303
};
300304
} // namespace facade

lib/util/include/facade/util/unique_task.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,13 @@ class UniqueTask<Ret(Args...)> {
4040
private:
4141
struct Base {
4242
virtual ~Base() = default;
43-
virtual void operator()() = 0;
43+
virtual void operator()(Args...) = 0;
4444
};
4545
template <typename F>
4646
struct Func : Base {
4747
F f;
4848
Func(F&& f) : f(std::move(f)) {}
49-
void operator()() final { f(); }
49+
void operator()(Args... args) final { f(args...); }
5050
};
5151

5252
std::unique_ptr<Base> m_func{};

lib/vk/include/facade/vk/pipeline.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class Pipeline {
99
struct State {
1010
vk::PolygonMode mode{vk::PolygonMode::eFill};
1111
vk::PrimitiveTopology topology{vk::PrimitiveTopology::eTriangleList};
12+
float line_width{1.0f};
1213
bool depth_test{true};
1314

1415
bool operator==(State const&) const = default;

src/config/config.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#include <config/config.hpp>
2+
#include <djson/json.hpp>
3+
#include <facade/engine/engine.hpp>
4+
#include <facade/util/logger.hpp>
5+
#include <filesystem>
6+
7+
namespace facade {
8+
namespace {
9+
template <int Dim, typename T>
10+
bool to_json(dj::Json& out, glm::vec<Dim, T> const& vec) {
11+
out["x"] = vec.x;
12+
if constexpr (Dim > 1) { out["y"] = vec.y; }
13+
if constexpr (Dim > 2) { out["z"] = vec.z; }
14+
if constexpr (Dim > 3) { out["w"] = vec.w; }
15+
return true;
16+
}
17+
18+
bool to_json(dj::Json& out, Config const& config) {
19+
auto window = dj::Json{};
20+
to_json(window["extent"], config.window.extent);
21+
if (config.window.position) { to_json(window["position"], *config.window.position); }
22+
window["msaa"] = static_cast<int>(config.window.msaa);
23+
auto file_menu = dj::Json{};
24+
file_menu["browse_path"] = config.file_menu.browse_path;
25+
if (!config.file_menu.recents.empty()) {
26+
auto recents = dj::Json{};
27+
for (auto const& path : config.file_menu.recents) { recents.push_back(path); }
28+
file_menu["recents"] = std::move(recents);
29+
}
30+
out["window"] = std::move(window);
31+
out["file_menu"] = std::move(file_menu);
32+
return true;
33+
}
34+
35+
template <int Dim, typename T>
36+
bool from_json(dj::Json const& json, glm::vec<Dim, T>& out) {
37+
out.x = json["x"].as<T>();
38+
if constexpr (Dim > 1) { out.y = json["y"].as<T>(); }
39+
if constexpr (Dim > 2) { out.z = json["z"].as<T>(); }
40+
if constexpr (Dim > 3) { out.w = json["w"].as<T>(); }
41+
return true;
42+
}
43+
44+
bool from_json(dj::Json const& json, Config& out) {
45+
auto const& window = json["window"];
46+
auto extent = glm::uvec2{};
47+
from_json<2, std::uint32_t>(window, extent);
48+
if (extent.x > 0 && extent.y > 0) { out.window.extent = extent; }
49+
if (auto const& position = window["position"]) {
50+
auto value = glm::ivec2{};
51+
from_json<2, std::int32_t>(position, value);
52+
out.window.position = value;
53+
}
54+
if (auto const& msaa = window["msaa"]) { out.window.msaa = static_cast<std::uint8_t>(msaa.as<int>()); }
55+
auto const& file_menu = json["file_menu"];
56+
out.file_menu.browse_path = file_menu["browse_path"].as_string();
57+
for (auto const& path : file_menu["recents"].array_view()) { out.file_menu.recents.emplace_back(path.as_string()); }
58+
return true;
59+
}
60+
} // namespace
61+
62+
Config Config::load(char const* path) {
63+
auto ret = Config{};
64+
if (!std::filesystem::is_regular_file(path)) { return ret; }
65+
auto json = dj::Json::from_file(path);
66+
from_json(json, ret);
67+
logger::info("Loaded config from [{}]", path);
68+
return ret;
69+
}
70+
71+
bool Config::save(char const* path) const {
72+
auto json = dj::Json{};
73+
if (!to_json(json, *this)) { return false; }
74+
if (!json.to_file(path)) {
75+
logger::warn("Failed to save config to [{}]", path);
76+
return false;
77+
}
78+
logger::info("Saved config to [{}]", path);
79+
return true;
80+
}
81+
82+
void Config::Scoped::update(Engine const& engine) {
83+
config.window.extent = engine.window_extent();
84+
config.window.position = engine.window_position();
85+
}
86+
} // namespace facade

src/config/config.hpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
#include <glm/vec2.hpp>
3+
#include <optional>
4+
#include <string>
5+
#include <vector>
6+
7+
namespace facade {
8+
class Engine;
9+
10+
struct Config {
11+
class Scoped;
12+
13+
struct {
14+
glm::uvec2 extent{1280, 720};
15+
std::optional<glm::ivec2> position{};
16+
std::uint8_t msaa{};
17+
} window{};
18+
19+
struct {
20+
std::string browse_path{};
21+
std::vector<std::string> recents{};
22+
} file_menu{};
23+
24+
static Config load(char const* path);
25+
bool save(char const* path) const;
26+
};
27+
28+
class Config::Scoped {
29+
std::string m_path{};
30+
31+
public:
32+
Scoped(std::string path = "facade.conf") : m_path(std::move(path)), config(load(m_path.c_str())) {}
33+
~Scoped() { config.save(m_path.c_str()); }
34+
35+
void update(Engine const& engine);
36+
37+
Config config{};
38+
};
39+
} // namespace facade

0 commit comments

Comments
 (0)