Files
open-wc/docs/modules/character-presentation.md
T
sindoring ad820699bc docs(M02): publish character presentation handoff
Work-Package: M02-RND-CHARACTER-PRESENTATION-001

Agent: sindo-main-codex
2026-07-14 23:42:42 +04:00

15 KiB

Character Presentation

Metadata

Field Value
Status Implemented boundary / Partial fidelity
Target/work package M02 / M02-RND-CHARACTER-PRESENTATION-001
Owners Character appearance composition and locomotion animation presentation
Last verified 97bb53f, 2026-07-14
Profiles/capabilities Current render sandbox character experiment

Purpose

Compose the current sandbox character GLB, geosets, skin textures and starter outfit below the existing Visual node, then independently present its stationary or moving animation. Keep visual asset loading and AnimationPlayer state out of gameplay movement and the player scene composition root.

Non-goals

  • Define authoritative character identity, equipment or movement state.
  • Change the existing GLB, geoset, texture-compositor, DBC or outfit formats.
  • Implement full build-12340 animation selection, blending or equipment visuals.
  • Load world assets, query terrain, process input or control the camera.
  • Introduce asynchronous streaming, background scene mutation or persistence.

Context and boundaries

flowchart LR
    Scene[ThirdPersonWowController composition] --> Appearance[CharacterAppearancePresenter]
    Model[Configured PackedScene] --> Appearance
    DBC[Extracted DBC/item textures] --> Appearance
    Appearance --> Root[CharacterModel root]
    Root --> Geosets[Existing geoset controller]
    Root --> Compositor[Existing texture compositor]
    Appearance -->|ready root| Scene
    Scene --> Animation[CharacterAnimationPresenter]
    Velocity[Movement read model] -->|moving boolean| Animation
    Animation --> Player[AnimationPlayer below root]

Allowed dependencies:

  • configured Godot PackedScene character asset;
  • existing CharacterGeosetController, CharacterTextureCompositor and WowCharacterOutfitResolver presentation components;
  • AnimationPlayer and animation resources below the composed character root;
  • main-thread player scene composition and movement velocity read model.

Forbidden dependencies:

  • MoveIntent, input devices or movement integration;
  • TerrainQuery, ADT parsing, coordinate conversion or streaming decisions;
  • cameras, network packets, server databases or gameplay reducers;
  • direct production database writes or cache-format ownership.

Public API

Symbol Kind Purpose Thread/lifetime Errors
CharacterAppearancePresenter.load_character_appearance(content_root_override) Command Replaces the owned character root and schedules final grounding/outfit composition Main thread; may be called again during scene lifetime Returns false for empty/invalid model; warning for invalid resource
CharacterAppearancePresenter.character_root Read-only state Current owned model root or null Stable until next load None
character_appearance_ready(character_root) Signal Publishes a fully attached, grounded root Deferred main-thread completion Not emitted for invalid/empty model
CharacterAnimationPresenter.bind_character_root(character_root) Command Discovers and prepares the first descendant AnimationPlayer Main thread; binding lifetime Returns false and clears state when absent
CharacterAnimationPresenter.present_locomotion(is_moving) Command Selects current stationary/moving candidate and starts it once Main/physics thread Returns false when unavailable, missing or unchanged
active_animation_name Read-only state Selected animation name Stable until bind/state transition Empty when unbound
has_animation_player Read-only state Whether the presenter has a valid bound player Stable until bind None

Inputs and outputs

Direction Contract/data Producer Consumer Ownership Thread/lifetime
Input Model resource path, class, scale and yaw Runtime scene composition Appearance presenter Exported configuration Captured at load
Input Extracted content root Player scene configuration Appearance presenter Copied string Captured at load
Input Packed character scene Godot resource loader Appearance presenter Shared resource; presenter owns instance Scene lifetime
Output Composed CharacterModel root Appearance presenter Scene/animation presenter Appearance presenter owns node Until replacement
Output Ready signal Appearance presenter Player composition Synchronous signal after deferred completion One per successful load
Input Moving/stationary boolean Player adapter from horizontal velocity Animation presenter Value copy One physics tick
Output AnimationPlayer playback mutation Animation presenter Rendered character Bound root owns player/resources Until next transition

