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