Skip to content

Commit e4a2180

Browse files
authored
Refactor SceneResources (#67)
* Refactor SceneResources - Make `Material` a value type. - Enable choosing between Lit and Unlit materials on Add. - Separate persistent `InspectData`. * Move ResourceArray to its own header - Add docs.
1 parent 843d531 commit e4a2180

File tree

16 files changed

+255
-155
lines changed

16 files changed

+255
-155
lines changed

lib/engine/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ target_sources(${PROJECT_NAME} PRIVATE
4343
include/${target_prefix}/engine/editor/browse_file.hpp
4444
include/${target_prefix}/engine/editor/common.hpp
4545
include/${target_prefix}/engine/editor/drag_drop_id.hpp
46+
include/${target_prefix}/engine/editor/inspect_data.hpp
4647
include/${target_prefix}/engine/editor/inspector.hpp
4748
include/${target_prefix}/engine/editor/log.hpp
4849
include/${target_prefix}/engine/editor/reflector.hpp
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
#include <string>
3+
4+
namespace facade::editor {
5+
struct InspectData {
6+
std::string input_buffer{};
7+
int material_type{};
8+
};
9+
} // namespace facade::editor

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22
#include <facade/engine/editor/common.hpp>
3+
#include <facade/engine/editor/inspect_data.hpp>
34
#include <facade/scene/scene.hpp>
45

56
namespace facade::editor {
@@ -20,7 +21,7 @@ class Inspector {
2021

2122
NotClosed<Window> m_target;
2223
Scene& m_scene;
23-
SceneResourcesMut m_resources;
24+
SceneResources& m_resources;
2425
};
2526

2627
///
@@ -67,15 +68,17 @@ class ResourceInspector : public Inspector {
6768
///
6869
class SceneInspector : public Inspector {
6970
public:
71+
using Data = InspectData;
72+
7073
SceneInspector(NotClosed<Window> target, Scene& out_scene) : Inspector(target, out_scene) {}
7174

7275
///
7376
/// \brief View/edit all resources.
74-
/// \param out_name_buf Persistent buffer for popups
77+
/// \param out_data Persistent data for popups
7578
///
7679
/// Uses ResourceInspector.
7780
///
78-
void resources(std::string& out_name_buf) const;
81+
void resources(Data& out_data) const;
7982
///
8083
/// \brief Inspect the Scene's camera.
8184
///

lib/engine/src/editor/inspector.cpp

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void edit_material(NotClosed<Window> target, SceneResources const& resources, Li
2626
make_id_slot(lit.emissive, "Emissive", name.c_str(), {true});
2727
}
2828

29-
void edit_material(SceneResourcesMut resources, UnlitMaterial& unlit) {
29+
void edit_material(SceneResources const& resources, UnlitMaterial& unlit) {
3030
auto name = FixedString<128>{};
3131
if (unlit.texture) { name = FixedString<128>{"{} ({})", resources.textures[*unlit.texture].name(), *unlit.texture}; }
3232
make_id_slot(unlit.texture, "Texture", name.c_str(), {true});
@@ -57,12 +57,13 @@ void ResourceInspector::view(StaticMesh const& mesh, Id<StaticMesh> id) const {
5757
}
5858

5959
void ResourceInspector::edit(Material& out_material, Id<Material> id) const {
60-
auto const name = FixedString<128>{"{} ({})", out_material.name, id};
60+
auto const name = FixedString<128>{"{} ({})", out_material.name(), id};
6161
auto tn = TreeNode{name.c_str()};
6262
drag_payload(id, name.c_str());
6363
if (tn) {
64-
if (auto* lit = dynamic_cast<LitMaterial*>(&out_material)) { return edit_material(m_target, m_resources, *lit); }
65-
if (auto* unlit = dynamic_cast<UnlitMaterial*>(&out_material)) { return edit_material(m_resources, *unlit); }
64+
auto& mat = out_material.base();
65+
if (auto* lit = dynamic_cast<LitMaterial*>(&mat)) { return edit_material(m_target, m_resources, *lit); }
66+
if (auto* unlit = dynamic_cast<UnlitMaterial*>(&mat)) { return edit_material(m_resources, *unlit); }
6667
}
6768
}
6869

@@ -78,7 +79,7 @@ void ResourceInspector::edit(Mesh& out_mesh, Id<Mesh> id) const {
7879
make_id_slot(primitive.static_mesh, "Static Mesh", name.c_str());
7980

8081
name = {};
81-
if (primitive.material) { name = FixedString<128>{"{} ({})", m_resources.materials[*primitive.material]->name, *primitive.material}; }
82+
if (primitive.material) { name = FixedString<128>{"{} ({})", m_resources.materials[*primitive.material].name(), *primitive.material}; }
8283
make_id_slot(primitive.material, "Material", name.c_str(), {true});
8384

8485
if (small_button_red("x###remove_primitive")) { to_erase = index; }
@@ -95,46 +96,54 @@ void ResourceInspector::edit(Mesh& out_mesh, Id<Mesh> id) const {
9596
}
9697
}
9798

98-
void SceneInspector::resources(std::string& out_name_buf) const {
99+
void SceneInspector::resources(Data& out_data) const {
99100
auto const ri = ResourceInspector{m_target, m_scene};
100101
static constexpr auto flags_v = ImGuiTreeNodeFlags_Framed;
101102
bool add_material{}, add_mesh{};
102103
if (auto tn = TreeNode("Textures", flags_v)) {
103-
for (auto [texture, id] : enumerate(m_resources.textures)) { ri.view(texture, id); }
104+
for (auto [texture, id] : enumerate(m_resources.textures.view())) { ri.view(texture, id); }
104105
}
105106
if (auto tn = TreeNode("Static Meshes", flags_v)) {
106-
for (auto [static_mesh, id] : enumerate(m_resources.static_meshes)) { ri.view(static_mesh, id); }
107+
for (auto [static_mesh, id] : enumerate(m_resources.static_meshes.view())) { ri.view(static_mesh, id); }
107108
}
108109
if (auto tn = TreeNode("Materials", flags_v)) {
109-
for (auto [material, id] : enumerate(m_resources.materials)) { ri.edit(*material, id); }
110+
for (auto [material, id] : enumerate(m_resources.materials.view())) { ri.edit(material, id); }
110111
if (ImGui::Button("Add...")) { add_material = true; }
111112
}
112113
if (auto tn = TreeNode{"Meshes", flags_v}) {
113-
for (auto [mesh, id] : enumerate(m_resources.meshes)) { ri.edit(mesh, id); }
114+
for (auto [mesh, id] : enumerate(m_resources.meshes.view())) { ri.edit(mesh, id); }
114115
if (ImGui::Button("Add...")) { add_mesh = true; }
115116
}
116117

117118
if (add_material) { Popup::open("Add Material"); }
119+
if (out_data.input_buffer.empty()) { out_data.input_buffer.resize(128, '\0'); }
120+
121+
if (auto popup = Popup{"Add Mesh"}) {
122+
ImGui::InputText("Name", out_data.input_buffer.data(), out_data.input_buffer.size());
123+
if (ImGui::Button("Add") && *out_data.input_buffer.c_str()) {
124+
m_scene.add(Mesh{.name = out_data.input_buffer.c_str()});
125+
Popup::close_current();
126+
}
127+
}
128+
118129
if (add_mesh) { Popup::open("Add Mesh"); }
119-
if (out_name_buf.empty()) { out_name_buf.resize(128, '\0'); }
120-
auto open_popup = [&out_name_buf](char const* id, auto func) {
121-
if (auto popup = Popup{id}) {
122-
ImGui::InputText("Name", out_name_buf.data(), out_name_buf.size());
123-
if (ImGui::Button(id)) {
124-
auto str = out_name_buf.c_str();
125-
if (!*str) { return; }
126-
func(str);
127-
Popup::close_current();
130+
if (auto popup = Popup{"Add Material"}) {
131+
ImGui::InputText("Name", out_data.input_buffer.data(), out_data.input_buffer.size());
132+
static constexpr char const* mat_types_v[] = {"Lit", "Unlit"};
133+
static constexpr auto mat_types_size_v{static_cast<int>(std::size(mat_types_v))};
134+
char const* mat_type = out_data.material_type >= 0 && out_data.material_type < mat_types_size_v ? mat_types_v[out_data.material_type] : "Invalid";
135+
ImGui::SliderInt("Type", &out_data.material_type, 0, mat_types_size_v - 1, mat_type);
136+
if (ImGui::Button("Add") && *out_data.input_buffer.c_str()) {
137+
auto mat = std::unique_ptr<MaterialBase>{};
138+
switch (out_data.material_type) {
139+
case 0: mat = std::make_unique<LitMaterial>(); break;
140+
case 1: mat = std::make_unique<UnlitMaterial>(); break;
128141
}
142+
mat->name = out_data.input_buffer.c_str();
143+
m_scene.add(Material{std::move(mat)});
144+
Popup::close_current();
129145
}
130-
};
131-
auto push_material = [this](std::string name) {
132-
auto mat = std::make_unique<LitMaterial>();
133-
mat->name = std::move(name);
134-
m_scene.add(std::move(mat));
135-
};
136-
open_popup("Add Material", [&push_material](char const* name) { push_material(name); });
137-
open_popup("Add Mesh", [this](char const* name) { m_scene.add(Mesh{.name = name}); });
146+
}
138147
}
139148

140149
void SceneInspector::camera() const {
@@ -218,7 +227,7 @@ void SceneInspector::mesh(Node& out_node) const {
218227
auto* mesh_id = out_node.find<Id<Mesh>>();
219228
if (auto tn = TreeNode{"Mesh", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed}) {
220229
auto name = FixedString<128>{};
221-
if (mesh_id) { name = FixedString<128>{"{} ({})", m_scene.find(*mesh_id)->name, *mesh_id}; }
230+
if (mesh_id) { name = FixedString<128>{"{} ({})", m_resources.meshes.find(*mesh_id)->name, *mesh_id}; }
222231
make_id_slot<Mesh>(out_node, "Mesh", name.c_str());
223232
}
224233
}

lib/engine/src/scene_renderer.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,17 @@ std::span<glm::mat4x4 const> SceneRenderer::make_instances(Node const& node, glm
9090
}
9191

9292
void SceneRenderer::render(Renderer& renderer, vk::CommandBuffer cb, Node const& node, glm::mat4 const& parent) {
93-
auto const resources = m_scene->resources();
93+
auto const& resources = m_scene->resources();
9494
if (auto const* mesh_id = node.find<Id<Mesh>>()) {
95-
static auto const s_default_material = LitMaterial{};
95+
static auto const s_default_material = Material{std::make_unique<LitMaterial>()};
9696
auto const& mesh = resources.meshes[*mesh_id];
9797
for (auto const& primitive : mesh.primitives) {
98-
auto const& material = primitive.material ? *resources.materials[primitive.material->value()] : static_cast<Material const&>(s_default_material);
98+
auto const& material = primitive.material ? resources.materials[primitive.material->value()] : static_cast<Material const&>(s_default_material);
9999
auto pipeline = renderer.bind_pipeline(cb, m_scene->pipeline_state, material.shader_id());
100100
pipeline.set_line_width(m_scene->pipeline_state.line_width);
101101

102102
update_view(pipeline);
103-
auto const store = TextureStore{resources.textures, m_white, m_black};
103+
auto const store = TextureStore{resources.textures.view(), m_white, m_black};
104104
material.write_sets(pipeline, store);
105105

106106
auto const& static_mesh = resources.static_meshes[primitive.static_mesh];

lib/scene/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ target_sources(${PROJECT_NAME} PRIVATE
4343
include/${target_prefix}/scene/mesh.hpp
4444
include/${target_prefix}/scene/node_data.hpp
4545
include/${target_prefix}/scene/node.hpp
46+
include/${target_prefix}/scene/resource_array.hpp
4647
include/${target_prefix}/scene/scene_resources.hpp
4748
include/${target_prefix}/scene/scene.hpp
4849

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

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,18 @@ struct TextureStore {
3939
};
4040

4141
///
42-
/// \brief Base Material: stores render parameters for a mesh.
42+
/// \brief Base class for concrete materials.
4343
///
44-
class Material {
44+
class MaterialBase {
4545
public:
4646
///
47-
/// \brief The alpha blend mode.
47+
/// \brief Alpha blend mode.
4848
///
4949
enum class AlphaMode : std::uint32_t { eOpaque = 0, eBlend, eMask };
5050

51-
///
52-
/// \brief Convert sRGB encoded colour to linear.
53-
///
54-
static glm::vec4 to_linear(glm::vec4 const& srgb);
55-
///
56-
/// \brief Convert linear encoded colour to sRGB.
57-
///
58-
static glm::vec4 to_srgb(glm::vec4 const& linear);
59-
6051
inline static std::string const default_shader_id{"default"};
6152

62-
virtual ~Material() = default;
53+
virtual ~MaterialBase() = default;
6354

6455
///
6556
/// \brief Obtain the ID for the shader used by this material.
@@ -73,13 +64,46 @@ class Material {
7364
///
7465
virtual void write_sets(Pipeline& pipeline, TextureStore const& store) const = 0;
7566

67+
///
68+
/// \brief Name of this instance.
69+
///
7670
std::string name{"(Unnamed)"};
7771
};
7872

73+
///
74+
/// \brief Value-semantic strategy wrapper for concrete materials.
75+
///
76+
class Material {
77+
public:
78+
using AlphaMode = MaterialBase::AlphaMode;
79+
80+
Material(std::unique_ptr<MaterialBase>&& base) : m_base(std::move(base)) {}
81+
82+
std::string const& shader_id() const {
83+
assert(m_base);
84+
return m_base->shader_id();
85+
}
86+
87+
void write_sets(Pipeline& pipeline, TextureStore const& store) const {
88+
assert(m_base);
89+
m_base->write_sets(pipeline, store);
90+
}
91+
92+
std::string_view name() const { return m_base->name; }
93+
94+
MaterialBase& base() const {
95+
assert(m_base);
96+
return *m_base;
97+
}
98+
99+
private:
100+
std::unique_ptr<MaterialBase> m_base;
101+
};
102+
79103
///
80104
/// \brief Unlit Material.
81105
///
82-
class UnlitMaterial : public Material {
106+
class UnlitMaterial : public MaterialBase {
83107
public:
84108
inline static std::string const shader_id_v{"unlit"};
85109

@@ -99,7 +123,7 @@ class UnlitMaterial : public Material {
99123
///
100124
/// \brief PBR lit material.
101125
///
102-
class LitMaterial : public Material {
126+
class LitMaterial : public MaterialBase {
103127
public:
104128
///
105129
/// \brief Base colour factor.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#pragma once
2+
#include <facade/scene/id.hpp>
3+
#include <facade/util/ptr.hpp>
4+
#include <span>
5+
#include <utility>
6+
#include <vector>
7+
8+
namespace facade {
9+
///
10+
/// \brief Wrapper over an array of resources, each identified by the index as its Id.
11+
///
12+
/// Only Scene has access to the underlying vector (via friend). Other types can operate
13+
/// on const / non-const spans, but cannot modify the array itself.
14+
///
15+
template <typename T>
16+
class ResourceArray {
17+
public:
18+
///
19+
/// \brief Obtain an immutable view into the underlying array.
20+
/// \returns Immutable span
21+
///
22+
std::span<T const> view() const { return m_array; }
23+
///
24+
/// \brief Obtain a mutable view into the underlying array.
25+
/// \returns Mutable span
26+
///
27+
std::span<T> view() { return m_array; }
28+
29+
///
30+
/// \brief Obtain an immutable pointer to the element at index id.
31+
/// \param id Id (index) of element
32+
/// \returns nullptr if id is out of bounds
33+
///
34+
Ptr<T const> find(Id<T> id) const {
35+
if (id >= m_array.size()) { return {}; }
36+
return &m_array[id];
37+
}
38+
39+
///
40+
/// \brief Obtain a mutable pointer to the element at index id.
41+
/// \param id Id (index) of element
42+
/// \returns nullptr if id is out of bounds
43+
///
44+
Ptr<T> find(Id<T> index) { return const_cast<Ptr<T>>(std::as_const(*this).find(index)); }
45+
46+
///
47+
/// \brief Obtain an immutable reference to the element at index id.
48+
/// \param id Id (index) of element
49+
/// \returns const reference to element
50+
///
51+
/// id must be valid / in range
52+
///
53+
T const& operator[](Id<T> id) const {
54+
auto ret = find(id);
55+
assert(ret);
56+
return *ret;
57+
}
58+
59+
///
60+
/// \brief Obtain an immutable reference to the element at index id.
61+
/// \param id Id (index) of element
62+
/// \returns const reference to element
63+
///
64+
/// id must be valid / in range
65+
///
66+
T& operator[](Id<T> index) { return const_cast<T&>(std::as_const(*this).operator[](index)); }
67+
68+
///
69+
/// \brief Obtain the size of the underlying array.
70+
///
71+
constexpr std::size_t size() const { return m_array.size(); }
72+
///
73+
/// \brief Check if the underlying array is empty.
74+
///
75+
constexpr bool empty() const { return m_array.empty(); }
76+
77+
private:
78+
std::vector<T> m_array{};
79+
80+
friend class Scene;
81+
};
82+
} // namespace facade

0 commit comments

Comments
 (0)