fix(rendering): restore retail terrain texture tiling
Carry TerrainTex.TexTiling through the terrain atlas and upload a layer-indexed table to the modern bindless shader so base, overlay, and road textures repeat at retail scale. Keep alpha masks cell-scaled and preserve retail's verified source-level-zero high-detail selection. Add the named-retail pseudocode, WorldBuilder/ACE/ACME cross-reference, adapter conformance tests, and inventory documentation so a future renderer migration keeps the contract. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
84b7d2d7cd
commit
bb5acab9e6
7 changed files with 297 additions and 7 deletions
|
|
@ -153,6 +153,15 @@ use from WB rather than re-implement.
|
|||
| `TextureAtlasManager` | Texture atlas builder |
|
||||
| `VertexLandscape` | Terrain vertex format |
|
||||
|
||||
**Modern terrain adapter:** acdream's bindless path uses `TerrainAtlas` plus
|
||||
`TerrainModernRenderer` rather than WB's draw manager, but retains
|
||||
`LandSurfaceManager`'s layer-indexed `TerrainTex.TexTiling` contract. The
|
||||
36-entry table is uploaded to `terrain_modern.frag`; base, overlay, and road
|
||||
layers each use their owning repeat count while alpha masks stay at cell scale.
|
||||
Retail `TexMerge::CopyAndTile` (`0x00503580`) and `TexMerge::Merge`
|
||||
(`0x005038C0`) are the behavior oracle; see
|
||||
`docs/research/2026-07-13-retail-terrain-texture-tiling-pseudocode.md`.
|
||||
|
||||
### Scenery (procedural placement: trees, bushes, rocks, fences)
|
||||
|
||||
| Component | What it does |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
# Retail terrain texture tiling
|
||||
|
||||
## Question
|
||||
|
||||
Why do acdream's grass, dirt, and road textures look lower-detail than the
|
||||
September 2013 retail client even though the modern terrain atlas loads 512 x
|
||||
512 source images, generates mipmaps, and enables anisotropic filtering?
|
||||
|
||||
## Retail oracle
|
||||
|
||||
Named retail symbols in
|
||||
`docs/research/named-retail/acclient_2013_pseudo_c.txt`:
|
||||
|
||||
- `TexMerge::CopyAndTile` at `0x00503580`
|
||||
- `TexMerge::Merge` at `0x005038C0`
|
||||
- `ImgTex::TileCSI` at `0x0053E740`
|
||||
- `ImgTex::GetSurfaceDID` at `0x0053F0E0`
|
||||
- `Render::ShouldDropHighDetail` at `0x0054C700`
|
||||
|
||||
The verbatim `TerrainTex` declaration in
|
||||
`docs/research/named-retail/acclient.h` contains `unsigned int tex_tiling`.
|
||||
|
||||
## Readable pseudocode
|
||||
|
||||
```text
|
||||
CopyAndTile(destination, destinationSize, terrainTexture):
|
||||
baseTexture = terrainTexture.base_texture
|
||||
if baseTexture is null:
|
||||
if terrainTexture.tex_gid is valid:
|
||||
terrainTexture.base_texture = load ImgTex(terrainTexture.tex_gid)
|
||||
baseTexture = terrainTexture.base_texture
|
||||
|
||||
if baseTexture is null:
|
||||
CopyCSI(destination, destinationSize, destinationSize, null, 1)
|
||||
return false
|
||||
|
||||
CopyCSI(
|
||||
destination,
|
||||
destinationSize,
|
||||
destinationSize,
|
||||
baseTexture,
|
||||
terrainTexture.tex_tiling)
|
||||
return true
|
||||
|
||||
Merge(destination, destinationSize, alphaTexture, rotation, terrainTexture):
|
||||
if alphaTexture is null:
|
||||
return false
|
||||
|
||||
if terrainTexture.base_texture is null:
|
||||
if not terrainTexture.InitEnd():
|
||||
return false
|
||||
|
||||
MergeTexture(
|
||||
destination,
|
||||
destinationSize,
|
||||
destinationSize,
|
||||
terrainTexture.base_texture,
|
||||
terrainTexture.tex_tiling,
|
||||
alphaTexture,
|
||||
rotation)
|
||||
return true
|
||||
|
||||
GetSurfaceDID(surfaceTexture):
|
||||
if surfaceTexture has one source level:
|
||||
return sourceLevels[0]
|
||||
if surfaceTexture has two source levels:
|
||||
if ShouldDropHighDetail():
|
||||
return sourceLevels[1]
|
||||
return sourceLevels[0]
|
||||
report invalid source-level count
|
||||
```
|
||||
|
||||
`ImgTex::TileCSI` repeats the source image `tex_tiling` times in both axes.
|
||||
`TexMerge::Merge` applies the same repeat count to every terrain layer before
|
||||
the cell-scale alpha mask is merged. Therefore the modern shader must multiply
|
||||
the terrain UV independently for the base layer, each overlay layer, and each
|
||||
road layer. The alpha-map UV must not be multiplied.
|
||||
|
||||
The source image selection is a separate policy. Retail uses source level zero
|
||||
unless its quality policy explicitly drops high detail, so changing acdream's
|
||||
atlas from `SurfaceTexture.Textures[0]` would invert the verified retail rule.
|
||||
|
||||
## Cross-references
|
||||
|
||||
- Extracted WorldBuilder lineage: `LandSurfaceManager` stores a 36-entry
|
||||
layer-indexed tiling table populated from `TerrainTex.TexTiling`, and its
|
||||
terrain shader multiplies each texture layer's UV by that value. The pinned
|
||||
WorldBuilder change introducing the path is commit `0d1405af65`.
|
||||
- ACE: `ACE.Server/Physics/Common/TexMerge.cs` forwards
|
||||
`TerrainTex.TexTiling` from both `CopyAndTile` and `Merge`; `ImgTex.cs`
|
||||
resolves the normal source as `Textures[0]`.
|
||||
- ACME-derived `tests/AcDream.Core.Tests/Terrain/ClientReference.cs` covers
|
||||
terrain split, pal/road codes, and blend selection, but not image tiling or
|
||||
source-level selection. It supplies no competing behavior for this path.
|
||||
|
||||
Retail remains the oracle where a reference differs: the pinned WorldBuilder
|
||||
reader selects the last surface source, while retail's `GetSurfaceDID` proves
|
||||
that the normal/high-detail source is index zero.
|
||||
|
||||
## acdream port shape
|
||||
|
||||
1. Capture `TerrainTex.TexTiling` while `TerrainAtlas` assigns each terrain
|
||||
type to an array layer.
|
||||
2. Convert the type-keyed values into the renderer's 36-entry layer-indexed
|
||||
uniform table. Unused layers default to `1.0`; mapped values are preserved
|
||||
verbatim.
|
||||
3. Upload the table after binding `terrain_modern.frag`.
|
||||
4. Sample base, overlay, and road textures with
|
||||
`cellUv * uTexTiling[layer]`; sample alpha masks with their original UV.
|
||||
Loading…
Add table
Add a link
Reference in a new issue