diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/EditorAttachmentImpl.cpp | 7 | ||||
-rw-r--r-- | source/EditorAttachmentImpl.hpp | 7 | ||||
-rw-r--r-- | source/EditorCore.cpp | 95 | ||||
-rw-r--r-- | source/EditorCore.hpp | 18 | ||||
-rw-r--r-- | source/EditorInspector.cpp | 15 | ||||
-rw-r--r-- | source/EditorInspector.hpp | 17 | ||||
-rw-r--r-- | source/EditorResources.cpp | 74 | ||||
-rw-r--r-- | source/EditorResources.hpp | 12 | ||||
-rw-r--r-- | source/GameObject.cpp | 2 | ||||
-rw-r--r-- | source/GameObject.hpp | 2 | ||||
-rw-r--r-- | source/GameObjectTags.hpp (renamed from source/GameObjectTypeTag.hpp) | 0 | ||||
-rw-r--r-- | source/GraphicsTags.cpp | 215 | ||||
-rw-r--r-- | source/GraphicsTags.hpp | 85 | ||||
-rw-r--r-- | source/Mesh.cpp | 61 | ||||
-rw-r--r-- | source/Mesh.hpp | 74 | ||||
-rw-r--r-- | source/Player.cpp | 5 | ||||
-rw-r--r-- | source/Player.hpp | 2 | ||||
-rw-r--r-- | source/RapidJsonHelper.hpp | 67 | ||||
-rw-r--r-- | source/Shader.cpp | 411 | ||||
-rw-r--r-- | source/Shader.hpp | 101 |
21 files changed, 1004 insertions, 268 deletions
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 0749b62..f851bcb 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -3,11 +3,13 @@ PRIVATE App.cpp CpuMesh.cpp EditorAccessories.cpp + EditorAttachmentImpl.cpp EditorCore.cpp EditorNotification.cpp EditorResources.cpp EditorUtils.cpp GameObject.cpp + GraphicsTags.cpp Level.cpp Material.cpp Mesh.cpp diff --git a/source/EditorAttachmentImpl.cpp b/source/EditorAttachmentImpl.cpp index 79c72ff..75d93cb 100644 --- a/source/EditorAttachmentImpl.cpp +++ b/source/EditorAttachmentImpl.cpp @@ -1,4 +1,8 @@ #include "EditorAttachmentImpl.hpp" +#include "EditorAttachment.hpp" + +EditorAttachment::EditorAttachment() { +} std::unique_ptr<EditorAttachment> EaGameObject::Create(GameObject* object) { EditorAttachment* result; @@ -14,6 +18,3 @@ std::unique_ptr<EditorAttachment> EaGameObject::Create(GameObject* object) { result->name = NameOf(object->GetTypeTag()); return std::unique_ptr<EditorAttachment>(result); } - -void EaShader::ShowInspector() { -} diff --git a/source/EditorAttachmentImpl.hpp b/source/EditorAttachmentImpl.hpp index 7763a8f..32618b1 100644 --- a/source/EditorAttachmentImpl.hpp +++ b/source/EditorAttachmentImpl.hpp @@ -1,7 +1,6 @@ #pragma once #include "EditorAttachment.hpp" -#include "EditorInspector.hpp" #include "GameObject.hpp" #include "Player.hpp" @@ -20,10 +19,8 @@ class EaLevelWrapper : public EditorAttachment { public: }; -class EaShader : public EditorAttachment, public IEditorInspectorTarget { +class EaShader : public EditorAttachment { public: Shader* shader; - -public: - void ShowInspector() override; + std::string name; }; diff --git a/source/EditorCore.cpp b/source/EditorCore.cpp index 7744793..7a77b26 100644 --- a/source/EditorCore.cpp +++ b/source/EditorCore.cpp @@ -7,7 +7,7 @@ #include "EditorAttachmentImpl.hpp" #include "EditorNotification.hpp" #include "EditorUtils.hpp" -#include "GameObjectTypeTag.hpp" +#include "GameObjectTags.hpp" #include "Level.hpp" #include "Mesh.hpp" #include "Player.hpp" @@ -41,8 +41,7 @@ void PushKeyCodeRecorder(App* app, int* writeKey, bool* writeKeyStatus) { EditorInstance::EditorInstance(App* app, GameWorld* world) : mApp{ app } , mWorld{ world } - , mEdInspector() - , mEdContentBrowser(&mEdInspector) {} + , mEdContentBrowser(this) {} EditorInstance::~EditorInstance() { } @@ -50,6 +49,11 @@ EditorInstance::~EditorInstance() { void EditorInstance::Show() { if (!mWorld) return; + auto& io = ImGui::GetIO(); + if (io.KeyCtrl && ImGui::IsKeyPressed(GLFW_KEY_SPACE, false)) { + mEdContentBrowserVisible = !mEdContentBrowserVisible; + } + ImGui::Begin("World properties"); ShowWorldProperties(); ImGui::End(); @@ -59,15 +63,91 @@ void EditorInstance::Show() { ImGui::End(); ImGui::Begin("Inspector"); - if (mSelectedGameObject) { - ShowInspector(mSelectedGameObject); + switch (mSelectedItt) { + case ITT_GameObject: ShowInspector(static_cast<GameObject*>(mSelectedItPtr)); break; + case ITT_Shader: ShowInspector(static_cast<Shader*>(mSelectedItPtr)); break; + case ITT_None: break; } ImGui::End(); + + if (mEdContentBrowserVisible) { + mEdContentBrowser.Show(&mEdContentBrowserVisible); + } +} + +void EditorInstance::SelectIt(void* ptr, InspectorTargetType itt) { + mSelectedItPtr = ptr; + mSelectedItt = itt; } void EditorInstance::ShowWorldProperties() { } +// TOOD move resource-specific and gameobject-specific inspector code into attachments mechanism + +void EditorInstance::ShowInspector(Shader* shader) { + using namespace Tags; + using namespace ProjectBrussel_UNITY_ID; + + auto info = shader->GetInfo(); + if (!info) { + ImGui::TextUnformatted("No info present for this shader."); + if (ImGui::Button("Create empty info")) { + shader->CreateEmptyInfoIfAbsent(); + } + if (ImGui::Button("Gather info")) { + shader->GatherInfoIfAbsent(); + } + return; + } + + 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()); + } + + // 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); + } + ImGui::SameLine(); + if (ImGui::Button("Export metadata", isAnnoymous)) { + char path[512]; + GetMetadataPath(path, sizeof(path)); + info->SaveToFile(path); + } + + auto ShowThing = [&](const std::vector<ShaderInfo::InputOutputThing>& things) { + for (auto& thing : things) { + ImGui::BulletText("Location %d\nName: %s\nSemantic: %s\nType: %s %dx%d", + thing.variable.location, + thing.variable.name.c_str(), + Tags::NameOf(thing.semantic).data(), + Tags::NameOfGLType(thing.variable.scalarType).data(), + thing.variable.width, + thing.variable.height); + } + }; + if (ImGui::CollapsingHeader("Inputs")) { + ShowThing(info->inputs); + } + if (ImGui::CollapsingHeader("Outputs")) { + ShowThing(info->outputs); + } + if (ImGui::CollapsingHeader("Uniforms")) { + } + if (ImGui::CollapsingHeader("Uniform blocks")) { + } +} + void EditorInstance::ShowInspector(GameObject* object) { using namespace Tags; using namespace ProjectBrussel_UNITY_ID; @@ -160,13 +240,14 @@ void EditorInstance::ShowGameObjectInTree(GameObject* object) { flags |= ImGuiTreeNodeFlags_OpenOnDoubleClick; flags |= ImGuiTreeNodeFlags_OpenOnArrow; flags |= ImGuiTreeNodeFlags_SpanAvailWidth; - if (mSelectedGameObject == object) { + if (mSelectedItPtr == object) { flags |= ImGuiTreeNodeFlags_Selected; } if (ImGui::TreeNodeEx(attachment->name.c_str(), flags)) { if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { - mSelectedGameObject = object; + mSelectedItPtr = object; + mSelectedItt = ITT_GameObject; } for (auto& child : object->GetChildren()) { diff --git a/source/EditorCore.hpp b/source/EditorCore.hpp index a9ef93a..28ad849 100644 --- a/source/EditorCore.hpp +++ b/source/EditorCore.hpp @@ -1,7 +1,6 @@ #pragma once #include "EditorAttachment.hpp" -#include "EditorInspector.hpp" #include "EditorResources.hpp" #include "GameObject.hpp" #include "World.hpp" @@ -11,12 +10,20 @@ class App; class EditorInstance { +public: + enum InspectorTargetType { + ITT_GameObject, + ITT_Shader, + ITT_None, + }; + private: App* mApp; GameWorld* mWorld; - GameObject* mSelectedGameObject = nullptr; - EditorInspector mEdInspector; + void* mSelectedItPtr = nullptr; EditorContentBrowser mEdContentBrowser; + InspectorTargetType mSelectedItt = ITT_None; + bool mEdContentBrowserVisible=false; public: EditorInstance(App* app, GameWorld* world); @@ -24,9 +31,14 @@ public: void Show(); + void* GetSelectedItPtr() const { return mSelectedItPtr; } + InspectorTargetType GetSelectedItt() const { return mSelectedItt; } + void SelectIt(void* ptr, InspectorTargetType itt); + private: void ShowWorldProperties(); + void ShowInspector(Shader* shader); void ShowInspector(GameObject* object); void ShowGameObjecetFields(GameObject* object); diff --git a/source/EditorInspector.cpp b/source/EditorInspector.cpp deleted file mode 100644 index 2f1d65c..0000000 --- a/source/EditorInspector.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "EditorInspector.hpp" - -void EditorInspector::Show() { - if (mCurrentTarget) { - mCurrentTarget->ShowInspector(); - } -} - -IEditorInspectorTarget* EditorInspector::GetCurrentTarget() const { - return mCurrentTarget; -} - -void EditorInspector::SetCurrentTarget(IEditorInspectorTarget* target) { - mCurrentTarget = target; -} diff --git a/source/EditorInspector.hpp b/source/EditorInspector.hpp deleted file mode 100644 index d74b7a8..0000000 --- a/source/EditorInspector.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -class IEditorInspectorTarget { -public: - virtual void ShowInspector() = 0; -}; - -class EditorInspector { -private: - IEditorInspectorTarget* mCurrentTarget = nullptr; - -public: - void Show(); - - IEditorInspectorTarget* GetCurrentTarget() const; - void SetCurrentTarget(IEditorInspectorTarget* target); -}; diff --git a/source/EditorResources.cpp b/source/EditorResources.cpp index 722c1f1..c6d4d09 100644 --- a/source/EditorResources.cpp +++ b/source/EditorResources.cpp @@ -1,72 +1,76 @@ #include "EditorResources.hpp" -#include "EditorAttachmentImpl.hpp" #include "EditorCore.hpp" -#include "EditorInspector.hpp" #include "EditorNotification.hpp" #include "EditorUtils.hpp" #include "Shader.hpp" #include <imgui.h> -EditorContentBrowser::EditorContentBrowser(EditorInspector* inspector) - : mInspector{ inspector } { +EditorContentBrowser::EditorContentBrowser(EditorInstance* editor) + : mEditor{ editor } { } -void EditorContentBrowser::Show() { - if (visible) { - ImGuiWindowFlags windowFlags; - if (docked) { - // Center window horizontally, align bottom vertically - auto& viewportSize = ImGui::GetMainViewport()->Size; - ImGui::SetNextWindowPos(ImVec2(viewportSize.x / 2, viewportSize.y), ImGuiCond_Always, ImVec2(0.5f, 1.0f)); - ImGui::SetNextWindowSizeRelScreen(0.8f, 0.5f); - windowFlags = ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; - } else { - windowFlags = 0; - } - ImGui::Begin("Content Browser", &visible, windowFlags); +EditorContentBrowser::~EditorContentBrowser() { +} - ImGui::Splitter(true, kSplitterThickness, &splitterLeft, &splitterRight, kLeftPaneMinWidth, kRightPaneMinWidth); +void EditorContentBrowser::Show(bool* open) { + ImGuiWindowFlags windowFlags; + if (docked) { + // Center window horizontally, align bottom vertically + auto& viewportSize = ImGui::GetMainViewport()->Size; + ImGui::SetNextWindowPos(ImVec2(viewportSize.x / 2, viewportSize.y), ImGuiCond_Always, ImVec2(0.5f, 1.0f)); + ImGui::SetNextWindowSizeRelScreen(0.8f, 0.5f); + windowFlags = ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; + } else { + windowFlags = 0; + } + ImGui::Begin("Content Browser", open, windowFlags); - ImGui::BeginChild("LeftPane", ImVec2(splitterLeft - kPadding, 0.0f)); + ImGui::Splitter(true, kSplitterThickness, &splitterLeft, &splitterRight, kLeftPaneMinWidth, kRightPaneMinWidth); + ImGui::BeginChild("LeftPane", ImVec2(splitterLeft - kPadding, 0.0f)); + { if (ImGui::Selectable("Settings", mPane == P_Settings)) { mPane = P_Settings; } if (ImGui::Selectable("Shaders", mPane == P_Shader)) { mPane = P_Shader; } + } + ImGui::EndChild(); - ImGui::EndChild(); // Window "LeftPane" - - ImGui::SameLine(0.0f, kPadding + kSplitterThickness + kPadding); - ImGui::BeginChild("RightPane"); // Fill remaining space - + ImGui::SameLine(0.0f, kPadding + kSplitterThickness + kPadding); + ImGui::BeginChild("RightPane"); // Fill remaining space + { switch (mPane) { case P_Settings: { ImGui::Checkbox("Docked", &docked); } break; case P_Shader: { + // TODO reload shaders while keeping existing references working + // if (ImGui::Button("Reload from disk")) { + // ShaderManager::instance->DiscoverShaders(); + // } + auto& shaders = ShaderManager::instance->GetShaders(); for (auto it = shaders.begin(); it != shaders.end(); ++it) { - auto name = it.key(); - auto shader = it.value().Get(); + auto name = it->first; + auto shader = it->second.Get(); - shader->GatherDetailsIfAbsent(); - auto details = shader->GetDetails(); - auto ea = static_cast<EaShader*>(shader->GetEditorAttachment()); + shader->GatherInfoIfAbsent(); + auto details = shader->GetInfo(); - if (ImGui::Selectable(name, mInspector->GetCurrentTarget() == ea)) { - mInspector->SetCurrentTarget(ea); + bool selected = mEditor->GetSelectedItPtr() == shader; + if (ImGui::Selectable(name.data(), selected)) { + mEditor->SelectIt(shader, EditorInstance::ITT_Shader); } } } break; } - - ImGui::EndChild(); // Window "RightPane" - - ImGui::End(); } + ImGui::EndChild(); + + ImGui::End(); } diff --git a/source/EditorResources.hpp b/source/EditorResources.hpp index f4e1ffe..db6a277 100644 --- a/source/EditorResources.hpp +++ b/source/EditorResources.hpp @@ -2,7 +2,7 @@ #include "Shader.hpp" -class EditorInspector; +class EditorInstance; class EditorContentBrowser { private: enum Pane { @@ -17,19 +17,15 @@ private: static constexpr float kLeftPaneMinWidth = 200.0f; static constexpr float kRightPaneMinWidth = 200.0f; - EditorInspector* mInspector; + EditorInstance* mEditor; Pane mPane = P_Settings; float splitterLeft = kLeftPaneMinWidth; float splitterRight = 0.0f; bool docked = true; - bool visible = false; public: - EditorContentBrowser(EditorInspector* inspector); + EditorContentBrowser(EditorInstance* editor); ~EditorContentBrowser(); - bool IsVisible() const; - void SetVisible(bool visible); - - void Show(); + void Show(bool* open = nullptr); }; diff --git a/source/GameObject.cpp b/source/GameObject.cpp index 2aaedeb..0a063e8 100644 --- a/source/GameObject.cpp +++ b/source/GameObject.cpp @@ -1,6 +1,6 @@ #include "GameObject.hpp" -#include "GameObjectTypeTag.hpp" +#include "GameObjectTags.hpp" #include "World.hpp" #include <string_view> diff --git a/source/GameObject.hpp b/source/GameObject.hpp index cb83da3..e1e6dd1 100644 --- a/source/GameObject.hpp +++ b/source/GameObject.hpp @@ -1,7 +1,7 @@ #pragma once #include "EditorAttachment.hpp" -#include "GameObjectTypeTag.hpp" +#include "GameObjectTags.hpp" #include "Material.hpp" #include "Mesh.hpp" #include "PodVector.hpp" diff --git a/source/GameObjectTypeTag.hpp b/source/GameObjectTags.hpp index 01a0ca4..01a0ca4 100644 --- a/source/GameObjectTypeTag.hpp +++ b/source/GameObjectTags.hpp diff --git a/source/GraphicsTags.cpp b/source/GraphicsTags.cpp new file mode 100644 index 0000000..d2da091 --- /dev/null +++ b/source/GraphicsTags.cpp @@ -0,0 +1,215 @@ +#include "GraphicsTags.hpp" + +#include <absl/container/flat_hash_map.h> +#include <cstddef> +#include <cstdint> + +using namespace std::literals; + +std::string_view Tags::NameOf(VertexElementSemantic semantic) { + switch (semantic) { + case VES_Position: return "Position"sv; + case VES_BlendWeights: return "BlendWeights"sv; + case VES_BlendIndices: return "BlendIndices"sv; + case VES_Normal: return "Normal"sv; + case VES_Color1: return "Color1"sv; + case VES_Color2: return "Color2"sv; + case VES_Texture_coordinates: return "Texture_coordinates"sv; + case VES_Binormal: return "Binormal"sv; + case VES_Tangent: return "Tangent"sv; + case VES_Generic: return "Generic"sv; + case VES_COUNT: break; + } + return std::string_view(); +} + +Tags::VertexElementSemantic Tags::FindVertexElementSemantic(std::string_view name) { + if (name == "Position"sv) return VES_Position; + if (name == "BlendWeights"sv) return VES_BlendWeights; + if (name == "BlendIndices"sv) return VES_BlendIndices; + if (name == "Normal"sv) return VES_Normal; + if (name == "Color1"sv) return VES_Color1; + if (name == "Color2"sv) return VES_Color2; + if (name == "Texture_coordinates"sv) return VES_Texture_coordinates; + if (name == "Binormal"sv) return VES_Binormal; + if (name == "Tangent"sv) return VES_Tangent; + if (name == "Generic"sv) return VES_Generic; + return VES_COUNT; +} + +int Tags::SizeOf(VertexElementType type) { + switch (type) { + case VET_Float1: + return sizeof(float); + case VET_Float2: + return sizeof(float) * 2; + case VET_Float3: + return sizeof(float) * 3; + case VET_Float4: + return sizeof(float) * 4; + case VET_Double1: + return sizeof(double); + case VET_Double2: + return sizeof(double) * 2; + case VET_Double3: + return sizeof(double) * 3; + case VET_Double4: + return sizeof(double) * 4; + case VET_Short2: + case VET_Short2Norm: + case VET_Ushort2: + case VET_Ushort2Norm: + return sizeof(short) * 2; + case VET_Short4: + case VET_Short4Norm: + case VET_Ushort4: + case VET_Ushort4Norm: + return sizeof(short) * 4; + case VET_Int1: + case VET_Uint1: + return sizeof(int); + case VET_Int2: + case VET_Uint2: + return sizeof(int) * 2; + case VET_Int3: + case VET_Uint3: + return sizeof(int) * 3; + case VET_Int4: + case VET_Uint4: + return sizeof(int) * 4; + case VET_Byte4: + case VET_Byte4Norm: + case VET_Ubyte4: + case VET_Ubyte4Norm: + return sizeof(char) * 4; + } + return 0; +} + +bool Tags::IsNormalized(VertexElementType type) { + return type >= VET_NORM_BEGIN && type <= VET_NORM_END; +} + +int Tags::SizeOf(IndexType type) { + switch (type) { + case IT_16Bit: return sizeof(uint16_t); + case IT_32Bit: return sizeof(uint32_t); + } + return 0; +} + +namespace ProjectBrussel_UNITY_ID { + +struct GLTypeInfo { + absl::flat_hash_map<GLenum, std::string_view> enum2Name; + absl::flat_hash_map<std::string_view, GLenum> name2Enum; + + GLTypeInfo() { + InsertEntry("float"sv, GL_FLOAT); + InsertEntry("double"sv, GL_DOUBLE); + InsertEntry("int"sv, GL_INT); + InsertEntry("uint"sv, GL_UNSIGNED_INT); + InsertEntry("bool"sv, GL_BOOL); + + InsertEntry("vec2"sv, GL_FLOAT_VEC2); + InsertEntry("vec3"sv, GL_FLOAT_VEC3); + InsertEntry("vec4"sv, GL_FLOAT_VEC4); + InsertEntry("dvec2"sv, GL_DOUBLE_VEC2); + InsertEntry("dvec3"sv, GL_DOUBLE_VEC3); + InsertEntry("dvec4"sv, GL_DOUBLE_VEC4); + InsertEntry("ivec2"sv, GL_INT_VEC2); + InsertEntry("ivec3"sv, GL_INT_VEC3); + InsertEntry("ivec4"sv, GL_INT_VEC4); + InsertEntry("uvec2"sv, GL_UNSIGNED_INT_VEC2); + InsertEntry("uvec3"sv, GL_UNSIGNED_INT_VEC3); + InsertEntry("uvec4"sv, GL_UNSIGNED_INT_VEC4); + InsertEntry("bvec2"sv, GL_BOOL_VEC2); + InsertEntry("bvec3"sv, GL_BOOL_VEC3); + InsertEntry("bvec4"sv, GL_BOOL_VEC4); + + InsertEntry("mat2"sv, GL_FLOAT_MAT2); + InsertEntry("mat3"sv, GL_FLOAT_MAT3); + InsertEntry("mat4"sv, GL_FLOAT_MAT4); + InsertEntry("mat2x3"sv, GL_FLOAT_MAT2x3); + InsertEntry("mat2x4"sv, GL_FLOAT_MAT2x4); + InsertEntry("mat3x2"sv, GL_FLOAT_MAT3x2); + InsertEntry("mat3x4"sv, GL_FLOAT_MAT3x4); + InsertEntry("mat4x2"sv, GL_FLOAT_MAT4x2); + InsertEntry("mat4x3"sv, GL_FLOAT_MAT4x3); + + InsertEntry("dmat2"sv, GL_DOUBLE_MAT2); + InsertEntry("dmat3"sv, GL_DOUBLE_MAT3); + InsertEntry("dmat4"sv, GL_DOUBLE_MAT4); + InsertEntry("dmat2x3"sv, GL_DOUBLE_MAT2x3); + InsertEntry("dmat2x4"sv, GL_DOUBLE_MAT2x4); + InsertEntry("dmat3x2"sv, GL_DOUBLE_MAT3x2); + InsertEntry("dmat3x4"sv, GL_DOUBLE_MAT3x4); + InsertEntry("dmat4x2"sv, GL_DOUBLE_MAT4x2); + InsertEntry("dmat4x3"sv, GL_DOUBLE_MAT4x3); + + InsertEntry("sampler1D"sv, GL_SAMPLER_1D); + InsertEntry("sampler2D"sv, GL_SAMPLER_2D); + InsertEntry("sampler3D"sv, GL_SAMPLER_3D); + InsertEntry("samplerCube"sv, GL_SAMPLER_CUBE); + InsertEntry("sampler1DShadow"sv, GL_SAMPLER_1D_SHADOW); + InsertEntry("sampler2DShadow"sv, GL_SAMPLER_2D_SHADOW); + InsertEntry("sampler1DArray"sv, GL_SAMPLER_1D_ARRAY); + InsertEntry("sampler2DArray"sv, GL_SAMPLER_2D_ARRAY); + InsertEntry("sampler1DArrayShadow"sv, GL_SAMPLER_1D_ARRAY_SHADOW); + InsertEntry("sampler2DArrayShadow"sv, GL_SAMPLER_2D_ARRAY_SHADOW); + InsertEntry("sampler2DMultisample"sv, GL_SAMPLER_2D_MULTISAMPLE); + InsertEntry("sampler2DMultisampleArray"sv, GL_SAMPLER_2D_MULTISAMPLE_ARRAY); + InsertEntry("samplerCubeShadow"sv, GL_SAMPLER_CUBE_SHADOW); + InsertEntry("samplerBuffer"sv, GL_SAMPLER_BUFFER); + InsertEntry("sampler2DRect"sv, GL_SAMPLER_2D_RECT); + InsertEntry("sampler2DRectShadow"sv, GL_SAMPLER_2D_RECT_SHADOW); + + InsertEntry("isampler1D"sv, GL_INT_SAMPLER_1D); + InsertEntry("isampler2D"sv, GL_INT_SAMPLER_2D); + InsertEntry("isampler3D"sv, GL_INT_SAMPLER_3D); + InsertEntry("isamplerCube"sv, GL_INT_SAMPLER_CUBE); + InsertEntry("isampler1DArray"sv, GL_INT_SAMPLER_1D_ARRAY); + InsertEntry("isampler2DArray"sv, GL_INT_SAMPLER_2D_ARRAY); + InsertEntry("isampler2DMultisample"sv, GL_INT_SAMPLER_2D_MULTISAMPLE); + InsertEntry("isampler2DMultisampleArray"sv, GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY); + InsertEntry("isamplerBuffer"sv, GL_INT_SAMPLER_BUFFER); + InsertEntry("isampler2DRect"sv, GL_INT_SAMPLER_2D_RECT); + + InsertEntry("usampler1D"sv, GL_UNSIGNED_INT_SAMPLER_1D); + InsertEntry("usampler2D"sv, GL_UNSIGNED_INT_SAMPLER_2D); + InsertEntry("usampler3D"sv, GL_UNSIGNED_INT_SAMPLER_3D); + InsertEntry("usamplerCube"sv, GL_UNSIGNED_INT_SAMPLER_CUBE); + InsertEntry("usampler1DArray"sv, GL_UNSIGNED_INT_SAMPLER_1D_ARRAY); + InsertEntry("usampler2DArray"sv, GL_UNSIGNED_INT_SAMPLER_2D_ARRAY); + InsertEntry("usampler2DMultisample"sv, GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE); + InsertEntry("usampler2DMultisampleArray"sv, GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY); + InsertEntry("usamplerBuffer"sv, GL_UNSIGNED_INT_SAMPLER_BUFFER); + InsertEntry("usampler2DRect"sv, GL_UNSIGNED_INT_SAMPLER_2D_RECT); + } + + void InsertEntry(std::string_view name, GLenum value) { + enum2Name.try_emplace(value, name); + name2Enum.try_emplace(name, value); + } +} const kGLTypeInfo; +} // namespace ProjectBrussel_UNITY_ID + +std::string_view Tags::NameOfGLType(GLenum value) { + using namespace ProjectBrussel_UNITY_ID; + auto iter = kGLTypeInfo.enum2Name.find(value); + if (iter != kGLTypeInfo.enum2Name.end()) { + return iter->second; + } else { + return std::string_view(); + } +} + +GLenum Tags::FindGLType(std::string_view name) { + using namespace ProjectBrussel_UNITY_ID; + auto iter = kGLTypeInfo.name2Enum.find(name); + if (iter != kGLTypeInfo.name2Enum.end()) { + return iter->second; + } else { + return GL_NONE; + } +} diff --git a/source/GraphicsTags.hpp b/source/GraphicsTags.hpp new file mode 100644 index 0000000..465c57d --- /dev/null +++ b/source/GraphicsTags.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include <glad/glad.h> +#include <string> +#include <string_view> + +namespace Tags { +/// Vertex element semantics, used to identify the meaning of vertex buffer contents +enum VertexElementSemantic { + /// Position, typically VET_Float3 + VES_Position, + /// Blending weights + VES_BlendWeights, + /// Blending indices + VES_BlendIndices, + /// Normal, typically VET_Float3 + VES_Normal, + /// Colour, typically VET_Ubyte4 + VES_Color1, + /// Secondary colour. Generally free for custom data. Means specular with OpenGL FFP. + VES_Color2, + /// Texture coordinates, typically VET_Float2 + VES_Texture_coordinates, + /// Binormal (Y axis if normal is Z) + VES_Binormal, + /// Tangent (X axis if normal is Z) + VES_Tangent, + /// Default semantic + VES_Generic, + VES_COUNT, +}; + +std::string_view NameOf(VertexElementSemantic semantic); +VertexElementSemantic FindVertexElementSemantic(std::string_view name); + +enum VertexElementType { + VET_Float1, + VET_Float2, + VET_Float3, + VET_Float4, + + VET_Short2, + VET_Short4, + VET_Ubyte4, + + // the following are not universally supported on all hardware: + VET_Double1, + VET_Double2, + VET_Double3, + VET_Double4, + VET_Ushort2, + VET_Ushort4, + VET_Int1, + VET_Int2, + VET_Int3, + VET_Int4, + VET_Uint1, + VET_Uint2, + VET_Uint3, + VET_Uint4, + VET_Byte4, /// signed bytes + + VET_NORM_BEGIN, + VET_Byte4Norm = VET_NORM_BEGIN, /// signed bytes (normalized to -1..1) + VET_Ubyte4Norm, /// unsigned bytes (normalized to 0..1) + VET_Short2Norm, /// signed shorts (normalized to -1..1) + VET_Short4Norm, + VET_Ushort2Norm, /// unsigned shorts (normalized to 0..1) + VET_Ushort4Norm, + VET_NORM_END = VET_Ushort4Norm, +}; + +int SizeOf(VertexElementType type); +bool IsNormalized(VertexElementType type); + +enum IndexType { + IT_16Bit, + IT_32Bit, +}; + +int SizeOf(IndexType type); + +std::string_view NameOfGLType(GLenum); +GLenum FindGLType(std::string_view name); +} // namespace Tags diff --git a/source/Mesh.cpp b/source/Mesh.cpp index 385ef55..3622d42 100644 --- a/source/Mesh.cpp +++ b/source/Mesh.cpp @@ -2,67 +2,6 @@ #include <algorithm> -int Tags::SizeOf(VertexElementType type) { - switch (type) { - case VET_Float1: - return sizeof(float); - case VET_Float2: - return sizeof(float) * 2; - case VET_Float3: - return sizeof(float) * 3; - case VET_Float4: - return sizeof(float) * 4; - case VET_Double1: - return sizeof(double); - case VET_Double2: - return sizeof(double) * 2; - case VET_Double3: - return sizeof(double) * 3; - case VET_Double4: - return sizeof(double) * 4; - case VET_Short2: - case VET_Short2Norm: - case VET_Ushort2: - case VET_Ushort2Norm: - return sizeof(short) * 2; - case VET_Short4: - case VET_Short4Norm: - case VET_Ushort4: - case VET_Ushort4Norm: - return sizeof(short) * 4; - case VET_Int1: - case VET_Uint1: - return sizeof(int); - case VET_Int2: - case VET_Uint2: - return sizeof(int) * 2; - case VET_Int3: - case VET_Uint3: - return sizeof(int) * 3; - case VET_Int4: - case VET_Uint4: - return sizeof(int) * 4; - case VET_Byte4: - case VET_Byte4Norm: - case VET_Ubyte4: - case VET_Ubyte4Norm: - return sizeof(char) * 4; - } - return 0; -} - -bool Tags::IsNormalized(VertexElementType type) { - return type >= VET_NORM_BEGIN && type <= VET_NORM_END; -} - -int Tags::SizeOf(IndexType type) { - switch (type) { - case IT_16Bit: return sizeof(uint16_t); - case IT_32Bit: return sizeof(uint32_t); - } - return 0; -} - GpuVertexBuffer::GpuVertexBuffer() { glGenBuffers(1, &handle); } diff --git a/source/Mesh.hpp b/source/Mesh.hpp index a208eda..a1c0984 100644 --- a/source/Mesh.hpp +++ b/source/Mesh.hpp @@ -1,5 +1,6 @@ #pragma once +#include "GraphicsTags.hpp" #include "RcPtr.hpp" #include "SmallVector.hpp" @@ -8,79 +9,6 @@ #include <cstdint> #include <vector> -namespace Tags { -/// Vertex element semantics, used to identify the meaning of vertex buffer contents -enum VertexElementSemantic { - /// Position, typically VET_Float3 - VES_Position = 1, - /// Blending weights - VES_BlendWeights = 2, - /// Blending indices - VES_BlendIndices = 3, - /// Normal, typically VET_Float3 - VES_Normal = 4, - /// Colour, typically VET_Ubyte4 - VES_Colour = 5, - /// Secondary colour. Generally free for custom data. Means specular with OpenGL FFP. - VES_Colour2 = 6, - /// Texture coordinates, typically VET_Float2 - VES_Texture_coordinates = 7, - /// Binormal (Y axis if normal is Z) - VES_Binormal = 8, - /// Tangent (X axis if normal is Z) - VES_Tangent = 9, - /// The number of VertexElementSemantic elements (note - the first value VES_Position is 1) - VES_COUNT = 9, -}; - -enum VertexElementType { - VET_Float1, - VET_Float2, - VET_Float3, - VET_Float4, - - VET_Short2, - VET_Short4, - VET_Ubyte4, - - // the following are not universally supported on all hardware: - VET_Double1, - VET_Double2, - VET_Double3, - VET_Double4, - VET_Ushort2, - VET_Ushort4, - VET_Int1, - VET_Int2, - VET_Int3, - VET_Int4, - VET_Uint1, - VET_Uint2, - VET_Uint3, - VET_Uint4, - VET_Byte4, /// signed bytes - - VET_NORM_BEGIN, - VET_Byte4Norm = VET_NORM_BEGIN, /// signed bytes (normalized to -1..1) - VET_Ubyte4Norm, /// unsigned bytes (normalized to 0..1) - VET_Short2Norm, /// signed shorts (normalized to -1..1) - VET_Short4Norm, - VET_Ushort2Norm, /// unsigned shorts (normalized to 0..1) - VET_Ushort4Norm, - VET_NORM_END = VET_Ushort4Norm, -}; - -int SizeOf(VertexElementType type); -bool IsNormalized(VertexElementType type); - -enum IndexType { - IT_16Bit, - IT_32Bit, -}; - -int SizeOf(IndexType type); -} // namespace Tags - struct GpuVertexBuffer : public RefCounted { GLuint handle; int sizeInBytes; diff --git a/source/Player.cpp b/source/Player.cpp index 2fa8d0d..47c3ccc 100644 --- a/source/Player.cpp +++ b/source/Player.cpp @@ -1,6 +1,7 @@ #include "Player.hpp" #include "AppConfig.hpp" +#include "ScopeGuard.hpp" #include "Utils.hpp" #include <cstdio> @@ -80,21 +81,21 @@ static FILE* OpenPlayerConfigFile(Player* player, Utils::IoMode mode) { bool Player::LoadFromFile() { auto file = OpenPlayerConfigFile(this, Utils::Read); if (!file) return false; + DEFER { fclose(file); }; // TODO input validation PLAYERKEYBINDS_DO_IO(fscanf, &); - fclose(file); return true; } bool Player::SaveToFile() { auto file = OpenPlayerConfigFile(this, Utils::WriteTruncate); if (!file) return false; + DEFER { fclose(file); }; PLAYERKEYBINDS_DO_IO(fprintf, ); - fclose(file); return true; } #pragma macro_pop("PLAYERKEYBINDS_DO_IO") diff --git a/source/Player.hpp b/source/Player.hpp index db8be33..4ae4f80 100644 --- a/source/Player.hpp +++ b/source/Player.hpp @@ -1,7 +1,7 @@ #pragma once #include "GameObject.hpp" -#include "GameObjectTypeTag.hpp" +#include "GameObjectTags.hpp" #define GLFW_INCLUDE_NONE #include <GLFW/glfw3.h> diff --git a/source/RapidJsonHelper.hpp b/source/RapidJsonHelper.hpp new file mode 100644 index 0000000..ac1f664 --- /dev/null +++ b/source/RapidJsonHelper.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include <rapidjson/document.h> +#include <cstring> +#include <string> +#include <string_view> + +#define BRUSSEL_JSON_GET(object, name, type, out, failAction) \ + { \ + auto it = (object).FindMember(name); \ + if (it == (object).MemberEnd()) failAction; \ + auto& value = it->value; \ + if (!value.Is<type>()) failAction; \ + (out) = value.Get<type>(); \ + } + +#define BRUSSEL_JSON_GET_DEFAULT(object, name, type, out, theDefault) \ + do { \ + auto it = (object).FindMember(name); \ + if (it == (object).MemberEnd()) { \ + (out) = theDefault; \ + break; \ + } \ + auto& value = it->value; \ + if (!value.Is<type>()) { \ + (out) = theDefault; \ + break; \ + } \ + (out) = value.Get<type>(); \ + } while (0); + +namespace rapidjson { + +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 (std::memcmp(it->name.GetString(), name.data(), name.size())) continue; + + return &it->value; + } + return nullptr; +} + +inline std::string_view AsStringView(const Value& value) { + return std::string_view(value.GetString(), value.GetStringLength()); +} + +inline std::string_view AsStringView(const GenericStringRef<char>& strRef) { + return std::string_view(strRef.s, strRef.length); +} + +inline std::string AsString(const Value& value) { + return std::string(value.GetString(), value.GetStringLength()); +} + +inline std::string AsString(const GenericStringRef<char>& strRef) { + return std::string(strRef.s, strRef.length); +} + +// RapidJson itself already provides std::string and const char* overloads +inline GenericStringRef<char> StringRef(std::string_view str) { + return GenericStringRef<char>( + str.data() ? str.data() : "", + str.size()); +} + +} // namespace rapidjson diff --git a/source/Shader.cpp b/source/Shader.cpp index a2d1c97..abb68bd 100644 --- a/source/Shader.cpp +++ b/source/Shader.cpp @@ -1,20 +1,110 @@ #include "Shader.hpp" #include "AppConfig.hpp" +#include "RapidJsonHelper.hpp" #include "ScopeGuard.hpp" #include "Utils.hpp" #include <imgui.h> +#include <rapidjson/document.h> +#include <rapidjson/filereadstream.h> +#include <rapidjson/filewritestream.h> +#include <rapidjson/writer.h> #include <cstddef> #include <cstdlib> -#include <filesystem> #include <utility> namespace fs = std::filesystem; -using namespace std::literals::string_literals; -using namespace std::literals::string_view_literals; +using namespace std::literals; -void ShaderDetails::ShowInspector() { +bool ShaderInfo::SaveToFile(const fs::path& filePath) const { + rapidjson::Document root(rapidjson::kObjectType); + + auto SaveInputOutputThings = [&](const char* name, const std::vector<InputOutputThing>& things) { + rapidjson::Value rvThings(rapidjson::kArrayType); + for (auto& thing : things) { + rapidjson::Value rvThing(rapidjson::kObjectType); + rvThing.AddMember("Name", thing.variable.name, root.GetAllocator()); + rvThing.AddMember("Semantic", rapidjson::StringRef(Tags::NameOf(thing.semantic)), root.GetAllocator()); + rvThing.AddMember("ScalarType", rapidjson::StringRef(Tags::NameOfGLType(thing.variable.scalarType)), root.GetAllocator()); + rvThing.AddMember("Width", thing.variable.width, root.GetAllocator()); + rvThing.AddMember("Height", thing.variable.height, root.GetAllocator()); + rvThing.AddMember("ArrayLength", thing.variable.arrayLength, root.GetAllocator()); + rvThing.AddMember("OpenGLLocation", thing.variable.location, root.GetAllocator()); + + rvThings.PushBack(rvThing, root.GetAllocator()); + } + root.AddMember(rapidjson::StringRef(name), rvThings, root.GetAllocator()); + }; + SaveInputOutputThings("Inputs", inputs); + SaveInputOutputThings("Outputs", outputs); + + // TODO uniforms + + 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 ShaderInfo::LoadFromFile(const fs::path& filePath) { + 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); + + auto LoadInputOutputThings = [&](std::string_view name, std::vector<InputOutputThing>& things) { + auto rvThings = rapidjson::GetProperty(root, rapidjson::kObjectType, name); + if (!rvThings) return; + if (!rvThings->IsArray()) return; + + for (auto& elm : rvThings->GetArray()) { + if (!elm.IsObject()) continue; + InputOutputThing thing; + + BRUSSEL_JSON_GET(elm, "Name", std::string, thing.variable.name, continue); + { // Semantic + auto value = rapidjson::GetProperty(elm, rapidjson::kStringType, "Semantic"sv); + if (!value) { + thing.semantic = Tags::VES_Generic; + } else { + thing.semantic = Tags::FindVertexElementSemantic(rapidjson::AsStringView(*value)); + } + } + { // Scalar type + auto value = rapidjson::GetProperty(elm, rapidjson::kStringType, "ScalarType"sv); + if (!value) continue; + thing.variable.scalarType = Tags::FindGLType(rapidjson::AsStringView(*value)); + } + BRUSSEL_JSON_GET(elm, "Width", int, thing.variable.width, continue); + BRUSSEL_JSON_GET(elm, "Height", int, thing.variable.height, continue); + BRUSSEL_JSON_GET(elm, "OpenGLLocation", int, thing.variable.location, continue); + BRUSSEL_JSON_GET_DEFAULT(elm, "ArrayLength", int, thing.variable.arrayLength, 1); + + things.push_back(std::move(thing)); + } + }; + LoadInputOutputThings("Inputs"sv, inputs); + LoadInputOutputThings("Outputs"sv, outputs); + + // TODO uniforms + + return true; +} + +Shader::Shader(std::string name) + : mName{ name } { } Shader::~Shader() { @@ -247,14 +337,275 @@ Shader::ErrorCode Shader::InitFromSource(std::string_view source) { #undef CATCH_ERROR -void Shader::GatherDetailsIfAbsent() { - if (mDetails) return; +namespace ProjectBrussel_UNITY_ID { +bool QueryMathInfo(GLenum type, GLenum& scalarType, int& width, int& height) { + auto DoOutput = [&](GLenum scalarTypeIn, int widthIn, int heightIn) { + width = widthIn; + height = heightIn; + scalarType = scalarTypeIn; + }; + + switch (type) { + case GL_FLOAT: + case GL_DOUBLE: + case GL_INT: + case GL_UNSIGNED_INT: + case GL_BOOL: { + DoOutput(type, 1, 1); + return true; + } + + case GL_FLOAT_VEC2: DoOutput(GL_FLOAT, 1, 2); return true; + case GL_FLOAT_VEC3: DoOutput(GL_FLOAT, 1, 3); return true; + case GL_FLOAT_VEC4: DoOutput(GL_FLOAT, 1, 4); return true; + case GL_DOUBLE_VEC2: DoOutput(GL_DOUBLE, 1, 2); return true; + case GL_DOUBLE_VEC3: DoOutput(GL_DOUBLE, 1, 3); return true; + case GL_DOUBLE_VEC4: DoOutput(GL_DOUBLE, 1, 4); return true; + case GL_INT_VEC2: DoOutput(GL_INT, 1, 2); return true; + case GL_INT_VEC3: DoOutput(GL_INT, 1, 3); return true; + case GL_INT_VEC4: DoOutput(GL_INT, 1, 4); return true; + case GL_UNSIGNED_INT_VEC2: DoOutput(GL_UNSIGNED_INT, 1, 2); return true; + case GL_UNSIGNED_INT_VEC3: DoOutput(GL_UNSIGNED_INT, 1, 3); return true; + case GL_UNSIGNED_INT_VEC4: DoOutput(GL_UNSIGNED_INT, 1, 4); return true; + case GL_BOOL_VEC2: DoOutput(GL_BOOL, 1, 2); return true; + case GL_BOOL_VEC3: DoOutput(GL_BOOL, 1, 3); return true; + case GL_BOOL_VEC4: DoOutput(GL_BOOL, 1, 4); return true; + + case GL_FLOAT_MAT2: DoOutput(GL_FLOAT, 2, 2); return true; + case GL_FLOAT_MAT3: DoOutput(GL_FLOAT, 3, 3); return true; + case GL_FLOAT_MAT4: DoOutput(GL_FLOAT, 4, 4); return true; + case GL_FLOAT_MAT2x3: DoOutput(GL_FLOAT, 2, 3); return true; + case GL_FLOAT_MAT2x4: DoOutput(GL_FLOAT, 2, 4); return true; + case GL_FLOAT_MAT3x2: DoOutput(GL_FLOAT, 3, 2); return true; + case GL_FLOAT_MAT3x4: DoOutput(GL_FLOAT, 3, 4); return true; + case GL_FLOAT_MAT4x2: DoOutput(GL_FLOAT, 4, 2); return true; + case GL_FLOAT_MAT4x3: DoOutput(GL_FLOAT, 4, 3); return true; + + case GL_DOUBLE_MAT2: DoOutput(GL_DOUBLE, 2, 2); return true; + case GL_DOUBLE_MAT3: DoOutput(GL_DOUBLE, 3, 3); return true; + case GL_DOUBLE_MAT4: DoOutput(GL_DOUBLE, 4, 4); return true; + case GL_DOUBLE_MAT2x3: DoOutput(GL_DOUBLE, 2, 3); return true; + case GL_DOUBLE_MAT2x4: DoOutput(GL_DOUBLE, 2, 4); return true; + case GL_DOUBLE_MAT3x2: DoOutput(GL_DOUBLE, 3, 2); return true; + case GL_DOUBLE_MAT3x4: DoOutput(GL_DOUBLE, 3, 4); return true; + case GL_DOUBLE_MAT4x2: DoOutput(GL_DOUBLE, 4, 2); return true; + case GL_DOUBLE_MAT4x3: DoOutput(GL_DOUBLE, 4, 3); return true; + } + + return false; +} + +bool QuerySamplerInfo(GLenum type) { + switch (type) { + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_BUFFER: + case GL_SAMPLER_2D_RECT: + case GL_SAMPLER_2D_RECT_SHADOW: + + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_BUFFER: + case GL_INT_SAMPLER_2D_RECT: - mDetails = std::make_unique<ShaderDetails>(); + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_2D_RECT: + return true; + } + + return false; } -const ShaderDetails* Shader::GetDetails() const { - return mDetails.get(); +std::unique_ptr<ShaderVariable> CreateVariable(GLenum type, GLuint loc) { + GLenum scalarType; + int width; + int height; + if (QueryMathInfo(type, scalarType, width, height)) { + auto res = std::make_unique<ShaderMathVariable>(); + res->location = loc; + res->scalarType = type; + res->width = width; + res->height = height; + return res; + } + + if (QuerySamplerInfo(type)) { + auto res = std::make_unique<ShaderSamplerVariable>(); + res->location = loc; + res->type = type; + return res; + } + + return nullptr; +} +} // namespace ProjectBrussel_UNITY_ID + +bool Shader::CreateEmptyInfoIfAbsent() { + if (mInfo || !IsValid()) { + return false; + } + + mInfo = std::make_unique<ShaderInfo>(); + return true; +} + +bool Shader::GatherInfoIfAbsent() { + using namespace ProjectBrussel_UNITY_ID; + using ThingId = ShaderInfo::ThingId; + + if (mInfo || !IsValid()) { + return false; + } + + mInfo = std::make_unique<ShaderInfo>(); + + // TODO handle differnt types of variables with the same name + + // TODO work with OpenGL < 4.3, possibly with glslang +#if 0 + int inputCount; + glGetProgramInterfaceiv(mHandle, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &inputCount); + int outputCount; + glGetProgramInterfaceiv(mHandle, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, &outputCount); + int uniformBlockCount; + glGetProgramInterfaceiv(mHandle, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &uniformBlockCount); + int uniformCount; + glGetProgramInterfaceiv(mHandle, GL_UNIFORM, GL_ACTIVE_RESOURCES, &uniformCount); + + // Gather inputs + auto GatherMathVars = [&](int count, GLenum resourceType, ShaderInfo::ThingKind 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)]; + glGetProgramResourceiv(mHandle, resourceType, i, std::size(query), query, std::size(props), nullptr, props); + auto& nameLength = props[0]; + auto& type = props[1]; + auto& loc = props[2]; + auto& arrayLength = props[3]; + + std::string fieldName(nameLength - 1, '\0'); + glGetProgramResourceName(mHandle, GL_UNIFORM, i, nameLength, nullptr, fieldName.data()); + + mInfo->things.insert(fieldName, ThingId{ resourceKind, i }); + + auto& thing = list.emplace_back(ShaderInfo::InputOutputThing{}); + auto& var = thing.variable; + var.name = std::move(fieldName); + var.arrayLength = arrayLength; + 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); + + // Gather uniform variables + for (int i = 0; i < uniformCount; ++i) { + const GLenum query[] = { GL_BLOCK_INDEX, GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_TOP_LEVEL_ARRAY_SIZE }; + GLint props[std::size(query)]; + glGetProgramResourceiv(mHandle, GL_UNIFORM, i, std::size(query), query, std::size(props), nullptr, props); + auto& blockIndex = props[0]; // Index in interface block + if (blockIndex != -1) { // If this is an interface block uniform, skip because it will be handled by our uniform blocks inspector + continue; + } + auto& nameLength = props[1]; + auto& type = props[2]; + auto& loc = props[3]; + auto& arrayLength = props[4]; + + 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->uniforms.push_back(CreateVariable(type, loc)); + } + + // Gather uniform blocks + for (int i = 0; i < uniformBlockCount; ++i) { + const GLenum blockQuery[] = { GL_NAME_LENGTH, GL_NUM_ACTIVE_VARIABLES }; + GLint blockProps[std::size(blockQuery)]; + glGetProgramResourceiv(mHandle, GL_UNIFORM_BLOCK, i, std::size(blockQuery), blockQuery, std::size(blockProps), nullptr, blockProps); + auto& nameLength = blockProps[0]; + auto& fieldCount = blockProps[1]; + + // glGetProgramResourceiv returns the length including the null terminator + std::string blockName(nameLength - 1, '\0'); + glGetProgramResourceName(mHandle, GL_UNIFORM_BLOCK, i, nameLength, nullptr, blockName.data()); + + const GLenum fieldsQuery[] = { GL_ACTIVE_VARIABLES }; + std::vector<GLint> fieldIndices(fieldCount); + glGetProgramResourceiv(mHandle, GL_UNIFORM_BLOCK, i, std::size(fieldsQuery), fieldsQuery, fieldIndices.size(), nullptr, fieldIndices.data()); + + ShaderUniformBlockVariable block; + block.index = i; + block.name = std::move(blockName); + // NOTE: blockName is invalid from this point on + + for (GLint idx : fieldIndices) { + const GLenum fieldQuery[] = { GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_TOP_LEVEL_ARRAY_SIZE }; + GLint fieldProps[std::size(fieldQuery)]; + glGetProgramResourceiv(mHandle, GL_UNIFORM, idx, std::size(fieldQuery), fieldQuery, std::size(fieldProps), nullptr, fieldProps); + auto& nameLength = fieldProps[0]; + auto& type = fieldProps[1]; + auto& location = fieldProps[2]; + auto& arrayLength = fieldProps[3]; + + std::string fieldName(nameLength - 1, '\0'); + glGetProgramResourceName(mHandle, GL_UNIFORM, idx, nameLength, nullptr, fieldName.data()); + + auto var = CreateVariable(type, location); + if (var->kind == ShaderVariable::KD_Math) { + auto math = static_cast<ShaderMathVariable*>(var.get()); + math->name = std::move(fieldName); + math->arrayLength = arrayLength; + } else if (var->kind == ShaderMathVariable::KD_Sampler) { + auto sampler = static_cast<ShaderSamplerVariable*>(var.get()); + sampler->name = std::move(fieldName); + sampler->arrayLength = arrayLength; + } + block.items.push_back(std::move(var)); + } + + mInfo->things.insert(block.name, { ShaderInfo::TKD_UniformBlock, i }); + mInfo->uniformBlocks.push_back(std::move(block)); + } + + mInfo->things.shrink_to_fit(); +#endif + + return true; +} + +ShaderInfo* Shader::GetInfo() const { + return mInfo.get(); +} + +const std::string& Shader::GetName() const { + return mName; } GLuint Shader::GetProgram() const { @@ -269,30 +620,44 @@ void ShaderManager::DiscoverShaders() { mShaders.clear(); auto path = AppConfig::assetDirPath / "Shaders"; + if (!fs::exists(path)) { + return; + } + for (auto& item : fs::directory_iterator(path)) { if (item.is_regular_file() && item.path().extension() == ".glsl") { - auto file = Utils::OpenCstdioFile(item.path(), Utils::Read); - if (!file) continue; + auto shaderFile = Utils::OpenCstdioFile(item.path(), Utils::Read); + if (!shaderFile) continue; + DEFER { fclose(shaderFile); }; - fseek(file, 0, SEEK_END); - auto fileSize = ftell(file); - rewind(file); + fseek(shaderFile, 0, SEEK_END); + auto shaderFileSize = ftell(shaderFile); + rewind(shaderFile); // Also add \0 ourselves - auto buffer = std::make_unique<char[]>(fileSize + 1); - fread(buffer.get(), fileSize, 1, file); - fclose(file); - buffer[fileSize] = '\0'; - std::string_view source(buffer.get(), fileSize); + auto buffer = std::make_unique<char[]>(shaderFileSize + 1); + fread(buffer.get(), shaderFileSize, 1, shaderFile); + buffer[shaderFileSize] = '\0'; + std::string_view source(buffer.get(), shaderFileSize); + + RcPtr shader(new Shader(item.path().stem().string())); + std::string_view shaderName(shader->GetName()); - RcPtr shader(new Shader()); + // Load shader auto err = shader->InitFromSource(source); if (err != Shader::Success) { continue; } - auto shaderName = item.path().stem().string(); - mShaders.insert(shaderName, std::move(shader)); + // Load shader info if present + fs::path infoPath(item.path()); + infoPath.replace_extension(".json"); + if (fs::exists(infoPath)) { + shader->CreateEmptyInfoIfAbsent(); + shader->GetInfo()->LoadFromFile(infoPath); + } + + mShaders.try_emplace(shaderName, std::move(shader)); } } } @@ -300,7 +665,7 @@ void ShaderManager::DiscoverShaders() { Shader* ShaderManager::FindShader(std::string_view name) { auto iter = mShaders.find(name); if (iter != mShaders.end()) { - return iter.value().Get(); + return iter->second.Get(); } else { return nullptr; } diff --git a/source/Shader.hpp b/source/Shader.hpp index e7a069b..8af5217 100644 --- a/source/Shader.hpp +++ b/source/Shader.hpp @@ -1,30 +1,102 @@ #pragma once #include "EditorAttachment.hpp" -#include "EditorInspector.hpp" +#include "GraphicsTags.hpp" #include "RcPtr.hpp" +#include <absl/container/flat_hash_map.h> #include <glad/glad.h> -#include <tsl/array_map.h> +#include <filesystem> #include <memory> #include <string_view> +#include <vector> -class ShaderDetails : public EditorAttachment, public IEditorInspectorTarget { -public: - std::string fileName; +// TODO move to variable after pattern matching is in the language -public: - virtual void ShowInspector() override; +struct ShaderVariable { + enum Kind { + KD_Math, + KD_Sampler, + KD_UniformBlock, + }; + + Kind kind; + +protected: + ShaderVariable(Kind kind) + : kind{ kind } {} +}; + +struct ShaderMathVariable : public ShaderVariable { + std::string name; + GLuint location; + GLenum scalarType; + int arrayLength; + int width; + int height; + + ShaderMathVariable() + : ShaderVariable(KD_Math) {} +}; + +struct ShaderSamplerVariable : public ShaderVariable { + std::string name; + GLuint location; + GLenum type; + int arrayLength; + + ShaderSamplerVariable() + : ShaderVariable(KD_Sampler) {} +}; + +struct ShaderUniformBlockVariable : public ShaderVariable { + std::string name; + /// Possible values: KD_Math + std::vector<std::unique_ptr<ShaderVariable>> items; + GLuint index; + + ShaderUniformBlockVariable() + : ShaderVariable(KD_UniformBlock) {} +}; + +struct ShaderInfo { + enum ThingKind { + TKD_Input, + TKD_Output, + TKD_Uniform, + TKD_UniformBlock, + }; + + struct ThingId { + ThingKind kind; + int index; + }; + + struct InputOutputThing { + ShaderMathVariable variable; + Tags::VertexElementSemantic semantic = Tags::VES_Generic; + }; + + absl::flat_hash_map<std::string, ThingId> things; + std::vector<InputOutputThing> inputs; + std::vector<InputOutputThing> outputs; + /// Possible values: KD_Math, KD_Sampler + std::vector<std::unique_ptr<ShaderVariable>> uniforms; + std::vector<ShaderUniformBlockVariable> uniformBlocks; + + bool SaveToFile(const std::filesystem::path& filePath) const; + bool LoadFromFile(const std::filesystem::path& filePath); }; class Shader : public RefCounted { private: - std::unique_ptr<ShaderDetails> mDetails; + std::string mName; + std::unique_ptr<ShaderInfo> mInfo; std::unique_ptr<EditorAttachment> mEditorAttachment; GLuint mHandle = 0; public: - Shader() = default; + Shader(std::string name = ""); ~Shader(); Shader(const Shader&) = delete; Shader& operator=(const Shader&) = delete; @@ -70,8 +142,11 @@ public: EditorAttachment* GetEditorAttachment() const { return mEditorAttachment.get(); } void SetEditorAttachment(EditorAttachment* attachment) { mEditorAttachment.reset(attachment); } - void GatherDetailsIfAbsent(); - const ShaderDetails* GetDetails() const; + bool CreateEmptyInfoIfAbsent(); + bool GatherInfoIfAbsent(); + ShaderInfo* GetInfo() const; + /// If not empty, this name must not duplicate with any other shader object in the process. + const std::string& GetName() const; GLuint GetProgram() const; bool IsValid() const; @@ -82,11 +157,11 @@ public: static inline ShaderManager* instance = nullptr; private: - tsl::array_map<char, RcPtr<Shader>> mShaders; + absl::flat_hash_map<std::string_view, RcPtr<Shader>> mShaders; public: void DiscoverShaders(); - const tsl::array_map<char, RcPtr<Shader>>& GetShaders() const { return mShaders; } + const auto& GetShaders() const { return mShaders; } Shader* FindShader(std::string_view name); }; |