Skip to content

Commit 7569239

Browse files
authored
Upgrade editor / inspectors, make Node Ids concurrent and never reset (#56)
* Concurrent node Ids, more inspection - Use atomic static incrementing Id for nodes in scenes: unique across all scenes. - Inspect instance transforms, if present. - Display textures in use, if any. - Add shortcut for leaf nodes: `TreeNode::leaf`. - Add enumerate. - Add cylinder, cone, arrow, manipulator geometries. - Move `Mesh` into dedicated header. - Add `Scene::Resources` as immutable view of all storage. * Add Rgb; inspectors for Rgb, nvec3, vec3 colour - Split `DirLight` into scene data vs SSBO data. - Prepare for inspecting dir lights in editor. * Inspect DirLights in editor - Put dir lights in `Lights`, inspect whole struct. - Fix `Buffer` views not using data size (instead whole size of buffer). - Fix RGB inspection: discard intensity when editing colour. - Allow adding / removing lights (at least one must remain). * Misc fixups
1 parent 6171e99 commit 7569239

File tree

28 files changed

+602
-115
lines changed

28 files changed

+602
-115
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class TreeNode : public Openable {
4949
public:
5050
explicit TreeNode(char const* label, int flags = {});
5151
~TreeNode();
52+
53+
static bool leaf(char const* label, int flags = {});
5254
};
5355

5456
///

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
#include <facade/engine/editor/common.hpp>
33
#include <facade/scene/material.hpp>
44
#include <facade/scene/node.hpp>
5+
#include <facade/util/nvec3.hpp>
6+
#include <facade/util/rgb.hpp>
57
#include <limits>
68

79
namespace facade {
810
struct Mesh;
11+
struct Lights;
912
class Scene;
1013

1114
namespace editor {
@@ -22,30 +25,37 @@ class Inspector {
2225
///
2326
/// Inspectors don't do anything on construction, constructors exist to enforce invariants instance-wide.
2427
/// For all Inspectors, an existing Window target is required, Inspector instances will not create any
25-
/// Note: target must be open
2628
///
27-
Inspector(Window const& target);
29+
Inspector(NotClosed<Window>) {}
2830

2931
bool inspect(char const* label, glm::vec2& out_vec2, float speed = 1.0f, float lo = min_v, float hi = max_v) const;
3032
bool inspect(char const* label, glm::vec3& out_vec3, float speed = 1.0f, float lo = min_v, float hi = max_v) const;
3133
bool inspect(char const* label, glm::vec4& out_vec4, float speed = 1.0f, float lo = min_v, float hi = max_v) const;
34+
bool inspect(char const* label, nvec3& out_vec3, float speed = 0.01f) const;
3235
bool inspect(char const* label, glm::quat& out_quat) const;
36+
bool inspect_rgb(char const* label, glm::vec3& out_rgb) const;
37+
bool inspect(char const* label, Rgb& out_rgb) const;
3338
bool inspect(Transform& out_transform, Bool& out_unified_scaling) const;
39+
bool inspect(std::span<Transform> out_instances, Bool unfied_scaling) const;
40+
bool inspect(Lights& out_lights) const;
41+
42+
private:
43+
bool do_inspect(Transform& out_transform, Bool& out_unified_scaling, Bool scaling_toggle) const;
3444
};
3545

3646
class SceneInspector : public Inspector {
3747
public:
3848
using Inspector::inspect;
3949

40-
SceneInspector(Window const& target, Scene& scene);
50+
SceneInspector(NotClosed<Window> target, Scene& scene);
4151

42-
bool inspect(TreeNode const& node, UnlitMaterial& out_material) const;
43-
bool inspect(TreeNode const& node, LitMaterial& out_material) const;
52+
bool inspect(NotClosed<TreeNode>, UnlitMaterial& out_material) const;
53+
bool inspect(NotClosed<TreeNode> node, LitMaterial& out_material) const;
4454
bool inspect(Id<Material> material_id) const;
45-
bool inspect(Id<Node> node_id, Bool& out_unified_scaling) const;
46-
4755
bool inspect(Id<Mesh> mesh) const;
4856

57+
bool inspect(Id<Node> node_id, Bool& out_unified_scaling) const;
58+
4959
private:
5060
Scene& m_scene;
5161
};

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace editor {
1111
///
1212
/// \brief Target Node to inspect (driven by SceneTree)
1313
///
14-
struct Inspectee {
14+
struct InspectNode {
1515
FixedString<128> name{"[Node]###Node"};
1616
Id<Node> id{};
1717

@@ -30,7 +30,7 @@ class SceneTree : public Pinned {
3030
/// \param inout Used to highlight matching Node, and set to clicked Node, if any
3131
/// \returns true if inout changed during tree walk
3232
///
33-
bool render(NotClosed<Window>, Inspectee& inout) const;
33+
bool render(NotClosed<Window>, InspectNode& inout) const;
3434

3535
private:
3636
Scene& m_scene;

lib/engine/src/editor/common.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ TreeNode::~TreeNode() {
2525
if (m_open) { ImGui::TreePop(); }
2626
}
2727

28+
bool TreeNode::leaf(char const* label, int flags) {
29+
if (TreeNode{label, flags | ImGuiTreeNodeFlags_Leaf}) { return true; }
30+
return false;
31+
}
32+
2833
Window::Menu::Menu(NotClosed<Window>) : MenuBar(ImGui::BeginMenuBar()) {}
2934

3035
Window::Menu::~Menu() {

lib/engine/src/editor/inspector.cpp

Lines changed: 123 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <imgui.h>
22
#include <facade/engine/editor/inspector.hpp>
33
#include <facade/scene/scene.hpp>
4+
#include <facade/util/enumerate.hpp>
45
#include <facade/util/fixed_string.hpp>
56
#include <cassert>
67

@@ -19,8 +20,6 @@ struct Modified {
1920
};
2021
} // namespace
2122

22-
Inspector::Inspector([[maybe_unused]] Window const& target) { assert(target.is_open()); }
23-
2423
bool Inspector::inspect(char const* label, glm::vec2& out_vec2, float speed, float lo, float hi) const {
2524
float arr[2] = {out_vec2.x, out_vec2.y};
2625
ImGui::DragFloat2(label, arr, speed, lo, hi);
@@ -51,6 +50,15 @@ bool Inspector::inspect(char const* label, glm::vec4& out_vec4, float speed, flo
5150
return false;
5251
}
5352

53+
bool Inspector::inspect(char const* label, nvec3& out_vec3, float speed) const {
54+
auto vec3 = out_vec3.value();
55+
if (inspect(label, vec3, speed, -1.0f, 1.0f)) {
56+
out_vec3 = vec3;
57+
return true;
58+
}
59+
return false;
60+
}
61+
5462
bool Inspector::inspect(char const* label, glm::quat& out_quat) const {
5563
auto euler = to_degree(glm::eulerAngles(out_quat));
5664
auto const org = euler;
@@ -63,39 +71,124 @@ bool Inspector::inspect(char const* label, glm::quat& out_quat) const {
6371
return false;
6472
}
6573

74+
bool Inspector::inspect_rgb(char const* label, glm::vec3& out_rgb) const {
75+
float arr[3] = {out_rgb.x, out_rgb.y, out_rgb.z};
76+
if (ImGui::ColorEdit3(label, arr)) {
77+
out_rgb = {arr[0], arr[1], arr[2]};
78+
return true;
79+
}
80+
return false;
81+
}
82+
83+
bool Inspector::inspect(char const* label, Rgb& out_rgb) const {
84+
auto ret = Modified{};
85+
if (auto tn = TreeNode{label}) {
86+
auto vec3 = Rgb{.channels = out_rgb.channels, .intensity = 1.0f}.to_vec3();
87+
if (inspect_rgb("RGB", vec3)) {
88+
out_rgb = Rgb::make(vec3, out_rgb.intensity);
89+
ret.value = true;
90+
}
91+
ret(ImGui::DragFloat("Intensity", &out_rgb.intensity, 0.05f, 0.1f, 1000.0f));
92+
}
93+
return ret.value;
94+
}
95+
6696
bool Inspector::inspect(Transform& out_transform, Bool& out_unified_scaling) const {
97+
if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) { return do_inspect(out_transform, out_unified_scaling, {true}); }
98+
return false;
99+
}
100+
101+
bool Inspector::inspect(std::span<Transform> out_instances, Bool unified_scaling) const {
102+
if (out_instances.empty()) { return false; }
103+
auto ret = Modified{};
104+
if (ImGui::CollapsingHeader("Instances")) {
105+
for (auto [transform, index] : enumerate(out_instances)) {
106+
if (auto tn = TreeNode{FixedString{"Instance [{}]", index}.c_str()}) { ret(do_inspect(transform, unified_scaling, {false})); }
107+
}
108+
}
109+
return ret.value;
110+
}
111+
112+
bool Inspector::inspect(Lights& out_lights) const {
113+
if (out_lights.dir_lights.empty()) { return false; }
67114
auto ret = Modified{};
68-
if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) {
69-
auto vec3 = out_transform.position();
70-
if (ret(inspect("Position", vec3))) { out_transform.set_position(vec3); }
71-
auto quat = out_transform.orientation();
72-
if (ret(inspect("Orientation", quat))) { out_transform.set_orientation(quat); }
73-
vec3 = out_transform.scale();
74-
if (out_unified_scaling) {
75-
if (ret(ImGui::DragFloat("Scale", &vec3.x, 0.1f))) { out_transform.set_scale({vec3.x, vec3.x, vec3.x}); }
76-
} else {
77-
if (ret(inspect("Scale", vec3, 0.1f))) { out_transform.set_scale(vec3); }
115+
auto to_remove = std::optional<std::size_t>{};
116+
bool allow_removal = out_lights.dir_lights.size() > 1;
117+
auto inspect_dir_light = [&](DirLight& out_light, std::size_t index) {
118+
auto tn = TreeNode{FixedString{"[{}]", index}.c_str()};
119+
if (allow_removal) {
120+
ImGui::SameLine(ImGui::GetWindowContentRegionWidth() - 10.0f);
121+
static constexpr auto colour_v = ImVec4{0.8f, 0.0f, 0.1f, 1.0f};
122+
ImGui::PushStyleColor(ImGuiCol_Button, colour_v);
123+
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, colour_v);
124+
ImGui::PushStyleColor(ImGuiCol_ButtonActive, colour_v);
125+
if (ImGui::SmallButton("x")) { to_remove = index; }
126+
ImGui::PopStyleColor(3);
127+
}
128+
if (tn) {
129+
ret(inspect("Direction", out_light.direction));
130+
ret(inspect("Albedo", out_light.rgb));
131+
}
132+
};
133+
if (ImGui::CollapsingHeader("DirLights")) {
134+
for (auto [light, index] : enumerate(out_lights.dir_lights.span())) { inspect_dir_light(light, index); }
135+
}
136+
if (to_remove) {
137+
auto replace = decltype(out_lights.dir_lights){};
138+
for (auto const [light, index] : enumerate(out_lights.dir_lights.span())) {
139+
if (index == *to_remove) { continue; }
140+
replace.insert(light);
78141
}
142+
out_lights.dir_lights = std::move(replace);
143+
}
144+
if (out_lights.dir_lights.size() < Lights::max_lights_v && ImGui::Button("[+]")) { out_lights.dir_lights.insert(DirLight{}); }
145+
return ret.value;
146+
}
147+
148+
bool Inspector::do_inspect(Transform& out_transform, Bool& out_unified_scaling, Bool scaling_toggle) const {
149+
auto ret = Modified{};
150+
auto vec3 = out_transform.position();
151+
if (ret(inspect("Position", vec3))) { out_transform.set_position(vec3); }
152+
auto quat = out_transform.orientation();
153+
if (ret(inspect("Orientation", quat))) { out_transform.set_orientation(quat); }
154+
vec3 = out_transform.scale();
155+
if (out_unified_scaling) {
156+
if (ret(ImGui::DragFloat("Scale", &vec3.x, 0.1f))) { out_transform.set_scale({vec3.x, vec3.x, vec3.x}); }
157+
} else {
158+
if (ret(inspect("Scale", vec3, 0.1f))) { out_transform.set_scale(vec3); }
159+
}
160+
if (scaling_toggle) {
79161
ImGui::SameLine();
80162
ImGui::Checkbox("Unified", &out_unified_scaling.value);
81163
}
82164
return ret.value;
83165
}
84166

85-
SceneInspector::SceneInspector([[maybe_unused]] Window const& target, Scene& scene) : Inspector(target), m_scene(scene) { assert(target.is_open()); }
167+
SceneInspector::SceneInspector(NotClosed<Window> target, Scene& scene) : Inspector(target), m_scene(scene) {}
86168

87-
bool SceneInspector::inspect([[maybe_unused]] TreeNode const& node, UnlitMaterial& out_material) const {
88-
assert(node.is_open());
169+
bool SceneInspector::inspect(NotClosed<TreeNode>, UnlitMaterial& out_material) const {
89170
auto ret = Modified{};
90171
ret(inspect("Tint", out_material.tint, 0.01f, 0.0f, 1.0f));
91172
return ret.value;
92173
}
93174

94-
bool SceneInspector::inspect([[maybe_unused]] TreeNode const& node, LitMaterial& out_material) const {
95-
assert(node.is_open());
175+
bool SceneInspector::inspect(NotClosed<TreeNode>, LitMaterial& out_material) const {
96176
auto ret = Modified{};
97177
ret(ImGui::SliderFloat("Metallic", &out_material.metallic, 0.0f, 1.0f));
98178
ret(ImGui::SliderFloat("Roughness", &out_material.roughness, 0.0f, 1.0f));
179+
ret(inspect_rgb("Albedo", out_material.albedo));
180+
if (out_material.base_colour || out_material.roughness_metallic) {
181+
if (auto tn = TreeNode{"Textures"}) {
182+
if (out_material.base_colour) {
183+
auto const* tex = m_scene.find(*out_material.base_colour);
184+
TreeNode::leaf(FixedString{"Albedo: {} ({})", tex->name, *out_material.base_colour}.c_str());
185+
}
186+
if (out_material.roughness_metallic) {
187+
auto const* tex = m_scene.find(*out_material.roughness_metallic);
188+
TreeNode::leaf(FixedString{"Roughness-Metallic: {} ({})", tex->name, *out_material.roughness_metallic}.c_str());
189+
}
190+
}
191+
}
99192
return ret.value;
100193
}
101194

@@ -112,28 +205,28 @@ bool SceneInspector::inspect(Id<Material> material_id) const {
112205
return false;
113206
}
114207

115-
bool SceneInspector::inspect(Id<Node> node_id, Bool& out_unified_scaling) const {
116-
auto ret = Modified{};
117-
auto* node = m_scene.find(node_id);
118-
if (!node) { return false; }
119-
ret(inspect(node->transform, out_unified_scaling));
120-
if (auto const* mesh_id = node->find<Id<Mesh>>()) { ret(inspect(*mesh_id)); }
121-
return ret.value;
122-
}
123-
124208
bool SceneInspector::inspect(Id<Mesh> mesh_id) const {
125209
auto ret = Modified{};
126210
auto* mesh = m_scene.find(mesh_id);
127211
if (!mesh) { return ret.value; }
128212
if (ImGui::CollapsingHeader(FixedString{"Mesh ({})###Mesh", mesh_id}.c_str())) {
129-
for (std::size_t i = 0; i < mesh->primitives.size(); ++i) {
130-
if (auto tn = TreeNode{FixedString{"Primitive {}", i}.c_str()}) {
131-
auto const& primitive = mesh->primitives[i];
132-
ImGui::Text("%s", FixedString{"Static Mesh ({})", primitive.static_mesh}.c_str());
213+
for (auto [primitive, index] : enumerate(mesh->primitives)) {
214+
if (auto tn = TreeNode{FixedString{"Primitive [{}]", index}.c_str()}) {
215+
TreeNode::leaf(FixedString{"Static Mesh ({})", primitive.static_mesh}.c_str());
133216
if (primitive.material) { ret(inspect(*primitive.material)); }
134217
}
135218
}
136219
}
137220
return ret.value;
138221
}
222+
223+
bool SceneInspector::inspect(Id<Node> node_id, Bool& out_unified_scaling) const {
224+
auto ret = Modified{};
225+
auto* node = m_scene.find(node_id);
226+
if (!node) { return false; }
227+
ret(inspect(node->transform, out_unified_scaling));
228+
ret(inspect(node->instances, out_unified_scaling));
229+
if (auto const* mesh_id = node->find<Id<Mesh>>()) { ret(inspect(*mesh_id)); }
230+
return ret.value;
231+
}
139232
} // namespace facade::editor

lib/engine/src/editor/scene_tree.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ FixedString<128> node_name(Node const& node) {
1111
return ret;
1212
}
1313

14-
Inspectee inspect(Node const& node) {
15-
auto ret = Inspectee{};
14+
InspectNode inspect(Node const& node) {
15+
auto ret = InspectNode{};
1616
ret.id = node.id();
1717
ret.name = node_name(node);
1818
ret.name += FixedString{"###Node"};
1919
return ret;
2020
}
2121

22-
void walk(Node& node, Inspectee& inout) {
22+
void walk(Node& node, InspectNode& inout) {
2323
auto flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth;
2424
if (node.id() == inout.id) { flags |= ImGuiTreeNodeFlags_Selected; }
2525
if (node.children().empty()) { flags |= ImGuiTreeNodeFlags_Leaf; }
@@ -35,7 +35,7 @@ void walk(Node& node, Inspectee& inout) {
3535
}
3636
} // namespace
3737

38-
bool SceneTree::render(NotClosed<Window>, Inspectee& inout) const {
38+
bool SceneTree::render(NotClosed<Window>, InspectNode& inout) const {
3939
auto const in = inout.id;
4040
if (!m_scene.find(in)) { inout = {}; }
4141
for (auto& root : m_scene.roots()) { walk(root, inout); }

lib/engine/src/scene_renderer.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ struct Img1x1 {
2424
};
2525
}
2626
};
27+
28+
struct DirLightSSBO {
29+
alignas(16) glm::vec3 direction{front_v};
30+
alignas(16) glm::vec3 ambient{0.04f};
31+
alignas(16) glm::vec3 diffuse{1.0f};
32+
33+
static DirLightSSBO make(DirLight const& light) {
34+
return {
35+
.direction = light.direction.value(),
36+
.diffuse = light.rgb.to_vec4(),
37+
};
38+
}
39+
};
2740
} // namespace
2841

2942
SceneRenderer::SceneRenderer(Gfx const& gfx)
@@ -51,7 +64,9 @@ void SceneRenderer::write_view(glm::vec2 const extent) {
5164
.pos_v = {cam_node.transform.position(), 1.0f},
5265
};
5366
m_view_proj.write(&vp, sizeof(vp));
54-
m_dir_lights.write(m_scene->dir_lights.data(), m_scene->dir_lights.size() * sizeof(DirLight));
67+
auto dir_lights = FlexArray<DirLightSSBO, 4>{};
68+
for (auto const& light : m_scene->lights.dir_lights.span()) { dir_lights.insert(DirLightSSBO::make(light)); }
69+
m_dir_lights.write(dir_lights.span().data(), dir_lights.span().size_bytes());
5570
}
5671

5772
void SceneRenderer::update_view(Pipeline& out_pipeline) const {

lib/render/src/renderer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ bool Renderer::next_frame(std::span<vk::CommandBuffer> out) {
187187
m_impl->render_target = m_impl->render_pass.refresh(acquired);
188188

189189
// refresh framebuffer
190-
m_impl->framebuffer = frame.refresh(m_impl->render_pass.render_pass(), *m_impl->render_target);
190+
m_impl->framebuffer = frame.refresh(m_impl->gfx.device, m_impl->render_pass.render_pass(), *m_impl->render_target);
191191

192192
// begin recording commands
193193
auto const cbii = vk::CommandBufferInheritanceInfo{m_impl->render_pass.render_pass(), 0, m_impl->framebuffer.framebuffer};

lib/scene/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ target_sources(${PROJECT_NAME} PRIVATE
3939
include/${target_prefix}/scene/lights.hpp
4040
include/${target_prefix}/scene/load_status.hpp
4141
include/${target_prefix}/scene/material.hpp
42+
include/${target_prefix}/scene/mesh.hpp
4243
include/${target_prefix}/scene/node_data.hpp
4344
include/${target_prefix}/scene/node.hpp
4445
include/${target_prefix}/scene/scene.hpp

0 commit comments

Comments
 (0)