Skip to content

Commit 768080c

Browse files
authored
Scene Tree (#36)
* Add scene tree (prototype) * Formalize editor::SceneTree - Stateless: user must track `Inspectee` across calls. - Requires existing `Window`.
1 parent 5556025 commit 768080c

File tree

14 files changed

+173
-36
lines changed

14 files changed

+173
-36
lines changed

facade-lib/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ target_sources(${PROJECT_NAME} PRIVATE
117117
include/facade/scene/id.hpp
118118
include/facade/scene/lights.hpp
119119
include/facade/scene/material.hpp
120+
include/facade/scene/node_data.hpp
120121
include/facade/scene/node.hpp
121122
include/facade/scene/rect.hpp
122123
include/facade/scene/renderer.hpp
@@ -128,6 +129,7 @@ target_sources(${PROJECT_NAME} PRIVATE
128129
include/facade/editor/common.hpp
129130
include/facade/editor/inspector.hpp
130131
include/facade/editor/log.hpp
132+
include/facade/editor/scene_tree.hpp
131133

132134
src/util/data_provider.cpp
133135
src/util/geometry.cpp
@@ -171,4 +173,5 @@ target_sources(${PROJECT_NAME} PRIVATE
171173
src/editor/common.cpp
172174
src/editor/inspector.cpp
173175
src/editor/log.cpp
176+
src/editor/scene_tree.cpp
174177
)

facade-lib/include/facade/editor/inspector.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Scene;
1010

1111
namespace editor {
1212
///
13-
/// \brief ImGui helper to inspect / reflect various properties
13+
/// \brief Stateless ImGui helper to inspect / reflect various properties
1414
///
1515
class Inspector {
1616
public:
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
#include <facade/editor/common.hpp>
3+
#include <facade/scene/id.hpp>
4+
#include <facade/util/fixed_string.hpp>
5+
6+
namespace facade {
7+
class Scene;
8+
class Node;
9+
10+
namespace editor {
11+
///
12+
/// \brief Target Node to inspect (driven by SceneTree)
13+
///
14+
struct Inspectee {
15+
FixedString<128> name{"[Node]###Node"};
16+
Id<Node> id{};
17+
18+
explicit operator bool() const { return id > Id<Node>{}; }
19+
};
20+
21+
///
22+
/// \brief Stateless wrapper to display interactive scene tree and pick Nodes to inspect
23+
///
24+
class SceneTree : public Pinned {
25+
public:
26+
SceneTree(Scene& scene) : m_scene(scene) {}
27+
28+
///
29+
/// \brief Render the scene tree into the passed window
30+
/// \param inout Used to highlight matching Node, and set to clicked Node, if any
31+
/// \returns true if inout changed during tree walk
32+
///
33+
bool render(NotClosed<Window>, Inspectee& inout) const;
34+
35+
private:
36+
Scene& m_scene;
37+
};
38+
} // namespace editor
39+
} // namespace facade

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Scene;
1010
struct Window;
1111

1212
struct EngineCreateInfo {
13-
glm::uvec2 extent{800, 800};
13+
glm::uvec2 extent{1280, 800};
1414
char const* title{"facade"};
1515
std::uint8_t msaa_samples{2};
1616
bool auto_show{false};

facade-lib/include/facade/scene/node.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <facade/scene/transform.hpp>
44
#include <facade/util/type_id.hpp>
55
#include <memory>
6+
#include <span>
67
#include <unordered_map>
78
#include <vector>
89

@@ -36,8 +37,12 @@ class Node {
3637

3738
Id<Node> id() const { return m_id; }
3839

40+
std::span<Node> children() { return m_children; }
41+
std::span<Node const> children() const { return m_children; }
42+
3943
Transform transform{};
4044
std::vector<Transform> instances{};
45+
std::string name{};
4146

4247
private:
4348
struct Hasher {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
#include <facade/scene/transform.hpp>
3+
#include <string>
4+
#include <vector>
5+
6+
namespace facade {
7+
struct NodeData {
8+
enum class Type { eNone, eMesh, eCamera };
9+
10+
std::string name{};
11+
Transform transform{};
12+
std::vector<std::size_t> children{};
13+
std::size_t index{};
14+
Type type{};
15+
};
16+
} // namespace facade

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <facade/scene/lights.hpp>
55
#include <facade/scene/material.hpp>
66
#include <facade/scene/node.hpp>
7+
#include <facade/scene/node_data.hpp>
78
#include <facade/scene/transform.hpp>
89
#include <facade/util/enum_array.hpp>
910
#include <facade/util/image.hpp>
@@ -59,6 +60,8 @@ class Scene {
5960
Node* find_node(Id<Node> id);
6061
Material* find_material(Id<Material> id) const;
6162
Mesh const* find_mesh(Id<Mesh> id) const;
63+
std::span<Node> roots() { return m_tree.roots; }
64+
std::span<Node const> roots() const { return m_tree.roots; }
6265

6366
std::size_t camera_count() const { return m_storage.cameras.size(); }
6467
bool select_camera(Id<Camera> id);
@@ -75,15 +78,6 @@ class Scene {
7578
private:
7679
struct TreeBuilder;
7780

78-
struct NodeData {
79-
enum class Type { eNone, eMesh, eCamera };
80-
81-
Transform transform{};
82-
std::vector<std::size_t> children{};
83-
std::size_t index{};
84-
Type type{};
85-
};
86-
8781
struct Tree {
8882
struct Data {
8983
std::vector<std::size_t> roots{};

facade-lib/include/facade/util/fixed_string.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ class FixedString {
2525

2626
template <std::size_t N>
2727
void append(FixedString<N> const& rhs) {
28-
auto const dsize = std::min(Capacity - m_size, rhs.m_size);
29-
std::memcpy(m_buffer + m_size, rhs.m_buffer, dsize);
28+
auto const dsize = std::min(Capacity - m_size, rhs.size());
29+
std::memcpy(m_buffer + m_size, rhs.data(), dsize);
3030
m_size += dsize;
3131
}
3232

facade-lib/src/detail/gltf.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ Asset Asset::parse(dj::Json const& json, DataProvider const& provider) {
605605
type = camera ? Node::Type::eCamera : Node::Type::eMesh;
606606
index = camera ? camera.as<std::size_t>() : mesh.as<std::size_t>();
607607
}
608-
ret.nodes.push_back(Node{transform(node), children(node["children"]), index, type});
608+
ret.nodes.push_back(Node{node["name"].as<std::string>(), transform(node), children(node["children"]), index, type});
609609
}
610610

611611
auto const& scenes = json["scenes"].array_view();

facade-lib/src/detail/gltf.hpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#pragma once
2+
#include <facade/scene/node_data.hpp>
23
#include <facade/scene/transform.hpp>
34
#include <facade/util/byte_buffer.hpp>
45
#include <facade/util/colour_space.hpp>
@@ -126,14 +127,7 @@ struct Texture {
126127
ColourSpace colour_space{ColourSpace::eSrgb};
127128
};
128129

129-
struct Node {
130-
enum class Type { eNone, eMesh, eCamera };
131-
132-
Transform transform{};
133-
std::vector<std::size_t> children{};
134-
std::size_t index{};
135-
Type type{};
136-
};
130+
using Node = NodeData;
137131

138132
struct Scene {
139133
std::vector<std::size_t> root_nodes{};

facade-lib/src/editor/inspector.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ bool Inspector::inspect(char const* label, glm::quat& out_quat) const {
6363

6464
bool Inspector::inspect(Transform& out_transform) const {
6565
auto ret = Modified{};
66+
ImGui::Separator();
6667
if (auto tn = TreeNode{"Transform"}) {
6768
auto vec3 = out_transform.position();
6869
if (ret(inspect("Position", vec3))) { out_transform.set_position(vec3); }
@@ -117,6 +118,7 @@ bool SceneInspector::inspect(Id<Mesh> mesh_id) const {
117118
auto ret = Modified{};
118119
auto* mesh = m_scene.find_mesh(mesh_id);
119120
if (!mesh) { return ret.value; }
121+
ImGui::Separator();
120122
if (auto tn = TreeNode{FixedString{"Mesh ({})", mesh_id}.c_str()})
121123
for (std::size_t i = 0; i < mesh->primitives.size(); ++i) {
122124
if (auto tn = TreeNode{FixedString{"Primitive {}", i}.c_str()}) {

facade-lib/src/editor/scene_tree.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <imgui.h>
2+
#include <facade/editor/scene_tree.hpp>
3+
#include <facade/scene/scene.hpp>
4+
5+
namespace facade::editor {
6+
namespace {
7+
FixedString<128> node_name(Node const& node) {
8+
auto ret = FixedString<128>{node.name};
9+
if (ret.empty()) { ret = "(Unnamed)"; }
10+
ret += FixedString{" ({})", node.id()};
11+
return ret;
12+
}
13+
14+
Inspectee inspect(Node const& node) {
15+
auto ret = Inspectee{};
16+
ret.id = node.id();
17+
ret.name = node_name(node);
18+
ret.name += FixedString{"###Node"};
19+
return ret;
20+
}
21+
22+
void walk(Node& node, Inspectee& inout) {
23+
auto flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth;
24+
if (node.id() == inout.id) { flags |= ImGuiTreeNodeFlags_Selected; }
25+
if (node.children().empty()) { flags |= ImGuiTreeNodeFlags_Leaf; }
26+
auto tn = editor::TreeNode{node_name(node).c_str(), flags};
27+
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
28+
inout = inspect(node);
29+
} else if ((flags & ImGuiTreeNodeFlags_Selected) && ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
30+
inout = {};
31+
}
32+
if (tn) {
33+
for (auto& child : node.children()) { walk(child, inout); }
34+
}
35+
}
36+
} // namespace
37+
38+
bool SceneTree::render(NotClosed<Window>, Inspectee& inout) const {
39+
auto const in = inout.id;
40+
if (!m_scene.find_node(in)) { inout = {}; }
41+
for (auto& root : m_scene.roots()) { walk(root, inout); }
42+
return inout.id != in;
43+
}
44+
} // namespace facade::editor

facade-lib/src/scene/scene.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ struct Scene::TreeBuilder {
109109

110110
void add(NodeData const& child, std::vector<Node>& out_children) {
111111
auto node = Node{};
112+
node.name = child.name;
112113
node.transform = child.transform;
113114
bool set_cam{};
114115
switch (child.type) {
@@ -176,9 +177,7 @@ bool Scene::load_gltf(dj::Json const& root, DataProvider const& provider) noexce
176177
m_storage.textures.push_back(to_texture(m_gfx, get_sampler(texture.sampler), m_storage.images.at(texture.source), texture));
177178
}
178179

179-
for (auto& node : asset.nodes) {
180-
m_storage.data.nodes.push_back(NodeData{node.transform, std::move(node.children), node.index, static_cast<NodeData::Type>(node.type)});
181-
}
180+
m_storage.data.nodes = std::move(asset.nodes);
182181
for (auto& scene : asset.scenes) { m_storage.data.trees.push_back(Tree::Data{.roots = std::move(scene.root_nodes)}); }
183182

184183
return load(asset.start_scene);

src/main.cpp

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <facade/editor/inspector.hpp>
2121
#include <facade/editor/log.hpp>
22+
#include <facade/editor/scene_tree.hpp>
2223

2324
#include <bin/shaders.hpp>
2425

@@ -118,20 +119,17 @@ static constexpr auto test_json_v = R"(
118119

119120
struct MainMenu {
120121
struct {
121-
bool inspector{};
122122
bool stats{};
123+
bool tree{};
123124
} windows{};
124125

125126
editor::Log log{};
127+
struct {
128+
FixedString<128> name{};
129+
Id<Node> id{};
130+
} inspecting{};
126131

127-
void inspector(Scene& scene) {
128-
ImGui::SetNextWindowSize({250.0f, 100.0f}, ImGuiCond_Once);
129-
if (auto window = editor::Window{"Node", &windows.inspector}) {
130-
static auto s_input = int{};
131-
ImGui::InputInt("Id", &s_input);
132-
editor::SceneInspector{window, scene}.inspect(Id<Node>{static_cast<std::size_t>(s_input)});
133-
}
134-
}
132+
editor::Inspectee inspectee{};
135133

136134
static constexpr std::string_view vsync_status(vk::PresentModeKHR const mode) {
137135
switch (mode) {
@@ -160,6 +158,13 @@ struct MainMenu {
160158
logger::info("Requesting present mode: [{}]", present_mode_str(next_mode));
161159
}
162160

161+
void inspector(Scene& scene) {
162+
bool show = true;
163+
ImGui::SetNextWindowSize({400.0f, 400.0f}, ImGuiCond_Once);
164+
if (auto window = editor::Window{inspectee.name.c_str(), &show}) { editor::SceneInspector{window, scene}.inspect(inspectee.id); }
165+
if (!show) { inspectee = {}; }
166+
}
167+
163168
void stats(Engine const& engine, float const dt) {
164169
ImGui::SetNextWindowSize({200.0f, 200.0f}, ImGuiCond_Once);
165170
if (auto window = editor::Window{"Frame Stats", &windows.stats}) {
@@ -175,22 +180,58 @@ struct MainMenu {
175180
}
176181
}
177182

183+
void tree(Scene& scene) {
184+
ImGui::SetNextWindowSize({250.0f, 350.0f}, ImGuiCond_Once);
185+
if (auto window = editor::Window{"Scene", &windows.tree}) { editor::SceneTree{scene}.render(window, inspectee); }
186+
}
187+
188+
static FixedString<128> node_name(Node const& node) {
189+
auto ret = FixedString<128>{node.name};
190+
if (ret.empty()) { ret = "(Unnamed)"; }
191+
ret += FixedString{" ({})", node.id()};
192+
return ret;
193+
}
194+
195+
void mark_inspect(Node const& node) {
196+
inspecting.id = node.id();
197+
inspecting.name = node_name(node);
198+
inspecting.name += FixedString{"###Node"};
199+
}
200+
201+
void uninspect() { inspecting = {.name = "[Node]###Node"}; }
202+
203+
void walk(Node& node) {
204+
auto flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth;
205+
if (node.id() == inspecting.id) { flags |= ImGuiTreeNodeFlags_Selected; }
206+
if (node.children().empty()) { flags |= ImGuiTreeNodeFlags_Leaf; }
207+
auto tn = editor::TreeNode{node_name(node).c_str(), flags};
208+
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
209+
mark_inspect(node);
210+
} else if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
211+
uninspect();
212+
}
213+
if (tn) {
214+
for (auto& child : node.children()) { walk(child); }
215+
}
216+
}
217+
178218
void display(Engine& engine, Scene& scene, float const dt) {
179219
if (auto main = editor::MainMenu{}) {
180220
if (auto file = editor::Menu{main, "File"}) {
181221
ImGui::Separator();
182222
if (ImGui::MenuItem("Exit")) { engine.request_stop(); }
183223
}
184224
if (auto window = editor::Menu{main, "Window"}) {
185-
if (ImGui::MenuItem("Inspect")) { windows.inspector = true; }
225+
if (ImGui::MenuItem("Tree")) { windows.tree = true; }
186226
if (ImGui::MenuItem("Stats")) { windows.stats = true; }
187227
if (ImGui::MenuItem("Log")) { log.show = true; }
188228
ImGui::Separator();
189229
if (ImGui::MenuItem("Close All")) { windows = {}; }
190230
}
191231
}
192232

193-
if (windows.inspector) { inspector(scene); }
233+
if (windows.tree) { tree(scene); }
234+
if (inspectee) { inspector(scene); }
194235
if (windows.stats) { stats(engine, dt); }
195236
if (log.show) { log.render(); }
196237
}

0 commit comments

Comments
 (0)