aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorhnOsmium0001 <[email protected]>2022-04-09 13:29:41 -0700
committerhnOsmium0001 <[email protected]>2022-04-09 13:29:41 -0700
commit906557f094e407ce21d429ef647bc75fe3179cf1 (patch)
tree5e6aaed6537a0328318b6cd6561f6a76bf8aa27d /source
parente47a98793e58a5dbbe76bfed27e59408e43538e4 (diff)
More work on editor
Diffstat (limited to 'source')
-rw-r--r--source/App.cpp8
-rw-r--r--source/App.hpp4
-rw-r--r--source/AppConfig.hpp5
-rw-r--r--source/CMakeLists.txt11
-rw-r--r--source/Color.hpp144
-rw-r--r--source/EditorAttachment.hpp12
-rw-r--r--source/EditorAttachmentImpl.cpp19
-rw-r--r--source/EditorAttachmentImpl.hpp29
-rw-r--r--source/EditorCore.cpp79
-rw-r--r--source/EditorCore.hpp26
-rw-r--r--source/EditorCoreAPI.hpp18
-rw-r--r--source/EditorCore_Egoa.hpp11
-rw-r--r--source/EditorInspector.cpp15
-rw-r--r--source/EditorInspector.hpp17
-rw-r--r--source/EditorResources.cpp71
-rw-r--r--source/EditorResources.hpp32
-rw-r--r--source/EditorUtils.cpp195
-rw-r--r--source/EditorUtils.hpp32
-rw-r--r--source/GameObject.cpp16
-rw-r--r--source/GameObject.hpp17
-rw-r--r--source/Shader.cpp142
-rw-r--r--source/Shader.hpp45
-rw-r--r--source/Utils.hpp4
-rw-r--r--source/main.cpp85
24 files changed, 787 insertions, 250 deletions
diff --git a/source/App.cpp b/source/App.cpp
index 3907af9..ff0a5a5 100644
--- a/source/App.cpp
+++ b/source/App.cpp
@@ -1,5 +1,4 @@
#include "App.hpp"
-#include "GLFW/glfw3.h"
#include <utility>
@@ -18,16 +17,13 @@ void App::Init() {
}
mCurrentWorld->Awaken();
- mEditor = EditorInstance_Alloc(this, mCurrentWorld.get());
+ mEditor = std::make_unique<EditorInstance>(this, mCurrentWorld.get());
}
void App::Shutdown() {
if (!mInitialized) return;
mInitialized = false;
-
- EditorInstance_Free(mEditor);
mEditor = nullptr;
-
mCurrentWorld->Resleep();
mCurrentWorld = nullptr;
mPlayers.clear();
@@ -37,7 +33,7 @@ void App::Show() {
mCurrentWorld->Draw();
if (mEditorShown) {
- EditorInstance_Show(mEditor);
+ mEditor->Show();
}
}
diff --git a/source/App.hpp b/source/App.hpp
index b861730..731ab06 100644
--- a/source/App.hpp
+++ b/source/App.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include "EditorCoreAPI.hpp"
+#include "EditorCore.hpp"
#include "Player.hpp"
#include "PodVector.hpp"
#include "World.hpp"
@@ -19,7 +19,7 @@ class App {
private:
std::deque<KeyCaptureCallback> mKeyCaptureCallbacks;
PodVector<Player*> mPlayers;
- EditorInstance* mEditor;
+ std::unique_ptr<EditorInstance> mEditor;
std::unique_ptr<GameWorld> mCurrentWorld;
bool mEditorShown = true;
bool mInitialized = false;
diff --git a/source/AppConfig.hpp b/source/AppConfig.hpp
index 53e84b1..8d69fad 100644
--- a/source/AppConfig.hpp
+++ b/source/AppConfig.hpp
@@ -8,5 +8,10 @@ constexpr std::string_view kAppName = "ProjectBrussel";
// Since kAppName is initialized by a C string literal, we know it's null termianted
constexpr const char* kAppNameC = kAppName.data();
+// Duplicate each as path and string so that on non-UTF-8 platforms (e.g. Windows) we can easily do string manipulation on the paths
+// NOTE: even though non-const, these should not be modified outside of main()
+inline std::filesystem::path dataDirPath;
inline std::string dataDir;
+inline std::filesystem::path assetDirPath;
+inline std::string assetDir;
} // namespace AppConfig
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 7c47026..0749b62 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -2,6 +2,11 @@ target_sources(${PROJECT_NAME}
PRIVATE
App.cpp
CpuMesh.cpp
+ EditorAccessories.cpp
+ EditorCore.cpp
+ EditorNotification.cpp
+ EditorResources.cpp
+ EditorUtils.cpp
GameObject.cpp
Level.cpp
Material.cpp
@@ -17,11 +22,7 @@ PRIVATE
set(ProjectBrussel_SINGLE_UNIT_SRC
stb_implementation.c
main.cpp
- Utils.cpp
- EditorAccessories.cpp
- EditorCore.cpp
- EditorNotification.cpp
- EditorResources.cpp
+ Utils.cpp # May include platform headers
)
target_sources(${PROJECT_NAME} PRIVATE ${ProjectBrussel_SINGLE_UNIT_SRC})
set_source_files_properties(${ProjectBrussel_SINGLE_UNIT_SRC}
diff --git a/source/Color.hpp b/source/Color.hpp
new file mode 100644
index 0000000..c7d633c
--- /dev/null
+++ b/source/Color.hpp
@@ -0,0 +1,144 @@
+#pragma once
+
+#include "Utils.hpp"
+
+#include <algorithm>
+#include <cstdint>
+#include <glm/glm.hpp>
+#include <limits>
+
+class HsvColor;
+class RgbaColor {
+public:
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
+
+public:
+ constexpr RgbaColor() noexcept
+ : r{ 255 }
+ , g{ 255 }
+ , b{ 255 }
+ , a{ 255 } {
+ }
+
+ constexpr RgbaColor(float r, float g, float b, float a = 1.0f) noexcept
+ : r{ static_cast<uint8_t>(r * 255.0f) }
+ , g{ static_cast<uint8_t>(g * 255.0f) }
+ , b{ static_cast<uint8_t>(b * 255.0f) }
+ , a{ static_cast<uint8_t>(a * 255.0f) } {
+ }
+
+ constexpr RgbaColor(int r, int g, int b, int a = 255) noexcept
+ : r{ static_cast<uint8_t>(r & 0xFF) }
+ , g{ static_cast<uint8_t>(g & 0xFF) }
+ , b{ static_cast<uint8_t>(b & 0xFF) }
+ , a{ static_cast<uint8_t>(a & 0xFF) } {
+ }
+
+ constexpr RgbaColor(uint32_t rgba) noexcept
+ : r{ static_cast<uint8_t>((rgba >> 0) & 0xFF) }
+ , g{ static_cast<uint8_t>((rgba >> 8) & 0xFF) }
+ , b{ static_cast<uint8_t>((rgba >> 16) & 0xFF) }
+ , a{ static_cast<uint8_t>((rgba >> 24) & 0xFF) } {
+ }
+
+ constexpr uint32_t GetScalar() const noexcept {
+ uint32_t res = 0;
+ res |= r << 0;
+ res |= g << 8;
+ res |= b << 16;
+ res |= a << 24;
+ return res;
+ }
+
+ constexpr void SetScalar(uint32_t scalar) noexcept {
+ r = (scalar >> 0) & 0xFF;
+ g = (scalar >> 8) & 0xFF;
+ b = (scalar >> 16) & 0xFF;
+ a = (scalar >> 24) & 0xFF;
+ }
+
+ constexpr float GetNormalizedRed() const noexcept {
+ return r / 255.0f;
+ }
+
+ constexpr float GetNormalizedGreen() const noexcept {
+ return g / 255.0f;
+ }
+
+ constexpr float GetNormalizedBlue() const noexcept {
+ return b / 255.0f;
+ }
+
+ constexpr float GetNormalizedAlpha() const noexcept {
+ return a / 255.0f;
+ }
+
+ constexpr glm::ivec4 ToIVec() const noexcept {
+ return { r, g, b, a };
+ }
+
+ constexpr glm::vec4 ToVec() const noexcept {
+ return { GetNormalizedRed(), GetNormalizedGreen(), GetNormalizedBlue(), GetNormalizedAlpha() };
+ }
+
+ // Forward declaring because cyclic reference between RgbaColor and HsvColor
+ constexpr HsvColor ToHsv() const noexcept;
+
+ friend constexpr bool operator==(const RgbaColor&, const RgbaColor&) noexcept = default;
+};
+
+class HsvColor {
+public:
+ float h;
+ float s;
+ float v;
+ float a;
+
+public:
+ constexpr HsvColor() noexcept
+ : h{ 0.0f }
+ , s{ 0.0f }
+ , v{ 1.0f }
+ , a{ 1.0f } {
+ }
+
+ constexpr HsvColor(float h, float s, float v, float a) noexcept
+ : h{ h }
+ , s{ s }
+ , v{ v }
+ , a{ a } {
+ }
+
+ // Forward declaring because cyclic reference between RgbaColor and HsvColor
+ constexpr RgbaColor ToRgba() const noexcept;
+};
+
+constexpr HsvColor RgbaColor::ToHsv() const noexcept {
+ float r = GetNormalizedRed();
+ float g = GetNormalizedBlue();
+ float b = GetNormalizedGreen();
+ float a = GetNormalizedAlpha();
+
+ auto p = g < b ? glm::vec4(b, g, -1, 2.0f / 3.0f) : glm::vec4(g, b, 0, -1.0f / 3.0f);
+ auto q = r < p.x ? glm::vec4(p.x, p.y, p.w, r) : glm::vec4(r, p.y, p.z, p.x);
+ float c = q.x - std::min(q.w, q.y);
+ float h = Utils::Abs((q.w - q.y) / (6 * c + std::numeric_limits<float>::epsilon()) + q.z);
+
+ glm::vec3 hcv{ h, c, q.x };
+ float s = hcv.y / (hcv.z + std::numeric_limits<float>::epsilon());
+ return HsvColor(hcv.x, s, hcv.z, a);
+}
+
+constexpr RgbaColor HsvColor::ToRgba() const noexcept {
+ float r = Utils::Abs(h * 6 - 3) - 1;
+ float g = 2 - Utils::Abs(h * 6 - 2);
+ float b = 2 - Utils::Abs(h * 6 - 4);
+
+ auto rgb = glm::vec3{ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f) };
+ auto vc = (rgb - glm::vec3{ 0, 0, 0 }) * s + glm::vec3{ 1, 1, 1 } * v;
+
+ return RgbaColor(vc.x, vc.y, vc.z, a);
+}
diff --git a/source/EditorAttachment.hpp b/source/EditorAttachment.hpp
new file mode 100644
index 0000000..61b824b
--- /dev/null
+++ b/source/EditorAttachment.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <string>
+
+class EditorAttachment {
+public:
+ std::string name;
+
+public:
+ EditorAttachment();
+ virtual ~EditorAttachment() = default;
+};
diff --git a/source/EditorAttachmentImpl.cpp b/source/EditorAttachmentImpl.cpp
new file mode 100644
index 0000000..79c72ff
--- /dev/null
+++ b/source/EditorAttachmentImpl.cpp
@@ -0,0 +1,19 @@
+#include "EditorAttachmentImpl.hpp"
+
+std::unique_ptr<EditorAttachment> EaGameObject::Create(GameObject* object) {
+ EditorAttachment* result;
+
+ using namespace Tags;
+ switch (object->GetTypeTag()) {
+ case GOT_Player: result = new EaPlayer(); break;
+ case GOT_LevelWrapper: result = new EaLevelWrapper(); break;
+
+ default: result = new EditorAttachment(); break;
+ }
+
+ result->name = NameOf(object->GetTypeTag());
+ return std::unique_ptr<EditorAttachment>(result);
+}
+
+void EaShader::ShowInspector() {
+}
diff --git a/source/EditorAttachmentImpl.hpp b/source/EditorAttachmentImpl.hpp
new file mode 100644
index 0000000..7763a8f
--- /dev/null
+++ b/source/EditorAttachmentImpl.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "EditorAttachment.hpp"
+#include "EditorInspector.hpp"
+#include "GameObject.hpp"
+#include "Player.hpp"
+
+#include <memory>
+
+class EaGameObject : public EditorAttachment {
+public:
+ static std::unique_ptr<EditorAttachment> Create(GameObject* object);
+};
+
+class EaPlayer : public EditorAttachment {
+public:
+};
+
+class EaLevelWrapper : public EditorAttachment {
+public:
+};
+
+class EaShader : public EditorAttachment, public IEditorInspectorTarget {
+public:
+ Shader* shader;
+
+public:
+ void ShowInspector() override;
+};
diff --git a/source/EditorCore.cpp b/source/EditorCore.cpp
index 3bf9ecb..7744793 100644
--- a/source/EditorCore.cpp
+++ b/source/EditorCore.cpp
@@ -1,12 +1,12 @@
#include "EditorCore.hpp"
-#include "EditorCoreAPI.hpp"
#include "App.hpp"
#include "AppConfig.hpp"
#include "CpuMesh.hpp"
#include "EditorAccessories.hpp"
-#include "EditorCore_Egoa.hpp"
+#include "EditorAttachmentImpl.hpp"
#include "EditorNotification.hpp"
+#include "EditorUtils.hpp"
#include "GameObjectTypeTag.hpp"
#include "Level.hpp"
#include "Mesh.hpp"
@@ -16,8 +16,6 @@
#include <GLFW/glfw3.h>
#include <ImGuizmo.h>
-#include <backends/imgui_impl_glfw.h>
-#include <imgui.h>
#include <functional>
#include <memory>
#include <utility>
@@ -40,23 +38,13 @@ void PushKeyCodeRecorder(App* app, int* writeKey, bool* writeKeyStatus) {
}
} // namespace ProjectBrussel_UNITY_ID
-const char* ImGui::GetKeyNameGlfw(int key) {
- return GetKeyName(ImGui_ImplGlfw_KeyToImGuiKey(key));
-}
-
-std::unique_ptr<EditorGameObjectAttachment> EditorGameObjectAttachment::Create(GameObject* object) {
- EditorGameObjectAttachment* result;
-
- using namespace Tags;
- switch (object->GetTypeTag()) {
- case GOT_Player: result = new EgoaPlayer(); break;
- case GOT_LevelWrapper: result = new EgoaLevelWrapper(); break;
-
- default: result = new EditorGameObjectAttachment(); break;
- }
+EditorInstance::EditorInstance(App* app, GameWorld* world)
+ : mApp{ app }
+ , mWorld{ world }
+ , mEdInspector()
+ , mEdContentBrowser(&mEdInspector) {}
- result->name = NameOf(object->GetTypeTag());
- return std::unique_ptr<EditorGameObjectAttachment>(result);
+EditorInstance::~EditorInstance() {
}
void EditorInstance::Show() {
@@ -71,26 +59,26 @@ void EditorInstance::Show() {
ImGui::End();
ImGui::Begin("Inspector");
- ShowInspector();
+ if (mSelectedGameObject) {
+ ShowInspector(mSelectedGameObject);
+ }
ImGui::End();
}
void EditorInstance::ShowWorldProperties() {
}
-void EditorInstance::ShowInspector() {
+void EditorInstance::ShowInspector(GameObject* object) {
using namespace Tags;
using namespace ProjectBrussel_UNITY_ID;
- if (!mSelectedGameObject) return;
-
- auto type = mSelectedGameObject->GetTypeTag();
+ auto type = object->GetTypeTag();
switch (type) {
case Tags::GOT_Player: {
- ShowGameObjecetFields(mSelectedGameObject);
+ ShowGameObjecetFields(object);
ImGui::Separator();
- auto player = static_cast<Player*>(mSelectedGameObject);
+ auto player = static_cast<Player*>(object);
auto& kb = player->keybinds;
ImGui::Text("Player #%d", player->GetId());
@@ -135,26 +123,35 @@ void EditorInstance::ShowInspector() {
} break;
case Tags::GOT_LevelWrapper: {
- ShowGameObjecetFields(mSelectedGameObject);
+ ShowGameObjecetFields(object);
ImGui::Separator();
- auto lwo = static_cast<LevelWrapperObject*>(mSelectedGameObject);
+ auto lwo = static_cast<LevelWrapperObject*>(object);
// TODO
} break;
default:
- ShowGameObjecetFields(mSelectedGameObject);
+ ShowGameObjecetFields(object);
break;
}
}
void EditorInstance::ShowGameObjecetFields(GameObject* object) {
+ auto pos = object->GetPos();
+ if (ImGui::InputFloat3("Position", &pos.x)) {
+ object->SetPos(pos);
+ }
+
+ auto quat = object->GetRotation();
+ if (ImGui::InputFloat4("Rotation", &quat.x)) {
+ object->SetRotation(quat);
+ }
}
void EditorInstance::ShowGameObjectInTree(GameObject* object) {
auto attachment = object->GetEditorAttachment();
if (!attachment) {
- attachment = EditorGameObjectAttachment::Create(object).release();
+ attachment = EaGameObject::Create(object).release();
object->SetEditorAttachment(attachment); // NOTE: takes ownership
}
@@ -178,23 +175,3 @@ void EditorInstance::ShowGameObjectInTree(GameObject* object) {
ImGui::TreePop();
}
}
-
-// ======================== //
-// EditorCoreAPI.hpp things //
-// ======================== //
-
-void EditorGameObjectAttachmentDeleter::operator()(EditorGameObjectAttachment* obj) {
- delete obj;
-}
-
-EditorInstance* EditorInstance_Alloc(App* app, GameWorld* world) {
- return new EditorInstance(app, world);
-}
-
-void EditorInstance_Free(EditorInstance* editor) {
- delete editor;
-}
-
-void EditorInstance_Show(EditorInstance* editor) {
- editor->Show();
-}
diff --git a/source/EditorCore.hpp b/source/EditorCore.hpp
index 3f9eb11..a9ef93a 100644
--- a/source/EditorCore.hpp
+++ b/source/EditorCore.hpp
@@ -1,5 +1,8 @@
#pragma once
+#include "EditorAttachment.hpp"
+#include "EditorInspector.hpp"
+#include "EditorResources.hpp"
#include "GameObject.hpp"
#include "World.hpp"
@@ -7,37 +10,24 @@
#include <string>
class App;
-
-namespace ImGui {
-const char* GetKeyNameGlfw(int key);
-} // namespace ImGui
-
-class EditorGameObjectAttachment {
-public:
- std::string name;
-
-public:
- static std::unique_ptr<EditorGameObjectAttachment> Create(GameObject* object);
- virtual ~EditorGameObjectAttachment() = default;
-};
-
class EditorInstance {
private:
App* mApp;
GameWorld* mWorld;
GameObject* mSelectedGameObject = nullptr;
+ EditorInspector mEdInspector;
+ EditorContentBrowser mEdContentBrowser;
public:
- EditorInstance(App* app, GameWorld* world)
- : mApp{ app }
- , mWorld{ world } {}
+ EditorInstance(App* app, GameWorld* world);
+ ~EditorInstance();
void Show();
private:
void ShowWorldProperties();
- void ShowInspector();
+ void ShowInspector(GameObject* object);
void ShowGameObjecetFields(GameObject* object);
void ShowGameObjectInTree(GameObject* object);
diff --git a/source/EditorCoreAPI.hpp b/source/EditorCoreAPI.hpp
deleted file mode 100644
index fa68822..0000000
--- a/source/EditorCoreAPI.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-// This file contains minimal definitions for other game components to integrate with editor
-// Doing this instead of directly includign EditorCore.hpp and drastically remove the amount of code that needs to be dragged into every header
-
-#pragma once
-
-// Forward declarations
-class App;
-class GameWorld;
-
-class EditorGameObjectAttachment;
-struct EditorGameObjectAttachmentDeleter {
- void operator()(EditorGameObjectAttachment* obj);
-};
-
-class EditorInstance;
-EditorInstance* EditorInstance_Alloc(App* app, GameWorld* world);
-void EditorInstance_Free(EditorInstance* editor);
-void EditorInstance_Show(EditorInstance* editor);
diff --git a/source/EditorCore_Egoa.hpp b/source/EditorCore_Egoa.hpp
deleted file mode 100644
index 3a8dc36..0000000
--- a/source/EditorCore_Egoa.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "EditorCore.hpp"
-
-class EgoaPlayer : public EditorGameObjectAttachment {
-public:
-};
-
-class EgoaLevelWrapper : public EditorGameObjectAttachment {
-public:
-};
diff --git a/source/EditorInspector.cpp b/source/EditorInspector.cpp
new file mode 100644
index 0000000..2f1d65c
--- /dev/null
+++ b/source/EditorInspector.cpp
@@ -0,0 +1,15 @@
+#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
new file mode 100644
index 0000000..d74b7a8
--- /dev/null
+++ b/source/EditorInspector.hpp
@@ -0,0 +1,17 @@
+#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 0f91523..722c1f1 100644
--- a/source/EditorResources.cpp
+++ b/source/EditorResources.cpp
@@ -1 +1,72 @@
#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 } {
+}
+
+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);
+
+ 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(); // Window "LeftPane"
+
+ 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: {
+ auto& shaders = ShaderManager::instance->GetShaders();
+ for (auto it = shaders.begin(); it != shaders.end(); ++it) {
+ auto name = it.key();
+ auto shader = it.value().Get();
+
+ shader->GatherDetailsIfAbsent();
+ auto details = shader->GetDetails();
+ auto ea = static_cast<EaShader*>(shader->GetEditorAttachment());
+
+ if (ImGui::Selectable(name, mInspector->GetCurrentTarget() == ea)) {
+ mInspector->SetCurrentTarget(ea);
+ }
+ }
+ } break;
+ }
+
+ ImGui::EndChild(); // Window "RightPane"
+
+ ImGui::End();
+ }
+}
diff --git a/source/EditorResources.hpp b/source/EditorResources.hpp
index e868d74..f4e1ffe 100644
--- a/source/EditorResources.hpp
+++ b/source/EditorResources.hpp
@@ -1,5 +1,35 @@
#pragma once
-class EditorResourcePane {
+#include "Shader.hpp"
+class EditorInspector;
+class EditorContentBrowser {
+private:
+ enum Pane {
+ P_Settings,
+ P_Shader,
+ };
+
+ static constexpr float kSplitterThickness = 3.0f;
+ static constexpr float kPadding = 4.0f;
+
+ // <root>
+ static constexpr float kLeftPaneMinWidth = 200.0f;
+ static constexpr float kRightPaneMinWidth = 200.0f;
+
+ EditorInspector* mInspector;
+ Pane mPane = P_Settings;
+ float splitterLeft = kLeftPaneMinWidth;
+ float splitterRight = 0.0f;
+ bool docked = true;
+ bool visible = false;
+
+public:
+ EditorContentBrowser(EditorInspector* inspector);
+ ~EditorContentBrowser();
+
+ bool IsVisible() const;
+ void SetVisible(bool visible);
+
+ void Show();
};
diff --git a/source/EditorUtils.cpp b/source/EditorUtils.cpp
new file mode 100644
index 0000000..ab6ffad
--- /dev/null
+++ b/source/EditorUtils.cpp
@@ -0,0 +1,195 @@
+#include "EditorUtils.hpp"
+
+#define IMGUI_DEFINE_MATH_OPERATORS
+#include <imgui_internal.h>
+
+#include <backends/imgui_impl_glfw.h>
+
+const char* ImGui::GetKeyNameGlfw(int key) {
+ return GetKeyName(ImGui_ImplGlfw_KeyToImGuiKey(key));
+}
+
+void ImGui::SetNextWindowSizeRelScreen(float xPercent, float yPercent, ImGuiCond cond) {
+ auto vs = ImGui::GetMainViewport()->Size;
+ ImGui::SetNextWindowSize({ vs.x * xPercent, vs.y * yPercent }, cond);
+}
+
+void ImGui::SetNextWindowCentered(ImGuiCond cond) {
+ auto vs = ImGui::GetMainViewport()->Size;
+ ImGui::SetNextWindowPos({ vs.x / 2, vs.y / 2 }, cond, { 0.5f, 0.5f });
+}
+
+void ImGui::PushDisabled() {
+ ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
+ ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f * ImGui::GetStyle().Alpha);
+}
+
+void ImGui::PopDisabled() {
+ ImGui::PopItemFlag();
+ ImGui::PopStyleVar();
+}
+
+bool ImGui::Button(const char* label, bool disabled) {
+ return Button(label, ImVec2{}, disabled);
+}
+
+bool ImGui::Button(const char* label, const ImVec2& sizeArg, bool disabled) {
+ if (disabled) PushDisabled();
+ bool res = ImGui::Button(label, sizeArg);
+ if (disabled) PopDisabled();
+
+ return res;
+}
+
+#define EDIT_RGBA_COLOR(EditorFunction, kUsesAlpha) \
+ float components[4]; \
+ components[0] = color->GetNormalizedRed(); \
+ components[1] = color->GetNormalizedGreen(); \
+ components[2] = color->GetNormalizedBlue(); \
+ if constexpr (kUsesAlpha) components[3] = color->GetNormalizedAlpha(); \
+ if (EditorFunction(label, components, flags)) { \
+ color->r = components[0] * 255; \
+ color->g = components[1] * 255; \
+ color->b = components[2] * 255; \
+ if constexpr (kUsesAlpha) color->a = components[3] * 255; \
+ return true; \
+ } else { \
+ return false; \
+ }
+
+bool ImGui::ColorEdit3(const char* label, RgbaColor* color, ImGuiColorEditFlags flags) {
+ EDIT_RGBA_COLOR(ColorEdit3, false);
+}
+
+bool ImGui::ColorEdit4(const char* label, RgbaColor* color, ImGuiColorEditFlags flags) {
+ EDIT_RGBA_COLOR(ColorEdit4, true);
+}
+
+bool ImGui::ColorPicker3(const char* label, RgbaColor* color, ImGuiColorEditFlags flags) {
+ EDIT_RGBA_COLOR(ColorPicker3, false);
+}
+
+bool ImGui::ColorPicker4(const char* label, RgbaColor* color, ImGuiColorEditFlags flags) {
+ EDIT_RGBA_COLOR(ColorPicker4, true);
+}
+
+#undef EDIT_RGBA_COLOR
+
+struct InputTextCallbackUserData {
+ std::string* str;
+ ImGuiInputTextCallback chainCallback;
+ void* chainCallbackUserData;
+};
+
+static int InputTextCallback(ImGuiInputTextCallbackData* data) {
+ auto user_data = (InputTextCallbackUserData*)data->UserData;
+ if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
+ // Resize string callback
+ // If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
+ auto str = user_data->str;
+ IM_ASSERT(data->Buf == str->c_str());
+ str->resize(data->BufTextLen);
+ data->Buf = (char*)str->c_str();
+ } else if (user_data->chainCallback) {
+ // Forward to user callback, if any
+ data->UserData = user_data->chainCallbackUserData;
+ return user_data->chainCallback(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
+
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiID id = window->GetID("##Splitter");
+ ImRect bb;
+ bb.Min = window->DC.CursorPos + (splitVertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1));
+ bb.Max = bb.Min + CalcItemSize(splitVertically ? ImVec2(thickness, splitterLongAxisSize) : ImVec2(splitterLongAxisSize, thickness), 0.0f, 0.0f);
+
+ // Adapted from ImGui::SplitterBehavior, changes:
+ // - Simplified unneeded logic (hover_extend and hover_visibility_delay)
+ // - Changed clamped delta to clamping result size1 and deriving size2 from size1, allowing automatically adapting to the latest window content region width
+
+ auto itemFlagsBackup = g.CurrentItemFlags;
+ g.CurrentItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus;
+ bool itemAdd = ItemAdd(bb, id);
+ g.CurrentItemFlags = itemFlagsBackup;
+ if (!itemAdd) {
+ return false;
+ }
+
+ bool hovered, held;
+ auto bbInteract = bb;
+ ButtonBehavior(bbInteract, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);
+ if (hovered) {
+ g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
+ } // for IsItemHovered(), because bbInteract is larger than bb
+ if (g.ActiveId != id) {
+ SetItemAllowOverlap();
+ }
+
+ if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= 0.0f)) {
+ SetMouseCursor((splitVertically ? ImGuiAxis_X : ImGuiAxis_Y) == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
+ }
+
+ float contentSize = splitVertically ? window->ContentRegionRect.GetWidth() : window->ContentRegionRect.GetHeight();
+ if (held) {
+ ImVec2 mouseDelta2D = g.IO.MousePos - g.ActiveIdClickOffset - bbInteract.Min;
+ float mouseDelta = ((splitVertically ? ImGuiAxis_X : ImGuiAxis_Y) == ImGuiAxis_Y) ? mouseDelta2D.y : mouseDelta2D.x;
+
+ // Apply resize
+ if (mouseDelta != 0.0f) {
+ *size1 = ImClamp(*size1 + mouseDelta, minSize1, contentSize - minSize2 - thickness);
+ *size2 = contentSize - *size1 - thickness;
+ MarkItemEdited(id);
+ }
+ }
+
+ ImU32 col;
+ if (held) {
+ col = GetColorU32(ImGuiCol_SeparatorActive);
+ } else if (hovered && g.HoveredIdTimer >= 0.0f) {
+ col = GetColorU32(ImGuiCol_SeparatorHovered);
+ } else {
+ col = GetColorU32(ImGuiCol_Separator);
+ }
+ window->DrawList->AddRectFilled(bb.Min, bb.Max, col, 0.0f);
+
+ return held;
+}
diff --git a/source/EditorUtils.hpp b/source/EditorUtils.hpp
new file mode 100644
index 0000000..27510fe
--- /dev/null
+++ b/source/EditorUtils.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "Color.hpp"
+
+#include <imgui.h>
+#include <string>
+
+namespace ImGui {
+
+const char* GetKeyNameGlfw(int key);
+
+void SetNextWindowSizeRelScreen(float xPercent, float yPercent, ImGuiCond cond = ImGuiCond_None);
+void SetNextWindowCentered(ImGuiCond cond = ImGuiCond_None);
+
+void PushDisabled();
+void PopDisabled();
+
+bool Button(const char* label, bool disabled);
+bool Button(const char* label, const ImVec2& sizeArg, bool disabled);
+
+bool ColorEdit3(const char* label, RgbaColor* color, ImGuiColorEditFlags flags = 0);
+bool ColorEdit4(const char* label, RgbaColor* color, ImGuiColorEditFlags flags = 0);
+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/GameObject.cpp b/source/GameObject.cpp
index 9f8a8bf..2aaedeb 100644
--- a/source/GameObject.cpp
+++ b/source/GameObject.cpp
@@ -128,6 +128,22 @@ PodVector<GameObject*> GameObject::RemoveAllChildren() {
return result;
}
+const glm::vec3& GameObject::GetPos() {
+ return mPos;
+}
+
+void GameObject::SetPos(const glm::vec3& pos) {
+ mPos = pos;
+}
+
+const glm::quat& GameObject::GetRotation() const {
+ return mRot;
+}
+
+void GameObject::SetRotation(const glm::quat& rotation) {
+ mRot = rotation;
+}
+
Tags::GameObjectMemoryManagement GameObject::GetMemoryManagement() const {
return Tags::GOMM_None;
};
diff --git a/source/GameObject.hpp b/source/GameObject.hpp
index 750ae4a..cb83da3 100644
--- a/source/GameObject.hpp
+++ b/source/GameObject.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include "EditorCoreAPI.hpp"
+#include "EditorAttachment.hpp"
#include "GameObjectTypeTag.hpp"
#include "Material.hpp"
#include "Mesh.hpp"
@@ -13,7 +13,7 @@
class GameWorld;
class GameObject {
public: // NOTE: public for editors
- std::unique_ptr<EditorGameObjectAttachment, EditorGameObjectAttachmentDeleter> mEditorAttachment = nullptr;
+ std::unique_ptr<EditorAttachment> mEditorAttachment = nullptr;
GameWorld* mWorld = nullptr;
GameObject* mParent = nullptr;
PodVector<GameObject*> mChildren;
@@ -40,12 +40,17 @@ public:
GameObject* RemoveChild(GameObject* child);
PodVector<GameObject*> RemoveAllChildren();
- EditorGameObjectAttachment* GetEditorAttachment() const { return mEditorAttachment.get(); }
- void SetEditorAttachment(EditorGameObjectAttachment* attachment) { mEditorAttachment.reset(attachment); }
+ EditorAttachment* GetEditorAttachment() const { return mEditorAttachment.get(); }
+ void SetEditorAttachment(EditorAttachment* attachment) { mEditorAttachment.reset(attachment); }
+
+ const glm::vec3& GetPos();
+ void SetPos(const glm::vec3& pos);
+
+ const glm::quat& GetRotation() const;
+ void SetRotation(const glm::quat& rotation);
// Tag
- virtual Tags::GameObjectMemoryManagement
- GetMemoryManagement() const;
+ virtual Tags::GameObjectMemoryManagement GetMemoryManagement() const;
virtual Tags::GameObjectType GetTypeTag() const;
// Visuals
diff --git a/source/Shader.cpp b/source/Shader.cpp
index 72b679e..a2d1c97 100644
--- a/source/Shader.cpp
+++ b/source/Shader.cpp
@@ -1,34 +1,27 @@
#include "Shader.hpp"
+#include "AppConfig.hpp"
#include "ScopeGuard.hpp"
+#include "Utils.hpp"
+#include <imgui.h>
#include <cstddef>
-#include <fstream>
-#include <iostream>
-#include <sstream>
+#include <cstdlib>
+#include <filesystem>
#include <utility>
+namespace fs = std::filesystem;
using namespace std::literals::string_literals;
using namespace std::literals::string_view_literals;
+void ShaderDetails::ShowInspector() {
+}
+
Shader::~Shader() {
glDeleteProgram(mHandle);
}
namespace ProjectBrussel_UNITY_ID {
-Shader::ErrorCode LoadFile(std::string& out, const char* filePath) {
- std::ifstream ifs(filePath);
- if (!ifs) {
- return Shader::FileIOFailed;
- }
-
- std::stringstream buf;
- buf << ifs.rdbuf();
- out = buf.str();
-
- return Shader::Success;
-}
-
// Grabs section [begin, end)
Shader::ErrorCode CreateShader(GLuint& out, const char* src, int beginIdx, int endIdx, GLenum type) {
out = glCreateShader(type);
@@ -135,69 +128,6 @@ Shader::ErrorCode Shader::InitFromSources(const ShaderSources& sources) {
return Success;
}
-Shader::ErrorCode Shader::InitFromFiles(const ShaderFilePaths& files) {
- using namespace ProjectBrussel_UNITY_ID;
-
- if (IsValid()) {
- return ShaderAlreadyCreated;
- }
-
- GLuint program = glCreateProgram();
- ScopeGuard sg = [&]() { glDeleteProgram(program); };
-
- GLuint vertex = 0;
- DEFER { glDeleteShader(vertex); };
- if (files.vertex) {
- std::string src;
- CATCH_ERROR(LoadFile(src, files.vertex));
- CATCH_ERROR(CreateShader(vertex, src, GL_VERTEX_SHADER));
- glAttachShader(program, vertex);
- }
-
- GLuint geometry = 0;
- DEFER { glDeleteShader(geometry); };
- if (files.geometry) {
- std::string src;
- CATCH_ERROR(LoadFile(src, files.geometry));
- CATCH_ERROR(CreateShader(geometry, src, GL_GEOMETRY_SHADER));
- glAttachShader(program, geometry);
- }
-
- GLuint tessControl = 0;
- DEFER { glDeleteShader(tessControl); };
- if (files.tessControl) {
- std::string src;
- CATCH_ERROR(LoadFile(src, files.tessControl));
- CATCH_ERROR(CreateShader(tessControl, src, GL_TESS_CONTROL_SHADER));
- glAttachShader(program, tessControl);
- }
-
- GLuint tessEval = 0;
- DEFER { glDeleteShader(tessEval); };
- if (files.tessEval) {
- std::string src;
- CATCH_ERROR(LoadFile(src, files.tessEval));
- CATCH_ERROR(CreateShader(tessEval, src, GL_TESS_EVALUATION_SHADER));
- glAttachShader(program, tessEval);
- }
-
- GLuint fragment = 0;
- DEFER { glDeleteShader(fragment); };
- if (files.fragment) {
- std::string src;
- CATCH_ERROR(LoadFile(src, files.fragment));
- CATCH_ERROR(CreateShader(fragment, src, GL_FRAGMENT_SHADER));
- glAttachShader(program, fragment);
- }
-
- CATCH_ERROR(LinkShaderProgram(program));
-
- sg.Dismiss();
- mHandle = program;
-
- return Success;
-}
-
Shader::ErrorCode Shader::InitFromSource(std::string_view source) {
using namespace ProjectBrussel_UNITY_ID;
@@ -315,14 +245,17 @@ Shader::ErrorCode Shader::InitFromSource(std::string_view source) {
return Success;
}
-Shader::ErrorCode Shader::InitFromFile(const char* filePath) {
- std::string src;
- CATCH_ERROR(ProjectBrussel_UNITY_ID::LoadFile(src, filePath));
+#undef CATCH_ERROR
- return InitFromSource(src);
+void Shader::GatherDetailsIfAbsent() {
+ if (mDetails) return;
+
+ mDetails = std::make_unique<ShaderDetails>();
}
-#undef CATCH_ERROR
+const ShaderDetails* Shader::GetDetails() const {
+ return mDetails.get();
+}
GLuint Shader::GetProgram() const {
return mHandle;
@@ -331,3 +264,44 @@ GLuint Shader::GetProgram() const {
bool Shader::IsValid() const {
return mHandle != 0;
}
+
+void ShaderManager::DiscoverShaders() {
+ mShaders.clear();
+
+ auto path = AppConfig::assetDirPath / "Shaders";
+ 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;
+
+ fseek(file, 0, SEEK_END);
+ auto fileSize = ftell(file);
+ rewind(file);
+
+ // 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);
+
+ RcPtr shader(new Shader());
+ auto err = shader->InitFromSource(source);
+ if (err != Shader::Success) {
+ continue;
+ }
+
+ auto shaderName = item.path().stem().string();
+ mShaders.insert(shaderName, std::move(shader));
+ }
+ }
+}
+
+Shader* ShaderManager::FindShader(std::string_view name) {
+ auto iter = mShaders.find(name);
+ if (iter != mShaders.end()) {
+ return iter.value().Get();
+ } else {
+ return nullptr;
+ }
+}
diff --git a/source/Shader.hpp b/source/Shader.hpp
index b481bd6..e7a069b 100644
--- a/source/Shader.hpp
+++ b/source/Shader.hpp
@@ -1,17 +1,26 @@
#pragma once
+#include "EditorAttachment.hpp"
+#include "EditorInspector.hpp"
#include "RcPtr.hpp"
#include <glad/glad.h>
+#include <tsl/array_map.h>
#include <memory>
#include <string_view>
-class ShaderDetails : public RefCounted {
+class ShaderDetails : public EditorAttachment, public IEditorInspectorTarget {
public:
+ std::string fileName;
+
+public:
+ virtual void ShowInspector() override;
};
class Shader : public RefCounted {
private:
+ std::unique_ptr<ShaderDetails> mDetails;
+ std::unique_ptr<EditorAttachment> mEditorAttachment;
GLuint mHandle = 0;
public:
@@ -45,19 +54,6 @@ public:
/// into a Shader object.
ErrorCode InitFromSources(const ShaderSources& sources);
- struct ShaderFilePaths {
- const char* vertex = nullptr;
- const char* geometry = nullptr;
- const char* tessControl = nullptr;
- const char* tessEval = nullptr;
- const char* fragment = nullptr;
- };
-
- /// Create shader by loading each specified file and combining them together to form a shader object.
- /// Use the mental model that this function simply loads the files from disk (synchronously) and then
- /// passing them to `FromSource(const ShaderSources& source)`.
- ErrorCode InitFromFiles(const ShaderFilePaths& files);
-
/// The given text will be split into different shader sections according to #type directives,
/// and combined to form a Shader object.
/// For OpenGL, this process involves compililng each section separately and then linking them
@@ -71,11 +67,26 @@ public:
/// - `#type fragment`: Fragment shader
ErrorCode InitFromSource(std::string_view source);
- /// Create shader by loading the file at the `file`, and then giving the contents to
- /// `FromSource(std::string_view source)`.
- ErrorCode InitFromFile(const char* file);
+ EditorAttachment* GetEditorAttachment() const { return mEditorAttachment.get(); }
+ void SetEditorAttachment(EditorAttachment* attachment) { mEditorAttachment.reset(attachment); }
+ void GatherDetailsIfAbsent();
+ const ShaderDetails* GetDetails() const;
GLuint GetProgram() const;
bool IsValid() const;
};
+
+class ShaderManager {
+public:
+ static inline ShaderManager* instance = nullptr;
+
+private:
+ tsl::array_map<char, RcPtr<Shader>> mShaders;
+
+public:
+ void DiscoverShaders();
+
+ const tsl::array_map<char, RcPtr<Shader>>& GetShaders() const { return mShaders; }
+ Shader* FindShader(std::string_view name);
+};
diff --git a/source/Utils.hpp b/source/Utils.hpp
index 3d04e62..03fdfed 100644
--- a/source/Utils.hpp
+++ b/source/Utils.hpp
@@ -14,4 +14,8 @@ enum IoMode {
FILE* OpenCstdioFile(const std::filesystem::path& path, IoMode mode, bool binary = false);
FILE* OpenCstdioFile(const char* path, IoMode mode, bool binary = false);
+constexpr float Abs(float v) noexcept {
+ return v < 0.0f ? -v : v;
+}
+
} // namespace Utils
diff --git a/source/main.cpp b/source/main.cpp
index a2ba5b2..f7cde70 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -2,16 +2,18 @@
#include "AppConfig.hpp"
#include "EditorNotification.hpp"
+#include "Shader.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 <iostream>
#include <string>
namespace fs = std::filesystem;
@@ -24,7 +26,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);
}
@@ -33,7 +35,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);
}
@@ -42,7 +44,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));
@@ -51,33 +53,51 @@ static void GlfwKeyCallback(GLFWwindow* window, int key, int scancode, int actio
}
int main(int argc, char* argv[]) {
- constexpr const char* kGameDataDir = "game-data-directory";
-
+ 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()
+ (kGameAssetDir, "Directory in which assets are looked up from. Can be relative paths to the executable.", cxxopts::value<std::string>()->default_value("."))
(kGameDataDir, "Directory in which game data (such as saves and options) are saved to. Leave empty to use the default directory on each platform.", cxxopts::value<std::string>())
- ;
+ ;
// 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);
- if (!fs::exists(dataDir)) {
- fs::create_directories(dataDir);
- }
+ fs::create_directories(dataDir);
+
AppConfig::dataDir = std::move(dataDir);
+ AppConfig::dataDirPath = std::move(dataDirPath);
} else {
+ // TODO platform default path
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
@@ -98,45 +118,48 @@ 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();
+
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);
@@ -145,18 +168,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;
}