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:
Erik 2026-07-13 21:04:22 +02:00
parent 84b7d2d7cd
commit bb5acab9e6
7 changed files with 297 additions and 7 deletions

View file

@ -29,6 +29,7 @@ out vec4 fragColor;
uniform uvec2 uTerrainHandle;
uniform uvec2 uAlphaHandle;
uniform float uTexTiling[36];
#define uTerrain sampler2DArray(uTerrainHandle)
#define uAlpha sampler2DArray(uAlphaHandle)
@ -46,7 +47,12 @@ layout(std140, binding = 1) uniform SceneLighting {
vec4 uCameraAndTime;
};
const float TILE = 1.0;
// Retail TexMerge::CopyAndTile (0x00503580) and TexMerge::Merge
// (0x005038C0) pass TerrainTex::tex_tiling to every terrain source before
// the cell-scale alpha mask is applied. The atlas stores that value by layer.
float terrainTiling(float layer) {
return uTexTiling[int(layer)];
}
vec4 maskBlend3(vec4 t0, vec4 t1, vec4 t2, float h0, float h1, float h2) {
float a0 = h0 == 0.0 ? 1.0 : t0.a;
@ -68,21 +74,21 @@ vec4 combineOverlays(vec2 baseUV, vec4 pOverlay0, vec4 pOverlay1, vec4 pOverlay2
vec4 t0 = vec4(0.0), t1 = vec4(0.0), t2 = vec4(0.0);
if (h0 > 0.0) {
t0 = texture(uTerrain, vec3(baseUV * TILE, pOverlay0.z));
t0 = texture(uTerrain, vec3(baseUV * terrainTiling(pOverlay0.z), pOverlay0.z));
if (pOverlay0.w >= 0.0) {
vec4 a = texture(uAlpha, vec3(pOverlay0.xy, pOverlay0.w));
t0.a = a.a;
}
}
if (h1 > 0.0) {
t1 = texture(uTerrain, vec3(baseUV * TILE, pOverlay1.z));
t1 = texture(uTerrain, vec3(baseUV * terrainTiling(pOverlay1.z), pOverlay1.z));
if (pOverlay1.w >= 0.0) {
vec4 a = texture(uAlpha, vec3(pOverlay1.xy, pOverlay1.w));
t1.a = a.a;
}
}
if (h2 > 0.0) {
t2 = texture(uTerrain, vec3(baseUV * TILE, pOverlay2.z));
t2 = texture(uTerrain, vec3(baseUV * terrainTiling(pOverlay2.z), pOverlay2.z));
if (pOverlay2.w >= 0.0) {
vec4 a = texture(uAlpha, vec3(pOverlay2.xy, pOverlay2.w));
t2.a = a.a;
@ -96,7 +102,7 @@ vec4 combineRoad(vec2 baseUV, vec4 pRoad0, vec4 pRoad1) {
float h1 = pRoad1.z < 0.0 ? 0.0 : 1.0;
vec4 result = vec4(0.0);
if (h0 > 0.0) {
result = texture(uTerrain, vec3(baseUV * TILE, pRoad0.z));
result = texture(uTerrain, vec3(baseUV * terrainTiling(pRoad0.z), pRoad0.z));
if (pRoad0.w >= 0.0) {
vec4 a0 = texture(uAlpha, vec3(pRoad0.xy, pRoad0.w));
result.a = 1.0 - a0.a;
@ -123,7 +129,7 @@ vec3 applyFog(vec3 lit, vec3 worldPos) {
void main() {
vec4 baseColor = vec4(0.0);
if (vBaseTexIdx >= 0.0) {
baseColor = texture(uTerrain, vec3(vBaseUV * TILE, vBaseTexIdx));
baseColor = texture(uTerrain, vec3(vBaseUV * terrainTiling(vBaseTexIdx), vBaseTexIdx));
}
vec4 overlays = vec4(0.0);