Files
Project-M/Docs/Vault/06_Roadmap/Lantern_Art_Pipeline_Recipes.md
T
kronic 73afab6ba5 Docs: LANTERN art-pipeline recipes (Workstream A0/A1) + currency re-mean spec
- Art pipeline recipes: the material foundation (palette atlas + 3 master shaders
  Lit-Palette/Emissive-Gloam/Skinned-Palette), statics recipe, skinned recipe (proven
  Rukhanka HUMANOID path), the 3 rig families (Drifter/Crawler/Strider), CreatureKit (A2)
  framing. Built on DR-022/023 + the proven Blender recipe; every carry-over gotcha flagged.
- Currency re-mean: Ore/Aether/Biomass/Charge -> Flame/Lumen/Gloam/Loudness/Salvage/Chum/
  Tallow with byte-id assignments; flags the fresh-saves-vs-migrate fork (Charge byte reuse).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 23:49:20 -07:00

10 KiB
Raw Blame History

title, date, tags, status, permalink
title date tags status permalink
LANTERN — Art Pipeline Recipes (Workstream A0 material + A1 statics/skinned) 2026-07-13
roadmap
lantern
art
blender
pipeline
workstream-a
recipes
draft-recipe gamevault/06-roadmap/lantern-art-pipeline-recipes

LANTERN — Art Pipeline Recipes

The operating manual the Art_Direction_Lantern hands off to. Workstream A of Roadmap_Lantern_Slice: the material decision made concrete (A0) + the statics/skinned recipes (A1) + the three rig families + the CreatureKit framing (A2). Built on the project's proven animation pipeline (DR-022_Animation_Pipeline_Rukhanka_Synty / DR-023_Enemy_Animation_MonsterMash + the machine-local Blender recipe). The product isn't assets — it's a repeatable factory where creature N+1 is a sitting.

Checklist-grade by design — this project runs on distilled gotchas; the art pipeline gets the same treatment. Every ⚠ is a carry-over hazard already paid for in blood.

0. The material foundation (A0 keystone — no per-asset texturing, ever)

One palette atlas + vertex colours under three master shaders. Every asset shares ~3 materials.

The palette atlas

  • A single small texture (e.g. 256×256) of flat colour bands — the whole game's palette. UVs are placed onto a band (island = a quad snapped to a colour cell), never unwrapped/painted (Imphenzia method).
  • Encode the ontology as bands: warm gold/amber (light/ours), cold corpse-green/blue (gloam), lure-red (bait), white-hot (conversion), plus neutral greys for hard-surface.
  • Import the atlas LINEAR, not sRGB — the ground bundle learned this: an sRGB-imported detail/colour map used in a multiply darkens ~2.3×. Palette atlas = colour source → sRGB is usually right for a base-colour map, but any map used as a MULTIPLY/detail must be LINEAR. Verify the actual look in Play, not the importer.
  • Generatable procedurally (deterministic, no artist eyes needed for v1): a script paints N bands into a PNG.

The three master shaders (author in ShaderGraph, multi-target UniversalTarget)

  1. Lit-Palette — statics. Samples the atlas by UV; vertex-colour tint; flat/near-flat lit. Cheap, batches.
  2. Emissive-Gloam — the palette rule as a shader. Parameterized hue · flicker-cadence · intensity emissive over the atlas base. One shader is the whole "true vs false light" language: steady = true, flicker = false (the load-bearing readability channel). Every glow in the game is this shader.
  3. Skinned-Palette — deformation-aware (Rukhanka-skinned creatures/suits). This is the role the current AnimatedLitShader fills — a multi-target ShaderGraph with UniversalTarget + the deformation nodes so Entities-Graphics GPU skinning works. ⚠ Stock URP/Lit renders a skinned mesh unskinned static + a "does not support skinning" warning — the deformation-aware graph is mandatory for anything Rukhanka drives.
  • Author all three Hybrid-Per-Instance if we want per-instance gloam-tint variants (the free variant axis): ShaderGraph overrideHLSLDeclaration:true + hlslDeclarationOverride:2 on _BaseColor. A stock Unity-Per-Material graph makes an URPMaterialPropertyBaseColor override silently no-op (material-property-body-flash-rukhanka) — the DOTS-authored AnimatedLitShader honours it; a stock Synty prop graph does not.
  • ⚠ Keep the three deformation/palette graphs in _Project/Shaders/ (GUID-preserved), not in any imported sample tree (importing Rukhanka "Animation Samples" drags in 26 subscenes + world-running sample systems + a TMP conflict — the archive documents the extraction).

