Original Phase 3a constants had SUN_DIR=(0.4,0.3,0.8) — heavily
vertical, so roofs and ground both landed near peak brightness and
only walls dropped. Combined with AMBIENT=0.4/DIFFUSE=0.6 the lit-vs-
shadow contrast was ~2.2x, which was real but hard to read through
textures. User feedback: "Ligtning looks the same I think."
Diagnosed with a temporary grayscale-lighting fragment output — walls
on different sides of the same building did show different brightness,
confirming the Phase 3a/3b pipeline is wired correctly end-to-end and
the issue was purely perceptual contrast.
Retuned: SUN_DIR=(0.5,0.4,0.6) (more oblique), AMBIENT=0.25, DIFFUSE=0.75.
Contrast ratio now ~3.3x. User-verified visually.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a hardcoded sun direction + ambient + Lambert diffuse to both
terrain.frag and mesh.frag. Both vertex shaders now forward a world-
space normal (computed as mat3(uModel) * aNormal) for the fragment
shader to dot against the sun vector.
Lighting model:
final_rgb = texture_rgb * (AMBIENT + DIFFUSE * max(0, dot(N, SUN)))
where AMBIENT=0.4, DIFFUSE=0.6, SUN=normalize(0.4,0.3,0.8).
Building walls facing the sun light up, walls in shadow dim to ~40%.
Scenery (trees, bushes, rocks) with real per-vertex normals from SWVertex
shades naturally. Terrain currently uses flat UnitZ normals so every
terrain fragment gets the same contribution — terrain will look a bit
washed out compared to real AC until a Phase 3b pass computes per-vertex
landblock normals from the heightmap.
Non-uniform scale (from scenery's random scale baked into MeshRef
PartTransform) would technically require the inverse-transpose for
correct normals, but scenery uses uniform scale so mat3(uModel) is
good enough. Flagging as a known Phase 3+ concern if nonuniform scale
ever shows up.
Build clean, runtime clean: 1133 entities hydrated, no shader compile
errors, process runs through startup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses the 'doors, windows, and alpha-keyed parts render bright
pink' issue the user observed after the Phase 2a visual checkpoint.
SurfaceDecoder gains a second overload taking an optional Palette
parameter. When the render surface format is PFID_INDEX16 and a
palette is supplied, each 16-bit value in SourceData is treated as
an index into Palette.Colors (a List<ColorARGB>) and the corresponding
ARGB color's channels are written to the output buffer. The original
no-palette overload is preserved so the Task 3 unit tests that
confirm INDEX16 -> magenta fallback still describe their behavior
correctly (INDEX16 without a palette still returns magenta).
TextureCache now resolves the RenderSurface's DefaultPaletteId via
the dats and passes the resulting Palette (or null) to the decoder.
mesh.frag adds an alpha cutout: fragments with sampled alpha < 0.5
are discarded. Without this, transparent regions of alpha-keyed
textures (doors, windows, foliage cutouts) would render as opaque
rectangles using the texture's background color. This is the
standard alpha-tested approach, simpler than full alpha blending
and matches how AC's original client rendered these surfaces.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a 2-stage GLSL shader (vertex + fragment), a Shader helper that
compiles/links and exposes SetMatrix4 for uniforms, and an OrbitCamera
with yaw/pitch/distance and a 192-unit-centered target for a single
landblock. TerrainRenderer now takes a Shader and issues an actual
DrawElements call with uView + uProjection uniforms. GameWindow owns
the Shader and Camera, routes mouse drag to camera yaw/pitch, and
scroll wheel to camera distance.
The fragment shader maps world Z to a green-brown-white ramp so
lowlands read green, midlands brown, and peaks white — no textures
yet, but enough to visually confirm the terrain shape.
Shaders are copied to the output dir via a <None Update> item group.
Smoke verified against real dats: process stays alive with no GL
errors, no shader compile/link failures, and no exception trail.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>