Files
gig-line/Docs/Architecture/09_architecture_decisions.md
T
zwitschi 05c1488c00 feat(camera): third-person rig, V toggle, visible body
One pawn, both rigs (ADR-9): a skeletal body hidden in first person, a
collision-tested spring arm, and a toggle that swaps the active camera and the
body's visibility. Shipped default stays first person; -thirdperson starts the
pawn in the third-person rig so the review shot is reproducible, and the
screenshot run passes it.

Three defects found and two fixed:
- The skin material had no SkeletalMesh usage flag, so Unreal silently substituted
  the default grey. That is why the body rendered as clay.
- render_screenshots.sh still called ./Engine/... on this branch (the fix lives on
  the facade branch and never reached main); applied here so the shots generate.
- OPEN: the idle clip is bound and PlayAnimation is called - the run logs the clip
  name - but the body still renders in bind pose, arms out. Recorded with that
  evidence in TODO.md rather than called done.

Shot 5 (demo-third-person) added and documented; camera collision is implemented
but unproven, since the spawn has open space behind it.
2026-11-09 10:33:06 +01:00

4.3 KiB
Raw Blame History

Architecture Decisions

ADR-1: Dynamic-only lighting for the demo

  • Context: The demo is a fixed daytime scene. Stationary lights demand a lighting build and produce "LIGHTING NEEDS TO BE REBUILT" warnings.

  • Decision: r.AllowStaticLighting=False, all lights MOVABLE.

  • Consequences: No bake step, no warning, simpler iteration. Slightly higher GPU cost per frame (no baked shadows) — acceptable for the demo scope.

ADR-2: Materials authored in Unreal, not imported from Blender

  • Context: FBX→Unreal import maps all Blender diffuse colours to white. Flat colour materials do not survive the pipeline.

  • Decision: Build all materials (asphalt, concrete, stucco, façade stone, road paint) directly in Unreal with procedural nodes.

  • Consequences: Materials cannot be previewed in Blender look-dev. Must round-trip to Unreal for visual check. But: full PBR control, world-space noise, no import surprise.

ADR-3: Complex-as-simple collision on terrain via C++ helper

  • Context: UE 5.8 removed bUseComplexAsSimpleCollision. Python cannot set CollisionTraceFlag on StaticMesh/BodySetup at all. The Interchange default is bbox-only simple collision, which placed an invisible lid 8 m above the street.

  • Decision: UGigLineAssetTools::SetComplexCollisionAsSimple (C++) forces CTF_UseComplexAsSimple and saves the asset.

  • Consequences: One C++ helper class. The flag must be saved on the asset (not just the map). No Python workaround exists.

ADR-4: Reset captures StartLocation on first grounded frame

  • Context: BeginPlay captures StartLocation at the PlayerStart, which is above grade (Z 600) so the capsule does not spawn embedded. Every reset dropped the player 1.4 m back into the air.

  • Decision: Repeating 0.25 s timer captures StartLocation once the character reports IsMovingOnGround(), then clears itself.

  • Consequences: Reset is correct. A one-shot timer at 2 s is wrong (fires after walking begins, records mid-stride position).

ADR-5: Core ticker for QA self-test, not world timer

  • Context: Pausing the game stops world timers. A timer-driven test freezes exactly when it proves pause works.

  • Decision: FTSTicker::GetCoreTicker() drives the -qaselftest harness.

  • Consequences: Test runs even while paused. Must be enabled explicitly (-qaselftest), never in normal play.

ADR-6: South = +Y, north = −Y in Unreal

  • Context: The street mesh established this convention: Sidewalk_North has Y range −1912..−1053, Sidewalk_South has +718..+1447.

  • Decision: Adopt it project-wide. Never place a building by assumption; always verify against a street mesh or neighbour.

  • Consequences: Matches Blender’s north→+Y after the Y-flip in the ue_metric_v1 export preset.

ADR-7: Headless Blender via bpy scripts, not MCP

  • Context: Blender skill packs assume an MCP server; the project does not run one.

  • Decision: All Blender work driven by Python scripts in Tools/. Skill packs used as method guides (checklists, naming, proportions) only.

  • Consequences: Scripts are version-controlled and repeatable. No live Blender session needed for CI.

ADR-9: Third-person body is an attachment of the first-person pawn, not a second pawn

  • Context: 0.2.0 needs a visible body and a third-person camera, while 0.1.0 shipped a first-person pawn with no body and the owner's decision was first person for that release. Two separate pawn classes would duplicate movement, input and reset logic, which is the code most likely to drift.

  • Decision: one pawn, both rigs. AGigLineCharacter carries a skeletal mesh (hidden in first person), a collision-tested spring-arm camera, and a V toggle that swaps the active camera and the body's visibility. The shipped default view stays first person; -thirdperson starts the pawn in the third-person rig and is what the screenshot run passes, so the review shot is reproducible without a human pressing a key.

  • Consequences: movement, reset, pause and the QA self-test stay in one place. The body's animation state is driven from Tick by speed rather than from input, so being pushed or falling animates correctly. The cost is that the camera swap is not a separate actor, so a future cinematic camera needs its own path rather than reusing the toggle.