Side effects:

  • loads and instantiates the configured model on the main thread;
  • adds/removes one owned CharacterModel subtree;
  • composes the existing texture/outfit components and reads their extracted inputs;
  • changes model transform for scale, yaw and ground alignment;
  • configures locomotion loop modes and starts AnimationPlayer playback;
  • does not write files, caches, databases or network state.

Data flow

flowchart TD
    Config[Model/class/scale/yaw/content root] --> Load[Load PackedScene]
    Load --> Replace[Replace owned CharacterModel]
    Replace --> Attach[Attach model and compositor]
    Attach --> Ground[Fit mesh bounds to visual ground]
    DBC[Existing outfit resolver] --> Outfit[Apply starter outfit]
    Ground --> Ready[Emit ready root]
    Outfit --> Ready
    Ready --> Bind[Discover AnimationPlayer]
    Velocity[Horizontal movement velocity] --> State[Moving or stationary]
    State --> Select[Run/Walk or Stand/Idle selection]
    Bind --> Select
    Select --> Playback[Loop and play with blend]

Lifecycle/state

stateDiagram-v2
    [*] --> Empty
    Empty --> Loading: valid load command
    Loading --> Ready: attach + deferred composition
    Loading --> Empty: invalid resource
    Ready --> Loading: replacement load
    Ready --> Empty: empty model command

    state Animation {
        [*] --> Unbound
        Unbound --> Stationary: bind root with animations
        Stationary --> Moving: moving=true
        Moving --> Stationary: moving=false
        Stationary --> Unbound: bind missing player
        Moving --> Unbound: bind missing player
    }

Appearance replacement removes the old root from the scene immediately and queues it for deletion. Deferred completion checks root identity, so a stale completion cannot publish a replaced model.

Main sequence

sequenceDiagram
    participant Scene as ThirdPersonWowController
    participant Appearance as CharacterAppearancePresenter
    participant Existing as Geoset/Compositor/Outfit
    participant Animation as CharacterAnimationPresenter
    participant Player as AnimationPlayer
    Scene->>Appearance: load_character_appearance(content root)
    Appearance->>Appearance: load, instantiate and attach root
    Appearance->>Existing: compose textures and starter outfit
    Appearance-->>Scene: character_appearance_ready(root)
    Scene->>Animation: bind_character_root(root)
    Animation->>Player: prepare loops and play Stand/Idle
    loop Physics ticks
        Scene->>Animation: present_locomotion(is moving)
        Animation->>Player: play only on state/name change
    end

Ownership, threading and resources

  • The runtime scene owns one appearance presenter on Visual and one animation presenter below it.
  • The appearance presenter exclusively owns the generated CharacterModel subtree; imported shared resources remain Godot-owned.
  • The animation presenter borrows an AnimationPlayer reference until rebind or subtree replacement; the player scene orders rebind through the ready signal.
  • Loading, node mutation, bounds calculation, outfit setup and playback occur on the main thread. There are no workers, locks, RIDs or cancellation tokens.
  • The player controller owns neither model resources nor animation state.

Errors, cancellation and recovery

Failure Detection Behavior Diagnostic Recovery
Empty model path Load guard Clears old root and returns false Contract regression Configure a path and retry
Invalid model resource Failed PackedScene cast Remains empty Named warning with path Correct/extract asset and retry
Replaced deferred completion Root identity check Stale completion ignored Replacement regression No action required
Missing DBC/outfit inputs Existing resolver returns false/empty Base model remains visible Existing resolver diagnostics Restore extracted inputs and reload
Missing AnimationPlayer Recursive discovery returns null Presenter becomes unbound Contract return/read state Bind a root with animations
Missing candidate animation Candidate selection returns empty Current playback unchanged Verifier/source diagnostics Extend verified candidate policy later

There is no background load cancellation. Replacement is the recovery path and invalid model state is explicit through the return value and null read model.

Configuration and capabilities

