render map objects: decode compiled W3DMesh art, skin and draw over terrain

Read the buildings and props a map places from its compiled art (the
uncompressed worldbuilder stream + embedded DDS textures):

- ra3.models: BAB asset-stream parser (lazy slices), W3DMesh + D3DHierarchy
  decode, DDS (DXT1/3/5 + uncompressed) decode, per-mesh bone remap and
  bind-pose single-joint skinning, flattened to one world-space triangle soup.
- ra3.map: parse the ObjectsList chunk into (type, x, y, z, angle).
- ra3.terrain: render3d rasterises the scene over the raymarched terrain with
  a z-buffer; gpu_terrain carries the scene for the GPU backends.
- ra3.vulkan: second pipeline + depth attachment, terrain.frag writes
  gl_FragDepth, and a small depth bias keeps ground decals from z-fighting.
- objects.fx shaders (compiled to SPIR-V), embedded like scene/terrain.

Only opaque parts are drawn: FX-light billboards (DefaultW3D.fx / BasicW3D.fx)
and damage-fill shells (BuildingsGenericDamageFill.fx) are skipped, since the
latter paint the wrecked interior (e.g. orange CBBuilding_Wood) over the shell.

Ground-decal meshes with no diffuse role and the Road templates themselves are
still not drawn.
This commit is contained in:
EnderTheCoder
2026-09-29 18:08:11 +08:00
parent 6e5df350c7
commit 70f35d8382
18 changed files with 2027 additions and 45 deletions
+22 -5
View File
@@ -18,7 +18,7 @@ Ghidra.
## Status
OpenRA3 is at **v0.6.0**. The engine compiles and runs headless, and a **minimal
OpenRA3 is at **v0.7.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 building a base,
extracting ore and fighting until one side is wiped out. The balance is the
@@ -31,7 +31,10 @@ software blit and null fallbacks; the backend is selectable from the in-game men
and 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
hard lines; the **buildings and props a map places** are decoded from the retail
compiled `W3DMesh` art (the uncompressed `worldbuilder.bin` stream and its
embedded DDS textures) and drawn with a depth test over the terrain. 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
@@ -93,6 +96,7 @@ built-in test map so the project still builds and runs in CI.
| `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/models/ra3.models.cppm` | compiled `W3DMesh` art (BAB stream + DDS textures) for the objects a map places |
| `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) |
@@ -290,9 +294,22 @@ 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.
never bleeds between tiles. It also draws the **buildings and props the map
places**: the `ObjectsList` chunk is decoded into `(type, position, angle)`
instances, the type resolves to its compiled `W3DMesh` parts in the map's art
stream (`Data\WBData.big`'s uncompressed `worldbuilder.bin`), the embedded DDS
textures are decoded, bind-pose skinning places each mesh's bone-space vertices
(`W3DHierarchy`), and the scene is flattened into one world-space triangle soup.
The terrain raymarcher writes a depth value so the model pass depth-tests
against the relief; the GPU path draws it in a second pipeline (shared camera
basis, with a small depth bias so ground decals do not z-fight) and the software
path rasterises it with a z-buffer. 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.
> **Not yet drawn:** the map's `Road` objects (the sidewalk/road segments that
> reference a `Road` template rather than a mesh) need the retail road-network
> mesher and are skipped; they are reported in the `objects: … missing` count.
**Offscreen image** (works anywhere, no display needed):