aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md40
-rw-r--r--meson.build100
-rw-r--r--src/engine/renderer.cpp34
3 files changed, 131 insertions, 43 deletions
diff --git a/README.md b/README.md
index a164756..9d43332 100644
--- a/README.md
+++ b/README.md
@@ -5,11 +5,14 @@ content driven by Lua. Work in progress. See CLAUDE.md for details.
## Stack
-C++20, Meson/Ninja (Podman), SDL3, Dear ImGui, a custom mass-spring physics
-solver, Lua 5.4 with sol2, and stb_image.
+C++20, Meson/Ninja, SDL3, Dear ImGui, a custom mass-spring physics solver,
+Lua 5.4 with sol2, and stb_image. Box2D is linked but idle; bgfx, SoLoud, ENet
+and EnTT are planned but not yet wired up.
## Build and run
+### Container (reproducible, any distro)
+
```sh
podman build -t forts-clone-dev .
podman run --rm -v "$PWD":/src:Z --userns=keep-id localhost/forts-clone-dev:latest \
@@ -17,6 +20,39 @@ podman run --rm -v "$PWD":/src:Z --userns=keep-id localhost/forts-clone-dev:late
./builddir/forts-clone # run from repo root
```
+The container builds every dependency into `/usr/local` and links Lua
+statically, so the resulting binary runs on any host. You still run it on the
+host (it needs the host's SDL3 and a display).
+
+### Local (Arch Linux)
+
+`meson.build` falls back to system packages, so a native build only needs the
+libraries the code actually uses. bgfx, SoLoud, ENet and EnTT are part of the
+planned stack but not yet wired up, so they are not required to build.
+
+```sh
+# repo packages
+sudo pacman -S --needed meson ninja gcc pkgconf sdl3 lua54 glm box2d
+
+# sol2 (header-only Lua bindings) from the AUR, e.g. with an AUR helper
+paru -S sol2 # or: yay -S sol2
+
+meson setup builddir
+meson compile -C builddir
+./builddir/forts-clone # run from repo root
+```
+
+Native builds link Lua dynamically against the system `lua54`, which is fine
+when you build and run on the same machine.
+
+### Removing the container image
+
+```sh
+podman rmi localhost/forts-clone-dev:latest # drop the dev image
+podman image prune -f # drop dangling build layers
+rm -rf builddir # meson dir, reconfigure after
+```
+
## Assets
The repo ships CC0 placeholders only. If you own Forts, real textures load at
diff --git a/meson.build b/meson.build
index 0f528a2..e063136 100644
--- a/meson.build
+++ b/meson.build
@@ -16,45 +16,75 @@ cpp = meson.get_compiler('cpp')
# Dependencies (pre-built in /usr/local by Containerfile)
# =============================================================================
-# SDL3 -- windowing + input (system package)
-sdl3_dep = dependency('sdl3', required: true)
-
-# bgfx -- GPU rendering (built from source in Containerfile)
-# CMake on Fedora installs to lib64
-bgfx_dep = cpp.find_library('bgfx', dirs: ['/usr/local/lib64', '/usr/local/lib'])
-bimg_dep = cpp.find_library('bimg', dirs: ['/usr/local/lib64', '/usr/local/lib'])
-bx_dep = cpp.find_library('bx', dirs: ['/usr/local/lib64', '/usr/local/lib'])
+fs = import('fs')
-# Box2D v3 -- physics engine (built from source in Containerfile)
-box2d_dep = cpp.find_library('box2d', dirs: ['/usr/local/lib64', '/usr/local/lib'])
+# A source-built /usr/local (what the container ships) provides the heavier
+# libraries. On a stock system these paths are absent, so everything below falls
+# back to system packages. include_directories() errors on a missing directory,
+# hence the guard.
+#
+# NOTE: add /usr/local/include as a whole, never /usr/local/include/bx etc.
+# individually. sys/types.h includes <endian.h>, which collides with bx's
+# endian.h if -I.../include/bx is on the path.
+local_incs = []
+if fs.is_dir('/usr/local/include')
+ local_incs += include_directories('/usr/local/include')
+endif
+usr_local_libdirs = ['/usr/local/lib64', '/usr/local/lib', '/usr/local/lib/static']
-# SoLoud -- audio engine (built from source in Containerfile)
-soloud_dep = cpp.find_library('soloud', dirs: ['/usr/local/lib64', '/usr/local/lib'])
+# --- Libraries the code actually compiles against ---------------------------
-# ENet -- networking (built from source in Containerfile)
-enet_dep = cpp.find_library('enet', dirs: ['/usr/local/lib/static', '/usr/local/lib64', '/usr/local/lib'])
+# SDL3 -- windowing + input. System pkg-config on every target.
+sdl3_dep = dependency('sdl3', required: true)
-# IMPORTANT: only add /usr/local/include, NOT /usr/local/include/bx etc.
-# as individual paths. sys/types.h does #include <endian.h> and would
-# collide with bx/endian.h if -I/usr/local/include/bx is in the path.
-local_inc = include_directories('/usr/local/include')
+# glm -- header-only math. pkg-config where available, else rely on the include
+# path (headers in /usr/include or the guarded /usr/local/include).
+glm_dep = dependency('glm', required: false)
+if not glm_dep.found()
+ glm_dep = declare_dependency()
+endif
-# Lua -- scripting. Linked STATICALLY from /usr/local/lib/liblua.a (built in the
-# Containerfile) so the binary needs no liblua .so at runtime and runs on the
-# host regardless of its distro's lua soname. Headers come from /usr/local/include.
-lua_static = cpp.find_library('lua', dirs: ['/usr/local/lib'], static: true, required: true)
-m_dep = cpp.find_library('m', required: false)
-dl_dep = cpp.find_library('dl', required: false)
-lua_dep = declare_dependency(dependencies: [lua_static, m_dep, dl_dep])
+# sol2 -- header-only Lua bindings. Headers resolve via the include path
+# (/usr/include/sol system/AUR, or /usr/local/include/sol in the container).
+sol2_dep = declare_dependency()
-# glm -- math (system package)
-glm_dep = dependency('glm', required: true)
+# Lua 5.4. Container: a STATIC liblua.a in /usr/local/lib, so the binary carries
+# Lua and runs on any host regardless of its liblua soname. Native build: the
+# system Lua 5.4 (dynamic linking is fine when built and run on the same box).
+lua_static = cpp.find_library('lua', dirs: ['/usr/local/lib'], static: true, required: false)
+if lua_static.found()
+ m_dep = cpp.find_library('m', required: false)
+ dl_dep = cpp.find_library('dl', required: false)
+ lua_dep = declare_dependency(dependencies: [lua_static, m_dep, dl_dep])
+else
+ lua_dep = dependency('lua5.4', 'lua54', 'lua-5.4', required: false)
+ if not lua_dep.found()
+ lua_dep = dependency('lua', required: true)
+ endif
+endif
-# sol2 -- Lua C++ bindings (header-only, installed to /usr/local/include)
-sol2_dep = declare_dependency(include_directories: '/usr/local/include')
+# Box2D v3 -- physics. Idle for now, but physics.cpp compiles against its header.
+# Try /usr/local (container), then pkg-config/cmake, then a plain system lib.
+box2d_dep = cpp.find_library('box2d', dirs: usr_local_libdirs, required: false)
+if not box2d_dep.found()
+ box2d_dep = dependency('box2d', required: false)
+endif
+if not box2d_dep.found()
+ box2d_dep = cpp.find_library('box2d', required: true)
+endif
-# EnTT -- ECS (header-only, installed to /usr/local/include)
-entt_dep = declare_dependency(include_directories: '/usr/local/include')
+# --- Declared but not yet wired up ------------------------------------------
+# bgfx/SoLoud/ENet/EnTT belong to the planned stack, but no source file
+# #includes them yet (the live renderer is SDL_Renderer; physics/audio/net are
+# idle). Link them only if a source build has provided them; a stock system
+# build simply skips them. Promote each to a real dependency when it is wired up.
+optional_deps = []
+foreach libname : ['bgfx', 'bimg', 'bx', 'soloud', 'enet']
+ found = cpp.find_library(libname, dirs: usr_local_libdirs, required: false)
+ if found.found()
+ optional_deps += found
+ endif
+endforeach
# ImGui -- skipped for M0, added when debug UI is needed
@@ -96,18 +126,14 @@ engine_src = [
engine_deps = [
sdl3_dep,
- bgfx_dep, bimg_dep, bx_dep,
box2d_dep,
- soloud_dep,
- enet_dep,
lua_dep,
glm_dep,
sol2_dep,
- entt_dep,
imgui_dep,
-]
+] + optional_deps
-extra_inc = [local_inc, stb_inc]
+extra_inc = local_incs + [stb_inc]
# Required by bx/bgfx (normally set by cmake targets; we need them manually
# since we link raw .a files).
diff --git a/src/engine/renderer.cpp b/src/engine/renderer.cpp
index 0be0e53..0ec6371 100644
--- a/src/engine/renderer.cpp
+++ b/src/engine/renderer.cpp
@@ -10,6 +10,35 @@
#include <cmath>
#include <vector>
#include <algorithm>
+#include <string>
+#include <filesystem>
+
+namespace {
+
+// Root of a bring-your-own Forts install, used to resolve "forts:REL" texture
+// paths. Tries known developer install locations first, then the FORTS_DATA
+// override, and returns the first that exists on disk. Resolved once, cached.
+const std::string& forts_data_root() {
+ static const std::string root = [] {
+ std::vector<std::string> candidates = {
+ "/opt/steam/steamapps/common/Forts/data", // Emil
+ "/mnt/games/SteamLibrary/steamapps/common/Forts/data", // Vaino
+ };
+ if (const char* env = std::getenv("FORTS_DATA")) {
+ candidates.emplace_back(env);
+ }
+ for (const auto& c : candidates) {
+ std::error_code ec;
+ if (std::filesystem::is_directory(c, ec)) return c;
+ }
+ // None present; return the override if set, else the first guess, so the
+ // failed-load message at least points somewhere meaningful.
+ return candidates.back();
+ }();
+ return root;
+}
+
+} // namespace
bool Renderer::init(SDL_Window* window, int width, int height) {
width_ = width;
@@ -140,10 +169,7 @@ SDL_Texture* Renderer::load_texture(const std::string& path) {
// files, like a Doom source port + WAD). Nothing is copied into the repo.
std::string file = path;
if (path.rfind("forts:", 0) == 0) {
- const char* env = std::getenv("FORTS_DATA");
- std::string root = env ? env
- : "/mnt/games/SteamLibrary/steamapps/common/Forts/data";
- file = root + "/" + path.substr(6);
+ file = forts_data_root() + "/" + path.substr(6);
}
int w = 0, h = 0, n = 0;