diff --git a/.clang-format b/.clang-format index 9264f3bc0f..4191b164f7 100644 --- a/.clang-format +++ b/.clang-format @@ -81,7 +81,7 @@ IndentCaseBlocks: false IndentCaseLabels: false IndentExternBlock: NoIndent IndentGotoLabels: false -IndentPPDirectives: None +IndentPPDirectives: BeforeHash IndentWidth: 4 IndentWrappedFunctionNames: false # clang-format-16 InsertNewlineAtEOF: true diff --git a/.github/workflows/macosx-ci.yml b/.github/workflows/macosx-ci.yml index e30906228d..2270121092 100644 --- a/.github/workflows/macosx-ci.yml +++ b/.github/workflows/macosx-ci.yml @@ -5,7 +5,7 @@ jobs: # https://docs.github.com/en/actions/reference/software-installed-on-github-hosted-runners # we install stuff not already there macos-build: - runs-on: macOS-latest + runs-on: macos-latest steps: - name: Checkout sources uses: actions/checkout@v4 @@ -19,7 +19,7 @@ jobs: string(TIMESTAMP current_date "%Y-%m-%d-%H:%M:%S" UTC) message("timestamp=${current_date}" >> $GITHUB_OUTPUT) - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache-deps with: path: | @@ -29,7 +29,7 @@ jobs: restore-keys: | ${{ runner.os }}-deps- - name: Cache ccache dir - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache-ccache with: path: ~/Library/Caches/ccache @@ -40,26 +40,14 @@ jobs: run: brew update-reset - name: Brew update run: brew update - - name: Install clang / LLVM 15.0.0 - run: | - set -x - brew install --force wget - mkdir -p /tmp/clang - cd /tmp/clang - wget https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.0/clang+llvm-15.0.0-x86_64-apple-darwin.tar.xz -O clang-15.0.0.tar.xz - ls - tar -xvf clang-15.0.0.tar.xz -C ~ - cd ~ - mv clang+llvm-15.0.0-x86_64-apple-darwin clang-15.0.0 - ~/clang-15.0.0/bin/clang++ --version - name: Brew install DeJaVu fonts run: brew install --cask font-dejavu - - name: Remove python's 2to3 link so that 'brew link' does not fail - run: rm /usr/local/bin/2to3* && rm /usr/local/bin/idle3* - name: Install environment helpers with homebrew run: brew install --force ccache - - name: Install dependencies with homebrew - run: brew install --force libepoxy freetype fontconfig harfbuzz opus opusfile qt6 libogg libpng toml11 eigen + - name: Install LLVM with homebrew + run: brew install --force llvm + - name: Install openage dependencies with homebrew + run: brew install --force cmake python3 libepoxy freetype fontconfig harfbuzz opus opusfile qt6 libogg libpng toml11 eigen - name: Install nyan dependencies with homebrew run: brew install --force flex make - name: Install python3 packages @@ -68,9 +56,7 @@ jobs: # numpy pulls gcc as dep? and pygments doesn't work. run: pip3 install --upgrade --break-system-packages cython numpy mako lz4 pillow pygments setuptools toml - name: Configure - run: | - CLANG_PATH="$HOME/clang-15.0.0/bin/clang++" - ./configure --compiler="$CLANG_PATH" --mode=debug --ccache --download-nyan + run: ./configure --compiler="$(brew --prefix llvm)/bin/clang++" --mode=release --ccache --download-nyan - name: Build run: make -j$(sysctl -n hw.logicalcpu) VERBOSE=1 - name: Test diff --git a/.github/workflows/ubuntu-22.04.yml b/.github/workflows/ubuntu-22.04.yml index b6eaa70230..311524f799 100644 --- a/.github/workflows/ubuntu-22.04.yml +++ b/.github/workflows/ubuntu-22.04.yml @@ -16,7 +16,7 @@ jobs: sudo docker save openage-devenv:latest | gzip > /tmp/staging/devenv.tar.gz shell: bash - name: Publish the Docker image - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: devenv-image-compressed.tar.gz path: '/tmp/staging/devenv.tar.gz' @@ -32,7 +32,7 @@ jobs: run: mkdir -p /tmp/image shell: bash - name: Download devenv image - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: devenv-image-compressed.tar.gz path: '/tmp/image' @@ -47,7 +47,7 @@ jobs: mkdir -p /tmp/openage tar -czvf /tmp/openage/openage-build.tar.gz ./build - name: Publish build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: openage-build.tar.gz path: '/tmp/openage/openage-build.tar.gz' diff --git a/.github/workflows/windows-server-2019.yml b/.github/workflows/windows-server-2019.yml index 7a2262c00f..df9d96284b 100644 --- a/.github/workflows/windows-server-2019.yml +++ b/.github/workflows/windows-server-2019.yml @@ -15,7 +15,7 @@ jobs: vswhere -latest shell: pwsh - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.9' architecture: 'x64' @@ -78,7 +78,7 @@ jobs: python -m openage --add-dll-search-path $DLL_PATH --version shell: pwsh - name: Publish build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: build-files @@ -86,7 +86,7 @@ jobs: if-no-files-found: error retention-days: 30 - name: Publish packaged artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: package-files diff --git a/.github/workflows/windows-server-2022.yml b/.github/workflows/windows-server-2022.yml index f0172b103e..bbc73b78c2 100644 --- a/.github/workflows/windows-server-2022.yml +++ b/.github/workflows/windows-server-2022.yml @@ -15,7 +15,7 @@ jobs: vswhere -latest shell: pwsh - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.9' architecture: 'x64' @@ -78,7 +78,7 @@ jobs: python -m openage --add-dll-search-path $DLL_PATH --version shell: pwsh - name: Publish build artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: build-files @@ -86,7 +86,7 @@ jobs: if-no-files-found: error retention-days: 30 - name: Publish packaged artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: package-files diff --git a/README.md b/README.md index ccd2b66c0c..266cdfc5fa 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Goals ----- * Fully authentic look and feel - * This can only be approximated, since the behaviour of the original game is mostly undocumented, + * This can only be approximated since the behavior of the original game is mostly undocumented, and guessing/experimenting can only get you this close * We will not implement useless artificial limitations (max 30 selectable units...) * An easily-moddable content format: [**nyan** yet another notation](https://github.com/SFTtech/nyan) @@ -79,7 +79,7 @@ Current State of the Project **Important notice**: At the moment, "gameplay" is basically non-functional. We're implementing the internal game simulation (how units even do anything) with simplicity and extensibility in mind, so we had to get rid of the temporary (but kind of working) previous version. -With these changes we can (finally) actually make use of our converted asset packs and our nyan API! +With these changes, we can (finally) actually make use of our converted asset packs and our nyan API! We're working day and night to make gameplay return\*. If you're interested, we wrote detailed explanations on our blog: [Part 1](https://blog.openage.dev/new-gamestate-2020.html), [Part 2](https://blog.openage.dev/engine-core-modules.html), [Monthly Devlog](https://blog.openage.dev/tag/news.html). @@ -99,16 +99,16 @@ If you're interested, we wrote detailed explanations on our blog: [Part 1](https Installation Packages --------------------- -There's many missing parts for an actually working game. +There are many missing parts for an actually working game. So if you "just wanna play", [you'll be disappointed](#current-state-of-the-project), unfortunately. -We strongly recommend to build the program from source to get the latest, greatest and shiniest project state :) +We strongly recommend building the program from source to get the latest, greatest, and shiniest project state :) -* For **Linux** check at [repology](https://repology.org/project/openage/versions) if your distribution has any packages available. Otherwise you need to build from source. +* For **Linux** check at [repology](https://repology.org/project/openage/versions) if your distribution has any packages available. Otherwise, you need to build from source. We don't release `*.deb`, `*.rpm`, Flatpak, snap or AppImage packages yet. * For **Windows** check our [release page](https://github.com/SFTtech/openage/releases) for the latest installer. - Otherwise, you need to build from source. + Otherwise, you need to build from the source. * For **macOS** we currently don't have any packages, you need to build from source. @@ -152,7 +152,7 @@ Contributing You might ask yourself now "Sounds cool, but how do I participate and ~~get famous~~ contribute useful features?". -Fortunately for you, there is a lot to do and we are very grateful for help. +Fortunately for you, there is a lot to do and we are very grateful for your help. ## Where do I start? @@ -160,7 +160,7 @@ Fortunately for you, there is a lot to do and we are very grateful for help. * **Ask us** in the [chat](https://matrix.to/#/#sfttech:matrix.org). Someone there could need help with something. * You can also **take the initiative** and fix a bug you found, create an issue for discussion or - implement a feature that we never though of, but always wanted. + implement a feature that we never thought of, but always wanted. ## Ok, I found something. What now? @@ -176,7 +176,7 @@ Fortunately for you, there is a lot to do and we are very grateful for help. ## How do I contribute my features/changes? * Read the **[contributing guide](/doc/contributing.md)**. -* You can upload work in progress (WIP) versions or drafts of your contribution to get feedback or support. +* You can upload work-in-progress (WIP) versions or drafts of your contribution to get feedback or support. * Tell us (again) when you want us to review your work. ## I want to help, but I'm not a programmer... diff --git a/copying.md b/copying.md index fb6501bab0..ec1ddd2a46 100644 --- a/copying.md +++ b/copying.md @@ -154,6 +154,8 @@ _the openage authors_ are: | Haoyang Bi | AyiStar | ayistar à outlook dawt com | | Michael Seibt | RoboSchmied | github à roboschmie dawt de | | Nikhil Ghosh | NikhilGhosh75 | nghosh606 à gmail dawt com | +| Edvin Lindholm | EdvinLndh | edvinlndh à gmail dawt com | +| Jeremiah Morgan | jere8184 | jeremiahmorgan dawt bham à outlook dawt com | If you're a first-time committer, add yourself to the above list. This is not just for legal reasons, but also to keep an overview of all those nicknames. diff --git a/doc/build_instructions/macos.md b/doc/build_instructions/macos.md index ba139c3b12..c9946f2d39 100644 --- a/doc/build_instructions/macos.md +++ b/doc/build_instructions/macos.md @@ -6,14 +6,10 @@ ``` brew update-reset && brew update -brew tap homebrew/cask-fonts -brew install font-dejavu +brew install --cask font-dejavu brew install cmake python3 libepoxy freetype fontconfig harfbuzz opus opusfile qt6 libogg libpng toml11 eigen brew install llvm -pip3 install cython numpy mako lz4 pillow pygments toml - -# optional, for documentation generation -brew install doxygen +pip3 install --upgrade --break-system-packages cython numpy mako lz4 pillow pygments setuptools toml ``` You will also need [nyan](https://github.com/SFTtech/nyan/blob/master/doc/building.md) and its dependencies: @@ -22,7 +18,14 @@ You will also need [nyan](https://github.com/SFTtech/nyan/blob/master/doc/buildi brew install flex make ``` +Optionally, for documentation generation: + +``` +brew install doxygen +``` + ## Clone the repository + ``` git clone https://github.com/SFTtech/openage cd openage @@ -30,8 +33,17 @@ cd openage ## Building +We advise against using the clang version that comes with macOS (Apple Clang) as it notoriously out of date and often causes compilation errors. Use homebrew's clang if you don't want any trouble. You can pass the path of homebrew clang to the openage `configure` script which will generate the CMake files for building: + +``` +# on Intel macOS, llvm is by default in /usr/local/Cellar/llvm/bin/ +# on ARM macOS, llvm is by default in /opt/homebrew/Cellar/llvm/bin/ +./configure --compiler="$(brew --prefix llvm)/bin/clang"" --download-nyan +``` + +Afterwards, trigger the build using `make`: + ``` -./configure --compiler=$(which clang++) --mode=release --download-nyan make -j$(sysctl -n hw.ncpu) ``` @@ -40,7 +52,7 @@ make -j$(sysctl -n hw.ncpu) ## Running -`make run` or `./bin/run` launches the game. Try `./bin/run --help`! +`make run` or `cd bin && ./run` launches the game. Try `./run --help` if you don't know what to do! ## To create the documentation diff --git a/doc/build_instructions/windows_msvc.md b/doc/build_instructions/windows_msvc.md index 5be6d9167c..af889d8b99 100644 --- a/doc/build_instructions/windows_msvc.md +++ b/doc/build_instructions/windows_msvc.md @@ -93,7 +93,7 @@ _Note:_ If you want to download and build Nyan automatically add `-DDOWNLOAD_NYA - If prebuilt QT6 was installed, the original location of QT6 DLLs is `\bin`. - Now, to run the openage: - - Open a CMD window in `\build\` and run `python -m openage game` + - Open a CMD window in `\build\` and run `python -m openage main` - Execute`\build\run.exe` every time after that and enjoy! ## Packaging diff --git a/libopenage/console/tests.cpp b/libopenage/console/tests.cpp index 445fb71b30..7da8dd02f4 100644 --- a/libopenage/console/tests.cpp +++ b/libopenage/console/tests.cpp @@ -1,12 +1,12 @@ -// Copyright 2014-2019 the openage authors. See copying.md for legal info. +// Copyright 2014-2024 the openage authors. See copying.md for legal info. -#include #include +#include #ifdef _MSC_VER -#define STDOUT_FILENO 1 + #define STDOUT_FILENO 1 #else -#include + #include #endif #include "../util/fds.h" #include "../util/pty.h" @@ -15,8 +15,8 @@ #include #include -#include "../log/log.h" #include "../error/error.h" +#include "../log/log.h" #include "buf.h" #include "console.h" @@ -51,7 +51,7 @@ void render() { void interactive() { - #ifndef _WIN32 +#ifndef _WIN32 console::Buf buf{{80, 25}, 1337, 80}; struct winsize ws; @@ -75,7 +75,7 @@ void interactive() { throw Error(MSG(err) << "execl(\"" << shell << "\", \"" << shell << "\", nullptr) failed: " << strerror(errno)); } default: - //we are the parent + // we are the parent break; } @@ -143,7 +143,7 @@ void interactive() { ssize_t retval = read(ptyin.fd, rdbuf, rdbuf_size); switch (retval) { case -1: - switch(errno) { + switch (errno) { case EIO: loop = false; break; @@ -175,7 +175,7 @@ void interactive() { // show cursor termout.puts("\x1b[?25h"); - #endif /* _WIN32 */ +#endif /* _WIN32 */ } diff --git a/libopenage/coord/coord.h.template b/libopenage/coord/coord.h.template index 8aeafadc5f..278b486ae4 100644 --- a/libopenage/coord/coord.h.template +++ b/libopenage/coord/coord.h.template @@ -74,12 +74,8 @@ struct Coord${camelcase}Absolute { return static_cast(*this); } - constexpr bool operator ==(const Absolute &other) const { - return ${formatted_members("(this->{0} == other.{0})", join_with=" && ")}; - } - - constexpr bool operator !=(const Absolute &other) const { - return !(*this == other); + friend constexpr bool operator ==(const Absolute &lhs, const Absolute &rhs) { + return ${formatted_members("(lhs.{0} == rhs.{0})", join_with=" && ")}; } }; @@ -167,12 +163,8 @@ struct Coord${camelcase}Relative { return static_cast(*this); } - constexpr bool operator ==(const Relative &other) const { - return ${formatted_members("(this->{0} == other.{0})", join_with=" && ")}; - } - - constexpr bool operator !=(const Relative &other) const { - return !(*this == other); + friend constexpr bool operator ==(const Relative &lhs, const Relative &rhs) { + return ${formatted_members("(lhs.{0} == rhs.{0})", join_with=" && ")}; } }; diff --git a/libopenage/engine/engine.h b/libopenage/engine/engine.h index 4f96ae74bf..1fe4298061 100644 --- a/libopenage/engine/engine.h +++ b/libopenage/engine/engine.h @@ -11,7 +11,7 @@ // TODO: Remove custom jthread definition when clang/libc++ finally supports it #if __llvm__ -#if !__cpp_lib_jthread + #if !__cpp_lib_jthread namespace std { class jthread : public thread { public: @@ -27,9 +27,9 @@ class jthread : public thread { } }; } // namespace std -#endif + #endif #else -#include + #include #endif diff --git a/libopenage/error/handlers.cpp b/libopenage/error/handlers.cpp index f608b0e78f..b3e61450b2 100644 --- a/libopenage/error/handlers.cpp +++ b/libopenage/error/handlers.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2023 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. /* * This file holds handlers for std::terminate and SIGSEGV. @@ -17,9 +17,9 @@ #include #ifdef _MSC_VER -#include + #include #else -#include + #include #endif #include "util/init.h" diff --git a/libopenage/error/stackanalyzer.cpp b/libopenage/error/stackanalyzer.cpp index 878cb01025..cc04dae05d 100644 --- a/libopenage/error/stackanalyzer.cpp +++ b/libopenage/error/stackanalyzer.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2023 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. #include "stackanalyzer.h" @@ -30,8 +30,8 @@ constexpr uint64_t base_skip_frames = 1; #if WITH_BACKTRACE -// use modern -#include + // use modern + #include namespace openage { namespace error { @@ -59,7 +59,7 @@ struct backtrace_state *bt_state; util::OnInit init_backtrace_state([]() { bt_state = backtrace_create_state( nullptr, // auto-determine filename - 1, // threaded + 1, // threaded backtrace_error_callback, nullptr // passed to the callback ); @@ -189,8 +189,8 @@ void StackAnalyzer::get_symbols(std::function cb #else // WITHOUT_BACKTRACE -#ifdef _WIN32 -#include + #ifdef _WIN32 + #include namespace openage { namespace error { @@ -208,10 +208,10 @@ void StackAnalyzer::analyze() { } // namespace error } // namespace openage -#else // not _MSC_VER + #else // not _MSC_VER -// use GNU's -#include + // use GNU's + #include namespace openage::error { @@ -256,7 +256,7 @@ void StackAnalyzer::analyze() { } // namespace openage::error -#endif // for _MSC_VER or GNU execinfo + #endif // for _MSC_VER or GNU execinfo namespace openage::error { diff --git a/libopenage/event/demo/gui.cpp b/libopenage/event/demo/gui.cpp index 100509c0f4..66d0bbf79d 100644 --- a/libopenage/event/demo/gui.cpp +++ b/libopenage/event/demo/gui.cpp @@ -1,25 +1,25 @@ -// Copyright 2016-2023 the openage authors. See copying.md for legal info. +// Copyright 2016-2024 the openage authors. See copying.md for legal info. #include "gui.h" // the gui requires ncurses. #if WITH_NCURSES -#include -#include -#include -#include -#ifdef __MINGW32__ -#include -#else -#include -#endif // __MINGW32__ -#include - -#include "curve/continuous.h" -#include "curve/discrete.h" -#include "event/demo/gamestate.h" -#include "util/fixed_point.h" + #include + #include + #include + #include + #ifdef __MINGW32__ + #include + #else + #include + #endif // __MINGW32__ + #include + + #include "curve/continuous.h" + #include "curve/discrete.h" + #include "event/demo/gamestate.h" + #include "util/fixed_point.h" namespace openage::event::demo { diff --git a/libopenage/event/demo/gui.h b/libopenage/event/demo/gui.h index de21a99d02..8e447d808f 100644 --- a/libopenage/event/demo/gui.h +++ b/libopenage/event/demo/gui.h @@ -1,18 +1,18 @@ -// Copyright 2015-2023 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. #pragma once #include "config.h" #if WITH_NCURSES -#include -#include -#include -#include - -#include "event/demo/gamestate.h" -#include "time/time.h" -#include "util/vector.h" + #include + #include + #include + #include + + #include "event/demo/gamestate.h" + #include "time/time.h" + #include "util/vector.h" namespace openage::event::demo { diff --git a/libopenage/event/demo/main.cpp b/libopenage/event/demo/main.cpp index 3ccbed5ac6..f5100b2e82 100644 --- a/libopenage/event/demo/main.cpp +++ b/libopenage/event/demo/main.cpp @@ -1,4 +1,4 @@ -// Copyright 2016-2023 the openage authors. See copying.md for legal info. +// Copyright 2016-2024 the openage authors. See copying.md for legal info. #include "main.h" @@ -16,11 +16,11 @@ #include "renderer/gui/integration/public/gui_application_with_logger.h" #if WITH_NCURSES -#ifdef __MINGW32__ -#include -#else -#include -#endif // __MINGW32__ + #ifdef __MINGW32__ + #include + #else + #include + #endif // __MINGW32__ #endif diff --git a/libopenage/event/demo/physics.cpp b/libopenage/event/demo/physics.cpp index e27a51d962..e7e969a533 100644 --- a/libopenage/event/demo/physics.cpp +++ b/libopenage/event/demo/physics.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2023 the openage authors. See copying.md for legal info. +// Copyright 2017-2024 the openage authors. See copying.md for legal info. #include "physics.h" @@ -9,13 +9,13 @@ #include #if WITH_NCURSES -#ifdef __MINGW32__ -#include -#else -#include -#endif // __MINGW32__ + #ifdef __MINGW32__ + #include + #else + #include + #endif // __MINGW32__ -#include "gui.h" + #include "gui.h" #endif #include "error/error.h" @@ -377,7 +377,7 @@ void Physics::init(const std::shared_ptr &gstate, loop->create_event("demo.ball.reflect_panel", state->ball->position, state, now); // FIXME once "reset": deregister - //reset(state, mgr, now); + // reset(state, mgr, now); } void Physics::process_input(const std::shared_ptr &state, @@ -466,11 +466,11 @@ void Physics::process_input(const std::shared_ptr &state, Physics::reset(state, *mgr, now); break; - //if (player->state->get(now).state == PongEvent::LOST) { + // if (player->state->get(now).state == PongEvent::LOST) { // state->ball.position->set_last(now, state.display_boundary * 0.5); - //} - //update_ball(state, now, init_recursion_limit); - //break; + // } + // update_ball(state, now, init_recursion_limit); + // break; default: break; diff --git a/libopenage/gamestate/component/api/idle.h b/libopenage/gamestate/component/api/idle.h index f9d42e5330..e8b114ff67 100644 --- a/libopenage/gamestate/component/api/idle.h +++ b/libopenage/gamestate/component/api/idle.h @@ -1,4 +1,4 @@ -// Copyright 2021-2023 the openage authors. See copying.md for legal info. +// Copyright 2021-2024 the openage authors. See copying.md for legal info. #pragma once @@ -7,7 +7,7 @@ namespace openage::gamestate::component { -class Idle : public APIComponent { +class Idle final : public APIComponent { public: using APIComponent::APIComponent; diff --git a/libopenage/gamestate/component/api/live.h b/libopenage/gamestate/component/api/live.h index cf733d5199..2e1f5e41d5 100644 --- a/libopenage/gamestate/component/api/live.h +++ b/libopenage/gamestate/component/api/live.h @@ -14,7 +14,7 @@ namespace openage::gamestate::component { -class Live : public APIComponent { +class Live final : public APIComponent { public: using APIComponent::APIComponent; diff --git a/libopenage/gamestate/component/api/move.h b/libopenage/gamestate/component/api/move.h index 1cc6d65cf7..99d87b8d72 100644 --- a/libopenage/gamestate/component/api/move.h +++ b/libopenage/gamestate/component/api/move.h @@ -1,4 +1,4 @@ -// Copyright 2021-2023 the openage authors. See copying.md for legal info. +// Copyright 2021-2024 the openage authors. See copying.md for legal info. #pragma once @@ -8,7 +8,7 @@ namespace openage::gamestate::component { -class Move : public APIComponent { +class Move final : public APIComponent { public: using APIComponent::APIComponent; diff --git a/libopenage/gamestate/component/api/selectable.h b/libopenage/gamestate/component/api/selectable.h index d12e1522a7..b37f674444 100644 --- a/libopenage/gamestate/component/api/selectable.h +++ b/libopenage/gamestate/component/api/selectable.h @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #pragma once @@ -10,7 +10,7 @@ namespace openage::gamestate::component { -class Selectable : public APIComponent { +class Selectable final : public APIComponent { public: using APIComponent::APIComponent; diff --git a/libopenage/gamestate/component/api/turn.h b/libopenage/gamestate/component/api/turn.h index 881bb05de9..9506bcd6db 100644 --- a/libopenage/gamestate/component/api/turn.h +++ b/libopenage/gamestate/component/api/turn.h @@ -1,4 +1,4 @@ -// Copyright 2021-2023 the openage authors. See copying.md for legal info. +// Copyright 2021-2024 the openage authors. See copying.md for legal info. #pragma once @@ -10,7 +10,7 @@ namespace openage::gamestate::component { -class Turn : public APIComponent { +class Turn final : public APIComponent { public: using APIComponent::APIComponent; diff --git a/libopenage/gamestate/component/internal/activity.h b/libopenage/gamestate/component/internal/activity.h index 16976b7e11..ae0fd73389 100644 --- a/libopenage/gamestate/component/internal/activity.h +++ b/libopenage/gamestate/component/internal/activity.h @@ -27,7 +27,7 @@ class Node; namespace component { -class Activity : public InternalComponent { +class Activity final : public InternalComponent { public: /** * Creates a new activity component. diff --git a/libopenage/gamestate/component/internal/command_queue.h b/libopenage/gamestate/component/internal/command_queue.h index ea0c26502a..a7905c4d24 100644 --- a/libopenage/gamestate/component/internal/command_queue.h +++ b/libopenage/gamestate/component/internal/command_queue.h @@ -1,4 +1,4 @@ -// Copyright 2021-2023 the openage authors. See copying.md for legal info. +// Copyright 2021-2024 the openage authors. See copying.md for legal info. #pragma once @@ -19,7 +19,7 @@ class EventLoop; namespace gamestate::component { -class CommandQueue : public InternalComponent { +class CommandQueue final : public InternalComponent { public: /** * Creates an Ownership component. diff --git a/libopenage/gamestate/component/internal/ownership.h b/libopenage/gamestate/component/internal/ownership.h index 8ac13a0ba7..ab4b30bed3 100644 --- a/libopenage/gamestate/component/internal/ownership.h +++ b/libopenage/gamestate/component/internal/ownership.h @@ -19,7 +19,7 @@ class EventLoop; namespace gamestate::component { -class Ownership : public InternalComponent { +class Ownership final : public InternalComponent { public: /** * Creates an Ownership component. diff --git a/libopenage/gamestate/component/internal/position.h b/libopenage/gamestate/component/internal/position.h index fc4808ed0f..ff4cab0da2 100644 --- a/libopenage/gamestate/component/internal/position.h +++ b/libopenage/gamestate/component/internal/position.h @@ -20,7 +20,7 @@ class EventLoop; namespace gamestate::component { -class Position : public InternalComponent { +class Position final : public InternalComponent { public: /** * Create a Position component. diff --git a/libopenage/gamestate/game_entity.cpp b/libopenage/gamestate/game_entity.cpp index a4e18a29e3..421c2847e7 100644 --- a/libopenage/gamestate/game_entity.cpp +++ b/libopenage/gamestate/game_entity.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "game_entity.h" @@ -30,7 +30,7 @@ entity_id_t GameEntity::get_id() const { return this->id; } -void GameEntity::set_render_entity(const std::shared_ptr &entity) { +void GameEntity::set_render_entity(const std::shared_ptr &entity) { // TODO: Transfer state from old render entity to new one? this->render_entity = entity; diff --git a/libopenage/gamestate/game_entity.h b/libopenage/gamestate/game_entity.h index 9b30e8ec76..923aed2eab 100644 --- a/libopenage/gamestate/game_entity.h +++ b/libopenage/gamestate/game_entity.h @@ -14,7 +14,7 @@ namespace openage { namespace renderer::world { -class WorldRenderEntity; +class RenderEntity; } namespace gamestate { @@ -62,7 +62,7 @@ class GameEntity { * * @param entity New render entity. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Set the event manager of this entity. @@ -142,7 +142,7 @@ class GameEntity { /** * Render entity for pushing updates to the renderer. Can be \p nullptr. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; /** * Event manager. diff --git a/libopenage/gamestate/terrain_chunk.cpp b/libopenage/gamestate/terrain_chunk.cpp index d863db9f67..3c624ff700 100644 --- a/libopenage/gamestate/terrain_chunk.cpp +++ b/libopenage/gamestate/terrain_chunk.cpp @@ -18,7 +18,7 @@ TerrainChunk::TerrainChunk(const util::Vector2s size, } } -void TerrainChunk::set_render_entity(const std::shared_ptr &entity) { +void TerrainChunk::set_render_entity(const std::shared_ptr &entity) { this->render_entity = entity; } diff --git a/libopenage/gamestate/terrain_chunk.h b/libopenage/gamestate/terrain_chunk.h index 574e63da4d..68d0a2e6f2 100644 --- a/libopenage/gamestate/terrain_chunk.h +++ b/libopenage/gamestate/terrain_chunk.h @@ -32,7 +32,7 @@ class TerrainChunk { * * @param entity New render entity. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Get the size of this terrain chunk. @@ -103,7 +103,7 @@ class TerrainChunk { /** * Render entity for pushing updates to the renderer. Can be \p nullptr. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; }; } // namespace openage::gamestate diff --git a/libopenage/input/controller/hud/controller.cpp b/libopenage/input/controller/hud/controller.cpp index b423ef79ae..af89923d1b 100644 --- a/libopenage/input/controller/hud/controller.cpp +++ b/libopenage/input/controller/hud/controller.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "controller.h" @@ -27,11 +27,11 @@ bool Controller::process(const event_arguments &ev_args, return true; } -void Controller::set_drag_entity(const std::shared_ptr &entity) { +void Controller::set_drag_entity(const std::shared_ptr &entity) { this->drag_entity = entity; } -const std::shared_ptr &Controller::get_drag_entity() const { +const std::shared_ptr &Controller::get_drag_entity() const { return this->drag_entity; } @@ -39,7 +39,7 @@ void setup_defaults(const std::shared_ptr &ctx, const std::shared_ptr &hud_renderer) { binding_func_t drag_selection_init{[&](const event_arguments &args, const std::shared_ptr controller) { - auto render_entity = std::make_shared(args.mouse); + auto render_entity = std::make_shared(args.mouse); hud_renderer->add_drag_entity(render_entity); controller->set_drag_entity(render_entity); }}; diff --git a/libopenage/input/controller/hud/controller.h b/libopenage/input/controller/hud/controller.h index 60e9ff2bb8..44d64ee833 100644 --- a/libopenage/input/controller/hud/controller.h +++ b/libopenage/input/controller/hud/controller.h @@ -10,7 +10,7 @@ namespace openage { namespace renderer::hud { -class HudDragRenderEntity; +class DragRenderEntity; class HudRenderStage; } // namespace renderer::hud @@ -42,20 +42,20 @@ class Controller : public std::enable_shared_from_this { * * @param entity New render entity. */ - void set_drag_entity(const std::shared_ptr &entity); + void set_drag_entity(const std::shared_ptr &entity); /** * Get the render entity for the selection box. * * @return Render entity for the selection box. */ - const std::shared_ptr &get_drag_entity() const; + const std::shared_ptr &get_drag_entity() const; private: /** * Render entity for the selection box. */ - std::shared_ptr drag_entity; + std::shared_ptr drag_entity; }; /** diff --git a/libopenage/log/level.h b/libopenage/log/level.h index ef7322ec63..0d35f163c5 100644 --- a/libopenage/log/level.h +++ b/libopenage/log/level.h @@ -1,4 +1,4 @@ -// Copyright 2015-2023 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. #pragma once @@ -50,11 +50,11 @@ struct OAAPI level : util::Enum { level(); #ifdef __MINGW32__ -// Do not try to optimize these out even if it seems they are not used. -// Namely MIN that is not used within the library. -#define NOOPTIMIZE __attribute__((__used__)) + // Do not try to optimize these out even if it seems they are not used. + // Namely MIN that is not used within the library. + #define NOOPTIMIZE __attribute__((__used__)) #else -#define NOOPTIMIZE + #define NOOPTIMIZE #endif // _win32 static constexpr level_value MIN NOOPTIMIZE{{"min loglevel", -1000}, "5"}; diff --git a/libopenage/log/message.h b/libopenage/log/message.h index d46ed18c7d..ca534e2efe 100644 --- a/libopenage/log/message.h +++ b/libopenage/log/message.h @@ -21,11 +21,11 @@ #if defined(__GNUC__) -#define OPENAGE_FUNC_NAME __PRETTY_FUNCTION__ + #define OPENAGE_FUNC_NAME __PRETTY_FUNCTION__ #elif defined(_MSC_VER) -#define OPENAGE_FUNC_NAME __FUNCSIG__ + #define OPENAGE_FUNC_NAME __FUNCSIG__ #else -#define OPENAGE_FUNC_NAME __FUNCTION__ + #define OPENAGE_FUNC_NAME __FUNCTION__ #endif namespace openage { diff --git a/libopenage/log/stdout_logsink.cpp b/libopenage/log/stdout_logsink.cpp index 647af51c49..99daecf5cd 100644 --- a/libopenage/log/stdout_logsink.cpp +++ b/libopenage/log/stdout_logsink.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2023 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. #include "stdout_logsink.h" @@ -7,8 +7,8 @@ #include #ifdef _MSC_VER -#define WIN32_LEAN_AND_MEAN -#include + #define WIN32_LEAN_AND_MEAN + #include #endif #include "log/level.h" diff --git a/libopenage/pathfinding/demo/demo_1.cpp b/libopenage/pathfinding/demo/demo_1.cpp index cd67e7b79f..1edeba52a1 100644 --- a/libopenage/pathfinding/demo/demo_1.cpp +++ b/libopenage/pathfinding/demo/demo_1.cpp @@ -92,6 +92,7 @@ void path_demo_1(const util::Path &path) { start, target, }; + grid->init_portal_nodes(); timer.start(); Path path_result = pathfinder->get_path(path_request); timer.stop(); diff --git a/libopenage/pathfinding/grid.cpp b/libopenage/pathfinding/grid.cpp index 62a2d3156f..45427b009d 100644 --- a/libopenage/pathfinding/grid.cpp +++ b/libopenage/pathfinding/grid.cpp @@ -98,4 +98,27 @@ void Grid::init_portals() { } } +const nodemap_t &Grid::get_portal_map() { + return portal_nodes; +} + +void Grid::init_portal_nodes() { + // create portal_nodes + for (auto §or : this->sectors) { + for (auto &portal : sector->get_portals()) { + if (!this->portal_nodes.contains(portal->get_id())) { + auto portal_node = std::make_shared(portal); + portal_node->node_sector_0 = sector->get_id(); + portal_node->node_sector_1 = portal_node->portal->get_exit_sector(sector->get_id()); + this->portal_nodes[portal->get_id()] = portal_node; + } + } + } + + // init portal_node exits + for (auto &[id, node] : this->portal_nodes) { + node->init_exits(this->portal_nodes); + } +} + } // namespace openage::path diff --git a/libopenage/pathfinding/grid.h b/libopenage/pathfinding/grid.h index 8f24743433..314d107a2e 100644 --- a/libopenage/pathfinding/grid.h +++ b/libopenage/pathfinding/grid.h @@ -7,6 +7,7 @@ #include #include +#include "pathfinding/pathfinder.h" #include "pathfinding/types.h" #include "util/vector.h" @@ -95,6 +96,16 @@ class Grid { */ void init_portals(); + /** + * returns map of portal ids to portal nodes + */ + const nodemap_t &get_portal_map(); + + /** + * Initialize the portal nodes of the grid with neigbouring nodes and distance costs. + */ + void init_portal_nodes(); + private: /** * ID of the grid. @@ -115,6 +126,13 @@ class Grid { * Sectors of the grid. */ std::vector> sectors; + + /** + * maps portal_ids to portal nodes, which store their neigbouring nodes and associated distance costs + * for pathfinding + */ + + nodemap_t portal_nodes; }; diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 288f7875df..e46919e62b 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -4,6 +4,7 @@ #include "coord/chunk.h" #include "coord/phys.h" +#include "error/error.h" #include "pathfinding/cost_field.h" #include "pathfinding/flow_field.h" #include "pathfinding/grid.h" @@ -201,6 +202,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req std::vector> result; auto grid = this->grids.at(request.grid_id); + auto &portal_map = grid->get_portal_map(); auto sector_size = grid->get_sector_size(); auto start_sector_x = request.start.ne / sector_size; @@ -210,8 +212,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req // path node storage, always provides cheapest next node. heap_t node_candidates; - // list of known portals and corresponding node. - nodemap_t visited_portals; + std::unordered_set visited_portals; // TODO: Compute cost to travel from one portal to another when creating portals // const int distance_cost = 1; @@ -223,7 +224,8 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req continue; } - auto portal_node = std::make_shared(portal, start_sector->get_id(), nullptr); + auto &portal_node = portal_map.at(portal->get_id()); + portal_node->entry_sector = start_sector->get_id(); auto sector_pos = grid->get_sector(portal->get_exit_sector(start_sector->get_id()))->get_position().to_tile(sector_size); auto portal_pos = portal->get_exit_center(start_sector->get_id()); @@ -235,7 +237,9 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req portal_node->future_cost = portal_node->current_cost + heuristic_cost; portal_node->heap_node = node_candidates.push(portal_node); - visited_portals[portal->get_id()] = portal_node; + portal_node->prev_portal = nullptr; + portal_node->was_best = false; + visited_portals.insert(portal->get_id()); } // track the closest we can get to the end position @@ -266,20 +270,21 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req } // get the exits of the current node - auto exits = current_node->get_exits(visited_portals, current_node->entry_sector); + const auto &exits = current_node->get_exits(current_node->entry_sector); // evaluate all neighbors of the current candidate for further progress - for (auto &exit : exits) { - if (exit->was_best) { + for (auto &[exit, distance_cost] : exits) { + exit->entry_sector = current_node->portal->get_exit_sector(current_node->entry_sector); + bool not_visited = !visited_portals.contains(exit->portal->get_id()); + + if (not_visited) { + exit->was_best = false; + } + else if (exit->was_best) { continue; } - // Get distance cost (from current node to exit node) - auto distance_cost = Pathfinder::distance_cost( - current_node->portal->get_exit_center(current_node->entry_sector), - exit->portal->get_entry_center(exit->entry_sector)); - bool not_visited = !visited_portals.contains(exit->portal->get_id()); auto tentative_cost = current_node->current_cost + distance_cost; if (not_visited or tentative_cost < exit->current_cost) { @@ -300,7 +305,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req if (not_visited) { exit->heap_node = node_candidates.push(exit); - visited_portals[exit->portal->get_id()] = exit; + visited_portals.insert(exit->portal->get_id()); } else { node_candidates.decrease(exit->heap_node); @@ -463,6 +468,16 @@ int Pathfinder::distance_cost(const coord::tile_delta &portal1_pos, } +PortalNode::PortalNode(const std::shared_ptr &portal) : + portal{portal}, + entry_sector{NULL}, + future_cost{std::numeric_limits::max()}, + current_cost{std::numeric_limits::max()}, + heuristic_cost{std::numeric_limits::max()}, + was_best{false}, + prev_portal{nullptr}, + heap_node{nullptr} {} + PortalNode::PortalNode(const std::shared_ptr &portal, sector_id_t entry_sector, const node_t &prev_portal) : @@ -511,27 +526,37 @@ std::vector PortalNode::generate_backtrace() { return waypoints; } -std::vector PortalNode::get_exits(const nodemap_t &nodes, - sector_id_t entry_sector) { - auto &exits = this->portal->get_exits(entry_sector); - std::vector exit_nodes; - exit_nodes.reserve(exits.size()); +void PortalNode::init_exits(const nodemap_t &node_map) { + auto exits = this->portal->get_exits(this->node_sector_0); + for (auto &exit : exits) { + int distance_cost = Pathfinder::distance_cost( + this->portal->get_exit_center(this->node_sector_0), + exit->get_entry_center(this->node_sector_1)); + + auto exit_node = node_map.at(exit->get_id()); + this->exits_1[exit_node] = distance_cost; + } - auto exit_sector = this->portal->get_exit_sector(entry_sector); + exits = this->portal->get_exits(this->node_sector_1); for (auto &exit : exits) { - auto exit_id = exit->get_id(); + int distance_cost = Pathfinder::distance_cost( + this->portal->get_exit_center(this->node_sector_1), + exit->get_entry_center(this->node_sector_0)); - auto exit_node = nodes.find(exit_id); - if (exit_node != nodes.end()) { - exit_nodes.push_back(exit_node->second); - } - else { - exit_nodes.push_back(std::make_shared(exit, - exit_sector, - this->shared_from_this())); - } + auto exit_node = node_map.at(exit->get_id()); + this->exits_0[exit_node] = distance_cost; + } +} + +const PortalNode::exits_t &PortalNode::get_exits(sector_id_t entry_sector) { + ENSURE(entry_sector == this->node_sector_0 || entry_sector == this->node_sector_1, "Invalid entry sector"); + + if (this->node_sector_0 == entry_sector) { + return exits_1; + } + else { + return exits_0; } - return exit_nodes; } diff --git a/libopenage/pathfinding/pathfinder.h b/libopenage/pathfinding/pathfinder.h index 1809b7524d..e2529987b7 100644 --- a/libopenage/pathfinding/pathfinder.h +++ b/libopenage/pathfinding/pathfinder.h @@ -2,9 +2,9 @@ #pragma once +#include #include #include -#include #include "coord/tile.h" #include "datastructure/pairing_heap.h" @@ -62,6 +62,17 @@ class Pathfinder { */ const Path get_path(const PathRequest &request); + + /** + * Calculate the distance cost between two portals. + * + * @param portal1_pos Center of the first portal (relative to sector origin). + * @param portal2_pos Center of the second portal (relative to sector origin). + * + * @return Distance cost between the portal centers. + */ + static int distance_cost(const coord::tile_delta &portal1_pos, const coord::tile_delta &portal2_pos); + private: using portal_star_t = std::pair>>; @@ -100,15 +111,6 @@ class Pathfinder { */ static int heuristic_cost(const coord::tile &portal_pos, const coord::tile &target_pos); - /** - * Calculate the distance cost between two portals. - * - * @param portal1_pos Center of the first portal (relative to sector origin). - * @param portal2_pos Center of the second portal (relative to sector origin). - * - * @return Distance cost between the portal centers. - */ - static int distance_cost(const coord::tile_delta &portal1_pos, const coord::tile_delta &portal2_pos); /** * Grids managed by this pathfinder. @@ -147,6 +149,7 @@ using nodemap_t = std::unordered_map; */ class PortalNode : public std::enable_shared_from_this { public: + PortalNode(const std::shared_ptr &portal); PortalNode(const std::shared_ptr &portal, sector_id_t entry_sector, const node_t &prev_portal); @@ -178,9 +181,25 @@ class PortalNode : public std::enable_shared_from_this { std::vector generate_backtrace(); /** - * Get all exits of a node. + * init PortalNode::exits. + */ + void init_exits(const nodemap_t &node_map); + + + /** + * maps node_t of a neigbhour portal to the distance cost to travel between the portals */ - std::vector get_exits(const nodemap_t &nodes, sector_id_t entry_sector); + using exits_t = std::map; + + + /** + * Get the exit portals reachable via the portal when entering from a specified sector. + * + * @param entry_sector Sector from which the portal is entered. + * + * @return Exit portals nodes reachable from the portal. + */ + const exits_t &get_exits(sector_id_t entry_sector); /** * The portal this node is associated to. @@ -226,6 +245,26 @@ class PortalNode : public std::enable_shared_from_this { * Priority queue node that contains this path node. */ heap_t::element_t heap_node; + + /** + * First sector connected by the portal. + */ + sector_id_t node_sector_0; + + /** + * Second sector connected by the portal. + */ + sector_id_t node_sector_1; + + /** + * Exits in sector 0 reachable from the portal. + */ + exits_t exits_0; + + /** + * Exits in sector 1 reachable from the portal. + */ + exits_t exits_1; }; diff --git a/libopenage/presenter/presenter.cpp b/libopenage/presenter/presenter.cpp index 33c798bcef..a37362be95 100644 --- a/libopenage/presenter/presenter.cpp +++ b/libopenage/presenter/presenter.cpp @@ -121,7 +121,17 @@ void Presenter::init_graphics(bool debug) { this->camera->resize(w, h); }); + // Camera manager this->camera_manager = std::make_shared(this->camera); + // TODO: Make boundaries dynamic based on map size. + this->camera_manager->set_camera_boundaries( + renderer::camera::CameraBoundaries{ + renderer::camera::X_BOUND_MIN, + renderer::camera::X_BOUND_MAX, + renderer::camera::Y_BOUND_MIN, + renderer::camera::Y_BOUND_MAX, + renderer::camera::Z_BOUND_MIN, + renderer::camera::Z_BOUND_MAX}); // Skybox this->skybox_renderer = std::make_shared( diff --git a/libopenage/renderer/camera/CMakeLists.txt b/libopenage/renderer/camera/CMakeLists.txt index aea6391e7a..71de5c7bd1 100644 --- a/libopenage/renderer/camera/CMakeLists.txt +++ b/libopenage/renderer/camera/CMakeLists.txt @@ -1,4 +1,5 @@ add_sources(libopenage + boundaries.cpp camera.cpp definitions.cpp frustum_2d.cpp diff --git a/libopenage/renderer/camera/boundaries.cpp b/libopenage/renderer/camera/boundaries.cpp new file mode 100644 index 0000000000..ca77344c7e --- /dev/null +++ b/libopenage/renderer/camera/boundaries.cpp @@ -0,0 +1,9 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#include "boundaries.h" + + +namespace openage::renderer::camera { + + +} // namespace openage::renderer::camera diff --git a/libopenage/renderer/camera/boundaries.h b/libopenage/renderer/camera/boundaries.h new file mode 100644 index 0000000000..484a398455 --- /dev/null +++ b/libopenage/renderer/camera/boundaries.h @@ -0,0 +1,25 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#pragma once + +namespace openage::renderer::camera { + +/** + * Defines boundaries for the camera's view. + */ +struct CameraBoundaries { + /// The minimum boundary for the camera's X-coordinate. + float x_min; + /// The maximum boundary for the camera's X-coordinate. + float x_max; + /// The minimum boundary for the camera's Y-coordinate. + float y_min; + /// The maximum boundary for the camera's Y-coordinate. + float y_max; + /// The minimum boundary for the camera's Z-coordinate. + float z_min; + /// The maximum boundary for the camera's Z-coordinate. + float z_max; +}; + +} // namespace openage::renderer::camera diff --git a/libopenage/renderer/camera/camera.cpp b/libopenage/renderer/camera/camera.cpp index f70efbee64..14504998e0 100644 --- a/libopenage/renderer/camera/camera.cpp +++ b/libopenage/renderer/camera/camera.cpp @@ -61,38 +61,7 @@ Camera::Camera(const std::shared_ptr &renderer, } void Camera::look_at_scene(Eigen::Vector3f scene_pos) { - if (scene_pos[1] > this->scene_pos[1]) { - // TODO: camera can't look at a position that's - // higher than it's own position - } - - // TODO: Although the below method should be faster, calculating and adding the direction - // vector from scene_pos to new_pos may be easier to understand - // i.e. new_pos = scene_pos + b/sin(30) * direction_vec - - // due to the fixed angle, the centered scene position - // and the new camera position form a right triangle. - // - // c - + new camera pos - // - |b - // center +------+ - // a - // - // we can calculate the new camera position via the offset a - // using the angle and length of side b. - auto y_delta = this->scene_pos[1] - scene_pos[1]; // b (vertical distance) - auto xz_distance = y_delta * std::numbers::sqrt3; // a (horizontal distance); a = b * (cos(30°) / sin(30°)) - - // get x and z offsets - // the camera is pointed diagonally to the negative x and z axis - // a is the length of the diagonal from camera.xz to scene_pos.xz - // so the x and z offest are sides of a square with the same diagonal - auto side_length = xz_distance / std::numbers::sqrt2; - auto new_pos = Eigen::Vector3f( - scene_pos[0] + side_length, - this->scene_pos[1], // height unchanged - scene_pos[2] + side_length); - + auto new_pos = calc_look_at(scene_pos); this->move_to(new_pos); } @@ -102,15 +71,17 @@ void Camera::look_at_coord(coord::scene3 coord_pos) { this->look_at_scene(scene_pos); } -void Camera::move_to(Eigen::Vector3f scene_pos) { - // TODO: Check and set bounds for where the camera can go and check them here +void Camera::move_to(Eigen::Vector3f scene_pos, const CameraBoundaries &camera_boundaries) { + scene_pos[0] = std::clamp(scene_pos[0], camera_boundaries.x_min, camera_boundaries.x_max); + scene_pos[1] = std::clamp(scene_pos[1], camera_boundaries.y_min, camera_boundaries.y_max); + scene_pos[2] = std::clamp(scene_pos[2], camera_boundaries.z_min, camera_boundaries.z_max); this->scene_pos = scene_pos; this->moved = true; } -void Camera::move_rel(Eigen::Vector3f direction, float delta) { - this->move_to(this->scene_pos + (direction * delta)); +void Camera::move_rel(Eigen::Vector3f direction, float delta, const CameraBoundaries &camera_boundaries) { + this->move_to(this->scene_pos + (direction * delta), camera_boundaries); } void Camera::set_zoom(float zoom) { @@ -292,8 +263,43 @@ void Camera::init_uniform_buffer(const std::shared_ptr &renderer) { this->uniform_buffer = renderer->add_uniform_buffer(ubo_info); } +Eigen::Vector3f Camera::calc_look_at(Eigen::Vector3f target) { + if (target[1] > this->scene_pos[1]) { + // TODO: camera can't look at a position that's + // higher than it's own position + } + + // TODO: Although the below method should be faster, calculating and adding the direction + // vector from scene_pos to new_pos may be easier to understand + // i.e. new_pos = scene_pos + b/sin(30) * direction_vec + + // due to the fixed angle, the centered scene position + // and the new camera position form a right triangle. + // + // c - + new camera pos + // - |b + // center +------+ + // a + // + // we can calculate the new camera position via the offset a + // using the angle and length of side b. + auto y_delta = this->scene_pos[1] - target[1]; // b (vertical distance) + auto xz_distance = y_delta * std::numbers::sqrt3; // a (horizontal distance); a = b * (cos(30°) / sin(30°)) + + // get x and z offsets + // the camera is pointed diagonally to the negative x and z axis + // a is the length of the diagonal from camera.xz to scene_pos.xz + // so the x and z offest are sides of a square with the same diagonal + auto side_length = xz_distance / std::numbers::sqrt2; + return Eigen::Vector3f( + target[0] + side_length, + this->scene_pos[1], // height unchanged + target[2] + side_length); +} + inline float Camera::get_real_zoom_factor() const { return 0.5f * this->default_zoom_ratio * this->zoom; } + } // namespace openage::renderer::camera diff --git a/libopenage/renderer/camera/camera.h b/libopenage/renderer/camera/camera.h index eccb5b40e6..5e5759c06e 100644 --- a/libopenage/renderer/camera/camera.h +++ b/libopenage/renderer/camera/camera.h @@ -4,7 +4,9 @@ #include #include +#include #include +#include #include @@ -12,6 +14,7 @@ #include "coord/scene.h" #include "util/vector.h" +#include "renderer/camera/boundaries.h" #include "renderer/camera/definitions.h" #include "renderer/camera/frustum_2d.h" #include "renderer/camera/frustum_3d.h" @@ -83,18 +86,21 @@ class Camera { * Move the camera position in the direction of a given vector. * * @param scene_pos New 3D position of the camera in the scene. + * @param camera_boundaries 3D boundaries for the camera. */ - void move_to(Eigen::Vector3f scene_pos); + void move_to(Eigen::Vector3f scene_pos, const CameraBoundaries &camera_boundaries = DEFAULT_CAM_BOUNDARIES); /** - * Move the camera position in the direction of a given vector. + * Move the camera position in the direction of a given vector taking the + * camera boundaries into account. * * @param direction Direction vector. Added to the current position. * @param delta Delta for controlling the amount by which the camera is moved. The * value is multiplied with the directional vector before its applied to * the positional vector. + * @param camera_boundaries 3D boundaries for the camera. */ - void move_rel(Eigen::Vector3f direction, float delta = 1.0f); + void move_rel(Eigen::Vector3f direction, float delta = 1.0f, const CameraBoundaries &camera_boundaries = DEFAULT_CAM_BOUNDARIES); /** * Set the zoom level of the camera. Values smaller than 1.0f let the @@ -200,6 +206,7 @@ class Camera { */ const Frustum3d get_frustum_3d() const; + private: /** * Create the uniform buffer for the camera. @@ -208,6 +215,13 @@ class Camera { */ void init_uniform_buffer(const std::shared_ptr &renderer); + /** + * Calculates the camera's position needed to center its view on the given target. + * + * @param target The target position in the 3D scene the camera should focus on. + */ + Eigen::Vector3f calc_look_at(Eigen::Vector3f target); + /** * Get the zoom factor applied to the camera projection. * diff --git a/libopenage/renderer/camera/definitions.h b/libopenage/renderer/camera/definitions.h index 9586239a2f..7c3bbdd5d3 100644 --- a/libopenage/renderer/camera/definitions.h +++ b/libopenage/renderer/camera/definitions.h @@ -3,7 +3,9 @@ #pragma once #include +#include +#include "renderer/camera/boundaries.h" namespace openage::renderer::camera { @@ -58,4 +60,23 @@ static constexpr float DEFAULT_MAX_ZOOM_OUT = 64.0f; */ static constexpr float DEFAULT_ZOOM_RATIO = 1.0f / 49; +static constexpr CameraBoundaries DEFAULT_CAM_BOUNDARIES{ + std::numeric_limits::lowest(), + std::numeric_limits::max(), + std::numeric_limits::lowest(), + std::numeric_limits::max(), + std::numeric_limits::lowest(), + std::numeric_limits::max()}; + +/** + * Constant values for the camera bounds (based on current fix terrain grid of 20x20). + * TODO: Make boundaries dynamic based on map size. + */ +static constexpr float X_BOUND_MIN = 12.25f; +static constexpr float X_BOUND_MAX = 32.25f; +static constexpr float Y_BOUND_MIN = 0.0f; +static constexpr float Y_BOUND_MAX = 20.0f; +static constexpr float Z_BOUND_MIN = -8.25f; +static constexpr float Z_BOUND_MAX = 12.25f; + } // namespace openage::renderer::camera diff --git a/libopenage/renderer/color.cpp b/libopenage/renderer/color.cpp index 7333dfb073..f03f883a0a 100644 --- a/libopenage/renderer/color.cpp +++ b/libopenage/renderer/color.cpp @@ -1,12 +1,11 @@ -// Copyright 2015-2019 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. #include "color.h" namespace openage { namespace renderer { -Color::Color() - : +Color::Color() : r{0}, g{0}, b{0}, @@ -14,8 +13,7 @@ Color::Color() // Empty } -Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) - : +Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r{r}, g{g}, b{b}, @@ -32,6 +30,7 @@ bool Color::operator!=(const Color &other) const { } Color Colors::WHITE = {255, 255, 255, 255}; -Color Colors::BLACK = { 0, 0, 0, 255}; +Color Colors::BLACK = {0, 0, 0, 255}; -}} // openage::renderer +} // namespace renderer +} // namespace openage diff --git a/libopenage/renderer/demo/demo_3.cpp b/libopenage/renderer/demo/demo_3.cpp index 408e699196..7de6372303 100644 --- a/libopenage/renderer/demo/demo_3.cpp +++ b/libopenage/renderer/demo/demo_3.cpp @@ -52,6 +52,18 @@ void renderer_demo_3(const util::Path &path) { // it is updated each frame before the render stages auto cam_manager = std::make_shared(camera); + // Set boundaries for camera movement in the scene + // this restricts camera movement to the area defined by the boundaries + // i.e. the map terrain in this case + cam_manager->set_camera_boundaries( + camera::CameraBoundaries{ + 12.25f, + 22.25f, + 0.0f, + 20.0f, + 2.25f, + 12.25f}); + // Render stages // every stage use a different subrenderer that manages renderables, // shaders, textures & more. @@ -123,7 +135,7 @@ void renderer_demo_3(const util::Path &path) { // Fill a 10x10 terrain grid with height values auto terrain_size = util::Vector2s{10, 10}; - std::vector> tiles{}; + std::vector> tiles{}; tiles.reserve(terrain_size[0] * terrain_size[1]); for (size_t i = 0; i < terrain_size[0] * terrain_size[1]; ++i) { tiles.emplace_back(0.0f, "./textures/test_terrain.terrain"); diff --git a/libopenage/renderer/demo/stresstest_0.cpp b/libopenage/renderer/demo/stresstest_0.cpp index 76c53d7b21..eae208c5d3 100644 --- a/libopenage/renderer/demo/stresstest_0.cpp +++ b/libopenage/renderer/demo/stresstest_0.cpp @@ -133,7 +133,7 @@ void renderer_stresstest_0(const util::Path &path) { // Fill a 10x10 terrain grid with height values auto terrain_size = util::Vector2s{10, 10}; - std::vector> tiles{}; + std::vector> tiles{}; tiles.reserve(terrain_size[0] * terrain_size[1]); for (size_t i = 0; i < terrain_size[0] * terrain_size[1]; ++i) { tiles.emplace_back(0.0f, "./textures/test_terrain.terrain"); @@ -147,7 +147,7 @@ void renderer_stresstest_0(const util::Path &path) { terrain0->update(terrain_size, tiles); // World entities - std::vector> render_entities{}; + std::vector> render_entities{}; auto add_world_entity = [&](const coord::phys3 initial_pos, const time::time_t time) { const auto animation_path = "./textures/test_tank_mirrored.sprite"; diff --git a/libopenage/renderer/demo/stresstest_1.cpp b/libopenage/renderer/demo/stresstest_1.cpp index ac6177f819..9d2d43968b 100644 --- a/libopenage/renderer/demo/stresstest_1.cpp +++ b/libopenage/renderer/demo/stresstest_1.cpp @@ -138,7 +138,7 @@ void renderer_stresstest_1(const util::Path &path) { // Fill a 10x10 terrain grid with height values auto terrain_size = util::Vector2s{10, 10}; - std::vector> tiles{}; + std::vector> tiles{}; tiles.reserve(terrain_size[0] * terrain_size[1]); for (size_t i = 0; i < terrain_size[0] * terrain_size[1]; ++i) { tiles.emplace_back(0.0f, "./textures/test_terrain.terrain"); @@ -151,7 +151,7 @@ void renderer_stresstest_1(const util::Path &path) { // send the terrain data to the terrain renderer terrain0->update(terrain_size, tiles); - std::vector> render_entities{}; + std::vector> render_entities{}; auto add_world_entity = [&](const coord::phys3 initial_pos, const time::time_t time) { const auto animation_path = "./textures/test_tank_mirrored.sprite"; diff --git a/libopenage/renderer/gui/guisys/private/opengl_debug_logger.cpp b/libopenage/renderer/gui/guisys/private/opengl_debug_logger.cpp index 61025528a6..0f565efaae 100644 --- a/libopenage/renderer/gui/guisys/private/opengl_debug_logger.cpp +++ b/libopenage/renderer/gui/guisys/private/opengl_debug_logger.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2023 the openage authors. See copying.md for legal info. +// Copyright 2017-2024 the openage authors. See copying.md for legal info. #include "opengl_debug_logger.h" @@ -7,11 +7,11 @@ #include #ifdef __APPLE__ -// from https://www.khronos.org/registry/OpenGL/api/GL/glext.h -#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 -#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 -#define GL_DEBUG_TYPE_ERROR 0x824C -#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E + // from https://www.khronos.org/registry/OpenGL/api/GL/glext.h + #define GL_DEBUG_CALLBACK_FUNCTION 0x8244 + #define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 + #define GL_DEBUG_TYPE_ERROR 0x824C + #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E #endif namespace qtgui { diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index 48639dc368..33b734790f 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -86,7 +86,7 @@ std::shared_ptr GlRenderer::get_display_target() { std::shared_ptr GlRenderer::add_uniform_buffer(resources::UniformBufferInfo const &info) { auto inputs = info.get_inputs(); - std::unordered_map uniforms{}; + std::vector uniforms{}; size_t offset = 0; for (auto const &input : inputs) { auto type = GL_UBO_INPUT_TYPE.get(input.type); @@ -94,14 +94,13 @@ std::shared_ptr GlRenderer::add_uniform_buffer(resources::Uniform // align offset to the size of the type offset += offset % size; - - uniforms.emplace( - std::make_pair(input.name, - GlInBlockUniform{type, - offset, - resources::UniformBufferInfo::get_size(input, info.get_layout()), - resources::UniformBufferInfo::get_stride_size(input.type, info.get_layout()), - input.count})); + uniforms.push_back( + GlInBlockUniform{type, + offset, + resources::UniformBufferInfo::get_size(input, info.get_layout()), + resources::UniformBufferInfo::get_stride_size(input.type, info.get_layout()), + input.count, + input.name}); offset += size; } diff --git a/libopenage/renderer/opengl/shader_data.h b/libopenage/renderer/opengl/shader_data.h index 7b987fb6a7..d60d16cb85 100644 --- a/libopenage/renderer/opengl/shader_data.h +++ b/libopenage/renderer/opengl/shader_data.h @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -61,6 +62,11 @@ struct GlInBlockUniform { * Only relevant for arrays. The number of elements in the array. */ size_t count; + + /** + * Name of the block uniform. + */ + std::string name; }; /** @@ -78,7 +84,7 @@ struct GlUniformBlock { /** * Maps uniform names within this block to their descriptions. */ - std::unordered_map uniforms; + std::vector uniforms; /** * The binding point assigned to this block. diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index da24ac1596..4b46a8263c 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -125,7 +125,7 @@ GlShaderProgram::GlShaderProgram(const std::shared_ptr &context, std::vector uniform_indices(val); glGetActiveUniformBlockiv(handle, i_unif_block, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, uniform_indices.data()); - std::unordered_map uniforms; + std::vector uniforms; for (GLuint const i_unif : uniform_indices) { in_block_unifs.insert(i_unif); @@ -152,14 +152,14 @@ GlShaderProgram::GlShaderProgram(const std::shared_ptr &context, // We do not need to handle sampler types here like in the uniform loop below, // because named blocks cannot contain samplers. - uniforms.insert(std::make_pair( - name.data(), + uniforms.push_back( GlInBlockUniform{ type, size_t(offset), size_t(count) * GL_UNIFORM_TYPE_SIZE.get(type), size_t(stride), - size_t(count)})); + size_t(count), + std::string(name.data())}); } // ENSURE(block_binding < caps.max_uniform_buffer_bindings, @@ -257,10 +257,10 @@ GlShaderProgram::GlShaderProgram(const std::shared_ptr &context, for (auto const &pair : this->uniform_blocks) { log::log(MSG(dbg) << "(" << pair.second.index << ") " << pair.first << " (size: " << pair.second.data_size << ") {"); - for (auto const &unif_pair : pair.second.uniforms) { - log::log(MSG(dbg) << "\t+" << unif_pair.second.offset - << " " << unif_pair.first << ": " - << GLSL_TYPE_NAME.get(unif_pair.second.type)); + for (auto const &unif : pair.second.uniforms) { + log::log(MSG(dbg) << "\t+" << unif.offset + << " " << unif.name << ": " + << GLSL_TYPE_NAME.get(unif.type)); } log::log(MSG(dbg) << "}"); } @@ -424,8 +424,11 @@ void GlShaderProgram::update_uniforms(std::shared_ptr const &uni glBindTexture(GL_TEXTURE_2D, tex); // TODO: maybe call this at a more appropriate position glUniform1i(loc, tex_unit_id); - auto &tex_value = *this->textures_per_texunits[tex_unit_id]; - tex_value = tex; + ENSURE(tex_unit_id < this->textures_per_texunits.size(), + "Tried to assign texture to non-existant texture unit at index " + << tex_unit_id + << " (max: " << this->textures_per_texunits.size() << ")."); + this->textures_per_texunits[tex_unit_id] = tex; break; } default: @@ -478,9 +481,9 @@ void GlShaderProgram::bind_uniform_buffer(const char *block_name, std::shared_pt auto &block = this->uniform_blocks[block_name]; // Check if the uniform buffer matches the block definition - for (auto const &pair : block.uniforms) { - ENSURE(gl_buffer->has_uniform(pair.first.c_str()), - "Uniform buffer does not contain uniform '" << pair.first << "' required by block " << block_name); + for (auto const &unif : block.uniforms) { + ENSURE(gl_buffer->has_uniform(unif.name.c_str()), + "Uniform buffer does not contain uniform '" << unif.name << "' required by block " << block_name); } block.binding_point = gl_buffer->get_binding_point(); diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index 5d31f2a071..e5c75374c3 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -195,6 +195,7 @@ class GlShaderProgram final : public ShaderProgram std::unordered_map attribs; /// Store which texture handles are currently bound to the shader's texture units. + /// A value of std::nullopt means the texture unit is unbound (no texture assigned). std::vector> textures_per_texunits; /// Whether this program has been validated. diff --git a/libopenage/renderer/opengl/uniform_buffer.cpp b/libopenage/renderer/opengl/uniform_buffer.cpp index 661f9e3173..cc3aa3d3dc 100644 --- a/libopenage/renderer/opengl/uniform_buffer.cpp +++ b/libopenage/renderer/opengl/uniform_buffer.cpp @@ -16,7 +16,7 @@ namespace openage::renderer::opengl { GlUniformBuffer::GlUniformBuffer(const std::shared_ptr &context, size_t size, - std::unordered_map uniforms, + std::vector uniforms, GLuint binding_point, GLenum usage) : GlSimpleObject(context, @@ -33,6 +33,12 @@ GlUniformBuffer::GlUniformBuffer(const std::shared_ptr &context, this->bind(); glBufferData(GL_UNIFORM_BUFFER, this->data_size, NULL, usage); + uniform_id_t unif_id = 0; + for (auto &uniform : uniforms) { + this->uniforms_by_name.insert(std::make_pair(uniform.name, unif_id)); + unif_id += 1; + } + glBindBufferRange(GL_UNIFORM_BUFFER, this->binding_point, *this->handle, 0, this->data_size); log::log(MSG(dbg) << "Created OpenGL uniform buffer (size: " @@ -51,23 +57,35 @@ void GlUniformBuffer::set_binding_point(GLuint binding_point) { void GlUniformBuffer::update_uniforms(std::shared_ptr const &unif_in) { auto glunif_in = std::dynamic_pointer_cast(unif_in); - ENSURE(glunif_in->get_buffer() == this->shared_from_this(), "Uniform input passed to different buffer than it was created with."); + ENSURE(glunif_in->get_buffer().get() == this, "Uniform input passed to different buffer than it was created with."); this->bind(); + const auto &update_offs = glunif_in->update_offs; + const auto &used_uniforms = glunif_in->used_uniforms; + const auto &uniforms = this->uniforms; uint8_t const *data = glunif_in->update_data.data(); - for (auto const &pair : glunif_in->update_offs) { - uint8_t const *ptr = data + pair.second; - auto unif_def = this->uniforms[pair.first]; - auto loc = unif_def.offset; - auto size = unif_def.size; + + size_t unif_count = used_uniforms.size(); + for (size_t i = 0; i < unif_count; ++i) { + uniform_id_t unif_id = used_uniforms[i]; + auto offset = update_offs[unif_id]; + + uint8_t const *ptr = data + offset.offset; + auto &unif = uniforms[unif_id]; + auto loc = unif.offset; + auto size = unif.size; glBufferSubData(GL_UNIFORM_BUFFER, loc, size, ptr); } } -bool GlUniformBuffer::has_uniform(const char *unif) { - return this->uniforms.count(unif) != 0; +const std::vector &GlUniformBuffer::get_uniforms() const { + return this->uniforms; +} + +bool GlUniformBuffer::has_uniform(const char *name) { + return this->uniforms_by_name.contains(name); } void GlUniformBuffer::bind() const { @@ -80,14 +98,14 @@ std::shared_ptr GlUniformBuffer::new_unif_in() { return in; } -void GlUniformBuffer::set_unif(std::shared_ptr const &in, const char *unif, void const *val, GLenum type) { - auto unif_in = std::dynamic_pointer_cast(in); +void GlUniformBuffer::set_unif(UniformBufferInput &in, const char *unif, void const *val, GLenum type) { + auto &unif_in = dynamic_cast(in); - auto uniform = this->uniforms.find(unif); - ENSURE(uniform != std::end(this->uniforms), - "Tried to set uniform " << unif << " that does not exist in the shader program."); + auto unif_id = this->uniforms_by_name.find(unif)->second; + ENSURE(unif_id < this->uniforms.size(), + "Tried to set uniform with invalid ID " << unif_id); - auto const &unif_data = uniform->second; + auto const &unif_data = this->uniforms[unif_id]; ENSURE(type == unif_data.type, "Tried to set uniform " << unif << " to a value of the wrong type."); @@ -96,83 +114,80 @@ void GlUniformBuffer::set_unif(std::shared_ptr const &in, co ENSURE(size == unif_data.size, "Tried to set uniform " << unif << " to a value of the wrong size."); - auto update_off = unif_in->update_offs.find(unif); - if (update_off != std::end(unif_in->update_offs)) [[likely]] { // always used after the uniform value is written once - // already wrote to this uniform since last upload - size_t off = update_off->second; - memcpy(unif_in->update_data.data() + off, val, size); - } - else { - // first time writing to this uniform since last upload, so - // extend the buffer before storing the uniform value - size_t prev_size = unif_in->update_data.size(); - unif_in->update_data.resize(prev_size + size); - memcpy(unif_in->update_data.data() + prev_size, val, size); - unif_in->update_offs.emplace(unif, prev_size); + auto &update_off = unif_in.update_offs[unif_id]; + auto offset = update_off.offset; + memcpy(unif_in.update_data.data() + offset, val, size); + if (not update_off.used) [[unlikely]] { // only true if the uniform value was not set before + auto lower_bound = std::lower_bound( + std::begin(unif_in.used_uniforms), + std::end(unif_in.used_uniforms), + unif_id); + unif_in.used_uniforms.insert(lower_bound, unif_id); + update_off.used = true; } } -void GlUniformBuffer::set_i32(std::shared_ptr const &in, const char *unif, int32_t val) { +void GlUniformBuffer::set_i32(UniformBufferInput &in, const char *unif, int32_t val) { this->set_unif(in, unif, &val, GL_INT); } -void GlUniformBuffer::set_u32(std::shared_ptr const &in, const char *unif, uint32_t val) { +void GlUniformBuffer::set_u32(UniformBufferInput &in, const char *unif, uint32_t val) { this->set_unif(in, unif, &val, GL_UNSIGNED_INT); } -void GlUniformBuffer::set_f32(std::shared_ptr const &in, const char *unif, float val) { +void GlUniformBuffer::set_f32(UniformBufferInput &in, const char *unif, float val) { this->set_unif(in, unif, &val, GL_FLOAT); } -void GlUniformBuffer::set_f64(std::shared_ptr const &in, const char *unif, double val) { +void GlUniformBuffer::set_f64(UniformBufferInput &in, const char *unif, double val) { this->set_unif(in, unif, &val, GL_DOUBLE); } -void GlUniformBuffer::set_bool(std::shared_ptr const &in, const char *unif, bool val) { +void GlUniformBuffer::set_bool(UniformBufferInput &in, const char *unif, bool val) { this->set_unif(in, unif, &val, GL_BOOL); } -void GlUniformBuffer::set_v2f32(std::shared_ptr const &in, const char *unif, Eigen::Vector2f const &val) { +void GlUniformBuffer::set_v2f32(UniformBufferInput &in, const char *unif, Eigen::Vector2f const &val) { this->set_unif(in, unif, val.data(), GL_FLOAT_VEC2); } -void GlUniformBuffer::set_v3f32(std::shared_ptr const &in, const char *unif, Eigen::Vector3f const &val) { +void GlUniformBuffer::set_v3f32(UniformBufferInput &in, const char *unif, Eigen::Vector3f const &val) { this->set_unif(in, unif, val.data(), GL_FLOAT_VEC3); } -void GlUniformBuffer::set_v4f32(std::shared_ptr const &in, const char *unif, Eigen::Vector4f const &val) { +void GlUniformBuffer::set_v4f32(UniformBufferInput &in, const char *unif, Eigen::Vector4f const &val) { this->set_unif(in, unif, val.data(), GL_FLOAT_VEC4); } -void GlUniformBuffer::set_v2i32(std::shared_ptr const &in, const char *unif, Eigen::Vector2i const &val) { +void GlUniformBuffer::set_v2i32(UniformBufferInput &in, const char *unif, Eigen::Vector2i const &val) { this->set_unif(in, unif, val.data(), GL_INT_VEC2); } -void GlUniformBuffer::set_v3i32(std::shared_ptr const &in, const char *unif, Eigen::Vector3i const &val) { +void GlUniformBuffer::set_v3i32(UniformBufferInput &in, const char *unif, Eigen::Vector3i const &val) { this->set_unif(in, unif, val.data(), GL_INT_VEC3); } -void GlUniformBuffer::set_v4i32(std::shared_ptr const &in, const char *unif, Eigen::Vector4i const &val) { +void GlUniformBuffer::set_v4i32(UniformBufferInput &in, const char *unif, Eigen::Vector4i const &val) { this->set_unif(in, unif, val.data(), GL_INT_VEC4); } -void GlUniformBuffer::set_v2ui32(std::shared_ptr const &in, const char *unif, Eigen::Vector2 const &val) { +void GlUniformBuffer::set_v2ui32(UniformBufferInput &in, const char *unif, Eigen::Vector2 const &val) { this->set_unif(in, unif, val.data(), GL_UNSIGNED_INT_VEC2); } -void GlUniformBuffer::set_v3ui32(std::shared_ptr const &in, const char *unif, Eigen::Vector3 const &val) { +void GlUniformBuffer::set_v3ui32(UniformBufferInput &in, const char *unif, Eigen::Vector3 const &val) { this->set_unif(in, unif, val.data(), GL_UNSIGNED_INT_VEC3); } -void GlUniformBuffer::set_v4ui32(std::shared_ptr const &in, const char *unif, Eigen::Vector4 const &val) { +void GlUniformBuffer::set_v4ui32(UniformBufferInput &in, const char *unif, Eigen::Vector4 const &val) { this->set_unif(in, unif, val.data(), GL_UNSIGNED_INT_VEC4); } -void GlUniformBuffer::set_m4f32(std::shared_ptr const &in, const char *unif, Eigen::Matrix4f const &val) { +void GlUniformBuffer::set_m4f32(UniformBufferInput &in, const char *unif, Eigen::Matrix4f const &val) { this->set_unif(in, unif, val.data(), GL_FLOAT_MAT4); } -void GlUniformBuffer::set_tex(std::shared_ptr const &in, const char *unif, std::shared_ptr const &val) { +void GlUniformBuffer::set_tex(UniformBufferInput &in, const char *unif, std::shared_ptr const &val) { auto tex = std::dynamic_pointer_cast(val); GLuint handle = tex->get_handle(); this->set_unif(in, unif, &handle, GL_SAMPLER_2D); diff --git a/libopenage/renderer/opengl/uniform_buffer.h b/libopenage/renderer/opengl/uniform_buffer.h index 6c7ca3d869..24fd1e23a2 100644 --- a/libopenage/renderer/opengl/uniform_buffer.h +++ b/libopenage/renderer/opengl/uniform_buffer.h @@ -22,7 +22,7 @@ class GlUniformBuffer final : public UniformBuffer public: GlUniformBuffer(const std::shared_ptr &context, size_t size, - std::unordered_map uniforms, + std::vector uniforms, GLuint binding_point = 0, GLenum usage = GL_DYNAMIC_DRAW); @@ -33,6 +33,13 @@ class GlUniformBuffer final : public UniformBuffer */ GLuint get_binding_point() const; + /** + * Get the uniform buffers uniforms. + * + * @return Uniforms in the shader program. + */ + const std::vector &get_uniforms() const; + /** * Set the binding point of the buffer. * @@ -51,22 +58,22 @@ class GlUniformBuffer final : public UniformBuffer protected: std::shared_ptr new_unif_in() override; - void set_i32(std::shared_ptr const &, const char *, int32_t) override; - void set_u32(std::shared_ptr const &, const char *, uint32_t) override; - void set_f32(std::shared_ptr const &, const char *, float) override; - void set_f64(std::shared_ptr const &, const char *, double) override; - void set_bool(std::shared_ptr const &, const char *, bool) override; - void set_v2f32(std::shared_ptr const &, const char *, Eigen::Vector2f const &) override; - void set_v3f32(std::shared_ptr const &, const char *, Eigen::Vector3f const &) override; - void set_v4f32(std::shared_ptr const &, const char *, Eigen::Vector4f const &) override; - void set_v2i32(std::shared_ptr const &, const char *, Eigen::Vector2i const &) override; - void set_v3i32(std::shared_ptr const &, const char *, Eigen::Vector3i const &) override; - void set_v4i32(std::shared_ptr const &, const char *, Eigen::Vector4i const &) override; - void set_v2ui32(std::shared_ptr const &, const char *, Eigen::Vector2 const &) override; - void set_v3ui32(std::shared_ptr const &, const char *, Eigen::Vector3 const &) override; - void set_v4ui32(std::shared_ptr const &, const char *, Eigen::Vector4 const &) override; - void set_m4f32(std::shared_ptr const &, const char *, Eigen::Matrix4f const &) override; - void set_tex(std::shared_ptr const &, const char *, std::shared_ptr const &) override; + void set_i32(UniformBufferInput &in, const char *, int32_t) override; + void set_u32(UniformBufferInput &in, const char *, uint32_t) override; + void set_f32(UniformBufferInput &in, const char *, float) override; + void set_f64(UniformBufferInput &in, const char *, double) override; + void set_bool(UniformBufferInput &in, const char *, bool) override; + void set_v2f32(UniformBufferInput &in, const char *, Eigen::Vector2f const &) override; + void set_v3f32(UniformBufferInput &in, const char *, Eigen::Vector3f const &) override; + void set_v4f32(UniformBufferInput &in, const char *, Eigen::Vector4f const &) override; + void set_v2i32(UniformBufferInput &in, const char *, Eigen::Vector2i const &) override; + void set_v3i32(UniformBufferInput &in, const char *, Eigen::Vector3i const &) override; + void set_v4i32(UniformBufferInput &in, const char *, Eigen::Vector4i const &) override; + void set_v2ui32(UniformBufferInput &in, const char *, Eigen::Vector2 const &) override; + void set_v3ui32(UniformBufferInput &in, const char *, Eigen::Vector3 const &) override; + void set_v4ui32(UniformBufferInput &in, const char *, Eigen::Vector4 const &) override; + void set_m4f32(UniformBufferInput &in, const char *, Eigen::Matrix4f const &) override; + void set_tex(UniformBufferInput &in, const char *, std::shared_ptr const &) override; private: /** @@ -80,7 +87,7 @@ class GlUniformBuffer final : public UniformBuffer * @param val Pointer to the value to update the uniform with. * @param type Type of the uniform. */ - void set_unif(std::shared_ptr const &in, + void set_unif(UniformBufferInput &in, const char *name, void const *val, GLenum type); @@ -88,7 +95,12 @@ class GlUniformBuffer final : public UniformBuffer /** * Uniform definitions inside the buffer. */ - std::unordered_map uniforms; + std::vector uniforms; + + /** + * Maps uniform names to their ID (the index in the uniform vector). + */ + std::unordered_map uniforms_by_name; /** * Size of the buffer (in bytes). diff --git a/libopenage/renderer/opengl/uniform_input.cpp b/libopenage/renderer/opengl/uniform_input.cpp index 111b33b879..38c63f2d66 100644 --- a/libopenage/renderer/opengl/uniform_input.cpp +++ b/libopenage/renderer/opengl/uniform_input.cpp @@ -4,6 +4,7 @@ #include "renderer/opengl/lookup.h" #include "renderer/opengl/shader_program.h" +#include "renderer/opengl/uniform_buffer.h" #include "renderer/opengl/util.h" @@ -28,7 +29,25 @@ GlUniformInput::GlUniformInput(const std::shared_ptr &prog) : this->update_data.resize(offset); } -GlUniformBufferInput::GlUniformBufferInput(std::shared_ptr const &buffer) : - UniformBufferInput{buffer} {} +GlUniformBufferInput::GlUniformBufferInput(const std::shared_ptr &buffer) : + UniformBufferInput{buffer} { + auto glBuf = std::dynamic_pointer_cast(buffer); + + auto uniforms = glBuf->get_uniforms(); + + // Reserve space for the used uniforms. + this->used_uniforms.reserve(uniforms.size()); + + // Calculate the byte-wise offsets of all uniforms. + size_t offset = 0; + this->update_offs.reserve(uniforms.size()); + for (auto &uniform : uniforms) { + this->update_offs.push_back({uniform.offset, false}); + offset += GL_UNIFORM_TYPE_SIZE.get(uniform.type); + } + + // Resize the update data buffer to the total size of all uniforms. + this->update_data.resize(offset); +} } // namespace openage::renderer::opengl diff --git a/libopenage/renderer/opengl/uniform_input.h b/libopenage/renderer/opengl/uniform_input.h index 8ca3ff02dd..961e12a2a5 100644 --- a/libopenage/renderer/opengl/uniform_input.h +++ b/libopenage/renderer/opengl/uniform_input.h @@ -18,6 +18,7 @@ class UniformBuffer; namespace opengl { class GlShaderProgram; +class GlUniformBuffer; /** * Describes OpenGL-specific uniform valuations. @@ -57,18 +58,28 @@ class GlUniformInput final : public UniformInput { * Describes OpenGL-specific uniform buffer valuations. */ class GlUniformBufferInput final : public UniformBufferInput { +private: + struct GlUniformOffset { + // Offset in the update_data buffer. + size_t offset; + /// Dtermine whether the uniform value has been set. + bool used; + }; + + public: - GlUniformBufferInput(std::shared_ptr const &); + GlUniformBufferInput(const std::shared_ptr &buffer); /** - * We store uniform updates lazily. They are only actually uploaded to GPU - * when a draw call is made. - * - * \p update_offs maps the uniform names to where their - * value is in \p update_data in terms of a byte-wise offset. This is only a partial - * valuation, so not all uniforms have to be present here. + * Store the IDs of the uniforms from the shader set by this uniform input. */ - std::unordered_map update_offs; + std::vector used_uniforms; + + /** + * Store offsets of uniforms in the update_data buffer and + * whether the uniform value has been set. + */ + std::vector update_offs; /** * Buffer containing untyped uniform update data. diff --git a/libopenage/renderer/render_factory.cpp b/libopenage/renderer/render_factory.cpp index 6d5e276479..3dee6dd6ae 100644 --- a/libopenage/renderer/render_factory.cpp +++ b/libopenage/renderer/render_factory.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "render_factory.h" @@ -15,16 +15,16 @@ RenderFactory::RenderFactory(const std::shared_ptr world_renderer{world_renderer} { } -std::shared_ptr RenderFactory::add_terrain_render_entity(const util::Vector2s chunk_size, - const coord::tile_delta chunk_offset) { - auto entity = std::make_shared(); +std::shared_ptr RenderFactory::add_terrain_render_entity(const util::Vector2s chunk_size, + const coord::tile_delta chunk_offset) { + auto entity = std::make_shared(); this->terrain_renderer->add_render_entity(entity, chunk_size, chunk_offset.to_phys2().to_scene2()); return entity; } -std::shared_ptr RenderFactory::add_world_render_entity() { - auto entity = std::make_shared(); +std::shared_ptr RenderFactory::add_world_render_entity() { + auto entity = std::make_shared(); this->world_renderer->add_render_entity(entity); return entity; diff --git a/libopenage/renderer/render_factory.h b/libopenage/renderer/render_factory.h index 26601bcf7f..a211e3edf4 100644 --- a/libopenage/renderer/render_factory.h +++ b/libopenage/renderer/render_factory.h @@ -11,12 +11,12 @@ namespace openage::renderer { namespace terrain { class TerrainRenderStage; -class TerrainRenderEntity; +class RenderEntity; } // namespace terrain namespace world { class WorldRenderStage; -class WorldRenderEntity; +class RenderEntity; } // namespace world /** @@ -47,15 +47,15 @@ class RenderFactory { * * @return Render entity for pushing terrain updates. */ - std::shared_ptr add_terrain_render_entity(const util::Vector2s chunk_size, - const coord::tile_delta chunk_offset); + std::shared_ptr add_terrain_render_entity(const util::Vector2s chunk_size, + const coord::tile_delta chunk_offset); /** * Create a new world render entity and register it at the world renderer. * * @return Render entity for pushing terrain updates. */ - std::shared_ptr add_world_render_entity(); + std::shared_ptr add_world_render_entity(); private: /** diff --git a/libopenage/renderer/stages/CMakeLists.txt b/libopenage/renderer/stages/CMakeLists.txt index c2ae5bf781..77d6faa091 100644 --- a/libopenage/renderer/stages/CMakeLists.txt +++ b/libopenage/renderer/stages/CMakeLists.txt @@ -1,3 +1,7 @@ +add_sources(libopenage + render_entity.cpp +) + add_subdirectory(camera/) add_subdirectory(hud/) add_subdirectory(screen/) diff --git a/libopenage/renderer/stages/camera/manager.cpp b/libopenage/renderer/stages/camera/manager.cpp index 39eddd2b52..d3cbd3f3b2 100644 --- a/libopenage/renderer/stages/camera/manager.cpp +++ b/libopenage/renderer/stages/camera/manager.cpp @@ -3,19 +3,21 @@ #include "manager.h" #include +#include -#include "renderer/camera/camera.h" #include "renderer/uniform_buffer.h" #include "renderer/uniform_input.h" namespace openage::renderer::camera { -CameraManager::CameraManager(const std::shared_ptr &camera) : +CameraManager::CameraManager(const std::shared_ptr &camera, + const CameraBoundaries &camera_boundaries) : camera{camera}, move_motion_directions{static_cast(MoveDirection::NONE)}, zoom_motion_direction{static_cast(ZoomDirection::NONE)}, move_motion_speed{0.2f}, - zoom_motion_speed{0.05f} { + zoom_motion_speed{0.05f}, + camera_boundaries{camera_boundaries} { this->uniforms = this->camera->get_uniform_buffer()->new_uniform_input( "view", camera->get_view_matrix(), @@ -33,18 +35,18 @@ void CameraManager::move_frame(MoveDirection direction, float speed) { case MoveDirection::LEFT: // half the speed because the relationship between forward/back and // left/right is 1:2 in our ortho projection. - this->camera->move_rel(Eigen::Vector3f(-1.0f, 0.0f, 1.0f), speed / 2); + this->camera->move_rel(Eigen::Vector3f(-1.0f, 0.0f, 1.0f), speed / 2, this->camera_boundaries); break; case MoveDirection::RIGHT: // half the speed because the relationship between forward/back and // left/right is 1:2 in our ortho projection. - this->camera->move_rel(Eigen::Vector3f(1.0f, 0.0f, -1.0f), speed / 2); + this->camera->move_rel(Eigen::Vector3f(1.0f, 0.0f, -1.0f), speed / 2, this->camera_boundaries); break; case MoveDirection::FORWARD: - this->camera->move_rel(Eigen::Vector3f(-1.0f, 0.0f, -1.0f), speed); + this->camera->move_rel(Eigen::Vector3f(-1.0f, 0.0f, -1.0f), speed, this->camera_boundaries); break; case MoveDirection::BACKWARD: - this->camera->move_rel(Eigen::Vector3f(1.0f, 0.0f, 1.0f), speed); + this->camera->move_rel(Eigen::Vector3f(1.0f, 0.0f, 1.0f), speed, this->camera_boundaries); break; default: @@ -66,6 +68,10 @@ void CameraManager::zoom_frame(ZoomDirection direction, float speed) { } } +void CameraManager::set_camera_boundaries(const CameraBoundaries &camera_boundaries) { + this->camera_boundaries = camera_boundaries; +} + void CameraManager::update_motion() { if (this->move_motion_directions != static_cast(MoveDirection::NONE)) { Eigen::Vector3f move_dir{0.0f, 0.0f, 0.0f}; @@ -83,7 +89,7 @@ void CameraManager::update_motion() { move_dir += Eigen::Vector3f(1.0f, 0.0f, 1.0f); } - this->camera->move_rel(move_dir, this->move_motion_speed); + this->camera->move_rel(move_dir, this->move_motion_speed, this->camera_boundaries); } if (this->zoom_motion_direction != static_cast(ZoomDirection::NONE)) { diff --git a/libopenage/renderer/stages/camera/manager.h b/libopenage/renderer/stages/camera/manager.h index 13d876b25f..36d605e959 100644 --- a/libopenage/renderer/stages/camera/manager.h +++ b/libopenage/renderer/stages/camera/manager.h @@ -3,7 +3,9 @@ #pragma once #include +#include +#include "renderer/camera/camera.h" namespace openage::renderer { class UniformBufferInput; @@ -47,8 +49,10 @@ class CameraManager { * Create a new camera manager. * * @param camera Camera to manage. + * @param camera_boundaries Boundaries for the camera movement in the scene. */ - CameraManager(const std::shared_ptr &camera); + CameraManager(const std::shared_ptr &camera, + const CameraBoundaries &camera_boundaries = DEFAULT_CAM_BOUNDARIES); ~CameraManager() = default; /** @@ -103,6 +107,13 @@ class CameraManager { */ void set_zoom_motion_speed(float speed); + /** + * Set boundaries for camera movement in the scene. + * + * @param camera_boundaries XYZ boundaries for the camera movement. + */ + void set_camera_boundaries(const CameraBoundaries &camera_boundaries); + private: /** * Update the camera parameters. @@ -143,6 +154,11 @@ class CameraManager { * Uniform buffer input for the camera. */ std::shared_ptr uniforms; + + /** + * Camera boundaries for X and Z movement. Contains minimum and maximum values for each axes. + */ + CameraBoundaries camera_boundaries; }; } // namespace camera diff --git a/libopenage/renderer/stages/hud/object.cpp b/libopenage/renderer/stages/hud/object.cpp index 2b53bd1b3b..6e23b6663a 100644 --- a/libopenage/renderer/stages/hud/object.cpp +++ b/libopenage/renderer/stages/hud/object.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "object.h" @@ -21,7 +21,7 @@ HudDragObject::HudDragObject(const std::shared_ptr &entity) { +void HudDragObject::set_render_entity(const std::shared_ptr &entity) { this->render_entity = entity; this->fetch_updates(); } @@ -38,8 +38,15 @@ void HudDragObject::fetch_updates(const time::time_t &time) { // Get data from render entity this->drag_start = this->render_entity->get_drag_start(); + + // Thread-safe access to curves needs a lock on the render entity's mutex + auto read_lock = this->render_entity->get_read_lock(); + this->drag_pos.sync(this->render_entity->get_drag_pos() /* , this->last_update */); + // Unlock the render entity mutex + read_lock.unlock(); + // Set self to changed so that world renderer can update the renderable this->changed = true; this->render_entity->clear_changed_flag(); diff --git a/libopenage/renderer/stages/hud/object.h b/libopenage/renderer/stages/hud/object.h index fac062f18d..d81e51ed8d 100644 --- a/libopenage/renderer/stages/hud/object.h +++ b/libopenage/renderer/stages/hud/object.h @@ -26,7 +26,7 @@ class Animation2dInfo; } // namespace resources namespace hud { -class HudDragRenderEntity; +class DragRenderEntity; /** * Stores the state of a renderable object in the HUD render stage. @@ -46,7 +46,7 @@ class HudDragObject { * * @param entity New world render entity. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Set the current camera of the scene. @@ -147,7 +147,7 @@ class HudDragObject { /** * Source for positional and texture data. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; /** * Position of the dragged corner. diff --git a/libopenage/renderer/stages/hud/render_entity.cpp b/libopenage/renderer/stages/hud/render_entity.cpp index f8cf19e692..2ff3f75649 100644 --- a/libopenage/renderer/stages/hud/render_entity.cpp +++ b/libopenage/renderer/stages/hud/render_entity.cpp @@ -1,4 +1,4 @@ -// Copyright 2023-2023 the openage authors. See copying.md for legal info. +// Copyright 2023-2024 the openage authors. See copying.md for legal info. #include "render_entity.h" @@ -7,15 +7,14 @@ namespace openage::renderer::hud { -HudDragRenderEntity::HudDragRenderEntity(const coord::input drag_start) : - changed{false}, - last_update{0.0}, +DragRenderEntity::DragRenderEntity(const coord::input drag_start) : + renderer::RenderEntity{}, drag_pos{nullptr, 0, "", nullptr, drag_start}, drag_start{drag_start} { } -void HudDragRenderEntity::update(const coord::input drag_pos, - const time::time_t time) { +void DragRenderEntity::update(const coord::input drag_pos, + const time::time_t time) { std::unique_lock lock{this->mutex}; this->drag_pos.set_insert(time, drag_pos); @@ -24,30 +23,16 @@ void HudDragRenderEntity::update(const coord::input drag_pos, this->changed = true; } -time::time_t HudDragRenderEntity::get_update_time() { +const curve::Continuous &DragRenderEntity::get_drag_pos() { std::shared_lock lock{this->mutex}; - return this->last_update; -} - -const curve::Continuous &HudDragRenderEntity::get_drag_pos() { return this->drag_pos; } -const coord::input &HudDragRenderEntity::get_drag_start() { - return this->drag_start; -} - -bool HudDragRenderEntity::is_changed() { +const coord::input DragRenderEntity::get_drag_start() { std::shared_lock lock{this->mutex}; - return this->changed; -} - -void HudDragRenderEntity::clear_changed_flag() { - std::unique_lock lock{this->mutex}; - - this->changed = false; + return this->drag_start; } } // namespace openage::renderer::hud diff --git a/libopenage/renderer/stages/hud/render_entity.h b/libopenage/renderer/stages/hud/render_entity.h index e2e2da65fa..9d15204386 100644 --- a/libopenage/renderer/stages/hud/render_entity.h +++ b/libopenage/renderer/stages/hud/render_entity.h @@ -3,12 +3,10 @@ #pragma once #include -#include -#include #include "coord/pixel.h" #include "curve/continuous.h" -#include "time/time.h" +#include "renderer/stages/render_entity.h" namespace openage::renderer::hud { @@ -16,36 +14,34 @@ namespace openage::renderer::hud { /** * Render entity for pushing drag selection updates to the HUD renderer. */ -class HudDragRenderEntity { +class DragRenderEntity final : public renderer::RenderEntity { public: /** * Create a new render entity for drag selection in the HUD. * * @param drag_start Position of the start corner. */ - HudDragRenderEntity(const coord::input drag_start); - ~HudDragRenderEntity() = default; + DragRenderEntity(const coord::input drag_start); + ~DragRenderEntity() = default; /** * Update the render entity with information from the gamestate * or input system. * + * Updating the render entity with this method is thread-safe. + * * @param drag_pos Position of the dragged corner. * @param time Current simulation time. */ void update(const coord::input drag_pos, const time::time_t time = 0.0); - /** - * Get the time of the last update. - * - * @return Time of last update. - */ - time::time_t get_update_time(); - /** * Get the position of the dragged corner. * + * Accessing the drag position curve REQUIRES a read lock on the render entity + * (using \p get_read_lock()) to ensure thread safety. + * * @return Coordinates of the dragged corner. */ const curve::Continuous &get_drag_pos(); @@ -53,35 +49,13 @@ class HudDragRenderEntity { /** * Get the position of the start corner. * - * @return Coordinates of the start corner. - */ - const coord::input &get_drag_start(); - - /** - * Check whether the render entity has received new updates. + * Accessing the drag start is thread-safe. * - * @return true if updates have been received, else false. - */ - bool is_changed(); - - /** - * Clear the update flag by setting it to false. + * @return Coordinates of the start corner. */ - void clear_changed_flag(); + const coord::input get_drag_start(); private: - /** - * Flag for determining if the render entity has been updated by the - * corresponding gamestate entity. Set to true every time \p update() - * is called. - */ - bool changed; - - /** - * Time of the last update call. - */ - time::time_t last_update; - /** * Position of the dragged corner. */ @@ -91,10 +65,5 @@ class HudDragRenderEntity { * Position of the start corner. */ coord::input drag_start; - - /** - * Mutex for protecting threaded access. - */ - std::shared_mutex mutex; }; } // namespace openage::renderer::hud diff --git a/libopenage/renderer/stages/hud/render_stage.cpp b/libopenage/renderer/stages/hud/render_stage.cpp index a55751ec5f..d5d7c5b83f 100644 --- a/libopenage/renderer/stages/hud/render_stage.cpp +++ b/libopenage/renderer/stages/hud/render_stage.cpp @@ -45,7 +45,7 @@ std::shared_ptr HudRenderStage::get_render_pass() { return this->render_pass; } -void HudRenderStage::add_drag_entity(const std::shared_ptr entity) { +void HudRenderStage::add_drag_entity(const std::shared_ptr entity) { std::unique_lock lock{this->mutex}; auto hud_object = std::make_shared(this->asset_manager); diff --git a/libopenage/renderer/stages/hud/render_stage.h b/libopenage/renderer/stages/hud/render_stage.h index f63662aa40..047aac6349 100644 --- a/libopenage/renderer/stages/hud/render_stage.h +++ b/libopenage/renderer/stages/hud/render_stage.h @@ -31,7 +31,7 @@ class AssetManager; namespace hud { class HudDragObject; -class HudDragRenderEntity; +class DragRenderEntity; /** * Renderer for the "Heads-Up Display" (HUD). @@ -71,7 +71,7 @@ class HudRenderStage { * * @param render_entity New render entity. */ - void add_drag_entity(const std::shared_ptr entity); + void add_drag_entity(const std::shared_ptr entity); /** * Remove the render object for drag selection. diff --git a/libopenage/renderer/stages/render_entity.cpp b/libopenage/renderer/stages/render_entity.cpp new file mode 100644 index 0000000000..dc95de9a6d --- /dev/null +++ b/libopenage/renderer/stages/render_entity.cpp @@ -0,0 +1,37 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#include "render_entity.h" + +#include + + +namespace openage::renderer { + +RenderEntity::RenderEntity() : + changed{false}, + last_update{time::time_t::zero()} { +} + +time::time_t RenderEntity::get_update_time() { + std::shared_lock lock{this->mutex}; + + return this->last_update; +} + +bool RenderEntity::is_changed() { + std::shared_lock lock{this->mutex}; + + return this->changed; +} + +void RenderEntity::clear_changed_flag() { + std::unique_lock lock{this->mutex}; + + this->changed = false; +} + +std::shared_lock RenderEntity::get_read_lock() { + return std::shared_lock{this->mutex}; +} + +} // namespace openage::renderer diff --git a/libopenage/renderer/stages/render_entity.h b/libopenage/renderer/stages/render_entity.h new file mode 100644 index 0000000000..f441452968 --- /dev/null +++ b/libopenage/renderer/stages/render_entity.h @@ -0,0 +1,83 @@ +// Copyright 2024-2024 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include + +#include "time/time.h" + + +namespace openage::renderer { + +/** + * Interface for render entities that allow pushing updates from game simulation + * to renderer. + */ +class RenderEntity { +public: + ~RenderEntity() = default; + + /** + * Get the time of the last update. + * + * Accessing the update time is thread-safe. + * + * @return Time of last update. + */ + time::time_t get_update_time(); + + /** + * Check whether the render entity has received new updates from the + * gamestate. + * + * @return true if updates have been received, else false. + */ + bool is_changed(); + + /** + * Clear the update flag by setting it to false. + */ + void clear_changed_flag(); + + /** + * Get a shared lock for thread-safe reading from the render entity. + * + * The caller is responsible for unlocking the mutex after reading. + * + * @return Lock for the render entity. + */ + std::shared_lock get_read_lock(); + +protected: + /** + * Create a new render entity. + * + * Members are initialized to these values by default: + * - \p changed: false + * - \p last_update: time::time_t::zero() + * + * Declared as protected to prevent direct instantiation of this class. + */ + RenderEntity(); + + /** + * Flag for determining if the render entity has been updated by the + * corresponding gamestate entity. Set to true every time \p update() + * is called. + */ + bool changed; + + /** + * Time of the last update call. + */ + time::time_t last_update; + + /** + * Mutex for protecting threaded access. + */ + std::shared_mutex mutex; +}; +} // namespace openage::renderer diff --git a/libopenage/renderer/stages/terrain/chunk.cpp b/libopenage/renderer/stages/terrain/chunk.cpp index 12b207bdc9..58a95d2a95 100644 --- a/libopenage/renderer/stages/terrain/chunk.cpp +++ b/libopenage/renderer/stages/terrain/chunk.cpp @@ -17,7 +17,7 @@ TerrainChunk::TerrainChunk(const std::shared_ptr &entity) { +void TerrainChunk::set_render_entity(const std::shared_ptr &entity) { this->render_entity = entity; } @@ -31,11 +31,19 @@ void TerrainChunk::fetch_updates(const time::time_t & /* time */) { if (not this->render_entity->is_changed()) { return; } + + // Get the terrain data from the render entity + auto terrain_size = this->render_entity->get_size(); + auto terrain_paths = this->render_entity->get_terrain_paths(); + auto tiles = this->render_entity->get_tiles(); + auto heightmap_verts = this->render_entity->get_vertices(); + + // Recreate the mesh data // TODO: Change mesh instead of recreating it // TODO: Multiple meshes this->meshes.clear(); - for (const auto &terrain_path : this->render_entity->get_terrain_paths()) { - auto new_mesh = this->create_mesh(terrain_path); + for (const auto &terrain_path : terrain_paths) { + auto new_mesh = this->create_mesh(terrain_size, tiles, heightmap_verts, terrain_path); new_mesh->create_model_matrix(this->offset); this->meshes.push_back(new_mesh); } @@ -59,13 +67,12 @@ const std::vector> &TerrainChunk::get_meshes( return this->meshes; } -std::shared_ptr TerrainChunk::create_mesh(const std::string &texture_path) { - auto size = this->render_entity->get_size(); - auto v_width = size[0]; - auto v_height = size[1]; - - auto tiles = this->render_entity->get_tiles(); - auto heightmap_verts = this->render_entity->get_vertices(); +std::shared_ptr TerrainChunk::create_mesh(const util::Vector2s vert_size, + const RenderEntity::tiles_t &tiles, + const std::vector &heightmap_verts, + const std::string &texture_path) { + auto v_width = vert_size[0]; + auto v_height = vert_size[1]; // vertex data for the mesh std::vector mesh_verts{}; diff --git a/libopenage/renderer/stages/terrain/chunk.h b/libopenage/renderer/stages/terrain/chunk.h index b64133e5b6..7d3d9fcc70 100644 --- a/libopenage/renderer/stages/terrain/chunk.h +++ b/libopenage/renderer/stages/terrain/chunk.h @@ -7,6 +7,7 @@ #include #include "coord/scene.h" +#include "renderer/stages/terrain/render_entity.h" #include "time/time.h" #include "util/vector.h" @@ -19,7 +20,6 @@ class AssetManager; namespace terrain { class TerrainRenderMesh; -class TerrainRenderEntity; /** * Stores the state of a terrain chunk in the terrain render stage. @@ -44,7 +44,7 @@ class TerrainChunk { * @param size Size of the chunk in tiles. * @param offset Offset of the chunk from origin in tiles. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Fetch updates from the render entity. @@ -85,9 +85,17 @@ class TerrainChunk { /** * Create a terrain mesh from the data provided by the render entity. * + * @param vert_size Size of the terrain in vertices. + * @param tiles Data for each tile (elevation, terrain path). + * @param heightmap_verts Position of each vertex in the chunk. + * @param texture_path Path to the texture for the terrain. + * * @return New terrain mesh. */ - std::shared_ptr create_mesh(const std::string &texture_path); + std::shared_ptr create_mesh(const util::Vector2s vert_size, + const RenderEntity::tiles_t &tiles, + const std::vector &heightmap_verts, + const std::string &texture_path); /** * Size of the chunk in tiles (width x height). @@ -114,7 +122,7 @@ class TerrainChunk { * Source for ingame terrain coordinates. These coordinates are translated into * our render vertex mesh when \p update() is called. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; }; diff --git a/libopenage/renderer/stages/terrain/model.cpp b/libopenage/renderer/stages/terrain/model.cpp index 8d8cc4c727..30e235c1bd 100644 --- a/libopenage/renderer/stages/terrain/model.cpp +++ b/libopenage/renderer/stages/terrain/model.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "model.h" @@ -26,7 +26,7 @@ TerrainRenderModel::TerrainRenderModel(const std::shared_ptr &entity, +void TerrainRenderModel::add_chunk(const std::shared_ptr &entity, const util::Vector2s size, const coord::scene2_delta offset) { auto chunk = std::make_shared(this->asset_manager, size, offset); diff --git a/libopenage/renderer/stages/terrain/model.h b/libopenage/renderer/stages/terrain/model.h index c06ea351c0..951599855d 100644 --- a/libopenage/renderer/stages/terrain/model.h +++ b/libopenage/renderer/stages/terrain/model.h @@ -21,7 +21,7 @@ class AssetManager; } namespace terrain { -class TerrainRenderEntity; +class RenderEntity; class TerrainRenderMesh; class TerrainChunk; @@ -46,7 +46,7 @@ class TerrainRenderModel { * @param chunk_size Size of the chunk in tiles. * @param chunk_offset Offset of the chunk from origin in tiles. */ - void add_chunk(const std::shared_ptr &entity, + void add_chunk(const std::shared_ptr &entity, const util::Vector2s chunk_size, const coord::scene2_delta chunk_offset); diff --git a/libopenage/renderer/stages/terrain/render_entity.cpp b/libopenage/renderer/stages/terrain/render_entity.cpp index 7f2e6d825e..2316f5dab7 100644 --- a/libopenage/renderer/stages/terrain/render_entity.cpp +++ b/libopenage/renderer/stages/terrain/render_entity.cpp @@ -9,22 +9,19 @@ namespace openage::renderer::terrain { -TerrainRenderEntity::TerrainRenderEntity() : - changed{false}, +RenderEntity::RenderEntity() : + renderer::RenderEntity{}, size{0, 0}, tiles{}, - last_update{0.0}, terrain_paths{}, - vertices{} -// terrain_path{nullptr, 0}, -{ + vertices{} { } -void TerrainRenderEntity::update_tile(const util::Vector2s size, - const coord::tile &pos, - const terrain_elevation_t elevation, - const std::string terrain_path, - const time::time_t time) { +void RenderEntity::update_tile(const util::Vector2s size, + const coord::tile &pos, + const terrain_elevation_t elevation, + const std::string terrain_path, + const time::time_t time) { std::unique_lock lock{this->mutex}; if (this->vertices.empty()) { @@ -52,9 +49,9 @@ void TerrainRenderEntity::update_tile(const util::Vector2s size, this->changed = true; } -void TerrainRenderEntity::update(const util::Vector2s size, - const tiles_t tiles, - const time::time_t time) { +void RenderEntity::update(const util::Vector2s size, + const tiles_t tiles, + const time::time_t time) { std::unique_lock lock{this->mutex}; // increase by 1 in every dimension because tiles @@ -108,40 +105,28 @@ void TerrainRenderEntity::update(const util::Vector2s size, this->changed = true; } -const std::vector &TerrainRenderEntity::get_vertices() { +const std::vector RenderEntity::get_vertices() { std::shared_lock lock{this->mutex}; return this->vertices; } -const TerrainRenderEntity::tiles_t &TerrainRenderEntity::get_tiles() { +const RenderEntity::tiles_t RenderEntity::get_tiles() { std::shared_lock lock{this->mutex}; return this->tiles; } -const std::unordered_set &TerrainRenderEntity::get_terrain_paths() { +const std::unordered_set RenderEntity::get_terrain_paths() { std::shared_lock lock{this->mutex}; return this->terrain_paths; } -const util::Vector2s &TerrainRenderEntity::get_size() { +const util::Vector2s RenderEntity::get_size() { std::shared_lock lock{this->mutex}; return this->size; } -bool TerrainRenderEntity::is_changed() { - std::shared_lock lock{this->mutex}; - - return this->changed; -} - -void TerrainRenderEntity::clear_changed_flag() { - std::unique_lock lock{this->mutex}; - - this->changed = false; -} - } // namespace openage::renderer::terrain diff --git a/libopenage/renderer/stages/terrain/render_entity.h b/libopenage/renderer/stages/terrain/render_entity.h index 4136c7c81a..0f726a2351 100644 --- a/libopenage/renderer/stages/terrain/render_entity.h +++ b/libopenage/renderer/stages/terrain/render_entity.h @@ -2,8 +2,6 @@ #pragma once -#include -#include #include #include #include @@ -11,7 +9,7 @@ #include "coord/scene.h" #include "coord/tile.h" #include "curve/discrete.h" -#include "time/time.h" +#include "renderer/stages/render_entity.h" #include "util/vector.h" @@ -20,10 +18,10 @@ namespace openage::renderer::terrain { /** * Render entity for pushing updates to the Terrain renderer. */ -class TerrainRenderEntity { +class RenderEntity final : public renderer::RenderEntity { public: - TerrainRenderEntity(); - ~TerrainRenderEntity() = default; + RenderEntity(); + ~RenderEntity() = default; using terrain_elevation_t = util::FixedPoint; using tiles_t = std::vector>; @@ -32,6 +30,8 @@ class TerrainRenderEntity { * Update a single tile of the displayed terrain (chunk) with information from the * gamestate. * + * Updating the render entity with this method is thread-safe. + * * @param size Size of the terrain in tiles (width x length) * @param pos Position of the tile in the chunk. * @param elevation Height of terrain tile. @@ -48,6 +48,8 @@ class TerrainRenderEntity { * Update the full grid of the displayed terrain (chunk) with information from the * gamestate. * + * Updating the render entity with this method is thread-safe. + * * @param size Size of the terrain in tiles (width x length) * @param tiles Animation data for each tile (elevation, terrain path). * @param time Simulation time of the update. @@ -59,52 +61,40 @@ class TerrainRenderEntity { /** * Get the vertices of the terrain. * + * Accessing the terrain vertices is thread-safe. + * * @return Vector of vertex coordinates. */ - const std::vector &get_vertices(); + const std::vector get_vertices(); /** * Get the tiles of the terrain. * + * Accessing the terrain tiles is thread-safe. + * * @return Terrain tiles. */ - const tiles_t &get_tiles(); + const tiles_t get_tiles(); /** * Get the terrain paths used in the terrain. * + * Accessing the terrain paths is thread-safe. + * * @return Terrain paths. */ - const std::unordered_set &get_terrain_paths(); + const std::unordered_set get_terrain_paths(); /** * Get the number of vertices on each side of the terrain. * - * @return Vector with width as first element and height as second element. - */ - const util::Vector2s &get_size(); - - /** - * Check whether the render entity has received new updates from the - * gamestate. + * Accessing the vertices size is thread-safe. * - * @return true if updates have been received, else false. - */ - bool is_changed(); - - /** - * Clear the update flag by setting it to false. + * @return Vector with width as first element and height as second element. */ - void clear_changed_flag(); + const util::Vector2s get_size(); private: - /** - * Flag for determining if the render entity has been updated by the - * corresponding gamestate entity. Set to true every time \p update() - * is called. - */ - bool changed; - /** * Chunk dimensions (width x height). */ @@ -115,11 +105,6 @@ class TerrainRenderEntity { */ tiles_t tiles; - /** - * Time of the last update call. - */ - time::time_t last_update; - /** * Terrain texture paths used in \p tiles . */ @@ -129,10 +114,5 @@ class TerrainRenderEntity { * Terrain vertices (ingame coordinates). */ std::vector vertices; - - /** - * Mutex for protecting threaded access. - */ - std::shared_mutex mutex; }; } // namespace openage::renderer::terrain diff --git a/libopenage/renderer/stages/terrain/render_stage.cpp b/libopenage/renderer/stages/terrain/render_stage.cpp index 740fe260f6..01376d886b 100644 --- a/libopenage/renderer/stages/terrain/render_stage.cpp +++ b/libopenage/renderer/stages/terrain/render_stage.cpp @@ -48,7 +48,7 @@ std::shared_ptr TerrainRenderStage::get_render_pass() { return this->render_pass; } -void TerrainRenderStage::add_render_entity(const std::shared_ptr entity, +void TerrainRenderStage::add_render_entity(const std::shared_ptr entity, const util::Vector2s chunk_size, const coord::scene2_delta chunk_offset) { std::unique_lock lock{this->mutex}; diff --git a/libopenage/renderer/stages/terrain/render_stage.h b/libopenage/renderer/stages/terrain/render_stage.h index 40caf2b309..6066dc90aa 100644 --- a/libopenage/renderer/stages/terrain/render_stage.h +++ b/libopenage/renderer/stages/terrain/render_stage.h @@ -32,7 +32,7 @@ class AssetManager; } namespace terrain { -class TerrainRenderEntity; +class RenderEntity; class TerrainRenderMesh; class TerrainRenderModel; @@ -73,7 +73,7 @@ class TerrainRenderStage { * * @param render_entity New render entity. */ - void add_render_entity(const std::shared_ptr entity, + void add_render_entity(const std::shared_ptr entity, const util::Vector2s chunk_size, const coord::scene2_delta chunk_offset); @@ -119,7 +119,7 @@ class TerrainRenderStage { /** * Engine interface for updating terrain draw information. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; /** * 3D model of the terrain. diff --git a/libopenage/renderer/stages/world/object.cpp b/libopenage/renderer/stages/world/object.cpp index 0a707807ca..2396c94ed2 100644 --- a/libopenage/renderer/stages/world/object.cpp +++ b/libopenage/renderer/stages/world/object.cpp @@ -43,7 +43,7 @@ WorldObject::WorldObject(const std::shared_ptr &entity) { +void WorldObject::set_render_entity(const std::shared_ptr &entity) { this->render_entity = entity; this->fetch_updates(); } @@ -63,6 +63,9 @@ void WorldObject::fetch_updates(const time::time_t &time) { // Get data from render entity this->ref_id = this->render_entity->get_id(); + + // Thread-safe access to curves needs a lock on the render entity's mutex + auto read_lock = this->render_entity->get_read_lock(); this->position.sync(this->render_entity->get_position()); this->animation_info.sync(this->render_entity->get_animation_path(), std::function(const std::string &)>( @@ -79,6 +82,9 @@ void WorldObject::fetch_updates(const time::time_t &time) { this->last_update); this->angle.sync(this->render_entity->get_angle(), this->last_update); + // Unlock mutex of the render entity + read_lock.unlock(); + // Set self to changed so that world renderer can update the renderable this->changed = true; this->render_entity->clear_changed_flag(); diff --git a/libopenage/renderer/stages/world/object.h b/libopenage/renderer/stages/world/object.h index 1302733954..c5579f62a0 100644 --- a/libopenage/renderer/stages/world/object.h +++ b/libopenage/renderer/stages/world/object.h @@ -29,7 +29,7 @@ class Animation2dInfo; } // namespace resources namespace world { -class WorldRenderEntity; +class RenderEntity; /** * Stores the state of a renderable object in the World render stage. @@ -49,7 +49,7 @@ class WorldObject { * * @param entity New world render entity. */ - void set_render_entity(const std::shared_ptr &entity); + void set_render_entity(const std::shared_ptr &entity); /** * Fetch updates from the render entity. @@ -177,7 +177,7 @@ class WorldObject { * Entity that gets updates from the gamestate, e.g. the position and * requested animation data. */ - std::shared_ptr render_entity; + std::shared_ptr render_entity; /** * Reference ID for passing interaction with the graphic (e.g. mouse clicks) back to diff --git a/libopenage/renderer/stages/world/render_entity.cpp b/libopenage/renderer/stages/world/render_entity.cpp index 6909a6554a..7a8e145741 100644 --- a/libopenage/renderer/stages/world/render_entity.cpp +++ b/libopenage/renderer/stages/world/render_entity.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2023 the openage authors. See copying.md for legal info. +// Copyright 2022-2024 the openage authors. See copying.md for legal info. #include "render_entity.h" @@ -10,20 +10,19 @@ namespace openage::renderer::world { -WorldRenderEntity::WorldRenderEntity() : - changed{false}, +RenderEntity::RenderEntity() : + renderer::RenderEntity{}, ref_id{0}, position{nullptr, 0, "", nullptr, SCENE_ORIGIN}, angle{nullptr, 0, "", nullptr, 0}, - animation_path{nullptr, 0}, - last_update{0.0} { + animation_path{nullptr, 0} { } -void WorldRenderEntity::update(const uint32_t ref_id, - const curve::Continuous &position, - const curve::Segmented &angle, - const std::string animation_path, - const time::time_t time) { +void RenderEntity::update(const uint32_t ref_id, + const curve::Continuous &position, + const curve::Segmented &angle, + const std::string animation_path, + const time::time_t time) { std::unique_lock lock{this->mutex}; this->ref_id = ref_id; @@ -41,10 +40,10 @@ void WorldRenderEntity::update(const uint32_t ref_id, this->last_update = time; } -void WorldRenderEntity::update(const uint32_t ref_id, - const coord::phys3 position, - const std::string animation_path, - const time::time_t time) { +void RenderEntity::update(const uint32_t ref_id, + const coord::phys3 position, + const std::string animation_path, + const time::time_t time) { std::unique_lock lock{this->mutex}; this->ref_id = ref_id; @@ -54,46 +53,28 @@ void WorldRenderEntity::update(const uint32_t ref_id, this->last_update = time; } -uint32_t WorldRenderEntity::get_id() { +uint32_t RenderEntity::get_id() { std::shared_lock lock{this->mutex}; return this->ref_id; } -const curve::Continuous &WorldRenderEntity::get_position() { +const curve::Continuous &RenderEntity::get_position() { std::shared_lock lock{this->mutex}; return this->position; } -const curve::Segmented &WorldRenderEntity::get_angle() { +const curve::Segmented &RenderEntity::get_angle() { std::shared_lock lock{this->mutex}; return this->angle; } -const curve::Discrete &WorldRenderEntity::get_animation_path() { +const curve::Discrete &RenderEntity::get_animation_path() { std::shared_lock lock{this->mutex}; return this->animation_path; } -time::time_t WorldRenderEntity::get_update_time() { - std::shared_lock lock{this->mutex}; - - return this->last_update; -} - -bool WorldRenderEntity::is_changed() { - std::shared_lock lock{this->mutex}; - - return this->changed; -} - -void WorldRenderEntity::clear_changed_flag() { - std::unique_lock lock{this->mutex}; - - this->changed = false; -} - } // namespace openage::renderer::world diff --git a/libopenage/renderer/stages/world/render_entity.h b/libopenage/renderer/stages/world/render_entity.h index 7c56635f89..ed9011b8a4 100644 --- a/libopenage/renderer/stages/world/render_entity.h +++ b/libopenage/renderer/stages/world/render_entity.h @@ -3,18 +3,14 @@ #pragma once #include -#include -#include #include -#include - #include "coord/phys.h" #include "coord/scene.h" #include "curve/continuous.h" #include "curve/discrete.h" #include "curve/segmented.h" -#include "time/time.h" +#include "renderer/stages/render_entity.h" namespace openage::renderer::world { @@ -22,14 +18,16 @@ namespace openage::renderer::world { /** * Render entity for pushing updates to the World renderer. */ -class WorldRenderEntity { +class RenderEntity final : public renderer::RenderEntity { public: - WorldRenderEntity(); - ~WorldRenderEntity() = default; + RenderEntity(); + ~RenderEntity() = default; /** * Update the render entity with information from the gamestate. * + * Updating the render entity with this method is thread-safe. + * * @param ref_id Game entity ID. * @param position Position of the game entity inside the game world. * @param angle Angle of the game entity inside the game world. @@ -47,6 +45,8 @@ class WorldRenderEntity { * * Update the render entity with information from the gamestate. * + * Updating the render entity with this method is thread-safe. + * * @param ref_id Game entity ID. * @param position Position of the game entity inside the game world. * @param animation_path Path to the animation definition. @@ -60,6 +60,8 @@ class WorldRenderEntity { /** * Get the ID of the corresponding game entity. * + * Accessing the game entity ID is thread-safe. + * * @return Game entity ID. */ uint32_t get_id(); @@ -67,6 +69,9 @@ class WorldRenderEntity { /** * Get the position of the entity inside the game world. * + * Accessing the position curve REQUIRES a read lock on the render entity + * (using \p get_read_lock()) to ensure thread safety. + * * @return Position curve of the entity. */ const curve::Continuous &get_position(); @@ -74,6 +79,9 @@ class WorldRenderEntity { /** * Get the angle of the entity inside the game world. * + * Accessing the angle curve REQUIRES a read lock on the render entity + * (using \p get_read_lock()) to ensure thread safety. + * * @return Angle curve of the entity. */ const curve::Segmented &get_angle(); @@ -81,38 +89,14 @@ class WorldRenderEntity { /** * Get the animation definition path. * + * Accessing the animation path curve requires a read lock on the render entity + * (using \p get_read_lock()) to ensure thread safety. + * * @return Path to the animation definition file. */ const curve::Discrete &get_animation_path(); - /** - * Get the time of the last update. - * - * @return Time of last update. - */ - time::time_t get_update_time(); - - /** - * Check whether the render entity has received new updates from the - * gamestate. - * - * @return true if updates have been received, else false. - */ - bool is_changed(); - - /** - * Clear the update flag by setting it to false. - */ - void clear_changed_flag(); - private: - /** - * Flag for determining if the render entity has been updated by the - * corresponding gamestate entity. Set to true every time \p update() - * is called. - */ - bool changed; - /** * ID of the game entity in the gamestate. */ @@ -132,15 +116,5 @@ class WorldRenderEntity { * Path to the animation definition file. */ curve::Discrete animation_path; - - /** - * Time of the last update call. - */ - time::time_t last_update; - - /** - * Mutex for protecting threaded access. - */ - std::shared_mutex mutex; }; } // namespace openage::renderer::world diff --git a/libopenage/renderer/stages/world/render_stage.cpp b/libopenage/renderer/stages/world/render_stage.cpp index 0434f3cf07..d8d93279af 100644 --- a/libopenage/renderer/stages/world/render_stage.cpp +++ b/libopenage/renderer/stages/world/render_stage.cpp @@ -50,7 +50,7 @@ std::shared_ptr WorldRenderStage::get_render_pass() { return this->render_pass; } -void WorldRenderStage::add_render_entity(const std::shared_ptr entity) { +void WorldRenderStage::add_render_entity(const std::shared_ptr entity) { std::unique_lock lock{this->mutex}; auto world_object = std::make_shared(this->asset_manager); diff --git a/libopenage/renderer/stages/world/render_stage.h b/libopenage/renderer/stages/world/render_stage.h index e6ae39c094..f1256f3b57 100644 --- a/libopenage/renderer/stages/world/render_stage.h +++ b/libopenage/renderer/stages/world/render_stage.h @@ -31,7 +31,7 @@ class AssetManager; } namespace world { -class WorldRenderEntity; +class RenderEntity; class WorldObject; /** @@ -74,7 +74,7 @@ class WorldRenderStage { * * @param render_entity New render entity. */ - void add_render_entity(const std::shared_ptr entity); + void add_render_entity(const std::shared_ptr entity); /** * Update the render entities and render positions. diff --git a/libopenage/renderer/uniform_buffer.h b/libopenage/renderer/uniform_buffer.h index 85040a4976..c91edf1132 100644 --- a/libopenage/renderer/uniform_buffer.h +++ b/libopenage/renderer/uniform_buffer.h @@ -50,22 +50,22 @@ class UniformBuffer : public std::enable_shared_from_this { protected: virtual std::shared_ptr new_unif_in() = 0; - virtual void set_i32(std::shared_ptr const &, const char *, int32_t) = 0; - virtual void set_u32(std::shared_ptr const &, const char *, uint32_t) = 0; - virtual void set_f32(std::shared_ptr const &, const char *, float) = 0; - virtual void set_f64(std::shared_ptr const &, const char *, double) = 0; - virtual void set_bool(std::shared_ptr const &, const char *, bool) = 0; - virtual void set_v2f32(std::shared_ptr const &, const char *, Eigen::Vector2f const &) = 0; - virtual void set_v3f32(std::shared_ptr const &, const char *, Eigen::Vector3f const &) = 0; - virtual void set_v4f32(std::shared_ptr const &, const char *, Eigen::Vector4f const &) = 0; - virtual void set_v2i32(std::shared_ptr const &, const char *, Eigen::Vector2i const &) = 0; - virtual void set_v3i32(std::shared_ptr const &, const char *, Eigen::Vector3i const &) = 0; - virtual void set_v4i32(std::shared_ptr const &, const char *, Eigen::Vector4i const &) = 0; - virtual void set_v2ui32(std::shared_ptr const &, const char *, Eigen::Vector2 const &) = 0; - virtual void set_v3ui32(std::shared_ptr const &, const char *, Eigen::Vector3 const &) = 0; - virtual void set_v4ui32(std::shared_ptr const &, const char *, Eigen::Vector4 const &) = 0; - virtual void set_m4f32(std::shared_ptr const &, const char *, Eigen::Matrix4f const &) = 0; - virtual void set_tex(std::shared_ptr const &, const char *, std::shared_ptr const &) = 0; + virtual void set_i32(UniformBufferInput &in, const char *, int32_t) = 0; + virtual void set_u32(UniformBufferInput &in, const char *, uint32_t) = 0; + virtual void set_f32(UniformBufferInput &in, const char *, float) = 0; + virtual void set_f64(UniformBufferInput &in, const char *, double) = 0; + virtual void set_bool(UniformBufferInput &in, const char *, bool) = 0; + virtual void set_v2f32(UniformBufferInput &in, const char *, Eigen::Vector2f const &) = 0; + virtual void set_v3f32(UniformBufferInput &in, const char *, Eigen::Vector3f const &) = 0; + virtual void set_v4f32(UniformBufferInput &in, const char *, Eigen::Vector4f const &) = 0; + virtual void set_v2i32(UniformBufferInput &in, const char *, Eigen::Vector2i const &) = 0; + virtual void set_v3i32(UniformBufferInput &in, const char *, Eigen::Vector3i const &) = 0; + virtual void set_v4i32(UniformBufferInput &in, const char *, Eigen::Vector4i const &) = 0; + virtual void set_v2ui32(UniformBufferInput &in, const char *, Eigen::Vector2 const &) = 0; + virtual void set_v3ui32(UniformBufferInput &in, const char *, Eigen::Vector3 const &) = 0; + virtual void set_v4ui32(UniformBufferInput &in, const char *, Eigen::Vector4 const &) = 0; + virtual void set_m4f32(UniformBufferInput &in, const char *, Eigen::Matrix4f const &) = 0; + virtual void set_tex(UniformBufferInput &in, const char *, std::shared_ptr const &) = 0; }; } // namespace openage::renderer diff --git a/libopenage/renderer/uniform_input.cpp b/libopenage/renderer/uniform_input.cpp index ee42202faf..b3abbe7ba8 100644 --- a/libopenage/renderer/uniform_input.cpp +++ b/libopenage/renderer/uniform_input.cpp @@ -157,71 +157,71 @@ UniformBufferInput::UniformBufferInput(std::shared_ptr const &buf void UniformBufferInput::update() {} void UniformBufferInput::update(const char *unif, int32_t val) { - this->buffer->set_i32(this->shared_from_this(), unif, val); + this->buffer->set_i32(*this, unif, val); } void UniformBufferInput::update(const char *unif, uint32_t val) { - this->buffer->set_u32(this->shared_from_this(), unif, val); + this->buffer->set_u32(*this, unif, val); } void UniformBufferInput::update(const char *unif, float val) { - this->buffer->set_f32(this->shared_from_this(), unif, val); + this->buffer->set_f32(*this, unif, val); } void UniformBufferInput::update(const char *unif, double val) { - this->buffer->set_f64(this->shared_from_this(), unif, val); + this->buffer->set_f64(*this, unif, val); } void UniformBufferInput::update(const char *unif, bool val) { - this->buffer->set_bool(this->shared_from_this(), unif, val); + this->buffer->set_bool(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Vector2f const &val) { - this->buffer->set_v2f32(this->shared_from_this(), unif, val); + this->buffer->set_v2f32(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Vector3f const &val) { - this->buffer->set_v3f32(this->shared_from_this(), unif, val); + this->buffer->set_v3f32(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Vector4f const &val) { - this->buffer->set_v4f32(this->shared_from_this(), unif, val); + this->buffer->set_v4f32(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Vector2i const &val) { - this->buffer->set_v2i32(this->shared_from_this(), unif, val); + this->buffer->set_v2i32(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Vector3i const &val) { - this->buffer->set_v3i32(this->shared_from_this(), unif, val); + this->buffer->set_v3i32(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Vector4i const &val) { - this->buffer->set_v4i32(this->shared_from_this(), unif, val); + this->buffer->set_v4i32(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Vector2 const &val) { - this->buffer->set_v2ui32(this->shared_from_this(), unif, val); + this->buffer->set_v2ui32(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Vector3 const &val) { - this->buffer->set_v3ui32(this->shared_from_this(), unif, val); + this->buffer->set_v3ui32(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Vector4 const &val) { - this->buffer->set_v4ui32(this->shared_from_this(), unif, val); + this->buffer->set_v4ui32(*this, unif, val); } void UniformBufferInput::update(const char *unif, std::shared_ptr const &val) { - this->buffer->set_tex(this->shared_from_this(), unif, val); + this->buffer->set_tex(*this, unif, val); } void UniformBufferInput::update(const char *unif, std::shared_ptr &val) { - this->buffer->set_tex(this->shared_from_this(), unif, val); + this->buffer->set_tex(*this, unif, val); } void UniformBufferInput::update(const char *unif, Eigen::Matrix4f const &val) { - this->buffer->set_m4f32(this->shared_from_this(), unif, val); + this->buffer->set_m4f32(*this, unif, val); } } // namespace openage::renderer diff --git a/libopenage/renderer/vulkan/loader.cpp b/libopenage/renderer/vulkan/loader.cpp index fdeb6f8c94..2dcf6bd2fc 100644 --- a/libopenage/renderer/vulkan/loader.cpp +++ b/libopenage/renderer/vulkan/loader.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2024 the openage authors. See copying.md for legal info. #include "loader.h" @@ -9,14 +9,14 @@ namespace openage { namespace renderer { namespace vulkan { -VlkLoader::VlkLoader() - : inited(false) {} +VlkLoader::VlkLoader() : + inited(false) {} void VlkLoader::init(VkInstance instance) { - #ifndef NDEBUG +#ifndef NDEBUG this->pCreateDebugReportCallbackEXT = PFN_vkCreateDebugReportCallbackEXT(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); this->pDestroyDebugReportCallbackEXT = PFN_vkDestroyDebugReportCallbackEXT(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); - #endif +#endif this->inited = true; } @@ -24,10 +24,9 @@ void VlkLoader::init(VkInstance instance) { #ifndef NDEBUG VkResult VlkLoader::vkCreateDebugReportCallbackEXT( VkInstance instance, - const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDebugReportCallbackEXT* pCallback -) { + const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkDebugReportCallbackEXT *pCallback) { if (!this->inited) { throw Error(MSG(err) << "Tried to request function from Vulkan extension loader before initializing it."); } @@ -42,8 +41,7 @@ VkResult VlkLoader::vkCreateDebugReportCallbackEXT( void VlkLoader::vkDestroyDebugReportCallbackEXT( VkInstance instance, VkDebugReportCallbackEXT callback, - const VkAllocationCallbacks* pAllocator -) { + const VkAllocationCallbacks *pAllocator) { if (!this->inited) { throw Error(MSG(err) << "Tried to request function from Vulkan extension loader before initializing it."); } @@ -54,4 +52,6 @@ void VlkLoader::vkDestroyDebugReportCallbackEXT( } #endif -}}} // openage::renderer::vulkan +} // namespace vulkan +} // namespace renderer +} // namespace openage diff --git a/libopenage/util/compiler.cpp b/libopenage/util/compiler.cpp index b854d91278..2fd850920d 100644 --- a/libopenage/util/compiler.cpp +++ b/libopenage/util/compiler.cpp @@ -1,22 +1,22 @@ -// Copyright 2015-2019 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. #include "compiler.h" #ifndef _WIN32 -#include -#include + #include + #include #else -#define WIN32_LEAN_AND_MEAN -#include -#include + #define WIN32_LEAN_AND_MEAN + #include + #include #endif #include "strings.h" #include #include -#include #include +#include namespace openage { namespace util { @@ -34,7 +34,8 @@ std::string demangle(const char *symbol) { if (status != 0) { return symbol; - } else { + } + else { std::string result{buf}; free(buf); return result; @@ -78,12 +79,12 @@ std::optional symbol_name_win(const void *addr) { constexpr int buffer_size = sizeof(SYMBOL_INFO) + name_buffer_size * sizeof(char); std::array buffer; - SYMBOL_INFO *symbol_info = reinterpret_cast(buffer.data()); + SYMBOL_INFO *symbol_info = reinterpret_cast(buffer.data()); symbol_info->SizeOfStruct = sizeof(SYMBOL_INFO); symbol_info->MaxNameLen = name_buffer_size; - if (SymFromAddr(process_handle, reinterpret_cast(addr), nullptr, symbol_info)) { + if (SymFromAddr(process_handle, reinterpret_cast(addr), nullptr, symbol_info)) { return std::string(symbol_info->Name); } } @@ -92,7 +93,7 @@ std::optional symbol_name_win(const void *addr) { } -} +} // namespace #endif std::string symbol_name(const void *addr, bool require_exact_addr, bool no_pure_addrs) { @@ -100,8 +101,7 @@ std::string symbol_name(const void *addr, bool require_exact_addr, bool no_pure_ auto symbol_name_result = symbol_name_win(addr); - if (!initialized_symbol_handler_successfully || - !symbol_name_result.has_value()) { + if (!initialized_symbol_handler_successfully || !symbol_name_result.has_value()) { return no_pure_addrs ? "" : addr_to_string(addr); } @@ -113,8 +113,9 @@ std::string symbol_name(const void *addr, bool require_exact_addr, bool no_pure_ if (dladdr(addr, &addr_info) == 0) { // dladdr has... failed. return no_pure_addrs ? "" : addr_to_string(addr); - } else { - size_t symbol_offset = (size_t) addr - (size_t) addr_info.dli_saddr; + } + else { + size_t symbol_offset = (size_t)addr - (size_t)addr_info.dli_saddr; if (addr_info.dli_sname == nullptr or (symbol_offset != 0 and require_exact_addr)) { return no_pure_addrs ? "" : addr_to_string(addr); @@ -123,7 +124,8 @@ std::string symbol_name(const void *addr, bool require_exact_addr, bool no_pure_ if (symbol_offset == 0) { // this is our symbol name. return demangle(addr_info.dli_sname); - } else { + } + else { return util::sformat("%s+0x%lx", demangle(addr_info.dli_sname).c_str(), symbol_offset); @@ -138,7 +140,8 @@ bool is_symbol(const void *addr) { if (!initialized_symbol_handler_successfully) { return true; - } else { + } + else { return symbol_name_win(addr).has_value(); } @@ -154,4 +157,5 @@ bool is_symbol(const void *addr) { } -}} // openage::util +} // namespace util +} // namespace openage diff --git a/libopenage/util/fds.cpp b/libopenage/util/fds.cpp index 468d3354bc..8d5188c10d 100644 --- a/libopenage/util/fds.cpp +++ b/libopenage/util/fds.cpp @@ -1,4 +1,4 @@ -// Copyright 2014-2019 the openage authors. See copying.md for legal info. +// Copyright 2014-2024 the openage authors. See copying.md for legal info. #include "fds.h" @@ -7,12 +7,12 @@ #include #include -#include #include "pty.h" +#include #ifdef _WIN32 -#include + #include #else -#include + #include #endif #include "unicode.h" @@ -28,10 +28,10 @@ FD::FD(int fd, bool set_nonblocking) { this->close_on_destroy = true; if (set_nonblocking) { - #ifndef _WIN32 +#ifndef _WIN32 int flags = ::fcntl(this->fd, F_GETFL, 0); ::fcntl(this->fd, F_SETFL, flags | O_NONBLOCK); - #endif +#endif } } @@ -64,31 +64,32 @@ int FD::putbyte(char c) { int FD::putcp(int cp) { char utf8buf[5]; if (util::utf8_encode(cp, utf8buf) == 0) { - //unrepresentable character (question mark in black rhombus) + // unrepresentable character (question mark in black rhombus) return this->puts("\uFFFD"); - } else { + } + else { return this->puts(utf8buf); } } int FD::printf(const char *format, ...) { const unsigned buf_size = 16; - char *buf = static_cast(malloc(sizeof(char) * buf_size)); + char *buf = static_cast(malloc(sizeof(char) * buf_size)); if (!buf) { return -1; } va_list vl; - //first, try to vsnprintf to a buffer of length 16 + // first, try to vsnprintf to a buffer of length 16 va_start(vl, format); unsigned len = vsnprintf(buf, buf_size, format, vl); va_end(vl); - //if that wasn't enough, allocate more memory and try again + // if that wasn't enough, allocate more memory and try again if (len >= buf_size) { char *oldbuf = buf; - buf = static_cast(realloc(oldbuf, sizeof(char) * (len + 1))); + buf = static_cast(realloc(oldbuf, sizeof(char) * (len + 1))); if (!buf) { free(oldbuf); return -1; @@ -99,42 +100,42 @@ int FD::printf(const char *format, ...) { va_end(vl); } - //output buf to the socket + // output buf to the socket int result = this->puts(buf); - //free the buffer + // free the buffer free(buf); return result; } void FD::setinputmodecanon() { - #ifndef _WIN32 +#ifndef _WIN32 if (::isatty(this->fd)) { - //get the terminal settings for stdin + // get the terminal settings for stdin ::tcgetattr(this->fd, &this->old_tio); - //backup old settings + // backup old settings struct termios new_tio = this->old_tio; - //disable buffered i/o (canonical mode) and local echo + // disable buffered i/o (canonical mode) and local echo new_tio.c_lflag &= (~ICANON & ~ECHO & ~ISIG); - //set the settings + // set the settings ::tcsetattr(this->fd, TCSANOW, &new_tio); this->restore_input_mode_on_destroy = true; } - #endif /* _WIN32 */ +#endif /* _WIN32 */ } void FD::restoreinputmode() { - #ifndef _WIN32 +#ifndef _WIN32 if (::isatty(this->fd)) { ::tcsetattr(this->fd, TCSANOW, &this->old_tio); this->restore_input_mode_on_destroy = false; } - #endif /* _WIN32 */ +#endif /* _WIN32 */ } -} // openage::util +} // namespace openage::util diff --git a/libopenage/util/fds.h b/libopenage/util/fds.h index 251ad0aec7..309384e1ad 100644 --- a/libopenage/util/fds.h +++ b/libopenage/util/fds.h @@ -5,7 +5,7 @@ #include #ifndef _WIN32 -#include + #include #endif namespace openage { diff --git a/libopenage/util/fslike/directory.cpp b/libopenage/util/fslike/directory.cpp index 22055e2c32..420757f3a6 100644 --- a/libopenage/util/fslike/directory.cpp +++ b/libopenage/util/fslike/directory.cpp @@ -1,11 +1,11 @@ -// Copyright 2017-2019 the openage authors. See copying.md for legal info. +// Copyright 2017-2024 the openage authors. See copying.md for legal info. #include "directory.h" // HACK: windows.h defines max and min as macros. This results in compile errors. #ifdef _WIN32 -// defining `NOMINMAX` disables the definition of those macros. -#define NOMINMAX + // defining `NOMINMAX` disables the definition of those macros. + #define NOMINMAX #endif #include @@ -17,34 +17,32 @@ #include #ifdef __APPLE__ -#include + #include #endif #ifdef _WIN32 -#include -#include -#include -// HACK: What the heck? I want the std::filesystem library! -#define O_NOCTTY 0 -#define O_NONBLOCK 0 -#define W_OK 2 + #include + #include + #include + // HACK: What the heck? I want the std::filesystem library! + #define O_NOCTTY 0 + #define O_NONBLOCK 0 + #define W_OK 2 #else // ! _MSC_VER -#include + #include #endif -#include "./native.h" #include "../file.h" #include "../filelike/native.h" #include "../misc.h" #include "../path.h" +#include "./native.h" namespace openage::util::fslike { -Directory::Directory(std::string basepath, bool create_if_missing) - : +Directory::Directory(std::string basepath, bool create_if_missing) : basepath{std::move(basepath)} { - if (create_if_missing) { this->mkdirs({}); } @@ -78,8 +76,7 @@ bool Directory::is_file(const Path::parts_t &parts) { auto stat_result = this->do_stat(parts); // test for regular file - if (std::get<1>(stat_result) == 0 and - S_ISREG(std::get<0>(stat_result).st_mode)) { + if (std::get<1>(stat_result) == 0 and S_ISREG(std::get<0>(stat_result).st_mode)) { return true; } @@ -91,8 +88,7 @@ bool Directory::is_dir(const Path::parts_t &parts) { auto stat_result = this->do_stat(parts); // test for regular file - if (std::get<1>(stat_result) == 0 and - S_ISDIR(std::get<0>(stat_result).st_mode)) { + if (std::get<1>(stat_result) == 0 and S_ISDIR(std::get<0>(stat_result).st_mode)) { return true; } @@ -104,9 +100,7 @@ bool Directory::writable(const Path::parts_t &parts) { Path::parts_t parts_test = parts; // try to find the first existing path-part - while (not (this->is_dir(parts_test) or - this->is_file(parts_test))) { - + while (not(this->is_dir(parts_test) or this->is_file(parts_test))) { if (parts_test.size() == 0) { throw Error{ERR << "file not found"}; } @@ -120,7 +114,6 @@ bool Directory::writable(const Path::parts_t &parts) { std::vector Directory::list(const Path::parts_t &parts) { - const std::string path = this->resolve(parts); std::vector ret; @@ -145,7 +138,6 @@ std::vector Directory::list(const Path::parts_t &parts) { bool Directory::mkdirs(const Path::parts_t &parts) { - Path::parts_t all_parts = util::split(this->basepath, PATHSEP); vector_extend(all_parts, parts); @@ -158,9 +150,7 @@ bool Directory::mkdirs(const Path::parts_t &parts) { struct stat buf; // it it exists already, try creating the next one - if (stat(dirpath.c_str(), &buf) == 0 and - S_ISDIR(buf.st_mode)) { - + if (stat(dirpath.c_str(), &buf) == 0 and S_ISDIR(buf.st_mode)) { continue; } @@ -184,40 +174,35 @@ bool Directory::mkdirs(const Path::parts_t &parts) { File Directory::open_r(const Path::parts_t &parts) { return File{ std::make_shared(this->resolve(parts), - filelike::Native::mode_t::R) - }; + filelike::Native::mode_t::R)}; } File Directory::open_w(const Path::parts_t &parts) { return File{ std::make_shared(this->resolve(parts), - filelike::Native::mode_t::W) - }; + filelike::Native::mode_t::W)}; } File Directory::open_rw(const Path::parts_t &parts) { return File{ std::make_shared(this->resolve(parts), - filelike::Native::mode_t::RW) - }; + filelike::Native::mode_t::RW)}; } File Directory::open_a(const Path::parts_t &parts) { return File{ std::make_shared(this->resolve(parts), - filelike::Native::mode_t::A) - }; + filelike::Native::mode_t::A)}; } File Directory::open_ar(const Path::parts_t &parts) { return File{ std::make_shared(this->resolve(parts), - filelike::Native::mode_t::AR) - }; + filelike::Native::mode_t::AR)}; } @@ -228,9 +213,9 @@ std::string Directory::get_native_path(const Path::parts_t &parts) { bool Directory::rename(const Path::parts_t &parts, const Path::parts_t &target_parts) { - return std::rename(this->resolve(parts).c_str(), - this->resolve(target_parts).c_str()) == 0; + this->resolve(target_parts).c_str()) + == 0; } @@ -245,9 +230,8 @@ bool Directory::touch(const Path::parts_t &parts) { // create the file if missing int fd = open( path.c_str(), - O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, - 0666 - ); + O_WRONLY | O_CREAT | O_NOCTTY | O_NONBLOCK, + 0666); if (fd < 0) { return false; @@ -308,4 +292,4 @@ std::ostream &Directory::repr(std::ostream &stream) { return stream; } -} // openage::util::fslike +} // namespace openage::util::fslike diff --git a/libopenage/util/macro/concat.h b/libopenage/util/macro/concat.h index b8f7b746ca..dd057ccd9f 100644 --- a/libopenage/util/macro/concat.h +++ b/libopenage/util/macro/concat.h @@ -9,16 +9,16 @@ #define CONCAT_N(_1, _2, _3, NAME, ...) NAME #ifdef _MSC_VER -#define CONCAT_COUNT_ARGS_IMPL(args) CONCAT_N args -#define CONCAT_COUNT_ARGS(...) CONCAT_COUNT_ARGS_IMPL((__VA_ARGS__, 3, 2, 1)) -#define CONCAT_HELPER2(count) CONCAT_##count -#define CONCAT_HELPER1(count) CONCAT_HELPER2(count) -#define CONCAT_HELPER(count) CONCAT_HELPER1(count) -#define CONCAT_GLUE(x, y) x y -#define CONCAT(OP, ...) CONCAT_GLUE(CONCAT_HELPER(CONCAT_COUNT_ARGS(__VA_ARGS__)), (OP, __VA_ARGS__)) + #define CONCAT_COUNT_ARGS_IMPL(args) CONCAT_N args + #define CONCAT_COUNT_ARGS(...) CONCAT_COUNT_ARGS_IMPL((__VA_ARGS__, 3, 2, 1)) + #define CONCAT_HELPER2(count) CONCAT_##count + #define CONCAT_HELPER1(count) CONCAT_HELPER2(count) + #define CONCAT_HELPER(count) CONCAT_HELPER1(count) + #define CONCAT_GLUE(x, y) x y + #define CONCAT(OP, ...) CONCAT_GLUE(CONCAT_HELPER(CONCAT_COUNT_ARGS(__VA_ARGS__)), (OP, __VA_ARGS__)) #else -#define CONCAT(OP, ...) CONCAT_N(__VA_ARGS__, \ - CONCAT_3, \ - CONCAT_2, \ - CONCAT_1)(OP, __VA_ARGS__) + #define CONCAT(OP, ...) CONCAT_N(__VA_ARGS__, \ + CONCAT_3, \ + CONCAT_2, \ + CONCAT_1)(OP, __VA_ARGS__) #endif diff --git a/libopenage/util/macro/loop.h b/libopenage/util/macro/loop.h index 5b8dc59e8e..c868a12694 100644 --- a/libopenage/util/macro/loop.h +++ b/libopenage/util/macro/loop.h @@ -9,16 +9,16 @@ #define LOOP_N(_1, _2, _3, NAME, ...) NAME #ifdef _MSC_VER -#define LOOP_COUNT_ARGS_IMPL(args) LOOP_N args -#define LOOP_COUNT_ARGS(...) LOOP_COUNT_ARGS_IMPL((__VA_ARGS__, 3, 2, 1)) -#define LOOP_HELPER2(count) LOOP_##count -#define LOOP_HELPER1(count) LOOP_HELPER2(count) -#define LOOP_HELPER(count) LOOP_HELPER1(count) -#define LOOP_GLUE(x, y) x y -#define LOOP(MACRO, ...) LOOP_GLUE(LOOP_HELPER(LOOP_COUNT_ARGS(__VA_ARGS__)), (MACRO, __VA_ARGS__)) + #define LOOP_COUNT_ARGS_IMPL(args) LOOP_N args + #define LOOP_COUNT_ARGS(...) LOOP_COUNT_ARGS_IMPL((__VA_ARGS__, 3, 2, 1)) + #define LOOP_HELPER2(count) LOOP_##count + #define LOOP_HELPER1(count) LOOP_HELPER2(count) + #define LOOP_HELPER(count) LOOP_HELPER1(count) + #define LOOP_GLUE(x, y) x y + #define LOOP(MACRO, ...) LOOP_GLUE(LOOP_HELPER(LOOP_COUNT_ARGS(__VA_ARGS__)), (MACRO, __VA_ARGS__)) #else -#define LOOP(MACRO, ...) LOOP_N(__VA_ARGS__, \ - LOOP_3, \ - LOOP_2, \ - LOOP_1)(MACRO, __VA_ARGS__) + #define LOOP(MACRO, ...) LOOP_N(__VA_ARGS__, \ + LOOP_3, \ + LOOP_2, \ + LOOP_1)(MACRO, __VA_ARGS__) #endif diff --git a/libopenage/util/os.cpp b/libopenage/util/os.cpp index c3bbea2da6..ccc68d86c6 100644 --- a/libopenage/util/os.cpp +++ b/libopenage/util/os.cpp @@ -1,4 +1,4 @@ -// Copyright 2014-2019 the openage authors. See copying.md for legal info. +// Copyright 2014-2024 the openage authors. See copying.md for legal info. #include "os.h" @@ -7,16 +7,16 @@ #ifdef _WIN32 // TODO not yet implemented #else -#include + #include #endif #ifdef __APPLE__ -#include + #include #endif #ifdef __FreeBSD__ -#include -#include + #include + #include #endif #include "../log/log.h" @@ -77,8 +77,7 @@ std::string self_exec_filename() { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, - -1 - }; + -1}; while (true) { std::unique_ptr buf{new char[bufsize]}; @@ -103,23 +102,23 @@ int execute_file(const char *path, bool background) { // TODO some sort of shell-open, not yet implemented return -1; // failure #else - std::string runner = ""; -#ifdef __APPLE__ - runner = subprocess::which("open"); -#else - runner = subprocess::which("xdg-open"); - // fallback - if (runner.size() == 0) { - runner = subprocess::which("gnome-open"); - } -#endif - if (runner.size() == 0) { - log::log(MSG(err) << "could not locate file-opener"); - return -1; - } + std::string runner = ""; + #ifdef __APPLE__ + runner = subprocess::which("open"); + #else + runner = subprocess::which("xdg-open"); + // fallback + if (runner.size() == 0) { + runner = subprocess::which("gnome-open"); + } + #endif + if (runner.size() == 0) { + log::log(MSG(err) << "could not locate file-opener"); + return -1; + } - return subprocess::call({runner.c_str(), path, nullptr}, not background); + return subprocess::call({runner.c_str(), path, nullptr}, not background); #endif } -} // namespace openage +} // namespace openage::os diff --git a/libopenage/util/pty.h b/libopenage/util/pty.h index 0ae9e72d23..cb85f38865 100644 --- a/libopenage/util/pty.h +++ b/libopenage/util/pty.h @@ -3,14 +3,14 @@ #pragma once #ifdef __APPLE__ -#include + #include #elif defined(__FreeBSD__) -#include -#include -#include -#include + #include + #include + #include + #include #elif _WIN32 // TODO not yet implemented #else -#include + #include #endif diff --git a/libopenage/util/strings.h b/libopenage/util/strings.h index ad04e5267c..69f2759fb4 100644 --- a/libopenage/util/strings.h +++ b/libopenage/util/strings.h @@ -11,9 +11,9 @@ #include #if defined(__GNUC__) -#define ATTRIBUTE_FORMAT(i, j) __attribute__((format(printf, i, j))) + #define ATTRIBUTE_FORMAT(i, j) __attribute__((format(printf, i, j))) #else -#define ATTRIBUTE_FORMAT(i, j) + #define ATTRIBUTE_FORMAT(i, j) #endif namespace openage { diff --git a/libopenage/util/subprocess.cpp b/libopenage/util/subprocess.cpp index 98ae6720eb..c9a7670159 100644 --- a/libopenage/util/subprocess.cpp +++ b/libopenage/util/subprocess.cpp @@ -1,19 +1,19 @@ -// Copyright 2014-2019 the openage authors. See copying.md for legal info. +// Copyright 2014-2024 the openage authors. See copying.md for legal info. #include "subprocess.h" +#include #include #include -#include #ifdef _WIN32 // TODO not yet implemented #else -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include #endif #include "../log/log.h" @@ -90,7 +90,7 @@ int call(const std::vector &argv, bool wait, const char *redirect_ if (redirect_stdout_to != nullptr) { replacement_stdout_fd = open( redirect_stdout_to, - O_WRONLY | O_CREAT|O_TRUNC|O_CLOEXEC, + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); if (replacement_stdout_fd < 0) { @@ -215,8 +215,8 @@ int call(const std::vector &argv, bool wait, const char *redirect_ } if (child_errno > 0) { - log::log(MSG(err) << "execv has failed: " << strerror(child_errno)); - return -1; + log::log(MSG(err) << "execv has failed: " << strerror(child_errno)); + return -1; } } @@ -235,7 +235,7 @@ int call(const std::vector &argv, bool wait, const char *redirect_ // everything went well. return status; - #endif +#endif } } // namespace openage::subprocess diff --git a/libopenage/util/thread_id.cpp b/libopenage/util/thread_id.cpp index 0bcfc0abc9..3a178c441d 100644 --- a/libopenage/util/thread_id.cpp +++ b/libopenage/util/thread_id.cpp @@ -1,13 +1,13 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. #include "thread_id.h" #include "config.h" #if HAVE_THREAD_LOCAL_STORAGE -#include + #include #else -#include + #include #endif namespace openage { @@ -23,9 +23,8 @@ namespace util { */ class ThreadIdSupplier { public: - ThreadIdSupplier() - : - val{ThreadIdSupplier::counting_id++} {} + ThreadIdSupplier() : + val{ThreadIdSupplier::counting_id++} {} const size_t val; @@ -42,12 +41,13 @@ std::atomic ThreadIdSupplier::counting_id{0}; #endif size_t get_current_thread_id() { - #if HAVE_THREAD_LOCAL_STORAGE +#if HAVE_THREAD_LOCAL_STORAGE static thread_local ThreadIdSupplier current_thread_id; return current_thread_id.val; - #else +#else return std::hash()(std::this_thread::get_id()); - #endif +#endif } -}} // namespace openage::util +} // namespace util +} // namespace openage diff --git a/libopenage/versions/versions.cpp b/libopenage/versions/versions.cpp index d2e1fd251a..29655b6db9 100644 --- a/libopenage/versions/versions.cpp +++ b/libopenage/versions/versions.cpp @@ -1,9 +1,9 @@ -// Copyright 2020-2023 the openage authors. See copying.md for legal info. +// Copyright 2020-2024 the openage authors. See copying.md for legal info. #include "versions.h" #ifdef __linux__ -#include + #include #endif #include diff --git a/openage/cppinterface/exctranslate.pyx b/openage/cppinterface/exctranslate.pyx index 9c6be25d36..ea99fc7042 100644 --- a/openage/cppinterface/exctranslate.pyx +++ b/openage/cppinterface/exctranslate.pyx @@ -1,4 +1,4 @@ -# Copyright 2015-2023 the openage authors. See copying.md for legal info. +# Copyright 2015-2024 the openage authors. See copying.md for legal info. """ Provides the raise_py_exception and describe_py_exception callbacks for @@ -10,21 +10,14 @@ from cpython.exc cimport ( PyErr_Occurred, PyErr_Fetch, PyErr_NormalizeException, - PyErr_SetObject, - PyErr_Restore -) -from cpython.pystate cimport ( - PyThreadState, - PyThreadState_Get + PyErr_SetObject ) -from libcpp.string cimport string from libcpp cimport bool as cppbool -from libopenage.log.level cimport level, err as lvl_err from libopenage.log.message cimport message from libopenage.error.error cimport Error -from libopenage.error.backtrace cimport Backtrace, backtrace_symbol +from libopenage.error.backtrace cimport backtrace_symbol from libopenage.pyinterface.functional cimport Func1 from libopenage.pyinterface.pyexception cimport ( PyException, @@ -36,26 +29,29 @@ from libopenage.pyinterface.exctranslate cimport ( set_exc_translation_funcs ) -import importlib from ..testing.testing import TestError -from ..log import err, info +from ..log import info cdef extern from "Python.h": int PyException_SetTraceback(PyObject *ex, PyObject *tb) -cdef extern from "traceback.h": - void _PyTraceback_Add(const char *funcname, const char *filename, int lineno) +# _PyTraceback_Add has been made private in Python 3.13 +# see https://github.com/python/cpython/pull/108453 +# TODO: Find another solution to add tracebacks +# cdef extern from "traceback.h": +# void _PyTraceback_Add(const char *funcname, const char *filename, int lineno) cdef void PyTraceback_Add(const char *functionname, const char *filename, int lineno) noexcept with gil: """ Add a new traceback stack frame. - Redirects to Python's internal _PyTraceback_Add function. - """ - # possible since 3.4.3 due to http://bugs.python.org/issue24436. - # the function will likely remain internal due to https://bugs.python.org/issue24743 - _PyTraceback_Add(functionname, filename, lineno) + Note: Currently does nothing, because _PyTraceback_Add is no longer + accessible since Python 3.13. + + TODO: Find another solution to add tracebacks. + """ + # _PyTraceback_Add(functionname, filename, lineno) cdef class CPPMessageObject: