Files
Project-M/Docs/Vault/07_Sessions/2026/2026-06-03_Visual_Upgrade_HomeBase.md
T
2026-06-03 13:46:13 -07:00

9.8 KiB
Raw Blame History

date, type, tags, permalink
date type tags permalink
2026-06-03 session
session
dots
netcode
art
urp
entities-graphics
materials
presentation
home-base
visual
gamevault/07-sessions/2026/2026-06-03-visual-upgrade-homebase

Session 2026-06-03 — Art import + home-base visual upgrade (Synty/BefourStudios → URP)

Goal

The operator imported a large art/VFX asset bundle (commit e362aae, ~4,830 files) and asked to inventory it and use it to make the project look visually better, with more Synty packs coming later. Scope was set via a 4-question gate: render pass + curated home-base dressing, enemies/player stay primitives (no character meshes in these packs), VFX deferred, theme = clean orbital base. Locked in DR-010_Art_Import_URP_Conversion_Visual_Upgrade.

Inventory (what was actually imported)

  • BefourStudios/Art (orbital-base modular kit — domes, batteries, crates, plants, modular blocks, lights, solar, storage), /SciFiEngineersRoom (interior), /BioHorrorSciFiEnvironment (organic/decay). ~560 prefabs, hundreds of FBX + PBR textures (T_*_B/_N/_ORM).
  • GabrielAguiarProductions VFX = NOT in the repo — only 3 PDF docs committed; zero .unitypackage/.vfx/prefabs. VFX deferred until imported.
  • No character meshes anywhere — packs are environment/props only.
  • Pipeline mismatch: the art is HDRP-authored (Shader Graphs/S_General, HDRP/Lit; _DISABLE_SSR_TRANSPARENT, RayTracingPrepass) while the project runs URP 17.4 + Entities Graphics → source materials render magenta under URP. Meshes + textures are fully usable; materials must be re-authored to stock URP/Lit (933532a4…, the same shader the project's working prototypes use → Entities-Graphics-compatible). Blank auto-generated MI_* URP stubs exist but carry no textures.

Process

  • Plan-gated (read-only): 3 Explore agents inventoried the packs + current visual/render setup; a Plan agent pressure-tested the execution sequence (corrected several of my assumptions by reading files). Operator approved.
  • Implementation = sequential via MCP (single editor, domain-reload-ordered; NOT a parallel swarm): a reusable Editor converter, ghost-prefab mesh swaps (PrefabUtility round-trip), subscene dressing (open additive → place → save → re-bake), and a SampleScene render pass.
  • Adversarial review workflow (3 lenses: code / netcode / conventions) over the diff. It earned its keep — caught 2 blockers + 1 major that the dark lighting masked in the screenshot. All fixed; minors triaged.

Done

  • Assets/_Project/Scripts/Editor/EnvArtTools.cs (new; Assembly-CSharp-Editor, #if UNITY_EDITOR) — reusable HDRP→URP material converter for future packs. Reads source texture refs across HDRP/Lit + ShaderGraph property names (_BaseColorMap/_BaseTexture_BaseMap, _NormalMap/_NormalTexture_BumpMap), type-guards every color/float/texture read (verify ShaderPropertyType before GetColor/GetFloat), low metallic (no reflection probe ⇒ high-metallic reads black), source-flag-gated emission, alpha-clip from source flags. ConvertCurated() menu item + RemapRenderersToEnv(go). GUID-preserving overwrite (refs survive re-runs). Outputs to Assets/_Project/Materials/Env/ (20 materials).
  • Ghost prop mesh swapsStorage.prefabSM_Storage_LOD0 (scale 0.6), UpgradePickup.prefabSM_Battery_LOD0 + emissive M_Env_PickupGlow (scale 2.5). GhostAuthoring/LinkedEntityGroup/gameplay authoring preserved; only mesh+material+scale changed.
  • Two server spawners patched to preserve baked scale (UpgradePickupSpawnSystem, SharedStorageSpawnSystem): LocalTransform.FromPosition() hard-sets Scale=1, which silently discarded the prefab scale — now they read the prefab's baked LocalTransform and only override Position.
  • Gameplay subscene dressedEnvDecor root of 13 collider-stripped, unpacked static props (dome backdrop, modular blocks, batteries/charger, crates, plants, light poles, solar panels) with URP materials; existing Wall_North/East/Pillar_Center re-skinned (colliders/positions kept) with tiled M_Env_Wall. No GhostAuthoring on scenery.
  • SampleScene render pass — cool key light @1.451.75; dark-space procedural skybox; flat dark ambient; exp fog @0.006 (keeps the z=26 dome visible); Ground re-skinned to tiled M_Env_Floor; PostFX_DarkSciFi profile populated (Bloom / ACES Tonemapping / ColorAdjustments / Vignette) and made the single active global volume (overlapping GlobalPostFX disabled).

Validation

  • EditMode 74/74 green (unchanged; the spawner edits compiled clean — note: EditMode test init timed out once on the unfocused editor, passed on retry with init_timeout=180000).
  • Runtime (single in-editor client, 6.4.7): both worlds boot, 11 ghosts replicate server↔client; player input → prediction → replication works and the character is blocked by the east wall (static PhysicsWorld survived the dressing); ghost scales now apply (Storage 0.60, pickups 2.50, others 1.0). Console clean of code/Burst/material/bake errors (only "Server Tick Batching" warnings — the documented unfocused-editor symptom; decor adds zero server-sim load). Screenshots Assets/Screenshots/result_clean_base.png (after) vs baseline_editmode.png (before) confirm the jump.
  • Adversarial review (3 lenses): 2 blockers + 1 major, all fixed (below); remaining findings are minor/future-pack robustness, addressed or noted.

Diagnosis notes (for future me / future Synty packs)

  • The art is HDRP, the project is URP. Source M_* materials render magenta. Convert to stock URP/Lit via EnvArtTools (do NOT switch the project to HDRP — it would break Entities Graphics). Meshes + _B/_N/_ORM textures are reusable as-is.
  • A screenshot under dark lighting MASKS material bugs. The adversarial review caught a black-_BaseColor blocker on ~16/20 materials: HasProperty("_BaseColorMultiply") was true (it's a float on S_General), GetColor() on a float returns (0,0,0) and logs a "doesn't have a color property" warning — the very warning I'd dismissed as cosmetic. Always shader.GetPropertyType(idx)-guard before GetColor/GetFloat; the real tint is _AlbedoTint. Verify material values, not just the render.
  • Source emission needs the _Emissive (0/1) flag gate. S_General carries a non-zero default _EmissiveColor even when emission is off → reading the color unconditionally made crates/dome/walls glow. Emit only when _Emissive>0.5 AND the name marks a light fixture; flat color emission can't reproduce the source's emission mask anyway.
  • VolumeProfile.Add<T>() does NOT persist the override as a sub-asset — on save the components list serializes 4 {fileID:0} nulls (works in-session, gone after reload). Must AssetDatabase.AddObjectToAsset(component, profile) for each, then SaveAssets. Verify on disk (non-null refs + N+1 MonoBehaviour blocks).
  • LocalTransform.FromPosition() resets Scale to 1 — any ghost spawned via it ignores its prefab's authored scale (and Scale is a replicated [GhostField], so it's consistent-but-wrong, not a desync). Read the prefab's baked LocalTransform and override only Position to keep authored scale.
  • High metallic + no reflection probe + dark skybox = near-black surfaces. Keep converted env metallic low (0.10.2); rely on albedo + direct light. (A baked reflection probe would let metallic read correctly later.)
  • Metallic without reflections looked "too dark" before the tint bug was even found — two compounding causes (black albedo + high metallic); fixing both gave a bright, readable base.
  • Editing a subscene via MCP: manage_scene load … additive opens the authoring scene (the baked-entity view it was showing goes away — empty game view is expected), edit + SaveScene, then close_scene so the SubScene re-bakes from the saved file on Play. Verify placement via execute_code enumerating the scene roots, not the game view.
  • HUD-free beauty shot: a positioned game_view capture (view_position/view_rotation) uses direct camera rendering, which excludes Screen-Space-Overlay UI — so the gameplay death overlay doesn't bleed into the render. (scene_view rejects positioned capture.)
  • execute_code is a method body (no usings; fully-qualify). Reusable converter logic belongs in a committed Editor script, not repeated execute_code blobs — the operator is importing more packs.

Open / deferred

  • VFX — import the GabrielAguiar .unitypackages, then wire the Shuriken packs (projectiles/explosions/shields/auras) into the existing client ParticleSystem pool; the VFX Graph packs (hits/beams) need separate URP setup.
  • Characters — player/enemies are still primitives; swap for real models when a character/Synty pack arrives (the prefab+material pattern generalizes).
  • Decor LODs bake into both worlds — the 13 props' full LOD hierarchies are in the shared subscene, so the server carries renderable+MeshLODGroup entities for nothing. For more decor, move pure cosmetics to a client-only/cosmetic subscene or flatten to LOD0.
  • Higher material fidelity — current converter uses uniform metallic/smoothness (ORM channels don't map to URP); a per-channel ORM→URP repack (or a small ORM ShaderGraph) + a baked reflection probe would deepen the look.
  • More dressing — mine S_OrbitalBase.unity for a fuller layout once the pipeline is proven; the BioHorror pack fits future "corrupted" arena zones (mixed-theme option from the scope gate).

Next

Either import + wire the VFX (biggest feel win, needs the operator to import the packs) or deepen the environment (more orbital-base dressing + a reflection probe + ORM fidelity). The reusable EnvArtTools converter + the duplicate-and-reskin prefab pattern are the foundation for every future Synty pack.