fix(core): ACME cross-check fixes — normals, placement, scenery

Four fixes from the ACME StaticObjectManager cross-reference:

1. GfxObjMesh: normalize vertex normals (1d). Dat normals may not be
   unit-length; without normalization, lighting is wrong per-vertex.

2. SetupMesh: add third-fallback placement frame (2a). If neither
   Resting nor Default exists, use the first available frame from
   PlacementFrames. Matches ACME's GetDefaultPlacementFrame.

3. SceneryGenerator: building cell exclusion (4d). Compute which
   terrain vertices have buildings (from LandBlockInfo.Objects +
   Buildings), skip scenery spawns in those cells. Prevents trees
   from spawning inside building footprints.

4. SceneryGenerator: slope filter (4e). Compute terrain normal Z at
   each displaced position and check against ObjectDesc.MinSlope /
   MaxSlope bounds. Prevents trees from spawning on cliff faces.

Also confirmed 4f (scenery Z=0) is NOT a bug — GameWindow's hydrator
lifts scenery to terrain Z at line 1213. The Z=0 in SceneryGenerator
is a placeholder correctly overridden at render time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-12 22:52:08 +02:00
parent 05749f52e0
commit 9d4967a461
4 changed files with 61 additions and 7 deletions

View file

@ -139,7 +139,7 @@ public static class GfxObjMesh
// culling disabled the shader still samples this normal
// for the diffuse term so getting it right matters
// regardless of backface state.
var normal = isNeg ? -sw.Normal : sw.Normal;
var normal = System.Numerics.Vector3.Normalize(isNeg ? -sw.Normal : sw.Normal);
var key = (posIdx, uvIdx, isNeg);
if (!bucket.Dedupe.TryGetValue(key, out var outIdx))