# JCOM and Conquer: Full Design Reference > Concatenated reference of all core design hub documents and standard repository READMEs. > Generated for one-shot LLM ingestion and comprehensive model training. ================================================================================ FILE: HUB OVERVIEW: README.md ================================================================================ # JCOM and Conquer: Design Hub Welcome to the JCOM and Conquer design hub repository and documentation node. This repository provides game architecture, design standards, and service skills for JCOM and Conquer. It serves as the hosted reference node for automated tools, language models (such as ChatGPT), and human contributors. ## Purpose The owner stated on 2026-10-20: - "new repo is _design make sure our game architecture, standards for everything are included." - "I need a hosted node that i can point chatgpt at so it knows what to design for" - "our design reference should point to it" This hub gathers all standards into one place. It publishes a static website for web browsers and language models. Language models should start by reading [`site/llms.txt`](site/llms.txt) and [`site/llms-full.txt`](site/llms-full.txt). ## Standard Repositories The design system splits rules across specialized repositories under the `z4yin` organization: | Repository | Visibility | Role & One-Line Purpose | |---|---|---| | `jcom_v0bb_simulation_design_standards_units` | PRIVATE | Unit coordinate frames, dimensions, node naming, joints, and clip rules. | | `jcom_v0bb_simulation_design_standards_maps` | PRIVATE | Map cell grid scales, heightmaps, theatres, terrain layers, and water rules. | | `jcom_v0bb_simulation_design_standards_logic` | PRIVATE | Simulation tick rate, lockstep determinism, clean-room rules, and mechanics. | | `jcom_v0bb_simulation_design_standards_geometry` | PRIVATE | Mesh topology, triangle budgets, LOD derivation, and geometry validation. | | `jcom_v0bb_simulation_design_standards_lookdev` | PRIVATE | PBR materials, textures, texel density, lighting, tone mapping, and colour. | | `jcom_v0bb_simulation_design_standards_blueprints` | PRIVATE | US Navy / US military, third-angle projection, Navy A1 facility sheet, JSLC STD-1 look (standard 0.3.0). | | `jcom_v0bb_simulation_svc_skills` | PRIVATE | Tooling skills, asset creation procedures, review rubrics, and workflows. | ## Core Documents - [`ARCHITECTURE.md`](ARCHITECTURE.md): The overall game architecture, engines, data flow, rendering, and deployment. - [`REPOS.md`](REPOS.md): Complete catalog of simulation repositories, naming conventions, and visibility rules. - [`OPEN-DECISIONS.md`](OPEN-DECISIONS.md): Master roll-up of all open decisions across the standard repositories, with owner decisions marked DECIDED. - [`site/`](site/): The static site tree deployed to Cloudflare Pages for human and model browsing. - [`scripts/build-site.mjs`](scripts/build-site.mjs): Rebuilds `site/` deterministically from the standard repositories. ## Machine Reading - [`llms.txt`](site/llms.txt): Machine index following the llmstxt.org standard. It lists what to read first. - [`llms-full.txt`](site/llms-full.txt): Complete concatenated text of all repository README files for single-file ingestion. ## Contributing Rules 1. Write in plain controlled language (about 80% ASD-STE100). 2. Keep sentences short and clear. Use active voice. 3. Make no direct edits to standard repositories from this hub. 4. Scan all files for secrets, tokens, keys, and private network addresses before deployment. 5. All commits use the author identity: `Eli Kopter `. 6. Rebuild and redeploy the hub after any push to a standard repo. ================================================================================ FILE: GAME ARCHITECTURE: ARCHITECTURE.md ================================================================================ # JCOM and Conquer: Architecture Compiled 2026-10-20 by the dolphin4 team. This document describes the existing system architecture. It sets no new rules. Each section cites its verified sources. Skills and approach live in `jcom_v0bb_simulation_svc_skills`. Repository definitions live in [`REPOS.md`](REPOS.md). Standards live in `standards/` across units, maps, logic, geometry, lookdev, and blueprints. --- ## 1. Two Code Trees The game currently has two active code trees: | Tree | Repository | Primary Stack & Role | |---|---|---| | **v3 Engine** (skirmish build) | `jcom_v0bb_simulation_jslcnc_v3_cnc_rev` | TypeScript + Three.js + Vite client with dedicated simulation core. Supports browser RTS skirmish and campaign modes. | | **2.5D Shell App** | `jcom_v0bb_simulation_jslcnc` | 2.5D shell app mounted in the Mainframe shell (`simulation-jslcnc-battlefield`). Derives sim core from the 2D game core. | Dispatch briefs decide which tree receives a given change. This document does not decide between them. --- ## 2. Shell and Modules - A module is a folder with `module.json`, `index.html` with `data-app-id`, `guest.js` handshake, theme sync, `serve.mjs`, and tests. - Applications mount in the shell at `/apps/`. - The shell navigation bar renders the registry `sidebarModules`. - The game module talks to the host shell via `postMessage` through `shellBridge.ts`. - Integration procedures follow `svc_skills/architecture-module-integration`. --- ## 3. Client: Skirmish and Campaign (v3 Tree) - The entry point is `src/main.ts`. - Pages include `index.html`, `campaign.html`, `builder.html`, `gallery.html`, and `compare.html`. - Simulation code lives in `src/sim.ts` and `src/core/`. - Simulation runs at a fixed tick rate (`TICK_HZ = 30`) with seeded pseudo-random number generation. - Simulation is fully deterministic for multiplayer lockstep and replay verification. - New mechanics live in `src/mechanics//`. Each mechanic carries a `MECHANICS-.md` record. - Shared files carry only hook lines to prevent merge collisions. --- ## 4. Rendering System - Built on Three.js using WebGL2 (`three` version `^0.170.0`). - Model contracts require facing `-Z` with `+Y` pointing up. Models use named pivots and LOD levels. - Team colour uses a dedicated `houseTint` material. - **WebGPU Status:** Currently blocked on WebGL2. Custom shaders for terrain splatting and water use `MeshStandardMaterial.onBeforeCompile`. WebGPURenderer does not support `onBeforeCompile`. Migration to TSL (Three Shading Language) node materials is planned. --- ## 5. Game Data and Rules - All tunable parameters live in JSON rules files with a `source` column. - Hard-coded literals inside TypeScript logic are prohibited. - Data directory root holds: `rules/`, `missions/`, `maps/`, `factions/`, `weapons/`, and `rigs/`. - Balance and stats belong to `jcom_v0bb_simulation_svc_data`. - Mission scripts belong to `jcom_v0bb_simulation_svc_maps`. - Audio cues belong to `jcom_v0bb_simulation_svc_audio`. --- ## 6. Asset Pipeline - Headless 3D models build with Blender on dedicated build workers. - Textures generate through Substance Designer. - Review process: Sol builds models; Opus reviews against reference photographs. - Assets store on private storage and index in Postgres catalogues. - The game resolves asset models through authoritative asset registry versions, never through hard-coded paths. --- ## 7. Maps and Missions - Maps are JSON files containing heightmaps, terrain layers, spawn points, and objects. - Map cell scales are standardized: 5 m (infantry/base), 20 m (tactical default), 80 m (operational coast). - Map generation runs through `npm run gen-map` and checks with map validation tools. - Map lab development lives in `jcom_v0bb_simulation_svc_lab_map`. - Map specifications follow `jcom_v0bb_simulation_design_standards_maps`. --- ## 8. Automated Testing - Test suites run with Vitest (`npm test`). - Performance and simulation benchmarks run with `test:perf` and `bench:sim`. - Game test suites execute on dedicated test workers (SV3/SV4) via `lane-test.ps1`. - Tests never run on development worker SV2. - Every new game mechanic must provide unit tests and verify determinism. --- ## 9. Deployment Architecture - Production hosting runs on Cloudflare Pages with automated deployment on git push. - Dual-site release topology: authenticated internal shell (`jslc.io`) and public shell (`jewishspacelasercommand.com`). - Public mirrors include `jcnc.io` (game site) and `jslcnc.com`. - Public deployment gates enforce: allowlist verification, public build, secret scanning, and promotion checks. - Changes deploy only after verification. Rollback artifacts stay preserved. --- ## 10. House Design System - Built on the JSLC design token vocabulary (`shell-tokens.css`). - Mobile-first target viewport: 393 × 852 px (iPhone reference). - In-game HUD follows the JCOM and Conquer HUD design system (`hud-tokens.json`). - UI styling avoids amber or decorative yellow colors. - Component layouts maintain strict contrast and touch target heights. ================================================================================ FILE: REPOSITORY CATALOG: REPOS.md ================================================================================ # JCOM and Conquer: Repositories Catalog of all repositories in the JCOM and Conquer game ecosystem under GitHub organization `z4yin`. Observed state recorded on 2026-10-20. --- ## 1. Primary Repositories | Repository | Visibility | Role & Description | |---|---|---| | `jcom_v0bb_simulation_design` | PRIVATE | Design hub: game architecture, repository index, standards roll-up, and hosted node for ChatGPT. | | `jcom_v0bb_simulation_svc_skills` | PRIVATE | Skills, asset workflows, and tooling knowledge (Blender, Substance, blueprints, wireframes). | | `jcom_v0bb_simulation_design_standards_units` | PRIVATE | Design standard for unit coordinate frames, node contracts, joints, dimensions, and clips. | | `jcom_v0bb_simulation_design_standards_maps` | PRIVATE | Design standard for map scales, cell dimensions, heightmaps, theatres, and terrain layers. | | `jcom_v0bb_simulation_design_standards_logic` | PRIVATE | Design standard for game logic, fixed-tick determinism, clean-room rules, and mechanics. | | `jcom_v0bb_simulation_design_standards_geometry` | PRIVATE | Design standard for mesh topology, triangle budgets, LOD derivation, and geometry validation. | | `jcom_v0bb_simulation_design_standards_lookdev` | PRIVATE | Design standard for PBR materials, textures, texel density, lighting, and tone mapping. | | `jcom_v0bb_simulation_design_standards_blueprints` | PRIVATE | US Navy / US military, third-angle projection, Navy A1 facility sheet, JSLC STD-1 look (standard 0.3.0). | | `jcom_v0bb_simulation_jslcnc` | PRIVATE | Primary game shell app (2.5D simulation battlefield). | | `jcom_v0bb_simulation_jslcnc_v3_cnc_rev` | PRIVATE | JCNC v3 engine: browser RTS (TS + Three.js), skirmish client and map loader. | | `jcom_v0bb_simulation_jslcnc_roster` | PRIVATE | Asset roster: unit catalog, faction art, and 3D model mirror. | | `jcom_v0bb_simulation_jslcnc_notes` | PUBLIC | Research notes and public logs. | | `jcom_v0bb_simulation_svc_assets` | PRIVATE | 2D asset service: sprites, key art, concept art, and showcase renders. | | `jcom_v0bb_simulation_svc_audio` | PRIVATE | Audio service: sound effects, unit voice lines, and music cues. | | `jcom_v0bb_simulation_svc_bible` | PRIVATE | JCNC Bible: reproduction-grade design, systems, lore, and architecture spec. | | `jcom_v0bb_simulation_svc_data` | PRIVATE | Data service: unit, structure, and faction balance tables and stats. | | `jcom_v0bb_simulation_svc_maps` | PRIVATE | Maps service: skirmish maps, campaign scenarios, and mission scripts. | | `jcom_v0bb_simulation_svc_engine` | PRIVATE | Shared engine modules and utilities. | | `jcom_v0bb_simulation_svc_lab_wireframe` | PRIVATE | Wireframe inspection lab and model viewer. | | `jcom_v0bb_simulation_svc_lab_map` | PRIVATE | Interactive map inspection and terrain generation lab. | --- ## 2. Renames on 2026-10-20 | Previous Name | New Canonical Name | Visibility | |---|---|---| | `jslcnc-v3_cnc_rev` | `jcom_v0bb_simulation_jslcnc_v3_cnc_rev` | PRIVATE | | `jslcnc_notes` | `jcom_v0bb_simulation_jslcnc_notes` | PUBLIC | GitHub redirects old names automatically. Local clones must update their `origin` remote URLs with `git remote set-url`. --- ## 3. Naming Rules Standard naming pattern: `jcom_v0bb_[_]`. - Underscores separate modules and nodes. - Hyphens represent spaces inside names (for example, `svc_lab_wireframe`). - Successor repositories are PRIVATE by owner mandate (with the exception of `jslcnc_notes`). - Commits must use the canonical identity: `Eli Kopter `. ================================================================================ FILE: OPEN DECISIONS MASTER ROLL-UP: OPEN-DECISIONS.md ================================================================================ # JCOM and Conquer: Open Decisions Master Roll-Up Status: Master index of open and decided architecture decisions across all JSLC design standards. Compiled: 2026-10-20. --- ## Executive Summary: Owner Rulings of 17:02 EDT The owner decided five key questions on 2026-10-20 at 17:02 EDT. Owner statement verbatim: *"Yes, Yes, whatever is recommended for 3 and 4, 5, sure"*. | ID | Topic | Owning Standard | Status & Decision | |---|---|---|---| | **OD-02** | Install Unreal Engine 5 | Lookdev | **DECIDED: Yes.** Install UE 5.x on build worker SV3, which has Visual Studio and Epic Launcher. Owner signs in; agents never sign in. | | **OD-03** | Tone Mapping Selection | Lookdev | **DECIDED: The recommendation.** Produce A/B/C test renders (ACES Filmic, AgX, Khronos PBR Neutral) at 8 canonical camera positions. AgX is the provisional target. | | **OD-04** | HQ Sun Lighting | Lookdev | **DECIDED: Yes, real physical sun.** Calculate real Arctic solar elevation at Camp Century (77.17° N) based on latitude, longitude, date (June 21), and local solar noon (max 36.3°). | | **OD-08** | WebGPU vs WebGL2 | Lookdev | **DECIDED: The recommendation, Option A.** Stay on WebGL2 for current phase. Freeze new `onBeforeCompile` shader hooks. Schedule TSL port for later phase. | | **OD-10** | Derivation Tools | Lookdev | **DECIDED: Yes.** Install gltfpack, glTF-Transform, KTX-Software (`toktx`), and glTF-Validator on SV3 and SV4. | | **OD-B4** | Blueprint Styling | Blueprints | **DECIDED: Adopt JSLC STD-1 look.** Decided at 17:12 EDT per owner instruction; E-2D blueprint sheet redrawn to standard. | --- ## 1. Unit Standard (`jcom_v0bb_simulation_design_standards_units`) ### OD-01. Forward Axis Orientation: -Z vs +Z - **Conflict:** Game part contract and loader specify `-Z` forward. Owner E-2D lab specification noted `+Z`. Shipped legacy models have mixed orientations. - **Status:** OPEN. - **Recommendation:** New units face `-Z` with `yawOffset = 0`. Models facing other axes must state their orientation in metadata so the manifest carries `yawOffset`. ### OD-02. Triangle Budgets: Master vs Game Caps - **Conflict:** Validator profiles allow 25k; game structure caps set vehicle at 8k, aircraft at 15k; quality tiers set tank at 3.5k; master budgets allow up to 40k–50k. - **Status:** OPEN. - **Recommendation:** Three-tier rule. Master follows owner master budgets (blender-model-builder); game copies follow game caps; validator checks masters in the library. ### OD-03. Maximum Texture Size - **Conflict:** Validator allows 4096; structure validator caps lod0 at 2048 and lod1 at 1024; quality tiers set low tiers at 256. - **Status:** OPEN. - **Recommendation:** 1024 px by default; 2048 px maximum for game lod0. 4096 px permitted only on uncompressed masters. ### OD-04. Dimension Tolerance - **Conflict:** Validator fails above 5% and warns above 2%; building dims require 2%. - **Status:** OPEN. - **Recommendation:** Model to reference dimensions within 2%. Validator fail threshold remains 5%. ### OD-05. Clip Direction and Gear Convention - **Conflict:** Game state fraction 0 is rest, 1 is deployed. Lab export clips ran out-and-back. - **Status:** OPEN. - **Recommendation:** Positional clips run one direction from rest pose to deployed pose. Lab slider mappings handle UI display. ### OD-06. Compression and Image Formats - **Conflict:** Game client registers only MeshoptDecoder; no KTX2 or WebP loader in client. - **Status:** OPEN. - **Recommendation:** Deliver uncompressed masters with PNG/JPEG. Script derives meshopt for game copies. Add KTX2Loader before shipping KTX2 textures. ### OD-07. Naval Vertical Datum - **Conflict:** Dossier standard sets design waterline at Y = 0 (keel below); structure validator checks ground at Y = 0. - **Status:** OPEN. - **Recommendation:** Ships place design waterline at Y = 0 with hull below. Ground check validator must exempt naval classes. ### OD-08. Joint Vocabulary - **Conflict:** Game uses `rotate, scroll, clip, extend`; rigs use `pitch, yaw, spin, roll, slide`; validator checks `revolute, prismatic, continuous, fixed`. - **Status:** OPEN. - **Recommendation:** Author actuators using validator format in `extras.actuator`. Conversion table maps to game terms. ### OD-09. Disagreeing Part Names - **Conflict:** `barrel_01` vs `barrel_recoil`; `wheel_l_0` vs `RoadWheel_Dual_L01`; `fuselage` vs `hull`. - **Status:** OPEN. - **Recommendation:** Use part contract names (`barrel_01`, `wheel_l_0`, `muzzle_01`, `fuselage`). Add parent helper nodes where required. ### OD-10. Infantry Animation Set - **Conflict:** Structure contract specifies minimum 3 joints and 1 clip; no standard clip names defined. - **Status:** OPEN. - **Recommendation:** Standardize on: `idle`, `move`, `attack`, `die`. All loop except `die`. ### OD-11. Damage and Destroyed Variants - **Conflict:** Contract lists variants as non-empty; text notes damaged/destroyed swaps as optional. - **Status:** OPEN. - **Recommendation:** `*_destroyed` swap model is mandatory for all units; `*_damaged` state is optional. ### OD-12. Exhaust Socket Name - **Conflict:** Forge uses `fx_exhaust_01`; rig specs use `exhaust_01`. - **Status:** OPEN. - **Recommendation:** Standardize on `fx_exhaust_01` using the common `fx_` prefix. ### OD-13. Delivery Format for LODs - **Conflict:** Separate GLB files vs embedded LOD nodes inside a single GLB. - **Status:** OPEN. - **Recommendation:** Deliver master under standard naming. Script derives `.lod0.glb`, `.lod1.glb`, `.lod2.glb`. ### OD-14. Dimension Field Names in GLB - **Conflict:** Game uses `lengthM`, `widthM`, `heightM`; validator uses `length_m`, `width_m`, `height_m`. - **Status:** OPEN. - **Recommendation:** Store validator field names in GLB extras (`unit_dimensions`). Preserve game rules schema. ### OD-15. E-2D Dimensions Reference - **Conflict:** Game dimensions set length at 17.60 m; lab spec measured model at 18.16 m (3.2% difference). - **Status:** OPEN. - **Recommendation:** Keep game specification (17.60 m) as the canonical reference. ### OD-16. Unreal Import Scale - **Conflict:** Centimetre conversion factor (x100) cited in brief but not in source files. - **Status:** OPEN. - **Recommendation:** Treat x100 as standard Unreal unit conversion, not a unique custom rule. --- ## 2. Map Standard (`jcom_v0bb_simulation_design_standards_maps`) ### OD-1. World Scale `metreToWorld` - **Conflict:** `metreToWorld` is set to 0.4 marked as tunable; client code contains hard-coded offsets. - **Status:** OPEN. - **Recommendation:** Confirm 0.4 as permanent fixed scale. Replace code literals with map coordinate transforms. ### OD-2. Cell Grid Fallbacks (5 m vs 20 m) - **Conflict:** 30 of 58 playable maps omit `metresPerCell` and fall back to 5 m legacy tiles. - **Status:** OPEN. - **Recommendation:** Require explicit top-level `metresPerCell` on all maps. Review legacy fallback sheets individually. ### OD-3. Standard Cell Sizes - **Conflict:** Sheets use 5, 20, 30, 80, and 500 m cells. - **Status:** OPEN. - **Recommendation:** Standardize on: 5 m (infantry/base), 20 m (tactical default), 80 m (operational coast). 500 m reserved for theater overview sheets. ### OD-4. Engagement Ranges: Real Metres vs Map Fractions - **Conflict:** Generals balance uses map fractions; realistic naval weapons specify real metres. - **Status:** OPEN. - **Recommendation:** Maintain map fractions for standard skirmish balance. Authorize real metres for dedicated naval/campaign maps. ### OD-5. Heightmap Units - **Conflict:** Missing `heightmapUnits` leads to guesswork between raw integers (x0.2 m) and true metres. - **Status:** OPEN. - **Recommendation:** Require `heightmapUnits: "metre"` explicitly on all maps. ### OD-6. Vertical Exaggeration - **Conflict:** Some forge maps bake 1.5x–4x exaggeration into stored height values. - **Status:** OPEN. - **Recommendation:** Store true metres in heightmaps with `heightScale: 1.0`. Apply any artistic exaggeration purely in render shaders. ### OD-7. Map Heading and Grid Rows - **Conflict:** Row 0 is at screen top (north); some comments describe row 0 as south. - **Status:** OPEN. - **Recommendation:** Define Row 0 = screen top = map-up. Record geographic north in `meta.northBearingDeg`. ### OD-8. Retail World Builder Map Scaling - **Conflict:** Retail Generals maps lack cell scale metadata. - **Status:** OPEN. - **Recommendation:** Measure known structural footprints to calibrate SAGE world units to metres before assigning `metresPerCell`. ### OD-9. Tunnel Network Map Scale Discrepancy - **Conflict:** `camp-aoi-05-tunnel-network` declares 5 m cells but derives from a 20 m undersea sheet. - **Status:** OPEN. - **Recommendation:** Confirm intended tactical scale (20 m recommended) and update metadata. ### OD-10. Duplicate Kharg Island Maps - **Conflict:** `kharg_island.json` (5 m) and `kharg_approach.json` (20 m) duplicate the same terrain. - **Status:** OPEN. - **Recommendation:** Retain the 20 m sheet as canonical; retire or mark the 5 m sheet legacy. ### OD-11. Missing Map Documentation and Tools - **Conflict:** ADR-010, ADR-011, and MAP-PIPELINE.md cited but absent from active tree. - **Status:** OPEN. - **Recommendation:** Check in canonical copies to the design hub repository. ### OD-12. Map Format Standardization - **Conflict:** Skirmish engine uses tiles 0–3; beta engine uses tiles 0–8. - **Status:** OPEN. - **Recommendation:** Skirmish engine format is canonical for all new maps. ### OD-13. HQ Region Theatre Identification - **Conflict:** Bunker map uses `disko_bay`, but placed geographically at Wolstenholme Fjord. - **Status:** OPEN. - **Recommendation:** Clarify theatre naming: introduce `wolstenholme_fjord` or confirm `disko_bay` as lore region. ### OD-14. HQ Sea Gate Portal Dimensions - **Conflict:** Discrepancy between rules file portal width (94.5 m) and bunker map width (124 m). - **Status:** OPEN. - **Recommendation:** Align rules file with the authoritative E02 bunker map model. ### OD-15. Dry Seabed Rendering Bug - **Conflict:** Negative height terrain with `waterLevel: 0` renders as dry seabed unless tile-1 is marked. - **Status:** OPEN. - **Recommendation:** Implement automated map validation checks for sea height and water plane consistency. --- ## 3. Logic Standard (`jcom_v0bb_simulation_design_standards_logic`) ### OD-L01. Clean-Room Reading Policy - **Conflict:** Brief bans reading original EA source; earlier skill references mentioned decompiled files. - **Status:** OPEN. - **Recommendation:** Enforce clean-room policy for game-code agents: read only public modding documentation and observed play. ### OD-L02. Location of Game Rules Files - **Conflict:** Rules split between `data/rules/` and `skirmish/src/data/`. - **Status:** OPEN. - **Recommendation:** Place all new rules in `data/rules/.json`. Existing files remain unmoved until planned cleanup. ### OD-L03. Mandatory `source` Attribution in Rules JSON - **Conflict:** Skill requires `source` on every tunable; schema lacked the field. - **Status:** OPEN. - **Recommendation:** Require `source` on all new tunable values (citation, Generals equivalent, or PROPOSED). ### OD-L04. Hard-Coded Unit Stats in Simulation Code - **Conflict:** `sim.ts` embeds speed, cooldown, and health constants. - **Status:** OPEN. - **Recommendation:** Extract hard-coded statistics into JSON rules with deterministic regression test. ### OD-L05. Single Tick Rate Authority - **Conflict:** `TICK_HZ = 30` duplicated across multiple files. - **Status:** OPEN. - **Recommendation:** `sim.ts` `TICK_HZ` is the single authority. All other files import or test against it. ### OD-L06. Dual Input-Delay Controls - **Conflict:** `inputDelayTicks` (0) vs `turnDelay` (2). - **Status:** OPEN. - **Recommendation:** Document roles clearly: `inputDelayTicks` for local input admission; `turnDelay` for network lockstep turns. ### OD-L07. AI Random Number Generator State Masking - **Conflict:** AI PRNG stream lacked bitwise `>>> 0` masking, causing drift after 4.9M iterations. - **Status:** OPEN. - **Recommendation:** Standardize on masked `mulberry32Step` algorithm for all PRNG instances. ### OD-L08. Damage State Threshold Alignment - **Conflict:** Sim damaged threshold is 0.5/0.75; visual damage threshold is 0.5 / smoke 0.35. - **Status:** OPEN. - **Recommendation:** Visual layers derive directly from sim body states. ### OD-L09. Test Suite Execution Environment - **Conflict:** Test execution restricted to dedicated test workers (SV3/SV4) via `lane-test.ps1`. - **Status:** OPEN. - **Recommendation:** Tests run exclusively via `lane-test.ps1` with maximum 5 total concurrent workers. Never on SV2. ### OD-L10. Canonical Architecture Specification - **Conflict:** Older briefings described multi-repo beta setup; live tree is unified skirmish Vite app. - **Status:** OPEN. - **Recommendation:** Live skirmish architecture is authoritative; older briefings classified as legacy context. ### OD-L11. Shared File Modification Protocol - **Conflict:** `sim.ts` expansion under strict "hooks only" rule. - **Status:** OPEN. - **Recommendation:** Maintain "hooks only" during feature work. Schedule dedicated extraction lanes for refactoring. ### OD-L12. Headless Simulation Module Dependencies - **Conflict:** `sim.ts` transitively imported `three.js` via weapon visual hook. - **Status:** OPEN. - **Recommendation:** Move visual imports to client render bootstrap. Ensure simulation core remains free of DOM and Three.js. ### OD-L13. HUD Design System Mandate - **Conflict:** HUD design system status marked PROPOSED while required by coding rules. - **Status:** OPEN. - **Recommendation:** Enforce HUD design system for all new and modified interface components. ### OD-L14. Vehicle Dynamics & Formation Algorithms - **Conflict:** Turn rates, accelerations, and formations lack verified performance data. - **Status:** OPEN. - **Recommendation:** Await dedicated vehicle movement research lane to deliver calibrated data. ### OD-L15. Canonical ADR Log Publishing - **Conflict:** Multiple ADR log copies exist across worktrees. - **Status:** OPEN. - **Recommendation:** Publish authoritative ADR log directly inside this design hub. ### OD-L16. Hold-Fire Behavior for Defensive Systems - **Conflict:** Does "Hold fire" silence automated point defense (CIWS/HELIOS)? - **Status:** OPEN. - **Recommendation:** Default discipline is `free`. Clarify whether point defense ignores player Hold fire orders. --- ## 4. Geometry Standard (`jcom_v0bb_simulation_design_standards_geometry`) ### OD-G1. E-2D Coordinate Handedness - **Conflict:** Lab export has starboard propellers at positive X while nose faces positive Z (left-handed frame). - **Status:** OPEN. - **Recommendation:** Verify NAVY lettering in lab. If inverted, mirror mesh and re-export facing `-Z`. Add handedness check to validator. ### OD-G2. Validator Profile Alignment - **Conflict:** Validator profile sets 25k; game structure gate enforces 8k/15k. - **Status:** OPEN. - **Recommendation:** Stage-aware validation: master validates against sanity limits; delivery validates against game caps. ### OD-G3. Capital Ship Triangle Budgets - **Conflict:** Desktop target budgets (5.5k lod1) exceed current hard caps (5k lod1). - **Status:** OPEN. - **Recommendation:** Add dedicated `capital_ship` budget class in structure rules (15k / 6k / 2k). ### OD-G4. Master Sanity Caps & File Sizes - **Conflict:** Proposed master caps (tank 250k, aircraft 300k, capital 2M) need formal baseline. - **Status:** OPEN. - **Recommendation:** Adopt proposed master caps as provisional safety limits. ### OD-G5. Material Count per LOD - **Conflict:** Multi-material models vs 1-material draw call targets. - **Status:** OPEN. - **Recommendation:** Require texture atlasing to consolidate models into 1 material plus `houseTint`. ### OD-G6. LOD Generation Responsibility - **Conflict:** Fully automatic decimation vs manual artist optimization. - **Status:** OPEN. - **Recommendation:** Automatic decimation with hand-crafted LOD2 models for hero units (aircraft and capital ships). ### OD-G7. Impostor Billboarding - **Conflict:** Far-distance impostor representations undefined. - **Status:** OPEN. - **Recommendation:** Standardize on 8-view 512 px baked atlas for far LODs. ### OD-G8. Unreal Scope and Tooling - **Conflict:** Unreal Engine not installed on test boxes. - **Status:** **RESOLVED by OD-02** (Installing UE 5.x on SV3). ### OD-G9. Vector Drawing User Units - **Conflict:** User units in vector masters (SVG mm vs px). - **Status:** OPEN. - **Recommendation:** Standardize on 1 SVG user unit = 1 mm. ### OD-G10. Standard Repository Name - **Conflict:** Repository naming (`geometry` vs `wireframe`). - **Status:** **RESOLVED.** Canonical name is `jcom_v0bb_simulation_design_standards_geometry`. --- ## 5. Look Development Standard (`jcom_v0bb_simulation_design_standards_lookdev`) ### OD-01. Repository Naming - **Status:** **RESOLVED.** Canonical name is `jcom_v0bb_simulation_design_standards_lookdev`. ### OD-02. Install Unreal Engine 5 - **Status:** **DECIDED (2026-10-20 17:02).** Install UE 5.x on worker SV3. ### OD-03. Tone Mapping Mode - **Status:** **DECIDED (2026-10-20 17:02).** A/B/C render comparison across ACES Filmic, AgX, and Khronos Neutral. AgX is the provisional target. ### OD-04. HQ Sun Solar Position - **Status:** **DECIDED (2026-10-20 17:02).** Real Arctic sun calculated from Camp Century coordinates (77.17° N) at June 21 solar noon (36.3° elevation). ### OD-05. Team Colour Application - **Conflict:** Separate `houseTint` material vs base color texture alpha mask. - **Status:** OPEN. - **Recommendation:** Retain `houseTint` material for current milestone. Revisit alpha masking during WebGPU port. ### OD-06. UDIM in Unreal Masters - **Conflict:** UDIM virtual texturing vs three.js single UV limitations. - **Status:** OPEN. - **Recommendation:** UV0 remains 0–1 unwrap for three.js. UDIM allowed only on secondary UV2 detail layers in Unreal masters. ### OD-07. Military Paint Color Specifications - **Conflict:** Unmeasured values for US Navy Haze Grey, Olive Drab, and Desert Tan. - **Status:** OPEN. - **Recommendation:** Sample standard swatches (e.g. Federal Standard FS 36270 Haze Grey) using Substance Sampler. ### OD-08. WebGPU Migration Strategy - **Status:** **DECIDED (2026-10-20 17:02).** Option A: Stay on WebGL2 for current phase; freeze new `onBeforeCompile` hooks; schedule TSL port for later phase. ### OD-09. Reference Mobile Hardware - **Conflict:** Target mobile test devices for rendering budgets. - **Status:** OPEN. - **Recommendation:** Primary iPhone target (393 × 852 px viewport) and secondary mid-range Android target. ### OD-10. Derivation Tools Installation - **Status:** **DECIDED (2026-10-20 17:02).** Install gltfpack, glTF-Transform, KTX-Software, and glTF-Validator on SV3 and SV4. ### OD-11. Compressed Texture Tier Scaling - **Conflict:** CPU downsampling skips compressed KTX2 textures on low memory tiers. - **Status:** OPEN. - **Recommendation:** Publish two KTX2 texture resolutions per map (full size and capped size). ### OD-12. Target Hardware Budgets - **Conflict:** Proposed rendering budgets (Phone: 150 draws, 500k tris; Desktop: 1,000 draws, 3M tris). - **Status:** OPEN. - **Recommendation:** Adopt proposed budgets as provisional targets; verify with hardware profiling. --- ## 6. Blueprints Standard (`jcom_v0bb_simulation_design_standards_blueprints`) ### OD-B1. Projection Angle (ISO First-Angle vs ASME Third-Angle) - **Status:** OPEN. - **Recommendation:** First-angle projection per ISO standard. ### OD-B2. Standard Sheet Sizes - **Status:** OPEN. - **Recommendation:** Standardize on ISO A2 (594 × 420 mm) landscape; permit A1 for large naval combatants. ### OD-B3. Ship Drawing Orientation - **Status:** OPEN. - **Recommendation:** Bow forward-left, consistent with aircraft and land vehicles. ### OD-B4. Blueprint Color Scheme - **Status:** **DECIDED (2026-10-20 17:12).** Adopt JSLC STD-1 drawing look. ### OD-B5. Hidden Line Rendering on General Arrangement Sheets - **Status:** OPEN. - **Recommendation:** Omit hidden lines on GA sheets per ISO aerospace drafting conventions. ### OD-B6. Wireframe Lab Accent Colors - **Status:** OPEN. - **Recommendation:** Confine amber accents to the standalone wireframe lab; engineering drawings never use amber. ### OD-B7. E-2D Model Deviations - **Status:** OPEN. - **Recommendation:** Annotate model dimension variance (3.2% length, 13% height) on engineering sheets; repair in model lane. ### OD-B8. Unified Scale Fleet Comparison Sheets - **Status:** OPEN. - **Recommendation:** Author class comparison sheets at uniform scales (aircraft 1:200, vehicles 1:100, ships 1:2000). ================================================================================ FILE: UNITS STANDARD: standards/standards_units/README.md ================================================================================ # Unit standard: what a unit model must be and do Status: **draft for owner review**, 2026-10-20. Machine-readable twin: [`unit-standard.json`](unit-standard.json). Conflicts between sources: [`OPEN-DECISIONS.md`](OPEN-DECISIONS.md). This standard is for anyone who builds a unit for **JCOM and Conquer**, a browser real-time strategy game built on TypeScript and Three.js. That includes outside 3D designers, AI model designers and our own agents. It covers what every unit model must contain, how it moves, which states it must show, and how to hand it in. Related standards: | Topic | Where | |---|---| | Blueprints and reference sheets | [`../standards_blueprints/README.md`](../standards_blueprints/README.md) | | Topology, triangle budgets, LODs, compression | [`../../design_standards_geometry/README.md`](../../design_standards_geometry/README.md) | | Materials, textures, lighting | [`../../design_standards_lookdev/README.md`](../../design_standards_lookdev/README.md) | ## How to read this - Every rule names its source. `GAME:` is the live game tree (`data/`, `skirmish/`). `VALIDATOR:` is the jslc-model-validator repo. `ESTATE:` is the z4yin-files checkout. `LAB:` is the owner's E-2D Wireframe Lab spec. `DR:` is the design-repo lane folder (`svc_skills/`). - **PROPOSED** marks a rule with no source yet. It is our best suggestion and waits for owner approval. - **OD-nn** points to an open decision. Until the owner decides, follow the recommended value given there and say in your submission which value you used. - The game rule files are the source of truth. If this page and a game file disagree, the game file wins. Please report the difference. --- ## 1. Scale, units and axes | Rule | Value | Source | |---|---|---| | Unit of length | metres. 1 model unit = 1 real metre | `GAME:data/rules/unit-part-contract.json:4`; `GAME:data/rules/model-precedence.json` (`scaleIsMetres: true`) | | Up | **+Y** | `GAME:data/rules/unit-part-contract.json:6`; `VALIDATOR:rules/rules.v1.json:11` | | Forward (nose, bow, gun) | **-Z** (see OD-01) | `GAME:data/rules/unit-part-contract.json:5`; `ESTATE:docs/assets/DOSSIER-STANDARD.md:61` | | Starboard (right) | +X | `ESTATE:docs/assets/DOSSIER-STANDARD.md:63` | | Ground | Wheels, tracks, feet and landing gear touch **Y = 0**, centred on the origin | `GAME:data/rules/unit-structure.json:2`; `ESTATE:docs/assets/DOSSIER-STANDARD.md:67` | | Ships | Design waterline at Y = 0, keel below (see OD-07) | `ESTATE:docs/assets/DOSSIER-STANDARD.md:65-66` | | Facing check | Travel direction must be within 0.02 rad of -Z | `GAME:data/rules/unit-structure.json:54` | | Ground check | Lowest point within 0.05 m or 2% of height of Y = 0 | `GAME:data/rules/unit-structure.json:55-56`; `VALIDATOR:rules/rules.v1.json:13` | In game, the engine scales metres by `metreToWorld = 0.4`. One map tile is 5 m, or 2 world units (`GAME:data/rules/world-scale.json:9-11`). **Do not pre-scale.** Deliver true metres; the game does the rest. A model that faces another way gets a `yawOffset` in the game manifest. New models should not need one (`GAME:data/rules/bldg-defence-dims.json:4`). Blender users: Blender is Z-up. The glTF exporter's "+Y Up" option produces the axes above. Model with the nose toward Blender **+Y**; it becomes glTF -Z (`GAME:data/rules/bldg-defence-dims.json:4`). ## 2. Dimensions and real-world sourcing Every unit has a row in `GAME:data/rules/unit-dimensions.json` (887 entries plus 8 class defaults). A row looks like this: | Field | Meaning | |---|---| | `lengthM`, `widthM`, `heightM` | Real metres. Width is the wingspan for aircraft | | `fit` | Which axis the game fits to: `length` (longest horizontal body), `wingspan`, or `height` (infantry) | | `bandMinM`, `bandMaxM` | Accepted range for the fitted axis | | `source` | Where the figure comes from, with a URL to a primary sheet when there is one | | `confidence` | `high`, `medium`, `low` or `design` | | `designSize` | `true` when the figure is a design choice rather than a real vehicle | | `represents` | The real thing the model stands for, for example "M1A2 SEPv3 Abrams" | Source: `GAME:data/rules/unit-dimensions.json:2-40`. Rules: 1. **Model to the row's figures, within 2%.** The model validator fails above 5% and warns above 2% (`VALIDATOR:rules/rules.v1.json:6-7`; see OD-04). 2. Use primary sources first: the manufacturer, the armed service, or NAVAIR/NAVSEA product pages. Each row's `source` field shows the standard expected. 3. If the unit has no row, or the row says `confidence: low` or `UNVERIFIED`, find a primary figure and include it in the dossier (section 11). 4. Infantry stand 1.60-1.90 m and no taller than 2.2 m. A standing rifleman with helmet is 1.85 m (`GAME:data/rules/unit-dimensions.json` class `Rifleman`; `GAME:data/rules/unit-structure.json:62`). 5. Ordinary buildings stay under the NASA VAB height of 160.02 m unless the row names an exception (`GAME:data/rules/unit-dimensions.json:12203-12227`). 6. **Canon look.** Castdb canon renders and blueprints are the visual reference for how a unit should look (owner memory, 2026-10-19). Real dimensions still come from the dimensions file. ## 3. Blueprints The sheet standard (views, scale bars, pixels per metre) lives in [`../standards_blueprints/README.md`](../standards_blueprints/README.md). No single pixels-per-metre value is set in the game files; it varies by sheet. Unit-specific needs (**PROPOSED**; no source yet): - One sheet per **distinct silhouette state**: carrier aircraft folded and spread, gear down for fixed-wing, ramp open for transports, and deployed for artillery and launchers with outriggers. - Pivot locations for every joint in section 6, marked on the side or top view. - Waterline marked on ship side views. ## 4. Geometry budgets Triangle budgets, topology and LOD rules are owned by [`../../design_standards_geometry/README.md`](../../design_standards_geometry/README.md). The sources disagree, so the budget conflict is recorded in OD-02. Unit-specific facts: - Each unit class maps to one **budget class**: `infantry`, `vehicle`, `aircraft`, `ship` or `structure` (`GAME:data/rules/unit-structure.json:36-46`; mapping in section 7). - A unit must not be a box. The forge quality gate needs a minimum triangle count per class: infantry 400, wheeled 1,000, tank 1,200, ship 1,500. It also needs at least 60% of the surface textured and at least 4 colour bins (`GAME:data/rules/unit-forge.json:114-165`). - Meshes must be closed (open edges under 2%). Moving parts must not be welded into the hull (`GAME:data/rules/unit-structure.json:59-61`). - **Master and game copies** (owner, 2026-10-12, recorded in `ESTATE:skills/blender-model-builder/SKILL.md:202-225`): - Every model has one high-resolution **master**. All modelling, rigging and animation happens on the master, and it is never decimated, merged or compressed. The 105,359-triangle E-2D lab export is an example (`LAB:input/OWNER-SPEC.txt:11`). - Game copies (LOD0, LOD1 and LOD2) are **derived by script**. Moving parts stay as separately named nodes with their pivots. - The export gate checks that the bounding box is within 1% of the master and that pivots are within 2 mm. - Outside designers deliver the master. They may also deliver game copies. - Master budgets differ from game budgets; see OD-02. ## 5. Node naming and hierarchy contract The game finds parts **by node name**. A wrong name means a part that does not move. ### 5.1 Contract names Source: `GAME:data/rules/unit-part-contract.json:128-146`. | Node | What it is | |---|---| | `hull` | Root mesh. Everything else hangs off it | | `turret` | Yaw pivot | | `turret_pitch` | Pitch pivot, child of `turret`. May be an empty node | | `barrel_01` (`_02`, `_03`) | Gun mesh. Recoils along its own axis toward +Z | | `muzzle_01` | **Empty.** Projectile spawn and muzzle flash, at the barrel tip | | `track_l`, `track_r` | Tread sheets. Their UVs scroll with speed | | `wheel_l_0`, `wheel_l_1`, ... and `wheel_r_0`, ... | Wheels, front to back. Index 0 steers on wheeled vehicles | | `teamcolor_hull` | Player-colour mesh. Any mesh using material `houseTint` also counts | | `fx_smoke_01`, ... | **Empty.** Damage smoke points | | `fx_dust_01`, ... | **Empty.** Track dust points | | `fx_exhaust_01` | **Empty.** Exhaust point (`GAME:data/rules/unit-forge.json:33-37`; see OD-12) | | `radar_01` | Ship radar dish. Constant spin | | `rotor_main`, `rotor_tail` | Rotors. They spin about their thinnest axis at the hub. The main rotor sheds on death | | `_damaged`, `_destroyed` | Swap meshes at the damage bands (section 8) | The joint file adds `refuel_probe`, `cargo_hook`, `ramp`, `rotor_hub` and `rotor_pivot` (`GAME:data/rules/unit-joints.json:121-176`). Use `hatch`, `door`, `gear`, `prop` or `propeller`, `launcher` and `radar` inside names for those parts. The structure check matches these words (`GAME:data/rules/unit-structure.json:77-89,91-229`). ### 5.2 Naming rules - Use lower_snake_case for contract names. Matching ignores case. **PROPOSED:** keep all names in lower_snake_case. - Do not use number-only names; the validator rejects them (`VALIDATOR:rules/rules.v1.json:42`). Unnamed nodes get a warning (`:43`). - Names such as `Scene`, `Root`, `RootNode`, `Armature`, `Sketchfab` and `node_12` are treated as generic, not as parts (`VALIDATOR:vendor/model-browser/parts-model.mjs:13`). - Some names disagree between sources (OD-09). Use `barrel_01` under an empty `barrel_recoil` node, `wheel_l_0`, and for jets `fuselage` plus `gear_nose`. ### 5.3 Pivot placement Set each moving node's **origin** on the real hinge or axle. Source: `GAME:data/rules/unit-structure.json:77-89`. Pivot tolerance is 20% of part size or 0.05 m, whichever is larger (`:57-58`). | Part | Pivot | |---|---| | turret, hatch, launcher | centred in X and Z, seated at the part's lowest point | | barrel | centred in X and Y, at the breech (the +Z end) | | wheel | centred in Y and Z (the axle) | | rotor, radar, gear | centred in X and Z | | prop | centred in X and Y | | door | on its hinge line along X | More rules from the Opus build reviews (`DR:svc_skills/unit-model-contract/references/ADDENDUM-opus-03.md:13-17`, where `DR` is the design-repo lane): - Every moving node's origin lies inside its own mesh bounds, at the joint. - `barrel_01` and tool meshes extend along local -Z. If a tool rests facing the rear, put the 180° yaw on the `turret` node, not on the mesh. - Pose-test each moving part at its travel limit. Nothing may pass through other geometry. - Openings such as portals, bays and doors are real recesses at least 2 m deep. A dark panel is not an opening (`:18-20`). - Every LOD keeps every major moving part, such as rotors, wheels and the gun (`ADDENDUM-opus-01.md:9`; `ADDENDUM-opus-02.md:11`). ### 5.4 Example hierarchy (tank) ``` hull ├── turret (yaw, +Y) │ └── turret_pitch (pitch, +X) │ └── barrel_recoil (empty) │ └── barrel_01 (recoil, +Z) │ └── muzzle_01 (empty) ├── track_l / track_r ├── wheel_l_0 … wheel_l_n / wheel_r_0 … wheel_r_n ├── sprocket_l / sprocket_r ├── teamcolor_hull ├── fx_smoke_01, fx_dust_01, fx_exhaust_01 (empties) └── hull_destroyed, turret_destroyed (hidden swaps) ``` ## 6. Joints and articulation ### 6.1 Joint kinds the game knows Source: `GAME:data/rules/unit-joints.json:21-176`. | Kind | Node | Motion | Default limits | |---|---|---|---| | turret-yaw | `turret` | rotate about Y | -180° to 180° (full traverse) | | gun-pitch | `turret_pitch` | rotate about X | -8° to +20° | | track | `track_l`/`track_r` | UV scroll along X | n/a | | wheel | `wheel_[lr]_n` | rotate about X | continuous | | hatch | `*hatch*` | rotate about X | 0° to 90° | | rotor | `rotor_main`, `rotor_hub`, `rotor_pivot` | rotate about Y | continuous | | refuel-probe | `refuel_probe` | extend along Z | 0 to 3.4 m | | cargo-hook | `cargo_hook` | rotate about X | -20° to 20° | | ramp | `ramp` | rotate about X | 0° to 90° | | wing-fold | clip with `wing` or `fold` | clip | n/a | | gear | clip with `gear` | clip | n/a | ### 6.2 Rates the game drives Source: `GAME:data/rules/unit-part-contract.json:35-116`. Model the limits to match the real vehicle; the game uses these rates. | Class | Turret yaw °/s | Pitch °/s | Pitch range | Recoil m / return s | |---|---|---|---|---| | tank | 180 | 40 | -8° to 20° | 0.35 / 0.45 | | IFV/APC | 120 | 45 | -8° to 30° | 0.22 / 0.35 | | artillery | 30 | 20 | 0° to 55° | 0.5 / 0.8 | | wheeled | 90 | 40 | -6° to 25° | 0.08 / 0.2 | | ship | 80 | 80 | -5° to 45° | 2.4 / 1.3 | Rotors run at 1,080 °/s (main) and 2,160 °/s (tail). Ship radar runs at 36 °/s (`:29-30,114`). The front wheel steers up to 28° on wheeled vehicles and artillery. ### 6.3 Declaring joints in the GLB Write each moving node's joint in `node.extras.actuator`. Source: `VALIDATOR:rules/rules.v1.json:46-53`; `VALIDATOR:src/validate.js:118-141`. ```json "extras": { "actuator": { "joint": "revolute", "axis": [1,0,0], "pivot": [0,1.2,-0.4], "limits": { "min": -8, "max": 20, "unit": "deg" } } } ``` - `joint` is one of `revolute`, `prismatic`, `continuous` or `fixed`. - Moving joints need an axis. - Revolute and prismatic joints need limits. The game and the rig specs use other words for the same thing (OD-08). This mapping is **PROPOSED**: | Validator | Game joint file | Rig spec | |---|---|---| | revolute | rotate (with limits) | yaw, pitch, fold | | continuous | rotate (0-360), scroll | spin, roll | | prismatic | extend | slide, recoil | | fixed | n/a | fixed | **One driver per joint.** A node is driven by either a clip or the game's procedural rig, never both (`LAB:input/OWNER-SPEC.txt:128`). ## 7. Unit classes, required parts, states and animations ### 7.1 How a model is classed The game assigns a class from the unit id. The first match wins, in this order: infantry, submarine, helicopter, jet, ship, artillery, defence, truck, tank, wheeled, building (`GAME:data/rules/unit-structure.json:23-35`). ### 7.2 Required parts by class Source: `GAME:data/rules/unit-structure.json:91-229`, plus forge required nodes at `GAME:data/rules/unit-forge.json:58-96`. | Class | Budget class | Must have (separate nodes) | |---|---|---| | tank | vehicle | hull; turret; barrel_recoil **and** barrel; tracks + sprocket, **or** 4+ wheels; muzzle_01 | | IFV/APC | vehicle | as tank (`unit-part-contract.json:52-67,118`) | | wheeled | vehicle | hull; 4+ wheels | | truck | vehicle | hull or cab; 4+ wheels | | artillery / MRL | vehicle | hull; 4+ wheels or tracks; launcher/pod **or** barrel; muzzle_01 | | helicopter | aircraft | hull/fuselage; rotor_main; rotor_tail **or** a second main rotor | | jet | aircraft | hull/fuselage/airframe; gear (forge wants `fuselage` and `gear_nose`) | | ship | ship | hull; at least one of prop, turret, launcher or radar | | submarine | ship | hull; prop; hatch | | infantry | infantry | a skinned figure with 3+ joints and 1+ clip; fallback node `static` | | building | structure | hull/body/shell; door | | defence | structure | turret; barrel or launcher | Every class also needs `teamcolor` (a mesh or a `houseTint` material) and a destroyed variant (section 8). ### 7.3 Required states and clips The game drives **named states** by playing glTF clips whose names match patterns. A clip with a matching name plays; one without is ignored (`GAME:data/rules/unit-states.json`, readme). **Clip rule.** A pose sets a clip to a fraction: 0 is the clip's first frame (rest) and 1 is its last frame (deployed). So every positional clip must run **one way**, from rest to deployed, with no return motion (`GAME:skirmish/src/render/unitStates.ts:1-2,264-270`; see OD-05). Spinning parts use a looping clip, or a named node the rig spins. **Rest pose** (the bind pose and the first frame of every clip): gear down, wings spread, hook stowed, flaps up, canopy closed, doors and hatches shut, turret forward, gun level. #### Land vehicles: tank, IFV, wheeled, truck, artillery States are `travel` (default), `deployed` and `combat`. Source: `GAME:data/rules/unit-states.json` class `vehicle` (from line 718). | Clip name contains | travel | deployed | combat | |---|---|---|---| | `wheel` or `drive` | loop | 0 | 0 | | `turret` | 0 | 0 | loop (slow) | | `hitch` | 0 | 1 | n/a | | `launcher`, `outrigger`, `stabilis`, `deploy` | 0 | 1 | 1 | | `radar` | 0 | 1 | loop | Turret, barrel, wheels and tracks are normally driven by the rig from node names (section 6), so clips are optional for them. A deploying vehicle needs a `deploy` or `outrigger` clip. Examples are a launcher that raises or outriggers that drop. #### Rotary-wing aircraft (helicopter, tilt-rotor) Land-based helicopters use states `parked` (default), `taxi` and `airborne`. Ship-based ones use the carrier set below. Source: `GAME:data/rules/unit-states.json` class `land-aircraft` (from line 541). | Clip name contains | parked | taxi | airborne | |---|---|---|---| | `stow` or `fold` (blade fold) | 1 | 0 | 0 | | `gear` (if retractable) | 0 | 0 | 1 | | `rotor` | 0 | loop idle | loop full | | `canopy` | 1 | 0 | 0 | Rotors may instead be spun by the rig from `rotor_main` and `rotor_tail` nodes. Transports add `ramp`, `cargo_hook` and `refuel_probe` nodes as fitted (`GAME:data/rules/unit-joints.json:199-214`). #### Fixed-wing aircraft (land-based) The states and table are the same as for rotary-wing. Use `propeller` for prop aircraft. Gear clip: 0 is down and 1 is retracted. #### Carrier aircraft with folding wings States are `hangar-deck`, `flight-deck` (default), `airborne` and `landing`. Source: `GAME:data/rules/unit-states.json:26-120`. | Clip name contains | hangar-deck | flight-deck | airborne | landing | |---|---|---|---|---| | `stow` or `fold` | **1 (folded)** | 0 | 0 | 0 | | `gear` | 0 (down) | 0 | **1 (up)** | 0 | | `hook` | 0 | 1 | 0 | **1** | | `flap` | 0 | 0 | 0 | **1** | | `propeller`, `propulsion`, `screw` | 0 | loop idle | loop full | loop approach | | `rotor` | 0 | loop idle | loop full | loop approach | | `dome` (rotodome) | 0 | 0 | loop | loop | | `canopy` | 1 | 0 | 0 | 0 | | `launch` (launch bar) | 0 | 1 | 0 | 0 | Durations the game uses for transitions: wing 5 s, gear 6 s, flap 4 s, spool-up 2 s (`GAME:data/rules/unit-states.json:13-21`). **Wing fold detail** (`LAB:input/OWNER-SPEC.txt:120-125`): outer wings fold aft. Ailerons and outboard flaps are children of the folding panel. Nacelles and inboard flaps stay on the fixed centre section. Gear retracts with its doors and linkages into modelled wells, and the wheels stay visible. Reference clip set from the shipped E-2D: `Wing_Port_StoWing`, `Wing_Starboard_StoWing`, `Main_Gear_Port_retraction`, `Main_Gear_Starboard_retraction`, `Nose_Gear_retraction`, `Tailhook_deploy`, `Launch_bar`, `Flap_*_Fowler`, `Aileron_*`, `Elevator_*`, `Rudder_*`, `Propeller_*_rotation` and `Rotodome_rotation` (`GAME:data/rigs/jslc.aircraft.e-2d-hawkeye.json`, model.animations). Control surfaces (aileron, elevator, rudder) are not used by game states today. Their convention, neutral at fraction 0.5, is **PROPOSED**. #### Naval: ships and submarines States are `moored` (default), `underway` and `action-stations`. Source: `GAME:data/rules/unit-states.json` class `ship` (from line 909). | Clip name contains | moored | underway | action-stations | |---|---|---|---| | `propeller`, `propulsion`, `screw` | 0 | loop | loop | | `radar` | 0 | 0 | loop | | `rotor` | 0 | 0 | loop | | `train` (gun traverse) | 0 | 0 | 1 | | `elevate` | 0 | 0 | 0.4 | | `door`, `hatch`, `gate` | 0 | 0 | 0 | Radars can instead be a `radar_01` node, which the rig spins. Carriers and amphibious ships also carry deck elevators and well-deck gates as clips. The Ford and Wasp base ships show the pattern (`GAME:data/rules/unit-states.json` models). #### Structures and defences States are `idle` (default), `active` and `alert`. Source: `GAME:data/rules/unit-states.json` class `building` (from line 1138); `GAME:data/rules/model-anim.json`. | Clip name contains | idle | active | alert | |---|---|---|---| | `door`, `hatch`, `gate`, `deploy`, `bolt` | 0 | 1 | 1 | | `track`, `ignite` | 0 | loop | loop fast | Buildings also use these clips (`GAME:data/rules/model-anim.json:13-18`): - Production: any clip matching `produc`, `work`, `active`, `build` or `open` plays while a unit is being built. - `damage`: plays below 50% health. - Construction: `construct`, `rise` or `build_up`. Without it, the building rises from the ground over 2.5 s. - Any other clip loops as idle. Nodes named `radar`, `dish`, `fan`, `turbine`, `pump` or `flag` animate procedurally when there are no clips. #### Infantry The structure gate needs a skinned figure with at least 3 joints and at least 1 named clip (`GAME:data/rules/unit-structure.json:196-205`). No source names the required clips (OD-10). **PROPOSED:** `idle`, `move`, `attack` (loops) and `die` (one-shot), on one skinned mesh of one person. A "team" roster row still uses one body (`GAME:data/rules/unit-dimensions.json` class `Rifleman` notes). ## 8. Damage and destroyed states Source: `GAME:data/rules/unit-part-contract.json:31-33,120-127,144-145`. | Health below | What happens | |---|---| | 50% | `*_damaged` swaps in if present. A building plays a `damage` clip | | 35% | Smoke starts at a random `fx_smoke_nn` | | 15% | `*_destroyed` swaps in. `rotor_main` sheds | What to model: - Name swap meshes after the part they replace: `hull_destroyed`, `turret_destroyed`, `hull_damaged`. Keep them **hidden** in the file, at the same pivot as the original. - At least one destroyed variant is required. Damaged variants are optional (OD-11). - Put two or more `fx_smoke` empties on the hull, at engine decks, hatches or wing roots. - If there is no destroyed swap, the game makes a procedural wreck: darkened by 0.32, tilted 0.06-0.3 rad, sunk 0.12, burning for 3.5 s, and kept for about 35 s (`GAME:data/rules/fx.json:121-128`). A modelled wreck replaces that. ## 9. Textures and materials Owned by [`../../design_standards_lookdev/README.md`](../../design_standards_lookdev/README.md): PBR, texel density, atlases, formats and sizes. Related open decisions are OD-03 (maximum size) and OD-06 (formats). Checks that belong to the **unit contract**: - **Team colour.** At least one material is named `houseTint`. The game also accepts `team_colour`, `teamColour`, `team-colour` and `TeamColor`. Or a mesh is named `teamcolor_*`. The game tints it to the player colour, and its base colour factor is white (1,1,1,1). Sources: `GAME:data/rules/unit-structure.json:48-51`; `GAME:data/rules/unit-part-contract.json:11-12`; `GAME:data/rules/unit-forge.json:25-30`. - **Keep team colour neutral** (`DR:svc_skills/unit-model-contract/references/ADDENDUM-opus-03.md:5-8`): - The `houseTint` base colour is near-white (#E0E0E0 or lighter), or the material is untextured with a white factor. - Never bake a faction colour into it. - Use exactly `houseTint` in every LOD, with no `.001` suffix. - Put fixed faction colours on a separate material. The game lerps the material colour 55% toward the team colour and never replaces textures (`GAME:data/rules/model-precedence.json:172,292-293`). - **Tread texture** on `track_l` and `track_r` tiles every 0.76 m along the track, with 4 links per tile, so that the UV scroll matches ground speed (`GAME:data/rules/unit-part-contract.json:13`). - Textures are embedded in the GLB. There are no external files and no data URIs (`VALIDATOR:rules/rules.v1.json:32`; `VALIDATOR:src/validate.js:41`). ## 10. Export and file format | Rule | Source | |---|---| | glTF 2.0 binary `.glb` with `asset.version` "2.0", one scene, embedded buffers | `VALIDATOR:src/validate.js:40-43` | | Must pass the Khronos glTF Validator with no errors | `VALIDATOR:rules/rules.v1.json:70-73` | | Clips are named; no more than 64 clips; none longer than 600 s | `VALIDATOR:rules/rules.v1.json:54-58` | | Positions are finite; degenerate triangles and duplicate meshes are warned | `VALIDATOR:rules/rules.v1.json:59-64` | | Real-size dimensions in `asset.extras.unit_dimensions` as `{ "length_m", "width_m", "height_m" }` (OD-14) | `VALIDATOR:src/validate.js:49-55` | | Up axis in `asset.extras.up_axis = "Y"` (optional) | `VALIDATOR:src/validate.js:69` | | Optional state map in `asset.extras.asset_management.condition_map`, schema `asset-management/conditions/v1`. Each state names its clips and visible or hidden nodes, and each transition names a clip | `VALIDATOR:vendor/model-browser/conditions.js:6-27` | | Compression, LOD files and decimation | owned by the geometry standard; OD-06 and OD-13 | **Do not** bake the current pose into the clips. Keep the rest transforms, and export from the rest pose (`LAB:input/OWNER-SPEC.txt:204,244`). **File names.** There is an owner rule (2026-10-12): no file name may claim a status such as fixed, final, good, new or latest. Names state what changed and when: `---.glb` (`ESTATE:skills/blender-model-builder/SKILL.md:199-200`). Use the roster id as ``. The game-copy names differ between sources (OD-13). ## 11. Dossier Every unit has a research dossier that follows `ESTATE:docs/assets/DOSSIER-STANDARD.md` (DOSSIER-STD-2026-V1). **YAML front matter.** Required fields: - `generated_at` (UTC ISO 8601) - `generator` - `skills` - `provider` - `models` - `query` - `sources_count` `asset_id` and `title` are recommended (`:18-51`). **Five sections** (`:55-93`): 1. Coordinate datum and dimensions: -Z forward, +Y up, ground or waterline at Y = 0, the full metric envelope, and mass. 2. 11-station transverse offset table. This is for hulls; **PROPOSED:** for aircraft and vehicles, give fuselage or hull stations instead. 3. Kinematic scene graph: an ASCII tree of every moving part with its pivot, axis and limits. This maps onto section 5. 4. Sensor siting and symmetry audit. 5. AI video and verification prompt package. The game turns the dossier into a **rig spec**, `GAME:data/rigs/.json` (schema `rig-spec/1`). The rig spec lists parts (name, role, parent, joint, axis, pivot, limits, clips), sockets (muzzle, smoke, dust, exhaust, launch, wake, rotor_wash, teamcolor) and damage zones. The owner writes free-text notes in `.notes.md` beside it, and agents read those first. The rig specs are drafts generated from dossiers; check them against the GLB. ## 12. Roster entry fields Every unit is one `RosterEntry` in `GAME:data/roster/{jslc,aoi,penguins}.json`, typed by `GAME:data/roster/schema.ts:212-250`. Designers do not write roster rows. They do need the fields that touch the model: | Field | Type / values | Why the modeller cares | |---|---|---| | `id` | canonical slug, e.g. `jslc.vehicle.paladin-tank` | Name your files and dossier `asset_id` after it | | `legacyId` | e.g. `coalition.vehicle.paladin-tank` | Older manifests and dimension rows use it | | `faction` | `jslc`, `aoi`, `penguins` (`:33`) | Faction look; colours from `FactionMetadata.colors` (`:42-46`) | | `kind` / `category` | unit, structure, upgrade, power / infantry, vehicle, aircraft, naval, building, defense (`:50-61`) | Picks the class family | | `name`, `tier`, `description` | text, 1-5 | Context | | `dimensions` | `lengthM`, `widthM`, `heightM`, `massTonnes`, `basis` (`official`, `representative`, `fictional`), optional `foldedFootprint` {L,W,H} and `rotorEnvelope` {L,W} (`:119-136`) | Folded size for carrier aircraft; rotor disc for helicopters. Your folded state must match `foldedFootprint` | | `locomotion.type` | foot, tracked, wheeled, hover, air, water, ship, amphibious, subsurface, static, orbital (`:65-76`) | Decides tracks vs wheels vs rotors, and so the required parts | | `locomotion.turnsInPlace` | bool | Tracked pivot turns: both tracks scroll opposite ways | | `weapons[]` | `id`, `name`, damage, range, `targets` (`:92-108`) | **PROPOSED:** one `muzzle_nn` empty per weapon, in the order listed | | `subsystems[]` | id or {`id`, `name`, `type`, `isExposed`, `hpSharePercent`} (`:138-145`, `:233-234`) | **PROPOSED:** an exposed subsystem should be a separate, named node so it can show damage | | `parasite` | `dockType`, `capacity`, `acceptedLaunchCategories` (catobar, stovl, rotorcraft, land), `wellDeckSlots`, `flightDeckSlots` (`:147-163`) | Carriers and transports need doors, ramps, elevators and spots to match | | `modelLink` | `set`, `key`, `unitKey`, `s3Key`, `sha256` (`:176-202`) | Filled by us at ingest; links the roster row to your GLB | | `_src` | field path to citation (`:245-249`) | Every figure must be cited, the same rule as this standard | Note: the roster's `dimensions.basis` (official, representative, fictional) and the dimension file's `confidence` (high, medium, low, design) are two different scales for related ideas. ## 13. Validation: what is checked Three checkers run on a submitted unit. **A. Model validator** (`jslc-model-validator`, rules version 1.0.1). Verdict: any fail gives `fail`, else any warning gives `warn`, else `pass` (`VALIDATOR:README.md`). | Check | Fails when | |---|---| | `gltf.valid` | Khronos validator errors, external buffers, no scene or mesh, or (in structural mode) compressed geometry | | `scale.dimensions` | any axis is more than 5% off the reference (warn above 2%) | | `orientation.up` | not Y-up, or ground gap above 0.05 m (warn) | | `budget.profile` | triangles, draw calls or bytes over the profile | | `textures.images` | missing, not power of two, over 4096, a data URI, or a format outside PNG, JPEG, WebP or KTX2 | | `rig.nodes` | missing required nodes, number-only names (unnamed nodes are warned) | | `actuators.defs` | unknown joint type, a moving joint with no axis, or a revolute or prismatic joint with no limits | | `animation.clips` | an unnamed clip, too many clips, or a clip that is too long | | `geometry.*` | non-finite positions (degenerate and duplicate meshes are warned) | | `lod.levels` | LOD0 missing (LODs that do not get smaller are warned) | | `conditions.embedded` | an embedded state map that names missing clips or nodes | **B. Game structure check** (`tools/validators/unit-structure.mjs`, rules in `GAME:data/rules/unit-structure.json`). It scores out of 100: parts 35, pivots 15, unwelded 10, closed 10, scale 8, facing 7, ground 5, budget 5, team colour 5. Only **100** earns `STRUCTURED`. The other verdicts are `PARTIAL`, `STATIC` and `BROKEN` (`:2,10,65-76`). **C. Forge quality gate** (`tools/unit-forge/quality-gate.mjs`, rules in `GAME:data/rules/unit-forge.json:114-165`). It catches box figures, untextured meshes and single flat colours. ## 14. Unit creation checklist Before you start: - [ ] Find the unit's row in `unit-dimensions.json` and note `fit`, the figures and `confidence`. - [ ] Find the castdb canon render or blueprint for the look. - [ ] Find or write the dossier (section 11). - [ ] Find the class (section 7.1) and its required parts and states (section 7). Modelling: - [ ] Metres, +Y up, nose to -Z, ground or waterline at Y = 0, centred on the origin. - [ ] Size within 2% of the reference on the fitted axis. - [ ] Every required part is a separate node with a contract name (section 5). - [ ] Every moving node's origin sits on its real hinge or axle. - [ ] Moving parts are not welded to the hull, and meshes are closed. - [ ] Empties are placed: `muzzle_01` per gun, two or more `fx_smoke`, plus `fx_dust` and `fx_exhaust` where they apply. - [ ] Team colour is on a `houseTint` material or a `teamcolor_*` mesh. - [ ] `*_destroyed` swap is present and hidden; `*_damaged` is optional. - [ ] Triangle count fits the budget class (geometry standard). Rig and animation: - [ ] The rest pose is the bind pose: gear down, wings spread, doors shut, gun level and forward. - [ ] Every positional clip runs one way, from rest to deployed. Clip names contain the keywords for the class's states. - [ ] Spinning parts have a loop clip or a spin-node name. - [ ] Each moving node has a joint in `extras.actuator`, with type, axis and limits. - [ ] Each joint has one driver: a clip or the rig, not both. Export: - [ ] Uncompressed GLB, embedded PNG or JPEG textures, glTF 2.0. - [ ] `asset.extras.unit_dimensions` is filled in. - [ ] The model validator passes, or each warning is explained. - [ ] Review stills: - a ground plane, a -Z front arrow and a 1.8 m human for scale (`DR:.../ADDENDUM-opus-02.md:9`); - a `_posed` still of each moving part at its limit, plus a `_34rear` view (`ADDENDUM-opus-03.md:16,21`); - grey-clay stills at 50 m and 120 m framing, and a 393x852 in-game crop (`GAME:data/rules/unit-forge.json:110`); - one set per required state. **PROPOSED:** this per-state requirement has no source yet. Submission: - [ ] GLB(s), dossier `.md` and the submission sheet (section 15). ## 15. Submitting to the asset library Today the asset library is the estate model browser and asset manager. It stores files in object storage and registers each version in a database (`ESTATE:docs/assets/PULL-IN.md`, sections 1-3). The public API at `assets.jslc.io` is **read-only** (`VALIDATOR:config/validator.config.json`). Dossier upload goes through an API on the local network only (`ESTATE:docs/assets/DOSSIER-STANDARD.md:97-112`). So an **outside designer cannot upload directly.** **PROPOSED process:** 1. Hand the files to the project contact. Send: - the GLB(s); - the dossier; - a short submission sheet giving the roster id, class, nose axis, the reference dimensions used and their source, the clip list with the meaning of 0 and 1 for each clip, any open-decision values used, and the licence and author of any borrowed parts. 2. An agent checks the SHA-256 for duplicates (`PULL-IN.md:43-58`). It then adds the model as a new asset version: - with the additive ingest tool `ESTATE:tools/model-ingest/README.md` (`ingest-version.cjs`, which never overwrites, writes a receipt, and has a `--verify` step); - or with the publish path `DR:svc_skills/model-publish-path/SKILL.md`. It uploads the dossier through the dossier API, runs the three checkers in section 13, and records the verdicts. The master is stored as the asset version; game copies are linked derivatives (`ESTATE:skills/blender-model-builder/SKILL.md:225`). 3. The model enters the game only through a manifest entry, after review. ## 16. Sources and known gaps - Game rule files were read from the live game tree on 2026-10-20. Most are marked `assumedDefault: true` under ADR-010, which means they are tunable defaults, not fixed law. - The dossier standard sits at `docs/assets/DOSSIER-STANDARD.md` in the z4yin-files **main checkout**. It is not in the `cc-dispatch-pages` worktree. - The recall index (`tools/recall/ask.mjs`) was built on 2026-10-13 and is 7 days stale. The worktree copy has no index. The estate `estate_find` search returned "Read failed", and `estate_context_search` returned no hits. So "no source found" in this document means not found in the files listed above. It does not prove that no rule exists. - No source was found for these, so they are marked PROPOSED: infantry clip names, the control-surface clip neutral, game-copy file names, the sheets and stills needed per state, the outside-designer hand-off, and one muzzle per weapon. ================================================================================ FILE: MAPS STANDARD: standards/standards_maps/README.md ================================================================================ # Map standard (standards_maps) What a map for JCOM and Conquer must be, so a designer (human or model) can build one the engine draws, paths and plays at the right scale. Status: draft 1, written 2026-10-20 from the live game tree. Nothing here is decided by the owner yet. Rules marked **RULE** quote what the engine or rules data does today. Rules marked **PROPOSED** are the recommended standard and have no source yet. Conflicts are numbered `OD-n` and listed in `OPEN-DECISIONS.md`. The machine-readable form is `map-standard.json`. Source tree abbreviations used in citations: - `game:` = the live game tree, `M:\z4yin-work-overflow\hermes-share\codex-lanes\asset-split\sol-maps\work` (read on 2026-10-20). - `share:` = `M:\z4yin-work-overflow\hermes-share`. The game repo docs `ADR-010`, `ADR-011` and `docs/guides/MAP-PIPELINE.md` are cited by the rules files but were not found in the live tree or the share (routes tried: live-tree find, share find to depth 8, `tools/recall/ask.mjs` (index stale, no match), estate_find (read failed)). Their content here is only what the rules files quote from them. --- ## 1. The numbers in one table | Quantity | Value | Source | |---|---|---| | World units per real metre (`metreToWorld`) | 0.4 | game:data/rules/world-scale.json:9 | | Legacy tile size (`metresPerTile`) | 5 m | game:data/rules/world-scale.json:10 | | Legacy tile in world units (`tileWorldSize`) | 2 (= 5 x 0.4) | game:data/rules/world-scale.json:11, :28 | | Map cell size | per map, `metresPerCell` (see 2.2) | game:skirmish/src/layers/scale.ts:21-31 | | Fallback cell size when a map names none | 5 m | game:data/rules/map-layers.json:10 | | Default height unit | metre | game:data/rules/world-scale.json:12 | | Raw 0-255 height sample | 0.2 m | game:data/rules/world-scale.json:18-19 | | Mesh cap | 512 segments per axis | game:data/rules/world-scale.json:21 | | Cliff slope | 32 degrees | game:data/rules/world-scale.json:23 | | Snow line | 180 m | game:data/rules/world-scale.json:22 | | Water-plane lift | 0.125 m | game:data/rules/world-scale.json:25 | | Shallow water | depth 0 to 3 m | game:data/rules/nav-domains.json:10 | | Amphibious max depth | 5 m | game:data/rules/nav-domains.json:14 | | Ship under-keel clearance | 1 m | game:data/rules/nav-domains.json:12 | | Naval / forge map cells | 20 m | game:data/rules/naval-maps.json:10, game:data/maps/specs/forge_jslc00_greenland.json | | Gameplay weapon/sight range | fraction of the map's shorter axis, in cells | game:data/rules/game-ranges.json:2-7, game:skirmish/src/gameRanges.ts:33-35 | The HQ map, `greenland_naval_bunker.json`, is 320 x 200 cells at 20 m, so 6.4 x 4.0 km. Its heights run from -18 m to 140 m in metres with `heightScale` 1. In world units it is 2560 x 1600. --- ## 2. Scale ### 2.1 One metre transform **RULE.** Terrain, water and unit models share one transform: world = metres x `metreToWorld` (0.4). The old terrain-only vertical scale of 0.25 is retired. (game:data/rules/world-scale.json:4, :27) **RULE.** `tileWorldSize` must equal `metresPerTile x metreToWorld`. Code derives it. Do not author it. (game:data/rules/world-scale.json:28; game:skirmish/src/terrain.ts:42, game:skirmish/src/unitDimensions.ts:22) These values are marked "assumed default, tunable (ADR-010)" (game:data/rules/world-scale.json:4-8). Until the owner fixes them (OD-1), a map must not hard-code world units. Author in metres and cells only. ### 2.2 Cell size (metres per cell) **RULE.** The engine resolves a map's cell size in this order (game:skirmish/src/layers/scale.ts:16-31): 1. the map's top-level `metresPerCell`; 2. `meta.cellSizeM`; 3. the per-file table `metresPerCellByMapFile` in game:data/rules/map-layers.json:12-16; 4. `fallbackMetresPerCell`, 5 m (game:data/rules/map-layers.json:10). The renderer multiplies by `metreToWorld` to get `cellWorldSize` (game:skirmish/src/layers/scale.ts:58; game:skirmish/src/main.ts:606-608). The sim reads the same value and converts legacy 5 m-tile values with `rangeCellScale = 5 / metresPerCell` (game:skirmish/src/sim.ts:1476-1477). **PROPOSED.** Every map sets top-level `metresPerCell` and the same value in `meta.cellSizeM`. A missing value is a validation error, not a fallback. Today 30 of the 58 playable sheets set neither and silently draw at 5 m (audit in section 15; OD-2). Cell sizes in use today: | Cell | Used by | Ground extent at that cell | |---|---|---| | 5 m | legacy and retail-derived sheets, AoI campaign sheets | 128 cells = 640 m | | 20 m | naval sheets, forge Greenland/Gaza, HQ, undersea sheets, kharg_approach | 320 cells = 6.4 km | | 30 m | forge_p01_queenmaud | 128 cells = 3.84 km | | 80 m | forge_bandar_abbas pair | 320 cells = 25.6 km | | 500 m | redsea_surface, camp-07-silicon-oasis | 200 x 512 cells = 100 x 256 km | **PROPOSED.** Standard cell sizes are 5 m (infantry/base scale, legacy), 20 m (default for new real-world tactical maps) and 80 m (operational coast). Theatre overview sheets may use 500 m but are not tactical. Any other value needs a note in `meta.notes` (OD-3). ### 2.3 Gameplay ranges scale with cells, not metres **RULE.** With the default range policy `generals-map-v1` (game:data/rules/sim-performance.json:5), weapon, sight and sensor ranges are fractions of the map's shorter axis measured in cells. The reference span is 4000 original Generals world units (game:data/rules/game-ranges.json:2-7; game:skirmish/src/gameRanges.ts:33-35; game:skirmish/SIM-PERFORMANCE.md:39-41). A rifle is 0.025 of the short axis: 3.2 cells on a 128-cell map, 5 cells on the 200-row HQ. Consequence for designers: the cell count of the short axis, not the metre size, decides how far units shoot and see. A 128 x 128 map at 20 m has the same engagement geometry, in cells, as one at 5 m. A map can pin `rangePolicy: "legacy"` for fixed-metre balance (game:skirmish/src/sim.ts:425, :1478-1479). Whether ranges should become real metres is OD-4. ### 2.4 Height units and heightScale **RULE.** World Y = sample x `heightUnitToMetre[heightmapUnits]` x `heightScale` x `metreToWorld` (game:skirmish/src/main.ts:677-690; game:skirmish/src/terrain.ts:466-469). Allowed units: `metre`/`metres`/`meters`/`m` (x1) and `raw0_255`/`0-255` (x0.2) (game:data/rules/world-scale.json:13-20). **RULE (data).** "A map must set heightmapUnits. Map size and corner values are not used to guess the unit." (game:data/rules/world-scale.json:29). The renderer defaults a missing unit to metre (game:skirmish/src/main.ts:677-679). The map checker guesses instead: a sheet with no unit, cells under 10 m and integer samples at or below 255 is read as raw 0.2 m (game:tools/map-check/engine.ts:521-535; game:data/rules/map-check.json:89-90). So `starter.json` and `kharg_island.json` are 5x taller in the game than in the checker (OD-5). **RULE.** `heightScale` (alias `elevationScale`) multiplies samples and also `waterLevel` (game:skirmish/src/terrain.ts:108-116, :469; game:skirmish/src/main.ts:853). Nav depth uses raw samples and raw `waterLevel` with no unit or scale (game:skirmish/src/nav/grid.ts:26-47). **RULE (forge).** `heightScale` = the spec's `verticalExaggeration` when XZ scales with cell size (game:data/rules/terrain-forge.json:61-67). Default exaggeration is 1.0. **PROPOSED.** Heights are stored in true metres above the map datum, `heightmapUnits: "metre"`, `heightScale: 1`. Any relief exaggeration is an art pass recorded in `meta` and kept out of `heightmap`, so the sim, nav and map-check all see true metres. Today `forge_jslc00_greenland` (1.5), `forge_aoi01_gaza` (1.5) and `forge_p01_queenmaud` (1.2) exaggerate through `heightScale`, and `forge_bandar_abbas` bakes a 4x exaggeration above 25 m into the stored heights (game:skirmish/MECHANICS-coastal-art.md:5). The HQ (`greenland_naval_bunker`) uses 1 (OD-6). --- ## 3. Grid **RULE.** A map is a raster of `width` columns by `height` rows. `tiles[y][x]` and `heightmap[y][x]` are row-major, one entry per cell, both `height` rows of `width` entries (game:skirmish/src/sim.ts:420-437; game:skirmish/src/nav/grid.ts:31-38). Heights are sampled at cell centres: cell (x, y) centre is at ((x + 0.5) x cell, (y + 0.5) x cell) metres (game:skirmish/src/layers/scale.ts:77-86). **RULE.** The terrain mesh and height query share the 512-segment cap per axis (game:data/rules/world-scale.json:21, :30). A side longer than 512 cells is downsampled when drawn. **RULE (forge).** `textureGrid` may be finer than the cell grid: k texels per cell, k = round(metresPerCell / 4 m), capped at 1.1 M texels; `textureGridScale` records k (game:data/rules/terrain-forge.json:129-133; `forge_jslc00_greenland.json` has `textureGridScale: 4`). Sizes in use: 64x64, 104x128, 128x73, 128x85, 128x128, 120x120, 160x160, 240x160, 320x200, 320x320, 360x240, 100x180, 200x512. **PROPOSED.** Width and height are each 64 to 512. Skirmish 1v1 short axis at least 128 cells; 4-player at least 160. (The 128 floor follows the Generals-derived maps, which are 128 cells on the long side; the 160 figure has no source.) --- ## 4. Coordinate frame and north **RULE.** Column `x` increases east. World X = (x + 0.5) x cellWorldSize - worldWidth / 2; world Z likewise from row `y`. The map is centred on the world origin (game:skirmish/src/layers/scale.ts:81-93; game:skirmish/src/main.ts tileToWorld after line 645). Y is up. Models are glTF Y-up, heading -Z, starboard +X (share:ARCHITECTURE-BRIEFING.md:43-44). **RULE.** Positions in `starts`, `objects`, `units`, mission `startingForces` and `areas` are tile-space (cell) coordinates, the sim's Unit x/y (game:data/missions/schema.json description). Metre twins `xM`, `yM` are optional and win when present in layer code (game:skirmish/src/layers/world.ts:84-92). **CONFLICT on north (OD-7).** The default camera sits on +Z looking toward -Z with yaw 0 called "north-up" (game:skirmish/src/render/cameraEye.ts:16-18; game:skirmish/src/data/keyscamera-rules.json:6; game:skirmish/src/main.ts:2279-2280), so row 0 is at the top of the screen. Mission scripting agrees: the `north` edge is `y = 0` (game:skirmish/src/script/engine.ts:246-247). But the layer and nav code and the naval generator say row 0 is south (game:skirmish/src/layers/scale.ts:72; game:skirmish/src/nav/types.ts:43; game:map-import/naval/README.md:24), and glacier dressing treats `y + 1` as north (game:skirmish/src/render/glacierDressing.ts:76). Forge maps are also rotated: `forge_jslc00_greenland` "map up points NNW" (`rotationDeg` 335), Gaza 34.5 degrees (game:data/maps/specs/*.json:19). **PROPOSED.** Row 0 is the top of the screen at default camera. Store true bearing of map-up as `meta.northBearingDeg` (degrees clockwise from true north to map-up; 0 when map-up is north). Real-world sheets record `meta.source.center` and `rotationDeg` as the forge specs do. --- ## 5. Layers ### 5.1 Tile layer (`tiles`) **RULE.** Codes: 0 open, 1 water, 2 road, 3 cliff. Water and cliff block ground movement (game:skirmish/src/sim.ts:410-413; game:skirmish/src/terrain.ts:33-37; legend repeated in game:data/rules/naval-maps.json `tiles`). No other value is read by the skirmish engine. The older `jslcnc` `beta/` engine used a 0-8 legend with `HARBOR_DOCK` = 8 (share:DOLPHIN-ANSWERS.md:102). That format is not read by the live skirmish engine (OD-12). ### 5.2 Height layer (`heightmap`) and water level **RULE.** `waterLevel` is the sea surface in height-sample units. Depth = waterLevel - height (game:skirmish/src/nav/grid.ts:26-47). Undersea sheets put the datum at -820 m with `datumM` and `waterLevel` -820 (e.g. `camp-aoi-05-tunnel-network.json`). ### 5.3 Navigation domains (derived, not authored) **RULE.** Nav builds a domain per cell from tiles, heights and water level (game:skirmish/src/nav/grid.ts:23-56; game:data/rules/nav-domains.json): | Domain | Condition | |---|---| | cliff | tile 3 (always blocked to surface movers) | | deep | depth > 3 m | | shallow | 0 < depth <= 3 m | | land | depth <= 0 | Movers: | Profile | Passes | |---|---| | ground | land (open and road), not water tiles, not cliff | | amphibious | land plus water to 5 m depth (game:data/rules/nav-domains.json:14) | | hover | land and water (nav/grid.ts header) | | ship | water with depth >= class draft + 1 m, plus beam clearance (game:data/rules/nav-domains.json:12, `shipClasses`) | | air | everywhere (game:data/rules/map-check.json `reach.airAlwaysReaches`) | Ship drafts in use: capital 11.9 m, destroyer 10.4 m, LHD 8.1 m, replenishment 11.6 m, submarine 6 m, corvette 5 m, small craft 2 m (game:data/rules/nav-domains.json `shipClasses`). A channel for a carrier therefore needs at least 12.9 m of water and 41 m of beam (about 3 cells at 20 m). If a water tile has no height sample, nav reads it as 4 m deep, so it counts as deep (game:skirmish/src/nav/grid.ts:40-42). A* steps cost 10 orthogonal and 14 diagonal. A blocked start or goal snaps up to 8 cells (game:data/rules/nav-domains.json:15-19). ### 5.4 Roads **RULE.** Roads are tile 2. Civilian traffic runs on tile 2 roads and treats tile 1 as water (game:skirmish/src/civilians/roadGraph.ts:3-6; `premade.civilianTraffic` in premade maps). Forge routes roads by slope cost and avoids water and cliffs (game:data/rules/terrain-forge.json `roads`). ### 5.5 Structures, footprints and aprons **RULE.** Placed objects are `objects[] {type, x, y, faction, id?}`. Building footprints come from unit dimensions in metres. The collision radius is the circumradius of length x width (game:data/rules/building-footprints.json metadata). Spacing and starting offsets are authored in legacy 5 m tiles and scaled to the map cell (game:skirmish/src/sim.ts:1582-1587). **RULE (base pads).** Forge flattens a base pad of 4 to 14 cells with a 10-cell blend, a 3 x 10-cell factory exit lane and a 2 m freeboard (game:data/rules/terrain-forge.json `pads`). map-check wants a 40 m buildable radius with no more than 25 m of relief (tan 32 degrees x 40 m) and a factory exit of at least 2 clear cells (game:data/rules/map-check.json `base`, `layout`). **RULE (naval aprons).** Ship goals are dock marker objects: `CapitalDrydock`, `CapitalBerth`, `SubmarinePen`, `SeaDragonDock`, `GlacierDrydock`, `ArcticSubPen`, `CaissonGate`, `Dock` (game:data/rules/nav-domains.json `dockMarkerTypes`). Drydock, berth and pen sizes are in game:data/rules/naval-maps.json `counts`. ### 5.6 Zones, layers stack, links **RULE.** `zones[]` are rectangles `{id, shape: "rect", x0M, y0M, x1M, y1M, role|kind}` in metres (HQ), or `{x0, y0, x1, y1}` in cells (premade maps). A multi-sheet world uses `layers {activeLayerId, stack[], verticalDatums[], elevator, portal}` (game:data/rules/map-layers.json; HQ `layers`). A map with no `layers` block becomes one layer, `single` (game:data/rules/map-layers.json `singleSheetLayerId`). **PROPOSED.** All zones and links use metres (`x0M` etc.) with cells derived. Mixed cell/metre zones are a validation warning. --- ## 6. Water rendering requirements The owner caught maps drawn with no visible sea. This is how the renderer decides whether to draw water. **RULE.** The terrain module draws water only when one of these is true (game:skirmish/src/terrain.ts:333-358, :480): 1. `waterLevel` (after unit and scale) is above 0: a full-map water plane; 2. the sheet is a real-surface or coastal sheet (`meta.realSurface`, coastal terrain): a continuous depth-clipped plane; 3. otherwise, quads only over cells whose tile is 1. So a sheet with `waterLevel` 0 whose sea cells are not tile 1 and which is not a real-surface sheet draws **no water**, even where heights are below 0. The fallback path (no terrain module) draws water only when the water level is above 0 or the layer datum is below 0 (game:skirmish/src/main.ts:815-826, :853-865). E02 (the HQ playable) owns its own basin and gets no raster water (game:skirmish/src/terrain.ts:480). **PROPOSED (water rules every map must pass):** 1. Every cell with height < `waterLevel` that is meant to be sea is tile 1. Every tile-1 cell has height < `waterLevel` (a dry "water" tile is an error). 2. A map whose `waterLevel` is 0 or below has tile-1 cells or `meta.realSurface`; otherwise it must declare `meta.dryInterior: true`. 3. Sea floor is at least 3 m deep near shore and deepens by 1.5 m per cell to 60 m (forge defaults, game:data/rules/terrain-forge.json `height`), so nav sees shallow then deep, not a cliff. 4. Shipping lanes meet the draft rule in 5.3 for every ship class the map spawns. 5. Water share: below 15 % water is an obstacle, not a naval domain (game:data/rules/map-check.json:148). A naval map is at least 15 % water. 6. The theatre sets `waterColour` (game:data/rules/theatres/disko_bay.json). 7. A screenshot at default camera shows water wherever the minimap shows water. This is a manual check until a tool does it. --- ## 7. Real-world sourcing **RULE (sources in use).** | Layer | Source | Where named | |---|---|---| | Land elevation | Copernicus DEM GLO-30 (DSM), AWS open data; Terrarium tiles for Bandar Abbas | game:data/rules/terrain-forge.json `sources.copernicus30`; game:data/maps/specs/forge_bandar_abbas.json `source.note` | | Bathymetry and coarse topography | GMRT Synthesis, ocean gaps filled with GEBCO | game:data/rules/redsea-map.json `sources.elevation` | | Land cover | ESA WorldCover 10 m 2021 v200 | game:data/rules/terrain-forge.json `sources.worldcover` | | Roads | OpenStreetMap highways via Overpass (ODbL) | game:data/rules/terrain-forge.json `sources.osm`; game:data/rules/redsea-map.json `sources.vectors` | | Ground textures | ambientCG CC0 | game:data/rules/terrain-forge.json `sources.ambientcg` | **RULE (projection).** Sheets use a local equal-metre grid: equirectangular at a reference latitude (`redsea-map.json` `projection`) or an "80 m square local tangent grid" (Bandar Abbas spec). The spec records `source.center {lat, lon}` and `rotationDeg`. **RULE (honesty).** Authored edits that are not survey data are labelled as such in `meta.realSurface` or the spec (`gameplayEdits`, "CONCEPT ... not surveyed"; game:map-import/real_terrain.py:227-261). Keep attribution strings in `meta.attribution`. **RULE (licence).** Licence is not a filter for this personal project (owner memory, 2026-10-19). Attribution is still recorded. New maps come from `tools/terrain-forge` with specs in `game:data/maps/specs/` and theatre looks in `game:data/rules/theatres/`. The naval generators are superseded (game:map-import/naval/README.md:3). The forge tool itself is not in the live tree (OD-11). --- ## 8. Map and theatre metadata **RULE (fields in use).** | Field | Meaning | Example | |---|---|---| | `id` | file stem, unique | `greenland_naval_bunker` | | `name` | display name | "The Forge in the Ice (Eqip Sermia, Disko Bay)" | | `theatreId` | key into the curated theatre list | `disko_bay` | | `metocTag` | weather profile; HQ is `hq_ideal` | HQ | | `datumM` | vertical datum of the sheet | 0, -820 | | `meta.cellSizeM` | twin of `metresPerCell` | 20 | | `meta.extentM {widthM, heightM}` | width x cell, height x cell | 6400 x 4000 | | `meta.axis` | free-text local frame | HQ | | `meta.tileLegend` | copy of the tile codes | HQ | | `meta.rules` | rules file that generated the sheet | `data/rules/naval-maps.json` | | `meta.realSurface` | art source classes and honesty notes | many | | `meta.notes[]` | design notes | many | | `premade {sourceMap, sourceKind, kept, edit}` | provenance of a sheet derived from another | premade_* | | `campaign`, `missionId` | campaign links | camp-aoi-05 | Theatre ids in the player list (game:data/maps/curated.json `theatres`): persian_gulf, red_sea_gaza_aqaba, greenland, diego_garcia, desert, disko_bay, gaza, litani, gulf_of_aden, queen_maud_land, weddell_sea, mount_erebus, wilkes_land, south_georgia, mid_atlantic_ridge, svalbard. Theatre look files exist for bandar_abbas, disko_bay, gaza, greenland and queen_maud_land (game:data/rules/theatres/). A theatre may `extend` another (disko_bay extends greenland). **RULE (curated list entry).** `{id, label, file, players, theatre, preview, kind}`, previews under `/maps/previews/` (game:data/maps/curated.json). **PROPOSED (required metadata).** `id`, `name`, `theatreId`, `metresPerCell`, `heightmapUnits`, `heightScale`, `waterLevel`, `meta.cellSizeM`, `meta.extentM`, `meta.northBearingDeg`, and `meta.source` (`{kind: "dem" | "synthetic" | "retail-derived" | "hand", center?, rotationDeg?, attribution?}`). --- ## 9. Missions and spawn data **RULE.** Skirmish starts are `starts[] {player, x, y, faction?}` in cells. Map forces are `units[] {type, x, y, player | faction, rosterId?}` (game:skirmish/src/sim.ts:427-436). **RULE.** A mission is a separate file in `game:data/missions/` and names its map: `map {file, theatreId?, variant?: "linked3" | "continuous"}`. It carries `players`, `startingForces`, `areas {id, xMin, yMin, xMax, yMax}` in tile space, `objectives`, `triggers` and times in seconds (game:data/missions/schema.json). It may set `replaceMapForces`. HQ missions may opt into weather with `environment.metocTag`; otherwise conditions are ideal (owner ruling 2026-10-19 in the schema). **RULE.** Fresh skirmish uses `Sim.spawnStartForces`; starts on isolated beaches are relocated by `game:data/rules/skirmish-starts.json`, which wants at least eight clear cells around a start. **RULE (map-check spawn gates).** Skirmish start pairs at least 15 SAGE cells apart; no enemy defence within 15 SAGE cells at t=0; nearest oil within 60 SAGE cells and not beyond the enemy; at least one oil per base-building start (game:data/rules/map-check.json `spawn`, `resources`). SAGE cells convert with 10 SAGE world units per cell over the map's metres per cell (`units`). --- ## 10. File format One JSON file per sheet in `game:skirmish/maps/`. Optional sidecars: `.blend.json` (terrain blend from a World Builder `.map`), previews in `skirmish/public/maps/previews/`. A generator spec lives in `game:data/maps/specs/.json`. | Field | Type | Required today | Required by this standard | |---|---|---|---| | `width`, `height` | integer | yes | yes | | `tiles` | int[height][width], 0-3 | yes | yes | | `heightmap` | number[height][width] | no (flat if missing) | yes | | `waterLevel` | number | yes | yes | | `starts` | array | yes | yes | | `objects`, `units` | array | yes | yes | | `metresPerCell` | number > 0 | no (5 m fallback) | yes | | `heightmapUnits` | enum | no (metre in game, guessed in check) | yes | | `heightScale` | number | no (1) | yes, 1 | | `id`, `name`, `theatreId` | string | no | yes | | `meta.cellSizeM`, `meta.extentM` | | no | yes | | `textures`, `textureGrid`, `textureGridScale` | | no | optional | | `zones`, `layers`, `datumM`, `metocTag`, `rangePolicy` | | no | optional | `map-standard.json` is the checkable form. Byte cap: naval generator 5 MB (game:data/rules/naval-maps.json:13). Largest sheet today: forge_bandar_abbas, 4.0 MB. --- ## 11. World Builder (C&C3 / SAGE) compatibility - Retail Generals maps were imported as 128-cell sheets with `.blend.json` blend sidecars that name the `.map` source (`alpine_assault`, `barstow_badlands`, `blue_zone_dam`, `tournament_desert` from `TD_CnC3`, `twilight_flame`). They have no `metresPerCell` and draw at the 5 m fallback. They are blocked from the player list (game:data/maps/curated.json `blockedIds`). - Premade maps reuse them (`premade_redsea_sar` from `tournament_desert`, recorded in `premade.sourceMap`). - map-check measures against SAGE: 1 heightmap cell = 10 SAGE world units, and uses the Generals column of the retail measurements (game:data/rules/map-check.json:18-19, `eraColumn`). - The client has a World Builder binary reader for presentation data (game:skirmish/src/mechanics/presentation/worldbuilder.ts). - A Zero Hour `.map` importer (terrain, water, objects, waypoints, triggers, scripts to our JSON) was in progress on lane anl9/ans9 and was not merged as of the briefing (share:ARCHITECTURE-BRIEFING.md:55; share:DOLPHIN-ANSWERS.md:104). Its tooling is under `M:\z4yin-work-overflow\worldbuilder`. - The metre size of a retail SAGE cell is not recorded in any source read, so the true scale of retail-derived maps is undetermined (OD-8). --- ## 12. Performance budgets | Budget | Value | Source | |---|---|---| | Desktop smoke gate | 30 fps target, p95 frame <= 50 ms, at most 5 stalls over 100 ms per 10 s sample | game:data/rules/runtime-budgets.json:8-11 | | JS heap / GPU memory | 1024 MB / 2048 MB | game:data/rules/runtime-budgets.json:12-13 | | Reference GPU | NVIDIA T1000, 1920x1080 | game:data/rules/runtime-budgets.json; game:data/rules/client-performance.json:2 | | Desktop display | 60 Hz minimum adaptive target | game:data/rules/client-performance.json:18 | | Terrain mesh | 512 segments per axis | game:data/rules/world-scale.json:21 | | Texture grid | 1.1 M texels | game:data/rules/terrain-forge.json:130 | | Props per sheet | 3500 max; client scatter off at 80+ authored props | game:data/rules/terrain-forge.json:138-141 | | Map file | 5 MB (naval generator cap) | game:data/rules/naval-maps.json:13 | | Model triangles lod0 | structure 6000, vehicle 8000, aircraft/ship 15000, infantry 3000 | game:data/rules/scale-overrides.json `desktopTriCaps` | --- ## 13. Map creation checklist 1. Pick the theatre (`theatreId` from section 8). Add a theatre look file if the theatre has none. 2. Pick the cell size (section 2.2) and the grid so the short axis gives the engagement size you want (section 2.3). 3. Write a spec in `data/maps/specs/.json`: size, `metresPerCell`, `verticalExaggeration` 1, `waterLevelM`, `source {kind, center, rotationDeg}`, starts, areas, roads. 4. Source heights in metres from the DEM and bathymetry in section 7. Record attribution. 5. Set `heightmapUnits: "metre"`, `heightScale: 1`, `metresPerCell`, `meta.cellSizeM`, `meta.extentM`, `meta.northBearingDeg`. 6. Paint tiles: every sea cell tile 1 and below `waterLevel`; cliffs tile 3 where slope exceeds 32 degrees; roads tile 2. 7. Check water (section 6) for each ship class the map spawns. 8. Place starts, oil and supply per section 9; flatten base pads per 5.5. 9. Put zones and links in metres. 10. Run the validators in section 14. Fix every error. 11. Load the map in the game and take a screenshot at default camera. Confirm water, scale of a known unit next to a known structure, and north. 12. Add a curated entry and preview if it is player-facing. --- ## 14. How a map is validated | Tool | Command (from the game root) | What it checks | |---|---|---| | map-check | `node tools/map-check/check.mjs [--mission ]`, `--all`, `--curated`, `--gate` | buildable base pad and relief, oil/supply/tech travel, ground reach (unless naval-only), chokes and wall-offs, start distance and spawn safety, fairness, passable share, water share, base layout score, optional AI play balance. Uses the real nav stack (game:tools/map-check/cli.ts:1-8; game:data/rules/map-check.json) | | scale print | `node /mapscale.mjs ...` (dolphin4 helper, `C:\Users\MaxGlasser\.claude\jobs\44b57189\tmp\mapscale.mjs`) | prints every scale field per map | | naval load check | `vite-node map-import/naval/check_naval_maps.ts` | naval sheets load (superseded generator) | | curated validation | `data/maps/curated.json` `validation` | 10-minute hard-AI matches for faction pairs plus map-check | Naval-only sheets are listed in `navalOnlyIds` and skip the ground-reach gate (game:data/rules/map-check.json:71). **PROPOSED.** Add a schema check that reads `map-standard.json`. It would fail a map that has no `metresPerCell`, no `heightmapUnits`, a `heightScale` other than 1, a height range out of bounds for its theatre, mismatched grid sizes, a dry water tile, or a sea with no drawable water. --- ## 15. Audit of current sheets (2026-10-20) Measured from `game:skirmish/maps/*.json`. "mpc" is the cell size the engine resolves. "-" means not set. | Map | Grid | mpc (source) | units | hScale | height m | water % | Flags | |---|---|---|---|---|---|---|---| | greenland_naval_bunker (HQ) | 320x200 | 20 | metre | 1 | -18..140 | 12 | none | | forge_jslc00_greenland | 320x200 | 20 | metre | 1.5 | -60..530 | 15 | heightScale drift | | camp-p04-disko-bay, camp-06-apollo-chronocast | 320x200 | 20 | metre | 1 | -80..2000 | 25 | none | | diego_garcia_surface / abyss | 120x120 / 360x240 | 20 | metre | 1 | | 68 / 65 | none | | kharg_approach | 160x160 | 20 (meta only) | - | - | 12..116 | 64 | no units | | kharg_island | 160x160 | 5 fallback | - | - | 12..116 | 64 | same data as kharg_approach at 1/4 size; checker reads heights as 0.2 m | | kharg_shiraz, ep1-3, theatre, camp-14-iron-ledger | 160-320 | 5 fallback | - | - | up to 2997 | 8-52 | 3 km relief on a 0.8-1.6 km sheet | | camp-aoi-05-tunnel-network | 240x160 | 5 | metre | 1 | -842..-720 | 43 | built from 20 m redsea_undersea, `meta.meshNote` says 20 m | | premade_redsea_sar | 104x128 | 5 fallback | - | 1 | 0..43 | 54 | retail-derived, scale undetermined | | premade_* others, camp-p01/02/05/06/09, camp-02/04/09/11b | 104-128 | 5 fallback | - | 1 | | | no cell size | | forge_aoi01_gaza | 128x85 | 20 | metre | 1.5 | | 13 | heightScale drift | | forge_p01_queenmaud | 128x128 | 30 | metre | 1.2 | 1114..1846 | 0 | heightScale drift; 30 m nonstandard | | forge_bandar_abbas (+invasion) | 320x320 | 80 | metre | 1 | -35..1289 | 24 | 4x exaggeration baked into heights | | redsea_surface | 200x512 | 500 | metre | 1 | -860..1338 | 6 | theatre overview scale | | camp-07-silicon-oasis | 100x180 | 500 | metre | 1 | 29..999 | 0 | 500 m tactical sheet | | starter | 64x64 | 5 fallback | - | - | 8..200 | 7 | checker reads heights as 0.2 m | ================================================================================ FILE: LOGIC STANDARD: standards/standards_logic/README.md ================================================================================ # Logic standard: game simulation and code Status: draft, 2026-10-20. Written for dolphin4 by a read-only subagent. The owner decides every item in `OPEN-DECISIONS.md`. This standard is for JCOM and Conquer, the browser RTS in TypeScript and Three.js. It covers: - how the simulation is built; - what every unit must be able to do; - how to add a mechanic. Designers, ChatGPT and other outside builders should design to it. Companion files in this folder: | File | What it is | |---|---| | `logic-standard.json` | Machine-readable list of unit capabilities, unit states and rules-file conventions. | | `OPEN-DECISIONS.md` | Places where sources disagree, with values, file:line and a recommendation. | Sibling standards: - `../standards_units/` covers models, rigs, animation clips, visual damage and dimensions. - `standards_maps` covers maps. This file does not repeat them. ## How to read the citations Every rule cites where it comes from. A rule marked **PROPOSED** has no source yet and needs owner approval. Path prefixes: | Prefix | Root | |---|---| | `GAME:` | Live game tree, `M:\z4yin-work-overflow\hermes-share\codex-lanes\asset-split\sol-maps\work` (`skirmish/`, `data/`). This is the tree served at port 4380. | | `SKILL:` | `D:\z4yin\z4yin-files\.claude\skills\game-logic-rules\SKILL.md`. A byte-identical copy is at `hermes-share\codex-lanes\game-logic-rules\SKILL.md`. | | `ADR:` | `hermes-share\codex-lanes\ships-fix\work-apx9\docs\DECISIONS.md`. This is a lane copy of the game repo's ADR log. Two other lane copies are the same size (45,100 bytes). | | `SHARE:` | `M:\z4yin-work-overflow\hermes-share` | | `INBOX:` | `D:\z4yin\.inbox\dolphin4` | | `MEM:` | Owner rulings recorded in `C:\Users\MaxGlasser\.claude\projects\D--z4yin-z4yin-files\memory\` | | `BRIEF:` | This lane's brief from dolphin4, 2026-10-20 (owner 16:31: "standards for everything"). | Line numbers were read on 2026-10-20. The tree changes daily. --- ## 1. Architecture at a glance | Layer | Where | Rule | |---|---|---| | Simulation (authoritative) | `GAME:skirmish/src/sim.ts`, `GAME:skirmish/src/core/`, `GAME:skirmish/src/mechanics//`, `nav/`, `ai.ts`, `fog.ts`, `collision.ts`, `simHash.ts` | Decides all game state. It has no wall clock, no `Math.random` and no rendering (`GAME:skirmish/src/sim.ts:55-56`). | | Rules data | `GAME:data/rules/*.json` (138 files), and also `GAME:skirmish/src/data/*.json` (24 files) | Every tunable value lives here (ADR-010, `ADR:221-252`). See OD-L02 for the two locations. | | Rosters | `GAME:data/roster/*.json` | Unit ids, costs and stats per faction. Unsourced values carry "PROPOSED" or "Assumed default (ADR-010)" notes in their source map, for example `GAME:data/roster/jslc.json:1572`. | | Network | `GAME:skirmish/src/net/` (`lockstep.ts`, `relay.ts`, `desync.ts`, `rejoin.ts`) | Deterministic lockstep over a LAN WebSocket relay. The relay never runs game logic (`ADR:481-492`). | | Render | `GAME:skirmish/src/main.ts`, `render/`, `models.ts` | Reads sim state. It never writes it, except by issuing orders (`GAME:skirmish/src/sim.ts:56`). | | HUD | `GAME:skirmish/src/ui/`, `hud.ts`; design system in `SHARE:codex-lanes/hud-design` | See section 9. | The stack is TypeScript 5.7, three ^0.170, Vite ^6 and Vitest ^2.1 (`GAME:skirmish/package.json`). The game is one Vite app, `skirmish/`. It keeps internal module boundaries and has no monorepo (`ADR:60-75`). **Concepts are binding, implementations are not.** Torch's game concepts are normative, including determinism and 30 Hz lockstep. His code is reference only (`ADR:42-58`). **One sim, any front end.** The game is finished in TypeScript first (ADR-026, `ADR:548-556`). Unity may later host the same TS sim in V8, with no logic fork (ADR-025, `ADR:527-546`). C++ is a parked proposal behind a benchmark gate (`SHARE:CPP-SIM-CORE.md`; `SHARE:ADVICE-sol-cpp.md`). Write the sim so it can be bundled without the DOM or three.js (`ADR:535`). OD-L12 notes one place that breaks this today. --- ## 2. The simulation ### 2.1 Fixed tick - The rate is `TICK_HZ = 30` (`GAME:skirmish/src/sim.ts:310`, ADR-004 superseded to 30 Hz, `ADR:97-111`). - Durations are stored in **seconds** in rules data. They are converted to ticks with `secondsToTicks` (`GAME:skirmish/src/sim.ts:316-318`), which rounds. - Speeds are stored per second and converted once (`GAME:skirmish/src/sim.ts:575-585`). - A gameplay job that repeats runs inside `Sim.step` and counts ticks. Never use `setInterval` for it (`GAME:skirmish/src/core/MECHANICS-tick.md`, "How to change it"). - Use `periodDue(tick, periodTicks)` and `ticksForPeriodMs` (`GAME:skirmish/src/core/tick.ts:67-81`). - Presentation timers may use the browser clock: HUD paint, panel refresh and autosave polling. They must not change sim state (`GAME:skirmish/src/core/MECHANICS-tick.md`). **Order of work in `Sim.step`** (`GAME:skirmish/src/sim.ts:3268-3420`). A new system must name the slot it runs in: 1. `tick++`, then admit due commands (`applyDueCommands`). 2. Garrison safety, stealth timers, tunnel heal and rehost. 3. World module, construction (`stepConstruction`), economy (`stepEconomy`). 4. AI controllers in ascending player id (`:3287`), then EW prep and the sight cache. 5. Per unit, in id order (`unitsInIdOrder`, `:3094`): cooldown, `stepUnit`, vision observer, target grid. 6. Garrison entries, tunnel attach, UNREP, transport, tractor, then **collision**. 7. HELIOS defence, weapon flights, power spacing, subsystem fire and flood. 8. Kill pass, economy toggles (PR, wear, upkeep), compaction of dead units. 9. Ops, subterranean, factions, docks and sea gate, air module, carrier ops, native runway, flight finish, aircraft heading. 10. Script, EW, weapon effects, kill pass, portal doors, naval win, `publishEvents`. 11. In `finally`: `stepPowers` once per tick (`:3413-3416`). ### 2.2 Determinism These rules come from `SKILL:16`, `GAME:skirmish/src/sim.ts:55`, `GAME:skirmish/src/simHash.ts:9-12` and `ADR:42-58`. - Never call `Math.random`, `Date.now` or `performance.now` in sim code. - Iterate in a stable order. Units walk in numeric id order, and AI runs in sorted player id order. Never let a `Map` or `Set` insertion order decide an outcome unless the insertion order is itself deterministic. - Sort strings by UTF-16 code units. Never use `localeCompare` (`GAME:skirmish/src/simHash.ts:12`). - Do not accumulate floating-point values across ticks without a fixed step. - Every field that can change a future tick must be serialized and included in `simHash`. Hashed floats keep their raw IEEE-754 bits (`GAME:skirmish/src/simHash.ts:9-11`). Mechanic state lives on `Unit` or in a hashed section. See `GAME:skirmish/src/mechanics/economy/MECHANICS-economy.md`, "Fixed-tick order", last paragraph, and `GAME:skirmish/src/mechanics/aistate/README.md`, "Root hash integration". - Presentation must not write hashed state. A HUD write to hashed state caused a real multiplayer desync (`SHARE:CPP-SIM-CORE.md`, "Desync" row). - When a system is off, its step must return immediately. It must not change the hash (for example `stepSubModule`, `GAME:skirmish/src/core/MECHANICS-tick.md`, edge cases). ### 2.3 Seeded RNG - The only gameplay random source is `Sim.rng()` (`GAME:skirmish/src/sim.ts:1607`). It draws mulberry32 through `mulberry32Step` in `GAME:skirmish/src/core/rng.ts:43-50`. - The state is a uint32, masked with `>>> 0` after every add. - `setSeed` masks the seed and seeds the AI controllers (`GAME:skirmish/src/sim.ts:1600-1603`). - Visual effects and civilians keep their **own** streams, so an effect cannot move the unit hash (`GAME:skirmish/src/core/MECHANICS-rng.md`, "How to change it"). - The increment and the 2^32 scale belong to the algorithm. They are not balance knobs and do not go in rules JSON (`GAME:skirmish/src/core/MECHANICS-rng.md`, "Data"). - Change the algorithm only with a replay-version bump and a new stored 5,000,000-draw checksum (`GAME:skirmish/src/core/MECHANICS-rng.md`). - Known defect: `ai.ts` still uses the unmasked add. See OD-L07. ### 2.4 Replay and save versions There are three separate numbers. Do not confuse them (`GAME:skirmish/src/core/REPLAY-VERSION.md`): | Number | Value | Where | Bump when | |---|---|---|---| | `REPLAY_STREAM_VERSION` | 2 | `GAME:skirmish/src/core/replayVersion.ts:7` | The logical RNG or command stream changes. | | `simHash` `SCHEMA` | 8 | `GAME:skirmish/src/simHash.ts:34` | Hashed sections are added or changed. | | `saveFormatVersion` | 1 | `GAME:data/menu/menu-save.json` | Saved-state layout changes. Add a `migrateSave` arm. This number is part of the rules hash, and raising it refuses every existing save. | ### 2.5 Lockstep - Commands are plain data: `{tick, playerId, seq, kind, payload}` (`GAME:skirmish/src/sim.ts:561-567`). There are 24 command kinds (`:443`). - Input from a player or the AI becomes a command. The sim admits it on its tick. A mouse callback never mutates the sim. Second-row actions must go through the queued dispatcher (`GAME:skirmish/src/mechanics/secondrow/MECHANICS-secondrow.md`, "This adapts at39's ..."). - Network: a command issued on frame F runs at F + `turnDelay`. `turnDelay` is 2, checksums run every 30 frames, and `tickHz` is 30 (`GAME:data/rules/multiplayer.json:11,12,19`). A mismatch pauses the game and shows the desync report (`GAME:skirmish/src/net/desync.ts`). A dropped seat becomes AI (`GAME:skirmish/src/net/lockstep.ts:3-5`). - Local input delay: `rules.lockstep.inputDelayTicks` is 0 (`GAME:data/rules/game-rules.json:302-305`). See OD-L06. - Reference: "1500 Archers on a 28.8" (`SKILL:51`). --- ## 3. Rules as data Sources: ADR-010 (`ADR:221-252`), `SKILL:14`, `GAME:data/rules/schema.ts:1-8`. - **"ALWAYS VARIABLES, NEVER HARDCODE WHENEVER POSSIBLE."** This covers every gameplay scalar, radius, duration, probability, cost, cooldown, multiplier and capacity. All of them live in JSON under `data/`, and TypeScript reads them. - Each value cites a `source`: - a real-world public figure, with the citation (for example `GAME:data/rules/unit-joints.json:676`, CH-53E NATOPS); - or the Generals equivalent scaled to our map (for example `GAME:skirmish/src/data/weapons-rules.json`, `weaponsByUnitType.Rifleman.source`); - or the words **"Assumed default (ADR-010)"** or **"PROPOSED"**, recorded in `docs/RULES-DEFAULTS-PENDING.md` (examples: `GAME:data/roster/jslc.json:1180,1572`; `GAME:data/roster/penguins.json:1294-1295`). - Units go in field names or in a `unit` key. Use metres, seconds, m/s, degrees, credits, tonnes and fractions 0 to 1. For example: `durationSec`, `rangeM`, `speedMps`, `payloadTonnes`, `minHpFraction`. `TunableNumber` is `{value, unit, description}` (`GAME:data/rules/schema.ts:10-14`). - Convert metres to tiles **once**, at the sim adapter, using the map's metres per tile (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, paragraph 2). World scale is 5 m per tile (`GAME:data/rules/world-scale.json`; README `GAME:skirmish/README.md`, "1 tile = 5 m"). - Validate rules on load and freeze them. Reject bad values with a thrown error. Example: `readProfile` in `GAME:skirmish/src/mechanics/weapons/index.ts:26-33`. `aistate` rules are "validated and recursively frozen, with no module-global mutation API" (`GAME:skirmish/src/mechanics/aistate/README.md`). - Every mechanic has an on/off switch in `GAME:data/rules/mechanics-toggles.json` or its own `enabled` flag. An unconfigured match keeps the old behaviour (`GAME:skirmish/src/mechanics/aistate/README.md`, line 3). - **Do not ask the owner about balance numbers.** Pick an assumed default, mark it, and move on (`ADR:229`, `ADR:247-249`). Known gaps: - Unit base stats are still TS literals in `sim.ts`. See OD-L04. - 82 of 159 rules files have no `source` key. See OD-L03. - Rules live in two folders. See OD-L02. --- ## 4. Code layout and hooks Sources: `SKILL:15`, `SKILL:19`. A new mechanic lives in `skirmish/src/mechanics//`: | File | Holds | |---|---| | `types.ts` | Plain-data state and payload types. They must serialize. | | `rules.ts` | Loads, validates and freezes the rules JSON. | | `logic.ts` | Pure functions over state, rules and ports. No imports from `main.ts` or the renderer. | | `ports.ts` | The narrow interface the mechanic needs from the sim. Live example: `GAME:skirmish/src/mechanics/economy/` (`index, logic, ports, rules, types`). | | `index.ts` | Public API. | | `MECHANICS-.md` | Doc for the C++ port (section 10). | **Shared files:** `sim.ts`, `main.ts`, `models.ts` and the UI files take only small marked hook lines: ```ts // HOOK : ``` - Inline form: `/* HOOK aus9: per-order part intent. */` (`GAME:skirmish/src/sim.ts:440`). - Today `sim.ts` has 151 hook lines, `main.ts` has 157 and `models.ts` has 16 (counted 2026-10-20). - Never reformat or refactor a shared file. - Adapt to the existing authority rather than adding a parallel controller. The integrated mechanics say which module stays authoritative. For example: "ship helm and aircraft remain authoritative" (`GAME:skirmish/src/sim.ts:33`), and "Original pure helper modules remain available ... they are not hooked as parallel engine controllers" (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, paragraph 4). **Coding style:** - TypeScript strict, ES modules. - TSDoc on every exported type and function (`SKILL:19`). Comment the *why* and the units. See `GAME:skirmish/src/core/rng.ts` and `tick.ts` for the house style. - Name constants that are not tunables, for example `AUDITED_BROWSER_PERIOD_MS` in `GAME:skirmish/src/core/tick.ts:10`. - No GLB paths in logic. Resolve models through the asset registry's authoritative version (`SKILL:20`). --- ## 5. Unit behaviour contract: what a unit must be able to do The owner asked: "what does a unit need to be able to do". This section answers from the live sim. Each capability lists its data and its authority. Status is **live**, **partial** or **PROPOSED**. The same list is in `logic-standard.json` → `unitCapabilities`. ### 5.1 Identity - `id` (number, stable), `player` (-1 is neutral), `type` (`UnitType`, `GAME:skirmish/src/sim.ts:417-419`) and `rosterId` (data variant) (`GAME:skirmish/src/sim.ts:622-640`). - Internal ids and save identities are stable and must not change. Display names go through wrappers (`SHARE:ARCHITECTURE-BRIEFING.md:51`). - The model comes from the asset registry. There is one model per unit (`SKILL:20`). ### 5.2 Orders and commands (live) - Order kinds (`GAME:skirmish/src/sim.ts:439`): `move`, `attack`, `attackMove`, `attackGround`, `guard`, `guardHold`, `guardAir`, `patrol`, `capture`, `rally`, `gather`, `returnCargo`, `follow`. - Orders queue on `unit.orders`, and Shift adds to the queue (`GAME:skirmish/README.md`, Controls). - `stop` and `scatter` are commands. - A replacement order or Stop clears pursuit, patrol and yield state. Queued orders survive a temporary yield (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`; `GAME:skirmish/src/mechanics/aistate/README.md`). - Contained units cannot change stance (`GAME:skirmish/src/mechanics/aistate/README.md`). ### 5.3 Movement (live, partial for naval turning) - Paths come from A* or the navgrid over passability per layer (`GAME:skirmish/src/nav/`, `GAME:skirmish/src/mechanics/navgrid/MECHANICS-navgrid.md`). - Ground vehicles accelerate and brake, steer a bounded chassis, and reverse to a nearby rear goal. Infantry may step while turning. The orientation budget is spent once per tick (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, paragraph 1). - A friendly blocker yields: it moves aside and returns, with no teleport. - Patrol runs as a loop or as ping-pong. - A formation moves at its slowest member's pace (`Unit.formationPace`, `GAME:skirmish/src/sim.ts:658`). - Ships use the ship helm. Aircraft have their own controller (`mechanics/locomotor/aircraft.ts`, `mechanics/flight`). - Naval turning circles and `attackPathing` are **off** (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, paragraph 4). - Numbers are in `GAME:skirmish/src/data/locomotorRules.json`. They are "declared gameplay assumptions" (same doc, paragraph 5). - Real turn rates, accelerations and formation algorithms are **pending research**. See section 13. - Damage slows a unit through `speedFactor` per body state (`GAME:skirmish/src/data/weapons-rules.json:161-190`). Flooding halves speed (`Unit.flooding`, `GAME:skirmish/src/sim.ts:688`). ### 5.4 Attack (live) - Each unit has a weapon profile (`GAME:skirmish/src/mechanics/weapons/index.ts:8-13`): - `damageType`; - domain flags `antiAir`, `antiGround`, `antiShip`; - `minRangeTiles`, `clipSize`, `clipReloadSec`, `preAttackDelaySec`; - `delivery` (`instant` or `projectile`); - splash; - `deliberateOnly`. - Damage type times armour class uses the SAGE-style matrix (`GAME:skirmish/src/mechanics/contracts.ts:20-50`). - **Stance** is one of `aggressive`, `guard`, `hold` or `passive` (`GAME:skirmish/src/mechanics/aistate/types.ts:16`). - **Fire discipline** is one of `free`, `tight` or `hold` (`GAME:skirmish/src/mechanics/weapons/index.ts:5`). Labels are in `GAME:skirmish/src/data/tacticalCommands.json`. Hold suppresses automatic fire, but an explicit Attack still fires. - Auto-acquire uses the acquisition range. Attack-move anchors at acquisition, has a chase timeout, and rejects a target until cooldown and forward progress (`GAME:skirmish/src/mechanics/aistate/README.md`). - Owner rulings (`SKILL:26-27`): - Turrets slew independently, and the hull turns only when no mount can bear (`GAME:skirmish/src/data/weapons-rules.json`, `shipTurrets.source`). - Defensive weapons fire automatically, while Tomahawk and SM-3 are deliberate. See OD-L16 on Hold fire. - Part targeting: `Order.aimMode` and `subsystemTargetId` (`GAME:skirmish/src/sim.ts:440`; `mechanics/parttarget`). - A shot reveals the shooter to its target (`Unit.lastAttackerId`, fog.json `attackerRevealSec`, `GAME:skirmish/src/sim.ts:701`). - Anti-overkill and focus fire are **partial**: `FocusFireRegistry` is not integrated (`GAME:skirmish/src/mechanics/aistate/README.md`). ### 5.5 Transport and containment (live) - One attachment contract covers all of them: `containedIn`, `slungBy`, `transportManifest` (`GAME:skirmish/src/sim.ts:645-646`). - Rules (`GAME:skirmish/src/mechanics/transport/transport-rules.json`): - slot cost by size class: infantry 1, light 2, medium 4, heavy 8; - per-transport seats, deck slots, maximum class, payload tonnes, fire ports and sling; - load and unload range and interval; - unload scatter; - casualty survival chance. - A transfer between hosts mutates the existing passenger record. It never spawns or teleports (`GAME:skirmish/src/mechanics/transport/TRANSFER.md`). - Garrison: `garrisonedIn`, capacity and evacuation of damaged buildings (`GAME:skirmish/src/garrison.ts`, `SHARE:...work/docs/mechanics/SYS-garrison.md`). ### 5.6 Carrier operations (live, partial) - Each carrier keeps a book (`CarrierBook`): its inventory, CAP point and named cooldowns (`GAME:skirmish/src/carrierOps/index.ts:21-32`). - A CAP flight's state is `deck`, `station`, `return` or `recover`. It tracks `fuel01` and `ammo01` (`GAME:skirmish/src/carrierOps/index.ts:33-41`). - Second-row named actions (`GAME:skirmish/src/mechanics/secondrow/MECHANICS-secondrow.md`): - LCAC out; - Scout E-2D; - Scramble; - Strike, which is atomic: the whole package or nothing. - **LCAC in** is unavailable (same doc). - The deck machine (`airops`) owns elevators, catapults, landing queues and well-deck gates. - Ships start fully stocked (`GAME:data/rules/carrier-ops.json` `metadata.governingPolicy`). - Aircraft that run out of ammunition return to base, reload by the missing fraction and keep the pending command (`GAME:skirmish/src/mechanics/flight/MECHANICS-flight.md`). ### 5.7 Production and construction (live) - Each facility holds a FIFO queue of up to `maxQueue` items (`GAME:skirmish/src/mechanics/production/MECHANICS-production.md`). - The cost is paid per tick against the cumulative rounded price. A queue head without money waits. - Hold keeps progress and spending. - Cancel refunds credits spent × `refundFraction`. Destruction discards the queue with no refund. - The exit is a physical path from inside the factory. The rally applies on exit (`GAME:skirmish/src/mechanics/scaffold/MECHANICS-scaffold.md`). - Construction advances one integer progress step per tick. An incomplete structure neither fights nor produces (`GAME:skirmish/src/sim.ts:3305`). ### 5.8 Economy and supply (live) - Supply workers carry integer boxes. They gather from a finite warehouse and deliver to a depot, which credits the box value once (`GAME:skirmish/src/mechanics/economy/MECHANICS-economy.md`). - Oil derricks pay out on a periodic timer. A capture reward is paid once per object. - Stock is conserved: warehouse stock + cargo + delivered value stays constant. - Base income is 0 on supply maps. Legacy maps keep 50 credits per second. - Upkeep, wear and PR legitimacy run behind toggles (`GAME:skirmish/src/sim.ts:3353-3363`; `GAME:data/rules/mechanics-toggles.json`). - PR never locks weapons (`GAME:skirmish/src/mechanics/contracts.ts:11`). ### 5.9 Damage and death (live) - `hp` and `maxHp`, `armorClass` and optional `subsystems` (`GAME:skirmish/src/sim.ts:622-716`). - Body states are `pristine`, `damaged`, `reallyDamaged` and `rubble`. Their thresholds and effects are in `GAME:skirmish/src/data/weapons-rules.json:161-190`. See OD-L08: these thresholds differ from the visual thresholds in standards_units. - Fire and flood spill hull damage at a fixed sub-rate (`GAME:skirmish/src/sim.ts:3337-3350`). - EMP sets `disabledUntil`. Infantry can be `surrendering`, and are then captured, not killed. - Death goes through `killUnit`. Dead units are compacted after the kill pass. ### 5.10 Vision, stealth and capture (live) - Sight range, fog and the sight cache (`GAME:skirmish/src/fog.ts`). - Camera coverage and HQ see through fog. Weather may only reduce ranges (`SKILL:24`; `MEM:hq-ideal-conditions-by-default.md`). - Stealth, detector pulses, capture and tunnels: `GAME:skirmish/src/mechanics/infiltration/`. ### 5.11 Veterancy (live) - Progression state, kill credit and power bonus (`GAME:skirmish/src/mechanics/progression/MECHANICS-progression.md`). Rules are in `GAME:skirmish/src/data/progression-rules.json`. ### 5.12 Unit states There are two kinds of state. Keep them apart: - **Sim states** decide behaviour and are hashed. They are the `Unit` fields listed in `logic-standard.json` → `simStates`: - `alive`; - order queue, `engageId`, `stance`, `fireDiscipline`; - `containedIn`, `garrisonedIn`, `embarkedIn`; - `airHold`, `standby`, `construction`, `disabledUntil`, `surrendering`, `flooding`; - body damage state. - **Presentation states** pick animation poses. They live in `GAME:data/rules/unit-states.json`: - carrier aircraft: `hangar-deck`, `flight-deck`, `airborne`, `landing`; - land aircraft: `parked`, `taxi`, `airborne`; - vehicles: `travel`, `deployed`, `combat`; - ships: `moored`, `underway`, `action-stations`; - buildings: `idle`, `active`, `alert`. The pose is derived from sim facts through the `situation` table (`GAME:data/rules/unit-states.json:1295`). It never feeds back into the sim. Clip conventions belong to `../standards_units/`. --- ## 6. AI - Computer players live in `GAME:skirmish/src/ai.ts`. They run once per tick, in player id order. Their tunables are in `GAME:data/rules/ai.json` and `difficultyScale`. - AI uses its own seeded stream, `aiRng` (see OD-L07). - The AI issues the same commands a player does. Build lists per match, faction and general come from `mechanics/matchai` (`GAME:skirmish/src/ai.ts:6`). - Unit-level autonomy (stance, guard leash, smart targeting) is in `mechanics/aistate`. An unconfigured unit keeps the old nearest-target behaviour (`GAME:skirmish/src/mechanics/aistate/README.md`). - Open AI findings from the ChatGPT audit are input, not canon (`SHARE:logic-inputs/chatgpt-project1-20261020/6ad64b1e_jslcnc_game_logic_audit_2026-10-19.md`, section 3). Examples: defence reserving units that immediately retreat, and economy ignoring harvesters already in production. --- ## 7. Clean room 1. **Never copy EA code or pseudo-code** (`SKILL:17`). 2. **Game-code agents never read Thyme or EA engine source.** Generals behaviour may be described in our own words from: - public modding documentation (the Generals Wiki, C&C Labs, the guides in `SHARE:MODDING-RESOURCES.md`); - INI field names; - play. The source of this rule is BRIEF (dolphin4 2026-10-20) and `C:\Users\MaxGlasser\.claude\jobs\44b57189\tmp\lanes\movement-research\BRIEF.md:27`. This **conflicts** with `SKILL:17` and `SKILL:45`, which name the EA GPL source as a reference. Twelve existing `MECHANICS-*.md` files cite EA source files. See OD-L01. 3. Cite the INI field or public page you learned a behaviour from, in the mechanic doc. 4. Retail and mod assets are private test placeholders. Never commit or publish them (ADR-005, `ADR:113-130`). The owner treats the game as personal, non-commercial use, and asset licence is not a filter (`MEM:game-personal-use-licensing.md`). That covers assets only. It does not relax the code clean-room rule. --- ## 8. Naming and identity (owner rulings) - The factions are **JSLC**, **AoI** and **Penguins**. `coalition`, `network` and `directorate` are legacy internal keys only (ADR-007, `ADR:152-174`). The product is "JCOM and Conquer" (`ADR:516`). - Ships take their real class name. Individual names come from the approved pool with JNS, JAF, JDF or JSF prefixes. Ensigns fly on masts only (`SKILL:28-29`). - The HQ admiral is **Bill "Maccabee" Adama**, everywhere. J.D. Vance is a different, real person and is never renamed (`MEM:admiral-is-bill-adama.md`). - **There are no banned names.** Never add a name-scrub step. Rename only on a specific owner request (`MEM:no-banned-names.md`). - Internal ids and save identities never change. Display names go through wrappers (`SHARE:ARCHITECTURE-BRIEFING.md:51`). - No emojis. Use Unicode line glyphs only (ADR-016, `ADR:349`). - The concept test is the tiebreaker: "If it is done in real life and it is terrible, then we include it" (ADR-009, `ADR:196-219`). --- ## 9. HUD and GUI boxes - Every new or changed box is built from the HUD design system (`SHARE:codex-lanes/hud-design`). Its levels are tokens, then primitives, components, layout and screens. In the same change, the box adds itself to the component registry and to the reference page (`SKILL:21`). - No inline styles. No hard-coded colours or px sizes. - Run `node tools/check.mjs` in `hud-design` and paste the output into the checkback. You can point it at a game file (`hud-design/README.md`, "How it nests"). - HUD code reads sim snapshots and issues commands. It never writes hashed state (section 2.2). - Every UI must work at 1440x900 and on iPhone 15 (393x852), including standalone PWA mode (`SHARE:ARCHITECTURE-BRIEFING.md:53`). - The design system's own README says "PROPOSED ... not wired into the game yet". See OD-L13. --- ## 10. Mechanic documentation (for the C++ port) Each mechanic folder has a `MECHANICS-.md` (`SKILL:19`). The live examples are `economy`, `production` and `scaffold`. Each file covers: 1. what the mechanic does; 2. the data fields and their units; 3. how to change it; 4. the per-tick order of operations; 5. edge cases; 6. clean-room references (section 7); 7. tests. --- ## 11. Testing - Every mechanic has Vitest tests (`SKILL:18`). Record the suite counts before and after, with no new failures. - Test real behaviour, not flags. Measure actual displacement, lateral movement and return, queued-order survival, and replay hashes. Checking only that a flag is set does not prove behaviour (`GAME:skirmish/src/mechanics/locomotor/MECHANICS-locomotor.md`, "New steps"). - Every mechanic that touches state needs a **determinism test**: - same commands give the same hash; - save, load, then advance gives the same hash as the original (`GAME:skirmish/src/mechanics/economy/MECHANICS-economy.md`, last section). - Existing suites: `tests/core-determinism.test.ts`, `core-rng.test.ts`, `hash.test.ts`, `lockstep.test.ts`, `replay.test.ts`. - **Mission and campaign tests:** `tests/missions-*.test.ts` and `tests/campaign*.test.ts`. The browser playthrough is `npm run test:campaign-play`. It plays every mission, never uses Record win, and fails on a console error. A subset is never reported as full acceptance (`GAME:skirmish/README-campaign-playthrough.md`). - **Where tests run:** - Only through `C:\lanes\dolphin4\game\lane-test.ps1`. - It runs on SV3 (`--maxWorkers=3`) or SV4 (`--maxWorkers=2`). That is one run per box and at most 5 workers in total. - Never on DSK, never on SV2. - A newer request from the same lane supersedes its waiting one, with exit code 98. - Prefer targeted files while iterating, and run the full suite once before DONE. - Sources: `C:\lanes\dolphin4\game\lane-test.ps1:1-9`, `INBOX:lanes/design-repo/TOOLS-SV3-SV4.md:52`, `MEM:dsk-off-limits-full-use-sv2-4.md`. - Local Vitest is clamped to 1 worker (`GAME:skirmish/vitest.config.ts`). - **Prove it in the running build.** Take before and after screenshots from the 4380 build or from a dev server on your own copy. Report `what_made_the_difference` and `new_steps` (`SKILL:30`). An effect is "done" only once it is observed running (ADR-019, `ADR:405-418`). --- ## 12. Integration and patch process 1. Work in an isolated lane copy with junctioned `node_modules` (`SKILL:58`). Never edit `public/` or `node_modules/`. Assets come as `public-add/` lists (`SKILL:34`). 2. Deliver: - `PATCH.diff`: `diff -ruN` of the baseline against `skirmish`, plus a data diff against `_base/data`; - `REPORT.md` with `new_steps` and `what_made_the_difference`; - tests; - screenshots. Source: `INBOX:launch/brief-au99.md:26-29`. 3. Shared files show only hook lines. Reviewers check this and the rest of the review checklist (`SKILL:32-41`). 4. An independent model reviews the patch. Only PASS turns an item green (ADR-019, `ADR:405-418`). Review each packet as it lands. Never leave finished patches waiting (`SKILL:41`). 5. After review, the integrator applies the patch to the live tree and deploys, keeping a backup of every replaced file. Then they test the running build on desktop and iPhone 15, and fix forward or roll back (`MEM:push-then-test.md`). 6. Never kill in-flight runs unasked (`MEM:let-running-runs-finish.md`). --- ## 13. Pending research (not yet rules) The movement research lane is **open**. Its results are proposals until the owner adopts them (`C:\Users\MaxGlasser\.claude\jobs\44b57189\tmp\lanes\movement-research\BRIEF.md`). It covers: - land, naval and air formation algorithms; - HPA*, JPS, navmesh and flow fields; - deterministic local avoidance (RVO, ORCA); - multi-layer and large-footprint movement; - real turn rate, acceleration and tactical diameter for named ships, ground vehicles and aircraft; - the behaviour of the Generals Locomotor fields, from modding docs only; - scaling real values for play. Its outputs will be `MOVEMENT-DESIGN-NOTE.md` and `movement-rules.proposal.json`. Until then, `locomotorRules.json` values stay "declared gameplay assumptions". Other non-canonical inputs: - `SHARE:logic-inputs/chatgpt-project1-20261020/DEEPRESEARCH_d154960d_Shared_RTS_Unit_Logic_Architecture...md`: command, snapshot and event APIs, auto-fire and target selection. - `DEEPRESEARCH_b16cf0b9_...Grouping_Formations...md`. --- ## 14. NEW MECHANIC CHECKLIST Copy this list into the lane brief and tick each item in the checkback. - [ ] **Owner concept named.** The mechanic is quoted from an owner ruling, canon or the gap list (`codex-lanes/gap/*/GAP-*.md`). It passes the concept test (ADR-009). - [ ] **Clean room.** Behaviour is described in our own words, from public modding docs, INI field names or play. No Thyme or EA source was read or copied. The reference is cited. - [ ] **Folder.** `src/mechanics//` has `types.ts`, `rules.ts`, `logic.ts`, `ports.ts` and `index.ts`. - [ ] **Rules JSON.** Every number is in data, with units in the key, and a `source` for each value or group. Unsourced values say "Assumed default (ADR-010)" or "PROPOSED" and are added to `RULES-DEFAULTS-PENDING.md`. There are no literals in logic. - [ ] **Rules validated and frozen** on load. Bad data throws. - [ ] **Toggle.** It has an `enabled` flag or a `mechanics-toggles.json` entry. When off, the hash is unchanged. - [ ] **Tick slot named.** The doc names where in `Sim.step` it runs, and why it runs before or after collision and the kill pass. - [ ] **Commands.** Player and AI actions arrive as queued `Command`s and are validated for ownership and enums. No mouse callback mutates the sim. - [ ] **Determinism:** - only `Sim.rng()` or its own seeded stream; - no wall clock; - id-ordered iteration; - no `localeCompare`; - all future-relevant state serialized and in `simHash` (bump `SCHEMA` when you add a section). - [ ] **Authority.** It reuses the existing authority (helm, aircraft, transport manifest, `followPath`, damage pipeline). It adds no parallel controller. - [ ] **Hooks.** Shared files carry only `// HOOK : ` lines. Nothing is reformatted. - [ ] **Unit contract.** It says which capabilities in section 5 it adds or changes and which `Unit` fields it touches. - [ ] **Model.** It uses no GLB path. The model resolves through the registry. - [ ] **HUD.** Any box is built from `hud-design` and added to its registry and reference page. `node tools/check.mjs` output is pasted. - [ ] **Owner standing rulings** are respected (`SKILL:22-29`): HQ ideal conditions, cameras see through fog, bow-out berthing, independent turrets, defensive auto-fire, real-class ship names. - [ ] **TSDoc** is on every export. `MECHANICS-.md` is written (section 10). - [ ] **Tests:** - behaviour tests that measure outcomes; - a determinism and save/load hash test; - mission tests when a mission uses it; - suite counts before and after; - run through `lane-test.ps1` only. - [ ] **Proof.** Before and after screenshots from the running build. `what_made_the_difference` and `new_steps` are in `REPORT.md`. - [ ] **Deliver** `PATCH.diff`, the data diff, `public-add/` (if any) and `REPORT.md`. Request an independent review. --- Signed 2026-10-20 16:52 EDT (observed `date` before writing). Author: read-only subagent of dolphin4 (aqv9_opu_5.5_md). The subagent is not separately registered. ================================================================================ FILE: GEOMETRY STANDARD: standards/standards_geometry/README.md ================================================================================ # Geometry standard: meshes, topology, budgets and LODs Repo: **`jcom_v0bb_simulation_design_standards_geometry`**. Status: draft, 2026-10-20. Written by the Cobalt design advisor for dolphin4. This is advice; the owner rules on every item in [OPEN-DECISIONS.md](OPEN-DECISIONS.md). Machine-readable form: [geometry-standard.json](geometry-standard.json). Tools: [SKILLS.md](SKILLS.md). **One rule:** author once, high, in real metres, with every part named and pivoted. Every smaller size is then a scripted derivation. Nobody models a second size by hand unless a derivation is shown to fail. ## Why "geometry" and not "wireframes" - A **wireframe** is a render mode, and it is the name of our inspection tool (the Wireframe Lab). It is not a standard. - **Geometry** is the term both target engines use for this subject: Unreal's "Static Mesh geometry", "Nanite virtualized geometry" and "geometry LODs", and glTF's mesh/primitive geometry. - The standard covers topology, budgets, LODs, compression and articulation. "Wireframes" would cover only the first, and only as seen on screen. - The lab keeps its name, `svc_lab_wireframe`. It validates this standard. ## Siblings (linked, not repeated) | Repo | Owns | Path | |---|---|---| | `design/standards_units` | axes, real dimensions, part names, joints and clips, delivery files. Its OD-01 to OD-16 are referenced here, not re-argued | [../design/standards_units/](../design/standards_units/) | | `design_standards_lookdev` | materials, textures, texel density, colour, lighting, KTX2 | `../design_standards_lookdev/` (not written yet, 16:51) | | `design_standards_blueprints` | orthographic sheets and SVG drawings | `../design/standards_blueprints/` per the units repo's link; not present yet | | Wireframe Lab | the validator UI; rules in `rules/rules.v1.json` | `D:\z4yin\.inbox\dolphin4\lanes\wireframe-lab\wireframe-lab\` | ## Source key - `GAME:` is the live game tree `M:\z4yin-work-overflow\hermes-share\codex-lanes\asset-split\sol-maps\work\` (read only). - `LAB:` is the wireframe-lab lane (`D:\z4yin\.inbox\dolphin4\lanes\wireframe-lab\`). - `V1:` is `LAB:wireframe-lab/rules/rules.v1.json`. - **PROPOSED** marks a value with no project source. It needs owner approval before any tool fails a model on it. --- ## Glossary | Term | Meaning here | |---|---| | Mesh / primitive | glTF mesh = one or more primitives; each primitive is one draw call with one material. | | Draw call | One GPU submission. Roughly one per primitive per visible instance (unless instanced). | | Triangle (tri) count | The budget unit. Quads and n-gons are counted after triangulation. | | Vertex count | Counted *after* splitting at UV seams and hard edges. Often 1.5-3x the welded count. Matters more than tris on mobile. | | Quad / n-gon | Four-sided / more-than-four-sided face. Quads for authoring; n-gons are not allowed on export. | | Edge flow / edge loop | How edges run across a surface. Loops must ring a hinge so the part can rotate without shearing. | | Manifold | Every edge has exactly two faces. Non-manifold: an edge with one face (open, a hole) or three or more faces (fins), or a vertex shared by two separate fans (bowtie). | | Watertight / closed | Manifold with no boundary edges. GAME:`unit-structure.json` scores "closed". | | Degenerate / zero-area face | A triangle whose area is about 0 (repeated or collinear vertices). V1:`geometry.degenerateAreaEpsilon`. | | Flipped normal / winding | Face winding opposite to its neighbours, so it renders inside out. | | Weld / merge by distance | Fuse coincident vertices. Weld inside a part; never weld across a joint. | | Hard / soft edge (smoothing groups, sharp edges) | A hard edge splits vertex normals for a crisp crease; a soft edge shares them. | | Custom (split) normals | Explicit per-corner normals stored in the mesh; how a baked or weighted-normal look survives export. | | UV0 / UV1 (TEXCOORD_0 / _1) | First UV set: material textures. Second: lightmaps or AO, non-overlapping. | | Texel density | Texture pixels per metre of surface. Owned by lookdev. | | Pivot / origin | The local (0,0,0) of a node. For a moving part, it sits on the real rotation axis. | | Node hierarchy | The glTF parent/child tree. Moving parts are child nodes of what carries them. | | LOD (level of detail) | Lower-detail stand-ins at distance. LOD0 is the full game mesh. | | HLOD / impostor / billboard | A merged far stand-in for many objects / a camera-facing card showing a pre-rendered view (octahedral impostors keep several views). | | Screen size (Unreal) | Projected bounding-sphere diameter / screen height. LOD switches are expressed in it. | | Geometric (simplification) error | Maximum distance the simplified surface moves from the original, in metres or screen pixels. | | Decimation / simplification | Removing vertices by edge collapse under an error bound (meshoptimizer `simplify`, Blender Decimate "Collapse"). | | Retopology | Rebuilding a clean low mesh over a high one (manual, QuadriFlow). | | Baking | Projecting high-mesh detail (normals, AO, curvature) into the low mesh's textures through its UVs. Cage = the inflated shell that rays are cast from. | | Quantisation | Storing positions, normals and UVs as 8- or 16-bit integers (`KHR_mesh_quantization`). | | Meshopt / Draco | Two glTF geometry compressions: `EXT_meshopt_compression` / `KHR_draco_mesh_compression`. | | Instancing | One draw call for many copies of the same geometry and material. | | Nanite | Unreal 5 virtualized geometry: streams cluster LODs automatically from a dense master mesh. | | Collision proxy / hull | A simple convex shape used for physics or picking instead of the render mesh. Unreal names them `UCX_`. | | Swept volume | The space a moving part occupies across its whole range of motion. | --- ## 1. Units and frame | Item | Rule | Source | |---|---|---| | Unit | 1 unit = 1 metre. The game multiplies by `metreToWorld` 0.4 at load. Never pre-scale a model. | GAME:`data/rules/unit-part-contract.json:4`; GAME:`data/rules/world-scale.json` | | Up | +Y | GAME:`unit-part-contract.json:6`; V1:`orientation.expectedUp` | | Forward | **-Z** (decision OD-01 in the units repo, recommended) | GAME:`unit-part-contract.json:5`; GAME:`unit-structure.json:2` | | Right (starboard) | +X. This follows from -Z forward and +Y up in glTF's right-handed frame. | glTF 2.0 spec §3.4 (right-handed, +Y up); units repo `axes.starboard` | | Ground | Lowest wheel/track/foot on y = 0 (tolerance 0.05 m). Naval datum is OD-07 in the units repo. | GAME:`unit-structure.json` `tolerances.groundEpsilonM` | | Unreal | Unreal is left-handed, Z-up, X-forward, 1 unit = 1 cm. Its glTF/Interchange importer converts on import; we never author in Unreal's frame. | Epic, "Coordinate system and spaces" and glTF importer docs (not fetched today) | ### Forward-axis conflict and a mirroring finding - The game uses -Z forward. The owner's E-2D lab spec says "+Y is up, +Z points toward the nose, and +X is starboard" (LAB:`input/OWNER-SPEC.txt:13`). - **New finding (checked 2026-10-20 in the GLB itself):** `reference_propeller_starboard` sits at x = +3.31 with the nose at +Z. glTF is right-handed. Facing +Z with +Y up, starboard must be at **-X**. So this export is either a **mirror image** or has its port and starboard labels swapped. - A yaw offset cannot fix a mirror image: turning it 180 degrees to face -Z leaves the labels on the wrong sides. - **Check before ingest:** read the NAVY lettering in the lab. Reversed text means the geometry is mirrored; fix with scale X -1 and recomputed winding. Readable text means the labels are swapped; rename the nodes. - Recorded as OD-G1. ### Pivot / origin per class | Class | Root origin | Moving-part pivots | Source | |---|---|---|---| | Ground vehicle | Centre of the track or wheel footprint in XZ, y = 0 at ground | turret: centre XZ, seat at its min Y. barrel: centred XY, breech at max Z. wheel: centred YZ on the axle | GAME:`unit-structure.json` `tolerances.pivots` | | Infantry | Between the feet, y = 0 | skinned; joints per the units repo | GAME:`unit-structure.json` | | Aircraft | Centre of the gear footprint in XZ, y = 0 with gear down | gear: centred XZ at the trunnion; rotor: hub centre XZ; prop: centred XY; fold: on the hinge line | GAME:`unit-structure.json` pivots; LAB spec | | Ship / submarine | Midships on the centreline, y = 0 at the design waterline (OD-07) | turret, launcher, radar: centre XZ, seat min Y; prop: shaft line | GAME:`unit-structure.json` pivots | | Building | Centre of the footprint, y = 0 at grade | doors on the hinge line (x) | GAME:`unit-structure.json` pivots; `bldg-defence-dims.json:4` | - **Pivot tolerance:** within 20% of the part's size or 0.05 m, whichever is larger. GAME:`unit-structure.json` `pivotFraction`, `pivotMinM`. - **Node hierarchy and names:** the units repo owns these, along with GAME:`unit-part-contract.json` `parts`, GAME:`unit-structure.json` `classes`, GAME:`unit-joints.json` `kinds` and `profiles`. The geometry rules that bear on names: - each named part is its own node and mesh; - LOD files reuse the **same node names and pivots** at every level, so the rig and states drive any LOD unchanged; - empties (`muzzle_01`, `fx_*`) carry no geometry. ## 2. Topology | Check id | Rule | Severity | Source | |---|---|---|---| | `topo.authoringQuads` | Author hard-surface in quads with supporting loops at creases. Triangles are fine where they stay flat. | advice | PROPOSED (industry practice) | | `topo.triangulateOnExport` | Export triangles only (glTF is triangles). Triangulate in Blender with a fixed method (Beauty), so every LOD triangulates the same way. | fail if quads or n-gons reach the export | glTF 2.0 primitive modes | | `topo.noNgons` | No n-gons in the source at hinges, curved areas or anywhere else that will be decimated or deformed. | warn | PROPOSED | | `topo.manifold` | No non-manifold edges inside a part. Open edges at most 2% of edges per part. | fail over the threshold | GAME:`unit-structure.json` `openEdgeFraction` 0.02 | | `topo.degenerate` | Triangle area at least 1e-12 m²; warn at 1. | warn | V1:`geometry.degenerateAreaEpsilon`, `warnDegenerateCount` | | `topo.finite` | All positions finite. | fail | V1:`geometry.requireFinitePositions` | | `topo.consistentWinding` | No flipped faces; all normals point outward. | fail | PROPOSED | | `topo.weldInsidePart` | Merge by distance at 0.1 mm inside each part. Never weld across parts. | fail if a moving part is welded to its parent | GAME:`unit-structure.json` "moving parts are not welded to the hull", `weldOverlapFraction` 0.08 | | `topo.duplicateMesh` | The same mesh copied many times is stored once and instanced (wheels, rivets, VLS cells). | warn | V1:`geometry.duplicateMeshWarn` | | `topo.hingeLoops` | Two or more edge loops ring every hinge and swivel. Hinge faces are separate shells with the clearance gap below. | advice / lab check | PROPOSED | **Normals:** - **Rule:** smooth by angle at 30 degrees, then mark hard edges where the real object has a crease. Export custom split normals. Every UV seam on a hard edge, and every hard edge on a UV seam, so the splits coincide and vertex count stays low. - **Weighted normals:** allowed on LOD0 bevelled hard surfaces. - **Bakes:** baked normal maps need tangents exported (MikkTSpace), matching the baker. - **Source:** PROPOSED (industry practice). **UVs:** - **UV0:** non-overlapping on unique detail. Mirrored or overlapped islands are allowed only on symmetric parts that carry no baked text or markings. - **UV1:** only when the target needs a lightmap or a separate AO. Unreal can generate one on import. - **Seams:** on hard edges and hidden edges. - **Padding:** at least 4 px at the 2048 bake. - **Texel density and texture sizes:** owned by [lookdev](../design_standards_lookdev/). The texture size caps are 2048/1024/512 per LOD (GAME:`unit-structure.json` `textureMax`; units repo OD-03). - **Source:** PROPOSED (industry practice). ## 3. Budgets There are three numbers per class and per LOD, plus a master: - **Master:** the source the LODs derive from. It never ships to the browser. - **Hard cap:** the game's STRUCTURED gate. Over it, the pipeline decimates or fails. - **Target:** what the runtime plans for and what LODs derive to. - The Unreal column is the Nanite master itself. ### Triangle budgets (LOD0 / LOD1 / LOD2) | Class | Master, sanity cap (PROPOSED) | Hard cap (game) | Desktop target | Phone target | Draw calls (game, not enforced) | |---|---|---|---|---|---| | infantry | 50,000 | 3,000 / 1,500 / 500 | 1,200 / 450 / 160 | 600 / 250 / 100 | 1 | | tank | 250,000 | 8,000 / 3,000 / 1,000 | 3,500 / 1,200 / 400 | 1,500 / 600 / 240 | 2 | | ifv_apc | 250,000 | 8,000 / 3,000 / 1,000 | 3,000 / 1,000 / 350 | 1,200 / 500 / 200 | 2 | | wheeled | 150,000 | 8,000 / 3,000 / 1,000 | 2,200 / 800 / 280 | 1,000 / 400 / 160 | 2 | | helicopter | 300,000 | 15,000 / 5,000 / 1,500 | 2,800 / 1,000 / 350 | 1,200 / 500 / 200 | 2 | | fixed_wing | 300,000 | 15,000 / 5,000 / 1,500 | 2,500 / 900 / 300 | 1,100 / 450 / 180 | 2 | | small_boat | 150,000 | 15,000 / 5,000 / 1,500 | 2,000 / 800 / 300 | 1,000 / 400 / 160 | 4 | | escort | 1,000,000 | 15,000 / 5,000 / 1,500 | 8,000 / 3,000 / 1,000 | 3,500 / 1,400 / 500 | 4 | | capital_ship | 2,000,000 | 15,000 / 5,000 / 1,500 | 15,000 / **5,500 / 1,800** | 6,000 / 2,500 / 900 | 4 | | building | 300,000 | 6,000 / 2,500 / 800 | 5,000 / 2,000 / 750 | 2,200 / 900 / 350 | 3 | Sources: - Hard caps: GAME:`data/rules/unit-structure.json` `budgets.byBudgetClass`, also GAME:`scale-overrides.json` `desktopTriCaps`. - Targets: GAME:`data/rules/quality-tiers.json` `classPolyBudgets`. - Draw calls: GAME:`unit-structure.json` (`enforceDrawCalls: false`). Notes: - **Conflict:** the capital-ship desktop target exceeds its own hard cap at LOD1 and LOD2 (5,500 > 5,000; 1,800 > 1,500). This is OD-G3. - **Materials:** at most 1 material plus the `houseTint` team-colour material per LOD (2 primitives) for vehicles, aircraft and infantry. Ships may use up to 4 (PROPOSED, matching the draw-call column). The E-2D lab export has **65 materials**: as a master that is acceptable; as a game LOD it needs a texture atlas and a material merge. - **Vertices:** after seams, at most 1.5x the triangle target (PROPOSED). This is a warning only. - **Bytes:** at most 2 MB per LOD0 GLB for units and 6 MB for ships, after meshopt and before textures (PROPOSED). V1 allows 16/32/64 MB, which is far too large for mobile. ### The 8k versus 25k conflict | Source | Vehicle LOD0 | Where | |---|---|---| | Game STRUCTURED gate | 8,000 | GAME:`unit-structure.json` `byBudgetClass.vehicle` | | Game desktop cap (decimate if over) | 8,000 | GAME:`scale-overrides.json` `desktopTriCaps.vehicle` | | Game runtime target | 3,500 (tank) | GAME:`quality-tiers.json` `classPolyBudgets.tank` | | Lab validator | 25,000 (`unit`), 40,000 (`escort`), 80,000 (`capital`) | V1:`budgets.profiles` | | Origin of 25k | "not the 25000 architecture line"; no architecture file found in the game tree | GAME:`unit-structure.json` `budgets.description` | These are not one number disagreeing. They measure **different stages**, and V1 has no stage concept. Options: - **A. Lower V1 to the game caps.** One truth. But the lab could then no longer check masters: the E-2D at 105k would fail. - **B. (Recommended) Give V1 stages.** - `master`: sanity caps from the table above. - `delivery`: reads the game caps by reference from `unit-structure.json`, never copied. - Delivery files are also *warned* at the quality-tier target. - The 25k `unit` profile is retired. - The lab shows which stage it judged. - **C. Keep 25k as a "showcase LOD"** for the lab and close-up cinematics. This is a fourth tier to keep in sync, and nothing in the game uses it. - **D. Raise the game caps to 25k.** Rejected: phone targets are 1,000-1,500 and the governor already sheds detail. The units repo's OD-02 recommends treating 25k as the master cap. That fits option B, but 25k is too low for a bake or Nanite master: the E-2D alone is 105k. This is OD-G2. ### Unreal - **Rigid parts (hulls, turrets, wings, rotodomes):** import the **master** as Nanite static meshes, one per articulated part, attached to the parent node or socket. Nanite supplies its own cluster LODs, so do not import a hand LOD chain for these. - **When Nanite does not apply:** - skinned meshes (infantry and other deforming meshes). Nanite skinning exists from about 5.5 behind `r.Nanite.AllowSkinnedMeshes`, but third-party guides call it beta (sources below); - translucent parts (canopy glass, blurred rotor discs). Nanite does not render translucency. Masked materials are supported from 5.1 once enabled; - mobile Unreal targets. - **Sources (third-party, 2026-10-20 web search; Epic's own page was not returned):** [uhiyama-lab Nanite notes](https://uhiyama-lab.com/en/notes/ue/nanite-high-poly-mesh-optimization/), [Medium, "When to use Nanite"](https://medium.com/@sarah.hyperdense/understanding-nanite-when-to-use-it-and-when-to-skip-it-b7fcfadc3058), [UE forum 5.8 translucent report](https://forums.unrealengine.com/t/bug-or-not-weird-static-mesh-result-in-ue5-8-after-applying-translucent-material-more-details/2733948). - **For those meshes:** use the **desktop target chain** as Unreal LODs 0 to 2. Add a master-derived "LOD -1" only for cinematics. - **Collision:** `UCX_` convex hulls (§6). - **Status:** Unreal is not installed on SV3 or SV4 (`TOOLS-SV3-SV4.md`), so none of this is tested. PROPOSED, per Epic's Nanite and Static Mesh LOD documentation. ## 4. LOD chain | Item | Rule | Source | |---|---|---| | Count | LOD0, LOD1, LOD2, then an impostor or counter. Same node names and pivots at every geometric LOD. | GAME:`unit-structure.json` `lodMarkers`; GAME:`quality-tiers.json` tiers | | Delivery | One GLB per LOD, `.lod0.glb` and so on (units repo OD-13). The lab also accepts in-file `LOD` nodes or `MSFT_lod`. | units repo OD-13; V1 `lod` | | Ordering | Triangles strictly descend. LOD0 is required. | V1:`lod.requireLod0`, `requireDescendingTriangles` | | Switch (current game) | Camera distance in metres: desktop LOD0 to 50, LOD1 to 120; phone 24 / 50. Per tier, `unitLodDistances` close/mid/far (ultra 50/120/400 down to potato 16/32/64). | GAME:`quality-tiers.json` `lodSwitch`, `tiers.*.unitLodDistances` | | Error metric | **Screen-space geometric error at most 0.75 physical pixels** at the switch distance, at FOV 50 degrees and 1080 px, split between quantisation and simplification. Relative error 1% is a secondary ceiling. | GAME:`data/rules/client-performance.json` `lod` (`maximumScreenErrorPixels`, `maximumRelativeError`); implemented in GAME:`skirmish/src/mechanics/clientPerformance/meshLod.ts` | | Screen size (Unreal, non-Nanite) | LOD1 at 0.5, LOD2 at 0.25, LOD3 (impostor) at 0.1 | PROPOSED | **The error formula** (the same one the game uses): - World size of one pixel at distance d: `p = d · 2·tan(fov/2) / H`. - Allowed geometric error: `e = 0.75 · p`. - Pass `e`, divided by the mesh's world scale, to meshoptimizer as an **absolute error**. meshoptimizer takes relative error, so divide by `meshopt_simplifyScale`. - A LOD derives to the class **target** count *or* the error bound, whichever keeps more triangles. If that still exceeds the hard cap, the pipeline does "decimate-then-fail" (GAME:`quality-tiers.json` `deriveBudgets`). Generation is automated, in this order: 1. **Per part.** Simplify each named part separately, never the merged unit, so joints and pivots survive. Preserve borders, seams and attributes. The game uses "exact full-attribute indexing preserves UV seams and hard normals" and a minimum of 12 triangles per part (GAME:`client-performance.json`). 2. **Tools.** - Blender headless `Decimate` (Collapse, triangulate, symmetry on X) for authored LODs. - `gltfpack -si ` or glTF-Transform `simplify` (both meshoptimizer) for scripted derivation. - The game already ships `meshoptimizer` 0.18.1 for runtime-derived far LODs (GAME:`skirmish/package.json`). 3. **Thin parts** (antennas, rotor blades, rails, masts) collapse badly. At LOD2, replace each with a card or a box; do not decimate it. This is the case where hand authoring is allowed. 4. **Far zoom (RTS).** - Beyond `impostorsAfter` units on screen, units become impostors. - Beyond `natoCountersAfter`, they become 2D counters (APP-6 style symbols, which are SVG masters; see §8). - Thresholds per tier: ultra impostors after 100 / counters after 140; potato 8 / 12 (GAME:`quality-tiers.json`). I did not check how the renderer implements impostors. - **Impostor bake (PROPOSED):** an 8-view or octahedral atlas from the master, 512 px, alpha-cut. 5. **Instancing.** Identical opaque rigid geometry and material is instanced from 2 copies (GAME:`client-performance.json` `instances.minimumCopies`). Team colour goes through the `houseTint` material, not separate geometry, so units of one class still instance. Moving parts each instance separately. ## 5. Minimisation and compression "Minimising" covers four separate operations, applied in this order: | Step | What | Rule | Source | |---|---|---|---| | 1. Merge | Join static, same-material meshes within a part. Atlas textures so one LOD has 1-2 materials. | Never merge across a joint. | PROPOSED | | 2. Simplify | The LOD chain above. | §4 | GAME:`client-performance.json` | | 3. Quantise | `KHR_mesh_quantization`: positions 14-16 bit, normals 8-10 bit, UVs 12-16 bit. | Quantisation error counts against the 0.75 px budget; the relative ceiling is 5%. | GAME:`client-performance.json` `maximumQuantizationRelativeError` 0.05 | | 4. Compress | `EXT_meshopt_compression` via `gltfpack -cc` or glTF-Transform `meshopt`. | **meshopt, not Draco.** The game registers MeshoptDecoder only and has no Draco loader. Meshopt also decodes faster and keeps animation and morph data. | units repo OD-06; GAME:`skirmish/src/models.ts` | | 5. Cull | Delete faces that are never visible: inside a turret ring, the hull bottom on ground units, interior bulkheads. | Only where no camera or cutaway reaches them; cutaway models keep them. | PROPOSED | - **Order of validation:** validate *before* compressing. The lab's structural fallback fails meshopt- and Draco-compressed files (units repo OD-06). Compression is the last step of the pipeline. - **Textures:** KTX2/Basis is a lookdev decision. The game has no KTX2 loader today. ### Collision proxies - **The sim does not use meshes.** It collides circles: `fallbackRadiusM`, `circumradiusScale` 0.5 (GAME:`data/rules/collision.json`). Mesh proxies are therefore for picking, line of sight and Unreal physics. - **three.js picking:** raycast the part bounding boxes or a `*_proxy` node, never the render mesh. - **Unreal:** `UCX__NN` convex hulls, at most 8 per part, each at most 32 vertices. Wheels and turrets get one hull each. - **Status:** PROPOSED (Unreal FBX/glTF collision naming convention). ## 6. Articulation geometry - **Separate meshes at every joint.** A moving part is its own node and mesh and is never welded to its parent (GAME:`unit-structure.json`, `weldOverlapFraction` 0.08). - **Pivots on the real axis.** The node origin sits on the hinge or rotation axis, and the joint axis is a local axis of that node: X for pitch, Y for yaw, Z for roll and extend (GAME:`unit-joints.json` `kinds`). - **Rest pose:** clips start at rest (units repo OD-05). - **Clearance:** each moving part keeps at least `clashPadM` 0.02 m from its neighbours across its whole range. Overlap growth stays at most `clashGrowM3` 0.0005 m³, checked in `sweepSteps` 12 samples over the limits (GAME:`unit-joints.json`). - **Interfaces:** - **Turret ring:** a cylinder gap of at least 0.02 m. - **Hinge:** model the knuckle as concentric cylinders on the pivot axis, so the gap stays constant through the rotation. - **Fold lines:** cut on the true fold axis, not across a skewed edge. - **Float:** no part may float more than `floatGapM` 0.15 m from its parent in any pose (GAME:`unit-joints.json`). - **What the lab checks:** - swept-volume clash (part against every non-parent part, sampled across the limits); - pivot on axis; - unwelded joints; - the same node set at every LOD. - **Actuator metadata:** in `extras.actuator` as V1 `actuators` (revolute, prismatic, continuous, fixed). - **Every LOD keeps the parts.** A LOD may simplify a part but may not merge two parts that move relative to each other. LOD2 may drop *secondary* moving parts (hatches, flaps) by making them static children, and must still keep turret, barrel, wheels, rotors and gear. ## 7. Author high, derive low: the pipeline ### The owner's question: start from the biggest? **Yes, with one correction: start from the most *detailed*, never from a bigger *scale*.** - **Scale stays fixed.** Every size of an asset is the same real object at 1:1 metres. A smaller LOD has fewer triangles and fewer texture pixels; it is never a smaller model. - **Why the detailed master comes first:** - **Reduction is a one-way, lossy function.** Simplification and downsampling can be scripted; adding detail cannot. Starting from the master makes every other size a mechanical output, and a change to the master regenerates all of them. - **The low mesh needs it.** The detail that makes a 3,500-triangle tank look good comes from normals and AO *baked from* a high mesh. Without a master there is nothing to bake. - **Unreal needs it.** Nanite consumes the dense master directly. One master feeds both engines: the browser gets the derivations and Unreal gets the original. - **Owner's principle.** "Everything is math and everything is designed to be real" supports this: one measured truth, with every other form computed from it. - **What is not free:** - Below about 1,000 triangles, an automatic decimation of a complex silhouette (rotors, antennas, rails) degrades. Budget hand fixes at LOD2 and the impostor, for hero units only. - A master that is unstructured (one welded, unnamed, wrong-axis mesh) cannot be derived. It must be fixed first. ### What must be anchored Anchor these once, in the master, and every smaller size is mechanical: 1. **Real-metre scale**, checked against GAME:`unit-dimensions.json` within 2%, with the 5% fail threshold (units repo OD-04). 2. **Axes:** +Y up, -Z forward, +X starboard, on the ground plane (§1). 3. **Part split, pivots and names** per the part contract. LODs inherit them by construction. 4. **UV0 layout made for the LOD0 game mesh**, so the master bakes into it. Lower LODs keep UV0 (simplification preserves attributes), so a **single texture set serves every LOD**: only the mip level changes. This is the single biggest saving. 5. **Baked maps** (normal in tangent space with MikkTSpace, AO, ID) baked once from master to LOD0. 6. **Hard-edge and seam marks**, which simplification is told to preserve. 7. **A metadata block** in `asset.extras`: unit id, `unit_dimensions`, master hash, standard version. A derived file names its master hash, so a stale LOD can be detected. ### Pipeline (SV3/SV4, headless; details in SKILLS.md) ``` master.blend (Blender 4.2.3; or Cinema 4D / Substance Modeler -> .blend) | bpy: validate (scale, axes, manifold, pivots, names) -> lab rules | bpy: build LOD0 game mesh (manual retopo or Decimate + UV0) -> per part | Blender bake (Cycles) or Substance Painter: master -> LOD0 normal/AO | bpy: Decimate per part to LOD1, LOD2 targets; triangulate; export GLB | gltf-validator + lab: validate each uncompressed GLB | gltfpack / glTF-Transform: quantise + meshopt (last step) | impostor bake from master (Blender orthographic renders) v .lod0.glb .lod1.glb .lod2.glb .impostor.png master -> Unreal (Nanite) [not testable: Unreal not installed] ``` ## 8. The SVG idea: vectors versus meshes **The owner's instinct is right for vector content and does not carry over to meshes or textures.** - **Vector content** (blueprint sheets, NATO/APP-6 counters, icons, decals, insignia, hull numbers, UI glyphs): - **one SVG is the master**; - set `viewBox` in real units: **1 user unit = 1 mm**, with `width`/`height` in `mm`; - an E-2D plan view is then `viewBox="0 0 24560 17600"` (span x length, from GAME:`unit-dimensions.json`); - put the origin at the model origin (shifted by `translate`) and include a scale bar; - it is "anchored" because it carries its true size, and every raster (decal at 2048, icon at 24 px, counter at 64 px) is a scripted render; - decals placed on a mesh use the same millimetre coordinates as the mesh's metres x 1000. - **Meshes and textures are not vector.** - A mesh is already resolution-independent in *scale* (metres), but not in *detail*. Its "smaller sizes" are fewer triangles, which needs the simplify and bake path in §7. - A texture is a raster, so its smaller sizes are mips and downsamples of the largest bake. Lookdev owns those. - **One link between the two:** blueprint SVGs drawn at 1:1 mm can be imported into Blender as curves at scale 0.001, to block out the master. The vector master then *anchors* the mesh master. ## 9. Acceptance (what the Wireframe Lab reports per file) | Check | Applies to | |---|---| | Stage | master or delivery | | Class | from the classifier, with the stage's budget | | Triangles, vertices, primitives, materials | against cap and target | | Scale and axes | including the handedness check (OD-G1) | | Manifold, degenerate, flipped, finite | | | Pivots on axis; moving parts unwelded | | | Swept-volume clash | at `clashPadM` | | LOD node-set equality; descending triangles | | | glTF validity | Khronos validator | | Compression | none at validation; meshopt only after | Rule ids are in [geometry-standard.json](geometry-standard.json) `checks`, aligned with V1 keys where V1 already has the rule. ================================================================================ FILE: LOOKDEV STANDARD: standards/standards_lookdev/README.md ================================================================================ # Look development and technical-art standard Repo: `jcom_v0bb_simulation_design_standards_lookdev`. Status: draft, 2026-10-20. It covers JCOM and Conquer: the three.js browser RTS on phones, and the same assets in Unreal Engine 5. This standard sets how surfaces, light, the camera image and effects look, and how one master asset becomes a phone asset and an Unreal asset. - Geometry is out of scope: topology, LOD triangle counts and mesh budgets belong to the sibling repo `design_standards_geometry`. - Reference sheets belong to `design_standards_blueprints`. - Unit sizes, facing and joints belong to `design/standards_units`. - This repo links to those three and does not copy them. Companion files: - `lookdev-standard.json`: the same rules in machine-readable form for validators. - `OPEN-DECISIONS.md`: what the owner still has to pick. - `SKILLS.md`: the tools and guides, each rated ADOPT, ADAPT, REFERENCE or SKIP. How to read the tags: - **[S#]** cites a web source listed at the end. - **[L#]** cites the live game tree or an estate file. - **PROPOSED** marks a value with no source. It needs owner approval. - **MATH** marks a value derived on the page, with its working shown. --- ## 0. The name and the one rule **Name.** "Look development" (lookdev) is the industry term for deciding how an asset's materials, textures and lighting look under the target renderer. "Technical art" (tech art) is the broader discipline. It adds performance budgets and the pipeline, and it also covers rigging and geometry. I chose **lookdev** for this repo for two reasons: - The sibling repos already own geometry and rigging. "Technical art" would overlap them. - Lookdev names exactly what is left: surfaces, light, the camera image and effects. The budgets and pipeline in sections 6 and 8 are the tech-art half of the job. They live here because they govern textures and shaders, not meshes. The alternative name `design_standards_techart` is OD-01 in OPEN-DECISIONS.md. **The one rule: author high, derive low, never the reverse.** Every asset has one master in real metres with lossless textures. Every game and Unreal file is produced from that master by a script. Nobody hand-edits a derived file. Section 8 answers the owner's "start from the biggest?" question. --- ## 1. Glossary: the term for each thing | We say | Industry term | What it means for us | |---|---|---| | The look of things | **Look development (lookdev)** | Materials, textures and light, judged in the target renderer against the canon renders [L13] | | Making it fast and portable | **Technical art** | Budgets, the pipeline and shaders | | Realistic materials | **PBR, metal/rough workflow** | Base colour, metallic, roughness, normal and occlusion, as in glTF 2.0 core [S23] | | Colour of the surface | **Base colour / albedo** | Diffuse colour for non-metals, specular colour for metals, with no light or shadow baked in [S1][S3] | | Shiny vs dull | **Roughness** | 0 is a mirror, 1 is fully matte. Surface variation lives here [S1] | | Metal or not | **Metallic (metalness)** | 0 or 1 only. In-between values are allowed only at rust and dust boundaries [S1] | | Bump detail | **Normal map (tangent space, MikkTSpace)** | Fake small relief. glTF and three.js use the OpenGL convention (green up). Unreal uses DirectX (green down), so the green channel is flipped on import [S23] | | Shadows in crevices | **Ambient occlusion (AO)**, baked | Stored in the R channel of ORM | | Three greyscale maps in one file | **Channel packing, ORM** | R occlusion, G roughness, B metallic, the glTF layout [S23]. Unreal's "OcclusionRoughnessMetallic" preset has the same layout | | sRGB vs linear | **Colour space / transfer function** | Colour maps are sRGB; data maps are linear [S7][S21] | | Smaller copies of a texture | **Mipmaps (mip chain)** | Add 33% memory and remove shimmer [S7] | | GPU-compressed textures | **Block compression**: ASTC, ETC2, BC7, BC5 | The texture stays compressed in GPU memory | | One file that becomes any of those | **KTX2 with Basis Universal** (ETC1S or UASTC) | Transcoded on the device to ASTC, ETC2 or BC [S6] | | Pixels per metre on the surface | **Texel density** (px/m) | Set per asset class (section 2.6) | | A strip texture reused across a ship | **Trim sheet** | Tiling strips of panel lines, rails and hatches, mapped by UV | | Many textures in one | **Texture atlas** | Fewer draw calls [S7]. Not to be confused with the banned "stripe atlases" [L10] | | Stickers: hull numbers, ensigns | **Decals**: mesh decals here, deferred or DBuffer decals in Unreal | Section 2.5 | | Player-colour paint | **Team colour mask / tint** | The house rule is a material named `houseTint` [L10] | | Light from the sky picture | **Image-based lighting (IBL)** | three.js builds it with `PMREMGenerator`. Unreal uses Sky Light and Lumen | | Squeezing HDR into the screen | **Tone mapping** (ACES, AgX, Khronos PBR Neutral) and **exposure** | Section 3.2 | | Shadows split by distance | **Cascaded shadow maps (CSM)** | Unreal replaces these with **Virtual Shadow Maps** | | Real-time global illumination | **Lumen** (UE5) | Not available in a browser | | Unlimited-detail meshes | **Nanite** (UE5 virtualized geometry) | Opaque and masked materials only [S19] | | Light baked into a texture | **Lightmap / baked lighting** | Second UV set | | Image effects after render | **Post-processing**: bloom, SSAO, SSR, FXAA, SMAA, TAA, MSAA | Section 4 | | Sparks, smoke, splashes | **VFX**: particles, flipbooks (sprite sheets), soft particles | Section 5 | | Many copies, one draw | **GPU instancing** (`InstancedMesh`, `BatchedMesh`, EXT_mesh_gpu_instancing) [S14] | | | A flat picture standing in for a far model | **Impostor (billboard)** | Already in quality tiers as `impostorsAfter` [L7] | | Cost counters | **Draw calls, triangles, texture memory, shader programs (variants)** | Section 6 | | One master, many outputs | **Asset pipeline / DCC-to-engine pipeline**, **LOD chain**, **bake (high-to-low)** | Section 8 | --- ## 2. Materials and textures ### 2.1 Workflow - Use glTF 2.0 core metal/rough, with three.js `MeshStandardMaterial` [S23][S5]. - Use `MeshPhysicalMaterial` only where an allowed extension below needs it. It is a heavier shader. - In Unreal, the same inputs feed one master material, and each asset uses an instance of it. ### 2.2 Real-world values Everything is meant to be real, so every base colour and roughness is checked against measured tables. Non-metal base colour is **50–240 sRGB** (about 0.03–0.87 linear) [S3]. Nothing in the world is pure black or pure white. | Material | Base colour (linear 0–1) | Source | |---|---|---| | Fresh asphalt, charcoal | 0.02–0.04 | [S1][S3] | | Worn asphalt | 0.08–0.12 | [S1][S3] | | Bare soil | 0.13–0.17 | [S1][S3] | | Green grass | 0.21–0.25 | [S1][S3] | | Desert sand | 0.36–0.40 | [S1][S3] | | Fresh concrete (runways, bunkers) | 0.51–0.55 | [S1][S2][S3] | | Ocean ice, glacier face | 0.50–0.70 | [S1][S3] | | Fresh snow (HQ roofs and runway ends, painted snow white [L14]) | 0.80–0.90 | [S1][S3] | | Iron, steel, bare metal (metallic 1) | (0.53–0.56, 0.51–0.57, 0.49–0.58) | [S1][S2] | | Stainless steel (metallic 1) | (0.669, 0.639, 0.598) | [S2] | | Aluminium (metallic 1) | (0.913–0.916, 0.921–0.923, 0.924–0.925) | [S1][S2] | | Titanium (metallic 1) | (0.542, 0.497, 0.449) | [S1] | | Water | Not a base colour. It gets its look from Fresnel with IOR 1.333 (F0 = 0.02) and depth absorption | [S2]; the live water already uses F0 0.02 [L3] | Further rules: - **Naval paint** (haze grey, deck non-skid) and **military paint** are non-metals (metallic 0) even on steel hulls, because the paint is what you see. Values are PROPOSED until a sampled reference sheet exists (OD-07). - **Metallic is 0 or 1.** Values between are allowed only on painted metal where the paint is worn through to bare metal [S1]. - **Specular stays at the default** (F0 4%, IOR 1.5). Do not author KHR_materials_specular or KHR_materials_ior [S1]. - The source tables disagree on whether their values are sRGB or linear. Unreal's page does not state it [S1]. This standard treats [S1] and [S3] as linear. Validators check base colour in linear after decoding sRGB. ### 2.3 glTF extensions | Extension | Verdict | Why | |---|---|---| | KHR_texture_basisu | **USE (required for game textures)** | KTX2 stays compressed on the GPU, about 4–8x less memory than PNG [S6][S7]. Loader support confirmed [S5] | | EXT_meshopt_compression (or KHR_meshopt_compression) | **USE** | Already used by the live loader [L6]. Smaller and faster to decode than Draco [S9] | | KHR_mesh_quantization | **USE** | gltfpack's default. Smaller vertex buffers [S9] | | KHR_texture_transform | **USE (sparingly)** | gltfpack quantization uses it [S9]. Also handles trim-sheet offset and tiling | | KHR_materials_emissive_strength | **USE** | For runway lights, screens and engine glow above 1.0 for bloom [S4] | | EXT_mesh_gpu_instancing | **USE for static props** | Repeated bollards and lights become one draw [S9] (`-mi`) | | KHR_materials_unlit | **ALLOW (UI-in-world, impostors only)** | Cheapest shader | | KHR_materials_clearcoat | **ALLOW on desktop and ultra only**; strip for phone | Needs MeshPhysicalMaterial. Worth it only for wet or lacquered hero surfaces. Packs into UASTC [S6] | | KHR_draco_mesh_compression | **AVOID** | Two decoders for one job. Meshopt is already in the loader [L6] | | KHR_materials_transmission | **AVOID on phone; ALLOW only for hero glass in Unreal** | In three.js it renders an extra transmission pass of the opaque scene. Use plain alpha blend for canopies and windows. In Unreal, translucency cannot be Nanite [S19] | | KHR_materials_volume, ior, specular, sheen, iridescence, anisotropy, dispersion | **AVOID** | Heavier physical shader, more shader variants, little visible gain at RTS distance. Sheen is for cloth | | KHR_materials_variants | **AVOID for now** | Loader support is unconfirmed [S5]. Team colour already uses `houseTint` [L10]. Damage states belong in the rig and state files owned by the units repo | | KHR_lights_punctual (lights in the file) | **AVOID in game files** | In forward rendering every light costs every lit pixel. Fixtures are emissive plus baked light. Unreal masters may carry them for Lumen | | EXT_texture_webp / AVIF | **AVOID** | Smaller downloads, but they decode to full RGBA8 in GPU memory. KTX2 is the target | ### 2.4 Texture maps: colour space, packing and format | Map | Channels | Colour space | Master (lossless) | three.js game (KTX2) | Unreal | |---|---|---|---|---|---| | Base colour (+ alpha) | RGB(A) | **sRGB** | 16-bit PNG | **ETC1S** for most, UASTC for hero or close-up [S6] | sRGB on, BC7/BC1 (Default) [S21] | | Normal | RG (B rebuilt) | **linear** | 16-bit PNG, OpenGL (green up) | **UASTC** [S6] | Normalmap / BC5, flip green [S21] | | ORM | R=AO, G=rough, B=metal | **linear** | 16-bit PNG | **UASTC** [S6] (channels differ) | Masks, sRGB off [S21] | | Emissive | RGB | **sRGB** | 16-bit PNG, or EXR for HDR | ETC1S [S6] | sRGB on | | Height or displacement (master only) | R | linear | 32-bit EXR or 16-bit PNG | not shipped; baked into the normal map | Nanite displacement or tessellation | | Team mask (if textured) | R | linear | 16-bit PNG | UASTC, or packed into base colour alpha (OD-05) | Mask | - Roughness sits in G. That is both the glTF layout and the channel mobile compressors keep most precisely [S7][S23]. - Sizes are powers of two, and always multiples of 4 for Basis [S6]. ### 2.5 Trim sheets, atlases and decals - **Trim sheets: USE for large hard-surface assets** (ships, bunkers, hangars, the sea gate). MATH: a 330 m carrier has roughly 50,000 m² of visible surface. A unique unwrap at 64 px/m would need 50,000 × 4,096 ≈ 205 M texels, a 14k texture, which is impossible on a phone. Large assets therefore use tiling materials and trim sheets on UV0 or UV1, plus a small unique texture for AO and dirt. Unreal masters can add a UDIM unique layer (OD-06). - **Atlases: USE for props, VFX flipbooks and decals**, so they share one material and one draw [S7]. - The house ban on **stripe atlases, value-noise tiles and 2x2 colour blocks** stands [L10]. - An atlas must hold real material maps, not synthetic colour bands. - **Decals: USE mesh decals in the game.** These are thin alpha-tested quads lifted off the surface with polygon offset and textured from one decal atlas. Use them for hull numbers, ensigns and deck markings. - **Decals: AVOID runtime `DecalGeometry`** for anything numerous, because it builds new geometry per decal. - In Unreal, the same decal atlas drives DBuffer decals or the mesh decals. - The decal count per tier is already in the quality rules: 128, 64, 24, 8, 0 [L7]. ### 2.6 Texel density per class (px/m) MATH, from the live camera [L1]: - Closest RTS zoom is 22 world units, which is 55 m, at a 50° vertical field of view. - The visible height there is 2 × 55 × tan 25° ≈ 51 m. - On a 1080 px desktop that is about 21 screen px per metre. A 2160 px 4K screen at the ultra pixel ratio of 2 gets about 42. - On a phone at the medium tier (pixel ratio 1.25), portrait gives 1065 px over 51 m, about 21 px/m. - Anything above about 2x screen density never reaches a pixel in the RTS view. The mip chain throws it away, but the download and memory still cost. | Class | Game (three.js) | Master and Unreal | Status | |---|---|---|---| | Units (vehicles, aircraft, infantry) | **64 px/m** (inspect and gallery view may load the 2048 tier) | 512 px/m | MATH + PROPOSED | | Buildings, props | **64 px/m** unique, with trim and tiling at 128 px/m | 512 px/m | MATH + PROPOSED | | Ships, large facilities | trim and tiling at **128 px/m**, unique AO and dirt at 8–16 px/m | 512 px/m trim, plus an optional UDIM unique layer | MATH + PROPOSED | | Terrain | macro 1–4 px/m, plus detail tiling at 12 m per repeat [L7 terrain-look] | 256–1024 px/m material sets | live value + PROPOSED | | Hero cinematics (Unreal only) | — | 1024 px/m (first-person class [S22]) | REFERENCE | The ratio of 8x between master and game equals three mip levels. Deriving the game texture is therefore just dropping the top three mips. ### 2.7 Maximum texture size per platform | | Phone | Tablet | Desktop | Unreal master | |---|---|---|---|---| | Per-texture cap (live tiers) | 1024 at medium, 512 at low, 256 at potato [L7] | 1024 | 2048 at ultra [L7] | 4096–8192 (UDIM allowed) | | Unit textures, house rule | ≤ 1024 [L10] | ≤ 1024 | ≤ 1024, 2048 for inspect | 4096 | | Total GPU texture memory | **≤ 192 MB** PROPOSED | ≤ 384 MB PROPOSED | ≤ 1024 MB (desktop GPU ceiling 2048 MB total [L7 runtime-budgets]) | not budgeted (virtual textures) | MATH, memory per 1024² texture with mips: - RGBA8 PNG: 4 B × 1,048,576 × 1.33 ≈ **5.6 MB**. - ASTC 4x4 or BC7: 1 B/px, ≈ **1.4 MB**. - ETC1S transcoded to ETC2 RGB: 0.5 B/px, ≈ **0.7 MB**. KTX2 therefore gives 4–8 times the texture budget for the same memory. --- ## 3. Lighting ### 3.1 What the live game does [L1][L2][L5][L12] - The renderer is WebGL with three 0.170. - Lighting is one sun (`DirectionalLight`) plus a `HemisphereLight`. - Intensities are art estimates: sun 2.2–2.65, hemisphere 0.85–1.35. - Tone mapping is ACES Filmic, with exposure 1.05–1.25. - Shadows are PCF soft. - The sun is fixed at azimuth 132°, elevation 48° for every map [L2]. - There is **no environment map**. The code itself notes that "very metallic exports have no environment probe in the RTS view" [L5]. Metals reflect nothing and read as dark. ### 3.2 three.js rules | Item | Rule | |---|---| | Light units | Physically based lights are the three.js default since r155 [S12]. Keep relative intensities, but lock the **ratios**: sun to sky, and sun to fixtures per time of day. Store them in `hq-environment.json` and `terrain-look.json`, as the game already does. PROPOSED: a clear-day sun-to-sky irradiance ratio of about 4:1 | | Environment (IBL) | **USE.** One `PMREMGenerator.fromScene` (or `fromEquirectangular`) of the same sky gradient the water already uses [L3]. Build it once per time-of-day change, not per frame. Set it as `scene.environment` with an `environmentIntensity` ratio. This fixes the black metals [L5] and costs one small cubemap | | Tone mapping | Keep ACES Filmic until an A/B test on the canon-render scenes. **Candidate: AgX**, because it is Blender 4.x's default view transform, so what artists see on SV3 and SV4 matches the game. It also keeps fire, explosions and runway lights from skewing hue. Khronos PBR Neutral is for product-accurate colour, not cinematic scenes. Options per [S11]. OD-03 | | Colour management | Working space linear sRGB. `outputColorSpace = SRGBColorSpace`. Colour textures are tagged `SRGBColorSpace`, data textures are not [S11]. When post-processing, end with `OutputPass` | | Exposure | One exposure value per scene preset, as the live game already has. No auto-exposure in the browser | | Shadows | One directional shadow, fitted to the **visible** play area. The live game already does this: boot sets world bounds, but every frame `fitShadowFrustum` refits the shadow to `min(camDist, shadowDistance)` [L1][L7]. Keep it. MATH for the reason: a whole 320-tile map is 1,600 m over 2,048 texels, 0.78 m per texel. The ultra radius of 180 world units (360 across, 900 m) gives 0.44 m per texel at 2048. At closest zoom (22 world units, the 25-unit floor, 125 m across) it is about 0.06 m per texel. The live `shadowCache` updates only when the light changes, which follows the static-shadow advice [S15]. PROPOSED: minimum `shadowMapSize` 1024 on phone medium, so a 7 m tank at mid zoom gets at least about 16 shadow texels | | Cascades (CSM addon) | **AVOID on phone.** It uses `onBeforeCompile`, which blocks WebGPU [S13], and it multiplies shadow passes. Desktop ultra only, if ever | | Point and spot light shadows | **AVOID.** Point shadows render 6 times [S15] | | Dynamic lights | Phone: sun plus hemisphere plus environment, nothing else. Muzzle flashes and explosions are emissive VFX, not lights. Desktop: at most 4 short-lived point lights, PROPOSED | | Baked AO and lightmaps | **USE** baked AO in ORM for every asset. Lightmaps (second UV) only for static interiors such as the E02 under-ice floors and hangars | ### 3.3 Sun from real geography (time of day) "Everything is math": the sun's position comes from latitude, longitude, date and time with the NOAA solar position algorithm [S25] (or the SunCalc library), not from a hand-picked angle. MATH, maximum sun elevation at the June solstice is 90° − latitude + 23.44°: | Site | Latitude | Max sun elevation | Equinox noon | |---|---|---|---| | Camp Century, Greenland HQ | 77.17° N | **36.3°** | 12.8° | | Kharg Island | 29.25° N | 84.2° | 60.8° | | Diego Garcia | 7.31° S | 90° (overhead, Feb/Oct) | 82.7° | The live HQ sun at 48° elevation [L2] is higher than the sun ever gets at Camp Century. Real HQ light is low, warm-to-white, with long shadows, and there is polar night from about November to February. This conflicts with the owner's standing HQ "ideal daylight" rule [L14]. The rule does not say which date. The proposal is OD-04: - HQ default: June 21, local solar noon, sun elevation 36.3°. - Missions set their own date and time. ### 3.4 Water and sea The live water [L3] is a `MeshStandardMaterial` with `onBeforeCompile`: - Depth fade comes from the terrain height texture. - Shore foam is drawn in the shader. - Waves are a normal perturbation. - The reflection is the sky gradient (horizon to zenith), weighted by a Schlick Fresnel with F0 0.02. That F0 is physically correct for water at IOR 1.333. - There are no planar reflections, no SSR and no environment cubemap. Rules: - **USE:** - the analytic sky reflection; - depth-based colour, with absorption in the deep colour; - shoreline foam from depth; - a sun glint from the real sun direction. - **Next step, USE:** sample the PMREM environment from 3.2 instead of a two-colour gradient, so clouds and the real sky tint show in the water. - **AVOID on phone:** - planar `Reflector` (it renders the scene a second time); - SSR; - refraction render targets; - FFT ocean simulation. - **Desktop ultra may add** a half-resolution planar reflection of units only (PROPOSED). - **WebGPU is blocked.** The terrain splat and water shaders use `onBeforeCompile`, which `WebGPURenderer` does not run [L4][S12]. The port is to TSL node materials, estimated in the code at 2–4 days [L4]. Until it is decided (OD-08): - no new `onBeforeCompile` shaders; - new shader work is written so that it ports to TSL: small pure functions with their uniforms named in the rules JSON. - **Unreal:** the Water plugin with Single Layer Water material, or a Gerstner and FFT ocean for the open sea; Sky Atmosphere for the sky [REFERENCE, page not fetched]. ### 3.5 Unreal lighting - **Lumen** is used for global illumination and reflections. Walls are at least 10 cm thick so light does not leak. Building shells are modular. Lumen's default scene range is 200 m [S20]. - **Nanite** is used for every opaque and masked mesh. Glass, water and canopies stay non-Nanite because translucent materials are not supported [S19]. - **Virtual Shadow Maps** are used with Nanite [REFERENCE]. - **Sky Atmosphere** is used, with the directional light set to the same real-geography sun as in 3.3 [REFERENCE]. - Turn off "Generate Lightmap UVs" because nothing is baked [S19]. - Use **virtual textures** for large unique assets [S19]. --- ## 4. Post-processing | Effect | Phone | Desktop | Note | |---|---|---|---| | Tone mapping + sRGB output | **USE** (renderer, no extra pass) | USE | | | MSAA via `antialias: true` | USE at medium and above if it holds frame rate; off at low [L7] | USE | Cheapest anti-aliasing on mobile tile GPUs [S15][S18]. With a composer, use a multisampled render target, or the MSAA is lost | | Bloom | **AVOID** (`postEffects` off at medium and below [L7]) | USE, one merged pass, threshold above 1.0 so only emissive glows | pmndrs `EffectPass` merges effects [S16]. The `UnrealBloomPass` chain in the compare tool [L8] is for review only | | FXAA, SMAA | AVOID | SMAA only if MSAA is off | Cost more than MSAA on mobile [S15][S18] | | SSAO, GTAO | **AVOID**; bake AO | AVOID; bake AO | No sourced mobile cost; baked AO is free at runtime | | SSR | AVOID | AVOID | Water uses environment reflections (3.4) | | TAA | AVOID | AVOID | Ghosting on fast units, and needs motion vectors. Unreal uses TSR natively | | Fog | USE linear `Fog`, as live [L1] | USE | Never hides anything inside camera coverage or at HQ [L14] | | Colour grading LUT | AVOID until there is a reason | ALLOW, one LUT, merged into the tone-map pass | | The frame budget is in section 6. Merging passes saves 10% or more of frame time on Adreno [S18]. --- ## 5. Effects (VFX) - **Particles:** use `InstancedMesh` or `Points` with one atlas material per effect family. - Count caps per tier are already live: 400, 200, 80, 24, 0 [L7]. - Use additive blending for fire and muzzle flashes; alpha blending for smoke and spray. - **Flipbooks** (sprite sheets) go in KTX2 atlases. Base colour uses ETC1S; normals, if any, use UASTC. - PROPOSED: at most 8×8 frames of 128 px in a 1024 atlas. - **Overdraw is the phone limit** (fill rate). Keep big translucent cards few and small on screen. - PROPOSED: no more than 3 layers of full-screen-equivalent overdraw from VFX on the phone tier. - Use soft particles (depth fade) only on desktop, because they need a depth texture. - **Real physics, not decoration.** Spray, wake and smoke sizes come from real dimensions in metres and real speeds. Examples: the Sea Dragon launch plume [L13]; steam at the sea gate. - **Unreal:** use Niagara with the same atlases, plus higher-resolution flipbooks from the master. - **Instancing:** identical units share one instanced draw. The live game already does this in `unitBatches` [L7 client-performance]. Use `BatchedMesh` for varied static props sharing one material [S14]. --- ## 6. Performance budgets (mid-range phone, 393×852 class) | Counter | Phone | Desktop | Source | |---|---|---|---| | Frame rate | 30 fps at low and potato, 60 at medium [L7 frameCapByTier] | 60, p95 under 50 ms [L7] | live | | Draw calls per frame | **≤ 150** target, 300 hard ceiling | ≤ 1,000 | PROPOSED, adapted from Quest at 100–200 [S17]; a phone renders one view, not two | | Triangles on screen | **≤ 500k** | ≤ 3M | PROPOSED from [S17]. Per-asset triangle budgets are owned by `design_standards_geometry` (the live `classPolyBudgets` [L7]) | | Triangle size | ≥ 4 px covered on average | — | [S18] | | Texture memory | **≤ 192 MB** | ≤ 1,024 MB | PROPOSED, MATH in 2.7 | | Shader programs | **≤ 40** linked programs | ≤ 120 | PROPOSED; every extension and material feature adds variants | | Lights affecting a pixel | 1 sun + hemisphere + environment | + up to 4 point lights | PROPOSED | | Post passes | 0 extra (tone mapping in the main pass) | ≤ 2 merged | [S16][S18] | | Pixel ratio | ≤ 1.25 at medium, 1 at low [L7] | ≤ 2 | live, [S15] | | Anisotropy | 2 on phone | 8 | [S7][L7] | **Measurement method:** 1. Read `renderer.info.render.calls`, `.triangles`, `renderer.info.programs.length` and `renderer.info.memory.textures` [S11]. With post-processing, set `autoReset = false` and reset once per frame. 2. Estimate texture memory from width × height × bytes per pixel × 1.33 per texture. WebGL has no GPU-memory counter [L7 runtime-budgets]. 3. Run on a real reference phone through Chrome remote debugging or Safari Web Inspector. Hold for 10 s and report p50 and p95 frame time, using the live quality harness [L7]. 4. Run the scene on the canon-render camera positions (section 7) so every report uses the same views. 5. The reference phone is OD-09. --- ## 7. Look reference - The **canon renders** in `castdb/canon/facilities/renders/` are the target look [L13]. Lookdev reviews compare a game screenshot and an Unreal still against the matching render at the same camera. - Owner corrections apply on top of the renders [L13]: - The Blue Aurora column ends at the waterline. - Only our own aircraft appear. - HQ is in ideal daylight unless a mission says otherwise [L14]. Night and aurora frames (for example the naval portal render) are **mission** looks, not the HQ default. - Chrome and HUD stay in the house design system: grey accent, glass panels, JSLC tokens. Nothing in the 3D look adds UI colour. Team colours come from the faction palette files, not from this repo. --- ## 8. Multi-target pipeline: one master, mechanical outputs ### 8.1 "Should we start from the biggest?" **Yes. Start from the highest-fidelity master and derive every smaller version from it.** 1. **Information only flows downhill.** Downsampling a texture, decimating a mesh and baking high detail to a normal map are mechanical and scriptable. Going up, by upscaling or adding detail to a low-poly mesh, means inventing data, which is exactly what "designed to be real" forbids. 2. **Unreal wants the master as it is.** Nanite meshes and 16-bit textures go in nearly unchanged. Only the phone needs heavy derivation. 3. **The owner's "multiple sizes of each asset" is normal and cheap.** A KTX2 already carries every mip. The phone tier is the same file with the top mips dropped, plus the LOD meshes the geometry repo specifies. Three caveats: - **"Biggest" means richest, not bloated.** The master is 16-bit lossless, in real metres, with clean UVs and named parts, at the master texel density in 2.6. An 8K texture on a bolt is not a better master; it is waste. - **Very large reductions need a planned intermediate.** Automatic decimation from a Nanite master (millions of triangles) straight to a 1,500-triangle phone LOD0 often breaks the silhouette. The master therefore includes a **game-res cage** (a mid-poly mesh built for baking), and the phone LODs come from that cage. Its triangle counts are set in `design_standards_geometry`. - **Some low versions are designed, not derived.** Impostors and NATO counters [L7] are separate deliverables generated from the master by script, not shrunk copies. ### 8.2 What must be anchored so conversion is mechanical | Anchor | Rule | Why it matters | |---|---|---| | **Scale** | 1 unit = 1 metre in the master and in glTF [S23]. The game multiplies by `metreToWorld` 0.4 at load [L9]. Unreal Interchange converts to centimetres | One number per target, never per asset | | **Up and forward** | Master and glTF: +Y up, forward −Z, ground or waterline at y = 0 (house rule [L10]; glTF's default front is +Z [S23], so the house rule needs the manifest yaw). Unreal: Z up, X forward, converted by the importer | Owned by `design/standards_units` (its OD-01) | | **Pivots** | Root at the ground-contact centre. Each moving part's pivot sits at its joint, inside its own bounds [L10] | Rigging and sockets survive every LOD | | **Names** | Node and material names are fixed and survive export: `teamcolor_*` meshes, `houseTint` material [L10], `turret`, `barrel_01`, `rotor_main`, and so on. Run gltfpack with `-kn -km` [S9] | Code finds parts by name | | **UV layout** | UV0 is the unique, non-overlapping unwrap and the bake target, with UV anisotropy median ≤ 1.5 [L10]. UV1 is optional trim/tiling or lightmap. No more than 2 UV sets. No UDIM in UV0 (OD-06) | glTF and three.js have no UDIM. Bakes need one 0–1 layout | | **Texture set** | One Substance Painter texture set per material, exported twice from the same project: preset **glTF PBR Metal Roughness** (OpenGL normal, ORM) and preset **Unreal Engine 4 (Packed)** (DirectX normal, OcclusionRoughnessMetallic) [L11]. Painter 12.1.5 may label the presets slightly differently; check them on SV4 | One paint job, two mechanical exports | | **Bit depth** | Masters 16-bit PNG, EXR for HDR or height. Game copies are derived KTX2. The existing 8-bit 2048 PNG game copies [L11] are an interim step | KTX2 is lossy and never a source | | **File names** | `__.`, for example `ddg51_basecolor_4096.png` or `ddg51_orm_1024.ktx2`. None of the banned words: fixed, final, good, new, latest [L11] | Scripts glob by pattern | ### 8.3 The derivation chain All tools are installed on SV4 unless marked otherwise [L15]. ``` MASTER (per asset, versioned) Modeler / Blender 4.2 / Cinema 4D -> high mesh (Nanite) + game-res cage, metres, named parts Substance Painter -> 16-bit texture sets at master texel density Substance Designer / Sampler -> tiling materials and trim sheets (.sbsar), rendered by sbsrender Photoshop / Illustrator -> decal atlas, markings, ensign vectors |-- UNREAL: master mesh (FBX/glTF via Interchange) + "Unreal Packed" export, Nanite on, | virtual textures for large assets. [Unreal not installed: OD-02] | `-- THREE.JS: Blender headless (blender -b --python): cage -> LOD0/1/2 per geometry repo -> bake normal/AO from high to cage (Painter or Blender) -> glTF export (metres, -Z) -> gltf-transform / gltfpack: resize to class cap, KTX2 (ETC1S colour, UASTC normal+ORM), meshopt, quantize, dedup, prune, instance [not installed: OD-10] -> glTF-Validator + lookdev-standard.json checks -> registry ``` --- ## 9. USE and AVOID ### USE 1. **Metal/rough PBR with measured values**: base colour 50–240 sRGB for non-metals, metallic 0 or 1 [S1][S3]. This is what makes things read as real. 2. **KTX2 / Basis**: ETC1S for colour, UASTC for normal and ORM [S6]. It gives 4–8 times the texture budget (2.7) and replaces the CPU canvas downscale [L6]. 3. **ORM channel packing**, with roughness in G [S7][S23]. One linear texture instead of three. 4. **A PMREM environment from the scene's own sky** [S11]. It gives metals and water something to reflect, and it is built once per time of day. 5. **A shadow frustum fitted to the view, as the live game already does** [L1][S15]. It must survive any refactor (MATH, 3.2). 6. **Sun from real latitude, longitude, date and time** [S25]. Everything is math, and HQ has its own light. 7. **Meshopt plus quantization** (already live) [S9][L6]. 8. **Trim sheets and mesh decals for ships and facilities**. The MATH in 2.5 shows unique unwraps cannot fit on a phone. 9. **Instancing and BatchedMesh** [S14][L7]. 10. **Baked AO in every asset**. It replaces SSAO. 11. **One Substance project, two export presets** (glTF and Unreal) [L11]. 12. **Author high, derive low**, with the anchors in 8.2. ### AVOID 1. **Uploading PNG or WebP textures as RGBA8 for the game.** It uses 4–8x the memory of KTX2. 2. **Draco.** Meshopt already covers it. 3. **KHR_materials_transmission, volume, sheen, iridescence, specular and ior on phones.** Extra passes and heavier shaders. 4. **Lights in glTF files, and point or spot shadows** [S15]. 5. **SSAO, SSR, TAA, FXAA and SMAA on phones**, and bloom below the high tier [S15][S18][L7]. 6. **Planar reflections and FFT ocean on phones.** 7. **New `onBeforeCompile` shaders.** They deepen the WebGPU block [L4][S12]. 8. **A shadow camera covering the whole world**: 0.78 m per texel on a 320-tile map. The live runtime avoids it through `fitShadowFrustum` [L1]. Keep it that way. 9. **Hand-picked sun angles**, including the impossible 48° at Camp Century [L2]. 10. **Synthetic atlases**: stripes, noise tiles and colour blocks [L10]. 11. **Pure black or pure white base colours, and in-between metallic values.** 12. **Editing derived files, or upscaling to make a master.** --- ## 10. Sources Web sources were checked on 2026-10-20 by a research worker unless marked otherwise: - **[S1]** Epic, Physically Based Materials. https://dev.epicgames.com/documentation/en-us/unreal-engine/physically-based-materials-in-unreal-engine - **[S2]** physicallybased.info (A. Palmqvist). https://physicallybased.info/ - **[S3]** S. Lagarde, "Feeding a physical based lighting mode". https://seblagarde.wordpress.com/2011/08/17/feeding-a-physical-based-lighting-mode/ - **[S4]** Khronos glTF extension registry. https://github.com/KhronosGroup/glTF/blob/main/extensions/README.md - **[S5]** three.js GLTFLoader source. https://github.com/mrdoob/three.js/blob/dev/examples/jsm/loaders/GLTFLoader.js - **[S6]** Khronos KTX Artist Guide. https://github.com/KhronosGroup/3D-Formats-Guidelines/blob/main/KTXArtistGuide.md - **[S7]** Android Developers, Textures. https://developer.android.com/games/optimize/textures - **[S8]** glTF-Transform CLI. https://gltf-transform.dev/cli - **[S9]** gltfpack README. https://github.com/zeux/meshoptimizer/blob/master/gltf/README.md - **[S10]** Khronos glTF-Validator. https://github.com/KhronosGroup/glTF-Validator - **[S11]** three.js WebGLRenderer docs (tone mapping, `info`). https://threejs.org/docs/pages/WebGLRenderer.html. Colour-management manual: https://threejs.org/manual/en/color-management.html (the fetch failed; the content is from search results). - **[S12]** three.js Migration Guide (r155 lights, r164 node builder). https://github.com/mrdoob/three.js/wiki/Migration-Guide. The WebGPURenderer manual's statement on `onBeforeCompile` comes from search results; the page itself 404'd. - **[S13]** three.js CSM addon source. https://github.com/mrdoob/three.js/blob/dev/examples/jsm/csm/CSM.js - **[S14]** three.js BatchedMesh. https://threejs.org/docs/pages/BatchedMesh.html - **[S15]** Discover three.js, Tips and Tricks. https://discoverthreejs.com/tips-and-tricks/ - **[S16]** pmndrs/postprocessing. https://github.com/pmndrs/postprocessing - **[S17]** Meta, Device optimization comparison. https://developers.meta.com/horizon/resources/device-optimization-comparison/ - **[S18]** Qualcomm Adreno mobile best practices. https://docs.qualcomm.com/doc/80-78185-2/topic/mobile_best_practices.html - **[S19]** Epic, Nanite. https://dev.epicgames.com/documentation/en-us/unreal-engine/nanite-virtualized-geometry-in-unreal-engine - **[S20]** Epic, Lumen technical details. https://dev.epicgames.com/documentation/en-us/unreal-engine/lumen-technical-details-in-unreal-engine - **[S21]** Epic, Texture compression settings (UE4.27; the UE5 page is not verified). https://docs.unrealengine.com/4.27/RenderingAndGraphics/Textures/TextureCompressionSettings - **[S22]** Maxon, Texel Density. https://help.maxon.net/c4d/en-us/Content/html/Texel_Density.html - **[S23]** Khronos glTF 2.0 specification (metres, +Y up, +Z front, metallicRoughness channels B and G, OpenGL normals). https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html. Known from the spec; not re-fetched this session. - **[S24]** cloudai-x/threejs-skills. https://github.com/cloudai-x/threejs-skills - **[S25]** NOAA Solar Calculator and its algorithm. https://gml.noaa.gov/grad/solcalc/. Not fetched this session. Live and estate sources were read on 2026-10-20 and not modified. `GAME` is `M:\z4yin-work-overflow\hermes-share\codex-lanes\asset-split\sol-maps\work\`. - **[L1]** `GAME skirmish/src/main.ts` 612–641: renderer, hemisphere and sun, with the boot shadow camera at world bounds. At 1765, `fitShadowFrustum` refits it every frame to `graphics.shadowRadius(camDist)`, defined in `quality/graphicsBind.ts:229`. Camera at 1668: `camMin` 22, field of view 50. - **[L2]** `GAME skirmish/src/terrainfx/look.ts` 170–189 and `data/rules/terrain-look.json`: ACES Filmic, PCFSoft, exposure 1.05, sun azimuth 132° and elevation 48°. - **[L3]** `GAME skirmish/src/terrainfx/water.ts`: `onBeforeCompile`, sky-gradient reflection, Schlick F0 0.02, depth foam. - **[L4]** `GAME skirmish/src/compare/main.ts:58`: the WEBGPU_BLOCKED note (three r170, TSL port 2–4 days). - **[L5]** `GAME skirmish/src/mechanics/coastalSkirmish/art.ts:62`: no environment probe. No `PMREM` or `scene.environment` anywhere in `src`. - **[L6]** `GAME skirmish/src/render/textureBudget.ts`: CPU canvas downscale that skips compressed textures. MeshoptDecoder is in `models.ts`. There is no KTX2Loader in `src`. - **[L7]** `GAME data/rules/quality-tiers.json`: tiers, `featureShedOrder`, `classPolyBudgets`, `lodSwitch`, `frameCapByTier`. Also `runtime-budgets.json` and `client-performance.json`. - **[L8]** `GAME skirmish/src/compare/post.ts`: EffectComposer and UnrealBloomPass (compare tool). - **[L9]** `GAME data/rules/world-scale.json`: `metreToWorld` 0.4, `metresPerTile` 5. - **[L10]** `unit-model-contract` skill (`D:\z4yin\.inbox\dolphin4\lanes\design-repo\svc_skills\unit-model-contract\SKILL.md`): −Z, `houseTint`, textures ≤ 1024, no stripe atlases, UV anisotropy. - **[L11]** `adobe-substance-pipeline` skill: 16-bit masters, 8-bit 2048 game copies, Painter export presets, `sbsrender`, banned file words. - **[L12]** `GAME data/rules/hq-environment.json`, `campaign-look.json`: art-estimate intensities and exposure. - **[L13]** Canon renders: `M:\z4yin-work-overflow\hermes-share\codex-lanes\castdb\canon\facilities\renders\` (8 PNGs). Owner ruling of 2026-10-19 that these are the look reference. - **[L14]** Owner HQ rulings, 2026-10-18: ideal conditions, snow-white roofs and runway ends, cameras see through fog. - **[L15]** `D:\z4yin\.inbox\dolphin4\lanes\design-repo\TOOLS-SV3-SV4.md`: installed software, 2026-10-20 16:48. ================================================================================ FILE: BLUEPRINTS STANDARD: standards/standards_blueprints/README.md ================================================================================ # Blueprint standard: how a JSLC blueprint is drawn Status: **PROPOSED, draft for owner review**, v0.2.1, 2026-10-20. US military fill applied. The owner has not issued this standard. Look remains JSLC STD-1. | File | What it is | |---|---| | [`blueprint-standard.json`](blueprint-standard.json) | the machine-readable twin of this page | | [`GENERATOR-SPEC.md`](GENERATOR-SPEC.md) | how to build the generator | | [`SKILLS.md`](SKILLS.md) | survey of tools and skills | | [`example/`](example/) | worked E-2D sheet | **The look is JSLC DRAWING STANDARD STD-1.** STD-1 is the look of the castdb gate and portal sheets. > Owner, 2026-10-20 17:10–17:12: "refer to the blueprints i gave you for the gate" / "i like how that looked" / "what you just showed me is not good looking". > > Owner, 16:41: "Blueprints should be done programatically and mathematically. Everything is math and everything is designed to be real." STD-1 sets how a sheet looks. US Navy facility practice and ASME Y14 practice set the maths. ISO is not an authority in this revision. | Area | Standard | Revision | |---|---|---| | Lines and lettering | ASME Y14.2 | 2014 (R2026). Dash table not read. STD-1 geometry kept. | | Views | ASME Y14.3 | 2012 (R2024). Third-angle. Body paragraph not read. | | Dimensions | ASME Y14.5 | 2018 (R2024). No geometric tolerances on these sheets. | | Sheet sizes | ASME Y14.1, publisher revision | 2020 (R2026). Size table not read. Navy FC names the metric sheet ISO A1. ISO is not an authority. DoD adoption of this reaffirmation is unverified. | | Drawing practice | ASME Y14.100 | 2017 (R2026) | | Drawing types | ASME Y14.24 | 2020 (R2026). Monodetail text not read. | | Parts lists | ASME Y14.34 | 2013 (R2024). Body not read. | | Revisions | ASME Y14.35 | 2025. Letter list not read. | | Facility sheets, text floor | NAVFAC FC 1-300-09N | 17 May 2024, Change 1, 17 Apr 2025, §12-3.3.4 | **PROPOSED** marks a rule the owner has not issued. Paywalled body text stays unverified. ASME dates in the table are publisher revisions. MIL-STD-100G Notice 1 (14 Dec 2001) names ASME Y14.100, Y14.24, Y14.35M, and Y14.34M after it cancels MIL-STD-100G. That notice does not prove DoD adoption of each 2024 or 2026 reaffirmation. DoD adoption of those current editions is unverified. ## 0. Output scope Draw buildings, gates, doors, tunnels, pens, and yards on the eight Flight 1 maps. Greenland HQ (`jslc-00`, `greenland_naval_bunker`) is first. Diego Garcia (`jslc-05`) is second. The other six maps in that set come after those two. Every map outside the eight is deferred. Aircraft, ship, submarine, vehicle, and infantry sheets are deferred. Their class rules stay in this file for a later job. Cite FILL-SCOPE. Do not emit a deferred class. | Related topic | Where | |---|---| | Unit naming, axes, real dimensions, nodes, joints | [`../standards_units/README.md`](../standards_units/README.md) | | STD-1 reference sheet | `M:\z4yin-work-overflow\hermes-share\codex-lanes\castdb\canon\facilities\blueprints\camp-century\05-tidewater-caisson-blast-gate-detail.svg` | | STD-1 definition (code) | `…\castdb\canon\facilities\tools\gen_campcentury_std.py`, class `StdSheet`. It is copied from `gen_node_neptune_portal.py` | | Ship drafting (frames, decks, compartments) | `D:\z4yin\z4yin-files\skills\blueprinter\SKILL.md` (§12) | --- ## 1. Principles 1. **A blueprint is computed.** Each line is a projection of the source GLB. Each number comes from the GLB, from a node pivot or joint, or from a cited dossier figure. Do not trace or guess. 2. **AI images and hand drawings are not canon.** 3. **The SVG is canon.** The SVG and its audit JSON are the record. The PDF is the colour vector copy. TIFF, PNG, and the sidecar are renditions made from the SVG. 4. **One look for every sheet: STD-1.** Every class uses the same paper, ink, panels, bottom band and title block. A tank sheet and a carrier sheet look like one drawing set. 5. **Line meaning comes from line type and weight.** Navy header bars mark panels. Red marks superseded content and FAIL values only, always with a word. Do not use yellow or amber. 6. **Show the model figure and the real figure.** If they disagree by more than the tolerance, the sheet says WARN or FAIL. The sheet does not hide a model error. ## 2. Coordinate frame | Item | Value | Source | |---|---|---| | Units | metres; 1 model unit = 1 m | unit standard §1 | | Up | +Y | unit standard §1 | | Forward (nose, bow, gun) | −Z | unit standard §1; OD-01 | | Starboard | +X | unit standard §1 | | Ground | Y = 0 | unit standard §1 | | Ships | design waterline DWL at Y = 0 | unit standard §1; OD-07 | **Canonicalisation.** The generator always projects in this frame. If a model faces a different direction, the generator turns it about +Y first. The angle is recorded as `canonYawDeg` in the title block and the SVG. **Conflict (OD-01).** - The E-2D lab spec says "+Z points toward the nose, and +X is starboard" (`LAB:OWNER-SPEC.txt` §1). - That combination is left-handed. In glTF, with +Y up and the nose at +Z, starboard is −X. - The GLB confirms this. After the 180° turn, the nodes named `*_port` are at +X, which is starboard. - So the sheet takes port and starboard from geometry, not from names (§8.6). ## 3. Sheets and scale ### 3.1 The STD-1 sheet STD-1 is drawn on a 2400 × 1600 px canvas. This standard keeps the STD-1 look and puts it on metric sheets in millimetres, landscape. The sizes used are A0 1189×841, A1 841×594, A2 594×420, A3 420×297 and A4 297×210 mm. NAVFAC FC 1-300-09N §12-3.3.4.1 calls the metric facility sheet ISO A1 (594 × 841 mm) and the imperial sheet ANSI D (22 × 34 in). Those are the Navy's words for the physical format. This standard adopts that Navy metric option. ASME Y14.1-2020 (R2026) is the selected size authority. Its size table was not read. ISO 216 is not an authority. The default facility sheet is metric A1, landscape, because the sheet unit is the millimetre. ANSI D is the imperial alternate, not the default. On A1, one STD-1 pixel is about 0.35 mm. Cite OD-B2. | Part | Rule (values for A1; JSON `std1` has all values) | |---|---| | Paper | Pale blueprint paper: a radial gradient from `#f5f9fc` to `#eaf1f8` to `#dce7f2`. The print variant uses flat white | | Frame | Double frame. The outer line is 1.2 mm navy, 10 mm from the edge. The inner line is 0.45 mm, 2.5 mm inside the outer line | | Grid zones | **1–12** across the top and bottom, **A–H** down both sides. Mono bold 3.5 mm. Short ticks at the zone boundaries. The 12 × 8 grid is the same on every size | | Paper grid | Fine dashed lines (`#c8daec`, 0.2 mm, dash 0.7 / 2.1). The pitch is the smallest 1-2-5 metre value p for which p × 1000/N ≥ 30 mm. The lines pass through the view A datum (FS 0 and the ground line). On the E-2D sheet: 5 m = 50 mm | | Header | Top left: `DWG NO: ` (mono 3.5 bold), then `SCALE: 1:N · UNITS: METRES · `. Centre: title (sans 7 bold), subtitle (sans 3.5 bold), and a double rule under them. Top right: `AUTHORITY: `, then `SHEET nn OF NN` | | View panels | Each view, section, detail and pictorial sits in its own panel. A panel has a pale fill, a 0.6 mm navy border and an 8 mm **navy header bar**. The bar has the panel name on the left (white, sans 3.5 bold) and a short note on the right (sans 2.5) | | Bottom band | 21% of the sheet height, below a 0.75 mm rule. It has four panels, each with a navy header bar: **Bill of Materials & Part Schedule** · **Calculations & Verified Data** · **Legend · Hatch Key · Scale** · **Title block** | | Human figure | A **1.80 m** STD-1 figure stands on the ground line in every elevation, at sheet scale. It has a head, torso, legs, a height bar and a "HUMAN 1.80 m" label box | | Fonts | Sans: Arial or Helvetica, for titles, part names and labels. Mono: Courier New, for drawing numbers, values, keys and dimensions | STD-1 uses equal 10 mm margins, which carry the zone labels. ASME Y14.1 margin numbers were not read. NAVFAC uses its own border. This standard keeps the STD-1 margins. Cite OD-B4. ### 3.2 Scale: a formula, not a choice Scales stay on the JSLC **1-2-5 × 10ⁿ** ladder. The ASME scale table was not read. Do not cite ISO 5455. `1:1 · 1:2 · 1:5 · 1:10 · 1:20 · 1:50 · 1:100 · 1:200 · 1:500 · 1:1000 · 1:2000 · 1:5000 · 1:10000` One metre of model is **1000 / N** mm of paper. The selection algorithm: 1. Project the model in its sheet pose. This gives the view extents in metres. For the GA sheet: profile length L, profile height H, forward-view width W, plan depth D. 2. Calculate the size of each panel at scale N, with k = 1000/N. A panel is its header bar, plus the view, plus an allowance on each side: - 6 mm on a side with no dimensions. - 16 + 8(n − 1) mm on a side with n tiers of dimensions. - Extra reserves: 28 mm for the human figure, 58 mm for level marks on panel C, and 16 mm for the callout row on panel A. 3. Place the panels in third-angle order (§5). Panel C is to the left of panel A in the same row. Panel B is above panel A in the same column. 4. The drawing area is the inner frame, minus the header and the bottom band. 5. Do this for each sheet size the class allows (§5.8), smallest first. For each sheet, find the smallest N that fits. 6. Take the smallest N found on any allowed sheet. Use the smallest sheet that reaches it. Adjacent scales differ by ×2.5 or less. So the drawing always fills at least **40%** of the limiting sheet dimension. Views on a sheet share the sheet scale. A view at another scale (a section, a detail) shows its own scale in its panel header. ### 3.3 Pixels per metre: one rule The current canon has no px/m rule. The castdb facility blueprints (`dimension-audit.json`, 38 sheets) use 26 different `px_per_m` values, from 0.2 to 200. Some scale labels are not standard: 1:31.25, 1:33.3, 1:166, 1:181.8, 1:3333. From now on: - **SVG:** user units per metre = **1000 / N**. One SVG unit is one millimetre. - **Raster:** px per metre = **(DPI / 25.4) × (1000 / N)**. Cite FILL-RESOLUTION. | Copy | File | DPI | Notes | |---|---|---|---| | Master | SVG and PDF | vector | 1 SVG unit = 1 mm. Sheet size on the file. | | Archive | TIFF, ITU-T T.6 Group 4 | 200 | Bi-level. JSLC adoption of MIL-PRF-28002C §3.1.4. Inactive for new design (Notice 1, 22 Apr 2010). Not a current new-design mandate. This TIFF does not satisfy the NAVFAC plotted-PDF rule. | | Modelling backdrop | PNG, lossless, pHYs set | 300 | Colour STD-1. px/m uses this DPI and the sheet scale. | | Screen preview | PNG, lossless, pHYs set | 150 | JSLC preview. Not a 28002 class. | | Thumbnail | PNG | long edge 512 px | Not a scale master. | | Copy | px/m at 1:100 | px/m at 1:200 | px/m at 1:1000 | |---|---|---|---| | 150 ppi preview | 59.055 | 29.528 | 5.906 | | 200 ppi archive | 78.740 | 39.370 | 7.874 | | 300 ppi model PNG | 118.110 | 59.055 | 11.811 | Landscape pixel sizes use `round(mm / 25.4 × DPI)`. The full table is in the JSON at `scale.resolution.pixelsLandscape` and in DECISIONS-FILLED.md under FILL-RESOLUTION. A1 at 200 ppi is 6622 × 4677. A1 at 300 ppi is 9933 × 7016. A1 at 150 ppi is 4967 × 3508. ANSI D at 200 ppi is 6800 × 4400, which matches MIL-PRF-28002C Table I size D. Metric 28002 table counts differ by a few pels. The sidecar records both. Every raster carries scale, DPI, and px/m in the title block and in a sidecar JSON. PNG pHYs pixels per metre equal DPI / 0.0254. A detail sheet uses its own scale. The 200 ppi and 300 ppi densities are a JSLC adoption of an inactive specification. They are not a current new-design mandate. FC 1-300-09N §12-3.3.4.2 requires a monochrome plotted PDF or hardcopy unless a color plot is requested. The owner requested the STD-1 colour look, so the SVG and the PDF stay in colour. This standard does not define a monochrome PDF. The Group 4 TIFF does not satisfy that plotted-PDF rule. ## 4. Projection method **Third-angle projection (ASME Y14.3-2012 (R2024)). Put the symbol in every title block.** Cite OD-B1. The Y14.3 body paragraph was not read. The historical DoD rule is the witness: DOD-STD-100D tells a decimal-inch drawing to state the angle when it is not third-angle. MIL-STD-100G offers third-angle or first-angle under ASME Y14.3M. This standard selects third-angle. ### 4.1 Placement rule A view sits on the same side of the principal view as the direction from which it is seen. - Plan (from above) sits above the principal view. - The view from below sits under the principal view. - The forward end view sits on the forward side of the principal view. - The aft end view sits on the aft side. - The opposite profile sits outboard of the aft end view. Ship lines-plan order is not taken from a NAVSEA page. No such page was found. A handbook calls one lines figure first-angle. Ship sheets are deferred. Facility sheets use the rule above. ## 5. Views ### 5.1 Catalogue View A is the principal view. For a facility, A is the elevation of the operational face. Forward points to the **left** of the sheet, and +Y is up. Ships are OD-B3 and are deferred: a later ship sheet uses bow-right. Cite FILL-VIEWS. | Letter | Code | Name | Viewer at | Third-angle position | |---|---|---|---|---| | A | PP | Principal elevation | −X | principal | | B | PL | Plan | +Y (above) | above A | | C | FW | From forward | −Z | left of A | | D | AF | From aft | +Z | right of A | | E | BL | From below | −Y | below A | | F | SP | Opposite elevation | +X | right of D | ### 5.2 Why these positions In third-angle, a view goes on the **same** side as the side it is looked at from. Looking from forward (the left of A), view C goes to the left of A. Looking from above, view B goes above A. The generator calculates placement from the vectors in §5.3, and check V6 confirms it. ### 5.3 View matrices Three unit vectors define a view: - **v** points from the object to the viewer; - **r** points to the right of the screen; - **u** points up the screen; - with **r × u = v**. A point p projects to (s, t) = (p·r, p·u), in metres. On paper: - x_mm = x₀ + (s − s_min) × 1000/N - y_mm = y₀ + (t_max − t) × 1000/N | View | v | r | u | What you see | |---|---|---|---|---| | A | (−1, 0, 0) | (0, 0, 1) | (0, 1, 0) | nose left, top up | | B | (0, 1, 0) | (0, 0, 1) | (1, 0, 0) | nose left, starboard toward A | | C | (0, 0, −1) | (−1, 0, 0) | (0, 1, 0) | port on the right | | D | (0, 0, 1) | (1, 0, 0) | (0, 1, 0) | starboard on the right | | E | (0, −1, 0) | (0, 0, 1) | (−1, 0, 0) | nose left, starboard toward A | | F | (1, 0, 0) | (0, 0, −1) | (0, 1, 0) | nose right | The example script checks r × u = v when it runs. ### 5.4 Pictorial views | Code | Name | Direction v | |---|---|---| | ISO-FPA | Isometric from forward-port-above | (−1, 1, −1)/√3 | | ISO-ASA | Isometric from aft-starboard-above | (1, 1, 1)/√3 | - Both are isometric pictorials under ASME Y14.3. Each axis is shortened by √(2/3) = 0.8165. The Y14.3 pictorial paragraph was not read. The factor is the geometric isometric scale. Codes ISO-FPA and ISO-ASA keep their names as generator tokens. - Pictorial views have no dimensions. The panel bar says "PICTORIAL · NOT TO BE SCALED". ### 5.5 Sections A section is the model cut by a plane. Cut faces are **hatched by material** (§7.3). The cutting plane on the parent view is shown as follows: - a 04.1 line, with 04.2 ends; - arrows; - an STD-1 section marker (a circle with the letter). The section panel bar names the station and the scale, for example "FS 3.58 m · SCALE 1:50". | Code | Plane | Viewed as | |---|---|---| | SEC-L | X = 0 | A | | SEC-T@station | Z = z | C | | SEC-H@level | Y = y (deck and floor plans) | B | | SEC-W@fraction | X = fraction × semispan | A | Floor plans of structures cut 1.50 m above the floor. No Navy figure for this height was found. The height is a JSLC rule. Cite PROPOSED-FLOOR-CUT. ### 5.6 Details A detail callout enlarges a circled region of a parent view. The scale is the next ladder value that fills the detail panel. Find numbers stay the same as on the parent view. ### 5.6.1 Detail sheets Every facility part that is modelled or generated on its own gets a detail treatment. Cite FILL-DETAIL and ASME Y14.24-2020 (R2026). The monodetail paragraph in Y14.24 was not read. MIL-STD-100D 201.2.1 says a monodetail drawing delineates one part. 201.2.2 says each part on a multidetail drawing still meets the detail requirements. That DoD text is superseded. It is the public definition of the words. - A part with its own pivot, joint limit, or swept clearance gets a monodetail drawing. - A large separate part (gate leaf, door, tunnel segment, pen gate) gets a monodetail drawing even when it does not move. - Small fixed parts may share one multidetail sheet. Each part on it still has its own views, its own scale, local-frame dimensions, node name, and find number. A moving part does not share a sheet. - If the parts do not fit at those scales on one Y14.1 sheet, split them. - Each detail sheet shows views at its own scale, the pivot and axis when it moves, joint limits, swept clearance, the node name, local-frame dimensions, and the find number from the arrangement parts list. - Monodetail number: the parent number plus `-D` and a three-digit find number. Example: `JCOM-NSD-GHQ-001-D003`. - Multidetail number: the parent number. No new root. Sheet count is `SHEET nn OF NN` inside that number. - The CAGE stand-in and the revision letter are not part of the number. ### 5.7 States (articulation) A state view is a normal view of a named pose. The reference configuration shows under it in phantom (05.1). ASME Y14.2 phantom lines show alternate and extreme positions. The Y14.2 dash table was not read. The current phantom geometry stays. Poses come from the GLB's own clips, using the clip mapping in the unit standard (for the E-2D, `OWNER-SPEC.txt` §7). | Class | Reference configuration | |---|---| | Aircraft | wings spread, gear down, controls neutral, rotors at reference azimuth, hook up | | Land vehicle | turret 0°, gun 0°, wheels straight | | Ship | masts up, at DWL | | Structure | doors closed | | Infantry | standing | ### 5.8 Views per class: exact counts O = orthographic, P = pictorial, S = section, D = detail, ST = state view. | Class (game classes) | O | P | S | D | ST | **Total** | Sheets | Sizes | |---|---|---|---|---|---|---|---|---| | Land vehicle, turreted (tank, ifv) | 6 | 2 | 2 (SEC-L, SEC-T turret ring) | 2 | 2 (traverse B, elevation A) | **14** | 4 | A2, A1 | | Land vehicle, no turret (wheeled, truck) | 6 | 2 | 2 | 2 | 1 (steering B) | **13** | 4 | A2, A1 | | Artillery | 6 | 2 | 2 | 2 | 4 | **16** | 5 | A2, A1 | | Rotary-wing (helicopter) | 6 | 2 | 2 (SEC-L, SEC-T main mast) | 2 | 0; +2 if blades fold, +1 if ramp | **12** (to 15) | 3–4 | A2, A1 | | Fixed-wing (jet) | 6 | 2 | 3 (SEC-L, SEC-T max fuselage, SEC-W 40%) | 2 | 4 (gear up A, C, E; controls B) | **17** | 5 | A2, A1 | | Carrier aircraft | 6 | 2 | 3 | 2 | 9 (jet's 4, folded A, B, C, ISO-FPA, hook A) | **22** | 6 | A2, A1 | | Ship (surface) | 6 | 1 | lines plan 3 + SEC-L + 2 SEC-T + n deck plans | 2 | 0 | **15 + n** (min 16) | 5+ | A1, A0 | | Submarine | 6 | 1 | lines plan 3 + SEC-L + 2 SEC-T | 2 | 1 (masts A) | **16** | 5 | A1, A0 | | Structure (building, defence) | 5 (no E) | 1 | SEC-L, SEC-T + n floor plans | 1 | 1 (door A; defence +1) | **10 + n** (min 11; defence 12) | 3–4 | A2, A1, A0 | | Infantry | 5 (no E) | 1 | 0 | 1 (weapon) | 2 (aim A, prone A) | **9** | 2 | A3, A2 | **Why these counts:** - **Six orthographic views for vehicles, aircraft and ships.** The game shows units from every side. View E (from below) shows gear wells, bays, tracks and hull bottoms. Structures and infantry have no useful underside, so they drop view E. A structure gets a floor plan in its place. - **Two isometrics** together show every face once. Ships and structures need only one. - **Sections only where the inside matters in play.** - **States only where the outline changes in play:** gear up, wing fold, deployment, turret limits. **Sheet sets:** | Sheet | Contents | |---|---| | 01 GA | A, B, C, ISO-FPA, a section, principal dimensions, callouts | | 02 View sheet | D, E, F, ISO-ASA | | 03 | sections | | 04 | details | | 05+ | states | | Ships | the lines plan and deck plans take sheets 03 onward | ## 6. Title block and bottom band (STD-1) The bottom band has four panels in this order. **1. Bill of Materials & Part Schedule.** Columns: FIND · PART / SUBASSEMBLY · NODE · SIZE L × W × H (m) · QTY · MASS t · PIN. - There is one row per callout. FIND is the circled find number, the same as on the views and on the detail sheet. Cite FILL-PARTS and MIL-STD-100G 405. PIN is "—" until a dossier gives one. The find number is not a procurement number. - PART is plain English, upper case. NODE is the exact contract node name. - SIZE comes from the part's GLB extents (one side). - MATERIAL and MASS come from the dossier. If there is no dossier, write "—" and say so in the panel footer. **2. Calculations & Verified Data.** One line per value: a label (sans 2.5 bold) and a value (mono 2.5). The generator calculates every value. Model and reference figures carry a deviation in %, with WARN or FAIL. FAIL values are red. Typical lines: - principal dimensions against reference figures; - folded and other state sizes; - pivot distances; - the scale-selection result; - mm per metre and px per metre; - model counts; - source SHA-256; - frame. **3. Legend · Hatch Key · Scale.** It holds: - the hatch key (only the materials used in the set, §7.3); - the line-type key; - the paper-grid pitch; - the STD-1 **graphic scale**: four bands, alternately filled and blank, labelled 0 … bar length in metres. **4. Title block.** It follows the STD-1 structure. ASME Y14.100 is the drawing-practice authority. The ISO 7200 field list is withdrawn. | Row | Content | |---|---| | 1 | `JSLC · · ` (sans 3.5 bold); `DWG NO · UNITS: METRES · REV ` (mono 2.5) | | 2 | TITLE; subtitle; `STATUS: ISSUED TO JSLC DRAWING STANDARD STD-1` (or PROPOSED, MOCK, SUPERSEDED) | | 3, left | SCALE (with sheet size and projection) · RULINGS · HUMAN REF 1.80 m ON ELEVATION · STANDARD `JSLC STD-1 · ASME Y14.1/Y14.2/Y14.3` | | 3, right | DRAWN (agent code) · CHECKED · third-angle symbol · `CAGE EQUIV: JSLC0` | | 4, left | "SUPERSEDED CONTENT IS MARKED ON SHEET IN RED"; SOURCE model; SHA-256; FRAME (yaw, axes); `JCOM / TECHNICAL REVIEW / CONTROLLED COPY`; distribution `JSLC-DIST-INT` | | 4, right | **SHEET NO.** with the number in sans 14 bold; "OF NN" | Field values: - **Identification number:** `JCOM---`, with ASCII hyphens. Example shape `JCOM-NSD-E2D-001`. Facility example `JCOM-NSD-GHQ-001`. Cite FILL-DWGNO. This is the one JSLC grammar. NAVFAC job numbers are not adopted. - **CAGE equivalent:** `JSLC0`. Five characters, fictional, not a DLA-registered code. It is not part of the drawing number. Cite FILL-CAGE and MIL-STD-100G 3.11 and 406.5. - **Distribution:** `JSLC-DIST-INT` by default. Also `JSLC-DIST-OWNER` and `JSLC-DIST-PUB`. These are fictional labels. Do not print a DoDI 5230.24 statement. Cite FILL-DIST. - **Revision letters:** A onward. Do not use I, O, Q, S, X or Z. That skip list is the v0.2.0 rule. The ASME Y14.35-2025 letter list was not read. - **Approval:** only the owner moves a sheet to ISSUED. The full field list, with patterns, is in the JSON at `titleBlock.fields`. **Revisions.** The revision letter stays on title-block row 1. A revision block sits inside the title-block panel. Columns: REV, DESCRIPTION, DATE, BY. The four band panels stay. The calculations panel does not carry the history. The full history stays in the generator sheet-history file. Cite PROPOSED-REV-PLACEMENT and ASME Y14.35-2025. The 2025 body text was not read. ## 7. Lines, colour and hatching ### 7.1 Line types (ASME Y14.2-2014 (R2026)) and STD-1 weights Y14.2 is the line-class authority. Its dash table was not read. Widths and dash lengths below stay the STD-1 geometry. Tokens 01.1, 01.2, 02.1, 04.1, 04.2 and 05.1 are generator names for visible-thin, visible-thick, hidden, center, cutting-plane ends, and phantom. Cite FILL-LINES. | Line group | Thick | Thin | Sheets | |---|---|---|---| | 0.5 | 0.50 mm | 0.25 mm | A3, A2 | | 0.7 | 0.70 mm | 0.35 mm | A1, A0 | | Type | Form | Used for | |---|---|---| | 01.2 | continuous, thick | visible silhouette and crease edges; section outlines | | 01.1 | continuous, thin | dimension and extension lines, leaders, hatching, GL and DWL | | 02.1 | dashed, thin (12d dash, 3d gap) | hidden edges, only where §8.3 allows | | 04.1 | long-dash dot, thin | centre lines, symmetry, cutting planes | | 04.2 | long-dash dot, thick | cutting-plane ends | | 05.1 | long-dash double-dot, thin | phantom: alternate positions, folded envelopes, reference pose | In the table, d is the line width. Dash lengths are in millimetres on paper. ### 7.2 Colour: STD-1 is the default All values below come from `gen_campcentury_std.py`. Contrast is measured on the darkest paper stop, `#dce7f2`. | Role | Hex | Contrast | |---|---|---| | Ink: outlines, frame, text | `#0b3c6d` | 8.91:1 | | Titles, dimensions | `#0d4175` | 8.25:1 | | Secondary text, zone labels, centre lines | `#1b5994` | 5.77:1 | | Data values | `#144d82` | 6.94:1 | | Header bars | `#0b3c6d`, with text `#f4f8fb` (10.45:1) and notes `#cfe2f2` (8.41:1) | | | Panel fill | `#eef4fa` (views), `#e6eef7` (band) | | | Table head; table rows | `#d3e2f2`; `#edf4fb` / `#e2ecf7` | | | Table rules | `#6b9fd1`, `#adc8e3` | | | Label boxes | `#ecf2f8` | | | Surface tint inside outlines | `#dbe7f3` (JSLC look; PROPOSED-SURFACE-TINT) | | | Paper grid | `#c8daec` | | | Superseded and FAIL only | `#c81e1e` (4.58:1), on `#fdeaea` | | | Variant | Use | |---|---| | **std1** (default) | Gradient paper as above. Screen, gallery, canon | | **print** | The same as std1 with flat white paper. For PDF and paper | | **graphite** | Light lines on the dark site background (`#0b1324`, from `shell-tokens.css`). Only for embedding in the site; never canon | **Dropped:** the v0.1.0 solid cobalt field (`#002eac`). The owner judged it "not good looking" (17:12). **Also superseded:** | Colour | Where it was used | |---|---| | `#0B3D91` | `skills/blueprinter` field | | `#FFE082` | `skills/blueprinter` callouts (yellow) | | `#8a5a00` | pre-STD-1 castdb dimensions (amber) | ### 7.3 Hatch patterns by material A hatch means a **cut face**: sections, deck plans, cut-away details. Elevations, plans and pictorials use the flat surface tint, not a hatch. ASME Y14.2 is the line authority. Its hatch figure was not read. The STD-1 patterns for concrete, steel, ice, rock, and water are the facility policy. The five unit patterns stay PROPOSED and deferred. This fill does not approve them. Cite OD-B8. The pattern geometry is in the JSON at `hatches.patterns`. | Material | Pattern | Source | |---|---|---| | Steel (gear, hook, fittings) | 45°, one heavy and one light line, ground `#c7daec` | STD-1 | | Reinforced concrete | diagonal lines plus aggregate dots, ground `#d6e4f2` | STD-1 | | Glacial firn and ice | −25°, solid and dashed lines | STD-1 | | Bedrock | 35°, heavy lines with dashed cross lines | STD-1 | | Seawater | short offset horizontal strokes | STD-1 | | Airframe skin and structure | 45°, single light lines, ground `#dbe7f3` | PROPOSED | | Armour plate | 45°, close double heavy lines | PROPOSED | | Composite and radome | −45°, solid and dashed lines | PROPOSED | | Transparent (canopy, windows) | short paired strokes on a very pale ground | PROPOSED | | Rubber (tyres, seals) | crossed lines | PROPOSED | The materials for each class are in the JSON at `hatches.byClass`. The material of each part comes from the dossier. Until a dossier says otherwise, a mock sheet uses "airframe" for a generic airframe cut. ## 8. Lettering, dimensions and marks ### 8.1 Lettering - **Fonts:** sans (Arial or Helvetica) and mono (Courier New), as in STD-1. All lettering is upper case, except node names, file names and hashes. - **Heights** use the STD-1 series, with the 1.8 mm step withdrawn. NAVFAC FC 1-300-09N §12-3.3.4.3 requires 2.4 mm minimum and prefers 3 mm for dimensions, notes, and callouts. View titles are at least 5 mm. Arial is the NAVFAC font at or below 6 mm. This standard uses Arial or Helvetica for words. Courier New stays for numbers. That mono column is a recorded look exception. Body text uses a **2.5 mm** floor, which clears the 2.4 mm rule. Cite FILL-LETTERING. The Y14.2 height table was not read. | Item | A0–A1 | A2–A4 | |---|---|---| | Title | 7 | 5 | | Subtitle | 3.5 | 2.5 | | DWG NO | 3.5 | 3.5 | | Panel and band bars | 3.5 | 2.5 | | Dimensions | 3.5 | 2.5 | | Callouts | 3.5 | 2.5 | | Table text | 3 | 2.5 | | Data and small text | 2.5 | 2.5 | | Zone labels | 3.5 | 2.5 | | Sheet number | 14 | 10 | - **Body minimum:** 2.5 mm. The sheet title is 7 mm on A0–A1 and 5 mm on A2–A4, so the sheet title meets the 5 mm Navy view-title floor. The panel-view header stays 3.5 mm on A0–A1 and 2.5 mm on A2–A4, inside the 8 mm navy bar. Both are below 5 mm. That is a recorded owner-look exception. It does not meet §12-3.3.4.3 for view titles. ### 8.2 Dimensions **Units:** metres, two decimals, decimal point. For example `18.16 m`. **STD-1 dimension style:** - A thin dimension line with closed filled arrowheads, 3 mm long and 1.2 mm half-width. - Extension lines at 75% opacity. They start 1 mm off the outline and run 2 mm beyond the dimension line. - The value sits in a **boxed label centred on the line**: mono 3.5 bold, box fill `#ecf2f8`, 5.4 mm high. - The first tier is 10 mm out from the outline. Each further tier is 8 mm beyond the one before. ASME Y14.5-2018 (R2024) is the dimensioning authority. Its text-placement paragraph was not read. The publisher says architectural practice is outside Y14.5. These sheets still show linear sizes. STD-1 puts the value in a box on the line. This standard keeps that box, for the look. Do not use geometric tolerances. Cite FILL-DIMS. **Reference figures:** - A real-world figure follows in parentheses, for example `18.16 m (17.60)`. - Add `WARN` above 2% deviation and `FAIL` above 5% (unit standard). **Required dimensions per class:** | Class | Required dimensions | |---|---| | All | length, width, height | | Aircraft | span; wheelbase and track measured between gear pivots; folded width and height (carrier); rotor diameter | | Land vehicles | track width, wheelbase, turret-ring station, gun-axis height | | Ships | LOA, beam, draft, air draft, flight deck | | Structures | footprint, height, door opening | | Infantry | height | **Traceability:** each dimension element carries `data-dim-m`, `data-span-mm` and `data-source`. **Tolerances:** do not use geometric tolerances. These are not manufacturing drawings. The model-against-real deviation is the one tolerance that matters. ### 8.3 Edges and hidden lines - **Visible edges:** - silhouette edges; - boundary edges; - crease edges at **18°** or more (the lab threshold, `LAB` §4). - **Weld vertices** within 1×10⁻⁵ m before you find creases. - **Hidden edges are off** on GA and view sheets. - **Hidden edges are on** for state sheets (gear in its wells), details, and listed sections. - **Merge** any segment shorter than 0.1 mm on paper into its neighbour. ### 8.4 Callouts, pivots and part names - **Callout:** a filled dot on the part, a thin leader, and a circle of radius 3 mm with the number. This is the STD-1 balloon. The site's no-circled-glyph rule applies to site chrome, not to drawings. - **Placement:** on panel A, callouts sit in a 16 mm row above the view. - **Numbering:** the number is the BOM TAG. Numbers run in node order, front to back. - **Joint limits:** drawn in phantom on the state sheet, with angular dimensions to 0.1°. ### 8.5 Datums and marks | Mark | Rule | |---|---| | Ground line `GL` | Y = 0, thin, on A, C, D, F | | Ships: `DWL` and `BL` | DWL and BL; frame stations every 10 frames | | Centreline `℄` | 04.1 on B, C, D, E | | Forward arrow | `FWD (−Z)` on A and B | | Level marks (STD-1) | On an elevation: a tick and slash, then `+h.hh m · NAME` in mono 2.5. Example: `+6.31 m · ROTODOME TOP` | | Human figure | 1.80 m, on GL, in every elevation | | Section marker | Circle with the letter at the cutting plane | | Centroid | Quartered circle. Labelled `MASS CG (DOSSIER)` only from a dossier; otherwise `GEOMETRIC CENTROID — UNIFORM DENSITY` | | Stations | Aircraft: FS = metres aft of the nose tip, BL = X, WL = Y | | Axis triad | Optional on STD-1 GA sheets. Required on view sheet 02 | ### 8.6 Port and starboard come from geometry The sign of X in the canonical frame decides port or starboard (+X = starboard). Names do not decide it. The audit lists each node whose name disagrees with its position. ## 9. Mathematical generation (summary) GENERATOR-SPEC.md has the full detail. In outline: 1. Hash the GLB. 2. Evaluate the pose with glTF semantics, then apply the canonical turn. 3. Measure extents from transformed vertices, and read pivots. 4. Project with the §5.3 matrices. 5. Find edges by type. 6. Remove hidden lines exactly, in object space. 7. Cut sections with a plane and chain the loops. 8. Choose the scale by §3.2 and place the panels. 9. Place dimensions, the human figure, level marks and callouts. 10. Fill the BOM, calculations and legend from the data. 11. Write the SVG and the audit JSON. Two read-only scripts in this folder reproduce the maths: - `tools/measure-glb.mjs` measures exact extents for each pose. - `example/make-e2d-std1.mjs` builds the STD-1 sheet. ## 10. Validation: what a checker verifies | # | Check | Pass rule | |---|---|---| | V1 | Source hash | sha256 of the GLB = title block = SVG metadata | | V2 | Scale bar | drawn length − (metres × 1000/N) ≤ 0.01 mm | | V3 | Dimensions | each `data-dim-m` is within 0.001 m of the GLB; drawn span = value × 1000/N, within 0.01 mm | | V4 | Reference deviation | WARN or FAIL is present exactly above 2% or 5% | | V5 | Required views | every view for the class is present | | V6 | Third-angle alignment | B is above A; C is left of A; panels A and C share the ground line; panels A and B share the long axis, within 0.01 mm; r × u = v | | V7 | Title block | every required field is filled and valid; projection symbol present | | V8 | Lines | only the Y14.2 classes named in §7.1; widths from the line group | | V9 | Colour | only std1 hexes (or the variant's); text ≥ 4.5:1 and lines ≥ 3:1 on `#dce7f2`; red only with a word | | V10 | Lettering | heights from the series, ≥ 2.5 mm; no text overlaps | | V11 | Hidden-line cross-check | IoU ≥ 0.98 against an independent Blender Freestyle render | | V12 | Bounding-box cross-check | equal to `tools/measure-glb.mjs` within 0.0001 m | | V13 | Side labels | port and starboard follow geometry | | V14 | Determinism | two runs give byte-identical SVG and JSON | | V15 | STD-1 look | header layout; zones 12 × 8; four band panels in order; every view in a barred panel; a 1.80 m human on every elevation | ## 11. Worked example: E-2D Hawkeye, sheet 01 (STD-1) This example was drawn under v0.2.0. This fill does not redraw it. The `example/` folder is not in this lane snapshot. Treat the sheet as a historical record. Aircraft sheets are deferred. **Files in [`example/`](example/):** | File | What it is | |---|---| | `e2d-sheet01-ga.std1.svg` | the sheet | | `e2d-sheet01-ga.std1.png` | 96 DPI preview | | `e2d-sheet01-ga.std1.audit.json` | the audit | | `e2d-measure.json` | extents and pivots per pose | | `make-e2d-std1.mjs` | the script that makes the sheet | **Source:** `e2d-hawkeye-fully-articulated.glb`, SHA-256 `e6d07e15…52b1ad` (matches `LAB` §1). It has 65 meshes, 105,359 triangles and 9 clips. The model nose is at +Z, so `canonYawDeg = 180`. **Measured** (wings spread, gear down): | Quantity | Model | Real | Deviation | |---|---|---|---| | Length | 18.163 m | 17.596 m (NAVAIR) | +3.23% **WARN** | | Span | 24.570 m | 24.562 m (NAVAIR) | +0.03% | | Height | 6.314 m | 5.58 m (E-2C figure, unverified for E-2D) | +13.16% **FAIL** | | Folded width | 10.424 m | not found | — | | Lowest point, gear up | Y = 0.883 m | — | — | | Wheelbase / track (pivots) | 7.360 / 6.621 m | — | — | | Section X–X at FS 3.58 m | 2.12 m wide × 2.16 m deep | — | — | **Scale selection** (STD-1 layout): | Sheet | Scale | Panels need | Area available | Result | |---|---|---|---|---| | A2 | 1:100 | 544 × 385.5 mm | 562 × 279 mm | does not fit | | A2 | 1:200 | 330 × 231 mm | 562 × 279 mm | fits | | A1 | 1:100 | 544 × 385.5 mm | 809 × 416 mm | fits | Result: **A1 at 1:100**. That gives 10 mm per metre and 59.055 px/m at 150 DPI. The paper grid is 5 m (50 mm). All 10 drawn lengths re-check to 0.000 m. **Sheet 01 layout:** | Panel | Contents | |---|---| | A — port profile | callouts 1–6, wheelbase, length, height, human | | C — from forward | span, track, level marks, human | | B — plan | spread and folded widths, folded envelope in phantom | | D — isometric | | | Section X–X | 1:50, airframe hatch | | Bottom band | the BOM with 7 rows, the calculations, the legend and the title block | **Mock limits:** - The outlines are raster silhouettes at 0.025 m. They include holes, such as the gap between the gear legs. The generator will draw exact silhouette and crease edges with hidden-line removal. - The section hatch fills the envelope of the exact plane cut. **What the example shows:** 1. The model is 13% taller than the E-2C figure and 3.2% long. 2. The model's side names are mirrored. 3. The lab spec's axis note is left-handed. ## 12. Existing material | Material | Verdict | |---|---| | castdb STD-1 sheets and generator (`gen_campcentury_std.py`, `gen_node_neptune_portal.py`) | **Adopt the look.** This standard makes STD-1 the default and adds the maths: the scale ladder, the px/m rule, projection matrices, views per class, and the checker | | castdb pre-STD-1 sheets (`*.pre-std1`) and the 26 different px/m values | Superseded. Redraw them to this standard over time | | `skills/blueprinter` (ship content, `slice_glb.py`, `gate.mjs`) | **Adapt.** Keep the naval content and the slicer. Replace its cobalt/yellow look with STD-1 | | Unit standard | Upstream source for axes, dimensions, nodes and clips | ## 13. Open decisions for the owner | # | Decision | Recommended | |---|---|---| | OD-B1 | First-angle or third-angle | Third-angle. Symbol in every title block. | | OD-B2 | Grow to A1, or lock every sheet to A2 | Metric A1 default. A0 and A2 remain available to the scale rule. | | OD-B3 | Nose-left for every class, or bow-right for ships | Facilities: operational face, forward left. Ships, if later drawn: bow-right. Ship sheets are deferred. | | OD-B4 | Equal 10 mm margins or a wide filing margin | STD-1 10 mm margins. | | OD-B5 | Hidden lines off on GA sheets | Off on arrangement sheets. On for details, states, and listed sections. | | OD-B6 | Lab accent `#bda77b` | Lab only. Never on a sheet. | | OD-B7 | E-2D model disagrees with the cited figures | Record WARN or FAIL on the sheet. Aircraft sheets are deferred. | | OD-B8 | Unit hatches | Facility sheets use STD-1 hatches. The five unit patterns stay PROPOSED and deferred. | | OD-B9 | Comparison sheets | Deferred. Do not generate them for Flight 1. | ## 14. Sources Normative sources are the Navy and US military documents and the ASME Y14 revisions named in §0 and §4. Paywalled ASME body text is unverified. ISO is not an authority. The citation table for this fill is MILSTD-SOURCES.md in the milstd lane. Do not copy that path into a sheet. - **STD-1:** - `M:\z4yin-work-overflow\hermes-share\codex-lanes\castdb\canon\facilities\tools\gen_campcentury_std.py` - `…\blueprints\camp-century\05-tidewater-caisson-blast-gate-detail.svg` - `…\blueprints\node-neptune-carrier-portal\04-door-leaf-front-elevation.png` - `…\blueprints\INDEX.md` - `…\blueprints\dimension-audit.json` - **NAVAIR E-2D:** https://www.navair.navy.mil/product/E-2D (57 ft 8.75 in; 80 ft 7 in). Read through search results, because a direct fetch was reset. - **aerospaceweb.org E-2:** https://aerospaceweb.org/aircraft/recon/e2 (E-2C height 18.31 ft). - **Estate:** - `cic/topology/shell-tokens.css` - `skills/blueprinter/SKILL.md` - `../standards_units/` - `D:\z4yin\.inbox\dolphin4\lanes\wireframe-lab\input\OWNER-SPEC.txt` - `D:\z4yin\.inbox\dolphin4\lanes\design-repo\TOOLS-SV3-SV4.md` ================================================================================ FILE: SERVICE SKILLS: standards/svc_skills/README.md ================================================================================ # jcom_v0bb_simulation_svc_skills Skills, approach and tool knowledge for JCOM and Conquer (repo prefix `jcom_v0bb_simulation`). Owner, 2026-10-20: "give me a PREFIX_svc_skills and make sure we are saving all relevant skills/info in there on approach. our design reference should point to it." / "it should absolutely include photoshop/3d modeling / wireframe skills if they exist". The design reference is `jcom_v0bb_simulation_design`. Compiled 2026-10-20 by a dolphin4 subagent. First-party content is copied with its folder layout; third-party content is pointer-only (EXTERNAL.md). Copies are snapshots; the source path is the live version. - **APPROACH.md**: how we work (lanes, briefs, review, integrate-then-test, clean room), with sources. - **TOOLS.md**: software installed on SV3/SV4 (Blender, Photoshop, Illustrator, Substance suite, Cinema 4D, Adobe video) with the pipeline role, scripting hook and existing scripts for each. - **EXTERNAL.md**: third-party skills and tools with verdicts, including Photoshop, wireframe and TechDraw candidates. - `_sources/`: dolphin4 inputs (`TOOLS-SV3-SV4.md`, `WHAT-WORKS.md`). - Blueprint standard: owned by a separate design agent at `jcom_v0bb_simulation_design/standards_blueprints/` (and its SKILLS.md). Geometry and look-dev standards drafts sit beside it (`design_standards_geometry`, `design_standards_lookdev` in the lane folder). This repo only indexes the blueprint skills. ## Index Applies-to keys: U = units/models, M = maps/terrain, L = game logic, D = design/HUD/UI, B = blueprints/technical drawing, P = process. | Skill | Purpose | Source path (live) | Applies to | |---|---|---|---| | game-logic-rules | Brief block and review checklist for every game-logic or HUD lane | `D:\z4yin\z4yin-files\.claude\skills\game-logic-rules` | L, D | | unit-model-contract | Game GLB contract: -Z front, sizes, budgets, named pivoted parts, houseTint, texture/UV gates, LODs (compiled from Opus addenda) | `M:\z4yin-work-overflow\wireframe-upgrade\` | U | | blender-model-builder | Spec-driven articulated Blender build, multi-clip GLB export, review renders; LESSONS and per-model advice | `D:\z4yin\z4yin-files\skills\blender-model-builder` | U | | vessel-builder | Sourced vessel workflow: look, moves, structure, plating, mechanisms, gates | `D:\z4yin\z4yin-files\skills\vessel-builder` | U, B | | naval-expert | Evidence-gated naval/military dimensions and kinematics dossiers; per-asset reference packs | `D:\z4yin\z4yin-files\skills\naval-expert` | U, B | | cnc-blender-parts | Headless Blender 4.2 playbook with 7 tested bpy snippets: weld, split parts, seam-safe cuts, origin to pivot, retopo, scale/facing, glTF export | `M:\cnc-work\lane-j3-finale\.claude\skills\cnc-blender-parts` (built for the v3 repo) | U | | blueprinter | Naval engineering blueprint SVG standard: cyanotype field, line weights, dimension and hidden lines, title block, GLB planar slicing, CDP gate | `D:\z4yin\z4yin-files\skills\blueprinter` (+ `M:\z4yin-work-overflow\blueprints` approach) | B, U | | blueprint-reference-sheets | bpgen multi-angle reference packs and ortho QA sheets from our GLBs; blueprint-research design | `D:\z4yin-work\blueprint-gen`, `D:\z4yin-work\blueprint-research` | B, U | | wireframe-review | Where wireframe/topology/assembly guidance lives; records that no standalone wireframe skill was finished | `D:\z4yin-work\wireframe-skills` (brief only) + blender advice | U | | adobe-substance-pipeline | Photoshop/Substance headless route (sbsrender), PBR material library and UV audit pilot | `D:\z4yin-work\adobe-sv4`, `D:\z4yin-work\adobe-units-maps` | U, M | | meshy-3d-generation | When generated 3D works and when to model procedurally | `D:\z4yin-work\meshy-research\MERGED-HOWTO.md` | U | | blender-ecosystem-research | 37 external Blender tools ranked; reverse-assembly research | `D:\z4yin\z4yin-files\skills\_vendor\*.md` | U | | model-publish-path | Publishing GLB revisions to the asset manager; R2 latest-2 hosting brief | `D:\z4yin-work\model-publish-path`, `C:\lanes\dolphin4\infra\assets-r2` | U, P | | hud-design-system | In-game HUD tokens/primitives/components/screens; battlefield redesign and cameo style | `M:\z4yin-work-overflow\hermes-share\codex-lanes\hud-design`, `M:\z4yin-work-overflow\game-redesign` | D | | ux-design-grok | Estate screen procedure with tokens.json and ux_check (draft) | `D:\z4yin-work\ux-research\grok\skills\ux-design` | D | | ux-design-agy | Estate UX design skill (alternative draft) | `D:\z4yin-work\ux-research\agy\skills\ux-design` | D | | forever-components | Five animation-technique drafts from the forevercomponents.com library | `D:\z4yin-work\forever-components\skills-draft` | D | | cobalt-design-lead | Pointer to the cobalt design-lead agent definition | `D:\z4yin\z4yin-files\.claude\agents\cobalt.md` | D | | architecture-module-integration | Integrating modules into the shared shell and public subsets | `D:\z4yin\z4yin-files\.claude\skills\architecture-module-integration` | D, P | | cnc-add-variable | v3 project: add a tunable the ADR-010 way (JSON value, docs row, loader read) | `M:\cnc-work\lane-j3-finale\.claude\skills\cnc-add-variable` | L | | cnc-operator-rules | v3 project binding operator rules | `M:\cnc-work\lane-j3-finale\.claude\skills\cnc-operator-rules` | L, P | | cnc-dispatch-lane | v3 project lane brief and dispatch | `M:\cnc-work\lane-j3-finale\.claude\skills\cnc-dispatch-lane` | P | | dispatch-a-lane | Dispatch, register and supervise bounded lanes | `D:\z4yin\z4yin-files\.claude\skills\dispatch-a-lane` | P | | lane-launch | CLI launch mechanics for agy, grok, codex | `D:\z4yin\z4yin-files\.claude\skills\lane-launch` | P | | ops-cli-delegation | Cross-CLI delegation with receipts | `D:\z4yin\z4yin-files\.claude\skills\ops-cli-delegation` | P | | asquads | AGY squad waves | `D:\z4yin\z4yin-files\.claude\skills\asquads` | P | | blind-competition | Blind entries, cross-review, resolution | `D:\z4yin\z4yin-files\.claude\skills\blind-competition` | P | | verify-a-build | Independent verification of a builder lane | `D:\z4yin\z4yin-files\.claude\skills\verify-a-build` | P | | qa-responsive-acceptance | Desktop/phone/tablet acceptance with real interactions | `D:\z4yin\z4yin-files\.claude\skills\qa-responsive-acceptance` | P, D | | commit-with-gates | Gated commits with explicit pathspecs | `D:\z4yin\z4yin-files\.claude\skills\commit-with-gates` | P | | publish | Deploy and publish under owner authorization | `D:\z4yin\z4yin-files\.claude\skills\publish` | P | Not copied (not game-relevant): lyric-reviewer, lyric-writer, pronunciation-specialist, pre-generation-check, suno-engineer, jev-post-only, context-backup-provenance, torch-checkin-checkout, ops-session-lifecycle, audit-delivery-reconciliation; vendor/marketplace skills under `~/.claude/plugins`, `~/.codex`, `~/.gemini`, `~/.grok` (see EXTERNAL.md for the relevant ones). ## Photoshop, 3D modelling, wireframe and blueprint: what exists - **3D modelling: yes.** blender-model-builder, vessel-builder, naval-expert, cnc-blender-parts, unit-model-contract, meshy-3d-generation, plus about 50 bpy lane scripts on SV3/SV4 (TOOLS.md). - **Photoshop: no skill.** Only notes (adobe-substance-pipeline) and a COM probe script on SV4. The texture route that works is Substance Designer `sbsrender`. Best external candidate: mikechambers/adb-mcp (EXTERNAL.md). - **Wireframe: no finished skill.** The 2026-10-10 wireframe-skills lane left only its brief. Guidance is spread across blender advice and LEVERAGE.md (wireframe-review indexes it). - **Blueprint / technical drawing: yes.** blueprinter (dimension lines, hidden/planned dashed lines, orthographic slices) and blueprint-gen. No FreeCAD/TechDraw skill; FreeCAD is not installed on SV3/SV4. ## Searches run (2026-10-20, 16:38-17:00 EDT) - Skill folders: `D:\z4yin\z4yin-files\.claude\skills\*`, `D:\z4yin\z4yin-files\skills\*`, `C:\Users\MaxGlasser\.claude\skills` (synced Anthropic skills), `~/.claude/plugins`, `~/.codex/skills` and plugin cache, `~/.codex/AGENTS.md` dir, `~/.gemini/antigravity`, `~/.gemini/antigravity-cli`, `~/.gemini/config/plugins`, `~/.grok/bundled/skills`, `D:\z4yin-work\*` (SKILL.md depth 5), `M:\` (depth 6: SKILL.md, *skill*.md, *photoshop*, *blender*skill*, *wireframe*skill*), `G:\My Drive` (depth 5: SKILL.md, *skill*, *photoshop*, *wireframe*, *blender*), SV3 and SV4 `C:\lanes` and SV4 `C:\Users\server\substance-work` over read-only ssh (bpy, substance_painter, sbsrender, sbscooker, c4dpy, Photoshop/Illustrator COM, SKILL.md). - Results with no relevant skill: `G:\My Drive` (only GLB asset versions and a Suno chat), `~/.gemini` (agy built-ins, chrome-devtools and data plugins), `~/.codex` (imagegen, docs, data-analytics templates), Claude plugin marketplace (no 3D/Photoshop skill). - Recall (`node tools/recall/ask.mjs`): the worktree has no index ("No index yet. Build it: node tools/recall/build.mjs"). From the main checkout the index is stale (built 2026-10-13 08:22, 7 days old, 14 canon files newer), so a miss may mean not indexed. Queries and top results: - "photoshop skill": owner 2026-10-10 lines on building agy/grok expert skills and skill refinement (session a2b7ada9). No Photoshop skill hit. - "3D modeling wireframe skill": owner 2026-10-09 wireframe/topology comments on the Ford hull and the Sketchfab MCP download (session a2b7ada9). - "blender MCP texture": owner 2026-10-10 Meshy credit lines, "textures missing;", and "read the Blender skill and LESSONS before model briefs". - Web searches for Photoshop MCP, FreeCAD TechDraw MCP, Blender wireframe/Freestyle skills, webgpu skill and web-3d-asset-pipeline: see EXTERNAL.md. ## Secret handling Every copied text file was scanned (private-key blocks, sk-/ghp_/github_pat_/AKIA/xox/AIza keys, Bearer tokens, password/secret/api_key/token assignments, user:pass URLs). No file matched. LAN IPv4 addresses were replaced with `` in 23 copied files. One commit email was redacted in `cobalt-design-lead/references/cobalt.md`. Excluded: `blender-model-builder/advice/cx-models-evidence.json` (over 400 KB, size limit, not a secret hit). Not copied by design: vault, auth.json, oauth, `D:\z4yin\secrets`, lane logs, Thyme and EA source.