diff --git a/.gitmodules b/.gitmodules
index 73bae7338..8a0787edd 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -65,3 +65,14 @@
[submodule "lib/libdeflate"]
path = lib/libdeflate
url = https://github.com/cuberite/libdeflate
+[submodule "lib/libpdw"]
+ path = lib/libpdw
+ url = https://github.com/oneechanhax/libpdw
+ branch = async
+[submodule "lib/libglez"]
+ path = lib/libglez
+ url = https://github.com/oneechanhax/libglez
+ branch = rainbow
+[submodule "lib/libhydride"]
+ path = lib/libhydride
+ url = https://github.com/oneechanhax/libhydride
diff --git a/CMake/AddDependencies.cmake b/CMake/AddDependencies.cmake
index 29a2b74f5..d8e1b17fb 100644
--- a/CMake/AddDependencies.cmake
+++ b/CMake/AddDependencies.cmake
@@ -31,6 +31,9 @@ function(build_dependencies)
# Enumerate all submodule libraries
# SQLiteCpp needs to be included before sqlite so the lsqlite target is available:
set(DEPENDENCIES expat fmt jsoncpp libdeflate libevent mbedtls SQLiteCpp)
+ if (BUILD_CLIENT)
+ set(DEPENDENCIES ${DEPENDENCIES} libglez libhydride libpdw)
+ endif()
foreach(DEPENDENCY ${DEPENDENCIES})
# Check that the libraries are present:
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/lib/${DEPENDENCY}/CMakeLists.txt")
@@ -42,6 +45,7 @@ function(build_dependencies)
# (mbedTLS also has test and example programs in their CMakeLists.txt, we don't want those):
add_subdirectory("lib/${DEPENDENCY}" EXCLUDE_FROM_ALL)
endforeach()
+
endfunction()
function(link_dependencies TARGET)
@@ -80,4 +84,24 @@ function(link_dependencies TARGET)
if(${CMAKE_SYSTEM_NAME} MATCHES FreeBSD)
target_link_libraries(${TARGET} PRIVATE kvm)
endif()
+
+ if(BUILD_CLIENT)
+ set(OpenGL_GL_PREFERENCE "GLVND")
+ find_package(PNG REQUIRED) # We need to link all this again since we prebuilt them staticly
+ find_package(GLEW REQUIRED)
+ find_package(OpenGL REQUIRED)
+ find_package(X11 REQUIRED)
+ find_package(Freetype REQUIRED)
+
+ target_include_directories(
+ ${TARGET} SYSTEM PRIVATE
+ lib/libpdw/include
+ )
+
+ target_link_libraries(${TARGET} PRIVATE
+ libpdw
+ hydride
+ glez
+ ${PNG_LIBRARIES} GL GLU GLEW ${FREETYPE_LIBRARIES} ${X11_X11_LIB} ${X11_Xext_LIB} ${X11_Xfixes_LIB})
+ endif()
endfunction()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1dca78260..37579baea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-# This is the top-level CMakeLists.txt file for the Cuberite project
+
#
# Use CMake to generate the build files for your platform
@@ -11,6 +11,7 @@ project(
LANGUAGES C CXX
)
+option(BUILD_CLIENT "Sets up the build to create a client rather than a server. This is in tesing and will result in a hybrid server/client setup." OFF)
option(BUILD_TOOLS "Sets up additional executables to be built along with the server" OFF)
option(BUILD_UNSTABLE_TOOLS "Sets up yet more executables to be built, these can be broken and generally are obsolete" OFF)
option(NO_NATIVE_OPTIMIZATION "Disables CPU-specific optimisations for the current machine, allows use on other CPUs of the same platform" OFF)
diff --git a/client-resources/logo.png b/client-resources/logo.png
new file mode 100644
index 000000000..f315e862e
Binary files /dev/null and b/client-resources/logo.png differ
diff --git a/client-resources/logo.xcf b/client-resources/logo.xcf
new file mode 100644
index 000000000..9849836c7
Binary files /dev/null and b/client-resources/logo.xcf differ
diff --git a/client-resources/opensans.ttf b/client-resources/opensans.ttf
new file mode 100644
index 000000000..fd79d43be
Binary files /dev/null and b/client-resources/opensans.ttf differ
diff --git a/lib/libglez b/lib/libglez
new file mode 160000
index 000000000..221db8e97
--- /dev/null
+++ b/lib/libglez
@@ -0,0 +1 @@
+Subproject commit 221db8e9721510449e7f14a644410b8c7aead09c
diff --git a/lib/libhydride b/lib/libhydride
new file mode 160000
index 000000000..30659e187
--- /dev/null
+++ b/lib/libhydride
@@ -0,0 +1 @@
+Subproject commit 30659e1877f036b090bafc1a1e76170b88ab9fda
diff --git a/lib/libpdw b/lib/libpdw
new file mode 160000
index 000000000..9cbc0a088
--- /dev/null
+++ b/lib/libpdw
@@ -0,0 +1 @@
+Subproject commit 9cbc0a088d5e65a498c4ddab2beeb8643a76a865
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5a882d940..ec85df253 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -152,6 +152,7 @@ set(FOLDERS
Generating HTTP Items mbedTLS++ Mobs Noise
OSSupport Physics Protocol Registries Simulator
Simulator/IncrementalRedstoneSimulator UI WorldStorage
+ nyqubel-client
)
# Add all child source directories:
diff --git a/src/Root.cpp b/src/Root.cpp
index 3d072a339..f4a3999b7 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -67,7 +67,9 @@
#include "Logger.h"
#include "ClientHandle.h"
-
+#ifdef NYQUBEL_CLIENT
+#include "nyqubel-client/client.hpp"
+#endif
@@ -145,6 +147,12 @@ bool cRoot::Run(cSettingsRepositoryInterface & a_OverridesRepo)
LoadGlobalSettings();
+#ifdef NYQUBEL_CLIENT
+ start_nyqubel_client();
+
+ return s_NextState == NextState::Restart;
+#endif
+
LOG("Creating new server instance...");
m_Server = new cServer();
diff --git a/src/nyqubel-client/CMakeLists.txt b/src/nyqubel-client/CMakeLists.txt
new file mode 100644
index 000000000..82b058a38
--- /dev/null
+++ b/src/nyqubel-client/CMakeLists.txt
@@ -0,0 +1,14 @@
+if(BUILD_CLIENT)
+
+ target_sources(
+ ${CMAKE_PROJECT_NAME} PRIVATE
+ client.cpp
+ client.hpp
+
+ util/geometry.cpp
+ util/geometry.hpp
+ )
+
+ target_compile_definitions(${CMAKE_PROJECT_NAME} PUBLIC "-DNYQUBEL_CLIENT=1")
+
+endif()
diff --git a/src/nyqubel-client/client.cpp b/src/nyqubel-client/client.cpp
new file mode 100644
index 000000000..e9fa49a63
--- /dev/null
+++ b/src/nyqubel-client/client.cpp
@@ -0,0 +1,953 @@
+
+/*
+ * Libpdw: Primitives Done Well!
+ * Copyright (C) 2022 Rebekah Rowe
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+// xorg conflict
+#undef RootWindow
+
+#include "libpdw/gui/widgets/slider.hpp"
+#include "libpdw/gui/widgets/titlebar.hpp"
+#include "libpdw/gui/canvas.hpp"
+#include "libpdw/gui/widgets/basebutton.hpp"
+#include "libpdw/gui/widgets/basewindow.hpp"
+#include "libpdw/gui/widgets/checkbox.hpp"
+#include "libpdw/gui/widgets/dropdown.hpp"
+
+#include "libpdw/gui/ncc/logo.hpp"
+
+namespace x11 {
+
+using RawKey = decltype(XK_2);
+constexpr std::pair key_dict[] = { { "0", XK_0 }, { "1", XK_1 }, { "2", XK_2 },
+ { "3", XK_3 }, { "4", XK_4 }, { "5", XK_5 },
+ { "6", XK_6 }, { "7", XK_7 }, { "8", XK_8 },
+ { "9", XK_9 }, { "a", XK_A }, { "b", XK_B },
+ { "c", XK_C }, { "d", XK_D }, { "e", XK_E },
+ { "f", XK_F }, { "g", XK_G }, { "h", XK_H },
+ { "i", XK_I }, { "j", XK_J }, { "k", XK_K },
+ { "l", XK_L }, { "m", XK_M }, { "n", XK_N },
+ { "o", XK_O }, { "p", XK_P }, { "q", XK_Q },
+ { "r", XK_R }, { "s", XK_S }, { "t", XK_T },
+ { "u", XK_U }, { "v", XK_V }, { "w", XK_W },
+ { "x", XK_X }, { "y", XK_Y }, { "z", XK_Z },
+
+ { "escape", XK_Escape }, { "{", XK_bracketleft },
+ { "}", XK_bracketright }, { ";", XK_semicolon },
+ { "'", XK_apostrophe }, { ",", XK_apostrophe },
+ { ".", XK_period }, { "/", XK_slash },
+ { "\\", XK_backslash }, { "minus", XK_minus },
+ { "=", XK_equal }, { "return", XK_Return },
+ { "space", XK_space }, { "backspace", XK_BackSpace },
+ { "tab", XK_Tab }, { "capslock", XK_Caps_Lock },
+
+ { "insert", XK_Insert },
+ { "delete", XK_Delete },
+ { "home", XK_Home }, { "end", XK_End },
+ { "pageup", XK_Page_Up }, { "pagedown", XK_Page_Down },
+
+ { "shift_r", XK_Shift_L }, { "shift_r", XK_Shift_R },
+ { "alt_l", XK_Alt_L }, { "alt_r", XK_Alt_R },
+ { "control_l", XK_Control_L }, { "control_l", XK_Control_R },
+
+ { "0_pad", XK_KP_0 }, { "1_pad", XK_KP_1 }, { "2_pad", XK_KP_2 },
+ { "3_pad", XK_KP_3 }, { "4_pad", XK_KP_4 }, { "5_pad", XK_KP_5 },
+ { "6_pad", XK_KP_6 }, { "7_pad", XK_KP_7 }, { "8_pad", XK_KP_8 },
+ { "9_pad", XK_KP_9 },
+
+ { "/_pad", XK_KP_Divide }, { "*_pad", XK_KP_Multiply },
+ { "-_pad", XK_KP_Subtract }, { "+_pad", XK_KP_Add },
+ { "enter_pad", XK_KP_Enter }, { "._pad", XK_KP_Decimal },
+
+ { "up", XK_Up }, { "left", XK_Left },
+ { "down", XK_Down }, { "right", XK_Right },
+
+ { "f1", XK_F1 }, { "f2", XK_F2 }, { "f3", XK_F3 },
+ { "f4", XK_F4 }, { "f5", XK_F5 }, { "f6", XK_F6 },
+ { "f7", XK_F7 }, { "f8", XK_F8 }, { "f9", XK_F9 },
+ { "f10", XK_F10 }, { "f11", XK_F11 }, { "f12", XK_F12 },
+
+ //{ "mouse_1", XK_Pointer_DfltBtnPrev },
+ //{ "mouse_2", XK_Pointer_DfltBtnNext }
+ { "mouse_wheel_up", XK_Pointer_DfltBtnPrev },
+ { "mouse_wheel_down", XK_Pointer_DfltBtnNext } };
+constexpr auto key_dict_size = []() {
+ std::size_t ret = 0;
+ for (auto i : key_dict)
+ ret++;
+ return ret;
+}();
+std::pair, std::bitset<3>> QueryPointer(decltype(hydride_library.display) dis, decltype(hydride_library.window) win) {
+ Window root_return, child_return;
+ std::pair mouse, root;
+ unsigned int mask_return;
+ if (!XQueryPointer(hydride_library.display, hydride_library.window, &root_return, &child_return, &root.first, &root.second, &mouse.first, &mouse.second, &mask_return))
+ throw std::runtime_error("Unable to query pointer from X11");
+
+ std::bitset<3> ret_buttons;
+ ret_buttons[0] = (mask_return & (Button1Mask));
+ ret_buttons[1] = (mask_return & (Button2Mask));
+ ret_buttons[2] = (mask_return & (Button3Mask));
+ return { mouse, ret_buttons };
+}
+
+using Keymap = std::array;
+bool PickFromKeymap(decltype(hydride_library.display) dis, Keymap map, decltype(XK_2) k) {
+ int current_key = XKeysymToKeycode(dis, k);
+ return (map[current_key / 8] & (1 << (current_key % 8)));
+}
+Keymap QueryKeymap(decltype(hydride_library.display) dis) {
+ Keymap keys;
+ XQueryKeymap(dis, keys.data());
+ return keys;
+}
+bool QueryKey(decltype(hydride_library.display) dis, decltype(XK_2) k) {
+ auto map = QueryKeymap(dis);
+ return PickFromKeymap(dis, map, k);
+}
+
+template
+class ChangeDetector { // generate async from sync
+ std::pair previous_mouse { -1, -1 };
+ std::pair previous_bounds { -1, -1 };
+ std::bitset previous_states;
+
+public:
+ ChangeDetector() {
+ this->previous_states.reset();
+ }
+ bool UpdateKey(std::size_t k, bool state) {
+ if (state == previous_states[k]) // Just to prevent issues, idk if we need this at runtime ;-;
+ return false;
+ previous_states.set(k, state);
+ return true;
+ }
+ decltype(previous_mouse) UpdateMouse(const decltype(previous_mouse)& state) {
+ if (state == previous_mouse) // Just to prevent issues, idk if we need this at runtime ;-;
+ return { 0, 0 };
+
+ decltype(previous_mouse) delta = { state.first - previous_mouse.first, state.second - previous_mouse.second };
+ previous_mouse = state;
+ return delta;
+ }
+ decltype(previous_bounds) UpdateBounds(const decltype(previous_bounds)& state) {
+ if (state == previous_bounds) // Just to prevent issues, idk if we need this at runtime ;-;
+ return { 0, 0 };
+
+ decltype(previous_bounds) delta = { state.first - previous_bounds.first, state.second - previous_bounds.second };
+ previous_bounds = state;
+ return delta;
+ }
+};
+
+class X11Poller {
+ ChangeDetector async_gen;
+ decltype(hydride_library.display) display;
+ decltype(hydride_library.window) window;
+
+public:
+ X11Poller(decltype(hydride_library.display) display, decltype(hydride_library.window) window)
+ : display(display)
+ , window(window) {
+ UpdateKeys();
+ UpdateMouse();
+ }
+ std::vector> UpdateKeys() {
+ auto km = QueryKeymap(display);
+
+ std::vector> ret;
+ for (std::size_t i = 0; i < key_dict_size; i++) {
+ bool key_state = PickFromKeymap(display, km, key_dict[i].second);
+ if (async_gen.UpdateKey(i, key_state))
+ ret.push_back({ key_dict[i].first, key_state });
+ }
+ return ret;
+ }
+ std::tuple, std::pair, std::bitset<3>> UpdateMouse() {
+ auto pointer_info = QueryPointer(this->display, this->window);
+
+ auto delta = async_gen.UpdateMouse(pointer_info.first);
+ return { pointer_info.first, delta, pointer_info.second };
+ }
+};
+
+} // namespace x11
+
+#include "util/geometry.hpp"
+
+namespace qubel {
+
+enum VoxelTypes {
+ kAir,
+ kStone,
+ kDirt,
+ kSand,
+ kGrass
+};
+
+enum CardinalDirections {
+ kNorth,
+ kSouth,
+ kEast,
+ kWest // ,
+ /*kUp,
+ kDown*/
+};
+
+class Chunk {
+public:
+ // "typedefs" or templates go here lol
+ static constexpr uint _dim_length = 2; // its in 2d!
+ static constexpr uint _size_in_power = 16; // 8x8 size
+ static constexpr std::size_t _chunk_array_size = _size_in_power * _size_in_power;
+
+ using BlockType = uint8_t;
+ // Using a single digit to locate the exact voxel's byte in the chunk, for very fast use.
+ using ChunkDataType = std::array;
+ using ChunkVecType = geo::internal::BVec2;
+ ChunkDataType raw_chunk_data = {
+ kStone,
+ kStone,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kStone,
+ kStone,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kGrass,
+ kGrass,
+ kGrass,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kGrass,
+ kDirt,
+ kDirt,
+ kDirt,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kGrass,
+ kGrass,
+ kDirt,
+ kDirt,
+ kDirt,
+ kDirt,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kSand,
+ kSand,
+ kDirt,
+ kDirt,
+ kDirt,
+ kStone,
+ kStone,
+ kStone,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kSand,
+ kSand,
+ kDirt,
+ kDirt,
+ kStone,
+ kStone,
+ kStone,
+ kStone,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kAir,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kAir,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kAir,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kAir,
+ kSand,
+ kAir,
+ kSand,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kSand,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kSand,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kAir,
+ kStone,
+ };
+ class LocalVoxel {
+ public:
+ std::size_t pos; // yes a single integer, x y and z can all exist due to the way chunking keeps it localized and x + (z * 64) means the values co-exist.
+ // LocalVoxel(geo::internal::BVec2 raw_chunk_pos)
+ //: pos(raw_chunk_pos) { }
+ static LocalVoxel GetVoxelFromPosInChunk(geo::internal::BVec2 in_pos) { // basicly your 2d/3d position to a index, make sure ur in the chunk! lol
+ assert(in_pos.x < _size_in_power && in_pos.y < _size_in_power);
+ LocalVoxel index;
+ index.pos = 0;
+ return index.AddPos(in_pos);
+ }
+ LocalVoxel& AddPos(geo::internal::BVec2 in_pos) {
+ this->pos += in_pos.x;
+ this->pos += in_pos.y * _size_in_power;
+ // if constexpr (_dim_length >= 3) // We gotta move these to a seperate class all-together... do this once the templates are split.
+ // index.pos += in_pos.z * (_size_in_power * _size_in_power);
+ return *this;
+ }
+ ChunkVecType ExtractVec() {
+ ChunkVecType ret_pos;
+ ret_pos.x = this->pos % _size_in_power;
+ ret_pos.y = (this->pos / _size_in_power) % _size_in_power;
+ // ret_pos.z = ((this->pos / _size_in_power) / _size_in_power) % _size_in_power;
+ return ret_pos;
+ }
+ // tmp, TODO replace/add with chunk pointer or chunk x and y... both??? oof if so
+ // need good way to keep the handle associated.
+ // Local HANDLES AND "standard voxel"/global voxel in other words one for chunk and one inheriting with the extra details? ah genious!
+ };
+ static LocalVoxel GetVoxelFromPosInChunk(geo::internal::BVec2 in_pos) {
+ return LocalVoxel::GetVoxelFromPosInChunk(in_pos);
+ }
+ BlockType GetVoxelType(LocalVoxel v) {
+ return raw_chunk_data[v.pos];
+ }
+ class AxisIterator {
+ public:
+ bool end_flagged = false;
+ ChunkVecType cur;
+ using VecMaskType = glm::bvec2;
+ const VecMaskType mask, reverse_mask; // stick it in reverse spongebob!
+ constexpr AxisIterator(const ChunkVecType& src, glm::bvec2 mask, glm::bvec2 reverse_mask = { false, false })
+ : cur(src)
+ , mask(mask)
+ , reverse_mask(reverse_mask) { }
+ AxisIterator& operator++(int) {
+ // new_iter.cur.AddPos(this->GetPosToAdd()); we cant do this cause we have to test it ;-;
+ this->cur += this->GetPosToAdd();
+ if (!IsPosValidWithinChunk(this->cur))
+ this->end_flagged = true;
+ return *this;
+ }
+ bool operator==(const AxisIterator& other) const {
+ if (other.end_flagged)
+ return this->end_flagged;
+
+ return other.cur == this->cur && other.mask == this->mask && other.reverse_mask == this->reverse_mask;
+ }
+ bool operator!=(const AxisIterator& other) const {
+ return !(*this == other);
+ }
+ static constexpr AxisIterator end() {
+ AxisIterator ret({}, {});
+ ret.end_flagged = true;
+ return ret;
+ }
+ ChunkVecType GetPosToAdd() const {
+ ChunkVecType pos_to_add = { 0, 0 };
+ if (mask.x)
+ pos_to_add.x = 1;
+ if (mask.y)
+ pos_to_add.y = 1;
+ if (reverse_mask.x && pos_to_add.x)
+ pos_to_add.x = -pos_to_add.x;
+ if (reverse_mask.y && pos_to_add.y)
+ pos_to_add.y = -pos_to_add.y;
+ return pos_to_add;
+ }
+ static constexpr auto CreateCardinalMask(const CardinalDirections dir) {
+ VecMaskType mask = { false, false }, reverse_mask = { false, false };
+ switch (dir) {
+ case CardinalDirections::kNorth:
+ reverse_mask.y = true;
+ case CardinalDirections::kSouth:
+ mask.y = true;
+ break;
+ case CardinalDirections::kWest:
+ reverse_mask.x = true;
+ case CardinalDirections::kEast:
+ mask.x = true;
+ break;
+ default:
+ throw std::logic_error("Trying to access a cardinal Direction that does not work!");
+ }
+ return std::make_pair(mask, reverse_mask);
+ }
+ };
+ enum MesherComplexity {
+ kPlain,
+ kNieve,
+ kGreedy, // Just expand same type faces
+ kIntrinsicGreedy, // Using bitwise operations :O
+ kGlobalLattice // literally Impossible to recreate, meshing everything INCLUDING air, and rendering it straight up in shader
+ };
+ static constexpr float box_size = 25.0f;
+ // glez::vertex not verbose enough for us sadly...
+ struct ChunkMeshedQuad {
+ geo::Box quad;
+ glez::rgba clr;
+ };
+ static constexpr glez::rgba chunk_textures[] = {
+ glez::rgba(0, 0, 0, 0),
+ glez::rgba(40, 40, 40),
+ glez::rgba(120, 40, 40),
+ glez::color::yellow,
+ glez::color::green
+ };
+ std::vector MeshChunk(MesherComplexity cmplxity) {
+ std::vector finished_quads;
+ std::cout << "Start Meshing: ";
+ auto start_time = std::chrono::high_resolution_clock::now();
+ switch (cmplxity) {
+ case MesherComplexity::kNieve: // I dont think this is possible in 2d for the time being...
+ case MesherComplexity::kPlain: {
+ std::cout << "Plain" << std::endl;
+ for (uint dim_x = 0; dim_x < _size_in_power; dim_x++) {
+ for (uint dim_y = 0; dim_y < _size_in_power; dim_y++) {
+ LocalVoxel cube = GetVoxelFromPosInChunk({ dim_x, dim_y });
+ auto type = this->GetVoxelType(cube);
+ if (type == VoxelTypes::kAir)
+ continue;
+ ChunkMeshedQuad new_face;
+ auto start = geo::Vec2(dim_x * box_size, dim_y * box_size);
+ // auto end = /*start +*/ geo::Vec2(box_size, box_size);
+ auto size = /*start +*/ geo::Vec2(box_size, box_size);
+ new_face.quad = geo::Box(start, size);
+ new_face.clr = chunk_textures[type];
+
+ finished_quads.push_back(new_face);
+ std::cout << " *Added quad! : {" << start.x << ", " << start.y << "} with size {" << size.x << ", " << size.y << "}" << std::endl;
+ }
+ }
+ std::cout << "Plain ";
+ break;
+ }
+ /*case MesherComplexity::kNieve: {
+
+ } */
+ case MesherComplexity::kGreedy: {
+ std::cout << "Greedy" << std::endl;
+ std::bitset<_chunk_array_size> chunk_finished_checksum;
+ auto FindOpenSpace = [&]() -> std::pair> {
+ for (std::size_t x = 0; x < _size_in_power; x++) {
+ for (std::size_t y = 0; y < _size_in_power; y++) {
+ auto vox = this->GetVoxelFromPosInChunk({ x, y });
+ if (!chunk_finished_checksum[vox.pos])
+ return { vox.pos, chunk_finished_checksum[vox.pos] };
+ }
+ }
+ return { std::size_t(-1), {} };
+ };
+ while (!chunk_finished_checksum.all()) {
+ auto unmeshed_face = FindOpenSpace();
+ if (!unmeshed_face.second.has_value())
+ throw std::logic_error("Greedy ChunkMesher checksum thinks there is a unmeshed chunk but unable to find it!");
+
+ LocalVoxel cube;
+ cube.pos = unmeshed_face.first;
+ auto type = this->GetVoxelType(cube);
+ if (type == VoxelTypes::kAir) {
+ chunk_finished_checksum[cube.pos] = true;
+ continue;
+ }
+ {
+ auto ext_cube = cube.ExtractVec();
+ std::cout << "Greedy found Seed block \"" << (int)type << "\": {" << (int)ext_cube.x << ", " << (int)ext_cube.y << "}" << std::endl;
+ }
+
+ ChunkVecType block_quad_size = { 1, 1 };
+ auto src_block_loc = cube.ExtractVec();
+
+ // Need a better "search line down" func... instead of one block we need to check multiple and return how many are the same.
+ constexpr auto south_dir_mask = AxisIterator::CreateCardinalMask(CardinalDirections::kSouth);
+ auto SearchLineSouthForSameTypes = [&](ChunkVecType src_line, std::uint8_t max_size = _size_in_power) -> std::uint8_t {
+ std::size_t alike_counter = 0;
+ const auto end = AxisIterator::end();
+ for (auto axis_crawl = AxisIterator(src_line, south_dir_mask.first, south_dir_mask.second); axis_crawl != end; axis_crawl++) {
+ LocalVoxel block_to_check = GetVoxelFromPosInChunk(axis_crawl.cur);
+ auto below_block_type = this->GetVoxelType(block_to_check);
+ if (below_block_type != type)
+ break;
+ if (chunk_finished_checksum[block_to_check.pos])
+ break;
+ alike_counter++;
+ if (alike_counter >= max_size)
+ break;
+ }
+ return alike_counter;
+ };
+ auto same_blocks_south_count = SearchLineSouthForSameTypes(src_block_loc);
+ assert(same_blocks_south_count);
+ block_quad_size.y = same_blocks_south_count;
+
+ std::cout << " Found blocks going south: " << (int)block_quad_size.y << std::endl;
+
+ auto SearchLineEastForSameTypes = [&]() -> std::uint8_t {
+ std::size_t alike_counter = 1;
+ const auto end = AxisIterator::end();
+ constexpr auto east_dir_mask = AxisIterator::CreateCardinalMask(CardinalDirections::kEast);
+ auto side_axis_crawl = AxisIterator(src_block_loc, east_dir_mask.first, east_dir_mask.second);
+ side_axis_crawl++; // starting one ahead
+ // if (side_axis_crawl != end) {
+ std::cout << " Searching More East!!" << std::endl;
+ for (; side_axis_crawl != end; side_axis_crawl++) {
+ LocalVoxel side_block_to_check = GetVoxelFromPosInChunk(side_axis_crawl.cur);
+ auto below_block_type = this->GetVoxelType(side_block_to_check);
+ if (below_block_type != type)
+ break;
+
+ // here we go again, dip down to this max length
+ auto additional_same_blocks_south_count = SearchLineSouthForSameTypes(side_axis_crawl.cur, same_blocks_south_count);
+ assert(additional_same_blocks_south_count <= same_blocks_south_count);
+ if (additional_same_blocks_south_count < same_blocks_south_count)
+ break;
+ alike_counter++;
+ }
+ //}
+ return alike_counter;
+ };
+ if (src_block_loc.x + 1 < _size_in_power) { // only if we have room to check!
+ auto same_blocks_east_count = SearchLineEastForSameTypes();
+ block_quad_size.x = same_blocks_east_count;
+ std::cout << " Found blocks going east: " << (int)block_quad_size.x << std::endl;
+ }
+
+ ChunkMeshedQuad new_face;
+ auto start = geo::Vec2(src_block_loc.x * box_size, src_block_loc.y * box_size);
+ auto size = geo::Vec2(block_quad_size.x * box_size, block_quad_size.y * box_size);
+
+ new_face.quad = geo::Box(start, size);
+ new_face.clr = chunk_textures[type];
+ std::cout << " *Added quad! : {" << start.x << ", " << start.y << "} with size {" << size.x << ", " << size.y << "}" << std::endl;
+ finished_quads.push_back(new_face);
+
+ auto MarkAreaFinished = [&](ChunkVecType origin, ChunkVecType size) {
+ assert(size.x && size.y);
+ for (std::size_t x = 0; x < size.x; x++) {
+ for (std::size_t y = 0; y < size.y; y++) {
+ auto block_to_clear = origin + ChunkVecType(x, y);
+ std::cout << " ------Marking Block Finished: {" << (int)block_to_clear.x << ", " << (int)block_to_clear.y << "}" << std::endl;
+ chunk_finished_checksum[this->GetVoxelFromPosInChunk(block_to_clear).pos] = true;
+ }
+ }
+ };
+ MarkAreaFinished(src_block_loc, block_quad_size);
+ // mark it off of the chunk_finished_checksum by setting the chunk_finished_checksum[i] to 1
+ }
+ std::cout << "Greedy ";
+ break;
+ }
+ default:
+ // Do nothing, no fail just no-op any mesher un-implimented.
+ break;
+ };
+ auto end_time = std::chrono::high_resolution_clock::now(); // Calculate duration and convert to milliseconds
+ auto duration = std::chrono::duration_cast(end_time - start_time).count();
+ std::cout << "Meshing Finished!!! QuadCount: " << (int)finished_quads.size() << ", Within: " << duration << " microseconds" << std::endl;
+
+ return finished_quads;
+ }
+ static bool IsPosValidWithinChunk(ChunkVecType voxel_loc) {
+ if (voxel_loc.x >= _size_in_power)
+ return false;
+ if (voxel_loc.y >= _size_in_power)
+ return false;
+ return true;
+ }
+};
+
+} // namespace qubel
+
+class QubelMeshingTestWindow : public CBaseWindow {
+public:
+ CCheckbox* activate;
+ CCheckbox* wireframe;
+ CDropdown* dropdown;
+ QubelMeshingTestWindow(IWidget* parent)
+ : CBaseWindow(parent, "qubelmesh_test_settings") {
+ this->always_visible = false;
+ this->hover = false;
+ this->SetMaxSize(1270, 1000);
+ this->SetPositionMode(PositionMode::FLOATING);
+
+ this->Add("~SimpleQubelMeshingTester~");
+
+ this->Add("activate_label", "Activate Viewing:");
+ activate = this->Add("activate", false);
+
+ this->Add("spread_label", "VoxelSpread:");
+ this->Add("spread")->SetStep(0.1f);
+
+ this->Add("wireframe_label", "Wireframe Mesh:");
+ wireframe = this->Add("wireframe", true);
+
+ this->Add("wanted_mesher_label", "Meshing Algorithm:");
+ dropdown = this->Add("wanted_mesher");
+ dropdown->AddValue("NoFaceOcclusion, Plain");
+ dropdown->AddValue("Nieve");
+ dropdown->AddValue("Greedy"); // Just expand same type faces
+ dropdown->AddValue("Intrinsic Greedy"); // Using bitwise operations :O
+ dropdown->AddValue("Global Lattice"); // literally Impossible to recreate, meshing everything INCLUDING air, and rendering it straight up in shader
+ dropdown->SetSize(150, 16);
+ dropdown->SetValue(2);
+
+ this->Add("binary_meshing_label", "Binary Meshing(ignore types):");
+ this->Add("binary_meshing", false);
+ }
+};
+
+using namespace qubel;
+class QubelMeshingTestRenderingWindow : public CBaseWindow {
+public:
+ const CTitleBar* titlebar;
+ const QubelMeshingTestWindow* settings;
+ Chunk world_slice;
+
+ class ChunkRenderer : public CBaseWidget {
+ public:
+ Chunk* world_slice;
+ QubelMeshingTestWindow* settings;
+ ChunkRenderer(IWidget* parent, QubelMeshingTestWindow* settings, Chunk* world_slice)
+ : CBaseWidget("qubelmesh_test_renderer_sceneoutput", parent)
+ , settings(settings)
+ , world_slice(world_slice) {
+ assert(settings && world_slice);
+ settings->dropdown->SetCallback([&](CDropdown*, int value) {
+ current_render_quads.clear();
+ });
+ }
+ std::vector current_render_quads;
+ virtual void Draw(ICanvas* the_drawing_machine) override {
+ this->CBaseWidget::Draw(the_drawing_machine);
+ const auto ConvertGLMVecToSTDPairVec = [](const geo::Vec2& vec_in) -> std::pair {
+ return std::pair(vec_in.x, vec_in.y);
+ };
+ const auto ConvertGLMQuadToSTDPairQuad = [&](const geo::Box& box_in) -> Canvas::TranslationMatrix {
+ return Canvas::TranslationMatrix(ConvertGLMVecToSTDPairVec(box_in.origin), ConvertGLMVecToSTDPairVec(box_in.GetSize()));
+ };
+ for (const Chunk::ChunkMeshedQuad& kinda_a_vert : current_render_quads)
+ the_drawing_machine->Rect(ConvertGLMQuadToSTDPairQuad(kinda_a_vert.quad), kinda_a_vert.clr);
+ if (this->settings->wireframe->Value()) {
+ for (const Chunk::ChunkMeshedQuad& kinda_a_vert : current_render_quads) {
+ the_drawing_machine->Rect({ ConvertGLMQuadToSTDPairQuad(kinda_a_vert.quad) }, glez::color::black, Canvas::RectType::Outline);
+ the_drawing_machine->Line({ ConvertGLMQuadToSTDPairQuad(kinda_a_vert.quad) }, glez::color::black);
+ }
+ }
+ }
+ virtual void Update() override {
+ this->CBaseWidget::Update();
+ if (current_render_quads.empty()) {
+ current_render_quads = world_slice->MeshChunk(Chunk::MesherComplexity(settings->dropdown->Value()));
+
+ std::pair found_size;
+ for (const Chunk::ChunkMeshedQuad& kinda_a_vert : current_render_quads) {
+ auto max_coord = kinda_a_vert.quad.GetMax();
+ if (max_coord.x > found_size.first)
+ found_size.first = max_coord.x;
+ if (max_coord.y > found_size.second)
+ found_size.second = max_coord.y;
+ }
+ std::cout << "Created mesh with pixel size: " << found_size.first << ", " << found_size.second << std::endl;
+ this->SetSize(found_size.first, found_size.second);
+ }
+ }
+ };
+ ChunkRenderer* render_scene;
+
+public:
+ QubelMeshingTestRenderingWindow(IWidget* parent, QubelMeshingTestWindow* settings)
+ : CBaseWindow(parent, "qubelmesh_test_renderer_frame")
+ , settings(settings) {
+ assert(settings);
+ this->always_visible = false;
+ this->hover = false;
+ this->SetPositionMode(PositionMode::FLOATING);
+ this->SetMaxSize(1270, 1000);
+ this->zindex = -999;
+
+ titlebar = this->Add("QubelWorld Mesh Test uwu~");
+ render_scene = this->Add(settings, &world_slice);
+ }
+ ~QubelMeshingTestRenderingWindow() { }
+ virtual bool AlwaysVisible() const override {
+ return this->settings->activate->Value();
+ }
+ virtual bool IsVisible() const override {
+ return this->settings->activate->Value() && this->CBaseWindow::IsVisible();
+ }
+
+public:
+ // std::chrono::time_point last_update;
+};
+
+int start_nyqubel_client() {
+ hydride_init();
+
+ glez::init(hydride_library.width, hydride_library.height);
+
+ Canvas* canvas;
+ x11::X11Poller x11_poller(hydride_library.display, hydride_library.window);
+ {
+ input::RefreshInput();
+
+ hydride_draw_begin();
+ glez::begin();
+
+ canvas = new Canvas();
+ canvas->Setup();
+
+ glez::end();
+ hydride_draw_end();
+ }
+
+ canvas->Add()->SetOffset(500, 525);
+
+ auto mesh_test_settings_window = canvas->Add();
+ auto mesh_test_rendering_window = canvas->Add(mesh_test_settings_window);
+ mesh_test_settings_window->SetOffset(2000, 400);
+ mesh_test_rendering_window->SetOffset(3000, 800);
+
+ bool client_exiting = false;
+ mesh_test_settings_window->Add("exit_button", "Press to Quit-rite", [&](CBaseButton*) {
+ client_exiting = true;
+ std::cout << "User Requested Client quit." << std::endl;
+ });
+
+ // Need a 856/480 size window.
+
+ hydride_show();
+ while (1) {
+ input::RefreshInput();
+ // Must be called in that order.
+ hydride_draw_begin();
+ glez::begin();
+ {
+ x11_poller.UpdateKeys();
+ x11_poller.UpdateMouse();
+
+ canvas->Update();
+ }
+ glez::end();
+ hydride_draw_end();
+
+ if (client_exiting)
+ break;
+ }
+ std::cout << "Client is Exiting!!!" << std::endl;
+ return 0;
+}
diff --git a/src/nyqubel-client/client.hpp b/src/nyqubel-client/client.hpp
new file mode 100644
index 000000000..d0e49e7fa
--- /dev/null
+++ b/src/nyqubel-client/client.hpp
@@ -0,0 +1,4 @@
+
+#pragma once
+
+int start_nyqubel_client();
diff --git a/src/nyqubel-client/util/geometry.cpp b/src/nyqubel-client/util/geometry.cpp
new file mode 100644
index 000000000..fb9f5506f
--- /dev/null
+++ b/src/nyqubel-client/util/geometry.cpp
@@ -0,0 +1,88 @@
+
+/*
+ * Nekohook: Free, fast and modular cheat software
+ * Copyright (C) 2018 Rebekah Rowe
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include // rand()
+
+#include "geometry.hpp"
+
+namespace qubel::geo::internal {
+
+Angle2& Angle2::Clamp() {
+
+ while (this->x > 89.0f) this->x -= 180.0f;
+ while (this->x < -89.0f) this->x += 180.0f;
+
+ while (this->y > 180.0f) this->y -= 360.0f;
+ while (this->y < -180.0f) this->y += 360.0f;
+
+ return *this;
+}
+
+// Returns angles to a point in space
+AngleBase<2> AngleBase<2>::PointTo(const Vec3& src_point, const Vec3& dest_point) {
+ Vec3 aim_point = dest_point - src_point;
+
+ Angle2 out;
+ out.y = atan2(aim_point.y, aim_point.x) * 180.0f / F_PI;
+ out.x = atan2(0, sqrt(aim_point.x * aim_point.x + aim_point.y * aim_point.y)) * 180.0f / F_PI;
+
+ return out.Clamp();
+}
+
+// A function to get the difference from angles, Please make sure inputs are
+// clamped
+AngleBase<2> AngleBase<2>::GetDelta(const AngleBase<2>& dest_angles) const {
+ // Our output difference
+ Angle2 diff;
+
+ // Yaw
+ if (this->y != dest_angles.y) {
+ // Check if yaw is on opposing poles
+ if (this->y < -90 && dest_angles.y > 90)
+ diff.y = this->y + 360 - dest_angles.y;
+ else if (this->y > 90 && dest_angles.y < -90)
+ diff.y = dest_angles.y + 360 - this->y;
+ else
+ diff.y = std::abs(this->y - dest_angles.y);
+ }
+ // Pitch
+ if (this->x != dest_angles.x) diff.x = std::abs(this->x - dest_angles.x);
+
+ return diff;
+}
+// Use input angles and our eye position to get fov to a destination point
+float AngleBase<2>::GetFov(const Vec3& src, const Vec3& dest) const {
+ return this->GetFov(PointTo(src, dest));
+}
+float AngleBase<2>::GetFov(const AngleBase<2>& pointed_angle) const {
+ Angle2 delta = this->GetDelta(pointed_angle);
+ return std::max(delta.x, delta.y);
+}
+
+Vec3 DirectionalMove(const Vec3& src, const AngleBase<2>& dir, float amt) {
+ // Math I dont understand
+ float sp = sinf(dir.x * F_PI / 180.f);
+ float cp = cosf(dir.x * F_PI / 180.f);
+ float sy = sinf(dir.y * F_PI / 180.f);
+ float cy = cosf(dir.y * F_PI / 180.f);
+
+ return Vec3(cp * cy, cp * sy, -sp) * amt + src;
+}
+
+} // namespace neko::math
diff --git a/src/nyqubel-client/util/geometry.hpp b/src/nyqubel-client/util/geometry.hpp
new file mode 100644
index 000000000..c6271d90f
--- /dev/null
+++ b/src/nyqubel-client/util/geometry.hpp
@@ -0,0 +1,475 @@
+
+/*
+ * Nekohook: Free, fast and modular cheat software
+ * Copyright (C) 2018 Rebekah Rowe
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace qubel::geo {
+
+namespace internal {
+
+#if true // NEKOHOOK_GFX == opengl
+template
+using BVec = glm::vec<_dim_length, T>;
+template
+using BVec2 = glm::vec<2, T>;
+template
+using BVec3 = glm::vec<3, T>;
+using Vec2 = glm::vec2;
+using Vec3 = glm::vec3;
+using DVec3 = glm::dvec3;
+using IVec2 = glm::ivec2;
+using UVec2 = glm::uvec2;
+
+constexpr float F_PI = glm::pi();
+
+template
+auto Distance(T... in) { return glm::distance(in...); }
+
+template
+auto GetMaskOfVec2(const T& src, glm::bvec2 mask) {
+ T ret = { 0, 0 };
+ if (mask.x)
+ ret.x = src.x;
+ if (mask.y)
+ ret.y = src.y;
+ return ret;
+}
+#endif
+
+template
+class BoxBase;
+
+template
+class BoxBase {
+ static_assert(T::length() == 2);
+
+protected:
+ BoxBase() { }
+ BoxBase(T _first, T _second)
+ : first(_first)
+ , second(_second) { }
+
+public:
+ // TODO, rotation around origin?
+
+ // Data
+ union {
+ T first, origin;
+ };
+ union {
+ T second, size;
+ };
+
+ T GetMax() const { return this->origin + this->size; }
+ T GetSize() const { return this->size; }
+
+ T GetPoint(int idx) const {
+ switch (idx) {
+ case 0:
+ return this->origin;
+ case 1:
+ return { this->origin.x + this->size.x, this->origin.y };
+ case 2:
+ return { this->origin.x, this->origin.y + this->size.y };
+ case 3:
+ return this->origin + this->size;
+ default:
+ throw std::logic_error("Unknown point idx: " + std::to_string(idx));
+ }
+ }
+ std::array GetPoints() const {
+ T max = this->GetMax();
+ return {
+ this->origin,
+ { max.x, this->origin.y },
+ { this->origin.x, max.y },
+ max
+ };
+ }
+
+ bool Contains(T in) const {
+ if (in.x > this->origin.x && in.y > this->origin.y) {
+ T max = this->GetMax();
+ return (in.x < max.x && in.x < max.y);
+ }
+ return false;
+ }
+
+ bool SegmentIntersects(T src, T dest) const {
+ if ((dest.x > this->min.x && src.x > this->min.x) && (dest.y > this->min.y && src.y > this->min.y)) {
+ T max = this->GetMax();
+ return (dest.x < max.x && src.x < max.x) || (dest.y < max.y && src.y < max.y);
+ }
+ return false;
+ }
+ enum class Direction {
+ kUp,
+ kDown,
+ kLeft,
+ kRight,
+ kPosX = kRight,
+ kPosY = kDown,
+ kNegX = kLeft,
+ kNegY = kUp
+ };
+ void Expand(Direction dir, int amt) { // TODO: Bitmongering(nopey)
+ switch (dir) {
+ case Direction::kUp:
+ this->origin.y -= amt;
+ case Direction::kDown:
+ this->size.y += amt;
+ break;
+ case Direction::kLeft:
+ this->origin.x -= amt;
+ case Direction::kRight:
+ this->size.x += amt;
+ break;
+ default:
+ assert(false);
+ }
+ }
+ void ExpandTo(T v) {
+ if (this->origin.x > v.x)
+ this->Expand(Direction::kNegX, this->origin.x - v.x);
+ if (this->origin.y > v.y)
+ this->Expand(Direction::kNegY, this->origin.y - v.y);
+ T t_max = this->GetMax();
+ if (t_max.x < v.x)
+ this->Expand(Direction::kPosX, v.x - this->origin.x);
+ if (t_max.y < v.y)
+ this->Expand(Direction::kPosY, v.y - this->origin.y);
+ }
+ // TODO: Center around
+};
+
+template
+class BoxBase {
+ static_assert(T::length() == 3);
+
+protected:
+ BoxBase() { }
+ BoxBase(T _first, T _second)
+ : first(_first)
+ , second(_second) { }
+
+public:
+ // Data
+ union {
+ T first, min;
+ };
+ union {
+ T second, max;
+ };
+
+ auto operator[](const int idx) const {
+ return this->data.at(idx);
+ }
+
+ // Utility functions
+ T GetSize() const { return this->max - this->min; }
+ T& GetMax() const { return this->max; }
+
+ T GetPoint(int idx) const {
+ switch (idx) {
+ case 0:
+ return this->min;
+ case 1:
+ return { this->max.x, this->min.y, this->min.z };
+ case 2:
+ return { this->max.x, this->max.y, this->min.z };
+ case 3:
+ return { this->min.x, this->max.y, this->max.z };
+ case 4:
+ return { this->min.x, this->min.y, this->max.z };
+ case 5:
+ return { this->max.x, this->min.y, this->max.z };
+ case 6:
+ return { this->min.x, this->max.y, this->min.z };
+ case 7:
+ return this->max;
+ default:
+ throw std::logic_error("GetPoint() is only available for 2 and 3 axis vectors");
+ }
+ }
+ std::array GetPoints() const {
+ return {
+ this->min,
+ { this->max.x, this->min.y, this->min.z },
+ { this->max.x, this->max.y, this->min.z },
+ { this->min.x, this->max.y, this->max.z },
+ { this->min.x, this->min.y, this->max.z },
+ { this->max.x, this->min.y, this->max.z },
+ { this->min.x, this->max.y, this->min.z },
+ this->max
+ };
+ }
+
+ bool Contains(T i) const {
+ if (i.x > this->min.x || i.y > this->min.y || i.z > this->min.z)
+ if (i.x < this->max.x || i.y < this->max.y || i.z < this->max.z)
+ return false;
+ return true;
+ }
+
+ // Credits to cathook
+ bool LineIntersects(T src, T dst) const {
+ if (dst.x < this->min.x && src.x < this->min.x)
+ return false;
+ if (dst.y < this->min.y && src.y < this->min.y)
+ return false;
+ if (dst.z < this->min.z && src.z < this->min.z)
+ return false;
+ if (dst.x > this->max.x && src.x > this->max.x)
+ return false;
+ if (dst.y > this->max.y && src.y > this->max.y)
+ return false;
+ if (dst.z > this->max.z && src.z > this->max.z)
+ return false;
+ return true;
+ }
+
+ enum Direction {
+ kUp,
+ kDown,
+ kLeft,
+ kRight,
+ kForward,
+ kBackward,
+ kPosX = kUp,
+ kPosY = kLeft,
+ kPosZ = kForward,
+ kNegX = kDown,
+ kNegY = kRight,
+ kNegZ = kBackward
+ };
+ void Expand(Direction dir, int amt) {
+ switch (dir) {
+ case Direction::kUp:
+ this->max.y += amt;
+ break;
+ case Direction::kDown:
+ this->min.y -= amt;
+ break;
+ case Direction::kLeft:
+ this->min.x += amt;
+ break;
+ case Direction::kRight:
+ this->min.x -= amt;
+ break;
+ case Direction::kForward:
+ this->min.z += amt;
+ break;
+ case Direction::kBackward:
+ this->max.z -= amt;
+ break;
+ default:
+ assert(false);
+ }
+ }
+};
+
+template
+class Box : public BoxBase {
+ using Parent = BoxBase;
+
+public:
+ using Direction = typename Parent::Direction;
+
+ Box() { }
+ Box(T _first, T _second)
+ : Parent(_first, _second) { }
+ Box(std::pair v)
+ : Box(v.first, v.second) { }
+ template
+ Box(TT v)
+ : Box(v.first, v.second) {
+ static_assert(!std::is_same_v, TT>);
+ }
+
+ template
+ Box& operator=(TT v) { *this = Box(v.first, v.second); }
+
+ // Unary arithmetic operators
+ bool operator==(T v) const {
+ return this->first == v.first && this->second == v.second;
+ }
+ bool operator!=(T v) const {
+ return !(*this == v);
+ }
+ Box operator+(T v) const {
+ return { this->min + v, this->max + v };
+ }
+ Box operator-(T v) const {
+ return { this->min - v, this->max - v };
+ }
+
+ Box operator*(typename T::value_type v) const {
+ T center = this->GetCenter();
+ T size = (this->GetSize() * v) / typename T::value_type(2);
+ return { center - size, center + size };
+ }
+ Box operator/(typename T::value_type v) const {
+ T center = this->GetCenter();
+ T delta = (this->GetSize() / v) / typename T::value_type(2);
+
+ return { center - delta, center + delta };
+ }
+ Box& operator/=(typename T::value_type v) {
+ return *this = *this / v;
+ }
+
+ // Utility functions
+ T GetCenter() const { return (this->first + this->second) / typename T::value_type(2); }
+ void Expand(std::initializer_list dirs, int amt) {
+ auto end = dirs.end();
+ for (auto i = dirs.begin(); i != end; i++)
+ this->Expand(*i, amt);
+ }
+ void Shrink(Direction dir, int amt) { this->Expand(dir, -amt); }
+};
+
+template
+class AngleBase;
+
+template <>
+class AngleBase<1> {
+ float data;
+
+protected:
+ template
+ AngleBase(T... args)
+ : data(args...) { }
+
+public:
+ operator float() const {
+ return this->data;
+ }
+ operator float&() {
+ return this->data;
+ }
+};
+
+template <>
+class AngleBase<2> : public Vec2 {
+ using Vec2::Vec2;
+ using Parent = Vec2;
+
+protected:
+ template
+ AngleBase(T... args)
+ : Vec2(args...) {
+ static_assert(!(std::is_same_v || ...));
+ }
+
+public:
+ using Vec2::operator=;
+ template
+ AngleBase operator-(T in) { return *this - Vec2(in); }
+ template
+ AngleBase operator+(T in) { return *this + Vec2(in); }
+
+ AngleBase<2>& Clamp();
+ AngleBase<2> Clamp() const;
+ AngleBase<2> GetDelta(const AngleBase<2>& other_angles) const;
+ float GetFov(const Vec3& view_src, const Vec3& dest_point) const;
+ float GetFov(const AngleBase<2>& pointed_angle) const;
+ static AngleBase<2> PointTo(const Vec3& src_point, const Vec3& dest_point);
+};
+
+template
+using Angle = AngleBase;
+using Angle1 = Angle<1>;
+using Angle2 = Angle<2>;
+
+template
+class Ray {
+private:
+ const T_Origin origin;
+ const T_Angle angle;
+
+public:
+ Ray(T_Origin origin, T_Angle angle)
+ : origin(origin)
+ , angle(angle) { }
+ std::pair Cast(float dist);
+};
+
+template
+class Segment : public std::pair {
+public:
+ using Parent = std::pair;
+ using Parent::Parent;
+ using Parent::operator=;
+ template
+ Segment(Ray r, float dist = 8192.0f)
+ : Parent(r.Cast(dist)) { }
+ Segment(const std::pair& p)
+ : Parent(p) { }
+ Segment(std::pair&& p)
+ : Parent(std::move(p)) { }
+ Segment(std::pair p)
+ : Parent(std::move(p)) { }
+};
+
+} // namespace internal
+
+using Vec3 = internal::Vec3;
+
+using Vec2 = internal::Vec2;
+
+// idk if these should be in here, but its too useful not to...
+using DVec3 = internal::DVec3;
+using IVec2 = internal::IVec2;
+using UVec2 = internal::UVec2;
+///
+
+template
+auto Distance(T... in) { return internal::Distance(in...); }
+
+template
+auto GetMaskOfVec2(const T& src, glm::bvec2 mask) { return internal::GetMaskOfVec2(src, mask); }
+
+template
+using Box = internal::Box;
+
+template
+using Angle = internal::AngleBase;
+
+template
+using Ray = internal::Ray;
+
+template
+using Segment = internal::Segment;
+using Segment2 = internal::Segment;
+using Segment3 = internal::Segment;
+
+template
+using Sphereoid = std::pair;
+
+} // namespace qubel::geo