diff options
106 files changed, 1459 insertions, 989 deletions
diff --git a/.github/workflows/cmake-tag.yml b/.github/workflows/cmake-tag.yml index 84809590..8b8aaf97 100644 --- a/.github/workflows/cmake-tag.yml +++ b/.github/workflows/cmake-tag.yml @@ -15,14 +15,14 @@ jobs: strategy: fail-fast: true matrix: - os: [ubuntu-22.04, windows-latest, macos-12] + os: [ubuntu-latest, windows-latest, macos-latest] include: - - os: ubuntu-22.04 - cmake: /usr/bin/env CC=gcc-13 CXX=g++-13 cmake - - os: macos-12 - cmake: /usr/bin/env CC="$(brew --prefix llvm@17)/bin/clang" CXX="$(brew --prefix llvm@17)/bin/clang++" cmake + - os: ubuntu-latest + cmake: /usr/bin/env CC=gcc-14 CXX=g++-14 cmake - os: windows-latest cmake: .\.github\workflows\build-windows.bat cmake + - os: macos-latest + cmake: /usr/bin/env CC="$(brew --prefix llvm)/bin/clang" CXX="$(brew --prefix llvm)/bin/clang++" cmake steps: - uses: actions/checkout@v3 @@ -32,21 +32,21 @@ jobs: - uses: abdes/gha-setup-ninja@master with: version: 1.11.1 - if: matrix.os != 'ubuntu-22.04' + if: matrix.os != 'ubuntu-latest' - name: Install Linux dependencies run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt -q=2 update - sudo apt install g++-13 gdb ninja-build libbenchmark-dev + sudo apt install g++-14 gdb ninja-build libbenchmark-dev sudo apt -q install libgl1-mesa-dri libgl-dev libglx-dev xorg-dev xvfb libopencv-dev sudo apt -q install libsdl2-dev - if: matrix.os == 'ubuntu-22.04' + if: matrix.os == 'ubuntu-latest' - name: Install OSX dependencies run: | - brew install SDL2 llvm@17 - if: matrix.os == 'macos-12' + brew install SDL2 llvm lld + if: matrix.os == 'macos-latest' - name: Configure run: ${{matrix.cmake}} -G "Ninja" -S ${{github.workspace}}/ -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} @@ -57,11 +57,12 @@ jobs: - name: Test run: | cd ${{github.workspace}}/build/install + export MAGNUM_LOG=default export LD_LIBRARY_PATH="$PWD/lib" ASAN_OPTIONS="detect_leaks=0:abort_on_error=1" set -e xvfb-run gdb -q -batch -x ../../.github/gdbscript.py --args bin/floormat-test xvfb-run gdb -q -batch -x ../../.github/gdbscript.py --args bin/floormat-benchmark - if: matrix.os == 'ubuntu-22.04' + if: matrix.os == 'ubuntu-latest' # - name: Upload build # uses: actions/upload-artifact@v2.2.4 diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index b18fff28..a790a869 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -15,39 +15,24 @@ jobs: strategy: fail-fast: true matrix: - #os: [ubuntu-22.04, windows-latest, macos-12] - os: [ubuntu-22.04] + os: [ubuntu-latest] include: - - os: ubuntu-22.04 - cmake: /usr/bin/env CC=gcc-13 CXX=g++-13 cmake - #- os: macos-12 - # cmake: /usr/bin/env CC="$(brew --prefix llvm@16)/bin/clang" CXX="$(brew --prefix llvm@16)/bin/clang++" cmake - #- os: windows-latest - # cmake: .\.github\workflows\build-windows.bat cmake + - os: ubuntu-latest + cmake: /usr/bin/env CC=gcc-14 CXX=g++-14 cmake steps: - uses: actions/checkout@v3 with: submodules: recursive - - uses: abdes/gha-setup-ninja@master - with: - version: 1.11.1 - if: matrix.os != 'ubuntu-22.04' - - name: Install Linux dependencies run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt -q=2 update - sudo apt install g++-13 gdb ninja-build libbenchmark-dev + sudo apt install g++-14 gdb ninja-build libbenchmark-dev sudo apt -q install libgl1-mesa-dri libgl-dev libglx-dev xorg-dev xvfb libopencv-dev sudo apt -q install libsdl2-dev - if: matrix.os == 'ubuntu-22.04' - - - name: Install OSX dependencies - run: | - brew install SDL2 llvm@16 - if: matrix.os == 'macos-12' + if: matrix.os == 'ubuntu-latest' - name: Configure run: ${{matrix.cmake}} -G "Ninja" -S ${{github.workspace}}/ -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} @@ -58,14 +43,9 @@ jobs: - name: Test run: | cd ${{github.workspace}}/build/install + export MAGNUM_LOG=default export LD_LIBRARY_PATH="$PWD/lib" ASAN_OPTIONS="detect_leaks=0:abort_on_error=1" set -e xvfb-run gdb -q -batch -x ../../.github/gdbscript.py --args bin/floormat-test xvfb-run gdb -q -batch -x ../../.github/gdbscript.py --args bin/floormat-benchmark - if: matrix.os == 'ubuntu-22.04' - -# - name: Upload build -# uses: actions/upload-artifact@v2.2.4 -# with: -# name: buildoutput -# path: ${{github.workspace}}/build/ + if: matrix.os == 'ubuntu-24.04' diff --git a/.gitmodules b/.gitmodules index 1b59229d..b4537dd2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,9 +22,6 @@ [submodule "fmt"] path = external/fmt url = https://github.com/fmtlib/fmt.git -[submodule "luajit"] - path = external/luajit - url = https://github.com/sthalik/luajit.git [submodule "robin-map"] path = external/robin-map url = https://github.com/Tessil/robin-map.git diff --git a/CMakeLists.txt b/CMakeLists.txt index c09dd5df..54840a58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,13 +151,26 @@ if(MSVC) add_link_options(-HIGHENTROPYVA) endif() else() + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang$") + add_definitions(-D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) + add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-ambiguous-reversed-operator>) + add_compile_options(-Wno-reserved-macro-identifier) + #add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:$<$<CONFIG:DEBUG,Debug>:_LIBCPP_ENABLE_ASSERTIONS>>) + else() + #add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:$<$<CONFIG:DEBUG,Debug>:-D_GLIBCXX_ASSERTIONS>>) + #add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:$<$<CONFIG:DEBUG,Debug>:-D_GLIBCXX_DEBUG>>) + #add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:$<$<CONFIG:DEBUG,Debug>:-D_GLIBCXX_DEBUG_PEDANTIC>>) + endif() + + add_compile_options(-Wstrict-aliasing -Werror=strict-aliasing) add_compile_options(-Wno-float-equal) - if(NOT WIN32) + + if(NOT APPLE) add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fuse-cxa-atexit>) endif() if(WIN32) add_link_options(-Wl,--nxcompat -Wl,--dynamicbase) - if(CMAKE_SIZEOF_VOID_P GREATER_EQUAL 8) + if(CMAKE_SIZEOF_VOID_P GREATER 4) add_link_options(-Wl,--high-entropy-va) else() add_link_options(-Wl,--large-address-aware) @@ -165,12 +178,6 @@ else() endif() endif() -if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:$<$<CONFIG:DEBUG,Debug>:_LIBCPP_ENABLE_ASSERTIONS>>) - add_compile_options(-Wno-reserved-macro-identifier) - add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wno-ambiguous-reversed-operator>) -endif() - set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS OFF) set_directory_properties(PROPERTIES CORRADE_CXX_STANDARD ${CMAKE_CXX_STANDARD}) set_directory_properties(PROPERTIES INTERFACE_CORRADE_CXX_STANDARD ${CMAKE_CXX_STANDARD}) @@ -223,7 +230,6 @@ if(CMAKE_COMPILER_IS_GNUCXX) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang$") - add_definitions(-D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) add_compile_options(-Wno-shift-op-parentheses) add_compile_options(-Wno-c99-compat) elseif(CMAKE_COMPILER_IS_GNUCXX) @@ -1,13 +1,29 @@ [](https://github.com/sthalik/floormat/actions/workflows/cmake.yml) -### floormat +# floormat Game project in early development. -### Build +## Build -Should build cleanly on Clang 17+, GCC 13.2+ and MSVC 17.7. Just make sure to check it out with submodules. +Should build cleanly on Clang 17+, GCC 13.2+ and MSVC 17.7+. -### License +### Debian/Ubuntu dependencies +`xorg-dev` `libsdl2-dev` `build-essential` `ninja-build` `cmake-curses-gui` -ISC +### Windows dependencies + +Only the compiler (MSVC, GCC, Clang) is needed. + +### Building +```console +git clone https://github.com/sthalik/floormat.git +cd floormat +git submodule update --init +mkdir build +cd build +cmake -GNinja ../ +ninja install +ln -s ../doc/saves ./save +install/bin/floormat-editor +``` diff --git a/anim-crop-tool/main.cpp b/anim-crop-tool/main.cpp index 7ba31b6e..8d88eab6 100644 --- a/anim-crop-tool/main.cpp +++ b/anim-crop-tool/main.cpp @@ -149,7 +149,7 @@ bool load_directory(anim_group& group, options& opts, anim_atlas_& atlas) for (max = 1; max <= 9999; max++) { char filename[9]; - sprintf(filename, "%04d.png", max); + sprintf(filename, "%04u.png", max); if (!Path::exists(Path::join(input_dir, filename))) break; } @@ -176,7 +176,7 @@ bool load_directory(anim_group& group, options& opts, anim_atlas_& atlas) for (unsigned i = 1; i < max; i++) { char filename[9]; - sprintf(filename, "%04d.png", i); + sprintf(filename, "%04u.png", i); if (!load_file(group, opts, atlas, Path::join(input_dir, filename))) return false; } diff --git a/bench/critter.cpp b/bench/critter.cpp index 418a4f9e..d8548452 100644 --- a/bench/critter.cpp +++ b/bench/critter.cpp @@ -1,4 +1,5 @@ #include "compat/debug.hpp" +#include "compat/assert.hpp" #include "compat/function2.hpp" #include "src/critter.hpp" #include "src/world.hpp" @@ -15,6 +16,7 @@ namespace floormat { namespace { +namespace fm_debug = floormat::debug::detail; using enum rotation; using fu2::function_view; using Function = function_view<Ns() const>; @@ -137,7 +139,7 @@ bool run(world& w, const function_view<Ns() const>& make_dt, if (b) [[likely]] return false; else - fm_emit_assert_fail("false", file, line); + fm_debug::emit_abort(file, line, "false"); }; for (i = 0; true; i++) diff --git a/cmake/msvc.cmake b/cmake/msvc.cmake index e21e17d7..5154c296 100644 --- a/cmake/msvc.cmake +++ b/cmake/msvc.cmake @@ -43,12 +43,12 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) #add_definitions(-D_ITERATOR_DEBUG_LEVEL=0) #add_compile_options(-Qvec-report:2) #add_compile_options(-d2cgsummary -Bt) -add_compile_options(-QIntel-jcc-erratum) +#add_compile_options(-QIntel-jcc-erratum) add_definitions(-D_HAS_EXCEPTIONS=0) if(DEFINED CMAKE_TOOLCHAIN_FILE) # ignore cmake warning: Manually-specified variable not used by the project - set(CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}}") + set(CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}") endif() #set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) diff --git a/compat/array-size.hpp b/compat/array-size.hpp index 15eb66ab..98da4d1e 100644 --- a/compat/array-size.hpp +++ b/compat/array-size.hpp @@ -5,15 +5,19 @@ namespace floormat::detail { template<typename T> struct array_size_; template<typename T, size_t N> struct array_size_<T(&)[N]> : std::integral_constant<size_t, N> {}; +template<typename T, size_t N> struct array_size_<T(*)[N]> : std::integral_constant<size_t, N> {}; template<typename T, size_t N> struct array_size_<T[N]> : std::integral_constant<size_t, N> {}; template<typename T, size_t N> struct array_size_<std::array<T, N>> : std::integral_constant<size_t, N> {}; template<typename T, size_t N> struct array_size_<StaticArray<N, T>> : std::integral_constant<size_t, N> {}; +template<typename C, typename T> struct array_size_<T C::*> : std::integral_constant<size_t, array_size_<std::remove_cvref_t<T>>::value> {}; +//template<typename T, typename U, size_t N> struct array_size_< T(U::*)[N] > : std::integral_constant<size_t, N> {}; // should be redundant + } // namespace floormat::detail namespace floormat { -template<typename T> constexpr inline size_t static_array_size = detail::array_size_<T>::value; -template<typename T> constexpr inline size_t array_size(const T&) noexcept { return detail::array_size_<T>::value; } +template<typename T> constexpr inline size_t static_array_size = detail::array_size_<std::remove_cvref_t<T>>::value; +template<typename T> constexpr inline size_t array_size(const T&) noexcept { return detail::array_size_<std::remove_cvref_t<T>>::value; } } // namespace floormat diff --git a/compat/assert.cpp b/compat/assert.cpp index cd6e9af3..3f030b6b 100644 --- a/compat/assert.cpp +++ b/compat/assert.cpp @@ -1,4 +1,5 @@ #include "assert.hpp" +#include "exception.hpp" #include <cstdlib> #include <cstdio> #include <cstdarg> @@ -7,13 +8,15 @@ #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif -namespace floormat { +namespace floormat::debug::detail { namespace { +bool do_soft_assert = false; + template<bool DoPrefix, bool DoSourceLocation> CORRADE_NEVER_INLINE -void fm_emit_debug_(const char* prefix, const char* file, int line, const char* fmt, va_list arg_ptr) +void emit_debug_(const char* prefix, const char* file, int line, const char* fmt, va_list arg_ptr) { std::fflush(stdout); std::fflush(stderr); @@ -29,31 +32,31 @@ void fm_emit_debug_(const char* prefix, const char* file, int line, const char* } // namespace -void fm_emit_debug(const char* prefix, fm_FORMAT_ARG_MSVC const char* fmt, ...) +void emit_debug(const char* prefix, fm_FORMAT_ARG_MSVC const char* fmt, ...) { va_list arg_ptr; va_start(arg_ptr, fmt); - fm_emit_debug_<true, false>(prefix, nullptr, 0, fmt, arg_ptr); + emit_debug_<true, false>(prefix, nullptr, 0, fmt, arg_ptr); va_end(arg_ptr); } -void fm_emit_debug0(fm_FORMAT_ARG_MSVC const char* fmt, ...) +void emit_debug0(fm_FORMAT_ARG_MSVC const char* fmt, ...) { va_list arg_ptr; va_start(arg_ptr, fmt); - fm_emit_debug_<false, false>(nullptr, nullptr, 0, fmt, arg_ptr); + emit_debug_<false, false>(nullptr, nullptr, 0, fmt, arg_ptr); va_end(arg_ptr); } -void CORRADE_NEVER_INLINE fm_emit_debug_loc(const char* prefix, const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) +void CORRADE_NEVER_INLINE emit_debug_loc(const char* prefix, const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) { va_list arg_ptr; va_start(arg_ptr, fmt); - fm_emit_debug_<true, true>(prefix, file, line, fmt, arg_ptr); + emit_debug_<true, true>(prefix, file, line, fmt, arg_ptr); va_end(arg_ptr); } -void fm_emit_assert_fail(const char* expr, const char* file, int line) +void emit_assert_fail(const char* expr, const char* file, int line) { std::fflush(stdout); std::fflush(stderr); @@ -62,20 +65,29 @@ void fm_emit_assert_fail(const char* expr, const char* file, int line) std::abort(); } -void fm_emit_abort(const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) +void emit_abort(const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) { va_list arg_ptr; va_start(arg_ptr, fmt); - fm_emit_debug_<true, true>("fatal: ", file, line, fmt, arg_ptr); + emit_debug_<true, true>("fatal: ", file, line, fmt, arg_ptr); va_end(arg_ptr); std::abort(); } -void fm_emit_abort() +void emit_abort() { std::fflush(stdout); std::fflush(stderr); std::abort(); } -} // namespace floormat +} // namespace floormat::debug::detail + +using namespace floormat::debug::detail; + +namespace floormat::debug { + +void set_soft_assert_mode(bool value) { do_soft_assert = value; } +bool soft_assert_mode() { return detail::do_soft_assert; } + +} // namespace floormat::debug diff --git a/compat/assert.hpp b/compat/assert.hpp index 8c882b0e..1333c6a6 100644 --- a/compat/assert.hpp +++ b/compat/assert.hpp @@ -12,16 +12,29 @@ #define fm_FORMAT_ARG_MSVC #endif -namespace floormat { +namespace floormat::debug::detail { + +void emit_debug(const char* prefix, fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(2); +void emit_debug0(fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(1); +void emit_debug_loc(const char* prefix, const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(4); +void emit_debug_loc0(const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(3); + +[[noreturn]] CORRADE_NEVER_INLINE void emit_assert_fail(const char* expr, const char* file, int line); +[[noreturn]] CORRADE_NEVER_INLINE void emit_abort(const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(3); +[[noreturn]] CORRADE_NEVER_INLINE void emit_abort(); + +} // namespace floormat::debug::detail + + +namespace floormat::debug { + +void set_soft_assert_mode(bool value); +bool soft_assert_mode(); -void fm_emit_debug(const char* prefix, fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(2); -void fm_emit_debug0(fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(1); -void fm_emit_debug_loc(const char* prefix, const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(4); -void fm_emit_debug_loc0(const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(3); +} // namespace floormat::debug -[[noreturn]] CORRADE_NEVER_INLINE void fm_emit_assert_fail(const char* expr, const char* file, int line); -[[noreturn]] CORRADE_NEVER_INLINE void fm_emit_abort(const char* file, int line, fm_FORMAT_ARG_MSVC const char* fmt, ...) fm_FORMAT_ARG(3); -[[noreturn]] CORRADE_NEVER_INLINE void fm_emit_abort(); + +namespace floormat { } // namespace floormat @@ -30,19 +43,29 @@ void fm_emit_debug_loc0(const char* file, int line, fm_FORMAT_ARG_MSVC const cha #pragma GCC diagnostic ignored "-Wunused-macros" #endif -#define fm_assert(...) ((__VA_ARGS__) ? void() : ::floormat::fm_emit_assert_fail(#__VA_ARGS__, __FILE__, __LINE__)) -#define fm_abort(...) (::floormat::fm_emit_abort(__FILE__, __LINE__, __VA_ARGS__)) -#define fm_warn(...) (::floormat::fm_emit_debug("warning: ", __VA_ARGS__)) -#define fm_error(...) (::floormat::fm_emit_debug("error: ", __VA_ARGS__)) -#define fm_log(...) (::floormat::fm_emit_debug0(__VA_ARGS__)) -#define fm_debug(...) (::floormat::fm_emit_debug0(__VA_ARGS__)) -#define fm_debug_loc(pfx, ...) (::floormat::fm_emit_debug_loc(pfx, __FILE__, __LINE__,__VA_ARGS__)) -#define fm_debug_loc0(...) (::floormat::fm_emit_debug_loc0(__FILE__, __LINE__,__VA_ARGS__)) +#define fm_assert(...) ((__VA_ARGS__) ? void() : ::floormat::debug::detail::emit_assert_fail(#__VA_ARGS__, __FILE__, __LINE__)) +#define fm_abort(...) (::floormat::debug::detail::emit_abort(__FILE__, __LINE__, __VA_ARGS__)) +#define fm_warn(...) (::floormat::debug::detail::emit_debug("warning: ", __VA_ARGS__)) +#define fm_error(...) (::floormat::debug::detail::emit_debug("error: ", __VA_ARGS__)) +#define fm_log(...) (::floormat::debug::detail::emit_debug0(__VA_ARGS__)) +#define fm_debug(...) (::floormat::debug::detail::emit_debug0(__VA_ARGS__)) +#define fm_debug_loc(pfx, ...) (::floormat::debug::detail::emit_debug_loc(pfx, __FILE__, __LINE__,__VA_ARGS__)) +#define fm_debug_loc0(...) (::floormat::debug::detail::emit_debug_loc0(__FILE__, __LINE__,__VA_ARGS__)) + +#if defined FM_NO_DEBUG && !defined FM_NO_DEBUG2 +#define FM_NO_DEBUG2 +#endif #ifndef FM_NO_DEBUG #define fm_debug_assert(...) fm_assert(__VA_ARGS__) #else -#define fm_debug_assert(...) void() +#define fm_debug_assert(...) (void()) +#endif + +#ifndef FM_NO_DEBUG2 +#define fm_debug2_assert(...) fm_assert(__VA_ARGS__) +#else +#define fm_debug2_assert(...) (void()) #endif #define fm_warn_once(...) do { \ @@ -66,7 +89,7 @@ void fm_emit_debug_loc0(const char* file, int line, fm_FORMAT_ARG_MSVC const cha ERR_nospace << #__VA_ARGS__; \ ERR_nospace << " expected: " << a; \ ERR_nospace << " actual: " << b; \ - fm_emit_abort(); \ + ::floormat::debug::detail::emit_abort(); \ } \ })(__VA_ARGS__) @@ -83,7 +106,7 @@ void fm_emit_debug_loc0(const char* file, int line, fm_FORMAT_ARG_MSVC const cha ERR_nospace << #__VA_ARGS__; \ ERR_nospace << "not expected: " << a; \ ERR_nospace << " actual: " << b; \ - fm_emit_abort(); \ + ::floormat::debug::detail::emit_abort(); \ } \ })(__VA_ARGS__) diff --git a/compat/borrowed-ptr-fwd.hpp b/compat/borrowed-ptr-fwd.hpp index 8fcc5dde..48c3e141 100644 --- a/compat/borrowed-ptr-fwd.hpp +++ b/compat/borrowed-ptr-fwd.hpp @@ -2,12 +2,18 @@ namespace floormat { +#define FM_BPTR_DEBUG +//#define FM_NO_WEAK_BPTR + struct bptr_base; template<typename T> class bptr; template<typename T> class weak_bptr; template<typename T> bptr(T* ptr) -> bptr<T>; -template<typename T> bptr(const T* ptr) -> bptr<const T>; + +#ifndef FM_NO_WEAK_BPTR +template<typename T> weak_bptr(const bptr<T>& ptr) -> weak_bptr<T>; +#endif } // namespace floormat diff --git a/compat/borrowed-ptr.cpp b/compat/borrowed-ptr.cpp index 1dded12a..e64468d5 100644 --- a/compat/borrowed-ptr.cpp +++ b/compat/borrowed-ptr.cpp @@ -4,6 +4,8 @@ namespace floormat::detail_bptr { void control_block::decrement(control_block*& blk) noexcept { + if (!blk) + return; auto c2 = --blk->_hard_count; fm_bptr_assert(c2 != (uint32_t)-1); if (c2 == 0) diff --git a/compat/borrowed-ptr.hpp b/compat/borrowed-ptr.hpp index 6a4e79f1..b818154c 100644 --- a/compat/borrowed-ptr.hpp +++ b/compat/borrowed-ptr.hpp @@ -1,9 +1,5 @@ #pragma once #include "borrowed-ptr-fwd.hpp" -#include <compare> - -#define FM_BPTR_DEBUG -#define FM_NO_WEAK_BPTR #ifdef __CLION_IDE__ #define fm_bptr_assert(...) (void(__VA_ARGS__)) @@ -113,10 +109,6 @@ public: bool operator==(const bptr<T>& other) const noexcept requires (!std::is_const_v<T>); bool operator==(const std::nullptr_t& other) const noexcept; - std::strong_ordering operator<=>(const bptr<const T>& other) const noexcept; - std::strong_ordering operator<=>(const bptr<T>& other) const noexcept requires (!std::is_const_v<T>); - std::strong_ordering operator<=>(const std::nullptr_t&) const noexcept; - template<typename U> friend class bptr; template<typename U> friend class weak_bptr; diff --git a/compat/borrowed-ptr.inl b/compat/borrowed-ptr.inl index e1a5f591..d8c561a5 100644 --- a/compat/borrowed-ptr.inl +++ b/compat/borrowed-ptr.inl @@ -9,7 +9,7 @@ namespace floormat { -template<typename T> bptr<T>::~bptr() noexcept { if (blk) detail_bptr::control_block::decrement(blk); } +template<typename T> bptr<T>::~bptr() noexcept { detail_bptr::control_block::decrement(blk); } template<typename T> bptr<T>::bptr(const bptr<std::remove_const_t<T>>& ptr) noexcept requires std::is_const_v<T>: bptr{ptr, nullptr} {} template<typename T> bptr<T>::bptr(bptr<std::remove_const_t<T>>&& ptr) noexcept requires std::is_const_v<T>: bptr{move(ptr), nullptr} {} @@ -41,14 +41,13 @@ template<detail_bptr::DerivedFrom<T> Y> bptr<T>& bptr<T>::operator=(bptr<Y>&& other) noexcept { return _move_assign(move(other)); } -template<typename T> void bptr<T>::reset() noexcept { if (blk) detail_bptr::control_block::decrement(blk); } +template<typename T> void bptr<T>::reset() noexcept { detail_bptr::control_block::decrement(blk); } template<typename T> template<detail_bptr::DerivedFrom<T> Y> void bptr<T>::reset(Y* ptr) noexcept { - if (blk) - detail_bptr::control_block::decrement(blk); + detail_bptr::control_block::decrement(blk); blk = ptr ? new detail_bptr::control_block{const_cast<std::remove_const_t<Y>*>(ptr), 1, #ifndef FM_NO_WEAK_BPTR 1, @@ -95,8 +94,7 @@ bptr<T>& bptr<T>::_copy_assign(const bptr<Y>& other) noexcept { if (blk != other.blk) { - if (blk) - detail_bptr::control_block::decrement(blk); + detail_bptr::control_block::decrement(blk); blk = other.blk; if (blk) { @@ -113,8 +111,7 @@ template<typename T> template<typename Y> bptr<T>& bptr<T>::_move_assign(bptr<Y>&& other) noexcept { - if (blk) - detail_bptr::control_block::decrement(blk); + detail_bptr::control_block::decrement(blk); blk = other.blk; other.blk = nullptr; return *this; @@ -151,16 +148,6 @@ template<typename T> bool bptr<T>::operator==(const bptr<T>& other) const noexce template<typename T> bool bptr<T>::operator==(const std::nullptr_t&) const noexcept { return !blk || !blk->_ptr; } -template<typename T> -std::strong_ordering bptr<T>::operator<=>(const bptr<const T>& other) const noexcept -{ return get() <=> other.get(); } - -template<typename T> std::strong_ordering bptr<T>::operator<=>(const bptr<T>& other) const noexcept requires (!std::is_const_v<T>) -{ return get() <=> other.get(); } - -template<typename T> std::strong_ordering bptr<T>::operator<=>(const std::nullptr_t&) const noexcept -{ return get() <=> (T*)nullptr; } - template<typename T> void bptr<T>::swap(bptr& other) noexcept { floormat::swap(blk, other.blk); } template<typename T> diff --git a/compat/format.hpp b/compat/format.hpp index 1cb4f365..800bce5a 100644 --- a/compat/format.hpp +++ b/compat/format.hpp @@ -9,12 +9,12 @@ namespace fmt { template<> struct formatter<Corrade::Containers::StringView> { template<typename ParseContext> static constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } - template<typename FormatContext> auto format(Corrade::Containers::StringView const& s, FormatContext& ctx); + template<typename FormatContext> auto format(Corrade::Containers::StringView const& s, FormatContext& ctx) const; }; template<> struct formatter<Corrade::Containers::String> { template<typename ParseContext> static constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } - template<typename FormatContext> auto format(Corrade::Containers::String const& s, FormatContext& ctx); + template<typename FormatContext> auto format(Corrade::Containers::String const& s, FormatContext& ctx) const; }; } // namespace fmt @@ -42,7 +42,7 @@ consteval auto operator""_cf() noexcept return FMT_COMPILE(s.data); } #else -using namespace fmt::literals; +using fmt::literals::operator""_cf; #endif namespace floormat { @@ -66,11 +66,11 @@ size_t snformat(char(&buf)[N], Fmt&& fmt, Xs&&... args) } // namespace floormat template<typename FormatContext> -auto fmt::formatter<Corrade::Containers::StringView>::format(Corrade::Containers::StringView const& s, FormatContext& ctx) { +auto fmt::formatter<Corrade::Containers::StringView>::format(Corrade::Containers::StringView const& s, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "{}"_cf, basic_string_view<char>{s.data(), s.size()}); } template<typename FormatContext> -auto fmt::formatter<Corrade::Containers::String>::format(Corrade::Containers::String const& s, FormatContext& ctx) { +auto fmt::formatter<Corrade::Containers::String>::format(Corrade::Containers::String const& s, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "{}"_cf, basic_string_view<char>{s.data(), s.size()}); } diff --git a/compat/fpu.hpp b/compat/fpu.hpp index 9433648a..e72e8fdd 100644 --- a/compat/fpu.hpp +++ b/compat/fpu.hpp @@ -19,7 +19,11 @@ static inline void set_fp_mask() #endif #ifdef __APPLE__ +#if defined __386__ || defined __x86_64__ fesetenv(FE_DFL_DISABLE_SSE_DENORMS_ENV); +#elif defined __arm64__ + fesetenv(FE_DFL_DISABLE_DENORMS_ENV); +#endif #endif #ifdef _WIN32 diff --git a/compat/function2.hpp b/compat/function2.hpp index 9e6b9b50..f53dcf7f 100644 --- a/compat/function2.hpp +++ b/compat/function2.hpp @@ -1082,8 +1082,8 @@ struct internal_capacity { /// Tag to access the structure in a type-safe way data_accessor accessor_; /// The internal capacity we use to allocate in-place - struct { - alignas(Capacity::alignment) unsigned char data[Capacity::capacity]; + struct alignas(Capacity::alignment) { + unsigned char data[Capacity::capacity]; } capacity_; } type; }; diff --git a/compat/heap.hpp b/compat/heap.hpp index 3d901755..665d11e3 100644 --- a/compat/heap.hpp +++ b/compat/heap.hpp @@ -1,6 +1,54 @@ #pragma once #if defined __GLIBCXX__ && defined _GLIBCXX_DEBUG +// Heap implementation -*- C++ -*- + +// Copyright (C) 2001-2024 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library 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, or (at your option) +// any later version. + +// This library 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. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// <http://www.gnu.org/licenses/>. + +/* + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * Copyright (c) 1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ #include <utility> #include <iterator> // regular STL implementation stripped of debug code diff --git a/compat/intrusive-ptr.hpp b/compat/intrusive-ptr.hpp index 8e9b3d6c..c8cf6ff3 100644 --- a/compat/intrusive-ptr.hpp +++ b/compat/intrusive-ptr.hpp @@ -41,10 +41,10 @@ struct refcount_ops using Tref = T*; #endif - static constexpr inline auto incr(T* ptr) noexcept -> size_type; // todo! remove constexpr from everywhere, then move everything to .inl - static constexpr inline auto decr(Tref ptr) noexcept -> size_type; - static constexpr inline auto count(T* ptr) noexcept -> size_type; - static constexpr inline void init_to_1(T* ptr) noexcept; + static constexpr auto incr(T* ptr) noexcept -> size_type; // todo! remove constexpr from everywhere, then move everything to .inl + static constexpr auto decr(Tref ptr) noexcept -> size_type; + static constexpr auto count(T* ptr) noexcept -> size_type; + static constexpr void init_to_1(T* ptr) noexcept; }; template<typename Tag, typename T> diff --git a/compat/unroll.hpp b/compat/unroll.hpp new file mode 100644 index 00000000..2b74ed1a --- /dev/null +++ b/compat/unroll.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace floormat { + +template<uint32_t Max, typename F> +constexpr CORRADE_ALWAYS_INLINE void unroll(F&& fn) +{ + [&]<size_t... Is>(std::index_sequence<Is...>) { + (..., fn(std::integral_constant<size_t, Is>{})); + }(std::make_index_sequence<Max>()); +} + +} // namespace floormat diff --git a/contrib/all.sh b/contrib/all.sh index e7bdd720..abab457e 100644 --- a/contrib/all.sh +++ b/contrib/all.sh @@ -32,19 +32,18 @@ run_test() { fi } -#cd "$(dirname -- "$0" || exit $?)" -cd f:/build/floormat +cd "$(dirname -- "$0" || exit $?)" +#cd f:/build/floormat #set -x configurations=' -clang64 clang -mingw64 gcc -msvc64 msvc-debug clang64 clang-asan - +msvc64 msvc-debug +mingw64 gcc-debug clang64 clang-release -msvc64 msvc +msvc64 msvc-release mingw64 gcc-release +clang64 clang-debug ' printf "%s\\n" "$configurations" | @@ -53,7 +52,7 @@ while read wrapper configs; do ( cd $i bprintf -- "***** Entering directory %s\\n" "$i" - "$wrapper" cmake c:/repos/floormat >/dev/null + "$wrapper" cmake ../.. >/dev/null if test $# -eq 0; then printf -- "> Running ninja for %s\\n" "$i" else diff --git a/doc/render-wall-holes.pdn b/doc/render-wall-holes.pdn Binary files differnew file mode 100644 index 00000000..eaf4ac8b --- /dev/null +++ b/doc/render-wall-holes.pdn diff --git a/draw/ground.cpp b/draw/ground.cpp index 515729b1..b049f96d 100644 --- a/draw/ground.cpp +++ b/draw/ground.cpp @@ -13,6 +13,8 @@ void ground_mesh::draw(tile_shader& shader, chunk& c) { constexpr int quad_index_count = 6; const auto [mesh_, ids, size] = c.ensure_ground_mesh(); + if (size == 0) + return; struct { ground_atlas* atlas = nullptr; size_t pos = 0; } last; GL::MeshView mesh{mesh_}; diff --git a/draw/wall.cpp b/draw/wall.cpp index 1c9c80cc..cbe90b23 100644 --- a/draw/wall.cpp +++ b/draw/wall.cpp @@ -16,6 +16,9 @@ wall_mesh::wall_mesh() = default; void wall_mesh::draw(tile_shader& shader, chunk& c) { const auto [mesh_, ids, size] = c.ensure_wall_mesh(); + if (size == 0) + return; + struct { wall_atlas* atlas = nullptr; size_t pos = 0; } last; GL::MeshView mesh{mesh_}; [[maybe_unused]] size_t draw_count = 0; diff --git a/editor/app.hpp b/editor/app.hpp index b04da7dc..800d3d10 100644 --- a/editor/app.hpp +++ b/editor/app.hpp @@ -108,11 +108,15 @@ private: void draw() override; - void on_mouse_move(const mouse_move_event& event) noexcept override; - void on_mouse_up_down(const mouse_button_event& event, bool is_down) noexcept override; - void on_mouse_scroll(const mouse_scroll_event& event) noexcept override; - void on_key_up_down(const key_event& event, bool is_down) noexcept override; - std::tuple<key, int> resolve_keybinding(int k, int mods); + [[nodiscard]] bool do_imgui_key(const sdl2::EvKey& ev, bool is_down); + [[nodiscard]] bool do_imgui_click(const sdl2::EvClick& ev, bool is_down); + [[nodiscard]] bool do_tests_key(const key_event& ev, bool is_down); + + void on_mouse_move(const mouse_move_event& event, const sdl2::EvMove& ev) noexcept override; + void on_mouse_up_down(const mouse_button_event& event, bool is_down, const sdl2::EvClick& ev) noexcept override; + void on_mouse_scroll(const mouse_scroll_event& event, const sdl2::EvScroll& ev) noexcept override; + void on_key_up_down(const key_event& event, bool is_down, const sdl2::EvKey& ev) noexcept override; + Pair<key, int> resolve_keybinding(int k, int mods); void on_text_input_event(const text_input_event& event) noexcept override; //bool on_text_editing_event(const text_editing_event& event) noexcept override; void on_viewport_event(const Magnum::Math::Vector2<int>& size) noexcept override; diff --git a/editor/camera.cpp b/editor/camera.cpp index 89147946..dfe985b4 100644 --- a/editor/camera.cpp +++ b/editor/camera.cpp @@ -102,15 +102,15 @@ object_id app::get_object_colliding_with_cursor() object_id ret = 0; rtree->Search(t0.data(), t1.data(), [&](uint64_t data, const rect_type& rect) { [[maybe_unused]] auto x = std::bit_cast<collision_data>(data); - if (x.tag == (uint64_t)collision_type::geometry) + if (x.type == (uint64_t)collision_type::geometry) return true; - Vector2 min(rect.m_min[0], rect.m_min[1]), max(rect.m_max[0], rect.m_max[1]); + Vector2 min{rect.m_min}, max{rect.m_max}; if (t0 >= min && t0 <= max) { - if (auto e_ = world.find_object(x.data); + if (auto e_ = world.find_object(x.id); e_ && Vector2ui(e_->bbox_size).product() != 0) { - ret = x.data; + ret = x.id; return false; } } diff --git a/editor/draw.cpp b/editor/draw.cpp index f5a906d2..b366deaa 100644 --- a/editor/draw.cpp +++ b/editor/draw.cpp @@ -139,11 +139,11 @@ void app::draw_collision_boxes() if (x.tag == (uint64_t)collision_type::geometry) return true; #endif - if (x.tag == (uint64_t)collision_type::geometry) + if (x.type == (uint64_t)collision_type::geometry) if (x.pass == (uint64_t)pass_mode::pass) - if (x.data < TILE_COUNT*2+1) + if (x.id < TILE_COUNT*2+1) return true; - Vector2 start(rect.m_min[0], rect.m_min[1]), end(rect.m_max[0], rect.m_max[1]); + Vector2 start{rect.m_min}, end{rect.m_max}; auto size = (end - start); auto center = Vector3(start + size*.5f, 0.f); shader.set_tint(x.pass == (uint64_t)pass_mode::pass ? pass_tint : tint); @@ -186,11 +186,11 @@ void app::draw_collision_boxes() if (x.tag == (uint64_t)collision_type::geometry) return true; #endif - if (x.tag == (uint64_t)collision_type::geometry) + if (x.type == (uint64_t)collision_type::geometry) if (x.pass == (uint64_t)pass_mode::pass) - if (x.data < TILE_COUNT*2+1) + if (x.id < TILE_COUNT*2+1) return true; - Vector2 start(rect.m_min[0], rect.m_min[1]), end(rect.m_max[0], rect.m_max[1]); + Vector2 start{rect.m_min}, end{rect.m_max}; auto size = end - start; auto center = Vector3(start + size*.5f, 0.f); _wireframe->rect.draw(shader, { center, size, 3 }); diff --git a/editor/events.cpp b/editor/events.cpp index 1a259d0e..6aaf1e70 100644 --- a/editor/events.cpp +++ b/editor/events.cpp @@ -2,29 +2,26 @@ #include "floormat/main.hpp" #include "floormat/events.hpp" +#include "main/sdl-fwd.inl" #include "src/world.hpp" #include "keys.hpp" #include "editor.hpp" #include "compat/enum-bitset.hpp" -#include <tuple> +#include <Corrade/Containers/Pair.h> +#include <Corrade/Containers/StructuredBindings.h> #include <Magnum/Platform/Sdl2Application.h> #include <Magnum/ImGuiIntegration/Context.hpp> namespace floormat { -void app::on_focus_in() noexcept {} -void app::on_mouse_enter() noexcept {} -void app::on_any_event(const any_event&) noexcept {} - -#define accessor(type, name) \ - type m_##name = {}; auto name() const noexcept { return m_##name; } +namespace { -static constexpr int fixup_mods_(int mods, int value, int mask) +constexpr int fixup_mods_(int mods, int value, int mask) { return !!(mods & mask) * value; } -static constexpr int fixup_mods(int mods) +constexpr int fixup_mods(int mods) { int ret = 0; ret |= fixup_mods_(mods, kmod_ctrl, KMOD_CTRL); @@ -34,11 +31,49 @@ static constexpr int fixup_mods(int mods) return ret; } +} // namespace + + +using PointerButtons = Platform::Sdl2Application::Pointer; +using PointerEvent = Platform::Sdl2Application::PointerEvent; +using PointerMoveEvent = Platform::Sdl2Application::PointerMoveEvent; + +void app::on_focus_in() noexcept {} +void app::on_mouse_enter() noexcept {} +void app::on_any_event(const any_event&) noexcept {} + +#define accessor(type, name) \ + type m_##name = {}; auto name() const noexcept { return m_##name; } + +bool app::do_imgui_key(const sdl2::EvKey& ev, bool is_down) +{ + if (is_down) + return _imgui->handleKeyPressEvent(ev.val); + else + return _imgui->handleKeyReleaseEvent(ev.val); +} + +bool app::do_imgui_click(const sdl2::EvClick& ev, bool is_down) +{ + if (is_down) + return _imgui->handlePointerPressEvent(ev.val); + else + return _imgui->handlePointerReleaseEvent(ev.val); +} + +bool app::do_tests_key(const key_event& ev, bool is_down) +{ + bool ret = _editor->mode() == editor_mode::tests; + if (ret) + return tests_handle_key(ev, is_down); + return ret; +} + void app::clear_keys(key min_inclusive, key max_exclusive) { auto& keys = *keys_; using key_type = std::decay_t<decltype(keys)>::value_type; - for (key_type i = key_type(min_inclusive); i < key_type(max_exclusive); i++) + for (auto i = key_type(min_inclusive); i < key_type(max_exclusive); i++) { const auto idx = key(i); keys[idx] = false; @@ -52,61 +87,62 @@ void app::clear_keys() key_modifiers = {}; } -void app::on_mouse_move(const mouse_move_event& event) noexcept +void app::on_mouse_move(const mouse_move_event& event, const sdl2::EvMove& ev) noexcept { - if (!(event.position >= Vector2i() && event.position < M->window_size())) - return; + do + { + cursor.in_imgui = _imgui->handlePointerMoveEvent(ev.val); + if (cursor.in_imgui) + break; + if (_editor->mode() == editor_mode::tests) + { + (void)tests_handle_mouse_move(event); + break; + } + } + while (false); - struct { - accessor(Vector2i, position) - } e = {event.position}; - - if ((cursor.in_imgui = _imgui->handleMouseMoveEvent(e))) - void(); - else if (_editor->mode() == editor_mode::tests && tests_handle_mouse_move(event)) - void(); - update_cursor_tile(event.position); + update_cursor_tile(Vector2i(event.position)); do_mouse_move(fixup_mods(event.mods)); } -void app::on_mouse_up_down(const mouse_button_event& event, bool is_down) noexcept +void app::on_mouse_up_down(const mouse_button_event& event, bool is_down, const sdl2::EvClick& ev) noexcept { - if (!(event.position >= Vector2i() && event.position < M->window_size())) + const auto p = Vector2i(event.position); + + if (!(p >= Vector2i{} && p < M->window_size())) return; - struct ev { - enum class Button : std::underlying_type_t<mouse_button> { - Left = mouse_button_left, - Right = mouse_button_right, - Middle = mouse_button_middle, - }; - accessor(Vector2i, position) - accessor(Button, button) - } e = {event.position, ev::Button(event.button)}; - - if ((cursor.in_imgui = is_down ? _imgui->handleMousePressEvent(e) : _imgui->handleMouseReleaseEvent(e))) - void(); - else if (_editor->mode() == editor_mode::tests && tests_handle_mouse_click(event, is_down)) - void(); - else + do + { + cursor.in_imgui = do_imgui_click(ev, is_down); + if (cursor.in_imgui) + break; + if (_editor->mode() == editor_mode::tests) + if (tests_handle_mouse_click(event, is_down)) + break; do_mouse_up_down(event.button, is_down, fixup_mods(event.mods)); + } + while(false); } -void app::on_mouse_scroll(const mouse_scroll_event& event) noexcept +void app::on_mouse_scroll(const mouse_scroll_event& event, const sdl2::EvScroll& ev) noexcept { - if (!(event.position >= Vector2i() && event.position < M->window_size())) - return; + const auto p = Vector2i(event.position); - struct { - accessor(Vector2, offset) - accessor(Vector2i, position) - } e = {event.offset, event.position}; - - if (!(cursor.in_imgui = _imgui->handleMouseScrollEvent(e))) - do_mouse_scroll((int)e.offset()[1]); + do + { + if (p >= Vector2i() && p < M->window_size()) + break; + cursor.in_imgui = _imgui->handleScrollEvent(ev.val); + if (cursor.in_imgui) + break; + do_mouse_scroll((int)ev.val.offset()[1]); + } + while (false); } -auto app::resolve_keybinding(int k_, int mods_) -> std::tuple<key, int> +auto app::resolve_keybinding(int k_, int mods_) -> Pair<key, int> { [[maybe_unused]] constexpr int CTRL = kmod_ctrl; [[maybe_unused]] constexpr int SHIFT = kmod_shift; @@ -190,23 +226,13 @@ auto app::resolve_keybinding(int k_, int mods_) -> std::tuple<key, int> void app::clear_non_global_keys() { clear_keys(key_MIN, key_GLOBAL); } void app::clear_non_repeated_keys() { clear_keys(key_NO_REPEAT, key_COUNT); } -void app::on_key_up_down(const key_event& event, bool is_down) noexcept +void app::on_key_up_down(const key_event& event, bool is_down, const sdl2::EvKey& ev) noexcept { - using KeyEvent = Platform::Sdl2Application::KeyEvent; - struct Ev - { - using Key = KeyEvent::Key; - using Modifier = KeyEvent::Modifier; - using Modifiers = KeyEvent::Modifiers; - accessor(Key, key) - accessor(Modifiers, modifiers) - } e = {Ev::Key(event.key), Ev::Modifier(event.mods)}; - auto [x, mods] = resolve_keybinding(event.key, event.mods); static_assert(key_GLOBAL >= key_NO_REPEAT); - if ((x == key_COUNT || x < key_GLOBAL) && (is_down ? _imgui->handleKeyPressEvent(e) : _imgui->handleKeyReleaseEvent(e)) || - (x == key_COUNT || x == key_escape) && _editor->mode() == editor_mode::tests && tests_handle_key(event, is_down)) + if ((x == key_COUNT || x < key_GLOBAL) && do_imgui_key(ev, is_down) || + (x == key_COUNT || x == key_escape) && do_tests_key(event, is_down)) clear_non_global_keys(); else if (x >= key_NO_REPEAT) is_down && !event.is_repeated ? do_key(x, mods, event.key & ~SDLK_SCANCODE_MASK) : void(); diff --git a/editor/imgui-editors.cpp b/editor/imgui-editors.cpp index 4e331bcf..1dfc18fc 100644 --- a/editor/imgui-editors.cpp +++ b/editor/imgui-editors.cpp @@ -114,7 +114,7 @@ void draw_editor_tile_pane_atlas(ground_editor& ed, StringView name, const bptr< snformat(buf, "##item_{}"_cf, i); const auto uv = atlas->texcoords_for_id(i); constexpr ImVec2 size_2 = { TILE_SIZE[0]*.5f, TILE_SIZE[1]*.5f }; - ImGui::ImageButton(buf, (void*)&atlas->texture(), ImVec2(size_2.x * dpi[0], size_2.y * dpi[1]), + ImGui::ImageButton(buf, atlas->texture().id(), ImVec2(size_2.x * dpi[0], size_2.y * dpi[1]), { uv[3][0], uv[3][1] }, { uv[0][0], uv[0][1] }); if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) ed.select_tile(atlas, i); @@ -181,7 +181,7 @@ void impl_draw_editor_scenery_pane(T& ed, Vector2 dpi) const ImVec2 uv0 {texcoords[3][0], texcoords[3][1]}, uv1 {texcoords[0][0], texcoords[0][1]}; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + std::max(0.f, .5f*(thumbnail_width - img_size.x))); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + .5f*std::max(0.f, row_height - img_size.y)); - ImGui::Image((void*)&atlas.texture(), img_size, uv0, uv1); + ImGui::Image(atlas.texture().id(), img_size, uv0, uv1); click_event(); } if (ImGui::TableSetColumnIndex(1)) diff --git a/editor/imgui-raii.cpp b/editor/imgui-raii.cpp index dd341e2e..7c0ccb8c 100644 --- a/editor/imgui-raii.cpp +++ b/editor/imgui-raii.cpp @@ -1,6 +1,7 @@ #include "imgui-raii.hpp" #include "compat/assert.hpp" #include <cstdio> +#include <utility> #include <Corrade/Containers/StringView.h> #include <Magnum/Magnum.h> #include <Magnum/Math/Color.h> diff --git a/editor/imgui.cpp b/editor/imgui.cpp index 2923c3ea..e25edb1f 100644 --- a/editor/imgui.cpp +++ b/editor/imgui.cpp @@ -107,7 +107,7 @@ float app::draw_main_menu() do_key(key_render_all_z_levels); } - main_menu_height = ImGui::GetContentRegionMax().y; + main_menu_height = ImGui::GetContentRegionAvail().y; } return main_menu_height; } @@ -312,7 +312,7 @@ void app::draw_lightmap_test(float main_menu_height) if (ImGui::Begin("Lightmap", &is_open, flags)) { - ImGui::Image(&shader.accum_texture(), preview_size, {0, 0}, {1, 1}); + ImGui::Image(shader.accum_texture().id(), preview_size, {0, 0}, {1, 1}); ImGui::End(); } else @@ -338,7 +338,7 @@ void app::do_popup_menu() { const auto [id, target] = _popup_target; auto& w = M->world(); - const auto eʹ = w.find_object(id); + auto eʹ = w.find_object(id); if (target == popup_target_type::none || !eʹ) { @@ -372,7 +372,7 @@ void app::do_popup_menu() if (!exists) add_inspector(std::exchange(_popup_target, {})); { - char buf2[10], buf3[128], buf[sizeof buf2 + sizeof buf3 - 1]; + char buf2[10], buf3[128], buf[sizeof buf2 + sizeof buf3 + 3 - 1]; entity_inspector_name(buf2, e.id); entity_friendly_name(buf3, sizeof buf3, e); std::snprintf(buf, sizeof buf, "%s###%s", buf3, buf2); @@ -393,7 +393,7 @@ void app::do_popup_menu() e.destroy_script_pre(eʹ, script_destroy_reason::kill); e.chunk().remove_object(e.index()); e.destroy_script_post(); - e.gone = true; + eʹ.destroy(); } } else diff --git a/editor/inspect-draw.cpp b/editor/inspect-draw.cpp index d5474b8b..ce1e3396 100644 --- a/editor/inspect-draw.cpp +++ b/editor/inspect-draw.cpp @@ -33,7 +33,7 @@ void app::draw_inspector() } auto& e = *eʹ; - char buf2[10], buf3[128], buf[sizeof buf2 + sizeof buf3 - 1]; + char buf2[10], buf3[128], buf[sizeof buf2 + sizeof buf3 + 3 - 1]; ImGui::SetNextWindowSize({375*dpi[0], 0}); entity_inspector_name(buf2, e.id); entity_friendly_name(buf3, sizeof buf3, e); diff --git a/editor/inspect-types.cpp b/editor/inspect-types.cpp index da5a8c9f..ad621cd3 100644 --- a/editor/inspect-types.cpp +++ b/editor/inspect-types.cpp @@ -155,13 +155,11 @@ template<> struct entity_accessors<hole, inspect_intent_t> E::type<uint8_t>::field{"height"_s, &hole::height, &hole::set_height, - [](const hole& x) { return x.flags.is_wall ? st::enabled : st::readonly; }, constantly(constraints::range<uint8_t>{0, tile_size_z}), }, E::type<uint8_t>::field{"z-offset"_s, &hole::z_offset, &hole::set_z_offset, - [](const hole& x) { return x.flags.is_wall ? st::enabled : st::readonly; }, constantly(constraints::range<uint8_t>{0, tile_size_z}), }, E::type<bool>::field{ "enabled"_s, diff --git a/editor/inspect.cpp b/editor/inspect.cpp index ab177fda..f6fcf980 100644 --- a/editor/inspect.cpp +++ b/editor/inspect.cpp @@ -5,6 +5,7 @@ #include "imgui-raii.hpp" #include <cstdio> #include <utility> +#include <Corrade/Containers/StructuredBindings.h> #include <Corrade/Containers/ArrayView.h> #include <Corrade/Containers/String.h> #include <Magnum/Math/Functions.h> @@ -186,7 +187,7 @@ bool do_inspect_field(void* datum, const erased_accessor& accessor, field_repr r { auto* state = ImGui::GetInputTextState(GImGui->ActiveId); if (state) - state->ReloadUserBuf = true; + state->WantReloadUserBuf = true; } return true; } diff --git a/editor/scenery-editor.cpp b/editor/scenery-editor.cpp index 778a0275..7b95df9e 100644 --- a/editor/scenery-editor.cpp +++ b/editor/scenery-editor.cpp @@ -93,12 +93,13 @@ start: while (auto id = a.get_object_colliding_with_cursor()) { for (auto i = 0uz; i < sz; i++) - if (const auto eʹ = es[i]; eʹ->id == id) + if (auto eʹ = es[i]; eʹ->id == id) { - eʹ->destroy_script_pre(eʹ, script_destroy_reason::kill); + auto& e = *eʹ; + e.destroy_script_pre(eʹ, script_destroy_reason::kill); c.remove_object(i); - eʹ->destroy_script_post(); - eʹ->gone = true; + e.destroy_script_post(); + eʹ.destroy(); goto start; } break; diff --git a/editor/tests/hole-test.cpp b/editor/tests/hole-test.cpp index b419f7e8..06624b01 100644 --- a/editor/tests/hole-test.cpp +++ b/editor/tests/hole-test.cpp @@ -131,7 +131,7 @@ void hole_test::draw_ui(app&, float) } { label_left("found", buf, label_width); - ImGui::Text("%s", res.found ? "true" : "false"); + ImGui::Text("%s", res.found() ? "true" : "false"); } ImGui::Unindent(style.FramePadding.x); diff --git a/editor/tests/raycast-test.cpp b/editor/tests/raycast-test.cpp index 07b44eaf..a8902145 100644 --- a/editor/tests/raycast-test.cpp +++ b/editor/tests/raycast-test.cpp @@ -181,7 +181,7 @@ struct raycast_test final : base_test { const char* type; - switch ((collision_type)result.collider.tag) + switch ((collision_type)result.collider.type) { using enum collision_type; default: type = "unknown?!"; break; @@ -199,7 +199,7 @@ struct raycast_test final : base_test do_column("collider"); std::snprintf(buf, array_size(buf), "%s @ %" PRIu64, - type, uint64_t{result.collider.data}); + type, uint64_t{result.collider.id}); { auto b = push_style_color(ImGuiCol_Text, 0xffff00ff_rgbaf); text(buf); } diff --git a/editor/vobj-editor.cpp b/editor/vobj-editor.cpp index 6dc8c907..7e7dde15 100644 --- a/editor/vobj-editor.cpp +++ b/editor/vobj-editor.cpp @@ -48,18 +48,20 @@ void vobj_editor::place_tile(world& w, global_coords pos, const vobj_* x, struct if (!x) { auto [c, t] = w[pos]; +start: const auto& es = c.objects(); -start: while (auto id = a.get_object_colliding_with_cursor()) + while (auto id = a.get_object_colliding_with_cursor()) { for (auto i = (int)(es.size()-1); i >= 0; i--) { - const auto eʹ = es[i]; - if (eʹ->id == id && eʹ->is_virtual()) + auto eʹ = es[i]; + auto& e = *eʹ; + if (e.id == id && eʹ->is_virtual()) { - eʹ->destroy_script_pre(eʹ, script_destroy_reason::kill); + e.destroy_script_pre(eʹ, script_destroy_reason::kill); c.remove_object((unsigned)i); - eʹ->destroy_script_post(); - eʹ->gone = true; + e.destroy_script_post(); + eʹ.destroy(); goto start; } } diff --git a/entity/accessor.hpp b/entity/accessor.hpp index 9dd9c3b6..22bef6e3 100644 --- a/entity/accessor.hpp +++ b/entity/accessor.hpp @@ -2,21 +2,11 @@ #include "util.hpp" #include "erased-constraints.hpp" #include "name-of.hpp" -#include <type_traits> +#include "field-status.hpp" #include <Corrade/Containers/StringView.h> -namespace floormat::erased_constraints { - -struct range; -struct max_length; -struct group; - -} // namespace floormat::erased_constraints - namespace floormat::entities { -enum class field_status : unsigned char { hidden, readonly, enabled, COUNT, }; - struct erased_accessor final { using reader_t = void; using writer_t = void; @@ -40,7 +30,6 @@ struct erased_accessor final { erased_constraints::max_length(*length_fun)(const Object*, const c_length_t*) = nullptr; explicit constexpr erased_accessor() noexcept = default; - constexpr erased_accessor(const erased_accessor&) = default; constexpr erased_accessor(const reader_t* reader, const writer_t* writer, const predicate_t* predicate, const c_range_t* range, const c_length_t* length, StringView field_name, StringView object_name, StringView field_type_name, diff --git a/entity/concepts.hpp b/entity/concepts.hpp index 5e01fcda..6c13299c 100644 --- a/entity/concepts.hpp +++ b/entity/concepts.hpp @@ -3,7 +3,6 @@ #include "compat/function2.hpp" #include "compat/assert.hpp" #include <concepts> -#include <type_traits> namespace floormat::entities { diff --git a/entity/constraints.hpp b/entity/constraints.hpp index 6a5587d5..0bfc2f15 100644 --- a/entity/constraints.hpp +++ b/entity/constraints.hpp @@ -1,8 +1,7 @@ #pragma once #include "compat/limits.hpp" #include "erased-constraints.hpp" -#include <type_traits> -#include <utility> +#include <cr/Pair.h> #include <mg/Vector.h> namespace floormat::entities::limit_detail { @@ -52,7 +51,7 @@ template<typename T> struct range T max = limit_detail::limit_traits<T>::max(); constexpr operator erased_constraints::range() const noexcept; - constexpr operator std::pair<T, T>() const noexcept; + constexpr operator Pair<T, T>() const noexcept; constexpr bool operator==(const range&) const noexcept = default; }; @@ -125,7 +124,7 @@ template<typename T> constexpr range<T>::operator erased_constraints::range() const noexcept { return erased_range_from_range(min, max); } -template<typename T> constexpr range<T>::operator std::pair<T, T>() const noexcept { return { min, max }; } +template<typename T> constexpr range<T>::operator Pair<T, T>() const noexcept { return { min, max }; } template<> struct range<String> { constexpr operator erased_constraints::range() const noexcept { return {}; } }; template<> struct range<StringView> { constexpr operator erased_constraints::range() const noexcept { return {}; } }; diff --git a/entity/erased-constraints.cpp b/entity/erased-constraints.cpp index 6e81820a..4cb915dc 100644 --- a/entity/erased-constraints.cpp +++ b/entity/erased-constraints.cpp @@ -2,17 +2,17 @@ #include "compat/assert.hpp" #include <cmath> #include <limits> -#include <Magnum/Magnum.h> -#include <Magnum/Math/Vector2.h> -#include <Magnum/Math/Vector3.h> -#include <Magnum/Math/Vector4.h> +#include <cr/Pair.h> +#include <mg/Vector2.h> +#include <mg/Vector3.h> +#include <mg/Vector4.h> namespace floormat::entities::erased_constraints { static_assert(sizeof(size_t) == sizeof(uintptr_t)); static_assert(sizeof(size_t) == sizeof(ptrdiff_t)); -template<typename T> std::pair<T, T> range::convert() const +template<typename T> Pair<T, T> range::convert() const { static_assert(sizeof(T) <= sizeof(min)); @@ -103,7 +103,7 @@ template<typename T> std::pair<T, T> range::convert() const } } -template<typename T> using pair2 = std::pair<T, T>; +template<typename T> using pair2 = Pair<T, T>; template pair2<uint8_t> range::convert() const; template pair2<uint16_t> range::convert() const; diff --git a/entity/erased-constraints.hpp b/entity/erased-constraints.hpp index af050bd6..64b469a3 100644 --- a/entity/erased-constraints.hpp +++ b/entity/erased-constraints.hpp @@ -1,4 +1,5 @@ #pragma once +#include <utility> #include <Corrade/Containers/StringView.h> #include <Magnum/Math/Vector4.h> @@ -25,7 +26,7 @@ struct range final element min, max; type_ type = type_none; - template<typename T> std::pair<T, T> convert() const; + template<typename T> Pair<T, T> convert() const; friend bool operator==(const range& a, const range& b); }; diff --git a/entity/field-status.hpp b/entity/field-status.hpp new file mode 100644 index 00000000..f95d54ed --- /dev/null +++ b/entity/field-status.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace floormat::entities { + +enum class field_status : unsigned char { hidden, readonly, enabled, COUNT, }; + +} // namespace floormat::entities diff --git a/entity/field.hpp b/entity/field.hpp new file mode 100644 index 00000000..02782651 --- /dev/null +++ b/entity/field.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include "constraints.hpp" +#include "concepts.hpp" +#include "accessor.hpp" +#include "field-status.hpp" +#include "compat/defs.hpp" + +namespace floormat::entities::detail { +template<typename Obj, typename Type, typename Default, size_t I, typename... Fs> struct find_reader; + +template<typename Obj, typename Type, typename Default, size_t I> struct find_reader<Obj, Type, Default, I> { + using type = Default; + static constexpr size_t index = I; +}; + +template<typename Obj, typename Type, typename Default, size_t I, typename F, typename... Fs> +struct find_reader<Obj, Type, Default, I, F, Fs...> { + using type = typename find_reader<Obj, Type, Default, I+1, Fs...>::type; + static constexpr size_t index = find_reader<Obj, Type, Default, I+1, Fs...>::index; +}; + +template<typename Obj, typename Type, typename Default, size_t I, typename F, typename... Fs> +requires FieldReader<F, Obj, Type> +struct find_reader<Obj, Type, Default, I, F, Fs...> { using type = F; static constexpr size_t index = I; }; + +} // namespace floormat::entities::detail + +namespace floormat::entities { + +constexpr auto constantly(const auto& x) noexcept { + return [x]<typename... Ts> (const Ts&...) constexpr -> const auto& { return x; }; +} + +template<typename Obj, typename Type> struct entity_field_base {}; + +template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> +struct entity_field : entity_field_base<Obj, Type> { +private: + static constexpr auto default_predicate = constantly(field_status::enabled); + static constexpr auto default_c_range = constantly(constraints::range<Type>{}); + static constexpr auto default_c_length = constantly(constraints::max_length{size_t(-1)}); + using default_predicate_t = std::decay_t<decltype(default_predicate)>; + using default_c_range_t = std::decay_t<decltype(default_c_range)>; + using default_c_length_t = std::decay_t<decltype(default_c_length)>; + using c_predicate = detail::find_reader<Obj, field_status, default_predicate_t, 0, Ts...>; + using c_range = detail::find_reader<Obj, constraints::range<Type>, default_c_range_t, 0, Ts...>; + using c_length = detail::find_reader<Obj, constraints::max_length, default_c_length_t, 0, Ts...>; + static constexpr size_t good_arguments = + unsigned(c_predicate::index != sizeof...(Ts)) + + unsigned(c_range::index != sizeof...(Ts)) + + unsigned(c_length::index != sizeof...(Ts)); + static_assert(sizeof...(Ts) == good_arguments, "ignored arguments"); + +public: + using ObjectType = Obj; + using FieldType = Type; + using Reader = R; + using Writer = W; + using Predicate = typename c_predicate::type; + using Range = typename c_range::type; + using Length = typename c_length::type; + + StringView name; + [[fm_no_unique_address]] R reader; + [[fm_no_unique_address]] W writer; + [[fm_no_unique_address]] Predicate predicate; + [[fm_no_unique_address]] Range range; + [[fm_no_unique_address]] Length length; + + fm_DECLARE_DEFAULT_MOVE_COPY_ASSIGNMENTS(entity_field); + + static constexpr decltype(auto) read(const R& reader, const Obj& x) { return detail::read_field<Obj, Type, R>::read(x, reader); } + static constexpr void write(const W& writer, Obj& x, Type v); + constexpr decltype(auto) read(const Obj& x) const { return read(reader, x); } + constexpr void write(Obj& x, Type value) const { write(writer, x, move(value)); } + static constexpr bool can_write = !std::is_same_v<std::nullptr_t, decltype(entity_field<Obj, Type, R, W, Ts...>::writer)>; + + static constexpr field_status is_enabled(const Predicate & p, const Obj& x); + constexpr field_status is_enabled(const Obj& x) const { return is_enabled(predicate, x); } + + static constexpr constraints::range<Type> get_range(const Range& r, const Obj& x); + constexpr constraints::range<Type> get_range(const Obj& x) const { return get_range(range, x); } + static constexpr constraints::max_length get_max_length(const Length& l, const Obj& x); + constexpr constraints::max_length get_max_length(const Obj& x) const { return get_max_length(length, x); } + + constexpr entity_field(StringView name, R r, W w, Ts&&... ts) noexcept : + name{name}, reader{r}, writer{w}, + predicate { std::get<c_predicate::index>(std::forward_as_tuple(ts..., default_predicate)) }, + range { std::get<c_range::index> (std::forward_as_tuple(ts..., default_c_range)) }, + length { std::get<c_length::index> (std::forward_as_tuple(ts..., default_c_length)) } + {} + constexpr erased_accessor erased() const; +}; + +template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> +constexpr void entity_field<Obj, Type, R, W, Ts...>::write(const W& writer, Obj& x, Type v) +{ + static_assert(can_write); detail::write_field<Obj, Type, W>::write(x, writer, move(v)); +} + +template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> +constexpr erased_accessor entity_field<Obj, Type, R, W, Ts...>::erased() const +{ + using reader_t = typename erased_accessor::reader_t; + using writer_t = typename erased_accessor::writer_t; + using predicate_t = typename erased_accessor::predicate_t; + using c_range_t = typename erased_accessor::c_range_t; + using c_length_t = typename erased_accessor::c_length_t; + constexpr auto obj_name = name_of<Obj>, field_name = name_of<Type>; + + constexpr auto reader_fn = [](const void* obj, const reader_t* reader, void* value) { + const auto& obj_ = *static_cast<const Obj*>(obj); + const auto& reader_ = *static_cast<const R*>(reader); + auto& value_ = *static_cast<Type*>(value); + value_ = read(reader_, obj_); + }; + constexpr auto writer_fn = [](void* obj, const writer_t* writer, void* value) { + auto& obj_ = *static_cast<Obj*>(obj); + const auto& writer_ = *static_cast<const W*>(writer); + Type value_ = move(*static_cast<Type*>(value)); + write(writer_, obj_, move(value_)); + }; + constexpr auto predicate_fn = [](const void* obj, const predicate_t* predicate) { + const auto& obj_ = *static_cast<const Obj*>(obj); + const auto& predicate_ = *static_cast<const Predicate*>(predicate); + return is_enabled(predicate_, obj_); + }; + constexpr auto writer_stub_fn = [](void*, const writer_t*, void*) { + fm_abort("no writer for this accessor"); + }; + constexpr bool has_writer = !std::is_same_v<std::decay_t<decltype(writer)>, std::nullptr_t>; + + constexpr auto c_range_fn = [](const void* obj, const c_range_t* reader) -> erased_constraints::range { + return get_range(*static_cast<const Range*>(reader), *static_cast<const Obj*>(obj)); + }; + constexpr auto c_length_fn = [](const void* obj, const c_length_t* reader) -> erased_constraints::max_length { + return get_max_length(*static_cast<const Length*>(reader), *static_cast<const Obj*>(obj)); + }; + return erased_accessor { + (void*)&reader, has_writer ? (void*)&writer : nullptr, + (void*)&predicate, + (void*)&range, (void*)&length, + name, obj_name, field_name, + reader_fn, has_writer ? writer_fn : writer_stub_fn, + predicate_fn, + c_range_fn, c_length_fn, + }; +} + +template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> +constexpr field_status entity_field<Obj, Type, R, W, Ts...>::is_enabled(const Predicate& p, const Obj& x) +{ return detail::read_field<Obj, field_status, Predicate>::read(x, p); } + +template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> +constexpr constraints::range<Type> entity_field<Obj, Type, R, W, Ts...>::get_range(const Range& r, const Obj& x) +{ return detail::read_field<Obj, constraints::range<Type>, Range>::read(x, r); } + +template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> +constexpr constraints::max_length entity_field<Obj, Type, R, W, Ts...>::get_max_length(const Length& l, const Obj& x) +{ return detail::read_field<Obj, constraints::max_length , Length>::read(x, l); } + +} // namespace floormat::entities diff --git a/entity/metadata.hpp b/entity/metadata.hpp index 101af0df..8e3b41d3 100644 --- a/entity/metadata.hpp +++ b/entity/metadata.hpp @@ -1,28 +1,13 @@ #pragma once #include "name-of.hpp" #include "accessor.hpp" -#include "constraints.hpp" -#include "util.hpp" +#include "field.hpp" #include "concepts.hpp" -#include "compat/defs.hpp" -#include <type_traits> -#include <concepts> -#include <limits> -#include <tuple> +#include <utility> #include <array> #include <compat/function2.hpp> #include <Corrade/Containers/StringView.h> -namespace floormat::entities { - -struct inspect_intent_t {}; -struct serialize_intent_t {}; -struct report_intent_t {}; - -template<typename T, typename Intent> struct entity_accessors; - -} // namespace floormat::entities - namespace floormat::entities::detail { template<typename F, typename Tuple, size_t N> @@ -55,158 +40,33 @@ template<typename T> struct decay_tuple_; template<typename... Ts> struct decay_tuple_<std::tuple<Ts...>> { using type = std::tuple<std::decay_t<Ts>...>; }; template<typename T> using decay_tuple = typename decay_tuple_<T>::type; -template<typename Obj, typename Type, typename Default, size_t I, typename... Fs> struct find_reader; - -template<typename Obj, typename Type, typename Default, size_t I> struct find_reader<Obj, Type, Default, I> { - using type = Default; - static constexpr size_t index = I; -}; - -template<typename Obj, typename Type, typename Default, size_t I, typename F, typename... Fs> -struct find_reader<Obj, Type, Default, I, F, Fs...> { - using type = typename find_reader<Obj, Type, Default, I+1, Fs...>::type; - static constexpr size_t index = find_reader<Obj, Type, Default, I+1, Fs...>::index; -}; - -template<typename Obj, typename Type, typename Default, size_t I, typename F, typename... Fs> -requires FieldReader<F, Obj, Type> -struct find_reader<Obj, Type, Default, I, F, Fs...> { using type = F; static constexpr size_t index = I; }; - } // namespace floormat::entities::detail namespace floormat::entities { -constexpr auto constantly(const auto& x) noexcept { - return [x]<typename... Ts> (const Ts&...) constexpr -> const auto& { return x; }; -} - -template<typename Obj, typename Type> struct entity_field_base {}; - -template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> -struct entity_field : entity_field_base<Obj, Type> { -private: - static constexpr auto default_predicate = constantly(field_status::enabled); - static constexpr auto default_c_range = constantly(constraints::range<Type>{}); - static constexpr auto default_c_length = constantly(constraints::max_length{size_t(-1)}); - using default_predicate_t = std::decay_t<decltype(default_predicate)>; - using default_c_range_t = std::decay_t<decltype(default_c_range)>; - using default_c_length_t = std::decay_t<decltype(default_c_length)>; - using c_predicate = detail::find_reader<Obj, field_status, default_predicate_t, 0, Ts...>; - using c_range = detail::find_reader<Obj, constraints::range<Type>, default_c_range_t, 0, Ts...>; - using c_length = detail::find_reader<Obj, constraints::max_length, default_c_length_t, 0, Ts...>; - static constexpr size_t good_arguments = - unsigned(c_predicate::index != sizeof...(Ts)) + - unsigned(c_range::index != sizeof...(Ts)) + - unsigned(c_length::index != sizeof...(Ts)); - static_assert(sizeof...(Ts) == good_arguments, "ignored arguments"); - -public: - using ObjectType = Obj; - using FieldType = Type; - using Reader = R; - using Writer = W; - using Predicate = typename c_predicate::type; - using Range = typename c_range::type; - using Length = typename c_length::type; - - StringView name; - [[fm_no_unique_address]] R reader; - [[fm_no_unique_address]] W writer; - [[fm_no_unique_address]] Predicate predicate; - [[fm_no_unique_address]] Range range; - [[fm_no_unique_address]] Length length; - - fm_DECLARE_DEFAULT_MOVE_COPY_ASSIGNMENTS(entity_field); - - static constexpr decltype(auto) read(const R& reader, const Obj& x) { return detail::read_field<Obj, Type, R>::read(x, reader); } - static constexpr void write(const W& writer, Obj& x, Type v); - constexpr decltype(auto) read(const Obj& x) const { return read(reader, x); } - constexpr void write(Obj& x, Type value) const { write(writer, x, move(value)); } - static constexpr bool can_write = !std::is_same_v<std::nullptr_t, decltype(entity_field<Obj, Type, R, W, Ts...>::writer)>; - - static constexpr field_status is_enabled(const Predicate & p, const Obj& x); - constexpr field_status is_enabled(const Obj& x) const { return is_enabled(predicate, x); } - - static constexpr constraints::range<Type> get_range(const Range& r, const Obj& x); - constexpr constraints::range<Type> get_range(const Obj& x) const { return get_range(range, x); } - static constexpr constraints::max_length get_max_length(const Length& l, const Obj& x); - constexpr constraints::max_length get_max_length(const Obj& x) const { return get_max_length(length, x); } - - constexpr entity_field(StringView name, R r, W w, Ts&&... ts) noexcept : - name{name}, reader{r}, writer{w}, - predicate { std::get<c_predicate::index>(std::forward_as_tuple(ts..., default_predicate)) }, - range { std::get<c_range::index> (std::forward_as_tuple(ts..., default_c_range)) }, - length { std::get<c_length::index> (std::forward_as_tuple(ts..., default_c_length)) } - {} - constexpr erased_accessor erased() const; -}; - -template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> -constexpr void entity_field<Obj, Type, R, W, Ts...>::write(const W& writer, Obj& x, Type v) +template<typename F, typename Tuple> +constexpr void visit_tuple(F&& fun, Tuple&& tuple) { - static_assert(can_write); detail::write_field<Obj, Type, W>::write(x, writer, move(v)); + using Size = std::tuple_size<std::decay_t<Tuple>>; + if constexpr(Size() > 0) + detail::visit_tuple<F, Tuple, 0>(floormat::forward<F>(fun), floormat::forward<Tuple>(tuple)); } -template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> -constexpr erased_accessor entity_field<Obj, Type, R, W, Ts...>::erased() const +template<typename F, typename Tuple> +constexpr bool find_in_tuple(F&& fun, Tuple&& tuple) { - using reader_t = typename erased_accessor::reader_t; - using writer_t = typename erased_accessor::writer_t; - using predicate_t = typename erased_accessor::predicate_t; - using c_range_t = typename erased_accessor::c_range_t; - using c_length_t = typename erased_accessor::c_length_t; - constexpr auto obj_name = name_of<Obj>, field_name = name_of<Type>; - - constexpr auto reader_fn = [](const void* obj, const reader_t* reader, void* value) { - const auto& obj_ = *static_cast<const Obj*>(obj); - const auto& reader_ = *static_cast<const R*>(reader); - auto& value_ = *static_cast<Type*>(value); - value_ = read(reader_, obj_); - }; - constexpr auto writer_fn = [](void* obj, const writer_t* writer, void* value) { - auto& obj_ = *static_cast<Obj*>(obj); - const auto& writer_ = *static_cast<const W*>(writer); - Type value_ = move(*static_cast<Type*>(value)); - write(writer_, obj_, move(value_)); - }; - constexpr auto predicate_fn = [](const void* obj, const predicate_t* predicate) { - const auto& obj_ = *static_cast<const Obj*>(obj); - const auto& predicate_ = *static_cast<const Predicate*>(predicate); - return is_enabled(predicate_, obj_); - }; - constexpr auto writer_stub_fn = [](void*, const writer_t*, void*) { - fm_abort("no writer for this accessor"); - }; - constexpr bool has_writer = !std::is_same_v<std::decay_t<decltype(writer)>, std::nullptr_t>; - - constexpr auto c_range_fn = [](const void* obj, const c_range_t* reader) -> erased_constraints::range { - return get_range(*static_cast<const Range*>(reader), *reinterpret_cast<const Obj*>(obj)); - }; - constexpr auto c_length_fn = [](const void* obj, const c_length_t* reader) -> erased_constraints::max_length { - return get_max_length(*static_cast<const Length*>(reader), *reinterpret_cast<const Obj*>(obj)); - }; - return erased_accessor { - (void*)&reader, has_writer ? (void*)&writer : nullptr, - (void*)&predicate, - (void*)&range, (void*)&length, - name, obj_name, field_name, - reader_fn, has_writer ? writer_fn : writer_stub_fn, - predicate_fn, - c_range_fn, c_length_fn, - }; + using Size = std::tuple_size<std::decay_t<Tuple>>; + if constexpr(Size() > 0) + return detail::find_in_tuple<F, Tuple, 0>(floormat::forward<F>(fun), floormat::forward<Tuple>(tuple)); + else + return false; } -template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> -constexpr field_status entity_field<Obj, Type, R, W, Ts...>::is_enabled(const Predicate& p, const Obj& x) -{ return detail::read_field<Obj, field_status, Predicate>::read(x, p); } - -template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> -constexpr constraints::range<Type> entity_field<Obj, Type, R, W, Ts...>::get_range(const Range& r, const Obj& x) -{ return detail::read_field<Obj, constraints::range<Type>, Range>::read(x, r); } +struct inspect_intent_t {}; +struct serialize_intent_t {}; +struct report_intent_t {}; -template<typename Obj, typename Type, FieldReader<Obj, Type> R, FieldWriter<Obj, Type> W, typename... Ts> -constexpr constraints::max_length entity_field<Obj, Type, R, W, Ts...>::get_max_length(const Length& l, const Obj& x) -{ return detail::read_field<Obj, constraints::max_length , Length>::read(x, l); } +template<typename T, typename Intent> struct entity_accessors; template<typename Obj> struct Entity final { @@ -228,24 +88,6 @@ struct Entity final { }; }; -template<typename F, typename Tuple> -constexpr void visit_tuple(F&& fun, Tuple&& tuple) -{ - using Size = std::tuple_size<std::decay_t<Tuple>>; - if constexpr(Size() > 0) - detail::visit_tuple<F, Tuple, 0>(floormat::forward<F>(fun), floormat::forward<Tuple>(tuple)); -} - -template<typename F, typename Tuple> -constexpr bool find_in_tuple(F&& fun, Tuple&& tuple) -{ - using Size = std::tuple_size<std::decay_t<Tuple>>; - if constexpr(Size() > 0) - return detail::find_in_tuple<F, Tuple, 0>(floormat::forward<F>(fun), floormat::forward<Tuple>(tuple)); - else - return false; -} - constexpr inline auto ignored_write = []<typename O, typename T>(O&, T) {}; } // namespace floormat::entities @@ -264,6 +106,8 @@ public: static constexpr auto accessors = entities::entity_accessors<T, Intent>::accessors(); static constexpr size_t size = std::tuple_size_v<std::decay_t<decltype(accessors)>>; static constexpr auto erased_accessors = erased_helper(accessors, std::make_index_sequence<size>{}); + + entity_metadata() = default; }; template<typename T, typename Intent> diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index e258fa87..303afda2 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -237,9 +237,7 @@ set(system "") if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.25) set(system "SYSTEM") endif() -if(NOT FLOORMAT_SUBMODULE-DEPENDENCIES OR NOT FLOORMAT_SUBMODULE-SDL2) - add_subdirectory(sdl2 ${system}) -endif() + if(FLOORMAT_SUBMODULE-DEPENDENCIES) sets(PATH IMGUI_DIR "${CMAKE_CURRENT_SOURCE_DIR}/imgui") @@ -270,19 +268,6 @@ if(FLOORMAT_SUBMODULE-DEPENDENCIES) install(TARGETS benchmark RUNTIME DESTINATION "bin") endfunction() - function(fm_add_luajit) - set(CMAKE_C_STANDARD 11) - set(CMAKE_C_EXTENSIONS 1) - unset(CMAKE_C_VISIBILITY_PRESET) - unset(CMAKE_VISIBILITY_INLINES_HIDDEN) - if(CMAKE_COMPILER_IS_GNUCXX) - # HACK - add_compile_options(-fno-sanitize=all) - add_link_options(-fno-sanitize=all) - add_subdirectory(luajit ${system}) - endif() - endfunction() - fm_run_hook(fm-userconfig-external) function(fm_add_libs) @@ -297,7 +282,6 @@ if(FLOORMAT_SUBMODULE-DEPENDENCIES) fm_add_benchmark() endif() fm_add_sdl2() - #fm_add_luajit() if(MSVC) add_compile_options(-GR-) else() diff --git a/external/benchmark b/external/benchmark -Subproject b40db869451036d222d155bc8cd6420c2fb9527 +Subproject b884717437fc468929cd47ca6a374005357ff18 diff --git a/external/corrade b/external/corrade -Subproject 1bc62dcd1430d93070343dc0d32b6a7db359ecb +Subproject 76d9b27792c1b4ff948a84870b34f805be08da4 diff --git a/external/fmt b/external/fmt -Subproject 6159e2b0ab58e6e22164272d22e929cbc9e34e2 +Subproject f2cec917dafffa45d5cb4573c68fdf706e2b541 diff --git a/external/imgui b/external/imgui -Subproject c554c402d307e6fde50e895adad009e23343ed2 +Subproject 2d20e13746c05bfe36d4a57ac465e7a9fe9005d diff --git a/external/luajit b/external/luajit deleted file mode 160000 -Subproject 0a29d98f56c1efe1c731c3da22e871e6ccb79d2 diff --git a/external/magnum b/external/magnum -Subproject 4dd86be6376275c0620d2470c2038f331462702 +Subproject 2af1aa24ab92b9fe92a79b8b2ba697ed20c668e diff --git a/external/magnum-integration b/external/magnum-integration -Subproject bf09698491f2061733dc263f375da1f02f41d8e +Subproject ed2730d95836fdcbdbb659c5c070bcde2eb0717 diff --git a/external/magnum-plugins b/external/magnum-plugins -Subproject 9a46ddaf4e7702954c76c12ff00cf9e15fc7b61 +Subproject 790d645741f4be0ce898691a8c9a99c2d9a5c8e diff --git a/external/robin-map b/external/robin-map -Subproject 3bf03fdc4f71f86b496bb73a4fda6034e44fdb8 +Subproject e662db269a48da97fdc480072519cd94b51ece4 diff --git a/external/sdl2 b/external/sdl2 -Subproject a29f37c14abc2ae418e770353657e872a70d90b +Subproject 28472738857ff194b5b6b8778a1975cb605210a diff --git a/floormat/app.hpp b/floormat/app.hpp index 02f7d24c..9d1ac7e3 100644 --- a/floormat/app.hpp +++ b/floormat/app.hpp @@ -1,4 +1,5 @@ #pragma once +#include "main/sdl-fwd.hpp" namespace Magnum::Math { template<typename T> class Vector2; template<class T> class Nanoseconds; } @@ -35,10 +36,10 @@ struct floormat_app virtual void draw() = 0; virtual z_bounds get_z_bounds() = 0; - virtual void on_mouse_move(const mouse_move_event& event) noexcept = 0; - virtual void on_mouse_up_down(const mouse_button_event& event, bool is_down) noexcept = 0; - virtual void on_mouse_scroll(const mouse_scroll_event& event) noexcept = 0; - virtual void on_key_up_down(const key_event& event, bool is_down) noexcept = 0; + virtual void on_mouse_move(const mouse_move_event& event, const sdl2::EvMove& ev) noexcept = 0; + virtual void on_mouse_up_down(const mouse_button_event& event, bool is_down, const sdl2::EvClick& ev) noexcept = 0; + virtual void on_mouse_scroll(const mouse_scroll_event& event, const sdl2::EvScroll& ev) noexcept = 0; + virtual void on_key_up_down(const key_event& event, bool is_down, const sdl2::EvKey& ev) noexcept = 0; virtual void on_text_input_event(const text_input_event& event) noexcept = 0; //virtual bool on_text_editing_event(const text_editing_event& event) noexcept = 0; virtual void on_viewport_event(const Magnum::Math::Vector2<int>& size) noexcept = 0; diff --git a/floormat/events.hpp b/floormat/events.hpp index 68c2bedd..af3e6248 100644 --- a/floormat/events.hpp +++ b/floormat/events.hpp @@ -14,22 +14,25 @@ enum mouse_button : unsigned char { }; struct mouse_button_event final { - Vector2i position; + Vector2 position; int mods = 0; mouse_button button = mouse_button_none; uint8_t click_count = 0; + bool is_primary : 1 = false; }; struct mouse_move_event final { - Vector2i position; - mouse_button buttons = mouse_button_none; + Vector2 position; int mods = 0; + mouse_button buttons = mouse_button_none; + bool is_primary : 1 = false; }; struct mouse_scroll_event final { Magnum::Vector2 offset; - Vector2i position; + Vector2 position; int mods = 0; + bool is_primary : 1 = false; }; struct text_input_event final { diff --git a/hash/xxhash.cpp b/hash/xxhash.cpp index 157232b9..59691baf 100644 --- a/hash/xxhash.cpp +++ b/hash/xxhash.cpp @@ -6,6 +6,8 @@ #ifdef __clang__ #pragma GCC diagnostic ignored "-Wdisabled-macro-expansion" #pragma GCC diagnostic ignored "-Wused-but-marked-unused" +#elif defined _MSC_VER +#pragma warning(disable: 4310) #endif #include "xxhash.inl" diff --git a/main/events.cpp b/main/events.cpp index 1f1c81a2..59dfe28e 100644 --- a/main/events.cpp +++ b/main/events.cpp @@ -1,84 +1,102 @@ #include "main-impl.hpp" #include "floormat/app.hpp" #include "floormat/events.hpp" +#include "sdl-fwd.inl" #include <cstring> #include <SDL_events.h> #include <SDL_keyboard.h> namespace floormat { -void main_impl::viewportEvent(Platform::Sdl2Application::ViewportEvent& event) +namespace { + +using Buttons = Platform::Sdl2Application::Pointers; + +mouse_button pointer_to_button_mask(Buttons b) { return mouse_button((uint8_t)b); } + +any_event make_any_event(const SDL_Event& e) +{ + static_assert(sizeof(SDL_Event) <= sizeof(any_event::buf)); + any_event ret; + std::memcpy(&ret.buf, &e, sizeof(SDL_Event)); + return ret; +} + +} // namespace + +void main_impl::viewportEvent(ViewportEvent& event) { _framebuffer_size = event.framebufferSize(); recalc_viewport(event.framebufferSize(), event.windowSize()); app.on_viewport_event(event.framebufferSize()); } -void main_impl::mousePressEvent(Platform::Sdl2Application::MouseEvent& event) +void main_impl::pointerPressEvent(PointerEvent& ev) { - app.on_mouse_up_down({event.position() * _virtual_scale, - (SDL_Keymod)(uint16_t)event.modifiers(), - mouse_button(SDL_BUTTON((uint8_t)event.button())), - uint8_t(std::min(255, event.clickCount()))}, - true); + app.on_mouse_up_down({ + ev.position() * _virtual_scale, + (SDL_Keymod)(uint16_t)ev.modifiers(), + pointer_to_button_mask(ev.pointer()), + uint8_t(std::min(255, ev.clickCount())), + }, true, {ev}); } -void main_impl::mouseReleaseEvent(Platform::Sdl2Application::MouseEvent& event) +void main_impl::pointerReleaseEvent(PointerEvent& ev) { - app.on_mouse_up_down({event.position() * _virtual_scale, - (SDL_Keymod)(uint16_t)event.modifiers(), - mouse_button(SDL_BUTTON((uint8_t)event.button())), - uint8_t(std::min(255, event.clickCount()))}, - false); + app.on_mouse_up_down({ + ev.position() * _virtual_scale, + (SDL_Keymod)(uint16_t)ev.modifiers(), + pointer_to_button_mask(ev.pointer()), + uint8_t(std::min(255, ev.clickCount())), + }, false, {ev}); } -void main_impl::mouseMoveEvent(Platform::Sdl2Application::MouseMoveEvent& event) +void main_impl::pointerMoveEvent(PointerMoveEvent& ev) { - app.on_mouse_move({event.position() * _virtual_scale, - (mouse_button)(uint8_t)uint32_t{event.buttons()}, - (SDL_Keymod)(uint16_t)event.modifiers()}); + app.on_mouse_move({ + ev.position() * _virtual_scale, + (SDL_Keymod)(uint16_t)ev.modifiers(), + pointer_to_button_mask(ev.pointers()), + ev.isPrimary(), + }, {ev}); } -void main_impl::mouseScrollEvent(Platform::Sdl2Application::MouseScrollEvent& event) +void main_impl::scrollEvent(ScrollEvent& ev) { - app.on_mouse_scroll({event.offset(), event.position() * _virtual_scale, - (SDL_Keymod)(uint16_t)event.modifiers()}); + app.on_mouse_scroll({ + ev.offset(), ev.position() * _virtual_scale, + (SDL_Keymod)(uint16_t)ev.modifiers(), + }, {ev}); } -void main_impl::textInputEvent(Platform::Sdl2Application::TextInputEvent& event) +void main_impl::textInputEvent(TextInputEvent& event) { app.on_text_input_event({event.text()}); } #if 0 -void main_impl::textEditingEvent(Platform::Sdl2Application::TextEditingEvent& event) +void main_impl::textEditingEvent(TextEditingEvent& event) { app.on_text_editing_event({event.text(), event.start(), event.length()}) } #endif -void main_impl::keyPressEvent(Platform::Sdl2Application::KeyEvent& event) -{ - app.on_key_up_down({(SDL_Keycode)(uint32_t)event.key(), - (SDL_Keymod)(uint16_t)event.modifiers(), - event.isRepeated()}, - true); -} - -void main_impl::keyReleaseEvent(Platform::Sdl2Application::KeyEvent& event) +void main_impl::keyPressEvent(KeyEvent& event) { - app.on_key_up_down({(SDL_Keycode)(uint32_t)event.key(), - (SDL_Keymod)(uint16_t)event.modifiers(), - event.isRepeated()}, - false); + app.on_key_up_down({ + (SDL_Keycode)(uint32_t)event.key(), + (SDL_Keymod)(uint16_t)event.modifiers(), + event.isRepeated() + }, true, {event}); } -static any_event make_any_event(const SDL_Event& e) +void main_impl::keyReleaseEvent(KeyEvent& event) { - static_assert(sizeof(SDL_Event) <= sizeof(any_event::buf)); - any_event ret; - std::memcpy(&ret.buf, &e, sizeof(SDL_Event)); - return ret; + app.on_key_up_down({ + (SDL_Keycode)(uint32_t)event.key(), + (SDL_Keymod)(uint16_t)event.modifiers(), + event.isRepeated() + }, false, {event}); } void main_impl::anyEvent(SDL_Event& event) @@ -108,7 +126,7 @@ void main_impl::anyEvent(SDL_Event& event) int floormat_main::get_mods() noexcept { - return (int)SDL_GetModState(); + return SDL_GetModState(); } } // namespace floormat diff --git a/main/main-impl.hpp b/main/main-impl.hpp index 3fb8bac5..17aecc72 100644 --- a/main/main-impl.hpp +++ b/main/main-impl.hpp @@ -9,6 +9,7 @@ #include "shaders/texture-unit-cache.hpp" #include "shaders/shader.hpp" #include "shaders/lightmap.hpp" +#include "main/sdl-fwd.hpp" #include "main/clickable.hpp" #include <Corrade/Containers/Array.h> #include <Corrade/Containers/String.h> @@ -76,10 +77,10 @@ struct main_impl final : private Platform::Sdl2Application, public floormat_main const Platform::Sdl2Application& application() const noexcept override; [[maybe_unused]] void viewportEvent(ViewportEvent& event) override; - [[maybe_unused]] void mousePressEvent(MouseEvent& event) override; - [[maybe_unused]] void mouseReleaseEvent(MouseEvent& event) override; - [[maybe_unused]] void mouseMoveEvent(MouseMoveEvent& event) override; - [[maybe_unused]] void mouseScrollEvent(MouseScrollEvent& event) override; + [[maybe_unused]] void pointerPressEvent(PointerEvent& ev) override; + [[maybe_unused]] void pointerReleaseEvent(PointerEvent& ev) override; + [[maybe_unused]] void pointerMoveEvent(PointerMoveEvent& ev) override; + [[maybe_unused]] void scrollEvent(ScrollEvent& ev) override; [[maybe_unused]] void textInputEvent(TextInputEvent& event) override; //[[maybe_unused]] void textEditingEvent(TextEditingEvent& event) override; [[maybe_unused]] void keyPressEvent(KeyEvent& event) override; diff --git a/main/sdl-fwd.hpp b/main/sdl-fwd.hpp new file mode 100644 index 00000000..776ec0a8 --- /dev/null +++ b/main/sdl-fwd.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace floormat::sdl2 { + +struct EvButtons; +struct EvClick; +struct EvMove; +struct EvScroll; +struct EvKey; + +} // namespace floormat::sdl2 diff --git a/main/sdl-fwd.inl b/main/sdl-fwd.inl new file mode 100644 index 00000000..25bc45ee --- /dev/null +++ b/main/sdl-fwd.inl @@ -0,0 +1,15 @@ +#pragma once +#include "sdl-fwd.hpp" +#include <Magnum/Platform/Sdl2Application.h> + +namespace floormat::sdl2 { + +using App = Platform::Sdl2Application; + +struct EvButtons { App::Pointer& val; }; +struct EvClick { App::PointerEvent& val; }; +struct EvMove { App::PointerMoveEvent& val; }; +struct EvScroll { App::ScrollEvent& val; }; +struct EvKey { App::KeyEvent& val; }; + +} // namespace floormat::sdl2 diff --git a/run-show-coverage.sh b/run-show-coverage.sh index 9b5f6974..f2fe844e 100644 --- a/run-show-coverage.sh +++ b/run-show-coverage.sh @@ -108,7 +108,8 @@ if test $run -gt 0; then Windows_NT) profdir="$(cygpath -m -- "$PWD")" ;; *) profdir="$PWD" ;; esac - LLVM_PROFILE_FILE="$profdir/${prof}.profraw" "$exe" --gpu-debug full --vsync on + LLVM_PROFILE_FILE="$profdir/${prof}.profraw" \ + "$exe" --magnum-gpu-validation=full --vsync=1 fi if test $generate -gt 0; then diff --git a/shaders/shader.hpp b/shaders/shader.hpp index 7f58e276..5eb384c5 100644 --- a/shaders/shader.hpp +++ b/shaders/shader.hpp @@ -37,7 +37,7 @@ struct tile_shader final : private GL::AbstractShaderProgram template<typename T = float> static constexpr Math::Vector2<T> project(const Math::Vector3<T>& pt); template<typename T = float> static constexpr Math::Vector2<T> unproject(const Math::Vector2<T>& px); - template<typename T, typename... Xs> decltype(auto) draw(GL::AbstractTexture& tex, T&& mesh, Xs&&... xs); + template<typename T, typename... Xs> GL::AbstractShaderProgram& draw(GL::AbstractTexture& tex, T&& mesh, Xs&&... xs); static constexpr Vector2s max_screen_tiles = {8, 8}; static constexpr float character_depth_offset = 1 + 2./64; @@ -74,7 +74,7 @@ private: }; template<typename T, typename... Xs> -decltype(auto) tile_shader::draw(GL::AbstractTexture& tex, T&& mesh, Xs&&... xs) +GL::AbstractShaderProgram& tile_shader::draw(GL::AbstractTexture& tex, T&& mesh, Xs&&... xs) { draw_pre(tex); decltype(auto) ret = GL::AbstractShaderProgram::draw(forward<T>(mesh), forward<Xs>(xs)...); diff --git a/src/RTree-fwd.h b/src/RTree-fwd.h index a93ec168..e5bcb35e 100644 --- a/src/RTree-fwd.h +++ b/src/RTree-fwd.h @@ -1,5 +1,12 @@ #pragma once +#include "object-id.hpp" template<class DATATYPE, class ELEMTYPE, int NUMDIMS, class ELEMTYPEREAL = ELEMTYPE, int TMAXNODES = 8, int TMINNODES = TMAXNODES / 2> class RTree; + +namespace floormat { + +using Chunk_RTree = ::RTree<object_id, float, 2, float>; + +} // namespace floormat diff --git a/src/RTree.cpp b/src/RTree.cpp index f77d0697..fcefdbcf 100644 --- a/src/RTree.cpp +++ b/src/RTree.cpp @@ -64,8 +64,7 @@ template<typename T> T* rtree_pool<T>::construct() template<typename T> void rtree_pool<T>::free(T* ptr) { ptr->~T(); - node_p p = {.ptr = ptr }; - node_u* n = p.data_ptr; + auto* n = reinterpret_cast<node_u*>(ptr); n->next = free_list; free_list = n; } diff --git a/src/RTree.h b/src/RTree.h index 7b8eeb7c..bbf9a486 100644 --- a/src/RTree.h +++ b/src/RTree.h @@ -37,13 +37,9 @@ template<typename T> struct rtree_pool final void free(T* pool); union node_u { - union { T data; }; + union { T data; char _empty = {}; }; node_u* next; }; - union node_p { - T* ptr; - node_u* data_ptr; - }; private: node_u* free_list = nullptr; diff --git a/src/anim-atlas.cpp b/src/anim-atlas.cpp index 43ba69b2..ee05987f 100644 --- a/src/anim-atlas.cpp +++ b/src/anim-atlas.cpp @@ -11,12 +11,14 @@ namespace floormat { +namespace { +constexpr inline char name_array[][3] = { "n", "ne", "e", "se", "s", "sw", "w", "nw", }; +constexpr inline auto rot_count = size_t(rotation_COUNT); +} // namespace + template class bptr<anim_atlas>; template class bptr<const anim_atlas>; -static constexpr const char name_array[][3] = { "n", "ne", "e", "se", "s", "sw", "w", "nw", }; -static constexpr inline auto rot_count = size_t(rotation_COUNT); - static_assert(array_size(name_array) == rot_count); static_assert(rot_count == 8); diff --git a/src/anim.cpp b/src/anim.cpp index 717b3ee6..db724154 100644 --- a/src/anim.cpp +++ b/src/anim.cpp @@ -1,6 +1,7 @@ #include "anim.hpp" #include "compat/exception.hpp" #include <cmath> +#include <utility> namespace floormat { diff --git a/src/chunk-collision.cpp b/src/chunk-collision.cpp index ec6f4ff2..b0170755 100644 --- a/src/chunk-collision.cpp +++ b/src/chunk-collision.cpp @@ -10,6 +10,7 @@ #include "src/hole.hpp" #include "src/wall-atlas.hpp" #include <bit> +#include <utility> #include <Corrade/Containers/StructuredBindings.h> #include <Corrade/Containers/Pair.h> @@ -17,7 +18,7 @@ namespace floormat { bool collision_data::operator==(const collision_data&) const noexcept = default; bool chunk::bbox::operator==(const floormat::chunk::bbox& other) const noexcept = default; -chunk::RTree* chunk::rtree() noexcept { ensure_passability(); return &*_rtree; } +Chunk_RTree* chunk::rtree() noexcept { ensure_passability(); return &*_rtree; } world& chunk::world() noexcept { return *_world; } namespace { @@ -34,7 +35,7 @@ constexpr object_id make_id(collision_type type, pass_mode p, object_id id) } template<bool IsNeighbor> -bool add_holes_from_chunk(chunk::RTree& rtree, chunk& c, Vector2b chunk_offset) +bool add_holes_from_chunk(Chunk_RTree& rtree, chunk& c, Vector2b chunk_offset) { bool has_holes = false; constexpr auto chunk_size = iTILE_SIZE2 * TILE_MAX_DIM; @@ -65,47 +66,22 @@ bool add_holes_from_chunk(chunk::RTree& rtree, chunk& c, Vector2b chunk_offset) return has_holes; } -#if 1 -CORRADE_NEVER_INLINE -bool find_hole_in_bbox(CutResult<float>::rect& hole, chunk::RTree& rtree, Vector2 min, Vector2 max) -{ - bool ret = true; - rtree.Search(min.data(), max.data(), [&](uint64_t data, const chunk::RTree::Rect& r) { - auto x = std::bit_cast<collision_data>(data); - if (x.pass == (uint64_t)pass_mode::pass && x.tag == (uint64_t)collision_type::none) - { - CutResult<float>::rect holeʹ { - .min = { r.m_min[0], r.m_min[1] }, - .max = { r.m_max[0], r.m_max[1] }, - }; - if (rect_intersects(holeʹ.min, holeʹ.max, min, max)) - { - hole = holeʹ; - return ret = false; - } - } - return true; - }); - return ret; -} - -CORRADE_NEVER_INLINE -void filter_through_holes(chunk::RTree& rtree, object_id id, Vector2 min, Vector2 max, bool has_holes) +void filter_through_holes(Chunk_RTree& rtree, object_id id, Vector2 min, Vector2 max, bool has_holes) { if (!has_holes) return rtree.Insert(min.data(), max.data(), id); start: - fm_assert(min != max); // todo! + fm_assert(min != max); CutResult<float>::rect hole; - bool ret = find_hole_in_bbox(hole, rtree, min, max); + bool ret = chunk::find_hole_in_bbox(hole, rtree, min, max); if (ret) [[likely]] rtree.Insert(min.data(), max.data(), id); else { auto res = CutResult<float>::cut(min, max, hole.min, hole.max); - if (!res.found) + if (!res.found()) { rtree.Insert(min.data(), max.data(), id); } @@ -117,19 +93,40 @@ start: } else { - for (auto i = 0uz; i < res.size; i++) + for (auto i = 0u; i < res.size; i++) filter_through_holes(rtree, id, res.array[i].min, res.array[i].max, has_holes); } } } -#else -void filter_through_holes(chunk::RTree& rtree, object_id id, Vector2 min, Vector2 max, bool) + +} // namespace + +#if 1 +bool chunk::find_hole_in_bbox(CutResult<float>::rect& hole, const Chunk_RTree& rtree, Vector2 min, Vector2 max) { - rtree.Insert(min.data(), max.data(), id); + bool ret = true; + rtree.Search(min.data(), max.data(), [&](uint64_t data, const Chunk_RTree::Rect& r) { + auto x = std::bit_cast<collision_data>(data); + if (x.pass == (uint64_t)pass_mode::pass && x.type == (uint64_t)collision_type::none) + { + CutResult<float>::rect holeʹ { + .min = { r.m_min[0], r.m_min[1] }, + .max = { r.m_max[0], r.m_max[1] }, + }; + if (rect_intersects(holeʹ.min, holeʹ.max, min, max)) + { + hole = holeʹ; + return ret = false; + } + } + return true; + }); + return ret; } +#else +bool chunk::find_hole_in_bbox(CutResult<float>::rect&, Chunk_RTree&, Vector2, Vector2) { return true; } #endif - -} // namespace +bool chunk::find_hole_in_bbox(CutResult<float>::rect& hole, Vector2 min, Vector2 max) { return find_hole_in_bbox(hole, *rtree(), min, max); } void chunk::ensure_passability() noexcept { @@ -143,15 +140,16 @@ void chunk::ensure_passability() noexcept //Debug{} << ".. reset passability" << _coord; bool has_holes = false; + auto& rtree = *_rtree; { - has_holes |= add_holes_from_chunk<false>(*_rtree, *this, {}); + has_holes |= add_holes_from_chunk<false>(rtree, *this, {}); const auto nbs = _world->neighbors(_coord); for (auto i = 0u; i < 8; i++) if (nbs[i]) - has_holes |= add_holes_from_chunk<true>(*_rtree, *nbs[i], world::neighbor_offsets[i]); + has_holes |= add_holes_from_chunk<true>(rtree, *nbs[i], world::neighbor_offsets[i]); } - for (auto i = 0uz; i < TILE_COUNT; i++) + for (auto i = 0u; i < TILE_COUNT; i++) { if (const auto* atlas = ground_atlas_at(i)) { @@ -160,29 +158,29 @@ void chunk::ensure_passability() noexcept if (pass == pass_mode::pass) [[likely]] continue; auto id = make_id(collision_type::geometry, pass, i+1); - filter_through_holes(*_rtree, id, min, max, has_holes); + filter_through_holes(rtree, id, min, max, has_holes); } } - for (auto i = 0uz; i < TILE_COUNT; i++) + for (auto i = 0u; i < TILE_COUNT; i++) { auto tile = operator[](i); if (const auto* atlas = tile.wall_north_atlas().get()) { auto [min, max] = wall_north(i, (float)atlas->info().depth); auto id = make_id(collision_type::geometry, atlas->info().passability, TILE_COUNT+i+1); - filter_through_holes(*_rtree, id, min, max, has_holes); + filter_through_holes(rtree, id, min, max, has_holes); if (tile.wall_west_atlas()) { auto [min, max] = wall_pillar(i, (float)atlas->info().depth); - filter_through_holes(*_rtree, id, min, max, has_holes); + filter_through_holes(rtree, id, min, max, has_holes); } } if (const auto* atlas = tile.wall_west_atlas().get()) { auto [min, max] = wall_west(i, (float)atlas->info().depth); auto id = make_id(collision_type::geometry, atlas->info().passability, TILE_COUNT*2+i+1); - filter_through_holes(*_rtree, id, min, max, has_holes); + filter_through_holes(rtree, id, min, max, has_holes); } } for (const bptr<object>& eʹ : objects()) @@ -193,7 +191,7 @@ void chunk::ensure_passability() noexcept if (_bbox_for_scenery(*eʹ, bb)) { if (!eʹ->is_dynamic()) - filter_through_holes(*_rtree, std::bit_cast<object_id>(bb.data), Vector2(bb.start), Vector2(bb.end), has_holes); + filter_through_holes(rtree, std::bit_cast<object_id>(bb.data), Vector2(bb.start), Vector2(bb.end), has_holes); else _add_bbox_dynamic(bb); } diff --git a/src/chunk-region.cpp b/src/chunk-region.cpp index 3c5a8746..ed978c0f 100644 --- a/src/chunk-region.cpp +++ b/src/chunk-region.cpp @@ -119,10 +119,10 @@ auto default_region_predicate(chunk& c) noexcept return [&c](collision_data data) { auto x = std::bit_cast<collision_data>(data); // XXX 'scenery' is used for all object types - if (x.tag == (uint64_t)collision_type::scenery) + if (x.type == (uint64_t)collision_type::scenery) { auto& w = c.world(); - auto obj = w.find_object(x.data); + auto obj = w.find_object(x.id); if (obj->type() == object_type::critter) return path_search_continue::pass; } diff --git a/src/chunk-render.cpp b/src/chunk-render.cpp index 529d4f64..0c5d5bc6 100644 --- a/src/chunk-render.cpp +++ b/src/chunk-render.cpp @@ -52,9 +52,15 @@ auto chunk::ensure_ground_mesh() noexcept -> ground_mesh_tuple if (_ground->atlases[i]) _ground->indexes[count++] = uint8_t(i); + if (count == 0) + { + ground_mesh = GL::Mesh{NoCreate}; + return { ground_mesh, {}, 0 }; + } + std::sort(_ground->indexes.begin(), _ground->indexes.begin() + count, [this](uint8_t a, uint8_t b) { - return _ground->atlases[a] < _ground->atlases[b]; + return _ground->atlases[a].get() < _ground->atlases[b].get(); }); float hack_offset = _coord.z <= 0 ? -16.f : 0.f; // XXX hack diff --git a/src/chunk-scenery.cpp b/src/chunk-scenery.cpp index 2e491d4d..e928abac 100644 --- a/src/chunk-scenery.cpp +++ b/src/chunk-scenery.cpp @@ -64,6 +64,8 @@ auto make_topo_sort_data(object& e, uint32_t mesh_idx) -> topo_sort_data return data; } +// todo! switch to using a stack as in https://www.geeksforgeeks.org/topological-sorting/ + void topo_dfs(Array<chunk::object_draw_order>& array, size_t& output, size_t i, size_t size) // NOLINT(misc-no-recursion) { using m = typename chunk::topo_sort_data::m; @@ -177,13 +179,18 @@ auto chunk::ensure_scenery_mesh(scenery_scratch_buffers buffers) noexcept -> sce i++; } - GL::Mesh mesh{GL::MeshPrimitive::Triangles}; - auto vert_view = ArrayView<const std::array<vertex, 4>>{scenery_vertexes, count}; - auto index_view = ArrayView<const std::array<UnsignedShort, 6>>{scenery_indexes, count}; - mesh.addVertexBuffer(GL::Buffer{vert_view}, 0, tile_shader::Position{}, tile_shader::TextureCoordinates{}, tile_shader::Depth{}) - .setIndexBuffer(GL::Buffer{index_view}, 0, GL::MeshIndexType::UnsignedShort) - .setCount(int32_t(6 * count)); - scenery_mesh = move(mesh); + if (count == 0) + scenery_mesh = GL::Mesh{NoCreate}; + else + { + GL::Mesh mesh{GL::MeshPrimitive::Triangles}; + auto vert_view = ArrayView<const std::array<vertex, 4>>{scenery_vertexes, count}; + auto index_view = ArrayView<const std::array<UnsignedShort, 6>>{scenery_indexes, count}; + mesh.addVertexBuffer(GL::Buffer{vert_view}, 0, tile_shader::Position{}, tile_shader::TextureCoordinates{}, tile_shader::Depth{}) + .setIndexBuffer(GL::Buffer{index_view}, 0, GL::MeshIndexType::UnsignedShort) + .setCount(int32_t(6 * count)); + scenery_mesh = move(mesh); + } } const auto size = _objects.size(); diff --git a/src/chunk-walls.cpp b/src/chunk-walls.cpp index fb4b6560..39faf0bd 100644 --- a/src/chunk-walls.cpp +++ b/src/chunk-walls.cpp @@ -2,11 +2,15 @@ #include "tile-bbox.hpp" #include "quads.hpp" #include "wall-atlas.hpp" +#include "tile-bbox.hpp" +#include "compat/unroll.hpp" +#include "RTree-search.hpp" #include "shaders/shader.hpp" -#include <Corrade/Containers/ArrayViewStl.h> -#include <Corrade/Containers/Pair.h> -#include <Corrade/Containers/Optional.h> +#include <cr/ArrayViewStl.h> +#include <cr/GrowableArray.h> +#include <cr/Optional.h> #include <utility> +#include <concepts> #include <algorithm> #include <ranges> @@ -30,116 +34,232 @@ wall_atlas* chunk::wall_atlas_at(size_t i) const noexcept namespace { +using Wall::Group; using Wall::Group_; using Wall::Direction_; +using Wall::Frame; + +template<typename T> using Vec2_ = VectorTypeFor<2, T>; +template<typename T> using Vec3_ = VectorTypeFor<3, T>; + +template<typename F, size_t N> +struct minmax_v +{ + VectorTypeFor<N, F> min, max; +}; -constexpr Quads::quad get_quad(Direction_ D, Group_ G, float depth) +struct quad_table_entry { - CORRADE_ASSUME(D < Direction_::COUNT); - CORRADE_ASSUME(G < Group_::COUNT); - constexpr Vector2 half_tile = TILE_SIZE2*.5f; - constexpr float X = half_tile.x(), Y = half_tile.y(), Z = TILE_SIZE.z(); - const bool is_west = D == Wall::Direction_::W; + bool x : 1, y : 1, z : 1, dmx :1 = false, dmy : 1 = false, _fuzz : 3 = false; +}; +static_assert(sizeof(quad_table_entry) == sizeof(uint8_t)); +template<bool IsWest> +constexpr std::array<quad_table_entry, 4> make_quad_table_entry(Group_ G) +{ fm_assert(G < Group_::COUNT); + + constexpr bool x0 = false, x1 = true, + y0 = false, y1 = true, + z0 = false, z1 = true; + constexpr bool t = true, f = false; + switch (G) { using enum Group_; case COUNT: std::unreachable(); case wall: - if (!is_west) + if (!IsWest) return {{ - { X, -Y, 0 }, - { X, -Y, Z }, - {-X, -Y, 0 }, - {-X, -Y, Z }, + { x1, y0, z0 }, + { x1, y0, z1 }, + { x0, y0, z0 }, + { x0, y0, z1 }, }}; else return {{ - {-X, -Y, 0 }, - {-X, -Y, Z }, - {-X, Y, 0 }, - {-X, Y, Z }, + { x0, y0, z0 }, + { x0, y0, z1 }, + { x0, y1, z0 }, + { x0, y1, z1 }, }}; case side: - if (!is_west) + if (!IsWest) return {{ - { X, -Y - depth, 0 }, - { X, -Y - depth, Z }, - { X, -Y, 0 }, - { X, -Y, Z }, + { x1, y0, z0, f, t }, + { x1, y0, z1, f, t }, + { x1, y0, z0 }, + { x1, y0, z1 }, }}; else return {{ - { -X, Y, 0 }, - { -X, Y, Z }, - { -X - depth, Y, 0 }, - { -X - depth, Y, Z }, + { x0, y1, z0 }, + { x0, y1, z1 }, + { x0, y1, z0, t, f }, + { x0, y1, z1, t, f }, }}; case top: - if (!is_west) + if (!IsWest) return {{ - { -X, -Y - depth, Z }, - { X, -Y - depth, Z }, - { -X, -Y, Z }, - { X, -Y, Z }, + { x0, y0, z1, f, t }, + { x1, y0, z1, f, t }, + { x0, y0, z1 }, + { x1, y0, z1 }, }}; else return {{ - { -X, -Y, Z }, - { -X, Y, Z }, - { -X - depth, -Y, Z }, - { -X - depth, Y, Z }, + { x0, y0, z1 }, + { x0, y1, z1 }, + { x0, y0, z1, t, f }, + { x0, y1, z1, t, f }, }}; case corner: - if (!is_west) + if (!IsWest) return {{ - {-X, -Y, 0 }, - {-X, -Y, Z }, - {-X - depth, -Y, 0 }, - {-X - depth, -Y, Z }, + { x0, y0, z0 }, + { x0, y0, z1 }, + { x0, y0, z0, t, f }, + { x0, y0, z1, t, f }, }}; else return {{ - {-X, -Y - depth, 0 }, - {-X, -Y - depth, Z }, - {-X, -Y, 0 }, - {-X, -Y, Z }, + { x0, y0, z0, f, t }, + { x0, y0, z1, f, t }, + { x0, y0, z0 }, + { x0, y0, z1 }, }}; } std::unreachable(); } -Array<Quads::indexes> make_indexes_() +template<Group_ G, bool IsWest, typename F = float> +constexpr auto get_quadʹ(minmax_v<F, 3> bounds, F d) { - auto array = Array<Quads::indexes>{NoInit, chunk::max_wall_quad_count }; - for (auto i = 0uz; i < chunk::max_wall_quad_count; i++) - array[i] = Quads::quad_indexes(i); + F x[2] { bounds.min.x(), bounds.max.x() }, + y[2] { bounds.min.y(), bounds.max.y() }, + z[2] { bounds.min.z(), bounds.max.z() }, + dmx[2] { 0, d }, + dmy[2] { 0, d }; + + std::array<Vec3_<F>, 4> array = {}; + + unroll<static_array_size<decltype(array)>>([&]<typename Index>(Index) { + constexpr size_t i = Index::value; + constexpr auto table = make_quad_table_entry<IsWest>(G); + constexpr auto e = table[i]; + array.data()[i] = { x[e.x] - dmx[e.dmx], y[e.y] - dmy[e.dmy], z[e.z], }; + }); + return array; } -ArrayView<const Quads::indexes> make_indexes(size_t max) +template<Group_ G, bool IsWest, typename F = float> +constexpr CORRADE_ALWAYS_INLINE auto get_quad(F d) +{ + constexpr auto half_tile = Vec2_<F>(TILE_SIZE2*.5f); + constexpr auto X = half_tile.x(), Y = half_tile.y(), Z = F(TILE_SIZE.z()); + + return get_quadʹ<G, IsWest, F>({ { -X, -Y, 0, }, { X, Y, Z, }, }, d); +} + +template<bool IsWest> +CutResult<Int>::rect get_wall_rect(local_coords tile) { - static const auto indexes = make_indexes_(); - fm_assert(max <= chunk::max_wall_quad_count); - return indexes.prefix(max); + constexpr auto nʹ = wall_north<Int>(0, 0), wʹ = wall_west<Int>(0, 0); + constexpr auto t0 = !IsWest ? nʹ.first() : wʹ.first(), + t1 = !IsWest ? nʹ.second() : wʹ.second(); + const auto offset = Vector2i{tile} * iTILE_SIZE2; + const auto min = offset + t0, max = offset + t1; + return { min, max }; } -constexpr float depth_offset_for_group(Group_ G, bool is_west) +template<bool IsWest, std::invocable<Vector2, Vector2> F> +void cut_holes_in_wall(chunk& c, local_coords tile, Vector2i min, Vector2i max, F&& fun) { - CORRADE_ASSUME(G < Group_::COUNT); - float p = is_west ? tile_shader::wall_west_offset : 0; + CutResult<float>::rect hole; + + //constexpr auto eps = Vector2{.125f}; + if (c.find_hole_in_bbox(hole, Vector2(min) /*- eps*/, Vector2(max) /*+ eps*/)) + { + fun(min, max); + } + else + { + fm_assert(Vector2(Vector2i(hole.min)) == hole.min); + fm_assert(Vector2(Vector2i(hole.max)) == hole.max); + auto res = CutResult<Int>::cut(min, max, Vector2i(hole.min), Vector2i(hole.max)); + if (!res.found()) + { + fun(min, max); + } + else + { + for (auto i = 0u; i < res.size; i++) + { + const auto [min, max] = res.array[i]; + cut_holes_in_wall<IsWest>(c, tile, min, max, fun); + } + } + } +} + +struct quad_tuple +{ + Vector2ui tex_pos, tex_size; + Vector2i min, max; +}; + +template<bool IsWest> +quad_tuple get_wall_quad_stuff(const CutResult<Int>::rect& geom, const CutResult<Int>::rect& orig, + Vector2ui orig_tex_pos, Vector2ui orig_tex_size) +{ + return {}; +} + +ArrayView<const Quads::indexes> make_indexes(uint32_t count) +{ + static auto arrayʹ = [] { + auto array = Array<Quads::indexes>{}; + arrayReserve(array, 64); + return array; + }(); + auto& array = arrayʹ; + auto i = (uint32_t)array.size(); + if (count > i) [[unlikely]] + { + arrayResize(array, NoInit, count); + for (; i < count; i++) + array.data()[i] = Quads::quad_indexes(i); + } + return array.prefix(count); +} + +Array<std::array<chunk::vertex, 4>>& make_vertexes() +{ + static auto array = [] { + Array<std::array<chunk::vertex, 4>> array; + arrayReserve(array, 32); + return array; + }(); + return array; +} + +template<Group_ G, bool IsWest> +constexpr float depth_offset_for_group() +{ + static_assert(G < Group_::COUNT); + float p = IsWest ? tile_shader::wall_west_offset : 0; switch (G) { default: return tile_shader::wall_depth_offset + p; - case Wall::Group_::corner: - case Wall::Group_::side: + case Group_::corner: + case Group_::side: return tile_shader::wall_side_offset + p; } } -Wall::Frame variant_from_frame(ArrayView<const Wall::Frame> frames, global_coords coord, variant_t variant, bool is_west) +Frame variant_from_frame(ArrayView<const Frame> frames, global_coords coord, variant_t variant, bool is_west) { auto sz = (unsigned)frames.size(); if (variant == (variant_t)-1) @@ -148,168 +268,183 @@ Wall::Frame variant_from_frame(ArrayView<const Wall::Frame> frames, global_coord return frames[variant]; } -} // namespace +constexpr std::array<chunk::vertex, 4>& alloc_wall_vertexes(uint32_t& N, auto& V, auto& M, uint32_t k) +{ + constexpr uint32_t reserve = 15, mask = ~reserve; + const auto i = N, sz = ++N + reserve & mask; + fm_assert(uint32_t{(UnsignedShort)sz} == sz); + arrayResize(V, NoInit, sz); + arrayResize(M, NoInit, sz); + M[i] = (uint16_t)k; + return V[i]; +}; -GL::Mesh chunk::make_wall_mesh() +template<Group_ G, bool IsWest> +void do_wall_part(const Group& group, wall_atlas& A, chunk& c, chunk::wall_stuff& W, + Array<std::array<chunk::vertex, 4>>& vertexes, + global_coords coord, uint32_t& N, uint32_t tile) { - fm_debug_assert(_walls); - uint32_t N = 0; + if (!group.is_defined) + return; - static auto vertexes = Array<std::array<vertex, 4>>{NoInit, max_wall_quad_count }; + const uint32_t k = tile*2 + IsWest; + constexpr auto D = IsWest ? Direction_::W : Direction_::N; + const auto variant_2 = W.variants[k]; + const auto pos = local_coords{tile}; + const auto center = Vector3(pos) * TILE_SIZE; + const auto& dir = A.calc_direction(D); + const auto Depth = A.info().depth; - for (uint32_t k = 0; k < 2*TILE_COUNT; k++) + if constexpr(G == Group_::side) [[unlikely]] { - const bool is_west = k & 1; - const auto D = is_west ? Wall::Direction_::W : Wall::Direction_::N; - const auto& atlas = _walls->atlases[k]; - if (!atlas) - continue; - const auto variant_2 = _walls->variants[k]; - const auto pos = local_coords{k / 2u}; - const auto center = Vector3(pos) * TILE_SIZE; - const auto& dir = atlas->calc_direction(D); - const auto coord = global_coords{_coord, pos}; - const auto Depth = atlas->info().depth; - - for (auto [s, member, G] : Wall::Direction::groups_for_draw) - { - CORRADE_ASSUME(G < Group_::COUNT); - - bool side_ok = G == Wall::Group_::side; + bool corner_ok = false, pillar_ok = false; - if (!(dir.*member).is_defined) - continue; - - if (G == Wall::Group_::side) [[unlikely]] + if constexpr(!IsWest) + { + if (auto t = c.at_offset(pos, {-1, 0}); !(t && t->wall_north_atlas())) { - bool corner_ok = false, pillar_ok = false; - - if (!is_west) - { - if (auto t = at_offset_(pos, {-1, 0}); !(t && t->wall_north_atlas())) - { - if (_walls->atlases[k+1]) // west on same tile - pillar_ok = true; - if (auto t = at_offset_(pos, {0, -1}); t && t->wall_west_atlas()) - corner_ok = true; - } - if (side_ok) - if (auto t = at_offset_(pos, {1, -1}); t && t->wall_west_atlas()) - side_ok = false; - if (side_ok) - if (auto t = at_offset_(pos, {1, -1}); t && t->wall_west_atlas()) - side_ok = false; - } - else - { - if (auto t = at_offset_(pos, {0, -1}); !(t && t->wall_west_atlas())) - if (auto t = at_offset_(pos, {-1, 0}); t && t->wall_north_atlas()) - corner_ok = true; - if (side_ok) - if (auto t = at_offset_(pos, {-1, 1}); t && t->wall_north_atlas()) - side_ok = false; - } - - if (pillar_ok) [[unlikely]] - { - if (dir.top.is_defined) - { - const auto frames = atlas->frames(dir.top); - const auto frame = variant_from_frame(frames, coord, variant_2, is_west); - constexpr Vector2 half_tile = TILE_SIZE2*.5f; - constexpr float X = half_tile.x(), Y = half_tile.y(), Z = TILE_SIZE.z(); - Quads::quad quad = {{ - { -X - Depth, -Y - Depth, Z }, - { -X, -Y - Depth, Z }, - { -X - Depth, -Y, Z }, - { -X, -Y, Z } - }}; - fm_assert(frame.size.x() == Depth); - fm_assert(frame.size.y() >= Depth); - auto start = frame.offset + Vector2ui{0, frame.size.y()} - Vector2ui{0, Depth}; - const auto texcoords = Quads::texcoords_at(start, Vector2ui{Depth, Depth}, atlas->image_size()); - const auto i = N++; - fm_assert(i < vertexes.size()); - _walls->mesh_indexes[i] = (uint16_t)k; - const auto depth_offset = depth_offset_for_group(Group_::top, is_west); - const auto depth = tile_shader::depth_value(pos, depth_offset); - auto& v = vertexes[i]; - for (auto& v : quad) - v += center; - for (uint8_t j = 0; j < 4; j++) - v[j] = { quad[j], texcoords[j], depth }; - } - } - if (corner_ok) [[unlikely]] - { - if (dir.corner.is_defined) - { - const auto frames = atlas->frames(dir.corner); - const auto depth_offset = depth_offset_for_group(Group_::corner, is_west); - const auto pos_x = !is_west ? (float)pos.x : (float)pos.x - 1; - const auto depth = tile_shader::depth_value(pos_x, pos.y, depth_offset); - const auto& frame = variant_from_frame(frames, coord, variant_2, is_west); - const auto texcoords = Quads::texcoords_at(frame.offset, frame.size, atlas->image_size()); - const auto i = N++; - fm_assert(i < vertexes.size()); - _walls->mesh_indexes[i] = (uint16_t)k; - auto& v = vertexes[i]; - auto quad = get_quad(D, Group_::corner, (float)Depth); - for (auto& v : quad) - v += center; - for (uint8_t j = 0; j < 4; j++) - v[j] = { quad[j], texcoords[j], depth }; - } - else if (dir.wall.is_defined) [[likely]] - { - const auto frames = atlas->frames(dir.wall); - const auto depth_offset = depth_offset_for_group(Group_::corner, is_west); - const auto depth = tile_shader::depth_value(!is_west ? (float)pos.x : (float)pos.x - 1, depth_offset); - const auto frame = variant_from_frame(frames, coord, variant_2, is_west); - fm_assert(frame.size.x() > Depth); - auto start = frame.offset + Vector2ui{frame.size.x(), 0} - Vector2ui{Depth, 0}; - const auto texcoords = Quads::texcoords_at(start, {Depth, frame.size.y()}, atlas->image_size()); - const auto i = N++; - fm_assert(i < vertexes.size()); - _walls->mesh_indexes[i] = (uint16_t)k; - auto& v = vertexes[i]; - auto quad = get_quad(D, Group_::corner, (float)Depth); - for (auto& v : quad) - v += center; - for (uint8_t j = 0; j < 4; j++) - v[j] = { quad[j], texcoords[j], depth }; - } - } + if (W.atlases[k + 1]) // west on same tile + pillar_ok = true; + if (auto t = c.at_offset(pos, {0, -1}); t && t->wall_west_atlas()) + corner_ok = true; } + } + else + { + if (auto t = c.at_offset(pos, {0, -1}); !(t && t->wall_west_atlas())) + if (auto t = c.at_offset(pos, {-1, 0}); t && t->wall_north_atlas()) + corner_ok = true; + } - if (G != Wall::Group_::side || side_ok) [[likely]] + if (pillar_ok) [[unlikely]] + { + if (dir.top.is_defined) { - const auto& group = dir.*member; - const auto frames = atlas->frames(group); - - const auto i = N++; - fm_assert(i < vertexes.size()); - _walls->mesh_indexes[i] = (uint16_t)k; - const auto frame = variant_from_frame(frames, coord, variant_2, is_west); - const auto texcoords = Quads::texcoords_at(frame.offset, frame.size, atlas->image_size()); - const auto depth_offset = depth_offset_for_group(G, is_west); + const auto frames = A.frames(dir.top); + const auto frame = variant_from_frame(frames, coord, variant_2, IsWest); + constexpr Vector2 half_tile = TILE_SIZE2 * .5f; + constexpr float X = half_tile.x(), Y = half_tile.y(), Z = TILE_SIZE.z(); + const auto Depthʹ = (float)(int)Depth; + Quads::quad quad = {{ + {-X - Depthʹ, -Y - Depthʹ, Z}, + {-X, -Y - Depthʹ, Z}, + {-X - Depthʹ, -Y, Z}, + {-X, -Y, Z}, + }}; + fm_assert(frame.size.x() == Depth); + fm_assert(frame.size.y() >= Depth); + auto start = frame.offset + Vector2ui{0, frame.size.y()} - Vector2ui{0, Depth}; + const auto texcoords = Quads::texcoords_at(start, Vector2ui{Depth, Depth}, A.image_size()); + const auto depth_offset = depth_offset_for_group<Group_::top, IsWest>(); const auto depth = tile_shader::depth_value(pos, depth_offset); - auto quad = get_quad(D, G, (float)Depth); for (auto& v : quad) v += center; - auto& v = vertexes[i]; + auto& v = alloc_wall_vertexes(N, vertexes, W.mesh_indexes, k); for (uint8_t j = 0; j < 4; j++) - v[j] = { quad[j], texcoords[j], depth }; + v[j] = {quad[j], texcoords[j], depth}; } } + if (corner_ok) [[unlikely]] + { + if (dir.corner.is_defined) + { + const auto frames = A.frames(dir.corner); + const auto depth_offset = depth_offset_for_group<Group_::corner, IsWest>(); + const auto pos_x = !IsWest ? (float)pos.x : (float)pos.x - 1; + const auto depth = tile_shader::depth_value(pos_x, pos.y, depth_offset); + const auto& frame = variant_from_frame(frames, coord, variant_2, IsWest); + const auto texcoords = Quads::texcoords_at(frame.offset, frame.size, A.image_size()); + auto quad = get_quad<Group_::corner, IsWest>((float)Depth); + for (auto& v : quad) + v += center; + auto& v = alloc_wall_vertexes(N, vertexes, W.mesh_indexes, k); + for (uint8_t j = 0; j < 4; j++) + v[j] = {quad[j], texcoords[j], depth}; + } + else if (dir.wall.is_defined) [[likely]] + { + const auto frames = A.frames(dir.wall); + const auto depth_offset = depth_offset_for_group<Group_::corner, IsWest>(); + const auto depth = tile_shader::depth_value(!IsWest ? (float)pos.x : (float)pos.x - 1, (float)pos.y, depth_offset); + const auto frame = variant_from_frame(frames, coord, variant_2, IsWest); + fm_assert(frame.size.x() > Depth); + auto start = frame.offset + Vector2ui{frame.size.x(), 0} - Vector2ui{Depth, 0}; + const auto texcoords = Quads::texcoords_at(start, {Depth, frame.size.y()}, A.image_size()); + auto quad = get_quad<Group_::corner, IsWest>((float)Depth); + for (auto& v : quad) + v += center; + auto& v = alloc_wall_vertexes(N, vertexes, W.mesh_indexes, k); + for (uint8_t j = 0; j < 4; j++) + v[j] = {quad[j], texcoords[j], depth}; + } + } + } + + { + const auto frames = A.frames(group); + const auto frame = variant_from_frame(frames, coord, variant_2, IsWest); + const auto texcoords = Quads::texcoords_at(frame.offset, frame.size, A.image_size()); + const auto depth_offset = depth_offset_for_group<G, IsWest>(); + const auto depth = tile_shader::depth_value(pos, depth_offset); + auto quad = get_quad<G, IsWest>((float)Depth); + for (auto& v : quad) + v += center; + auto& v = alloc_wall_vertexes(N, vertexes, W.mesh_indexes, k); + for (uint8_t j = 0; j < 4; j++) + v[j] = {quad[j], texcoords[j], depth}; + } +} + +} // namespace + +GL::Mesh chunk::make_wall_mesh() +{ + fm_debug_assert(_walls); + + uint32_t N = 0; +#ifdef __CLION_IDE__ + extern const uint32_t _foo; N = _foo; +#endif + + auto& vertexes = make_vertexes(); + auto& W = *_walls; + + arrayResize(vertexes, 0); + + for (uint32_t k = 0; k < TILE_COUNT; k++) + { + const auto coord = global_coords{_coord, local_coords{k}}; + + static_assert(Wall::Group_COUNT == /* 5 */ 4); + static_assert((int)Direction_::COUNT == 2); + + if (auto* A_nʹ = W.atlases[k*2 + 0].get()) + { + auto& A_n = *A_nʹ; + const auto& dir = A_n.calc_direction(Direction_::N); + do_wall_part<Group_::wall, false>(dir.wall, A_n, *this, W, vertexes, coord, N, k); + do_wall_part<Group_::side, false>(dir.side, A_n, *this, W, vertexes, coord, N, k); + do_wall_part<Group_::top, false>(dir.top, A_n, *this, W, vertexes, coord, N, k); + } + if (auto* A_wʹ = W.atlases[k*2 + 1].get()) + { + auto& A_w = *A_wʹ; + const auto& dir = A_w.calc_direction(Direction_::W); + do_wall_part<Group_::wall, true>(dir.wall, A_w, *this, W, vertexes, coord, N, k); + do_wall_part<Group_::side, true>(dir.side, A_w, *this, W, vertexes, coord, N, k); + do_wall_part<Group_::top, true>(dir.top, A_w, *this, W, vertexes, coord, N, k); + } } + if (N == 0) + return GL::Mesh{NoCreate}; + ranges::sort(ranges::zip_view(vertexes.prefix(N), - ArrayView<uint_fast16_t>{_walls->mesh_indexes.data(), N}), + ArrayView{_walls->mesh_indexes.data(), N}), [&A = _walls->atlases](const auto& a, const auto& b) { const auto& [av, ai] = a; const auto& [bv, bi] = b; - return A[ai] < A[bi]; + return A[ai].get() < A[bi].get(); }); auto vertex_view = std::as_const(vertexes).prefix(N); diff --git a/src/chunk.cpp b/src/chunk.cpp index ac529090..a06d55c3 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -41,17 +41,7 @@ tile_proto chunk::operator[](local_coords xy) const noexcept { return operator[] chunk_coords_ chunk::coord() const noexcept { return _coord; } -tile_ref chunk::at_offset(local_coords pos, Vector2i off) -{ - const auto coord = global_coords{_coord, pos}; - const auto coord2 = coord + off; - if (coord.chunk() == coord2.chunk()) [[likely]] - return operator[](coord2.local()); - else - return (*_world)[coord2].t; -} - -Optional<tile_ref> chunk::at_offset_(local_coords pos, Vector2i off) +Optional<tile_ref> chunk::at_offset(local_coords pos, Vector2i off) { const auto coord = global_coords{_coord, pos}; const auto coord2 = coord + off; @@ -66,8 +56,7 @@ Optional<tile_ref> chunk::at_offset_(local_coords pos, Vector2i off) } } -tile_ref chunk::at_offset(tile_ref r, Vector2i off) { return at_offset(local_coords{r.index()}, off); } -Optional<tile_ref> chunk::at_offset_(tile_ref r, Vector2i off) { return at_offset_(local_coords{r.index()}, off); } +Optional<tile_ref> chunk::at_offset(tile_ref r, Vector2i off) { return at_offset(local_coords{r.index()}, off); } void chunk::mark_ground_modified() noexcept { @@ -139,7 +128,6 @@ void chunk::sort_objects() void chunk::add_object_pre(const bptr<object>& e) { - fm_assert(!e->gone); fm_assert(&*e->c == this); const auto dyn = e->is_dynamic(), upd = e->updates_passability(); if (!dyn) @@ -191,7 +179,6 @@ void chunk::remove_object(size_t i) { auto& e = *eʹ; fm_assert(e.c == this); - fm_assert(!e.gone); const auto dyn = e.is_dynamic(), upd = e.updates_passability(); if (!dyn) @@ -207,7 +194,6 @@ void chunk::remove_object(size_t i) } arrayRemove(_objects, i); - //eʹ.destroy(); } ArrayView<const bptr<object>> chunk::objects() const diff --git a/src/chunk.hpp b/src/chunk.hpp index cf41a970..f49a9653 100644 --- a/src/chunk.hpp +++ b/src/chunk.hpp @@ -4,9 +4,8 @@ #include "local-coords.hpp" #include "src/RTree-fwd.h" #include "global-coords.hpp" -#include "wall-defs.hpp" #include "search-pred.hpp" -#include <type_traits> +#include "hole-cut.hpp" #include <array> #include <Corrade/Containers/Array.h> #include <Corrade/Containers/Pointer.h> @@ -34,10 +33,8 @@ public: tile_proto operator[](local_coords xy) const noexcept; chunk_coords_ coord() const noexcept; - tile_ref at_offset(local_coords pos, Vector2i off); - tile_ref at_offset(tile_ref r, Vector2i off); - Optional<tile_ref> at_offset_(local_coords pos, Vector2i off); - Optional<tile_ref> at_offset_(tile_ref r, Vector2i off); + Optional<tile_ref> at_offset(local_coords pos, Vector2i off); + Optional<tile_ref> at_offset(tile_ref r, Vector2i off); using iterator = tile_iterator; using const_iterator = tile_const_iterator; @@ -99,6 +96,8 @@ public: class world& world() noexcept; [[nodiscard]] bool can_place_object(const object_proto& proto, local_coords pos); + [[nodiscard]] bool find_hole_in_bbox(CutResult<float>::rect& hole, Vector2 min, Vector2 max); + [[nodiscard]] static bool find_hole_in_bbox(CutResult<float>::rect& hole, const Chunk_RTree& rtree, Vector2 min, Vector2 max); void on_teardown(); bool is_teardown() const; @@ -107,14 +106,9 @@ public: void remove_object(size_t i); void sort_objects(); - // for drawing only - static constexpr size_t max_wall_quad_count = - TILE_COUNT*Wall::Direction_COUNT*(Wall::Group_COUNT+4); - pass_region make_pass_region(bool debug = false, ArrayView<const Vector2i> positions = {}); pass_region make_pass_region(const Search::pred& f, bool debug = false, ArrayView<const Vector2i> positions = {}); -private: struct ground_stuff { std::array<bptr<ground_atlas>, TILE_COUNT> atlases; @@ -126,9 +120,10 @@ private: { std::array<bptr<wall_atlas>, 2*TILE_COUNT> atlases; std::array<variant_t, 2*TILE_COUNT> variants; - std::array<uint_fast16_t, max_wall_quad_count> mesh_indexes; + Array<uint_fast16_t> mesh_indexes; }; +private: Pointer<ground_stuff> _ground; Pointer<wall_stuff> _walls; Array<bptr<object>> _objects; diff --git a/src/collision.hpp b/src/collision.hpp index 914d6e1a..eef51d88 100644 --- a/src/collision.hpp +++ b/src/collision.hpp @@ -9,9 +9,9 @@ enum class collision_type : unsigned char { constexpr inline size_t collision_data_BITS = 60; struct collision_data final { - uint64_t tag : 2; - uint64_t pass : 2; - uint64_t data : collision_data_BITS; + uint64_t type : 2; + uint64_t pass : 2; + uint64_t id : collision_data_BITS; bool operator==(const collision_data&) const noexcept; }; diff --git a/src/critter-script-walk.cpp b/src/critter-script-walk.cpp index 2402fe74..c0e800e8 100644 --- a/src/critter-script-walk.cpp +++ b/src/critter-script-walk.cpp @@ -7,6 +7,7 @@ #include "search-result.hpp" #include "search-astar.hpp" #include "entity/name-of.hpp" +#include <utility> namespace floormat { diff --git a/src/critter.cpp b/src/critter.cpp index 32d1048b..e99eb3a4 100644 --- a/src/critter.cpp +++ b/src/critter.cpp @@ -55,15 +55,15 @@ constexpr rotation arrows_to_dir_from_mask(unsigned mask) fm_assert(false); } -constexpr auto arrows_to_dir_array = map(arrows_to_dir_from_mask, iota_array<uint8_t, 16>); - constexpr auto arrows_to_dir(bool left, bool right, bool up, bool down) { + constexpr auto table = map(arrows_to_dir_from_mask, iota_array<uint8_t, 16>); constexpr uint8_t L = 1 << 3, R = 1 << 2, U = 1 << 1, D = 1 << 0; - const uint8_t bits = left*L | right*R | up*U | down*D; constexpr uint8_t mask = L|R|U|D; + + const uint8_t bits = left*L | right*R | up*U | down*D; CORRADE_ASSUME((bits & mask) == bits); - return arrows_to_dir_array.data()[bits]; + return table.data()[bits]; } #if 0 @@ -118,26 +118,31 @@ constexpr auto rotation_to_similar_array = map(rotation_to_similar_, iota_array< constexpr std::array<rotation, 3> rotation_to_similar(rotation r) { - CORRADE_ASSUME(r < rotation_COUNT); return rotation_to_similar_array.data()[(uint8_t)r]; } -template<rotation r> constexpr uint8_t get_length_axis() +constexpr uint8_t get_length_axisʹ(rotation r) { - static_assert((int)r % 2 == 0); using enum rotation; - if constexpr(r == N || r == S) + if (r == N || r == S) return 1; - else if constexpr(r == W || r == E) + else if (r == W || r == E) return 0; - fm_assert(false); + else + return 0; // garbage in, garbage out +} + +constexpr uint8_t get_length_axis(rotation r) +{ + constexpr auto table = map(get_length_axisʹ, iota_array<rotation, (size_t)rotation_COUNT>); + return table.data()[(size_t)r]; } -template<rotation new_r, bool MultiStep> -CORRADE_ALWAYS_INLINE -bool update_movement_body(size_t& i, critter& C, const anim_def& info, uint8_t nsteps) +template<bool MultiStep> +CORRADE_NEVER_INLINE +bool update_movement_body(size_t& i, critter& C, const anim_def& info, uint8_t nsteps, rotation new_r, rotation visible_r) { - constexpr auto vec = rotation_to_vec(new_r); + const auto vec = rotation_to_vec(new_r); using Frac = decltype(critter::offset_frac); constexpr auto frac = (float{limits<Frac>::max}+1)/2; constexpr auto inv_frac = 1 / frac; @@ -156,7 +161,7 @@ bool update_movement_body(size_t& i, critter& C, const anim_def& info, uint8_t n C.offset_frac = Frac(rem * frac); if (C.can_move_to(off_i)) { - C.move_to(i, off_i, new_r); + C.move_to(i, off_i, visible_r); if constexpr(MultiStep) (C.frame += nsteps) %= info.nframes; else @@ -173,64 +178,60 @@ bool update_movement_body(size_t& i, critter& C, const anim_def& info, uint8_t n return false; } -template<rotation r> -CORRADE_ALWAYS_INLINE -bool update_movement_3way(size_t& i, critter& C, const anim_def& info) +bool update_movement_3way(size_t& i, critter& C, const anim_def& info, rotation new_r) { - constexpr auto rotations = rotation_to_similar(r); - if (update_movement_body<rotations[0], false>(i, C, info, 0)) + const auto rotations = rotation_to_similar(new_r); + if (update_movement_body<false>(i, C, info, 0, rotations.data()[0], new_r)) return true; - if (update_movement_body<rotations[1], false>(i, C, info, 0)) + if (update_movement_body<false>(i, C, info, 0, rotations.data()[1], new_r)) return true; - if (update_movement_body<rotations[2], false>(i, C, info, 0)) + if (update_movement_body<false>(i, C, info, 0, rotations.data()[2], new_r)) return true; return false; } constexpr bool DoUnroll = true; -template<rotation new_r> -requires ((int)new_r % 2 != 0) -CORRADE_ALWAYS_INLINE -bool update_movement_1(critter& C, size_t& i, const anim_def& info, uint32_t nframes) +template<bool IsEven> +requires (!IsEven) +bool update_movement_1(critter& C, size_t& i, const anim_def& info, uint32_t nframes, rotation new_r) { + //Debug{} << "< nframes" << nframes; if constexpr(DoUnroll) { - //Debug{} << "< nframes" << nframes; while (nframes > 1) { auto len = (uint8_t)Math::min(nframes, (uint32_t)C.bbox_size.min()); if (len <= 1) break; - if (!update_movement_body<new_r, true>(i, C, info, len)) + if (!update_movement_body<true>(i, C, info, len, new_r, new_r)) break; //Debug{} << " " << len; nframes -= len; } - //Debug{} << ">" << nframes; } + //Debug{} << ">" << nframes; for (auto k = 0u; k < nframes; k++) - if (!update_movement_3way<new_r>(i, C, info)) + if (!update_movement_3way(i, C, info, new_r)) return false; return true; } -template<rotation new_r> -requires (((int)new_r & 1) % 2 == 0) -CORRADE_NEVER_INLINE -bool update_movement_1(critter& C, size_t& i, const anim_def& info, uint32_t nframes) +template<bool IsEven> +requires IsEven +bool update_movement_1(critter& C, size_t& i, const anim_def& info, uint32_t nframes, rotation new_r) { if constexpr(DoUnroll) { //Debug{} << "< nframes" << nframes; while (nframes > 1) { - constexpr auto len_axis = get_length_axis<new_r>(); + const auto len_axis = get_length_axis(new_r); auto len = (uint8_t)Math::min(nframes, (uint32_t)C.bbox_size.data()[len_axis]); if (len <= 1) [[unlikely]] break; - if (!update_movement_body<new_r, true>(i, C, info, len)) + if (!update_movement_body<true>(i, C, info, len, new_r, new_r)) break; //Debug{} << " " << len; nframes -= len; @@ -239,20 +240,11 @@ bool update_movement_1(critter& C, size_t& i, const anim_def& info, uint32_t nfr } for (auto k = 0u; k < nframes; k++) - if (!update_movement_body<new_r, false>(i, C, info, 0)) + if (!update_movement_body<false>(i, C, info, 0, new_r, new_r)) return false; return true; } -template bool update_movement_1<(rotation)0>(critter& C, size_t& i, const anim_def& info, uint32_t nframes); -template bool update_movement_1<(rotation)1>(critter& C, size_t& i, const anim_def& info, uint32_t nframes); -template bool update_movement_1<(rotation)2>(critter& C, size_t& i, const anim_def& info, uint32_t nframes); -template bool update_movement_1<(rotation)3>(critter& C, size_t& i, const anim_def& info, uint32_t nframes); -template bool update_movement_1<(rotation)4>(critter& C, size_t& i, const anim_def& info, uint32_t nframes); -template bool update_movement_1<(rotation)5>(critter& C, size_t& i, const anim_def& info, uint32_t nframes); -template bool update_movement_1<(rotation)6>(critter& C, size_t& i, const anim_def& info, uint32_t nframes); -template bool update_movement_1<(rotation)7>(critter& C, size_t& i, const anim_def& info, uint32_t nframes); - struct step_s { uint32_t count; @@ -309,10 +301,10 @@ constexpr rotation dir_from_step_mask(uint8_t val) } } -constexpr auto dir_from_step_array = map(dir_from_step_mask, iota_array<uint8_t, 1 << 4>); - constexpr rotation dir_from_step(step_s step) { + constexpr auto table = map(dir_from_step_mask, iota_array<uint8_t, 1 << 4>); + if (step.direction.isZero()) [[unlikely]] return rotation_COUNT; @@ -320,7 +312,7 @@ constexpr rotation dir_from_step(step_s step) auto y = uint8_t(step.direction.y() + 1); //fm_debug_assert((x & 3) == x && (y & 3) == y); auto val = uint8_t(x << 2 | y); - return dir_from_step_array.data()[val]; + return table.data()[val]; } constexpr step_s next_step(point from, point to) @@ -441,15 +433,12 @@ void critter::update_movement(size_t& i, const Ns& dt, rotation new_r) switch (new_r) { + using enum rotation; default: std::unreachable(); - case (rotation)0: ret = update_movement_1<(rotation)0>(*this, i, info, nframes); break; - case (rotation)1: ret = update_movement_1<(rotation)1>(*this, i, info, nframes); break; - case (rotation)2: ret = update_movement_1<(rotation)2>(*this, i, info, nframes); break; - case (rotation)3: ret = update_movement_1<(rotation)3>(*this, i, info, nframes); break; - case (rotation)4: ret = update_movement_1<(rotation)4>(*this, i, info, nframes); break; - case (rotation)5: ret = update_movement_1<(rotation)5>(*this, i, info, nframes); break; - case (rotation)6: ret = update_movement_1<(rotation)6>(*this, i, info, nframes); break; - case (rotation)7: ret = update_movement_1<(rotation)7>(*this, i, info, nframes); break; + case N: case E: case S: case W: + ret = update_movement_1<true >(*this, i, info, nframes, new_r); break; + case NW: case NE: case SE: case SW: + ret = update_movement_1<false>(*this, i, info, nframes, new_r); break; } if (!ret) [[unlikely]] diff --git a/src/hole-cut.cpp b/src/hole-cut.cpp index 1ecf96ce..a7736762 100644 --- a/src/hole-cut.cpp +++ b/src/hole-cut.cpp @@ -2,6 +2,7 @@ #include "compat/array-size.hpp" #include "compat/iota.hpp" #include "compat/map.hpp" +#include "hole-cut.hpp" //#include <mg/Functions.h> #ifdef __GNUG__ @@ -32,7 +33,7 @@ struct coords struct element { - uint8_t size; + uint8_t s, size; std::array<coords, 8> array; }; @@ -45,9 +46,9 @@ constexpr element make_element(uint8_t s) switch (s) { using enum location; - case x0|x1|y0|y1: return element{0, {{ // 9.1 + case x0|x1|y0|y1: return element{s, 0, {{ // 9.1 }}}; - case __|__|__|__: return element{8, {{ // 14.1 + case __|__|__|__: return element{s, 8, {{ // 14.1 {R0, H0, R0, H0}, {H0, H1, R0, H0}, {H1, R1, R0, H0}, @@ -58,62 +59,62 @@ constexpr element make_element(uint8_t s) {H1, R1, H1, R1}, }}}; - case x0|x1|__|__: return element{2, {{ // 13.1 + case x0|x1|__|__: return element{s, 2, {{ // 13.1 {R0, R1, R0, H0}, {R0, R1, H1, R1}, }}}; - case __|__|y0|y1: return element{2, {{ // 13.2 + case __|__|y0|y1: return element{s, 2, {{ // 13.2 {R0, H0, R0, R1}, {H1, R1, R0, R1}, }}}; - case x0|x1|y0|__: return element{1, {{ // 12.1 + case x0|x1|y0|__: return element{s, 1, {{ // 12.1 {R0, R1, H1, R1}, }}}; - case x0|x1|__|y1: return element{1, {{ // 12.2 + case x0|x1|__|y1: return element{s, 1, {{ // 12.2 {R0, R1, R0, H0}, }}}; - case x0|__|y0|y1: return element{1, {{ // 12.3 + case x0|__|y0|y1: return element{s, 1, {{ // 12.3 {H1, R1, R0, R1}, }}}; - case __|x1|y0|y1: return element{1, {{ // 12.4 + case __|x1|y0|y1: return element{s, 1, {{ // 12.4 {R0, H0, R0, R1}, }}}; - case x0|__|__|__: return element{3, {{ // 10.1 + case x0|__|__|__: return element{s, 3, {{ // 10.1 {R0, R1, R0, H0}, {H1, R1, H0, H1}, {R0, R1, H1, R1}, }}}; - case __|x1|__|__: return element{3, {{ // 10.2 + case __|x1|__|__: return element{s, 3, {{ // 10.2 {R0, R1, R0, H0}, {R0, H0, H0, H1}, {R0, R1, H1, R1}, }}}; - case __|__|y0|__: return element{3, {{ // 10.3 + case __|__|y0|__: return element{s, 3, {{ // 10.3 {R0, H0, R0, R1}, {H0, H1, H1, R1}, {H1, R1, R0, R1}, }}}; - case __|__|__|y1: return element{3, {{ // 10.4 + case __|__|__|y1: return element{s, 3, {{ // 10.4 {R0, H0, R0, R1}, {H0, H1, R0, H0}, {H1, R1, R0, R1}, }}}; - case x0|__|y0|__: return element{2, {{ // 11.1 + case x0|__|y0|__: return element{s, 2, {{ // 11.1 {H1, R1, R0, H1}, {R0, R1, H1, R1}, }}}; - case __|x1|y0|__: return element{2, {{ // 11.2 + case __|x1|y0|__: return element{s, 2, {{ // 11.2 {R0, H0, R0, H1}, {R0, R1, H1, R1}, }}}; - case x0|__|__|y1: return element{2, {{ // 11.3 + case x0|__|__|y1: return element{s, 2, {{ // 11.3 {R0, R1, R0, H0}, {H1, R1, H0, R1}, }}}; - case __|x1|__|y1: return element{2, {{ // 11.4 + case __|x1|__|y1: return element{s, 2, {{ // 11.4 {R0, R1, R0, H0}, {R0, H0, H0, R1}, }}}; @@ -123,8 +124,6 @@ constexpr element make_element(uint8_t s) fm_assert(false); } -constexpr auto elements = map(make_element, iota_array<uint8_t, 16>); - template<typename T> constexpr auto get_value_from_coord(Vec2ʹ<T> r0, Vec2ʹ<T> r1, Vec2ʹ<T> h0, Vec2ʹ<T> h1, coords c) { @@ -141,13 +140,32 @@ template<typename T> [[nodiscard]] constexpr bool check_empty(Vec2ʹ<T> r0, Vec2ʹ<T> r1, Vec2ʹ<T> h0, Vec2ʹ<T> h1) { - bool iempty = r0.x() == r1.x() | r0.y() == r1.y(); + //bool iempty = r0.x() == r1.x() | r0.y() == r1.y(); bool hempty = h0.x() == h1.x() | h0.y() == h1.y(); bool empty_before_x = h1.x() <= r0.x(); bool empty_after_x = h0.x() >= r1.x(); bool empty_before_y = h1.y() <= r0.y(); bool empty_after_y = h0.y() >= r1.y(); - return iempty | hempty | empty_before_x | empty_after_x | empty_before_y | empty_after_y; + return /*iempty |*/ hempty | empty_before_x | empty_after_x | empty_before_y | empty_after_y; +} + +template<typename T> +constexpr Cr<T> cut_rectangleʹ(Vec2ʹ<T> r0, Vec2ʹ<T> r1, Vec2ʹ<T> h0, Vec2ʹ<T> h1, uint8_t val) +{ + constexpr auto table = map(make_element, iota_array<uint8_t, 16>); + + CORRADE_ASSUME(val < 16); + const auto elt = table[val]; + const auto sz = elt.size; + Cr<T> res = { + .s = val, + .size = sz, + .array = {}, + }; + + for (auto i = 0u; i < 8; i++) + res.array[i] = get_value_from_coord<T>(r0, r1, h0, h1, elt.array[i]); + return res; } template<typename T> @@ -155,9 +173,9 @@ constexpr Cr<T> cut_rectangle(Vec2ʹ<T> r0, Vec2ʹ<T> r1, Vec2ʹ<T> h0, Vec2ʹ<T { if (check_empty<T>(r0, r1, h0, h1)) return { - .array = {{ { r0, r1 }, }}, + .s = (uint8_t)-1, .size = 1, - .found = false, + .array = {{ { r0, r1 }, }}, }; const bool sx = h0.x() <= r0.x(); @@ -165,18 +183,9 @@ constexpr Cr<T> cut_rectangle(Vec2ʹ<T> r0, Vec2ʹ<T> r1, Vec2ʹ<T> h0, Vec2ʹ<T const bool sy = h0.y() <= r0.y(); const bool ey = h1.y() >= r1.y(); - auto val = uint8_t(sx << 0 | ex << 1 | sy << 2 | ey << 3); + const auto val = uint8_t(sx << 0 | ex << 1 | sy << 2 | ey << 3); CORRADE_ASSUME(val < 16); - const auto elt = elements[val]; - const auto sz = elt.size; - Cr<T> res = { - .array = {}, - .size = sz, - .found = true, - }; - - for (auto i = 0u; i < 8; i++) - res.array[i] = get_value_from_coord<T>(r0, r1, h0, h1, elt.array[i]); + const auto res = cut_rectangleʹ<T>(r0, r1, h0, h1, val); return res; } @@ -199,10 +208,18 @@ constexpr Cr<T> cut_rectangle(bbox<T> input, bbox<T> hole) } // namespace -template<typename T> Cr<T> CutResult<T>::cut(bbox input, bbox hole) { return cut_rectangle<T>(input, hole); } +template<typename T> Cr<T> CutResult<T>::cutʹ(Vec2 r0, Vec2 r1, Vec2 h0, Vec2 h1, uint8_t s) +{ + return cut_rectangleʹ<T>(r0, r1, h0, h1, s); +} + +template<typename T> Cr<T> CutResult<T>::cut(bbox input, bbox hole) requires std::is_signed_v<T> { return cut_rectangle<T>(input, hole); } template<typename T> Cr<T> CutResult<T>::cut(Vec2 r0, Vec2 r1, Vec2 h0, Vec2 h1) { return cut_rectangle<T>(r0, r1, h0, h1); } -template struct CutResult<Int>; +template<typename T> bool CutResult<T>::found() const { return s != (uint8_t)-1; } + +template struct CutResult<uint32_t>; +template struct CutResult<int32_t>; template struct CutResult<float>; } // namespace floormat diff --git a/src/hole-cut.hpp b/src/hole-cut.hpp new file mode 100644 index 00000000..4ec5c168 --- /dev/null +++ b/src/hole-cut.hpp @@ -0,0 +1,25 @@ +#pragma once +#include <array> +#include <mg/Vector2.h> +#include <Magnum/DimensionTraits.h> + +namespace floormat { + +template<typename T> +struct CutResult +{ + using Vec2 = VectorTypeFor<2, T>; + struct bbox { Vec2 position; Vector2ub bbox_size; }; + struct rect { Vec2 min, max; }; + + static CutResult cut(bbox input, bbox hole) requires std::is_signed_v<T>; + static CutResult cut(Vec2 r0, Vec2 r1, Vec2 h0, Vec2 h1); + static CutResult cutʹ(Vec2 r0, Vec2 r1, Vec2 h0, Vec2 h1, uint8_t s); + + uint8_t s = (uint8_t)-1, size = 0; + std::array<rect, 8> array; + + bool found() const; +}; + +} // namespace floormat diff --git a/src/hole.hpp b/src/hole.hpp index 35fd29d9..f70fac15 100644 --- a/src/hole.hpp +++ b/src/hole.hpp @@ -22,7 +22,6 @@ struct hole_proto final : object_proto bool on_render : 1 = true; bool on_physics : 1 = true; bool enabled : 1 = true; - bool is_wall : 1 = false; }; uint8_t height = 0, z_offset = tile_size_z/2; @@ -57,19 +56,4 @@ private: void maybe_mark_neighbor_chunks_modified() override; }; -template<typename T> -struct CutResult -{ - using Vec2 = VectorTypeFor<2, T>; - struct bbox { Vec2 position; Vector2ub bbox_size; }; - struct rect { Vec2 min, max; }; - - static CutResult cut(bbox input, bbox hole); - static CutResult cut(Vec2 r0, Vec2 r1, Vec2 h0, Vec2 h1); - - std::array<rect, 8> array; - uint8_t size = 0; - bool found = false; -}; - } // namespace floormat diff --git a/src/object.cpp b/src/object.cpp index 449a0fcb..1679f5b7 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -173,7 +173,7 @@ static bool do_search(class chunk* c, chunk_coords_ coord, bool ret = true; c->rtree()->Search(min.data(), max.data(), [&](object_id data, const auto& r) { auto x = std::bit_cast<collision_data>(data); - if (x.data != id && x.pass != (uint64_t)pass_mode::pass && + if (x.id != id && x.pass != (uint64_t)pass_mode::pass && rect_intersects(min, max, {r.m_min[0], r.m_min[1]}, {r.m_max[0], r.m_max[1]})) return ret = false; else diff --git a/src/object.hpp b/src/object.hpp index a73f97b1..e3cbb628 100644 --- a/src/object.hpp +++ b/src/object.hpp @@ -57,7 +57,6 @@ struct object : bptr_base const rotation r = rotation::N; // todo remove bitfield? const pass_mode pass = pass_mode::see_through; bool ephemeral : 1 = false; - bool gone : 1 = false; //char _pad[4]; // got 4 bytes left virtual ~object() noexcept override; diff --git a/src/point.hpp b/src/point.hpp index 8d40992b..f0e39aed 100644 --- a/src/point.hpp +++ b/src/point.hpp @@ -30,7 +30,7 @@ struct point constexpr chunk_coords_ chunk3() const; constexpr local_coords local() const; constexpr Vector2b offset() const; - template<size_t N> std::tuple_element_t<N, point> constexpr get() const; + template<size_t N> typename std::tuple_element<N, point>::type constexpr get() const; friend Debug& operator<<(Debug& dbg, const point& pt); @@ -59,7 +59,7 @@ constexpr chunk_coords point::chunk() const { return {cx, cy}; } constexpr local_coords point::local() const { return tile; } constexpr Vector2b point::offset() const { return _offset; } -template<size_t N> std::tuple_element_t<N, point> constexpr point::get() const +template<size_t N> typename std::tuple_element<N, point>::type constexpr point::get() const { static_assert(N < 2); if constexpr(N == 0) diff --git a/src/raycast.cpp b/src/raycast.cpp index 06cc416e..cb939758 100644 --- a/src/raycast.cpp +++ b/src/raycast.cpp @@ -168,9 +168,9 @@ raycast_result_s do_raycasting(std::conditional_t<EnableDiagnostics, raycast_dia .to = to, .collision = {}, .collider = { - .tag = (uint64_t)collision_type::none, + .type = (uint64_t)collision_type::none, .pass = (uint64_t)pass_mode::pass, - .data = ((uint64_t)1 << collision_data_BITS)-1, + .id = ((uint64_t)1 << collision_data_BITS)-1, }, .has_result = true, .success = false, @@ -234,7 +234,7 @@ raycast_result_s do_raycasting(std::conditional_t<EnableDiagnostics, raycast_dia const auto do_check_collider = [&](uint64_t data, const Rect& r) { auto x = std::bit_cast<collision_data>(data); - if (x.data == self || x.pass == (uint64_t)pass_mode::pass) + if (x.id == self || x.pass == (uint64_t)pass_mode::pass) return; //Debug{} << "item" << x.data << Vector2(r.m_min[0], r.m_min[1]) << Vector2(r.m_max[0], r.m_max[1]); auto ret = ray_aabb_intersection(origin, dir_inv_norm, diff --git a/src/script.cpp b/src/script.cpp index f2b7d30d..23d11e0d 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -5,6 +5,8 @@ namespace floormat { namespace { +namespace fm_debug = floormat::debug::detail; + constexpr StringView names[(size_t)script_lifecycle::COUNT] = { "no-init"_s, "initializing"_s, "created"_s, "destroying"_s, "torn-down"_s, @@ -23,10 +25,10 @@ StringView base_script::state_name(script_lifecycle x) void base_script::_assert_state(script_lifecycle old_state, script_lifecycle s, const char* file, int line) { if (old_state != s) [[unlikely]] - fm_emit_abort(file, line, - "invalid state transition from '%s' to '%s'", - state_name(old_state).data(), - state_name(s).data()); + fm_debug::emit_abort(file, line, + "invalid state transition from '%s' to '%s'", + state_name(old_state).data(), + state_name(s).data()); } base_script::~base_script() noexcept = default; diff --git a/src/search.cpp b/src/search.cpp index 220e3869..424b1743 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -45,7 +45,7 @@ bool path_search::is_passable_1(chunk& c, Vector2 min, Vector2 max, object_id ow rt.Search(min.data(), max.data(), [&](uint64_t data, const auto& r) { auto x = std::bit_cast<collision_data>(data); - if (x.data != own_id && x.pass != (uint64_t)pass_mode::pass) + if (x.id != own_id && x.pass != (uint64_t)pass_mode::pass) { if (rect_intersects(min, max, {r.m_min[0], r.m_min[1]}, {r.m_max[0], r.m_max[1]})) if (p(x) != path_search_continue::pass) diff --git a/src/tile-bbox.hpp b/src/tile-bbox.hpp index 755004d2..6d2228b1 100644 --- a/src/tile-bbox.hpp +++ b/src/tile-bbox.hpp @@ -8,11 +8,12 @@ namespace floormat { template<typename T = float> -constexpr Vector2 tile_start(size_t k) +constexpr VectorTypeFor<2, T> tile_start(size_t k) { - constexpr auto half_tile = VectorTypeFor<2,T>(tile_size_xy/2); + using Vec2 = VectorTypeFor<2,T>; + constexpr auto half_tile = Vec2{tile_size_xy/2}; const local_coords coord{k}; - return TILE_SIZE2 * VectorTypeFor<2,T>(coord) - half_tile; + return Vec2(TILE_SIZE2) * Vec2(coord) - half_tile; } constexpr Pair<Vector2i, Vector2i> scenery_tile(local_coords local, Vector2b offset, Vector2b bbox_offset, Vector2ub bbox_size) @@ -31,24 +32,27 @@ constexpr Pair<VectorTypeFor<2,T>, VectorTypeFor<2,T>> whole_tile(size_t k) } template<typename T = float> -constexpr Pair<VectorTypeFor<2,T>, VectorTypeFor<2,T>> wall_north(size_t k, float wall_depth) +constexpr Pair<VectorTypeFor<2,T>, VectorTypeFor<2,T>> wall_north(size_t k, T wall_depth) { - auto min = tile_start<T>(k) - VectorTypeFor<2,T>{0, wall_depth}; - return { min, min + VectorTypeFor<2,T>{TILE_SIZE2.x(), wall_depth} }; + using Vec2 = VectorTypeFor<2,T>; + auto min = tile_start<T>(k) - Vec2{0, wall_depth}; + return { min, min + Vec2{tile_size_xy, wall_depth} }; } template<typename T = float> -constexpr Pair<VectorTypeFor<2,T>, VectorTypeFor<2,T>> wall_west(size_t k, float wall_depth) +constexpr Pair<VectorTypeFor<2,T>, VectorTypeFor<2,T>> wall_west(size_t k, T wall_depth) { - auto min = tile_start<T>(k) - VectorTypeFor<2,T>{wall_depth, 0}; - return { min, min + VectorTypeFor<2,T>{wall_depth, TILE_SIZE2.y()} }; + using Vec2 = VectorTypeFor<2,T>; + auto min = tile_start<T>(k) - Vec2{wall_depth, 0}; + return { min, min + Vec2{wall_depth, tile_size_xy} }; } template<typename T = float> -constexpr Pair<VectorTypeFor<2,T>, VectorTypeFor<2,T>> wall_pillar(size_t k, float wall_depth) +constexpr Pair<VectorTypeFor<2,T>, VectorTypeFor<2,T>> wall_pillar(size_t k, T wall_depth) { - auto min = tile_start<T>(k) - VectorTypeFor<2,T>{wall_depth, 0}; - return { min - VectorTypeFor<2,T>{0, wall_depth}, min + VectorTypeFor<2,T>{wall_depth, TILE_SIZE2.y()} }; + using Vec2 = VectorTypeFor<2,T>; + auto min = tile_start<T>(k) - Vec2{wall_depth, 0}; + return { min - Vec2{0, wall_depth}, min + Vec2{wall_depth, tile_size_xy} }; } } // namespace floormat diff --git a/src/wall-atlas.hpp b/src/wall-atlas.hpp index e0203165..20dd2a84 100644 --- a/src/wall-atlas.hpp +++ b/src/wall-atlas.hpp @@ -53,19 +53,13 @@ struct Direction bool operator==(const Direction&) const noexcept; - static constexpr inline member_tuple groups[] = { + static constexpr member_tuple groups[] = { { "wall"_s, &Direction::wall, Group_::wall }, { "side"_s, &Direction::side, Group_::side }, { "top"_s, &Direction::top, Group_::top }, { "corner"_s, &Direction::corner, Group_::corner }, }; - static_assert(array_size(groups) == (size_t)Group_::COUNT); - - static constexpr inline member_tuple groups_for_draw[] = { - { "wall"_s, &Direction::wall, Group_::wall }, - { "side"_s, &Direction::side, Group_::side }, - { "top"_s, &Direction::top, Group_::top }, - }; + static_assert(array_size(groups) + /*pillar 1 */ 0 == (size_t)Group_::COUNT); }; struct Info diff --git a/src/wall-defs.hpp b/src/wall-defs.hpp index a19d560b..9f88853c 100644 --- a/src/wall-defs.hpp +++ b/src/wall-defs.hpp @@ -3,7 +3,7 @@ namespace floormat::Wall { -enum class Group_ : uint8_t { wall, side, top, corner, COUNT }; +enum class Group_ : uint8_t { wall, side, top, corner, /*pillar,*/ COUNT }; enum class Direction_ : uint8_t { N, W, COUNT }; diff --git a/src/world.cpp b/src/world.cpp index 89c127cc..1c7c45c9 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -33,9 +33,12 @@ struct world::robin_map_wrapper final : tsl::robin_map<object_id, bptr<object>, world::world(world&& w) noexcept = default; -world::world(std::unordered_map<chunk_coords_, chunk>&& chunks) : - world{std::max(initial_capacity, size_t(1/max_load_factor * 2 * chunks.size()))} +world::world(std::unordered_map<chunk_coords_, chunk, chunk_coords_hasher>&& chunks) { + const auto capʹ = (size_t)(1e-4f + (float)chunks.size() / max_load_factor); + const auto cap = std::max(capʹ, initial_capacity); + _chunks.reserve(cap); + _chunks.max_load_factor(max_load_factor); for (auto&& [coord, c] : chunks) operator[](coord) = move(c); } diff --git a/src/world.hpp b/src/world.hpp index a643de3b..01fc5cc9 100644 --- a/src/world.hpp +++ b/src/world.hpp @@ -67,7 +67,7 @@ private: public: explicit world(); ~world() noexcept; - explicit world(std::unordered_map<chunk_coords_, chunk>&& chunks); + explicit world(std::unordered_map<chunk_coords_, chunk, chunk_coords_hasher>&& chunks); struct pair_chunk_tile final { chunk& c; tile_ref t; }; // NOLINT diff --git a/test/app.cpp b/test/app.cpp index 6ace3ca5..c3ffa8fd 100644 --- a/test/app.cpp +++ b/test/app.cpp @@ -58,6 +58,7 @@ int App::exec() FM_TEST(test_local), // fast FM_TEST(test_magnum_math), + FM_TEST(test_util), FM_TEST(test_math), FM_TEST(test_astar_pool), FM_TEST(test_coords), diff --git a/test/app.hpp b/test/app.hpp index b1995e77..0e4d7a3a 100644 --- a/test/app.hpp +++ b/test/app.hpp @@ -41,6 +41,7 @@ void test_rtree(); void test_save(); void test_saves(); void test_script(); +void test_util(); void test_wall_atlas(); void test_wall_atlas2(); diff --git a/test/bptr.cpp b/test/bptr.cpp index e9d0dff3..7e225a2d 100644 --- a/test/bptr.cpp +++ b/test/bptr.cpp @@ -2,7 +2,11 @@ #include "compat/borrowed-ptr.inl" #include "compat/assert.hpp" #include "compat/defs.hpp" +#ifndef FM_NO_WEAK_BPTR +#include "compat/weak-borrowed-ptr.inl" +#endif #include <array> +#include <cr/Debug.h> namespace floormat { @@ -291,7 +295,7 @@ void test8() p1 = p2; fm_assert(A_total == 2 && A_alive == 1); - p2 = move(p1); + p2 = move(p1); (void)p2; fm_assert(A_total == 2 && A_alive == 1); p1.reset(); @@ -332,7 +336,7 @@ void test9() fm_assert(p1.use_count() == 0); fm_assert(p2.use_count() == 0); fm_assert(A_total == 1 && A_alive == 0); -}; +} void test10() { @@ -373,10 +377,22 @@ void test11() auto p1 = bptr<bptr_base>{new Foo{1}}; auto p2 = static_pointer_cast<Foo>(p1); auto p3 = static_pointer_cast<bptr_base>(p1); + fm_assert(p2->x == 1); fm_assert(p3); p1.destroy(); fm_assert(!p2); fm_assert(!p3); + + p1.destroy(); + p1.destroy(); + p1.destroy(); + p2.destroy(); + p2.destroy(); + p2.destroy(); + p3.destroy(); + p3.destroy(); + p3.destroy(); + fm_assert(!p1); fm_assert(!p2); fm_assert(!p3); } void test12() @@ -390,6 +406,48 @@ void test12() fm_assert(p1.use_count() == 1); } +void test13() +{ +#ifndef FM_NO_WEAK_BPTR + auto p1 = bptr<Foo>{InPlace, 13}; + fm_assert_equal(13, p1->x); + auto w1 = weak_bptr{p1}; + fm_assert(p1); fm_assert(w1.lock()); + fm_assert_equal(1u, p1.use_count()); + auto p2 = p1; + fm_assert_equal(2u, p2.use_count()); + p1 = {}; + fm_assert_equal(1u, p2.use_count()); + fm_assert(!p1); fm_assert(p2); + fm_assert(w1.lock()); + fm_assert_equal(13, w1.lock()->x); + p2 = {}; (void)p2; + fm_assert(!w1.lock()); +#endif +} + +void test14() +{ +#ifndef FM_NO_WEAK_BPTR + auto p1 = bptr<Foo>{InPlace, 14}; + auto w1 = weak_bptr{p1}; + auto w2 = weak_bptr{p1}; + fm_assert_equal(14, w1.lock()->x); + fm_assert_equal(14, w2.lock()->x); + fm_assert_equal(1u, p1.use_count()); + w1 = {}; + fm_assert(p1); + fm_assert_equal(1u, p1.use_count()); + w2 = {}; (void)w2; + fm_assert(p1); + fm_assert_equal(1u, p1.use_count()); + auto w3 = weak_bptr{p1}; + fm_assert_equal(14, w3.lock()->x); + p1 = {}; (void)p1; + fm_assert(!w1.lock()); fm_assert(!w2.lock()); fm_assert(!w3.lock()); +#endif +} + } // namespace void Test::test_bptr() @@ -406,6 +464,8 @@ void Test::test_bptr() test10(); test11(); test12(); + test13(); + test14(); } } // namespace floormat diff --git a/test/critter.cpp b/test/critter.cpp index 90619ce8..5b7ee52d 100644 --- a/test/critter.cpp +++ b/test/critter.cpp @@ -18,6 +18,8 @@ namespace floormat { namespace { +namespace fm_debug = floormat::debug::detail; + using enum rotation; using fu2::function_view; using Function = function_view<Ns() const>; @@ -147,7 +149,7 @@ bool run(world& w, const function_view<Ns() const>& make_dt, if (b) [[likely]] return false; else - fm_emit_assert_fail("false", file, line); + fm_debug::emit_abort(file, line, "false"); }; for (i = 0; true; i++) diff --git a/test/entity.cpp b/test/entity.cpp index 2bbdf29a..97ffd7a7 100644 --- a/test/entity.cpp +++ b/test/entity.cpp @@ -258,7 +258,7 @@ void test_range2() const auto A = m_foo.erased(); auto r = A.get_range(&x); auto i = r.convert<int>(); - fm_assert(i.second == rʹ.max); + fm_assert(i.second() == rʹ.max); } constexpr bool test_range3() @@ -277,8 +277,8 @@ void test_range4() constexpr auto rʹ = f.get_range(f.range, TestAccessors{}); const auto A = f.erased(); const auto r = A.get_range(&x).convert<Vector2i>(); - fm_assert(r.first == rʹ.min); - fm_assert(r.second == rʹ.max); + fm_assert(r.first() == rʹ.min); + fm_assert(r.second() == rʹ.max); } constexpr bool test_enum_range() diff --git a/test/hole.cpp b/test/hole.cpp index 7ccc86c3..09395c47 100644 --- a/test/hole.cpp +++ b/test/hole.cpp @@ -1,5 +1,7 @@ #include "app.hpp" #include "src/hole.hpp" +#include "src/hole-cut.hpp" +#include "src/tile-constants.hpp" namespace floormat { namespace { @@ -53,6 +55,44 @@ void test1(Vector2i offset) #endif } +auto make_search_predicate(const CutResult<int>& res) +{ + return [&](Vector2i min, Vector2i max) -> bool { + for (auto i = 0u; i < res.size; i++) + if (res.array[i].min == min && res.array[i].max == max) + return true; + return false; + }; +} + +void test2() +{ + const auto res = CutResult<int>::cut({{}, Vector2ub{tile_size_xy}}, {Vector2i(-tile_size_xy/2), Vector2ub{tile_size_xy}}); + fm_assert(res.size == 2); + const auto has = make_search_predicate(res); + fm_assert(has({-32, 0}, {32, 32})); + fm_assert(has({0, -32}, {32, 0})); +} + +void test3() +{ + constexpr auto h = tile_size_xy/2; + + { + const auto res = CutResult<Int>::cut({-h, -1}, {h, 1}, {-2, -100}, {2, 100}); + fm_assert(res.found()); + fm_assert_equal(2, (int)res.size); + } + { + const auto res = CutResult<Int>::cut({-h, 0}, {h, 0}, {-2, -100}, {2, 100}); + fm_assert(res.found()); + fm_assert_equal(2, (int)res.size); + const auto has = make_search_predicate(res); + fm_assert(has({-h, 0}, {-2, 0})); + fm_assert(has({ 2, 0}, { h, 0})); + } +} + } // namespace void Test::test_hole() @@ -67,6 +107,9 @@ void Test::test_hole() for (auto offset : offsets) test1(offset); + + test2(); + test3(); } } // namespace floormat diff --git a/test/save.cpp b/test/save.cpp index 04c9810c..76cdd271 100644 --- a/test/save.cpp +++ b/test/save.cpp @@ -103,7 +103,6 @@ void assert_chunks_equal(const chunk& a, const chunk& b) case object_type::COUNT: fm_assert(false); case object_type::critter: { - // todo! remove duplication const auto& e1 = static_cast<const critter&>(ae); const auto& e2 = static_cast<const critter&>(be); const auto p1 = critter_proto(e1), p2 = critter_proto(e2); diff --git a/test/util.cpp b/test/util.cpp new file mode 100644 index 00000000..e250b072 --- /dev/null +++ b/test/util.cpp @@ -0,0 +1,45 @@ +#include "app.hpp" +#include "compat/array-size.hpp" + +namespace floormat::Test { + +namespace { + +struct Foo +{ + static constexpr std::array<int, 11> Array_1 = {}; + static constexpr const void* Array_2[22] = {}; + + std::array<int, 33> array_3; + int array_4[55] = {}; +}; + +constexpr bool test_array_size() +{ + fm_assert(static_array_size<decltype(Foo::Array_1)> == 11); + fm_assert(array_size(Foo::Array_1) == 11); + + fm_assert(static_array_size<decltype(Foo::Array_2)> == 22); + fm_assert(array_size(&Foo::Array_2) == 22); + + fm_assert(static_array_size<decltype(Foo{}.array_3)> == 33); + fm_assert(array_size(Foo{}.array_3) == 33); + fm_assert(array_size(&Foo::array_3) == 33); + + fm_assert(static_array_size<const int(&)[44]> == 44); + + fm_assert(static_array_size<decltype(Foo::array_4)> == 55); + fm_assert(array_size(&Foo::array_4) == 55); + fm_assert(array_size(Foo{}.array_4) == 55); + + return true; +} + +} // namespace + +void test_util() +{ + static_assert(test_array_size()); +} + +} // namespace floormat::Test diff --git a/userconfig-sthalik@Windows-Clang.cmake b/userconfig-sthalik@Windows-Clang.cmake index dc0ba1dd..4f2d9dfa 100644 --- a/userconfig-sthalik@Windows-Clang.cmake +++ b/userconfig-sthalik@Windows-Clang.cmake @@ -11,6 +11,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "DEBUG") -fcoverage-mapping ) endif() + add_definitions(-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE) else() set(BUILD_SHARED_LIBS OFF) add_compile_options(-march=native -mavx2) @@ -19,6 +20,8 @@ else() add_link_options(-fmerge-all-constants -flto=full -fwhole-program-vtables -fforce-emit-vtables) add_link_options(-Wl,--gc-sections -Wl,--icf=all) add_compile_options(-Wno-nan-infinity-disabled) + add_definitions(-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST) + add_compile_options(-fpointer-tbaa) endif() if(FLOORMAT_ASAN) @@ -26,13 +29,13 @@ if(FLOORMAT_ASAN) add_link_options(-fsanitize=undefined,bounds,address) endif() -set(OpenCV_DIR "f:/build/opencv/build-clang-release-floormat/install" CACHE PATH "" FORCE) +set(OpenCV_DIR "d:/dev/opencv-floormat-clang-release" CACHE PATH "" FORCE) set(CMAKE_INSTALL_MESSAGE NEVER) sets(STRING CMAKE_C_FLAGS "-march=x86-64-v2 -mtune=native -mavx2 -maes -g -gcolumn-info -gdwarf-aranges" CMAKE_C_FLAGS_DEBUG "-O0 -fstack-protector-all -g" - CMAKE_C_FLAGS_RELEASE "-Ofast -ffast-math -mpopcnt -fomit-frame-pointer -fno-stack-protector -static" + CMAKE_C_FLAGS_RELEASE "-O3 -ffast-math -mpopcnt -fomit-frame-pointer -fno-stack-protector -static" CMAKE_EXE_LINKER_FLAGS_DEBUG "" CMAKE_SHARED_LINKER_FLAGS_DEBUG "" ) @@ -42,12 +45,12 @@ sets(STRING CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" ) +add_definitions(-D_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) + if(NOT CMAKE_CXX_COMPILER_VERSION LESS "18.0") add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fassume-nothrow-exception-dtor>) endif() -set(FLOORMAT_SUBMODULE-SDL2 1) - # for building submodule dependencies function(fm-userconfig-external) add_compile_options( @@ -62,15 +65,15 @@ function(fm-userconfig-external) SDL_SHARED OFF SDL_STATIC ON CORRADE_BUILD_STATIC ON - CORRADE_BUILD_TESTS OFF + CORRADE_BUILD_TESTS ON CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT ON MAGNUM_BUILD_PLUGINS_STATIC ON MAGNUM_BUILD_STATIC ON - MAGNUM_BUILD_TESTS OFF + MAGNUM_BUILD_TESTS ON ) else() sets(BOOL - FLOORMAT_SUBMODULE-SDL2 OFF + FLOORMAT_SUBMODULE-SDL2 ON SDL_SHARED ON SDL_STATIC OFF CORRADE_BUILD_STATIC OFF @@ -81,14 +84,6 @@ function(fm-userconfig-external) MAGNUM_BUILD_TESTS ON ) endif() - if(FLOORMAT_ASAN) - sets(BOOL - CORRADE_BUILD_STATIC ON - CORRADE_BUILD_TESTS OFF - MAGNUM_BUILD_STATIC ON - MAGNUM_BUILD_TESTS OFF - ) - endif() endfunction() # for test_app sources only diff --git a/userconfig-sthalik@Windows-GNU.cmake b/userconfig-sthalik@Windows-GNU.cmake index 81d99338..99bb412a 100644 --- a/userconfig-sthalik@Windows-GNU.cmake +++ b/userconfig-sthalik@Windows-GNU.cmake @@ -13,23 +13,22 @@ sets(STRING list(APPEND CMAKE_IGNORE_PATH "c:/msys64" "c:/msys64/clang64") list(APPEND CMAKE_IGNORE_PREFIX_PATH "c:/msys64" "c:/msys64/clang64") -add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fconcepts-diagnostics-depth=3>) +add_definitions(-D_GLIBCXX_USE_DEPRECATED=0 -D_GLIBCXX_USE_CXX11_ABI) add_compile_options(-fdiagnostics-color=always) -add_compile_options(-fstack-usage -Wstack-usage=16384) +add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fconcepts-diagnostics-depth=3>) + +add_definitions(-D_GLIBCXX_ASSERTIONS) +add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:$<$<CONFIG:DEBUG,Debug>:-D_GLIBCXX_DEBUG>>) +add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:$<$<CONFIG:DEBUG,Debug>:-D_GLIBCXX_DEBUG_PEDANTIC>>) +add_compile_definitions($<$<NOT:$<CONFIG:Debug,DEBUG>>:_FORTIFY_SOURCE=2>) +add_compile_definitions($<$<CONFIG:Debug,DEBUG>:_FORTIFY_SOURCE=3>) if(CMAKE_BUILD_TYPE STREQUAL "DEBUG") - add_definitions(-D_GLIBCXX_ASSERTIONS) - add_definitions(-D_GLIBCXX_USE_DEPRECATED=0 -D_GLIBCXX_USE_CXX11_ABI) - add_definitions(-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC) set(OpenCV_DIR "f:/build/opencv/build-gcc-debug-floormat/install" CACHE PATH "" FORCE) else() set(BUILD_SHARED_LIBS OFF) set(OpenCV_DIR "f:/build/opencv/build-gcc-release-floormat/install" CACHE PATH "" FORCE) endif() -add_compile_definitions("$<$<CONFIG:Debug,DEBUG>:_FORTIFY_SOURCE=3>") -add_compile_definitions("$<IF:$<CONFIG:Debug,DEBUG>,,_FORTIFY_SOURCE=3>") - -set(FLOORMAT_SUBMODULE-SDL2 1) # for building submodule dependencies function(fm-userconfig-external) @@ -70,23 +69,30 @@ endfunction() # for floormat sources only function(fm-userconfig-src) + add_compile_options(-fstack-usage -Wstack-usage=16384) add_compile_options( -Wall -Wextra -Wpedantic -Wno-old-style-cast -Wno-padded -Wstringop-overflow -Wstringop-truncation -Wswitch-enum -Wlarger-than=8192 -Wlogical-op -Wunsafe-loop-optimizations + -Wctor-dtor-privacy -Wno-error=ctor-dtor-privacy + -Winvalid-constexpr -Winvalid-imported-macros + -Woverloaded-virtual + #-fconcepts-diagnostics-depth=2 ) #add_compile_options(-Wuseless-cast) - add_link_options(-Wno-lto-type-mismatch -Wno-odr) + add_link_options(-Wno-lto-type-mismatch -Wodr -Wno-error=odr) add_compile_options( #-Wno-c++20-compat -Wno-switch-enum -Wno-ctad-maybe-unsupported -Wno-ignored-attributes -Wno-parentheses - -Wno-lto-type-mismatch -Wno-odr + #-Wno-lto-type-mismatch -Wno-odr + -Wno-error=lto-type-mismatch + -Wodr -Wno-error=odr ) add_compile_options( -Werror=format |
