aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/EditorAttachmentImpl.hpp7
-rw-r--r--source/EditorCore.cpp141
-rw-r--r--source/EditorCore.hpp2
-rw-r--r--source/EditorResources.cpp83
-rw-r--r--source/EditorResources.hpp3
-rw-r--r--source/EditorUtils.cpp33
-rw-r--r--source/EditorUtils.hpp7
-rw-r--r--source/Material.cpp362
-rw-r--r--source/Material.hpp61
-rw-r--r--source/RapidJsonHelper.hpp11
-rw-r--r--source/ScopeGuard.hpp7
-rw-r--r--source/Shader.cpp50
-rw-r--r--source/Shader.hpp39
-rw-r--r--source/Texture.cpp13
-rw-r--r--source/Texture.hpp15
-rw-r--r--source/main.cpp75
16 files changed, 773 insertions, 136 deletions
diff --git a/source/EditorAttachmentImpl.hpp b/source/EditorAttachmentImpl.hpp
index 32618b1..cb96348 100644
--- a/source/EditorAttachmentImpl.hpp
+++ b/source/EditorAttachmentImpl.hpp
@@ -22,5 +22,10 @@ public:
class EaShader : public EditorAttachment {
public:
Shader* shader;
- std::string name;
+};
+
+class EaMaterial : public EditorAttachment {
+public:
+ std::string editingScratch;
+ bool isEditingName = false;
};
diff --git a/source/EditorCore.cpp b/source/EditorCore.cpp
index 7a77b26..1846962 100644
--- a/source/EditorCore.cpp
+++ b/source/EditorCore.cpp
@@ -16,6 +16,8 @@
#include <GLFW/glfw3.h>
#include <ImGuizmo.h>
+#include <imgui.h>
+#include <misc/cpp/imgui_stdlib.h>
#include <functional>
#include <memory>
#include <utility>
@@ -36,6 +38,34 @@ void PushKeyCodeRecorder(App* app, int* writeKey, bool* writeKeyStatus) {
return false;
});
}
+
+void ShowShaderName(const Shader* shader) {
+ if (shader) {
+ auto& name = shader->GetName();
+ bool isAnnoymous = name.empty();
+ if (isAnnoymous) {
+ ImGui::Text("Shader <annoymous at %p>", (void*)shader);
+ } else {
+ ImGui::Text("Shader: %s", name.c_str());
+ }
+ } else {
+ ImGui::TextUnformatted("Shader: <null>");
+ }
+}
+
+void ShowMaterialName(const Material* material) {
+ if (material) {
+ auto& name = material->GetName();
+ bool isAnnoymous = name.empty();
+ if (isAnnoymous) {
+ ImGui::Text("Material: <annoymous at %p>", (void*)material);
+ } else {
+ ImGui::Text("Material: %s", name.c_str());
+ }
+ } else {
+ ImGui::TextUnformatted("Material: <null>");
+ }
+}
} // namespace ProjectBrussel_UNITY_ID
EditorInstance::EditorInstance(App* app, GameWorld* world)
@@ -66,6 +96,7 @@ void EditorInstance::Show() {
switch (mSelectedItt) {
case ITT_GameObject: ShowInspector(static_cast<GameObject*>(mSelectedItPtr)); break;
case ITT_Shader: ShowInspector(static_cast<Shader*>(mSelectedItPtr)); break;
+ case ITT_Material: ShowInspector(static_cast<Material*>(mSelectedItPtr)); break;
case ITT_None: break;
}
ImGui::End();
@@ -89,6 +120,15 @@ void EditorInstance::ShowInspector(Shader* shader) {
using namespace Tags;
using namespace ProjectBrussel_UNITY_ID;
+ EaShader* attachment;
+ if (auto ea = shader->GetEditorAttachment()) {
+ attachment = static_cast<EaShader*>(ea);
+ } else {
+ attachment = new EaShader();
+ attachment->shader = shader;
+ shader->SetEditorAttachment(attachment);
+ }
+
auto info = shader->GetInfo();
if (!info) {
ImGui::TextUnformatted("No info present for this shader.");
@@ -103,26 +143,14 @@ void EditorInstance::ShowInspector(Shader* shader) {
auto& name = shader->GetName();
bool isAnnoymous = name.empty();
- if (isAnnoymous) {
- ImGui::Text("<Annoymous Shader at %p>", (void*)shader);
- } else {
- ImGui::Text("Name: %s", shader->GetName().c_str());
- }
+ ShowShaderName(shader);
- // TODO use std::filesystem::path
- auto GetMetadataPath = [&](char* path, int pathLength) {
- snprintf(path, pathLength, "%s/Shaders/%s.json", AppConfig::assetDir.c_str(), shader->GetName().c_str());
- };
if (ImGui::Button("Reimport metadata", isAnnoymous)) {
- char path[512];
- GetMetadataPath(path, sizeof(path));
- info->LoadFromFile(path);
+ info->LoadFromFile(shader->GetDesignatedMetadataPath());
}
ImGui::SameLine();
if (ImGui::Button("Export metadata", isAnnoymous)) {
- char path[512];
- GetMetadataPath(path, sizeof(path));
- info->SaveToFile(path);
+ info->SaveToFile(shader->GetDesignatedMetadataPath());
}
auto ShowThing = [&](const std::vector<ShaderInfo::InputOutputThing>& things) {
@@ -148,6 +176,89 @@ void EditorInstance::ShowInspector(Shader* shader) {
}
}
+void EditorInstance::ShowInspector(Material* material) {
+ using namespace Tags;
+ using namespace ProjectBrussel_UNITY_ID;
+
+ EaMaterial* attachment;
+ if (auto ea = material->GetEditorAttachment()) {
+ attachment = static_cast<EaMaterial*>(ea);
+ } else {
+ attachment = new EaMaterial();
+ material->SetEditorAttachment(attachment);
+ }
+
+ auto& name = material->GetName();
+ bool isAnnoymous = name.empty();
+ auto shader = material->GetShader();
+
+ if (isAnnoymous) {
+ ImGui::Text("<Annoymous Material at %p>", (void*)(&material));
+ } else {
+ if (attachment->isEditingName) {
+ bool save = false;
+ save |= ImGui::InputText("##", &attachment->editingScratch, ImGuiInputTextFlags_EnterReturnsTrue);
+ ImGui::SameLine();
+ save |= ImGui::Button("Save");
+ if (save) {
+ bool success = MaterialManager::instance->RenameMaterial(material, attachment->editingScratch);
+ if (success) {
+ attachment->isEditingName = false;
+ }
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Cancel")) {
+ attachment->isEditingName = false;
+ }
+
+ ImGui::Text("%s", attachment->editingScratch.c_str());
+ } else {
+ // NOTE: ReadOnly shouldn't write any data into the buffer
+ ImGui::InputText("##", material->mName.data(), name.size() + 1, ImGuiInputTextFlags_ReadOnly);
+ ImGui::SameLine();
+ if (ImGui::Button("Edit")) {
+ attachment->editingScratch = name; // Copy
+ attachment->isEditingName = true;
+ }
+ }
+ }
+
+ ShowShaderName(shader);
+ if (ImGui::BeginDragDropTarget()) {
+ if (auto payload = ImGui::AcceptDragDropPayload(BRUSSEL_DRAG_DROP_SHADER)) {
+ auto shader = *static_cast<Shader* const*>(payload->Data);
+ material->SetShader(shader);
+ }
+ ImGui::EndDragDropTarget();
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("GoTo", shader == nullptr)) {
+ mSelectedItt = ITT_Shader;
+ mSelectedItPtr = shader;
+ }
+
+ if (ImGui::Button("Reload", isAnnoymous)) {
+ material->LoadFromFile(material->GetDesignatedPath());
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Save", isAnnoymous)) {
+ material->SaveToFile(material->GetDesignatedPath());
+ }
+
+ for (auto& field : material->mBoundScalars) {
+ // TODO
+ }
+ for (auto& field : material->mBoundVectors) {
+ // TODO
+ }
+ for (auto& field : material->mBoundMatrices) {
+ // TODO
+ }
+ for (auto& field : material->mBoundTextures) {
+ // TODO
+ }
+}
+
void EditorInstance::ShowInspector(GameObject* object) {
using namespace Tags;
using namespace ProjectBrussel_UNITY_ID;
diff --git a/source/EditorCore.hpp b/source/EditorCore.hpp
index 28ad849..3d91fa6 100644
--- a/source/EditorCore.hpp
+++ b/source/EditorCore.hpp
@@ -14,6 +14,7 @@ public:
enum InspectorTargetType {
ITT_GameObject,
ITT_Shader,
+ ITT_Material,
ITT_None,
};
@@ -39,6 +40,7 @@ private:
void ShowWorldProperties();
void ShowInspector(Shader* shader);
+ void ShowInspector(Material* material);
void ShowInspector(GameObject* object);
void ShowGameObjecetFields(GameObject* object);
diff --git a/source/EditorResources.cpp b/source/EditorResources.cpp
index c6d4d09..be7cefb 100644
--- a/source/EditorResources.cpp
+++ b/source/EditorResources.cpp
@@ -3,9 +3,18 @@
#include "EditorCore.hpp"
#include "EditorNotification.hpp"
#include "EditorUtils.hpp"
+#include "Macros.hpp"
+#include "ScopeGuard.hpp"
#include "Shader.hpp"
#include <imgui.h>
+#include <misc/cpp/imgui_stdlib.h>
+#include <cstdlib>
+#include <limits>
+#include <string>
+#include <string_view>
+
+using namespace std::literals;
EditorContentBrowser::EditorContentBrowser(EditorInstance* editor)
: mEditor{ editor } {
@@ -37,6 +46,9 @@ void EditorContentBrowser::Show(bool* open) {
if (ImGui::Selectable("Shaders", mPane == P_Shader)) {
mPane = P_Shader;
}
+ if (ImGui::Selectable("Materials", mPane == P_Material)) {
+ mPane = P_Material;
+ }
}
ImGui::EndChild();
@@ -49,23 +61,82 @@ void EditorContentBrowser::Show(bool* open) {
} break;
case P_Shader: {
- // TODO reload shaders while keeping existing references working
- // if (ImGui::Button("Reload from disk")) {
- // ShaderManager::instance->DiscoverShaders();
- // }
+ if (ImGui::Button("Refresh")) {
+ // TODO reload shaders while keeping existing references working
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Save all")) {
+ auto& shaders = ShaderManager::instance->GetShaders();
+ for (auto&& [DISCARD, shader] : shaders) {
+ auto info = shader->GetInfo();
+ if (info) {
+ info->SaveToFile(shader->GetDesignatedMetadataPath());
+ }
+ }
+ }
auto& shaders = ShaderManager::instance->GetShaders();
for (auto it = shaders.begin(); it != shaders.end(); ++it) {
- auto name = it->first;
auto shader = it->second.Get();
+ auto& name = shader->GetName();
shader->GatherInfoIfAbsent();
auto details = shader->GetInfo();
bool selected = mEditor->GetSelectedItPtr() == shader;
- if (ImGui::Selectable(name.data(), selected)) {
+ if (ImGui::Selectable(name.c_str(), selected)) {
mEditor->SelectIt(shader, EditorInstance::ITT_Shader);
}
+
+ if (ImGui::BeginDragDropSource()) {
+ // Reason: intentionally using pointer as Fpayload
+ ImGui::SetDragDropPayload(BRUSSEL_DRAG_DROP_SHADER, &shader, sizeof(shader)); // NOLINT(bugprone-sizeof-expression)
+ ImGui::Text("Shader '%s'", name.c_str());
+ ImGui::EndDragDropSource();
+ }
+ }
+ } break;
+
+ case P_Material: {
+ if (ImGui::Button("New")) {
+ int n = std::rand();
+ auto mat = new Material(nullptr, "Unnamed Material " + std::to_string(n));
+ auto guard = GuardDeletion(mat);
+ auto [DISCARD, inserted] = MaterialManager::instance->SaveMaterial(mat);
+ if (inserted) {
+ guard.Dismiss();
+ } else {
+ ImGui::AddNotification(ImGuiToast(ImGuiToastType_Error, "Failed to create material."));
+ }
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Refresh")) {
+ // TODO
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Save all")) {
+ auto& mats = MaterialManager::instance->GetMaterials();
+ for (auto&& [DISCARD, mat] : mats) {
+ mat->SaveToFile(mat->GetDesignatedPath());
+ }
+ }
+
+ auto& mats = MaterialManager::instance->GetMaterials();
+ for (auto it = mats.begin(); it != mats.end(); ++it) {
+ auto mat = it->second.Get();
+ auto& name = mat->GetName();
+
+ bool selected = mEditor->GetSelectedItPtr() == mat;
+ if (ImGui::Selectable(name.c_str(), selected)) {
+ mEditor->SelectIt(mat, EditorInstance::ITT_Material);
+ }
+
+ if (ImGui::BeginDragDropSource()) {
+ // Reason: intentionally using pointer as payload
+ ImGui::SetDragDropPayload(BRUSSEL_DRAG_DROP_MATERIAL, &mat, sizeof(mat)); // NOLINT(bugprone-sizeof-expression)
+ ImGui::Text("Material '%s'", name.c_str());
+ ImGui::EndDragDropSource();
+ }
}
} break;
}
diff --git a/source/EditorResources.hpp b/source/EditorResources.hpp
index db6a277..81145a3 100644
--- a/source/EditorResources.hpp
+++ b/source/EditorResources.hpp
@@ -2,12 +2,15 @@
#include "Shader.hpp"
+#include <string>
+
class EditorInstance;
class EditorContentBrowser {
private:
enum Pane {
P_Settings,
P_Shader,
+ P_Material,
};
static constexpr float kSplitterThickness = 3.0f;
diff --git a/source/EditorUtils.cpp b/source/EditorUtils.cpp
index ab6ffad..a0d2fc7 100644
--- a/source/EditorUtils.cpp
+++ b/source/EditorUtils.cpp
@@ -98,39 +98,6 @@ static int InputTextCallback(ImGuiInputTextCallbackData* data) {
return 0;
}
-bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) {
- IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
- flags |= ImGuiInputTextFlags_CallbackResize;
-
- InputTextCallbackUserData cbUserData;
- cbUserData.str = str;
- cbUserData.chainCallback = callback;
- cbUserData.chainCallbackUserData = user_data;
- return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cbUserData);
-}
-
-bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* userData) {
- IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
- flags |= ImGuiInputTextFlags_CallbackResize;
-
- InputTextCallbackUserData cbUserData;
- cbUserData.str = str;
- cbUserData.chainCallback = callback;
- cbUserData.chainCallbackUserData = userData;
- return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cbUserData);
-}
-
-bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* userData) {
- IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
- flags |= ImGuiInputTextFlags_CallbackResize;
-
- InputTextCallbackUserData cbUserData;
- cbUserData.str = str;
- cbUserData.chainCallback = callback;
- cbUserData.chainCallbackUserData = userData;
- return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cbUserData);
-}
-
bool ImGui::Splitter(bool splitVertically, float thickness, float* size1, float* size2, float minSize1, float minSize2, float splitterLongAxisSize) {
// Adapted from https://github.com/thedmd/imgui-node-editor/blob/master/examples/blueprints-example/blueprints-example.cpp
// ::Splitter
diff --git a/source/EditorUtils.hpp b/source/EditorUtils.hpp
index 27510fe..090f7f6 100644
--- a/source/EditorUtils.hpp
+++ b/source/EditorUtils.hpp
@@ -5,6 +5,9 @@
#include <imgui.h>
#include <string>
+#define BRUSSEL_DRAG_DROP_SHADER "Shader"
+#define BRUSSEL_DRAG_DROP_MATERIAL "Mat"
+
namespace ImGui {
const char* GetKeyNameGlfw(int key);
@@ -23,10 +26,6 @@ bool ColorEdit4(const char* label, RgbaColor* color, ImGuiColorEditFlags flags =
bool ColorPicker3(const char* label, RgbaColor* color, ImGuiColorEditFlags flags = 0);
bool ColorPicker4(const char* label, RgbaColor* color, ImGuiColorEditFlags flags = 0);
-bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
-bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* userData = nullptr);
-bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* userData = nullptr);
-
bool Splitter(bool splitVertically, float thickness, float* size1, float* size2, float minSize1, float minSize2, float splitterLongAxisSize = -1.0f);
} // namespace ImGui
diff --git a/source/Material.cpp b/source/Material.cpp
index 138434c..db76b21 100644
--- a/source/Material.cpp
+++ b/source/Material.cpp
@@ -1,15 +1,43 @@
#include "Material.hpp"
+#include "AppConfig.hpp"
+#include "RapidJsonHelper.hpp"
+#include "ScopeGuard.hpp"
+#include "Utils.hpp"
+
+#include <rapidjson/document.h>
+#include <rapidjson/filereadstream.h>
+#include <rapidjson/filewritestream.h>
+#include <rapidjson/writer.h>
#include <cstdlib>
#include <cstring>
+#include <utility>
+
+namespace fs = std::filesystem;
+using namespace std::literals;
-Material::Material(Shader* shader)
- : mShader(shader) {
+Material::Material(Shader* shader, std::string name)
+ : mShader(shader)
+ , mName(std::move(name)) {
}
namespace ProjectBrussel_UNITY_ID {
+bool TryFindShaderId(Shader* shader, std::string_view name, int& out) {
+ auto info = shader->GetInfo();
+ if (!info) return false;
+
+ auto iter = info->things.find(name);
+ if (iter == info->things.end()) return false;
+ auto& id = iter->second;
+
+ if (id.kind != ShaderThingId::KD_Uniform) return false;
+
+ out = id.index;
+ return true;
+}
+
template <class TUniform>
-TUniform& ObtainUniform(std::vector<TUniform>& uniforms, GLint location) {
+TUniform& ObtainUniform(Shader* shader, const char* name, std::vector<TUniform>& uniforms, GLint location) {
for (auto& uniform : uniforms) {
if (uniform.location == location) {
return uniform;
@@ -18,37 +46,114 @@ TUniform& ObtainUniform(std::vector<TUniform>& uniforms, GLint location) {
auto& uniform = uniforms.emplace_back();
uniform.location = location;
+ if (!TryFindShaderId(shader, name, uniform.infoUniformIndex)) {
+ uniform.infoUniformIndex = -1;
+ }
+
return uniform;
}
+
+rapidjson::Value MakeVectorJson(const Material::VectorUniform& vector, rapidjson::Document& root) {
+ int len = vector.actualLength;
+
+ rapidjson::Value result(rapidjson::kArrayType);
+ result.Reserve(len, root.GetAllocator());
+
+ for (int i = 0; i < len; ++i) {
+ result.PushBack(vector.value[i], root.GetAllocator());
+ }
+
+ return result;
+}
+
+Material::VectorUniform ReadVectorFromJson(const rapidjson::Value& rv) {
+ assert(rv.IsArray());
+ Material::VectorUniform result;
+ int len = result.actualLength = rv.Size();
+ for (int i = 0; i < len; ++i) {
+ result.value[i] = rv[i].GetFloat();
+ }
+ return result;
+}
+
+rapidjson::Value MakeMatrixJson(const Material::MatrixUniform& matrix, rapidjson::Document& root) {
+ int w = matrix.actualWidth;
+ int h = matrix.actualHeight;
+
+ rapidjson::Value result(rapidjson::kArrayType);
+ result.Reserve(h, root.GetAllocator());
+
+ for (int y = 0; y < h; ++y) {
+ rapidjson::Value row(rapidjson::kArrayType);
+ row.Reserve(w, root.GetAllocator());
+
+ for (int x = 0; x < w; ++x) {
+ // Each item in a column is consecutive in memory in glm::mat<> structs
+ row.PushBack(matrix.value[x * h + y], root.GetAllocator());
+ }
+
+ result.PushBack(row, root.GetAllocator());
+ }
+
+ return result;
+}
+
+Material::MatrixUniform ReadMatrixFromjson(const rapidjson::Value& rv) {
+ assert(rv.IsArray());
+ assert(rv.Size() > 0);
+ assert(rv[0].IsArray());
+ Material::MatrixUniform result;
+ int w = result.actualWidth = rv[0].Size();
+ int h = result.actualHeight = rv.Size();
+ for (int y = 0; y < h; ++y) {
+ auto& row = rv[y];
+ assert(row.IsArray());
+ assert(row.Size() == w);
+ for (int x = 0; x < w; ++x) {
+ auto& val = row[x];
+ assert(val.IsFloat());
+ result.value[x * h + y] = val.GetFloat();
+ }
+ }
+ return result;
+}
} // namespace ProjectBrussel_UNITY_ID
void Material::SetFloat(const char* name, float value) {
+ assert(IsValid());
+
GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mBoundScalars, location);
+ auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundScalars, location);
uniform.floatValue = value;
uniform.actualType = GL_FLOAT;
}
void Material::SetInt(const char* name, int32_t value) {
+ assert(IsValid());
+
GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mBoundScalars, location);
+ auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundScalars, location);
uniform.intValue = value;
uniform.actualType = GL_INT;
}
void Material::SetUInt(const char* name, uint32_t value) {
+ assert(IsValid());
+
GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mBoundScalars, location);
+ auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundScalars, location);
uniform.uintValue = value;
uniform.actualType = GL_UNSIGNED_INT;
}
template <int length>
void Material::SetVector(const char* name, const glm::vec<length, float>& vec) {
+ assert(IsValid());
+
static_assert(length >= 1 && length <= 4);
GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mBoundVectors, location);
+ auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundVectors, location);
uniform.actualLength = length;
std::memset(uniform.value, 0, sizeof(uniform.value));
std::memcpy(uniform.value, &vec[0], length * sizeof(float));
@@ -65,7 +170,7 @@ void Material::SetMatrix(const char* name, const glm::mat<width, height, float>&
static_assert(height >= 1 && height <= 4);
GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mBoundMatrices, location);
+ auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundMatrices, location);
uniform.actualWidth = width;
uniform.actualHeight = height;
std::memset(uniform.value, 0, sizeof(uniform.value));
@@ -86,6 +191,8 @@ template void Material::SetMatrix<3, 4>(const char*, const glm::mat<3, 4, float>
template void Material::SetMatrix<4, 3>(const char*, const glm::mat<4, 3, float>&);
void Material::SetTexture(const char* name, Texture* texture) {
+ assert(IsValid());
+
GLint location = glGetUniformLocation(mShader->GetProgram(), name);
for (auto& uniform : mBoundTextures) {
@@ -112,8 +219,25 @@ std::span<const Material::TextureUniform> Material::GetTextures() const {
return mBoundTextures;
}
-const Shader& Material::GetShader() const {
- return *mShader;
+Shader* Material::GetShader() const {
+ return mShader.Get();
+}
+
+void Material::SetShader(Shader* shader) {
+ // TODO validate uniforms?
+ mShader.Attach(shader);
+}
+
+const std::string& Material::GetName() const {
+ return mName;
+}
+
+bool Material::IsAnnoymous() const {
+ return mName.empty();
+}
+
+bool Material::IsValid() const {
+ return mShader != nullptr;
}
static constexpr int IdentifyMatrixSize(int width, int height) {
@@ -167,3 +291,221 @@ void Material::UseUniforms() const {
++i;
}
}
+
+void Material::GetDesignatedPath(char* buffer, int bufferSize) {
+ snprintf(buffer, bufferSize, "%s/Materials/%s.json", AppConfig::assetDir.c_str(), mName.c_str());
+}
+
+fs::path Material::GetDesignatedPath() {
+ return AppConfig::assetDirPath / "Materials" / fs::path(mName).replace_extension(".json");
+}
+
+bool Material::SaveToFile(const fs::path& filePath) const {
+ using namespace ProjectBrussel_UNITY_ID;
+
+ if (IsAnnoymous()) return false;
+ if (!IsValid()) return false;
+
+ auto info = mShader->GetInfo();
+
+ rapidjson::Document root(rapidjson::kObjectType);
+
+ root.AddMember("Name", mName, root.GetAllocator());
+ root.AddMember("ShaderName", mShader->GetName(), root.GetAllocator());
+
+ rapidjson::Value fields(rapidjson::kArrayType);
+ for (auto& scalar : mBoundScalars) {
+ rapidjson::Value rvField(rapidjson::kObjectType);
+ if (info) rvField.AddMember("Name", info->uniforms[scalar.infoUniformIndex]->name, root.GetAllocator());
+ rvField.AddMember("Type", "Scalar", root.GetAllocator());
+ switch (scalar.actualType) {
+ case GL_FLOAT: rvField.AddMember("Value", scalar.floatValue, root.GetAllocator()); break;
+ case GL_INT: rvField.AddMember("Value", scalar.intValue, root.GetAllocator()); break;
+ case GL_UNSIGNED_INT: rvField.AddMember("Value", scalar.uintValue, root.GetAllocator()); break;
+ }
+ fields.PushBack(rvField, root.GetAllocator());
+ }
+ for (auto& vector : mBoundVectors) {
+ rapidjson::Value rvField(rapidjson::kObjectType);
+ if (info) rvField.AddMember("Name", info->uniforms[vector.infoUniformIndex]->name, root.GetAllocator());
+ rvField.AddMember("Type", "Vector", root.GetAllocator());
+ rvField.AddMember("Value", MakeVectorJson(vector, root).Move(), root.GetAllocator());
+ fields.PushBack(rvField, root.GetAllocator());
+ }
+ for (auto& matrix : mBoundMatrices) {
+ rapidjson::Value rvField(rapidjson::kObjectType);
+ if (info) rvField.AddMember("Name", info->uniforms[matrix.infoUniformIndex]->name, root.GetAllocator());
+ rvField.AddMember("Type", "Matrix", root.GetAllocator());
+ rvField.AddMember("Value", MakeMatrixJson(matrix, root).Move(), root.GetAllocator());
+ fields.PushBack(rvField, root.GetAllocator());
+ }
+ for (auto& texture : mBoundTextures) {
+ // TODO
+ }
+ root.AddMember("Fields", fields, root.GetAllocator());
+
+ auto file = Utils::OpenCstdioFile(filePath, Utils::WriteTruncate);
+ if (!file) return false;
+ DEFER { fclose(file); };
+
+ char writerBuffer[65536];
+ rapidjson::FileWriteStream stream(file, writerBuffer, sizeof(writerBuffer));
+ rapidjson::Writer<rapidjson::FileWriteStream> writer(stream);
+ root.Accept(writer);
+
+ return true;
+}
+
+bool Material::LoadFromFile(const fs::path& filePath) {
+ using namespace ProjectBrussel_UNITY_ID;
+
+ auto file = Utils::OpenCstdioFile(filePath, Utils::Read);
+ if (!file) return false;
+ DEFER { fclose(file); };
+
+ char readerBuffer[65536];
+ rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer));
+
+ rapidjson::Document root;
+ root.ParseStream(stream);
+
+ // TODO update reference in MaterialManager
+ {
+ auto rvName = rapidjson::GetProperty(root, rapidjson::kStringType, "Name"sv);
+ if (rvName) {
+ mName = rapidjson::AsString(*rvName);
+ } else {
+ mName = "";
+ }
+ }
+
+ {
+ auto rvShaderName = rapidjson::GetProperty(root, rapidjson::kStringType, "ShaderName"sv);
+ if (!rvShaderName) return false;
+
+ auto shader = ShaderManager::instance->FindShader(rapidjson::AsStringView(*rvShaderName));
+ if (!shader) return false;
+
+ mShader.Attach(shader);
+ }
+
+ auto fields = rapidjson::GetProperty(root, rapidjson::kArrayType, "Fields"sv);
+ if (!fields) return false;
+
+ for (auto& rvField : fields->GetArray()) {
+ if (!rvField.IsObject()) continue;
+
+ auto rvName = rapidjson::GetProperty(rvField, rapidjson::kStringType, "Name"sv);
+
+ auto rvType = rapidjson::GetProperty(rvField, rapidjson::kStringType, "Type"sv);
+ if (!rvType) continue;
+ auto type = rapidjson::AsStringView(*rvType);
+
+ auto rvValue = rapidjson::GetProperty(rvField, "Value"sv);
+
+ if (type == "Scalar"sv) {
+ ScalarUniform uniform;
+ if (rvName) {
+ TryFindShaderId(mShader.Get(), rapidjson::AsStringView(*rvName), uniform.infoUniformIndex);
+ }
+ if (rvValue->IsFloat()) {
+ uniform.actualType = GL_FLOAT;
+ uniform.floatValue = rvValue->GetFloat();
+ } else if (rvValue->IsInt()) {
+ uniform.actualType = GL_INT;
+ uniform.intValue = rvValue->GetInt();
+ } else if (rvValue->IsUint()) {
+ uniform.actualType = GL_UNSIGNED_INT;
+ uniform.uintValue = rvValue->GetUint();
+ }
+ mBoundScalars.push_back(std::move(uniform));
+ } else if (type == "Vector"sv) {
+ auto uniform = ReadVectorFromJson(*rvValue);
+ if (rvName) {
+ TryFindShaderId(mShader.Get(), rapidjson::AsStringView(*rvName), uniform.infoUniformIndex);
+ }
+ mBoundVectors.push_back(std::move(uniform));
+ } else if (type == "Matrix"sv) {
+ auto uniform = ReadMatrixFromjson(*rvValue);
+ if (rvName) {
+ TryFindShaderId(mShader.Get(), rapidjson::AsStringView(*rvName), uniform.infoUniformIndex);
+ }
+ mBoundMatrices.push_back(uniform);
+ } else if (type == "Texture"sv) {
+ // TODO
+ }
+ }
+
+ return true;
+}
+
+void MaterialManager::DiscoverMaterials() {
+ mMaterials.clear();
+
+ auto path = AppConfig::assetDirPath / "Materials";
+ if (!fs::exists(path)) {
+ return;
+ }
+
+ for (auto& item : fs::directory_iterator(path)) {
+ if (item.is_regular_file()) {
+ RcPtr mat(new Material());
+ if (!mat->LoadFromFile(item.path())) {
+ continue;
+ }
+
+ auto& matName = mat->GetName();
+ mMaterials.try_emplace(matName, std::move(mat));
+ }
+ }
+}
+
+std::pair<Material*, bool> MaterialManager::SaveMaterial(Material* mat) {
+ // NOTE: we explicitly allow invalid materials (i.e. without a shader)
+ if (mat->IsAnnoymous()) return { nullptr, false };
+
+ auto [iter, inserted] = mMaterials.try_emplace(mat->GetName(), mat);
+ if (inserted) {
+ mat->SaveToFile(mat->GetDesignatedPath());
+ return { mat, true };
+ } else {
+ return { iter->second.Get(), false };
+ }
+}
+
+void MaterialManager::DeleteMaterial(Material* mat, bool onDisk) {
+ // TODO
+ assert(false && "unimplemented");
+}
+
+Material* MaterialManager::LoadMaterial(std::string_view name) {
+ // TODO
+ assert(false && "unimplemented");
+}
+
+bool MaterialManager::RenameMaterial(Material* mat, std::string newName) {
+ if (mMaterials.contains(newName)) {
+ return false;
+ }
+
+ // Keep the material from being deleted, in case the old entry in map is the only one existing
+ RcPtr rc(mat);
+
+ // Remove old entry (must do before replacing Material::mName, because the std::string_view in the map is a reference to it)
+ mMaterials.erase(mat->GetName());
+
+ // Add new entry
+ mat->mName = std::move(newName);
+ mMaterials.try_emplace(mat->GetName(), mat);
+
+ return true;
+}
+
+Material* MaterialManager::FindMaterial(std::string_view name) {
+ auto iter = mMaterials.find(name);
+ if (iter != mMaterials.end()) {
+ return iter->second.Get();
+ } else {
+ return nullptr;
+ }
+}
diff --git a/source/Material.hpp b/source/Material.hpp
index 6290a25..6b431be 100644
--- a/source/Material.hpp
+++ b/source/Material.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "EditorAttachment.hpp"
#include "RcPtr.hpp"
#include "Shader.hpp"
#include "Texture.hpp"
@@ -7,15 +8,18 @@
#include <glad/glad.h>
#include <cstddef>
#include <cstdint>
+#include <filesystem>
#include <glm/glm.hpp>
#include <memory>
#include <span>
#include <string_view>
#include <vector>
-// TODO support multiple sizes of vectors and matrices
+class MaterialManager;
class Material : public RefCounted {
-public:
+public: // NOTE: public for internal helpers and editor
+ // NOTE: specialize between scalar vs matrix vs vector to save memory
+
struct ScalarUniform {
union {
float floatValue;
@@ -23,27 +27,33 @@ public:
uint32_t uintValue;
};
GLenum actualType;
- GLint location;
+ /* Saves 'name' */ int infoUniformIndex;
+ /* Transient */ GLint location;
};
struct VectorUniform {
float value[4];
int actualLength;
- GLint location;
+ /* Saves 'name' */ int infoUniformIndex;
+ /* Transient */ GLint location;
};
struct MatrixUniform {
float value[16];
int actualWidth;
int actualHeight;
- GLint location;
+ /* Saves 'name' */ int infoUniformIndex;
+ /* Transient */ GLint location;
};
struct TextureUniform {
RcPtr<Texture> value;
- GLint location;
+ /* Saves 'name' */ int infoUniformIndex;
+ /* Transient */ GLint location;
};
+ std::string mName;
+ std::unique_ptr<EditorAttachment> mEditorAttachment;
RcPtr<Shader> mShader;
std::vector<ScalarUniform> mBoundScalars;
std::vector<VectorUniform> mBoundVectors;
@@ -51,7 +61,9 @@ public:
std::vector<TextureUniform> mBoundTextures;
public:
- Material(Shader* shader);
+ // NOTE: constructs invalid object
+ Material() = default;
+ Material(Shader* shader, std::string name = "");
void SetFloat(const char* name, float value);
void SetInt(const char* name, int32_t value);
@@ -70,7 +82,40 @@ public:
std::span<const VectorUniform> GetVectors() const;
std::span<const MatrixUniform> GetMatrices() const;
std::span<const TextureUniform> GetTextures() const;
- const Shader& GetShader() const;
+ Shader* GetShader() const;
+ void SetShader(Shader* shader);
+ const std::string& GetName() const;
+
+ bool IsAnnoymous() const;
+ bool IsValid() const;
void UseUniforms() const;
+
+ EditorAttachment* GetEditorAttachment() const { return mEditorAttachment.get(); }
+ void SetEditorAttachment(EditorAttachment* attachment) { mEditorAttachment.reset(attachment); }
+
+ void GetDesignatedPath(char* buffer, int bufferSize);
+ std::filesystem::path GetDesignatedPath();
+
+ bool SaveToFile(const std::filesystem::path& filePath) const;
+ bool LoadFromFile(const std::filesystem::path& filePath);
+};
+
+class MaterialManager {
+public:
+ static inline MaterialManager* instance = nullptr;
+
+private:
+ absl::flat_hash_map<std::string_view, RcPtr<Material>> mMaterials;
+
+public:
+ void DiscoverMaterials();
+
+ std::pair<Material*, bool> SaveMaterial(Material* mat);
+ void DeleteMaterial(Material* mat, bool onDisk = false);
+ Material* LoadMaterial(std::string_view name);
+ bool RenameMaterial(Material* mat, std::string newName);
+
+ const auto& GetMaterials() const { return mMaterials; }
+ Material* FindMaterial(std::string_view name);
};
diff --git a/source/RapidJsonHelper.hpp b/source/RapidJsonHelper.hpp
index ac1f664..9dc0701 100644
--- a/source/RapidJsonHelper.hpp
+++ b/source/RapidJsonHelper.hpp
@@ -31,9 +31,20 @@
namespace rapidjson {
+inline const Value* GetProperty(const Value& value, std::string_view name) {
+ for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
+ if (it->name.GetStringLength() != name.size()) continue;
+ if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue;
+
+ return &it->value;
+ }
+ return nullptr;
+}
+
inline const Value* GetProperty(const Value& value, Type type, std::string_view name) {
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
if (it->name.GetStringLength() != name.size()) continue;
+ if (it->value.GetType() != type) continue;
if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue;
return &it->value;
diff --git a/source/ScopeGuard.hpp b/source/ScopeGuard.hpp
index b4a1749..28f3385 100644
--- a/source/ScopeGuard.hpp
+++ b/source/ScopeGuard.hpp
@@ -49,5 +49,12 @@ public:
}
};
+template <class T>
+auto GuardDeletion(T* ptr) {
+ return ScopeGuard([ptr]() {
+ delete ptr;
+ });
+}
+
#define SCOPE_GUARD(name) ScopeGuard name = [&]()
#define DEFER ScopeGuard UNIQUE_NAME(scopeGuard) = [&]()
diff --git a/source/Shader.cpp b/source/Shader.cpp
index 3ff0d10..4a576d0 100644
--- a/source/Shader.cpp
+++ b/source/Shader.cpp
@@ -10,6 +10,7 @@
#include <rapidjson/filereadstream.h>
#include <rapidjson/filewritestream.h>
#include <rapidjson/writer.h>
+#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <utility>
@@ -17,6 +18,20 @@
namespace fs = std::filesystem;
using namespace std::literals;
+bool ShaderThingId::IsValid() const {
+ return kind == KD_Invalid;
+}
+
+ShaderVariable* ShaderInfo::FindVariable(const ShaderThingId& thing) {
+ switch (thing.kind) {
+ case ShaderThingId::KD_Input: return &inputs[thing.index].variable;
+ case ShaderThingId::KD_Output: return &outputs[thing.index].variable;
+ case ShaderThingId::KD_Uniform: return uniforms[thing.index].get();
+ case ShaderThingId::KD_Invalid: break;
+ }
+ return nullptr;
+}
+
bool ShaderInfo::SaveToFile(const fs::path& filePath) const {
rapidjson::Document root(rapidjson::kObjectType);
@@ -103,6 +118,16 @@ bool ShaderInfo::LoadFromFile(const fs::path& filePath) {
return true;
}
+void ShaderInfo::LoadLocations(const Shader& ownerShader) {
+ GLuint program = ownerShader.GetProgram();
+
+ // TODO inputs
+ // TODO outputs
+ for (auto& uniform : uniforms) {
+ uniform->location = glGetUniformLocation(ownerShader.GetProgram(), uniform->name.c_str());
+ }
+}
+
Shader::Shader(std::string name)
: mName{ name } {
}
@@ -337,6 +362,14 @@ Shader::ErrorCode Shader::InitFromSource(std::string_view source) {
#undef CATCH_ERROR
+void Shader::GetDesignatedMetadataPath(char* buffer, int bufferSize) {
+ snprintf(buffer, bufferSize, "%s/Shaders/%s.json", AppConfig::assetDir.c_str(), mName.c_str());
+}
+
+fs::path Shader::GetDesignatedMetadataPath() {
+ return AppConfig::assetDirPath / "Shaders" / fs::path(mName).replace_extension(".json");
+}
+
namespace ProjectBrussel_UNITY_ID {
bool QueryMathInfo(GLenum type, GLenum& scalarType, int& width, int& height) {
auto DoOutput = [&](GLenum scalarTypeIn, int widthIn, int heightIn) {
@@ -476,7 +509,6 @@ bool Shader::CreateEmptyInfoIfAbsent() {
bool Shader::GatherInfoIfAbsent() {
using namespace ProjectBrussel_UNITY_ID;
- using ThingId = ShaderInfo::ThingId;
if (mInfo || !IsValid()) {
return false;
@@ -487,7 +519,8 @@ bool Shader::GatherInfoIfAbsent() {
// TODO handle differnt types of variables with the same name
// TODO work with OpenGL < 4.3, possibly with glslang
-#if 0
+ return true;
+
int inputCount;
glGetProgramInterfaceiv(mHandle, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &inputCount);
int outputCount;
@@ -498,7 +531,7 @@ bool Shader::GatherInfoIfAbsent() {
glGetProgramInterfaceiv(mHandle, GL_UNIFORM, GL_ACTIVE_RESOURCES, &uniformCount);
// Gather inputs
- auto GatherMathVars = [&](int count, GLenum resourceType, ShaderInfo::ThingKind resourceKind, std::vector<ShaderInfo::InputOutputThing>& list) {
+ auto GatherMathVars = [&](int count, GLenum resourceType, ShaderThingId::Kind resourceKind, std::vector<ShaderInfo::InputOutputThing>& list) {
for (int i = 0; i < count; ++i) {
const GLenum query[] = { GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_TOP_LEVEL_ARRAY_SIZE };
GLint props[std::size(query)];
@@ -511,7 +544,7 @@ bool Shader::GatherInfoIfAbsent() {
std::string fieldName(nameLength - 1, '\0');
glGetProgramResourceName(mHandle, GL_UNIFORM, i, nameLength, nullptr, fieldName.data());
- mInfo->things.insert(fieldName, ThingId{ resourceKind, i });
+ mInfo->things.try_emplace(fieldName, ShaderThingId{ resourceKind, i });
auto& thing = list.emplace_back(ShaderInfo::InputOutputThing{});
auto& var = thing.variable;
@@ -520,8 +553,8 @@ bool Shader::GatherInfoIfAbsent() {
QueryMathInfo(type, var.scalarType, var.width, var.height);
}
};
- GatherMathVars(inputCount, GL_PROGRAM_INPUT, ShaderInfo::TKD_Input, mInfo->inputs);
- GatherMathVars(outputCount, GL_PROGRAM_OUTPUT, ShaderInfo::TKD_Output, mInfo->outputs);
+ GatherMathVars(inputCount, GL_PROGRAM_INPUT, ShaderThingId::KD_Input, mInfo->inputs);
+ GatherMathVars(outputCount, GL_PROGRAM_OUTPUT, ShaderThingId::KD_Output, mInfo->outputs);
// Gather uniform variables
for (int i = 0; i < uniformCount; ++i) {
@@ -540,13 +573,10 @@ bool Shader::GatherInfoIfAbsent() {
std::string fieldName(nameLength - 1, '\0');
glGetProgramResourceName(mHandle, GL_UNIFORM, i, nameLength, nullptr, fieldName.data());
- mInfo->things.insert(fieldName, ThingId{ ShaderInfo::TKD_Uniform, i });
+ mInfo->things.try_emplace(fieldName, ShaderThingId{ ShaderThingId::KD_Uniform, i });
mInfo->uniforms.push_back(CreateVariable(type, loc));
}
- mInfo->things.shrink_to_fit();
-#endif
-
return true;
}
diff --git a/source/Shader.hpp b/source/Shader.hpp
index cda5531..79262a6 100644
--- a/source/Shader.hpp
+++ b/source/Shader.hpp
@@ -13,13 +13,18 @@
// TODO move to variable after pattern matching is in the language
+// Forward declarations
+class Shader;
+
struct ShaderVariable {
enum Kind {
KD_Math,
KD_Sampler,
};
+ std::string name;
Kind kind;
+ GLuint location;
protected:
ShaderVariable(Kind kind)
@@ -27,8 +32,6 @@ protected:
};
struct ShaderMathVariable : public ShaderVariable {
- std::string name;
- GLuint location;
GLenum scalarType;
int arrayLength;
int width;
@@ -39,8 +42,6 @@ struct ShaderMathVariable : public ShaderVariable {
};
struct ShaderSamplerVariable : public ShaderVariable {
- std::string name;
- GLuint location;
GLenum type;
int arrayLength;
@@ -48,31 +49,36 @@ struct ShaderSamplerVariable : public ShaderVariable {
: ShaderVariable(KD_Sampler) {}
};
-struct ShaderInfo {
- enum ThingKind {
- TKD_Input,
- TKD_Output,
- TKD_Uniform,
- TKD_UniformBlock,
+struct ShaderThingId {
+ enum Kind {
+ KD_Input,
+ KD_Output,
+ KD_Uniform,
+ KD_Invalid,
};
- struct ThingId {
- ThingKind kind;
- int index;
- };
+ Kind kind = KD_Invalid;
+ int index = 0;
+
+ bool IsValid() const;
+};
+struct ShaderInfo {
struct InputOutputThing {
ShaderMathVariable variable;
Tags::VertexElementSemantic semantic = Tags::VES_Generic;
};
- absl::flat_hash_map<std::string, ThingId> things;
+ absl::flat_hash_map<std::string, ShaderThingId> things;
std::vector<InputOutputThing> inputs;
std::vector<InputOutputThing> outputs;
std::vector<std::unique_ptr<ShaderVariable>> uniforms;
+ ShaderVariable* FindVariable(const ShaderThingId& thing);
+
bool SaveToFile(const std::filesystem::path& filePath) const;
bool LoadFromFile(const std::filesystem::path& filePath);
+ void LoadLocations(const Shader& ownerShader);
};
class Shader : public RefCounted {
@@ -129,6 +135,9 @@ public:
EditorAttachment* GetEditorAttachment() const { return mEditorAttachment.get(); }
void SetEditorAttachment(EditorAttachment* attachment) { mEditorAttachment.reset(attachment); }
+ void GetDesignatedMetadataPath(char* buffer, int bufferSize);
+ std::filesystem::path GetDesignatedMetadataPath();
+
bool CreateEmptyInfoIfAbsent();
bool GatherInfoIfAbsent();
ShaderInfo* GetInfo() const;
diff --git a/source/Texture.cpp b/source/Texture.cpp
index 968c4bc..08641e1 100644
--- a/source/Texture.cpp
+++ b/source/Texture.cpp
@@ -97,3 +97,16 @@ GLuint Texture::GetHandle() const {
bool Texture::IsValid() const {
return mHandle != 0;
}
+
+void TextureManager::DiscoverTextures() {
+ // TODO
+}
+
+Texture* TextureManager::FindTexture(std::string_view name) {
+ auto iter = mTextures.find(name);
+ if (iter != mTextures.end()) {
+ return iter->second.Get();
+ } else {
+ return nullptr;
+ }
+}
diff --git a/source/Texture.hpp b/source/Texture.hpp
index c372998..c330bb3 100644
--- a/source/Texture.hpp
+++ b/source/Texture.hpp
@@ -2,6 +2,7 @@
#include "RcPtr.hpp"
+#include <absl/container/flat_hash_map.h>
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <memory>
@@ -64,3 +65,17 @@ struct Subregion {
struct TextureSubregion : public Subregion {
RcPtr<Texture> atlasTexture;
};
+
+class TextureManager {
+public:
+ static inline TextureManager* instance = nullptr;
+
+private:
+ absl::flat_hash_map<std::string_view, RcPtr<Texture>> mTextures;
+
+public:
+ void DiscoverTextures();
+
+ const auto& GetTextures() const { return mTextures; }
+ Texture* FindTexture(std::string_view name);
+};
diff --git a/source/main.cpp b/source/main.cpp
index f7cde70..8f16403 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -2,18 +2,20 @@
#include "AppConfig.hpp"
#include "EditorNotification.hpp"
+#include "Material.hpp"
#include "Shader.hpp"
+#include "Texture.hpp"
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
-#include <filesystem>
#include <backends/imgui_impl_glfw.h>
#include <backends/imgui_impl_opengl3.h>
#include <glad/glad.h>
#include <imgui.h>
#include <cstdlib>
#include <cxxopts.hpp>
+#include <filesystem>
#include <string>
namespace fs = std::filesystem;
@@ -26,7 +28,7 @@ static void GlfwMouseCallback(GLFWwindow* window, int button, int action, int mo
if (ImGui::GetIO().WantCaptureMouse) {
return;
}
-
+
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
app->HandleMouse(button, action);
}
@@ -35,7 +37,7 @@ static void GlfwMouseMotionCallback(GLFWwindow* window, double xOff, double yOff
if (ImGui::GetIO().WantCaptureMouse) {
return;
}
-
+
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
app->HandleMouseMotion(xOff, yOff);
}
@@ -44,7 +46,7 @@ static void GlfwKeyCallback(GLFWwindow* window, int key, int scancode, int actio
if (ImGui::GetIO().WantCaptureKeyboard) {
return;
}
-
+
GLFWkeyboard* keyboard = glfwGetLastActiveKeyboard();
if (keyboard) {
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
@@ -55,7 +57,7 @@ static void GlfwKeyCallback(GLFWwindow* window, int key, int scancode, int actio
int main(int argc, char* argv[]) {
constexpr auto kGameDataDir = "game-data-directory";
constexpr auto kGameAssetDir = "game-asset-directory";
-
+
cxxopts::Options options(std::string(AppConfig::kAppName), "");
// clang-format off
options.add_options()
@@ -64,26 +66,26 @@ int main(int argc, char* argv[]) {
;
// clang-format on
auto args = options.parse(argc, argv);
-
+
{
auto assetDir = args[kGameAssetDir].as<std::string>();
-
+
fs::path assetDirPath(assetDir);
if (!fs::exists(assetDirPath)) {
fprintf(stderr, "Invalid asset directory.\n");
return -4;
}
-
+
AppConfig::assetDir = std::move(assetDir);
AppConfig::assetDirPath = std::move(assetDirPath);
}
-
+
if (args.count(kGameDataDir) > 0) {
auto dataDir = args[kGameDataDir].as<std::string>();
-
+
fs::path dataDirPath(dataDir);
fs::create_directories(dataDir);
-
+
AppConfig::dataDir = std::move(dataDir);
AppConfig::dataDirPath = std::move(dataDirPath);
} else {
@@ -91,13 +93,13 @@ int main(int argc, char* argv[]) {
AppConfig::dataDir = ".";
AppConfig::dataDirPath = fs::path(".");
}
-
+
if (!glfwInit()) {
return -1;
}
-
+
glfwSetErrorCallback(&GlfwErrorCallback);
-
+
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100
@@ -118,48 +120,53 @@ int main(int argc, char* argv[]) {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
#endif
-
+
App app;
-
+
GLFWwindow* window = glfwCreateWindow(1280, 720, "ImGui Command Palette Example", nullptr, nullptr);
if (window == nullptr) {
return -2;
}
-
+
glfwSetWindowUserPointer(window, &app);
-
+
// Window callbacks are retained by ImGui GLFW backend
glfwSetKeyCallback(window, &GlfwKeyCallback);
glfwSetMouseButtonCallback(window, &GlfwMouseCallback);
glfwSetCursorPosCallback(window, &GlfwMouseMotionCallback);
-
+
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
-
+
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
return -3;
}
-
+
IMGUI_CHECKVERSION();
ImGui::CreateContext();
-
+
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
-
- ShaderManager::instance = new ShaderManager();
- ShaderManager::instance->DiscoverShaders();
-
+
+ ShaderManager::instance = new ShaderManager();
+ TextureManager::instance = new TextureManager();
+ MaterialManager::instance = new MaterialManager();
+
+ ShaderManager::instance->DiscoverShaders();
+ TextureManager::instance->DiscoverTextures();
+ MaterialManager::instance->DiscoverMaterials();
+
app.Init();
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
-
+
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
-
+
app.Show();
ImGui::ShowNotifications();
-
+
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);
@@ -168,18 +175,18 @@ int main(int argc, char* argv[]) {
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
-
+
glfwSwapBuffers(window);
}
app.Shutdown();
-
+
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
-
+
ImGui::DestroyContext();
-
+
glfwDestroyWindow(window);
glfwTerminate();
-
+
return 0;
}