Add dense, art-directed grass to PRK.Client terrain
Implement GPU-rendered swaying grass in PRK.Client by identifying grass terrain tiles and placing animated grass geometry over them. Acceptance criteria: - Grass terrain identification is derived from a decoded ground-texture vegetation mask sampled at each candidate's rotated terrain UV, rather than unstable tile texture-array indices. - Grass instances are generated only for qualifying tiles and managed with terrain lifecycle. - Both the initial terrain-patch prototype and the subsequent Poly Haven clump approach were visually unsatisfactory. The renewed target is tasteful, dense ground-cover grass with barely visible ground in eligible near-field areas, matching AO terrain palettes, asset scale, lighting and biome character rather than importing a generic meadow look. - Research direction (pending visual prototype validation): compact deterministic placement data and shared/procedural blade geometry, spatially coherent wind, coverage-preserving distance LOD, and sparse authored accent plants. Prefer a portable instanced vertex-shader baseline; evaluate compute culling/indirect draws as a deliberate graphics-engine extension rather than requiring mesh shaders or a deferred-renderer rewrite. Validate aliasing early: the inspected renderer uses single-sample rasterization. Performance claims require measured CPU/GPU costs and streaming behavior, not blade counts alone. - Preserve authored RdbTileMaterial classification and existing tile-material audio behavior. Reuse packed tile decoding and rotated terrain UV sampling, but keep sound categories separate from finer biome/vegetation eligibility and per-texture masks. The existing green-channel heuristic is not sufficient for dry or alien vegetation. Visual round 1 accepted density within patches but rejected square coverage gaps. Removed the whole-tile footstep-category gate: green coverage now follows the rotated visual texture mask, including transition tiles and missing/other audio categories. User accepted the improved coverage as pretty decent. A subsequent shoreline screenshot exposed tan sand growing dry-coloured blades; removed the tan/brown eligibility fallback, which cannot distinguish sand from dry grass even with material 6 as a hint. Dry/alien vegetation requires reviewed texture-specific masks rather than that heuristic. Sand correction awaits user visual verification; green transition coverage remains unchanged. User requested gradual slope fading: coverage now follows a smoothstep from full at 25 degrees (normal Y 0.9063078) to zero at the existing approximately 45.6-degree limit (normal Y 0.7). Deterministic thinning retains stable candidates; surviving blades also shorten toward 60% height near the limit. Slope appearance awaits user visual verification. User requested modest regional density and size variation: added deterministic world-space smooth value noise at 18/5-unit scales for nominal 84..118 blades per square unit, and correlated 9-unit height variation at 0.85..1.18 scale. Candidate grid increased to 96x96 per 8-unit patch to permit denser-than-baseline areas; root jitter margins account for byte-coordinate quantization. Variation is continuous across patch boundaries and layered with existing texture/slope eligibility. Visual tuning and the higher candidate-grid GPU cost remain unverified; extended tests remain deferred during these rounds. - User requested trying original AO-authored tuft grass as the normal ground-cover asset instead of the current flat-colour procedural blades. Identify the asset and obtain user confirmation from its texture PNG before integrating. Read-only inspection of the selected PRK SQLite found ICC Shuttleport (4582) places spikey_green_grass_largecluster.abiff (mesh 206506) 720 times and spikey_green_grass_smallcluster.abiff (206508) 27 times. Both use plant_billboards_1024 (texture 1010004/206492), decoded as 512x512 RGBA. Large mesh: 468 vertices/234 triangles, approximately 4.91x0.61x4.47 units; small: 16 vertices/8 triangles, approximately 1.26x0.51x1.12 units. Authored material is alpha-tested at approximately 0.502 and one-sided. Extracted full atlas and UV-region crop PNGs in /tmp/prk-ao-grass; user confirmed the candidate and chose the atlas, not the premade cluster mesh. Next experiment uses tightly fitted crossed cards for custom grass tufts, retaining terrain placement and variation; other atlas plants remain out of this first grass-only iteration. User requested and received a rollback checkpoint before experimentation: e8fd4fe48 (feature: Add dense procedural terrain grass) on feature/dense-terrain-grass; checkout clean after commit. Atlas experiment is now implemented after the checkpoint: custom two-plane tufts use the two wide grass UV regions confirmed from mesh 206508; the original cluster mesh and other atlas plants are not used. Resolve plant_billboards_1024.png through the installed name index/TextureLoader, preserving alpha. Fragment shading converts sampled RGB to luminance, normalizes by measured grass-region mean approximately 0.083 in linear space, and modulates existing terrain-derived colour; no original green tint is carried through. Cutout threshold is 0.5. Placement, slope fading, spatial size/density variation, wind and LOD remain; a 24x24 candidate grid and nominal 4.2..5.9 tufts per square unit replace individual-blade density. Packed terrain normals ground-fit card bases. Shared card topology now uses identical 12-vertex/8-triangle two-plane tufts at every distance LOD, with nested density subsets; tuft shape no longer changes with camera distance. Material descriptors now support texture slots 0 and 1 and invalidate on either texture generation/disposal; shared sampler pool capacity doubles accordingly. Missing atlas disables grass with a diagnostic rather than breaking ground loading. Short client build passed with zero warnings/errors; both grass shader variants compiled and passed spirv-val. No extended tests run. User visually accepted the atlas-based grass as looking great, authentic to AO while improving quality. Keep the tuft atlas, terrain-derived tint and appearance settings as the accepted visual baseline. Subsequent movement review exposed camera-driven growth/motion from inherited distance scaling/sinking. Removed width compensation, width-based density fading, vertical sinking, geometry morphing and distance-dependent wind amplitude. Tufts retain size/position/shape; density and far-range visibility now use smooth cutout coverage with a deterministic texture-space hash anchored per tuft, independent of screen position/time. User found fading too visible close to the character and requested a larger character-centered full-strength region. GrassOrigin is now published from player position each render update (offline position override only as fallback), independent of CameraPos and unchanged in reflection uniforms; grass streaming likewise prefers character position. Full density extends to 40 units, transitions to half density at 64 and quarter at 96, and fades out over 108..128; resource retention extends to 144 and mesh-density LOD thresholds/hysteresis match. Ordinary terrain/camera rendering retains its existing origin behavior. Short client build passes with zero warnings/errors; both shader variants compile and pass spirv-val. User subsequently accepted the current behavior as working great, including naturally thinner coverage over dead-looking ground from the texture mask. Current character-centered distance settings and texture-driven density are the accepted visual/behavior baseline; do not retune them without further feedback. This does not establish measured larger-radius performance, exhaustive aliasing checks or resource-lifetime validation. No extended tests run for this iteration. This confirms appearance, not measured performance or all-scene coverage: alpha aliasing, GPU costs and two-texture runtime/lifetime validation remain open; extended tests stay deferred during visual iterations. Reuse existing masks, slope/field variation, asset decoders and texture/material owners when integrating; density must be retuned for tufts rather than replacing every procedural blade with a whole cluster. - Implementation baseline: shared 7/5/3-vertex blade templates, compact per-patch root/colour data textures, vertex-stage shaping/wind, nested spatial density LOD and distance sinking; bounded grass streaming uses the existing world streaming thread. Client build, both shader variants and focused synthetic tests passed before round 1. Installed assets in playfields 540/550/4001 produced grass. GPU timings, aliasing, biome completeness and visual acceptance remain open. User requested short screenshot-driven iterations and no extended test runs between rounds; broad automated testing is deferred. - User chose a fresh implementation rather than continuing/preserving the original prototype as the implementation baseline. Active branch: feature/dense-terrain-grass, created from local develop 7918400a0 in /home/mathias/Projects/engines. Do not carry over the old clump renderer or Poly Haven asset bundle; retain existing develop terrain classification and tile-material audio. The old worktree remains untouched; its deletion is not required for the new implementation. - Rendering integrates with existing client graphics conventions and validates cleanly. - Grass variety research and implementation: decoded 14 installed alpha-bearing foliage textures and wrote PNGs plus a contact sheet under /tmp/prk-grass-texture-candidates. Confirmed grass_small.png (95359, 64x32), grass_medium_high.png (93065, 128x64), extra_tall_grass.png (93142, 256x256), dry counterparts 95574/95569/95567, stoneplant_grass.png (25870, 256x128), existing plant_billboards_1024.png (206492) plus lars/lars2 atlases 223014/223695, vertical strip 223461 and Inferno variants 227836/227842. shadowfern.png (204630) is a separate accent. Several atlas variants largely change palette rather than shape, so desaturating and terrain-tinting them may add little variety. The current atlas already has additional thin-grass regions beyond the two used tufts; these are the lowest-cost first variety candidates. Name-index entries supersgrass_green.png (120622) and stoneplant_drygrass.png (96000) did not yield texture payloads in the selected installation; do not assume listed names guarantee available assets. User approved short-grass variety and explicitly excluded tall/dry grass because placement would require an unreliable heuristic. Added two soft-green grass regions from the existing atlas, verified against authored soft_green_grass_smallcluster mesh 206505 (also shared by large cluster 206504): UV rectangles (0.585712,0.239318)..(0.953329,0.385526) and (0.130470,0.281391)..(0.458497,0.377496). Existing spikey regions remain 70% of stable per-card selections; fuller soft grass is 20% and finer soft grass 10%. Variant selection depends only on root seed/card, never camera/time/LOD. Existing tint, placement density, tuft dimensions, slope behavior and quality distances remain unchanged. Normalize additional regions by measured alpha-visible linear luminance approximately 0.123/0.134, preserving the prior 0.083 normalization for accepted spikey grass. No extra textures, bindings, geometry or draw passes. Both shader variants compile and pass spirv-val. User verified the added variety works well and explicitly called the feature good here. Current visual/functionality scope is accepted and complete, supported by user client verification, prior focused tests and shader/build checks. Broad test runs, measured GPU benchmarking and exhaustive lifetime audits remain deferred follow-up work, not claimed validation. Final variety edits are committed as 6c2d3eac0 (feature: Vary short grass shapes within the AO atlas); preceding accepted baseline is b5da03c18. No further tuning or tests requested. - User requested final graphics-options integration: Off/Low/Medium/High grass quality, with the accepted distances retained exactly as Medium and missing saved values defaulting to Medium. Quality affects distance, not terrain eligibility, tint or local tuft density. Low scales Medium distances by 0.625 (80-unit cutoff); High by 1.5 (192-unit cutoff). Shader distance ranges and CPU streaming/LOD share one typed profile. Implemented live Apply: Off immediately suppresses shader output and unloads patch resources on the streaming pass. Saved through existing machine graphics preferences with startup restoration, graphics preset/default/apply/discard behavior and invalid-enum rejection. Options labels show Off, Low (80 m), Medium (128 m), High (192 m). Medium is the missing-setting and Restore Defaults value; explicit Low/Medium/High graphics presets choose matching grass quality. No restart is required. Focused validation passed: 25 preference/profile/UI/uniform-layout tests (149 ms test execution), client compilation, and both grass shader variants compiled and passed spirv-val. No broad long-running tests or assistant client launch. User subsequently tested the options integration and confirmed it works perfectly. Options behavior is user-verified. Accepted atlas-based appearance, character-centered fading and persisted quality options are committed as b5da03c18 (feature: Add AO grass tufts and live quality options) on feature/dense-terrain-grass; checkout is clean. Procedural rollback checkpoint remains e8fd4fe48. Measured larger-radius performance and exhaustive resource-lifetime validation remain separate. Execution checklist: - Trace terrain tile classification and rendering lifecycle. - Implement grass placement, rendering, and sway animation. - Add focused automated coverage where practical and run client validation. Pull request PR #2639 — feature: Add dense AO-style terrain grass https://git.project-rk.com/prk/engines/pulls/2639 Head feature/dense-terrain-grass -> base develop; pushed with origin upstream. Final tip 6c2d3eac0; checkout clean. Final PR validation: 58 focused tests passed (0 failed/skipped), including installed fixtures 540/550/4001, client/test compilation, four grass shader modules compiled and SPIR-V validated, full branch whitespace check and clean merge-tree check against current develop. No broad suite or GPU benchmarking. PR #2639 headless Vulkan CI failures are fixed in pushed commit 2a36df8f6 (fix: Upload neutral texture before material lifetime tests). Both original failures were reproduced locally with llvmpipe: the fixture initialized but never uploaded the neutral fallback now required by the second sampler. Fixture now uploads it; material creation waits for both textures before allocating buffers/descriptors, and texture accounting compares against the baseline including the fallback. New regressions verify repeated unready-fallback attempts do not exhaust descriptors and explicit second-texture replacement/disposal invalidates cached material descriptors. CI-equivalent non-window Release suite with TreatWarningsAsErrors passes 13/13 using software Vulkan with validation; remote CI rerun is pending. Accepted grass visuals/settings are unchanged, checkout clean, and findings are posted on PR #2639.
Checklist
6 of 6 complete- Trace terrain tile classification and rendering lifecycle
- Research production grass techniques and audit the previous prototype against PRK's density, art-style and renderer constraints
- Implement grass placement, rendering, and sway animation
- Add focused automated coverage where practical and run client validation
- Wire Off/Low/Medium/High grass distance options into live rendering and persisted graphics preferences, default missing values to Medium, and verify focused preference/UI/profile behavior
- Reproduce and fix CI headless Vulkan material-descriptor failures, verify neutral fallback and second-texture lifetimes with software rendering
- Area
- Project Rubi-Ka
- Project
- engines
- Kind
- Feature
- Priority
- Medium
- Task ID
tsk_vqbrqxbr3b- Updated