aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/10-common/Color.hpp148
-rw-r--r--source/10-common/DtoHelper.hpp10
-rw-r--r--source/10-common/Enum.hpp110
-rw-r--r--source/10-common/Log.cpp116
-rw-r--r--source/10-common/Log.hpp55
-rw-r--r--source/10-common/LookupTable.hpp64
-rw-r--r--source/10-common/Macros.hpp31
-rw-r--r--source/10-common/OpaqueIterator.hpp31
-rw-r--r--source/10-common/PodVector.hpp297
-rw-r--r--source/10-common/RTTI.hpp44
-rw-r--r--source/10-common/RapidJsonHelper.hpp114
-rw-r--r--source/10-common/RcPtr.hpp120
-rw-r--r--source/10-common/Rect.hpp164
-rw-r--r--source/10-common/RingBuffer.hpp191
-rw-r--r--source/10-common/ScopeGuard.hpp60
-rw-r--r--source/10-common/SmallVector.cpp145
-rw-r--r--source/10-common/SmallVector.hpp1332
-rw-r--r--source/10-common/StbImplementations.c14
-rw-r--r--source/10-common/Type2ObjectMap.hpp38
-rw-r--r--source/10-common/TypeTraits.hpp27
-rw-r--r--source/10-common/Uid.cpp70
-rw-r--r--source/10-common/Uid.hpp46
-rw-r--r--source/10-common/Utils.cpp130
-rw-r--r--source/10-common/Utils.hpp77
-rw-r--r--source/10-common/YCombinator.hpp14
-rw-r--r--source/10-editor-common/ImGuiGuizmo.cpp2897
-rw-r--r--source/10-editor-common/ImGuiGuizmo.hpp232
-rw-r--r--source/10-editor-common/ImGuiNotification.cpp277
-rw-r--r--source/10-editor-common/ImGuiNotification.hpp81
-rw-r--r--source/20-codegen-compiler/CodegenConfig.hpp11
-rw-r--r--source/20-codegen-compiler/CodegenDecl.cpp74
-rw-r--r--source/20-codegen-compiler/CodegenDecl.hpp154
-rw-r--r--source/20-codegen-compiler/CodegenLexer.cpp202
-rw-r--r--source/20-codegen-compiler/CodegenLexer.hpp49
-rw-r--r--source/20-codegen-compiler/CodegenModel.cpp732
-rw-r--r--source/20-codegen-compiler/CodegenModel.hpp61
-rw-r--r--source/20-codegen-compiler/CodegenOutput.cpp39
-rw-r--r--source/20-codegen-compiler/CodegenOutput.hpp34
-rw-r--r--source/20-codegen-compiler/CodegenUtils.cpp171
-rw-r--r--source/20-codegen-compiler/CodegenUtils.hpp57
-rw-r--r--source/20-codegen-compiler/SQLiteHelper.hpp220
-rw-r--r--source/20-codegen-compiler/main.cpp1443
-rw-r--r--source/20-codegen-compiler/test/examples/TestClass.hpp.txt38
-rw-r--r--source/20-codegen-compiler/test/examples/TestEnum.hpp.txt44
-rw-r--r--source/20-codegen-runtime/MacrosCodegen.hpp10
-rw-r--r--source/20-codegen-runtime/Metadata.cpp45
-rw-r--r--source/20-codegen-runtime/Metadata.hpp33
-rw-r--r--source/20-codegen-runtime/MetadataBase.cpp5
-rw-r--r--source/20-codegen-runtime/MetadataBase.hpp53
-rw-r--r--source/20-codegen-runtime/MetadataDetails.hpp7
-rw-r--r--source/30-game/App.cpp168
-rw-r--r--source/30-game/App.hpp62
-rw-r--r--source/30-game/AppConfig.hpp26
-rw-r--r--source/30-game/Camera.cpp46
-rw-r--r--source/30-game/Camera.hpp35
-rw-r--r--source/30-game/CommonVertexIndex.cpp152
-rw-r--r--source/30-game/CommonVertexIndex.hpp77
-rw-r--r--source/30-game/EditorAccessories.cpp26
-rw-r--r--source/30-game/EditorAccessories.hpp8
-rw-r--r--source/30-game/EditorAttachment.hpp12
-rw-r--r--source/30-game/EditorAttachmentImpl.cpp23
-rw-r--r--source/30-game/EditorAttachmentImpl.hpp34
-rw-r--r--source/30-game/EditorCommandPalette.cpp406
-rw-r--r--source/30-game/EditorCommandPalette.hpp94
-rw-r--r--source/30-game/EditorCore.hpp39
-rw-r--r--source/30-game/EditorCorePrivate.cpp1171
-rw-r--r--source/30-game/EditorCorePrivate.hpp136
-rw-r--r--source/30-game/EditorUtils.cpp447
-rw-r--r--source/30-game/EditorUtils.hpp84
-rw-r--r--source/30-game/EditorWorldGuides.cpp26
-rw-r--r--source/30-game/EditorWorldGuides.hpp16
-rw-r--r--source/30-game/FuzzyMatch.cpp174
-rw-r--r--source/30-game/FuzzyMatch.hpp10
-rw-r--r--source/30-game/GameObject.cpp230
-rw-r--r--source/30-game/GameObject.hpp107
-rw-r--r--source/30-game/GraphicsTags.cpp273
-rw-r--r--source/30-game/GraphicsTags.hpp111
-rw-r--r--source/30-game/Image.cpp101
-rw-r--r--source/30-game/Image.hpp38
-rw-r--r--source/30-game/Input.cpp23
-rw-r--r--source/30-game/Input.hpp23
-rw-r--r--source/30-game/Ires.cpp436
-rw-r--r--source/30-game/Ires.hpp130
-rw-r--r--source/30-game/Level.cpp228
-rw-r--r--source/30-game/Level.hpp95
-rw-r--r--source/30-game/Material.cpp526
-rw-r--r--source/30-game/Material.hpp125
-rw-r--r--source/30-game/Mesh.cpp54
-rw-r--r--source/30-game/Mesh.hpp45
-rw-r--r--source/30-game/Player.cpp139
-rw-r--r--source/30-game/Player.hpp59
-rw-r--r--source/30-game/Renderer.cpp256
-rw-r--r--source/30-game/Renderer.hpp92
-rw-r--r--source/30-game/SceneThings.cpp142
-rw-r--r--source/30-game/SceneThings.hpp52
-rw-r--r--source/30-game/Shader.cpp711
-rw-r--r--source/30-game/Shader.hpp180
-rw-r--r--source/30-game/Sprite.cpp328
-rw-r--r--source/30-game/Sprite.hpp111
-rw-r--r--source/30-game/Texture.cpp250
-rw-r--r--source/30-game/Texture.hpp99
-rw-r--r--source/30-game/VertexIndex.cpp84
-rw-r--r--source/30-game/VertexIndex.hpp67
-rw-r--r--source/30-game/World.cpp79
-rw-r--r--source/30-game/World.hpp25
-rw-r--r--source/30-game/main.cpp545
106 files changed, 0 insertions, 19453 deletions
diff --git a/source/10-common/Color.hpp b/source/10-common/Color.hpp
deleted file mode 100644
index ef0c5a9..0000000
--- a/source/10-common/Color.hpp
+++ /dev/null
@@ -1,148 +0,0 @@
-#pragma once
-
-#include "Utils.hpp"
-
-#include <algorithm>
-#include <cstdint>
-#include <glm/glm.hpp>
-#include <limits>
-
-class HsvColor;
-class RgbaColor {
-public:
- uint8_t r;
- uint8_t g;
- uint8_t b;
- uint8_t a;
-
-public:
- constexpr RgbaColor() noexcept
- : r{ 255 }
- , g{ 255 }
- , b{ 255 }
- , a{ 255 } {
- }
-
- constexpr RgbaColor(float r, float g, float b, float a = 1.0f) noexcept
- : r{ static_cast<uint8_t>(r * 255.0f) }
- , g{ static_cast<uint8_t>(g * 255.0f) }
- , b{ static_cast<uint8_t>(b * 255.0f) }
- , a{ static_cast<uint8_t>(a * 255.0f) } {
- }
-
- constexpr RgbaColor(int r, int g, int b, int a = 255) noexcept
- : r{ static_cast<uint8_t>(r & 0xFF) }
- , g{ static_cast<uint8_t>(g & 0xFF) }
- , b{ static_cast<uint8_t>(b & 0xFF) }
- , a{ static_cast<uint8_t>(a & 0xFF) } {
- }
-
- constexpr RgbaColor(uint32_t rgba) noexcept
- : r{ static_cast<uint8_t>((rgba >> 0) & 0xFF) }
- , g{ static_cast<uint8_t>((rgba >> 8) & 0xFF) }
- , b{ static_cast<uint8_t>((rgba >> 16) & 0xFF) }
- , a{ static_cast<uint8_t>((rgba >> 24) & 0xFF) } {
- }
-
- constexpr uint32_t GetScalar() const noexcept {
- uint32_t res = 0;
- res |= r << 0;
- res |= g << 8;
- res |= b << 16;
- res |= a << 24;
- return res;
- }
-
- constexpr void SetScalar(uint32_t scalar) noexcept {
- r = (scalar >> 0) & 0xFF;
- g = (scalar >> 8) & 0xFF;
- b = (scalar >> 16) & 0xFF;
- a = (scalar >> 24) & 0xFF;
- }
-
- constexpr float GetNormalizedRed() const noexcept {
- return r / 255.0f;
- }
-
- constexpr float GetNormalizedGreen() const noexcept {
- return g / 255.0f;
- }
-
- constexpr float GetNormalizedBlue() const noexcept {
- return b / 255.0f;
- }
-
- constexpr float GetNormalizedAlpha() const noexcept {
- return a / 255.0f;
- }
-
- constexpr glm::ivec4 ToIVec() const noexcept {
- return { r, g, b, a };
- }
-
- constexpr glm::vec4 ToVec() const noexcept {
- return { GetNormalizedRed(), GetNormalizedGreen(), GetNormalizedBlue(), GetNormalizedAlpha() };
- }
-
- // Forward declaring because cyclic reference between RgbaColor and HsvColor
- constexpr HsvColor ToHsv() const noexcept;
-
- friend constexpr bool operator==(const RgbaColor&, const RgbaColor&) noexcept = default;
-};
-
-constexpr RgbaColor kXAxisColor(0xFF, 0x80, 0x80, 0xFF);
-constexpr RgbaColor kYAxisColor(0x80, 0xFF, 0x80, 0xFF);
-constexpr RgbaColor kZAxisColor(0x80, 0x80, 0xFF, 0xFF);
-
-class HsvColor {
-public:
- float h;
- float s;
- float v;
- float a;
-
-public:
- constexpr HsvColor() noexcept
- : h{ 0.0f }
- , s{ 0.0f }
- , v{ 1.0f }
- , a{ 1.0f } {
- }
-
- constexpr HsvColor(float h, float s, float v, float a) noexcept
- : h{ h }
- , s{ s }
- , v{ v }
- , a{ a } {
- }
-
- // Forward declaring because cyclic reference between RgbaColor and HsvColor
- constexpr RgbaColor ToRgba() const noexcept;
-};
-
-constexpr HsvColor RgbaColor::ToHsv() const noexcept {
- float r = GetNormalizedRed();
- float g = GetNormalizedBlue();
- float b = GetNormalizedGreen();
- float a = GetNormalizedAlpha();
-
- auto p = g < b ? glm::vec4(b, g, -1, 2.0f / 3.0f) : glm::vec4(g, b, 0, -1.0f / 3.0f);
- auto q = r < p.x ? glm::vec4(p.x, p.y, p.w, r) : glm::vec4(r, p.y, p.z, p.x);
- float c = q.x - std::min(q.w, q.y);
- float h = Utils::Abs((q.w - q.y) / (6 * c + std::numeric_limits<float>::epsilon()) + q.z);
-
- glm::vec3 hcv{ h, c, q.x };
- float s = hcv.y / (hcv.z + std::numeric_limits<float>::epsilon());
- return HsvColor(hcv.x, s, hcv.z, a);
-}
-
-constexpr RgbaColor HsvColor::ToRgba() const noexcept {
- float r = Utils::Abs(h * 6 - 3) - 1;
- float g = 2 - Utils::Abs(h * 6 - 2);
- float b = 2 - Utils::Abs(h * 6 - 4);
-
- auto rgb = glm::vec3{ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f) };
- auto vc = (rgb - glm::vec3{ 0, 0, 0 }) * s + glm::vec3{ 1, 1, 1 } * v;
-
- return RgbaColor(vc.x, vc.y, vc.z, a);
-}
diff --git a/source/10-common/DtoHelper.hpp b/source/10-common/DtoHelper.hpp
deleted file mode 100644
index 871f9c6..0000000
--- a/source/10-common/DtoHelper.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-#include <rapidjson/document.h>
-#include <json_dto/pub.hpp>
-
-namespace json_dto {
-
-
-
-}
diff --git a/source/10-common/Enum.hpp b/source/10-common/Enum.hpp
deleted file mode 100644
index 7afbe8e..0000000
--- a/source/10-common/Enum.hpp
+++ /dev/null
@@ -1,110 +0,0 @@
-#pragma once
-
-#include <initializer_list>
-#include <type_traits>
-
-template <typename TEnum>
-class EnumFlags {
-public:
- using Enum = TEnum;
- using Underlying = std::underlying_type_t<TEnum>;
-
-private:
- Underlying mValue;
-
-public:
- EnumFlags()
- : mValue{ 0 } {
- }
-
- EnumFlags(TEnum e)
- : mValue{ static_cast<Underlying>(1) << static_cast<Underlying>(e) } {
- }
-
- bool IsSet(EnumFlags mask) const {
- return (mValue & mask.mValue) == mask.mValue;
- }
-
- bool IsSet(std::initializer_list<TEnum> enums) {
- EnumFlags flags;
- for (auto& e : enums) {
- flags.mValue |= static_cast<Underlying>(e);
- }
- return IsSet(flags);
- }
-
- bool IsSetExclusive(EnumFlags mask) const {
- return mValue == mask.mValue;
- }
-
- bool IsSetExclusive(std::initializer_list<TEnum> enums) {
- EnumFlags flags;
- for (auto& e : enums) {
- flags.mValue |= static_cast<Underlying>(e);
- }
- return IsSetExclusive(flags);
- }
-
- void SetOn(EnumFlags mask) {
- mValue |= mask.mValue;
- }
-
- void SetOff(EnumFlags mask) {
- mValue &= ~mask.mValue;
- }
-
- void Set(EnumFlags mask, bool enabled) {
- if (enabled) {
- SetOn(mask);
- } else {
- SetOff(mask);
- }
- }
-
- EnumFlags& operator|=(EnumFlags that) const {
- mValue |= that.mValue;
- return *this;
- }
-
- EnumFlags& operator&=(EnumFlags that) const {
- mValue &= that.mValue;
- return *this;
- }
-
- EnumFlags& operator^=(EnumFlags that) const {
- mValue ^= that.mValue;
- return *this;
- }
-
- EnumFlags& operator|=(TEnum e) const {
- mValue |= 1 << static_cast<Underlying>(e);
- return *this;
- }
-
- EnumFlags& operator&=(TEnum e) const {
- mValue &= 1 << static_cast<Underlying>(e);
- return *this;
- }
-
- EnumFlags& operator^=(TEnum e) const {
- mValue ^= 1 << static_cast<Underlying>(e);
- return *this;
- }
-
- EnumFlags operator|(EnumFlags that) const { return EnumFlags(mValue | that.mValue); }
- EnumFlags operator&(EnumFlags that) const { return EnumFlags(mValue & that.mValue); }
- EnumFlags operator^(EnumFlags that) const { return EnumFlags(mValue ^ that.mValue); }
-
- EnumFlags operator|(TEnum e) const { return EnumFlags(mValue | 1 << static_cast<Underlying>(e)); }
- EnumFlags operator&(TEnum e) const { return EnumFlags(mValue & 1 << static_cast<Underlying>(e)); }
- EnumFlags operator^(TEnum e) const { return EnumFlags(mValue ^ 1 << static_cast<Underlying>(e)); }
-
- EnumFlags operator~() const { return EnumFlags(~mValue); }
-};
-
-// Helper class for enumerating enum elements for ImGui::Begin/EndCombo
-template <typename TEnum>
-struct EnumElement {
- const char* name;
- TEnum value;
-};
diff --git a/source/10-common/Log.cpp b/source/10-common/Log.cpp
deleted file mode 100644
index 83d81e9..0000000
--- a/source/10-common/Log.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-#include "Log.hpp"
-
-#include "Macros.hpp"
-
-#include <robin_hood.h>
-#include <algorithm>
-#include <cstdio>
-
-namespace ProjectBrussel_UNITY_ID {
-using namespace Log;
-
-const char* MapMessageLevelToString(MessageLevel level) {
- switch (level) {
- using enum MessageLevel;
- case Debug: return "DEBUG";
- case Info: return "INFO";
- case Warning: return "WARN";
- case Error: return "ERROR";
- default: UNREACHABLE;
- }
-}
-
-void PrintMessage(const Message& msg) {
- using namespace std::chrono;
-
- auto t = system_clock::to_time_t(msg.time);
- char timeStr[128];
- strftime(timeStr, sizeof(timeStr), "%H:%M:%S", localtime(&t));
- printf("[%s][%s][%s:%u] %.*s\n",
- MapMessageLevelToString(msg.level),
- timeStr,
- msg.srcLoc.function_name(),
- msg.srcLoc.line(),
- PRINTF_STRING_VIEW(msg.text));
-}
-
-MessageBufferId gNextBufferId = 0;
-robin_hood::unordered_map<MessageBufferId, MessageBuffer*> gBuffers;
-} // namespace ProjectBrussel_UNITY_ID
-
-namespace Log {
-bool gPrintToStdOut = true;
-#if BRUSSEL_DEV_ENV
-MessageBuffer gDefaultBuffer;
-MessageBufferId gDefaultBufferId;
-#endif
-} // namespace Log
-
-Log::MessageBufferId Log::RegisterBuffer(MessageBuffer& buffer) {
- using namespace ProjectBrussel_UNITY_ID;
-
- auto id = gNextBufferId++;
- gBuffers.try_emplace(id, &buffer);
- return id;
-}
-
-void Log::UnregisterBuffer(MessageBufferId id) {
- using namespace ProjectBrussel_UNITY_ID;
-
- gBuffers.erase(id);
-}
-
-Log::MessageBuffer* Log::GetBuffer(MessageBufferId id) {
- using namespace ProjectBrussel_UNITY_ID;
-
- auto iter = gBuffers.find(id);
- if (iter != gBuffers.end()) {
- return iter->second;
- } else {
- return nullptr;
- }
-}
-
-void Log::DumpRegisteredBuffers() {
- using namespace ProjectBrussel_UNITY_ID;
-
- puts("================ BEGIN LOG BUFFER DUMP ================");
- for (const auto& [id, buffer] : gBuffers) {
- printf("Buffer #%d at %p\n", id, buffer);
- printf("Buffer size: %zu\n", buffer->messages.capacity());
- bool needsWrapAround = buffer->messages.GetHeadIdx() >= buffer->messages.GetTailIdx();
- if (needsWrapAround) {
- printf("Fill size: %zu in [%zu,%zu) and [0,%zu)\n",
- buffer->messages.size(),
- // First chunk: [begin,end)
- buffer->messages.GetHeadIdx(),
- buffer->messages.capacity(),
- // Second chunk: [0,end)
- buffer->messages.GetTailIdx());
- } else {
- printf("Fill size: %zu in [%zu,%zu)\n",
- buffer->messages.size(),
- // [begin,end)
- buffer->messages.GetHeadIdx(),
- buffer->messages.GetTailIdx());
- }
- for (const auto& msg : buffer->messages) {
- // Indent log messages in this buffer
- printf("\t");
- PrintMessage(msg);
- }
- }
- puts("================ END LOG BUFFER DUMP ================");
-}
-
-void Log::Add(const Message& msg) {
- using namespace ProjectBrussel_UNITY_ID;
-
- if (gPrintToStdOut) {
- PrintMessage(msg);
- }
-
- for (auto& [_, buffer] : gBuffers) {
- buffer->messages.push_back(msg);
- }
-}
diff --git a/source/10-common/Log.hpp b/source/10-common/Log.hpp
deleted file mode 100644
index aeba984..0000000
--- a/source/10-common/Log.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma once
-
-#include "RingBuffer.hpp"
-
-#include <fmt/format.h>
-#include <chrono>
-#include <source_location>
-#include <string_view>
-
-// NOTE: we keep this on one line so std::soruce_location reports the correct information
-#define GENERIC_LOG(lvl, fmtString, ...) Log::Add(Log::Message{ .level = lvl, .time = std::chrono::system_clock::now(), .srcLoc = std::source_location::current(), .text = fmt::format(fmtString __VA_OPT__(, ) __VA_ARGS__) })
-
-#define LOG_DEBUG(...) GENERIC_LOG(Log::MessageLevel::Debug, __VA_ARGS__)
-#define LOG_INFO(...) GENERIC_LOG(Log::MessageLevel::Info, __VA_ARGS__)
-#define LOG_WARNING(...) GENERIC_LOG(Log::MessageLevel::Warning, __VA_ARGS__)
-#define LOG_ERROR(...) GENERIC_LOG(Log::MessageLevel::Error, __VA_ARGS__)
-
-namespace Log {
-enum class MessageLevel {
- Debug,
- Info,
- Warning,
- Error,
-};
-
-struct Message {
- MessageLevel level;
- std::chrono::time_point<std::chrono::system_clock> time;
- std::source_location srcLoc;
- std::string text;
-};
-
-/// A mRing buffer of log messages for programmatic inspection at runtime.
-struct MessageBuffer {
- RingBuffer<Message> messages;
-};
-
-/// Unique ID identifying a currently registered MessageBuffer.
-using MessageBufferId = int;
-
-MessageBufferId RegisterBuffer(MessageBuffer& buffer);
-void UnregisterBuffer(MessageBufferId id);
-MessageBuffer* GetBuffer(MessageBufferId id);
-void DumpRegisteredBuffers();
-
-extern bool gPrintToStdOut;
-#if BRUSSEL_DEV_ENV
-// NOTE: initialized in main.cpp
-extern MessageBuffer gDefaultBuffer;
-extern MessageBufferId gDefaultBufferId;
-#endif
-
-// TODO improve this interface: don't copy std::string when there is in fact no MessageBuffer registered
-void Add(const Message& msg);
-} // namespace Log
diff --git a/source/10-common/LookupTable.hpp b/source/10-common/LookupTable.hpp
deleted file mode 100644
index 54548f2..0000000
--- a/source/10-common/LookupTable.hpp
+++ /dev/null
@@ -1,64 +0,0 @@
-#pragma once
-
-#include <robin_hood.h>
-#include <string_view>
-
-// BIDI stands for bi-directional
-#define BIDI_LUT_DECL(name, aType, aCount, bType, bCount) \
- int gLutBidi_##name##_A2B[aCount]; \
- int gLutBidi_##name##_B2A[bCount]; \
- using name##AType = aType; \
- using name##BType = bType; \
- void InitializeLutBidi##name()
-#define BIDI_LUT_MAP_FOR(name) \
- int* lutMappingA2B = gLutBidi_##name##_A2B; \
- int* lutMappingB2A = gLutBidi_##name##_B2A
-#define BIDI_LUT_MAP(from, to) \
- lutMappingA2B[from] = to; \
- lutMappingB2A[to] = from
-#define BIDI_LUT_INIT(name) InitializeLutBidi##name()
-#define BIDI_LUT_A2B_LOOKUP(name, from) (name##BType)(gLutBidi_##name##_A2B[from])
-#define BIDI_LUT_B2A_LOOKUP(name, to) (name##AType)(gLutBidi_##name##_B2A[to])
-
-// Forward string lookup
-#define FSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \
- constexpr int kLutFwMinVal_##name = enumMinValue; \
- const char* gLutFw_##name[(int)enumMaxValue - (int)enumMinValue]; \
- void InitializeLutFw##name()
-#define FSTR_LUT_MAP_FOR(name) \
- const char** lutMapping = gLutFw_##name; \
- int lutMappingMinValue = kLutFwMinVal_##name
-#define FSTR_LUT_MAP(value, text) lutMapping[value - lutMappingMinValue] = text
-#define FSTR_LUT_MAP_ENUM(enumValue) FSTR_LUT_MAP(enumValue, #enumValue)
-#define FSTR_LUT_LOOKUP(name, enumValue) gLutFw_##name[enumValue - kLutFwMinVal_##name]
-#define FSTR_LUT_INIT(name) InitializeLutFw##name()
-
-// RSTR stands for reverse-string lookup
-#define RSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \
- robin_hood::unordered_flat_map<std::string_view, decltype(enumMaxValue)> gLutRv_##name; \
- void InitializeLutRv##name()
-#define RSTR_LUT_MAP_FOR(name) auto& lutMapping = gLutRv_##name;
-#define RSTR_LUT_MAP(value, text) lutMapping.insert_or_assign(std::string_view(text), value);
-#define RSTR_LUT(name) gLutRv_##name
-#define BSTR_LUT_LOOKUP(name, string) gLutRv_##name.find(std::string_view(text))->second
-#define RSTR_LUT_INIT(name) InitializeLutRv##name()
-
-// BSTR stands for bi-directional string lookup
-#define BSTR_LUT_DECL(name, enumMinValue, enumMaxValue) \
- constexpr int kLutBstrMinVal_##name = enumMinValue; \
- const char* gLutBstr_##name##_V2S[(int)enumMaxValue - (int)enumMinValue]; \
- robin_hood::unordered_flat_map<std::string_view, decltype(enumMaxValue)> gLutBstr_##name##_S2V; \
- void InitializeLutBstr##name()
-#define BSTR_LUT_MAP_FOR(name) \
- const char** lutMappingV2S = gLutBstr_##name##_V2S; \
- auto& lutMappingS2V = gLutBstr_##name##_S2V; \
- int lutMappingMinValue = kLutBstrMinVal_##name
-#define BSTR_LUT_MAP(value, text) \
- lutMappingV2S[value - lutMappingMinValue] = text; \
- lutMappingS2V.insert_or_assign(std::string_view(text), value);
-#define BSTR_LUT_MAP_ENUM(enumValue) BSTR_LUT_MAP(enumValue, #enumValue)
-#define BSTR_LUT_V2S(name) gLutBstr_##name##_V2S
-#define BSTR_LUT_S2V(name) gLutBstr_##name##_S2V
-#define BSTR_LUT_V2S_LOOKUP(name, enumValue) gLutBstr_##name##_V2S[enumValue - kLutBstrMinVal_##name]
-#define BSTR_LUT_S2V_LOOKUP(name, string) gLutBstr_##name##_S2V.find(std::string_view(text))->second
-#define BSTR_LUT_INIT(name) InitializeLutBstr##name()
diff --git a/source/10-common/Macros.hpp b/source/10-common/Macros.hpp
deleted file mode 100644
index a255ada..0000000
--- a/source/10-common/Macros.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#define STRINGIFY_IMPL(text) #text
-#define STRINGIFY(text) STRINGIFY_IMPL(text)
-
-#define CONCAT_IMPL(a, b) a##b
-#define CONCAT(a, b) CONCAT_IMPL(a, b)
-#define CONCAT_3(a, b, c) CONCAT(a, CONCAT(b, c))
-#define CONCAT_4(a, b, c, d) CONCAT(CONCAT(a, b), CONCAT(c, d))
-
-#define UNIQUE_NAME(prefix) CONCAT(prefix, __COUNTER__)
-#define UNIQUE_NAME_LINE(prefix) CONCAT(prefix, __LINE__)
-#define DISCARD UNIQUE_NAME(_discard)
-
-#define UNUSED(x) (void)x;
-
-#define PRINTF_STRING_VIEW(s) (int)s.size(), s.data()
-
-#if defined(_MSC_VER)
-# define UNREACHABLE __assume(0)
-#elif defined(__GNUC__) || defined(__clang__)
-# define UNREACHABLE __builtin_unreachable()
-#else
-# define UNREACHABLE
-#endif
-
-#if _WIN32
-# define PLATFORM_PATH_STR "%ls"
-#else
-# define PLATFORM_PATH_STR "%s"
-#endif
diff --git a/source/10-common/OpaqueIterator.hpp b/source/10-common/OpaqueIterator.hpp
deleted file mode 100644
index 128cbc6..0000000
--- a/source/10-common/OpaqueIterator.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-template <typename T>
-class IOpaqueIterator {
-public:
- virtual ~IOpaqueIterator() = default;
- virtual bool HasNext() const = 0;
- virtual T Next() = 0;
-};
-
-template <typename TContainer>
-class ContainerOpaqueIterator : public IOpaqueIterator<typename TContainer::reference_type> {
-private:
- typename TContainer::iterator mIter;
- typename TContainer::const_iterator mEnd;
-
-public:
- ContainerOpaqueIterator(TContainer& container)
- : mIter{ container.begin() }
- , mEnd{ container.end() } {}
-
- virtual bool HasNext() const override {
- return mIter != mEnd;
- }
-
- virtual typename TContainer::reference_type Next() override {
- auto result = *mIter;
- ++mIter;
- return result;
- }
-};
diff --git a/source/10-common/PodVector.hpp b/source/10-common/PodVector.hpp
deleted file mode 100644
index bd92e7d..0000000
--- a/source/10-common/PodVector.hpp
+++ /dev/null
@@ -1,297 +0,0 @@
-// File adapted from dear-imgui's ImVector, implemented in https://github.com/ocornut/imgUI/blob/master/imgui.h
-#pragma once
-
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-#include <span>
-
-template <typename T>
-class PodVector {
-public:
- using value_type = T;
- using iterator = value_type*;
- using const_iterator = const value_type*;
-
-private:
- int mSize;
- int mCapacity;
- T* mData;
-
-public:
- PodVector() {
- mSize = mCapacity = 0;
- mData = nullptr;
- }
-
- PodVector(const PodVector<T>& src) {
- mSize = mCapacity = 0;
- mData = nullptr;
- operator=(src);
- }
-
- PodVector<T>& operator=(const PodVector<T>& src) {
- clear();
- resize(src.mSize);
- std::memcpy(mData, src.mData, (size_t)mSize * sizeof(T));
- return *this;
- }
-
- PodVector(PodVector&& src) {
- mSize = src.mSize;
- mCapacity = src.mCapacity;
- mData = src.mData;
-
- src.mSize = src.mCapacity = 0;
- src.mData = nullptr;
- }
-
- PodVector& operator=(PodVector&& src) {
- if (this != &src) {
- std::free(mData);
-
- mSize = src.mSize;
- mCapacity = src.mCapacity;
- mData = src.mData;
-
- src.mSize = src.mCapacity = 0;
- src.mData = nullptr;
- }
- return *this;
- }
-
- ~PodVector() {
- std::free(mData);
- }
-
- bool empty() const { return mSize == 0; }
- int size() const { return mSize; }
- int size_in_bytes() const { return mSize * (int)sizeof(T); }
- int max_size() const { return 0x7FFFFFFF / (int)sizeof(T); }
- int capacity() const { return mCapacity; }
-
- T& operator[](int i) {
- assert(i >= 0 && i < mSize);
- return mData[i];
- }
-
- const T& operator[](int i) const {
- assert(i >= 0 && i < mSize);
- return mData[i];
- }
-
- void clear() {
- if (mData) {
- mSize = mCapacity = 0;
- std::free(mData);
- mData = nullptr;
- }
- }
-
- T* begin() { return mData; }
- const T* begin() const { return mData; }
- T* end() { return mData + mSize; }
- const T* end() const { return mData + mSize; }
-
- T* data() { return mData; }
-
- T& front() {
- assert(mSize > 0);
- return mData[0];
- }
-
- const T& front() const {
- assert(mSize > 0);
- return mData[0];
- }
-
- T& back() {
- assert(mSize > 0);
- return mData[mSize - 1];
- }
-
- const T& back() const {
- assert(mSize > 0);
- return mData[mSize - 1];
- }
-
- void swap(PodVector<T>& rhs) {
- int rhs_size = rhs.mSize;
- rhs.mSize = mSize;
- mSize = rhs_size;
- int rhs_cap = rhs.mCapacity;
- rhs.mCapacity = mCapacity;
- mCapacity = rhs_cap;
- T* rhs_mDataTmp = rhs.mData;
- rhs.mData = mData;
- mData = rhs_mDataTmp;
- }
-
- int grow_capacity(int sz) const {
- int newCapacity = mCapacity ? (mCapacity + mCapacity / 2) : 8;
- return newCapacity > sz ? newCapacity : sz;
- }
-
- void resize(int new_size) {
- if (new_size > mCapacity) reserve(grow_capacity(new_size));
- mSize = new_size;
- }
-
- void resize_more(int size) {
- resize(mSize + size);
- }
-
- void resize(int new_size, const T& v) {
- if (new_size > mCapacity) reserve(grow_capacity(new_size));
- if (new_size > mSize) {
- for (int n = mSize; n < new_size; n++) {
- std::memcpy(&mData[n], &v, sizeof(v));
- }
- }
- mSize = new_size;
- }
-
- void resize_more(int size, const T& v) {
- resize(mSize + size, v);
- }
-
- void shrink(int new_size) {
- assert(new_size <= mSize);
- mSize = new_size;
- }
-
- /// Resize a vector to a smaller mSize, guaranteed not to cause a reallocation
- void reserve(int newCapacity) {
- if (newCapacity <= mCapacity) return;
- auto tmp = (T*)std::malloc((size_t)newCapacity * sizeof(T));
- if (mData) {
- std::memcpy(tmp, mData, (size_t)mSize * sizeof(T));
- std::free(mData);
- }
- mData = tmp;
- mCapacity = newCapacity;
- }
-
- void reserve_more(int size) {
- reserve(mSize + size);
- }
-
- /// NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the PodVector data itself! e.g. v.push_back(v[10]) is forbidden.
- void push_back(const T& v) {
- if (mSize == mCapacity) reserve(grow_capacity(mSize + 1));
- std::memcpy(&mData[mSize], &v, sizeof(v));
- mSize++;
- }
-
- void pop_back() {
- assert(mSize > 0);
- mSize--;
- }
-
- void push_front(const T& v) {
- if (mSize == 0) {
- push_back(v);
- } else {
- insert(mData, v);
- }
- }
-
- T* erase(const T* it) {
- assert(it >= mData && it < mData + mSize);
- const ptrdiff_t off = it - mData;
- std::memmove(mData + off, mData + off + 1, ((size_t)mSize - (size_t)off - 1) * sizeof(T));
- mSize--;
- return mData + off;
- }
-
- T* erase(const T* it, const T* it_last) {
- assert(it >= mData && it < mData + mSize && it_last > it && it_last <= mData + mSize);
- const ptrdiff_t count = it_last - it;
- const ptrdiff_t off = it - mData;
- std::memmove(mData + off, mData + off + count, ((size_t)mSize - (size_t)off - count) * sizeof(T));
- mSize -= (int)count;
- return mData + off;
- }
-
- T* erase_unsorted(const T* it) {
- assert(it >= mData && it < mData + mSize);
- const ptrdiff_t off = it - mData;
- if (it < mData + mSize - 1) std::memcpy(mData + off, mData + mSize - 1, sizeof(T));
- mSize--;
- return mData + off;
- }
-
- T* insert(const T* it, const T& v) {
- assert(it >= mData && it <= mData + mSize);
- const ptrdiff_t off = it - mData;
- if (mSize == mCapacity) reserve(grow_capacity(mSize + 1));
- if (off < (int)mSize) std::memmove(mData + off + 1, mData + off, ((size_t)mSize - (size_t)off) * sizeof(T));
- std::memcpy(&mData[off], &v, sizeof(v));
- mSize++;
- return mData + off;
- }
-
- bool contains(const T& v) const {
- const T* data = mData;
- const T* dataEnd = mData + mSize;
- while (data < dataEnd) {
- if (*data++ == v) return true;
- }
- return false;
- }
-
- T* find(const T& v) {
- T* data = mData;
- const T* dataEnd = mData + mSize;
- while (data < dataEnd)
- if (*data == v)
- break;
- else
- ++data;
- return data;
- }
-
- const T* find(const T& v) const {
- const T* data = mData;
- const T* dataEnd = mData + mSize;
- while (data < dataEnd)
- if (*data == v)
- break;
- else
- ++data;
- return data;
- }
-
- bool find_erase(const T& v) {
- const T* it = find(v);
- if (it < mData + mSize) {
- erase(it);
- return true;
- }
- return false;
- }
-
- bool find_erase_unsorted(const T& v) {
- const T* it = find(v);
- if (it < mData + mSize) {
- erase_unsorted(it);
- return true;
- }
- return false;
- }
-
- int index_from_ptr(const T* it) const {
- assert(it >= mData && it < mData + mSize);
- const ptrdiff_t off = it - mData;
- return (int)off;
- }
-
- // Custom utility functions
-
- std::span<T> as_span() { return { mData, (size_t)mSize }; }
- std::span<uint8_t> as_data_span() { return { (uint8_t*)mData, (size_t)mSize * sizeof(T) }; }
- std::span<const T> as_span() const { return { mData, (size_t)mSize }; }
- std::span<const uint8_t> as_data_span() const { return { (uint8_t*)mData, (size_t)mSize * sizeof(T) }; }
-};
diff --git a/source/10-common/RTTI.hpp b/source/10-common/RTTI.hpp
deleted file mode 100644
index bd9475b..0000000
--- a/source/10-common/RTTI.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include <cassert>
-
-template <typename T, typename TBase>
-bool is_a(TBase* t) {
- assert(t != nullptr);
- return T::IsInstance(t);
-}
-
-template <typename T, typename TBase>
-bool is_a_nullable(TBase* t) {
- if (t) {
- return is_a<T, TBase>(t);
- } else {
- return false;
- }
-}
-
-template <typename T, typename TBase>
-T* dyn_cast(TBase* t) {
- assert(t != nullptr);
- if (T::IsInstance(t)) {
- return static_cast<T*>(t);
- } else {
- return nullptr;
- }
-}
-
-template <typename T, typename TBase>
-const T* dyn_cast(const TBase* t) {
- assert(t != nullptr);
- if (T::IsInstance(t)) {
- return static_cast<const T*>(t);
- } else {
- return nullptr;
- }
-}
-
-template <typename T, typename TBase>
-T* dyn_cast_nullable(TBase* t) {
- if (!t) return nullptr;
- return dyn_cast<T, TBase>(t);
-}
diff --git a/source/10-common/RapidJsonHelper.hpp b/source/10-common/RapidJsonHelper.hpp
deleted file mode 100644
index a992dbc..0000000
--- a/source/10-common/RapidJsonHelper.hpp
+++ /dev/null
@@ -1,114 +0,0 @@
-#pragma once
-
-#include <rapidjson/document.h>
-#include <cstring>
-#include <string>
-#include <string_view>
-
-#define BRUSSEL_JSON_GET(object, name, type, out, failAction) \
- { \
- auto it = (object).FindMember(name); \
- if (it == (object).MemberEnd()) failAction; \
- auto& value = it->value; \
- if (!value.Is<type>()) failAction; \
- (out) = value.Get<type>(); \
- }
-
-#define BRUSSEL_JSON_GET_DEFAULT(object, name, type, out, theDefault) \
- do { \
- auto it = (object).FindMember(name); \
- if (it == (object).MemberEnd()) { \
- (out) = theDefault; \
- break; \
- } \
- auto& value = it->value; \
- if (!value.Is<type>()) { \
- (out) = theDefault; \
- break; \
- } \
- (out) = value.Get<type>(); \
- } while (0);
-
-namespace rapidjson {
-
-inline const Value* GetProperty(const Value& value, std::string_view name) {
- for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
- if (it->name.GetStringLength() != name.size()) continue;
- if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue;
-
- return &it->value;
- }
- return nullptr;
-}
-
-inline const Value* GetProperty(const Value& value, Type type, std::string_view name) {
- for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
- if (it->name.GetStringLength() != name.size()) continue;
- if (it->value.GetType() != type) continue;
- if (std::memcmp(it->name.GetString(), name.data(), name.size())) continue;
-
- return &it->value;
- }
- return nullptr;
-}
-
-inline std::string_view AsStringView(const Value& value) {
- return std::string_view(value.GetString(), value.GetStringLength());
-}
-
-inline std::string_view AsStringView(const GenericStringRef<char>& strRef) {
- return std::string_view(strRef.s, strRef.length);
-}
-
-inline std::string AsString(const Value& value) {
- return std::string(value.GetString(), value.GetStringLength());
-}
-
-inline std::string AsString(const GenericStringRef<char>& strRef) {
- return std::string(strRef.s, strRef.length);
-}
-
-// RapidJson itself already provides std::string and const char* overloads
-inline GenericStringRef<char> StringRef(std::string_view str) {
- return GenericStringRef<char>(
- str.data() ? str.data() : "",
- str.size());
-}
-
-template <typename TIter, typename TSentienl>
-rapidjson::Value WriteVectorPrimitives(rapidjson::Document& root, TIter begin, TSentienl end) {
- using TElement = typename TIter::value_type;
-
- rapidjson::Value list;
- while (begin != end) {
- if constexpr (std::is_same_v<TElement, std::string>) {
- auto& elm = *begin;
- list.PushBack(rapidjson::Value(elm.c_str(), elm.size()), root.GetAllocator());
- } else {
- list.PushBack(*begin, root.GetAllocator());
- }
- ++begin;
- }
- return list;
-}
-
-template <typename TContainer>
-bool ReadVectorPrimitives(const rapidjson::Value& value, TContainer& list) {
- using TElement = typename TContainer::value_type;
-
- if (!value.IsArray()) return false;
-
- list.reserve(value.Size());
- for (auto& elm : value.GetArray()) {
- if (!elm.Is<TElement>()) return {};
- list.push_back(elm.Get<TElement>());
- }
-
- return true;
-}
-
-} // namespace rapidjson
-
-inline rapidjson::GenericStringRef<char> operator""_rj_sv(const char* str, size_t len) {
- return rapidjson::StringRef(str, len);
-}
diff --git a/source/10-common/RcPtr.hpp b/source/10-common/RcPtr.hpp
deleted file mode 100644
index e3e420e..0000000
--- a/source/10-common/RcPtr.hpp
+++ /dev/null
@@ -1,120 +0,0 @@
-#pragma once
-
-#include "Macros.hpp"
-#include "TypeTraits.hpp"
-
-#include <cstddef>
-#include <cstdint>
-#include <optional>
-#include <type_traits>
-
-class RefCounted {
-public:
- // DO NOT MODIFY this field, unless explicitly documented the use
- uint32_t refCount = 0;
- uint32_t weakCount = 0; // TODO implement
-};
-
-template <typename T, typename TDeleter = DefaultDeleter<T>>
-class RcPtr : TDeleter {
-private:
- static_assert(std::is_base_of_v<RefCounted, T>);
- T* mPtr;
-
-public:
- RcPtr()
- : mPtr{ nullptr } {
- }
-
- explicit RcPtr(T* ptr)
- : mPtr{ ptr } {
- if (ptr) {
- ++ptr->RefCounted::refCount;
- }
- }
-
- ~RcPtr() {
- CleanUp();
- }
-
- void Attach(T* ptr) {
- CleanUp();
- mPtr = ptr;
- if (ptr) {
- ++ptr->RefCounted::refCount;
- }
- }
-
- void Detatch() {
- CleanUp();
- mPtr = nullptr;
- }
-
- RcPtr(const RcPtr& that)
- : mPtr{ that.mPtr } {
- if (mPtr) {
- ++mPtr->RefCounted::refCount;
- }
- }
-
- RcPtr& operator=(const RcPtr& that) {
- CleanUp();
- mPtr = that.mPtr;
- if (mPtr) {
- ++mPtr->RefCounted::refCount;
- }
- return *this;
- }
-
- RcPtr(RcPtr&& that)
- : mPtr{ that.mPtr } {
- that.mPtr = nullptr;
- }
-
- RcPtr& operator=(RcPtr&& that) {
- CleanUp();
- mPtr = that.mPtr;
- that.mPtr = nullptr;
- return *this;
- }
-
- template <typename TBase>
- requires std::is_base_of_v<TBase, T>
- operator RcPtr<TBase>() const {
- return RcPtr<TBase>(mPtr);
- }
-
- bool operator==(std::nullptr_t ptr) const {
- return mPtr == nullptr;
- }
-
- bool operator==(const T* ptr) const {
- return mPtr == ptr;
- }
-
- bool operator==(T* ptr) const {
- return mPtr == ptr;
- }
-
- template <typename TThat>
- bool operator==(const RcPtr<TThat>& ptr) const {
- return mPtr == ptr.Get();
- }
-
- T* Get() const {
- return mPtr;
- }
-
- T& operator*() const { return *mPtr; }
- T* operator->() const { return mPtr; }
-
-private:
- void CleanUp() {
- if (mPtr) {
- --mPtr->RefCounted::refCount;
- if (mPtr->RefCounted::refCount == 0) {
- TDeleter::operator()(mPtr);
- }
- }
- }
-};
diff --git a/source/10-common/Rect.hpp b/source/10-common/Rect.hpp
deleted file mode 100644
index 86a1268..0000000
--- a/source/10-common/Rect.hpp
+++ /dev/null
@@ -1,164 +0,0 @@
-#pragma once
-
-#include <glm/glm.hpp>
-
-/// Rect is a rectangle representation based on a point and a dimensions, in television coordinate space
-/// (x increases from left to right, y increases from top to bottom).
-template <typename T>
-class Rect {
-public:
- using ScalarType = T;
- using VectorType = glm::vec<2, T>;
-
-public:
- T x;
- T y;
- T width;
- T height;
-
-public:
- Rect()
- : x{ 0 }, y{ 0 }, width{ 0 }, height{ 0 } {
- }
-
- Rect(T x, T y, T width, T height)
- : x{ x }, y{ y }, width{ width }, height{ height } {
- }
-
- Rect(VectorType pos, VectorType size)
- : x{ pos.x }
- , y{ pos.y }
- , width{ size.x }
- , height{ size.y } {
- }
-
- T x0() const { return x; }
- T y0() const { return y; }
- T x1() const { return x + width; }
- T y1() const { return y + height; }
-
- VectorType TopLeft() const {
- return VectorType{ x, y };
- }
-
- VectorType TopRight() const {
- return VectorType{ x + width, y };
- }
-
- VectorType BottomLeft() const {
- return VectorType{ x, y + height };
- }
-
- VectorType BottomRight() const {
- return VectorType{ x + width, y + height };
- }
-
- VectorType Center() const {
- return TopLeft() + VectorType{ width / 2, height / 2 };
- }
-
- VectorType Dimensions() const {
- return VectorType{ width, height };
- }
-
- VectorType Extents() const {
- return VectorType{ width / 2, height / 2 };
- }
-
- /// Assumes `bySize * 2` is smaller than both `width` and `height` (does not produce a negative-dimension rectangle).
- Rect Shrink(T bySize) const {
- T two = bySize * 2;
- return Rect{ x + bySize, y + bySize, width - two, height - two };
- }
-
- Rect Shrink(T left, T top, T right, T bottom) const {
- return Rect{
- x + left,
- y + top,
- width - left - right,
- height - top - bottom,
- };
- }
-
- Rect Expand(T bySize) const {
- T two = bySize * 2;
- return Rect{ x - bySize, y - bySize, width + two, height + two };
- }
-
- Rect Expand(T left, T top, T right, T bottom) const {
- return Rect{
- x - left,
- y - top,
- width + left + right,
- height + top + bottom,
- };
- }
-
- bool Contains(VectorType point) const {
- return point.x >= x &&
- point.y >= y &&
- point.x < x + width &&
- point.y < y + height;
- }
-
- bool Intersects(const Rect& that) const {
- bool xBetween = x > that.x0() && x < that.x1();
- bool yBetween = y > that.y0() && y < that.y1();
- return xBetween && yBetween;
- }
-
- // Write min()/max() tenary by hand so that we don't have to include <algorithm>
- // This file is practically going to be included in every file in this project
-
- static Rect Intersection(const Rect& a, const Rect& b) {
- auto x0 = a.x0() > b.x0() ? a.x0() : b.x0(); // Max
- auto y0 = a.y0() > b.y0() ? a.y0() : b.y0(); // Max
- auto x1 = a.x1() < b.x1() ? a.x1() : b.x1(); // Min
- auto y1 = a.y1() < b.y1() ? a.y1() : b.y1(); // Min
- auto width = x1 - x0;
- auto height = y1 - x0;
- return Rect{ x0, y0, width, height };
- }
-
- static Rect Union(const Rect& a, const Rect& b) {
- auto x0 = a.x0() < b.x0() ? a.x0() : b.x0(); // Min
- auto y0 = a.y0() < b.y0() ? a.y0() : b.y0(); // Min
- auto x1 = a.x1() > b.x1() ? a.x1() : b.x1(); // Max
- auto y1 = a.y1() > b.y1() ? a.y1() : b.y1(); // Max
- auto width = x1 - x0;
- auto height = y1 - x0;
- return Rect{ x0, y0, width, height };
- }
-
- friend bool operator==(const Rect<T>&, const Rect<T>&) = default;
-
- Rect operator+(glm::vec<2, T> offset) const {
- return { x + offset.x, y + offset.y, width, height };
- }
-
- Rect operator-(glm::vec<2, T> offset) const {
- return { x - offset.x, y - offset.y, width, height };
- }
-
- Rect& operator+=(glm::vec<2, T> offset) {
- x += offset.x;
- y += offset.y;
- return *this;
- }
-
- Rect& operator-=(glm::vec<2, T> offset) {
- x -= offset.x;
- y -= offset.y;
- return *this;
- }
-
- template <typename TTarget>
- Rect<TTarget> Cast() const {
- return {
- static_cast<TTarget>(x),
- static_cast<TTarget>(y),
- static_cast<TTarget>(width),
- static_cast<TTarget>(height),
- };
- }
-};
diff --git a/source/10-common/RingBuffer.hpp b/source/10-common/RingBuffer.hpp
deleted file mode 100644
index 4eaa007..0000000
--- a/source/10-common/RingBuffer.hpp
+++ /dev/null
@@ -1,191 +0,0 @@
-#pragma once
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <iterator>
-
-class RingBufferSentinel {};
-
-template <typename TContainer>
-class RingBufferIterator {
-public:
- using difference_type = TContainer::difference_type;
- using value_type = TContainer::value_type; // C++20 relaxed usage requirements of `typename`, now locations where a type is required (like here in a using statement) it's no longer mandatory
- using pointer = value_type*;
- using reference = value_type&;
- using iterator_category = std::random_access_iterator_tag;
-
-public:
- TContainer* container;
- TContainer::size_type curr; // C++20 relaxed usage requirements of `typename`, same here
- bool needsWrapAround;
- bool hasWrappedAround = false;
-
-public:
- reference operator*() const {
- return container->mRing[curr];
- }
-
- RingBufferIterator& operator++() {
- assert(*this != RingBufferSentinel{});
- ++curr;
- if (needsWrapAround && curr == container->mCapacity) {
- hasWrappedAround = true;
- curr = 0;
- }
- return *this;
- }
-
- bool operator==(const RingBufferIterator& that) const {
- assert(this->container == that.container);
- return this->curr == that.curr;
- }
-
- bool operator==(const RingBufferSentinel&) const {
- return curr == container->mTailIdx && (!needsWrapAround || hasWrappedAround);
- }
-};
-
-template <typename T>
-class RingBuffer {
-public:
- using value_type = T;
- using reference = T&;
- using const_reference = const T&;
- friend class RingBufferIterator<RingBuffer>;
- using iterator = RingBufferIterator<RingBuffer>;
- friend class RingBufferIterator<const RingBuffer>;
- using const_iterator = RingBufferIterator<const RingBuffer>;
- using sentinel = RingBufferSentinel; // Not a part of C++'s Container named requirements, added here for convenience
- using difference_type = ptrdiff_t;
- using size_type = size_t;
-
-private:
- T* mRing = nullptr;
- size_type mHeadIdx = 0;
- size_type mTailIdx = 0;
- size_type mCapacity = 0;
- size_type mSize = 0;
-
-public:
- RingBuffer() noexcept = default;
-
- ~RingBuffer() noexcept {
- delete[] mRing;
- }
-
- RingBuffer(const RingBuffer&) noexcept = delete;
- RingBuffer& operator=(const RingBuffer&) noexcept = delete;
-
- RingBuffer(RingBuffer&& that) noexcept
- : mRing{ that.mRing }
- , mHeadIdx{ that.mHeadIdx }
- , mTailIdx{ that.mTailIdx }
- , mCapacity{ that.mCapacity }
- , mSize{ that.mSize } {
- that.mRing = nullptr;
- }
-
- RingBuffer& operator=(RingBuffer&& that) noexcept {
- if (this != &that) {
- auto oldThisRing = this->mRing;
- this->mRing = that.mRing;
- that.mRing = nullptr;
- delete oldThisRing;
-
- this->mHeadIdx = that.mHeadIdx;
- this->mTailIdx = that.mTailIdx;
- this->mCapacity = that.mCapacity;
- this->mSize = that.mSize;
- }
-
- return *this;
- }
-
- [[nodiscard]] iterator begin() {
- return {
- .container = this,
- .curr = mHeadIdx,
- .needsWrapAround = mHeadIdx >= mTailIdx,
- };
- }
-
- [[nodiscard]] const_iterator begin() const { return cbegin(); }
-
- // Same type for both const this and non-const `this`
- [[nodiscard]] sentinel end() const { return sentinel{}; }
-
- [[nodiscard]] const_iterator cbegin() const {
- return {
- .container = this,
- .curr = mHeadIdx,
- .needsWrapAround = mHeadIdx >= mTailIdx,
- };
- }
-
- [[nodiscard]] sentinel cend() const { return sentinel{}; }
-
- [[nodiscard]] T& operator[](size_type i) { return const_cast<T&>(const_cast<const RingBuffer&>(*this)[i]); }
- [[nodiscard]] const T& operator[](size_type i) const {
- assert(mRing != nullptr);
- size_type idx = mHeadIdx + i;
- if (idx >= mCapacity) {
- idx -= mCapacity;
- }
- return mRing[idx];
- }
-
- void push_back(T t) {
- assert(mRing != nullptr);
- if (mTailIdx == mCapacity) {
- // Ring buffer is filled to the right, warp around to the beginning
- // mHeadIdx > 0 must be true, since we checked that as condition (1) above
- mRing[0] = std::move(t);
- mTailIdx = 1;
- } else {
- mRing[mTailIdx] = std::move(t);
- mTailIdx += 1;
- }
-
- // Push mHeadIdx backwards if overwrote element in a filled buffer
- bool bufferFilled = mSize == mCapacity;
- if (bufferFilled && mTailIdx > mHeadIdx) {
- mHeadIdx += 1;
- if (mHeadIdx == mCapacity) {
- mHeadIdx = 0;
- }
- }
-
- if (!bufferFilled) {
- ++mSize;
- }
- }
-
- [[nodiscard]] size_type capacity() const {
- return mCapacity;
- }
-
- [[nodiscard]] size_type size() const {
- return mSize;
- }
-
- [[nodiscard]] T* GetBuffer() const { return mRing; }
- [[nodiscard]] size_type GetHeadIdx() const { return mHeadIdx; }
- [[nodiscard]] size_type GetTailIdx() const { return mTailIdx; }
-
- void resize(size_type newCapacity) {
- auto size = this->size();
-
- auto oldRing = mRing;
- auto newRing = mRing = new T[newCapacity];
- if (oldRing != nullptr) {
- std::rotate_copy(oldRing, oldRing + mHeadIdx, oldRing + mCapacity, newRing);
- delete[] oldRing;
- }
-
- mCapacity = newCapacity;
- mHeadIdx = 0;
- mTailIdx = size;
- }
-};
diff --git a/source/10-common/ScopeGuard.hpp b/source/10-common/ScopeGuard.hpp
deleted file mode 100644
index 4e1a348..0000000
--- a/source/10-common/ScopeGuard.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#pragma once
-
-#include "Macros.hpp"
-
-#include <utility>
-
-template <typename TCleanupFunc>
-class ScopeGuard {
-private:
- TCleanupFunc mFunc;
- bool mDismissed = false;
-
-public:
- /// Specifically left this implicit so that constructs like
- /// \code
- /// ScopeGuard sg = [&]() { res.Cleanup(); };
- /// \endcode
- /// would work. It is highly discourage and unlikely that one would want to use ScopeGuard as a function
- /// parameter, so the normal argument that implicit conversion are harmful doesn't really apply here.
- // Deliberately not explicit to allow usages like: ScopeGuard var = lambda;
- ScopeGuard(TCleanupFunc&& function) noexcept
- : mFunc{ std::move(function) } {
- }
-
- ~ScopeGuard() noexcept {
- if (!mDismissed) {
- mFunc();
- }
- }
-
- ScopeGuard(const ScopeGuard&) = delete;
- ScopeGuard& operator=(const ScopeGuard&) = delete;
-
- ScopeGuard(ScopeGuard&& that) noexcept
- : mFunc{ std::move(that.mFunc) } {
- that.Cancel();
- }
-
- ScopeGuard& operator=(ScopeGuard&& that) noexcept {
- if (!mDismissed) {
- mFunc();
- }
- this->mFunc = std::move(that.mFunc);
- this->cancelled = std::exchange(that.cancelled, true);
- }
-
- void Dismiss() noexcept {
- mDismissed = true;
- }
-};
-
-template <typename T>
-auto GuardDeletion(T* ptr) {
- return ScopeGuard([ptr]() {
- delete ptr;
- });
-}
-
-#define SCOPE_GUARD(name) ScopeGuard name = [&]()
-#define DEFER ScopeGuard UNIQUE_NAME(scopeGuard) = [&]()
diff --git a/source/10-common/SmallVector.cpp b/source/10-common/SmallVector.cpp
deleted file mode 100644
index 65953f0..0000000
--- a/source/10-common/SmallVector.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-// Obtained from https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/SmallVector.cpp
-// commit 4b82bb6d82f65f98f23d0e4c2cd5297dc162864c
-// adapted in code style and utilities to fix this project
-
-//===- llvm/ADT/SmallVector.cpp - 'Normally small' vectors ----------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements the SmallVector class.
-//
-//===----------------------------------------------------------------------===//
-
-#include "SmallVector.hpp"
-
-#include <cstdlib>
-#include <stdexcept>
-#include <string>
-
-// Check that no bytes are wasted and everything is well-aligned.
-namespace {
-// These structures may cause binary compat warnings on AIX. Suppress the
-// warning since we are only using these types for the static assertions below.
-#if defined(_AIX)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Waix-compat"
-#endif
-struct Struct16B {
- alignas(16) void* X;
-};
-struct Struct32B {
- alignas(32) void* X;
-};
-#if defined(_AIX)
-# pragma GCC diagnostic pop
-#endif
-} // namespace
-static_assert(sizeof(SmallVector<void*, 0>) ==
- sizeof(unsigned) * 2 + sizeof(void*),
- "wasted space in SmallVector size 0");
-static_assert(alignof(SmallVector<Struct16B, 0>) >= alignof(Struct16B),
- "wrong alignment for 16-byte aligned T");
-static_assert(alignof(SmallVector<Struct32B, 0>) >= alignof(Struct32B),
- "wrong alignment for 32-byte aligned T");
-static_assert(sizeof(SmallVector<Struct16B, 0>) >= alignof(Struct16B),
- "missing padding for 16-byte aligned T");
-static_assert(sizeof(SmallVector<Struct32B, 0>) >= alignof(Struct32B),
- "missing padding for 32-byte aligned T");
-static_assert(sizeof(SmallVector<void*, 1>) ==
- sizeof(unsigned) * 2 + sizeof(void*) * 2,
- "wasted space in SmallVector size 1");
-
-static_assert(sizeof(SmallVector<char, 0>) ==
- sizeof(void*) * 2 + sizeof(void*),
- "1 byte elements have word-sized type for size and capacity");
-
-/// Report that MinSize doesn't fit into this vector's size type. Throws
-/// std::length_error or calls report_fatal_error.
-[[noreturn]] static void report_size_overflow(size_t MinSize, size_t MaxSize);
-static void report_size_overflow(size_t MinSize, size_t MaxSize) {
- std::string Reason = "SmallVector unable to grow. Requested capacity (" +
- std::to_string(MinSize) +
- ") is larger than maximum value for size type (" +
- std::to_string(MaxSize) + ")";
- throw std::length_error(Reason);
-}
-
-/// Report that this vector is already at maximum capacity. Throws
-/// std::length_error or calls report_fatal_error.
-[[noreturn]] static void report_at_maximum_capacity(size_t MaxSize);
-static void report_at_maximum_capacity(size_t MaxSize) {
- std::string Reason =
- "SmallVector capacity unable to grow. Already at maximum size " +
- std::to_string(MaxSize);
- throw std::length_error(Reason);
-}
-
-// Note: Moving this function into the header may cause performance regression.
-template <typename Size_T>
-static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
- constexpr size_t MaxSize = std::numeric_limits<Size_T>::max();
-
- // Ensure we can fit the new capacity.
- // This is only going to be applicable when the capacity is 32 bit.
- if (MinSize > MaxSize)
- report_size_overflow(MinSize, MaxSize);
-
- // Ensure we can meet the guarantee of space for at least one more element.
- // The above check alone will not catch the case where grow is called with a
- // default MinSize of 0, but the current capacity cannot be increased.
- // This is only going to be applicable when the capacity is 32 bit.
- if (OldCapacity == MaxSize)
- report_at_maximum_capacity(MaxSize);
-
- // In theory 2*capacity can overflow if the capacity is 64 bit, but the
- // original capacity would never be large enough for this to be a problem.
- size_t NewCapacity = 2 * OldCapacity + 1; // Always grow.
- return std::min(std::max(NewCapacity, MinSize), MaxSize);
-}
-
-// Note: Moving this function into the header may cause performance regression.
-template <typename Size_T>
-void* SmallVectorBase<Size_T>::mallocForGrow(size_t MinSize, size_t TSize, size_t& NewCapacity) {
- NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity());
- return malloc(NewCapacity * TSize);
-}
-
-// Note: Moving this function into the header may cause performance regression.
-template <typename Size_T>
-void SmallVectorBase<Size_T>::grow_pod(void* FirstEl, size_t MinSize, size_t TSize) {
- size_t NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity());
- void* NewElts;
- if (BeginX == FirstEl) {
- NewElts = malloc(NewCapacity * TSize);
-
- // Copy the elements over. No need to run dtors on PODs.
- memcpy(NewElts, this->BeginX, size() * TSize);
- } else {
- // If this wasn't grown from the inline copy, grow the allocated space.
- NewElts = realloc(this->BeginX, NewCapacity * TSize);
- }
-
- this->BeginX = NewElts;
- this->Capacity = NewCapacity;
-}
-
-template class SmallVectorBase<uint32_t>;
-
-// Disable the uint64_t instantiation for 32-bit builds.
-// Both uint32_t and uint64_t instantiations are needed for 64-bit builds.
-// This instantiation will never be used in 32-bit builds, and will cause
-// warnings when sizeof(Size_T) > sizeof(size_t).
-#if SIZE_MAX > UINT32_MAX
-template class SmallVectorBase<uint64_t>;
-
-// Assertions to ensure this #if stays in sync with SmallVectorSizeType.
-static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint64_t),
- "Expected SmallVectorBase<uint64_t> variant to be in use.");
-#else
-static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint32_t),
- "Expected SmallVectorBase<uint32_t> variant to be in use.");
-#endif
diff --git a/source/10-common/SmallVector.hpp b/source/10-common/SmallVector.hpp
deleted file mode 100644
index 3fc7519..0000000
--- a/source/10-common/SmallVector.hpp
+++ /dev/null
@@ -1,1332 +0,0 @@
-// Obtained from https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/ADT/SmallVector.h
-// commit 4b82bb6d82f65f98f23d0e4c2cd5297dc162864c
-// adapted in code style and utilities to fix this project
-
-//===- llvm/ADT/SmallVector.h - 'Normally small' vectors --------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// This file defines the SmallVector class.
-///
-//===----------------------------------------------------------------------===//
-
-#pragma once
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <cstdlib>
-#include <cstring>
-#include <functional>
-#include <initializer_list>
-#include <iterator>
-#include <limits>
-#include <memory>
-#include <new>
-#include <type_traits>
-#include <utility>
-
-#ifdef _MSC_VER
-# pragma warning(push)
-# pragma warning(disable : 4267) // The compiler detected a conversion from size_t to a smaller type.
-#endif
-
-#if __has_builtin(__builtin_expect) || defined(__GNUC__)
-# define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true)
-# define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false)
-#else
-# define LLVM_LIKELY(EXPR) (EXPR)
-# define LLVM_UNLIKELY(EXPR) (EXPR)
-#endif
-
-template <typename IteratorT>
-class iterator_range;
-
-/// This is all the stuff common to all SmallVectors.
-///
-/// The template parameter specifies the type which should be used to hold the
-/// Size and Capacity of the SmallVector, so it can be adjusted.
-/// Using 32 bit size is desirable to shrink the size of the SmallVector.
-/// Using 64 bit size is desirable for cases like SmallVector<char>, where a
-/// 32 bit size would limit the vector to ~4GB. SmallVectors are used for
-/// buffering bitcode output - which can exceed 4GB.
-template <typename Size_T>
-class SmallVectorBase {
-protected:
- void* BeginX;
- Size_T Size = 0, Capacity;
-
- /// The maximum value of the Size_T used.
- static constexpr size_t SizeTypeMax() {
- return std::numeric_limits<Size_T>::max();
- }
-
- SmallVectorBase() = delete;
- SmallVectorBase(void* FirstEl, size_t TotalCapacity)
- : BeginX(FirstEl), Capacity(TotalCapacity) {}
-
- /// This is a helper for \a grow() that's out of line to reduce code
- /// duplication. This function will report a fatal error if it can't grow at
- /// least to \p MinSize.
- void* mallocForGrow(size_t MinSize, size_t TSize, size_t& NewCapacity);
-
- /// This is an implementation of the grow() method which only works
- /// on POD-like data types and is out of line to reduce code duplication.
- /// This function will report a fatal error if it cannot increase capacity.
- void grow_pod(void* FirstEl, size_t MinSize, size_t TSize);
-
-public:
- size_t size() const { return Size; }
- size_t capacity() const { return Capacity; }
-
- [[nodiscard]] bool empty() const { return !Size; }
-
-protected:
- /// Set the array size to \p N, which the current array must have enough
- /// capacity for.
- ///
- /// This does not construct or destroy any elements in the vector.
- void set_size(size_t N) {
- assert(N <= capacity());
- Size = N;
- }
-};
-
-template <typename T>
-using SmallVectorSizeType =
- typename std::conditional<sizeof(T) < 4 && sizeof(void*) >= 8, uint64_t, uint32_t>::type;
-
-/// Figure out the offset of the first element.
-template <typename T, typename = void>
-struct SmallVectorAlignmentAndSize {
- alignas(SmallVectorBase<SmallVectorSizeType<T>>) char Base[sizeof(
- SmallVectorBase<SmallVectorSizeType<T>>)];
- alignas(T) char FirstEl[sizeof(T)];
-};
-
-/// This is the part of SmallVectorTemplateBase which does not depend on whether
-/// the type T is a POD. The extra dummy template argument is used by ArrayRef
-/// to avoid unnecessarily requiring T to be complete.
-template <typename T, typename = void>
-class SmallVectorTemplateCommon
- : public SmallVectorBase<SmallVectorSizeType<T>> {
- using Base = SmallVectorBase<SmallVectorSizeType<T>>;
-
- /// Find the address of the first element. For this pointer math to be valid
- /// with small-size of 0 for T with lots of alignment, it's important that
- /// SmallVectorStorage is properly-aligned even for small-size of 0.
- void* getFirstEl() const {
- return const_cast<void*>(reinterpret_cast<const void*>(
- reinterpret_cast<const char*>(this) +
- offsetof(SmallVectorAlignmentAndSize<T>, FirstEl)));
- }
- // Space after 'FirstEl' is clobbered, do not add any instance vars after it.
-
-protected:
- SmallVectorTemplateCommon(size_t Size)
- : Base(getFirstEl(), Size) {}
-
- void grow_pod(size_t MinSize, size_t TSize) {
- Base::grow_pod(getFirstEl(), MinSize, TSize);
- }
-
- /// Return true if this is a smallvector which has not had dynamic
- /// memory allocated for it.
- bool isSmall() const { return this->BeginX == getFirstEl(); }
-
- /// Put this vector in a state of being small.
- void resetToSmall() {
- this->BeginX = getFirstEl();
- this->Size = this->Capacity = 0; // FIXME: Setting Capacity to 0 is suspect.
- }
-
- /// Return true if V is an internal reference to the given range.
- bool isReferenceToRange(const void* V, const void* First, const void* Last) const {
- // Use std::less to avoid UB.
- std::less<> LessThan;
- return !LessThan(V, First) && LessThan(V, Last);
- }
-
- /// Return true if V is an internal reference to this vector.
- bool isReferenceToStorage(const void* V) const {
- return isReferenceToRange(V, this->begin(), this->end());
- }
-
- /// Return true if First and Last form a valid (possibly empty) range in this
- /// vector's storage.
- bool isRangeInStorage(const void* First, const void* Last) const {
- // Use std::less to avoid UB.
- std::less<> LessThan;
- return !LessThan(First, this->begin()) && !LessThan(Last, First) &&
- !LessThan(this->end(), Last);
- }
-
- /// Return true unless Elt will be invalidated by resizing the vector to
- /// NewSize.
- bool isSafeToReferenceAfterResize(const void* Elt, size_t NewSize) {
- // Past the end.
- if (LLVM_LIKELY(!isReferenceToStorage(Elt)))
- return true;
-
- // Return false if Elt will be destroyed by shrinking.
- if (NewSize <= this->size())
- return Elt < this->begin() + NewSize;
-
- // Return false if we need to grow.
- return NewSize <= this->capacity();
- }
-
- /// Check whether Elt will be invalidated by resizing the vector to NewSize.
- void assertSafeToReferenceAfterResize(const void* Elt, size_t NewSize) {
- assert(isSafeToReferenceAfterResize(Elt, NewSize) &&
- "Attempting to reference an element of the vector in an operation "
- "that invalidates it");
- }
-
- /// Check whether Elt will be invalidated by increasing the size of the
- /// vector by N.
- void assertSafeToAdd(const void* Elt, size_t N = 1) {
- this->assertSafeToReferenceAfterResize(Elt, this->size() + N);
- }
-
- /// Check whether any part of the range will be invalidated by clearing.
- void assertSafeToReferenceAfterClear(const T* From, const T* To) {
- if (From == To)
- return;
- this->assertSafeToReferenceAfterResize(From, 0);
- this->assertSafeToReferenceAfterResize(To - 1, 0);
- }
- template <
- class ItTy,
- std::enable_if_t<!std::is_same<std::remove_const_t<ItTy>, T*>::value,
- bool> = false>
- void assertSafeToReferenceAfterClear(ItTy, ItTy) {}
-
- /// Check whether any part of the range will be invalidated by growing.
- void assertSafeToAddRange(const T* From, const T* To) {
- if (From == To)
- return;
- this->assertSafeToAdd(From, To - From);
- this->assertSafeToAdd(To - 1, To - From);
- }
- template <
- class ItTy,
- std::enable_if_t<!std::is_same<std::remove_const_t<ItTy>, T*>::value,
- bool> = false>
- void assertSafeToAddRange(ItTy, ItTy) {}
-
- /// Reserve enough space to add one element, and return the updated element
- /// pointer in case it was a reference to the storage.
- template <typename U>
- static const T* reserveForParamAndGetAddressImpl(U* This, const T& Elt, size_t N) {
- size_t NewSize = This->size() + N;
- if (LLVM_LIKELY(NewSize <= This->capacity()))
- return &Elt;
-
- bool ReferencesStorage = false;
- int64_t Index = -1;
- if (!U::TakesParamByValue) {
- if (LLVM_UNLIKELY(This->isReferenceToStorage(&Elt))) {
- ReferencesStorage = true;
- Index = &Elt - This->begin();
- }
- }
- This->grow(NewSize);
- return ReferencesStorage ? This->begin() + Index : &Elt;
- }
-
-public:
- using size_type = size_t;
- using difference_type = ptrdiff_t;
- using value_type = T;
- using iterator = T*;
- using const_iterator = const T*;
-
- using const_reverse_iterator = std::reverse_iterator<const_iterator>;
- using reverse_iterator = std::reverse_iterator<iterator>;
-
- using reference = T&;
- using const_reference = const T&;
- using pointer = T*;
- using const_pointer = const T*;
-
- using Base::capacity;
- using Base::empty;
- using Base::size;
-
- // forward iterator creation methods.
- iterator begin() { return (iterator)this->BeginX; }
- const_iterator begin() const { return (const_iterator)this->BeginX; }
- iterator end() { return begin() + size(); }
- const_iterator end() const { return begin() + size(); }
-
- // reverse iterator creation methods.
- reverse_iterator rbegin() { return reverse_iterator(end()); }
- const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
- reverse_iterator rend() { return reverse_iterator(begin()); }
- const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
-
- size_type size_in_bytes() const { return size() * sizeof(T); }
- size_type max_size() const {
- return std::min(this->SizeTypeMax(), size_type(-1) / sizeof(T));
- }
-
- size_t capacity_in_bytes() const { return capacity() * sizeof(T); }
-
- /// Return a pointer to the vector's buffer, even if empty().
- pointer data() { return pointer(begin()); }
- /// Return a pointer to the vector's buffer, even if empty().
- const_pointer data() const { return const_pointer(begin()); }
-
- reference operator[](size_type idx) {
- assert(idx < size());
- return begin()[idx];
- }
- const_reference operator[](size_type idx) const {
- assert(idx < size());
- return begin()[idx];
- }
-
- reference front() {
- assert(!empty());
- return begin()[0];
- }
- const_reference front() const {
- assert(!empty());
- return begin()[0];
- }
-
- reference back() {
- assert(!empty());
- return end()[-1];
- }
- const_reference back() const {
- assert(!empty());
- return end()[-1];
- }
-};
-
-/// SmallVectorTemplateBase<TriviallyCopyable = false> - This is where we put
-/// method implementations that are designed to work with non-trivial T's.
-///
-/// We approximate is_trivially_copyable with trivial move/copy construction and
-/// trivial destruction. While the standard doesn't specify that you're allowed
-/// copy these types with memcpy, there is no way for the type to observe this.
-/// This catches the important case of std::pair<POD, POD>, which is not
-/// trivially assignable.
-template <typename T, bool = (std::is_trivially_copy_constructible<T>::value) && (std::is_trivially_move_constructible<T>::value) && std::is_trivially_destructible<T>::value>
-class SmallVectorTemplateBase : public SmallVectorTemplateCommon<T> {
- friend class SmallVectorTemplateCommon<T>;
-
-protected:
- static constexpr bool TakesParamByValue = false;
- using ValueParamT = const T&;
-
- SmallVectorTemplateBase(size_t Size)
- : SmallVectorTemplateCommon<T>(Size) {}
-
- static void destroy_range(T* S, T* E) {
- while (S != E) {
- --E;
- E->~T();
- }
- }
-
- /// Move the range [I, E) into the uninitialized memory starting with "Dest",
- /// constructing elements as needed.
- template <typename It1, typename It2>
- static void uninitialized_move(It1 I, It1 E, It2 Dest) {
- std::uninitialized_copy(std::make_move_iterator(I),
- std::make_move_iterator(E),
- Dest);
- }
-
- /// Copy the range [I, E) onto the uninitialized memory starting with "Dest",
- /// constructing elements as needed.
- template <typename It1, typename It2>
- static void uninitialized_copy(It1 I, It1 E, It2 Dest) {
- std::uninitialized_copy(I, E, Dest);
- }
-
- /// Grow the allocated memory (without initializing new elements), doubling
- /// the size of the allocated memory. Guarantees space for at least one more
- /// element, or MinSize more elements if specified.
- void grow(size_t MinSize = 0);
-
- /// Create a new allocation big enough for \p MinSize and pass back its size
- /// in \p NewCapacity. This is the first section of \a grow().
- T* mallocForGrow(size_t MinSize, size_t& NewCapacity) {
- return static_cast<T*>(
- SmallVectorBase<SmallVectorSizeType<T>>::mallocForGrow(
- MinSize, sizeof(T), NewCapacity));
- }
-
- /// Move existing elements over to the new allocation \p NewElts, the middle
- /// section of \a grow().
- void moveElementsForGrow(T* NewElts);
-
- /// Transfer ownership of the allocation, finishing up \a grow().
- void takeAllocationForGrow(T* NewElts, size_t NewCapacity);
-
- /// Reserve enough space to add one element, and return the updated element
- /// pointer in case it was a reference to the storage.
- const T* reserveForParamAndGetAddress(const T& Elt, size_t N = 1) {
- return this->reserveForParamAndGetAddressImpl(this, Elt, N);
- }
-
- /// Reserve enough space to add one element, and return the updated element
- /// pointer in case it was a reference to the storage.
- T* reserveForParamAndGetAddress(T& Elt, size_t N = 1) {
- return const_cast<T*>(
- this->reserveForParamAndGetAddressImpl(this, Elt, N));
- }
-
- static T&& forward_value_param(T&& V) { return std::move(V); }
- static const T& forward_value_param(const T& V) { return V; }
-
- void growAndAssign(size_t NumElts, const T& Elt) {
- // Grow manually in case Elt is an internal reference.
- size_t NewCapacity;
- T* NewElts = mallocForGrow(NumElts, NewCapacity);
- std::uninitialized_fill_n(NewElts, NumElts, Elt);
- this->destroy_range(this->begin(), this->end());
- takeAllocationForGrow(NewElts, NewCapacity);
- this->set_size(NumElts);
- }
-
- template <typename... ArgTypes>
- T& growAndEmplaceBack(ArgTypes&&... Args) {
- // Grow manually in case one of Args is an internal reference.
- size_t NewCapacity;
- T* NewElts = mallocForGrow(0, NewCapacity);
- ::new ((void*)(NewElts + this->size())) T(std::forward<ArgTypes>(Args)...);
- moveElementsForGrow(NewElts);
- takeAllocationForGrow(NewElts, NewCapacity);
- this->set_size(this->size() + 1);
- return this->back();
- }
-
-public:
- void push_back(const T& Elt) {
- const T* EltPtr = reserveForParamAndGetAddress(Elt);
- ::new ((void*)this->end()) T(*EltPtr);
- this->set_size(this->size() + 1);
- }
-
- void push_back(T&& Elt) {
- T* EltPtr = reserveForParamAndGetAddress(Elt);
- ::new ((void*)this->end()) T(::std::move(*EltPtr));
- this->set_size(this->size() + 1);
- }
-
- void pop_back() {
- this->set_size(this->size() - 1);
- this->end()->~T();
- }
-};
-
-// Define this out-of-line to dissuade the C++ compiler from inlining it.
-template <typename T, bool TriviallyCopyable>
-void SmallVectorTemplateBase<T, TriviallyCopyable>::grow(size_t MinSize) {
- size_t NewCapacity;
- T* NewElts = mallocForGrow(MinSize, NewCapacity);
- moveElementsForGrow(NewElts);
- takeAllocationForGrow(NewElts, NewCapacity);
-}
-
-// Define this out-of-line to dissuade the C++ compiler from inlining it.
-template <typename T, bool TriviallyCopyable>
-void SmallVectorTemplateBase<T, TriviallyCopyable>::moveElementsForGrow(
- T* NewElts) {
- // Move the elements over.
- this->uninitialized_move(this->begin(), this->end(), NewElts);
-
- // Destroy the original elements.
- destroy_range(this->begin(), this->end());
-}
-
-// Define this out-of-line to dissuade the C++ compiler from inlining it.
-template <typename T, bool TriviallyCopyable>
-void SmallVectorTemplateBase<T, TriviallyCopyable>::takeAllocationForGrow(
- T* NewElts, size_t NewCapacity) {
- // If this wasn't grown from the inline copy, deallocate the old space.
- if (!this->isSmall())
- free(this->begin());
-
- this->BeginX = NewElts;
- this->Capacity = NewCapacity;
-}
-
-/// SmallVectorTemplateBase<TriviallyCopyable = true> - This is where we put
-/// method implementations that are designed to work with trivially copyable
-/// T's. This allows using memcpy in place of copy/move construction and
-/// skipping destruction.
-template <typename T>
-class SmallVectorTemplateBase<T, true> : public SmallVectorTemplateCommon<T> {
- friend class SmallVectorTemplateCommon<T>;
-
-protected:
- /// True if it's cheap enough to take parameters by value. Doing so avoids
- /// overhead related to mitigations for reference invalidation.
- static constexpr bool TakesParamByValue = sizeof(T) <= 2 * sizeof(void*);
-
- /// Either const T& or T, depending on whether it's cheap enough to take
- /// parameters by value.
- using ValueParamT =
- typename std::conditional<TakesParamByValue, T, const T&>::type;
-
- SmallVectorTemplateBase(size_t Size)
- : SmallVectorTemplateCommon<T>(Size) {}
-
- // No need to do a destroy loop for POD's.
- static void destroy_range(T*, T*) {}
-
- /// Move the range [I, E) onto the uninitialized memory
- /// starting with "Dest", constructing elements into it as needed.
- template <typename It1, typename It2>
- static void uninitialized_move(It1 I, It1 E, It2 Dest) {
- // Just do a copy.
- uninitialized_copy(I, E, Dest);
- }
-
- /// Copy the range [I, E) onto the uninitialized memory
- /// starting with "Dest", constructing elements into it as needed.
- template <typename It1, typename It2>
- static void uninitialized_copy(It1 I, It1 E, It2 Dest) {
- // Arbitrary iterator types; just use the basic implementation.
- std::uninitialized_copy(I, E, Dest);
- }
-
- /// Copy the range [I, E) onto the uninitialized memory
- /// starting with "Dest", constructing elements into it as needed.
- template <typename T1, typename T2>
- static void uninitialized_copy(
- T1* I, T1* E, T2* Dest, std::enable_if_t<std::is_same<typename std::remove_const<T1>::type, T2>::value>* = nullptr) {
- // Use memcpy for PODs iterated by pointers (which includes SmallVector
- // iterators): std::uninitialized_copy optimizes to memmove, but we can
- // use memcpy here. Note that I and E are iterators and thus might be
- // invalid for memcpy if they are equal.
- if (I != E)
- memcpy(reinterpret_cast<void*>(Dest), I, (E - I) * sizeof(T));
- }
-
- /// Double the size of the allocated memory, guaranteeing space for at
- /// least one more element or MinSize if specified.
- void grow(size_t MinSize = 0) { this->grow_pod(MinSize, sizeof(T)); }
-
- /// Reserve enough space to add one element, and return the updated element
- /// pointer in case it was a reference to the storage.
- const T* reserveForParamAndGetAddress(const T& Elt, size_t N = 1) {
- return this->reserveForParamAndGetAddressImpl(this, Elt, N);
- }
-
- /// Reserve enough space to add one element, and return the updated element
- /// pointer in case it was a reference to the storage.
- T* reserveForParamAndGetAddress(T& Elt, size_t N = 1) {
- return const_cast<T*>(
- this->reserveForParamAndGetAddressImpl(this, Elt, N));
- }
-
- /// Copy \p V or return a reference, depending on \a ValueParamT.
- static ValueParamT forward_value_param(ValueParamT V) { return V; }
-
- void growAndAssign(size_t NumElts, T Elt) {
- // Elt has been copied in case it's an internal reference, side-stepping
- // reference invalidation problems without losing the realloc optimization.
- this->set_size(0);
- this->grow(NumElts);
- std::uninitialized_fill_n(this->begin(), NumElts, Elt);
- this->set_size(NumElts);
- }
-
- template <typename... ArgTypes>
- T& growAndEmplaceBack(ArgTypes&&... Args) {
- // Use push_back with a copy in case Args has an internal reference,
- // side-stepping reference invalidation problems without losing the realloc
- // optimization.
- push_back(T(std::forward<ArgTypes>(Args)...));
- return this->back();
- }
-
-public:
- void push_back(ValueParamT Elt) {
- const T* EltPtr = reserveForParamAndGetAddress(Elt);
- memcpy(reinterpret_cast<void*>(this->end()), EltPtr, sizeof(T));
- this->set_size(this->size() + 1);
- }
-
- void pop_back() { this->set_size(this->size() - 1); }
-};
-
-/// This class consists of common code factored out of the SmallVector class to
-/// reduce code duplication based on the SmallVector 'N' template parameter.
-template <typename T>
-class SmallVectorImpl : public SmallVectorTemplateBase<T> {
- using SuperClass = SmallVectorTemplateBase<T>;
-
-public:
- using iterator = typename SuperClass::iterator;
- using const_iterator = typename SuperClass::const_iterator;
- using reference = typename SuperClass::reference;
- using size_type = typename SuperClass::size_type;
-
-protected:
- using SmallVectorTemplateBase<T>::TakesParamByValue;
- using ValueParamT = typename SuperClass::ValueParamT;
-
- // Default ctor - Initialize to empty.
- explicit SmallVectorImpl(unsigned N)
- : SmallVectorTemplateBase<T>(N) {}
-
- void assignRemote(SmallVectorImpl&& RHS) {
- this->destroy_range(this->begin(), this->end());
- if (!this->isSmall())
- free(this->begin());
- this->BeginX = RHS.BeginX;
- this->Size = RHS.Size;
- this->Capacity = RHS.Capacity;
- RHS.resetToSmall();
- }
-
-public:
- SmallVectorImpl(const SmallVectorImpl&) = delete;
-
- ~SmallVectorImpl() {
- // Subclass has already destructed this vector's elements.
- // If this wasn't grown from the inline copy, deallocate the old space.
- if (!this->isSmall())
- free(this->begin());
- }
-
- void clear() {
- this->destroy_range(this->begin(), this->end());
- this->Size = 0;
- }
-
-private:
- // Make set_size() private to avoid misuse in subclasses.
- using SuperClass::set_size;
-
- template <bool ForOverwrite>
- void resizeImpl(size_type N) {
- if (N == this->size())
- return;
-
- if (N < this->size()) {
- this->truncate(N);
- return;
- }
-
- this->reserve(N);
- for (auto I = this->end(), E = this->begin() + N; I != E; ++I)
- if (ForOverwrite)
- new (&*I) T;
- else
- new (&*I) T();
- this->set_size(N);
- }
-
-public:
- void resize(size_type N) { resizeImpl<false>(N); }
-
- /// Like resize, but \ref T is POD, the new values won't be initialized.
- void resize_for_overwrite(size_type N) { resizeImpl<true>(N); }
-
- /// Like resize, but requires that \p N is less than \a size().
- void truncate(size_type N) {
- assert(this->size() >= N && "Cannot increase size with truncate");
- this->destroy_range(this->begin() + N, this->end());
- this->set_size(N);
- }
-
- void resize(size_type N, ValueParamT NV) {
- if (N == this->size())
- return;
-
- if (N < this->size()) {
- this->truncate(N);
- return;
- }
-
- // N > this->size(). Defer to append.
- this->append(N - this->size(), NV);
- }
-
- void reserve(size_type N) {
- if (this->capacity() < N)
- this->grow(N);
- }
-
- void pop_back_n(size_type NumItems) {
- assert(this->size() >= NumItems);
- truncate(this->size() - NumItems);
- }
-
- [[nodiscard]] T pop_back_val() {
- T Result = ::std::move(this->back());
- this->pop_back();
- return Result;
- }
-
- void swap(SmallVectorImpl& RHS);
-
- /// Add the specified range to the end of the SmallVector.
- template <typename in_iter,
- typename = std::enable_if_t<std::is_convertible<
- typename std::iterator_traits<in_iter>::iterator_category,
- std::input_iterator_tag>::value>>
- void append(in_iter in_start, in_iter in_end) {
- this->assertSafeToAddRange(in_start, in_end);
- size_type NumInputs = std::distance(in_start, in_end);
- this->reserve(this->size() + NumInputs);
- this->uninitialized_copy(in_start, in_end, this->end());
- this->set_size(this->size() + NumInputs);
- }
-
- /// Append \p NumInputs copies of \p Elt to the end.
- void append(size_type NumInputs, ValueParamT Elt) {
- const T* EltPtr = this->reserveForParamAndGetAddress(Elt, NumInputs);
- std::uninitialized_fill_n(this->end(), NumInputs, *EltPtr);
- this->set_size(this->size() + NumInputs);
- }
-
- void append(std::initializer_list<T> IL) {
- append(IL.begin(), IL.end());
- }
-
- void append(const SmallVectorImpl& RHS) { append(RHS.begin(), RHS.end()); }
-
- void assign(size_type NumElts, ValueParamT Elt) {
- // Note that Elt could be an internal reference.
- if (NumElts > this->capacity()) {
- this->growAndAssign(NumElts, Elt);
- return;
- }
-
- // Assign over existing elements.
- std::fill_n(this->begin(), std::min(NumElts, this->size()), Elt);
- if (NumElts > this->size())
- std::uninitialized_fill_n(this->end(), NumElts - this->size(), Elt);
- else if (NumElts < this->size())
- this->destroy_range(this->begin() + NumElts, this->end());
- this->set_size(NumElts);
- }
-
- // FIXME: Consider assigning over existing elements, rather than clearing &
- // re-initializing them - for all assign(...) variants.
-
- template <typename in_iter,
- typename = std::enable_if_t<std::is_convertible<
- typename std::iterator_traits<in_iter>::iterator_category,
- std::input_iterator_tag>::value>>
- void assign(in_iter in_start, in_iter in_end) {
- this->assertSafeToReferenceAfterClear(in_start, in_end);
- clear();
- append(in_start, in_end);
- }
-
- void assign(std::initializer_list<T> IL) {
- clear();
- append(IL);
- }
-
- void assign(const SmallVectorImpl& RHS) { assign(RHS.begin(), RHS.end()); }
-
- iterator erase(const_iterator CI) {
- // Just cast away constness because this is a non-const member function.
- iterator I = const_cast<iterator>(CI);
-
- assert(this->isReferenceToStorage(CI) && "Iterator to erase is out of bounds.");
-
- iterator N = I;
- // Shift all elts down one.
- std::move(I + 1, this->end(), I);
- // Drop the last elt.
- this->pop_back();
- return (N);
- }
-
- iterator erase(const_iterator CS, const_iterator CE) {
- // Just cast away constness because this is a non-const member function.
- iterator S = const_cast<iterator>(CS);
- iterator E = const_cast<iterator>(CE);
-
- assert(this->isRangeInStorage(S, E) && "Range to erase is out of bounds.");
-
- iterator N = S;
- // Shift all elts down.
- iterator I = std::move(E, this->end(), S);
- // Drop the last elts.
- this->destroy_range(I, this->end());
- this->set_size(I - this->begin());
- return (N);
- }
-
-private:
- template <typename ArgType>
- iterator insert_one_impl(iterator I, ArgType&& Elt) {
- // Callers ensure that ArgType is derived from T.
- static_assert(
- std::is_same<std::remove_const_t<std::remove_reference_t<ArgType>>,
- T>::value,
- "ArgType must be derived from T!");
-
- if (I == this->end()) { // Important special case for empty vector.
- this->push_back(::std::forward<ArgType>(Elt));
- return this->end() - 1;
- }
-
- assert(this->isReferenceToStorage(I) && "Insertion iterator is out of bounds.");
-
- // Grow if necessary.
- size_t Index = I - this->begin();
- std::remove_reference_t<ArgType>* EltPtr =
- this->reserveForParamAndGetAddress(Elt);
- I = this->begin() + Index;
-
- ::new ((void*)this->end()) T(::std::move(this->back()));
- // Push everything else over.
- std::move_backward(I, this->end() - 1, this->end());
- this->set_size(this->size() + 1);
-
- // If we just moved the element we're inserting, be sure to update
- // the reference (never happens if TakesParamByValue).
- static_assert(!TakesParamByValue || std::is_same<ArgType, T>::value,
- "ArgType must be 'T' when taking by value!");
- if (!TakesParamByValue && this->isReferenceToRange(EltPtr, I, this->end()))
- ++EltPtr;
-
- *I = ::std::forward<ArgType>(*EltPtr);
- return I;
- }
-
-public:
- iterator insert(iterator I, T&& Elt) {
- return insert_one_impl(I, this->forward_value_param(std::move(Elt)));
- }
-
- iterator insert(iterator I, const T& Elt) {
- return insert_one_impl(I, this->forward_value_param(Elt));
- }
-
- iterator insert(iterator I, size_type NumToInsert, ValueParamT Elt) {
- // Convert iterator to elt# to avoid invalidating iterator when we reserve()
- size_t InsertElt = I - this->begin();
-
- if (I == this->end()) { // Important special case for empty vector.
- append(NumToInsert, Elt);
- return this->begin() + InsertElt;
- }
-
- assert(this->isReferenceToStorage(I) && "Insertion iterator is out of bounds.");
-
- // Ensure there is enough space, and get the (maybe updated) address of
- // Elt.
- const T* EltPtr = this->reserveForParamAndGetAddress(Elt, NumToInsert);
-
- // Uninvalidate the iterator.
- I = this->begin() + InsertElt;
-
- // If there are more elements between the insertion point and the end of the
- // range than there are being inserted, we can use a simple approach to
- // insertion. Since we already reserved space, we know that this won't
- // reallocate the vector.
- if (size_t(this->end() - I) >= NumToInsert) {
- T* OldEnd = this->end();
- append(std::move_iterator<iterator>(this->end() - NumToInsert),
- std::move_iterator<iterator>(this->end()));
-
- // Copy the existing elements that get replaced.
- std::move_backward(I, OldEnd - NumToInsert, OldEnd);
-
- // If we just moved the element we're inserting, be sure to update
- // the reference (never happens if TakesParamByValue).
- if (!TakesParamByValue && I <= EltPtr && EltPtr < this->end())
- EltPtr += NumToInsert;
-
- std::fill_n(I, NumToInsert, *EltPtr);
- return I;
- }
-
- // Otherwise, we're inserting more elements than exist already, and we're
- // not inserting at the end.
-
- // Move over the elements that we're about to overwrite.
- T* OldEnd = this->end();
- this->set_size(this->size() + NumToInsert);
- size_t NumOverwritten = OldEnd - I;
- this->uninitialized_move(I, OldEnd, this->end() - NumOverwritten);
-
- // If we just moved the element we're inserting, be sure to update
- // the reference (never happens if TakesParamByValue).
- if (!TakesParamByValue && I <= EltPtr && EltPtr < this->end())
- EltPtr += NumToInsert;
-
- // Replace the overwritten part.
- std::fill_n(I, NumOverwritten, *EltPtr);
-
- // Insert the non-overwritten middle part.
- std::uninitialized_fill_n(OldEnd, NumToInsert - NumOverwritten, *EltPtr);
- return I;
- }
-
- template <typename ItTy,
- typename = std::enable_if_t<std::is_convertible<
- typename std::iterator_traits<ItTy>::iterator_category,
- std::input_iterator_tag>::value>>
- iterator insert(iterator I, ItTy From, ItTy To) {
- // Convert iterator to elt# to avoid invalidating iterator when we reserve()
- size_t InsertElt = I - this->begin();
-
- if (I == this->end()) { // Important special case for empty vector.
- append(From, To);
- return this->begin() + InsertElt;
- }
-
- assert(this->isReferenceToStorage(I) && "Insertion iterator is out of bounds.");
-
- // Check that the reserve that follows doesn't invalidate the iterators.
- this->assertSafeToAddRange(From, To);
-
- size_t NumToInsert = std::distance(From, To);
-
- // Ensure there is enough space.
- reserve(this->size() + NumToInsert);
-
- // Uninvalidate the iterator.
- I = this->begin() + InsertElt;
-
- // If there are more elements between the insertion point and the end of the
- // range than there are being inserted, we can use a simple approach to
- // insertion. Since we already reserved space, we know that this won't
- // reallocate the vector.
- if (size_t(this->end() - I) >= NumToInsert) {
- T* OldEnd = this->end();
- append(std::move_iterator<iterator>(this->end() - NumToInsert),
- std::move_iterator<iterator>(this->end()));
-
- // Copy the existing elements that get replaced.
- std::move_backward(I, OldEnd - NumToInsert, OldEnd);
-
- std::copy(From, To, I);
- return I;
- }
-
- // Otherwise, we're inserting more elements than exist already, and we're
- // not inserting at the end.
-
- // Move over the elements that we're about to overwrite.
- T* OldEnd = this->end();
- this->set_size(this->size() + NumToInsert);
- size_t NumOverwritten = OldEnd - I;
- this->uninitialized_move(I, OldEnd, this->end() - NumOverwritten);
-
- // Replace the overwritten part.
- for (T* J = I; NumOverwritten > 0; --NumOverwritten) {
- *J = *From;
- ++J;
- ++From;
- }
-
- // Insert the non-overwritten middle part.
- this->uninitialized_copy(From, To, OldEnd);
- return I;
- }
-
- void insert(iterator I, std::initializer_list<T> IL) {
- insert(I, IL.begin(), IL.end());
- }
-
- template <typename... ArgTypes>
- reference emplace_back(ArgTypes&&... Args) {
- if (LLVM_UNLIKELY(this->size() >= this->capacity()))
- return this->growAndEmplaceBack(std::forward<ArgTypes>(Args)...);
-
- ::new ((void*)this->end()) T(std::forward<ArgTypes>(Args)...);
- this->set_size(this->size() + 1);
- return this->back();
- }
-
- SmallVectorImpl& operator=(const SmallVectorImpl& RHS);
-
- SmallVectorImpl& operator=(SmallVectorImpl&& RHS);
-
- bool operator==(const SmallVectorImpl& RHS) const {
- if (this->size() != RHS.size()) return false;
- return std::equal(this->begin(), this->end(), RHS.begin());
- }
- bool operator!=(const SmallVectorImpl& RHS) const {
- return !(*this == RHS);
- }
-
- bool operator<(const SmallVectorImpl& RHS) const {
- return std::lexicographical_compare(this->begin(), this->end(), RHS.begin(), RHS.end());
- }
-};
-
-template <typename T>
-void SmallVectorImpl<T>::swap(SmallVectorImpl<T>& RHS) {
- if (this == &RHS) return;
-
- // We can only avoid copying elements if neither vector is small.
- if (!this->isSmall() && !RHS.isSmall()) {
- std::swap(this->BeginX, RHS.BeginX);
- std::swap(this->Size, RHS.Size);
- std::swap(this->Capacity, RHS.Capacity);
- return;
- }
- this->reserve(RHS.size());
- RHS.reserve(this->size());
-
- // Swap the shared elements.
- size_t NumShared = this->size();
- if (NumShared > RHS.size()) NumShared = RHS.size();
- for (size_type i = 0; i != NumShared; ++i)
- std::swap((*this)[i], RHS[i]);
-
- // Copy over the extra elts.
- if (this->size() > RHS.size()) {
- size_t EltDiff = this->size() - RHS.size();
- this->uninitialized_copy(this->begin() + NumShared, this->end(), RHS.end());
- RHS.set_size(RHS.size() + EltDiff);
- this->destroy_range(this->begin() + NumShared, this->end());
- this->set_size(NumShared);
- } else if (RHS.size() > this->size()) {
- size_t EltDiff = RHS.size() - this->size();
- this->uninitialized_copy(RHS.begin() + NumShared, RHS.end(), this->end());
- this->set_size(this->size() + EltDiff);
- this->destroy_range(RHS.begin() + NumShared, RHS.end());
- RHS.set_size(NumShared);
- }
-}
-
-template <typename T>
-SmallVectorImpl<T>& SmallVectorImpl<T>::
-operator=(const SmallVectorImpl<T>& RHS) {
- // Avoid self-assignment.
- if (this == &RHS) return *this;
-
- // If we already have sufficient space, assign the common elements, then
- // destroy any excess.
- size_t RHSSize = RHS.size();
- size_t CurSize = this->size();
- if (CurSize >= RHSSize) {
- // Assign common elements.
- iterator NewEnd;
- if (RHSSize)
- NewEnd = std::copy(RHS.begin(), RHS.begin() + RHSSize, this->begin());
- else
- NewEnd = this->begin();
-
- // Destroy excess elements.
- this->destroy_range(NewEnd, this->end());
-
- // Trim.
- this->set_size(RHSSize);
- return *this;
- }
-
- // If we have to grow to have enough elements, destroy the current elements.
- // This allows us to avoid copying them during the grow.
- // FIXME: don't do this if they're efficiently moveable.
- if (this->capacity() < RHSSize) {
- // Destroy current elements.
- this->clear();
- CurSize = 0;
- this->grow(RHSSize);
- } else if (CurSize) {
- // Otherwise, use assignment for the already-constructed elements.
- std::copy(RHS.begin(), RHS.begin() + CurSize, this->begin());
- }
-
- // Copy construct the new elements in place.
- this->uninitialized_copy(RHS.begin() + CurSize, RHS.end(), this->begin() + CurSize);
-
- // Set end.
- this->set_size(RHSSize);
- return *this;
-}
-
-template <typename T>
-SmallVectorImpl<T>& SmallVectorImpl<T>::operator=(SmallVectorImpl<T>&& RHS) {
- // Avoid self-assignment.
- if (this == &RHS) return *this;
-
- // If the RHS isn't small, clear this vector and then steal its buffer.
- if (!RHS.isSmall()) {
- this->assignRemote(std::move(RHS));
- return *this;
- }
-
- // If we already have sufficient space, assign the common elements, then
- // destroy any excess.
- size_t RHSSize = RHS.size();
- size_t CurSize = this->size();
- if (CurSize >= RHSSize) {
- // Assign common elements.
- iterator NewEnd = this->begin();
- if (RHSSize)
- NewEnd = std::move(RHS.begin(), RHS.end(), NewEnd);
-
- // Destroy excess elements and trim the bounds.
- this->destroy_range(NewEnd, this->end());
- this->set_size(RHSSize);
-
- // Clear the RHS.
- RHS.clear();
-
- return *this;
- }
-
- // If we have to grow to have enough elements, destroy the current elements.
- // This allows us to avoid copying them during the grow.
- // FIXME: this may not actually make any sense if we can efficiently move
- // elements.
- if (this->capacity() < RHSSize) {
- // Destroy current elements.
- this->clear();
- CurSize = 0;
- this->grow(RHSSize);
- } else if (CurSize) {
- // Otherwise, use assignment for the already-constructed elements.
- std::move(RHS.begin(), RHS.begin() + CurSize, this->begin());
- }
-
- // Move-construct the new elements in place.
- this->uninitialized_move(RHS.begin() + CurSize, RHS.end(), this->begin() + CurSize);
-
- // Set end.
- this->set_size(RHSSize);
-
- RHS.clear();
- return *this;
-}
-
-/// Storage for the SmallVector elements. This is specialized for the N=0 case
-/// to avoid allocating unnecessary storage.
-template <typename T, unsigned N>
-struct SmallVectorStorage {
- alignas(T) char InlineElts[N * sizeof(T)];
-};
-
-/// We need the storage to be properly aligned even for small-size of 0 so that
-/// the pointer math in \a SmallVectorTemplateCommon::getFirstEl() is
-/// well-defined.
-template <typename T>
-struct alignas(T) SmallVectorStorage<T, 0> {};
-
-/// Forward declaration of SmallVector so that
-/// calculateSmallVectorDefaultInlinedElements can reference
-/// `sizeof(SmallVector<T, 0>)`.
-template <typename T, unsigned N>
-class SmallVector;
-
-/// Helper class for calculating the default number of inline elements for
-/// `SmallVector<T>`.
-///
-/// This should be migrated to a constexpr function when our minimum
-/// compiler support is enough for multi-statement constexpr functions.
-template <typename T>
-struct CalculateSmallVectorDefaultInlinedElements {
- // Parameter controlling the default number of inlined elements
- // for `SmallVector<T>`.
- //
- // The default number of inlined elements ensures that
- // 1. There is at least one inlined element.
- // 2. `sizeof(SmallVector<T>) <= kPreferredSmallVectorSizeof` unless
- // it contradicts 1.
- static constexpr size_t kPreferredSmallVectorSizeof = 64;
-
- // static_assert that sizeof(T) is not "too big".
- //
- // Because our policy guarantees at least one inlined element, it is possible
- // for an arbitrarily large inlined element to allocate an arbitrarily large
- // amount of inline storage. We generally consider it an antipattern for a
- // SmallVector to allocate an excessive amount of inline storage, so we want
- // to call attention to these cases and make sure that users are making an
- // intentional decision if they request a lot of inline storage.
- //
- // We want this assertion to trigger in pathological cases, but otherwise
- // not be too easy to hit. To accomplish that, the cutoff is actually somewhat
- // larger than kPreferredSmallVectorSizeof (otherwise,
- // `SmallVector<SmallVector<T>>` would be one easy way to trip it, and that
- // pattern seems useful in practice).
- //
- // One wrinkle is that this assertion is in theory non-portable, since
- // sizeof(T) is in general platform-dependent. However, we don't expect this
- // to be much of an issue, because most LLVM development happens on 64-bit
- // hosts, and therefore sizeof(T) is expected to *decrease* when compiled for
- // 32-bit hosts, dodging the issue. The reverse situation, where development
- // happens on a 32-bit host and then fails due to sizeof(T) *increasing* on a
- // 64-bit host, is expected to be very rare.
- static_assert(
- sizeof(T) <= 256,
- "You are trying to use a default number of inlined elements for "
- "`SmallVector<T>` but `sizeof(T)` is really big! Please use an "
- "explicit number of inlined elements with `SmallVector<T, N>` to make "
- "sure you really want that much inline storage.");
-
- // Discount the size of the header itself when calculating the maximum inline
- // bytes.
- static constexpr size_t PreferredInlineBytes =
- kPreferredSmallVectorSizeof - sizeof(SmallVector<T, 0>);
- static constexpr size_t NumElementsThatFit = PreferredInlineBytes / sizeof(T);
- static constexpr size_t value =
- NumElementsThatFit == 0 ? 1 : NumElementsThatFit;
-};
-
-/// This is a 'vector' (really, a variable-sized array), optimized
-/// for the case when the array is small. It contains some number of elements
-/// in-place, which allows it to avoid heap allocation when the actual number of
-/// elements is below that threshold. This allows normal "small" cases to be
-/// fast without losing generality for large inputs.
-///
-/// \note
-/// In the absence of a well-motivated choice for the number of inlined
-/// elements \p N, it is recommended to use \c SmallVector<T> (that is,
-/// omitting the \p N). This will choose a default number of inlined elements
-/// reasonable for allocation on the stack (for example, trying to keep \c
-/// sizeof(SmallVector<T>) around 64 bytes).
-///
-/// \warning This does not attempt to be exception safe.
-///
-/// \see https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h
-template <typename T,
- unsigned N = CalculateSmallVectorDefaultInlinedElements<T>::value>
-class SmallVector : public SmallVectorImpl<T>,
- SmallVectorStorage<T, N> {
-public:
- SmallVector()
- : SmallVectorImpl<T>(N) {}
-
- ~SmallVector() {
- // Destroy the constructed elements in the vector.
- this->destroy_range(this->begin(), this->end());
- }
-
- explicit SmallVector(size_t Size, const T& Value = T())
- : SmallVectorImpl<T>(N) {
- this->assign(Size, Value);
- }
-
- template <typename ItTy,
- typename = std::enable_if_t<std::is_convertible<
- typename std::iterator_traits<ItTy>::iterator_category,
- std::input_iterator_tag>::value>>
- SmallVector(ItTy S, ItTy E)
- : SmallVectorImpl<T>(N) {
- this->append(S, E);
- }
-
- template <typename RangeTy>
- explicit SmallVector(const iterator_range<RangeTy>& R)
- : SmallVectorImpl<T>(N) {
- this->append(R.begin(), R.end());
- }
-
- SmallVector(std::initializer_list<T> IL)
- : SmallVectorImpl<T>(N) {
- this->assign(IL);
- }
-
- SmallVector(const SmallVector& RHS)
- : SmallVectorImpl<T>(N) {
- if (!RHS.empty())
- SmallVectorImpl<T>::operator=(RHS);
- }
-
- SmallVector& operator=(const SmallVector& RHS) {
- SmallVectorImpl<T>::operator=(RHS);
- return *this;
- }
-
- SmallVector(SmallVector&& RHS)
- : SmallVectorImpl<T>(N) {
- if (!RHS.empty())
- SmallVectorImpl<T>::operator=(::std::move(RHS));
- }
-
- SmallVector(SmallVectorImpl<T>&& RHS)
- : SmallVectorImpl<T>(N) {
- if (!RHS.empty())
- SmallVectorImpl<T>::operator=(::std::move(RHS));
- }
-
- SmallVector& operator=(SmallVector&& RHS) {
- if (N) {
- SmallVectorImpl<T>::operator=(::std::move(RHS));
- return *this;
- }
- // SmallVectorImpl<T>::operator= does not leverage N==0. Optimize the
- // case.
- if (this == &RHS)
- return *this;
- if (RHS.empty()) {
- this->destroy_range(this->begin(), this->end());
- this->Size = 0;
- } else {
- this->assignRemote(std::move(RHS));
- }
- return *this;
- }
-
- SmallVector& operator=(SmallVectorImpl<T>&& RHS) {
- SmallVectorImpl<T>::operator=(::std::move(RHS));
- return *this;
- }
-
- SmallVector& operator=(std::initializer_list<T> IL) {
- this->assign(IL);
- return *this;
- }
-};
-
-template <typename T, unsigned N>
-inline size_t capacity_in_bytes(const SmallVector<T, N>& X) {
- return X.capacity_in_bytes();
-}
-
-template <typename RangeType>
-using ValueTypeFromRangeType =
- typename std::remove_const<typename std::remove_reference<
- decltype(*std::begin(std::declval<RangeType&>()))>::type>::type;
-
-/// Given a range of type R, iterate the entire range and return a
-/// SmallVector with elements of the vector. This is useful, for example,
-/// when you want to iterate a range and then sort the results.
-template <unsigned Size, typename R>
-SmallVector<ValueTypeFromRangeType<R>, Size> to_vector(R&& Range) {
- return { std::begin(Range), std::end(Range) };
-}
-template <typename R>
-SmallVector<ValueTypeFromRangeType<R>,
- CalculateSmallVectorDefaultInlinedElements<
- ValueTypeFromRangeType<R>>::value>
-to_vector(R&& Range) {
- return { std::begin(Range), std::end(Range) };
-}
-
-namespace std {
-
-/// Implement std::swap in terms of SmallVector swap.
-template <typename T>
-inline void swap(SmallVectorImpl<T>& LHS, SmallVectorImpl<T>& RHS) {
- LHS.swap(RHS);
-}
-
-/// Implement std::swap in terms of SmallVector swap.
-template <typename T, unsigned N>
-inline void swap(SmallVector<T, N>& LHS, SmallVector<T, N>& RHS) {
- LHS.swap(RHS);
-}
-
-} // namespace std
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-#endif
diff --git a/source/10-common/StbImplementations.c b/source/10-common/StbImplementations.c
deleted file mode 100644
index 73bbc2a..0000000
--- a/source/10-common/StbImplementations.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#define STB_RECT_PACK_IMPLEMENTATION
-#include <stb_rect_pack.h>
-
-#define STB_TRUETYPE_IMPLEMENTATION
-#include <stb_truetype.h>
-
-#define STB_IMAGE_IMPLEMENTATION
-#include <stb_image.h>
-
-#define STB_SPRINTF_IMPLEMENTATION
-#include <stb_sprintf.h>
-
-#define STB_C_LEXER_IMPLEMENTATION
-#include <stb_c_lexer.h>
diff --git a/source/10-common/Type2ObjectMap.hpp b/source/10-common/Type2ObjectMap.hpp
deleted file mode 100644
index 0976d2e..0000000
--- a/source/10-common/Type2ObjectMap.hpp
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include "TypeTraits.hpp"
-
-#include <cstddef>
-
-template <typename TValue>
-class Type2ObjectMap {
-public:
- template <typename TType>
- TType& Insert(TType&& value) {
- // TODO
- }
-
- template <typename TType>
- TType& InsertOrAssign(TType& value) {
- // TODO
- }
-
- template <typename TType>
- TType Remove() {
- // TODO
- }
-
- template <typename TType>
- const TValue* Find() const {
- // TODO
- }
-
- template <typename TType>
- TValue* Find() {
- return const_cast<TValue*>(const_cast<const Type2ObjectMap*>(this)->Find<TType>());
- }
-
- size_t size() const {
- // TODO
- }
-};
diff --git a/source/10-common/TypeTraits.hpp b/source/10-common/TypeTraits.hpp
deleted file mode 100644
index 73a56f9..0000000
--- a/source/10-common/TypeTraits.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include <cstddef>
-
-/// This template will be instanciated for each unique type, and the char variable will be ODR-used which gives it an unique address.
-template <typename T>
-struct TypeIdentifier {
- static const char obj = 0;
-};
-
-template <typename T>
-struct DefaultDeleter {
- void operator()(T* ptr) const {
- delete ptr;
- }
-};
-
-template <typename>
-struct RemoveMemberPtrImpl {};
-
-template <typename T, typename U>
-struct RemoveMemberPtrImpl<U T::*> {
- using Type = U;
-};
-
-template <typename T>
-using RemoveMemberPtr = typename RemoveMemberPtrImpl<T>::Type;
diff --git a/source/10-common/Uid.cpp b/source/10-common/Uid.cpp
deleted file mode 100644
index 58dfffd..0000000
--- a/source/10-common/Uid.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "Uid.hpp"
-
-#include "RapidJsonHelper.hpp"
-
-#include <rapidjson/document.h>
-#include <cstring>
-#include <random>
-
-Uid Uid::Create() {
- std::random_device rd;
- std::mt19937_64 gen(rd());
- std::uniform_int_distribution<uint64_t> dist(
- std::numeric_limits<uint64_t>::min(),
- std::numeric_limits<uint64_t>::max());
-
- Uid uid;
- uid.upper = dist(gen);
- uid.lower = dist(gen);
- return uid;
-}
-
-bool Uid::IsNull() const {
- return upper == 0 && lower == 0;
-}
-
-void Uid::ReadString(std::string_view str) {
- sscanf(str.data(), BRUSSEL_Uid_SCAN_STR, &upper, &lower);
-}
-
-std::string Uid::WriteString() {
- char buf[256];
- snprintf(buf, sizeof(buf), BRUSSEL_Uid_FORMAT_STR, upper, lower);
- return std::string(buf);
-}
-
-void Uid::Read(const rapidjson::Value& value) {
- if (value.IsString()) {
- ReadString(rapidjson::AsStringView(value));
- } else if (value.IsArray()) {
- // Compatibility support
- assert(value.Size() == 2);
- auto& upper = value[0];
- assert(upper.IsUint64());
- auto& lower = value[1];
- assert(lower.IsUint64());
-
- this->upper = upper.GetUint64();
- this->lower = lower.GetUint64();
- } else {
- assert(false);
- }
-}
-
-void Uid::WriteInto(rapidjson::Value& value, rapidjson::Document& root) const {
-#if BRUSSEL_Uid_WRITE_USE_ARRAY
- value.Reserve(2, root.GetAllocator());
- value.PushBack((uint64_t)upper, root.GetAllocator());
- value.PushBack((uint64_t)lower, root.GetAllocator());
-#else
- char buf[256];
- int len = snprintf(buf, sizeof(buf), BRUSSEL_Uid_FORMAT_STR, upper, lower);
- value.SetString(buf, len, root.GetAllocator());
-#endif
-}
-
-rapidjson::Value Uid::Write(rapidjson::Document& root) const {
- rapidjson::Value result(rapidjson::kArrayType);
- WriteInto(result, root);
- return result;
-}
diff --git a/source/10-common/Uid.hpp b/source/10-common/Uid.hpp
deleted file mode 100644
index a691911..0000000
--- a/source/10-common/Uid.hpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#pragma once
-
-#include "Utils.hpp"
-
-#include <rapidjson/fwd.h>
-#include <cinttypes>
-#include <functional>
-#include <string>
-#include <string_view>
-
-#define BRUSSEL_Uid_SCAN_STR "%" PRIx64 "-%" PRIx64
-#define BRUSSEL_Uid_SCAN_EXPAND(uid) &((uid).upper), &((uid).upper)
-#define BRUSSEL_Uid_FORMAT_STR "%016" PRIx64 "-%016" PRIx64
-#define BRUSSEL_Uid_FORMAT_EXPAND(uid) (uid).upper, (uid).lower
-
-// Serialize Uid object as an array with two elements, instead of the simple string format
-#define BRUSSEL_Uid_WRITE_USE_ARRAY 0
-
-struct Uid {
- uint64_t upper = 0;
- uint64_t lower = 0;
-
- // Generate a random Uid
- static Uid Create();
-
- bool IsNull() const;
-
- void ReadString(std::string_view str);
- std::string WriteString();
-
- void Read(const rapidjson::Value& value);
- void WriteInto(rapidjson::Value& value, rapidjson::Document& root) const;
- rapidjson::Value Write(rapidjson::Document& root) const;
-
- auto operator<=>(const Uid&) const = default;
-};
-
-template <>
-struct std::hash<Uid> {
- size_t operator()(const Uid& uid) const {
- size_t hash = 0;
- Utils::HashCombine(hash, uid.upper);
- Utils::HashCombine(hash, uid.lower);
- return hash;
- }
-};
diff --git a/source/10-common/Utils.cpp b/source/10-common/Utils.cpp
deleted file mode 100644
index f0ff76d..0000000
--- a/source/10-common/Utils.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-#include "Utils.hpp"
-
-#include "Macros.hpp"
-#include "ScopeGuard.hpp"
-
-#ifdef _WIN32
-# include <Windows.h>
-#endif
-
-namespace fs = std::filesystem;
-
-#ifdef _WIN32
-# define BRUSSEL_MODE_STRING(string) L##string
-#else
-# define BRUSSEL_MODE_STRING(string) string
-#endif
-
-#if _WIN32
-using FopenModeString = const wchar_t*;
-#else
-using FopenModeString = const char*;
-#endif
-
-static FopenModeString GetModeString(Utils::IoMode mode, bool binary) {
- using namespace Utils;
- if (binary) {
- switch (mode) {
- case Read: return BRUSSEL_MODE_STRING("rb");
- case WriteTruncate: return BRUSSEL_MODE_STRING("wb");
- case WriteAppend: return BRUSSEL_MODE_STRING("ab");
- }
- } else {
- switch (mode) {
- case Read: return BRUSSEL_MODE_STRING("r");
- case WriteTruncate: return BRUSSEL_MODE_STRING("w");
- case WriteAppend: return BRUSSEL_MODE_STRING("a");
- }
- }
- return nullptr;
-}
-
-FILE* Utils::OpenCstdioFile(const fs::path& path, IoMode mode, bool binary) {
-#ifdef _WIN32
- // fs::path::c_str() returns `const wchar_t*` under Windows, because NT uses UTF-16 natively
- // NOTE: _wfopen() only affects the type of path parameter, otherwise the file stream created is identical to the one by fopen()
- return _wfopen(path.c_str(), ::GetModeString(mode, binary));
-#else
- return fopen(path.c_str(), ::GetModeString(mode, binary));
-#endif
-}
-
-FILE* Utils::OpenCstdioFile(const char* path, IoMode mode, bool binary) {
-#ifdef _WIN32
- // On Windows, fopen() accepts ANSI codepage encoded path, convert our UTF-8 string to UTF-16 to ensure that no matter what the locale is, the path continues to work
- WCHAR platformPath[MAX_PATH];
- if (MultiByteToWideChar(CP_UTF8, 0, path, -1, platformPath, MAX_PATH) == 0) {
- return nullptr;
- }
- return _wfopen(platformPath, ::GetModeString(mode, binary));
-#else
- return fopen(path, ::GetModeString(mode, binary));
-#endif
-}
-
-std::string Utils::ReadFileAsString(const fs::path& path) {
- auto file = Utils::OpenCstdioFile(path, Utils::Read);
- if (!file) throw std::runtime_error("Failed to open source file.");
- DEFER { fclose(file); };
-
- fseek(file, 0, SEEK_END);
- auto fileSize = ftell(file);
- rewind(file);
-
- std::string result(fileSize, '\0');
- fread(result.data(), fileSize, 1, file);
-
- return result;
-}
-
-bool Utils::ReadCstdioLine(FILE* file, std::string& buffer) {
- buffer.clear();
- while (true) {
- int c = fgetc(file);
- if (c == EOF) {
- if (buffer.empty() || buffer.back() != '\n') {
- buffer += '\n';
- }
- return false;
- } else if (c == '\n') {
- buffer += '\n';
- return true;
- } else {
- buffer += c;
- }
- }
-}
-
-bool Utils::ReadCstdioLine(FILE* file, char* buffer, size_t bufferSize, size_t* outLineLength) {
- // TODO
- assert(false && "Unimplemented");
-}
-
-bool Utils::InRangeInclusive(int n, int lower, int upper) {
- if (lower > upper) {
- std::swap(lower, upper);
- }
- return n >= lower && n <= upper;
-}
-
-bool Utils::LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate) {
- bool verticalLine = p1.x == p2.x && InRangeInclusive(candidate.x, p1.x, p2.x);
- bool horizontalLine = p1.y == p2.y && InRangeInclusive(candidate.y, p1.y, p2.y);
- return verticalLine && horizontalLine;
-}
-
-bool Utils::IsColinear(glm::ivec2 p1, glm::ivec2 p2) {
- return p1.x == p2.x || p1.y == p2.y;
-}
-
-std::string Utils::MakeRandomNumberedName(const char* tag) {
- int n = std::rand();
-#define RNG_NAME_PATTERN "Unnamed %s #%d", tag, n
- // NOTE: does not include null-terminator
- int size = snprintf(nullptr, 0, RNG_NAME_PATTERN);
- std::string result;
- result.resize(size); // std::string::resize handles storage for null-terminator alreaedy
- snprintf(result.data(), size, RNG_NAME_PATTERN);
-#undef RNG_NAME_PATTERN
- return result;
-}
diff --git a/source/10-common/Utils.hpp b/source/10-common/Utils.hpp
deleted file mode 100644
index 668261b..0000000
--- a/source/10-common/Utils.hpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-
-#include <robin_hood.h>
-#include <cstdio>
-#include <cstring>
-#include <filesystem>
-#include <glm/glm.hpp>
-#include <string>
-#include <string_view>
-
-namespace Utils {
-
-enum IoMode {
- Read,
- WriteTruncate,
- WriteAppend,
-};
-
-FILE* OpenCstdioFile(const std::filesystem::path& path, IoMode mode, bool binary = false);
-FILE* OpenCstdioFile(const char* path, IoMode mode, bool binary = false);
-
-/// Retrieve a whole line (marked by `\n` or EOF) into the buffer. If the line ends with EOF, two things happen:
-/// 1. a `\n` character is appended to the line content, emulating as-if the line ended with `\n`.
-/// 2. `false` is returned
-/// Otherwise, `true` is returned.
-///
-/// Empty lines are not skipped at all, including the very last empty line if it exists.
-bool ReadCstdioLine(FILE* file, std::string& buffer);
-/// Same as the other overload, except working with a fixed-size buffer.
-/// NOTE: this also gives the length of the line compared to `std::fgets`.
-/// `std::fgets` requires us to run `std::strlen` on the output again to find the length
-bool ReadCstdioLine(FILE* file, char* buffer, size_t bufferSize, size_t* outLineLength = nullptr);
-
-std::string ReadFileAsString(const std::filesystem::path& path);
-
-constexpr float Abs(float v) noexcept {
- return v < 0.0f ? -v : v;
-}
-
-bool InRangeInclusive(int n, int lower, int upper);
-bool LineContains(glm::ivec2 p1, glm::ivec2 p2, glm::ivec2 candidate);
-
-bool IsColinear(glm::ivec2 p1, glm::ivec2 p2);
-
-template <typename T>
-void HashCombine(std::size_t& seed, const T& v) {
- seed ^= std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
-}
-
-std::string MakeRandomNumberedName(const char* tag);
-
-} // namespace Utils
-
-struct StringHash {
- using is_transparent = void;
-
- std::size_t operator()(const std::string& key) const { return robin_hood::hash_bytes(key.c_str(), key.size()); }
- std::size_t operator()(std::string_view key) const { return robin_hood::hash_bytes(key.data(), key.size()); }
- std::size_t operator()(const char* key) const { return robin_hood::hash_bytes(key, std::strlen(key)); }
-};
-
-struct StringEqual {
- using is_transparent = int;
-
- bool operator()(std::string_view lhs, const std::string& rhs) const {
- const std::string_view view = rhs;
- return lhs == view;
- }
-
- bool operator()(const char* lhs, const std::string& rhs) const {
- return std::strcmp(lhs, rhs.c_str()) == 0;
- }
-
- bool operator()(const std::string& lhs, const std::string& rhs) const {
- return lhs == rhs;
- }
-};
diff --git a/source/10-common/YCombinator.hpp b/source/10-common/YCombinator.hpp
deleted file mode 100644
index 2da06c8..0000000
--- a/source/10-common/YCombinator.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-template <typename Func>
-struct YCombinator {
- // NOTE: implicit constructor allows initializing this
- Func func;
-
- template <typename... Ts>
- decltype(auto) operator()(Ts&&... args) const {
- // NOTE: static_cast<Ts>(args)... is equivalent to std::forward<Ts>(args)...
- // written this way so that we don't have to include <utility>, as well as reducing template instanciations to help compile time
- return func(*this, static_cast<Ts>(args)...);
- }
-};
diff --git a/source/10-editor-common/ImGuiGuizmo.cpp b/source/10-editor-common/ImGuiGuizmo.cpp
deleted file mode 100644
index 3786076..0000000
--- a/source/10-editor-common/ImGuiGuizmo.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 "ImGuiGuizmo.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/10-editor-common/ImGuiGuizmo.hpp b/source/10-editor-common/ImGuiGuizmo.hpp
deleted file mode 100644
index 0560050..0000000
--- a/source/10-editor-common/ImGuiGuizmo.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/10-editor-common/ImGuiNotification.cpp b/source/10-editor-common/ImGuiNotification.cpp
deleted file mode 100644
index 5d375b3..0000000
--- a/source/10-editor-common/ImGuiNotification.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-// Adapted from https://github.com/patrickcjk/imgui-notify
-#include "ImGuiNotification.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/10-editor-common/ImGuiNotification.hpp b/source/10-editor-common/ImGuiNotification.hpp
deleted file mode 100644
index 3af8c2d..0000000
--- a/source/10-editor-common/ImGuiNotification.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/20-codegen-compiler/CodegenConfig.hpp b/source/20-codegen-compiler/CodegenConfig.hpp
deleted file mode 100644
index b9dc56c..0000000
--- a/source/20-codegen-compiler/CodegenConfig.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#ifndef CODEGEN_DEBUG_PRINT
-# define CODEGEN_DEBUG_PRINT 0
-#endif
-
-#if CODEGEN_DEBUG_PRINT
-# define DEBUG_PRINTF(...) printf(__VA_ARGS__)
-#else
-# define DEBUG_PRINTF(...)
-#endif
diff --git a/source/20-codegen-compiler/CodegenDecl.cpp b/source/20-codegen-compiler/CodegenDecl.cpp
deleted file mode 100644
index 11e1bb5..0000000
--- a/source/20-codegen-compiler/CodegenDecl.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "CodegenDecl.hpp"
-
-#include "CodegenUtils.hpp"
-
-#include <fmt/format.h>
-#include <Utils.hpp>
-
-const std::string& DeclStruct::GetMangledName() const {
- if (mangledName.empty()) {
- mangledName = Utils::MakeMangledName(name, container);
- }
- return mangledName;
-}
-
-std::string DeclXGlobalVar::MangleCtorName(std::string_view targetName) {
- return fmt::format("{}_MANGLED_ctor", targetName);
-}
-
-std::string DeclXGlobalVar::MangleDtorName(std::string_view targetName) {
- return fmt::format("{}_MANGLED_dtor", targetName);
-}
-
-const std::string& DeclEnum::GetMangledName() const {
- if (mangledName.empty()) {
- mangledName = Utils::MakeMangledName(name, container);
- }
- return mangledName;
-}
-
-static EnumValuePattern NextPattern(EnumValuePattern val) {
- return (EnumValuePattern)(val + 1);
-}
-
-EnumValuePattern DeclEnum::CalcPattern() const {
- if (elements.empty()) return EVP_Continuous;
-
- auto pattern = EVP_Continuous;
-restart:
- auto lastVal = elements[0].value;
- for (size_t i = 1; i < elements.size(); ++i) {
- auto currVal = elements[i].value;
- switch (pattern) {
- case EVP_Continuous: {
- bool satisfy = lastVal + 1 == currVal;
- if (!satisfy) {
- pattern = NextPattern(pattern);
- goto restart;
- }
- } break;
-
- case EVP_Bits: {
- bool satisfy = (lastVal << 1) == currVal;
- if (!satisfy) {
- pattern = NextPattern(pattern);
- goto restart;
- }
- } break;
-
- // A random pattern can match anything
- case EVP_Random:
- case EVP_COUNT: break;
- }
- lastVal = currVal;
- }
-
- return pattern;
-}
-
-EnumValuePattern DeclEnum::GetPattern() const {
- if (pattern == EVP_COUNT) {
- pattern = CalcPattern();
- }
- return pattern;
-}
diff --git a/source/20-codegen-compiler/CodegenDecl.hpp b/source/20-codegen-compiler/CodegenDecl.hpp
deleted file mode 100644
index f1ac5b1..0000000
--- a/source/20-codegen-compiler/CodegenDecl.hpp
+++ /dev/null
@@ -1,154 +0,0 @@
-#pragma once
-
-#include "CodegenOutput.hpp"
-
-#include <string>
-#include <vector>
-
-// TODO replace std::string name with std::string_view into the token storage?
-
-struct SourceFile {
- std::string filename;
- CodegenOutput preHeaderOutput;
- CodegenOutput postHeaderOutput;
- CodegenOutput postSourceOutput;
- CodegenOutput tuOutput; // "tu" = Translation Unit, produces a separately compiled .cpp file
- bool header = false;
- /// Whether this file is being reprocessed in this invocation of codegen.exe or not.
- bool reprocessing = false;
-};
-
-struct DeclNamespace {
- // NOTE: namespace doesn't have a source file field, because the same namespace can be "reopened" in multipled files
-
- DeclNamespace* container = nullptr;
- std::string name;
- const std::string* fullname = nullptr; // View into storage map key
-};
-
-struct DeclStruct;
-struct DeclMemberVariable {
- DeclStruct* containerStruct = nullptr;
- std::string name;
- std::string type;
- std::string getterName;
- std::string setterName;
- bool isGetterGenerated = false;
- bool isSetterGenerated = false;
-};
-struct DeclMemberFunction {
- DeclStruct* containerStruct = nullptr;
- // TODO
-};
-
-// Structs or classes
-struct DeclStruct {
- SourceFile* sourceFile = nullptr;
- DeclNamespace* container = nullptr;
- std::vector<const DeclStruct*> baseClasses;
- std::vector<DeclMemberVariable> memberVariables;
- std::vector<DeclMemberVariable> generatedVariables;
- std::vector<DeclMemberFunction> memberFunctions;
- std::vector<DeclMemberFunction> generatedFunctions;
- std::string name;
- mutable std::string mangledName;
- const std::string* fullname = nullptr; // View into storage map key
-
- // Scanned generation options
- bool generating : 1 = false;
- bool generatingInheritanceHiearchy : 1 = false;
-
- const std::string& GetName() const { return name; }
- const std::string& GetFullName() const { return *fullname; }
- const std::string& GetMangledName() const;
-};
-
-struct DeclXGlobalVar {
- std::string name;
- bool hasCtor = false;
- bool hasDtor = false;
-
- static std::string MangleCtorName(std::string_view targetName);
- std::string GetMangledCtorName() const { return MangleCtorName(name); }
- static std::string MangleDtorName(std::string_view targetName);
- std::string GetMangledDtorName() const { return MangleDtorName(name); }
-};
-
-enum EnumUnderlyingType {
- EUT_Int8,
- EUT_Int16,
- EUT_Int32,
- EUT_Int64,
- EUT_Uint8,
- EUT_Uint16,
- EUT_Uint32,
- EUT_Uint64,
- EUT_COUNT,
-};
-
-enum EnumValuePattern {
- // The numbers cover n..m with no gaps
- EVP_Continuous,
- // The numbers cover for i in n..m, 1 << i
- // e.g. [0] = 1 << 0,
- // [1] = 1 << 1.
- // [2] = 1 << 2. etc.
- EVP_Bits,
- // The numbesr don't have a particular pattern
- EVP_Random,
- EVP_COUNT,
-};
-
-struct DeclEnumElement {
- std::string name;
- // TODO support int64_t, etc. enum underlying types
- uint64_t value;
-};
-
-struct DeclEnum {
- SourceFile* sourceFile = nullptr;
- DeclNamespace* container = nullptr;
- std::string name;
- mutable std::string mangledName;
- const std::string* fullname = nullptr; // View into storage map key
- std::vector<DeclEnumElement> elements;
- EnumUnderlyingType underlyingType;
- // Start with invalid value, calculate on demand
- mutable EnumValuePattern pattern = EVP_COUNT;
-
- // TODO replace this with a regex?
- std::string generateRemovingPrefix;
- std::string generatingAddingPrefix;
- // NOTE: this flag acts as a gate for every specific generating option, must be enabled for them to work
- bool generating : 1 = false;
- bool generateToString : 1 = false;
- bool generateFromString : 1 = false;
- // NOTE: see GenerateForEnum() for the exact heuristics
- bool generateExcludeUseHeuristics : 1 = false;
-
- const std::string& GetName() const { return name; }
- const std::string& GetFullName() const { return *fullname; }
- const std::string& GetMangledName() const;
-
- std::string_view GetUnderlyingTypeName() const;
-
- EnumValuePattern CalcPattern() const;
- EnumValuePattern GetPattern() const;
-};
-
-struct DeclFunctionArgument {
- std::string type;
- std::string name;
-};
-
-struct DeclFunction {
- SourceFile* sourceFile = nullptr;
- DeclNamespace* container = nullptr;
- // Things like extern, static, etc. that gets written before the function return type
- std::string prefix;
- std::string name;
- const std::string* fullname = nullptr; // View into storage map key
- std::string returnType;
- std::vector<DeclFunctionArgument> arguments;
- std::string body;
-};
diff --git a/source/20-codegen-compiler/CodegenLexer.cpp b/source/20-codegen-compiler/CodegenLexer.cpp
deleted file mode 100644
index ecb2186..0000000
--- a/source/20-codegen-compiler/CodegenLexer.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-#include "CodegenLexer.hpp"
-
-#include <cassert>
-
-int StbLexerToken::Reamalgamate() const {
- if (type == CLEX_ext_single_char) {
- return text[0];
- } else {
- return type;
- }
-}
-
-bool StbTokenIsSingleChar(int lexerToken) {
- return lexerToken >= 0 && lexerToken < 256;
-}
-
-bool StbTokenIsMultiChar(int lexerToken) {
- return !StbTokenIsMultiChar(lexerToken);
-}
-
-std::string CombineTokens(std::span<const StbLexerToken> tokens, std::string_view separator) {
- if (tokens.empty()) {
- return {};
- }
-
- size_t length = 0;
- for (auto& token : tokens) {
- length += token.text.size();
- length += separator.size();
- }
- // Intentionally counting an extra separator: leave space for the last append below
-
- std::string result;
- result.reserve(length);
- for (auto& token : tokens) {
- result += token.text;
- result += separator;
- }
- // Remove the trailing separator
- result.resize(result.size() - separator.size());
-
- return result;
-}
-
-const StbLexerToken& CodegenLexer::Current() const {
- assert(idx < tokens.size());
- return tokens[idx];
-}
-
-void CodegenLexer::InitializeFrom(std::string_view source) {
- this->tokens = {};
- this->idx = 0;
-
- stb_lexer lexer;
- char stringStorage[65536];
- const char* srcBegin = source.data();
- const char* srcEnd = srcBegin + source.length();
- stb_c_lexer_init(&lexer, srcBegin, srcEnd, stringStorage, sizeof(stringStorage));
-
- struct TokenCombiningPattern {
- StbLexerToken result;
- char matchChars[16];
- };
-
- const TokenCombiningPattern kDoubleColon = {
- .result = {
- .text = "::",
- .type = CLEX_ext_double_colon,
- },
- .matchChars = { ':', ':', '\0' },
- };
- const TokenCombiningPattern kDotDotDot = {
- .result = {
- .text = "...",
- .type = CLEX_ext_dot_dot_dot,
- },
- .matchChars = { '.', '.', '.', '\0' },
- };
-
- const TokenCombiningPattern* currentState = nullptr;
- int currentStateCharIdx = 0;
-
- while (true) {
- // See stb_c_lexer.h's comments, here are a few additinos that aren't made clear in the file:
- // - `lexer->token` (noted as "token" below) after calling stb_c_lexer_get_token() contains either:
- // 1. 0 <= token < 256: an ASCII character (more precisely a single char that the lexer ate; technically can be an incomplete code unit)
- // 2. token < 0: an unknown token
- // 3. One of the `CLEX_*` enums: a special, recognized token such as an operator
-
- int stbToken = stb_c_lexer_get_token(&lexer);
- if (stbToken == 0) {
- // EOF
- break;
- }
-
- if (lexer.token == CLEX_parse_error) {
- printf("[ERROR] stb_c_lexer countered a parse error.\n");
- // TODO how to handle?
- continue;
- }
-
- StbLexerToken token;
- if (StbTokenIsSingleChar(lexer.token)) {
- char c = lexer.token;
-
- token.type = CLEX_ext_single_char;
- token.text = std::string(1, c);
-
- if (!currentState) {
-#define TRY_START_MATCH(states) \
- if (states.matchChars[0] == c) { \
- currentState = &states; \
- currentStateCharIdx = 1; \
- }
- TRY_START_MATCH(kDoubleColon);
- TRY_START_MATCH(kDotDotDot);
-#undef TRY_START_MATCH
- } else {
- if (currentState->matchChars[currentStateCharIdx] == c) {
- // Match success
- ++currentStateCharIdx;
-
- // If we matched all of the chars...
- if (currentState->matchChars[currentStateCharIdx] == '\0') {
- // We matched (currentStateCharIdx) tokens though this one is pushed into the vector, leaving (currentStateCharIdx - 1) tokens to be removed
- for (int i = 0, count = currentStateCharIdx - 1; i < count; ++i) {
- tokens.pop_back();
- }
-
- // Set the current token to desired result
- token = currentState->result;
-
- currentState = nullptr;
- currentStateCharIdx = 0;
- }
- } else {
- // Match fail, reset
-
- currentState = nullptr;
- currentStateCharIdx = 0;
- }
- }
- } else {
- token.type = lexer.token;
- // WORKAROUND: use null terminated string, stb_c_lexer doens't set string_len properly when parsing identifiers
- token.text = std::string(lexer.string);
-
- switch (token.type) {
- case CLEX_intlit:
- token.lexerIntNumber = lexer.int_number;
- break;
-
- case CLEX_floatlit:
- token.lexerRealNumber = lexer.real_number;
- break;
- }
- }
- tokens.push_back(std::move(token));
- token = {};
- }
-}
-
-const StbLexerToken* CodegenLexer::TryConsumeToken(int type) {
- auto& token = tokens[idx];
- if (token.type == type) {
- ++idx;
- return &token;
- }
- return nullptr;
-}
-
-const StbLexerToken* CodegenLexer::TryConsumeSingleCharToken(char c) {
- auto& token = tokens[idx];
- if (token.type == CLEX_ext_single_char &&
- token.text[0] == c)
- {
- ++idx;
- return &token;
- }
- return nullptr;
-}
-
-void CodegenLexer::SkipUntilToken(int type) {
- while (idx < tokens.size()) {
- if (Current().type == type) {
- break;
- }
- ++idx;
- }
-}
-
-void CodegenLexer::SkipUntilTokenSingleChar(char c) {
- while (idx < tokens.size()) {
- auto& curr = Current();
- if (curr.type == CLEX_ext_single_char &&
- curr.text[0] == c)
- {
- break;
- }
- ++idx;
- }
-}
diff --git a/source/20-codegen-compiler/CodegenLexer.hpp b/source/20-codegen-compiler/CodegenLexer.hpp
deleted file mode 100644
index ec8c8b7..0000000
--- a/source/20-codegen-compiler/CodegenLexer.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-#include <LookupTable.hpp>
-
-#include <stb_c_lexer.h>
-#include <span>
-#include <string>
-#include <string_view>
-#include <vector>
-
-enum {
- CLEX_ext_single_char = CLEX_first_unused_token,
- CLEX_ext_double_colon,
- CLEX_ext_dot_dot_dot,
- CLEX_ext_COUNT,
-};
-
-struct StbLexerToken {
- std::string text;
-
- union {
- double lexerRealNumber;
- long lexerIntNumber;
- };
-
- // Can either be CLEX_* or CLEX_ext_* values
- int type;
-
- int Reamalgamate() const;
-};
-
-bool StbTokenIsSingleChar(int lexerToken);
-bool StbTokenIsMultiChar(int lexerToken);
-std::string CombineTokens(std::span<const StbLexerToken> tokens, std::string_view separator = {});
-
-struct CodegenLexer {
- std::vector<StbLexerToken> tokens;
- size_t idx = 0;
-
- void InitializeFrom(std::string_view source);
-
- const StbLexerToken& Current() const;
-
- const StbLexerToken* TryConsumeToken(int type);
- const StbLexerToken* TryConsumeSingleCharToken(char c);
-
- void SkipUntilToken(int type);
- void SkipUntilTokenSingleChar(char c);
-};
diff --git a/source/20-codegen-compiler/CodegenModel.cpp b/source/20-codegen-compiler/CodegenModel.cpp
deleted file mode 100644
index 303ad4e..0000000
--- a/source/20-codegen-compiler/CodegenModel.cpp
+++ /dev/null
@@ -1,732 +0,0 @@
-#include "CodegenModel.hpp"
-
-#include "CodegenUtils.hpp"
-#include "SQLiteHelper.hpp"
-
-#include <Macros.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <robin_hood.h>
-#include <cassert>
-#include <cstdint>
-#include <stdexcept>
-#include <string>
-#include <variant>
-
-using namespace std::literals;
-
-// TODO only delete unused records from model instead of regenerating all records every time
-
-struct SomeDecl {
- std::variant<DeclStruct, DeclFunction, DeclEnum> v;
-};
-
-class CodegenRuntimeModel::Private {
- friend class CodegenArchiveModel;
-
-public:
- // We want address stability for everything
- robin_hood::unordered_node_map<std::string, SomeDecl, StringHash, StringEqual> decls;
- robin_hood::unordered_node_map<std::string, DeclNamespace, StringHash, StringEqual> namespaces;
-};
-
-// A number for `PRAGMA user_vesrion`, representing the current database version. Increment when the table format changes.
-#define CURRENT_DATABASE_VERSION 1
-constexpr int64_t kGlobalNamespaceId = 1;
-
-namespace {
-void PrintErrMsgIfPresent(char*& errMsg) {
- if (errMsg) {
- printf("SQLite error: %s\n", errMsg);
- sqlite3_free(errMsg);
- }
-}
-} // namespace
-
-class CodegenArchiveModel::Private {
- friend class CodegenRuntimeModel;
-
-public:
- // NOTE: this must be the first field, because we want it to destruct after all other statement fields
- SQLiteDatabase database;
- /* Core Statements */
- SQLiteStatement beginTransactionStmt;
- SQLiteStatement commitTransactionStmt;
- SQLiteStatement rollbackTransactionStmt;
- SQLiteStatement findFileStmt;
- SQLiteStatement storeFileStmt;
- SQLiteStatement findNamespaceStmt;
- SQLiteStatement getNamespaceStmt;
- SQLiteStatement storeNamespaceStmt;
- /* Component Statements, initalized on demand */
- SQLiteStatement storeStructStmt;
- SQLiteStatement storeStructBaseClassStmt;
- SQLiteStatement storeStructPropertyStmt;
- // TODO store method
- SQLiteStatement storeEnumStmt;
- SQLiteStatement storeEnumElmStmt;
- SQLiteStatement deleteFunctionDeclByFilenameStmt;
- SQLiteStatement deleteStructDeclByFilenameStmt;
- SQLiteStatement deleteEnumDeclByFilenameStmt;
- // TODO
- // SQLiteStatement getRootClassStmt;
-
- void InitializeDatabase() {
- char* errMsg = nullptr;
-
- int result = sqlite3_exec(database, "PRAGMA user_version = " STRINGIFY(CURRENT_DATABASE_VERSION), nullptr, nullptr, &errMsg);
- PrintErrMsgIfPresent(errMsg);
- assert(result == SQLITE_OK);
-
- // TODO unique with overloading, and container structs
- result = sqlite3_exec(database, R"""(
-BEGIN TRANSACTION;
-CREATE TABLE Files(
- -- NOTE: SQLite forbids foreign keys referencing the implicit `rowid` column, we have to create an alias for it
- Id INTEGER PRIMARY KEY,
- FileName TEXT,
- UNIQUE (FileName)
-);
-
-CREATE TABLE Namespaces(
- Id INTEGER PRIMARY KEY,
- ParentNamespaceId INTEGER REFERENCES Namespaces(Id),
- Name TEXT,
- UNIQUE (ParentNamespaceId, Name)
-);
-
-CREATE TABLE DeclFunctions(
- Id INTEGER PRIMARY KEY,
- FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE,
- NamespaceId INTEGER REFERENCES Namespaces(Id),
- Name TEXT
-);
-CREATE TABLE DeclFunctionParameters(
- FunctionId INTEGER REFERENCES DeclFunctions(Id) ON DELETE CASCADE,
- Name TEXT,
- Type TEXT,
- UNIQUE (FunctionId, Name)
-);
-
-CREATE TABLE DeclStructs(
- Id INTEGER PRIMARY KEY,
- FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE,
- NamespaceId INTEGER REFERENCES Namespaces(Id),
- Name TEXT,
- IsMetadataMarked INTEGER
-);
-CREATE TABLE DeclStructBaseClassRelations(
- StructId INTEGER REFERENCES DeclStructs(Id) ON DELETE CASCADE,
- -- NOTE: intentionally not foreign keys, because we want relations to still exist even if the base class is deleted
- -- we do validation after a complete regeneration pass, on reads
- ParentStructNamespaceId INTEGER,
- ParentStructName TEXT,
- UNIQUE (StructId, ParentStructNamespaceId, ParentStructName)
-);
-CREATE TABLE DeclStructProperties(
- StructId INTEGER REFERENCES DeclStructs(Id) ON DELETE CASCADE,
- Name TEXT,
- Type TEXT,
- -- NOTE: getter and setter may or may not be methods; search the DeclStructMethods table if needed
- GetterName TEXT,
- SetterName TEXT,
- IsPlainField INTEGER GENERATED ALWAYS AS (GetterName = '' AND SetterName = '') VIRTUAL,
- IsMetadataMarked INTEGER
-);
-CREATE TABLE DeclStructMethods(
- Id INTEGER PRIMARY KEY,
- StructId INTEGER REFERENCES DeclStructs(Id) ON DELETE CASCADE,
- Name TEXT,
- Type TEXT,
- IsConst INTEGER,
- IsMetadataMarked INTEGER
-);
-CREATE TABLE DeclStructMethodParameters(
- MethodId INTEGER REFERENCES DeclStructMethods(Id) ON DELETE CASCADE,
- Name TEXT,
- Type TEXT,
- UNIQUE (MethodId, Name)
-);
-
-CREATE TABLE DeclEnums(
- Id INTEGER PRIMARY KEY,
-FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE,
- NamespaceId INTEGER REFERENCES Namespaces(Id),
- Name TEXT,
- UnderlyingType TEXT
-);
-CREATE TABLE DeclEnumElements(
- EnumId INTEGER REFERENCES DeclEnums(Id) ON DELETE CASCADE,
- Name TEXT,
- Value INTEGER,
- UNIQUE (EnumId, Name)
-);
-
-CREATE INDEX Index_DeclFunctions_FileId ON DeclFunctions(FileId);
-CREATE INDEX Index_DeclStructs_FileId ON DeclStructs(FileId);
-CREATE INDEX Index_DeclEnums_FileId ON DeclEnums(FileId);
-
-CREATE UNIQUE INDEX Index_DeclFunctions_Identity ON DeclFunctions(NamespaceId, Name);
-
-CREATE UNIQUE INDEX Index_DeclStruct_Identity ON DeclStructs(NamespaceId, Name);
-CREATE UNIQUE INDEX Index_DeclStructProperties_Identity ON DeclStructProperties(StructId, Name);
-CREATE UNIQUE INDEX Index_DeclStructMethods_Identity ON DeclStructMethods(StructId, Name);
-
-CREATE UNIQUE INDEX Index_DeclEnums_Identity ON DeclEnums(NamespaceId, Name);
-
--- Special global namespace that has no parent, and Id should always be 1
-INSERT INTO Namespaces(Id, ParentNamespaceId, Name)
-VALUES (1, NULL, '<global namespace>');
-
-COMMIT TRANSACTION;
-)""",
- nullptr,
- nullptr,
- &errMsg);
- PrintErrMsgIfPresent(errMsg);
- assert(result == SQLITE_OK);
- }
-
- void BeginTransaction() {
- int result = sqlite3_step(beginTransactionStmt);
- assert(result == SQLITE_DONE);
- sqlite3_reset(beginTransactionStmt);
- }
-
- void CommitTransaction() {
- int result = sqlite3_step(commitTransactionStmt);
- assert(result == SQLITE_DONE);
- sqlite3_reset(commitTransactionStmt);
- }
-
- void RollbackTransaction() {
- int result = sqlite3_step(rollbackTransactionStmt);
- assert(result == SQLITE_DONE);
- sqlite3_reset(rollbackTransactionStmt);
- }
-
- /// \return Row ID of the namespace, or 0 if it currently doesn't exist.
- int64_t FindNamespace(const DeclNamespace* ns) {
- if (!ns) {
- return kGlobalNamespaceId;
- }
-
- return FindNamespaceImpl(*ns);
- }
-
- /// \return Row ID of the namespace.
- int64_t FindOrStoreNamespace(const DeclNamespace* ns) {
- if (!ns) {
- return kGlobalNamespaceId;
- }
-
- if (auto rowId = FindNamespaceImpl(*ns); rowId != 0) {
- return rowId;
- }
-
- SQLiteRunningStatement rt(storeNamespaceStmt);
- rt.BindArguments(FindOrStoreNamespace(ns->container), ns->name);
-
- rt.StepAndCheck(SQLITE_ROW);
-
- auto [nsId] = rt.ResultColumns<int64_t>();
- return nsId;
- }
-
- std::string GetNamespaceFullName(int64_t nsId) const {
- return GetNamespaceFullNameImpl(nsId, nullptr, 0);
- }
-
- std::string GetDeclFullName(int64_t nsId, std::string_view declName) const {
- return GetNamespaceFullNameImpl(nsId, declName.data(), declName.size());
- }
-
- /// \return Row ID of the file, or 0 if it currently doesn't exist.
- int64_t FindFile(std::string_view filename) {
- SQLiteRunningStatement rt(findFileStmt);
- rt.BindArguments(filename);
-
- int result = rt.Step();
-
- if (result == SQLITE_ROW) {
- auto [fileId] = rt.ResultColumns<int64_t>();
- return fileId;
- } else {
- return 0;
- }
- }
-
- /// \return Row ID of the file
- int64_t FindOrStoreFile(std::string_view filename) {
- if (auto id = FindFile(filename); id != 0) {
- return id;
- }
-
- SQLiteRunningStatement rt(storeFileStmt);
- rt.BindArguments(filename);
-
- rt.StepAndCheck(SQLITE_ROW);
-
- auto [fileId] = rt.ResultColumns<int64_t>();
- return fileId;
- }
-
- /// \return Row ID of the file, or 0 if not found.
- int64_t FindOrStoreFile(/*nullable*/ const SourceFile* file) {
- if (!file) {
- return 0;
- }
- return FindOrStoreFile(file->filename);
- }
-
-private:
- // TODO maybe merge with Utils::MakeFullName?
- std::string GetNamespaceFullNameImpl(int64_t nsId, const char* append, size_t appendLength) const {
- std::vector<std::string> namespaceNames;
- size_t fullnameLength = 0;
-
- sqlite3_stmt* stmt = getNamespaceStmt;
- int64_t currentNsId = nsId;
- while (true) {
- SQLiteRunningStatement rt(getNamespaceStmt);
- rt.BindArguments(currentNsId);
-
- rt.StepAndCheck(SQLITE_ROW);
-
- auto [id, parentNamespaceId, name] = rt.ResultColumns<int64_t, int64_t, std::string>();
- currentNsId = parentNamespaceId;
- fullnameLength += name.size() + 2;
- namespaceNames.push_back(std::move(name));
-
- if (parentNamespaceId == kGlobalNamespaceId) {
- break;
- }
- }
- if (append) {
- // Already has the '::' at the end
- fullnameLength += appendLength;
- } else {
- fullnameLength -= 2;
- }
-
- std::string fullname;
- fullname.reserve(fullnameLength);
-
- for (auto it = namespaceNames.rbegin(); it != namespaceNames.rend(); ++it) {
- fullname.append(*it);
- if (append || std::next(it) != namespaceNames.rend()) {
- fullname.append("::");
- }
- }
- if (append) {
- fullname += std::string_view(append, appendLength);
- }
-
- return fullname;
- }
-
- int64_t FindNamespaceImpl(const DeclNamespace& ns) {
- int64_t parentNsRowId;
- if (ns.container) {
- parentNsRowId = FindNamespaceImpl(*ns.container);
- if (parentNsRowId == 0) {
- // Parent namespace doesn't exist in database, shortcircuit
- return 0;
- }
- } else {
- parentNsRowId = kGlobalNamespaceId;
- }
-
- return FindNamespaceImpl(ns, parentNsRowId);
- }
-
- int64_t FindNamespaceImpl(const DeclNamespace& ns, int64_t parentNsRowId) {
- sqlite3_stmt* stmt = findNamespaceStmt;
- SQLiteRunningStatement rt(findNamespaceStmt);
- rt.BindArguments(parentNsRowId, ns.name);
-
- int result = rt.Step();
- if (result == SQLITE_ROW) {
- auto [nsId] = rt.ResultColumns<int64_t>();
- return nsId;
- } else {
- return 0;
- }
- }
-};
-
-CodegenRuntimeModel::CodegenRuntimeModel()
- : m{ new Private() } //
-{
-}
-
-CodegenRuntimeModel::~CodegenRuntimeModel() {
- delete m;
-}
-
-#define STORE_DECL_OF_TYPE(DeclType, fullname, decl) \
- auto [iter, success] = m->decls.try_emplace(std::move(fullname), SomeDecl{ .v = std::move(decl) }); \
- auto& key = iter->first; \
- auto& val = iter->second; \
- auto& declRef = std::get<DeclType>(val.v); \
- declRef.fullname = &key; \
- return &declRef
-
-DeclEnum* CodegenRuntimeModel::AddEnum(std::string fullname, DeclEnum decl) {
-#if CODEGEN_DEBUG_PRINT
- printf("Committed enum '%s'\n", decl.name.c_str());
- for (auto& elm : decl.elements) {
- printf(" - element %s = %" PRId64 "\n", elm.name.c_str(), elm.value);
- }
-#endif
-
- STORE_DECL_OF_TYPE(DeclEnum, fullname, decl);
-}
-
-DeclStruct* CodegenRuntimeModel::AddStruct(std::string fullname, DeclStruct decl) {
-#if CODEGEN_DEBUG_PRINT
- printf("Committed struct '%s'\n", decl.name.c_str());
- printf(" Base classes:\n");
- for (auto& base : decl.baseClasses) {
- printf(" - %.*s\n", PRINTF_STRING_VIEW(base->name));
- }
-#endif
-
- STORE_DECL_OF_TYPE(DeclStruct, fullname, decl);
-}
-
-#define FIND_DECL_OF_TYPE(DeclType) \
- auto iter = m->decls.find(name); \
- if (iter != m->decls.end()) { \
- auto& some = iter->second.v; \
- if (auto decl = std::get_if<DeclType>(&some)) { \
- return decl; \
- } \
- } \
- return nullptr
-
-const DeclEnum* CodegenRuntimeModel::FindEnum(std::string_view name) const {
- FIND_DECL_OF_TYPE(DeclEnum);
-}
-
-const DeclStruct* CodegenRuntimeModel::FindStruct(std::string_view name) const {
- FIND_DECL_OF_TYPE(DeclStruct);
-}
-
-DeclNamespace* CodegenRuntimeModel::AddNamespace(DeclNamespace ns) {
- auto path = Utils::MakeFullName(""sv, &ns);
- auto [iter, success] = m->namespaces.try_emplace(std::move(path), std::move(ns));
- auto& nsRef = iter->second;
- if (success) {
- nsRef.fullname = &iter->first;
- }
- return &nsRef;
-}
-
-const DeclNamespace* CodegenRuntimeModel::FindNamespace(std::string_view fullname) const {
- auto iter = m->namespaces.find(fullname);
- if (iter != m->namespaces.end()) {
- return &iter->second;
- } else {
- return nullptr;
- }
-}
-
-DeclNamespace* CodegenRuntimeModel::FindNamespace(std::string_view name) {
- return const_cast<DeclNamespace*>(const_cast<const CodegenRuntimeModel*>(this)->FindNamespace(name));
-}
-
-CodegenArchiveModel::CodegenArchiveModel(std::string_view dbPath)
- : m{ new Private() } //
-{
- std::string zstrPath(dbPath);
- int reuslt = sqlite3_open(zstrPath.c_str(), &m->database);
- if (reuslt != SQLITE_OK) {
- std::string msg;
- msg += "Failed to open SQLite3 database, error message:\n";
- msg += sqlite3_errmsg(m->database);
- throw std::runtime_error(msg);
- }
-
- // NOTE: These pragmas are not persistent, so we need to set them every time
- // As of SQLite3 3.38.5, it defaults to foreign_keys = OFF, so we need this to be on for ON DELETE CASCADE and etc. to work
- sqlite3_exec(m->database, "PRAGMA foreign_keys = ON", nullptr, nullptr, nullptr);
- // This database is used for a buildsystem and can be regenerated at any time. We don't care for the slightest about data integrity, we just want fast updates
- sqlite3_exec(m->database, "PRAGMA synchronous = OFF", nullptr, nullptr, nullptr);
- sqlite3_exec(m->database, "PRAGMA journal_mode = MEMORY", nullptr, nullptr, nullptr);
-
- {
- SQLiteStatement readVersionStmt;
- readVersionStmt.InitializeLazily(m->database, "PRAGMA user_version"sv);
-
- int result = sqlite3_step(readVersionStmt);
- assert(result == SQLITE_ROW);
- int currentDatabaseVersion = sqlite3_column_int(readVersionStmt, 0);
-
- result = sqlite3_step(readVersionStmt);
- assert(result == SQLITE_DONE);
-
- if (currentDatabaseVersion == 0) {
- // Newly created database, initialize it
- m->InitializeDatabase();
- } else if (currentDatabaseVersion == CURRENT_DATABASE_VERSION) {
- // Same version, no need to do anything
- } else {
- INPLACE_FMT(msg, "Incompatbile database versions %d (in file) vs %d (expected).", currentDatabaseVersion, CURRENT_DATABASE_VERSION);
- throw std::runtime_error(msg);
- }
- }
-
- // Initialize core statements
- m->beginTransactionStmt.Initialize(m->database, "BEGIN TRANSACTION");
- m->commitTransactionStmt.Initialize(m->database, "COMMIT TRANSACTION");
- m->rollbackTransactionStmt.Initialize(m->database, "ROLLBACK TRANSACTION");
- m->findFileStmt.Initialize(m->database, "SELECT Id FROM Files WHERE FileName = ?1");
- m->storeFileStmt.Initialize(m->database, "INSERT INTO Files(FileName) VALUES (?1) RETURNING Id");
- m->findNamespaceStmt.Initialize(m->database, "SELECT Id FROM Namespaces WHERE ParentNamespaceId = ?1 AND Name = ?2");
- m->getNamespaceStmt.Initialize(m->database, "SELECT * FROM Namespaces WHERE Id = ?1"sv);
- m->storeNamespaceStmt.Initialize(m->database, "INSERT INTO Namespaces(ParentNamespaceId, Name) VALUES (?1, ?2) RETURNING Id");
-}
-
-CodegenArchiveModel::~CodegenArchiveModel() {
- delete m;
-}
-
-void CodegenArchiveModel::DeleteDeclsRelatedToFile(std::string_view filename) {
- // -Argument- -Description-
- // ?1 The filename to delete
- m->deleteFunctionDeclByFilenameStmt.InitializeLazily(m->database, "DELETE FROM DeclFunctions WHERE FileId = (SELECT Id FROM Files WHERE FileName = ?1)"sv);
- m->deleteStructDeclByFilenameStmt.InitializeLazily(m->database, "DELETE FROM DeclStructs WHERE FileId = (SELECT Id FROM Files WHERE FileName = ?1);"sv);
- m->deleteEnumDeclByFilenameStmt.InitializeLazily(m->database, "DELETE FROM DeclEnums WHERE FileId = (SELECT Id FROM Files WHERE FileName = ?1);"sv);
-
- m->BeginTransaction();
- auto stmtList = {
- m->deleteFunctionDeclByFilenameStmt.stmt,
- m->deleteStructDeclByFilenameStmt.stmt,
- m->deleteEnumDeclByFilenameStmt.stmt,
- };
- for (auto& stmt : stmtList) {
- SQLiteRunningStatement rt(stmt);
- rt.BindArguments(filename);
- rt.StepUntilDone();
- }
- m->CommitTransaction();
-}
-
-void CodegenArchiveModel::Store(const CodegenRuntimeModel& cgModel) {
- auto& cgm = cgModel.GetPimpl();
-
- struct Visiter {
- CodegenArchiveModel* self;
-
- void operator()(const DeclStruct& decl) const {
- self->StoreStruct(decl);
- }
- void operator()(const DeclFunction& decl) const {
- self->StoreFunction(decl);
- }
- void operator()(const DeclEnum& decl) const {
- self->StoreEnum(decl);
- }
- } visiter;
- visiter.self = this;
-
- m->BeginTransaction();
-
- for (auto&& [DISCARD, ns] : cgm.namespaces) {
- // This will insert the namespace if it doesn't exist, or no-op (fetches data) if it already exists
- m->FindOrStoreNamespace(&ns);
- }
- for (auto&& [DISCARD, value] : cgm.decls) {
- std::visit(visiter, value.v);
- }
-
- m->CommitTransaction();
-}
-
-void CodegenArchiveModel::LoadInto(CodegenRuntimeModel& model) const {
- // TODO
-}
-
-CodegenRuntimeModel CodegenArchiveModel::Load() const {
- CodegenRuntimeModel cgModel;
-
- // TODO files
- // TODO namespaces
-
- robin_hood::unordered_map<int64_t, DeclStruct*> structsById;
- robin_hood::unordered_map<int64_t, DeclMemberVariable*> propertiesById;
- robin_hood::unordered_map<int64_t, DeclMemberFunction*> methodsById;
-
- { // Load structs
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructs"sv);
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- auto [id, fileId, nsId, name] = rt.ResultColumns<int64_t, int64_t, int64_t, std::string_view>();
-
- auto decl = cgModel.AddStruct(m->GetDeclFullName(nsId, name), DeclStruct{});
- structsById.try_emplace(id, decl);
- }
- }
- { // Load struct's base classes
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructBaseClassRelations");
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- auto [structId, parentStructNsId, parentStructName] = rt.ResultColumns<int64_t, int64_t, std::string_view>();
-
- auto declThis = structsById.at(structId);
- auto declParent = cgModel.FindStruct(parentStructName); // TODO namespace
- declThis->baseClasses.push_back(declParent);
- }
- }
- { // Load struct properties
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructProperties"sv);
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- // TODO
- }
- }
- { // Load struct methods
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethods"sv);
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- // TODO
- }
- }
- { // Load method params
- SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethodParameters"sv);
- SQLiteRunningStatement rt(stmt);
- while (true) {
- int result = rt.StepAndCheckError();
- if (result == SQLITE_DONE) break;
- assert(result == SQLITE_ROW);
-
- // TODO
- }
- }
-
- return cgModel;
-}
-
-void CodegenArchiveModel::StoreStruct(const DeclStruct& decl) {
- // -Argument- -Description-
- // ?1 Namespace ID
- // ?2 Struct name
- // ?3 File ID containing the struct
- // ?4 Is this struct marked for metadata generation?
- m->storeStructStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclStructs(NamespaceId, Name, FileId, IsMetadataMarked)
-VALUES (?1, ?2, ?3, ?4)
-ON CONFLICT DO UPDATE SET
- FileId = ?3,
- IsMetadataMarked = ?4
-RETURNING Id
-)"""sv);
-
- // -Argument- -Description-
- // ?1 Struct ID
- // ?2 Parent struct's namespace ID
- // ?3 Parent struct's name
- m->storeStructBaseClassStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclStructBaseClassRelations(StructId, ParentStructNamespaceId, ParentStructName)
-VALUES (?1, ?2, ?3)
-)"""sv);
-
- // -Argument- -Description-
- // ?1 Struct ID
- // ?2 Property name
- // ?3 Property type
- // ?4 Getter name (optional)
- // ?5 Setter name (optional)
- // ?6 Is this property marked for metadata generation?
- m->storeStructPropertyStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclStructProperties(StructId, Name, Type, GetterName, SetterName, IsMetadataMarked)
-VALUES (?1, ?2, ?3, ?4, ?5, ?6)
-)"""sv);
-
- SQLiteRunningStatement rt(m->storeStructStmt);
- rt.BindArguments(m->FindOrStoreNamespace(decl.container), decl.name, m->FindOrStoreFile(decl.sourceFile), decl.generating);
- rt.StepAndCheck(SQLITE_ROW);
- auto [structId] = rt.ResultColumns<int64_t>();
-
- for (auto& baseClass : decl.baseClasses) {
- SQLiteRunningStatement rt(m->storeStructBaseClassStmt);
- rt.BindArguments(structId, m->FindOrStoreNamespace(baseClass->container), baseClass->name);
- rt.StepUntilDone();
- }
-
- for (auto& property : decl.memberVariables) {
- SQLiteRunningStatement rt(m->storeStructPropertyStmt);
- rt.BindArguments(
- structId,
- property.name,
- property.type,
- property.getterName,
- property.setterName,
- // Since DeclMemberVariable entries currently only exist if it's marked BRUSSEL_PROPERTY
- true);
- rt.StepUntilDone();
- }
-
- for (auto& method : decl.memberFunctions) {
- // TODO
- }
-}
-
-void CodegenArchiveModel::StoreFunction(const DeclFunction& decl) {
- // TODO
-}
-
-void CodegenArchiveModel::StoreEnum(const DeclEnum& decl) {
- // -Argument- -Description-
- // ?1 Namespace ID
- // ?2 Enum name
- // ?3 Enum underlying type
- // ?4 File ID containing the enum
- m->storeEnumStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclEnums(NamespaceId, Name, UnderlyingType, FileId)
-VALUES (?1, ?2, ?3, ?4)
-ON CONFLICT DO UPDATE SET
- UnderlyingType = ?3,
- FileId = ?4
-RETURNING Id
-)"""sv);
-
- // -Argument- -Description-
- // ?1 Container enum's id
- // ?2 Enum element name
- // ?3 Enum element value
- m->storeEnumElmStmt.InitializeLazily(m->database, R"""(
-INSERT INTO DeclEnumElements(EnumId, Name, Value)
-VALUES (?1, ?2, ?3)
-ON CONFLICT DO UPDATE SET Value=?3
-)"""sv);
-
- SQLiteRunningStatement rt(m->storeEnumStmt);
- rt.BindArguments(m->FindOrStoreNamespace(decl.container), decl.name, decl.GetUnderlyingTypeName(), m->FindOrStoreFile(decl.sourceFile));
- rt.StepAndCheck(SQLITE_ROW);
- auto [enumId] = rt.ResultColumns<int64_t>();
-
- for (auto& elm : decl.elements) {
- SQLiteRunningStatement rt(m->storeEnumElmStmt);
- rt.BindArguments(enumId, elm.name, elm.value);
- rt.StepUntilDone();
- }
-}
diff --git a/source/20-codegen-compiler/CodegenModel.hpp b/source/20-codegen-compiler/CodegenModel.hpp
deleted file mode 100644
index 99c345d..0000000
--- a/source/20-codegen-compiler/CodegenModel.hpp
+++ /dev/null
@@ -1,61 +0,0 @@
-#pragma once
-
-#include "CodegenConfig.hpp"
-#include "CodegenDecl.hpp"
-#include "CodegenModel.hpp"
-#include "CodegenUtils.hpp"
-
-#include <sqlite3.h>
-#include <cinttypes>
-#include <cstdint>
-#include <string>
-#include <string_view>
-
-using namespace std::literals;
-
-class CodegenRuntimeModel {
-private:
- class Private;
- Private* m;
-
-public:
- CodegenRuntimeModel();
- ~CodegenRuntimeModel();
-
- // Implementation detail helper, don't use outside
- Private& GetPimpl() const { return *m; }
-
- DeclEnum* AddEnum(std::string fullname, DeclEnum decl);
- DeclStruct* AddStruct(std::string fullname, DeclStruct decl);
-
- const DeclEnum* FindEnum(std::string_view name) const;
- const DeclStruct* FindStruct(std::string_view name) const;
-
- DeclNamespace* AddNamespace(DeclNamespace ns);
-
- const DeclNamespace* FindNamespace(std::string_view fullname) const;
- DeclNamespace* FindNamespace(std::string_view name);
-};
-
-class CodegenArchiveModel {
-private:
- class Private;
- Private* m;
-
-public:
- CodegenArchiveModel(std::string_view dbPath);
- ~CodegenArchiveModel();
-
- // Implementation detail helper, don't use outside
- Private& GetPimpl() const { return *m; }
-
- void DeleteDeclsRelatedToFile(std::string_view filename);
-
- void Store(const CodegenRuntimeModel& model);
- void LoadInto(CodegenRuntimeModel& model) const;
- CodegenRuntimeModel Load() const;
-
- void StoreStruct(const DeclStruct& decl);
- void StoreFunction(const DeclFunction& decl);
- void StoreEnum(const DeclEnum& decl);
-};
diff --git a/source/20-codegen-compiler/CodegenOutput.cpp b/source/20-codegen-compiler/CodegenOutput.cpp
deleted file mode 100644
index d85feac..0000000
--- a/source/20-codegen-compiler/CodegenOutput.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "CodegenOutput.hpp"
-
-#include "CodegenUtils.hpp"
-
-void CodegenOutput::AddRequestInclude(std::string_view include) {
- if (!mRequestIncludes.contains(include)) {
- mRequestIncludes.insert(std::string(include));
- }
-}
-
-void CodegenOutput::AddOutputThing(CodegenOutputThing thing, int placementLocation) {
- if (placementLocation < 0 || placementLocation >= mOutThings.size()) {
- mOutThings.push_back(std::move(thing));
- } else {
- int maxIndex = (int)mOutThings.size() - 1;
- if (placementLocation > maxIndex) {
- placementLocation = maxIndex;
- }
-
- auto placementIter = mOutThings.begin() + placementLocation;
- mOutThings.insert(placementIter, std::move(thing));
- }
-}
-
-void CodegenOutput::MergeContents(CodegenOutput other) {
- std::move(other.mOutThings.begin(), other.mOutThings.end(), std::back_inserter(this->mOutThings));
-}
-
-void CodegenOutput::Write(FILE* file) const {
- for (auto& include : mRequestIncludes) {
- // TODO how to resolve to the correct include paths?
- WRITE_FMT_LN(file, "#include <%s>", include.c_str());
- }
-
- for (auto& thing : mOutThings) {
- fwrite(thing.text.c_str(), sizeof(char), thing.text.size(), file);
- WRITE_LIT(file, "\n");
- }
-}
diff --git a/source/20-codegen-compiler/CodegenOutput.hpp b/source/20-codegen-compiler/CodegenOutput.hpp
deleted file mode 100644
index df949f5..0000000
--- a/source/20-codegen-compiler/CodegenOutput.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include <Utils.hpp>
-
-#include <robin_hood.h>
-#include <algorithm>
-#include <cstdio>
-#include <cstdlib>
-#include <string>
-#include <vector>
-
-// A generic "thing" (could be anything, comments, string-concated functionsm, etc.) to spit into the output file
-struct CodegenOutputThing {
- std::string text;
-};
-
-class CodegenOutput {
-private:
- robin_hood::unordered_set<std::string, StringHash, StringEqual> mRequestIncludes;
- std::vector<CodegenOutputThing> mOutThings;
-
-public:
- std::string optionOutPrefix;
- // Whether to add prefixes mOutPrefix to all global names or not
- bool optionAutoAddPrefix : 1 = false;
-
-public:
- void AddRequestInclude(std::string_view include);
- void AddOutputThing(CodegenOutputThing thing, int placementLocation = -1);
-
- void MergeContents(CodegenOutput other);
-
- void Write(FILE* file) const;
-};
diff --git a/source/20-codegen-compiler/CodegenUtils.cpp b/source/20-codegen-compiler/CodegenUtils.cpp
deleted file mode 100644
index 5bc5d79..0000000
--- a/source/20-codegen-compiler/CodegenUtils.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-#include "CodegenUtils.hpp"
-
-#include <Macros.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <cstdio>
-#include <cstdlib>
-
-using namespace std::literals;
-
-bool Utils::WriteOutputFile(const CodegenOutput& output, const char* path) {
- auto outputFile = Utils::OpenCstdioFile(path, Utils::WriteTruncate);
- if (!outputFile) {
- printf("[ERROR] unable to open output file %s\n", path);
- return false;
- }
- DEFER {
- fclose(outputFile);
- };
-
- DEBUG_PRINTF("Writing output %s\n", path);
- output.Write(outputFile);
-
- return true;
-}
-
-std::string Utils::JoinNames(DeclNamespace* ns, std::string_view prefix, std::string_view suffix, std::string_view delimiter) {
- size_t length = 0;
- if (!prefix.empty()) {
- length += prefix.length() + delimiter.length();
- }
- if (!suffix.empty()) {
- length += suffix.length() + delimiter.length();
- }
- size_t nsCount = 0;
- {
- DeclNamespace* curr = ns;
- while (curr) {
- length += curr->name.length() + delimiter.length();
-
- curr = curr->container;
- ++nsCount;
- }
- }
- length -= delimiter.length();
-
- std::string joined;
- joined.reserve(length);
-
- if (!prefix.empty()) {
- joined += prefix;
- joined += delimiter;
- }
- {
- DeclNamespace* curr = ns;
- size_t i = 0;
- while (curr) {
- joined += curr->name;
- if (!suffix.empty() || i != (nsCount - 1)) {
- joined += delimiter;
- }
-
- curr = curr->container;
- ++i;
- }
- }
- if (!suffix.empty()) {
- joined += suffix;
- }
-
- return joined;
-}
-
-std::string Utils::MakeFullName(std::string_view name, DeclNamespace* ns) {
- return JoinNames(ns, ""sv, name, "::"sv);
-}
-
-std::string Utils::MakeMangledName(std::string_view name, DeclNamespace* ns) {
- return JoinNames(ns, ""sv, name, "_"sv);
-}
-
-// NOTE: assuming we are only dealing with ASCII characters
-static bool IsLowerCase(char c) {
- return c >= 'a' && c <= 'z';
-}
-static bool IsUpperCase(char c) {
- return c >= 'A' && c <= 'Z';
-}
-static bool IsAlphabetic(char c) {
- return IsLowerCase(c) || IsUpperCase(c);
-}
-static char MakeUpperCase(char c) {
- if (IsAlphabetic(c)) {
- return IsUpperCase(c)
- ? c
- : ('A' + (c - 'a'));
- }
- return c;
-}
-
-std::vector<std::string_view> Utils::SplitIdentifier(std::string_view name) {
- // TODO handle SCREAMING_CASE
-
- size_t chunkStart = 0;
- size_t chunkEnd = 0;
- std::vector<std::string_view> result;
- auto PushChunk = [&]() { result.push_back(std::string_view(name.begin() + chunkStart, name.begin() + chunkEnd)); };
- while (chunkEnd < name.size()) {
- char c = name[chunkEnd];
- if (IsUpperCase(c)) {
- // Start of next chunk, using camelCase or PascalCase
- PushChunk();
- chunkStart = chunkEnd;
- chunkEnd = chunkStart + 1;
- continue;
- } else if (c == '_') {
- // End of this chunk, using snake_case
- PushChunk();
- chunkStart = chunkEnd + 1;
- chunkEnd = chunkStart + 1;
- continue;
- } else if (c == '-') {
- // End of this chunk, using kebab-case
- PushChunk();
- chunkStart = chunkEnd + 1;
- chunkEnd = chunkStart + 1;
- continue;
- }
- ++chunkEnd;
- }
-
- if ((chunkEnd - chunkStart) >= 1) {
- PushChunk();
- }
-
- return result;
-}
-
-std::string Utils::MakePascalCase(std::string_view name) {
- std::string result;
- for (auto part : SplitIdentifier(name)) {
- result += MakeUpperCase(part[0]);
- result += part.substr(1);
- }
- return result;
-}
-
-void Utils::ProduceGeneratedHeader(const char* headerFilename, CodegenOutput& header, const char* sourceFilename, CodegenOutput& source) {
- CodegenOutputThing headerOut;
- headerOut.text += &R"""(
-// This file is generated. Any changes will be overidden when building.
-#include <MetadataBase.hpp>
-#include <cstddef>
-#include <cstdint>
-)"""[1];
-
- CodegenOutputThing sourceOut;
- APPEND_LIT_LN(sourceOut.text, "// This file is generated. Any changes will be overidden when building.");
- APPEND_FMT_LN(sourceOut.text, "#include \"%s\"", headerFilename);
- sourceOut.text += &R"""(
-#include <frozen/string.h>
-#include <frozen/unordered_map.h>
-#include <MetadataDetails.hpp>
-using namespace std::literals;
-using namespace Metadata;
-)"""[1];
-
- header.AddOutputThing(std::move(headerOut), 0);
- source.AddOutputThing(std::move(sourceOut), 0);
-}
diff --git a/source/20-codegen-compiler/CodegenUtils.hpp b/source/20-codegen-compiler/CodegenUtils.hpp
deleted file mode 100644
index 2d5b684..0000000
--- a/source/20-codegen-compiler/CodegenUtils.hpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#include "CodegenConfig.hpp"
-#include "CodegenDecl.hpp"
-#include "CodegenOutput.hpp"
-
-#include <algorithm>
-#include <string>
-#include <string_view>
-
-// I give up, hopefully nothing overflows this buffer
-// TODO handle buffer sizing properly
-
-#define INPLACE_FMT(varName, format, ...) \
- char varName[2048]; \
- snprintf(varName, sizeof(varName), format, __VA_ARGS__);
-
-#define APPEND_LIT(out, str) \
- out += str
-
-#define APPEND_FMT(out, format, ...) \
- { \
- char buffer[65536]; \
- snprintf(buffer, sizeof(buffer), format, __VA_ARGS__); \
- out += buffer; \
- }
-
-#define WRITE_LIT(file, str) \
- fwrite(str, sizeof(char), sizeof(str) - 1, file)
-
-// NOTE: snprintf() returns the size written (given an infinite buffer) not including \0
-#define WRITE_FMT(file, format, ...) \
- { \
- char buffer[65536]; \
- int size = snprintf(buffer, sizeof(buffer), format, __VA_ARGS__); \
- fwrite(buffer, sizeof(char), std::min<int>(size, sizeof(buffer)), file); \
- }
-
-#define APPEND_LIT_LN(out, str) APPEND_LIT(out, (str "\n"))
-#define APPEND_FMT_LN(out, format, ...) APPEND_FMT(out, (format "\n"), __VA_ARGS__)
-#define WRITE_LIT_LN(out, str) WRITE_LIT(out, (str "\n"))
-#define WRITE_FMT_LN(out, format, ...) WRITE_FMT(out, (format "\n"), __VA_ARGS__)
-
-namespace Utils {
-
-bool WriteOutputFile(const CodegenOutput& output, const char* path);
-
-std::string JoinNames(DeclNamespace* ns, std::string_view prefix, std::string_view suffix, std::string_view delimiter);
-std::string MakeFullName(std::string_view name, DeclNamespace* ns = nullptr);
-std::string MakeMangledName(std::string_view name, DeclNamespace* ns = nullptr);
-
-std::vector<std::string_view> SplitIdentifier(std::string_view name);
-std::string MakePascalCase(std::string_view name);
-
-void ProduceGeneratedHeader(const char* headerFilename, CodegenOutput& header, const char* sourceFilename, CodegenOutput& source);
-
-} // namespace Utils
diff --git a/source/20-codegen-compiler/SQLiteHelper.hpp b/source/20-codegen-compiler/SQLiteHelper.hpp
deleted file mode 100644
index c24e476..0000000
--- a/source/20-codegen-compiler/SQLiteHelper.hpp
+++ /dev/null
@@ -1,220 +0,0 @@
-#pragma once
-
-#include <fmt/format.h>
-#include <sqlite3.h>
-#include <cassert>
-#include <chrono>
-#include <cstddef>
-#include <cstdint>
-#include <sstream>
-#include <stdexcept>
-#include <string_view>
-#include <tuple>
-#include <type_traits>
-
-struct SQLiteDatabase {
- sqlite3* database = nullptr;
-
- ~SQLiteDatabase() {
- // NOTE: calling with NULL is a harmless no-op
- int result = sqlite3_close(database);
- assert(result == SQLITE_OK);
- }
-
- operator sqlite3*() const { return database; }
- sqlite3** operator&() { return &database; }
-};
-
-struct SQLiteStatement {
- sqlite3_stmt* stmt = nullptr;
-
- SQLiteStatement(const SQLiteStatement&) = delete;
- SQLiteStatement& operator=(const SQLiteStatement&) = delete;
-
- SQLiteStatement() = default;
-
- SQLiteStatement(sqlite3* database, std::string_view sql) {
- Initialize(database, sql);
- }
-
- ~SQLiteStatement() {
- // NOTE: calling with NULL is a harmless no-op
- // NOTE: we don't care about the error code, because they are returned if the statement has errored in the most recent execution
- // but deleting it will succeeed anyways
- sqlite3_finalize(stmt);
- }
-
- operator sqlite3_stmt*() const { return stmt; }
- sqlite3_stmt** operator&() { return &stmt; }
-
- void Initialize(sqlite3* database, std::string_view sql) {
- int result = sqlite3_prepare_v2(database, sql.data(), sql.size(), &stmt, nullptr);
- if (result != SQLITE_OK) {
- auto msg = fmt::format(
- "Failed to prepare SQLite3 statement, error message: {}",
- sqlite3_errmsg(sqlite3_db_handle(stmt)));
- throw std::runtime_error(msg);
- }
- }
-
- bool InitializeLazily(sqlite3* database, std::string_view sql) {
- if (!stmt) {
- Initialize(database, sql);
- return true;
- }
- return false;
- }
-};
-
-struct SQLiteRunningStatement {
- sqlite3_stmt* stmt;
-
- SQLiteRunningStatement(sqlite3_stmt* stmt)
- : stmt{ stmt } {
- }
-
- SQLiteRunningStatement(const SQLiteStatement& stmt)
- : stmt{ stmt.stmt } {
- }
-
- ~SQLiteRunningStatement() {
- sqlite3_clear_bindings(stmt);
- sqlite3_reset(stmt);
- }
-
- void BindArgument(int index, int32_t value) {
- sqlite3_bind_int(stmt, index, (int)value);
- }
-
- void BindArgument(int index, uint32_t value) {
- sqlite3_bind_int(stmt, index, (int)value);
- }
-
- void BindArgument(int index, int64_t value) {
- sqlite3_bind_int64(stmt, index, value);
- }
-
- void BindArgument(int index, uint64_t value) {
- sqlite3_bind_int64(stmt, index, (int64_t)value);
- }
-
- void BindArgument(int index, const char* value) {
- sqlite3_bind_text(stmt, index, value, -1, nullptr);
- }
-
- void BindArgument(int index, std::string_view value) {
- sqlite3_bind_text(stmt, index, value.data(), value.size(), nullptr);
- }
-
- void BindArgument(int index, std::nullptr_t) {
- // Noop
- }
-
- template <typename... Ts>
- void BindArguments(Ts&&... args) {
- // NOTE: SQLite3 argument index starts at 1
- size_t idx = 1;
- auto HandleEachArgument = [this, &idx]<typename T>(T&& arg) {
- BindArgument(idx, std::forward<T>(arg));
- ++idx;
- };
- (HandleEachArgument(std::forward<Ts>(args)), ...);
- }
-
- int Step() {
- return sqlite3_step(stmt);
- }
-
- void StepAndCheck(int forErr) {
- int err = sqlite3_step(stmt);
- assert(err == forErr);
- }
-
- int StepAndCheckError() {
- int err = sqlite3_step(stmt);
- if (err != SQLITE_DONE || err != SQLITE_ROW) {
- auto msg = fmt::format(
- "Error {} executing SQLite3 statement, error message: {}",
- sqlite3_errstr(err),
- sqlite3_errmsg(sqlite3_db_handle(stmt)));
- throw std::runtime_error(msg);
- }
- return err;
- }
-
- void StepUntilDone() {
- while (true) {
- int err = sqlite3_step(stmt);
- // SQLITE_OK is never returned for sqlite3_step() //TODO fact check this
- if (err == SQLITE_DONE) {
- break;
- }
- if (err == SQLITE_ROW) {
- continue;
- }
-
- auto msg = fmt::format(
- "Error {} executing SQLite3 statement, error message: {}",
- sqlite3_errstr(err),
- sqlite3_errmsg(sqlite3_db_handle(stmt)));
- throw std::runtime_error(msg);
- }
- }
-
- using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
- using TpFromUnixTimestamp = std::pair<TimePoint, int64_t>;
- using TpFromDateTime = std::pair<TimePoint, const char*>;
-
- // TODO replace with overloads?
- template <typename T>
- auto ResultColumn(int column) const {
- if constexpr (std::is_enum_v<T>) {
- auto value = sqlite3_column_int64(stmt, column);
- return static_cast<T>(value);
- } else if constexpr (std::is_same_v<T, int> || std::is_same_v<T, bool>) {
- return (T)sqlite3_column_int(stmt, column);
- } else if constexpr (std::is_same_v<T, int64_t>) {
- return (T)sqlite3_column_int64(stmt, column);
- } else if constexpr (std::is_same_v<T, const char*>) {
- return (const char*)sqlite3_column_text(stmt, column);
- } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) {
- // SQLite3 uses `unsigned char` to represent UTF-8 code units, on all platforms we care about this is the same as plain `char`
- auto cstr = (const char*)sqlite3_column_text(stmt, column);
- // For std::string_view, this finds size based on null terminator and stores reference to pointer
- // For std::string, this also allocates buffer and copies `cstr` content
- return T(cstr);
- } else if constexpr (std::is_same_v<T, TpFromUnixTimestamp>) {
- auto unixTimestamp = sqlite3_column_int64(stmt, column);
- auto chrono = std::chrono::seconds(unixTimestamp);
- return TimePoint(chrono);
- } else if constexpr (std::is_same_v<T, TpFromDateTime>) {
- // TODO wait for libstdc++ and libc++ implement c++20 std::chrono addition
-#ifdef _MSC_VER
- auto datetime = (const char*)sqlite3_column_text(stmt, column);
- if (datetime) {
- std::stringstream ss(datetime);
- TimePoint timepoint;
- ss >> std::chrono::parse("%F %T", timepoint);
- return timepoint;
- } else {
- return TimePoint();
- }
-#else
- static_assert(false && sizeof(T), "Unimplemented");
-#endif
- } else {
- static_assert(false && sizeof(T), "Unknown type");
- }
- }
-
- template <typename... Ts>
- auto ResultColumns() {
- // NOTE: SQLite3 column index starts at 0
- // NOTE: ((size_t)-1) + 1 == 0
- size_t idx = -1;
- // NOTE: std::make_tuple() -- variadic template function
- // std::tuple() -- CTAD constructor
- // Both of these cause make the comma operator unsequenced, not viable here
- return std::tuple{ (++idx, ResultColumn<Ts>(idx))... };
- }
-};
diff --git a/source/20-codegen-compiler/main.cpp b/source/20-codegen-compiler/main.cpp
deleted file mode 100644
index a2e50f5..0000000
--- a/source/20-codegen-compiler/main.cpp
+++ /dev/null
@@ -1,1443 +0,0 @@
-#include "CodegenConfig.hpp"
-#include "CodegenDecl.hpp"
-#include "CodegenLexer.hpp"
-#include "CodegenModel.hpp"
-#include "CodegenOutput.hpp"
-#include "CodegenUtils.hpp"
-
-#include <Enum.hpp>
-#include <LookupTable.hpp>
-#include <Macros.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <robin_hood.h>
-#include <stb_c_lexer.h>
-#include <cinttypes>
-#include <cstdlib>
-#include <filesystem>
-#include <memory>
-#include <optional>
-#include <span>
-#include <string>
-#include <string_view>
-
-using namespace std::literals;
-namespace fs = std::filesystem;
-
-// TODO support codegen target in .cpp files
-// TOOD maybe switch to libclang, maintaining this parser is just too painful
-
-struct AppState {
- CodegenRuntimeModel* runtimeModel;
- CodegenArchiveModel* archiveModel;
- // NOTE: decl objects reference the SourceFile objects by pointer
- robin_hood::unordered_node_map<std::string, SourceFile, StringHash, StringEqual> sourceFiles;
- std::vector<DeclEnum*> enumsToRevisit;
- std::vector<DeclStruct*> structsToRevisit;
- std::string_view outputDir;
- std::string_view databaseFilePath;
-
- SourceFile& GetOrCreateSourceFile(std::string_view filename) {
- auto iter = sourceFiles.find(filename);
- if (iter != sourceFiles.end()) {
- return iter->second;
- } else {
- auto [iter, success] = sourceFiles.try_emplace(std::string(filename), SourceFile{});
- // NOTE: "persistent" means pointer stable below
- auto& persistentFilename = iter->first;
- auto& persistentSourceFile = iter->second;
- persistentSourceFile.filename = persistentFilename;
- return persistentSourceFile;
- }
- }
-};
-
-FSTR_LUT_DECL(ClexNames, CLEX_eof, CLEX_ext_COUNT) {
- FSTR_LUT_MAP_FOR(ClexNames);
- FSTR_LUT_MAP_ENUM(CLEX_intlit);
- FSTR_LUT_MAP_ENUM(CLEX_floatlit);
- FSTR_LUT_MAP_ENUM(CLEX_id);
- FSTR_LUT_MAP_ENUM(CLEX_dqstring);
- FSTR_LUT_MAP_ENUM(CLEX_sqstring);
- FSTR_LUT_MAP_ENUM(CLEX_charlit);
- FSTR_LUT_MAP_ENUM(CLEX_eq);
- FSTR_LUT_MAP_ENUM(CLEX_noteq);
- FSTR_LUT_MAP_ENUM(CLEX_lesseq);
- FSTR_LUT_MAP_ENUM(CLEX_greatereq);
- FSTR_LUT_MAP_ENUM(CLEX_andand);
- FSTR_LUT_MAP_ENUM(CLEX_oror);
- FSTR_LUT_MAP_ENUM(CLEX_shl);
- FSTR_LUT_MAP_ENUM(CLEX_shr);
- FSTR_LUT_MAP_ENUM(CLEX_plusplus);
- FSTR_LUT_MAP_ENUM(CLEX_minusminus);
- FSTR_LUT_MAP_ENUM(CLEX_pluseq);
- FSTR_LUT_MAP_ENUM(CLEX_minuseq);
- FSTR_LUT_MAP_ENUM(CLEX_muleq);
- FSTR_LUT_MAP_ENUM(CLEX_diveq);
- FSTR_LUT_MAP_ENUM(CLEX_modeq);
- FSTR_LUT_MAP_ENUM(CLEX_andeq);
- FSTR_LUT_MAP_ENUM(CLEX_oreq);
- FSTR_LUT_MAP_ENUM(CLEX_xoreq);
- FSTR_LUT_MAP_ENUM(CLEX_arrow);
- FSTR_LUT_MAP_ENUM(CLEX_eqarrow);
- FSTR_LUT_MAP_ENUM(CLEX_shleq);
- FSTR_LUT_MAP_ENUM(CLEX_shreq);
- FSTR_LUT_MAP_ENUM(CLEX_ext_single_char);
- FSTR_LUT_MAP_ENUM(CLEX_ext_double_colon);
- FSTR_LUT_MAP_ENUM(CLEX_ext_dot_dot_dot);
-}
-
-FSTR_LUT_DECL(EnumUnderlyingType, 0, EUT_COUNT) {
- FSTR_LUT_MAP_FOR(EnumUnderlyingType);
- FSTR_LUT_MAP(EUT_Int8, "int8_t");
- FSTR_LUT_MAP(EUT_Int16, "int16_t");
- FSTR_LUT_MAP(EUT_Int32, "int32_t");
- FSTR_LUT_MAP(EUT_Int64, "int64_t");
- FSTR_LUT_MAP(EUT_Uint8, "uint8_t");
- FSTR_LUT_MAP(EUT_Uint16, "uint16_t");
- FSTR_LUT_MAP(EUT_Uint32, "uint32_t");
- FSTR_LUT_MAP(EUT_Uint64, "uint64_t");
-}
-
-RSTR_LUT_DECL(EnumUnderlyingType, 0, EUT_COUNT) {
- RSTR_LUT_MAP_FOR(EnumUnderlyingType);
-
- // Platform-dependent types
- RSTR_LUT_MAP(EUT_Int16, "short");
- RSTR_LUT_MAP(EUT_Int16, "short int");
- RSTR_LUT_MAP(EUT_Uint16, "unsigned short");
- RSTR_LUT_MAP(EUT_Uint16, "unsigned short int");
- RSTR_LUT_MAP(EUT_Int32, "int");
- RSTR_LUT_MAP(EUT_Uint32, "unsigned");
- RSTR_LUT_MAP(EUT_Uint32, "unsigned int");
-#ifdef _WIN32
- RSTR_LUT_MAP(EUT_Int32, "long");
- RSTR_LUT_MAP(EUT_Int32, "long int");
- RSTR_LUT_MAP(EUT_Uint32, "unsigned long");
- RSTR_LUT_MAP(EUT_Uint32, "unsigned long int");
-#else
- RSTR_LUT_MAP(EUT_Int64, "long");
- RSTR_LUT_MAP(EUT_Int64, "long int");
- RSTR_LUT_MAP(EUT_Uint64, "unsigned long");
- RSTR_LUT_MAP(EUT_Uint64, "unsigned long int");
-#endif
- RSTR_LUT_MAP(EUT_Int64, "long long");
- RSTR_LUT_MAP(EUT_Int64, "long long int");
- RSTR_LUT_MAP(EUT_Uint64, "unsigned long long");
- RSTR_LUT_MAP(EUT_Uint64, "unsigned long long int");
-
- // Sized types
- RSTR_LUT_MAP(EUT_Int8, "int8_t");
- RSTR_LUT_MAP(EUT_Int16, "int16_t");
- RSTR_LUT_MAP(EUT_Int32, "int32_t");
- RSTR_LUT_MAP(EUT_Int64, "int64_t");
- RSTR_LUT_MAP(EUT_Uint8, "uint8_t");
- RSTR_LUT_MAP(EUT_Uint16, "uint16_t");
- RSTR_LUT_MAP(EUT_Uint32, "uint32_t");
- RSTR_LUT_MAP(EUT_Uint64, "uint64_t");
-}
-
-FSTR_LUT_DECL(EnumValuePattern, 0, EVP_COUNT) {
- FSTR_LUT_MAP_FOR(EnumValuePattern);
- FSTR_LUT_MAP_ENUM(EVP_Continuous);
- FSTR_LUT_MAP_ENUM(EVP_Bits);
- FSTR_LUT_MAP_ENUM(EVP_Random);
-}
-
-enum CppKeyword {
- CKw_Namespace,
- CKw_Struct,
- CKw_Class,
- CKw_Enum,
- CKw_Public,
- CKw_Protected,
- CKw_Private,
- CKw_Virtual,
- CKw_Using,
- CKw_Template,
- CKw_COUNT,
-};
-
-RSTR_LUT_DECL(CppKeyword, 0, CKw_COUNT) {
- RSTR_LUT_MAP_FOR(CppKeyword);
- RSTR_LUT_MAP(CKw_Namespace, "namespace");
- RSTR_LUT_MAP(CKw_Struct, "struct");
- RSTR_LUT_MAP(CKw_Class, "class");
- RSTR_LUT_MAP(CKw_Enum, "enum");
- RSTR_LUT_MAP(CKw_Public, "public");
- RSTR_LUT_MAP(CKw_Protected, "protected");
- RSTR_LUT_MAP(CKw_Private, "private");
- RSTR_LUT_MAP(CKw_Virtual, "virtual");
- RSTR_LUT_MAP(CKw_Using, "using");
- RSTR_LUT_MAP(CKw_Template, "template");
-}
-
-enum CodegenDirective {
- CD_Class,
- CD_ClassProperty,
- CD_ClassMethod,
- CD_Enum,
- CD_XGlobalVar,
- CD_XGlobalVarCtor,
- CD_XGlobalVarDtor,
- CD_COUNT,
-};
-
-RSTR_LUT_DECL(CodegenDirective, 0, CD_COUNT) {
- RSTR_LUT_MAP_FOR(CodegenDirective);
- RSTR_LUT_MAP(CD_Class, "BRUSSEL_CLASS");
- RSTR_LUT_MAP(CD_ClassProperty, "BRUSSEL_PROPERTY");
- RSTR_LUT_MAP(CD_ClassMethod, "BRUSSEL_METHOD");
- RSTR_LUT_MAP(CD_Enum, "BRUSSEL_ENUM");
- RSTR_LUT_MAP(CD_XGlobalVar, "BRUSSEL_GLOBAL_DECL");
- RSTR_LUT_MAP(CD_XGlobalVarCtor, "BRUSSEL_GLOBAL_CTOR");
- RSTR_LUT_MAP(CD_XGlobalVarDtor, "BRUSSEL_GLOBAL_DTOR");
-}
-
-std::vector<std::vector<const StbLexerToken*>>
-TryConsumeDirectiveArgumentList(CodegenLexer& lexer) {
- std::vector<std::vector<const StbLexerToken*>> result;
- decltype(result)::value_type currentArg;
-
- size_t i = lexer.idx;
- int parenDepth = 0;
- for (; i < lexer.tokens.size(); ++i) {
- auto& token = lexer.tokens[i];
- if (token.text[0] == '(') {
- if (parenDepth > 0) {
- currentArg.push_back(&token);
- }
- ++parenDepth;
- } else if (token.text[0] == ')') {
- --parenDepth;
- if (parenDepth == 0) {
- // End of argument list
- ++i; // Consume the ')' token
- break;
- }
- } else if (parenDepth > 0) {
- // Parse these only if we are inside the argument list
- if (token.text[0] == ',') {
- result.push_back(std::move(currentArg));
- currentArg = {};
- } else {
- currentArg.push_back(&token);
- }
- }
- }
-
- if (!currentArg.empty()) {
- result.push_back(std::move(currentArg));
- }
-
- lexer.idx = i;
- return result;
-}
-
-bool TryConsumeKeyword(CodegenLexer& lexer, CppKeyword keyword) {
- auto& token = lexer.Current();
- if (token.type == CLEX_id) {
- auto iter = RSTR_LUT(CppKeyword).find(token.text);
- if (iter != RSTR_LUT(CppKeyword).end()) {
- ++lexer.idx;
- return true;
- }
- }
- return false;
-}
-
-bool TryConsumeAnyKeyword(CodegenLexer& lexer) {
- auto& token = lexer.Current();
- if (token.type == CLEX_id &&
- RSTR_LUT(CppKeyword).contains(token.text))
- {
- ++lexer.idx;
- return true;
- }
- return false;
-}
-
-std::optional<DeclMemberVariable>
-TryConsumeMemberVariable(CodegenLexer& lexer) {
- // The identifier/name will always be one single token, right before the 1st '=' (if has initializer) or ';' (no initializer)
- // NOTE: we assume there is no (a == b) stuff in the templates
-
- auto& tokens = lexer.tokens;
- auto& idx = lexer.idx;
-
- size_t idenTokIdx;
- size_t typeStart = idx;
- size_t typeEnd;
- for (; idx < tokens.size(); ++idx) {
- auto& token = tokens[idx];
- if (token.type == CLEX_ext_single_char) {
- if (token.text[0] == '=') {
- typeEnd = idx - 1;
- idenTokIdx = idx - 1;
- lexer.SkipUntilTokenSingleChar(';');
- goto found;
- } else if (token.text[0] == ';') {
- typeEnd = idx - 1;
- idenTokIdx = idx - 1;
- goto found;
- }
- }
- }
- // We reached end of input but still no end of statement
- return {};
-
-found:
- if (tokens[idenTokIdx].type != CLEX_id) {
- // Expected identifier, found something else
- return {};
- }
-
- DeclMemberVariable result;
- result.name = tokens[idenTokIdx].text;
- result.type = CombineTokens(std::span(&tokens[typeStart], &tokens[typeEnd]));
-
- // Consume the '=' or ';' token
- ++idx;
-
- return result;
-}
-
-EnumUnderlyingType TryConsumeEnumUnderlyingType(CodegenLexer& lexer) {
- // Try 1, 2, 3, 4 tokens from the current position
- // NOTE: see the FSTR map initialization code for reference that there is max 4 tokens
- for (int i = 4; i >= 1; --i) {
- auto text = CombineTokens(std::span(&lexer.Current(), i), " "sv);
- auto iter = RSTR_LUT(EnumUnderlyingType).find(text);
- if (iter != RSTR_LUT(EnumUnderlyingType).end()) {
- lexer.idx += i;
- return iter->second;
- }
- }
- return EUT_COUNT;
-}
-
-// Also includes the ':' token in the front
-EnumUnderlyingType TryConsumeEnumUnderlyingTypeClause(CodegenLexer& lexer) {
- if (lexer.Current().text != ":") {
- return EUT_COUNT;
- }
-
- ++lexer.idx;
- return TryConsumeEnumUnderlyingType(lexer);
-}
-
-enum StructMetaGenOptions {
- SMGO_InheritanceHiearchy,
- SMGO_COUNT,
-};
-
-RSTR_LUT_DECL(StructMetaGenOptions, 0, SMGO_COUNT) {
- RSTR_LUT_MAP_FOR(StructMetaGenOptions);
- RSTR_LUT_MAP(SMGO_InheritanceHiearchy, "InheritanceHiearchy");
-}
-
-enum StructPropertyOptions {
- SPO_Getter,
- SPO_Setter,
- SPO_COUNT,
-};
-
-RSTR_LUT_DECL(StructPropertyOptions, 0, SPO_COUNT) {
- RSTR_LUT_MAP_FOR(StructPropertyOptions);
- RSTR_LUT_MAP(SPO_Getter, "GETTER");
- RSTR_LUT_MAP(SPO_Setter, "SETTER");
-}
-
-enum EnumMetaGenOptions {
- EMGO_ToString,
- EMGO_FromString,
- EMGO_ExcludeUseHeuristics,
- EMGO_RemovePrefix,
- EMGO_AddPrefix,
- EMGO_COUNT,
-};
-
-RSTR_LUT_DECL(EnumMetaGenOptions, 0, EMGO_COUNT) {
- RSTR_LUT_MAP_FOR(EnumMetaGenOptions);
- RSTR_LUT_MAP(EMGO_ToString, "ToString");
- RSTR_LUT_MAP(EMGO_FromString, "FromString");
- RSTR_LUT_MAP(EMGO_ExcludeUseHeuristics, "ExcludeHeuristics");
- RSTR_LUT_MAP(EMGO_RemovePrefix, "RemovePrefix");
- RSTR_LUT_MAP(EMGO_AddPrefix, "AddPrefix");
-}
-
-void GenerateEnumStringArray(CodegenOutput& out, const DeclEnum& decl, const char* arrayName, const std::vector<DeclEnumElement>& filteredElements) {
- CodegenOutputThing thing;
- APPEND_FMT_LN(thing.text, "const char* %s[] = {", arrayName);
- for (auto& elm : filteredElements) {
- APPEND_FMT_LN(thing.text, "\"%s\",", elm.name.c_str());
- }
- APPEND_LIT_LN(thing.text, "};");
- out.AddOutputThing(std::move(thing));
-}
-
-void GenerateEnumStringMap(CodegenOutput& out, const DeclEnum& decl, const char* mapName, const std::vector<DeclEnumElement>& filteredElements) {
- CodegenOutputThing thing;
- // TODO
- out.AddOutputThing(std::move(thing));
-}
-
-void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const DeclEnum& decl) {
- auto& enumName = decl.GetFullName();
- auto& mangledName = decl.GetMangledName();
-
- auto useExcludeHeuristics = decl.generateExcludeUseHeuristics;
- auto filteredElements = [&]() {
- if (useExcludeHeuristics) {
- decltype(decl.elements) result;
- for (auto& elm : decl.elements) {
- if (elm.name.ends_with("COUNT")) continue;
-
- std::string_view trimmedName = elm.name;
- if (!decl.generateRemovingPrefix.empty() &&
- elm.name.starts_with(decl.generateRemovingPrefix))
- {
- trimmedName = trimmedName.substr(decl.generateRemovingPrefix.size());
- }
-
- result.push_back(DeclEnumElement{
- .name = decl.generatingAddingPrefix + std::string(trimmedName),
- .value = elm.value,
- });
- }
- return result;
- } else {
- return decl.elements;
- }
- }();
-
- if (decl.generateToString) {
- // Generate value -> string lookup table and function
- INPLACE_FMT(val2StrName, "gCG_%s_Val2Str", mangledName.c_str());
-
- switch (decl.GetPattern()) {
- case EVP_Continuous: {
- GenerateEnumStringArray(sourceOut, decl, val2StrName, filteredElements);
- int minVal = filteredElements.empty() ? 0 : filteredElements.front().value;
- int maxVal = filteredElements.empty() ? 0 : filteredElements.back().value;
-
- CodegenOutputThing lookupFunctionDecl;
- {
- auto& o = lookupFunctionDecl.text;
- APPEND_LIT_LN(o, "template <>");
- APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value);", enumName.c_str(), enumName.c_str());
- }
-
- CodegenOutputThing lookupFunctionDef;
- {
- auto& o = lookupFunctionDef.text;
- APPEND_LIT_LN(o, "template <>");
- APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value) {", enumName.c_str(), enumName.c_str());
- APPEND_FMT_LN(o, " auto intVal = (%s)value;", FSTR_LUT_LOOKUP(EnumUnderlyingType, decl.underlyingType));
- APPEND_FMT_LN(o, " if (intVal < %d || intVal > %d) return {};", minVal, maxVal);
- APPEND_FMT_LN(o, " return %s[intVal - %d];", val2StrName, minVal);
- APPEND_LIT_LN(o, "}");
- }
-
- headerOut.AddOutputThing(std::move(lookupFunctionDecl));
- sourceOut.AddOutputThing(std::move(lookupFunctionDef));
- } break;
-
- case EVP_Bits: {
- GenerateEnumStringArray(sourceOut, decl, val2StrName, filteredElements);
- // TODO
- } break;
-
- case EVP_Random: {
- GenerateEnumStringMap(sourceOut, decl, val2StrName, filteredElements);
- // TODO
- } break;
-
- case EVP_COUNT: break;
- }
- }
-
- if (decl.generateFromString) {
- // Generate string -> value lookup table
- INPLACE_FMT(str2ValName, "gCG_%s_Str2Val", mangledName.c_str());
-
- CodegenOutputThing lookupTable;
- {
- auto& o = lookupTable.text;
- // TODO use correct underlying type
- APPEND_FMT_LN(o, "constinit frozen::unordered_map<frozen::string, %s, %" PRId64 "> %s = {", FSTR_LUT_LOOKUP(EnumUnderlyingType, decl.underlyingType), filteredElements.size(), str2ValName);
- for (auto& elm : filteredElements) {
- APPEND_FMT_LN(o, "{\"%s\", %" PRId64 "},", elm.name.c_str(), elm.value);
- }
- APPEND_LIT_LN(o, "};");
- }
-
- // Generate lookup function
- CodegenOutputThing lookupFunctionDecl;
- {
- auto& o = lookupFunctionDecl.text;
- APPEND_LIT_LN(o, "template <>");
- APPEND_FMT_LN(o, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value);", enumName.c_str(), enumName.c_str());
- }
-
- CodegenOutputThing lookupFunctionDef;
- {
- auto& o = lookupFunctionDef.text;
- APPEND_LIT_LN(o, "template <>");
- APPEND_FMT_LN(o, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value) {", enumName.c_str(), enumName.c_str());
- APPEND_FMT_LN(o, " auto iter = %s.find(value);", str2ValName);
- APPEND_FMT_LN(o, " if (iter != %s.end()) {", str2ValName);
- APPEND_FMT_LN(o, " return (%s)iter->second;", enumName.c_str());
- APPEND_LIT_LN(o, " } else {");
- APPEND_LIT_LN(o, " return {};");
- APPEND_LIT_LN(o, " }");
- APPEND_LIT_LN(o, "}");
- }
-
- sourceOut.AddOutputThing(std::move(lookupTable));
- headerOut.AddOutputThing(std::move(lookupFunctionDecl));
- sourceOut.AddOutputThing(std::move(lookupFunctionDef));
- }
-}
-
-void GenerateClassProperty(CodegenOutput& headerOutput, CodegenOutput& sourceOutput) {
- CodegenOutputThing thing;
- APPEND_FMT_LN(thing.text, "TypePropertyInfo gCGtype_%s_%s_Property = {", "TODO", "TODO");
- APPEND_LIT_LN(thing.text, "};");
-
- sourceOutput.AddOutputThing(std::move(thing));
-}
-
-void GenerateClassFunction(CodegenOutput& headerOutput, CodegenOutput& sourceOutput) {
- // TODO
-}
-
-void GenerateForClassMetadata(
- CodegenOutput& headerOutput,
- CodegenOutput& sourceOutput,
- const DeclStruct& decl //
-) {
- auto& mangedName = decl.GetMangledName();
- auto mangedNameCstr = mangedName.c_str();
-
- CodegenOutputThing data;
- // TODO generate type id, this needs global scanning
-
- if (!decl.baseClasses.empty()) {
- // Forward declare the variables (which may appear before this section, after this section, or in another TU)
- for (auto& baseClass : decl.baseClasses) {
- auto baseClassIdName = baseClass->name.c_str();
- APPEND_FMT_LN(data.text, "extern const TypeInfo gCGtype_%s_TypeInfo;", baseClassIdName);
- }
- APPEND_FMT_LN(data.text, "const TypeInfo* const gCGtype_%s_BaseClasses[] = {", mangedNameCstr);
- for (auto& baseClass : decl.baseClasses) {
- auto baseClassIdName = baseClass->name.c_str();
- APPEND_FMT_LN(data.text, "gCGtype_%s_TypeInfo,", baseClassIdName);
- }
- APPEND_LIT_LN(data.text, "};");
- }
-
- if (!decl.memberVariables.empty()) {
- APPEND_FMT_LN(data.text, "const TypePropertyInfo gCGtype_%s_Properties[] = {", mangedNameCstr);
- for (auto& property : decl.memberVariables) {
- APPEND_FMT_LN(data.text, "{.name=\"%s\"sv, .getterName=\"%s\"sv, .setterName=\"%s\"sv},", property.name.c_str(), property.getterName.c_str(), property.setterName.c_str());
- }
- APPEND_LIT_LN(data.text, "};");
- }
-
- if (!decl.memberFunctions.empty()) {
- APPEND_FMT_LN(data.text, "const TypeMethodInfo gCGtype_%s_Methods[] = {", mangedNameCstr);
- for (auto& method : decl.memberFunctions) {
- // TODO
- }
- APPEND_LIT_LN(data.text, "};");
- }
-
- APPEND_FMT_LN(data.text, "const TypeInfo gCGtype_%s_TypeInfo = {", mangedNameCstr);
- APPEND_FMT_LN(data.text, ".name = \"%s\"sv,", mangedNameCstr);
- if (!decl.baseClasses.empty()) APPEND_FMT_LN(data.text, ".parents = gCGtype_%s_BaseClasses,", mangedNameCstr);
- if (!decl.memberVariables.empty()) APPEND_FMT_LN(data.text, ".properties = gCGtype_%s_Properties,", mangedNameCstr);
- if (!decl.memberFunctions.empty()) APPEND_FMT_LN(data.text, ".methods = gCGtype_%s_Methods,", mangedNameCstr);
- APPEND_LIT_LN(data.text, "};");
-
- CodegenOutputThing queryFunc;
- APPEND_FMT(queryFunc.text,
- "template <>\n"
- "const TypeInfo* Metadata::GetTypeInfo<%s>() {\n"
- " return &gCGtype_%s_TypeInfo;\n"
- "}\n",
- decl.fullname->c_str(),
- mangedNameCstr);
-
- sourceOutput.AddOutputThing(std::move(data));
- sourceOutput.AddOutputThing(std::move(queryFunc));
-}
-
-struct NamespaceStackframe {
- // The current namespace that owns the brace level, see example
- DeclNamespace* ns = nullptr;
- // Brace depth `ns` was created at (e.g. [std::details].depth == 0)
- int depth = 0;
-};
-
-struct ParserState {
- // TODO
-};
-
-struct ParserOutput {
- // Example:
- // namespace std::details {
- // /* [stack top].ns = std::details */
- // /* [stack top].depth = std */
- // }
- // namespace foo {
- // /* [stack top].ns = foo */
- // /* [stack top].depth = foo */
- // namespace details {
- // /* [stack top].ns = foo::details */
- // /* [stack top].depth = foo::details */
- // }
- // }
- std::vector<NamespaceStackframe> nsStack;
- // The current effective namespace, see example
- DeclNamespace* currentNamespace = nullptr;
-
- DeclStruct* currentStruct = nullptr;
- DeclEnum* currentEnum = nullptr;
- int currentBraceDepth = 0;
- int currentStructBraceDepth = -1;
- int currentEnumBraceDepth = -1;
-};
-
-void HandleDirectiveEnum(AppState& as, ParserOutput& ps, CodegenLexer& lexer) {
- // Consume the directive
- ++lexer.idx;
-
- if (!ps.currentEnum) {
- printf("[ERROR] BRUSSEL_ENUM must be used within a enum\n");
- return;
- }
-
- auto argList = TryConsumeDirectiveArgumentList(lexer);
- auto& lut = RSTR_LUT(EnumMetaGenOptions);
- for (auto& arg : argList) {
- if (arg.empty()) {
- printf("[ERROR] empty argument is invalid in BRUSSEL_ENUM\n");
- continue;
- }
-
- auto& optionDirective = arg[0]->text;
- auto iter = lut.find(optionDirective);
- if (iter == lut.end()) {
- printf("[ERROR] BRUSSEL_ENUM: invalid option '%s'\n", optionDirective.c_str());
- }
-
- auto option = iter->second;
- switch (option) {
- case EMGO_ToString: ps.currentEnum->generateToString = true; break;
- case EMGO_FromString: ps.currentEnum->generateFromString = true; break;
- case EMGO_ExcludeUseHeuristics: ps.currentEnum->generateExcludeUseHeuristics = true; break;
-
- case EMGO_RemovePrefix: {
- if (argList.size() <= 1) {
- printf("[ERROR] missing argument for RemovePrefix");
- break;
- }
- ps.currentEnum->generateRemovingPrefix = arg[1]->text;
- } break;
- case EMGO_AddPrefix: {
- if (argList.size() <= 1) {
- printf("[ERROR] missing argument for AddPrefix");
- break;
- }
- ps.currentEnum->generatingAddingPrefix = arg[1]->text;
- } break;
-
- case EMGO_COUNT: break;
- }
- }
-
- ps.currentEnum->generating = true;
-}
-
-CodegenLexer LexInputFile(AppState& as, std::string_view source) {
- CodegenLexer result;
- result.InitializeFrom(source);
- return result;
-}
-
-void ParseInputFileAndGenerate(AppState& as, CodegenLexer& /*lexingState*/ ls, std::string_view filenameStem) {
-#if CODEGEN_DEBUG_PRINT
- printf("BEGIN tokens\n");
- for (auto& token : ls.tokens) {
- switch (token.type) {
- case CLEX_intlit: {
- printf(" token %-32s = %ld\n", FSTR_LUT_LOOKUP(ClexNames, token.type), token.lexerIntNumber);
- } break;
-
- case CLEX_floatlit: {
- printf(" token %-32s = %f\n", FSTR_LUT_LOOKUP(ClexNames, token.type), token.lexerRealNumber);
- } break;
-
- default: {
- printf(" token %-32s '%s'\n", FSTR_LUT_LOOKUP(ClexNames, token.type), token.text.c_str());
- } break;
- }
- }
- printf("END tokens\n");
-#endif
-
- auto& sourceFile = as.GetOrCreateSourceFile(filenameStem);
- sourceFile.header = true;
- sourceFile.reprocessing = true;
-
- // TODO move lexedTokens and consumption related functions to ParserState struct
-
- ParserOutput po;
-
- auto& tokens = ls.tokens;
- auto& idx = ls.idx;
- while (ls.idx < ls.tokens.size()) {
- auto& token = ls.Current();
-
- bool incrementTokenIdx = true;
- switch (token.Reamalgamate()) {
- case CLEX_id: {
- CppKeyword keyword;
- {
- auto& map = RSTR_LUT(CppKeyword);
- auto iter = map.find(token.text);
- if (iter != map.end()) {
- keyword = iter->second;
- } else {
- keyword = CKw_COUNT; // Skip keyword section
- }
- }
- switch (keyword) {
- case CKw_Namespace: {
- ++idx;
- incrementTokenIdx = false;
-
- int nestingCount = 0;
- while (true) {
- if (tokens[idx].type != CLEX_id) {
- // TODO better error recovery
- // TODO handle annoymous namespaces
- printf("[ERROR] invalid syntax for namespace\n");
- break;
- }
-
- po.currentNamespace = as.runtimeModel->AddNamespace(DeclNamespace{
- .container = po.currentNamespace,
- .name = tokens[idx].text,
- });
-
- // Consume the identifier token
- ++idx;
-
- if (tokens[idx].type == CLEX_ext_double_colon) {
- // Consume the "::" token
- ++idx;
- } else {
- break;
- }
- }
-
- po.nsStack.push_back(NamespaceStackframe{
- .ns = po.currentNamespace,
- .depth = po.currentBraceDepth,
- });
-
- goto endCaseCLEX_id;
- }
-
- case CKw_Struct:
- case CKw_Class: {
- // Consume the 'class' or 'struct' keyword
- ++idx;
- incrementTokenIdx = false;
-
- // For forward declarations, there are always 2 tokens after `class`: an identifier, and the ';' token
- // Example:
- // class MyClass;
- if (tokens[idx + 0].type == CLEX_id &&
- tokens[idx + 1].text == ";")
- {
- // Skip class forward declarations
- idx += 2;
- break;
- }
-
- auto& idenTok = tokens[idx];
- if (idenTok.type != CLEX_id) {
- printf("[ERROR] invalid syntax for struct or class\n");
- break;
- }
-
- DEBUG_PRINTF("[DEBUG] found struct named %s\n", idenTok.text.c_str());
-
- auto& name = idenTok.text;
- auto fullname = Utils::MakeFullName(name, po.currentNamespace);
- DeclStruct structDecl;
- structDecl.sourceFile = &sourceFile;
- structDecl.container = po.currentNamespace;
- structDecl.name = name;
-
- // Consume the identifier token
- ++idx;
-
- if (ls.TryConsumeSingleCharToken(':')) {
- while (true) {
- // Public, protected, etc.
- TryConsumeAnyKeyword(ls);
-
- auto& idenTok = tokens[idx];
- if (idenTok.type != CLEX_id) {
- printf("[ERROR] invalid syntax for class inheritance list\n");
- goto endCase;
- }
-
- // TODO support namespace qualified names
- auto baseClassFullname = Utils::MakeFullName(idenTok.text, po.currentNamespace);
- auto baseClassDecl = as.runtimeModel->FindStruct(baseClassFullname);
- if (baseClassDecl) {
- // TODO retreive class from database
- // ---- Or just silent create it, and assume the code was valid?
- // We silently ignore a non-existent base class, because they may reside in a file that we didn't scan
- structDecl.baseClasses.push_back(baseClassDecl);
- }
-
- // Consume the identifier token
- ++idx;
-
- if (ls.TryConsumeSingleCharToken('{')) {
- // End of base class list
- --idx; // Give the '{' token back to the main loop
- break;
- } else if (!ls.TryConsumeSingleCharToken(',')) {
- // If the list didn't end, we expect a comma (then followed by more entries)
- printf("[ERROR] invalid syntax for class inheritance list\n");
- goto endCase;
- }
-
- // NOTE: we currently only scan one base class to workaround some code inherits from template classes after their initial base class
- // TODO remove this hack
- break;
- }
- }
-
- {
- // Get a pointer to the decl inside CodegenInput's storage
- auto decl = as.runtimeModel->AddStruct(std::move(fullname), std::move(structDecl));
- po.currentStruct = decl;
- po.currentStructBraceDepth = po.currentBraceDepth;
- }
-
- endCase:
- goto endCaseCLEX_id;
- }
-
- case CKw_Enum: {
- if (po.currentStruct) {
- // TODO parsing enums inside classes is currently broken (1. model database is not modeled for this 2. codegen logic is not modeled)
- break;
- }
-
- // Consume the "enum" keyword
- ++idx;
- incrementTokenIdx = false;
-
- StbLexerToken* idenTok;
- if (tokens[idx].text == "class") {
- // Consume the "class" keyword
- ++idx;
- idenTok = &tokens[idx];
- DEBUG_PRINTF("[DEBUG] found enum class named %s\n", idenTok->text.c_str());
- } else {
- idenTok = &tokens[idx];
- DEBUG_PRINTF("[DEBUG] found enum named %s\n", idenTok->text.c_str());
- }
-
- auto& name = tokens[idx].text;
- auto fullname = Utils::MakeFullName(name, po.currentNamespace);
- DeclEnum enumDecl;
- enumDecl.sourceFile = &sourceFile;
- enumDecl.container = po.currentNamespace;
- enumDecl.name = name;
- // Setting underlying type: see below
-
- // Temporarily bind the pointers to local variable, HandleDirectiveEnum() and other functions expect these to the set
- po.currentEnum = &enumDecl;
- po.currentEnumBraceDepth = po.currentBraceDepth;
-
- // Consume the enum name identifier
- ++idx;
-
- // Setting underlying type
- if (auto eut = TryConsumeEnumUnderlyingTypeClause(ls);
- eut != EUT_COUNT)
- {
- enumDecl.underlyingType = eut;
- } else {
- enumDecl.underlyingType = EUT_Int32;
- }
-
- int enumClosingBraceCount = 0;
- int enumBraceDepth = 0;
- while (enumClosingBraceCount == 0 && idx < tokens.size()) {
- auto& token = tokens[idx];
- switch (token.Reamalgamate()) {
- case CLEX_id: {
- if (token.text == "BRUSSEL_ENUM") {
- // Consume the argument list and skip advancing index: this function already consumed all the tokens about BRUSSEL_ENUM
- HandleDirectiveEnum(as, po, ls);
- continue;
- } else {
- auto& vec = enumDecl.elements;
- // Set to the previous enum element's value + 1, or starting from 0 if this is the first
- // Also overridden in the CLEX_intlit branch
- auto value = vec.empty() ? 0 : vec.back().value + 1;
- vec.push_back(DeclEnumElement{
- .name = token.text,
- .value = value,
- });
- }
- } break;
-
- case CLEX_intlit: {
- auto& vec = enumDecl.elements;
- if (!vec.empty()) {
- auto& lastElm = vec.back();
- lastElm.value = token.lexerIntNumber;
- }
- } break;
-
- case '{': {
- ++enumBraceDepth;
- } break;
-
- case '}': {
- --enumBraceDepth;
- ++enumClosingBraceCount;
- } break;
- }
-
- ++idx;
- }
-
- {
- auto decl = as.runtimeModel->AddEnum(std::move(fullname), std::move(enumDecl));
- // Fix pointers
- po.currentEnum = decl;
- po.currentEnumBraceDepth = po.currentBraceDepth;
- }
-
- if (po.currentEnum->generating) {
- as.enumsToRevisit.push_back(po.currentEnum);
- }
-
- // NOTE: we parse the whole enum at once (above code), the enum ends right here after the closing brace '}'
- po.currentEnum = nullptr;
- po.currentEnumBraceDepth = -1;
-
- goto endCaseCLEX_id;
- }
-
- // Consume the whole statement, because this statement may contain `enum` or `class` keywords that will pollute the parser
- case CKw_Template: {
- // `template` is either a template list which we don't care about, or a part of a type which we don't care about,
- // unless it's a part of a function declaration, where the tokens are handled inside CG_ClassMethod parsing
- // TODO handle nested templates or operator> inside template expression
- ls.SkipUntilTokenSingleChar('>');
- } break;
- case CKw_Using: {
- // `using` indicates a type alias or namespace import which we don't care about
- ls.SkipUntilTokenSingleChar(';');
- } break;
-
- // We don't care about these keywords
- case CKw_Public:
- case CKw_Protected:
- case CKw_Private:
- case CKw_Virtual:
- case CKw_COUNT: break;
- }
-
- CodegenDirective directive;
- {
- auto& map = RSTR_LUT(CodegenDirective);
- auto iter = map.find(token.text);
- if (iter != map.end()) {
- directive = iter->second;
- } else {
- directive = CD_COUNT; // Skip directive section
- }
- }
- switch (directive) {
- case CD_Class: {
- // Consume the directive
- ++idx;
- incrementTokenIdx = false;
-
- if (!po.currentStruct) {
- printf("[ERROR] BRUSSEL_CLASS must be used within a class or struct\n");
- break;
- }
-
- // Always-on option
- po.currentStruct->generating = true;
-
- auto argList = TryConsumeDirectiveArgumentList(ls);
- auto& lut = RSTR_LUT(StructMetaGenOptions);
- for (auto& arg : argList) {
- if (arg.empty()) {
- printf("[ERROR] empty argument is invalid in BRUSSEL_CLASS\n");
- continue;
- }
-
- auto& optionDirective = arg[0]->text;
- auto iter = lut.find(optionDirective);
- if (iter == lut.end()) continue;
- switch (iter->second) {
- case SMGO_InheritanceHiearchy: po.currentStruct->generatingInheritanceHiearchy = true; break;
- case SMGO_COUNT: break;
- }
- }
-
- goto endCaseCLEX_id;
- }
-
- case CD_ClassProperty: {
- // Consume the directive
- ++idx;
- incrementTokenIdx = false;
-
- if (!po.currentStruct ||
- !po.currentStruct->generating)
- {
- printf("[ERROR] BRUSSEL_PROPERTY must be used within a class or struct, that has the BRUSSEL_CLASS directive\n");
- break;
- }
-
- auto argList = TryConsumeDirectiveArgumentList(ls);
- auto declOpt = TryConsumeMemberVariable(ls);
- if (!declOpt.has_value()) {
- printf("[ERROR] a member variable must immediately follow a BRUSSEL_PROPERTY\n");
- break;
- }
- auto& decl = declOpt.value();
- decl.containerStruct = po.currentStruct;
-
- // Different option's common logic
- std::string pascalCaseName;
- auto GetPascalCasedName = [&]() -> const std::string& {
- if (pascalCaseName.empty()) {
- pascalCaseName = Utils::MakePascalCase(decl.name);
- }
- return pascalCaseName;
- };
-
- auto& lut = RSTR_LUT(StructPropertyOptions);
- for (auto& arg : argList) {
- if (arg.empty()) {
- printf("[ERROR] empty argument is invalid in BRUSSEL_PROPERTY\n");
- continue;
- }
-
- auto& optionDirective = arg[0]->text;
- auto iter = lut.find(optionDirective);
- if (iter == lut.end()) continue;
- switch (iter->second) {
- case SPO_Getter: {
- // NOTE: I'm too lazy to write error checks, just let the codegen crash if syntax is invalid
- auto& getterName = arg.at(1)->text;
- if (getterName == "auto") {
- // NOTE: intentionally shadowing
- INPLACE_FMT(getterName, "Get%s", GetPascalCasedName().c_str());
-
- decl.getterName = getterName;
- decl.isGetterGenerated = true;
- } else {
- decl.getterName = getterName;
- }
- } break;
-
- case SPO_Setter: {
- // NOTE: I'm too lazy to write error checks, just let the codegen crash if syntax is invalid
- auto& setterName = arg.at(1)->text;
- if (setterName == "auto") {
- // NOTE: intentionally shadowing
- INPLACE_FMT(setterName, "Set%s", GetPascalCasedName().c_str());
-
- decl.setterName = setterName;
- decl.isSetterGenerated = true;
- } else {
- decl.setterName = setterName;
- }
- } break;
-
- case SPO_COUNT: break;
- }
- }
-
- po.currentStruct->memberVariables.push_back(std::move(decl));
-
- goto endCaseCLEX_id;
- }
-
- case CD_ClassMethod: {
- // Consume the directive
- ++idx;
- incrementTokenIdx = false;
-
- goto endCaseCLEX_id;
- }
-
- case CD_XGlobalVar: {
- // TODO
- goto endCaseCLEX_id;
- }
-
- case CD_XGlobalVarCtor: {
- // TODO
- goto endCaseCLEX_id;
- }
-
- case CD_XGlobalVarDtor: {
- // TODO
- goto endCaseCLEX_id;
- }
-
- // This directive always appear inside a enum{} block, which is handled above in the keywords section
- case CD_Enum:
- case CD_COUNT: break;
- }
-
- endCaseCLEX_id:;
- } break;
-
- case '{': {
- po.currentBraceDepth++;
- if (po.currentBraceDepth < 0) {
- printf("[WARNING] unbalanced brace\n");
- }
- } break;
-
- case '}': {
- po.currentBraceDepth--;
- if (po.currentBraceDepth < 0) {
- printf("[WARNING] unbalanced brace\n");
- }
-
- if (!po.nsStack.empty()) {
- auto& ns = po.nsStack.back();
- if (ns.depth == po.currentBraceDepth) {
- po.nsStack.pop_back();
-
- if (!po.nsStack.empty()) {
- po.currentNamespace = po.nsStack.back().ns;
- } else {
- po.currentNamespace = nullptr;
- }
- }
- }
-
- if (po.currentStruct && po.currentBraceDepth == po.currentStructBraceDepth) {
- // Exit struct
-
- if (po.currentStruct->generating) {
- as.structsToRevisit.push_back(po.currentStruct);
- }
-
- po.currentStruct = nullptr;
- po.currentStructBraceDepth = -1;
- }
- if (po.currentEnum && po.currentBraceDepth == po.currentEnumBraceDepth) {
- // Exit enum
-
- // TODO this is unused currently, see CKw_Enum branch
- if (po.currentEnum->generating) {
- as.enumsToRevisit.push_back(po.currentEnum);
- }
-
- po.currentEnum = nullptr;
- po.currentEnumBraceDepth = -1;
- }
- } break;
- }
-
- if (incrementTokenIdx) {
- ++idx;
- }
- }
-
- if (po.currentBraceDepth != 0) {
- printf("[WARNING] unbalanced brace at end of file\n");
- }
-
- as.archiveModel->DeleteDeclsRelatedToFile(filenameStem);
- // as.modelArchive->Store(po.model);
-}
-
-void HandleInputFile(AppState& as, const fs::path& path) {
- auto filenameStem = path.stem().string();
- auto lexingState = LexInputFile(as, Utils::ReadFileAsString(path));
- ParseInputFileAndGenerate(as, lexingState, filenameStem);
-}
-
-enum InputOpcode {
- IOP_ProcessSingleFile,
- IOP_ProcessRecursively,
- IOP_ProcessFileList,
- IOP_COUNT,
-};
-
-void HandleArgument(AppState& as, InputOpcode opcode, std::string_view operand) {
- switch (opcode) {
- case IOP_ProcessSingleFile: {
- DEBUG_PRINTF("Processing single file %.*s\n", PRINTF_STRING_VIEW(operand));
- HandleInputFile(as, fs::path(operand));
- } break;
-
- case IOP_ProcessRecursively: {
- DEBUG_PRINTF("Recursively processing folder %.*s\n", PRINTF_STRING_VIEW(operand));
-
- fs::path startPath(operand);
- for (auto& item : fs::recursive_directory_iterator(startPath)) {
- if (!item.is_regular_file()) {
- continue;
- }
-
- auto& path = item.path();
- if (auto pathExt = path.extension();
- pathExt != ".h" &&
- pathExt != ".hpp")
- {
- continue;
- }
-
- DEBUG_PRINTF("Processing subfile %s\n", path.string().c_str());
- HandleInputFile(as, path);
- }
- } break;
-
- case IOP_ProcessFileList: {
- DEBUG_PRINTF("Processing file list %.*s\n", PRINTF_STRING_VIEW(operand));
-
- fs::path fileListPath(operand);
- auto fileList = Utils::OpenCstdioFile(fileListPath, Utils::Read);
- if (!fileList) {
- // NOTE: need this because our dirty-file-list generation algorithm in CMakeLists.txt doesn't produce a file when nothing is changed
- DEBUG_PRINTF("File-list file does not exist, silently skipping.\n");
- break;
- }
- DEFER {
- fclose(fileList);
- };
-
- std::string line;
- while (Utils::ReadCstdioLine(fileList, line)) {
- // Remove '\n'
- line.pop_back();
-
- DEBUG_PRINTF("Processing file in list %.*s\n", line.c_str());
- HandleInputFile(as, fs::path(line));
- }
- } break;
-
- case IOP_COUNT: break;
- }
-}
-
-InputOpcode ParseInputOpcode(std::string_view text) {
- if (text == "single"sv) {
- return IOP_ProcessSingleFile;
- } else if (text == "rec"sv) {
- return IOP_ProcessRecursively;
- } else if (text == "fileList"sv) {
- return IOP_ProcessFileList;
- } else {
- INPLACE_FMT(msg, "Unknown input opcode %s\n", text.data());
- throw std::runtime_error(msg);
- }
-}
-
-int main(int argc, char* argv[]) {
- FSTR_LUT_INIT(ClexNames);
- FSTR_LUT_INIT(EnumUnderlyingType);
- RSTR_LUT_INIT(EnumUnderlyingType);
- FSTR_LUT_INIT(EnumValuePattern);
- RSTR_LUT_INIT(CppKeyword);
- RSTR_LUT_INIT(CodegenDirective);
- RSTR_LUT_INIT(StructMetaGenOptions);
- RSTR_LUT_INIT(StructPropertyOptions);
- RSTR_LUT_INIT(EnumMetaGenOptions);
-
- // TODO better arg parser
- // option 1: use cxxopts and positional arguments
- // option 2: take one argument only, being a json objecet
-
- AppState as;
-
- // If no cli is provided (argv[0] conventionally but not mandatorily the cli), this will do thing
- // Otherwise, start with the 2nd element in the array, which is the 1st actual argument
- if (argc <= 1) {
- // NOTE: keep in sync with various enum options and parser code
- printf(&R"""(
-USAGE: codegen.exe --output-dir=<path> [--database=<path>] [<opcode>:<input path>]...
-where --output-dir=<path>: the *directory* to write generated contents to. This will NOT automatically create the directory.
- --database=<path>: the *file* to use for the code model database.
- <opcode> is one of:
- "single" process this <input path> file only
- "rec" starting at the given directory <input path>, recursively process all .h .hpp files
- "fileList" read <input path> as a text file, and process each line as a separate file path
-)"""[1]);
- return -1;
- }
-
- // Named argument pass
- robin_hood::unordered_map<std::string_view, std::string_view*> namedArguments{
- { "output-dir"sv, &as.outputDir },
- { "database"sv, &as.databaseFilePath },
- };
- for (int i = 1; i < argc; ++i) {
- std::string_view arg(argv[i]);
- if (!arg.starts_with("--")) {
- // Convention: a "--" argument indicates everything afterwords are positional arguments
- if (arg.size() == 2) {
- break;
- } else {
- continue;
- }
- }
-
- size_t equalLoc = arg.find('=');
- auto oper = arg.substr(/*--*/ 2, equalLoc - 2);
- auto iter = namedArguments.find(oper);
- if (iter != namedArguments.end()) {
- auto storage = iter->second;
- if (storage) {
- if (equalLoc == std::string_view::npos) {
- *storage = ""sv;
- } else {
- *storage = arg.substr(equalLoc + 1);
- }
- }
- }
- }
-
- DEBUG_PRINTF("Outputting to directory %.*s.\n", PRINTF_STRING_VIEW(as.outputDir));
- DEBUG_PRINTF("Databse file: %.*s.\n", PRINTF_STRING_VIEW(as.databaseFilePath));
-
- // TODO move the actual output logic after processing all input commands, based on SQLite batabase model instead of the in-memory CodegenModel model
- // this allows better consistency between direct in-file entities (like enums) vs. multi-file entities (like struct inheritance hierarchy)
- // this would also mean almost rewriting the whole codegen logic, to work on a changelist fetched from SQLite database instead of being embedded inside the parser loop
- // TODO how do we detect the case of
- // 1. has: Foo.hpp Bar.hpp
- // 2. struct Foo; struct Bar : Foo;
- // 3. struct Foo is removed from Foo.hpp, but our parser only recieves Foo.hpp as file changed--and can't figure out that there is still a reference to Foo in Bar.hpp
- // possible solutions
- // - use some kind of database scanner to review all references to a class when removing (e.g. detect for logic error on foreign key linked columns)
- // - follow the file links in database, and propagate parsing to those files in the hierarchy
- // - pretty much defeats the purpose of using an incremental parser: some classes like GameObject will have links throughout a very large portion of the project code
- // - [x] out of parser generation
- // - [ ] database readback
- // - [ ] full database based generation (tentative)
- CodegenRuntimeModel runtimeModel;
- CodegenArchiveModel archiveModel(as.databaseFilePath);
-
- as.runtimeModel = &runtimeModel;
- as.archiveModel = &archiveModel;
-
- // Positional argument pass
- for (int i = 1; i < argc; ++i) {
- std::string_view arg(argv[i]);
- if (arg.starts_with("--")) {
- continue;
- }
-
- DEBUG_PRINTF("Processing input command %s\n", argv[i]);
-
- auto separatorLoc = arg.find(':');
- if (separatorLoc != std::string_view::npos) {
- auto opcodeString = arg.substr(0, separatorLoc);
- auto opcode = ParseInputOpcode(opcodeString);
- auto operand = arg.substr(separatorLoc + 1);
-
- HandleArgument(as, opcode, operand);
- }
- }
-
- for (auto decl : as.enumsToRevisit) {
- if (!decl->generating) {
- continue;
- }
-
- auto& headerOutput = decl->sourceFile->postHeaderOutput;
- auto& sourceOutput = decl->sourceFile->postSourceOutput;
- GenerateForEnum(headerOutput, sourceOutput, *decl);
- }
- for (auto decl : as.structsToRevisit) {
- if (!decl->generating) {
- continue;
- }
-
- auto& headerOutput = decl->sourceFile->postHeaderOutput;
- auto& sourceOutput = decl->sourceFile->postSourceOutput;
-
- // Always-on metdata
- GenerateForClassMetadata(headerOutput, sourceOutput, *decl);
-
- if (decl->generatingInheritanceHiearchy) {
- // TODO
- }
-
- for (auto& property : decl->memberVariables) {
- if (property.isGetterGenerated) {
- // TODO work with pass-by-value vs pass-by-reference
- // this probably needs libclang to detect the size and existance of trivial copy-ctors
- CodegenOutputThing data;
- APPEND_FMT_LN(data.text, "const %s& %s::%s() const {", property.type.c_str(), property.containerStruct->fullname->c_str(), property.getterName.c_str());
- APPEND_FMT_LN(data.text, " return %s;", property.name.c_str());
- APPEND_LIT_LN(data.text, "}");
-
- sourceOutput.AddOutputThing(std::move(data));
- }
- if (property.isSetterGenerated) {
- CodegenOutputThing data;
- APPEND_FMT_LN(data.text, "void %s::%s(const %s& value) const {", property.containerStruct->fullname->c_str(), property.setterName.c_str(), property.type.c_str());
- APPEND_FMT_LN(data.text, " this->%s = value;", property.name.c_str());
- APPEND_LIT_LN(data.text, "}");
-
- sourceOutput.AddOutputThing(std::move(data));
- }
- }
- for (auto& method : decl->memberFunctions) {
- // TODO
- }
- }
-
- archiveModel.Store(runtimeModel);
-
- // Write output files
- for (auto&& [_, sourceFile] : as.sourceFiles) {
- INPLACE_FMT(hpp, "%.*s.gh.inl", PRINTF_STRING_VIEW(sourceFile.filename));
- INPLACE_FMT(cpp, "%.*s.gs.inl", PRINTF_STRING_VIEW(sourceFile.filename));
- Utils::ProduceGeneratedHeader(hpp, sourceFile.postHeaderOutput, cpp, sourceFile.postSourceOutput);
-
- INPLACE_FMT(generatedHeaderInlName, "%.*s/%s", PRINTF_STRING_VIEW(as.outputDir), hpp);
- Utils::WriteOutputFile(sourceFile.postHeaderOutput, generatedHeaderInlName);
- INPLACE_FMT(generatedSourceInlName, "%.*s/%s", PRINTF_STRING_VIEW(as.outputDir), cpp);
- Utils::WriteOutputFile(sourceFile.postSourceOutput, generatedSourceInlName);
- INPLACE_FMT(generatedCppName, "%.*s/%.*s.g.cpp", PRINTF_STRING_VIEW(as.outputDir), PRINTF_STRING_VIEW(sourceFile.filename));
- Utils::WriteOutputFile(sourceFile.tuOutput, generatedCppName);
- }
-
- return 0;
-}
-
-// TODO move this function to CodegenDecl.cpp, after making LUT able to cross TUs
-std::string_view DeclEnum::GetUnderlyingTypeName() const {
- return FSTR_LUT_LOOKUP(EnumUnderlyingType, underlyingType);
-}
diff --git a/source/20-codegen-compiler/test/examples/TestClass.hpp.txt b/source/20-codegen-compiler/test/examples/TestClass.hpp.txt
deleted file mode 100644
index 3eed8db..0000000
--- a/source/20-codegen-compiler/test/examples/TestClass.hpp.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-#include <TestClass.gph.inl>
-
-class MyClass {
- BRUSSEL_CLASS()
-
-public:
- BRUSSEL_PROPERTY(GETTER GetName, SETTER SetName)
- std::string name;
-
- BRUSSEL_PROPERTY(GETTER auto, SETTER auto)
- std::string tag;
-
- BRUSSEL_PROPERTY()
- int foo;
-
- BRUSSEL_PROPERTY()
- int bar;
-
-public:
- const std::string& GetName() const { return name; }
- void SetName(std::string name) { this->name = std::move(name); }
-};
-
-namespace MyNamespace {
-struct Base {
- BRUSSEL_CLASS(InheritanceHiearchy)
-};
-
-struct DerviedFoo : public Base {
- BRUSSEL_CLASS()
-};
-
-struct DerviedBar : Base {
- BRUSSEL_CLASS()
-};
-}
-
-#include <TestClass.gh.inl>
diff --git a/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt b/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt
deleted file mode 100644
index 132bac0..0000000
--- a/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-enum MyEnum {
- BRUSSEL_ENUM(ToString, FromString)
- EnumElement1,
- EnumElement2,
- EnumElement3,
-};
-
-// Let's also test enum class
-enum class CountedEnumAll {
- BRUSSEL_ENUM(ToString, FromString)
- CEA_Foo,
- CEA_Bar,
- CEA_COUNT,
-};
-
-enum CountedEnum : unsigned short int {
- BRUSSEL_ENUM(ToString, FromString, RemovePrefix CE_, AddPrefix CustomPrefix, ExcludeHeuristics)
- CE_Foo,
- CE_Bar,
- CE_FooBar,
- CE_COUNT,
-};
-
-namespace MyNamespace {
- enum class MyNamespacedEnum {
- BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics)
- MNE_Foo,
- MNE_Bar,
- };
-
- namespace details {
- enum MyNamespacedEnum {
- BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics)
- MNE_Foo,
- MNE_Bar,
- };
- }
-}
-
-namespace foo::details {
- enum Enum {
- BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics)
- };
-}
diff --git a/source/20-codegen-runtime/MacrosCodegen.hpp b/source/20-codegen-runtime/MacrosCodegen.hpp
deleted file mode 100644
index 43ae99c..0000000
--- a/source/20-codegen-runtime/MacrosCodegen.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-// NOTE: Contents of this file is coupled with the codegen compiler.
-// When updating, change both sides at the same time.
-
-#pragma once
-
-#define BRUSSEL_CLASS(...)
-#define BRUSSEL_PROPERTY(...)
-#define BRUSSEL_METHOD(...)
-
-#define BRUSSEL_ENUM(...)
diff --git a/source/20-codegen-runtime/Metadata.cpp b/source/20-codegen-runtime/Metadata.cpp
deleted file mode 100644
index 0d640da..0000000
--- a/source/20-codegen-runtime/Metadata.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "Metadata.hpp"
-
-auto Metadata::TypeInfoList::Iterator::operator*() const -> const TypeInfo& {
- // TODO
-}
-
-auto Metadata::TypeInfoList::Iterator::operator++() -> Iterator& {
- // TODO
-}
-
-auto Metadata::TypeInfoList::Iterator::operator++(int) -> Iterator {
- auto copy = *this;
- ++copy;
- return copy;
-}
-
-bool Metadata::TypeInfoList::Iterator::operator==(const Iterator& that) const {
- return this->data == that.data;
-}
-
-bool Metadata::TypeInfoList::Iterator::operator==(const Sentinel&) const {
- // TODO
-}
-
-auto Metadata::TypeInfoList::begin() const -> Iterator {
- return Iterator();
-}
-
-auto Metadata::TypeInfoList::end() const -> Sentinel {
- return Sentinel();
-}
-
-auto Metadata::GetTypeInfoList() -> const TypeInfoList& {
- // TODO
-}
-
-auto Metadata::QueryTypeInfo(TypeId id) -> const TypeInfo* {
- // TODO
- return nullptr;
-}
-
-auto Metadata::QueryTypeInfo(std::string_view id) -> const TypeInfo* {
- // TODO
- return nullptr;
-}
diff --git a/source/20-codegen-runtime/Metadata.hpp b/source/20-codegen-runtime/Metadata.hpp
deleted file mode 100644
index e89fd8f..0000000
--- a/source/20-codegen-runtime/Metadata.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include "MacrosCodegen.hpp"
-#include "MetadataBase.hpp"
-
-namespace Metadata {
-
-struct TypeInfoList {
- struct Sentinel {
- };
-
- struct Iterator {
- void* data;
-
- const TypeInfo& operator*() const;
- Iterator& operator++();
- Iterator operator++(int);
-
- bool operator==(const Iterator&) const;
- bool operator==(const Sentinel&) const;
- };
-
- Iterator begin() const;
- Sentinel end() const;
-};
-
-/// Get a list of all type infos present.
-const TypeInfoList& GetTypeInfoList();
-
-const TypeInfo* QueryTypeInfo(TypeId id);
-const TypeInfo* QueryTypeInfo(std::string_view name);
-
-} // namespace Metadata
diff --git a/source/20-codegen-runtime/MetadataBase.cpp b/source/20-codegen-runtime/MetadataBase.cpp
deleted file mode 100644
index 2f2ef94..0000000
--- a/source/20-codegen-runtime/MetadataBase.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-#include "MetadataBase.hpp"
-
-bool Metadata::TypePropertyInfo::IsDirectAccess() const {
- return getterName.empty() && setterName.empty();
-}
diff --git a/source/20-codegen-runtime/MetadataBase.hpp b/source/20-codegen-runtime/MetadataBase.hpp
deleted file mode 100644
index f84a152..0000000
--- a/source/20-codegen-runtime/MetadataBase.hpp
+++ /dev/null
@@ -1,53 +0,0 @@
-#pragma once
-
-#include <cstddef>
-#include <optional>
-#include <span>
-#include <string_view>
-
-namespace Metadata {
-
-struct TypeId {
- size_t id;
-};
-
-struct TypeInfo;
-
-struct TypePropertyInfo {
- std::string_view name;
- const TypeInfo* type;
- std::string_view getterName;
- std::string_view setterName;
-
- bool IsDirectAccess() const;
-};
-
-struct TypeMethodInfo {
- std::string_view name;
- // TODO
-};
-
-struct TypeInfo {
- TypeId typeId;
- std::string_view name;
- std::span<const TypeInfo* const> parents;
- std::span<const TypePropertyInfo> properties;
- std::span<const TypeMethodInfo> methods;
-
- /// Whether this object is registered at runtime or statically compiled
- bool dynamic = false;
-};
-
-// NOTE: implemented by generating specializations; not-generated ones should generated an error in the linking phase
-template <typename T>
-const TypeInfo* GetTypeInfo();
-
-// NOTE: implemented by generating specializations
-template <typename TEnum>
-std::string_view EnumToString(TEnum value) = delete;
-
-// NOTE: implemented by generating specializations
-template <typename TEnum>
-std::optional<TEnum> EnumFromString(std::string_view str) = delete;
-
-} // namespace Metadata
diff --git a/source/20-codegen-runtime/MetadataDetails.hpp b/source/20-codegen-runtime/MetadataDetails.hpp
deleted file mode 100644
index 09b71ff..0000000
--- a/source/20-codegen-runtime/MetadataDetails.hpp
+++ /dev/null
@@ -1,7 +0,0 @@
-// This file contains implementation details used by the outputs of the code generaetor. Consumers do not use.
-#pragma once
-
-#include "MetadataBase.hpp"
-
-namespace Metadata::Details {
-} // namespace Metadata::Details
diff --git a/source/30-game/App.cpp b/source/30-game/App.cpp
deleted file mode 100644
index 8328589..0000000
--- a/source/30-game/App.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-#include "App.hpp"
-
-#include "ScopeGuard.hpp"
-#include "Utils.hpp"
-
-#include <rapidjson/document.h>
-#include <rapidjson/filereadstream.h>
-#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);
-
- do {
- auto file = Utils::OpenCstdioFile("assets/GameRendererBindings.json", Utils::Read);
- if (!file) break;
- DEFER { fclose(file); };
-
- char readerBuffer[65536];
- rapidjson::FileReadStream stream(file, readerBuffer, sizeof(readerBuffer));
-
- rapidjson::Document root;
- root.ParseStream(stream);
-
- mWorldRenderer.LoadBindings(root);
- } while (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/30-game/App.hpp b/source/30-game/App.hpp
deleted file mode 100644
index c73c5a1..0000000
--- a/source/30-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/30-game/AppConfig.hpp b/source/30-game/AppConfig.hpp
deleted file mode 100644
index 794bee5..0000000
--- a/source/30-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/30-game/Camera.cpp b/source/30-game/Camera.cpp
deleted file mode 100644
index 39f0369..0000000
--- a/source/30-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/30-game/Camera.hpp b/source/30-game/Camera.hpp
deleted file mode 100644
index 7bf0a6c..0000000
--- a/source/30-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/30-game/CommonVertexIndex.cpp b/source/30-game/CommonVertexIndex.cpp
deleted file mode 100644
index e9a3ce6..0000000
--- a/source/30-game/CommonVertexIndex.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-#include "CommonVertexIndex.hpp"
-
-template <typename 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 <typename 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 <typename 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 <typename 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 <typename TVertex>
-static void AssignDepths(TVertex vertices[4], float z) {
- for (int i = 0; i < 4; ++i) {
- auto& vert = vertices[i];
- vert.z = z;
- }
-}
-
-template <typename 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 <typename 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/30-game/CommonVertexIndex.hpp b/source/30-game/CommonVertexIndex.hpp
deleted file mode 100644
index 7e6aa66..0000000
--- a/source/30-game/CommonVertexIndex.hpp
+++ /dev/null
@@ -1,77 +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{};
-inline RcPtr<VertexFormat> gVformatLines{};
-
-// 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/30-game/EditorAccessories.cpp b/source/30-game/EditorAccessories.cpp
deleted file mode 100644
index 821d41e..0000000
--- a/source/30-game/EditorAccessories.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "EditorAccessories.hpp"
-
-#include "Input.hpp"
-
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
-
-#include <imgui.h>
-
-void EditorKeyboardViewer::Show(bool* open) {
- ImGui::Begin("Keyboards", open);
-
- int count;
- GLFWkeyboard** keyboards = glfwGetKeyboards(&count);
-
- for (int i = 0; i < count; ++i) {
- GLFWkeyboard* keyboard = keyboards[i];
- auto attachment = static_cast<GlfwKeyboardAttachment*>(glfwGetKeyboardUserPointer(keyboard));
-
- ImGui::BulletText("%s", glfwGetKeyboardName(keyboard));
- ImGui::Indent();
- ImGui::Unindent();
- }
-
- ImGui::End();
-}
diff --git a/source/30-game/EditorAccessories.hpp b/source/30-game/EditorAccessories.hpp
deleted file mode 100644
index 56a8238..0000000
--- a/source/30-game/EditorAccessories.hpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-#include "Player.hpp"
-
-class EditorKeyboardViewer {
-public:
- void Show(bool* open = nullptr);
-};
diff --git a/source/30-game/EditorAttachment.hpp b/source/30-game/EditorAttachment.hpp
deleted file mode 100644
index 61b824b..0000000
--- a/source/30-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/30-game/EditorAttachmentImpl.cpp b/source/30-game/EditorAttachmentImpl.cpp
deleted file mode 100644
index b09c133..0000000
--- a/source/30-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/30-game/EditorAttachmentImpl.hpp b/source/30-game/EditorAttachmentImpl.hpp
deleted file mode 100644
index 53bcd37..0000000
--- a/source/30-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/30-game/EditorCommandPalette.cpp b/source/30-game/EditorCommandPalette.cpp
deleted file mode 100644
index 0e7b894..0000000
--- a/source/30-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/30-game/EditorCommandPalette.hpp b/source/30-game/EditorCommandPalette.hpp
deleted file mode 100644
index 101344d..0000000
--- a/source/30-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/30-game/EditorCore.hpp b/source/30-game/EditorCore.hpp
deleted file mode 100644
index 726f43e..0000000
--- a/source/30-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/30-game/EditorCorePrivate.cpp b/source/30-game/EditorCorePrivate.cpp
deleted file mode 100644
index 3efa33c..0000000
--- a/source/30-game/EditorCorePrivate.cpp
+++ /dev/null
@@ -1,1171 +0,0 @@
-#include "EditorCorePrivate.hpp"
-
-#include "App.hpp"
-#include "AppConfig.hpp"
-#include "EditorAccessories.hpp"
-#include "EditorAttachmentImpl.hpp"
-#include "EditorCommandPalette.hpp"
-#include "EditorUtils.hpp"
-#include "GameObject.hpp"
-#include "Mesh.hpp"
-#include "Player.hpp"
-#include "SceneThings.hpp"
-#include "VertexIndex.hpp"
-
-#include <ImGuiNotification.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 <rapidjson/document.h>
-#include <rapidjson/filereadstream.h>
-#include <rapidjson/filewritestream.h>
-#include <rapidjson/writer.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();
- }
-
- ImGui::SameLine();
- if (ImGui::Button("...")) {
- ImGui::OpenPopup("More Actions");
- }
- if (ImGui::BeginPopup("More Actions")) {
- if (ImGui::MenuItem("Rewrite all Ires to disk")) {
- IresManager::instance->OverwriteAllToDisk();
- }
- 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
-
-void SaveRendererBindings(const Renderer& renderer) {
- auto file = Utils::OpenCstdioFile("assets/GameRendererBindings.json", Utils::WriteTruncate);
- if (!file) return;
- DEFER {
- fclose(file);
- };
-
- char writerBuffer[65536];
- rapidjson::FileWriteStream stream(file, writerBuffer, sizeof(writerBuffer));
- rapidjson::Writer writer(stream);
-
- rapidjson::Document root(rapidjson::kObjectType);
- renderer.SaveBindings(root, root);
-
- root.Accept(writer);
-}
-} // 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)
- , mEdGuides(app, this) //
-{
- 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("Keyboard Viewer", nullptr, &mWindowVisible_KeyboardViewer);
- 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);
- }
-
- if (mWindowVisible_KeyboardViewer) {
- mEdKbViewer.Show(&mWindowVisible_KeyboardViewer);
- }
-
- 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() {
- using namespace ProjectBrussel_UNITY_ID;
-
- 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& renderer = *mApp->GetWorldRenderer();
- auto& camera = *mApp->GetActiveCamera();
-
- if (ImGui::CollapsingHeader("Renderer settings")) {
- if (ImGui::Checkbox("Draw shaded", &mRenderer_DrawShaded)) {
- renderer.SetRenderOption(Renderer::RO_Shading, mRenderer_DrawShaded);
- }
- if (ImGui::Checkbox("Draw wireframe", &mRenderer_DrawWireFrame)) {
- renderer.SetRenderOption(Renderer::RO_Wireframe, mRenderer_DrawWireFrame);
- }
-
- if (auto ires = Utils::SimpleIresReceptor<IresMaterial>(renderer.binding_WireframeMaterial->GetIres(), *this, IresObject::KD_Material)) {
- renderer.binding_WireframeMaterial.Attach(ires->GetInstance());
- SaveRendererBindings(renderer);
- }
- }
-
- if (ImGui::CollapsingHeader("Camera settings")) {
- // 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/30-game/EditorCorePrivate.hpp b/source/30-game/EditorCorePrivate.hpp
deleted file mode 100644
index 4071e7a..0000000
--- a/source/30-game/EditorCorePrivate.hpp
+++ /dev/null
@@ -1,136 +0,0 @@
-#pragma once
-
-#include "App.hpp"
-#include "EditorAccessories.hpp"
-#include "EditorAttachment.hpp"
-#include "EditorCommandPalette.hpp"
-#include "EditorCore.hpp"
-#include "EditorUtils.hpp"
-#include "EditorWorldGuides.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;
- EditorKeyboardViewer mEdKbViewer;
- EditorWorldGuides mEdGuides;
- 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 mWindowVisible_KeyboardViewer = false;
- bool mDragCam_Happening = false;
- bool mMoveCamKeyboard = false;
- bool mMoveCamScrollWheel = false;
- bool mRenderer_DrawShaded = true;
- bool mRenderer_DrawWireFrame = 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/30-game/EditorUtils.cpp b/source/30-game/EditorUtils.cpp
deleted file mode 100644
index 20caef7..0000000
--- a/source/30-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/30-game/EditorUtils.hpp b/source/30-game/EditorUtils.hpp
deleted file mode 100644
index 96e92d3..0000000
--- a/source/30-game/EditorUtils.hpp
+++ /dev/null
@@ -1,84 +0,0 @@
-#pragma once
-
-#include "EditorCore.hpp"
-#include "ImGuiGuizmo.hpp"
-#include "Ires.hpp"
-
-#include <Color.hpp>
-#include <Metadata.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);
-
-// TODO get kind from T
-template <typename T>
-T* SimpleIresReceptor(T* existing, IEditor& editor, IresObject::Kind kind) {
- if (existing) {
- existing->ShowReference(editor);
- } else {
- IresObject::ShowReferenceNull(editor);
- }
- if (ImGui::BeginDragDropTarget()) {
- if (auto payload = ImGui::AcceptDragDropPayload(Metadata::EnumToString(kind).data())) {
- return *static_cast<T* const*>(payload->Data);
- }
- ImGui::EndDragDropTarget();
- }
- return nullptr;
-}
-
-} // namespace Utils
diff --git a/source/30-game/EditorWorldGuides.cpp b/source/30-game/EditorWorldGuides.cpp
deleted file mode 100644
index f0d66b8..0000000
--- a/source/30-game/EditorWorldGuides.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "EditorWorldGuides.hpp"
-
-#include "App.hpp"
-#include "CommonVertexIndex.hpp"
-#include "GraphicsTags.hpp"
-#include "VertexIndex.hpp"
-
-EditorWorldGuides::EditorWorldGuides(App* app, IEditor* editor)
- : mApp{ app }
- , mEditor{ editor } //
-{
- using namespace Tags;
-
- mRoGrid.SetFormat(gVformatLines.Get(), IT_16Bit);
- mRoGrid.SetMaterial(gDefaultMaterial.Get());
- mRoGrid.RebuildIfNecessary();
-
- mRoDebugSkybox.SetFormat(gVformatStandard.Get(), IT_16Bit);
- mRoDebugSkybox.SetMaterial(gDefaultMaterial.Get());
- mRoDebugSkybox.RebuildIfNecessary();
-}
-
-void EditorWorldGuides::Update() {
- auto& camera = *mApp->GetActiveCamera();
- // TODO
-}
diff --git a/source/30-game/EditorWorldGuides.hpp b/source/30-game/EditorWorldGuides.hpp
deleted file mode 100644
index 0dfdea2..0000000
--- a/source/30-game/EditorWorldGuides.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include "Renderer.hpp"
-
-class EditorWorldGuides {
-private:
- App* mApp;
- IEditor* mEditor;
- RenderObject mRoGrid;
- RenderObject mRoDebugSkybox;
-
-public:
- EditorWorldGuides(App* app, IEditor* editor);
-
- void Update();
-};
diff --git a/source/30-game/FuzzyMatch.cpp b/source/30-game/FuzzyMatch.cpp
deleted file mode 100644
index 0ab604d..0000000
--- a/source/30-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/30-game/FuzzyMatch.hpp b/source/30-game/FuzzyMatch.hpp
deleted file mode 100644
index 7a26b7e..0000000
--- a/source/30-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/30-game/GameObject.cpp b/source/30-game/GameObject.cpp
deleted file mode 100644
index 3b15111..0000000
--- a/source/30-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/30-game/GameObject.hpp b/source/30-game/GameObject.hpp
deleted file mode 100644
index 40c52e7..0000000
--- a/source/30-game/GameObject.hpp
+++ /dev/null
@@ -1,107 +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 {
- // clang-format off
- BRUSSEL_ENUM(ToString, FromString, RemovePrefix KD_, AddPrefix GameObject, ExcludeHeuristics)
- // clang-format on
-
- KD_Generic,
- KD_Player,
- KD_SimpleGeometry,
- KD_Building,
- KD_LevelWrapper,
- KD_COUNT,
-};
-} // namespace Tags
-
-class GameWorld;
-class GameObject {
- BRUSSEL_CLASS(InheritanceHiearchy)
-
-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/30-game/GraphicsTags.cpp b/source/30-game/GraphicsTags.cpp
deleted file mode 100644
index 83d52f8..0000000
--- a/source/30-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 0;
-}
-
-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 0;
- }
-}
-
-#include <generated/GraphicsTags.gs.inl>
diff --git a/source/30-game/GraphicsTags.hpp b/source/30-game/GraphicsTags.hpp
deleted file mode 100644
index f9628b2..0000000
--- a/source/30-game/GraphicsTags.hpp
+++ /dev/null
@@ -1,111 +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 {
- // clang-format off
- BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics)
- // clang-format on
-
- /// 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,
-};
-
-enum VertexElementType {
- // clang-format off
- BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics)
- // clang-format on
-
- 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;
-
-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 {
- // clang-format off
- BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics)
- // clang-format on
-
- TF_Linear,
- TF_Nearest,
-};
-
-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/30-game/Image.cpp b/source/30-game/Image.cpp
deleted file mode 100644
index 3673acc..0000000
--- a/source/30-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/30-game/Image.hpp b/source/30-game/Image.hpp
deleted file mode 100644
index c577c24..0000000
--- a/source/30-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/30-game/Input.cpp b/source/30-game/Input.cpp
deleted file mode 100644
index 9f304ff..0000000
--- a/source/30-game/Input.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "Input.hpp"
-
-#include <cassert>
-
-GLFWkeyboard* InputState::FindKeyboard(std::string_view name) {
- assert(false);
- // TODO
-}
-
-GlfwKeyboardAttachment* InputState::ConnectKeyboard(GLFWkeyboard* keyboard) {
- auto attachment = new GlfwKeyboardAttachment();
- glfwSetKeyboardUserPointer(keyboard, attachment);
-
- return attachment;
-}
-
-void InputState::DisconnectKeyboard(GLFWkeyboard* keyboard) {
- InputState::instance->DisconnectKeyboard(keyboard);
- auto attachment = static_cast<GlfwKeyboardAttachment*>(glfwGetKeyboardUserPointer(keyboard));
- // Defensive: this GLFWkeyboard* will be deleted after this callback ends anyways
- glfwSetKeyboardUserPointer(keyboard, nullptr);
- delete attachment;
-}
diff --git a/source/30-game/Input.hpp b/source/30-game/Input.hpp
deleted file mode 100644
index feb50f0..0000000
--- a/source/30-game/Input.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
-
-#include <string_view>
-#include <utility>
-#include <vector>
-
-/// Extra data attached to a GLFWkeyboard object
-struct GlfwKeyboardAttachment {
-};
-
-class InputState {
-public:
- static inline InputState* instance = nullptr;
-
-public:
- GLFWkeyboard* FindKeyboard(std::string_view name);
-
- GlfwKeyboardAttachment* ConnectKeyboard(GLFWkeyboard* keyboard);
- void DisconnectKeyboard(GLFWkeyboard* keyboard);
-};
diff --git a/source/30-game/Ires.cpp b/source/30-game/Ires.cpp
deleted file mode 100644
index 0529395..0000000
--- a/source/30-game/Ires.cpp
+++ /dev/null
@@ -1,436 +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) {
- std::unique_ptr<IresObject> ires;
- Uid uid;
- for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
- if (it->name == "Type"_rj_sv) {
- if (it->value.GetType() != rapidjson::kStringType)
- return nullptr;
- auto kind = Metadata::EnumFromString<Kind>(rapidjson::AsStringView(it->value));
- if (!kind.has_value())
- return nullptr;
- if ((ires = Create(*kind)) == nullptr)
- return nullptr;
- } else if (it->name == "Uid"_rj_sv) {
- // FIXME this needs to do type checks
- uid.Read(it->value);
- }
- }
- if (ires == nullptr || uid.IsNull())
- return nullptr;
-
- ires->mUid = uid;
- 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.\n", 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.\n", 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);
- // We no longer need this after disabling BRUSSEL_Uid_WRITE_USE_ARRAY
-// writer.SetFormatOptions(rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
-#if BRUSSEL_Uid_WRITE_USE_ARRAY
-# warning "Writing Uid in array format but single line formatting isn't enabled, this might cause excessively long ires files."
-#endif
- root.Accept(writer);
-}
-
-void IresManager::OverwriteAllToDisk() {
- for (const auto& [DISCARD, ires] : mObjByUid) {
- Save(ires.Get());
- }
-}
-
-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/30-game/Ires.hpp b/source/30-game/Ires.hpp
deleted file mode 100644
index e2e79bd..0000000
--- a/source/30-game/Ires.hpp
+++ /dev/null
@@ -1,130 +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 {
- // clang-format off
- BRUSSEL_ENUM(ToString, FromString, RemovePrefix KD_, AddPrefix Ires, ExcludeHeuristics)
- // clang-format on
-
- KD_Texture,
- KD_Shader,
- KD_Material,
- KD_SpriteFiles,
- KD_Spritesheet,
- KD_COUNT,
-};
-} // 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);
-
- void OverwriteAllToDisk();
-
- const auto& GetObjects() const { return mObjByUid; }
- virtual IresObject* FindIres(const Uid& uid) const override;
-};
-
-#include <generated/Ires.gh.inl>
diff --git a/source/30-game/Level.cpp b/source/30-game/Level.cpp
deleted file mode 100644
index 076e5d5..0000000
--- a/source/30-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/30-game/Level.hpp b/source/30-game/Level.hpp
deleted file mode 100644
index c030b8e..0000000
--- a/source/30-game/Level.hpp
+++ /dev/null
@@ -1,95 +0,0 @@
-#pragma once
-
-#include "EditorCore.hpp"
-#include "GameObject.hpp"
-
-#include <MacrosCodegen.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 {
- BRUSSEL_CLASS()
-
-private:
- RcPtr<Level> mLevel;
-
-public:
- LevelWrapperObject(GameWorld* world);
- ~LevelWrapperObject() override;
-
- Level* GetBoundLevel() const;
- void SetBoundLevel(Level* level);
-};
diff --git a/source/30-game/Material.cpp b/source/30-game/Material.cpp
deleted file mode 100644
index 4443ae5..0000000
--- a/source/30-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 <typename 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/30-game/Material.hpp b/source/30-game/Material.hpp
deleted file mode 100644
index f1cd7dd..0000000
--- a/source/30-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/30-game/Mesh.cpp b/source/30-game/Mesh.cpp
deleted file mode 100644
index 244e2e3..0000000
--- a/source/30-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/30-game/Mesh.hpp b/source/30-game/Mesh.hpp
deleted file mode 100644
index f86fd55..0000000
--- a/source/30-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/30-game/Player.cpp b/source/30-game/Player.cpp
deleted file mode 100644
index 34c4549..0000000
--- a/source/30-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/30-game/Player.hpp b/source/30-game/Player.hpp
deleted file mode 100644
index 5a6bab7..0000000
--- a/source/30-game/Player.hpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#pragma once
-
-#include "GameObject.hpp"
-#include "Material.hpp"
-#include "Sprite.hpp"
-
-#include <MacrosCodegen.hpp>
-#include <RcPtr.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 {
- BRUSSEL_CLASS()
-
-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/30-game/Renderer.cpp b/source/30-game/Renderer.cpp
deleted file mode 100644
index 0454efe..0000000
--- a/source/30-game/Renderer.cpp
+++ /dev/null
@@ -1,256 +0,0 @@
-#include "Renderer.hpp"
-
-#include "GameObject.hpp"
-
-#include <RapidJsonHelper.hpp>
-
-#include <rapidjson/document.h>
-#include <cassert>
-#include <glm/gtc/matrix_transform.hpp>
-#include <glm/gtc/quaternion.hpp>
-#include <glm/gtx/quaternion.hpp>
-#include <string_view>
-
-using namespace std::literals;
-
-RenderObject::RenderObject()
- : mVao{ 0 } {
-}
-
-RenderObject::~RenderObject() {
- DeleteGLObjects();
-}
-
-GLuint RenderObject::GetGLVao() const {
- return mVao;
-}
-
-void RenderObject::RebuildIfNecessary() {
- if (mVao != 0) {
- 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(0);
-}
-
-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 != 0) {
- glDeleteVertexArrays(1, &mVao);
- mVao = 0;
- }
-}
-
-Renderer::Renderer()
- : binding_WireframeMaterial{ gDefaultMaterial } //
-{
- mRenderOptions[RO_Shading] = true;
- mRenderOptions[RO_Wireframe] = false;
-}
-
-void Renderer::LoadBindings(const rapidjson::Value& bindings) {
- if (auto rvWireframe = rapidjson::GetProperty(bindings, "WireframeMaterial"sv)) {
- Uid uidWireframe;
- uidWireframe.Read(*rvWireframe);
- // TODO don't assume
- binding_WireframeMaterial.Attach(((IresMaterial*)IresManager::instance->FindIres(uidWireframe))->GetInstance());
- }
-}
-
-void Renderer::SaveBindings(rapidjson::Value& into, rapidjson::Document& root) const {
- if (auto ires = binding_WireframeMaterial->GetIres()) {
- into.AddMember("WireframeMaterial", ires->GetUid().Write(root), root.GetAllocator());
- }
-}
-
-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);
-
- // Desired order: proj * view * (translate * rotate * scale) * vec
- // <----- order of application <----- ^^^ input
- glm::mat4 objectMatrix(1.0f);
- objectMatrix = glm::translate(objectMatrix, gameObject->GetPos());
- objectMatrix *= glm::toMat4(gameObject->GetRotation());
- objectMatrix = glm::scale(objectMatrix, gameObject->GetScale());
- auto mvpMatrix = mFrame.matrixProj * mFrame.matrixView * objectMatrix;
-
- if (GetRenderOption(RO_Shading)) {
- // 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) {
- glUniformMatrix4fv(shader->autofill_Transform, 1, GL_FALSE, &mvpMatrix[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);
- }
- }
-
- if (GetRenderOption(RO_Wireframe)) {
- auto& mat = *binding_WireframeMaterial;
- auto& shader = *mat.GetShader();
- auto& shaderInfo = shader.GetInfo();
-
- glUseProgram(shader.GetProgram());
- mat.UseUniforms();
-
- // TODO reduce calls with consecutive wireframe setting
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- for (size_t i = 0; i < count; ++i) {
- auto& object = objects[i];
-
- auto& vBindings = object.GetVertexBufferBindings().bindings;
- auto vf = object.GetVertexFormat();
-
- // Setup vertex buffers
- for (auto& elm : vf->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),
- vf->vertexSize,
- (void*)(uintptr_t)elm.offset);
- }
-
- // Setup index buffer
- auto indexBuffer = object.GetIndexBuffer();
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer->handle);
-
- glDrawElements(GL_TRIANGLES, indexBuffer->count, indexBuffer->GetIndexTypeGL(), 0);
- }
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-
- return;
- }
-}
-
-bool Renderer::GetRenderOption(RenderOption option) const {
- return mRenderOptions[option];
-}
-
-void Renderer::SetRenderOption(RenderOption option, bool flag) {
- mRenderOptions[option] = flag;
-}
diff --git a/source/30-game/Renderer.hpp b/source/30-game/Renderer.hpp
deleted file mode 100644
index 856dc31..0000000
--- a/source/30-game/Renderer.hpp
+++ /dev/null
@@ -1,92 +0,0 @@
-#pragma once
-
-#include "Camera.hpp"
-#include "Material.hpp"
-#include "VertexIndex.hpp"
-
-#include <RcPtr.hpp>
-
-#include <glad/glad.h>
-#include <rapidjson/fwd.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 {
-public:
- // NOTE: see Renderer constructor for default values
- enum RenderOption {
- /// Render everything directly using objects' provided material and vertex/index data.
- RO_Shading,
- /// Render everything as wireframes using provided position data.
- RO_Wireframe,
- RO_COUNT,
- };
-
-public:
- RcPtr<Material> binding_WireframeMaterial;
-
-private:
- RendererFrameInfo mFrame;
- bool mInsideFrame = false;
- bool mRenderOptions[RO_COUNT] = {};
-
-public:
- Renderer();
-
- void LoadBindings(const rapidjson::Value& bindings);
- void SaveBindings(rapidjson::Value& into, rapidjson::Document& root) const;
-
- 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();
-
- bool GetRenderOption(RenderOption option) const;
- void SetRenderOption(RenderOption option, bool flag);
-};
diff --git a/source/30-game/SceneThings.cpp b/source/30-game/SceneThings.cpp
deleted file mode 100644
index 3fa0436..0000000
--- a/source/30-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/30-game/SceneThings.hpp b/source/30-game/SceneThings.hpp
deleted file mode 100644
index 761eb59..0000000
--- a/source/30-game/SceneThings.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include "Color.hpp"
-#include "GameObject.hpp"
-#include "Renderer.hpp"
-
-#include <MacrosCodegen.hpp>
-
-#include <glm/glm.hpp>
-#include <vector>
-
-class SimpleGeometryObject : public GameObject {
- BRUSSEL_CLASS()
-
-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 {
- BRUSSEL_CLASS()
-
-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/30-game/Shader.cpp b/source/30-game/Shader.cpp
deleted file mode 100644
index 9bf2e0e..0000000
--- a/source/30-game/Shader.cpp
+++ /dev/null
@@ -1,711 +0,0 @@
-#include "Shader.hpp"
-
-#include "AppConfig.hpp"
-
-#include <Metadata.hpp>
-#include <RapidJsonHelper.hpp>
-#include <ScopeGuard.hpp>
-#include <Utils.hpp>
-
-#include <fmt/format.h>
-#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)));
-}
-
-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);
-}
-
-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;
-
- default: break;
- }
-
- 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;
-
- default: break;
- }
-
- return false;
-}
-
-std::variant<ShaderMathVariable, ShaderSamplerVariable> CreateVariable(GLenum type, GLuint loc) {
- GLenum scalarType;
- int width;
- int height;
- if (QueryMathInfo(type, scalarType, width, height)) {
- ShaderMathVariable res;
- res.location = loc;
- res.scalarType = type;
- res.width = width;
- res.height = height;
- return res;
- }
-
- if (QuerySamplerInfo(type)) {
- ShaderSamplerVariable res;
- res.location = loc;
- res.samplerType = type;
- return res;
- }
-
- throw std::runtime_error(fmt::format("Unknown OpenGL shader uniform type {}", type));
-}
-} // 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;
-}
-
-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) {
- std::visit([](auto&& v) { v.ShowInfo(); }, uniform);
- }
- 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);
- json_dto::json_output_t out( value, root.GetAllocator() );
- out << mInstance->mInfo;
-}
-
-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/30-game/Shader.hpp b/source/30-game/Shader.hpp
deleted file mode 100644
index cb980cd..0000000
--- a/source/30-game/Shader.hpp
+++ /dev/null
@@ -1,180 +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 <json_dto/pub.hpp>
-#include <memory>
-#include <string_view>
-#include <variant>
-#include <vector>
-
-// TODO move to variable after pattern matching is in the language
-
-// Forward declarations
-class Shader;
-class IresShader;
-
-struct ShaderMathVariable {
- std::string name;
- GLuint location;
- Tags::VertexElementSemantic semantic = Tags::VES_Generic;
- GLenum scalarType;
- int width;
- int height;
- int arrayLength = 1;
-
- void ShowInfo() const;
-
- template <typename TJsonIo>
- void json_io(TJsonIo& io) {
- io& json_dto::mandatory("Name", name);
- io& json_dto::mandatory("Semantic", static_cast<int>(semantic));
- io& json_dto::mandatory("ScalarType", scalarType);
- io& json_dto::mandatory("Width", width);
- io& json_dto::mandatory("Height", height);
- io& json_dto::optional("ArrayLength", arrayLength, 1);
- }
-};
-
-struct ShaderSamplerVariable {
- std::string name;
- GLuint location;
- Tags::VertexElementSemantic semantic = Tags::VES_Generic;
- GLenum samplerType;
- int arrayLength = 1;
-
- void ShowInfo() const;
-
- template <typename TJsonIo>
- void json_io(TJsonIo& io) {
- io& json_dto::mandatory("Name", name);
- io& json_dto::mandatory("Semantic", static_cast<int>(semantic));
- io& json_dto::mandatory("SamplerType", samplerType);
- io& json_dto::optional("ArrayLength", arrayLength, 1);
- }
-};
-
-struct ShaderThingId {
- enum Kind {
- KD_Input,
- KD_Output,
- KD_Uniform,
- };
-
- Kind kind;
- int index;
-};
-
-struct ShaderInfo {
- robin_hood::unordered_map<std::string, ShaderThingId, StringHash, StringEqual> things;
- std::vector<ShaderMathVariable> inputs;
- std::vector<ShaderMathVariable> outputs;
- std::vector<std::variant<ShaderMathVariable, ShaderSamplerVariable>> uniforms;
-
- // Find the first variable with the matching semantic
- GLuint FindInputLocation(Tags::VertexElementSemantic semantic);
- GLuint FindOutputLocation(Tags::VertexElementSemantic semantic);
-
- template <typename TJsonIo>
- void json_io(TJsonIo& io) {
- io& json_dto::mandatory("Inputs", inputs);
- io& json_dto::mandatory("Outputs", outputs);
- // TODO make json_dto support std::variant
-// io& json_dto::mandatory("Uniforms", uniforms);
- }
-};
-
-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/30-game/Sprite.cpp b/source/30-game/Sprite.cpp
deleted file mode 100644
index 2b4923c..0000000
--- a/source/30-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/30-game/Sprite.hpp b/source/30-game/Sprite.hpp
deleted file mode 100644
index e163a01..0000000
--- a/source/30-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/30-game/Texture.cpp b/source/30-game/Texture.cpp
deleted file mode 100644
index 6fa7c8a..0000000
--- a/source/30-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/30-game/Texture.hpp b/source/30-game/Texture.hpp
deleted file mode 100644
index 108dfa7..0000000
--- a/source/30-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/30-game/VertexIndex.cpp b/source/30-game/VertexIndex.cpp
deleted file mode 100644
index ac68289..0000000
--- a/source/30-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/30-game/VertexIndex.hpp b/source/30-game/VertexIndex.hpp
deleted file mode 100644
index 2d65617..0000000
--- a/source/30-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/30-game/World.cpp b/source/30-game/World.cpp
deleted file mode 100644
index 83b9a10..0000000
--- a/source/30-game/World.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-#include "World.hpp"
-
-#include "GameObject.hpp"
-#include "PodVector.hpp"
-
-#include <glad/glad.h>
-
-namespace ProjectBrussel_UNITY_ID {
-template <typename 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);
- }
-}
-} // 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() {
- using namespace ProjectBrussel_UNITY_ID;
-
- if (mAwakened) {
- return;
- }
-
- CallGameObjectRecursive(mRoot, [](GameObject* obj) { obj->Awaken(); });
- mAwakened = true;
-}
-
-void GameWorld::Resleep() {
- using namespace ProjectBrussel_UNITY_ID;
-
- if (!mAwakened) {
- return;
- }
-
- CallGameObjectRecursive(mRoot, [](GameObject* obj) { obj->Resleep(); });
- mAwakened = false;
-}
-
-void GameWorld::Update() {
- using namespace 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/30-game/World.hpp b/source/30-game/World.hpp
deleted file mode 100644
index 288142e..0000000
--- a/source/30-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/30-game/main.cpp b/source/30-game/main.cpp
deleted file mode 100644
index 30ba9a6..0000000
--- a/source/30-game/main.cpp
+++ /dev/null
@@ -1,545 +0,0 @@
-#include "App.hpp"
-
-#include "AppConfig.hpp"
-#include "CommonVertexIndex.hpp"
-#include "ImGuiGuizmo.hpp"
-#include "Input.hpp"
-#include "Ires.hpp"
-#include "Level.hpp"
-#include "Log.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 GlfwKeyboardCallback(GLFWkeyboard* keyboard, int event) {
- if (InputState::instance == nullptr) {
- // Called before initialization, skipping because we'll do a collect pass anyways when initializing
- return;
- }
-
- switch (event) {
- case GLFW_CONNECTED: {
- InputState::instance->ConnectKeyboard(keyboard);
- } break;
-
- case GLFW_DISCONNECTED: {
- InputState::instance->DisconnectKeyboard(keyboard);
- } break;
- }
-}
-
-void OpenGLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
- fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message);
-}
-
-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);
- }
-}
-
-// For platform data path selection below
-// 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;
-
-#if BRUSSEL_DEV_ENV
- Log::gDefaultBuffer.messages.resize(1024);
- Log::gDefaultBufferId = Log::RegisterBuffer(Log::gDefaultBuffer);
-#endif
-
- constexpr auto kOpenGLDebug = "opengl-debug";
- constexpr auto kImGuiBackend = "imgui-backend";
- constexpr auto kGameDataDir = "game-data-dir";
- constexpr auto kGameAssetDir = "game-asset-dir";
-
- cxxopts::Options options(std::string(AppConfig::kAppName), "");
- // clang-format off
- options.add_options()
- (kOpenGLDebug, "Enable OpenGL debugging messages.")
- (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 {
- AppConfig::assetDir = ".";
- AppConfig::assetDirPath = fs::path(".");
- }
-
- 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 {
-#if BRUSSEL_DEV_ENV
- AppConfig::dataDir = ".";
- AppConfig::dataDirPath = fs::path(".");
-#else
-// In a regular build, use default platform data paths
-# if defined(_WIN32)
- fs::path dataDirPath;
-
- PWSTR path = nullptr;
- HRESULT hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, nullptr, &path);
- if (SUCCEEDED(hr)) {
- dataDirPath = fs::path(path) / AppConfig::kAppName;
- CoTaskMemFree(path);
-
- fs::create_directories(dataDirPath);
- } 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 dataDirPath = fs::path("~/Library/Application Support/") / AppConfig::kAppName;
- fs::create_directories(dataDirPath);
-# elif defined(__linux__)
- auto dataDirPath = GetEnvVar("XDG_DATA_HOME", "~/.local/share") / AppConfig::kAppName;
- fs::create_directories(dataDirPath);
-# endif
- AppConfig::dataDir = dataDirPath.string();
- AppConfig::dataDirPath = dataDirPath;
-#endif
- }
-
- if (!glfwInit()) {
- return -1;
- }
-
- glfwSetErrorCallback(&GlfwErrorCallback);
- glfwSetKeyboardCallback(&GlfwKeyboardCallback);
-
- 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, AppConfig::kAppNameC, 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);
-
- if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
- return -3;
- }
-
-#if defined(BRUSSEL_DEV_ENV)
- auto glVersionString = glGetString(GL_VERSION);
-
- int glMajorVersion;
- glGetIntegerv(GL_MAJOR_VERSION, &glMajorVersion);
- int glMinorVersion;
- glGetIntegerv(GL_MINOR_VERSION, &glMinorVersion);
-
- printf("OpenGL version (via glGetString(GL_VERSION)): %s\n", glVersionString);
- printf("OpenGL version (via glGetIntegerv() with GL_MAJOR_VERSION and GL_MINOR_VERSION): %d.%d\n", glMajorVersion, glMinorVersion);
-#endif
-
- bool useOpenGLDebug = args[kOpenGLDebug].as<bool>();
- if (useOpenGLDebug) {
- printf("Using OpenGL debugging\n --%s", kOpenGLDebug);
-
- // TODO check extension KHR_debug availability
- // TODO conan glad is not including any extensions
- // NOTE: KHR_debug is a core extension, which means it may be available in lower version even though the feature is added in 4.3
-
- glEnable(GL_DEBUG_OUTPUT);
- glDebugMessageCallback(&OpenGLDebugCallback, 0);
- }
-
- 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();
- }
-
- InputState::instance = new InputState();
- {
- int count;
- GLFWkeyboard** list = glfwGetKeyboards(&count);
- for (int i = 0; i < count; ++i) {
- GLFWkeyboard* keyboard = list[i];
- InputState::instance->ConnectKeyboard(keyboard);
- }
- }
-
- 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,
- });
-
- gVformatLines.Attach(new VertexFormat());
- gVformatLines->AddElement(VertexElementFormat{
- .bindingIndex = 0,
- .type = VET_Float3,
- .semantic = VES_Position,
- });
- gVformatLines->AddElement(VertexElementFormat{
- .bindingIndex = 0,
- .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;
-}