This is the Phase 2 / Cycle 1 acceptance standard for Pixel Forge and other runtime assets in Terror in the Jungle. It is a stabilization gate, not an art direction memo: an asset can look good and still be rejected if it cannot prove its upload, memory, draw-call, LOD, culling, and visual parity costs.
Current strategic constraint: reinforce WebGL for stabilization. Do not use an asset review to start WebGPU migration work.
Every runtime asset acceptance note must include:
- Source commit SHA and artifact paths.
- Asset registry path or loader path.
- Runtime mode coverage: at minimum the mode that uses the asset, plus Open Frontier or A Shau when the asset can affect large-world rendering.
- Screenshot evidence for visual acceptance, unless the asset is invisible infrastructure.
- Perf or startup evidence when the asset changes texture residency, upload timing, draw calls, triangles, shader/program first-use, culling, or LOD.
- A statement of whether the evidence is trusted, diagnostic, or blocked.
Do not claim optimization or remediation without before/after numbers and the artifact paths for both sides.
Mechanical gate:
npm run check:pixel-forge-texturesAcceptance rules:
- Every registered runtime texture must exist on disk and match the dimensions
declared by
src/config/pixelForgeAssets.ts. - GPU residency must be estimated as uncompressed RGBA plus full mip chain. Source PNG byte size is not an acceptance proxy.
- A single runtime texture at or above
16MiBestimated mipmapped RGBA is a warning and requires an explicit acceptance note. - A single runtime texture at or above
32MiBestimated mipmapped RGBA is blocked unless there is startup-upload evidence proving it is pre-uploaded behind a truthful loading state or replaced by a compressed, downscaled, or partitioned representation. - Texture exceptions must include the largest-upload table from a startup UI artifact when the asset can participate in startup.
Current Cycle 1 measured context: the fresh texture audit at
artifacts/perf/2026-05-02T22-04-56-474Z/pixel-forge-texture-audit/texture-audit.json
still reports 38/42 registered Pixel Forge textures flagged, 781.17MiB
estimated mipmapped RGBA residency, and 407.75MiB candidate savings.
For billboard, imposter, and animated atlas assets:
- Runtime pixels per meter must be recorded from the atlas metadata and runtime size, not from source image dimensions alone.
- Vegetation atlases above
80pxper runtime meter require visual justification or regeneration at a lower tile size. - NPC imposter atlases below
24pxper runtime meter require matched close-GLB/imposter screenshots before acceptance. - Candidate atlas sizes are planning estimates until KB-OPTIK visual evidence proves silhouette readability, animation readability, luma parity, and LOD scale parity.
Mechanical supporting gates:
npm run check:pixel-forge-optics
npx tsx scripts/audit-archive/optics-scale-proof.ts
npm run check:vegetation-horizoncheck:pixel-forge-optics and check:vegetation-horizon are static audits.
scripts/audit-archive/optics-scale-proof.ts is the matched
close-GLB/imposter crop and same-scale aircraft reference proof. Passing the
proof only means the evidence is trusted enough for review; visual-scale, luma,
shader, atlas, or aircraft-size changes still need before/after proof.
Normal maps are accepted only when their runtime value beats their upload and residency cost.
- Vegetation billboard/imposter normal maps must have a side-by-side screenshot comparison against the same species without the normal map under at least one representative daytime lighting state.
- Ground-cover and small mid-level vegetation should default to hemisphere lighting unless the normal map materially improves readability.
- Normal maps that exceed the
16MiBwarning threshold follow the same exception process as color textures. - A normal-map removal or replacement cannot land without checking vegetation luma/chroma parity and fog/atmosphere integration.
Budgets are provisional Cycle 1 WebGL stabilization gates. They can be adjusted only with scene attribution and perf evidence.
| Asset class | Provisional budget | Required proof |
|---|---|---|
| Close NPC GLB | <= 5,000 source triangles per faction GLB; no silent over-cap fallback |
Close/imposter screenshot parity and close-pool capacity evidence |
| Weapon attachment | <= 1,500 triangles per weapon; merged where practical |
Renderer stats with close NPCs visible |
| Static prop/building/structure | <= 2,500 triangles per placement and <= 4 optimized material/draw buckets |
Open Frontier scene attribution and draw-call delta |
| Aircraft/helicopter | <= 15,000 triangles LOD0 and <= 20 optimized draw buckets |
Distance visibility or LOD proof plus Open Frontier/A Shau renderer stats |
| Vegetation/imposter bucket | Instanced/bucketed submission only; no per-instance draw path | Scene attribution and culling/visibility registration |
| Effect asset/material | No first-use shader/program stall above 50ms around trigger |
Low-load effect probe with CPU profile and browser stalls |
Any asset over budget is blocked unless its acceptance note includes:
- Why the visual/gameplay value requires the exception.
- Draw calls, triangles, textures, and programs from a trusted capture.
- Scene attribution showing the asset class is named and visible-unattributed
triangles remain below
10%. - A rollback path.
GLB replacement is not a file-copy task. The runtime contract must be checked against the source contract before import.
The general import path for Pixel Forge GLB batches is
npm run assets:import-war-catalog (scripts/import-war-catalog.ts), which
generalizes the earlier aircraft-only importer: per-class axis wrap under a
TIJ_AxisNormalize root, canonical rig-joint grafts from named meshes, index
and vertex-storage canonicalization (uniform indexing, tightly packed
attributes — both required for mergeGeometries in three r184), budget triage
with REROLL output, and a generated catalog
(src/config/generated/warAssetCatalog.ts) recording forward, measured dims,
tris, and per-asset magazine/muzzle/joint node metadata. The importer owns ALL
rotation normalization; loaders keep their documented per-class assumption and
must never re-litigate engine frames at load time.
Per-class on-disk convention (enforced by the importer; forward is recorded
per asset in the generated catalog so the convention is data, not tribal
knowledge):
| Class | Source frame | On-disk target | Loader behavior |
|---|---|---|---|
| weapons | +X fwd | +Z fwd | WeaponRigManager rotates +Z to the rig axis |
| aircraft | +X fwd | +Z fwd | HelicopterGeometry / fixed-wing -Math.PI/2 wrap (matches the original TIJ_AxisNormalize_XForward_To_ZForward aircraft importer) |
| ground vehicles | +X fwd | -Z fwd | VehicleGlbVisuals applies no rotation |
| buildings/structures/props/animals | +X "front" | +Z front | layout yaw applied at placement; orientation-sensitive assets verified in the /gallery dev route |
Runtime code that re-parents nodes out of the TIJ_AxisNormalize wrapper MUST
use Object3D.attach() (or otherwise preserve world transforms) — naive
removeFromParent() + position arithmetic silently drops the wrapper rotation.
This defect class shipped twice (m48 turret seating, viewmodel magazine group)
before becoming policy; tests for node re-parenting must use fixtures that
include the wrapper node.
- Animated rotor, propeller, turret, or weapon pivots must remain as named GLB nodes with preserved animation tracks. Runtime code may infer spin axes from the tracks, but cannot silently assume a single global axis for all assets.
- Draw-call optimization may merge static aircraft descendants, but animated pivot descendants must be excluded by ancestor name or explicit metadata.
- Sidecar provenance belongs in
docs/asset-provenance/<source-batch>/, and acceptance notes must record provider, prompt/source, source triangles, structural warnings, and any manual normalization applied. - A GLB can pass static import checks and still fail acceptance if browser screenshots, renderer stats, or fixed-wing/helicopter probes expose bad orientation, frozen blades, incorrect bounds, or unacceptable draw cost.
Every accepted runtime asset must be registered in the appropriate visibility contract:
- Static world features must pass through
WorldFeatureSystemplacement andModelDrawCallOptimizerwhere applicable. - Aircraft and helicopters must use the existing distance/fog visibility or a documented LOD path.
- NPCs must respect the close GLB cap, mid/far imposter path, and explicit over-cap reporting.
- Vegetation must state whether it is cell-resident, shader-faded, or part of a future outer-canopy tier.
frustumCulled=falseis allowed only for an instanced/bucketed path with explicit runtime culling or distance cutoff evidence.
KB-CULL cannot certify culling from static file inventory alone. It needs
trusted scene-attribution.json, runtime-samples.json renderer stats, and
representative screenshots or camera paths.
Use this evidence matrix before accepting changes:
| Change type | Required evidence |
|---|---|
| Texture downscale/compression | Before/after startup UI artifact with WebGL upload table, plus visual screenshots |
| NPC imposter scale/luma | Matched close GLB and imposter screenshots at LOD switch distances; projected height and mean luma/chroma deltas |
| Vegetation atlas/normal change | Ground and elevated screenshots in Open Frontier and A Shau, plus texture audit |
| Static feature/vehicle culling | Trusted Open Frontier or A Shau perf capture with renderer stats and scene attribution |
| Aircraft GLB replacement | Import summary with provenance, standalone viewer screenshots, npm run probe:fixed-wing, Open Frontier/A Shau renderer stats, CI/deploy/live Pages verification before delivery claim, and human playtest before aircraft-feel sign-off |
| Grenade/effect first-use fix | Low-load two-grenade probe before/after; no long task above 50ms within the trigger window |
| Outer canopy | Elevated Open Frontier and A Shau screenshots plus p95 frame and draw-call deltas |
KB-OPTIK cannot accept imposter fixes without matched GLB/imposter visual evidence. KB-EFFECTS cannot close grenade spikes until the measured first-use stall is reproduced and removed. KB-CULL cannot certify culling without draw-call and renderer telemetry.
Cycle 1 benchmark bundles were certified by a dedicated bundle-certifier script that consumed the startup-UI, combat120, open-frontier-short, ashau-short, and grenade-spike artifact directories. That one-off script has since been removed; its already-emitted outputs remain the certification record. For a new bundle, gather the same artifacts and re-record the metadata fields below by hand or in a replacement script.
The certifier wrote a bundle summary and a
projekt-143-cycle1-metadata.json sidecar into each source artifact directory.
Those sidecars record commit SHA, mode, timing windows, warmup policy,
browser/runtime metadata, instrumentation flags, renderer/scene evidence, and
measurement-trust status.