Roadside scanned rock placement
2026-09-16: rejected experiment — ascent scans alongside the terrain overlay
The native Docs/QA/20260916-003637-campaign/alps-02-pass0-view1.png still shows a large planar rock surface with a hard green boundary. Its player log reports 1 scanned instance and 25 omitted by HasCoherentRoadCut. That helper measures at least 80 square metres of weighted overlay triangles in a 32-metre road section. It does not test the rendered transition or geometric rock detail; therefore its coverage result did not justify replacing the 3D scans.
The earlier native control at Docs/QA/20260915-202407-campaign/player.log established road-facing alignment approximately 1, 70/88 exposed road-facing samples, and metre-scale bounds. The bounded experiment restored the original ascent (stage==1) scan sequence beside the overlay. It retained scale, LOD, orientation and full-route footprint clearance, while seating the downhill foot using SceneryGroundHeight and the existing 0.35-metre embed. It introduced no scan collider, road edit or terrain edit.
The experiment was visually rejected and reverted after the technically passing 1/1 Alps02-only native run. Docs/QA/20260916-005104-campaign/player.log confirms 26 scanned instances, 0 clearance rejections, 0 overlay replacements, 25 scans alongside terrain cuts. The actual Docs/QA/20260916-005104-campaign/alps-02-pass0-view1.png was inspected: the scans appear as detached, diagonal open rock flaps floating above or emerging from the flat green toe. Correct orientation and vertex exposure do not establish a convincing fit to this terrain. The large planar surface and hard green boundary also remain unresolved.
Only this final RockCuts experiment was rolled back: the omission guard, original GroundHeight foot seating and original logging are restored to the 00:36 state. The seven authored valley trees, summer grass and summer forest colour correction remain in place. No further art variant or full test round is introduced by this rollback. The retained intermediate terrain treatment is not a claim of final photorealistic acceptance.
The true frame Docs/QA/20260915-165917-campaign/alps-02-pass0-view1.png was viewed. At roughly 48% of the ascent, its left bank is a smooth large mapped slope; no convincing scanned cut is visible despite RockCuts covering 32–66% of the stage. This remains a native visual rejection, not evidence that the asset is missing.
A bounded CPU-only Blender import audit inspected the actual delivered RockFace_LOD1.fbx, without rendering, saving a Blender scene or starting Unity. ArtSource/Alps/audit_rock_fbx.py reproduces the audit; rock-fbx-audit.json retains the resulting vertex geometry and transforms. The imported metric bounds in Unity's authored coordinate convention are:
- X: −3.55124 to +3.55412m.
- Y: −0.00692 to +4.98471m.
- Z: −2.81548 to +2.81483m.
- Object scale in the CPU Blender reimport: 1,1,1; no metre/centimetre or displaced-root error in that reimport. Unity's importer can produce a different root transform and requires its own native evidence.
The previous placement used an arbitrary centre 18–32m uphill, with its height taken at the centre and then lowered 0.6m. The scaled source is only about 5.5–9m tall. That rule does not place its exposed face at the near road bank and can leave the foreground smooth slope unobstructed. The CPU audit found no obvious source-scale/root problem in Blender; it does not rule out a Unity root-transform difference or prove a GPU visibility or backface cause.
The minimal correction changes only ChapterScenery.RockCuts:
- Calculate a conservative horizontal footprint radius from the verified source bounds and the existing per-instance scale:
sqrt((3.555 × scaleX)^2 + (2.816 × scaleZ)^2). - Place the centre at
8m + radius + 0.9mfrom the sampled road centre toward the uphill side. Require the existing full-routeClear(position, radius)check, retaining thedistance > 8m + radiusrule. - Set the base height from the actual ground at the downhill/road-facing part of its footprint and embed by 0.35m. This seats the visible face near the road bank while the uphill rear intersects the existing slope.
The existing mesh, yaw convention, scale range, LOD selection and number of rock placements remain unchanged. No collider is added; no terrain, road, physics or SnowBanks code changes. The correction is a justified placement improvement, with native screenshot acceptance still pending.
Managed compilation passed after the change: 91 Runtime + 20 Editor files, zero warnings/errors. Assets were then frozen for Root's shared native build.
Follow-up after the 18:04 Windows build
Docs/QA/20260915-180949-campaign/alps-02-pass0-view1.png was viewed and still fails: the near left bank is smooth green with no convincing scanned face. Therefore the earlier placement change is not considered an artistic fix.
ArtSource/Alps/audit_rock_exposure.py checks the audited LOD1 vertex geometry against the unchanged resampled Alps02 polyline and the production road-to-DEM grading formula. It is a CPU approximation without the precise native bank and terrain triangles. Around d=1959.939m (47.806%) and d=2013.939m (49.123%), across the scale range, approximately 80–84% of all sampled source vertices lie above the graded ground; about 49–58% of the road-facing half does. Maximum exposed heights are approximately 4.8–7.8m. This does not support another blind whole-object lift. The original scan is an open face with its visible area concentrated toward one local horizontal direction, so actual imported orientation must also be checked.
A separate concrete code defect was found: ChapterAssets.Rock always returned true when source parts existed, even when WorldMesh.Append silently skipped an unreadable source mesh and added zero vertices. It now verifies the actual appended vertex count, logs an explicit error for zero geometry, and returns false. Its existing procedural fallback now checks its own conservative 6.81m horizontal radius before emission.
The next native run has narrowly targeted logs:
RAMPAGE_ROCK_ROOT: the imported resource root position, rotation, scale and local-to-world matrix, before the relative part matrix is calculated.RAMPAGE_ROCK_SOURCE: imported mesh name, read/write flag, vertex count, native mesh bounds, part matrix and actual appended count, once per LOD.RAMPAGE_ROCK_CONTROL: first placement from 47.5% onward, stage/distance, road/rock coordinates, scale/yaw, measured full-route clearance and required radius. It samples the actually imported vertices against nativeTrackRuntime.SceneryGroundHeightand reports exposure and transformed bounds. This includes the actual visible apron triangles, which can sit above the ground function used by the earlier CPU approximation.RAMPAGE_ROCK_CUTS: scanned placement count, clearance rejection count, authored range and quality.
These logs distinguish an import/empty-batch failure from terrain burial or facing without replacing materials, moving the road or claiming an unobserved root cause. The required render control remains the same Alps02 view1 near 48%, with the associated native log. Additional diagnostics have not yet established a final artistic fix; native interpretation is pending.
Native cause established, 19:17 campaign run
The actual player log at Docs/QA/20260915-191751-campaign/player.log, lines 52–71, establishes the import defect:
- Both imported RockFace roots have scale (100,100,100) and approximately +90° X rotation. Their local-to-world matrices retain Unity's unit and up-axis conversion.
- The native LOD1 mesh itself has bounds extents approximately (0.04,0.03,0.02)m, while the previously retained part matrix is identity.
prefab.worldToLocalMatrix * filter.localToWorldMatrixcancelled the conversion even when the filter was the imported root itself. - At Alps02 d=1959.939m, all 1,716 native vertices were appended, but the sampled placed bounds had extents only (0.05,0.04,0.05)m. 0 of 66 sampled vertices protruded above the actual visible ground; the range was −1.776886 to −1.70517m.
- The stage emitted 26 scanned instances with no clearance rejection. The model was therefore present and readable, but its unit and orientation conversion was lost. The accompanying
alps-02-pass0-view1.pngwas inspected and confirms the missing outcrops.
The correction is confined to the RockFace branch in ChapterAssets.Load: its part matrix now retains the imported filter's full local-to-world transform, including the root conversion. Other resources keep their existing relative transform behavior. This restores the authored metre scale and up-axis without hardcoding a 100x replacement, changing the placement heights, adding colliders, or changing terrain/routes. The existing conservative footprint uses the independently verified authored metric bounds and remains enforced.
Managed compilation after this fix passed 91 Runtime + 20 Editor files, zero warnings/errors. The same native logs and Alps02 view1 remain the acceptance point for the corrected build. Root-transform loss is now proven; the resulting artistic quality has not yet been visually accepted.
Open-face orientation after the 19:54 native run
Docs/QA/20260915-195454-campaign/alps-02-pass0-view1.png was inspected. The scale correction is present, but narrow dark rear/rim fragments are visible instead of a convincing road-facing limestone wall. The native Alps02 control now reports metric extents (5.56,3.74,4.96)m, with 44/66 vertices above visible ground and a maximum height delta 6.923676m. The separate Alps03 control has 50/66 exposed vertices. These must not be confused. Whole-object burial no longer explains the complete front's absence.
A CPU-only read-only import of the delivered LOD1, recorded by ArtSource/Alps/audit_rock_orientation.py and rock-orientation-audit.json, confirms an open directed scan: 2,599 triangles, 55.576696m² total area, Blender-world area vector (−1.30538,−29.09266,30.57561) and area-weighted normal/winding agreement 0.91589. The delivered FBX also contains a real UVMap: 7,797 corners, U range 0.003675–0.989568 and V range 0.003837–0.994092. A missing exported UV channel is not supported. These Blender coordinates are explicitly not treated as proof of Unity's axis convention.
The previous fixed outwardYaw+180° rule assumed the scan's imported face was local +Z. ChapterAssets.RockFacingYaw now derives the dominant horizontal face from the actual Unity mesh triangle winding, after the preserved root transform and the same mirrored-transform handling used by WorldMesh.Append. It compares that winding with the imported normals, corrects the resulting normal direction for the placement's nonuniform scale, and computes the yaw that points toward the nearest road position. This does not guess +Z/−Z or hide wrong orientation with a two-sided material.
The scan's footprint, placement height, source textures, material, terrain and colliders remain unchanged. Its full horizontal bounding circle continues to pass the road clearance check for any selected yaw.
New native evidence on the next shared build will include:
RAMPAGE_ROCK_FACING_SOURCE: actual imported winding/normal area vectors, total area, normal agreement and selected horizontal face.legacyYawDelta: the measured correction relative to the old assumption; this establishes whether a reversal was actually needed.frontToRoadDot: the corrected face/road alignment, expected approximately 1.towardRoadArea,awayRoadAreaandexposedRoadFacingSamples: distinguish orientation success from residual front burial in the actual visible apron.
Managed compilation passed 91 Runtime + 21 Editor files, zero warnings/errors. The control remains Alps02 view1; the new orientation and resulting visual quality await native validation. If the reported yaw correction is small and road-facing exposure still fails, the remaining placement explanation must be evaluated from those values rather than asserting a proven 180° error in advance.