Setting/capability Default Profile Runtime mutable Effect
Character model Human male sandbox GLB Current sandbox Before reload Model scene input
Character class ID 1 Current sandbox Before reload Starter outfit lookup
Uniform scale 1.0 Current sandbox Before reload Character root scale
Yaw offset 90 degrees Current sandbox Before reload Imported model correction
Extracted content root res://data/extracted Local content Per load override DBC/item texture input
Animation blend 0.15 seconds Current sandbox Before bind/transition Playback cross-fade/default blend
Stationary candidates Stand, then Idle Current sandbox Fixed Exact then substring selection
Moving candidates Run, then Walk Current sandbox Fixed Exact then substring selection

Persistence, cache and migration

Presenter state is transient. No saved settings, user profile, database schema or cache format changes. Existing scene paths remain ThirdPersonPlayer/Visual and add only Visual/AnimationPresenter; model and extracted-data paths retain their defaults. Moving the model exports from the player node to Visual is a scene-authoring ownership change, not persisted runtime data migration.

Diagnostics and observability

  • Logs: invalid model path warns with the responsible presenter and path.
  • Read models: current character root, animation binding and active name.
  • Tests: dedicated verifier reports appearance, animation, scene and player boundaries.
  • Metrics/correlation IDs: not applicable to this synchronous sandbox presenter.

Verification

  • Unit/contract: synthetic model fixture verifies load, scale, yaw, grounding, compositor composition, ready signal, replacement and empty-model clearing.
  • Animation: synthetic libraries verify nested discovery, exact and substring candidate selection, loop policy, blend configuration and duplicate suppression.
  • Boundary: player source contains no model loading, outfit, bounds or AnimationPlayer logic; appearance and animation dependencies remain separate.
  • Integration: Eastern Kingdoms, Kalimdor and player regression scenes compose both presenters; existing input/movement/terrain/camera tests remain required.
  • Fidelity evidence: only preservation of the current sandbox defaults and selection rules. No build-12340 appearance or animation parity is claimed.
  • Performance: one synchronous model composition per load and constant-time locomotion calls except on state transition; asset streaming remains future work.

Extension points

  • Replace starter outfit inputs with a versioned appearance/equipment snapshot.
  • Replace boolean locomotion input with a typed presentation state after movement/network contracts define walk, run, jump, fall, swim, mount and emotes.
  • Add asynchronous/preloaded model providers behind the appearance command without moving resource loading back into gameplay.
  • Add verified animation IDs/variation policy after build-12340 fixtures exist.

Capability status

Capability Status Evidence Gap/next step
Independent appearance composition Implemented Synthetic fixture and three scene wirings Integrator target update
Independent locomotion animation Implemented Exact/fallback/loop/blend regression Add typed presentation state later
Current starter outfit Partial Existing resolver reused unchanged Needs extracted DBC fixture and client comparison
Current skin/geoset composition Partial Existing components reused Full equipment/customization fidelity incomplete
Build-12340 animation semantics Planned No original-client fixture Capture animation IDs/transitions/timing
Runtime equipment/network updates Planned No snapshot contract M08/M09/M12 work

Known gaps and risks

  • Model loading remains synchronous and can hitch when not preloaded.
  • Exact appearance, equipment, animation choice, playback speed and blending are unverified.
  • The moving input is a sandbox threshold boolean, not authoritative locomotion state.
  • The first descendant AnimationPlayer wins; multi-player imported scenes need a more explicit binding contract if they appear.
  • Existing geoset/compositor/outfit implementations retain their own fidelity gaps.

Source map

Path Responsibility
src/scenes/character/character_appearance_presenter.gd Model-root ownership, composition, ground fit and outfit setup
src/scenes/character/character_animation_presenter.gd AnimationPlayer binding and locomotion playback state
src/scenes/character/character_geoset_controller.gd Existing geoset visibility and outfit defaults
src/scenes/character/character_texture_compositor.gd Existing skin/equipment texture composition
src/scenes/character/wow_character_outfit_resolver.gd Existing DBC starter outfit lookup
src/scenes/player/third_person_wow_controller.gd Scene composition and moving-state adapter
src/tests/scenes/character_presentation_model_fixture.tscn Asset-free model/grounding fixture
src/tools/verify_character_presentation.gd Presenter, scene and dependency regression
  • ADR: none; current behavior and resource formats are preserved.
  • Upstream/reference: targets/02-player-decomposition.md, targets/roadmap/02-rendering-and-graphics.md, targets/roadmap/04-gameplay-systems.md, RENDER.md.