1. Statics recipe (target: minutes per prop)

  1. Model in Blender to budget (props ≤ ~800 tris); place UVs onto atlas bands (no unwrap).
  2. Export FBX (skinned-capable) or glb via glTFast (statics). ⚠ Apply Scalings = FBX Units Scale; realize any geometry-node instances before export (geo-nodes don't survive the exporter).
  3. Collider policy: cosmetic decor → strip colliders, no GhostAuthoring (classic-URP colliders are inert to the DOTS PhysicsWorld anyway). Gameplay-blocking geometry → an Environment-layer collider baked into the gameplay subscene (EG renders only baked entities; static collider, no Rigidbody → deterministic, no replication). ⚠ A MeshCollider bakes ONLY if the mesh has Read/Write enabled — else an InvalidOperationException per bake and NO baked shape (the classic view lies; only a baked-CollisionWorld probe catches it). Prefer ColliderFitTools (audit/apply) to fit convex hulls to the visual meshes.
  4. Assign Lit-Palette (or Emissive-Gloam for glow props); one material, atlas-driven.
  5. Bake into the subscene; verify in the baked CollisionWorld + a Play screenshot (⚠ a dark render masks material bugs — check shader property values, GetPropertyType-guarded).

2. Skinned recipe (creatures + suit-frames)

The proven Rukhanka path — HUMANOID rig pipeline (muscle clips retarget across rigs; the old "Generic" note was wrong):

  1. Rig in Blender on a shared family armature (see §3). The rig is Humanoid.
  2. Per-action clips — one FBX per action. ⚠ Key All Bones + Force Start/End keys + FBX Units Scale; root motion baked into the pose (CC owns the transform → root motion OFF on import). ⚠ Base any overlay/ partial anim on a full-body pose — a clip keying only a bone-subset collapses the un-keyed bones (mesh sinks) (rukhanka-partial-clip-collapse).
  3. Import CreateFromThisModel — ⚠ CopyFromOther FAILS on Blender's extra Armature node. Optimize Game Objects OFF.
  4. Muscle retarget the clips onto the player + creature rigs (humanoid muscle space).
  5. Rig bakes on the SAME entity holding the gameplay components the drive job reads — Animator + RigDefinitionAuthoring on the root, SMRs flattened under it. ⚠ Skeleton-root = walk up from a bone to the model's direct child, NOT SkinnedMeshRenderer.rootBone (the bounds root — e.g. a head SMR's is Spine_03, which destroys the lower skeleton).
  6. Material = Skinned-Palette (deformation-aware, atlas-driven). ⚠ First Rukhanka bake is ~60 s, main- thread-synchronous (editor freezes — not a hang; blob caches after).
  7. Prefab via EnemyRigTools (GUID-preserving CopyAsset + marker authoring — ⚠ DeleteAsset+CopyAsset orphans subscene refs). Enemies = ownerless interpolated ghosts driven client-side by EnemyAnimationDriveSystem (remote-path velocity from LocalTransform delta). ⚠ The server runs Rukhanka unless strippedServerStripAnimationSystem disables every Rukhanka.Runtime system server-side (matched by assembly name).

3. The three rig families (the amortization decision — clips per FAMILY, not per creature)

Every slice creature maps onto one shared armature so clips are made once per family:

Family Body plan Slice members Clip budget (45)
Drifter spine-chain swimmer Wisp-Choir motes, lure-fish (later: serpents/rays) swim-idle · dart · attack · death-sink
Crawler legged/scuttling floor unit Grindylow (later: kappa-kin, Tenders) skitter · leap · attack · death
Strider biped-ish Drowner, Wrecker, and the suit-frames walk · windup · attack · death

Per-creature attack accents only where identity demands (a Wrecker's cast, a boss phase). Suit-frames: A1 pass = kitbash on the existing humanoid (Strider) rig (helmet/tank/plate swaps over the proven player rig); the custom Wick silhouette sculpt is an A3 upgrade, not a Phase-1 blocker.

4. Procedural assists (geometry nodes + blendermcp scripting)

  • Geometry nodes for glow-gardens / coral / tube-worm / wreck families — parametric families, not hand- modeled instances. ⚠ Realize instances before export.
  • blendermcp scripts for the repetitive steps (scripted posing → per-action FBX export; batch import/ material-assign). The Blender MCP is live this machine; the proven combat-clip recipe is scripted end-to-end.

5. CreatureKit (A2 — the factory tool; spec, built in Phase 2)

One editor tool or hard checklist that takes a Blender export → a play-ready enemy. The measure: adding enemy N+1 touches zero undocumented steps. It must yield, from one export:

  • the prefab (rig + SMRs on root, Skinned-Palette material),
  • ghost/network authoring (ownerless interpolated GhostAuthoringComponent + LinkedEntityGroup),
  • Environment/hit colliders,
  • a tuning-table row (Health/speed/windup/attack-range — bytes, not enums, for Burst),
  • spawn-table + bestiary hooks (the kind-indexed director roster — ⚠ directors index the prefab buffer by Kind, not round-robin; a stale round-robin pool silently mis-maps — enemy-director-kind-indexed-roster).
  • Variant policy (cheapest content first): palette-shift / gloam-tint / scale / part-swap of an existing mesh BEFORE any new mesh. One Drowner mesh → 3 visual tiers via the Emissive-Gloam params (which are also gameplay information). VFX-first identity: emissive + a dynamic light + silhouette + animation carry a creature in the dark; mesh detail is the last 20%.

Throughput rules (solo sanity)

Timebox per class — a prop is a morning, a creature is days-not-weeks, the boss is the exception. The done-bar is "reads at gameplay zoom, under murk, in motion" — never render-portfolio quality. When stuck, ship the placeholder and log the upgrade. ⚠ For skinned-mesh visual bugs, use the operator's eyesWorldRenderBounds/ bone reads return the bind AABB, not the deformed pose (rukhanka-partial-clip-collapse).