v0.3.0: display abstraction, map-browser menu, SAGE terrain tiling/blends

- ra3.client::display: shared interactive loops; SDL and Vulkan backends
  implement only the primitives (init/present/poll_event/window_size/
  key_down/present_terrain). ra3.display picks the backend.
- Menu: maps by localized name (gamestrings.csf), red/gold theme, hover
  highlight, mouse + keyboard, wheel scroll, fullscreen and FPS/vsync
  options, loading progress bar.
- Terrain: continuous tile sampling via a texture array (REPEAT, uv =
  cell/(2*cellSize)) removes per-cell grid seams; SAGE blend ramp for
  material transitions; FPS label + top-right minimap overlays.
- Skip the skirmish sim for map views; reuse the Vulkan texture; no idle
  terrain redraw.
This commit is contained in:
EnderTheCoder
2026-09-20 02:20:39 +08:00
parent 702b4e29aa
commit b6d619254d
66 changed files with 37661 additions and 406 deletions
+86 -26
View File
@@ -18,13 +18,21 @@ Ghidra.
## Status
OpenRA3 is at **v0.2.0**. The engine compiles and runs headless, and a **minimal
OpenRA3 is at **v0.3.0**. The engine compiles and runs headless, and a **minimal
skirmish** is playable: it reads a real multiplayer map out of your install,
recovers the player start waypoints, and simulates two sides earning credits,
training units and fighting until one base falls. The map and match state can be
**rendered** to a window (SDL3) or to an image. The whole tree builds for
**Linux** (clang + libc++) and cross-compiles to **Windows** (`openra3.exe` +
`SDL3.dll`) with llvm-mingw — both using C++26 modules and `import std;`.
recovers the player start waypoints, and simulates two sides building a base,
extracting ore and fighting until one side is wiped out. The balance is the
**retail Red Alert 3 balance**, pinned in `ra3.data` from EA's open RA3 XML:
damage types, `ArmorTemplate` percentages, weapon target masks, build costs and
the ore economy. Presentation is a **Vulkan** backend (`ra3.vulkan`) with a null
fallback; the software renderer still produces headless images. Terrain tiles
cross-fade the way the retail `Terrain.fx` does (a per-cell blend ramp plus a
gutter-padded atlas), so material boundaries are smooth instead of a grid of
hard lines. An in-window **menu** lists the maps by their localized name and
exposes every render/skirmish option for tweaking before launch. The whole tree
builds for **Linux** (clang + libc++) and cross-compiles to **Windows**
(`openra3.exe` + `SDL3.dll`) with llvm-mingw — both using C++26 modules and
`import std;`.
```text
$ openra3 skirmish --game-dir "/game" --map map_mp_2_feasel4
@@ -32,9 +40,9 @@ OpenRA3 skirmish map=map_mp_2_feasel4 source=archive:map_mp_2_feasel4 seed=1
start positions:
P0 (1338, 1940)
P1 (1291, 1404)
result: decided winner=0 frames=3397 (113.2 s)
P0 Commander Allied money= 129 units=19 kills=21 losses=3
P1 AI Soviet money= 840 units= 0 kills=3 losses=21
result: decided winner=0 frames=9293 (309.8 s)
P0 Commander Allied money= 700 units=15 kills=21 losses=15
P1 AI Soviet money= 376 units= 0 kills=15 losses=21
```
## Offline only
@@ -47,15 +55,25 @@ tested without any external service.
## Assets
The engine reads the retail game from your local install:
The runtime reads a portable `assets/` folder **next to the executable** — there
is no `--game-dir` at run time. The build extracts it from your install:
- default path `C:\Red Alert 3`, or `--game-dir DIR`, or `$RA3_GAME_DIR`;
- `Data\MapsMultiplayer.big` is read for the map catalog;
- maps are unpacked from `BIG4` + RefPack and their start waypoints recovered.
- `openra3_assets` (build target) runs `cmake/extract_assets.cmake`, which
- extracts every map (double-unwrapped `CkMp`) and terrain TGA into `assets/`
via the engine's own `openra3 extract`;
- with `-DOPENRA3_EXTRACT_ALL=ON` (default) also dumps **every** `.big` entry
plus models, textures (`.png`), sound effects/voice and movie audio via the
[`ra3-headless/ra3tools`](https://github.com/) scripts.
- CMake cache vars: `RA3_GAME_DIR` (default `C:/Red Alert 3`), `RA3TOOLS_DIR`
(the `ra3tools` scripts), `PYTHON_EXECUTABLE`.
- The step is skipped when the exe cannot run on the build host (a Windows
cross-build in a Linux container); the exe then extracts maps/terrain on first
launch, and the full dump can be run explicitly:
`cmake --build <build> --target openra3_assets` on a native host, or
`openra3 extract --game-dir DIR --out DIR`.
**No game data is committed or distributed.** `reference/` (the GPLv3 Generals
source) and any extracted assets are git-ignored; `tools/fetch_reference.sh`
fetches the former on demand. Without an install, the engine falls back to a
**No game data is committed or distributed.** `reference/`, `assets/` and the
ra3tools checkout are git-ignored. Without an install the engine falls back to a
built-in test map so the project still builds and runs in CI.
## Layout
@@ -64,15 +82,19 @@ built-in test map so the project still builds and runs in CI.
| --- | --- |
| `src/core/ra3.core.cppm` | fundamental types, math, strings, random, message stream |
| `src/logic/ra3.logic.cppm` | objects, players, teams, spatial partition, game loop |
| `src/client/ra3.client.cppm` | display abstraction + client facade / frame loop |
| `src/client/ra3.client.cppm` | `display` abstraction + shared interactive loops + client facade |
| `src/display/ra3.display.cppm` | picks the backend (Vulkan, then SDL) for the app |
| `src/game/ra3.game.cppm` | RA3 sides, player templates, skirmish defaults |
| `src/data/ra3.data.cppm` | retail RA3 balance: damage types, armour, weapons, units, economy |
| `src/fs/ra3.fs.cppm` | `BIG4` archives, RefPack codec, local install locator |
| `src/map/ra3.map.cppm` | map catalog, `EAR`/RefPack unwrap, start waypoints |
| `src/skirmish/ra3.skirmish.cppm` | units, economy, AI, combat, win condition |
| `src/render/ra3.render.cppm` | ARGB framebuffer, TGA decode, BMP encode, map compositing |
| `src/ui/ra3.ui.*.cppm` | SDL3 window viewer (null backend when SDL3 is absent) |
| `src/map/ra3.map.cppm` | map catalog, `EAR`/RefPack unwrap, start waypoints, `gamestrings.csf` display names |
| `src/skirmish/ra3.skirmish.cppm` | base building, economy, AI, combat, win condition |
| `src/terrain/ra3.terrain.cppm` | `CkMp` terrain: `HeightMapData`, `BlendTileData` (tiles + blends), `Terrain.big` tiles |
| `src/render/ra3.render.cppm` | ARGB framebuffer, TGA decode, BMP encode, map compositing, bitmap-font text |
| `src/ui/ra3.ui.*.cppm` | SDL3 window viewer and menu (null backend when SDL3 is absent) |
| `src/vulkan/ra3.vulkan.*.cppm` | Vulkan presentation backend and menu (null fallback without a loader) |
| `src/ra3.cppm` | umbrella module re-exporting the SDK |
| `apps/openra3/main.cpp` | `maps` / `skirmish` CLI |
| `apps/openra3/main.cpp` | `menu` / `maps` / `skirmish` / `render` CLI |
| `tests/ra3_tests.cpp` | smoke tests (run via `ctest`) |
| `tools/` | reference fetch + Ghidra-driven reconstruction helpers |
| `docs/` | architecture, reverse-engineering notes, roadmap |
@@ -122,6 +144,18 @@ docker run --rm -v "$PWD:/work" -w /work openra3-windows:local cmake --build bui
Behind a slow or blocked mirror, pass `--build-arg APT_MIRROR=<url>`.
The two images are deliberately separate so the Linux (clang + libc++) and
Windows (llvm-mingw + MinGW SDL3) toolchains never interfere. The Windows demo
is `build/windows/bin/openra3.exe` next to `SDL3.dll`:
```powershell
# show a real map in a Vulkan window
openra3.exe render --game-dir "C:\Red Alert 3" --vulkan
```
Vulkan is provided by **vendored volk + headers** (`third_party/`), resolved at
runtime, so neither image needs a Vulkan SDK.
## Running a skirmish
```bash
@@ -140,10 +174,33 @@ it from `cmd`/PowerShell, e.g. `openra3.exe skirmish --game-dir "C:\Red Alert 3"
`--frames N` caps the simulation length (default 15 minutes of game time at
30 Hz). The result is deterministic for a given map and seed.
## Menu
Run `openra3` with no arguments (or `openra3 menu`) to open a window listing the
maps by their **localized display name** (read from the install's
`gamestrings.csf`, e.g. `map_mp_2_feasel4` → "Battlebase Beta") with their id
below. The `Options` column exposes every `render`/`skirmish` parameter — mode
(3D terrain / top-down / skirmish), window size, camera pitch/yaw/height, FOV,
zoom, terrain scale, terrain pitch, world size, seed, frame cap, the overview
thumbnail toggle and a BMP output path. `Up`/`Down` selects, `Left`/`Right`
changes a value (or moves the text caret on the BMP field), `Tab` switches
between the map list and the options, `Enter` starts and `Esc` quits. Starting a
3D or top-down view opens the viewer; closing it returns to the menu. When no
window backend is available the same flow falls back to a console picker, and
`openra3 menu-preview` renders one menu frame to a BMP for inspection.
## Rendering the map
The renderer draws the map's own overview art, a world grid and the match state
(start markers in yellow, player 0 in blue, player 1 in red).
`render` draws the **real terrain**: it parses the map's `HeightMapData`
(elevation grid) and `BlendTileData` (per-cell tile index plus the per-cell
`Blends`/`ThreeWayBlends` and their `BlendDescription`s), loads the tile
textures from `Data\Terrain.big` (RefPack + TGA), and rasterises the map with an
elevation shade. Each cell cross-fades into its blend neighbour with the same
linear ramp the retail `Terrain.fx` uses, so material transitions are smooth; on
the GPU path the tile atlas is padded with a replicated gutter so filtering
never bleeds between tiles. Match state is overlaid (start markers in yellow,
player 0 in blue, player 1 in red). `--thumbnail` uses the old `<map>_art.tga`
overview instead.
**Offscreen image** (works anywhere, no display needed):
@@ -155,8 +212,11 @@ docker run --rm -v "/path/to/Red Alert 3:/game:ro" -v "$PWD/out:/out" openra3-li
On Windows the same command runs natively: `openra3.exe render --game-dir
"C:\Red Alert 3" --map map_mp_2_feasel4` opens an SDL3 window.
**Interactive window** (SDL3; drag to pan, wheel to zoom, Esc to quit). In a
container you need an X server on the host — on Windows run
**Interactive window** (Vulkan with `--vulkan`, otherwise SDL3). The camera
follows the retail tactical view: it opens centred on the first player's start,
the wheel zooms, pushing the cursor against a screen edge scrolls, dragging with
the left button pans, and Esc quits. `--zoom Z` sets the initial zoom (1 fits
the whole map). In a container you need an X server on the host — on Windows run
[VcXsrv](https://sourceforge.net/projects/vcxsrv/) and launch it with "Disable
access control", then: