aboutsummaryrefslogtreecommitdiff
path: root/source/Game
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2022-06-03 23:30:01 -0700
committerrtk0c <[email protected]>2022-06-03 23:30:01 -0700
commit791b3f354b378769bffe623b05f1305c91b77101 (patch)
tree5409b311e6232eb4a6d3f8259b780d76b8ee1c59 /source/Game
parent60ccc62f4934e44ad5b905fdbcf458302b8d8a09 (diff)
Changeset: 64 [WIP] Rename directoriesmaster-switch-to-build2
Diffstat (limited to 'source/Game')
-rw-r--r--source/Game/App.cpp149
-rw-r--r--source/Game/App.hpp62
-rw-r--r--source/Game/AppConfig.hpp26
-rw-r--r--source/Game/Camera.cpp46
-rw-r--r--source/Game/Camera.hpp35
-rw-r--r--source/Game/CommonVertexIndex.cpp152
-rw-r--r--source/Game/CommonVertexIndex.hpp76
-rw-r--r--source/Game/EditorAccessories.cpp6
-rw-r--r--source/Game/EditorAccessories.hpp6
-rw-r--r--source/Game/EditorAttachment.hpp12
-rw-r--r--source/Game/EditorAttachmentImpl.cpp23
-rw-r--r--source/Game/EditorAttachmentImpl.hpp34
-rw-r--r--source/Game/EditorCommandPalette.cpp406
-rw-r--r--source/Game/EditorCommandPalette.hpp94
-rw-r--r--source/Game/EditorCore.hpp39
-rw-r--r--source/Game/EditorCorePrivate.cpp1113
-rw-r--r--source/Game/EditorCorePrivate.hpp130
-rw-r--r--source/Game/EditorGuizmo.cpp2897
-rw-r--r--source/Game/EditorGuizmo.hpp232
-rw-r--r--source/Game/EditorNotification.cpp277
-rw-r--r--source/Game/EditorNotification.hpp81
-rw-r--r--source/Game/EditorUtils.cpp447
-rw-r--r--source/Game/EditorUtils.hpp63
-rw-r--r--source/Game/FuzzyMatch.cpp174
-rw-r--r--source/Game/FuzzyMatch.hpp10
-rw-r--r--source/Game/GameObject.cpp230
-rw-r--r--source/Game/GameObject.hpp102
-rw-r--r--source/Game/GraphicsTags.cpp273
-rw-r--r--source/Game/GraphicsTags.hpp102
-rw-r--r--source/Game/Image.cpp101
-rw-r--r--source/Game/Image.hpp38
-rw-r--r--source/Game/Ires.cpp409
-rw-r--r--source/Game/Ires.hpp125
-rw-r--r--source/Game/Level.cpp228
-rw-r--r--source/Game/Level.hpp92
-rw-r--r--source/Game/Material.cpp526
-rw-r--r--source/Game/Material.hpp125
-rw-r--r--source/Game/Mesh.cpp54
-rw-r--r--source/Game/Mesh.hpp45
-rw-r--r--source/Game/Player.cpp139
-rw-r--r--source/Game/Player.hpp55
-rw-r--r--source/Game/Renderer.cpp170
-rw-r--r--source/Game/Renderer.hpp68
-rw-r--r--source/Game/SceneThings.cpp142
-rw-r--r--source/Game/SceneThings.hpp46
-rw-r--r--source/Game/Shader.cpp773
-rw-r--r--source/Game/Shader.hpp173
-rw-r--r--source/Game/Sprite.cpp328
-rw-r--r--source/Game/Sprite.hpp111
-rw-r--r--source/Game/Texture.cpp250
-rw-r--r--source/Game/Texture.hpp99
-rw-r--r--source/Game/VertexIndex.cpp84
-rw-r--r--source/Game/VertexIndex.hpp67
-rw-r--r--source/Game/World.cpp74
-rw-r--r--source/Game/World.hpp25
-rw-r--r--source/Game/buildfile0
-rw-r--r--source/Game/main.cpp461
57 files changed, 0 insertions, 12105 deletions
diff --git a/source/Game/App.cpp b/source/Game/App.cpp
deleted file mode 100644
index 45a7545..0000000
--- a/source/Game/App.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-#include "App.hpp"
-
-#include <string>
-#include <utility>
-
-using namespace std::literals;
-
-App::App()
- : mActiveCamera{ &mMainCamera } {
- auto& worldRoot = mWorld.GetRoot();
-
- constexpr int kPlayerCount = 2;
- for (int i = 0; i < kPlayerCount; ++i) {
- auto player = new Player(&mWorld, i);
- worldRoot.AddChild(player);
- mPlayers.push_back(player);
- };
-
-#if defined(BRUSSEL_DEV_ENV)
- SetGameRunning(false);
- SetEditorVisible(true);
-#else
- SetGameRunning(true);
-#endif
-
- mMainCamera.name = "Main Camera"s;
- mMainCamera.SetEyePos(glm::vec3(0, 0, 1));
- mMainCamera.SetTargetDirection(glm::vec3(0, 0, -1));
- mMainCamera.SetHasPerspective(false);
-}
-
-App::~App() {
-}
-
-Camera* App::GetActiveCamera() const {
- return mActiveCamera;
-}
-
-void App::BindActiveCamera(Camera* camera) {
- mActiveCamera = camera;
-}
-
-void App::UnbindActiveCamera() {
- mActiveCamera = &mMainCamera;
-}
-
-bool App::IsGameRunning() const {
- return mGameRunning;
-}
-
-void App::SetGameRunning(bool running) {
- if (mGameRunning != running) {
- mGameRunning = running;
- if (running) {
- mWorld.Awaken();
- } else {
- mWorld.Resleep();
- }
- }
-}
-
-bool App::IsEditorVisible() const {
- return mEditorVisible;
-}
-
-void App::SetEditorVisible(bool visible) {
- if (mEditorVisible != visible) {
- if (visible) {
-#if BRUSSEL_ENABLE_EDITOR
- mEditorVisible = true;
- if (mEditor == nullptr) {
- mEditor = IEditor::CreateInstance(this);
- }
-#endif
- } else {
- mEditorVisible = false;
- }
- }
-}
-
-void App::Show() {
- if (mEditorVisible) {
- mEditor->Show();
- }
-}
-
-void App::Update() {
- if (IsGameRunning()) {
- mWorld.Update();
- }
-}
-
-void App::Draw(float currentTime, float deltaTime) {
- mWorldRenderer.BeginFrame(*mActiveCamera, currentTime, deltaTime);
-
- PodVector<GameObject*> stack;
- stack.push_back(&mWorld.GetRoot());
-
- while (!stack.empty()) {
- auto obj = stack.back();
- stack.pop_back();
-
- for (auto child : obj->GetChildren()) {
- stack.push_back(child);
- }
-
- auto renderObjects = obj->GetRenderObjects();
- mWorldRenderer.Draw(renderObjects.data(), obj, renderObjects.size());
- }
-
- mWorldRenderer.EndFrame();
-}
-
-void App::HandleMouse(int button, int action) {
-}
-
-void App::HandleMouseMotion(double xOff, double yOff) {
-}
-
-void App::HandleKey(GLFWkeyboard* keyboard, int key, int action) {
- if (!mKeyCaptureCallbacks.empty()) {
- auto& callback = mKeyCaptureCallbacks.front();
- bool remove = callback(key, action);
- if (remove) {
- mKeyCaptureCallbacks.pop_front();
- }
- }
-
- switch (key) {
- case GLFW_KEY_F3: {
- if (action == GLFW_PRESS) {
- SetEditorVisible(!IsEditorVisible());
- }
- return;
- }
- }
-
- for (auto& player : mPlayers) {
- for (auto playerKeyboard : player->boundKeyboards) {
- if (playerKeyboard == keyboard) {
- player->HandleKeyInput(key, action);
- }
- }
- }
-}
-
-void App::PushKeyCaptureCallback(KeyCaptureCallback callback) {
- mKeyCaptureCallbacks.push_back(std::move(callback));
-}
diff --git a/source/Game/App.hpp b/source/Game/App.hpp
deleted file mode 100644
index c73c5a1..0000000
--- a/source/Game/App.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma once
-
-#include "Camera.hpp"
-#include "EditorCore.hpp"
-#include "Player.hpp"
-#include "PodVector.hpp"
-#include "Renderer.hpp"
-#include "World.hpp"
-
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
-
-#include <deque>
-#include <functional>
-#include <memory>
-#include <vector>
-
-using KeyCaptureCallback = std::function<bool(int, int)>;
-
-class App {
-private:
- std::deque<KeyCaptureCallback> mKeyCaptureCallbacks;
- PodVector<Player*> mPlayers;
- std::unique_ptr<IEditor> mEditor;
- GameWorld mWorld;
- Renderer mWorldRenderer;
- Camera mMainCamera;
- Camera* mActiveCamera;
- // NOTE: should only be true when mEditor != nullptr
- bool mEditorVisible = false;
- bool mGameRunning = false;
-
-public:
- App();
- ~App();
-
- IEditor* GetEditor() { return mEditor.get(); }
- GameWorld* GetWorld() { return &mWorld; }
- Renderer* GetWorldRenderer() { return &mWorldRenderer; }
-
- Camera* GetActiveCamera() const;
- void BindActiveCamera(Camera* camera);
- void UnbindActiveCamera();
-
- bool IsGameRunning() const;
- void SetGameRunning(bool running);
-
- bool IsEditorVisible() const;
- void SetEditorVisible(bool visible);
-
- // Do ImGui calls
- void Show();
- // Do regular calls
- void Update();
- void Draw(float currentTime, float deltaTime);
-
- void HandleMouse(int button, int action);
- void HandleMouseMotion(double xOff, double yOff);
- void HandleKey(GLFWkeyboard* keyboard, int key, int action);
-
- void PushKeyCaptureCallback(KeyCaptureCallback callback);
-};
diff --git a/source/Game/AppConfig.hpp b/source/Game/AppConfig.hpp
deleted file mode 100644
index 794bee5..0000000
--- a/source/Game/AppConfig.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <imgui.h>
-#include <filesystem>
-#include <string>
-
-namespace AppConfig {
-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();
-
-inline float mainWindowWidth;
-inline float mainWindowHeight;
-inline float mainWindowAspectRatio;
-
-// TODO add a bold font
-inline ImFont* fontRegular = nullptr;
-inline ImFont* fontBold = nullptr;
-
-// 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/Game/Camera.cpp b/source/Game/Camera.cpp
deleted file mode 100644
index 39f0369..0000000
--- a/source/Game/Camera.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#include "Camera.hpp"
-
-#include "AppConfig.hpp"
-
-#include <glm/gtc/matrix_transform.hpp>
-
-Camera::Camera()
- : eye(0.0f, 0.0f, 0.0f)
- , target(0.0, 0.0f, -2.0f)
- , pixelsPerMeter{ 50.0f } // Basic default
- , fov{ M_PI / 4 } // 45deg is the convention
- , perspective{ false } //
-{
-}
-
-void Camera::SetEyePos(glm::vec3 pos) {
- auto lookVector = this->target - /*Old pos*/ this->eye;
- this->eye = pos;
- this->target = pos + lookVector;
-}
-
-void Camera::SetTargetPos(glm::vec3 pos) {
- this->target = pos;
-}
-
-void Camera::SetTargetDirection(glm::vec3 lookVector) {
- this->target = this->eye + lookVector;
-}
-
-void Camera::SetHasPerspective(bool perspective) {
- this->perspective = perspective;
-}
-
-glm::mat4 Camera::CalcViewMatrix() const {
- return glm::lookAt(eye, target, glm::vec3(0, 1, 0));
-}
-
-glm::mat4 Camera::CalcProjectionMatrix() const {
- if (perspective) {
- return glm::perspective(fov, AppConfig::mainWindowAspectRatio, 0.1f, 1000.0f);
- } else {
- float widthMeters = AppConfig::mainWindowWidth / pixelsPerMeter;
- float heightMeters = AppConfig::mainWindowHeight / pixelsPerMeter;
- return glm::ortho(-widthMeters / 2, +widthMeters / 2, -heightMeters / 2, +heightMeters / 2);
- }
-}
diff --git a/source/Game/Camera.hpp b/source/Game/Camera.hpp
deleted file mode 100644
index 7bf0a6c..0000000
--- a/source/Game/Camera.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include <glm/glm.hpp>
-#include <string>
-
-class Camera {
-public:
- std::string name;
- glm::vec3 eye;
- glm::vec3 target;
-
- // --- Orthographic settings ---
- float pixelsPerMeter;
- // --- Orthographic settings ---
-
- // ---- Perspective settings ---
- /// In radians
- float fov;
- // ---- Perspective settings ---
-
- bool perspective;
-
-public:
- Camera();
-
- void SetEyePos(glm::vec3 pos);
- void SetTargetPos(glm::vec3 pos);
- void SetTargetDirection(glm::vec3 lookVector);
-
- bool HasPerspective() const { return perspective; }
- void SetHasPerspective(bool perspective);
-
- glm::mat4 CalcViewMatrix() const;
- glm::mat4 CalcProjectionMatrix() const;
-};
diff --git a/source/Game/CommonVertexIndex.cpp b/source/Game/CommonVertexIndex.cpp
deleted file mode 100644
index 786274e..0000000
--- a/source/Game/CommonVertexIndex.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-#include "CommonVertexIndex.hpp"
-
-template <class TNumber>
-static void AssignIndices(TNumber indices[6], TNumber startIdx) {
- // Triangle #1
- indices[0] = startIdx + 1; // Top right
- indices[1] = startIdx + 0; // Top left
- indices[2] = startIdx + 3; // Bottom left
- // Triangle #2
- indices[3] = startIdx + 1; // Top right
- indices[4] = startIdx + 3; // Bottom left
- indices[5] = startIdx + 2; // Bottom right
-}
-
-template <class TNumber>
-static void AssignIndices(TNumber indices[6], TNumber topLeft, TNumber topRight, TNumber bottomRight, TNumber bottomLeft) {
- // Triangle #1
- indices[0] = topRight;
- indices[1] = topLeft;
- indices[2] = bottomLeft;
- // Triangle #2
- indices[3] = topRight;
- indices[4] = bottomLeft;
- indices[5] = bottomRight;
-}
-
-template <class TVertex>
-static void AssignPositions(TVertex vertices[4], const Rect<float>& rect) {
- // Top left
- vertices[0].x = rect.x0();
- vertices[0].y = rect.y0();
- // Top right
- vertices[1].x = rect.x1();
- vertices[1].y = rect.y0();
- // Bottom right
- vertices[2].x = rect.x1();
- vertices[2].y = rect.y1();
- // Bottom left
- vertices[3].x = rect.x0();
- vertices[3].y = rect.y1();
-}
-
-template <class TVertex>
-static void AssignPositions(TVertex vertices[4], glm::vec2 bl, glm::vec2 tr) {
- // Top left
- vertices[0].x = bl.x;
- vertices[0].y = tr.y;
- // Top right
- vertices[1].x = tr.x;
- vertices[1].y = tr.y;
- // Bottom right
- vertices[2].x = tr.x;
- vertices[2].y = bl.y;
- // Bottom left
- vertices[3].x = bl.x;
- vertices[3].y = bl.y;
-}
-
-template <class TVertex>
-static void AssignDepths(TVertex vertices[4], float z) {
- for (int i = 0; i < 4; ++i) {
- auto& vert = vertices[i];
- vert.z = z;
- }
-}
-
-template <class TVertex>
-static void AssignTexCoords(TVertex vertices[4], const Subregion& texcoords) {
- // Top left
- vertices[0].u = texcoords.u0;
- vertices[0].v = texcoords.v1;
- // Top right
- vertices[1].u = texcoords.u1;
- vertices[1].v = texcoords.v1;
- // Bottom right
- vertices[2].u = texcoords.u1;
- vertices[2].v = texcoords.v0;
- // Bottom left
- vertices[3].u = texcoords.u0;
- vertices[3].v = texcoords.v0;
-}
-
-template <class TVertex>
-static void AssignColors(TVertex vertices[4], RgbaColor color) {
- for (int i = 0; i < 4; ++i) {
- auto& vert = vertices[i];
- vert.r = color.r;
- vert.g = color.g;
- vert.b = color.b;
- vert.a = color.a;
- }
-}
-
-void Index_U16::Assign(uint16_t indices[6], uint16_t startIdx) {
- ::AssignIndices(indices, startIdx);
-}
-
-void Index_U16::Assign(uint16_t indices[6], uint16_t startIdx, uint16_t topLeft, uint16_t topRight, uint16_t bottomRight, uint16_t bottomLeft) {
- ::AssignIndices<uint16_t>(indices, startIdx + topLeft, startIdx + topRight, startIdx + bottomRight, startIdx + bottomLeft);
-}
-
-void Index_U16::Assign(uint16_t indices[6], uint16_t topLeft, uint16_t topRight, uint16_t bottomRight, uint16_t bottomLeft) {
- ::AssignIndices<uint16_t>(indices, topLeft, topRight, bottomRight, bottomLeft);
-}
-
-void Index_U32::Assign(uint32_t indices[6], uint32_t startIdx) {
- ::AssignIndices(indices, startIdx);
-}
-
-void Index_U32::Assign(uint32_t indices[6], uint32_t startIdx, uint32_t topLeft, uint32_t topRight, uint32_t bottomRight, uint32_t bottomLeft) {
- ::AssignIndices<uint32_t>(indices, startIdx + topLeft, startIdx + topRight, startIdx + bottomRight, startIdx + bottomLeft);
-}
-
-void Index_U32::Assign(uint32_t indices[6], uint32_t topLeft, uint32_t topRight, uint32_t bottomRight, uint32_t bottomLeft) {
- ::AssignIndices<uint32_t>(indices, topLeft, topRight, bottomRight, bottomLeft);
-}
-
-void Vertex_PC::Assign(Vertex_PC vertices[4], const Rect<float>& rect) {
- ::AssignPositions(vertices, rect);
-}
-
-void Vertex_PC::Assign(Vertex_PC vertices[4], glm::vec2 bottomLeft, glm::vec2 topRight) {
- ::AssignPositions(vertices, bottomLeft, topRight);
-}
-
-void Vertex_PC::Assign(Vertex_PC vertices[4], float z) {
- ::AssignDepths(vertices, z);
-}
-
-void Vertex_PC::Assign(Vertex_PC vertices[4], RgbaColor color) {
- ::AssignColors(vertices, color);
-}
-
-void Vertex_PTC::Assign(Vertex_PTC vertices[4], const Rect<float>& rect) {
- ::AssignPositions(vertices, rect);
-}
-
-void Vertex_PTC::Assign(Vertex_PTC vertices[4], glm::vec2 bottomLeft, glm::vec2 topRight) {
- ::AssignPositions(vertices, bottomLeft, topRight);
-}
-
-void Vertex_PTC::Assign(Vertex_PTC vertices[4], float z) {
- ::AssignDepths(vertices, z);
-}
-
-void Vertex_PTC::Assign(Vertex_PTC vertices[4], const Subregion& texcoords) {
- ::AssignTexCoords(vertices, texcoords);
-}
-
-void Vertex_PTC::Assign(Vertex_PTC vertices[4], RgbaColor color) {
- ::AssignColors(vertices, color);
-}
diff --git a/source/Game/CommonVertexIndex.hpp b/source/Game/CommonVertexIndex.hpp
deleted file mode 100644
index adb81b6..0000000
--- a/source/Game/CommonVertexIndex.hpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#pragma once
-
-#include "Color.hpp"
-#include "RcPtr.hpp"
-#include "Rect.hpp"
-#include "Texture.hpp"
-#include "VertexIndex.hpp"
-
-#include <cstdint>
-
-// Initialized in main()
-inline RcPtr<VertexFormat> gVformatStandard{};
-inline RcPtr<VertexFormat> gVformatStandardSplit{};
-
-// Suffixes:
-// - _P_osition
-// - _T_exture coordiantes
-// - _C_olor
-// - _N_ormal
-// When an number is attached to some suffix, it means there are N number of this element
-
-struct Index_U16 {
- uint16_t value;
-
- static void Assign(uint16_t indices[6], uint16_t startIdx);
- static void Assign(uint16_t indices[6], uint16_t startIdx, uint16_t topLeft, uint16_t topRight, uint16_t bottomRight, uint16_t bottomLeft);
- static void Assign(uint16_t indices[6], uint16_t topLeft, uint16_t topRight, uint16_t bottomRight, uint16_t bottomLeft);
-};
-
-struct Index_U32 {
- uint32_t value;
-
- static void Assign(uint32_t indices[6], uint32_t startIdx);
- static void Assign(uint32_t indices[6], uint32_t startIdx, uint32_t topLeft, uint32_t topRight, uint32_t bottomRight, uint32_t bottomLeft);
- static void Assign(uint32_t indices[6], uint32_t topLeft, uint32_t topRight, uint32_t bottomRight, uint32_t bottomLeft);
-};
-
-struct Vertex_PC {
- float x, y, z;
- uint8_t r, g, b, a;
-
- /// Assumes the 4 vertices come in TL, TR, BR, BL order.
- static void Assign(Vertex_PC vertices[4], const Rect<float>& rect);
- /// Assign position in regular cartesian coordinate space (x increases from left to right, y increases from top to bottom).
- /// Assumes the 4 vertices come in TL, TR, BR, BL order.
- static void Assign(Vertex_PC vertices[4], glm::vec2 bottomLeft, glm::vec2 topRight);
- /// Assumes the 4 vertices come in TL, TR, BR, BL order.
- static void Assign(Vertex_PC vertices[4], float z);
- /// Assumes the 4 vertices come in TL, TR, BR, BL order.
- static void Assign(Vertex_PC vertices[4], RgbaColor color);
-};
-
-struct Vertex_PTC {
- float x, y, z;
- float u, v;
- uint8_t r, g, b, a;
-
- /// Assumes the 4 vertices come in TL, TR, BR, BL order.
- static void Assign(Vertex_PTC vertices[4], const Rect<float>& rect);
- /// Assign position in regular cartesian coordinate space (x increases from left to right, y increases from top to bottom).
- /// Assumes the 4 vertices come in TL, TR, BR, BL order.
- static void Assign(Vertex_PTC vertices[4], glm::vec2 bottomLeft, glm::vec2 topRight);
- /// Assumes the 4 vertices come in TL, TR, BR, BL order.
- static void Assign(Vertex_PTC vertices[4], float z);
- /// Assumes the 4 vertices come in TL, TR, BR, BL order.
- static void Assign(Vertex_PTC vertices[4], const Subregion& uvs);
- /// Assumes the 4 vertices come in TL, TR, BR, BL order.
- static void Assign(Vertex_PTC vertices[4], RgbaColor color);
-};
-
-struct Vertex_PTNC {
- float x, y, z;
- float nx, ny, nz;
- float u, v;
- uint8_t r, g, b, a;
-};
diff --git a/source/Game/EditorAccessories.cpp b/source/Game/EditorAccessories.cpp
deleted file mode 100644
index 08d08ec..0000000
--- a/source/Game/EditorAccessories.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "EditorAccessories.hpp"
-
-#include <imgui.h>
-
-void EditorSettings::Show() {
-}
diff --git a/source/Game/EditorAccessories.hpp b/source/Game/EditorAccessories.hpp
deleted file mode 100644
index 687b509..0000000
--- a/source/Game/EditorAccessories.hpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-class EditorSettings {
-public:
- void Show();
-};
diff --git a/source/Game/EditorAttachment.hpp b/source/Game/EditorAttachment.hpp
deleted file mode 100644
index 61b824b..0000000
--- a/source/Game/EditorAttachment.hpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-
-#include <string>
-
-class EditorAttachment {
-public:
- std::string name;
-
-public:
- EditorAttachment();
- virtual ~EditorAttachment() = default;
-};
diff --git a/source/Game/EditorAttachmentImpl.cpp b/source/Game/EditorAttachmentImpl.cpp
deleted file mode 100644
index b09c133..0000000
--- a/source/Game/EditorAttachmentImpl.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "EditorAttachmentImpl.hpp"
-#include "EditorAttachment.hpp"
-
-#include <Metadata.hpp>
-
-EditorAttachment::EditorAttachment() {
-}
-
-std::unique_ptr<EditorAttachment> EaGameObject::Create(GameObject* object) {
- EaGameObject* result;
-
- auto kind = object->GetKind();
- switch (kind) {
- case GameObject::KD_Player: result = new EaPlayer(); break;
- case GameObject::KD_LevelWrapper: result = new EaLevelWrapper(); break;
-
- default: result = new EaGameObject(); break;
- }
-
- result->name = Metadata::EnumToString(kind);
- result->eulerAnglesRotation = glm::eulerAngles(object->GetRotation());
- return std::unique_ptr<EditorAttachment>(result);
-}
diff --git a/source/Game/EditorAttachmentImpl.hpp b/source/Game/EditorAttachmentImpl.hpp
deleted file mode 100644
index 53bcd37..0000000
--- a/source/Game/EditorAttachmentImpl.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include "EditorAttachment.hpp"
-#include "GameObject.hpp"
-#include "Material.hpp"
-#include "Player.hpp"
-#include "Sprite.hpp"
-
-#include <memory>
-
-class EaGameObject : public EditorAttachment {
-public:
- // NOTE: in degrees
- glm::vec3 eulerAnglesRotation;
-
-public:
- static std::unique_ptr<EditorAttachment> Create(GameObject* object);
-};
-
-class EaPlayer : public EaGameObject {
-public:
- RcPtr<IresSpritesheet> confSprite;
- RcPtr<IresMaterial> confMaterial;
-};
-
-class EaLevelWrapper : public EaGameObject {
-public:
-};
-
-class EaIresObject : public EditorAttachment {
-public:
- std::string nameEditingScratch;
- bool isEditingName = false;
-};
diff --git a/source/Game/EditorCommandPalette.cpp b/source/Game/EditorCommandPalette.cpp
deleted file mode 100644
index 0e7b894..0000000
--- a/source/Game/EditorCommandPalette.cpp
+++ /dev/null
@@ -1,406 +0,0 @@
-#include "EditorCommandPalette.hpp"
-
-#include "AppConfig.hpp"
-#include "EditorUtils.hpp"
-#include "FuzzyMatch.hpp"
-#include "Utils.hpp"
-
-#include <GLFW/glfw3.h>
-#include <imgui.h>
-#include <misc/cpp/imgui_stdlib.h>
-#include <algorithm>
-#include <limits>
-#include <utility>
-
-#define IMGUI_DEFINE_MATH_OPERATORS
-#include <imgui_internal.h>
-
-using namespace std::literals;
-
-bool EditorCommandExecuteContext::IsInitiated() const {
- return mCommand != nullptr;
-}
-
-const EditorCommand* EditorCommandExecuteContext::GetCurrentCommand() const {
- return mCommand;
-}
-
-void EditorCommandExecuteContext::Initiate(const EditorCommand& command) {
- if (mCommand == nullptr) {
- mCommand = &command;
- }
-}
-
-void EditorCommandExecuteContext::Prompt(std::vector<std::string> options) {
- assert(mCommand != nullptr);
- mCurrentOptions = std::move(options);
- ++mDepth;
-}
-
-void EditorCommandExecuteContext::Finish() {
- assert(mCommand != nullptr);
- mCommand = nullptr;
- mCurrentOptions.clear();
- mDepth = 0;
-}
-
-int EditorCommandExecuteContext::GetExecutionDepth() const {
- return mDepth;
-}
-
-struct EditorCommandPalette::SearchResult {
- int itemIndex;
- int score;
- int matchCount;
- uint8_t matches[32];
-};
-
-struct EditorCommandPalette::Item {
- bool hovered = false;
- bool held = false;
-};
-
-EditorCommandPalette::EditorCommandPalette() = default;
-EditorCommandPalette::~EditorCommandPalette() = default;
-
-namespace P6503_UNITY_ID {
-std::string MakeCommandName(std::string_view category, std::string_view name) {
- std::string result;
- constexpr auto infix = ": "sv;
- result.reserve(category.size() + infix.size() + name.size());
- result.append(category);
- result.append(infix);
- result.append(name);
- return result;
-}
-} // namespace P6503_UNITY_ID
-
-void EditorCommandPalette::AddCommand(std::string_view category, std::string_view name, EditorCommand command) {
- command.name = P6503_UNITY_ID::MakeCommandName(category, name);
-
- auto location = std::lower_bound(
- mCommands.begin(),
- mCommands.end(),
- command,
- [](const EditorCommand& a, const EditorCommand& b) -> bool {
- return a.name < b.name;
- });
- auto iter = mCommands.insert(location, std::move(command));
-
- InvalidateSearchResults();
-}
-
-void EditorCommandPalette::RemoveCommand(std::string_view category, std::string_view name) {
- auto commandName = P6503_UNITY_ID::MakeCommandName(category, name);
- RemoveCommand(commandName);
-}
-
-void EditorCommandPalette::RemoveCommand(const std::string& commandName) {
- struct Comparator {
- bool operator()(const EditorCommand& command, const std::string& str) const {
- return command.name < str;
- }
-
- bool operator()(const std::string& str, const EditorCommand& command) const {
- return str < command.name;
- }
- };
-
- auto range = std::equal_range(mCommands.begin(), mCommands.end(), commandName, Comparator{});
- mCommands.erase(range.first, range.second);
-
- InvalidateSearchResults();
-}
-
-void EditorCommandPalette::Show(bool* open) {
- // Center window horizontally, align top vertically
- ImGui::SetNextWindowPos(ImVec2(ImGui::GetMainViewport()->Size.x / 2, 0), ImGuiCond_Always, ImVec2(0.5f, 0.0f));
- ImGui::SetNextWindowSizeRelScreen(0.3f, 0.0f);
-
- ImGui::Begin("Command Palette", open, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar);
- float width = ImGui::GetWindowContentRegionMax().x - ImGui::GetWindowContentRegionMin().x;
-
- if (!ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows) || mShouldCloseNextFrame) {
- // Close popup when user unfocused the command palette window (clicking elsewhere)
- // or some action requested closing this window
- mShouldCloseNextFrame = false;
- if (open) {
- *open = false;
- }
- }
-
- if (ImGui::IsWindowAppearing() || mFocusSearchBox) {
- mFocusSearchBox = false;
-
- // Focus the search box when user first brings command palette window up
- // Note: this only affects the next frame
- ImGui::SetKeyboardFocusHere(0);
- }
- ImGui::SetNextItemWidth(width);
- if (ImGui::InputText("##", &mSearchText)) {
- // Search string updated, update search results
-
- mFocusedItemId = 0;
- mSearchResults.clear();
-
- size_t itemCount;
- if (mExeCtx.GetExecutionDepth() == 0) {
- itemCount = mCommands.size();
- } else {
- itemCount = mExeCtx.mCurrentOptions.size();
- }
-
- for (size_t i = 0; i < itemCount; ++i) {
- const char* text;
- if (mExeCtx.GetExecutionDepth() == 0) {
- text = mCommands[i].name.c_str();
- } else {
- text = mExeCtx.mCurrentOptions[i].c_str();
- }
-
- SearchResult result{
- .itemIndex = (int)i,
- };
- if (FuzzyMatch::Search(mSearchText.c_str(), text, result.score, result.matches, std::size(result.matches), result.matchCount)) {
- mSearchResults.push_back(result);
- }
- }
-
- std::sort(
- mSearchResults.begin(),
- mSearchResults.end(),
- [](const SearchResult& a, const SearchResult& b) -> bool {
- // We want the biggest element first
- return a.score > b.score;
- });
- }
-
- ImGui::BeginChild("SearchResults", ImVec2(width, 300), false, ImGuiWindowFlags_AlwaysAutoResize);
- auto window = ImGui::GetCurrentWindow();
-
- auto& io = ImGui::GetIO();
- auto dlSharedData = ImGui::GetDrawListSharedData();
-
- auto textColor = ImGui::GetColorU32(ImGuiCol_Text);
- auto itemHoveredColor = ImGui::GetColorU32(ImGuiCol_HeaderHovered);
- auto itemActiveColor = ImGui::GetColorU32(ImGuiCol_HeaderActive);
- auto itemSelectedColor = ImGui::GetColorU32(ImGuiCol_Header);
-
- int itemCount = GetItemCount();
- if (mItems.size() < itemCount) {
- mItems.resize(itemCount);
- }
-
- // Flag used to delay item selection until after the loop ends
- bool selectFocusedItem = false;
- for (size_t i = 0; i < itemCount; ++i) {
- auto id = window->GetID(static_cast<int>(i));
-
- ImVec2 size{
- ImGui::GetContentRegionAvail().x,
- dlSharedData->Font->FontSize,
- };
- ImRect rect{
- window->DC.CursorPos,
- window->DC.CursorPos + ImGui::CalcItemSize(size, 0.0f, 0.0f),
- };
-
- bool& hovered = mItems[i].hovered;
- bool& held = mItems[i].held;
- if (held && hovered) {
- window->DrawList->AddRectFilled(rect.Min, rect.Max, itemActiveColor);
- } else if (hovered) {
- window->DrawList->AddRectFilled(rect.Min, rect.Max, itemHoveredColor);
- } else if (mFocusedItemId == i) {
- window->DrawList->AddRectFilled(rect.Min, rect.Max, itemSelectedColor);
- }
-
- auto item = GetItem(i);
- if (item.indexType == SearchResultIndex) {
- // Iterating search results: draw text with highlights at matched chars
-
- auto& searchResult = mSearchResults[i];
- auto textPos = window->DC.CursorPos;
- int rangeBegin;
- int rangeEnd;
- int lastRangeEnd = 0;
-
- auto DrawCurrentRange = [&]() -> void {
- if (rangeBegin != lastRangeEnd) {
- // Draw normal text between last highlighted range end and current highlighted range start
- auto begin = item.text + lastRangeEnd;
- auto end = item.text + rangeBegin;
- window->DrawList->AddText(textPos, textColor, begin, end);
-
- auto segmentSize = dlSharedData->Font->CalcTextSizeA(dlSharedData->Font->FontSize, std::numeric_limits<float>::max(), 0.0f, begin, end);
- textPos.x += segmentSize.x;
- }
-
- auto begin = item.text + rangeBegin;
- auto end = item.text + rangeEnd;
- window->DrawList->AddText(AppConfig::fontBold, AppConfig::fontBold->FontSize, textPos, textColor, begin, end);
-
- auto segmentSize = AppConfig::fontBold->CalcTextSizeA(AppConfig::fontBold->FontSize, std::numeric_limits<float>::max(), 0.0f, begin, end);
- textPos.x += segmentSize.x;
- };
-
- assert(searchResult.matchCount >= 1);
- rangeBegin = searchResult.matches[0];
- rangeEnd = rangeBegin;
-
- int lastCharIdx = -1;
- for (int j = 0; j < searchResult.matchCount; ++j) {
- int charIdx = searchResult.matches[j];
-
- if (charIdx == lastCharIdx + 1) {
- // These 2 indices are equal, extend our current range by 1
- ++rangeEnd;
- } else {
- DrawCurrentRange();
- lastRangeEnd = rangeEnd;
- rangeBegin = charIdx;
- rangeEnd = charIdx + 1;
- }
-
- lastCharIdx = charIdx;
- }
-
- // Draw the remaining range (if any)
- if (rangeBegin != rangeEnd) {
- DrawCurrentRange();
- }
-
- // Draw the text after the last range (if any)
- window->DrawList->AddText(textPos, textColor, item.text + rangeEnd); // Draw until \0
- } else {
- // Iterating everything else: draw text as-is, there is no highlights
-
- window->DrawList->AddText(window->DC.CursorPos, textColor, item.text);
- }
-
- ImGui::ItemSize(rect);
- if (!ImGui::ItemAdd(rect, id)) {
- continue;
- }
- if (ImGui::ButtonBehavior(rect, id, &hovered, &held)) {
- mFocusedItemId = i;
- selectFocusedItem = true;
- }
- }
-
- if (ImGui::IsKeyPressed(GLFW_KEY_UP)) {
- mFocusedItemId = std::max(mFocusedItemId - 1, 0);
- } else if (ImGui::IsKeyPressed(GLFW_KEY_DOWN)) {
- mFocusedItemId = std::min(mFocusedItemId + 1, itemCount - 1);
- }
- if (ImGui::IsKeyPressed(GLFW_KEY_ENTER) || selectFocusedItem) {
- SelectFocusedItem();
- }
-
- ImGui::EndChild();
-
- ImGui::End();
-}
-
-size_t EditorCommandPalette::GetItemCount() const {
- int depth = mExeCtx.GetExecutionDepth();
- if (depth == 0) {
- if (mSearchText.empty()) {
- return mCommands.size();
- } else {
- return mSearchResults.size();
- }
- } else {
- if (mSearchText.empty()) {
- return mExeCtx.mCurrentOptions.size();
- } else {
- return mSearchResults.size();
- }
- }
-}
-
-EditorCommandPalette::ItemInfo EditorCommandPalette::GetItem(size_t idx) const {
- ItemInfo option;
-
- int depth = mExeCtx.GetExecutionDepth();
- if (depth == 0) {
- if (mSearchText.empty()) {
- option.text = mCommands[idx].name.c_str();
- option.command = &mCommands[idx];
- option.itemId = idx;
- option.indexType = DirectIndex;
- } else {
- auto id = mSearchResults[idx].itemIndex;
- option.text = mCommands[id].name.c_str();
- option.command = &mCommands[id];
- option.itemId = id;
- option.indexType = SearchResultIndex;
- }
- option.itemType = CommandItem;
- } else {
- assert(mExeCtx.GetCurrentCommand() != nullptr);
- if (mSearchText.empty()) {
- option.text = mExeCtx.mCurrentOptions[idx].c_str();
- option.command = mExeCtx.GetCurrentCommand();
- option.itemId = idx;
- option.indexType = DirectIndex;
- } else {
- auto id = mSearchResults[idx].itemIndex;
- option.text = mExeCtx.mCurrentOptions[id].c_str();
- option.command = mExeCtx.GetCurrentCommand();
- option.itemId = id;
- option.indexType = SearchResultIndex;
- }
- option.itemType = CommandOptionItem;
- }
-
- return option;
-}
-
-void EditorCommandPalette::SelectFocusedItem() {
- if (mFocusedItemId < 0 || mFocusedItemId >= GetItemCount()) {
- return;
- }
-
- auto selectedItem = GetItem(mFocusedItemId);
- auto& command = *selectedItem.command;
-
- int depth = mExeCtx.GetExecutionDepth();
- if (depth == 0) {
- assert(!mExeCtx.IsInitiated());
-
- mExeCtx.Initiate(*selectedItem.command);
- if (command.callback) {
- command.callback(mExeCtx);
-
- mFocusSearchBox = true;
- // Don't invalidate search results if no further actions have been requested (returning to global list of commands)
- if (mExeCtx.IsInitiated()) {
- InvalidateSearchResults();
- }
- } else {
- mExeCtx.Finish();
- }
- } else {
- assert(mExeCtx.IsInitiated());
- assert(command.subsequentCallback);
- command.subsequentCallback(mExeCtx, selectedItem.itemId);
-
- mFocusSearchBox = true;
- InvalidateSearchResults();
- }
-
- // This action terminated execution, close command palette window
- if (!mExeCtx.IsInitiated()) {
- if (command.terminate) {
- command.terminate();
- }
- mShouldCloseNextFrame = true;
- }
-}
-
-void EditorCommandPalette::InvalidateSearchResults() {
- mSearchText.clear();
- mSearchResults.clear();
- mFocusedItemId = 0;
-}
diff --git a/source/Game/EditorCommandPalette.hpp b/source/Game/EditorCommandPalette.hpp
deleted file mode 100644
index 101344d..0000000
--- a/source/Game/EditorCommandPalette.hpp
+++ /dev/null
@@ -1,94 +0,0 @@
-#pragma once
-
-#include <imgui.h>
-#include <cstddef>
-#include <functional>
-#include <string>
-#include <string_view>
-
-class EditorCommandExecuteContext;
-class EditorCommand {
-public:
- std::string name;
- std::function<void(EditorCommandExecuteContext& ctx)> callback;
- std::function<void(EditorCommandExecuteContext& ctx, size_t selectedOptionId)> subsequentCallback;
- std::function<void()> terminate;
-};
-
-class EditorCommandExecuteContext {
- friend class EditorCommandPalette;
-
-private:
- const EditorCommand* mCommand = nullptr;
- std::vector<std::string> mCurrentOptions;
- int mDepth = 0;
-
-public:
- bool IsInitiated() const;
- const EditorCommand* GetCurrentCommand() const;
- void Initiate(const EditorCommand& command);
-
- void Prompt(std::vector<std::string> options);
- void Finish();
-
- /// Return the number of prompts that the user is currently completing. For example, when the user opens command
- /// palette fresh and selects a command, 0 is returned. If the command asks some prompt, and then the user selects
- /// again, 1 is returned.
- int GetExecutionDepth() const;
-};
-
-class EditorCommandPalette {
-private:
- struct SearchResult;
- struct Item;
-
- std::vector<EditorCommand> mCommands;
- std::vector<Item> mItems;
- std::vector<SearchResult> mSearchResults;
- std::string mSearchText;
- EditorCommandExecuteContext mExeCtx;
- int mFocusedItemId = 0;
- bool mFocusSearchBox = false;
- bool mShouldCloseNextFrame = false;
-
-public:
- EditorCommandPalette();
- ~EditorCommandPalette();
-
- EditorCommandPalette(const EditorCommandPalette&) = delete;
- EditorCommandPalette& operator=(const EditorCommandPalette&) = delete;
- EditorCommandPalette(EditorCommandPalette&&) = default;
- EditorCommandPalette& operator=(EditorCommandPalette&&) = default;
-
- void AddCommand(std::string_view category, std::string_view name, EditorCommand command);
- void RemoveCommand(std::string_view category, std::string_view name);
- void RemoveCommand(const std::string& commandName);
-
- void Show(bool* open = nullptr);
-
- enum ItemType {
- CommandItem,
- CommandOptionItem,
- };
-
- enum IndexType {
- DirectIndex,
- SearchResultIndex,
- };
-
- struct ItemInfo {
- const char* text;
- const EditorCommand* command;
- int itemId;
- ItemType itemType;
- IndexType indexType;
- };
-
- size_t GetItemCount() const;
- ItemInfo GetItem(size_t idx) const;
-
- void SelectFocusedItem();
-
-private:
- void InvalidateSearchResults();
-};
diff --git a/source/Game/EditorCore.hpp b/source/Game/EditorCore.hpp
deleted file mode 100644
index 726f43e..0000000
--- a/source/Game/EditorCore.hpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-
-#include <memory>
-
-class App;
-class SpriteDefinition;
-
-class IEditorInspector {
-public:
- enum TargetType {
- ITT_GameObject,
- ITT_Ires,
- ITT_Level,
- ITT_None,
- };
-
-public:
- virtual ~IEditorInspector() = default;
- virtual void SelectTarget(TargetType type, void* object) = 0;
-};
-
-class IEditorContentBrowser {
-public:
- virtual ~IEditorContentBrowser() = default;
-};
-
-class IEditor {
-public:
- static std::unique_ptr<IEditor> CreateInstance(App* app);
- virtual ~IEditor() = default;
-
- virtual void OnGameStateChanged(bool running) = 0;
- virtual void Show() = 0;
-
- virtual IEditorInspector& GetInspector() = 0;
- virtual IEditorContentBrowser& GetContentBrowser() = 0;
-
- virtual void OpenSpriteViewer(SpriteDefinition* sprite) = 0;
-};
diff --git a/source/Game/EditorCorePrivate.cpp b/source/Game/EditorCorePrivate.cpp
deleted file mode 100644
index 43857a8..0000000
--- a/source/Game/EditorCorePrivate.cpp
+++ /dev/null
@@ -1,1113 +0,0 @@
-#include "EditorCorePrivate.hpp"
-
-#include "App.hpp"
-#include "AppConfig.hpp"
-#include "EditorAccessories.hpp"
-#include "EditorAttachmentImpl.hpp"
-#include "EditorCommandPalette.hpp"
-#include "EditorNotification.hpp"
-#include "EditorUtils.hpp"
-#include "GameObject.hpp"
-#include "Mesh.hpp"
-#include "Player.hpp"
-#include "SceneThings.hpp"
-#include "VertexIndex.hpp"
-
-#include <Macros.hpp>
-#include <Metadata.hpp>
-#include <ScopeGuard.hpp>
-#include <YCombinator.hpp>
-
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
-
-#include <imgui.h>
-#include <misc/cpp/imgui_stdlib.h>
-#include <cstddef>
-#include <cstdint>
-#include <cstdlib>
-#include <functional>
-#include <glm/gtc/quaternion.hpp>
-#include <glm/gtc/type_ptr.hpp>
-#include <glm/gtx/quaternion.hpp>
-#include <limits>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <utility>
-
-using namespace std::literals;
-
-namespace ProjectBrussel_UNITY_ID {
-// TODO handle internal state internally and move this to EditorUtils.hpp
-enum RenamableSelectableAction {
- RSA_None,
- RSA_Selected,
- RSA_RenameCommitted,
- RSA_RenameCancelled,
-};
-RenamableSelectableAction RenamableSelectable(const char* displayName, bool selected, bool& renaming, std::string& renamingScratchBuffer) //
-{
- RenamableSelectableAction result = RSA_None;
-
- ImGuiSelectableFlags flags = 0;
- // When renaming, disable all other entries that is not the one being renamed
- if (renaming && !selected) {
- flags |= ImGuiSelectableFlags_Disabled;
- }
-
- if (renaming && selected) {
- // State: being renamed
-
- ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, 0 });
- ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0);
- ImGui::SetKeyboardFocusHere();
- if (ImGui::InputText("##Rename", &renamingScratchBuffer, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue)) {
- // Confirm
- renaming = false;
- result = RSA_RenameCommitted;
- }
- ImGui::PopStyleVar(2);
-
- if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
- // Cancel
- renaming = false;
- result = RSA_RenameCancelled;
- }
- } else {
- // State: normal
-
- if (ImGui::Selectable(displayName, selected, flags)) {
- result = RSA_Selected;
- }
- }
-
- return result;
-}
-} // namespace ProjectBrussel_UNITY_ID
-
-void EditorInspector::SelectTarget(TargetType type, void* object) {
- selectedItt = type;
- selectedItPtr = object;
- renaming = false;
- renamingScratchBuffer.clear();
-}
-
-EditorContentBrowser::EditorContentBrowser(EditorInspector* inspector)
- : mInspector{ inspector } {
-}
-
-EditorContentBrowser::~EditorContentBrowser() {
-}
-
-void EditorContentBrowser::Show(bool* open) {
- using namespace ProjectBrussel_UNITY_ID;
-
- ImGuiWindowFlags windowFlags;
- if (mDocked) {
- // 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, mBrowserHeight);
- windowFlags = ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse;
- } else {
- windowFlags = 0;
- }
- ImGui::Begin("Content Browser", open, windowFlags);
-
- ImGui::Splitter(true, kSplitterThickness, &mSplitterLeft, &mSplitterRight, kLeftPaneMinWidth, kRightPaneMinWidth);
-
- ImGui::BeginChild("LeftPane", ImVec2(mSplitterLeft - kPadding, 0.0f));
- {
- if (ImGui::Selectable("Settings", mPane == P_Settings)) {
- mPane = P_Settings;
- }
- if (ImGui::Selectable("Ires", mPane == P_Ires)) {
- mPane = P_Ires;
- }
- if (ImGui::Selectable("Levels", mPane == P_Level)) {
- mPane = P_Level;
- }
- }
- ImGui::EndChild();
-
- ImGui::SameLine(0.0f, kPadding + kSplitterThickness + kPadding);
- ImGui::BeginChild("RightPane"); // Fill remaining space
- auto origItt = mInspector->selectedItt;
- auto origItPtr = mInspector->selectedItPtr;
- switch (mPane) {
- case P_Settings: {
- ImGui::Checkbox("Docked", &mDocked);
- ImGui::SliderFloat("Height", &mBrowserHeight, 0.1f, 1.0f);
- } break;
-
- case P_Ires: {
- bool isIttIres = origItt == EditorInspector::ITT_Ires;
-
- if (ImGui::Button("New")) {
- ImGui::OpenPopup("New Ires");
- }
- if (ImGui::BeginPopup("New Ires")) {
- for (int i = 0; i < (int)IresObject::KD_COUNT; ++i) {
- auto kind = static_cast<IresObject::Kind>(i);
- if (ImGui::MenuItem(Metadata::EnumToString(kind).data())) {
- auto ires = IresObject::Create(kind);
- auto [DISCARD, success] = IresManager::instance->Add(ires.get());
- if (success) {
- (void)ires.release();
- }
- }
- }
- ImGui::EndPopup();
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Refresh list") ||
- ImGui::IsKeyPressed(ImGuiKey_F5))
- {
- // TODO
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Save", !isIttIres)) {
- auto ires = static_cast<IresObject*>(origItPtr);
- IresManager::instance->Save(ires);
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Reload", !isIttIres)) {
- auto ires = static_cast<IresObject*>(origItPtr);
- IresManager::instance->Reload(ires);
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Rename", !isIttIres) ||
- (isIttIres && ImGui::IsKeyPressed(ImGuiKey_F2, false)))
- {
- auto ires = static_cast<IresObject*>(origItPtr);
- mInspector->renaming = true;
- mInspector->renamingScratchBuffer = ires->GetName();
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Delete", !isIttIres) ||
- (isIttIres && ImGui::IsKeyPressed(ImGuiKey_Delete, false)))
- {
- ImGui::OpenPopup("Delete Ires");
- }
- bool openedDummy = true;
- if (ImGui::BeginPopupModal("Delete Ires", &openedDummy, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize)) {
- if (ImGui::Button("Confirm")) {
- auto ires = static_cast<IresObject*>(origItPtr);
- IresManager::instance->Delete(ires);
- }
- ImGui::SameLine();
- if (ImGui::Button("Cancel")) {
- ImGui::CloseCurrentPopup();
- }
- ImGui::EndPopup();
- }
-
- auto& objects = IresManager::instance->GetObjects();
- for (auto it = objects.begin(); it != objects.end(); ++it) {
- auto ires = it->second.Get();
- auto& name = ires->GetName();
-
- bool selected = origItPtr == ires;
-
- switch (RenamableSelectable(name.c_str(), selected, mInspector->renaming, mInspector->renamingScratchBuffer)) {
- case RSA_Selected: {
- mInspector->SelectTarget(EditorInspector::ITT_Ires, ires);
- } break;
-
- case RSA_RenameCommitted: {
- ires->SetName(std::move(mInspector->renamingScratchBuffer));
- } break;
-
- // Do nothing
- case RSA_RenameCancelled:
- case RSA_None: break;
- }
- if (!mInspector->renaming) {
- if (ImGui::BeginDragDropSource()) {
- auto kindName = Metadata::EnumToString(ires->GetKind());
- // Reason: intentionally using pointer as payload
- ImGui::SetDragDropPayload(kindName.data(), &ires, sizeof(ires)); // NOLINT(bugprone-sizeof-expression)
- ImGui::Text("%s '%s'", kindName.data(), name.c_str());
- ImGui::EndDragDropSource();
- }
- }
- }
- } break;
-
- case P_Level: {
- bool isIttLevel = origItt == EditorInspector::ITT_Level;
-
- if (ImGui::Button("New")) {
- auto uid = Uid::Create();
- auto& ldObj = LevelManager::instance->AddLevel(uid);
- mInspector->SelectTarget(EditorInspector::ITT_Level, &ldObj);
- mInspector->renaming = true;
- mInspector->renamingScratchBuffer = ldObj.name;
- }
-
- if (ImGui::Button("Save", !isIttLevel)) {
- auto ldObj = static_cast<LevelManager::LoadableObject*>(origItPtr);
- LevelManager::instance->SaveLevel(ldObj->level->GetUid());
- }
-
- auto& objects = LevelManager::instance->mObjByUid;
- for (auto it = objects.begin(); it != objects.end(); ++it) {
- auto& uid = it->first;
- auto& ldObj = it->second;
- auto* level = ldObj.level.Get();
- bool selected = origItPtr == &ldObj;
- const char* displayName = ldObj.name.c_str();
- if (strcmp(displayName, "") == 0) {
- displayName = "<unnamed level>";
- }
-
- switch (RenamableSelectable(displayName, selected, mInspector->renaming, mInspector->renamingScratchBuffer)) {
- case RSA_Selected: {
- mInspector->SelectTarget(EditorInspector::ITT_Level, &ldObj);
- } break;
-
- case RSA_RenameCommitted: {
- ldObj.name = std::move(mInspector->renamingScratchBuffer);
- } break;
-
- // Do nothing
- case RSA_RenameCancelled:
- case RSA_None: break;
- }
- if (!mInspector->renaming) {
- if (ImGui::BeginDragDropSource()) {
- // Reason: intentionally using pointer as payload
- ImGui::SetDragDropPayload(BRUSSEL_TAG_Level, &ldObj, sizeof(ldObj)); // NOLINT(bugprone-sizeof-expression)
- ImGui::Text(BRUSSEL_Uid_FORMAT_STR, BRUSSEL_Uid_FORMAT_EXPAND(uid));
- ImGui::TextUnformatted(ldObj.name.c_str());
- ImGui::EndDragDropSource();
- }
- }
- }
- } break;
- }
- ImGui::EndChild();
-
- ImGui::End();
-}
-
-namespace ProjectBrussel_UNITY_ID {
-glm::quat CalcQuaternionFromDegreesEulerAngle(glm::vec3 eulerAngleDegrees) {
- glm::vec3 eulerAngleRadians;
- eulerAngleRadians.x = eulerAngleDegrees.x / 180 * M_PI;
- eulerAngleRadians.y = eulerAngleDegrees.y / 180 * M_PI;
- eulerAngleRadians.z = eulerAngleDegrees.z / 180 * M_PI;
- return glm::quat(eulerAngleRadians);
-}
-
-void PushKeyCodeRecorder(App* app, int* writeKey, bool* writeKeyStatus) {
- app->PushKeyCaptureCallback([=](int key, int action) {
- // Allow the user to cancel by pressing Esc
- if (key == GLFW_KEY_ESCAPE) {
- return true;
- }
-
- if (action == GLFW_PRESS) {
- *writeKey = key;
- *writeKeyStatus = writeKeyStatus;
- return true;
- }
- return false;
- });
-}
-
-struct GobjTreeNodeShowInfo {
- EditorInstance* in_editor;
- GameObject* out_openPopup = nullptr;
-};
-
-void GobjTreeNode(GobjTreeNodeShowInfo& showInfo, GameObject* object) {
- auto& inspector = showInfo.in_editor->GetInspector();
-
- auto attachment = object->GetEditorAttachment();
- if (!attachment) {
- attachment = EaGameObject::Create(object).release();
- object->SetEditorAttachment(attachment); // NOTE: takes ownership
- }
-
- ImGuiTreeNodeFlags flags =
- ImGuiTreeNodeFlags_DefaultOpen |
- ImGuiTreeNodeFlags_OpenOnDoubleClick |
- ImGuiTreeNodeFlags_OpenOnArrow |
- ImGuiTreeNodeFlags_SpanAvailWidth |
- ImGuiTreeNodeFlags_NoTreePushOnOpen;
- if (inspector.selectedItPtr == object) {
- flags |= ImGuiTreeNodeFlags_Selected;
- }
-
- ImGui::PushID(reinterpret_cast<uintptr_t>(object));
- // BEGIN tree node
-
- bool opened = ImGui::TreeNodeEx(attachment->name.c_str(), flags);
- if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
- inspector.SelectTarget(EditorInspector::ITT_GameObject, object);
- }
- if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) &&
- ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
- {
- showInfo.out_openPopup = object;
- }
-
- if (opened) {
- ImGui::Indent();
- for (auto& child : object->GetChildren()) {
- GobjTreeNode(showInfo, child);
- }
- ImGui::Unindent();
- }
-
- // END tree node
- ImGui::PopID();
-};
-
-#define GAMEOBJECT_CONSTRUCTOR(ClassName) [](GameWorld* world) -> GameObject* { return new ClassName(world); }
-struct CreatableGameObject {
- GameObject* (*factory)(GameWorld*);
- const char* name;
- GameObject::Kind kind;
-} creatableGameObjects[] = {
- {
- .factory = GAMEOBJECT_CONSTRUCTOR(GameObject),
- .name = "GameObject",
- .kind = GameObject::KD_Generic,
- },
- {
- .factory = GAMEOBJECT_CONSTRUCTOR(SimpleGeometryObject),
- .name = "Simple Geometry",
- .kind = GameObject::KD_SimpleGeometry,
- },
- {
- .factory = GAMEOBJECT_CONSTRUCTOR(BuildingObject),
- .name = "Building",
- .kind = GameObject::KD_Building,
- },
- {
- .factory = GAMEOBJECT_CONSTRUCTOR(LevelWrapperObject),
- .name = "Level Wrapper",
- .kind = GameObject::KD_LevelWrapper,
- },
-};
-#undef GAMEOBJECT_CONSTRUCTOR
-} // namespace ProjectBrussel_UNITY_ID
-
-std::unique_ptr<IEditor> IEditor::CreateInstance(App* app) {
- return std::make_unique<EditorInstance>(app);
-}
-
-EditorInstance::EditorInstance(App* app)
- : mApp{ app }
- , mEdContentBrowser(&mEdInspector) {
- mEditorCamera.name = "Editor Camera"s;
- mEditorCamera.SetEyePos(glm::vec3(0, 0, 1));
- mEditorCamera.SetTargetDirection(glm::vec3(0, 0, -1));
- app->BindActiveCamera(&mEditorCamera);
-}
-
-EditorInstance::~EditorInstance() {
-}
-
-void EditorInstance::OnGameStateChanged(bool running) {
- if (running) {
- mApp->UnbindActiveCamera();
- } else {
- mApp->BindActiveCamera(&mEditorCamera);
- }
-}
-
-void EditorInstance::Show() {
- using namespace ProjectBrussel_UNITY_ID;
- using namespace Tags;
-
- auto world = mApp->GetWorld();
- auto& io = ImGui::GetIO();
-
- ImGui::BeginMainMenuBar();
- if (ImGui::BeginMenu("View")) {
- ImGui::MenuItem("ImGui Demo", nullptr, &mWindowVisible_ImGuiDemo);
- ImGui::MenuItem("Command Palette", "Ctrl+Shift+P", &mWindowVisible_CommandPalette);
- ImGui::MenuItem("Inspector", nullptr, &mWindowVisible_Inspector);
- ImGui::MenuItem("Content Browser", "Ctrl+Space", &mWindowVisible_ContentBrowser);
- ImGui::MenuItem("World Structure", nullptr, &mWindowVisible_WorldStructure);
- ImGui::MenuItem("World Properties", nullptr, &mWindowVisible_WorldProperties);
- ImGui::EndMenu();
- }
- ImGui::EndMainMenuBar();
-
- if (mWindowVisible_ImGuiDemo) {
- ImGui::ShowDemoWindow(&mWindowVisible_ImGuiDemo);
- }
-
- if (io.KeyCtrl && io.KeyShift && ImGui::IsKeyPressed(GLFW_KEY_P, false)) {
- mWindowVisible_CommandPalette = !mWindowVisible_CommandPalette;
- }
- if (mWindowVisible_CommandPalette) {
- mEdCommandPalette.Show(&mWindowVisible_CommandPalette);
- }
-
- if (io.KeyCtrl && ImGui::IsKeyPressed(GLFW_KEY_SPACE, false)) {
- mWindowVisible_ContentBrowser = !mWindowVisible_ContentBrowser;
- }
- if (mWindowVisible_ContentBrowser) {
- mEdContentBrowser.Show(&mWindowVisible_ContentBrowser);
- }
-
- auto& camera = *mApp->GetActiveCamera();
- ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
- ImGuizmo::SetDrawlist(ImGui::GetBackgroundDrawList());
-
- if (IsCurrentCameraEditor() && mEcm == ECM_Side3D) {
- float viewManipulateRight = io.DisplaySize.x;
- float viewManipulateTop = 0;
-
- // TODO get rid of this massive hack: how to manage const better for intuitively read-only, but write doesn't-care data?
- auto& lastFrameInfo = const_cast<RendererFrameInfo&>(mApp->GetWorldRenderer()->GetLastFrameInfo());
- auto& view = lastFrameInfo.matrixView;
- auto& proj = lastFrameInfo.matrixProj;
-
-#if 0
- ImGuizmo::ViewManipulate(
- glm::value_ptr(view),
- 200.0f, // TODO
- ImVec2(viewManipulateRight - 128, viewManipulateTop),
- ImVec2(128, 128),
- 0x10101010);
- // Extract eye and target position from view matrix
- // - View matrix transforms world space to view space
- // - Inverse view matrix should transform view space into world space
- // - In view space, camera's pos is (0,0,0) and the look/forward vector should be (0,0,-1)
- auto invView = glm::inverse(view);
- camera.eye = invView * glm::vec4(0, 0, 0, 1);
- camera.target = camera.eye + glm::vec3(invView * glm::vec4(0, 0, -1, 1));
-#endif
-
- // TODO draw this as a part of the world so it doesn't block objects
-#if 0
- glm::mat4 identity(1.00f);
- ImGuizmo::DrawGrid(
- glm::value_ptr(view),
- glm::value_ptr(proj),
- glm::value_ptr(identity),
- 100.f);
-#endif
-
- { // Camera controls
- auto cameraPos = camera.eye;
- auto cameraForward = glm::normalize(camera.target - camera.eye);
- // Always move on the horzontal flat plane
- cameraForward.y = 0.0f;
-
- if (mMoveCamKeyboard) {
- if (ImGui::IsKeyDown(ImGuiKey_W)) {
- cameraPos += mMoveCamSlideSpeed * cameraForward;
- }
- if (ImGui::IsKeyDown(ImGuiKey_S)) {
- auto cameraBack = glm::normalize(-cameraForward);
- cameraPos += mMoveCamSlideSpeed * cameraBack;
- }
- if (ImGui::IsKeyDown(ImGuiKey_A)) {
- auto cameraRight = glm::normalize(glm::cross(cameraForward, glm::vec3(0, 1, 0)));
- auto cameraLeft = -cameraRight;
- cameraPos += mMoveCamSlideSpeed * cameraLeft;
- }
- if (ImGui::IsKeyDown(ImGuiKey_D)) {
- auto cameraRight = glm::normalize(glm::cross(cameraForward, glm::vec3(0, 1, 0)));
- cameraPos += mMoveCamSlideSpeed * cameraRight;
- }
- if (ImGui::IsKeyDown(ImGuiKey_Space)) {
- cameraPos.y += mMoveCamSlideSpeed;
- }
- if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) {
- cameraPos.y -= mMoveCamSlideSpeed;
- }
- }
-
- if (mMoveCamScrollWheel) {
- cameraPos += cameraForward * io.MouseWheel * mMoveCamScrollSpeed;
- }
-
- camera.SetEyePos(cameraPos);
- }
- } else {
- { // Camera controls
- auto cameraPos = camera.eye;
-
- if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) && !io.WantCaptureMouse && !mDragCam_Happening) {
- mDragCam_CamInitial = camera.eye;
- mDragCam_CursorInitial = ImGui::GetMousePos();
- mDragCam_Happening = true;
- }
- if (mDragCam_Happening) {
- auto newPos = ImGui::GetMousePos();
- // NOTE: we are emulating as if the mouse is dragging the "canvas", through moving the camera in the opposite direction of the natural position delta
- float deltaX = mDragCam_CursorInitial.x - newPos.x;
- cameraPos.x = mDragCam_CamInitial.x + deltaX / camera.pixelsPerMeter;
- float deltaY = -(mDragCam_CursorInitial.y - newPos.y); // Invert Y delta because ImGui uses top-left origin (mouse moving down translates to positive value, but in our coordinate system down is negative)
- cameraPos.y = mDragCam_CamInitial.y + deltaY / camera.pixelsPerMeter;
- }
- if (ImGui::IsMouseReleased(ImGuiMouseButton_Right)) {
- mDragCam_Happening = false;
- }
-
- if (mMoveCamKeyboard) {
- if (ImGui::IsKeyDown(ImGuiKey_W)) {
- cameraPos.y += mMoveCamSlideSpeed;
- }
- if (ImGui::IsKeyDown(ImGuiKey_S)) {
- cameraPos.y -= mMoveCamSlideSpeed;
- }
- if (ImGui::IsKeyDown(ImGuiKey_A)) {
- cameraPos.x -= mMoveCamSlideSpeed;
- }
- if (ImGui::IsKeyDown(ImGuiKey_D)) {
- cameraPos.x += mMoveCamSlideSpeed;
- }
- }
-
- if (mMoveCamScrollWheel) {
- cameraPos.z = std::clamp(cameraPos.z + io.MouseWheel, 0.1f, 100.0f);
- }
-
- camera.SetEyePos(cameraPos);
- }
- }
-
- if (mWindowVisible_Inspector) {
- ImGui::Begin("Inspector");
- switch (mEdInspector.selectedItt) {
- case EditorInspector::ITT_GameObject: {
- auto object = static_cast<GameObject*>(mEdInspector.selectedItPtr);
- ShowInspector(object);
- } break;
-
- case EditorInspector::ITT_Ires: {
- auto ires = static_cast<IresObject*>(mEdInspector.selectedItPtr);
- ShowInspector(ires);
- } break;
-
- case EditorInspector::ITT_Level: {
- auto ldObj = static_cast<LevelManager::LoadableObject*>(mEdInspector.selectedItPtr);
- ShowInspector(ldObj);
- } break;
-
- case EditorInspector::ITT_None: break;
- }
- ImGui::End();
- }
-
- if (mWindowVisible_WorldProperties) {
- ImGui::Begin("World properties");
- ShowWorldProperties();
- ImGui::End();
- }
-
- if (mWindowVisible_WorldStructure) {
- ImGui::Begin("World structure");
- GobjTreeNodeShowInfo showInfo{
- .in_editor = this,
- };
- GobjTreeNode(showInfo, &world->GetRoot());
-
- if (showInfo.out_openPopup) {
- mPopupCurrent_GameObject = showInfo.out_openPopup;
-
- ImGui::OpenPopup("GameObject Popup");
- ImGui::SetNextWindowPos(ImGui::GetMousePos());
- }
- if (ImGui::BeginPopup("GameObject Popup")) {
- // Target no longer selected during popup open
- if (!mPopupCurrent_GameObject) {
- ImGui::CloseCurrentPopup();
- }
-
- if (ImGui::BeginMenu("Add child")) {
- for (size_t i = 0; i < std::size(creatableGameObjects); ++i) {
- auto& info = creatableGameObjects[i];
- if (ImGui::MenuItem(info.name)) {
- auto object = info.factory(world);
- mPopupCurrent_GameObject->AddChild(object);
- }
- }
- ImGui::EndMenu();
- }
- ImGui::Separator();
- if (ImGui::MenuItem("Remove")) {
- ImGui::DialogConfirmation("Are you sure you want to delete this GameObject?", [object = mPopupCurrent_GameObject](bool yes) {
- object->RemoveSelfFromParent();
- delete object;
- });
- }
- ImGui::EndPopup();
- }
- ImGui::End();
- }
-
- ShowSpriteViewer();
-
- ImGui::ShowDialogs();
- ImGui::ShowNotifications();
-}
-
-void EditorInstance::ShowWorldProperties() {
- if (mApp->IsGameRunning()) {
- if (ImGui::Button("Pause")) {
- mApp->SetGameRunning(false);
- }
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("The game is currently running. Click to pause.");
- }
- } else {
- if (ImGui::Button("Play")) {
- mApp->SetGameRunning(true);
- }
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("The game is currently paused. Click to run.");
- }
- }
-
- auto& camera = *mApp->GetActiveCamera();
-
- // vvv Camera settings (per instance)
- ImGui::TextUnformatted("Active camera:");
- ImGui::Indent();
-
- ImGui::TextUnformatted(camera.name.c_str());
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Object at <%p>", &camera);
- }
-
- ImGui::InputFloat3("Eye", glm::value_ptr(camera.eye));
- if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsItemHovered()) {
- ImGui::OpenPopup("##CTXMENU");
- }
- ImGui::InputFloat3("Target", glm::value_ptr(camera.target));
- if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsItemHovered()) {
- ImGui::OpenPopup("##CTXMENU");
- }
- if (ImGui::BeginPopup("##CTXMENU", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings)) {
- if (ImGui::MenuItem("Reset to origin")) {
- camera.SetEyePos(glm::vec3(1.0f, 1.0f, 1.0f));
- }
- if (ImGui::MenuItem("Reset completely")) {
- camera.eye = glm::vec3(0, 0, 1);
- camera.target = glm::vec3(0, 0, 0);
- }
- ImGui::EndPopup();
- }
-
- if (IsCurrentCameraEditor()) {
- const char* preview;
- switch (mEcm) {
- case ECM_2D: preview = "2D view"; break;
- case ECM_Side3D: preview = "Side 3D view"; break;
- }
- if (ImGui::BeginCombo("Mode", preview)) {
- if (ImGui::Selectable("2D view", mEcm == ECM_2D)) {
- if (mEcm != ECM_2D) {
- mEcm = ECM_2D;
-
- // TODO project eye to world plane
-
- camera.SetHasPerspective(false);
- }
- }
- if (ImGui::Selectable("Side 3D view", mEcm == ECM_Side3D, ImGuiSelectableFlags_None)) {
- if (mEcm != ECM_Side3D) {
- mEcm = ECM_Side3D;
-
- auto origEye = camera.eye;
- auto origTarget = camera.target;
-
- // New setup: focus on the point of world plane that we were originally "hovering above"
- camera.target = origEye;
- camera.target.z = 0.0f;
-
- // New setup: move the eye back at an angle
- camera.eye = camera.target;
- camera.eye.x += 4.0f * std::cos(60.0f);
- camera.eye.y += 0.0f;
- camera.eye.z += 4.0f * std::sin(60.0f);
-
- camera.SetHasPerspective(true);
- }
- }
- ImGui::EndCombo();
- }
- }
-
- ImGui::Checkbox("Perspective", &camera.perspective);
- if (camera.perspective) {
- float fovDegress = camera.fov / M_PI * 180.0f;
- if (ImGui::SliderFloat("FOV", &fovDegress, 1.0f, 180.0f)) {
- camera.fov = fovDegress / 180.0f * M_PI;
- }
- } else {
- if (ImGui::InputFloat("Pixels per meter", &camera.pixelsPerMeter)) {
- camera.pixelsPerMeter = std::max(camera.pixelsPerMeter, 0.0f);
- }
- }
-
- ImGui::Unindent();
- // ^^^ Camera settings (per instance)
- // vvv Camera control settings
- ImGui::TextUnformatted("Camera controls:");
- ImGui::Indent();
-
- ImGui::Checkbox("Move camera with WASD", &mMoveCamKeyboard);
- ImGui::SliderFloat("Slide speed", &mMoveCamSlideSpeed, 0.1f, 10.0f);
-
- ImGui::Checkbox("Move camera with scoll wheel", &mMoveCamScrollWheel);
- ImGui::SliderFloat("Scroll speed", &mMoveCamScrollSpeed, 0.01, 10.0f);
-
- ImGui::Unindent();
- // ^^^ Camera control settings
-}
-
-// TOOD move resource-specific and gameobject-specific inspector code into attachments mechanism
-
-void EditorInstance::ShowInspector(IresObject* ires) {
- ires->ShowEditor(*this);
-}
-
-void EditorInstance::ShowInspector(LevelManager::LoadableObject* ldObj) {
- using namespace Tags;
- using namespace ProjectBrussel_UNITY_ID;
-
- ImGui::InputText("Name", &ldObj->name);
- ImGui::InputTextMultiline("Desciption", &ldObj->description);
-
- if (ImGui::CollapsingHeader("Instanciation Entries")) {
- ldObj->level->ShowInstanciationEntries(*this);
- }
-}
-
-void EditorInstance::ShowInspector(GameObject* object) {
- using namespace Tags;
- using namespace ProjectBrussel_UNITY_ID;
-
- auto& io = ImGui::GetIO();
-
- auto objectEa = static_cast<EaGameObject*>(object->GetEditorAttachment());
-
- auto ShowInspector = [&](/*array[6]*/ float* bounds = nullptr) {
- glm::mat4 identityMatrix(1.00f);
-
- if (io.KeyAlt && ImGui::IsKeyPressed(ImGuiKey_T))
- mGuizmo.currOperation = ImGuizmo::TRANSLATE;
- if (io.KeyAlt && ImGui::IsKeyPressed(ImGuiKey_R))
- mGuizmo.currOperation = ImGuizmo::ROTATE;
- if (io.KeyAlt && ImGui::IsKeyPressed(ImGuiKey_S))
- mGuizmo.currOperation = ImGuizmo::SCALE;
- if (ImGui::RadioButton("Translate", mGuizmo.currOperation == ImGuizmo::TRANSLATE))
- mGuizmo.currOperation = ImGuizmo::TRANSLATE;
- ImGui::SameLine();
- if (ImGui::RadioButton("Rotate", mGuizmo.currOperation == ImGuizmo::ROTATE))
- mGuizmo.currOperation = ImGuizmo::ROTATE;
- ImGui::SameLine();
- if (ImGui::RadioButton("Scale", mGuizmo.currOperation == ImGuizmo::SCALE))
- mGuizmo.currOperation = ImGuizmo::SCALE;
- ImGui::SameLine();
- if (ImGui::RadioButton("Universal", mGuizmo.currOperation == ImGuizmo::UNIVERSAL))
- mGuizmo.currOperation = ImGuizmo::UNIVERSAL;
-
- if (mGuizmo.currOperation != ImGuizmo::SCALE) {
- if (ImGui::RadioButton("Local", mGuizmo.currMode == ImGuizmo::LOCAL))
- mGuizmo.currMode = ImGuizmo::LOCAL;
- ImGui::SameLine();
- if (ImGui::RadioButton("World", mGuizmo.currMode == ImGuizmo::WORLD))
- mGuizmo.currMode = ImGuizmo::WORLD;
- }
-
- ImGui::Checkbox("##UseSnap", &mGuizmo.useSnap);
- ImGui::SameLine();
- switch (mGuizmo.currOperation) {
- case ImGuizmo::TRANSLATE:
- ImGui::InputFloat3("Pos Snap", &mGuizmo.snap[0]);
- break;
- case ImGuizmo::ROTATE:
- ImGui::InputFloat("Angle Snap", &mGuizmo.snap[0]);
- break;
- case ImGuizmo::SCALE:
- ImGui::InputFloat("Scale Snap", &mGuizmo.snap[0]);
- break;
- default: break;
- }
-
- if (bounds) {
- ImGui::Checkbox("Bound Sizing", &mGuizmo.boundSizing);
- if (mGuizmo.boundSizing) {
- ImGui::PushID(3);
- ImGui::Checkbox("##BoundSizing", &mGuizmo.boundSizingSnap);
- ImGui::SameLine();
- ImGui::InputFloat3("Bound Snap", mGuizmo.boundsSnap);
- ImGui::PopID();
- }
- }
-
- ImGui::Separator();
-
- auto objectPos = object->GetPos();
- if (ImGui::InputFloat3("Translation", &objectPos.x)) {
- object->SetPos(objectPos);
- }
-
- auto objectRot = object->GetRotation();
- if (ImGui::InputFloat3("Rotation", &objectEa->eulerAnglesRotation.x)) {
- objectRot = CalcQuaternionFromDegreesEulerAngle(objectEa->eulerAnglesRotation);
- object->SetRotation(objectRot);
- }
-
- auto objectScale = object->GetScale();
- if (ImGui::InputFloat3("Scale", &objectScale.x)) {
- object->SetScale(objectScale);
- }
- };
-
- auto ShowGuizmo = [&](/*array[6]*/ float* bounds = nullptr) {
- glm::mat4 identityMatrix(1.00f);
-
- auto& lastFrameInfo = mApp->GetWorldRenderer()->GetLastFrameInfo();
- auto& cameraViewMat = lastFrameInfo.matrixView;
- const float* cameraView = &cameraViewMat[0][0];
- auto& cameraProjMat = lastFrameInfo.matrixProj;
- const float* cameraProj = &cameraProjMat[0][0];
-
- auto objectPos = object->GetPos();
- auto objectScale = object->GetScale();
-
- float matrix[16];
- ImGuizmo::RecomposeMatrixFromComponents(&objectPos.x, &objectEa->eulerAnglesRotation.x, &objectScale.x, matrix);
-
- bool edited = ImGuizmo::Manipulate(
- cameraView,
- cameraProj,
- mGuizmo.currOperation,
- mGuizmo.currMode,
- matrix,
- nullptr,
- mGuizmo.useSnap ? mGuizmo.snap : nullptr,
- mGuizmo.boundSizing ? bounds : nullptr,
- (bounds && mGuizmo.boundSizingSnap) ? mGuizmo.boundsSnap : nullptr);
-
- if (edited) {
- ImGuizmo::DecomposeMatrixToComponents(matrix, &objectPos.x, &objectEa->eulerAnglesRotation.x, &objectScale.x);
- object->SetPos(objectPos);
- object->SetRotation(CalcQuaternionFromDegreesEulerAngle(objectEa->eulerAnglesRotation));
- object->SetScale(objectScale);
- }
- };
-
- auto type = object->GetKind();
- switch (type) {
- case GameObject::KD_Player: {
- ShowInspector();
- ShowGuizmo();
- ImGui::Separator();
-
- auto player = static_cast<Player*>(object);
- auto ea = static_cast<EaPlayer*>(objectEa);
- auto& kb = player->keybinds;
-
- ImGui::Text("Player #%d", player->GetId());
-
- ImGui::TextUnformatted("Spritesheet: ");
- ImGui::SameLine();
- IresObject::ShowReferenceSafe(*this, ea->confSprite.Get());
- if (ImGui::BeginDragDropTarget()) {
- if (auto payload = ImGui::AcceptDragDropPayload(Metadata::EnumToString(IresObject::KD_Spritesheet).data())) {
- auto spritesheet = *static_cast<IresSpritesheet* const*>(payload->Data);
- ea->confSprite.Attach(spritesheet);
- auto def = spritesheet->GetInstance();
- player->sprite.SetDefinition(def);
- player->renderObject.autofill_TextureAtlas.Attach(def->GetAtlas());
- }
- ImGui::EndDragDropTarget();
- }
-
- ImGui::TextUnformatted("Material: ");
- ImGui::SameLine();
- IresObject::ShowReferenceSafe(*this, ea->confMaterial.Get());
- if (ImGui::BeginDragDropTarget()) {
- if (auto payload = ImGui::AcceptDragDropPayload(Metadata::EnumToString(IresObject::KD_Material).data())) {
- auto material = *static_cast<IresMaterial* const*>(payload->Data);
- ea->confMaterial.Attach(material);
- player->SetMaterial(material->GetInstance());
- }
- ImGui::EndDragDropTarget();
- }
-
- if (ImGui::Button("Load config")) {
- bool success = player->LoadFromFile();
- if (success) {
- ImGui::AddNotification(ImGuiToast(ImGuiToastType_Success, "Successfully loaded player config"));
- }
- }
- ImGui::SameLine();
- if (ImGui::Button("Save config")) {
- bool success = player->SaveToFile();
- if (success) {
- ImGui::AddNotification(ImGuiToast(ImGuiToastType_Success, "Successfully saved player config"));
- }
- }
-
- ImGui::Text("Move left (%s)", ImGui::GetKeyNameGlfw(kb.keyLeft));
- ImGui::SameLine();
- if (ImGui::Button("Change##Move left")) {
- PushKeyCodeRecorder(mApp, &kb.keyLeft, &kb.pressedLeft);
- }
-
- ImGui::Text("Move right (%s)", ImGui::GetKeyNameGlfw(kb.keyRight));
- ImGui::SameLine();
- if (ImGui::Button("Change##Move right")) {
- PushKeyCodeRecorder(mApp, &kb.keyRight, &kb.pressedRight);
- }
-
- ImGui::Text("Jump (%s)", ImGui::GetKeyNameGlfw(kb.keyJump));
- ImGui::SameLine();
- if (ImGui::Button("Change##Jump")) {
- PushKeyCodeRecorder(mApp, &kb.keyJump, &kb.pressedJump);
- }
-
- ImGui::Text("Attack (%s)", ImGui::GetKeyNameGlfw(kb.keyAttack));
- ImGui::SameLine();
- if (ImGui::Button("Change##Attack")) {
- PushKeyCodeRecorder(mApp, &kb.keyAttack, &kb.pressedAttack);
- }
- } break;
-
- case GameObject::KD_SimpleGeometry: {
- auto sg = static_cast<SimpleGeometryObject*>(object);
-
- float bounds[6] = {
- /*x1*/ -sg->GetSize().x / 2.0f,
- /*y1*/ -sg->GetSize().y / 2.0f,
- /*z1*/ -sg->GetSize().z / 2.0f,
- /*x2*/ +sg->GetSize().x / 2.0f,
- /*y2*/ +sg->GetSize().y / 2.0f,
- /*z2*/ +sg->GetSize().z / 2.0f,
- };
-
- ShowInspector(bounds);
- ShowGuizmo(bounds);
- ImGui::Separator();
-
- sg->SetSize(glm::vec3(bounds[3] - bounds[0], bounds[4] - bounds[1], bounds[5] - bounds[2]));
-
- auto size = sg->GetSize();
- if (ImGui::InputFloat3("Size", &size.x)) {
- sg->SetSize(size);
- }
-
- auto xFaceColor = sg->GetXFaceColor();
- if (ImGui::ColorEdit4("X color", &xFaceColor)) {
- sg->SetXFaceColor(xFaceColor);
- }
- auto yFaceColor = sg->GetYFaceColor();
- if (ImGui::ColorEdit4("Y color", &yFaceColor)) {
- sg->SetYFaceColor(yFaceColor);
- }
- auto zFaceColor = sg->GetZFaceColor();
- if (ImGui::ColorEdit4("Z color", &zFaceColor)) {
- sg->SetZFaceColor(zFaceColor);
- }
-
- if (ImGui::Button("Sync from X color")) {
- sg->SetYFaceColor(xFaceColor);
- sg->SetZFaceColor(xFaceColor);
- }
- ImGui::SameLine();
- if (ImGui::Button("Reset")) {
- sg->SetXFaceColor(kXAxisColor);
- sg->SetYFaceColor(kYAxisColor);
- sg->SetZFaceColor(kZAxisColor);
- }
- } break;
-
- case GameObject::KD_Building: {
- ShowInspector();
- ShowGuizmo();
- ImGui::Separator();
-
- auto b = static_cast<BuildingObject*>(object);
- // TODO
- } break;
-
- case GameObject::KD_LevelWrapper: {
- ShowInspector();
- ShowGuizmo();
- ImGui::Separator();
-
- auto lwo = static_cast<LevelWrapperObject*>(object);
- // TODO
- } break;
-
- default: {
- ShowInspector();
- ShowGuizmo();
- } break;
- }
-}
-
-void EditorInstance::OpenSpriteViewer(SpriteDefinition* sprite) {
- mSpriteView_Instance.Attach(sprite);
- mSpriteView_Frame = 0;
- mSpriteView_OpenNextFrame = true;
-}
-
-void EditorInstance::ShowSpriteViewer() {
- if (mSpriteView_Instance == nullptr) return;
- if (!mSpriteView_Instance->IsValid()) return;
-
- if (mSpriteView_OpenNextFrame) {
- mSpriteView_OpenNextFrame = false;
- ImGui::OpenPopup("Sprite Viewer");
- }
-
- bool windowOpen = true;
- if (ImGui::BeginPopupModal("Sprite Viewer", &windowOpen)) {
- auto atlas = mSpriteView_Instance->GetAtlas();
- auto atlasSize = atlas->GetInfo().size;
- auto& frames = mSpriteView_Instance->GetFrames();
-
- int frameCount = mSpriteView_Instance->GetFrames().size();
- if (ImGui::Button("<")) {
- --mSpriteView_Frame;
- }
- ImGui::SameLine();
- ImGui::Text("%d/%d", mSpriteView_Frame + 1, frameCount);
- ImGui::SameLine();
- if (ImGui::Button(">")) {
- ++mSpriteView_Frame;
- }
- // Scrolling down (negative value) should advance, so invert the sign
- mSpriteView_Frame += -std::round(ImGui::GetIO().MouseWheel);
- // Clamp mSpriteView_Frame to range [0, frameCount)
- if (mSpriteView_Frame < 0) {
- mSpriteView_Frame = frameCount - 1;
- } else if (mSpriteView_Frame >= frameCount) {
- mSpriteView_Frame = 0;
- }
-
- auto& currFrame = frames[mSpriteView_Frame];
- auto boundingBox = mSpriteView_Instance->GetBoundingBox();
- ImGui::Text("Frame size: (%d, %d)", boundingBox.x, boundingBox.y);
- ImGui::Text("Frame location: (%f, %f) to (%f, %f)", currFrame.u0, currFrame.v0, currFrame.u1, currFrame.v1);
- ImGui::Image(
- (ImTextureID)(uintptr_t)atlas->GetHandle(),
- Utils::FitImage(atlasSize),
- ImVec2(currFrame.u0, currFrame.v0),
- ImVec2(currFrame.u1, currFrame.v1));
-
- ImGui::EndPopup();
- }
-}
diff --git a/source/Game/EditorCorePrivate.hpp b/source/Game/EditorCorePrivate.hpp
deleted file mode 100644
index 4fbfb72..0000000
--- a/source/Game/EditorCorePrivate.hpp
+++ /dev/null
@@ -1,130 +0,0 @@
-#pragma once
-
-#include "EditorCore.hpp"
-
-#include "App.hpp"
-#include "EditorAttachment.hpp"
-#include "EditorCommandPalette.hpp"
-#include "EditorUtils.hpp"
-#include "GameObject.hpp"
-#include "Ires.hpp"
-#include "Level.hpp"
-#include "RcPtr.hpp"
-#include "Sprite.hpp"
-#include "World.hpp"
-
-#include <memory>
-#include <string>
-
-// TODO move inspector drawing to this class
-class EditorInspector final : public IEditorInspector {
-public:
- std::string renamingScratchBuffer;
- void* selectedItPtr = nullptr;
- TargetType selectedItt = ITT_None;
- bool renaming = false;
-
- void SelectTarget(TargetType type, void* object) override;
-};
-
-class EditorContentBrowser final : public IEditorContentBrowser {
-private:
- enum Pane {
- P_Settings,
- P_Ires,
- P_Level,
- };
-
- 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 mBrowserHeight = 0.5f;
- float mSplitterLeft = kLeftPaneMinWidth;
- float mSplitterRight = 0.0f;
- bool mDocked = true;
-
-public:
- EditorContentBrowser(EditorInspector* inspector);
- ~EditorContentBrowser() override;
-
- void Show(bool* open = nullptr);
-};
-
-struct GuizmoState {
- ImGuizmo::OPERATION currOperation = ImGuizmo::TRANSLATE;
- ImGuizmo::MODE currMode = ImGuizmo::LOCAL;
- glm::mat4 cubeMatrix;
- float snap[3] = { 1.f, 1.f, 1.f };
- float boundsSnap[3] = { 0.1f, 0.1f, 0.1f };
- bool useSnap = false;
- bool boundSizing = false;
- bool boundSizingSnap = false;
-};
-
-// TODO editor undo stack
-class EditorInstance : public IEditor {
-public:
- enum EditorCameraMode {
- // "Game mode": the camera views from the side, where the forward vector is perpendicular to the world plane
- ECM_2D,
- // "Editor mode": equivalent to Unity's 3D mode, the camera is completely free and controlled as a 3d camera
- ECM_Side3D,
- };
-
-private:
- App* mApp;
- GameObject* mPopupCurrent_GameObject = nullptr;
- Camera mEditorCamera;
- RcPtr<SpriteDefinition> mSpriteView_Instance;
- EditorCommandPalette mEdCommandPalette;
- EditorInspector mEdInspector;
- EditorContentBrowser mEdContentBrowser;
- GuizmoState mGuizmo;
- glm::vec3 mDragCam_CamInitial;
- ImVec2 mDragCam_CursorInitial;
- int mSpriteView_Frame;
- float mMoveCamScrollSpeed = 1.0f;
- float mMoveCamSlideSpeed = 0.1f;
- EditorCameraMode mEcm = ECM_2D;
- bool mSpriteView_OpenNextFrame = false;
- bool mWindowVisible_ImGuiDemo = false;
- bool mWindowVisible_CommandPalette = false;
- bool mWindowVisible_Inspector = true;
- bool mWindowVisible_ContentBrowser = true;
- bool mWindowVisible_WorldStructure = true;
- bool mWindowVisible_WorldProperties = true;
- bool mDragCam_Happening = false;
- bool mMoveCamKeyboard = false;
- bool mMoveCamScrollWheel = false;
-
-public:
- EditorInstance(App* app);
- ~EditorInstance() override;
-
- void OnGameStateChanged(bool running) override;
- void Show() override;
-
- EditorInspector& GetInspector() override { return mEdInspector; }
- EditorContentBrowser& GetContentBrowser() override { return mEdContentBrowser; }
-
- void OpenSpriteViewer(SpriteDefinition* sprite) override;
-
-private:
- bool IsCurrentCameraEditor() const {
- return !mApp->IsGameRunning();
- }
-
- void ShowWorldProperties();
-
- void ShowInspector(IresObject* ires);
- void ShowInspector(LevelManager::LoadableObject* ldObj);
- void ShowInspector(GameObject* object);
-
- void ShowSpriteViewer();
-};
diff --git a/source/Game/EditorGuizmo.cpp b/source/Game/EditorGuizmo.cpp
deleted file mode 100644
index 3e4f890..0000000
--- a/source/Game/EditorGuizmo.cpp
+++ /dev/null
@@ -1,2897 +0,0 @@
-// https://github.com/CedricGuillemet/ImGuizmo
-// v 1.84 WIP
-//
-// The MIT License(MIT)
-//
-// Copyright(c) 2021 Cedric Guillemet
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files(the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions :
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-
-#ifndef IMGUI_DEFINE_MATH_OPERATORS
-# define IMGUI_DEFINE_MATH_OPERATORS
-#endif
-#include "EditorGuizmo.hpp"
-#include "imgui_internal.h"
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
-# include <malloc.h>
-#endif
-#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
-# define _malloca(x) alloca(x)
-# define _freea(x)
-#endif
-
-// includes patches for multiview from
-// https://github.com/CedricGuillemet/ImGuizmo/issues/15
-
-namespace IMGUIZMO_NAMESPACE {
-static const float ZPI = 3.14159265358979323846f;
-static const float RAD2DEG = (180.f / ZPI);
-static const float DEG2RAD = (ZPI / 180.f);
-const float screenRotateSize = 0.06f;
-// scale a bit so translate axis do not touch when in universal
-const float rotationDisplayFactor = 1.2f;
-
-static OPERATION operator&(OPERATION lhs, OPERATION rhs) {
- return static_cast<OPERATION>(static_cast<int>(lhs) & static_cast<int>(rhs));
-}
-
-static bool operator!=(OPERATION lhs, int rhs) {
- return static_cast<int>(lhs) != rhs;
-}
-
-static bool Intersects(OPERATION lhs, OPERATION rhs) {
- return (lhs & rhs) != 0;
-}
-
-// True if lhs contains rhs
-static bool Contains(OPERATION lhs, OPERATION rhs) {
- return (lhs & rhs) == rhs;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// utility and math
-
-void FPU_MatrixF_x_MatrixF(const float* a, const float* b, float* r) {
- r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];
- r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];
- r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];
- r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];
-
- r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];
- r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];
- r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];
- r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];
-
- r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];
- r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];
- r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];
- r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];
-
- r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];
- r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];
- r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];
- r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];
-}
-
-void Frustum(float left, float right, float bottom, float top, float znear, float zfar, float* m16) {
- float temp, temp2, temp3, temp4;
- temp = 2.0f * znear;
- temp2 = right - left;
- temp3 = top - bottom;
- temp4 = zfar - znear;
- m16[0] = temp / temp2;
- m16[1] = 0.0;
- m16[2] = 0.0;
- m16[3] = 0.0;
- m16[4] = 0.0;
- m16[5] = temp / temp3;
- m16[6] = 0.0;
- m16[7] = 0.0;
- m16[8] = (right + left) / temp2;
- m16[9] = (top + bottom) / temp3;
- m16[10] = (-zfar - znear) / temp4;
- m16[11] = -1.0f;
- m16[12] = 0.0;
- m16[13] = 0.0;
- m16[14] = (-temp * zfar) / temp4;
- m16[15] = 0.0;
-}
-
-void Perspective(float fovyInDegrees, float aspectRatio, float znear, float zfar, float* m16) {
- float ymax, xmax;
- ymax = znear * tanf(fovyInDegrees * DEG2RAD);
- xmax = ymax * aspectRatio;
- Frustum(-xmax, xmax, -ymax, ymax, znear, zfar, m16);
-}
-
-void Cross(const float* a, const float* b, float* r) {
- r[0] = a[1] * b[2] - a[2] * b[1];
- r[1] = a[2] * b[0] - a[0] * b[2];
- r[2] = a[0] * b[1] - a[1] * b[0];
-}
-
-float Dot(const float* a, const float* b) {
- return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
-}
-
-void Normalize(const float* a, float* r) {
- float il = 1.f / (sqrtf(Dot(a, a)) + FLT_EPSILON);
- r[0] = a[0] * il;
- r[1] = a[1] * il;
- r[2] = a[2] * il;
-}
-
-void LookAt(const float* eye, const float* at, const float* up, float* m16) {
- float X[3], Y[3], Z[3], tmp[3];
-
- tmp[0] = eye[0] - at[0];
- tmp[1] = eye[1] - at[1];
- tmp[2] = eye[2] - at[2];
- Normalize(tmp, Z);
- Normalize(up, Y);
- Cross(Y, Z, tmp);
- Normalize(tmp, X);
- Cross(Z, X, tmp);
- Normalize(tmp, Y);
-
- m16[0] = X[0];
- m16[1] = Y[0];
- m16[2] = Z[0];
- m16[3] = 0.0f;
- m16[4] = X[1];
- m16[5] = Y[1];
- m16[6] = Z[1];
- m16[7] = 0.0f;
- m16[8] = X[2];
- m16[9] = Y[2];
- m16[10] = Z[2];
- m16[11] = 0.0f;
- m16[12] = -Dot(X, eye);
- m16[13] = -Dot(Y, eye);
- m16[14] = -Dot(Z, eye);
- m16[15] = 1.0f;
-}
-
-template <typename T>
-T Clamp(T x, T y, T z) {
- return ((x < y) ? y : ((x > z) ? z : x));
-}
-template <typename T>
-T max(T x, T y) {
- return (x > y) ? x : y;
-}
-template <typename T>
-T min(T x, T y) {
- return (x < y) ? x : y;
-}
-template <typename T>
-bool IsWithin(T x, T y, T z) {
- return (x >= y) && (x <= z);
-}
-
-struct matrix_t;
-struct vec_t {
-public:
- float x, y, z, w;
-
- void Lerp(const vec_t& v, float t) {
- x += (v.x - x) * t;
- y += (v.y - y) * t;
- z += (v.z - z) * t;
- w += (v.w - w) * t;
- }
-
- void Set(float v) { x = y = z = w = v; }
- void Set(float _x, float _y, float _z = 0.f, float _w = 0.f) {
- x = _x;
- y = _y;
- z = _z;
- w = _w;
- }
-
- vec_t& operator-=(const vec_t& v) {
- x -= v.x;
- y -= v.y;
- z -= v.z;
- w -= v.w;
- return *this;
- }
- vec_t& operator+=(const vec_t& v) {
- x += v.x;
- y += v.y;
- z += v.z;
- w += v.w;
- return *this;
- }
- vec_t& operator*=(const vec_t& v) {
- x *= v.x;
- y *= v.y;
- z *= v.z;
- w *= v.w;
- return *this;
- }
- vec_t& operator*=(float v) {
- x *= v;
- y *= v;
- z *= v;
- w *= v;
- return *this;
- }
-
- vec_t operator*(float f) const;
- vec_t operator-() const;
- vec_t operator-(const vec_t& v) const;
- vec_t operator+(const vec_t& v) const;
- vec_t operator*(const vec_t& v) const;
-
- const vec_t& operator+() const { return (*this); }
- float Length() const { return sqrtf(x * x + y * y + z * z); };
- float LengthSq() const { return (x * x + y * y + z * z); };
- vec_t Normalize() {
- (*this) *= (1.f / (Length() > FLT_EPSILON ? Length() : FLT_EPSILON));
- return (*this);
- }
- vec_t Normalize(const vec_t& v) {
- this->Set(v.x, v.y, v.z, v.w);
- this->Normalize();
- return (*this);
- }
- vec_t Abs() const;
-
- void Cross(const vec_t& v) {
- vec_t res;
- res.x = y * v.z - z * v.y;
- res.y = z * v.x - x * v.z;
- res.z = x * v.y - y * v.x;
-
- x = res.x;
- y = res.y;
- z = res.z;
- w = 0.f;
- }
-
- void Cross(const vec_t& v1, const vec_t& v2) {
- x = v1.y * v2.z - v1.z * v2.y;
- y = v1.z * v2.x - v1.x * v2.z;
- z = v1.x * v2.y - v1.y * v2.x;
- w = 0.f;
- }
-
- float Dot(const vec_t& v) const {
- return (x * v.x) + (y * v.y) + (z * v.z) + (w * v.w);
- }
-
- float Dot3(const vec_t& v) const {
- return (x * v.x) + (y * v.y) + (z * v.z);
- }
-
- void Transform(const matrix_t& matrix);
- void Transform(const vec_t& s, const matrix_t& matrix);
-
- void TransformVector(const matrix_t& matrix);
- void TransformPoint(const matrix_t& matrix);
- void TransformVector(const vec_t& v, const matrix_t& matrix) {
- (*this) = v;
- this->TransformVector(matrix);
- }
- void TransformPoint(const vec_t& v, const matrix_t& matrix) {
- (*this) = v;
- this->TransformPoint(matrix);
- }
-
- float& operator[](size_t index) { return ((float*)&x)[index]; }
- const float& operator[](size_t index) const { return ((float*)&x)[index]; }
- bool operator!=(const vec_t& other) const { return memcmp(this, &other, sizeof(vec_t)); }
-};
-
-vec_t makeVect(float _x, float _y, float _z = 0.f, float _w = 0.f) {
- vec_t res;
- res.x = _x;
- res.y = _y;
- res.z = _z;
- res.w = _w;
- return res;
-}
-vec_t makeVect(ImVec2 v) {
- vec_t res;
- res.x = v.x;
- res.y = v.y;
- res.z = 0.f;
- res.w = 0.f;
- return res;
-}
-vec_t vec_t::operator*(float f) const {
- return makeVect(x * f, y * f, z * f, w * f);
-}
-vec_t vec_t::operator-() const {
- return makeVect(-x, -y, -z, -w);
-}
-vec_t vec_t::operator-(const vec_t& v) const {
- return makeVect(x - v.x, y - v.y, z - v.z, w - v.w);
-}
-vec_t vec_t::operator+(const vec_t& v) const {
- return makeVect(x + v.x, y + v.y, z + v.z, w + v.w);
-}
-vec_t vec_t::operator*(const vec_t& v) const {
- return makeVect(x * v.x, y * v.y, z * v.z, w * v.w);
-}
-vec_t vec_t::Abs() const {
- return makeVect(fabsf(x), fabsf(y), fabsf(z));
-}
-
-vec_t Normalized(const vec_t& v) {
- vec_t res;
- res = v;
- res.Normalize();
- return res;
-}
-vec_t Cross(const vec_t& v1, const vec_t& v2) {
- vec_t res;
- res.x = v1.y * v2.z - v1.z * v2.y;
- res.y = v1.z * v2.x - v1.x * v2.z;
- res.z = v1.x * v2.y - v1.y * v2.x;
- res.w = 0.f;
- return res;
-}
-
-float Dot(const vec_t& v1, const vec_t& v2) {
- return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z);
-}
-
-vec_t BuildPlan(const vec_t& p_point1, const vec_t& p_normal) {
- vec_t normal, res;
- normal.Normalize(p_normal);
- res.w = normal.Dot(p_point1);
- res.x = normal.x;
- res.y = normal.y;
- res.z = normal.z;
- return res;
-}
-
-struct matrix_t {
-public:
- union {
- float m[4][4];
- float m16[16];
- struct
- {
- vec_t right, up, dir, position;
- } v;
- vec_t component[4];
- };
-
- operator float*() { return m16; }
- operator const float*() const { return m16; }
- void Translation(float _x, float _y, float _z) { this->Translation(makeVect(_x, _y, _z)); }
-
- void Translation(const vec_t& vt) {
- v.right.Set(1.f, 0.f, 0.f, 0.f);
- v.up.Set(0.f, 1.f, 0.f, 0.f);
- v.dir.Set(0.f, 0.f, 1.f, 0.f);
- v.position.Set(vt.x, vt.y, vt.z, 1.f);
- }
-
- void Scale(float _x, float _y, float _z) {
- v.right.Set(_x, 0.f, 0.f, 0.f);
- v.up.Set(0.f, _y, 0.f, 0.f);
- v.dir.Set(0.f, 0.f, _z, 0.f);
- v.position.Set(0.f, 0.f, 0.f, 1.f);
- }
- void Scale(const vec_t& s) { Scale(s.x, s.y, s.z); }
-
- matrix_t& operator*=(const matrix_t& mat) {
- matrix_t tmpMat;
- tmpMat = *this;
- tmpMat.Multiply(mat);
- *this = tmpMat;
- return *this;
- }
- matrix_t operator*(const matrix_t& mat) const {
- matrix_t matT;
- matT.Multiply(*this, mat);
- return matT;
- }
-
- void Multiply(const matrix_t& matrix) {
- matrix_t tmp;
- tmp = *this;
-
- FPU_MatrixF_x_MatrixF((float*)&tmp, (float*)&matrix, (float*)this);
- }
-
- void Multiply(const matrix_t& m1, const matrix_t& m2) {
- FPU_MatrixF_x_MatrixF((float*)&m1, (float*)&m2, (float*)this);
- }
-
- float GetDeterminant() const {
- return m[0][0] * m[1][1] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1] -
- m[0][2] * m[1][1] * m[2][0] - m[0][1] * m[1][0] * m[2][2] - m[0][0] * m[1][2] * m[2][1];
- }
-
- float Inverse(const matrix_t& srcMatrix, bool affine = false);
- void SetToIdentity() {
- v.right.Set(1.f, 0.f, 0.f, 0.f);
- v.up.Set(0.f, 1.f, 0.f, 0.f);
- v.dir.Set(0.f, 0.f, 1.f, 0.f);
- v.position.Set(0.f, 0.f, 0.f, 1.f);
- }
- void Transpose() {
- matrix_t tmpm;
- for (int l = 0; l < 4; l++)
- {
- for (int c = 0; c < 4; c++)
- {
- tmpm.m[l][c] = m[c][l];
- }
- }
- (*this) = tmpm;
- }
-
- void RotationAxis(const vec_t& axis, float angle);
-
- void OrthoNormalize() {
- v.right.Normalize();
- v.up.Normalize();
- v.dir.Normalize();
- }
-};
-
-void vec_t::Transform(const matrix_t& matrix) {
- vec_t out;
-
- out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + w * matrix.m[3][0];
- out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + w * matrix.m[3][1];
- out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + w * matrix.m[3][2];
- out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + w * matrix.m[3][3];
-
- x = out.x;
- y = out.y;
- z = out.z;
- w = out.w;
-}
-
-void vec_t::Transform(const vec_t& s, const matrix_t& matrix) {
- *this = s;
- Transform(matrix);
-}
-
-void vec_t::TransformPoint(const matrix_t& matrix) {
- vec_t out;
-
- out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0] + matrix.m[3][0];
- out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1] + matrix.m[3][1];
- out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2] + matrix.m[3][2];
- out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3] + matrix.m[3][3];
-
- x = out.x;
- y = out.y;
- z = out.z;
- w = out.w;
-}
-
-void vec_t::TransformVector(const matrix_t& matrix) {
- vec_t out;
-
- out.x = x * matrix.m[0][0] + y * matrix.m[1][0] + z * matrix.m[2][0];
- out.y = x * matrix.m[0][1] + y * matrix.m[1][1] + z * matrix.m[2][1];
- out.z = x * matrix.m[0][2] + y * matrix.m[1][2] + z * matrix.m[2][2];
- out.w = x * matrix.m[0][3] + y * matrix.m[1][3] + z * matrix.m[2][3];
-
- x = out.x;
- y = out.y;
- z = out.z;
- w = out.w;
-}
-
-float matrix_t::Inverse(const matrix_t& srcMatrix, bool affine) {
- float det = 0;
-
- if (affine)
- {
- det = GetDeterminant();
- float s = 1 / det;
- m[0][0] = (srcMatrix.m[1][1] * srcMatrix.m[2][2] - srcMatrix.m[1][2] * srcMatrix.m[2][1]) * s;
- m[0][1] = (srcMatrix.m[2][1] * srcMatrix.m[0][2] - srcMatrix.m[2][2] * srcMatrix.m[0][1]) * s;
- m[0][2] = (srcMatrix.m[0][1] * srcMatrix.m[1][2] - srcMatrix.m[0][2] * srcMatrix.m[1][1]) * s;
- m[1][0] = (srcMatrix.m[1][2] * srcMatrix.m[2][0] - srcMatrix.m[1][0] * srcMatrix.m[2][2]) * s;
- m[1][1] = (srcMatrix.m[2][2] * srcMatrix.m[0][0] - srcMatrix.m[2][0] * srcMatrix.m[0][2]) * s;
- m[1][2] = (srcMatrix.m[0][2] * srcMatrix.m[1][0] - srcMatrix.m[0][0] * srcMatrix.m[1][2]) * s;
- m[2][0] = (srcMatrix.m[1][0] * srcMatrix.m[2][1] - srcMatrix.m[1][1] * srcMatrix.m[2][0]) * s;
- m[2][1] = (srcMatrix.m[2][0] * srcMatrix.m[0][1] - srcMatrix.m[2][1] * srcMatrix.m[0][0]) * s;
- m[2][2] = (srcMatrix.m[0][0] * srcMatrix.m[1][1] - srcMatrix.m[0][1] * srcMatrix.m[1][0]) * s;
- m[3][0] = -(m[0][0] * srcMatrix.m[3][0] + m[1][0] * srcMatrix.m[3][1] + m[2][0] * srcMatrix.m[3][2]);
- m[3][1] = -(m[0][1] * srcMatrix.m[3][0] + m[1][1] * srcMatrix.m[3][1] + m[2][1] * srcMatrix.m[3][2]);
- m[3][2] = -(m[0][2] * srcMatrix.m[3][0] + m[1][2] * srcMatrix.m[3][1] + m[2][2] * srcMatrix.m[3][2]);
- } else
- {
- // transpose matrix
- float src[16];
- for (int i = 0; i < 4; ++i)
- {
- src[i] = srcMatrix.m16[i * 4];
- src[i + 4] = srcMatrix.m16[i * 4 + 1];
- src[i + 8] = srcMatrix.m16[i * 4 + 2];
- src[i + 12] = srcMatrix.m16[i * 4 + 3];
- }
-
- // calculate pairs for first 8 elements (cofactors)
- float tmp[12]; // temp array for pairs
- tmp[0] = src[10] * src[15];
- tmp[1] = src[11] * src[14];
- tmp[2] = src[9] * src[15];
- tmp[3] = src[11] * src[13];
- tmp[4] = src[9] * src[14];
- tmp[5] = src[10] * src[13];
- tmp[6] = src[8] * src[15];
- tmp[7] = src[11] * src[12];
- tmp[8] = src[8] * src[14];
- tmp[9] = src[10] * src[12];
- tmp[10] = src[8] * src[13];
- tmp[11] = src[9] * src[12];
-
- // calculate first 8 elements (cofactors)
- m16[0] = (tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7]) - (tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7]);
- m16[1] = (tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7]) - (tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7]);
- m16[2] = (tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7]) - (tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7]);
- m16[3] = (tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6]) - (tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6]);
- m16[4] = (tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3]) - (tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3]);
- m16[5] = (tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3]) - (tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3]);
- m16[6] = (tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3]) - (tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3]);
- m16[7] = (tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2]) - (tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2]);
-
- // calculate pairs for second 8 elements (cofactors)
- tmp[0] = src[2] * src[7];
- tmp[1] = src[3] * src[6];
- tmp[2] = src[1] * src[7];
- tmp[3] = src[3] * src[5];
- tmp[4] = src[1] * src[6];
- tmp[5] = src[2] * src[5];
- tmp[6] = src[0] * src[7];
- tmp[7] = src[3] * src[4];
- tmp[8] = src[0] * src[6];
- tmp[9] = src[2] * src[4];
- tmp[10] = src[0] * src[5];
- tmp[11] = src[1] * src[4];
-
- // calculate second 8 elements (cofactors)
- m16[8] = (tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15]) - (tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15]);
- m16[9] = (tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15]) - (tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15]);
- m16[10] = (tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15]) - (tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15]);
- m16[11] = (tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14]) - (tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14]);
- m16[12] = (tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9]) - (tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10]);
- m16[13] = (tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10]) - (tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8]);
- m16[14] = (tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8]) - (tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9]);
- m16[15] = (tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9]) - (tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8]);
-
- // calculate determinant
- det = src[0] * m16[0] + src[1] * m16[1] + src[2] * m16[2] + src[3] * m16[3];
-
- // calculate matrix inverse
- float invdet = 1 / det;
- for (int j = 0; j < 16; ++j)
- {
- m16[j] *= invdet;
- }
- }
-
- return det;
-}
-
-void matrix_t::RotationAxis(const vec_t& axis, float angle) {
- float length2 = axis.LengthSq();
- if (length2 < FLT_EPSILON)
- {
- SetToIdentity();
- return;
- }
-
- vec_t n = axis * (1.f / sqrtf(length2));
- float s = sinf(angle);
- float c = cosf(angle);
- float k = 1.f - c;
-
- float xx = n.x * n.x * k + c;
- float yy = n.y * n.y * k + c;
- float zz = n.z * n.z * k + c;
- float xy = n.x * n.y * k;
- float yz = n.y * n.z * k;
- float zx = n.z * n.x * k;
- float xs = n.x * s;
- float ys = n.y * s;
- float zs = n.z * s;
-
- m[0][0] = xx;
- m[0][1] = xy + zs;
- m[0][2] = zx - ys;
- m[0][3] = 0.f;
- m[1][0] = xy - zs;
- m[1][1] = yy;
- m[1][2] = yz + xs;
- m[1][3] = 0.f;
- m[2][0] = zx + ys;
- m[2][1] = yz - xs;
- m[2][2] = zz;
- m[2][3] = 0.f;
- m[3][0] = 0.f;
- m[3][1] = 0.f;
- m[3][2] = 0.f;
- m[3][3] = 1.f;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-
-enum MOVETYPE {
- MT_NONE,
- MT_MOVE_X,
- MT_MOVE_Y,
- MT_MOVE_Z,
- MT_MOVE_YZ,
- MT_MOVE_ZX,
- MT_MOVE_XY,
- MT_MOVE_SCREEN,
- MT_ROTATE_X,
- MT_ROTATE_Y,
- MT_ROTATE_Z,
- MT_ROTATE_SCREEN,
- MT_SCALE_X,
- MT_SCALE_Y,
- MT_SCALE_Z,
- MT_SCALE_XYZ
-};
-
-static bool IsTranslateType(int type) {
- return type >= MT_MOVE_X && type <= MT_MOVE_SCREEN;
-}
-
-static bool IsRotateType(int type) {
- return type >= MT_ROTATE_X && type <= MT_ROTATE_SCREEN;
-}
-
-static bool IsScaleType(int type) {
- return type >= MT_SCALE_X && type <= MT_SCALE_XYZ;
-}
-
-// Matches MT_MOVE_AB order
-static const OPERATION TRANSLATE_PLANS[3] = { TRANSLATE_Y | TRANSLATE_Z, TRANSLATE_X | TRANSLATE_Z, TRANSLATE_X | TRANSLATE_Y };
-
-struct Context {
- Context()
- : mbUsing(false), mbEnable(true), mbUsingBounds(false) {
- }
-
- ImDrawList* mDrawList;
-
- MODE mMode;
- matrix_t mViewMat;
- matrix_t mProjectionMat;
- matrix_t mModel;
- matrix_t mModelLocal; // orthonormalized model
- matrix_t mModelInverse;
- matrix_t mModelSource;
- matrix_t mModelSourceInverse;
- matrix_t mMVP;
- matrix_t mMVPLocal; // MVP with full model matrix whereas mMVP's model matrix might only be translation in case of World space edition
- matrix_t mViewProjection;
-
- vec_t mModelScaleOrigin;
- vec_t mCameraEye;
- vec_t mCameraRight;
- vec_t mCameraDir;
- vec_t mCameraUp;
- vec_t mRayOrigin;
- vec_t mRayVector;
-
- float mRadiusSquareCenter;
- ImVec2 mScreenSquareCenter;
- ImVec2 mScreenSquareMin;
- ImVec2 mScreenSquareMax;
-
- float mScreenFactor;
- vec_t mRelativeOrigin;
-
- bool mbUsing;
- bool mbEnable;
- bool mbMouseOver;
- bool mReversed; // reversed projection matrix
-
- // translation
- vec_t mTranslationPlan;
- vec_t mTranslationPlanOrigin;
- vec_t mMatrixOrigin;
- vec_t mTranslationLastDelta;
-
- // rotation
- vec_t mRotationVectorSource;
- float mRotationAngle;
- float mRotationAngleOrigin;
- // vec_t mWorldToLocalAxis;
-
- // scale
- vec_t mScale;
- vec_t mScaleValueOrigin;
- vec_t mScaleLast;
- float mSaveMousePosx;
-
- // save axis factor when using gizmo
- bool mBelowAxisLimit[3];
- bool mBelowPlaneLimit[3];
- float mAxisFactor[3];
-
- // bounds stretching
- // NOTE: these variable only lives during the duration of a drag
- /// Position in world space, of the knob on the opposite side of the knob being dragged.
- /// This is the point that needs to space regardless of where anchor is placed.
- vec_t mBoundsPivot;
- /// Position in world space, of the knob begin dragged.
- /// This is the point that's being moved.
- vec_t mBoundsAnchor;
- vec_t mBoundsPlan;
- /// Position in local space, of the knob on the opposite side of the knob being dragged
- vec_t mBoundsLocalPivot;
- int mBoundsBestAxis;
- /// The axes that are being modified by the current operation. May contain 1 or 2 elements.
- /// Unused elements are filled with -1 during the operation.
- int mBoundsAxis[2];
- /// The index of the corner that pivot data is fetched from (opposite side from anchor).
- int mBoundsPivotCornerIndex;
- bool mbUsingBounds;
- bool mbIsUsingBigAnchor;
- /// Model matrix passed into ImGuizmo::Manipulate()
- matrix_t mBoundsMatrix;
-
- //
- int mCurrentOperation;
-
- float mX = 0.f;
- float mY = 0.f;
- float mWidth = 0.f;
- float mHeight = 0.f;
- float mXMax = 0.f;
- float mYMax = 0.f;
- float mDisplayRatio = 1.f;
-
- bool mIsOrthographic = false;
-
- int mActualID = -1;
- int mEditingID = -1;
- OPERATION mOperation = OPERATION(-1);
-
- bool mAllowAxisFlip = true;
- float mGizmoSizeClipSpace = 0.1f;
-};
-
-static Context gContext;
-
-static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) };
-static const ImU32 directionColor[3] = { IM_COL32(0xAA, 0, 0, 0xFF), IM_COL32(0, 0xAA, 0, 0xFF), IM_COL32(0, 0, 0xAA, 0XFF) };
-
-// Alpha: 100%: FF, 87%: DE, 70%: B3, 54%: 8A, 50%: 80, 38%: 61, 12%: 1F
-static const ImU32 planeColor[3] = { IM_COL32(0xAA, 0, 0, 0x61), IM_COL32(0, 0xAA, 0, 0x61), IM_COL32(0, 0, 0xAA, 0x61) };
-static const ImU32 selectionColor = IM_COL32(0xFF, 0x80, 0x10, 0x8A);
-static const ImU32 inactiveColor = IM_COL32(0x99, 0x99, 0x99, 0x99);
-static const ImU32 translationLineColor = IM_COL32(0xAA, 0xAA, 0xAA, 0xAA);
-static const char* translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f", "X : %5.3f Y : %5.3f Z : %5.3f" };
-static const char* scaleInfoMask[] = { "X : %5.2f", "Y : %5.2f", "Z : %5.2f", "XYZ : %5.2f" };
-static const char* rotationInfoMask[] = { "X : %5.2f deg %5.2f rad", "Y : %5.2f deg %5.2f rad", "Z : %5.2f deg %5.2f rad", "Screen : %5.2f deg %5.2f rad" };
-static const int translationInfoIndex[] = { 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 2, 0, 0, 2, 0, 0, 1, 0, 0, 1, 2 };
-static const float quadMin = 0.5f;
-static const float quadMax = 0.8f;
-static const float quadUV[8] = { quadMin, quadMin, quadMin, quadMax, quadMax, quadMax, quadMax, quadMin };
-static const int halfCircleSegmentCount = 64;
-static const float snapTension = 0.5f;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion);
-static int GetRotateType(OPERATION op);
-static int GetScaleType(OPERATION op);
-
-static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight)) {
- vec_t trans;
- trans.TransformPoint(worldPos, mat);
- trans *= 0.5f / trans.w;
- trans += makeVect(0.5f, 0.5f);
- trans.y = 1.f - trans.y;
- trans.x *= size.x;
- trans.y *= size.y;
- trans.x += position.x;
- trans.y += position.y;
- return ImVec2(trans.x, trans.y);
-}
-
-static void ComputeCameraRay(vec_t& rayOrigin, vec_t& rayDir, ImVec2 position = ImVec2(gContext.mX, gContext.mY), ImVec2 size = ImVec2(gContext.mWidth, gContext.mHeight)) {
- ImGuiIO& io = ImGui::GetIO();
-
- matrix_t mViewProjInverse;
- mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat);
-
- const float mox = ((io.MousePos.x - position.x) / size.x) * 2.f - 1.f;
- const float moy = (1.f - ((io.MousePos.y - position.y) / size.y)) * 2.f - 1.f;
-
- const float zNear = gContext.mReversed ? (1.f - FLT_EPSILON) : 0.f;
- const float zFar = gContext.mReversed ? 0.f : (1.f - FLT_EPSILON);
-
- rayOrigin.Transform(makeVect(mox, moy, zNear, 1.f), mViewProjInverse);
- rayOrigin *= 1.f / rayOrigin.w;
- vec_t rayEnd;
- rayEnd.Transform(makeVect(mox, moy, zFar, 1.f), mViewProjInverse);
- rayEnd *= 1.f / rayEnd.w;
- rayDir = Normalized(rayEnd - rayOrigin);
-}
-
-static float GetSegmentLengthClipSpace(const vec_t& start, const vec_t& end, const bool localCoordinates = false) {
- vec_t startOfSegment = start;
- const matrix_t& mvp = localCoordinates ? gContext.mMVPLocal : gContext.mMVP;
- startOfSegment.TransformPoint(mvp);
- if (fabsf(startOfSegment.w) > FLT_EPSILON) // check for axis aligned with camera direction
- {
- startOfSegment *= 1.f / startOfSegment.w;
- }
-
- vec_t endOfSegment = end;
- endOfSegment.TransformPoint(mvp);
- if (fabsf(endOfSegment.w) > FLT_EPSILON) // check for axis aligned with camera direction
- {
- endOfSegment *= 1.f / endOfSegment.w;
- }
-
- vec_t clipSpaceAxis = endOfSegment - startOfSegment;
- clipSpaceAxis.y /= gContext.mDisplayRatio;
- float segmentLengthInClipSpace = sqrtf(clipSpaceAxis.x * clipSpaceAxis.x + clipSpaceAxis.y * clipSpaceAxis.y);
- return segmentLengthInClipSpace;
-}
-
-static float GetParallelogram(const vec_t& ptO, const vec_t& ptA, const vec_t& ptB) {
- vec_t pts[] = { ptO, ptA, ptB };
- for (unsigned int i = 0; i < 3; i++)
- {
- pts[i].TransformPoint(gContext.mMVP);
- if (fabsf(pts[i].w) > FLT_EPSILON) // check for axis aligned with camera direction
- {
- pts[i] *= 1.f / pts[i].w;
- }
- }
- vec_t segA = pts[1] - pts[0];
- vec_t segB = pts[2] - pts[0];
- segA.y /= gContext.mDisplayRatio;
- segB.y /= gContext.mDisplayRatio;
- vec_t segAOrtho = makeVect(-segA.y, segA.x);
- segAOrtho.Normalize();
- float dt = segAOrtho.Dot3(segB);
- float surface = sqrtf(segA.x * segA.x + segA.y * segA.y) * fabsf(dt);
- return surface;
-}
-
-inline vec_t PointOnSegment(const vec_t& point, const vec_t& vertPos1, const vec_t& vertPos2) {
- vec_t c = point - vertPos1;
- vec_t V;
-
- V.Normalize(vertPos2 - vertPos1);
- float d = (vertPos2 - vertPos1).Length();
- float t = V.Dot3(c);
-
- if (t < 0.f)
- {
- return vertPos1;
- }
-
- if (t > d)
- {
- return vertPos2;
- }
-
- return vertPos1 + V * t;
-}
-
-static float IntersectRayPlane(const vec_t& rOrigin, const vec_t& rVector, const vec_t& plan) {
- const float numer = plan.Dot3(rOrigin) - plan.w;
- const float denom = plan.Dot3(rVector);
-
- if (fabsf(denom) < FLT_EPSILON) // normal is orthogonal to vector, cant intersect
- {
- return -1.0f;
- }
-
- return -(numer / denom);
-}
-
-static float DistanceToPlane(const vec_t& point, const vec_t& plan) {
- return plan.Dot3(point) + plan.w;
-}
-
-static bool IsInContextRect(ImVec2 p) {
- return IsWithin(p.x, gContext.mX, gContext.mXMax) && IsWithin(p.y, gContext.mY, gContext.mYMax);
-}
-
-static bool IsHoveringWindow() {
- ImGuiContext& g = *ImGui::GetCurrentContext();
- ImGuiWindow* window = ImGui::FindWindowByName(gContext.mDrawList->_OwnerName);
- if (g.HoveredWindow == window) // Mouse hovering drawlist window
- return true;
- if (g.HoveredWindow != NULL) // Any other window is hovered
- return false;
- if (ImGui::IsMouseHoveringRect(window->InnerRect.Min, window->InnerRect.Max, false)) // Hovering drawlist window rect, while no other window is hovered (for _NoInputs windows)
- return true;
- return false;
-}
-
-void SetRect(float x, float y, float width, float height) {
- gContext.mX = x;
- gContext.mY = y;
- gContext.mWidth = width;
- gContext.mHeight = height;
- gContext.mXMax = gContext.mX + gContext.mWidth;
- gContext.mYMax = gContext.mY + gContext.mXMax;
- gContext.mDisplayRatio = width / height;
-}
-
-void SetOrthographic(bool isOrthographic) {
- gContext.mIsOrthographic = isOrthographic;
-}
-
-void SetDrawlist(ImDrawList* drawlist) {
- gContext.mDrawList = drawlist ? drawlist : ImGui::GetWindowDrawList();
-}
-
-void SetImGuiContext(ImGuiContext* ctx) {
- ImGui::SetCurrentContext(ctx);
-}
-
-void BeginFrame() {
- const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus;
-
-#ifdef IMGUI_HAS_VIEWPORT
- ImGui::SetNextWindowSize(ImGui::GetMainViewport()->Size);
- ImGui::SetNextWindowPos(ImGui::GetMainViewport()->Pos);
-#else
- ImGuiIO& io = ImGui::GetIO();
- ImGui::SetNextWindowSize(io.DisplaySize);
- ImGui::SetNextWindowPos(ImVec2(0, 0));
-#endif
-
- ImGui::PushStyleColor(ImGuiCol_WindowBg, 0);
- ImGui::PushStyleColor(ImGuiCol_Border, 0);
- ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
-
- ImGui::Begin("gizmo", NULL, flags);
- gContext.mDrawList = ImGui::GetWindowDrawList();
- ImGui::End();
- ImGui::PopStyleVar();
- ImGui::PopStyleColor(2);
-}
-
-bool IsUsing() {
- return gContext.mbUsing || gContext.mbUsingBounds;
-}
-
-bool IsOver() {
- return (Intersects(gContext.mOperation, TRANSLATE) && GetMoveType(gContext.mOperation, NULL) != MT_NONE) ||
- (Intersects(gContext.mOperation, ROTATE) && GetRotateType(gContext.mOperation) != MT_NONE) ||
- (Intersects(gContext.mOperation, SCALE) && GetScaleType(gContext.mOperation) != MT_NONE) || IsUsing();
-}
-
-bool IsOver(OPERATION op) {
- if (IsUsing())
- {
- return true;
- }
- if (Intersects(op, SCALE) && GetScaleType(op) != MT_NONE)
- {
- return true;
- }
- if (Intersects(op, ROTATE) && GetRotateType(op) != MT_NONE)
- {
- return true;
- }
- if (Intersects(op, TRANSLATE) && GetMoveType(op, NULL) != MT_NONE)
- {
- return true;
- }
- return false;
-}
-
-void Enable(bool enable) {
- gContext.mbEnable = enable;
- if (!enable)
- {
- gContext.mbUsing = false;
- gContext.mbUsingBounds = false;
- }
-}
-
-static void ComputeContext(const float* view, const float* projection, float* matrix, MODE mode) {
- gContext.mMode = mode;
- gContext.mViewMat = *(matrix_t*)view;
- gContext.mProjectionMat = *(matrix_t*)projection;
- gContext.mbMouseOver = IsHoveringWindow();
-
- gContext.mModelLocal = *(matrix_t*)matrix;
- gContext.mModelLocal.OrthoNormalize();
-
- if (mode == LOCAL)
- {
- gContext.mModel = gContext.mModelLocal;
- } else
- {
- gContext.mModel.Translation(((matrix_t*)matrix)->v.position);
- }
- gContext.mModelSource = *(matrix_t*)matrix;
- gContext.mModelScaleOrigin.Set(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length());
-
- gContext.mModelInverse.Inverse(gContext.mModel);
- gContext.mModelSourceInverse.Inverse(gContext.mModelSource);
- gContext.mViewProjection = gContext.mViewMat * gContext.mProjectionMat;
- gContext.mMVP = gContext.mModel * gContext.mViewProjection;
- gContext.mMVPLocal = gContext.mModelLocal * gContext.mViewProjection;
-
- matrix_t viewInverse;
- viewInverse.Inverse(gContext.mViewMat);
- gContext.mCameraDir = viewInverse.v.dir;
- gContext.mCameraEye = viewInverse.v.position;
- gContext.mCameraRight = viewInverse.v.right;
- gContext.mCameraUp = viewInverse.v.up;
-
- // projection reverse
- vec_t nearPos, farPos;
- nearPos.Transform(makeVect(0, 0, 1.f, 1.f), gContext.mProjectionMat);
- farPos.Transform(makeVect(0, 0, 2.f, 1.f), gContext.mProjectionMat);
-
- gContext.mReversed = (nearPos.z / nearPos.w) > (farPos.z / farPos.w);
-
- // compute scale from the size of camera right vector projected on screen at the matrix position
- vec_t pointRight = viewInverse.v.right;
- pointRight.TransformPoint(gContext.mViewProjection);
- gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / (pointRight.x / pointRight.w - gContext.mMVP.v.position.x / gContext.mMVP.v.position.w);
-
- vec_t rightViewInverse = viewInverse.v.right;
- rightViewInverse.TransformVector(gContext.mModelInverse);
- float rightLength = GetSegmentLengthClipSpace(makeVect(0.f, 0.f), rightViewInverse);
- gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / rightLength;
-
- ImVec2 centerSSpace = worldToPos(makeVect(0.f, 0.f), gContext.mMVP);
- gContext.mScreenSquareCenter = centerSSpace;
- gContext.mScreenSquareMin = ImVec2(centerSSpace.x - 10.f, centerSSpace.y - 10.f);
- gContext.mScreenSquareMax = ImVec2(centerSSpace.x + 10.f, centerSSpace.y + 10.f);
-
- ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector);
-}
-
-static void ComputeColors(ImU32* colors, int type, OPERATION operation) {
- if (gContext.mbEnable)
- {
- switch (operation)
- {
- case TRANSLATE:
- colors[0] = (type == MT_MOVE_SCREEN) ? selectionColor : IM_COL32_WHITE;
- for (int i = 0; i < 3; i++)
- {
- colors[i + 1] = (type == (int)(MT_MOVE_X + i)) ? selectionColor : directionColor[i];
- colors[i + 4] = (type == (int)(MT_MOVE_YZ + i)) ? selectionColor : planeColor[i];
- colors[i + 4] = (type == MT_MOVE_SCREEN) ? selectionColor : colors[i + 4];
- }
- break;
- case ROTATE:
- colors[0] = (type == MT_ROTATE_SCREEN) ? selectionColor : IM_COL32_WHITE;
- for (int i = 0; i < 3; i++)
- {
- colors[i + 1] = (type == (int)(MT_ROTATE_X + i)) ? selectionColor : directionColor[i];
- }
- break;
- case SCALEU:
- case SCALE:
- colors[0] = (type == MT_SCALE_XYZ) ? selectionColor : IM_COL32_WHITE;
- for (int i = 0; i < 3; i++)
- {
- colors[i + 1] = (type == (int)(MT_SCALE_X + i)) ? selectionColor : directionColor[i];
- }
- break;
- // note: this internal function is only called with three possible values for operation
- default:
- break;
- }
- } else
- {
- for (int i = 0; i < 7; i++)
- {
- colors[i] = inactiveColor;
- }
- }
-}
-
-static void ComputeTripodAxisAndVisibility(const int axisIndex, vec_t& dirAxis, vec_t& dirPlaneX, vec_t& dirPlaneY, bool& belowAxisLimit, bool& belowPlaneLimit, const bool localCoordinates = false) {
- dirAxis = directionUnary[axisIndex];
- dirPlaneX = directionUnary[(axisIndex + 1) % 3];
- dirPlaneY = directionUnary[(axisIndex + 2) % 3];
-
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
- {
- // when using, use stored factors so the gizmo doesn't flip when we translate
- belowAxisLimit = gContext.mBelowAxisLimit[axisIndex];
- belowPlaneLimit = gContext.mBelowPlaneLimit[axisIndex];
-
- dirAxis *= gContext.mAxisFactor[axisIndex];
- dirPlaneX *= gContext.mAxisFactor[(axisIndex + 1) % 3];
- dirPlaneY *= gContext.mAxisFactor[(axisIndex + 2) % 3];
- } else
- {
- // new method
- float lenDir = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis, localCoordinates);
- float lenDirMinus = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirAxis, localCoordinates);
-
- float lenDirPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneX, localCoordinates);
- float lenDirMinusPlaneX = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneX, localCoordinates);
-
- float lenDirPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirPlaneY, localCoordinates);
- float lenDirMinusPlaneY = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), -dirPlaneY, localCoordinates);
-
- // For readability
- bool& allowFlip = gContext.mAllowAxisFlip;
- float mulAxis = (allowFlip && lenDir < lenDirMinus && fabsf(lenDir - lenDirMinus) > FLT_EPSILON) ? -1.f : 1.f;
- float mulAxisX = (allowFlip && lenDirPlaneX < lenDirMinusPlaneX && fabsf(lenDirPlaneX - lenDirMinusPlaneX) > FLT_EPSILON) ? -1.f : 1.f;
- float mulAxisY = (allowFlip && lenDirPlaneY < lenDirMinusPlaneY && fabsf(lenDirPlaneY - lenDirMinusPlaneY) > FLT_EPSILON) ? -1.f : 1.f;
- dirAxis *= mulAxis;
- dirPlaneX *= mulAxisX;
- dirPlaneY *= mulAxisY;
-
- // for axis
- float axisLengthInClipSpace = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis * gContext.mScreenFactor, localCoordinates);
-
- float paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor);
- belowPlaneLimit = (paraSurf > 0.0025f);
- belowAxisLimit = (axisLengthInClipSpace > 0.02f);
-
- // and store values
- gContext.mAxisFactor[axisIndex] = mulAxis;
- gContext.mAxisFactor[(axisIndex + 1) % 3] = mulAxisX;
- gContext.mAxisFactor[(axisIndex + 2) % 3] = mulAxisY;
- gContext.mBelowAxisLimit[axisIndex] = belowAxisLimit;
- gContext.mBelowPlaneLimit[axisIndex] = belowPlaneLimit;
- }
-}
-
-static void ComputeSnap(float* value, float snap) {
- if (snap <= FLT_EPSILON)
- {
- return;
- }
-
- float modulo = fmodf(*value, snap);
- float moduloRatio = fabsf(modulo) / snap;
- if (moduloRatio < snapTension)
- {
- *value -= modulo;
- } else if (moduloRatio > (1.f - snapTension))
- {
- *value = *value - modulo + snap * ((*value < 0.f) ? -1.f : 1.f);
- }
-}
-static void ComputeSnap(vec_t& value, const float* snap) {
- for (int i = 0; i < 3; i++)
- {
- ComputeSnap(&value[i], snap[i]);
- }
-}
-
-static float ComputeAngleOnPlan() {
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
- vec_t localPos = Normalized(gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position);
-
- vec_t perpendicularVector;
- perpendicularVector.Cross(gContext.mRotationVectorSource, gContext.mTranslationPlan);
- perpendicularVector.Normalize();
- float acosAngle = Clamp(Dot(localPos, gContext.mRotationVectorSource), -1.f, 1.f);
- float angle = acosf(acosAngle);
- angle *= (Dot(localPos, perpendicularVector) < 0.f) ? 1.f : -1.f;
- return angle;
-}
-
-static void DrawRotationGizmo(OPERATION op, int type) {
- if (!Intersects(op, ROTATE))
- {
- return;
- }
- ImDrawList* drawList = gContext.mDrawList;
-
- // colors
- ImU32 colors[7];
- ComputeColors(colors, type, ROTATE);
-
- vec_t cameraToModelNormalized;
- if (gContext.mIsOrthographic)
- {
- matrix_t viewInverse;
- viewInverse.Inverse(*(matrix_t*)&gContext.mViewMat);
- cameraToModelNormalized = viewInverse.v.dir;
- } else
- {
- cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
- }
-
- cameraToModelNormalized.TransformVector(gContext.mModelInverse);
-
- gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight;
-
- bool hasRSC = Intersects(op, ROTATE_SCREEN);
- for (int axis = 0; axis < 3; axis++)
- {
- if (!Intersects(op, static_cast<OPERATION>(ROTATE_Z >> axis)))
- {
- continue;
- }
- const bool usingAxis = (gContext.mbUsing && type == MT_ROTATE_Z - axis);
- const int circleMul = (hasRSC && !usingAxis) ? 1 : 2;
-
- ImVec2* circlePos = (ImVec2*)alloca(sizeof(ImVec2) * (circleMul * halfCircleSegmentCount + 1));
-
- float angleStart = atan2f(cameraToModelNormalized[(4 - axis) % 3], cameraToModelNormalized[(3 - axis) % 3]) + ZPI * 0.5f;
-
- for (int i = 0; i < circleMul * halfCircleSegmentCount + 1; i++)
- {
- float ng = angleStart + (float)circleMul * ZPI * ((float)i / (float)halfCircleSegmentCount);
- vec_t axisPos = makeVect(cosf(ng), sinf(ng), 0.f);
- vec_t pos = makeVect(axisPos[axis], axisPos[(axis + 1) % 3], axisPos[(axis + 2) % 3]) * gContext.mScreenFactor * rotationDisplayFactor;
- circlePos[i] = worldToPos(pos, gContext.mMVP);
- }
- if (!gContext.mbUsing || usingAxis)
- {
- drawList->AddPolyline(circlePos, circleMul * halfCircleSegmentCount + 1, colors[3 - axis], false, 2);
- }
-
- float radiusAxis = sqrtf((ImLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0])));
- if (radiusAxis > gContext.mRadiusSquareCenter)
- {
- gContext.mRadiusSquareCenter = radiusAxis;
- }
- }
- if (hasRSC && (!gContext.mbUsing || type == MT_ROTATE_SCREEN))
- {
- drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, 3.f);
- }
-
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsRotateType(type))
- {
- ImVec2 circlePos[halfCircleSegmentCount + 1];
-
- circlePos[0] = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
- for (unsigned int i = 1; i < halfCircleSegmentCount; i++)
- {
- float ng = gContext.mRotationAngle * ((float)(i - 1) / (float)(halfCircleSegmentCount - 1));
- matrix_t rotateVectorMatrix;
- rotateVectorMatrix.RotationAxis(gContext.mTranslationPlan, ng);
- vec_t pos;
- pos.TransformPoint(gContext.mRotationVectorSource, rotateVectorMatrix);
- pos *= gContext.mScreenFactor * rotationDisplayFactor;
- circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection);
- }
- drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, IM_COL32(0xFF, 0x80, 0x10, 0x80));
- drawList->AddPolyline(circlePos, halfCircleSegmentCount, IM_COL32(0xFF, 0x80, 0x10, 0xFF), true, 2);
-
- ImVec2 destinationPosOnScreen = circlePos[1];
- char tmps[512];
- ImFormatString(tmps, sizeof(tmps), rotationInfoMask[type - MT_ROTATE_X], (gContext.mRotationAngle / ZPI) * 180.f, gContext.mRotationAngle);
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps);
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps);
- }
-}
-
-static void DrawHatchedAxis(const vec_t& axis) {
- for (int j = 1; j < 10; j++)
- {
- ImVec2 baseSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2) * gContext.mScreenFactor, gContext.mMVP);
- ImVec2 worldDirSSpace2 = worldToPos(axis * 0.05f * (float)(j * 2 + 1) * gContext.mScreenFactor, gContext.mMVP);
- gContext.mDrawList->AddLine(baseSSpace2, worldDirSSpace2, IM_COL32(0, 0, 0, 0x80), 6.f);
- }
-}
-
-static void DrawScaleGizmo(OPERATION op, int type) {
- ImDrawList* drawList = gContext.mDrawList;
-
- if (!Intersects(op, SCALE))
- {
- return;
- }
-
- // colors
- ImU32 colors[7];
- ComputeColors(colors, type, SCALE);
-
- // draw
- vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f };
-
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
- {
- scaleDisplay = gContext.mScale;
- }
-
- for (int i = 0; i < 3; i++)
- {
- if (!Intersects(op, static_cast<OPERATION>(SCALE_X << i)))
- {
- continue;
- }
- const bool usingAxis = (gContext.mbUsing && type == MT_SCALE_X + i);
- if (!gContext.mbUsing || usingAxis)
- {
- vec_t dirPlaneX, dirPlaneY, dirAxis;
- bool belowAxisLimit, belowPlaneLimit;
- ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true);
-
- // draw axis
- if (belowAxisLimit)
- {
- bool hasTranslateOnAxis = Contains(op, static_cast<OPERATION>(TRANSLATE_X << i));
- float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f;
- ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP);
- ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP);
- ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP);
-
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
- {
- drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, IM_COL32(0x40, 0x40, 0x40, 0xFF), 3.f);
- drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, IM_COL32(0x40, 0x40, 0x40, 0xFF));
- }
-
- if (!hasTranslateOnAxis || gContext.mbUsing)
- {
- drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
- }
- drawList->AddCircleFilled(worldDirSSpace, 6.f, colors[i + 1]);
-
- if (gContext.mAxisFactor[i] < 0.f)
- {
- DrawHatchedAxis(dirAxis * scaleDisplay[i]);
- }
- }
- }
- }
-
- // draw screen cirle
- drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
-
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(type))
- {
- // ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
- ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
- /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y);
- dif.Normalize();
- dif *= 5.f;
- drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor);
- drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor);
- drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f);
- */
- char tmps[512];
- // vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin;
- int componentInfoIndex = (type - MT_SCALE_X) * 3;
- ImFormatString(tmps, sizeof(tmps), scaleInfoMask[type - MT_SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]);
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps);
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps);
- }
-}
-
-static void DrawScaleUniveralGizmo(OPERATION op, int type) {
- ImDrawList* drawList = gContext.mDrawList;
-
- if (!Intersects(op, SCALEU))
- {
- return;
- }
-
- // colors
- ImU32 colors[7];
- ComputeColors(colors, type, SCALEU);
-
- // draw
- vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f };
-
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
- {
- scaleDisplay = gContext.mScale;
- }
-
- for (int i = 0; i < 3; i++)
- {
- if (!Intersects(op, static_cast<OPERATION>(SCALE_XU << i)))
- {
- continue;
- }
- const bool usingAxis = (gContext.mbUsing && type == MT_SCALE_X + i);
- if (!gContext.mbUsing || usingAxis)
- {
- vec_t dirPlaneX, dirPlaneY, dirAxis;
- bool belowAxisLimit, belowPlaneLimit;
- ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true);
-
- // draw axis
- if (belowAxisLimit)
- {
- bool hasTranslateOnAxis = Contains(op, static_cast<OPERATION>(TRANSLATE_X << i));
- float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f;
- // ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVPLocal);
- // ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP);
- ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVPLocal);
-
-#if 0
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
- {
- drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, IM_COL32(0x40, 0x40, 0x40, 0xFF), 3.f);
- drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, IM_COL32(0x40, 0x40, 0x40, 0xFF));
- }
- /*
- if (!hasTranslateOnAxis || gContext.mbUsing)
- {
- drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
- }
- */
-#endif
- drawList->AddCircleFilled(worldDirSSpace, 12.f, colors[i + 1]);
- }
- }
- }
-
- // draw screen cirle
- drawList->AddCircle(gContext.mScreenSquareCenter, 20.f, colors[0], 32, 3.f);
-
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(type))
- {
- // ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
- ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
- /*vec_t dif(destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y);
- dif.Normalize();
- dif *= 5.f;
- drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor);
- drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor);
- drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f);
- */
- char tmps[512];
- // vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin;
- int componentInfoIndex = (type - MT_SCALE_X) * 3;
- ImFormatString(tmps, sizeof(tmps), scaleInfoMask[type - MT_SCALE_X], scaleDisplay[translationInfoIndex[componentInfoIndex]]);
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps);
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps);
- }
-}
-
-static void DrawTranslationGizmo(OPERATION op, int type) {
- ImDrawList* drawList = gContext.mDrawList;
- if (!drawList)
- {
- return;
- }
-
- if (!Intersects(op, TRANSLATE))
- {
- return;
- }
-
- // colors
- ImU32 colors[7];
- ComputeColors(colors, type, TRANSLATE);
-
- const ImVec2 origin = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
-
- // draw
- bool belowAxisLimit = false;
- bool belowPlaneLimit = false;
- for (int i = 0; i < 3; ++i)
- {
- vec_t dirPlaneX, dirPlaneY, dirAxis;
- ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
-
- if (!gContext.mbUsing || (gContext.mbUsing && type == MT_MOVE_X + i))
- {
- // draw axis
- if (belowAxisLimit && Intersects(op, static_cast<OPERATION>(TRANSLATE_X << i)))
- {
- ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVP);
- ImVec2 worldDirSSpace = worldToPos(dirAxis * gContext.mScreenFactor, gContext.mMVP);
-
- drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
-
- // Arrow head begin
- ImVec2 dir(origin - worldDirSSpace);
-
- float d = sqrtf(ImLengthSqr(dir));
- dir /= d; // Normalize
- dir *= 6.0f;
-
- ImVec2 ortogonalDir(dir.y, -dir.x); // Perpendicular vector
- ImVec2 a(worldDirSSpace + dir);
- drawList->AddTriangleFilled(worldDirSSpace - dir, a + ortogonalDir, a - ortogonalDir, colors[i + 1]);
- // Arrow head end
-
- if (gContext.mAxisFactor[i] < 0.f)
- {
- DrawHatchedAxis(dirAxis);
- }
- }
- }
- // draw plane
- if (!gContext.mbUsing || (gContext.mbUsing && type == MT_MOVE_YZ + i))
- {
- if (belowPlaneLimit && Contains(op, TRANSLATE_PLANS[i]))
- {
- ImVec2 screenQuadPts[4];
- for (int j = 0; j < 4; ++j)
- {
- vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor;
- screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP);
- }
- drawList->AddPolyline(screenQuadPts, 4, directionColor[i], true, 1.0f);
- drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]);
- }
- }
- }
-
- drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
-
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsTranslateType(type))
- {
- ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
- ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
- vec_t dif = { destinationPosOnScreen.x - sourcePosOnScreen.x, destinationPosOnScreen.y - sourcePosOnScreen.y, 0.f, 0.f };
- dif.Normalize();
- dif *= 5.f;
- drawList->AddCircle(sourcePosOnScreen, 6.f, translationLineColor);
- drawList->AddCircle(destinationPosOnScreen, 6.f, translationLineColor);
- drawList->AddLine(ImVec2(sourcePosOnScreen.x + dif.x, sourcePosOnScreen.y + dif.y), ImVec2(destinationPosOnScreen.x - dif.x, destinationPosOnScreen.y - dif.y), translationLineColor, 2.f);
-
- char tmps[512];
- vec_t deltaInfo = gContext.mModel.v.position - gContext.mMatrixOrigin;
- int componentInfoIndex = (type - MT_MOVE_X) * 3;
- ImFormatString(tmps, sizeof(tmps), translationInfoMask[type - MT_MOVE_X], deltaInfo[translationInfoIndex[componentInfoIndex]], deltaInfo[translationInfoIndex[componentInfoIndex + 1]], deltaInfo[translationInfoIndex[componentInfoIndex + 2]]);
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps);
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps);
- }
-}
-
-static bool CanActivate() {
- if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemActive())
- {
- return true;
- }
- return false;
-}
-
-static bool HandleAndDrawLocalBounds(float* bounds, matrix_t* matrix, const float* snapValues, OPERATION operation) {
- ImGuiIO& io = ImGui::GetIO();
- ImDrawList* drawList = gContext.mDrawList;
-
- // compute best projection axis
- vec_t axesWorldDirections[3];
- vec_t bestAxisWorldDirection = { 0.0f, 0.0f, 0.0f, 0.0f };
- int axes[3];
- unsigned int numAxes = 1;
- axes[0] = gContext.mBoundsBestAxis;
- int bestAxis = axes[0];
- if (!gContext.mbUsingBounds)
- {
- numAxes = 0;
- float bestDot = 0.f;
- for (int i = 0; i < 3; i++)
- {
- vec_t dirPlaneNormalWorld;
- dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource);
- dirPlaneNormalWorld.Normalize();
-
- float dt = fabsf(Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld));
- if (dt >= bestDot)
- {
- bestDot = dt;
- bestAxis = i;
- bestAxisWorldDirection = dirPlaneNormalWorld;
- }
-
- if (dt >= 0.1f)
- {
- axes[numAxes] = i;
- axesWorldDirections[numAxes] = dirPlaneNormalWorld;
- ++numAxes;
- }
- }
- }
-
- if (numAxes == 0)
- {
- axes[0] = bestAxis;
- axesWorldDirections[0] = bestAxisWorldDirection;
- numAxes = 1;
- }
-
- else if (bestAxis != axes[0])
- {
- unsigned int bestIndex = 0;
- for (unsigned int i = 0; i < numAxes; i++)
- {
- if (axes[i] == bestAxis)
- {
- bestIndex = i;
- break;
- }
- }
- int tempAxis = axes[0];
- axes[0] = axes[bestIndex];
- axes[bestIndex] = tempAxis;
- vec_t tempDirection = axesWorldDirections[0];
- axesWorldDirections[0] = axesWorldDirections[bestIndex];
- axesWorldDirections[bestIndex] = tempDirection;
- }
-
- matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection;
- for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex)
- {
- bestAxis = axes[axisIndex];
- bestAxisWorldDirection = axesWorldDirections[axisIndex];
-
- // Corners of the plane (rectangle) containing bestAxis
- vec_t corners[4];
-
- int secondAxis = (bestAxis + 1) % 3;
- int thirdAxis = (bestAxis + 2) % 3;
- // ImU32 col[] = { IM_COL32(255, 0, 0, 255), IM_COL32(0, 255, 0, 255), IM_COL32(0, 0, 255, 255) };
- for (int i = 0; i < 4; i++) {
- corners[i].w = 0.0f;
- corners[i][bestAxis] = 0.0f;
- corners[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)];
- corners[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))];
-
- // ImVec2 pos = worldToPos(corners[i], boundsMVP);
- // drawList->AddCircleFilled(pos, 10.0f, col[axisIndex]);
- }
-
- // draw bounds
- unsigned int anchorAlpha = gContext.mbEnable ? IM_COL32_BLACK : IM_COL32(0, 0, 0, 0x80);
-
- for (int i = 0; i < 4; i++)
- {
- ImVec2 worldBound1 = worldToPos(corners[i], boundsMVP);
- ImVec2 worldBound2 = worldToPos(corners[(i + 1) % 4], boundsMVP);
- if (!IsInContextRect(worldBound1) || !IsInContextRect(worldBound2))
- {
- continue;
- }
- float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2));
- int stepCount = (int)(boundDistance / 10.f);
- stepCount = min(stepCount, 1000);
- float stepLength = 1.f / (float)stepCount;
- for (int j = 0; j < stepCount; j++)
- {
- float t1 = (float)j * stepLength;
- float t2 = (float)j * stepLength + stepLength * 0.5f;
- ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1));
- ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2));
- // drawList->AddLine(worldBoundSS1, worldBoundSS2, IM_COL32(0, 0, 0, 0) + anchorAlpha, 3.f);
- drawList->AddLine(worldBoundSS1, worldBoundSS2, IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha, 2.f);
- }
- vec_t midPoint = (corners[i] + corners[(i + 1) % 4]) * 0.5f;
- ImVec2 midBound = worldToPos(midPoint, boundsMVP);
- static const float AnchorBigRadius = 8.f;
- static const float AnchorSmallRadius = 6.f;
- bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius * AnchorBigRadius);
- bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius * AnchorBigRadius);
-
- int type = MT_NONE;
- vec_t gizmoHitProportion;
-
- if (Intersects(operation, TRANSLATE))
- {
- type = GetMoveType(operation, &gizmoHitProportion);
- }
- if (Intersects(operation, ROTATE) && type == MT_NONE)
- {
- type = GetRotateType(operation);
- }
- if (Intersects(operation, SCALE) && type == MT_NONE)
- {
- type = GetScaleType(operation);
- }
-
- if (type != MT_NONE)
- {
- overBigAnchor = false;
- overSmallAnchor = false;
- }
-
- unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha);
- unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (IM_COL32(0xAA, 0xAA, 0xAA, 0) + anchorAlpha);
-
- drawList->AddCircleFilled(worldBound1, AnchorBigRadius, IM_COL32_BLACK);
- drawList->AddCircleFilled(worldBound1, AnchorBigRadius - 1.2f, bigAnchorColor);
-
- drawList->AddCircleFilled(midBound, AnchorSmallRadius, IM_COL32_BLACK);
- drawList->AddCircleFilled(midBound, AnchorSmallRadius - 1.2f, smallAnchorColor);
- int oppositeIndex = (i + 2) % 4;
- // big anchor on corners
- if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate())
- {
- gContext.mBoundsPivot.TransformPoint(corners[(i + 2) % 4], gContext.mModelSource);
- gContext.mBoundsAnchor.TransformPoint(corners[i], gContext.mModelSource);
- gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
- gContext.mBoundsBestAxis = bestAxis;
- gContext.mBoundsAxis[0] = secondAxis;
- gContext.mBoundsAxis[1] = thirdAxis;
-
- gContext.mBoundsLocalPivot.Set(0.f);
- gContext.mBoundsLocalPivot[secondAxis] = corners[oppositeIndex][secondAxis];
- gContext.mBoundsLocalPivot[thirdAxis] = corners[oppositeIndex][thirdAxis];
- gContext.mBoundsPivotCornerIndex = oppositeIndex;
-
- gContext.mbUsingBounds = true;
- gContext.mEditingID = gContext.mActualID;
- gContext.mBoundsMatrix = gContext.mModelSource;
-
- gContext.mbIsUsingBigAnchor = true;
- }
- // small anchor on middle of segment
- if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate())
- {
- vec_t midPointOpposite = (corners[(i + 2) % 4] + corners[(i + 3) % 4]) * 0.5f;
- gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource);
- gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource);
- gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
- gContext.mBoundsBestAxis = bestAxis;
- int indices[] = { secondAxis, thirdAxis };
- gContext.mBoundsAxis[0] = indices[i % 2];
- gContext.mBoundsAxis[1] = -1;
-
- int localPivotComponentIdx = gContext.mBoundsAxis[0];
- gContext.mBoundsLocalPivot.Set(0.f);
- gContext.mBoundsLocalPivot[localPivotComponentIdx] = corners[oppositeIndex][localPivotComponentIdx]; // bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f);
- gContext.mBoundsPivotCornerIndex = oppositeIndex;
-
- gContext.mbUsingBounds = true;
- gContext.mEditingID = gContext.mActualID;
- gContext.mBoundsMatrix = gContext.mModelSource;
-
- gContext.mbIsUsingBigAnchor = false;
- }
- }
-
- ImGui::Text("bounds pivot: %.2f, %.2f, %.2f", gContext.mBoundsPivot.x, gContext.mBoundsPivot.y, gContext.mBoundsPivot.z);
- ImGui::Text("bounds anchor: %.2f, %.2f, %.2f", gContext.mBoundsAnchor.x, gContext.mBoundsAnchor.y, gContext.mBoundsAnchor.z);
- ImGui::Text("bounds plan: %.2f, %.2f, %.2f", gContext.mBoundsPlan.x, gContext.mBoundsPlan.y, gContext.mBoundsPlan.z);
- ImGui::Text("bounds local pivot: %.2f, %.2f, %.2f", gContext.mBoundsLocalPivot.x, gContext.mBoundsLocalPivot.y, gContext.mBoundsLocalPivot.z);
- if (gContext.mbUsingBounds && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
- {
- // compute projected mouse position on plan
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan);
- vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
-
- // compute a reference and delta vectors base on mouse move
- vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs();
- vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs();
-
- ImGui::Text("Delta: %.2f, %.2f, %.2f", deltaVector.x, deltaVector.y, deltaVector.z);
- ImGui::Text("Ref: %.2f, %.2f, %.2f", referenceVector.x, referenceVector.y, referenceVector.z);
- ImGui::Separator();
-
- // for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length
- for (int axisIndex1 : gContext.mBoundsAxis) {
- if (axisIndex1 == -1) {
- continue;
- }
-
- vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs();
- // ImGui::Text("Axisdir: %.2f, %.2f, %.2f", axisDir.x, axisDir.y, axisDir.z);
-
- float refAxisComp = axisDir.Dot(referenceVector);
- float deltaAxisComp = axisDir.Dot(deltaVector);
- // ImGui::Text("refAxisComp: %.2f", refAxisComp);
-
- float length = deltaAxisComp;
- if (snapValues) {
- ComputeSnap(&length, snapValues[axisIndex1]);
- }
-
- // ImGui::Text("axis idx %d", axisIndex1);
- // TODO(hnosm): logic that mapps mouse pos to bound seems to account for translation fixup already?
- bounds[axisIndex1] = -length / 2;
- bounds[axisIndex1 + 3] = +length / 2;
- }
-
- // Update corner positions, translation fixup code needs them
- for (int i = 0; i < 4; i++) {
- corners[i].w = 0.0f;
- corners[i][bestAxis] = 0.0f;
- corners[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)];
- corners[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))];
- }
-
- // Translation (object center) fixup - make sure pivot stays in place
- // TODO(hnosm): is there a better way to write this that doesn't involve transferring a bunch of extra state from begin drag frame?
- vec_t newLocalPivot;
- if (gContext.mbIsUsingBigAnchor) {
- newLocalPivot.Set(0.0f);
- newLocalPivot[secondAxis] = corners[gContext.mBoundsPivotCornerIndex][secondAxis];
- newLocalPivot[thirdAxis] = corners[gContext.mBoundsPivotCornerIndex][thirdAxis];
- } else {
- newLocalPivot.Set(0.0f);
- int localPivotComponentIdx = gContext.mBoundsAxis[0];
- newLocalPivot[localPivotComponentIdx] = corners[gContext.mBoundsPivotCornerIndex][localPivotComponentIdx];
- }
-
- vec_t delta = gContext.mBoundsLocalPivot - newLocalPivot;
- vec_t oldTranslation = gContext.mBoundsMatrix.component[3];
- matrix->component[3] = oldTranslation + delta;
-
- // info text
- char tmps[512];
- ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
- ImFormatString(tmps, sizeof(tmps),
- // Size of the bounds in each axis direction
- "X: %.2f Y: %.2f Z:%.2f",
- (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length(),
- (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length(),
- (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length());
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), IM_COL32_BLACK, tmps);
- drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), IM_COL32_WHITE, tmps);
- }
-
- if (!io.MouseDown[0]) {
- gContext.mbUsingBounds = false;
- gContext.mEditingID = -1;
- }
- if (gContext.mbUsingBounds)
- {
- break;
- }
- }
-
- return gContext.mbUsingBounds;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-
-static int GetScaleType(OPERATION op) {
- if (gContext.mbUsing)
- {
- return MT_NONE;
- }
- ImGuiIO& io = ImGui::GetIO();
- int type = MT_NONE;
-
- // screen
- if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x &&
- io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y &&
- Contains(op, SCALE))
- {
- type = MT_SCALE_XYZ;
- }
-
- // compute
- for (int i = 0; i < 3 && type == MT_NONE; i++)
- {
- if (!Intersects(op, static_cast<OPERATION>(SCALE_X << i)))
- {
- continue;
- }
- vec_t dirPlaneX, dirPlaneY, dirAxis;
- bool belowAxisLimit, belowPlaneLimit;
- ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true);
- dirAxis.TransformVector(gContext.mModelLocal);
- dirPlaneX.TransformVector(gContext.mModelLocal);
- dirPlaneY.TransformVector(gContext.mModelLocal);
-
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModelLocal.v.position, dirAxis));
- vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len;
-
- const float startOffset = Contains(op, static_cast<OPERATION>(TRANSLATE_X << i)) ? 1.0f : 0.1f;
- const float endOffset = Contains(op, static_cast<OPERATION>(TRANSLATE_X << i)) ? 1.4f : 1.0f;
- const ImVec2 posOnPlanScreen = worldToPos(posOnPlan, gContext.mViewProjection);
- const ImVec2 axisStartOnScreen = worldToPos(gContext.mModelLocal.v.position + dirAxis * gContext.mScreenFactor * startOffset, gContext.mViewProjection);
- const ImVec2 axisEndOnScreen = worldToPos(gContext.mModelLocal.v.position + dirAxis * gContext.mScreenFactor * endOffset, gContext.mViewProjection);
-
- vec_t closestPointOnAxis = PointOnSegment(makeVect(posOnPlanScreen), makeVect(axisStartOnScreen), makeVect(axisEndOnScreen));
-
- if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size
- {
- type = MT_SCALE_X + i;
- }
- }
-
- // universal
-
- vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f };
- float dist = deltaScreen.Length();
- if (Contains(op, SCALEU) && dist >= 17.0f && dist < 23.0f)
- {
- type = MT_SCALE_XYZ;
- }
-
- for (int i = 0; i < 3 && type == MT_NONE; i++)
- {
- if (!Intersects(op, static_cast<OPERATION>(SCALE_XU << i)))
- {
- continue;
- }
-
- vec_t dirPlaneX, dirPlaneY, dirAxis;
- bool belowAxisLimit, belowPlaneLimit;
- ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true);
-
- // draw axis
- if (belowAxisLimit)
- {
- bool hasTranslateOnAxis = Contains(op, static_cast<OPERATION>(TRANSLATE_X << i));
- float markerScale = hasTranslateOnAxis ? 1.4f : 1.0f;
- // ImVec2 baseSSpace = worldToPos(dirAxis * 0.1f * gContext.mScreenFactor, gContext.mMVPLocal);
- // ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP);
- ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale) * gContext.mScreenFactor, gContext.mMVPLocal);
-
- float distance = sqrtf(ImLengthSqr(worldDirSSpace - io.MousePos));
- if (distance < 12.f)
- {
- type = MT_SCALE_X + i;
- }
- }
- }
- return type;
-}
-
-static int GetRotateType(OPERATION op) {
- if (gContext.mbUsing)
- {
- return MT_NONE;
- }
- ImGuiIO& io = ImGui::GetIO();
- int type = MT_NONE;
-
- vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f };
- float dist = deltaScreen.Length();
- if (Intersects(op, ROTATE_SCREEN) && dist >= (gContext.mRadiusSquareCenter - 4.0f) && dist < (gContext.mRadiusSquareCenter + 4.0f))
- {
- type = MT_ROTATE_SCREEN;
- }
-
- const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir };
-
- vec_t modelViewPos;
- modelViewPos.TransformPoint(gContext.mModel.v.position, gContext.mViewMat);
-
- for (int i = 0; i < 3 && type == MT_NONE; i++)
- {
- if (!Intersects(op, static_cast<OPERATION>(ROTATE_X << i)))
- {
- continue;
- }
- // pickup plan
- vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]);
-
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, pickupPlan);
- const vec_t intersectWorldPos = gContext.mRayOrigin + gContext.mRayVector * len;
- vec_t intersectViewPos;
- intersectViewPos.TransformPoint(intersectWorldPos, gContext.mViewMat);
-
- if (ImAbs(modelViewPos.z) - ImAbs(intersectViewPos.z) < -FLT_EPSILON)
- {
- continue;
- }
-
- const vec_t localPos = intersectWorldPos - gContext.mModel.v.position;
- vec_t idealPosOnCircle = Normalized(localPos);
- idealPosOnCircle.TransformVector(gContext.mModelInverse);
- const ImVec2 idealPosOnCircleScreen = worldToPos(idealPosOnCircle * rotationDisplayFactor * gContext.mScreenFactor, gContext.mMVP);
-
- // gContext.mDrawList->AddCircle(idealPosOnCircleScreen, 5.f, IM_COL32_WHITE);
- const ImVec2 distanceOnScreen = idealPosOnCircleScreen - io.MousePos;
-
- const float distance = makeVect(distanceOnScreen).Length();
- if (distance < 8.f) // pixel size
- {
- type = MT_ROTATE_X + i;
- }
- }
-
- return type;
-}
-
-static int GetMoveType(OPERATION op, vec_t* gizmoHitProportion) {
- if (!Intersects(op, TRANSLATE) || gContext.mbUsing || !gContext.mbMouseOver)
- {
- return MT_NONE;
- }
- ImGuiIO& io = ImGui::GetIO();
- int type = MT_NONE;
-
- // screen
- if (io.MousePos.x >= gContext.mScreenSquareMin.x && io.MousePos.x <= gContext.mScreenSquareMax.x &&
- io.MousePos.y >= gContext.mScreenSquareMin.y && io.MousePos.y <= gContext.mScreenSquareMax.y &&
- Contains(op, TRANSLATE))
- {
- type = MT_MOVE_SCREEN;
- }
-
- const vec_t screenCoord = makeVect(io.MousePos - ImVec2(gContext.mX, gContext.mY));
-
- // compute
- for (int i = 0; i < 3 && type == MT_NONE; i++)
- {
- vec_t dirPlaneX, dirPlaneY, dirAxis;
- bool belowAxisLimit, belowPlaneLimit;
- ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
- dirAxis.TransformVector(gContext.mModel);
- dirPlaneX.TransformVector(gContext.mModel);
- dirPlaneY.TransformVector(gContext.mModel);
-
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, BuildPlan(gContext.mModel.v.position, dirAxis));
- vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len;
-
- const ImVec2 axisStartOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor * 0.1f, gContext.mViewProjection) - ImVec2(gContext.mX, gContext.mY);
- const ImVec2 axisEndOnScreen = worldToPos(gContext.mModel.v.position + dirAxis * gContext.mScreenFactor, gContext.mViewProjection) - ImVec2(gContext.mX, gContext.mY);
-
- vec_t closestPointOnAxis = PointOnSegment(screenCoord, makeVect(axisStartOnScreen), makeVect(axisEndOnScreen));
- if ((closestPointOnAxis - screenCoord).Length() < 12.f && Intersects(op, static_cast<OPERATION>(TRANSLATE_X << i))) // pixel size
- {
- type = MT_MOVE_X + i;
- }
-
- const float dx = dirPlaneX.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
- const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
- if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3] && Contains(op, TRANSLATE_PLANS[i]))
- {
- type = MT_MOVE_YZ + i;
- }
-
- if (gizmoHitProportion)
- {
- *gizmoHitProportion = makeVect(dx, dy, 0.f);
- }
- }
- return type;
-}
-
-static bool HandleTranslation(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap) {
- if (!Intersects(op, TRANSLATE) || type != MT_NONE)
- {
- return false;
- }
- const ImGuiIO& io = ImGui::GetIO();
- const bool applyRotationLocaly = gContext.mMode == LOCAL || type == MT_MOVE_SCREEN;
- bool modified = false;
-
- // move
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsTranslateType(gContext.mCurrentOperation))
- {
- ImGui::CaptureMouseFromApp();
- const float signedLength = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
- const float len = fabsf(signedLength); // near plan
- const vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
-
- // compute delta
- const vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
- vec_t delta = newOrigin - gContext.mModel.v.position;
-
- // 1 axis constraint
- if (gContext.mCurrentOperation >= MT_MOVE_X && gContext.mCurrentOperation <= MT_MOVE_Z)
- {
- const int axisIndex = gContext.mCurrentOperation - MT_MOVE_X;
- const vec_t& axisValue = *(vec_t*)&gContext.mModel.m[axisIndex];
- const float lengthOnAxis = Dot(axisValue, delta);
- delta = axisValue * lengthOnAxis;
- }
-
- // snap
- if (snap)
- {
- vec_t cumulativeDelta = gContext.mModel.v.position + delta - gContext.mMatrixOrigin;
- if (applyRotationLocaly)
- {
- matrix_t modelSourceNormalized = gContext.mModelSource;
- modelSourceNormalized.OrthoNormalize();
- matrix_t modelSourceNormalizedInverse;
- modelSourceNormalizedInverse.Inverse(modelSourceNormalized);
- cumulativeDelta.TransformVector(modelSourceNormalizedInverse);
- ComputeSnap(cumulativeDelta, snap);
- cumulativeDelta.TransformVector(modelSourceNormalized);
- } else
- {
- ComputeSnap(cumulativeDelta, snap);
- }
- delta = gContext.mMatrixOrigin + cumulativeDelta - gContext.mModel.v.position;
- }
-
- if (delta != gContext.mTranslationLastDelta)
- {
- modified = true;
- }
- gContext.mTranslationLastDelta = delta;
-
- // compute matrix & delta
- matrix_t deltaMatrixTranslation;
- deltaMatrixTranslation.Translation(delta);
- if (deltaMatrix)
- {
- memcpy(deltaMatrix, deltaMatrixTranslation.m16, sizeof(float) * 16);
- }
-
- const matrix_t res = gContext.mModelSource * deltaMatrixTranslation;
- *(matrix_t*)matrix = res;
-
- if (!io.MouseDown[0])
- {
- gContext.mbUsing = false;
- }
-
- type = gContext.mCurrentOperation;
- } else
- {
- // find new possible way to move
- vec_t gizmoHitProportion;
- type = GetMoveType(op, &gizmoHitProportion);
- if (type != MT_NONE)
- {
- ImGui::CaptureMouseFromApp();
- }
- if (CanActivate() && type != MT_NONE)
- {
- gContext.mbUsing = true;
- gContext.mEditingID = gContext.mActualID;
- gContext.mCurrentOperation = type;
- vec_t movePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir };
-
- vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
- for (unsigned int i = 0; i < 3; i++)
- {
- vec_t orthoVector = Cross(movePlanNormal[i], cameraToModelNormalized);
- movePlanNormal[i].Cross(orthoVector);
- movePlanNormal[i].Normalize();
- }
- // pickup plan
- gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MT_MOVE_X]);
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
- gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len;
- gContext.mMatrixOrigin = gContext.mModel.v.position;
-
- gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor);
- }
- }
- return modified;
-}
-
-static bool HandleScale(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap) {
- if ((!Intersects(op, SCALE) && !Intersects(op, SCALEU)) || type != MT_NONE || !gContext.mbMouseOver)
- {
- return false;
- }
- ImGuiIO& io = ImGui::GetIO();
- bool modified = false;
-
- if (!gContext.mbUsing)
- {
- // find new possible way to scale
- type = GetScaleType(op);
- if (type != MT_NONE)
- {
- ImGui::CaptureMouseFromApp();
- }
- if (CanActivate() && type != MT_NONE)
- {
- gContext.mbUsing = true;
- gContext.mEditingID = gContext.mActualID;
- gContext.mCurrentOperation = type;
- const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.up, gContext.mModel.v.right, -gContext.mCameraDir };
- // pickup plan
-
- gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MT_SCALE_X]);
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
- gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len;
- gContext.mMatrixOrigin = gContext.mModel.v.position;
- gContext.mScale.Set(1.f, 1.f, 1.f);
- gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor);
- gContext.mScaleValueOrigin = makeVect(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length());
- gContext.mSaveMousePosx = io.MousePos.x;
- }
- }
- // scale
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(gContext.mCurrentOperation))
- {
- ImGui::CaptureMouseFromApp();
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
- vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
- vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
- vec_t delta = newOrigin - gContext.mModelLocal.v.position;
-
- // 1 axis constraint
- if (gContext.mCurrentOperation >= MT_SCALE_X && gContext.mCurrentOperation <= MT_SCALE_Z)
- {
- int axisIndex = gContext.mCurrentOperation - MT_SCALE_X;
- const vec_t& axisValue = *(vec_t*)&gContext.mModelLocal.m[axisIndex];
- float lengthOnAxis = Dot(axisValue, delta);
- delta = axisValue * lengthOnAxis;
-
- vec_t baseVector = gContext.mTranslationPlanOrigin - gContext.mModelLocal.v.position;
- float ratio = Dot(axisValue, baseVector + delta) / Dot(axisValue, baseVector);
-
- gContext.mScale[axisIndex] = max(ratio, 0.001f);
- } else
- {
- float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx) * 0.01f;
- gContext.mScale.Set(max(1.f + scaleDelta, 0.001f));
- }
-
- // snap
- if (snap)
- {
- float scaleSnap[] = { snap[0], snap[0], snap[0] };
- ComputeSnap(gContext.mScale, scaleSnap);
- }
-
- // no 0 allowed
- for (int i = 0; i < 3; i++)
- gContext.mScale[i] = max(gContext.mScale[i], 0.001f);
-
- if (gContext.mScaleLast != gContext.mScale)
- {
- modified = true;
- }
- gContext.mScaleLast = gContext.mScale;
-
- // compute matrix & delta
- matrix_t deltaMatrixScale;
- deltaMatrixScale.Scale(gContext.mScale * gContext.mScaleValueOrigin);
-
- matrix_t res = deltaMatrixScale * gContext.mModelLocal;
- *(matrix_t*)matrix = res;
-
- if (deltaMatrix)
- {
- vec_t deltaScale = gContext.mScale * gContext.mScaleValueOrigin;
-
- vec_t originalScaleDivider;
- originalScaleDivider.x = 1 / gContext.mModelScaleOrigin.x;
- originalScaleDivider.y = 1 / gContext.mModelScaleOrigin.y;
- originalScaleDivider.z = 1 / gContext.mModelScaleOrigin.z;
-
- deltaScale = deltaScale * originalScaleDivider;
-
- deltaMatrixScale.Scale(deltaScale);
- memcpy(deltaMatrix, deltaMatrixScale.m16, sizeof(float) * 16);
- }
-
- if (!io.MouseDown[0])
- {
- gContext.mbUsing = false;
- gContext.mScale.Set(1.f, 1.f, 1.f);
- }
-
- type = gContext.mCurrentOperation;
- }
- return modified;
-}
-
-static bool HandleRotation(float* matrix, float* deltaMatrix, OPERATION op, int& type, const float* snap) {
- if (!Intersects(op, ROTATE) || type != MT_NONE || !gContext.mbMouseOver)
- {
- return false;
- }
- ImGuiIO& io = ImGui::GetIO();
- bool applyRotationLocaly = gContext.mMode == LOCAL;
- bool modified = false;
-
- if (!gContext.mbUsing)
- {
- type = GetRotateType(op);
-
- if (type != MT_NONE)
- {
- ImGui::CaptureMouseFromApp();
- }
-
- if (type == MT_ROTATE_SCREEN)
- {
- applyRotationLocaly = true;
- }
-
- if (CanActivate() && type != MT_NONE)
- {
- gContext.mbUsing = true;
- gContext.mEditingID = gContext.mActualID;
- gContext.mCurrentOperation = type;
- const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir };
- // pickup plan
- if (applyRotationLocaly)
- {
- gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, rotatePlanNormal[type - MT_ROTATE_X]);
- } else
- {
- gContext.mTranslationPlan = BuildPlan(gContext.mModelSource.v.position, directionUnary[type - MT_ROTATE_X]);
- }
-
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
- vec_t localPos = gContext.mRayOrigin + gContext.mRayVector * len - gContext.mModel.v.position;
- gContext.mRotationVectorSource = Normalized(localPos);
- gContext.mRotationAngleOrigin = ComputeAngleOnPlan();
- }
- }
-
- // rotation
- if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsRotateType(gContext.mCurrentOperation))
- {
- ImGui::CaptureMouseFromApp();
- gContext.mRotationAngle = ComputeAngleOnPlan();
- if (snap)
- {
- float snapInRadian = snap[0] * DEG2RAD;
- ComputeSnap(&gContext.mRotationAngle, snapInRadian);
- }
- vec_t rotationAxisLocalSpace;
-
- rotationAxisLocalSpace.TransformVector(makeVect(gContext.mTranslationPlan.x, gContext.mTranslationPlan.y, gContext.mTranslationPlan.z, 0.f), gContext.mModelInverse);
- rotationAxisLocalSpace.Normalize();
-
- matrix_t deltaRotation;
- deltaRotation.RotationAxis(rotationAxisLocalSpace, gContext.mRotationAngle - gContext.mRotationAngleOrigin);
- if (gContext.mRotationAngle != gContext.mRotationAngleOrigin)
- {
- modified = true;
- }
- gContext.mRotationAngleOrigin = gContext.mRotationAngle;
-
- matrix_t scaleOrigin;
- scaleOrigin.Scale(gContext.mModelScaleOrigin);
-
- if (applyRotationLocaly)
- {
- *(matrix_t*)matrix = scaleOrigin * deltaRotation * gContext.mModelLocal;
- } else
- {
- matrix_t res = gContext.mModelSource;
- res.v.position.Set(0.f);
-
- *(matrix_t*)matrix = res * deltaRotation;
- ((matrix_t*)matrix)->v.position = gContext.mModelSource.v.position;
- }
-
- if (deltaMatrix)
- {
- *(matrix_t*)deltaMatrix = gContext.mModelInverse * deltaRotation * gContext.mModel;
- }
-
- if (!io.MouseDown[0])
- {
- gContext.mbUsing = false;
- gContext.mEditingID = -1;
- }
- type = gContext.mCurrentOperation;
- }
- return modified;
-}
-
-void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale) {
- matrix_t mat = *(matrix_t*)matrix;
-
- scale[0] = mat.v.right.Length();
- scale[1] = mat.v.up.Length();
- scale[2] = mat.v.dir.Length();
-
- mat.OrthoNormalize();
-
- rotation[0] = RAD2DEG * atan2f(mat.m[1][2], mat.m[2][2]);
- rotation[1] = RAD2DEG * atan2f(-mat.m[0][2], sqrtf(mat.m[1][2] * mat.m[1][2] + mat.m[2][2] * mat.m[2][2]));
- rotation[2] = RAD2DEG * atan2f(mat.m[0][1], mat.m[0][0]);
-
- translation[0] = mat.v.position.x;
- translation[1] = mat.v.position.y;
- translation[2] = mat.v.position.z;
-}
-
-void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix) {
- matrix_t& mat = *(matrix_t*)matrix;
-
- matrix_t rot[3];
- for (int i = 0; i < 3; i++)
- {
- rot[i].RotationAxis(directionUnary[i], rotation[i] * DEG2RAD);
- }
-
- mat = rot[0] * rot[1] * rot[2];
-
- float validScale[3];
- for (int i = 0; i < 3; i++)
- {
- if (fabsf(scale[i]) < FLT_EPSILON)
- {
- validScale[i] = 0.001f;
- } else
- {
- validScale[i] = scale[i];
- }
- }
- mat.v.right *= validScale[0];
- mat.v.up *= validScale[1];
- mat.v.dir *= validScale[2];
- mat.v.position.Set(translation[0], translation[1], translation[2], 1.f);
-}
-
-void SetID(int id) {
- gContext.mActualID = id;
-}
-
-void AllowAxisFlip(bool value) {
- gContext.mAllowAxisFlip = value;
-}
-
-bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix, const float* snap, float* localBounds, const float* boundsSnap) {
- // Scale is always local or matrix will be skewed when applying world scale or oriented matrix
- ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode);
-
- // set delta to identity
- if (deltaMatrix)
- {
- ((matrix_t*)deltaMatrix)->SetToIdentity();
- }
-
- // behind camera
- vec_t camSpacePosition;
- camSpacePosition.TransformPoint(makeVect(0.f, 0.f, 0.f), gContext.mMVP);
- if (!gContext.mIsOrthographic && camSpacePosition.z < 0.001f)
- {
- return false;
- }
-
- // --
- int type = MT_NONE;
- bool manipulated = false;
- if (gContext.mbEnable)
- {
- if (!gContext.mbUsingBounds)
- {
- manipulated |= HandleTranslation(matrix, deltaMatrix, operation, type, snap) ||
- HandleScale(matrix, deltaMatrix, operation, type, snap) ||
- HandleRotation(matrix, deltaMatrix, operation, type, snap);
- }
- }
- if (localBounds && !gContext.mbUsing)
- {
- manipulated |= HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap, operation);
- }
-
- gContext.mOperation = operation;
- if (!gContext.mbUsingBounds)
- {
- DrawRotationGizmo(operation, type);
- DrawTranslationGizmo(operation, type);
- DrawScaleGizmo(operation, type);
- DrawScaleUniveralGizmo(operation, type);
- }
- return manipulated;
-}
-
-void SetGizmoSizeClipSpace(float value) {
- gContext.mGizmoSizeClipSpace = value;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-void ComputeFrustumPlanes(vec_t* frustum, const float* clip) {
- frustum[0].x = clip[3] - clip[0];
- frustum[0].y = clip[7] - clip[4];
- frustum[0].z = clip[11] - clip[8];
- frustum[0].w = clip[15] - clip[12];
-
- frustum[1].x = clip[3] + clip[0];
- frustum[1].y = clip[7] + clip[4];
- frustum[1].z = clip[11] + clip[8];
- frustum[1].w = clip[15] + clip[12];
-
- frustum[2].x = clip[3] + clip[1];
- frustum[2].y = clip[7] + clip[5];
- frustum[2].z = clip[11] + clip[9];
- frustum[2].w = clip[15] + clip[13];
-
- frustum[3].x = clip[3] - clip[1];
- frustum[3].y = clip[7] - clip[5];
- frustum[3].z = clip[11] - clip[9];
- frustum[3].w = clip[15] - clip[13];
-
- frustum[4].x = clip[3] - clip[2];
- frustum[4].y = clip[7] - clip[6];
- frustum[4].z = clip[11] - clip[10];
- frustum[4].w = clip[15] - clip[14];
-
- frustum[5].x = clip[3] + clip[2];
- frustum[5].y = clip[7] + clip[6];
- frustum[5].z = clip[11] + clip[10];
- frustum[5].w = clip[15] + clip[14];
-
- for (int i = 0; i < 6; i++)
- {
- frustum[i].Normalize();
- }
-}
-
-void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount) {
- matrix_t viewInverse;
- viewInverse.Inverse(*(matrix_t*)view);
-
- struct CubeFace {
- float z;
- ImVec2 faceCoordsScreen[4];
- ImU32 color;
- };
- CubeFace* faces = (CubeFace*)_malloca(sizeof(CubeFace) * matrixCount * 6);
-
- if (!faces)
- {
- return;
- }
-
- vec_t frustum[6];
- matrix_t viewProjection = *(matrix_t*)view * *(matrix_t*)projection;
- ComputeFrustumPlanes(frustum, viewProjection.m16);
-
- int cubeFaceCount = 0;
- for (int cube = 0; cube < matrixCount; cube++)
- {
- const float* matrix = &matrices[cube * 16];
-
- matrix_t res = *(matrix_t*)matrix * *(matrix_t*)view * *(matrix_t*)projection;
-
- for (int iFace = 0; iFace < 6; iFace++)
- {
- const int normalIndex = (iFace % 3);
- const int perpXIndex = (normalIndex + 1) % 3;
- const int perpYIndex = (normalIndex + 2) % 3;
- const float invert = (iFace > 2) ? -1.f : 1.f;
-
- const vec_t faceCoords[4] = {
- directionUnary[normalIndex] + directionUnary[perpXIndex] + directionUnary[perpYIndex],
- directionUnary[normalIndex] + directionUnary[perpXIndex] - directionUnary[perpYIndex],
- directionUnary[normalIndex] - directionUnary[perpXIndex] - directionUnary[perpYIndex],
- directionUnary[normalIndex] - directionUnary[perpXIndex] + directionUnary[perpYIndex],
- };
-
- // clipping
- /*
- bool skipFace = false;
- for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
- {
- vec_t camSpacePosition;
- camSpacePosition.TransformPoint(faceCoords[iCoord] * 0.5f * invert, res);
- if (camSpacePosition.z < 0.001f)
- {
- skipFace = true;
- break;
- }
- }
- if (skipFace)
- {
- continue;
- }
- */
- vec_t centerPosition, centerPositionVP;
- centerPosition.TransformPoint(directionUnary[normalIndex] * 0.5f * invert, *(matrix_t*)matrix);
- centerPositionVP.TransformPoint(directionUnary[normalIndex] * 0.5f * invert, res);
-
- bool inFrustum = true;
- for (int iFrustum = 0; iFrustum < 6; iFrustum++)
- {
- float dist = DistanceToPlane(centerPosition, frustum[iFrustum]);
- if (dist < 0.f)
- {
- inFrustum = false;
- break;
- }
- }
-
- if (!inFrustum)
- {
- continue;
- }
- CubeFace& cubeFace = faces[cubeFaceCount];
-
- // 3D->2D
- // ImVec2 faceCoordsScreen[4];
- for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
- {
- cubeFace.faceCoordsScreen[iCoord] = worldToPos(faceCoords[iCoord] * 0.5f * invert, res);
- }
- cubeFace.color = directionColor[normalIndex] | IM_COL32(0x80, 0x80, 0x80, 0);
-
- cubeFace.z = centerPositionVP.z / centerPositionVP.w;
- cubeFaceCount++;
- }
- }
- qsort(faces, cubeFaceCount, sizeof(CubeFace), [](void const* _a, void const* _b) {
- CubeFace* a = (CubeFace*)_a;
- CubeFace* b = (CubeFace*)_b;
- if (a->z < b->z)
- {
- return 1;
- }
- return -1;
- });
- // draw face with lighter color
- for (int iFace = 0; iFace < cubeFaceCount; iFace++)
- {
- const CubeFace& cubeFace = faces[iFace];
- gContext.mDrawList->AddConvexPolyFilled(cubeFace.faceCoordsScreen, 4, cubeFace.color);
- }
-
- _freea(faces);
-}
-
-void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize) {
- matrix_t viewProjection = *(matrix_t*)view * *(matrix_t*)projection;
- vec_t frustum[6];
- ComputeFrustumPlanes(frustum, viewProjection.m16);
- matrix_t res = *(matrix_t*)matrix * viewProjection;
-
- for (float f = -gridSize; f <= gridSize; f += 1.f)
- {
- for (int dir = 0; dir < 2; dir++)
- {
- vec_t ptA = makeVect(dir ? -gridSize : f, 0.f, dir ? f : -gridSize);
- vec_t ptB = makeVect(dir ? gridSize : f, 0.f, dir ? f : gridSize);
- bool visible = true;
- for (int i = 0; i < 6; i++)
- {
- float dA = DistanceToPlane(ptA, frustum[i]);
- float dB = DistanceToPlane(ptB, frustum[i]);
- if (dA < 0.f && dB < 0.f)
- {
- visible = false;
- break;
- }
- if (dA > 0.f && dB > 0.f)
- {
- continue;
- }
- if (dA < 0.f)
- {
- float len = fabsf(dA - dB);
- float t = fabsf(dA) / len;
- ptA.Lerp(ptB, t);
- }
- if (dB < 0.f)
- {
- float len = fabsf(dB - dA);
- float t = fabsf(dB) / len;
- ptB.Lerp(ptA, t);
- }
- }
- if (visible)
- {
- ImU32 col = IM_COL32(0x80, 0x80, 0x80, 0xFF);
- col = (fmodf(fabsf(f), 10.f) < FLT_EPSILON) ? IM_COL32(0x90, 0x90, 0x90, 0xFF) : col;
- col = (fabsf(f) < FLT_EPSILON) ? IM_COL32(0x40, 0x40, 0x40, 0xFF) : col;
-
- float thickness = 1.f;
- thickness = (fmodf(fabsf(f), 10.f) < FLT_EPSILON) ? 1.5f : thickness;
- thickness = (fabsf(f) < FLT_EPSILON) ? 2.3f : thickness;
-
- gContext.mDrawList->AddLine(worldToPos(ptA, res), worldToPos(ptB, res), col, thickness);
- }
- }
- }
-}
-
-void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor) {
- static bool isDraging = false;
- static bool isClicking = false;
- static bool isInside = false;
- static vec_t interpolationUp;
- static vec_t interpolationDir;
- static int interpolationFrames = 0;
- const vec_t referenceUp = makeVect(0.f, 1.f, 0.f);
-
- matrix_t svgView, svgProjection;
- svgView = gContext.mViewMat;
- svgProjection = gContext.mProjectionMat;
-
- ImGuiIO& io = ImGui::GetIO();
- gContext.mDrawList->AddRectFilled(position, position + size, backgroundColor);
- matrix_t viewInverse;
- viewInverse.Inverse(*(matrix_t*)view);
-
- const vec_t camTarget = viewInverse.v.position - viewInverse.v.dir * length;
-
- // view/projection matrices
- const float distance = 3.f;
- matrix_t cubeProjection, cubeView;
- float fov = acosf(distance / (sqrtf(distance * distance + 3.f))) * RAD2DEG;
- Perspective(fov / sqrtf(2.f), size.x / size.y, 0.01f, 1000.f, cubeProjection.m16);
-
- vec_t dir = makeVect(viewInverse.m[2][0], viewInverse.m[2][1], viewInverse.m[2][2]);
- vec_t up = makeVect(viewInverse.m[1][0], viewInverse.m[1][1], viewInverse.m[1][2]);
- vec_t eye = dir * distance;
- vec_t zero = makeVect(0.f, 0.f);
- LookAt(&eye.x, &zero.x, &up.x, cubeView.m16);
-
- // set context
- gContext.mViewMat = cubeView;
- gContext.mProjectionMat = cubeProjection;
- ComputeCameraRay(gContext.mRayOrigin, gContext.mRayVector, position, size);
-
- const matrix_t res = cubeView * cubeProjection;
-
- // panels
- static const ImVec2 panelPosition[9] = { ImVec2(0.75f, 0.75f), ImVec2(0.25f, 0.75f), ImVec2(0.f, 0.75f), ImVec2(0.75f, 0.25f), ImVec2(0.25f, 0.25f), ImVec2(0.f, 0.25f), ImVec2(0.75f, 0.f), ImVec2(0.25f, 0.f), ImVec2(0.f, 0.f) };
-
- static const ImVec2 panelSize[9] = { ImVec2(0.25f, 0.25f), ImVec2(0.5f, 0.25f), ImVec2(0.25f, 0.25f), ImVec2(0.25f, 0.5f), ImVec2(0.5f, 0.5f), ImVec2(0.25f, 0.5f), ImVec2(0.25f, 0.25f), ImVec2(0.5f, 0.25f), ImVec2(0.25f, 0.25f) };
-
- // tag faces
- bool boxes[27]{};
- for (int iPass = 0; iPass < 2; iPass++)
- {
- for (int iFace = 0; iFace < 6; iFace++)
- {
- const int normalIndex = (iFace % 3);
- const int perpXIndex = (normalIndex + 1) % 3;
- const int perpYIndex = (normalIndex + 2) % 3;
- const float invert = (iFace > 2) ? -1.f : 1.f;
- const vec_t indexVectorX = directionUnary[perpXIndex] * invert;
- const vec_t indexVectorY = directionUnary[perpYIndex] * invert;
- const vec_t boxOrigin = directionUnary[normalIndex] * -invert - indexVectorX - indexVectorY;
-
- // plan local space
- const vec_t n = directionUnary[normalIndex] * invert;
- vec_t viewSpaceNormal = n;
- vec_t viewSpacePoint = n * 0.5f;
- viewSpaceNormal.TransformVector(cubeView);
- viewSpaceNormal.Normalize();
- viewSpacePoint.TransformPoint(cubeView);
- const vec_t viewSpaceFacePlan = BuildPlan(viewSpacePoint, viewSpaceNormal);
-
- // back face culling
- if (viewSpaceFacePlan.w > 0.f)
- {
- continue;
- }
-
- const vec_t facePlan = BuildPlan(n * 0.5f, n);
-
- const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, facePlan);
- vec_t posOnPlan = gContext.mRayOrigin + gContext.mRayVector * len - (n * 0.5f);
-
- float localx = Dot(directionUnary[perpXIndex], posOnPlan) * invert + 0.5f;
- float localy = Dot(directionUnary[perpYIndex], posOnPlan) * invert + 0.5f;
-
- // panels
- const vec_t dx = directionUnary[perpXIndex];
- const vec_t dy = directionUnary[perpYIndex];
- const vec_t origin = directionUnary[normalIndex] - dx - dy;
- for (int iPanel = 0; iPanel < 9; iPanel++)
- {
- vec_t boxCoord = boxOrigin + indexVectorX * float(iPanel % 3) + indexVectorY * float(iPanel / 3) + makeVect(1.f, 1.f, 1.f);
- const ImVec2 p = panelPosition[iPanel] * 2.f;
- const ImVec2 s = panelSize[iPanel] * 2.f;
- ImVec2 faceCoordsScreen[4];
- vec_t panelPos[4] = { dx * p.x + dy * p.y,
- dx * p.x + dy * (p.y + s.y),
- dx * (p.x + s.x) + dy * (p.y + s.y),
- dx * (p.x + s.x) + dy * p.y };
-
- for (unsigned int iCoord = 0; iCoord < 4; iCoord++)
- {
- faceCoordsScreen[iCoord] = worldToPos((panelPos[iCoord] + origin) * 0.5f * invert, res, position, size);
- }
-
- const ImVec2 panelCorners[2] = { panelPosition[iPanel], panelPosition[iPanel] + panelSize[iPanel] };
- bool insidePanel = localx > panelCorners[0].x && localx < panelCorners[1].x && localy > panelCorners[0].y && localy < panelCorners[1].y;
- int boxCoordInt = int(boxCoord.x * 9.f + boxCoord.y * 3.f + boxCoord.z);
- IM_ASSERT(boxCoordInt < 27);
- boxes[boxCoordInt] |= insidePanel && (!isDraging) && gContext.mbMouseOver;
-
- // draw face with lighter color
- if (iPass)
- {
- gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, (directionColor[normalIndex] | IM_COL32(0x80, 0x80, 0x80, 0x80)) | (isInside ? IM_COL32(0x08, 0x08, 0x08, 0) : 0));
- if (boxes[boxCoordInt])
- {
- gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, IM_COL32(0xF0, 0xA0, 0x60, 0x80));
-
- if (!io.MouseDown[0] && !isDraging && isClicking)
- {
- // apply new view direction
- int cx = boxCoordInt / 9;
- int cy = (boxCoordInt - cx * 9) / 3;
- int cz = boxCoordInt % 3;
- interpolationDir = makeVect(1.f - (float)cx, 1.f - (float)cy, 1.f - (float)cz);
- interpolationDir.Normalize();
-
- if (fabsf(Dot(interpolationDir, referenceUp)) > 1.0f - 0.01f)
- {
- vec_t right = viewInverse.v.right;
- if (fabsf(right.x) > fabsf(right.z))
- {
- right.z = 0.f;
- } else
- {
- right.x = 0.f;
- }
- right.Normalize();
- interpolationUp = Cross(interpolationDir, right);
- interpolationUp.Normalize();
- } else
- {
- interpolationUp = referenceUp;
- }
- interpolationFrames = 40;
- isClicking = false;
- }
- if (io.MouseClicked[0] && !isDraging)
- {
- isClicking = true;
- }
- }
- }
- }
- }
- }
- if (interpolationFrames)
- {
- interpolationFrames--;
- vec_t newDir = viewInverse.v.dir;
- newDir.Lerp(interpolationDir, 0.2f);
- newDir.Normalize();
-
- vec_t newUp = viewInverse.v.up;
- newUp.Lerp(interpolationUp, 0.3f);
- newUp.Normalize();
- newUp = interpolationUp;
- vec_t newEye = camTarget + newDir * length;
- LookAt(&newEye.x, &camTarget.x, &newUp.x, view);
- }
- isInside = gContext.mbMouseOver && ImRect(position, position + size).Contains(io.MousePos);
-
- // drag view
- if (!isDraging && io.MouseClicked[0] && isInside)
- {
- isDraging = true;
- isClicking = false;
- } else if (isDraging && !io.MouseDown[0])
- {
- isDraging = false;
- }
-
- if (isDraging)
- {
- matrix_t rx, ry, roll;
-
- rx.RotationAxis(referenceUp, -io.MouseDelta.x * 0.01f);
- ry.RotationAxis(viewInverse.v.right, -io.MouseDelta.y * 0.01f);
-
- roll = rx * ry;
-
- vec_t newDir = viewInverse.v.dir;
- newDir.TransformVector(roll);
- newDir.Normalize();
-
- // clamp
- vec_t planDir = Cross(viewInverse.v.right, referenceUp);
- planDir.y = 0.f;
- planDir.Normalize();
- float dt = Dot(planDir, newDir);
- if (dt < 0.0f)
- {
- newDir += planDir * dt;
- newDir.Normalize();
- }
-
- vec_t newEye = camTarget + newDir * length;
- LookAt(&newEye.x, &camTarget.x, &referenceUp.x, view);
- }
-
- // restore view/projection because it was used to compute ray
- ComputeContext(svgView.m16, svgProjection.m16, gContext.mModelSource.m16, gContext.mMode);
-}
-}; // namespace IMGUIZMO_NAMESPACE
diff --git a/source/Game/EditorGuizmo.hpp b/source/Game/EditorGuizmo.hpp
deleted file mode 100644
index 0560050..0000000
--- a/source/Game/EditorGuizmo.hpp
+++ /dev/null
@@ -1,232 +0,0 @@
-// https://github.com/CedricGuillemet/ImGuizmo
-// v 1.84 WIP
-//
-// The MIT License(MIT)
-//
-// Copyright(c) 2021 Cedric Guillemet
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files(the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions :
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-// -------------------------------------------------------------------------------------------
-// History :
-// 2019/11/03 View gizmo
-// 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode.
-// 2016/09/09 Hatched negative axis. Snapping. Documentation update.
-// 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved
-// 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results.
-// 2016/08/31 First version
-//
-// -------------------------------------------------------------------------------------------
-// Future (no order):
-//
-// - Multi view
-// - display rotation/translation/scale infos in local/world space and not only local
-// - finish local/world matrix application
-// - OPERATION as bitmask
-//
-// -------------------------------------------------------------------------------------------
-// Example
-#if 0
-void EditTransform(const Camera& camera, matrix_t& matrix)
-{
- static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
- static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
- if (ImGui::IsKeyPressed(90))
- mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
- if (ImGui::IsKeyPressed(69))
- mCurrentGizmoOperation = ImGuizmo::ROTATE;
- if (ImGui::IsKeyPressed(82)) // r Key
- mCurrentGizmoOperation = ImGuizmo::SCALE;
- if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
- mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
- ImGui::SameLine();
- if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
- mCurrentGizmoOperation = ImGuizmo::ROTATE;
- ImGui::SameLine();
- if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
- mCurrentGizmoOperation = ImGuizmo::SCALE;
- float matrixTranslation[3], matrixRotation[3], matrixScale[3];
- ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
- ImGui::InputFloat3("Tr", matrixTranslation, 3);
- ImGui::InputFloat3("Rt", matrixRotation, 3);
- ImGui::InputFloat3("Sc", matrixScale, 3);
- ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
-
- if (mCurrentGizmoOperation != ImGuizmo::SCALE)
- {
- if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
- mCurrentGizmoMode = ImGuizmo::LOCAL;
- ImGui::SameLine();
- if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
- mCurrentGizmoMode = ImGuizmo::WORLD;
- }
- static bool useSnap(false);
- if (ImGui::IsKeyPressed(83))
- useSnap = !useSnap;
- ImGui::Checkbox("", &useSnap);
- ImGui::SameLine();
- vec_t snap;
- switch (mCurrentGizmoOperation)
- {
- case ImGuizmo::TRANSLATE:
- snap = config.mSnapTranslation;
- ImGui::InputFloat3("Snap", &snap.x);
- break;
- case ImGuizmo::ROTATE:
- snap = config.mSnapRotation;
- ImGui::InputFloat("Angle Snap", &snap.x);
- break;
- case ImGuizmo::SCALE:
- snap = config.mSnapScale;
- ImGui::InputFloat("Scale Snap", &snap.x);
- break;
- }
- ImGuiIO& io = ImGui::GetIO();
- ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
- ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
-}
-#endif
-#pragma once
-
-#ifdef USE_IMGUI_API
-# include "imconfig.h"
-#endif
-#ifndef IMGUI_API
-# define IMGUI_API
-#endif
-
-// NOTE(hnosm): added so that we don't have to force #include <ImGuizmo.h> after everything else
-#include <imgui.h>
-
-#ifndef IMGUIZMO_NAMESPACE
-# define IMGUIZMO_NAMESPACE ImGuizmo
-#endif
-
-namespace IMGUIZMO_NAMESPACE {
-// call inside your own window and before Manipulate() in order to draw gizmo to that window.
-// Or pass a specific ImDrawList to draw to (e.g. ImGui::GetForegroundDrawList()).
-IMGUI_API void SetDrawlist(ImDrawList* drawlist = nullptr);
-
-// call BeginFrame right after ImGui_XXXX_NewFrame();
-IMGUI_API void BeginFrame();
-
-// this is necessary because when imguizmo is compiled into a dll, and imgui into another
-// globals are not shared between them.
-// More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam
-// expose method to set imgui context
-IMGUI_API void SetImGuiContext(ImGuiContext* ctx);
-
-// return true if mouse cursor is over any gizmo control (axis, plan or screen component)
-IMGUI_API bool IsOver();
-
-// return true if mouse IsOver or if the gizmo is in moving state
-IMGUI_API bool IsUsing();
-
-// enable/disable the gizmo. Stay in the state until next call to Enable.
-// gizmo is rendered with gray half transparent color when disabled
-IMGUI_API void Enable(bool enable);
-
-// helper functions for manualy editing translation/rotation/scale with an input float
-// translation, rotation and scale float points to 3 floats each
-// Angles are in degrees (more suitable for human editing)
-// example:
-// float matrixTranslation[3], matrixRotation[3], matrixScale[3];
-// ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
-// ImGui::InputFloat3("Tr", matrixTranslation, 3);
-// ImGui::InputFloat3("Rt", matrixRotation, 3);
-// ImGui::InputFloat3("Sc", matrixScale, 3);
-// ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
-//
-// These functions have some numerical stability issues for now. Use with caution.
-IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale);
-IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix);
-
-IMGUI_API void SetRect(float x, float y, float width, float height);
-// default is false
-IMGUI_API void SetOrthographic(bool isOrthographic);
-
-// Render a cube with face color corresponding to face normal. Usefull for debug/tests
-IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount);
-IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize);
-
-// call it when you want a gizmo
-// Needs view and projection matrices.
-// matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional
-// translation is applied in world space
-enum OPERATION {
- TRANSLATE_X = (1u << 0),
- TRANSLATE_Y = (1u << 1),
- TRANSLATE_Z = (1u << 2),
- ROTATE_X = (1u << 3),
- ROTATE_Y = (1u << 4),
- ROTATE_Z = (1u << 5),
- ROTATE_SCREEN = (1u << 6),
- SCALE_X = (1u << 7),
- SCALE_Y = (1u << 8),
- SCALE_Z = (1u << 9),
- BOUNDS = (1u << 10),
- SCALE_XU = (1u << 11),
- SCALE_YU = (1u << 12),
- SCALE_ZU = (1u << 13),
-
- TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z,
- ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN,
- SCALE = SCALE_X | SCALE_Y | SCALE_Z,
- SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal
- UNIVERSAL = TRANSLATE | ROTATE | SCALEU
-};
-
-inline OPERATION operator|(OPERATION lhs, OPERATION rhs) {
- return static_cast<OPERATION>(static_cast<int>(lhs) | static_cast<int>(rhs));
-}
-
-enum MODE {
- LOCAL,
- WORLD
-};
-
-IMGUI_API bool Manipulate(
- const float* view,
- const float* projection,
- OPERATION operation,
- MODE mode,
- float* matrix,
- float* deltaMatrix = NULL,
- const float* snap = NULL,
- float* localBounds = NULL,
- const float* boundsSnap = NULL);
-
-//
-// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
-// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
-// other software are using the same mechanics. But just in case, you are now warned!
-//
-IMGUI_API void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
-
-IMGUI_API void SetID(int id);
-
-// return true if the cursor is over the operation's gizmo
-IMGUI_API bool IsOver(OPERATION op);
-IMGUI_API void SetGizmoSizeClipSpace(float value);
-
-// Allow axis to flip
-// When true (default), the guizmo axis flip for better visibility
-// When false, they always stay along the positive world/local axis
-IMGUI_API void AllowAxisFlip(bool value);
-} // namespace IMGUIZMO_NAMESPACE
diff --git a/source/Game/EditorNotification.cpp b/source/Game/EditorNotification.cpp
deleted file mode 100644
index e4a869e..0000000
--- a/source/Game/EditorNotification.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-// Adapted from https://github.com/patrickcjk/imgui-notify
-#include "EditorNotification.hpp"
-
-#include "Macros.hpp"
-
-#define IMGUI_DEFINE_MATH_OPERATORS
-#include <imgui_internal.h>
-
-#include <chrono>
-#include <cstdarg>
-#include <cstdio>
-#include <utility>
-#include <vector>
-
-ImGuiToast::ImGuiToast(ImGuiToastType type, int dismissTime) {
- IM_ASSERT(type < ImGuiToastType_COUNT);
-
- mType = type;
- mDismissTime = dismissTime;
-
- using namespace std::chrono;
- auto timeStamp = system_clock::now().time_since_epoch();
- mCreationTime = duration_cast<milliseconds>(timeStamp).count();
-
- memset(mTitle, 0, sizeof(mTitle));
- memset(mContent, 0, sizeof(mContent));
-}
-
-ImGuiToast::ImGuiToast(ImGuiToastType type, const char* format, ...)
- : ImGuiToast(type) {
- if (format) {
- va_list args;
- va_start(args, format);
- SetContent(format, args);
- va_end(args);
- }
-}
-
-ImGuiToast::ImGuiToast(ImGuiToastType type, int dismissTime, const char* format, ...)
- : ImGuiToast(type, dismissTime) {
- if (format) {
- va_list args;
- va_start(args, format);
- SetContent(format, args);
- va_end(args);
- }
-}
-
-void ImGuiToast::SetTitle(const char* format, ...) {
- if (format) {
- va_list args;
- va_start(args, format);
- SetTitle(format, args);
- va_end(args);
- }
-}
-
-void ImGuiToast::SetContent(const char* format, ...) {
- if (format) {
- va_list args;
- va_start(args, format);
- SetContent(format, args);
- va_end(args);
- }
-}
-
-void ImGuiToast::SetType(const ImGuiToastType& type) {
- IM_ASSERT(type < ImGuiToastType_COUNT);
- mType = type;
-}
-
-const char* ImGuiToast::GetTitle() {
- return mTitle;
-}
-
-const char* ImGuiToast::GetDefaultTitle() {
- if (!strlen(mTitle)) {
- switch (mType) {
- case ImGuiToastType_None: return nullptr;
- case ImGuiToastType_Success: return "Success";
- case ImGuiToastType_Warning: return "Warning";
- case ImGuiToastType_Error: return "Error";
- case ImGuiToastType_Info: return "Info";
- case ImGuiToastType_COUNT: UNREACHABLE;
- }
- }
-
- return mTitle;
-}
-
-ImGuiToastType ImGuiToast::GetType() {
- return mType;
-}
-
-ImVec4 ImGuiToast::GetColor() {
- switch (mType) {
- case ImGuiToastType_None: return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // White
- case ImGuiToastType_Success: return ImVec4(0, 1.0f, 0, 1.0f); // Green
- case ImGuiToastType_Warning: return ImVec4(1.0f, 1.0f, 0, 1.0f); // Yellow
- case ImGuiToastType_Error: return ImVec4(1.0f, 0, 0, 1.0f); // Red
- case ImGuiToastType_Info: return ImVec4(0, 0.616, 1.0f, 1.0f); // Blue
- case ImGuiToastType_COUNT: UNREACHABLE;
- }
- return ImVec4();
-}
-
-const char* ImGuiToast::GetIcon() {
- switch (mType) {
- case ImGuiToastType_None: return nullptr;
-#if 1
- // TODO add IconFontHeaders and replace with proper icons
- case ImGuiToastType_Success: return nullptr;
- case ImGuiToastType_Warning: return nullptr;
- case ImGuiToastType_Error: return nullptr;
- case ImGuiToastType_Info: return nullptr;
-#else
- case ImGuiToastType_Success: return ICON_FA_CHECK_CIRCLE;
- case ImGuiToastType_Warning: return ICON_FA_EXCLAMATION_TRIANGLE;
- case ImGuiToastType_Error: return ICON_FA_TIMES_CIRCLE;
- case ImGuiToastType_Info: return ICON_FA_INFO_CIRCLE;
-#endif
- case ImGuiToastType_COUNT: UNREACHABLE;
- }
- return nullptr;
-}
-
-const char* ImGuiToast::GetContent() {
- return this->mContent;
-}
-
-uint64_t ImGuiToast::GetElapsedTime() {
- using namespace std::chrono;
- auto timeStamp = system_clock::now().time_since_epoch();
- auto timeStampI = duration_cast<milliseconds>(timeStamp).count();
- return timeStampI - mCreationTime;
-}
-
-ImGuiToastPhase ImGuiToast::GetPhase() {
- const auto elapsed = GetElapsedTime();
-
- if (elapsed > kNotifyFadeInOutTime + mDismissTime + kNotifyFadeInOutTime) {
- return ImGuiToastPhase_Expired;
- } else if (elapsed > kNotifyFadeInOutTime + mDismissTime) {
- return ImGuiToastPhase_FadeOut;
- } else if (elapsed > kNotifyFadeInOutTime) {
- return ImGuiToastPhase_Wait;
- } else {
- return ImGuiToastPhase_FadeIn;
- }
-}
-
-float ImGuiToast::GetFadePercent() {
- const auto phase = GetPhase();
- const auto elapsed = GetElapsedTime();
-
- if (phase == ImGuiToastPhase_FadeIn)
- {
- return ((float)elapsed / (float)kNotifyFadeInOutTime) * kNotifyOpacity;
- } else if (phase == ImGuiToastPhase_FadeOut)
- {
- return (1.0f - (((float)elapsed - (float)kNotifyFadeInOutTime - (float)mDismissTime) / (float)kNotifyFadeInOutTime)) * kNotifyOpacity;
- }
-
- return 1.0f * kNotifyOpacity;
-}
-
-void ImGuiToast::SetTitle(const char* format, va_list args) {
- vsnprintf(mTitle, sizeof(mTitle), format, args);
-}
-
-void ImGuiToast::SetContent(const char* format, va_list args) {
- vsnprintf(mContent, sizeof(mContent), format, args);
-}
-
-namespace ImGui {
-static std::vector<ImGuiToast> notifications;
-}
-
-static bool IsNullOrEmpty(const char* str) {
- return !str || !strlen(str);
-}
-
-void ImGui::AddNotification(ImGuiToast toast) {
- notifications.push_back(std::move(toast));
-}
-
-void ImGui::RemoveNotification(int index) {
- notifications.erase(notifications.begin() + index);
-}
-
-void ImGui::ShowNotifications() {
- auto vpSize = GetMainViewport()->Size;
-
- float height = 0.0f;
- for (auto i = 0; i < notifications.size(); i++) {
- auto* currentToast = &notifications[i];
-
- // Remove toast if expired
- if (currentToast->GetPhase() == ImGuiToastPhase_Expired) {
- RemoveNotification(i);
- continue;
- }
-
- // Get icon, title and other data
- const auto icon = currentToast->GetIcon();
- const auto title = currentToast->GetTitle();
- const auto content = currentToast->GetContent();
- const auto defaultTitle = currentToast->GetDefaultTitle();
- const auto opacity = currentToast->GetFadePercent(); // Get opacity based of the current phase
-
- // Window rendering
- auto textColor = currentToast->GetColor();
- textColor.w = opacity;
-
- // Generate new unique name for this toast
- char windowName[50];
- snprintf(windowName, std::size(windowName), "##TOAST%d", i);
-
- SetNextWindowBgAlpha(opacity);
- SetNextWindowPos(ImVec2(vpSize.x - kNotifyPaddingX, vpSize.y - kNotifyPaddingY - height), ImGuiCond_Always, ImVec2(1.0f, 1.0f));
- Begin(windowName, nullptr, kNotifyToastFlags);
- BringWindowToDisplayFront(GetCurrentWindow());
-
- // Here we render the toast content
- {
- PushTextWrapPos(vpSize.x / 3.0f); // We want to support multi-line text, this will wrap the text after 1/3 of the screen width
-
- bool wasTitleRendered = false;
-
- // If an icon is set
- if (!::IsNullOrEmpty(icon)) {
- // Render icon text
- PushStyleColor(ImGuiCol_Text, textColor);
- TextUnformatted(icon);
- PopStyleColor();
- wasTitleRendered = true;
- }
-
- // If a title is set
- if (!::IsNullOrEmpty(title)) {
- // If a title and an icon is set, we want to render on same line
- if (!::IsNullOrEmpty(icon))
- SameLine();
-
- TextUnformatted(title); // Render title text
- wasTitleRendered = true;
- } else if (!::IsNullOrEmpty(defaultTitle)) {
- if (!::IsNullOrEmpty(icon))
- SameLine();
-
- TextUnformatted(defaultTitle); // Render default title text (ImGuiToastType_Success -> "Success", etc...)
- wasTitleRendered = true;
- }
-
- // In case ANYTHING was rendered in the top, we want to add a small padding so the text (or icon) looks centered vertically
- if (wasTitleRendered && !::IsNullOrEmpty(content)) {
- SetCursorPosY(GetCursorPosY() + 5.0f); // Must be a better way to do this!!!!
- }
-
- // If a content is set
- if (!::IsNullOrEmpty(content)) {
- if (wasTitleRendered) {
- Separator();
- }
-
- TextUnformatted(content); // Render content text
- }
-
- PopTextWrapPos();
- }
-
- // Save height for next toasts
- height += GetWindowHeight() + kNotifyPaddingMessageY;
-
- End();
- }
-}
diff --git a/source/Game/EditorNotification.hpp b/source/Game/EditorNotification.hpp
deleted file mode 100644
index 3af8c2d..0000000
--- a/source/Game/EditorNotification.hpp
+++ /dev/null
@@ -1,81 +0,0 @@
-// Adapted from https://github.com/patrickcjk/imgui-notify
-#pragma once
-
-#include <imgui.h>
-#include <cstdint>
-
-enum ImGuiToastType {
- ImGuiToastType_None,
- ImGuiToastType_Success,
- ImGuiToastType_Warning,
- ImGuiToastType_Error,
- ImGuiToastType_Info,
- ImGuiToastType_COUNT
-};
-
-enum ImGuiToastPhase {
- ImGuiToastPhase_FadeIn,
- ImGuiToastPhase_Wait,
- ImGuiToastPhase_FadeOut,
- ImGuiToastPhase_Expired,
- ImGuiToastPhase_COUNT
-};
-
-enum ImGuiToastPos {
- ImGuiToastPos_TopLeft,
- ImGuiToastPos_TopCenter,
- ImGuiToastPos_TopRight,
- ImGuiToastPos_BottomLeft,
- ImGuiToastPos_BottomCenter,
- ImGuiToastPos_BottomRight,
- ImGuiToastPos_Center,
- ImGuiToastPos_COUNT
-};
-
-constexpr int kNotifyMaxMsgLength = 4096; // Max message content length
-constexpr float kNotifyPaddingX = 20.0f; // Bottom-left X padding
-constexpr float kNotifyPaddingY = 20.0f; // Bottom-left Y padding
-constexpr float kNotifyPaddingMessageY = 10.0f; // Padding Y between each message
-constexpr uint64_t kNotifyFadeInOutTime = 150; // Fade in and out duration
-constexpr uint64_t kNotifyDefaultDismiss = 3000; // Auto dismiss after X ms (default, applied only of no data provided in constructors)
-constexpr float kNotifyOpacity = 1.0f; // 0-1 Toast opacity
-constexpr ImGuiWindowFlags kNotifyToastFlags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing;
-
-class ImGuiToast {
-private:
- ImGuiToastType mType = ImGuiToastType_None;
- char mTitle[kNotifyMaxMsgLength] = {};
- char mContent[kNotifyMaxMsgLength] = {};
- int mDismissTime = kNotifyDefaultDismiss;
- uint64_t mCreationTime = 0;
-
-public:
- ImGuiToast(ImGuiToastType type, int dismissTime = kNotifyDefaultDismiss);
- ImGuiToast(ImGuiToastType type, const char* format, ...);
- ImGuiToast(ImGuiToastType type, int dismissTime, const char* format, ...);
-
- void SetTitle(const char* format, ...);
- void SetContent(const char* format, ...);
- void SetType(const ImGuiToastType& type);
-
- const char* GetTitle();
- const char* GetDefaultTitle();
- ImGuiToastType GetType();
- ImVec4 GetColor();
- const char* GetIcon();
- const char* GetContent();
-
- uint64_t GetElapsedTime();
- ImGuiToastPhase GetPhase();
- float GetFadePercent();
-
-private:
- void SetTitle(const char* format, va_list args);
- void SetContent(const char* format, va_list args);
-};
-
-namespace ImGui {
-void AddNotification(ImGuiToast toast);
-void RemoveNotification(int index);
-void ShowNotifications();
-} // namespace ImGui
diff --git a/source/Game/EditorUtils.cpp b/source/Game/EditorUtils.cpp
deleted file mode 100644
index 20caef7..0000000
--- a/source/Game/EditorUtils.cpp
+++ /dev/null
@@ -1,447 +0,0 @@
-#include "EditorUtils.hpp"
-
-#define IMGUI_DEFINE_MATH_OPERATORS
-#include <imgui_internal.h>
-
-#include <backends/imgui_impl_glfw.h>
-#include <cmath>
-#include <glm/gtc/quaternion.hpp>
-#include <glm/gtx/quaternion.hpp>
-#include <numbers>
-
-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::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;
-}
-
-void ImGui::AddUnderLine(ImColor col) {
- auto min = ImGui::GetItemRectMin();
- auto max = ImGui::GetItemRectMax();
- min.y = max.y;
- ImGui::GetWindowDrawList()->AddLine(min, max, col, 1.0f);
-}
-
-void ImGui::DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor) {
- // Taken from https://github.com/thedmd/imgui-node-editor/blob/master/examples/blueprints-example/utilities/drawing.cpp
- // ax::NodeEditor::DrawIcon
-
- auto rect = ImRect(a, b);
- auto rect_x = rect.Min.x;
- auto rect_y = rect.Min.y;
- auto rect_w = rect.Max.x - rect.Min.x;
- auto rect_h = rect.Max.y - rect.Min.y;
- auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
- auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
- auto rect_center = ImVec2(rect_center_x, rect_center_y);
- const auto outline_scale = rect_w / 24.0f;
- const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
-
- if (type == IconType::Flow) {
- const auto origin_scale = rect_w / 24.0f;
-
- const auto offset_x = 1.0f * origin_scale;
- const auto offset_y = 0.0f * origin_scale;
- const auto margin = 2.0f * origin_scale;
- const auto rounding = 0.1f * origin_scale;
- const auto tip_round = 0.7f; // percentage of triangle edge (for tip)
- // const auto edge_round = 0.7f; // percentage of triangle edge (for corner)
- const auto canvas = ImRect(
- rect.Min.x + margin + offset_x,
- rect.Min.y + margin + offset_y,
- rect.Max.x - margin + offset_x,
- rect.Max.y - margin + offset_y);
- const auto canvas_x = canvas.Min.x;
- const auto canvas_y = canvas.Min.y;
- const auto canvas_w = canvas.Max.x - canvas.Min.x;
- const auto canvas_h = canvas.Max.y - canvas.Min.y;
-
- const auto left = canvas_x + canvas_w * 0.5f * 0.3f;
- const auto right = canvas_x + canvas_w - canvas_w * 0.5f * 0.3f;
- const auto top = canvas_y + canvas_h * 0.5f * 0.2f;
- const auto bottom = canvas_y + canvas_h - canvas_h * 0.5f * 0.2f;
- const auto center_y = (top + bottom) * 0.5f;
- // const auto angle = AX_PI * 0.5f * 0.5f * 0.5f;
-
- const auto tip_top = ImVec2(canvas_x + canvas_w * 0.5f, top);
- const auto tip_right = ImVec2(right, center_y);
- const auto tip_bottom = ImVec2(canvas_x + canvas_w * 0.5f, bottom);
-
- drawList->PathLineTo(ImVec2(left, top) + ImVec2(0, rounding));
- drawList->PathBezierCubicCurveTo(
- ImVec2(left, top),
- ImVec2(left, top),
- ImVec2(left, top) + ImVec2(rounding, 0));
- drawList->PathLineTo(tip_top);
- drawList->PathLineTo(tip_top + (tip_right - tip_top) * tip_round);
- drawList->PathBezierCubicCurveTo(
- tip_right,
- tip_right,
- tip_bottom + (tip_right - tip_bottom) * tip_round);
- drawList->PathLineTo(tip_bottom);
- drawList->PathLineTo(ImVec2(left, bottom) + ImVec2(rounding, 0));
- drawList->PathBezierCubicCurveTo(
- ImVec2(left, bottom),
- ImVec2(left, bottom),
- ImVec2(left, bottom) - ImVec2(0, rounding));
-
- if (!filled) {
- if (innerColor & 0xFF000000) {
- drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor);
- }
-
- drawList->PathStroke(color, true, 2.0f * outline_scale);
- } else {
- drawList->PathFillConvex(color);
- }
- } else {
- auto triangleStart = rect_center_x + 0.32f * rect_w;
-
- auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f);
-
- rect.Min.x += rect_offset;
- rect.Max.x += rect_offset;
- rect_x += rect_offset;
- rect_center_x += rect_offset * 0.5f;
- rect_center.x += rect_offset * 0.5f;
-
- if (type == IconType::Circle) {
- const auto c = rect_center;
-
- if (!filled) {
- const auto r = 0.5f * rect_w / 2.0f - 0.5f;
-
- if (innerColor & 0xFF000000)
- drawList->AddCircleFilled(c, r, innerColor, 12 + extra_segments);
- drawList->AddCircle(c, r, color, 12 + extra_segments, 2.0f * outline_scale);
- } else {
- drawList->AddCircleFilled(c, 0.5f * rect_w / 2.0f, color, 12 + extra_segments);
- }
- }
-
- if (type == IconType::Square) {
- if (filled) {
- const auto r = 0.5f * rect_w / 2.0f;
- const auto p0 = rect_center - ImVec2(r, r);
- const auto p1 = rect_center + ImVec2(r, r);
-
- drawList->AddRectFilled(p0, p1, color, 0, 15 + extra_segments);
- } else {
- const auto r = 0.5f * rect_w / 2.0f - 0.5f;
- const auto p0 = rect_center - ImVec2(r, r);
- const auto p1 = rect_center + ImVec2(r, r);
-
- if (innerColor & 0xFF000000)
- drawList->AddRectFilled(p0, p1, innerColor, 0, 15 + extra_segments);
-
- drawList->AddRect(p0, p1, color, 0, 15 + extra_segments, 2.0f * outline_scale);
- }
- }
-
- if (type == IconType::Grid) {
- const auto r = 0.5f * rect_w / 2.0f;
- const auto w = ceilf(r / 3.0f);
-
- const auto baseTl = ImVec2(floorf(rect_center_x - w * 2.5f), floorf(rect_center_y - w * 2.5f));
- const auto baseBr = ImVec2(floorf(baseTl.x + w), floorf(baseTl.y + w));
-
- auto tl = baseTl;
- auto br = baseBr;
- for (int i = 0; i < 3; ++i) {
- tl.x = baseTl.x;
- br.x = baseBr.x;
- drawList->AddRectFilled(tl, br, color);
- tl.x += w * 2;
- br.x += w * 2;
- if (i != 1 || filled)
- drawList->AddRectFilled(tl, br, color);
- tl.x += w * 2;
- br.x += w * 2;
- drawList->AddRectFilled(tl, br, color);
-
- tl.y += w * 2;
- br.y += w * 2;
- }
-
- triangleStart = br.x + w + 1.0f / 24.0f * rect_w;
- }
-
- if (type == IconType::RoundSquare) {
- if (filled) {
- const auto r = 0.5f * rect_w / 2.0f;
- const auto cr = r * 0.5f;
- const auto p0 = rect_center - ImVec2(r, r);
- const auto p1 = rect_center + ImVec2(r, r);
-
- drawList->AddRectFilled(p0, p1, color, cr, 15);
- } else {
- const auto r = 0.5f * rect_w / 2.0f - 0.5f;
- const auto cr = r * 0.5f;
- const auto p0 = rect_center - ImVec2(r, r);
- const auto p1 = rect_center + ImVec2(r, r);
-
- if (innerColor & 0xFF000000)
- drawList->AddRectFilled(p0, p1, innerColor, cr, 15);
-
- drawList->AddRect(p0, p1, color, cr, 15, 2.0f * outline_scale);
- }
- } else if (type == IconType::Diamond) {
- if (filled) {
- const auto r = 0.607f * rect_w / 2.0f;
- const auto c = rect_center;
-
- drawList->PathLineTo(c + ImVec2(0, -r));
- drawList->PathLineTo(c + ImVec2(r, 0));
- drawList->PathLineTo(c + ImVec2(0, r));
- drawList->PathLineTo(c + ImVec2(-r, 0));
- drawList->PathFillConvex(color);
- } else {
- const auto r = 0.607f * rect_w / 2.0f - 0.5f;
- const auto c = rect_center;
-
- drawList->PathLineTo(c + ImVec2(0, -r));
- drawList->PathLineTo(c + ImVec2(r, 0));
- drawList->PathLineTo(c + ImVec2(0, r));
- drawList->PathLineTo(c + ImVec2(-r, 0));
-
- if (innerColor & 0xFF000000)
- drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor);
-
- drawList->PathStroke(color, true, 2.0f * outline_scale);
- }
- } else {
- const auto triangleTip = triangleStart + rect_w * (0.45f - 0.32f);
-
- drawList->AddTriangleFilled(
- ImVec2(ceilf(triangleTip), rect_y + rect_h * 0.5f),
- ImVec2(triangleStart, rect_center_y + 0.15f * rect_h),
- ImVec2(triangleStart, rect_center_y - 0.15f * rect_h),
- color);
- }
- }
-}
-
-void ImGui::Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color, const ImVec4& innerColor) {
- // Taken from https://github.com/thedmd/imgui-node-editor/blob/master/examples/blueprints-example/utilities/widgets.cpp
- // ax::NodeEditor::Icon
-
- if (ImGui::IsRectVisible(size)) {
- auto cursorPos = ImGui::GetCursorScreenPos();
- auto drawList = ImGui::GetWindowDrawList();
- DrawIcon(drawList, cursorPos, cursorPos + size, type, filled, ImColor(color), ImColor(innerColor));
- }
-
- ImGui::Dummy(size);
-}
-
-void ImGui::DrawArrow(ImDrawList* drawList, ImVec2 from, ImVec2 to, ImU32 color, float lineThickness) {
- // Adapted from https://stackoverflow.com/a/6333775
-
- using namespace std::numbers;
- constexpr float kHeadLength = 10;
-
- auto angle = std::atan2(to.y - from.y, to.x - from.x);
- drawList->AddLine(from, to, color, lineThickness);
- drawList->AddLine(to, ImVec2(to.x - kHeadLength * std::cos(angle - pi / 6), to.y - kHeadLength * std::sin(angle - pi / 6)), color, lineThickness);
- drawList->AddLine(to, ImVec2(to.x - kHeadLength * std::cos(angle + pi / 6), to.y - kHeadLength * std::sin(angle + pi / 6)), color, lineThickness);
-}
-
-struct DialogObject {
- std::string message;
- std::function<void(int)> callback;
-};
-
-static DialogObject gConfirmationDialog{};
-
-void ImGui::DialogConfirmation(std::string message, std::function<void(bool)> callback) {
- gConfirmationDialog.message = std::move(message);
- // TODO is converting void(bool) to void(int) fine?
- gConfirmationDialog.callback = std::move(callback);
-}
-
-void ImGui::ShowDialogs() {
- if (gConfirmationDialog.callback) {
- if (ImGui::BeginPopupModal("Confirmation")) {
- ImGui::Text("%s", gConfirmationDialog.message.c_str());
- if (ImGui::Button("Cancel")) {
- gConfirmationDialog.callback(false);
- gConfirmationDialog.callback = {};
- ImGui::CloseCurrentPopup();
- }
- ImGui::SameLine();
- if (ImGui::Button("Confirm")) {
- gConfirmationDialog.callback(true);
- gConfirmationDialog.callback = {};
- ImGui::CloseCurrentPopup();
- }
- ImGui::EndPopup();
- }
- }
-}
-
-float Utils::CalcImageHeight(glm::vec2 original, int targetWidth) {
- // Xorig / Yorig = Xnew / Ynew
- // Ynew = Xnew * Yorig / Xorig
- return targetWidth * original.y / original.x;
-}
-
-float Utils::CalcImageWidth(glm::vec2 original, float targetHeight) {
- // Xorig / Yorig = Xnew / Ynew
- // Xnew = Xorig / Yorig * Ynew
- return original.x / original.y * targetHeight;
-}
-
-ImVec2 Utils::FitImage(glm::vec2 original) {
- float newWidth = ImGui::GetContentRegionAvail().x;
- return ImVec2(newWidth, CalcImageHeight(original, newWidth));
-}
diff --git a/source/Game/EditorUtils.hpp b/source/Game/EditorUtils.hpp
deleted file mode 100644
index 99c522b..0000000
--- a/source/Game/EditorUtils.hpp
+++ /dev/null
@@ -1,63 +0,0 @@
-#pragma once
-
-#include "Color.hpp"
-#include "EditorGuizmo.hpp"
-
-#include <imgui.h>
-#include <string>
-
-// To check whether a payload is of this type, use starts_with()
-#define BRUSSEL_TAG_PREFIX_GameObject "GameObject"
-#define BRUSSEL_TAG_PREFIX_Ires "Ires"
-
-#define BRUSSEL_TAG_Level "Level"
-
-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 Splitter(bool splitVertically, float thickness, float* size1, float* size2, float minSize1, float minSize2, float splitterLongAxisSize = -1.0f);
-
-void AddUnderLine(ImColor col);
-
-enum class IconType {
- Flow,
- Circle,
- Square,
- Grid,
- RoundSquare,
- Diamond,
-};
-
-void DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor);
-void Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color = ImVec4(1, 1, 1, 1), const ImVec4& innerColor = ImVec4(0, 0, 0, 0));
-
-void DrawArrow(ImDrawList* drawList, ImVec2 from, ImVec2 to, ImU32 color, float lineThickness = 1.0f);
-
-// NOTE: string is copied into an internal storage
-void DialogConfirmation(std::string message, std::function<void(bool)> callback);
-void ShowDialogs();
-
-} // namespace ImGui
-
-namespace Utils {
-
-float CalcImageHeight(glm::vec2 original, int targetWidth);
-float CalcImageWidth(glm::vec2 original, float targetHeight);
-ImVec2 FitImage(glm::vec2 original);
-
-} // namespace Utils
diff --git a/source/Game/FuzzyMatch.cpp b/source/Game/FuzzyMatch.cpp
deleted file mode 100644
index 0ab604d..0000000
--- a/source/Game/FuzzyMatch.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-// Adapted from https://github.com/forrestthewoods/lib_fts/blob/master/code/fts_fuzzy_match.h
-#include "FuzzyMatch.hpp"
-
-#include <cctype>
-#include <cstring>
-
-namespace FuzzyMatch {
-
-namespace P6503_UNITY_ID {
- bool SearchRecursive(const char* pattern, const char* src, int& outScore, const char* strBegin, const uint8_t srcMatches[], uint8_t newMatches[], int maxMatches, int& nextMatch, int& recursionCount, int recursionLimit);
-} // namespace P6503_UNITY_ID
-
-bool SearchSimple(char const* pattern, char const* haystack) {
- while (*pattern != '\0' && *haystack != '\0') {
- if (tolower(*pattern) == tolower(*haystack)) {
- ++pattern;
- }
- ++haystack;
- }
-
- return *pattern == '\0';
-}
-
-bool Search(char const* pattern, char const* haystack, int& outScore) {
- uint8_t matches[256];
- int matchCount = 0;
- return Search(pattern, haystack, outScore, matches, sizeof(matches), matchCount);
-}
-
-bool Search(char const* pattern, char const* haystack, int& outScore, uint8_t matches[], int maxMatches, int& outMatches) {
- int recursionCount = 0;
- int recursionLimit = 10;
- int newMatches = 0;
- bool result = P6503_UNITY_ID::SearchRecursive(pattern, haystack, outScore, haystack, nullptr, matches, maxMatches, newMatches, recursionCount, recursionLimit);
- outMatches = newMatches;
- return result;
-}
-
-namespace P6503_UNITY_ID {
- bool SearchRecursive(const char* pattern, const char* src, int& outScore, const char* strBegin, const uint8_t srcMatches[], uint8_t newMatches[], int maxMatches, int& nextMatch, int& recursionCount, int recursionLimit) {
- // Count recursions
- ++recursionCount;
- if (recursionCount >= recursionLimit) {
- return false;
- }
-
- // Detect end of strings
- if (*pattern == '\0' || *src == '\0') {
- return false;
- }
-
- // Recursion params
- bool recursiveMatch = false;
- uint8_t bestRecursiveMatches[256];
- int bestRecursiveScore = 0;
-
- // Loop through pattern and str looking for a match
- bool firstMatch = true;
- while (*pattern != '\0' && *src != '\0') {
- // Found match
- if (tolower(*pattern) == tolower(*src)) {
- // Supplied matches buffer was too short
- if (nextMatch >= maxMatches) {
- return false;
- }
-
- // "Copy-on-Write" srcMatches into matches
- if (firstMatch && srcMatches) {
- memcpy(newMatches, srcMatches, nextMatch);
- firstMatch = false;
- }
-
- // Recursive call that "skips" this match
- uint8_t recursiveMatches[256];
- int recursiveScore;
- int recursiveNextMatch = nextMatch;
- if (SearchRecursive(pattern, src + 1, recursiveScore, strBegin, newMatches, recursiveMatches, sizeof(recursiveMatches), recursiveNextMatch, recursionCount, recursionLimit)) {
- // Pick the best recursive score
- if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
- memcpy(bestRecursiveMatches, recursiveMatches, 256);
- bestRecursiveScore = recursiveScore;
- }
- recursiveMatch = true;
- }
-
- // Advance
- newMatches[nextMatch++] = (uint8_t)(src - strBegin);
- ++pattern;
- }
- ++src;
- }
-
- // Determine if full pattern was matched
- bool matched = *pattern == '\0';
-
- // Calculate score
- if (matched) {
- const int sequentialBonus = 15; // bonus for adjacent matches
- const int separatorBonus = 30; // bonus if match occurs after a separator
- const int camelBonus = 30; // bonus if match is uppercase and prev is lower
- const int firstLetterBonus = 15; // bonus if the first letter is matched
-
- const int leadingLetterPenalty = -5; // penalty applied for every letter in str before the first match
- const int maxLeadingLetterPenalty = -15; // maximum penalty for leading letters
- const int unmatchedLetterPenalty = -1; // penalty for every letter that doesn't matter
-
- // Iterate str to end
- while (*src != '\0') {
- ++src;
- }
-
- // Initialize score
- outScore = 100;
-
- // Apply leading letter penalty
- int penalty = leadingLetterPenalty * newMatches[0];
- if (penalty < maxLeadingLetterPenalty) {
- penalty = maxLeadingLetterPenalty;
- }
- outScore += penalty;
-
- // Apply unmatched penalty
- int unmatched = (int)(src - strBegin) - nextMatch;
- outScore += unmatchedLetterPenalty * unmatched;
-
- // Apply ordering bonuses
- for (int i = 0; i < nextMatch; ++i) {
- uint8_t currIdx = newMatches[i];
-
- if (i > 0) {
- uint8_t prevIdx = newMatches[i - 1];
-
- // Sequential
- if (currIdx == (prevIdx + 1))
- outScore += sequentialBonus;
- }
-
- // Check for bonuses based on neighbor character value
- if (currIdx > 0) {
- // Camel case
- char neighbor = strBegin[currIdx - 1];
- char curr = strBegin[currIdx];
- if (::islower(neighbor) && ::isupper(curr)) {
- outScore += camelBonus;
- }
-
- // Separator
- bool neighborSeparator = neighbor == '_' || neighbor == ' ';
- if (neighborSeparator) {
- outScore += separatorBonus;
- }
- } else {
- // First letter
- outScore += firstLetterBonus;
- }
- }
- }
-
- // Return best result
- if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
- // Recursive score is better than "this"
- memcpy(newMatches, bestRecursiveMatches, maxMatches);
- outScore = bestRecursiveScore;
- return true;
- } else if (matched) {
- // "this" score is better than recursive
- return true;
- } else {
- // no match
- return false;
- }
- }
-} // namespace P6503_UNITY_ID
-} // namespace FuzzyMatch
diff --git a/source/Game/FuzzyMatch.hpp b/source/Game/FuzzyMatch.hpp
deleted file mode 100644
index 7a26b7e..0000000
--- a/source/Game/FuzzyMatch.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-// Adapted from https://github.com/forrestthewoods/lib_fts/blob/master/code/fts_fuzzy_match.h
-#pragma once
-
-#include <cstdint>
-
-namespace FuzzyMatch {
-bool SearchSimple(char const* pattern, char const* haystack);
-bool Search(char const* pattern, char const* haystack, int& outScore);
-bool Search(char const* pattern, char const* haystack, int& outScore, uint8_t matches[], int maxMatches, int& outMatches);
-} // namespace FuzzyMatch
diff --git a/source/Game/GameObject.cpp b/source/Game/GameObject.cpp
deleted file mode 100644
index 3b15111..0000000
--- a/source/Game/GameObject.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-#include "GameObject.hpp"
-
-#include "Level.hpp"
-#include "Player.hpp"
-#include "SceneThings.hpp"
-#include "World.hpp"
-
-#include <Metadata.hpp>
-#include <RapidJsonHelper.hpp>
-
-#include <rapidjson/document.h>
-#include <cassert>
-#include <string_view>
-#include <utility>
-
-using namespace std::literals;
-
-namespace ProjectBrussel_UNITY_ID {
-GameObject* CreateGameObject(GameObject::Kind kind, GameWorld* world) {
- using enum Tags::GameObjectKind;
- switch (kind) {
- case KD_Generic: return new GameObject(world);
- case KD_SimpleGeometry: return new SimpleGeometryObject(world);
- case KD_Building: return new BuildingObject(world);
- case KD_LevelWrapper: return new LevelWrapperObject(world);
- default: break;
- }
- return nullptr;
-}
-
-bool ValidateGameObjectChild(GameObject* parent, GameObject* child) {
- return parent->GetWorld() == child->GetWorld();
-}
-} // namespace ProjectBrussel_UNITY_ID
-
-void GameObject::FreeRecursive(GameObject* obj) {
- if (!obj->mStopFreePropagation) {
- for (auto child : obj->GetChildren()) {
- FreeRecursive(obj);
- }
- }
- delete obj;
-}
-
-GameObject::GameObject(GameWorld* world)
- : GameObject(KD_Generic, world) {
-}
-
-GameObject::GameObject(Kind kind, GameWorld* world)
- : mEditorAttachment{ nullptr }
- , mWorld{ world }
- , mParent{ nullptr }
- , mRot(1.0f, 0.0f, 0.0f, 0.0f)
- , mPos(0.0f, 0.0f, 0.0f)
- , mScale(1.0f, 1.0f, 1.0f)
- , mKind{ kind } {
-}
-
-GameObject::~GameObject() {
- RemoveAllChildren();
- if (mParent) {
- mParent->RemoveChild(this);
- // NOTE: from this point on, mParent will be nullptr
- }
-}
-
-GameObject::Kind GameObject::GetKind() const {
- return mKind;
-}
-
-GameWorld* GameObject::GetWorld() const {
- return mWorld;
-}
-
-GameObject* GameObject::GetParent() const {
- return mParent;
-}
-
-const PodVector<GameObject*>& GameObject::GetChildren() const {
- return mChildren;
-}
-
-void GameObject::AddChild(GameObject* child) {
- using namespace ProjectBrussel_UNITY_ID;
-
- if (child->mParent) {
- return;
- }
- if (!ValidateGameObjectChild(this, child)) {
- return;
- }
-
- mChildren.push_back(child);
- child->SetParent(this);
-}
-
-GameObject* GameObject::RemoveChild(int index) {
- if (index < 0 || index >= mChildren.size()) {
- return nullptr;
- }
-
- auto it = mChildren.begin() + index;
- auto child = *it;
-
- // cancelUpdate(ret);
-
- std::swap(*it, mChildren.back());
- mChildren.pop_back();
- child->SetParent(nullptr);
- return child;
-}
-
-GameObject* GameObject::RemoveChild(GameObject* child) {
- if (child) {
- for (auto it = mChildren.begin(); it != mChildren.end(); ++it) {
- if (*it == child) {
- // cancelUpdate(child);
-
- std::swap(*it, mChildren.back());
- mChildren.pop_back();
- child->SetParent(nullptr);
- return child;
- }
- }
- }
- return nullptr;
-}
-
-void GameObject::RemoveSelfFromParent() {
- if (mParent) {
- mParent->RemoveChild(this);
- }
-}
-
-PodVector<GameObject*> GameObject::RemoveAllChildren() {
- for (auto& child : mChildren) {
- child->SetParent(nullptr);
- }
-
- auto result = std::move(mChildren);
- // Moving from STL object leaves it in a valid but _unspecified_ state, call std::vector::clear() to guarantee it's empty
- // NOTE: even though we have the source code of PodVector<T>, we still do this to follow convention
- mChildren.clear();
- return result;
-}
-
-const glm::vec3& GameObject::GetPos() const {
- 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;
-}
-
-const glm::vec3& GameObject::GetScale() const {
- return mScale;
-}
-
-void GameObject::SetScale(const glm::vec3& scale) {
- mScale = scale;
-}
-
-std::span<const RenderObject> GameObject::GetRenderObjects() const {
- return {};
-}
-
-void GameObject::OnInitialized() {
-}
-
-void GameObject::Awaken() {
-}
-
-void GameObject::Resleep() {
-}
-
-void GameObject::Update() {
-}
-
-rapidjson::Value GameObject::Serialize(GameObject* obj, rapidjson::Document& root) {
- rapidjson::Value result(rapidjson::kObjectType);
-
- result.AddMember("Type", rapidjson::StringRef(Metadata::EnumToString(obj->GetKind())), root.GetAllocator());
-
- rapidjson::Value rvValue(rapidjson::kObjectType);
- obj->WriteSaveFormat(rvValue, root);
- result.AddMember("Value", rvValue, root.GetAllocator());
-
- return result;
-}
-
-std::unique_ptr<GameObject> GameObject::Deserialize(const rapidjson::Value& value, GameWorld* world) {
- using namespace ProjectBrussel_UNITY_ID;
-
- auto rvType = rapidjson::GetProperty(value, rapidjson::kStringType, "Type"sv);
- if (!rvType) return nullptr;
-
- auto rvValue = rapidjson::GetProperty(value, rapidjson::kObjectType, "Value"sv);
- if (!rvValue) return nullptr;
-
- auto kind = Metadata::EnumFromString<Kind>(rapidjson::AsStringView(*rvType));
- assert(kind.has_value());
- auto obj = std::unique_ptr<GameObject>(CreateGameObject(kind.value(), world));
- if (!obj) return nullptr;
- obj->ReadSaveFormat(*rvValue);
-
- return obj;
-}
-
-void GameObject::ReadSaveFormat(const rapidjson::Value& value) {
-}
-
-void GameObject::WriteSaveFormat(rapidjson::Value& value, rapidjson::Document& root) {
-}
-
-void GameObject::SetParent(GameObject* parent) {
- if (mParent != parent) {
- mParent = parent;
- // needUpdate();
- }
-}
-
-#include <generated/GameObject.gs.inl>
diff --git a/source/Game/GameObject.hpp b/source/Game/GameObject.hpp
deleted file mode 100644
index f975803..0000000
--- a/source/Game/GameObject.hpp
+++ /dev/null
@@ -1,102 +0,0 @@
-#pragma once
-
-#include "EditorAttachment.hpp"
-#include "Material.hpp"
-#include "Renderer.hpp"
-#include "VertexIndex.hpp"
-
-#include <MacrosCodegen.hpp>
-#include <PodVector.hpp>
-
-#include <rapidjson/fwd.h>
-#include <glm/glm.hpp>
-#include <glm/gtc/quaternion.hpp>
-#include <span>
-#include <vector>
-
-namespace Tags {
-enum class GameObjectKind {
- KD_Generic,
- KD_Player,
- KD_SimpleGeometry,
- KD_Building,
- KD_LevelWrapper,
- KD_COUNT,
-};
-BRUSSEL_ENUM(GameObjectKind, ToString FromString ExcludeHeuristics);
-} // namespace Tags
-
-class GameWorld;
-class GameObject {
-public:
- using Kind = Tags::GameObjectKind;
- using enum Tags::GameObjectKind;
-
-private:
- std::unique_ptr<EditorAttachment> mEditorAttachment;
- GameWorld* mWorld;
- GameObject* mParent;
- PodVector<GameObject*> mChildren;
- glm::quat mRot;
- glm::vec3 mPos;
- glm::vec3 mScale;
- Kind mKind;
-
-protected:
- bool mStopFreePropagation : 1 = false;
-
-public:
- static void FreeRecursive(GameObject* object);
-
- // TODO allow moving between worlds
- GameObject(GameWorld* world);
- GameObject(Kind kind, GameWorld* world);
- virtual ~GameObject();
-
- GameObject(const GameObject&) = delete;
- GameObject& operator=(const GameObject&) = delete;
- GameObject(GameObject&&) = default;
- GameObject& operator=(GameObject&&) = default;
-
- Kind GetKind() const;
-
- GameWorld* GetWorld() const;
- GameObject* GetParent() const;
- const PodVector<GameObject*>& GetChildren() const;
- void AddChild(GameObject* child);
- GameObject* RemoveChild(int index);
- GameObject* RemoveChild(GameObject* child);
- void RemoveSelfFromParent();
- PodVector<GameObject*> RemoveAllChildren();
-
- EditorAttachment* GetEditorAttachment() const { return mEditorAttachment.get(); }
- void SetEditorAttachment(EditorAttachment* attachment) { mEditorAttachment.reset(attachment); }
-
- const glm::vec3& GetPos() const;
- void SetPos(const glm::vec3& pos);
-
- const glm::quat& GetRotation() const;
- void SetRotation(const glm::quat& rotation);
-
- const glm::vec3& GetScale() const;
- void SetScale(const glm::vec3& scale);
-
- // Visuals
- virtual std::span<const RenderObject> GetRenderObjects() const;
-
- // Lifetime hooks
- virtual void OnInitialized();
- virtual void Awaken();
- virtual void Resleep();
- virtual void Update();
-
- static rapidjson::Value Serialize(GameObject* obj, rapidjson::Document& root);
- static std::unique_ptr<GameObject> Deserialize(const rapidjson::Value& value, GameWorld* world);
- virtual void ReadSaveFormat(const rapidjson::Value& value);
- virtual void WriteSaveFormat(rapidjson::Value& value, rapidjson::Document& root);
-
-protected:
- void SetParent(GameObject* parent);
-};
-
-#include <generated/GameObject.gh.inl>
diff --git a/source/Game/GraphicsTags.cpp b/source/Game/GraphicsTags.cpp
deleted file mode 100644
index eb9a079..0000000
--- a/source/Game/GraphicsTags.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-#include "GraphicsTags.hpp"
-
-#include <robin_hood.h>
-#include <cstddef>
-#include <cstdint>
-
-using namespace std::literals;
-
-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;
-}
-
-int Tags::VectorLenOf(VertexElementType type) {
- switch (type) {
- case VET_Float1:
- case VET_Double1:
- case VET_Int1:
- case VET_Uint1:
- return 1;
- case VET_Float2:
- case VET_Double2:
- case VET_Short2:
- case VET_Short2Norm:
- case VET_Ushort2:
- case VET_Ushort2Norm:
- case VET_Int2:
- case VET_Uint2:
- return 2;
- case VET_Float3:
- case VET_Double3:
- case VET_Int3:
- case VET_Uint3:
- return 3;
- case VET_Float4:
- case VET_Double4:
- case VET_Short4:
- case VET_Short4Norm:
- case VET_Ushort4:
- case VET_Ushort4Norm:
- case VET_Int4:
- case VET_Uint4:
- case VET_Byte4:
- case VET_Byte4Norm:
- case VET_Ubyte4:
- case VET_Ubyte4Norm:
- return 4;
- }
- return 0;
-}
-
-GLenum Tags::FindGLType(VertexElementType type) {
- switch (type) {
- case VET_Float1:
- case VET_Float2:
- case VET_Float3:
- case VET_Float4:
- return GL_FLOAT;
- case VET_Double1:
- case VET_Double2:
- case VET_Double3:
- case VET_Double4:
- return GL_DOUBLE;
- case VET_Short2:
- case VET_Short2Norm:
- case VET_Short4:
- case VET_Short4Norm:
- return GL_SHORT;
- case VET_Ushort2:
- case VET_Ushort2Norm:
- case VET_Ushort4:
- case VET_Ushort4Norm:
- return GL_UNSIGNED_SHORT;
- case VET_Int1:
- case VET_Int2:
- case VET_Int3:
- case VET_Int4:
- return GL_INT;
- case VET_Uint1:
- case VET_Uint2:
- case VET_Uint3:
- case VET_Uint4:
- return GL_UNSIGNED_INT;
- case VET_Byte4:
- case VET_Byte4Norm:
- return GL_BYTE;
- case VET_Ubyte4:
- case VET_Ubyte4Norm:
- return GL_UNSIGNED_BYTE;
- }
- 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;
-}
-
-GLenum Tags::FindGLType(IndexType type) {
- switch (type) {
- case IT_16Bit: return GL_UNSIGNED_SHORT;
- case IT_32Bit: return GL_UNSIGNED_BYTE;
- }
- return GL_NONE;
-}
-
-namespace ProjectBrussel_UNITY_ID {
-struct GLTypeInfo {
- robin_hood::unordered_flat_map<GLenum, std::string_view> enum2Name;
- robin_hood::unordered_flat_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::GLTypeToString(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::GLTypeFromString(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;
- }
-}
-
-#include <generated/GraphicsTags.gs.inl>
diff --git a/source/Game/GraphicsTags.hpp b/source/Game/GraphicsTags.hpp
deleted file mode 100644
index cdf79eb..0000000
--- a/source/Game/GraphicsTags.hpp
+++ /dev/null
@@ -1,102 +0,0 @@
-#pragma once
-
-#include <MacrosCodegen.hpp>
-
-#include <glad/glad.h>
-#include <limits>
-#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,
- VES_Color2,
- VES_Color3,
- /// Texture coordinates, typically VET_Float2
- VES_TexCoords1,
- VES_TexCoords2,
- VES_TexCoords3,
- /// Binormal (Y axis if normal is Z)
- VES_Binormal,
- /// Tangent (X axis if normal is Z)
- VES_Tangent,
- /// Default semantic
- VES_Generic,
- VES_COUNT,
-};
-BRUSSEL_ENUM(VertexElementSemantic, ToString FromString ExcludeHeuristics);
-
-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_Byte4Norm, /// 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,
-};
-constexpr auto VET_NORM_BEGIN = VET_Byte4Norm;
-constexpr auto VET_NORM_END = VET_Ushort4Norm;
-BRUSSEL_ENUM(VertexElementType, ToString FromString ExcludeHeuristics);
-
-int SizeOf(VertexElementType type);
-int VectorLenOf(VertexElementType type);
-GLenum FindGLType(VertexElementType type);
-bool IsNormalized(VertexElementType type);
-
-enum IndexType {
- IT_16Bit,
- IT_32Bit,
-};
-
-int SizeOf(IndexType type);
-GLenum FindGLType(IndexType type);
-
-enum TexFilter {
- TF_Linear,
- TF_Nearest,
-};
-BRUSSEL_ENUM(TexFilter, ToString FromString ExcludeHeuristics);
-
-std::string_view GLTypeToString(GLenum);
-GLenum GLTypeFromString(std::string_view name);
-
-constexpr auto kInvalidLocation = std::numeric_limits<GLuint>::max();
-} // namespace Tags
-
-#include <generated/GraphicsTags.gh.inl>
diff --git a/source/Game/Image.cpp b/source/Game/Image.cpp
deleted file mode 100644
index 3673acc..0000000
--- a/source/Game/Image.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-#include "Image.hpp"
-
-#include <stb_image.h>
-#include <stb_rect_pack.h>
-#include <cstring>
-
-Image::Image()
- : mSize{}
- , mChannels{ 0 } {
-}
-
-bool Image::InitFromImageFile(const char* filePath, int desiredChannels) {
- // Dimensions of the image in
- int width, height;
- // Number of channels that the image has, we'll get `desiredChannels` channels in our output (if it's non-0, which is the default argument)
- int channels;
-
- // NOTE: don't free, the data is passed to std::unique_ptr
- auto result = (uint8_t*)stbi_load(filePath, &width, &height, &channels, desiredChannels);
- if (!result) {
- return false;
- }
-
- mData.reset(result);
- mSize = { width, height };
- mChannels = desiredChannels == 0 ? channels : desiredChannels;
- return true;
-}
-
-bool Image::InitFromImageData(std::span<uint8_t> data, int desiredChannels) {
- int width, height;
- int channels;
-
- // NOTE: don't free, the data is passed to std::unique_ptr
- auto result = (uint8_t*)stbi_load_from_memory(data.data(), data.size(), &width, &height, &channels, desiredChannels);
- if (!result) {
- return false;
- }
-
- mData.reset(result);
- mSize = { width, height };
- mChannels = desiredChannels == 0 ? channels : desiredChannels;
- return true;
-}
-
-bool Image::InitFromPixels(std::span<uint8_t> pixels, glm::ivec2 dimensions, int channels) {
- mData = std::make_unique<uint8_t[]>(pixels.size());
- std::memcpy(mData.get(), pixels.data(), pixels.size());
- mSize = dimensions;
- mChannels = channels;
- return true;
-}
-
-bool Image::InitFromPixels(std::unique_ptr<uint8_t[]> pixels, glm::ivec2 dimensions, int channels) {
- mData = std::move(pixels);
- mSize = dimensions;
- mChannels = channels;
- return true;
-}
-
-RgbaColor Image::GetPixel(int x, int y) const {
- size_t offset = (y * mSize.x + x) * mChannels;
- RgbaColor color;
- color.r = mData.get()[offset + 0];
- color.g = mData.get()[offset + 1];
- color.b = mData.get()[offset + 2];
- color.a = mData.get()[offset + 3];
- return color;
-}
-
-void Image::SetPixel(int x, int y, RgbaColor color) {
- size_t offset = (y * mSize.x + x) * mChannels;
- mData.get()[offset + 0] = color.r;
- mData.get()[offset + 1] = color.g;
- mData.get()[offset + 2] = color.b;
- mData.get()[offset + 3] = color.a;
-}
-
-uint8_t* Image::GetDataPtr() const {
- return mData.get();
-}
-
-size_t Image::GetDataLength() const {
- return mSize.x * mSize.y * mChannels * sizeof(uint8_t);
-}
-
-std::span<uint8_t> Image::GetData() const {
- return { mData.get(), GetDataLength() };
-}
-
-glm::ivec2 Image::GetSize() const {
- return mSize;
-}
-
-int Image::GetChannels() const {
- return mChannels;
-}
-
-bool Image::IsEmpty() const {
- return mSize.x == 0 || mSize.y == 0;
-}
diff --git a/source/Game/Image.hpp b/source/Game/Image.hpp
deleted file mode 100644
index c577c24..0000000
--- a/source/Game/Image.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include "Color.hpp"
-#include "RcPtr.hpp"
-
-#include <cstdint>
-#include <glm/glm.hpp>
-#include <memory>
-#include <span>
-
-/// Image is a 2d array of pixels, stored as a continuous array in memory, with the first pixel
-/// being the top-left pixel. If a vertically flipped image data is needed, load using stb_image
-/// yourself, or flip the data here.
-class Image : public RefCounted {
-private:
- std::unique_ptr<uint8_t[]> mData;
- glm::ivec2 mSize;
- int mChannels;
-
-public:
- Image();
-
- bool InitFromImageFile(const char* filePath, int desiredChannels = 0);
- bool InitFromImageData(std::span<uint8_t> data, int desiredChannels = 0);
- bool InitFromPixels(std::span<uint8_t> pixels, glm::ivec2 dimensions, int channels);
- bool InitFromPixels(std::unique_ptr<uint8_t[]> pixels, glm::ivec2 dimensions, int channels);
-
- /// Get the pixel at the given location.
- RgbaColor GetPixel(int x, int y) const;
- void SetPixel(int x, int y, RgbaColor color);
-
- uint8_t* GetDataPtr() const;
- size_t GetDataLength() const;
- std::span<uint8_t> GetData() const;
- glm::ivec2 GetSize() const;
- int GetChannels() const;
- bool IsEmpty() const;
-};
diff --git a/source/Game/Ires.cpp b/source/Game/Ires.cpp
deleted file mode 100644
index bfa4cdf..0000000
--- a/source/Game/Ires.cpp
+++ /dev/null
@@ -1,409 +0,0 @@
-#include "Ires.hpp"
-
-#include "AppConfig.hpp"
-#include "EditorCore.hpp"
-#include "EditorUtils.hpp"
-#include "Material.hpp"
-#include "Shader.hpp"
-#include "Sprite.hpp"
-#include "Texture.hpp"
-
-#include <Macros.hpp>
-#include <Metadata.hpp>
-#include <RapidJsonHelper.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <imgui.h>
-#include <misc/cpp/imgui_stdlib.h>
-#include <rapidjson/document.h>
-#include <rapidjson/filereadstream.h>
-#include <rapidjson/filewritestream.h>
-#include <rapidjson/prettywriter.h>
-#include <rapidjson/writer.h>
-#include <algorithm>
-#include <cassert>
-
-namespace fs = std::filesystem;
-using namespace std::literals;
-
-IresObject::IresObject(Kind kind)
- : mKind{ kind } {
-}
-
-std::unique_ptr<IresObject> IresObject::Create(Kind kind) {
- switch (kind) {
- case KD_Texture: return std::make_unique<IresTexture>();
- case KD_Shader: return std::make_unique<IresShader>();
- case KD_Material: return std::make_unique<IresMaterial>();
- case KD_SpriteFiles: return std::make_unique<IresSpriteFiles>();
- case KD_Spritesheet: return std::make_unique<IresSpritesheet>();
- case KD_COUNT: break;
- }
- return nullptr;
-}
-
-bool IresObject::IsAnnoymous() const {
- return mName.empty();
-}
-
-void IresObject::SetName(std::string name) {
- if (mMan) {
- mMan->Rename(this, std::move(name));
- } else {
- mName = std::move(name);
- }
-}
-
-void IresObject::ShowNameSafe(IresObject* ires) {
- if (ires) {
- ires->ShowName();
- } else {
- ShowNameNull();
- }
-}
-
-void IresObject::ShowNameNull() {
- ImGui::Text("<null>");
-}
-
-void IresObject::ShowName() const {
- if (IsAnnoymous()) {
- ImGui::Text("<annoymous %p>", (void*)this);
- } else {
- ImGui::Text("%s", mName.c_str());
- }
-}
-
-void IresObject::ShowReferenceSafe(IEditor& editor, IresObject* ires) {
- if (ires) {
- ires->ShowReference(editor);
- } else {
- ShowReferenceNull(editor);
- }
-}
-
-void IresObject::ShowReferenceNull(IEditor& editor) {
- ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
- ImGui::Text("<null>");
- ImGui::PopStyleColor();
- ImGui::AddUnderLine(ImGui::GetStyle().Colors[ImGuiCol_Button]);
-}
-
-void IresObject::ShowReference(IEditor& editor) {
- ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
- if (IsAnnoymous()) {
- ImGui::Text("<annoymous %p>", (void*)this);
- } else {
- ImGui::Text("%s", mName.c_str());
- }
- ImGui::PopStyleColor();
- if (ImGui::IsItemHovered()) {
- if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
- editor.GetInspector().SelectTarget(IEditorInspector::ITT_Ires, this);
- }
- ImGui::AddUnderLine(ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
- } else {
- ImGui::AddUnderLine(ImGui::GetStyle().Colors[ImGuiCol_Button]);
- }
-}
-
-void IresObject::ShowEditor(IEditor& editor) {
- ImGui::Text("%.*s", PRINTF_STRING_VIEW(Metadata::EnumToString(mKind)));
-
- bool isAnnoymous = mName.empty();
- if (isAnnoymous) {
- ImGui::Text("Name: <annoymous: %p>", (void*)this);
- } else {
- ImGui::Text("Name: %s", mName.c_str());
- }
-
- if (mUid.IsNull()) {
- ImGui::TextUnformatted("Uid: <none>");
- } else {
- ImGui::Text("Uid: %lx-%lx", mUid.upper, mUid.lower);
- }
-}
-
-void IresObject::WriteFull(IresWritingContext& ctx, IresObject* ires, rapidjson::Value& value, rapidjson::Document& root) {
- rapidjson::Value rvIres(rapidjson::kObjectType);
- ires->Write(ctx, rvIres, root);
-
- value.AddMember("Type", rapidjson::StringRef(Metadata::EnumToString(ires->GetKind())), root.GetAllocator());
- value.AddMember("Uid", ires->mUid.Write(root), root.GetAllocator());
- value.AddMember("Value", rvIres, root.GetAllocator());
-}
-
-std::unique_ptr<IresObject> IresObject::ReadFull(IresLoadingContext& ctx, const rapidjson::Value& value) {
- auto ires = ReadBasic(value);
- if (!ires) {
- return nullptr;
- }
-
- if (!ReadPartial(ctx, ires.get(), value)) {
- return nullptr;
- }
-
- return ires;
-}
-
-std::unique_ptr<IresObject> IresObject::ReadBasic(const rapidjson::Value& value) {
- auto rvType = rapidjson::GetProperty(value, rapidjson::kStringType, "Type"sv);
- if (!rvType) return nullptr;
- auto kind = Metadata::EnumFromString<Kind>(rapidjson::AsStringView(*rvType));
- assert(kind.has_value());
- auto ires = Create(kind.value());
- if (!ires) return nullptr;
-
- auto rvUid = rapidjson::GetProperty(value, rapidjson::kArrayType, "Uid"sv);
- if (!rvUid) return nullptr;
- ires->mUid.Read(*rvUid);
-
- return ires;
-}
-
-bool IresObject::ReadPartial(IresLoadingContext& ctx, IresObject* ires, const rapidjson::Value& value) {
- auto rvValue = rapidjson::GetProperty(value, "Value"sv);
- if (!rvValue) return false;
- ires->Read(ctx, *rvValue);
- return true;
-}
-
-void IresObject::Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const {
-}
-
-void IresObject::Read(IresLoadingContext& ctx, const rapidjson::Value& value) {
-}
-
-void IresManager::DiscoverFilesDesignatedLocation() {
- auto path = AppConfig::assetDirPath / "Ires";
- DiscoverFiles(path);
-}
-
-void IresManager::DiscoverFiles(const fs::path& dir) {
- struct LoadCandidate {
- fs::path path;
- rapidjson::Document data;
- RcPtr<IresObject> ires;
- };
-
- class IresLoadTimeContext final : public IresLoadingContext {
- public:
- // NOTE: pointer stability required
- robin_hood::unordered_node_map<Uid, LoadCandidate> candidates;
- std::vector<std::vector<LoadCandidate*>> candidatesByKind;
-
- IresLoadTimeContext() {
- candidatesByKind.resize((int)IresObject::KD_COUNT);
- }
-
- std::vector<LoadCandidate*>& GetByKind(IresObject::Kind kind) {
- int i = static_cast<int>(kind);
- return candidatesByKind[i];
- }
-
- virtual IresObject* FindIres(const Uid& uid) const override {
- auto iter = candidates.find(uid);
- if (iter != candidates.end()) {
- auto& cand = iter->second;
- return cand.ires.Get();
- } else {
- return nullptr;
- }
- }
- } ctx;
-
- for (auto& item : fs::directory_iterator(dir)) {
- if (!item.is_regular_file()) {
- continue;
- }
- if (item.path().extension() != ".json") {
- continue;
- }
-
- auto file = Utils::OpenCstdioFile(item.path(), Utils::Read);
- if (!file) {
- fprintf(stderr, "Ires file [" PLATFORM_PATH_STR "] Failed to open file.", item.path().c_str());
- continue;
- }
- DEFER { fclose(file); };
-
- char readerBuffer[65536];
- rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer));
-
- rapidjson::Document root;
- root.ParseStream(stream);
-
- auto ires = IresObject::ReadBasic(root);
- if (!ires) {
- fprintf(stderr, "Ires file: [" PLATFORM_PATH_STR "] Failed to parse header.", item.path().c_str());
- continue;
- }
-
- // Load name from filename
- ires->mName = item.path().filename().replace_extension().string();
- auto& iresName = ires->mName;
-
- // Load uid should be handled by IresObject::ReadBasic
- assert(!ires->mUid.IsNull());
- auto iresUid = ires->GetUid();
-
- auto iresKind = ires->GetKind();
-
- auto&& [iter, DISCARD] = ctx.candidates.try_emplace(
- iresUid,
- LoadCandidate{
- .path = item.path(),
- .data = std::move(root),
- .ires = RcPtr(ires.release()),
- });
- auto& cand = iter->second;
-
- ctx.GetByKind(iresKind).push_back(&cand);
- }
-
- // Load Ires in order by type, there are dependencies between them
- // TODO full arbitary dependency between Ires
- for (int i = 0; i < (int)IresObject::KD_COUNT; ++i) {
- auto kind = static_cast<IresObject::Kind>(i);
- auto& list = ctx.GetByKind(kind);
- for (auto cand : list) {
- auto& ires = cand->ires;
-
- if (!IresObject::ReadPartial(ctx, ires.Get(), cand->data)) {
- fprintf(stderr, "Ires file: [" PLATFORM_PATH_STR "] Failed to parse object data.", cand->path.c_str());
- continue;
- }
-
- ires->mMan = this;
- mObjByUid.try_emplace(ires->GetUid(), ires);
- }
- }
-}
-
-std::pair<IresObject*, bool> IresManager::Add(IresObject* ires) {
- auto& name = ires->mName;
- if (name.empty()) {
- name = Utils::MakeRandomNumberedName(Metadata::EnumToString(ires->GetKind()).data());
- }
-
- auto& uid = ires->mUid;
- if (uid.IsNull()) {
- uid = Uid::Create();
- }
-
- auto [iter, inserted] = mObjByUid.try_emplace(uid, ires);
- if (inserted) {
- ires->mMan = this;
- // TODO handle full path
- return { ires, true };
- } else {
- return { iter->second.Get(), false };
- }
-}
-
-IresObject* IresManager::Load(const fs::path& filePath) {
- auto file = Utils::OpenCstdioFile(filePath, Utils::Read);
- if (!file) return nullptr;
- DEFER { fclose(file); };
-
- char readerBuffer[65536];
- rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer));
-
- rapidjson::Document root;
- root.ParseStream(stream);
-
- auto ires = IresObject::ReadFull(*this, root);
- if (!ires) {
- return nullptr;
- }
-
- // Load uid should be handled by IresObject::ReadFull
- assert(!ires->mUid.IsNull());
- // Load name from filename
- ires->mName = filePath.filename().replace_extension().string();
- Add(ires.get());
-
- return ires.release();
-}
-
-static fs::path GetDesignatedPath(IresObject* ires) {
- return AppConfig::assetDirPath / "Ires" / fs::path(ires->GetName()).replace_extension(".json");
-}
-
-void IresManager::Delete(IresObject* ires) {
- // TODO
-}
-
-bool IresManager::Rename(IresObject* ires, std::string newName) {
- auto oldPath = GetDesignatedPath(ires);
- ires->mName = std::move(newName);
- auto newPath = GetDesignatedPath(ires);
- if (fs::exists(oldPath)) {
- fs::rename(oldPath, newPath);
- }
-
- // TODO validate no name duplication
-#if 0
- if (mObjByPath.contains(newName)) {
- return false;
- }
-
- // Keep the material from being deleted, in case the old entry in map is the only one existing
- RcPtr rc(ires);
-
- // Remove old entry (must do before replacing Material::mName, because the std::string_view in the map is a reference to it)
- mObjByPath.erase(ires->GetName());
-
- // Add new entry
- ires->mName = std::move(newName);
- // TODO handle full path
- mObjByPath.try_emplace(ires->GetName(), ires);
-#endif
- return true;
-}
-
-void IresManager::Reload(IresObject* ires) {
- auto file = Utils::OpenCstdioFile(GetDesignatedPath(ires), Utils::Read);
- if (!file) return;
- DEFER { fclose(file); };
-
- char readerBuffer[65536];
- rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer));
-
- rapidjson::Document root;
- root.ParseStream(stream);
-
- IresObject::ReadPartial(*this, ires, root);
-}
-
-void IresManager::Save(IresObject* ires) {
- Save(ires, GetDesignatedPath(ires));
-}
-
-void IresManager::Save(IresObject* ires, const fs::path& filePath) {
- rapidjson::Document root(rapidjson::kObjectType);
-
- IresObject::WriteFull(*this, ires, root, root);
-
- auto file = Utils::OpenCstdioFile(filePath, Utils::WriteTruncate);
- if (!file) return;
- DEFER { fclose(file); };
-
- char writerBuffer[65536];
- rapidjson::FileWriteStream stream(file, writerBuffer, sizeof(writerBuffer));
- rapidjson::PrettyWriter<rapidjson::FileWriteStream> writer(stream);
- writer.SetFormatOptions(rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
- root.Accept(writer);
-}
-
-IresObject* IresManager::FindIres(const Uid& uid) const {
- auto iter = mObjByUid.find(uid);
- if (iter != mObjByUid.end()) {
- return iter->second.Get();
- } else {
- return nullptr;
- }
-}
-
-#include <generated/Ires.gs.inl>
diff --git a/source/Game/Ires.hpp b/source/Game/Ires.hpp
deleted file mode 100644
index b6420f3..0000000
--- a/source/Game/Ires.hpp
+++ /dev/null
@@ -1,125 +0,0 @@
-#pragma once
-
-#include "EditorAttachment.hpp"
-#include "EditorCore.hpp"
-
-#include <MacrosCodegen.hpp>
-#include <RcPtr.hpp>
-#include <Uid.hpp>
-#include <Utils.hpp>
-
-#include <rapidjson/fwd.h>
-#include <robin_hood.h>
-#include <filesystem>
-#include <memory>
-#include <string_view>
-
-// Forward declarations
-class IresManager;
-class IresWritingContext;
-class IresLoadingContext;
-
-namespace Tags {
-enum class IresObjectKind {
- KD_Texture,
- KD_Shader,
- KD_Material,
- KD_SpriteFiles,
- KD_Spritesheet,
- KD_COUNT,
-};
-BRUSSEL_ENUM(IresObjectKind, ToString FromString ExcludeHeuristics);
-} // namespace Tags
-
-class IresObject : public RefCounted {
- friend class IresManager;
-
-public:
- using Kind = Tags::IresObjectKind;
- using enum Tags::IresObjectKind;
-
-private:
- std::string mName; // Serialized as filename
- Uid mUid; // Serialized in full mode
- std::unique_ptr<EditorAttachment> mEditorAttachment; // Transient
- IresManager* mMan = nullptr; // Transient
- Kind mKind; // Serialized in full mode
-
-public:
- IresObject(Kind kind);
- virtual ~IresObject() = default;
-
- static std::unique_ptr<IresObject> Create(Kind kind);
- Kind GetKind() const { return mKind; }
-
- IresManager* GetAssociatedManager() const { return mMan; }
- bool IsAnnoymous() const;
- const std::string& GetName() const { return mName; }
- void SetName(std::string name);
- const Uid& GetUid() const { return mUid; }
-
- static void ShowNameSafe(IresObject* ires);
- static void ShowNameNull();
- void ShowName() const;
-
- static void ShowReferenceSafe(IEditor& editor, IresObject* ires);
- static void ShowReferenceNull(IEditor& editor);
- void ShowReference(IEditor& editor);
-
- virtual void ShowEditor(IEditor& editor);
-
- EditorAttachment* GetEditorAttachment() const { return mEditorAttachment.get(); }
- void SetEditorAttachment(EditorAttachment* attachment) { mEditorAttachment.reset(attachment); }
-
- static void WriteFull(IresWritingContext& ctx, IresObject* ires, rapidjson::Value& value, rapidjson::Document& root);
-
- /// Sequentially call ReadBasic() and then ReadPartial()
- static std::unique_ptr<IresObject> ReadFull(IresLoadingContext& ctx, const rapidjson::Value& value);
- /// Reads the type and UID of the object from data, and return a newly constructed Ires object.
- static std::unique_ptr<IresObject> ReadBasic(const rapidjson::Value& value);
- /// Reads all object-speific data from the root-level data object into the given Ires object.
- static bool ReadPartial(IresLoadingContext& ctx, IresObject* ires, const rapidjson::Value& value);
-
- virtual void Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const;
- virtual void Read(IresLoadingContext& ctx, const rapidjson::Value& value);
-
-protected:
- rapidjson::Value WriteIresRef(IresObject* object);
-};
-
-class IresWritingContext {
-public:
- virtual ~IresWritingContext() = default;
-};
-
-class IresLoadingContext {
-public:
- virtual ~IresLoadingContext() = default;
- virtual IresObject* FindIres(const Uid& uid) const = 0;
-};
-
-class IresManager final : public IresWritingContext, public IresLoadingContext {
-public:
- static inline IresManager* instance = nullptr;
-
-private:
- robin_hood::unordered_map<Uid, RcPtr<IresObject>> mObjByUid;
-
-public:
- void DiscoverFilesDesignatedLocation();
- void DiscoverFiles(const std::filesystem::path& dir);
-
- std::pair<IresObject*, bool> Add(IresObject* mat);
- IresObject* Load(const std::filesystem::path& filePath);
- void Delete(IresObject* ires);
- bool Rename(IresObject* ires, std::string newName);
-
- void Reload(IresObject* ires);
- void Save(IresObject* ires);
- void Save(IresObject* ires, const std::filesystem::path& filePath);
-
- const auto& GetObjects() const { return mObjByUid; }
- virtual IresObject* FindIres(const Uid& uid) const override;
-};
-
-#include <generated/Ires.gh.inl>
diff --git a/source/Game/Level.cpp b/source/Game/Level.cpp
deleted file mode 100644
index 076e5d5..0000000
--- a/source/Game/Level.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-#include "Level.hpp"
-
-#include "AppConfig.hpp"
-
-#include <PodVector.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/prettywriter.h>
-#include <cstdio>
-#include <filesystem>
-
-using namespace std::literals;
-namespace fs = std::filesystem;
-
-constexpr auto kParentToRootObject = std::numeric_limits<size_t>::max();
-constexpr auto kInvalidEntryId = std::numeric_limits<size_t>::max();
-
-struct Level::InstanciationEntry {
- // If set to std::numeric_limits<size_t>::max(), this object is parented to the "root" provided when instanciating
- size_t parentId;
- rapidjson::Document data;
-};
-
-Level::Level() = default;
-
-Level::~Level() = default;
-
-void Level::Instanciate(GameObject* relRoot) const {
- auto objectsLut = std::make_unique<GameObject*[]>(mEntries.size());
- for (auto& entry : mEntries) {
- GameObject* parent;
- if (entry.parentId == kParentToRootObject) {
- parent = relRoot;
- } else {
- parent = objectsLut[entry.parentId];
- }
-
- // TODO deser object
- }
-}
-
-void Level::ShowInstanciationEntries(IEditor& editor) {
- for (auto& entry : mEntries) {
- // TODO
- }
-}
-
-void LevelManager::DiscoverFilesDesignatedLocation() {
- auto path = AppConfig::assetDirPath / "Levels";
- DiscoverFiles(path);
-}
-
-void LevelManager::DiscoverFiles(const std::filesystem::path& dir) {
- for (auto& item : fs::directory_iterator(dir)) {
- auto& path = item.path();
- if (!item.is_regular_file()) {
- continue;
- }
- if (path.extension() != ".json") {
- continue;
- }
-
- // Parse uid from filename, map key
- Uid uid;
- uid.ReadString(path.filename().string());
-
- // Map value
- LoadableObject obj;
- obj.filePath = path;
-
- mObjByUid.try_emplace(uid, std::move(obj));
- }
-}
-
-Level* LevelManager::FindLevel(const Uid& uid) const {
- auto iter = mObjByUid.find(uid);
- if (iter != mObjByUid.end()) {
- return iter->second.level.Get();
- } else {
- return nullptr;
- }
-}
-
-#define BRUSSEL_DEF_LEVEL_NAME "New Level"
-#define BRUSSEL_DEF_LEVEL_DESC "No description."
-
-Level* LevelManager::LoadLevel(const Uid& uid) {
- auto iter = mObjByUid.find(uid);
- if (iter != mObjByUid.end()) {
- auto& ldObj = iter->second;
- if (ldObj.level != nullptr) {
- auto file = Utils::OpenCstdioFile(ldObj.filePath, Utils::Read, false);
- if (!file) {
- fprintf(stderr, "Cannot open file level file %s that was discovered on game startup.", ldObj.filePath.string().c_str());
- return nullptr;
- }
- DEFER { fclose(file); };
-
- char readerBuffer[65536];
- rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer));
-
- rapidjson::Document root;
- root.ParseStream(stream);
-
- Level* level;
- ldObj.level.Attach(level = new Level());
-
- level->mMan = this;
- level->mUid = uid;
-
-#if defined(BRUSSEL_DEV_ENV)
- BRUSSEL_JSON_GET_DEFAULT(root, "Name", std::string, ldObj.name, BRUSSEL_DEF_LEVEL_NAME);
- BRUSSEL_JSON_GET_DEFAULT(root, "Description", std::string, ldObj.description, BRUSSEL_DEF_LEVEL_DESC)
-#endif
-
- auto rvEntries = rapidjson::GetProperty(root, rapidjson::kArrayType, "DataEntries"sv);
- if (!rvEntries) return nullptr;
- for (auto iter = rvEntries->Begin(); iter != rvEntries->End(); ++iter) {
- Level::InstanciationEntry entry;
-
- BRUSSEL_JSON_GET_DEFAULT(*iter, "ParentId", int, entry.parentId, kInvalidEntryId);
-
- auto rvDataEntry = rapidjson::GetProperty(*iter, "Data"sv);
- if (!rvDataEntry) return nullptr;
- entry.data.CopyFrom(*iter, entry.data.GetAllocator());
-
- level->mEntries.push_back(std::move(entry));
- }
- }
- return ldObj.level.Get();
- } else {
- return nullptr;
- }
-}
-
-void LevelManager::PrepareLevel(const Uid& uid) {
- // TODO
-}
-
-LevelManager::LoadableObject& LevelManager::AddLevel(const Uid& uid) {
- auto&& [iter, inserted] = mObjByUid.try_emplace(uid);
- auto& ldObj = iter->second;
- ldObj.level->mUid = uid;
-#if defined(BRUSSEL_DEV_ENV)
- ldObj.name = BRUSSEL_DEF_LEVEL_NAME;
- ldObj.description = BRUSSEL_DEF_LEVEL_DESC;
-#endif
- return ldObj;
-}
-
-void LevelManager::SaveLevel(const Uid& uid) const {
- auto iter = mObjByUid.find(uid);
- if (iter == mObjByUid.end()) return;
- auto& obj = iter->second;
-
- SaveLevelImpl(obj, obj.filePath);
-}
-
-void LevelManager::SaveLevel(const Uid& uid, const std::filesystem::path& path) const {
- auto iter = mObjByUid.find(uid);
- if (iter == mObjByUid.end()) return;
- auto& obj = iter->second;
-
- SaveLevelImpl(obj, path);
-}
-
-void LevelManager::SaveLevelImpl(const LoadableObject& obj, const std::filesystem::path& path) const {
- rapidjson::Document root;
-
- // TODO
-
- auto file = Utils::OpenCstdioFile(path, Utils::WriteTruncate);
- if (!file) return;
- DEFER { fclose(file); };
-
- char writerBuffer[65536];
- rapidjson::FileWriteStream stream(file, writerBuffer, sizeof(writerBuffer));
- rapidjson::Writer<rapidjson::FileWriteStream> writer(stream);
- root.Accept(writer);
-}
-
-LevelWrapperObject::LevelWrapperObject(GameWorld* world)
- : GameObject(KD_LevelWrapper, world) //
-{
- mStopFreePropagation = true;
-}
-
-LevelWrapperObject::~LevelWrapperObject() {
- // Destruction/freeing of this object is handled by our parent
- for (auto child : GetChildren()) {
- FreeRecursive(child);
- }
-}
-
-void LevelWrapperObject::SetBoundLevel(Level* level) {
- if (mLevel != level) {
- mLevel.Attach(level);
-
- // Cleanup old children
- // TODO needs to Resleep()?
- auto children = RemoveAllChildren();
- for (auto child : children) {
- FreeRecursive(child);
- }
- }
-
- level->Instanciate(this);
-
- PodVector<GameObject*> stack;
- stack.push_back(this);
-
- while (!stack.empty()) {
- auto obj = stack.back();
- stack.pop_back();
-
- for (auto child : obj->GetChildren()) {
- stack.push_back(child);
- }
-
- obj->Awaken();
- }
-}
diff --git a/source/Game/Level.hpp b/source/Game/Level.hpp
deleted file mode 100644
index 9114a64..0000000
--- a/source/Game/Level.hpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#pragma once
-
-#include "EditorCore.hpp"
-#include "GameObject.hpp"
-
-#include <RcPtr.hpp>
-#include <Uid.hpp>
-
-#include <robin_hood.h>
-#include <filesystem>
-#include <vector>
-
-// Forward declarations
-class Level;
-class LevelManager;
-
-/// Represents a seralized GameObject tree.
-class Level : public RefCounted {
- friend class LevelManager;
-
-private:
- struct InstanciationEntry;
-
- LevelManager* mMan;
- Uid mUid;
- std::vector<InstanciationEntry> mEntries;
-
-public:
- Level();
- ~Level();
-
- void Instanciate(GameObject* relRoot) const;
-
- LevelManager* GetLinkedLevelManager() const { return mMan; }
- const Uid& GetUid() const { return mUid; }
-
- // Editor stuff
- void ShowInstanciationEntries(IEditor& editor);
-};
-
-class LevelManager {
-public:
- static inline LevelManager* instance = nullptr;
-
-public: // NOTE: public for the editor; actual game components should not modify the map using this
- // TODO maybe cut this struct to only the first RcPtr<Level> field in release mode?
- struct LoadableObject {
- RcPtr<Level> level; // TODO make weak pointer
- std::filesystem::path filePath;
- // NOTE: these fields are only loaded in dev mode
- std::string name;
- std::string description;
-
- // Editor book keeping fields
- bool edited = false;
- };
- // We want pointer stability here for the editor (inspector object)
- robin_hood::unordered_node_map<Uid, LoadableObject> mObjByUid;
-
-public:
- void DiscoverFilesDesignatedLocation();
- void DiscoverFiles(const std::filesystem::path& dir);
-
- Level* FindLevel(const Uid& uid) const;
- /// Get or load the given level
- Level* LoadLevel(const Uid& uid);
- /// Send the given level to be loaded on another thread
- void PrepareLevel(const Uid& uid);
-
- /// Create and add a new level object with the given uid.
- /// Should only be used by the editor.
- LoadableObject& AddLevel(const Uid& uid);
- /// Should only be used by the editor.
- void SaveLevel(const Uid& uid) const;
- /// Should only be used by the editor.
- void SaveLevel(const Uid& uid, const std::filesystem::path& path) const;
-
-private:
- void SaveLevelImpl(const LoadableObject& obj, const std::filesystem::path& path) const;
-};
-
-class LevelWrapperObject : public GameObject {
-private:
- RcPtr<Level> mLevel;
-
-public:
- LevelWrapperObject(GameWorld* world);
- ~LevelWrapperObject() override;
-
- Level* GetBoundLevel() const;
- void SetBoundLevel(Level* level);
-};
diff --git a/source/Game/Material.cpp b/source/Game/Material.cpp
deleted file mode 100644
index 9b0c42d..0000000
--- a/source/Game/Material.cpp
+++ /dev/null
@@ -1,526 +0,0 @@
-#include "Material.hpp"
-
-#include "AppConfig.hpp"
-#include "EditorCore.hpp"
-#include "EditorUtils.hpp"
-
-#include <Metadata.hpp>
-#include <RapidJsonHelper.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <imgui.h>
-#include <rapidjson/document.h>
-#include <cstdlib>
-#include <cstring>
-#include <utility>
-
-using namespace std::literals;
-
-Material::Material() {
-}
-
-namespace ProjectBrussel_UNITY_ID {
-bool TryFindShaderId(Shader* shader, std::string_view name, int& out) {
- auto& info = shader->GetInfo();
- auto iter = info.things.find(name);
- if (iter == info.things.end()) return false;
- auto& id = iter->second;
-
- if (id.kind != ShaderThingId::KD_Uniform) return false;
-
- out = id.index;
- return true;
-}
-
-template <class TUniform>
-TUniform& ObtainUniform(Shader* shader, const char* name, std::vector<TUniform>& uniforms, GLint location) {
- for (auto& uniform : uniforms) {
- if (uniform.location == location) {
- return uniform;
- }
- }
-
- auto& uniform = uniforms.emplace_back();
- uniform.location = location;
- if (!TryFindShaderId(shader, name, uniform.infoUniformIndex)) {
- uniform.infoUniformIndex = -1;
- }
-
- return uniform;
-}
-
-rapidjson::Value MakeVectorJson(const Material::VectorUniform& vector, rapidjson::Document& root) {
- int len = vector.actualLength;
-
- rapidjson::Value result(rapidjson::kArrayType);
- result.Reserve(len, root.GetAllocator());
-
- for (int i = 0; i < len; ++i) {
- result.PushBack(vector.value[i], root.GetAllocator());
- }
-
- return result;
-}
-
-Material::VectorUniform ReadVectorFromJson(const rapidjson::Value& rv) {
- assert(rv.IsArray());
- Material::VectorUniform result;
- int len = result.actualLength = rv.Size();
- for (int i = 0; i < len; ++i) {
- result.value[i] = rv[i].GetFloat();
- }
- return result;
-}
-
-rapidjson::Value MakeMatrixJson(const Material::MatrixUniform& matrix, rapidjson::Document& root) {
- int w = matrix.actualWidth;
- int h = matrix.actualHeight;
-
- rapidjson::Value result(rapidjson::kArrayType);
- result.Reserve(h, root.GetAllocator());
-
- for (int y = 0; y < h; ++y) {
- rapidjson::Value row(rapidjson::kArrayType);
- row.Reserve(w, root.GetAllocator());
-
- for (int x = 0; x < w; ++x) {
- // Each item in a column is consecutive in memory in glm::mat<> structs
- row.PushBack(matrix.value[x * h + y], root.GetAllocator());
- }
-
- result.PushBack(row, root.GetAllocator());
- }
-
- return result;
-}
-
-Material::MatrixUniform ReadMatrixFromjson(const rapidjson::Value& rv) {
- assert(rv.IsArray());
- assert(rv.Size() > 0);
- assert(rv[0].IsArray());
- Material::MatrixUniform result;
- int w = result.actualWidth = rv[0].Size();
- int h = result.actualHeight = rv.Size();
- for (int y = 0; y < h; ++y) {
- auto& row = rv[y];
- assert(row.IsArray());
- assert(row.Size() == w);
- for (int x = 0; x < w; ++x) {
- auto& val = row[x];
- assert(val.IsNumber());
- result.value[x * h + y] = val.GetFloat();
- }
- }
- return result;
-}
-} // namespace ProjectBrussel_UNITY_ID
-
-void Material::SetFloat(const char* name, float value) {
- assert(IsValid());
-
- GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundScalars, location);
- uniform.floatValue = value;
- uniform.actualType = GL_FLOAT;
-}
-
-void Material::SetInt(const char* name, int32_t value) {
- assert(IsValid());
-
- GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundScalars, location);
- uniform.intValue = value;
- uniform.actualType = GL_INT;
-}
-
-void Material::SetUInt(const char* name, uint32_t value) {
- assert(IsValid());
-
- GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundScalars, location);
- uniform.uintValue = value;
- uniform.actualType = GL_UNSIGNED_INT;
-}
-
-template <int length>
-void Material::SetVector(const char* name, const glm::vec<length, float>& vec) {
- assert(IsValid());
-
- static_assert(length >= 1 && length <= 4);
-
- GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundVectors, location);
- uniform.actualLength = length;
- std::memset(uniform.value, 0, sizeof(uniform.value));
- std::memcpy(uniform.value, &vec[0], length * sizeof(float));
-}
-
-template void Material::SetVector<1>(const char*, const glm::vec<1, float>&);
-template void Material::SetVector<2>(const char*, const glm::vec<2, float>&);
-template void Material::SetVector<3>(const char*, const glm::vec<3, float>&);
-template void Material::SetVector<4>(const char*, const glm::vec<4, float>&);
-
-template <int width, int height>
-void Material::SetMatrix(const char* name, const glm::mat<width, height, float>& mat) {
- static_assert(width >= 1 && width <= 4);
- static_assert(height >= 1 && height <= 4);
-
- GLint location = glGetUniformLocation(mShader->GetProgram(), name);
- auto& uniform = ProjectBrussel_UNITY_ID::ObtainUniform(mShader.Get(), name, mBoundMatrices, location);
- uniform.actualWidth = width;
- uniform.actualHeight = height;
- std::memset(uniform.value, 0, sizeof(uniform.value));
- std::memcpy(uniform.value, &mat[0][0], width * height * sizeof(float));
-}
-
-template void Material::SetMatrix<2, 2>(const char*, const glm::mat<2, 2, float>&);
-template void Material::SetMatrix<3, 3>(const char*, const glm::mat<3, 3, float>&);
-template void Material::SetMatrix<4, 4>(const char*, const glm::mat<4, 4, float>&);
-
-template void Material::SetMatrix<2, 3>(const char*, const glm::mat<2, 3, float>&);
-template void Material::SetMatrix<3, 2>(const char*, const glm::mat<3, 2, float>&);
-
-template void Material::SetMatrix<2, 4>(const char*, const glm::mat<2, 4, float>&);
-template void Material::SetMatrix<4, 2>(const char*, const glm::mat<4, 2, float>&);
-
-template void Material::SetMatrix<3, 4>(const char*, const glm::mat<3, 4, float>&);
-template void Material::SetMatrix<4, 3>(const char*, const glm::mat<4, 3, float>&);
-
-void Material::SetTexture(const char* name, Texture* texture) {
- assert(IsValid());
-
- GLint location = glGetUniformLocation(mShader->GetProgram(), name);
-
- for (auto& uniform : mBoundTextures) {
- if (uniform.location == location) {
- uniform.value.Attach(texture);
- return;
- }
- }
-
- auto& uniform = mBoundTextures.emplace_back();
- uniform.value.Attach(texture);
- uniform.location = location;
-}
-
-std::span<const Material::VectorUniform> Material::GetVectors() const {
- return mBoundVectors;
-}
-
-std::span<const Material::MatrixUniform> Material::GetMatrices() const {
- return mBoundMatrices;
-}
-
-std::span<const Material::TextureUniform> Material::GetTextures() const {
- return mBoundTextures;
-}
-
-Shader* Material::GetShader() const {
- return mShader.Get();
-}
-
-void Material::SetShader(Shader* shader) {
- mShader.Attach(shader);
- auto& info = shader->GetInfo();
-
- mBoundScalars.clear();
- mBoundVectors.clear();
- mBoundMatrices.clear();
- mBoundTextures.clear();
- for (int i = 0; i < info.uniforms.size(); ++i) {
- auto& decl = info.uniforms[i];
- switch (decl->kind) {
- case ShaderVariable::KD_Math: {
- auto& mathDecl = static_cast<ShaderMathVariable&>(*decl);
- if (mathDecl.width == 1) {
- if (mathDecl.height == 1) {
- // Scalar
- auto& scalar = mBoundScalars.emplace_back();
- scalar.location = decl->location;
- scalar.infoUniformIndex = i;
- } else {
- // Vector
- auto& vec = mBoundVectors.emplace_back();
- vec.location = decl->location;
- vec.infoUniformIndex = i;
- vec.actualLength = mathDecl.height;
- }
- } else {
- // Matrix
- auto& mat = mBoundMatrices.emplace_back();
- mat.location = decl->location;
- mat.infoUniformIndex = i;
- mat.actualWidth = mathDecl.width;
- mat.actualHeight = mathDecl.height;
- }
- } break;
-
- case ShaderVariable::KD_Sampler: {
- auto& uniform = mBoundTextures.emplace_back();
- uniform.location = decl->location;
- uniform.infoUniformIndex = i;
- } break;
- }
- }
-}
-
-bool Material::IsValid() const {
- return mShader != nullptr;
-}
-
-static constexpr int IdentifyMatrixSize(int width, int height) {
- return width * 10 + height;
-}
-
-void Material::UseUniforms() const {
- for (auto& uniform : mBoundScalars) {
- switch (uniform.actualType) {
- case GL_FLOAT: glUniform1f(uniform.location, uniform.intValue); break;
- case GL_INT: glUniform1i(uniform.location, uniform.intValue); break;
- case GL_UNSIGNED_INT: glUniform1ui(uniform.location, uniform.intValue); break;
- default: break;
- }
- }
-
- for (auto& uniform : mBoundVectors) {
- switch (uniform.actualLength) {
- case 1: glUniform1fv(uniform.location, 1, &uniform.value[0]); break;
- case 2: glUniform2fv(uniform.location, 1, &uniform.value[0]); break;
- case 3: glUniform3fv(uniform.location, 1, &uniform.value[0]); break;
- case 4: glUniform4fv(uniform.location, 1, &uniform.value[0]); break;
- default: break;
- }
- }
-
- for (auto& uniform : mBoundMatrices) {
- switch (IdentifyMatrixSize(uniform.actualWidth, uniform.actualHeight)) {
- case IdentifyMatrixSize(2, 2): glUniformMatrix2fv(uniform.location, 1, GL_FALSE, uniform.value); break;
- case IdentifyMatrixSize(3, 3): glUniformMatrix3fv(uniform.location, 1, GL_FALSE, uniform.value); break;
- case IdentifyMatrixSize(4, 4): glUniformMatrix4fv(uniform.location, 1, GL_FALSE, uniform.value); break;
-
- case IdentifyMatrixSize(2, 3): glUniformMatrix2x3fv(uniform.location, 1, GL_FALSE, uniform.value); break;
- case IdentifyMatrixSize(3, 2): glUniformMatrix3x2fv(uniform.location, 1, GL_FALSE, uniform.value); break;
-
- case IdentifyMatrixSize(2, 4): glUniformMatrix2x4fv(uniform.location, 1, GL_FALSE, uniform.value); break;
- case IdentifyMatrixSize(4, 2): glUniformMatrix4x2fv(uniform.location, 1, GL_FALSE, uniform.value); break;
-
- case IdentifyMatrixSize(3, 4): glUniformMatrix3x4fv(uniform.location, 1, GL_FALSE, uniform.value); break;
- case IdentifyMatrixSize(4, 3): glUniformMatrix4x3fv(uniform.location, 1, GL_FALSE, uniform.value); break;
-
- default: break;
- }
- }
-
- int i = 0;
- for (auto& uniform : mBoundTextures) {
- glActiveTexture(GL_TEXTURE0 + i);
- glBindTexture(GL_TEXTURE_2D, uniform.value->GetHandle());
- glUniform1i(uniform.location, i);
- ++i;
- }
-}
-
-IresMaterial::IresMaterial()
- : IresObject(KD_Material)
- , mInstance(new Material()) {
- mInstance->mIres = this;
-}
-
-Material* IresMaterial::GetInstance() const {
- return mInstance.Get();
-}
-
-void IresMaterial::InvalidateInstance() {
- if (mInstance != nullptr) {
- mInstance->mIres = nullptr;
- }
- mInstance.Attach(new Material());
- mInstance->mIres = this;
-}
-
-void IresMaterial::ShowEditor(IEditor& editor) {
- using namespace Tags;
-
- IresObject::ShowEditor(editor);
-
- auto shader = mInstance->GetShader();
- if (shader) {
- shader->GetIres()->ShowReference(editor);
- } else {
- IresObject::ShowReferenceNull(editor);
- }
- if (ImGui::BeginDragDropTarget()) {
- if (auto payload = ImGui::AcceptDragDropPayload(Metadata::EnumToString(KD_Shader).data())) {
- auto shader = *static_cast<IresShader* const*>(payload->Data);
- mInstance->SetShader(shader->GetInstance());
- }
- ImGui::EndDragDropTarget();
- }
-
- if (!shader) return;
- auto& shaderInfo = shader->GetInfo();
- auto shaderIres = shader->GetIres();
-
- for (auto& field : mInstance->mBoundScalars) {
- auto& decl = static_cast<ShaderMathVariable&>(*shaderInfo.uniforms[field.infoUniformIndex]);
- decl.ShowInfo();
-
- ImGui::Indent();
- switch (decl.scalarType) {
- case GL_FLOAT: ImGui::InputFloat("##", &field.floatValue); break;
- case GL_INT: ImGui::InputInt("##", &field.intValue); break;
- // TODO proper uint edit?
- case GL_UNSIGNED_INT: ImGui::InputInt("##", (int32_t*)(&field.uintValue), 0, std::numeric_limits<int32_t>::max()); break;
- default: ImGui::TextUnformatted("Unsupported scalar type"); break;
- }
- ImGui::Unindent();
- }
- for (auto& field : mInstance->mBoundVectors) {
- auto& decl = static_cast<ShaderMathVariable&>(*shaderInfo.uniforms[field.infoUniformIndex]);
- decl.ShowInfo();
-
- ImGui::Indent();
- switch (decl.semantic) {
- case VES_Color1:
- case VES_Color2: {
- ImGui::ColorEdit4("##", field.value);
- } break;
-
- default: {
- ImGui::InputFloat4("##", field.value);
- } break;
- }
- ImGui::Unindent();
- }
- for (auto& field : mInstance->mBoundMatrices) {
- auto& decl = static_cast<ShaderMathVariable&>(*shaderInfo.uniforms[field.infoUniformIndex]);
- decl.ShowInfo();
-
- // TODO
- }
- for (auto& field : mInstance->mBoundTextures) {
- auto& decl = static_cast<ShaderSamplerVariable&>(*shaderInfo.uniforms[field.infoUniformIndex]);
- decl.ShowInfo();
-
- // TODO
- }
-}
-
-void IresMaterial::Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const {
- using namespace ProjectBrussel_UNITY_ID;
-
- IresObject::Write(ctx, value, root);
-
- if (!mInstance->IsValid()) {
- return;
- }
-
- auto& shaderInfo = mInstance->mShader->GetInfo();
- auto shaderUid = mInstance->mShader->GetIres()->GetUid();
- value.AddMember("Shader", shaderUid.Write(root), root.GetAllocator());
-
- rapidjson::Value fields(rapidjson::kArrayType);
- for (auto& scalar : mInstance->mBoundScalars) {
- rapidjson::Value rvField(rapidjson::kObjectType);
- rvField.AddMember("Name", shaderInfo.uniforms[scalar.infoUniformIndex]->name, root.GetAllocator());
- rvField.AddMember("Type", "Scalar", root.GetAllocator());
- switch (scalar.actualType) {
- case GL_FLOAT: rvField.AddMember("Value", scalar.floatValue, root.GetAllocator()); break;
- case GL_INT: rvField.AddMember("Value", scalar.intValue, root.GetAllocator()); break;
- case GL_UNSIGNED_INT: rvField.AddMember("Value", scalar.uintValue, root.GetAllocator()); break;
- }
- fields.PushBack(rvField, root.GetAllocator());
- }
- for (auto& vector : mInstance->mBoundVectors) {
- rapidjson::Value rvField(rapidjson::kObjectType);
- rvField.AddMember("Name", shaderInfo.uniforms[vector.infoUniformIndex]->name, root.GetAllocator());
- rvField.AddMember("Type", "Vector", root.GetAllocator());
- rvField.AddMember("Value", MakeVectorJson(vector, root).Move(), root.GetAllocator());
- fields.PushBack(rvField, root.GetAllocator());
- }
- for (auto& matrix : mInstance->mBoundMatrices) {
- rapidjson::Value rvField(rapidjson::kObjectType);
- rvField.AddMember("Name", shaderInfo.uniforms[matrix.infoUniformIndex]->name, root.GetAllocator());
- rvField.AddMember("Type", "Matrix", root.GetAllocator());
- rvField.AddMember("Value", MakeMatrixJson(matrix, root).Move(), root.GetAllocator());
- fields.PushBack(rvField, root.GetAllocator());
- }
- for (auto& texture : mInstance->mBoundTextures) {
- // TODO
- }
- value.AddMember("Fields", fields, root.GetAllocator());
-}
-
-void IresMaterial::Read(IresLoadingContext& ctx, const rapidjson::Value& value) {
- using namespace ProjectBrussel_UNITY_ID;
-
- IresObject::Read(ctx, value);
-
- {
- auto rvShader = rapidjson::GetProperty(value, "Shader"sv);
- if (!rvShader) return;
-
- Uid uid;
- uid.Read(*rvShader);
-
- auto ires = ctx.FindIres(uid);
- if (!ires) return;
- if (ires->GetKind() != KD_Shader) return;
- auto shader = static_cast<IresShader*>(ires);
-
- mInstance->mShader.Attach(shader->GetInstance());
- }
- auto shader = mInstance->mShader.Get();
- auto& shaderInfo = shader->GetInfo();
-
- auto fields = rapidjson::GetProperty(value, rapidjson::kArrayType, "Fields"sv);
- if (!fields) return;
-
- for (auto& rvField : fields->GetArray()) {
- if (!rvField.IsObject()) continue;
-
- auto rvName = rapidjson::GetProperty(rvField, rapidjson::kStringType, "Name"sv);
-
- auto rvType = rapidjson::GetProperty(rvField, rapidjson::kStringType, "Type"sv);
- if (!rvType) continue;
- auto type = rapidjson::AsStringView(*rvType);
-
- auto rvValue = rapidjson::GetProperty(rvField, "Value"sv);
-
- if (type == "Scalar"sv) {
- Material::ScalarUniform uniform;
- if (rvName) {
- TryFindShaderId(shader, rapidjson::AsStringView(*rvName), uniform.infoUniformIndex);
- uniform.location = shaderInfo.uniforms[uniform.infoUniformIndex]->location;
- }
- if (rvValue->IsFloat()) {
- uniform.actualType = GL_FLOAT;
- uniform.floatValue = rvValue->GetFloat();
- } else if (rvValue->IsInt()) {
- uniform.actualType = GL_INT;
- uniform.intValue = rvValue->GetInt();
- } else if (rvValue->IsUint()) {
- uniform.actualType = GL_UNSIGNED_INT;
- uniform.uintValue = rvValue->GetUint();
- }
- mInstance->mBoundScalars.push_back(std::move(uniform));
- } else if (type == "Vector"sv) {
- auto uniform = ReadVectorFromJson(*rvValue);
- if (rvName) {
- TryFindShaderId(shader, rapidjson::AsStringView(*rvName), uniform.infoUniformIndex);
- uniform.location = shaderInfo.uniforms[uniform.infoUniformIndex]->location;
- }
- mInstance->mBoundVectors.push_back(std::move(uniform));
- } else if (type == "Matrix"sv) {
- auto uniform = ReadMatrixFromjson(*rvValue);
- if (rvName) {
- TryFindShaderId(shader, rapidjson::AsStringView(*rvName), uniform.infoUniformIndex);
- uniform.location = shaderInfo.uniforms[uniform.infoUniformIndex]->location;
- }
- mInstance->mBoundMatrices.push_back(uniform);
- } else if (type == "Texture"sv) {
- // TODO
- }
- }
-}
diff --git a/source/Game/Material.hpp b/source/Game/Material.hpp
deleted file mode 100644
index f1cd7dd..0000000
--- a/source/Game/Material.hpp
+++ /dev/null
@@ -1,125 +0,0 @@
-#pragma once
-
-#include "Ires.hpp"
-#include "RcPtr.hpp"
-#include "Shader.hpp"
-#include "Texture.hpp"
-
-#include <glad/glad.h>
-#include <robin_hood.h>
-#include <cstddef>
-#include <cstdint>
-#include <glm/glm.hpp>
-#include <memory>
-#include <span>
-#include <string_view>
-#include <vector>
-
-// Forward declarations
-class Material;
-class IresMaterial;
-
-class Material : public RefCounted {
- friend class IresMaterial;
-
-public:
- // NOTE: specialize between scalar vs matrix vs vector to save memory
-
- enum UniformType : uint16_t {
- UT_Scalar,
- UT_Vector,
- UT_Matrix,
- };
-
- struct UniformIndex {
- UniformType type;
- uint16_t index;
- };
-
- struct ScalarUniform {
- union {
- float floatValue;
- int32_t intValue;
- uint32_t uintValue;
- };
- GLenum actualType;
- /* Transient */ int infoUniformIndex;
- /* Transient */ GLint location;
- };
-
- struct VectorUniform {
- float value[4];
- int actualLength;
- /* Transient */ int infoUniformIndex;
- /* Transient */ GLint location;
- };
-
- struct MatrixUniform {
- float value[16];
- int actualWidth;
- int actualHeight;
- /* Transient */ int infoUniformIndex;
- /* Transient */ GLint location;
- };
-
- struct TextureUniform {
- RcPtr<Texture> value;
- /* Transient */ int infoUniformIndex;
- /* Transient */ GLint location;
- };
-
- IresMaterial* mIres = nullptr;
- RcPtr<Shader> mShader;
- std::vector<ScalarUniform> mBoundScalars;
- std::vector<VectorUniform> mBoundVectors;
- std::vector<MatrixUniform> mBoundMatrices;
- std::vector<TextureUniform> mBoundTextures;
-
-public:
- Material();
-
- void SetFloat(const char* name, float value);
- void SetInt(const char* name, int32_t value);
- void SetUInt(const char* name, uint32_t value);
-
- /// Instanciated for length == 1, 2, 3, 4
- template <int length>
- void SetVector(const char* name, const glm::vec<length, float>& vec);
-
- /// Instanciated for sizes (2,2) (3,3) (4,4) (2,3) (3,2) (2,4) (4,2) (3,4) (4,3)
- template <int width, int height>
- void SetMatrix(const char* name, const glm::mat<width, height, float>& mat);
-
- void SetTexture(const char* name, Texture* texture);
-
- std::span<const VectorUniform> GetVectors() const;
- std::span<const MatrixUniform> GetMatrices() const;
- std::span<const TextureUniform> GetTextures() const;
- Shader* GetShader() const;
- void SetShader(Shader* shader);
-
- IresMaterial* GetIres() const { return mIres; }
-
- bool IsValid() const;
-
- void UseUniforms() const;
-};
-
-// Initialized in main()
-inline RcPtr<Material> gDefaultMaterial;
-
-class IresMaterial : public IresObject {
-private:
- RcPtr<Material> mInstance;
-
-public:
- IresMaterial();
-
- Material* GetInstance() const;
- void InvalidateInstance();
-
- void ShowEditor(IEditor& editor) override;
-
- void Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const override;
- void Read(IresLoadingContext& ctx, const rapidjson::Value& value) override;
-};
diff --git a/source/Game/Mesh.cpp b/source/Game/Mesh.cpp
deleted file mode 100644
index 244e2e3..0000000
--- a/source/Game/Mesh.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#include "Mesh.hpp"
-
-#include <cstring>
-
-// StandardCpuMesh::StandardCpuMesh()
-// : mGpuMesh(new GpuMesh()) {
-// mGpuMesh->vertFormat = gVformatStandard;
-// mGpuMesh->vertBufBindings.SetBinding(0, new GpuVertexBuffer());
-// mGpuMesh->vertBufBindings.SetBinding(1, new GpuVertexBuffer());
-// mGpuMesh->indexBuf.Attach(new GpuIndexBuffer());
-// }
-
-// StandardCpuMesh::~StandardCpuMesh() {
-// delete mData;
-// }
-
-// void StandardCpuMesh::CreateCpuData() {
-// if (!mData) {
-// mData = new StandardCpuMeshData();
-// }
-// }
-
-// GpuVertexBuffer* StandardCpuMesh::GetPosBuffer() const {
-// return mGpuMesh->vertBufBindings.bindings[0].Get();
-// }
-
-// GpuVertexBuffer* StandardCpuMesh::GetExtraBuffer() const {
-// return mGpuMesh->vertBufBindings.bindings[1].Get();
-// }
-
-// bool StandardCpuMesh::UpdatePositions(glm::vec3* pos, size_t count, size_t startVertIndex) {
-// if (mData) {
-// std::memcpy(&mData->vertPositions[startVertIndex], pos, count * sizeof(glm::vec3));
-// }
-// auto posBuf = GetPosBuffer();
-// glBindBuffer(GL_ARRAY_BUFFER, posBuf->handle);
-// glBufferSubData(GL_ARRAY_BUFFER, startVertIndex * mGpuMesh->vertFormat->vertexSize, count * sizeof(glm::vec3), pos);
-// return true;
-// }
-
-// bool StandardCpuMesh::UpdateColors(RgbaColor* color, size_t count, size_t starVertIndex) {
-// if (!mData) return false;
-// // TODO
-// }
-
-// bool StandardCpuMesh::UpdateNormals(glm::vec2* normals, size_t count, size_t startVertIndex) {
-// if (!mData) return false;
-// // TODO
-// }
-
-// bool StandardCpuMesh::UpdateIndices(uint32_t* indices, size_t count, size_t startVertIndex) {
-// if (!mData) return false;
-// // TODO
-// }
diff --git a/source/Game/Mesh.hpp b/source/Game/Mesh.hpp
deleted file mode 100644
index f86fd55..0000000
--- a/source/Game/Mesh.hpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#pragma once
-
-#include "Color.hpp"
-#include "VertexIndex.hpp"
-#include "PodVector.hpp"
-#include "RcPtr.hpp"
-
-#include <cstddef>
-#include <cstdint>
-#include <glm/glm.hpp>
-#include <memory>
-
-struct StandardVertexExtra {
- float u, v;
- uint8_t r, g, b, a;
-};
-
-class StandardCpuMeshData {
-public:
- PodVector<glm::vec3> vertPositions;
- PodVector<StandardVertexExtra> vertExtra;
- PodVector<uint32_t> index;
- size_t vertexCount;
- size_t triangleCount;
-};
-
-class StandardCpuMesh {
-// private:
-// StandardCpuMeshData* mData = nullptr;
-// RcPtr<GpuMesh> mGpuMesh;
-
-// public:
-// StandardCpuMesh();
-// ~StandardCpuMesh();
-
-// GpuVertexBuffer* GetPosBuffer() const;
-// GpuVertexBuffer* GetExtraBuffer() const;
-// GpuMesh* GetGpuMesh() const { return mGpuMesh.Get(); }
-
-// void CreateCpuData();
-// bool UpdatePositions(glm::vec3* pos, size_t count, size_t startVertIndex);
-// bool UpdateColors(RgbaColor* color, size_t count, size_t starVertIndex);
-// bool UpdateNormals(glm::vec2* normals, size_t count, size_t startVertIndex);
-// bool UpdateIndices(uint32_t* indices, size_t count, size_t startVertIndex);
-};
diff --git a/source/Game/Player.cpp b/source/Game/Player.cpp
deleted file mode 100644
index 34c4549..0000000
--- a/source/Game/Player.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-#include "Player.hpp"
-
-#include "AppConfig.hpp"
-#include "CommonVertexIndex.hpp"
-#include "ScopeGuard.hpp"
-#include "Utils.hpp"
-
-#include <cstdio>
-#include <cstdlib>
-
-// Keep the same number as # of fields in `struct {}` in PlayerKeyBinds
-constexpr int kPlayerKeyBindCount = 4;
-
-// Here be dragons: this treats consecutive fiels as an array, technically UB
-std::span<int> PlayerKeyBinds::GetKeyArray() {
- return { &keyLeft, kPlayerKeyBindCount };
-}
-std::span<bool> PlayerKeyBinds::GetKeyStatusArray() {
- return { &pressedLeft, kPlayerKeyBindCount };
-}
-
-Player::Player(GameWorld* world, int id)
- : GameObject(KD_Player, world)
- , mId{ id } {
- renderObject.SetMaterial(gDefaultMaterial.Get());
- renderObject.SetFormat(gVformatStandard.Get(), Tags::IT_16Bit);
- renderObject.RebuildIfNecessary();
-}
-
-void Player::Awaken() {
- LoadFromFile();
-}
-
-void Player::Resleep() {
- SaveToFile();
-}
-
-void Player::Update() {
- using namespace Tags;
-
- if (keybinds.pressedLeft) {
- }
- if (keybinds.pressedRight) {
- }
-
- // TODO jump controller
-
- // TODO attack controller
-
- // TODO set default sprite to get rid of this check
- if (sprite.GetDefinition()) {
- int prevFrame = sprite.GetFrame();
- sprite.PlayFrame();
- int currFrame = sprite.GetFrame();
- if (prevFrame != currFrame) {
- uint16_t indices[6];
- Index_U16::Assign(indices, 0);
- renderObject.GetIndexBuffer()->Upload((const std::byte*)indices, IT_16Bit, std::size(indices));
-
- Vertex_PTC vertices[4];
- Vertex_PTC::Assign(vertices, Rect<float>{ GetPos(), sprite.GetDefinition()->GetBoundingBox() });
- Vertex_PTC::Assign(vertices, 0.0f);
- Vertex_PTC::Assign(vertices, RgbaColor(255, 255, 255));
- Vertex_PTC::Assign(vertices, sprite.GetFrameSubregion());
- renderObject.GetVertexBufferBindings().bindings[0]->Upload((const std::byte*)vertices, sizeof(vertices));
- }
- }
-}
-
-Material* Player::GetMaterial() const {
- return renderObject.GetMaterial();
-}
-
-void Player::SetMaterial(Material* material) {
- renderObject.SetMaterial(material);
- renderObject.RebuildIfNecessary();
-}
-
-std::span<const RenderObject> Player::GetRenderObjects() const {
- return { &renderObject, 1 };
-}
-
-void Player::HandleKeyInput(int key, int action) {
- bool pressed;
- if (action == GLFW_PRESS) {
- pressed = true;
- } else if (action == GLFW_REPEAT) {
- return;
- } else /* action == GLFW_RELEASE */ {
- pressed = false;
- }
-
- for (int i = 0; i < kPlayerKeyBindCount; ++i) {
- int kbKey = keybinds.GetKeyArray()[i];
- bool& kbStatus = keybinds.GetKeyStatusArray()[i];
-
- if (kbKey == key) {
- kbStatus = pressed;
- break;
- }
- }
-}
-
-#pragma push_macro("PLAYERKEYBINDS_DO_IO")
-#undef PLAYERKEYBINDS_DO_IO
-#define PLAYERKEYBINDS_DO_IO(function, fieldPrefix) \
- function(file, "left=%d\n", fieldPrefix keybinds.keyLeft); \
- function(file, "right=%d\n", fieldPrefix keybinds.keyRight); \
- function(file, "jump=%d\n", fieldPrefix keybinds.keyJump); \
- function(file, "attack=%d\n", fieldPrefix keybinds.keyAttack);
-
-static FILE* OpenPlayerConfigFile(Player* player, Utils::IoMode mode) {
- char path[2048];
- snprintf(path, sizeof(path), "%s/player%d.txt", AppConfig::dataDir.c_str(), player->GetId());
-
- return Utils::OpenCstdioFile(path, mode);
-}
-
-bool Player::LoadFromFile() {
- auto file = OpenPlayerConfigFile(this, Utils::Read);
- if (!file) return false;
- DEFER { fclose(file); };
-
- // TODO input validation
- PLAYERKEYBINDS_DO_IO(fscanf, &);
-
- return true;
-}
-
-bool Player::SaveToFile() {
- auto file = OpenPlayerConfigFile(this, Utils::WriteTruncate);
- if (!file) return false;
- DEFER { fclose(file); };
-
- PLAYERKEYBINDS_DO_IO(fprintf, );
-
- return true;
-}
-#pragma pop_macro("PLAYERKEYBINDS_DO_IO")
diff --git a/source/Game/Player.hpp b/source/Game/Player.hpp
deleted file mode 100644
index d003a25..0000000
--- a/source/Game/Player.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma once
-
-#include "GameObject.hpp"
-#include "Material.hpp"
-#include "RcPtr.hpp"
-#include "Sprite.hpp"
-
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
-
-#include <span>
-#include <vector>
-
-struct PlayerKeyBinds {
- int keyLeft = GLFW_KEY_A;
- int keyRight = GLFW_KEY_D;
- int keyJump = GLFW_KEY_SPACE;
- int keyAttack = GLFW_KEY_J;
-
- bool pressedLeft = 0;
- bool pressedRight = 0;
- bool pressedJump = 0;
- bool pressedAttack = 0;
-
- std::span<int> GetKeyArray();
- std::span<bool> GetKeyStatusArray();
-};
-
-class Player : public GameObject {
-public:
- std::vector<GLFWkeyboard*> boundKeyboards;
- PlayerKeyBinds keybinds;
- Sprite sprite;
- RenderObject renderObject;
- int mId;
-
-public:
- Player(GameWorld* world, int id);
-
- virtual void Awaken() override;
- virtual void Resleep() override;
- virtual void Update() override;
-
- Material* GetMaterial() const;
- void SetMaterial(Material* material);
- virtual std::span<const RenderObject> GetRenderObjects() const override;
-
- int GetId() const { return mId; }
-
- void HandleKeyInput(int key, int action);
-
- // File is designated by player ID
- bool LoadFromFile();
- bool SaveToFile();
-};
diff --git a/source/Game/Renderer.cpp b/source/Game/Renderer.cpp
deleted file mode 100644
index 3497449..0000000
--- a/source/Game/Renderer.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-#include "Renderer.hpp"
-
-#include "GameObject.hpp"
-
-#include <cassert>
-#include <glm/gtc/matrix_transform.hpp>
-#include <glm/gtc/quaternion.hpp>
-#include <glm/gtx/quaternion.hpp>
-
-RenderObject::RenderObject()
- : mVao{ GL_NONE } {
-}
-
-RenderObject::~RenderObject() {
- DeleteGLObjects();
-}
-
-GLuint RenderObject::GetGLVao() const {
- return mVao;
-}
-
-void RenderObject::RebuildIfNecessary() {
- if (mVao != GL_NONE) {
- return;
- }
-
- assert(mIndexBuf != nullptr);
- assert(mVertexFormat != nullptr);
-
- glGenVertexArrays(1, &mVao);
- glBindVertexArray(mVao);
-
- auto& vBindings = mVertexBufBinding.bindings;
- auto& shaderInfo = mMaterial->GetShader()->GetInfo();
-
- // Setup vertex buffers
- for (auto& elm : mVertexFormat->elements) {
- assert(elm.bindingIndex < vBindings.size());
- auto& buffer = vBindings[elm.bindingIndex];
-
- int index = shaderInfo.FindInputLocation(elm.semantic);
- if (index == -1) {
- continue;
- }
-
- glBindBuffer(GL_ARRAY_BUFFER, buffer->handle);
- glEnableVertexAttribArray(index);
- glVertexAttribPointer(
- index,
- Tags::VectorLenOf(elm.type),
- Tags::FindGLType(elm.type),
- Tags::IsNormalized(elm.type),
- mVertexFormat->vertexSize,
- (void*)(uintptr_t)elm.offset);
- }
-
- // Setup index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuf->handle);
-
- glBindVertexArray(GL_NONE);
-}
-
-void RenderObject::SetMaterial(Material* material) {
- mMaterial.Attach(material);
- DeleteGLObjects();
-}
-
-void RenderObject::UpdateIndexBuffer(GpuIndexBuffer* indexBuffer) {
- mIndexBuf.Attach(indexBuffer);
- DeleteGLObjects();
-}
-
-void RenderObject::UpdateVertexFormat(VertexFormat* vertexFormat) {
- mVertexFormat.Attach(vertexFormat);
- DeleteGLObjects();
-}
-
-void RenderObject::UpdateVertexBufferBindings(BufferBindings** bindingsOut) {
- *bindingsOut = &mVertexBufBinding;
- DeleteGLObjects();
-}
-
-void RenderObject::SetFormat(VertexFormat* vertexFormat, Tags::IndexType indexFormat) {
- mIndexBuf.Attach(new GpuIndexBuffer());
- mIndexBuf->indexType = indexFormat;
- mIndexBuf->count = 0;
-
- mVertexFormat.Attach(vertexFormat);
- mVertexBufBinding.Clear();
- for (auto& element : vertexFormat->elements) {
- if (mVertexBufBinding.GetBinding(element.bindingIndex) == nullptr) {
- mVertexBufBinding.SetBinding(element.bindingIndex, new GpuVertexBuffer());
- }
- }
-}
-
-void RenderObject::DeleteGLObjects() {
- if (mVao != GL_NONE) {
- glDeleteVertexArrays(1, &mVao);
- mVao = GL_NONE;
- }
-}
-
-void Renderer::BeginFrame(Camera& camera, float currentTime, float deltaTime) {
- assert(mInsideFrame == false);
- mInsideFrame = true;
- mFrame.camera = &camera;
- mFrame.matrixView = camera.CalcViewMatrix();
- mFrame.matrixProj = camera.CalcProjectionMatrix();
- mFrame.time = currentTime;
- mFrame.deltaTime = deltaTime;
-}
-
-void Renderer::EndFrame() {
- assert(mInsideFrame == true);
- mInsideFrame = false;
-}
-
-void Renderer::Draw(const RenderObject* objects, const GameObject* gameObject, size_t count) {
- using namespace Tags;
-
- assert(mInsideFrame);
-
- auto vpMatrix = mFrame.matrixProj * mFrame.matrixView;
-
- // TODO shader grouping
- // TODO material grouping
- for (size_t i = 0; i < count; ++i) {
- auto& object = objects[i];
- auto indexBuffer = object.GetIndexBuffer();
- auto mat = object.GetMaterial();
- auto shader = mat->GetShader();
-
- glUseProgram(shader->GetProgram());
-
- // Material uniforms
- mat->UseUniforms();
-
- // Next available texture unit ID after all material textures
- int texIdx = mat->GetTextures().size();
-
- // Autofill uniforms
- if (shader->autofill_Transform != kInvalidLocation) {
- glm::mat4 objectMatrix(1.0f);
- objectMatrix = glm::translate(objectMatrix, gameObject->GetPos());
- objectMatrix *= glm::toMat4(gameObject->GetRotation());
- objectMatrix = glm::scale(objectMatrix, gameObject->GetScale());
- glm::mat4 transform = vpMatrix * objectMatrix;
-
- glUniformMatrix4fv(shader->autofill_Transform, 1, GL_FALSE, &transform[0][0]);
- }
- if (shader->autofill_Time != kInvalidLocation) {
- glUniform1f(shader->autofill_Time, mFrame.time);
- }
- if (shader->autofill_DeltaTime != kInvalidLocation) {
- glUniform1f(shader->autofill_DeltaTime, mFrame.deltaTime);
- }
- if (shader->autofill_TextureAtlas != kInvalidLocation &&
- object.autofill_TextureAtlas != nullptr)
- {
- glActiveTexture(GL_TEXTURE0 + texIdx);
- glBindTexture(GL_TEXTURE_2D, object.autofill_TextureAtlas->GetHandle());
- glUniform1i(shader->autofill_TextureAtlas, texIdx);
- ++texIdx;
- }
-
- glBindVertexArray(object.GetGLVao());
- glDrawElements(GL_TRIANGLES, indexBuffer->count, indexBuffer->GetIndexTypeGL(), 0);
- }
-}
diff --git a/source/Game/Renderer.hpp b/source/Game/Renderer.hpp
deleted file mode 100644
index 98a9f28..0000000
--- a/source/Game/Renderer.hpp
+++ /dev/null
@@ -1,68 +0,0 @@
-#pragma once
-
-#include "Camera.hpp"
-#include "Material.hpp"
-#include "RcPtr.hpp"
-#include "VertexIndex.hpp"
-
-#include <glad/glad.h>
-#include <cstddef>
-#include <glm/glm.hpp>
-
-// TODO add optional support for OpenGL separate attrib binding & only depend on vertex format
-
-class GameObject;
-
-class RenderObject {
-public:
- RcPtr<Texture> autofill_TextureAtlas;
-
-private:
- RcPtr<Material> mMaterial;
- RcPtr<GpuIndexBuffer> mIndexBuf;
- RcPtr<VertexFormat> mVertexFormat;
- BufferBindings mVertexBufBinding;
- GLuint mVao;
-
-public:
- RenderObject();
- ~RenderObject();
-
- GLuint GetGLVao() const;
- void RebuildIfNecessary();
-
- Material* GetMaterial() const { return mMaterial.Get(); }
- void SetMaterial(Material* material);
-
- GpuIndexBuffer* GetIndexBuffer() const { return mIndexBuf.Get(); }
- const VertexFormat* GetVertexFormat() const { return mVertexFormat.Get(); }
- const BufferBindings& GetVertexBufferBindings() const { return mVertexBufBinding; }
- void UpdateIndexBuffer(GpuIndexBuffer* indexBuffer);
- void UpdateVertexFormat(VertexFormat* vertexFormat);
- // Assumes the fetched BufferBinding object is modified
- void UpdateVertexBufferBindings(BufferBindings** bindingsOut);
- void SetFormat(VertexFormat* vertexFormat, Tags::IndexType indexFormat);
-
-private:
- void DeleteGLObjects();
-};
-
-struct RendererFrameInfo {
- Camera* camera;
- glm::mat4 matrixView;
- glm::mat4 matrixProj;
- float time;
- float deltaTime;
-};
-
-class Renderer {
-private:
- RendererFrameInfo mFrame;
- bool mInsideFrame = false;
-
-public:
- void BeginFrame(Camera& camera, float currentTime, float deltaTime);
- const RendererFrameInfo& GetLastFrameInfo() const { return mFrame; }
- void Draw(const RenderObject* objects, const GameObject* gameObject, size_t count);
- void EndFrame();
-};
diff --git a/source/Game/SceneThings.cpp b/source/Game/SceneThings.cpp
deleted file mode 100644
index 3fa0436..0000000
--- a/source/Game/SceneThings.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "SceneThings.hpp"
-
-#include "CommonVertexIndex.hpp"
-#include "Rect.hpp"
-
-#include <utility>
-
-SimpleGeometryObject::SimpleGeometryObject(GameWorld* world)
- : GameObject(KD_SimpleGeometry, world)
- , mRenderObject()
- , mSize{ 1.0f, 1.0f, 1.0f }
- , mXFaceColor(kXAxisColor)
- , mYFaceColor(kYAxisColor)
- , mZFaceColor(kZAxisColor)
- , mNeedsRebuildMesh{ true } {
- mRenderObject.SetMaterial(gDefaultMaterial.Get());
- mRenderObject.SetFormat(gVformatStandard.Get(), Tags::IT_16Bit);
- mRenderObject.RebuildIfNecessary();
-}
-
-void SimpleGeometryObject::SetSize(glm::vec3 size) {
- mSize = size;
- mNeedsRebuildMesh = true;
-}
-
-void SimpleGeometryObject::SetXFaceColor(RgbaColor color) {
- mXFaceColor = color;
- mNeedsRebuildMesh = true;
-}
-
-void SimpleGeometryObject::SetYFaceColor(RgbaColor color) {
- mYFaceColor = color;
- mNeedsRebuildMesh = true;
-}
-
-void SimpleGeometryObject::SetZFaceColor(RgbaColor color) {
- mZFaceColor = color;
- mNeedsRebuildMesh = true;
-}
-
-std::span<const RenderObject> SimpleGeometryObject::GetRenderObjects() const {
- using namespace Tags;
-
- if (mNeedsRebuildMesh) {
- mNeedsRebuildMesh = false;
-
- Vertex_PTC vertices[4 /*vertices per face*/ * 6 /*faces*/];
- uint16_t indices[3 /*indices per triangle*/ * 2 /*triangles per face*/ * 6 /*faces*/];
-
- auto extents = mSize / 2.0f;
-
- int faceGenVerticesIdx = 0;
- int faceGenIndicesIdx = 0;
- auto GenerateFace = [&](glm::vec3 faceCenter, glm::vec3 firstExtentVec, glm::vec3 secondExtentVec, RgbaColor color) {
- // Generates (if viewing top down on the face): bottom left, top left, bottom right, top right
- // (-1, -1) , (-1, 1) , (1, -1) , (1, 1)
- // idx=0 , idx=1 , idx=2 , idx=3
-
- // These are index offsets, see above comment
- constexpr int kBottomLeft = 0;
- constexpr int kTopLeft = 1;
- constexpr int kBottomRight = 2;
- constexpr int kTopRight = 3;
-
- int startVertIdx = faceGenVerticesIdx;
- for (float firstDir : { -1, 1 }) {
- for (float secondDir : { -1, 1 }) {
- auto vertPos = faceCenter + firstExtentVec * firstDir + secondExtentVec * secondDir;
- auto& vert = vertices[faceGenVerticesIdx];
- vert.x = vertPos.x;
- vert.y = vertPos.y;
- vert.z = vertPos.z;
- vert.r = color.r;
- vert.g = color.g;
- vert.b = color.b;
- vert.a = color.a;
- faceGenVerticesIdx += 1;
- }
- }
-
- // Triangle #1
- indices[faceGenIndicesIdx++] = startVertIdx + kTopRight;
- indices[faceGenIndicesIdx++] = startVertIdx + kTopLeft;
- indices[faceGenIndicesIdx++] = startVertIdx + kBottomLeft;
- // Triangle #2
- indices[faceGenIndicesIdx++] = startVertIdx + kTopRight;
- indices[faceGenIndicesIdx++] = startVertIdx + kBottomLeft;
- indices[faceGenIndicesIdx++] = startVertIdx + kBottomRight;
- };
- for (int xDir : { -1, 1 }) {
- float x = xDir * extents.x;
- GenerateFace(glm::vec3(x, 0, 0), glm::vec3(0, 0, extents.z), glm::vec3(0, extents.y, 0), mXFaceColor);
- }
- for (int yDir : { -1, 1 }) {
- float y = yDir * extents.y;
- GenerateFace(glm::vec3(0, y, 0), glm::vec3(extents.x, 0, 0), glm::vec3(0, 0, extents.z), mYFaceColor);
- }
- for (int zDir : { -1, 1 }) {
- float z = zDir * extents.z;
- GenerateFace(glm::vec3(0, 0, z), glm::vec3(extents.x, 0, 0), glm::vec3(0, extents.y, 0), mZFaceColor);
- }
-
- for (auto& vert : vertices) {
- vert.u = 0.0f;
- vert.v = 0.0f;
- }
-
- mRenderObject.GetVertexBufferBindings().bindings[0]->Upload((const std::byte*)vertices, sizeof(vertices));
- mRenderObject.GetIndexBuffer()->Upload((const std::byte*)indices, IT_16Bit, std::size(indices));
- }
-
- return { &mRenderObject, 1 };
-}
-
-BuildingObject::BuildingObject(GameWorld* world)
- : GameObject(KD_Building, world) {
- mRenderObject.SetMaterial(gDefaultMaterial.Get());
- mRenderObject.SetFormat(gVformatStandard.Get(), Tags::IT_32Bit);
- mRenderObject.RebuildIfNecessary();
-}
-
-// void BuildingObject::SetMeshMaterial(Material* material) {
-// mMaterial.Attach(material);
-// // TODO update render
-// }
-
-// const Material* BuildingObject::GetMeshMaterial() const {
-// return mMaterial.Get();
-// }
-
-// void BuildingObject::SetMesh(GpuMesh* mesh) {
-// mMesh.Attach(mesh);
-// // TODO update render
-// }
-
-// const GpuMesh* BuildingObject::GetMesh() const {
-// return mMesh.Get();
-// }
-
-std::span<const RenderObject> BuildingObject::GetRenderObjects() const {
- return { &mRenderObject, 1 };
-}
diff --git a/source/Game/SceneThings.hpp b/source/Game/SceneThings.hpp
deleted file mode 100644
index c261fbb..0000000
--- a/source/Game/SceneThings.hpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#pragma once
-
-#include "Color.hpp"
-#include "GameObject.hpp"
-#include "Renderer.hpp"
-
-#include <glm/glm.hpp>
-#include <vector>
-
-class SimpleGeometryObject : public GameObject {
-private:
- RenderObject mRenderObject;
- glm::vec3 mSize;
- RgbaColor mXFaceColor;
- RgbaColor mYFaceColor;
- RgbaColor mZFaceColor;
- mutable bool mNeedsRebuildMesh ;
-
-public:
- SimpleGeometryObject(GameWorld* world);
-
- glm::vec3 GetSize() const { return mSize; }
- void SetSize(glm::vec3 size);
- RgbaColor GetXFaceColor() const { return mXFaceColor; }
- void SetXFaceColor(RgbaColor color);
- RgbaColor GetYFaceColor() const { return mYFaceColor; }
- void SetYFaceColor(RgbaColor color);
- RgbaColor GetZFaceColor() const { return mZFaceColor; }
- void SetZFaceColor(RgbaColor color);
- virtual std::span<const RenderObject> GetRenderObjects() const override;
-};
-
-class BuildingObject : public GameObject {
-private:
- RenderObject mRenderObject;
-
-public:
- BuildingObject(GameWorld* world);
-
- // TODO
- // void SetMeshMaterial(Material* material);
- // virtual const Material* GetMeshMaterial() const override;
- // void SetMesh(GpuMesh* mesh);
- // virtual const GpuMesh* GetMesh() const override;
- virtual std::span<const RenderObject> GetRenderObjects() const override;
-};
diff --git a/source/Game/Shader.cpp b/source/Game/Shader.cpp
deleted file mode 100644
index 4a58635..0000000
--- a/source/Game/Shader.cpp
+++ /dev/null
@@ -1,773 +0,0 @@
-#include "Shader.hpp"
-
-#include "AppConfig.hpp"
-
-#include <Metadata.hpp>
-#include <RapidJsonHelper.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <imgui.h>
-#include <misc/cpp/imgui_stdlib.h>
-#include <rapidjson/document.h>
-#include <cassert>
-#include <cstddef>
-#include <cstdlib>
-#include <utility>
-
-using namespace std::literals;
-
-void ShaderMathVariable::ShowInfo() const {
- ImGui::BulletText("Location: %d\nName: %s\nSemantic: %.*s\nType: %.*s %dx%d",
- location,
- name.c_str(),
- PRINTF_STRING_VIEW(Metadata::EnumToString(semantic)),
- PRINTF_STRING_VIEW(Tags::GLTypeToString(scalarType)),
- width,
- height);
-}
-
-void ShaderSamplerVariable::ShowInfo() const {
- ImGui::BulletText("Location: %d\nName: %s\nSemantic: %.*s\nType: Sampler",
- location,
- name.c_str(),
- PRINTF_STRING_VIEW(Metadata::EnumToString(semantic)));
-}
-
-bool ShaderThingId::IsValid() const {
- return kind == KD_Invalid;
-}
-
-namespace ProjectBrussel_UNITY_ID {
-GLuint FindLocation(const std::vector<ShaderMathVariable>& vars, Tags::VertexElementSemantic semantic) {
- for (auto& var : vars) {
- if (var.semantic == semantic) {
- return var.location;
- }
- }
- return Tags::kInvalidLocation;
-}
-
-constexpr auto kAfnTransform = "transform";
-constexpr auto kAfnTime = "time";
-constexpr auto kAfnDeltaTime = "deltaTime";
-constexpr auto kAfnTextureAtlas = "textureAtlas";
-
-void InitAutoFill(const char* name, GLuint program, GLuint& location) {
- GLint result = glGetUniformLocation(program, name);
- if (result != -1) {
- location = result;
- }
-}
-
-void InitAutoFills(Shader& shader) {
- GLuint pg = shader.GetProgram();
- InitAutoFill(kAfnTransform, pg, shader.autofill_Transform);
- InitAutoFill(kAfnTime, pg, shader.autofill_Time);
- InitAutoFill(kAfnDeltaTime, pg, shader.autofill_DeltaTime);
- InitAutoFill(kAfnTextureAtlas, pg, shader.autofill_TextureAtlas);
-}
-} // namespace ProjectBrussel_UNITY_ID
-
-GLuint ShaderInfo::FindInputLocation(Tags::VertexElementSemantic semantic) {
- using namespace ProjectBrussel_UNITY_ID;
- return FindLocation(inputs, semantic);
-}
-
-GLuint ShaderInfo::FindOutputLocation(Tags::VertexElementSemantic semantic) {
- using namespace ProjectBrussel_UNITY_ID;
- return FindLocation(outputs, semantic);
-}
-
-ShaderVariable* ShaderInfo::FindVariable(const ShaderThingId& thing) {
- switch (thing.kind) {
- case ShaderThingId::KD_Input: return &inputs[thing.index];
- case ShaderThingId::KD_Output: return &outputs[thing.index];
- case ShaderThingId::KD_Uniform: return uniforms[thing.index].get();
- case ShaderThingId::KD_Invalid: break;
- }
- return nullptr;
-}
-
-Shader::Shader() {
-}
-
-Shader::~Shader() {
- glDeleteProgram(mProgram);
-}
-
-namespace ProjectBrussel_UNITY_ID {
-// Grabs section [begin, end)
-Shader::ErrorCode CreateShader(GLuint& out, const char* src, int beginIdx, int endIdx, GLenum type) {
- out = glCreateShader(type);
-
- const GLchar* begin = &src[beginIdx];
- const GLint len = endIdx - beginIdx;
- glShaderSource(out, 1, &begin, &len);
-
- glCompileShader(out);
- GLint compileStatus;
- glGetShaderiv(out, GL_COMPILE_STATUS, &compileStatus);
- if (compileStatus == GL_FALSE) {
- GLint len;
- glGetShaderiv(out, GL_INFO_LOG_LENGTH, &len);
-
- std::string log(len, '\0');
- glGetShaderInfoLog(out, len, nullptr, log.data());
-
- return Shader ::EC_CompilationFailed;
- }
-
- return Shader::EC_Success;
-}
-
-Shader::ErrorCode CreateShader(GLuint& out, std::string_view str, GLenum type) {
- return CreateShader(out, str.data(), 0, str.size(), type);
-}
-
-Shader::ErrorCode LinkShaderProgram(GLuint program) {
- glLinkProgram(program);
- GLint linkStatus;
- glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
- if (linkStatus == GL_FALSE) {
- GLint len;
- glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
-
- std::string log(len, '\0');
- glGetProgramInfoLog(program, len, nullptr, log.data());
-
- return Shader::EC_LinkingFailed;
- }
-
- return Shader::EC_Success;
-}
-} // namespace ProjectBrussel_UNITY_ID
-
-#define CATCH_ERROR_IMPL(x, name) \
- auto name = x; \
- if (name != Shader::EC_Success) { \
- return name; \
- }
-#define CATCH_ERROR(x) CATCH_ERROR_IMPL(x, UNIQUE_NAME(result))
-
-Shader::ErrorCode Shader::InitFromSources(const ShaderSources& sources) {
- using namespace ProjectBrussel_UNITY_ID;
-
- if (IsValid()) {
- return EC_AlreadyInitialized;
- }
-
- GLuint program = glCreateProgram();
- ScopeGuard sg = [&]() { glDeleteProgram(program); };
-
- GLuint vertex = 0;
- DEFER { glDeleteShader(vertex); };
- if (!sources.vertex.empty()) {
- CATCH_ERROR(CreateShader(vertex, sources.vertex, GL_VERTEX_SHADER));
- glAttachShader(program, vertex);
- }
-
- GLuint geometry = 0;
- DEFER { glDeleteShader(geometry); };
- if (!sources.geometry.empty()) {
- CATCH_ERROR(CreateShader(geometry, sources.geometry, GL_GEOMETRY_SHADER));
- glAttachShader(program, geometry);
- }
-
- GLuint tessControl = 0;
- DEFER { glDeleteShader(tessControl); };
- if (!sources.tessControl.empty()) {
- CATCH_ERROR(CreateShader(tessControl, sources.tessControl, GL_TESS_CONTROL_SHADER));
- glAttachShader(program, tessControl);
- }
-
- GLuint tessEval = 0;
- DEFER { glDeleteShader(tessEval); };
- if (!sources.tessEval.empty()) {
- CATCH_ERROR(CreateShader(tessEval, sources.tessEval, GL_TESS_EVALUATION_SHADER));
- glAttachShader(program, tessEval);
- }
-
- GLuint fragment = 0;
- DEFER { glDeleteShader(fragment); };
- if (!sources.fragment.empty()) {
- CATCH_ERROR(CreateShader(fragment, sources.fragment, GL_FRAGMENT_SHADER));
- glAttachShader(program, fragment);
- }
-
- CATCH_ERROR(LinkShaderProgram(program));
-
- sg.Dismiss();
- mProgram = program;
-
- InitAutoFills(*this);
-
- return EC_Success;
-}
-
-Shader::ErrorCode Shader::InitFromSource(std::string_view source) {
- using namespace ProjectBrussel_UNITY_ID;
-
- GLuint vertex = 0;
- DEFER { glDeleteShader(vertex); };
-
- GLuint geometry = 0;
- DEFER { glDeleteShader(geometry); };
-
- GLuint tessControl = 0;
- DEFER { glDeleteShader(tessControl); };
-
- GLuint tessEval = 0;
- DEFER { glDeleteShader(tessEval); };
-
- GLuint fragment = 0;
- DEFER { glDeleteShader(fragment); };
-
- int prevBegin = -1; // Excluding #type marker
- int prevEnd = -1; // [begin, end)
- std::string prevShaderVariant;
-
- auto CommitSection = [&]() -> ErrorCode {
- if (prevBegin == -1 || prevEnd == -1) {
- // Not actually "succeeding" here, but we just want to skip this call and continue
- return EC_Success;
- }
-
- if (prevShaderVariant == "vertex" && !vertex) {
- CATCH_ERROR(CreateShader(vertex, source.data(), prevBegin, prevEnd, GL_VERTEX_SHADER));
- } else if (prevShaderVariant == "geometry" && !geometry) {
- CATCH_ERROR(CreateShader(geometry, source.data(), prevBegin, prevEnd, GL_GEOMETRY_SHADER));
- } else if (prevShaderVariant == "tessellation_control" && !tessControl) {
- CATCH_ERROR(CreateShader(tessControl, source.data(), prevBegin, prevEnd, GL_TESS_CONTROL_SHADER));
- } else if (prevShaderVariant == "tessellation_evaluation" && !tessEval) {
- CATCH_ERROR(CreateShader(tessEval, source.data(), prevBegin, prevEnd, GL_TESS_EVALUATION_SHADER));
- } else if (prevShaderVariant == "fragment" && !fragment) {
- CATCH_ERROR(CreateShader(fragment, source.data(), prevBegin, prevEnd, GL_FRAGMENT_SHADER));
- } else {
- return EC_InvalidShaderVariant;
- }
-
- prevBegin = -1;
- prevEnd = -1;
- prevShaderVariant.clear();
-
- return EC_Success;
- };
-
- constexpr const char* kMarker = "#type ";
- bool matchingDirective = true; // If true, we are matching marker pattern; if false, we are accumulating shader variant identifier
- int matchIndex = 0; // Current index of the pattern trying to match
- std::string shaderVariant;
-
- // Don't use utf8 iterator, shader sources are expected to be ASCII only
- for (size_t i = 0; i < source.size(); ++i) {
- char c = source[i];
-
- if (matchingDirective) {
- if (c == kMarker[matchIndex]) {
- // Matched the expected character, go to next char in pattern
- matchIndex++;
-
- // If we are at the end of the marker pattern...
- if (kMarker[matchIndex] == '\0') {
- matchingDirective = false;
- matchIndex = 0;
- continue;
- }
-
- // This might be a shader variant directive -> might be end of a section
- if (c == '#') {
- prevEnd = i;
- continue;
- }
- } else {
- // Unexpected character, rollback to beginning
- matchIndex = 0;
- }
- } else {
- if (c == '\n') {
- // Found complete shader variant directive
-
- CATCH_ERROR(CommitSection()); // Try commit section, for the first apparent of #type this should do nothing, as `prevEnd` will still be -1
- prevBegin = i + 1; // +1 to skip new line (technically not needed)
- prevShaderVariant = std::move(shaderVariant);
-
- matchingDirective = true;
- shaderVariant.clear();
- } else {
- // Simply accumulate to shader variant buffer
- shaderVariant += c;
- }
- }
- }
-
- // Commit the last section
- prevEnd = static_cast<int>(source.size());
- CATCH_ERROR(CommitSection());
-
- GLuint program = glCreateProgram();
- ScopeGuard sg = [&]() { glDeleteProgram(program); };
-
- if (vertex) glAttachShader(program, vertex);
- if (geometry) glAttachShader(program, geometry);
- if (tessControl) glAttachShader(program, tessControl);
- if (tessEval) glAttachShader(program, tessEval);
- if (fragment) glAttachShader(program, fragment);
-
- CATCH_ERROR(LinkShaderProgram(program));
-
- sg.Dismiss();
- mProgram = program;
-
- InitAutoFills(*this);
-
- return EC_Success;
-}
-
-#undef CATCH_ERROR
-
-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:
-
- 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;
-}
-
-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::GatherInfoShaderIntrospection() {
- using namespace ProjectBrussel_UNITY_ID;
-
- mInfo = {};
-
- // TODO handle differnt types of variables with the same name
-
- // TODO work with OpenGL < 4.3, possibly with glslang
- return true;
-
- int inputCount;
- glGetProgramInterfaceiv(mProgram, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &inputCount);
- int outputCount;
- glGetProgramInterfaceiv(mProgram, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, &outputCount);
- int uniformBlockCount;
- glGetProgramInterfaceiv(mProgram, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &uniformBlockCount);
- int uniformCount;
- glGetProgramInterfaceiv(mProgram, GL_UNIFORM, GL_ACTIVE_RESOURCES, &uniformCount);
-
- // Gather inputs
- auto GatherMathVars = [&](int count, GLenum resourceType, ShaderThingId::Kind resourceKind, std::vector<ShaderMathVariable>& 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(mProgram, 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(mProgram, GL_UNIFORM, i, nameLength, nullptr, fieldName.data());
-
- mInfo.things.try_emplace(fieldName, ShaderThingId{ resourceKind, i });
-
- auto& thing = list.emplace_back();
- thing.name = std::move(fieldName);
- thing.arrayLength = arrayLength;
- QueryMathInfo(type, thing.scalarType, thing.width, thing.height);
- }
- };
- GatherMathVars(inputCount, GL_PROGRAM_INPUT, ShaderThingId::KD_Input, mInfo.inputs);
- GatherMathVars(outputCount, GL_PROGRAM_OUTPUT, ShaderThingId::KD_Output, mInfo.outputs);
-
- // Gather uniform variables
- for (int i = 0; i < uniformCount; ++i) {
- const GLenum query[] = { GL_BLOCK_INDEX, GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_TOP_LEVEL_ARRAY_SIZE };
- GLint props[std::size(query)];
- glGetProgramResourceiv(mProgram, 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(mProgram, GL_UNIFORM, i, nameLength, nullptr, fieldName.data());
-
- mInfo.things.try_emplace(fieldName, ShaderThingId{ ShaderThingId::KD_Uniform, i });
- mInfo.uniforms.push_back(CreateVariable(type, loc));
- }
-
- return true;
-}
-
-bool Shader::IsValid() const {
- return mProgram != 0;
-}
-
-namespace ProjectBrussel_UNITY_ID {
-void WriteShaderVariable(rapidjson::Value& value, rapidjson::Document& root, const ShaderVariable& var) {
- value.AddMember("Name", var.name, root.GetAllocator());
- value.AddMember("Semantic", rapidjson::StringRef(Metadata::EnumToString(var.semantic)), root.GetAllocator());
- value.AddMember("OpenGLLocation", var.location, root.GetAllocator());
-}
-
-bool ReadShaderVariable(const rapidjson::Value& value, ShaderVariable& var) {
- using namespace Tags;
-
- BRUSSEL_JSON_GET(value, "Name", std::string, var.name, return false);
- { // Semantic
- auto rvSemantic = rapidjson::GetProperty(value, rapidjson::kStringType, "Semantic"sv);
- if (!rvSemantic) {
- var.semantic = VES_Generic;
- } else {
- auto str = rapidjson::AsStringView(*rvSemantic);
- auto lookup = Metadata::EnumFromString<VertexElementSemantic>(str);
- var.semantic = lookup.value_or(VES_Generic);
- }
- }
- BRUSSEL_JSON_GET_DEFAULT(value, "OpenGLLocation", int, var.location, 0);
- return true;
-}
-
-void WriteShaderMathVariable(rapidjson::Value& value, rapidjson::Document& root, const ShaderMathVariable& var) {
- WriteShaderVariable(value, root, var);
- value.AddMember("ScalarType", rapidjson::StringRef(Tags::GLTypeToString(var.scalarType)), root.GetAllocator());
- value.AddMember("Width", var.width, root.GetAllocator());
- value.AddMember("Height", var.height, root.GetAllocator());
- value.AddMember("ArrayLength", var.arrayLength, root.GetAllocator());
-}
-
-bool ReadShaderMathVariable(const rapidjson::Value& value, ShaderMathVariable& var) {
- if (!ReadShaderVariable(value, var)) return false;
- {
- auto rvScalar = rapidjson::GetProperty(value, rapidjson::kStringType, "ScalarType"sv);
- if (!rvScalar) return false;
- var.scalarType = Tags::GLTypeFromString(rapidjson::AsStringView(*rvScalar));
- }
- BRUSSEL_JSON_GET(value, "Width", int, var.width, return false);
- BRUSSEL_JSON_GET(value, "Height", int, var.height, return false);
- BRUSSEL_JSON_GET_DEFAULT(value, "ArrayLength", int, var.arrayLength, 1);
- return true;
-}
-
-void WriteShaderSamplerVariable(rapidjson::Value& value, rapidjson::Document& root, const ShaderSamplerVariable& var) {
- WriteShaderVariable(value, root, var);
- // TODO
-}
-
-bool ReadShaderSamplerVariable(const rapidjson::Value& value, ShaderSamplerVariable& var) {
- if (!ReadShaderVariable(value, var)) return false;
- BRUSSEL_JSON_GET_DEFAULT(value, "ArrayLength", int, var.arrayLength, 1);
- return true;
-}
-} // namespace ProjectBrussel_UNITY_ID
-
-IresShader::IresShader()
- : IresObject(KD_Shader) {
- InvalidateInstance();
-}
-
-Shader* IresShader::GetInstance() const {
- return mInstance.Get();
-}
-
-void IresShader::InvalidateInstance() {
- if (mInstance != nullptr) {
- mInstance->mIres = nullptr;
- }
- mInstance.Attach(new Shader());
- mInstance->mIres = this;
-}
-
-void IresShader::ShowEditor(IEditor& editor) {
- using namespace Tags;
- using namespace ProjectBrussel_UNITY_ID;
-
- IresObject::ShowEditor(editor);
-
- if (ImGui::Button("Gather info")) {
- mInstance->GatherInfoShaderIntrospection();
- }
-
- if (ImGui::InputText("Source file", &mSourceFile, ImGuiInputTextFlags_EnterReturnsTrue)) {
- InvalidateInstance();
- }
- // In other cases, mSourceFile will be reverted to before edit
-
- // TODO macros
-
- ImGui::Separator();
-
- auto& info = mInstance->GetInfo();
- if (ImGui::CollapsingHeader("General")) {
- ImGui::Text("OpenGL program ID: %u", mInstance->GetProgram());
- }
- if (ImGui::CollapsingHeader("Inputs")) {
- for (auto& input : info.inputs) {
- input.ShowInfo();
- }
- }
- if (ImGui::CollapsingHeader("Outputs")) {
- for (auto& output : info.outputs) {
- output.ShowInfo();
- }
- }
- if (ImGui::CollapsingHeader("Uniforms")) {
- for (auto& uniform : info.uniforms) {
- uniform->ShowInfo();
- }
- if (auto loc = mInstance->autofill_Transform; loc != kInvalidLocation) {
- ImGui::BulletText("(Autofill)\nLocation: %d\nName: %s", loc, kAfnTransform);
- }
- if (auto loc = mInstance->autofill_Time; loc != kInvalidLocation) {
- ImGui::BulletText("(Autofill)\nLocation: %d\nName: %s", loc, kAfnTime);
- }
- if (auto loc = mInstance->autofill_DeltaTime; loc != kInvalidLocation) {
- ImGui::BulletText("(Autofill)\nLocation: %d\nName: %s", loc, kAfnDeltaTime);
- }
- if (auto loc = mInstance->autofill_TextureAtlas; loc != kInvalidLocation) {
- ImGui::BulletText("(Autofill)\nLocation: %d\nName: %s", loc, kAfnTextureAtlas);
- }
- }
-}
-
-void IresShader::Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const {
- using namespace ProjectBrussel_UNITY_ID;
-
- IresObject::Write(ctx, value, root);
-
- auto& shaderInfo = mInstance->mInfo;
-
- value.AddMember("SourceFile", mSourceFile, root.GetAllocator());
-
- auto SaveMathVars = [&](const char* name, const std::vector<ShaderMathVariable>& vars) {
- rapidjson::Value rvThings(rapidjson::kArrayType);
- for (auto& thing : vars) {
- rapidjson::Value rvThing(rapidjson::kObjectType);
- WriteShaderMathVariable(rvThing, root, thing);
-
- rvThings.PushBack(rvThing, root.GetAllocator());
- }
- value.AddMember(rapidjson::StringRef(name), rvThings, root.GetAllocator());
- };
- SaveMathVars("Inputs", shaderInfo.inputs);
- SaveMathVars("Outputs", shaderInfo.outputs);
-
- // TODO uniforms
-}
-
-void IresShader::Read(IresLoadingContext& ctx, const rapidjson::Value& value) {
- using namespace ProjectBrussel_UNITY_ID;
-
- IresObject::Read(ctx, value);
-
- auto rvSourceFile = rapidjson::GetProperty(value, rapidjson::kStringType, "SourceFile"sv);
- if (!rvSourceFile) {
- return;
- } else {
- this->mSourceFile = rapidjson::AsString(*rvSourceFile);
-
- char shaderFilePath[256];
- snprintf(shaderFilePath, sizeof(shaderFilePath), "%s/%s", AppConfig::assetDir.c_str(), rvSourceFile->GetString());
-
- auto shaderFile = Utils::OpenCstdioFile(shaderFilePath, Utils::Read);
- if (!shaderFile) return;
- DEFER { fclose(shaderFile); };
-
- fseek(shaderFile, 0, SEEK_END);
- auto shaderFileSize = ftell(shaderFile);
- rewind(shaderFile);
-
- // Also add \0 ourselves
- auto buffer = std::make_unique<char[]>(shaderFileSize + 1);
- fread(buffer.get(), shaderFileSize, 1, shaderFile);
- buffer[shaderFileSize] = '\0';
- std::string_view source(buffer.get(), shaderFileSize);
-
- if (mInstance->InitFromSource(source) != Shader::EC_Success) {
- return;
- }
- }
-
- auto& shaderInfo = mInstance->mInfo;
- auto shaderProgram = mInstance->GetProgram();
-
- auto LoadMathVars = [&](std::string_view name, ShaderThingId::Kind kind, std::vector<ShaderMathVariable>& vars) {
- auto rvThings = rapidjson::GetProperty(value, rapidjson::kArrayType, name);
- if (!rvThings) return;
-
- for (auto& rv : rvThings->GetArray()) {
- if (!rv.IsObject()) continue;
- ShaderMathVariable thing;
- ReadShaderMathVariable(rv, thing);
-
- shaderInfo.things.try_emplace(thing.name, ShaderThingId{ kind, (int)vars.size() });
- vars.push_back(std::move(thing));
- }
- };
- LoadMathVars("Inputs"sv, ShaderThingId::KD_Input, shaderInfo.inputs);
- LoadMathVars("Outputs"sv, ShaderThingId::KD_Output, shaderInfo.outputs);
-
- auto rvUniforms = rapidjson::GetProperty(value, rapidjson::kArrayType, "Uniforms"sv);
- if (!rvUniforms) return;
- for (auto& rvUniform : rvUniforms->GetArray()) {
- if (!rvUniform.IsObject()) continue;
-
- auto rvType = rapidjson::GetProperty(rvUniform, rapidjson::kStringType, "Type"sv);
- if (!rvType) continue;
- auto type = rapidjson::AsStringView(*rvType);
-
- auto rvValue = rapidjson::GetProperty(rvUniform, rapidjson::kObjectType, "Value"sv);
- if (!rvValue) continue;
-
- bool autoFill; // TODO store autofill uniforms somewhere else
- BRUSSEL_JSON_GET_DEFAULT(rvUniform, "AutoFill", bool, autoFill, false);
- if (autoFill) continue;
-
- auto uniform = [&]() -> std::unique_ptr<ShaderVariable> {
- if (type == "Math"sv) {
- auto uniform = std::make_unique<ShaderMathVariable>();
- ReadShaderMathVariable(*rvValue, *uniform);
-
- return uniform;
- } else if (type == "Sampler"sv) {
- auto uniform = std::make_unique<ShaderSamplerVariable>();
- ReadShaderSamplerVariable(*rvValue, *uniform);
-
- return uniform;
- }
-
- return nullptr;
- }();
- if (uniform) {
- shaderInfo.things.try_emplace(uniform->name, ShaderThingId{ ShaderThingId::KD_Uniform, (int)shaderInfo.uniforms.size() });
- shaderInfo.uniforms.emplace_back(std::move(uniform));
- }
- }
-
- for (auto& uniform : shaderInfo.uniforms) {
- uniform->location = glGetUniformLocation(shaderProgram, uniform->name.c_str());
- }
-}
diff --git a/source/Game/Shader.hpp b/source/Game/Shader.hpp
deleted file mode 100644
index 707e6cc..0000000
--- a/source/Game/Shader.hpp
+++ /dev/null
@@ -1,173 +0,0 @@
-#pragma once
-
-#include "GraphicsTags.hpp"
-#include "Ires.hpp"
-#include "RcPtr.hpp"
-#include "Utils.hpp"
-
-#include <glad/glad.h>
-#include <robin_hood.h>
-#include <memory>
-#include <string_view>
-#include <vector>
-
-// TODO move to variable after pattern matching is in the language
-
-// Forward declarations
-class Shader;
-class IresShader;
-
-struct ShaderVariable {
- enum Kind {
- KD_Math,
- KD_Sampler,
- };
-
- std::string name;
- Kind kind;
- GLuint location;
- Tags::VertexElementSemantic semantic = Tags::VES_Generic;
-
- virtual void ShowInfo() const = 0;
-
-protected:
- ShaderVariable(Kind kind)
- : kind{ kind } {}
-};
-
-struct ShaderMathVariable : public ShaderVariable {
- GLenum scalarType;
- int arrayLength;
- int width;
- int height;
-
- ShaderMathVariable()
- : ShaderVariable(KD_Math) {}
-
- virtual void ShowInfo() const override;
-};
-
-struct ShaderSamplerVariable : public ShaderVariable {
- GLenum type;
- int arrayLength;
-
- ShaderSamplerVariable()
- : ShaderVariable(KD_Sampler) {}
-
- virtual void ShowInfo() const override;
-};
-
-struct ShaderThingId {
- enum Kind {
- KD_Input,
- KD_Output,
- KD_Uniform,
- KD_Invalid,
- };
-
- Kind kind = KD_Invalid;
- int index = 0;
-
- bool IsValid() const;
-};
-
-struct ShaderInfo {
- robin_hood::unordered_map<std::string, ShaderThingId, StringHash, StringEqual> things;
- std::vector<ShaderMathVariable> inputs;
- std::vector<ShaderMathVariable> outputs;
- std::vector<std::unique_ptr<ShaderVariable>> uniforms;
-
- GLuint FindInputLocation(Tags::VertexElementSemantic semantic);
- GLuint FindOutputLocation(Tags::VertexElementSemantic semantic);
- ShaderVariable* FindVariable(const ShaderThingId& thing);
-};
-
-class Shader : public RefCounted {
- friend class IresShader;
-
-private:
- IresShader* mIres = nullptr;
- ShaderInfo mInfo;
- GLuint mProgram = 0;
-
-public:
- GLuint autofill_Transform = Tags::kInvalidLocation;
- GLuint autofill_Time = Tags::kInvalidLocation;
- GLuint autofill_DeltaTime = Tags::kInvalidLocation;
- GLuint autofill_TextureAtlas = Tags::kInvalidLocation;
-
-public:
- Shader();
- ~Shader();
- Shader(const Shader&) = delete;
- Shader& operator=(const Shader&) = delete;
- Shader(Shader&&) = default;
- Shader& operator=(Shader&&) = default;
-
- enum ErrorCode {
- EC_Success,
- /// Generated when Init*() functions are called on an already initialized Shader object.
- EC_AlreadyInitialized,
- /// Generated when the one-source-file text contains invalid or duplicate shader variants.
- EC_InvalidShaderVariant,
- EC_FileIoFailed,
- EC_CompilationFailed,
- EC_LinkingFailed,
- };
-
- struct ShaderSources {
- std::string_view vertex;
- std::string_view geometry;
- std::string_view tessControl;
- std::string_view tessEval;
- std::string_view fragment;
- };
-
- /// Create shader by compiling each shader source file separately and then combining them together
- /// into a Shader object.
- ErrorCode InitFromSources(const ShaderSources& sources);
-
- /// 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
- /// together.
- ///
- /// There are a directive for each shader type
- /// - `#type vertex`: Vertex shader
- /// - `#type geometry`: Geometry shader
- /// - `#type tessellation_control`: Tessellation control shader
- /// - `#type tessellation_evaluation`: Tessellation evaluation shader
- /// - `#type fragment`: Fragment shader
- ErrorCode InitFromSource(std::string_view source);
-
- /// Rebuild info object using OpenGL shader introspection API. Requires OpenGL 4.3 or above. Overrides existing info object.
- bool GatherInfoShaderIntrospection();
- const ShaderInfo& GetInfo() const { return mInfo; }
- ShaderInfo& GetInfo() { return mInfo; }
- /// If not empty, this name must not duplicate with any other shader object in the process.
- GLuint GetProgram() const { return mProgram; }
-
- IresShader* GetIres() const { return mIres; }
-
- bool IsValid() const;
-};
-
-// Initialized in main()
-inline RcPtr<Shader> gDefaultShader;
-
-class IresShader : public IresObject {
-private:
- RcPtr<Shader> mInstance;
- std::string mSourceFile;
-
-public:
- IresShader();
-
- Shader* GetInstance() const;
- void InvalidateInstance();
-
- void ShowEditor(IEditor& editor) override;
-
- void Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const override;
- void Read(IresLoadingContext& ctx, const rapidjson::Value& value) override;
-};
diff --git a/source/Game/Sprite.cpp b/source/Game/Sprite.cpp
deleted file mode 100644
index 2b4923c..0000000
--- a/source/Game/Sprite.cpp
+++ /dev/null
@@ -1,328 +0,0 @@
-#include "Sprite.hpp"
-
-#include "AppConfig.hpp"
-#include "CommonVertexIndex.hpp"
-#include "EditorCore.hpp"
-#include "EditorUtils.hpp"
-#include "Image.hpp"
-#include "RapidJsonHelper.hpp"
-#include "Rect.hpp"
-
-#include <imgui.h>
-#include <misc/cpp/imgui_stdlib.h>
-#include <rapidjson/document.h>
-#include <memory>
-
-using namespace std::literals;
-
-bool SpriteDefinition::IsValid() const {
- return mAtlas != nullptr;
-}
-
-bool IresSpriteFiles::IsValid() const {
- return !spriteFiles.empty();
-}
-
-SpriteDefinition* IresSpriteFiles::CreateInstance() const {
- if (IsValid()) {
- return nullptr;
- }
-
- std::vector<Texture::AtlasSource> sources;
- sources.resize(spriteFiles.size());
- for (auto& file : spriteFiles) {
- }
-
- Texture::AtlasOutput atlasOut;
- Texture::AtlasInput atlasIn{
- .sources = sources,
- .packingMode = Texture::PM_KeepSquare,
- };
- atlasIn.sources = sources;
- auto atlas = std::make_unique<Texture>();
- if (atlas->InitAtlas(atlasIn, &atlasOut) != Texture::EC_Success) {
- return nullptr;
- }
-
- auto sprite = std::make_unique<SpriteDefinition>();
- sprite->mAtlas.Attach(atlas.release());
- sprite->mBoundingBox = atlasOut.elements[0].subregionSize;
- sprite->mFrames.reserve(atlasOut.elements.size());
- for (auto& elm : atlasOut.elements) {
- // Validate bounding box
- if (sprite->mBoundingBox != elm.subregionSize) {
- return nullptr;
- }
-
- // Copy frame subregion
- sprite->mFrames.push_back(elm.subregion);
- }
- return sprite.release();
-}
-
-SpriteDefinition* IresSpriteFiles::GetInstance() {
- if (mInstance == nullptr) {
- mInstance.Attach(CreateInstance());
- }
- return mInstance.Get();
-}
-
-void IresSpriteFiles::InvalidateInstance() {
- mInstance.Attach(nullptr);
-}
-
-void IresSpriteFiles::Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const {
- IresObject::Write(ctx, value, root);
- value.AddMember("Sprites", rapidjson::WriteVectorPrimitives(root, spriteFiles.begin(), spriteFiles.end()), root.GetAllocator());
-}
-
-void IresSpriteFiles::Read(IresLoadingContext& ctx, const rapidjson::Value& value) {
- InvalidateInstance();
-
- IresObject::Read(ctx, value);
-
- auto rvFileList = rapidjson::GetProperty(value, rapidjson::kArrayType, "Sprites"sv);
- if (!rvFileList) return;
- spriteFiles.clear();
- rapidjson::ReadVectorPrimitives<decltype(spriteFiles)>(*rvFileList, spriteFiles);
-}
-
-bool IresSpritesheet::IsValid() const {
- return !spritesheetFile.empty() &&
- sheetWSplit != 0 &&
- sheetHSplit != 0;
-}
-
-void IresSpritesheet::ResplitSpritesheet(SpriteDefinition* sprite, const IresSpritesheet* conf) {
- ResplitSpritesheet(sprite, conf->sheetWSplit, conf->sheetHSplit, conf->frameCountOverride);
-}
-
-void IresSpritesheet::ResplitSpritesheet(SpriteDefinition* sprite, int wSplit, int hSplit, int frameCount) {
- auto atlas = sprite->GetAtlas();
- auto size = atlas->GetInfo().size;
- int frameWidth = size.x / wSplit;
- int frameHeight = size.y / hSplit;
-
- sprite->mBoundingBox = { frameWidth, frameHeight };
- sprite->mFrames.clear();
- sprite->mFrames.reserve(wSplit * hSplit);
-
- // Width and height in UV coordinates for each frame
- float deltaU = 1.0f / wSplit;
- float deltaV = 1.0f / hSplit;
- int i = 0;
- if (frameCount < 0) {
- frameCount = std::numeric_limits<int>::max();
- }
- for (int y = 0; y < hSplit; ++y) {
- for (int x = 0; x < wSplit; ++x) {
- auto& subregion = sprite->mFrames.emplace_back();
- // Top left
- subregion.u0 = deltaU * x;
- subregion.v0 = deltaV * y;
- // Bottom right
- subregion.u1 = subregion.u0 + deltaU;
- subregion.v1 = subregion.v0 + deltaV;
-
- if ((i + 1) >= frameCount) {
- return;
- }
-
- ++i;
- }
- }
-}
-
-SpriteDefinition* IresSpritesheet::CreateInstance() const {
- if (!IsValid()) {
- return nullptr;
- }
-
- char path[2048];
- snprintf(path, sizeof(path), "%s/%s", AppConfig::assetDir.c_str(), spritesheetFile.c_str());
-
- auto atlas = std::make_unique<Texture>();
- if (atlas->InitFromFile(path) != Texture::EC_Success) {
- return nullptr;
- }
-
- auto sprite = std::make_unique<SpriteDefinition>();
- sprite->mAtlas.Attach(atlas.release());
- ResplitSpritesheet(sprite.get(), this);
- return sprite.release();
-}
-
-SpriteDefinition* IresSpritesheet::GetInstance() {
- if (mInstance == nullptr) {
- mInstance.Attach(CreateInstance());
- }
- return mInstance.Get();
-}
-
-void IresSpritesheet::InvalidateInstance() {
- mInstance.Attach(nullptr);
-}
-
-bool IresSpritesheet::IsFrameCountOverriden() const {
- return frameCountOverride > 0;
-}
-
-int IresSpritesheet::GetFrameCount() const {
- if (IsFrameCountOverriden()) {
- return frameCountOverride;
- } else {
- return sheetWSplit * sheetHSplit;
- }
-}
-
-void IresSpritesheet::ShowEditor(IEditor& editor) {
- IresObject::ShowEditor(editor);
-
- bool doInvalidateInstance = false;
- auto instance = GetInstance(); // NOTE: may be null
-
- if (ImGui::Button("View Sprite", instance == nullptr)) {
- editor.OpenSpriteViewer(instance);
- }
-
- if (ImGui::InputText("Spritesheet", &spritesheetFile)) {
- doInvalidateInstance = true;
- }
-
- if (ImGui::InputInt("Horizontal split", &sheetWSplit)) {
- sheetWSplit = std::max(sheetWSplit, 1);
- if (instance) IresSpritesheet::ResplitSpritesheet(instance, this);
- }
-
- if (ImGui::InputInt("Vertical split", &sheetHSplit)) {
- sheetHSplit = std::max(sheetHSplit, 1);
- if (instance) IresSpritesheet::ResplitSpritesheet(instance, this);
- }
-
- bool frameCountOverriden = frameCountOverride > 0;
- if (ImGui::Checkbox("##", &frameCountOverriden)) {
- if (frameCountOverriden) {
- frameCountOverride = sheetWSplit * sheetHSplit;
- } else {
- frameCountOverride = 0;
- }
- }
- ImGui::SameLine();
- if (frameCountOverriden) {
- if (ImGui::InputInt("Frame count", &frameCountOverride)) {
- frameCountOverride = std::max(frameCountOverride, 1);
- if (instance) IresSpritesheet::ResplitSpritesheet(instance, this);
- }
- } else {
- int dummy = sheetWSplit * sheetHSplit;
- ImGui::PushDisabled();
- ImGui::InputInt("Frame count", &dummy, ImGuiInputTextFlags_ReadOnly);
- ImGui::PopDisabled();
- }
-
- if (instance) {
- auto atlas = instance->GetAtlas();
- auto imageSize = Utils::FitImage(atlas->GetInfo().size);
- auto imagePos = ImGui::GetCursorScreenPos();
- ImGui::Image((ImTextureID)(uintptr_t)atlas->GetHandle(), imageSize);
-
- auto drawlist = ImGui::GetWindowDrawList();
- float deltaX = imageSize.x / sheetWSplit;
- for (int ix = 0; ix < sheetWSplit + 1; ++ix) {
- float x = ix * deltaX;
- ImVec2 start{ imagePos.x + x, imagePos.y };
- ImVec2 end{ imagePos.x + x, imagePos.y + imageSize.y };
- drawlist->AddLine(start, end, IM_COL32(255, 255, 0, 255));
- }
- float deltaY = imageSize.y / sheetHSplit;
- for (int iy = 0; iy < sheetHSplit + 1; ++iy) {
- float y = iy * deltaY;
- ImVec2 start{ imagePos.x, imagePos.y + y };
- ImVec2 end{ imagePos.x + imageSize.x, imagePos.y + y };
- drawlist->AddLine(start, end, IM_COL32(255, 255, 0, 255));
- }
-
- int i = sheetWSplit * sheetHSplit;
- int frameCount = GetFrameCount();
- for (int y = sheetHSplit - 1; y >= 0; --y) {
- for (int x = sheetWSplit - 1; x >= 0; --x) {
- if (i > frameCount) {
- ImVec2 tl{ imagePos.x + x * deltaX + 1.0f, imagePos.y + y * deltaY + 1.0f };
- ImVec2 br{ imagePos.x + (x + 1) * deltaX, imagePos.y + (y + 1) * deltaY };
- drawlist->AddRectFilled(tl, br, IM_COL32(255, 0, 0, 100));
- }
- --i;
- }
- }
- } else {
- ImGui::TextUnformatted("Sprite configuration invalid");
- }
-
- if (doInvalidateInstance) {
- InvalidateInstance();
- }
-}
-
-void IresSpritesheet::Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const {
- IresObject::Write(ctx, value, root);
- value.AddMember("SpriteSheet", spritesheetFile, root.GetAllocator());
- value.AddMember("WSplit", sheetWSplit, root.GetAllocator());
- value.AddMember("HSplit", sheetHSplit, root.GetAllocator());
- if (frameCountOverride > 0) {
- value.AddMember("FrameCount", frameCountOverride, root.GetAllocator());
- }
-}
-
-void IresSpritesheet::Read(IresLoadingContext& ctx, const rapidjson::Value& value) {
- InvalidateInstance();
-
- IresObject::Read(ctx, value);
- BRUSSEL_JSON_GET(value, "SpriteSheet", std::string, spritesheetFile, return );
- BRUSSEL_JSON_GET(value, "WSplit", int, sheetWSplit, return );
- BRUSSEL_JSON_GET(value, "HSplit", int, sheetHSplit, return );
- BRUSSEL_JSON_GET_DEFAULT(value, "FrameCount", int, frameCountOverride, 0);
-}
-
-Sprite::Sprite()
- : mDefinition(nullptr) {
-}
-
-bool Sprite::IsValid() const {
- return mDefinition != nullptr;
-}
-
-void Sprite::SetDefinition(SpriteDefinition* definition) {
- mDefinition.Attach(definition);
- mCurrentFrame = 0;
-}
-
-int Sprite::GetFrame() const {
- return mCurrentFrame;
-}
-
-const Subregion& Sprite::GetFrameSubregion() const {
- return mDefinition->GetFrames()[mCurrentFrame];
-}
-
-void Sprite::SetFrame(int frame) {
- mCurrentFrame = frame;
-}
-
-void Sprite::PlayFrame() {
- ++mTimeElapsed;
- if (mTimeElapsed >= mPlaybackSpeed) {
- mTimeElapsed -= mPlaybackSpeed;
-
- int frameCount = mDefinition->GetFrames().size();
- int nextFrame = (mCurrentFrame + 1) % frameCount;
- SetFrame(nextFrame);
- }
-}
-
-int Sprite::GetPlaybackSpeed() const {
- return mPlaybackSpeed;
-}
-
-void Sprite::SetPlaybackSpeed(int speed) {
- // TODO
-}
diff --git a/source/Game/Sprite.hpp b/source/Game/Sprite.hpp
deleted file mode 100644
index e163a01..0000000
--- a/source/Game/Sprite.hpp
+++ /dev/null
@@ -1,111 +0,0 @@
-#pragma once
-
-#include "Ires.hpp"
-#include "PodVector.hpp"
-#include "RcPtr.hpp"
-#include "Renderer.hpp"
-#include "Texture.hpp"
-
-#include <rapidjson/fwd.h>
-#include <glm/glm.hpp>
-#include <string>
-#include <string_view>
-#include <vector>
-
-class SpriteDefinition : public RefCounted {
- friend class IresSpriteFiles;
- friend class IresSpritesheet;
-
-private:
- RcPtr<Texture> mAtlas;
- glm::ivec2 mBoundingBox;
- std::vector<Subregion> mFrames;
-
-public:
- bool IsValid() const;
- Texture* GetAtlas() const { return mAtlas.Get(); }
- glm::ivec2 GetBoundingBox() const { return mBoundingBox; }
- const decltype(mFrames)& GetFrames() const { return mFrames; }
-};
-
-class IresSpriteFiles : public IresObject {
-public:
- RcPtr<SpriteDefinition> mInstance;
- std::vector<std::string> spriteFiles;
-
-public:
- IresSpriteFiles()
- : IresObject(KD_SpriteFiles) {}
-
- // NOTE: does not check whether all specified files have the same dimensions
- bool IsValid() const;
-
- SpriteDefinition* CreateInstance() const;
- SpriteDefinition* GetInstance();
- void InvalidateInstance();
-
- void Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const override;
- void Read(IresLoadingContext& ctx, const rapidjson::Value& value) override;
-};
-
-class IresSpritesheet : public IresObject {
-public:
- RcPtr<SpriteDefinition> mInstance;
- std::string spritesheetFile;
- int sheetWSplit = 1;
- int sheetHSplit = 1;
- int frameCountOverride = 0;
-
-public:
- IresSpritesheet()
- : IresObject(KD_Spritesheet) {}
-
- bool IsValid() const;
-
- static void ResplitSpritesheet(SpriteDefinition* sprite, const IresSpritesheet* conf);
- static void ResplitSpritesheet(SpriteDefinition* sprite, int wSplit, int hSplit, int frameCountOverride = -1);
-
- SpriteDefinition* CreateInstance() const;
- SpriteDefinition* GetInstance();
- void InvalidateInstance();
-
- bool IsFrameCountOverriden() const;
- int GetFrameCount() const;
-
- void ShowEditor(IEditor& editor) override;
-
- void Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const override;
- void Read(IresLoadingContext& ctx, const rapidjson::Value& value) override;
-};
-
-// TODO
-class SpriteCollection {
-private:
- std::vector<SpriteDefinition> mSprites;
-};
-
-class Sprite {
-private:
- RcPtr<SpriteDefinition> mDefinition;
- int mCurrentFrame = 0;
- int mTimeElapsed = 0;
- // # of frames per second
- int mPlaybackSpeed = 5;
-
-public:
- Sprite();
-
- bool IsValid() const;
-
- SpriteDefinition* GetDefinition() const { return mDefinition.Get(); }
- void SetDefinition(SpriteDefinition* definition);
-
- int GetFrame() const;
- const Subregion& GetFrameSubregion() const;
- void SetFrame(int frame);
- // Update as if a render frame has passed
- void PlayFrame();
-
- int GetPlaybackSpeed() const;
- void SetPlaybackSpeed(int speed);
-};
diff --git a/source/Game/Texture.cpp b/source/Game/Texture.cpp
deleted file mode 100644
index 6fa7c8a..0000000
--- a/source/Game/Texture.cpp
+++ /dev/null
@@ -1,250 +0,0 @@
-#include "Texture.hpp"
-
-#include "Macros.hpp"
-#include "PodVector.hpp"
-#include "ScopeGuard.hpp"
-
-#include <stb_image.h>
-#include <stb_rect_pack.h>
-#include <bit>
-#include <cstring>
-#include <utility>
-
-Texture::~Texture() {
- glDeleteTextures(1, &mHandle);
-}
-
-static GLenum MapTextureFilteringToGL(Tags::TexFilter option) {
- using namespace Tags;
- switch (option) {
- case TF_Linear: return GL_LINEAR;
- case TF_Nearest: return GL_NEAREST;
- }
- return 0;
-}
-
-Texture::ErrorCode Texture::InitFromFile(const char* filePath) {
- if (IsValid()) {
- return EC_AlreadyInitialized;
- }
-
- int width, height;
- int channels;
-
- auto result = (uint8_t*)stbi_load(filePath, &width, &height, &channels, 4);
- if (!result) {
- return EC_FileIoFailed;
- }
- DEFER { stbi_image_free(result); };
-
- glGenTextures(1, &mHandle);
- glBindTexture(GL_TEXTURE_2D, mHandle);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, result);
-
- mInfo.size = { width, height };
-
- return EC_Success;
-}
-
-Texture::ErrorCode Texture::InitFromImage(const Image& image) {
- if (IsValid()) {
- return EC_AlreadyInitialized;
- }
-
- GLenum sourceFormat;
- switch (image.GetChannels()) {
- case 1: sourceFormat = GL_RED; break;
- case 2: sourceFormat = GL_RG; break;
- case 3: sourceFormat = GL_RGB; break;
- case 4: sourceFormat = GL_RGBA; break;
- default: return EC_InvalidImage;
- }
-
- auto size = image.GetSize();
- uint8_t* dataPtr = image.GetDataPtr();
-
- glGenTextures(1, &mHandle);
- glBindTexture(GL_TEXTURE_2D, mHandle);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexImage2D(GL_TEXTURE_2D, 0, sourceFormat, size.x, size.y, 0, sourceFormat, GL_UNSIGNED_BYTE, dataPtr);
-
- mInfo.size = size;
-
- return EC_Success;
-}
-
-Texture::ErrorCode Texture::InitAtlas(const AtlasInput& in, AtlasOutput* out) {
- // Force RGBA for easier time uploading to GL texture
- constexpr int kDesiredChannels = 4;
-
- PodVector<stbrp_rect> rects;
- rects.resize(in.sources.size());
-
- for (size_t i = 0; i < in.sources.size(); ++i) {
- auto size = in.sources[i].image.GetSize();
- auto& rect = rects[i];
- rect.w = static_cast<stbrp_coord>(size.x);
- rect.h = static_cast<stbrp_coord>(size.y);
- }
-
- int atlasWidth;
- int atlasHeight;
-
- // 1. Pack the candidate rectanges onto the (not yet allocated) atlas
- // Note that the coordinates here are top-left origin
- switch (in.packingMode) {
- case PM_KeepSquare: {
- atlasWidth = 512;
- atlasHeight = 512;
-
- PodVector<stbrp_node> nodes;
- while (true) {
- // No need to zero initialize stbrp_node, library will take care of that
- nodes.resize(atlasWidth);
-
- stbrp_context ctx;
- stbrp_init_target(&ctx, atlasWidth, atlasHeight, &nodes[0], (int)nodes.size());
- int result = stbrp_pack_rects(&ctx, rects.data(), (int)rects.size());
-
- if (result != 1) {
- atlasWidth *= 2;
- atlasHeight *= 2;
- } else {
- // Break out of the while loop
- break;
- }
- }
- } break;
-
- case PM_VerticalExtension:
- case PM_HorizontalExtension: {
- constexpr int kMaxHeight = 1024 * 32;
- atlasWidth = 0;
- atlasHeight = 0;
-
- PodVector<stbrp_node> nodes;
- stbrp_context ctx;
- stbrp_init_target(&ctx, atlasWidth, atlasHeight, &nodes[0], nodes.size());
- stbrp_pack_rects(&ctx, rects.data(), rects.size());
-
- // Calculate width/height needed for atlas
- auto& limiter = in.packingMode == PM_VerticalExtension ? atlasHeight : atlasWidth;
- for (auto& rect : rects) {
- int bottom = rect.y + rect.h;
- limiter = std::max(limiter, bottom);
- }
- limiter = std::bit_ceil<uint32_t>(limiter);
- } break;
- }
-
- // 2. Allocate atlas bitmap
-
- // Number of bytes in *bitmap*
- auto bytes = atlasWidth * atlasHeight * kDesiredChannels * sizeof(uint8_t);
- // Note that the origin (first pixel) is the bottom-left corner, to be consistent with OpenGL
- auto bitmap = std::make_unique<uint8_t[]>(bytes);
- std::memset(bitmap.get(), 0, bytes * sizeof(uint8_t));
-
- // 3. Put all candidate images to the atlas bitmap
- // TODO don't flip
- // We essentially flip the candidate images vertically when putting into the atlas bitmap, so that when OpenGL reads
- // these bytes, it sees the "bottom row" (if talking in top-left origin) first
- // (empty spots are set with 0, "flipping" doesn't apply to them)
- //
- // Conceptually, we flip the atlas bitmap vertically so that the origin is at bottom-left
- // i.e. all the coordinates we talk (e.g. rect.x/y) are still in top-left origin
-
- // Unit: bytes
- size_t bitmapRowStride = atlasWidth * kDesiredChannels * sizeof(uint8_t);
- for (size_t i = 0; i < in.sources.size(); ++i) {
- auto& rect = rects[i];
- // Data is assumed to be stored in top-left origin
- auto data = in.sources[i].image.GetDataPtr();
-
- // We need to copy row by row, because the candidate image bytes won't land in a continuous chunk in our atlas bitmap
- // Unit: bytes
- size_t incomingRowStride = rect.w * kDesiredChannels * sizeof(uint8_t);
- // Unit: bytes
- size_t bitmapX = rect.x * kDesiredChannels * sizeof(uint8_t);
- for (int y = 0; y < rect.h; ++y) {
- auto src = data + y * incomingRowStride;
-
- int bitmapY = y;
- auto dst = bitmap.get() + bitmapY * bitmapRowStride + bitmapX;
-
- std::memcpy(dst, src, incomingRowStride);
- }
- }
-
- // 4. Upload to VRAM
- GLuint atlasTexture;
- glGenTextures(1, &atlasTexture);
- glBindTexture(GL_TEXTURE_2D, atlasTexture);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, atlasWidth, atlasHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap.get());
-
- // 5. Generate atlas texture info
- mHandle = atlasTexture;
- mInfo.size = { atlasWidth, atlasHeight };
-
- // 6. Generate output information
- if (out) {
- out->elements.reserve(in.sources.size());
- for (size_t i = 0; i < in.sources.size(); ++i) {
- auto& rect = rects[i];
- auto& source = in.sources[i];
- out->elements.push_back(AltasElement{
- .name = source.name,
- .subregion = Subregion{
- .u0 = (float)(rect.x) / atlasWidth,
- .v0 = (float)(rect.y + rect.h) / atlasHeight,
- .u1 = (float)(rect.x + rect.w) / atlasWidth,
- .v1 = (float)(rect.y) / atlasHeight,
- },
- .subregionSize = glm::ivec2(rect.w, rect.h),
- });
- }
- }
-
- return EC_Success;
-}
-
-const TextureInfo& Texture::GetInfo() const {
- return mInfo;
-}
-
-GLuint Texture::GetHandle() const {
- return mHandle;
-}
-
-bool Texture::IsValid() const {
- return mHandle != 0;
-}
-
-Texture* IresTexture::CreateInstance() const {
- return new Texture();
-}
-
-Texture* IresTexture::GetInstance() {
- if (mInstance == nullptr) {
- mInstance.Attach(CreateInstance());
- }
- return mInstance.Get();
-}
-
-void IresTexture::Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const {
- IresObject::Write(ctx, value, root);
- // TODO
-}
-
-void IresTexture::Read(IresLoadingContext& ctx, const rapidjson::Value& value) {
- IresObject::Read(ctx, value);
- // TODO
-}
diff --git a/source/Game/Texture.hpp b/source/Game/Texture.hpp
deleted file mode 100644
index 108dfa7..0000000
--- a/source/Game/Texture.hpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#pragma once
-
-#include "GraphicsTags.hpp"
-#include "Image.hpp"
-#include "Ires.hpp"
-#include "RcPtr.hpp"
-
-#include <glad/glad.h>
-#include <cstdint>
-#include <glm/glm.hpp>
-#include <span>
-
-// TODO abstract texture traits such as component sizes from OpenGL
-
-struct Subregion {
- float u0 = 0.0f;
- float v0 = 0.0f;
- float u1 = 0.0f;
- float v1 = 0.0f;
-};
-
-struct TextureInfo {
- glm::ivec2 size;
- Tags::TexFilter minifyingFilter = Tags::TF_Linear;
- Tags::TexFilter magnifyingFilter = Tags::TF_Linear;
-};
-
-class Texture : public RefCounted {
- friend class TextureStitcher;
-
-private:
- TextureInfo mInfo;
- GLuint mHandle = 0;
-
-public:
- Texture() = default;
- ~Texture();
-
- Texture(const Texture&) = delete;
- Texture& operator=(const Texture&) = delete;
- Texture(Texture&&) = default;
- Texture& operator=(Texture&&) = default;
-
- enum ErrorCode {
- EC_Success,
- EC_AlreadyInitialized,
- EC_FileIoFailed,
- EC_InvalidImage,
- };
-
- ErrorCode InitFromFile(const char* filePath);
- ErrorCode InitFromImage(const Image& image);
-
- struct AtlasSource {
- std::string name;
- Image image;
- };
-
- struct AltasElement {
- std::string name;
- Subregion subregion;
- glm::ivec2 subregionSize;
- };
-
- enum PackingMode {
- PM_KeepSquare,
- PM_VerticalExtension,
- PM_HorizontalExtension,
- };
-
- struct AtlasInput {
- std::span<AtlasSource> sources;
- PackingMode packingMode;
- };
- struct AtlasOutput {
- std::vector<AltasElement> elements;
- };
- ErrorCode InitAtlas(const AtlasInput& in, AtlasOutput* out = nullptr);
-
- const TextureInfo& GetInfo() const;
- GLuint GetHandle() const;
-
- bool IsValid() const;
-};
-
-class IresTexture : public IresObject {
-public:
- RcPtr<Texture> mInstance;
-
-public:
- IresTexture()
- : IresObject(KD_Texture) {}
-
- Texture* CreateInstance() const;
- Texture* GetInstance();
-
- void Write(IresWritingContext& ctx, rapidjson::Value& value, rapidjson::Document& root) const override;
- void Read(IresLoadingContext& ctx, const rapidjson::Value& value) override;
-};
diff --git a/source/Game/VertexIndex.cpp b/source/Game/VertexIndex.cpp
deleted file mode 100644
index ac68289..0000000
--- a/source/Game/VertexIndex.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-#include "VertexIndex.hpp"
-
-#include <algorithm>
-
-GpuVertexBuffer::GpuVertexBuffer() {
- glGenBuffers(1, &handle);
-}
-
-GpuVertexBuffer::~GpuVertexBuffer() {
- glDeleteBuffers(1, &handle);
-}
-
-void GpuVertexBuffer::Upload(const std::byte* data, size_t sizeInBytes) {
- glBindBuffer(GL_ARRAY_BUFFER, handle);
- glBufferData(GL_ARRAY_BUFFER, sizeInBytes, data, GL_DYNAMIC_DRAW);
-}
-
-GpuIndexBuffer::GpuIndexBuffer() {
- glGenBuffers(1, &handle);
-}
-
-GpuIndexBuffer::~GpuIndexBuffer() {
- glDeleteBuffers(1, &handle);
-}
-
-void GpuIndexBuffer::Upload(const std::byte* data, Tags::IndexType type, size_t count) {
- this->indexType = type;
- this->count = count;
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * Tags::SizeOf(type), data, GL_DYNAMIC_DRAW);
-}
-
-int BufferBindings::GetMaxBindingIndex() const {
- return bindings.size() - 1;
-}
-
-GpuVertexBuffer* BufferBindings::GetBinding(int index) const {
- if (index >= 0 && index < bindings.size()) {
- return bindings[index].Get();
- } else {
- return nullptr;
- }
-}
-
-void BufferBindings::SetBinding(int index, GpuVertexBuffer* buffer) {
- int maxBindingIndex = GetMaxBindingIndex();
- if (index > maxBindingIndex) {
- int countDelta = index - maxBindingIndex;
- bindings.resize(bindings.size() + countDelta);
- }
-
- bindings[index].Attach(buffer);
- if (index == maxBindingIndex && buffer == nullptr) {
- bindings.pop_back();
- }
-}
-
-void BufferBindings::Clear() {
- bindings.clear();
-}
-
-int VertexElementFormat::GetStride() const {
- return Tags::SizeOf(type);
-}
-
-void VertexFormat::AddElement(VertexElementFormat element) {
- vertexSize += element.GetStride();
-
- int lastIdx = (int)elements.size() - 1;
- if (lastIdx >= 0) {
- auto& last = elements[lastIdx];
- element.offset = last.offset + last.GetStride();
- } else {
- element.offset = 0;
- }
-
- elements.push_back(std::move(element));
-}
-
-void VertexFormat::RemoveElement(int index) {
- auto& element = elements[index];
- vertexSize -= element.GetStride();
- elements.erase(elements.begin() + index);
-}
diff --git a/source/Game/VertexIndex.hpp b/source/Game/VertexIndex.hpp
deleted file mode 100644
index 2d65617..0000000
--- a/source/Game/VertexIndex.hpp
+++ /dev/null
@@ -1,67 +0,0 @@
-#pragma once
-
-#include "GraphicsTags.hpp"
-#include "RcPtr.hpp"
-#include "SmallVector.hpp"
-
-#include <glad/glad.h>
-#include <cstddef>
-#include <cstdint>
-#include <vector>
-
-struct GpuVertexBuffer : public RefCounted {
- GLuint handle;
- int sizeInBytes;
-
- GpuVertexBuffer();
- ~GpuVertexBuffer();
-
- void Upload(const std::byte* data, size_t sizeInBytes);
-};
-
-struct GpuIndexBuffer : public RefCounted {
- GLuint handle;
- Tags::IndexType indexType;
- int count;
-
- GpuIndexBuffer();
- ~GpuIndexBuffer();
-
- Tags::IndexType GetIndexType() const { return indexType; }
- GLenum GetIndexTypeGL() const { return Tags::FindGLType(indexType); }
-
- void Upload(const std::byte* data, Tags::IndexType type, size_t count);
-};
-
-struct BufferBindings {
- SmallVector<RcPtr<GpuVertexBuffer>, 4> bindings;
-
- int GetMaxBindingIndex() const;
-
- /// Safe. Returns nullptr if the index is not bound to any buffers.
- GpuVertexBuffer* GetBinding(int index) const;
- /// Adds or updates a buffer binding. Setting a binding to nullptr effectively removes the binding.
- void SetBinding(int index, GpuVertexBuffer* buffer);
- void Clear();
-};
-
-struct VertexElementFormat {
- /// NOTE:
- /// "Automatic" means it will be set inside VertexFormat::AddElement()
- /// "Parameter" means it must be set by the user
- /* Automatic */ int offset;
- /* Parameter */ int bindingIndex;
- /* Parameter */ Tags::VertexElementType type;
- /* Parameter */ Tags::VertexElementSemantic semantic;
-
- int GetStride() const;
-};
-
-struct VertexFormat : public RefCounted {
- SmallVector<VertexElementFormat, 4> elements;
- int vertexSize = 0;
-
- const decltype(elements)& GetElements() const { return elements; }
- void AddElement(VertexElementFormat element);
- void RemoveElement(int index);
-};
diff --git a/source/Game/World.cpp b/source/Game/World.cpp
deleted file mode 100644
index d4a8344..0000000
--- a/source/Game/World.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "World.hpp"
-
-#include "GameObject.hpp"
-#include "PodVector.hpp"
-
-#include <glad/glad.h>
-
-namespace ProjectBrussel_UNITY_ID {
-template <class TFunction>
-void CallGameObjectRecursive(GameObject* start, TFunction&& func) {
- PodVector<GameObject*> stack;
- stack.push_back(start);
-
- while (!stack.empty()) {
- auto obj = stack.back();
- stack.pop_back();
-
- for (auto child : obj->GetChildren()) {
- stack.push_back(child);
- }
-
- func(obj);
- }
-}
-
-struct DrawCall {
- GLuint vao;
- GLuint vbo;
-};
-} // namespace ProjectBrussel_UNITY_ID
-
-GameWorld::GameWorld()
- : mRoot{ new GameObject(this) } {
-}
-
-GameWorld::~GameWorld() {
- if (mAwakened) {
- Resleep();
- }
-
- delete mRoot;
-}
-
-const GameObject& GameWorld::GetRoot() const {
- return *mRoot;
-};
-
-void GameWorld::Awaken() {
- if (mAwakened) return;
-
- ProjectBrussel_UNITY_ID::CallGameObjectRecursive(mRoot, [](GameObject* obj) { obj->Awaken(); });
- mAwakened = true;
-}
-
-void GameWorld::Resleep() {
- if (!mAwakened) return;
-
- ProjectBrussel_UNITY_ID::CallGameObjectRecursive(mRoot, [](GameObject* obj) { obj->Resleep(); });
- mAwakened = false;
-}
-
-void GameWorld::Update() {
- ProjectBrussel_UNITY_ID::CallGameObjectRecursive(mRoot, [this](GameObject* obj) {
- obj->Update();
- });
-}
-
-GameObject& GameWorld::GetRoot() {
- return *mRoot;
-}
-
-bool GameWorld::IsAwake() const {
- return mAwakened;
-}
diff --git a/source/Game/World.hpp b/source/Game/World.hpp
deleted file mode 100644
index 288142e..0000000
--- a/source/Game/World.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-class GameObject;
-class GameWorld {
-private:
- GameObject* mRoot;
- bool mAwakened = false;
-
-public:
- GameWorld();
- ~GameWorld();
-
- GameWorld(const GameWorld&) = delete;
- GameWorld& operator=(const GameWorld&) = delete;
- GameWorld(GameWorld&&) = default;
- GameWorld& operator=(GameWorld&&) = default;
-
- bool IsAwake() const;
- void Awaken();
- void Resleep();
- void Update();
-
- const GameObject& GetRoot() const;
- GameObject& GetRoot();
-};
diff --git a/source/Game/buildfile b/source/Game/buildfile
deleted file mode 100644
index e69de29..0000000
--- a/source/Game/buildfile
+++ /dev/null
diff --git a/source/Game/main.cpp b/source/Game/main.cpp
deleted file mode 100644
index c49fc0b..0000000
--- a/source/Game/main.cpp
+++ /dev/null
@@ -1,461 +0,0 @@
-#include "App.hpp"
-
-#include "AppConfig.hpp"
-#include "CommonVertexIndex.hpp"
-#include "EditorGuizmo.hpp"
-#include "Ires.hpp"
-#include "Level.hpp"
-#include "Material.hpp"
-#include "Shader.hpp"
-
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
-
-#include <backends/imgui_impl_glfw.h>
-#include <backends/imgui_impl_opengl2.h>
-#include <backends/imgui_impl_opengl3.h>
-#include <glad/glad.h>
-#include <imgui.h>
-#include <imgui_internal.h>
-#include <cstdlib>
-#include <cxxopts.hpp>
-#include <filesystem>
-#include <string>
-
-#include <tracy/Tracy.hpp>
-#include <tracy/TracyClient.cpp>
-
-namespace fs = std::filesystem;
-using namespace std::literals;
-
-struct GlfwUserData {
- App* app = nullptr;
-};
-
-void GlfwErrorCallback(int error, const char* description) {
- fprintf(stderr, "[GLFW] Error %d: %s\n", error, description);
-}
-
-void GlfwFramebufferResizeCallback(GLFWwindow* window, int width, int height) {
- AppConfig::mainWindowWidth = width;
- AppConfig::mainWindowHeight = height;
- AppConfig::mainWindowAspectRatio = (float)width / height;
-}
-
-void GlfwMouseCallback(GLFWwindow* window, int button, int action, int mods) {
- if (ImGui::GetIO().WantCaptureMouse) {
- return;
- }
-
- auto userData = static_cast<GlfwUserData*>(glfwGetWindowUserPointer(window));
- auto app = userData->app;
- app->HandleMouse(button, action);
-}
-
-void GlfwMouseMotionCallback(GLFWwindow* window, double xOff, double yOff) {
- if (ImGui::GetIO().WantCaptureMouse) {
- return;
- }
-
- auto userData = static_cast<GlfwUserData*>(glfwGetWindowUserPointer(window));
- auto app = userData->app;
- app->HandleMouseMotion(xOff, yOff);
-}
-
-void GlfwKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
- if (ImGui::GetIO().WantCaptureKeyboard) {
- return;
- }
-
- GLFWkeyboard* keyboard = glfwGetLastActiveKeyboard();
- if (keyboard) {
- auto userData = static_cast<GlfwUserData*>(glfwGetWindowUserPointer(window));
- auto app = userData->app;
- app->HandleKey(keyboard, key, action);
- }
-}
-
-// https://stackoverflow.com/questions/54499256/how-to-find-the-saved-games-folder-programmatically-in-c-c
-#if defined(_WIN32)
-# if defined(__MINGW32__)
-# include <ShlObj.h>
-# else
-# include <ShlObj_core.h>
-# endif
-# include <objbase.h>
-# pragma comment(lib, "shell32.lib")
-# pragma comment(lib, "ole32.lib")
-#elif defined(__linux__)
-fs::path GetEnvVar(const char* name, const char* backup) {
- if (const char* path = std::getenv(name)) {
- fs::path dataDir(path);
- fs::create_directories(dataDir);
- return dataDir;
- } else {
- fs::path dataDir(backup);
- fs::create_directories(dataDir);
- return dataDir;
- }
-}
-#endif
-
-int main(int argc, char* argv[]) {
- using namespace Tags;
-
- constexpr auto kImGuiBackend = "imgui-backend";
- 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()
- (kImGuiBackend, "ImGui backend. Options: opengl2, opengl3. Leave empty to default.", cxxopts::value<std::string>())
- (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);
-
- bool imguiUseOpenGL3;
- if (args.count(kImGuiBackend) > 0) {
- auto imguiBackend = args[kImGuiBackend].as<std::string>();
- if (imguiBackend == "opengl2") {
- imguiUseOpenGL3 = false;
- } else if (imguiBackend == "opengl3") {
- imguiUseOpenGL3 = true;
- } else {
- // TODO support more backends?
- imguiUseOpenGL3 = true;
- }
- } else {
- imguiUseOpenGL3 = true;
- }
-
- if (args.count(kGameAssetDir) > 0) {
- 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);
- } else {
-#if defined(_WIN32)
- fs::path dataDir;
-
- PWSTR path = nullptr;
- HRESULT hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, nullptr, &path);
- if (SUCCEEDED(hr)) {
- dataDir = fs::path(path) / AppConfig::kAppName;
- CoTaskMemFree(path);
-
- fs::create_directories(dataDir);
- } else {
- std::string msg;
- msg += "Failed to find/create the default user data directory at %APPDATA%. Error code: ";
- msg += hr;
- throw std::runtime_error(msg);
- }
-#elif defined(__APPLE__)
- // MacOS programming guide recommends apps to hardcode the path - user customization of "where data are stored" is done in Finder
- auto dataDir = fs::path("~/Library/Application Support/") / AppConfig::kAppName;
- fs::create_directories(dataDir);
-#elif defined(__linux__)
- auto dataDir = GetEnvVar("XDG_DATA_HOME", "~/.local/share") / AppConfig::kAppName;
- fs::create_directories(dataDir);
-#endif
- }
-
- if (args.count(kGameDataDir) > 0) {
- auto dataDir = args[kGameDataDir].as<std::string>();
-
- fs::path dataDirPath(dataDir);
- fs::create_directories(dataDir);
-
- AppConfig::dataDir = std::move(dataDir);
- AppConfig::dataDirPath = std::move(dataDirPath);
- } else {
- // TODO platform default path
- AppConfig::dataDir = ".";
- AppConfig::dataDirPath = fs::path(".");
- }
-
- if (!glfwInit()) {
- return -1;
- }
-
- glfwSetErrorCallback(&GlfwErrorCallback);
-
- glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
- glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
-
-#if defined(__APPLE__)
- const char* imguiGlslVersion = "#version 150";
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
-#else
- const char* imguiGlslVersion = "#version 130";
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
-#endif
-
- GlfwUserData glfwUserData;
-
- GLFWwindow* window = glfwCreateWindow(1280, 720, "ImGui Command Palette Example", nullptr, nullptr);
- if (window == nullptr) {
- return -2;
- }
-
- glfwSetWindowUserPointer(window, &glfwUserData);
-
- // Window callbacks are retained by ImGui GLFW backend
- glfwSetFramebufferSizeCallback(window, &GlfwFramebufferResizeCallback);
- glfwSetKeyCallback(window, &GlfwKeyCallback);
- glfwSetMouseButtonCallback(window, &GlfwMouseCallback);
- glfwSetCursorPosCallback(window, &GlfwMouseMotionCallback);
-
- {
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
- GlfwFramebufferResizeCallback(window, width, height);
- }
-
- glfwMakeContextCurrent(window);
- glfwSwapInterval(1);
-
- // TODO setup opengl debug context
- if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
- return -3;
- }
-
- IMGUI_CHECKVERSION();
- auto ctx = ImGui::CreateContext();
- auto& io = ImGui::GetIO();
- ImGuizmo::SetImGuiContext(ctx);
-
- ImGui_ImplGlfw_InitForOpenGL(window, true);
- if (imguiUseOpenGL3) {
- ImGui_ImplOpenGL3_Init(imguiGlslVersion);
- } else {
- ImGui_ImplOpenGL2_Init();
- }
-
- IresManager::instance = new IresManager();
- IresManager::instance->DiscoverFilesDesignatedLocation();
-
- LevelManager::instance = new LevelManager();
- LevelManager::instance->DiscoverFilesDesignatedLocation();
-
- gVformatStandard.Attach(new VertexFormat());
- gVformatStandard->AddElement(VertexElementFormat{
- .bindingIndex = 0,
- .type = VET_Float3,
- .semantic = VES_Position,
- });
- gVformatStandard->AddElement(VertexElementFormat{
- .bindingIndex = 0,
- .type = VET_Float2,
- .semantic = VES_TexCoords1,
- });
- gVformatStandard->AddElement(VertexElementFormat{
- .bindingIndex = 0,
- .type = VET_Ubyte4Norm,
- .semantic = VES_Color1,
- });
-
- gVformatStandardSplit.Attach(new VertexFormat());
- gVformatStandardSplit->AddElement(VertexElementFormat{
- .bindingIndex = 0,
- .type = VET_Float3,
- .semantic = VES_Position,
- });
- gVformatStandardSplit->AddElement(VertexElementFormat{
- .bindingIndex = 1,
- .type = VET_Float2,
- .semantic = VES_TexCoords1,
- });
- gVformatStandardSplit->AddElement(VertexElementFormat{
- .bindingIndex = 1,
- .type = VET_Ubyte4Norm,
- .semantic = VES_Color1,
- });
-
- // Matches gVformatStandard
- gDefaultShader.Attach(new Shader());
- gDefaultShader->InitFromSources(Shader::ShaderSources{
- .vertex = R"""(
-#version 330 core
-layout(location = 0) in vec3 pos;
-layout(location = 1) in vec4 color;
-out vec4 v2fColor;
-uniform mat4 transform;
-void main() {
- gl_Position = transform * vec4(pos, 1.0);
- v2fColor = color;
-}
-)"""sv,
- .fragment = R"""(
-#version 330 core
-in vec4 v2fColor;
-out vec4 fragColor;
-void main() {
- fragColor = v2fColor;
-}
-)"""sv,
- });
- { // in vec3 pos;
- ShaderMathVariable var;
- var.scalarType = GL_FLOAT;
- var.width = 1;
- var.height = 3;
- var.arrayLength = 1;
- var.semantic = VES_Position;
- var.location = 0;
- gDefaultShader->GetInfo().inputs.push_back(std::move(var));
- gDefaultShader->GetInfo().things.try_emplace(
- "pos"s,
- ShaderThingId{
- .kind = ShaderThingId::KD_Input,
- .index = (int)gDefaultShader->GetInfo().inputs.size() - 1,
- });
- }
- { // in vec4 color;
- ShaderMathVariable var;
- var.scalarType = GL_FLOAT;
- var.width = 1;
- var.height = 4;
- var.arrayLength = 1;
- var.semantic = VES_Color1;
- var.location = 1;
- gDefaultShader->GetInfo().inputs.push_back(std::move(var));
- gDefaultShader->GetInfo().things.try_emplace(
- "color"s,
- ShaderThingId{
- .kind = ShaderThingId::KD_Input,
- .index = (int)gDefaultShader->GetInfo().inputs.size() - 1,
- });
- }
- { // out vec4 fragColor;
- ShaderMathVariable var;
- var.scalarType = GL_FLOAT;
- var.width = 1;
- var.height = 4;
- var.arrayLength = 1;
- gDefaultShader->GetInfo().outputs.push_back(std::move(var));
- gDefaultShader->GetInfo().things.try_emplace(
- "fragColor"s,
- ShaderThingId{
- .kind = ShaderThingId::KD_Output,
- .index = (int)gDefaultShader->GetInfo().outputs.size() - 1,
- });
- }
- // NOTE: autofill uniforms not recorded here
-
- gDefaultMaterial.Attach(new Material());
- gDefaultMaterial->SetShader(gDefaultShader.Get());
-
- { // Main loop
- App app;
- glfwUserData.app = &app;
-
- // NOTE: don't enable backface culling, because the game mainly runs in 2D and sometimes we'd like to flip sprites around
- // it also helps with debugging layers in 3D view
- glEnable(GL_DEPTH_TEST);
-
- // 60 updates per second
- constexpr double kMsPerUpdate = 1000.0 / 60;
- constexpr double kSecondsPerUpdate = kMsPerUpdate / 1000;
- double prevTime = glfwGetTime();
- double accumulatedTime = 0.0;
- while (!glfwWindowShouldClose(window)) {
- {
- ZoneScopedN("GameInput");
- glfwPollEvents();
- }
-
- double currTime = glfwGetTime();
- double deltaTime = prevTime - currTime;
-
- // In seconds
- accumulatedTime += currTime - prevTime;
-
- // Update
- // Play "catch up" to ensure a deterministic number of Update()'s per second
- while (accumulatedTime >= kSecondsPerUpdate) {
- double beg = glfwGetTime();
- {
- ZoneScopedN("GameUpdate");
- app.Update();
- }
- double end = glfwGetTime();
-
- // Update is taking longer than it should be, start skipping updates
- auto diff = end - beg;
- if (diff >= kSecondsPerUpdate) {
- auto skippedUpdates = (int)(accumulatedTime / kSecondsPerUpdate);
- accumulatedTime = 0.0;
- fprintf(stderr, "Elapsed time %f, skipped %d updates.", diff, skippedUpdates);
- } else {
- accumulatedTime -= kSecondsPerUpdate;
- }
- }
-
- int fbWidth = AppConfig::mainWindowWidth;
- int fbHeight = AppConfig::mainWindowHeight;
- glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
- glViewport(0, 0, fbWidth, fbHeight);
- auto clearColor = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
- glClearColor(clearColor.x * clearColor.w, clearColor.y * clearColor.w, clearColor.z * clearColor.w, clearColor.w);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- { // Regular draw
- ZoneScopedN("Render");
- app.Draw(currTime, deltaTime);
- }
-
- { // ImGui draw
- ZoneScopedN("ImGui");
- if (imguiUseOpenGL3) {
- ImGui_ImplOpenGL3_NewFrame();
- } else {
- ImGui_ImplOpenGL2_NewFrame();
- }
- ImGui_ImplGlfw_NewFrame();
- ImGui::NewFrame();
-
- app.Show();
-
- ImGui::Render();
- if (imguiUseOpenGL3) {
- ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
- } else {
- ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
- }
- }
-
- glfwSwapBuffers(window);
- FrameMark;
-
- prevTime = currTime;
- }
- }
-
- if (imguiUseOpenGL3) {
- ImGui_ImplOpenGL3_Shutdown();
- } else {
- ImGui_ImplOpenGL2_Shutdown();
- }
- ImGui_ImplGlfw_Shutdown();
-
- ImGui::DestroyContext();
-
- glfwDestroyWindow(window);
- glfwTerminate();
-
- return 0;
-}