# UE5 Client Modules & Agent Embodiment

V6 — **Egbe** — is a universe of LLM-driven autonomous beings, the **Ori**, and
the product promise is that you can stand next to one. An Ori is, by design,
split across three authorities: its decisions live in the Moirai cognition
cluster, its biography lives in the Ori service, and neither of them is a
_body_. The body is the UE5 client's job. The canonical Egbe client is a single
UE5.5 LTS project at `V6/ue/`, carved into eighteen `*.Build.cs` modules, and
its hardest problem is one V3's metaverse never faced at this scale: a single
Orun ground routinely renders _far_ more simultaneous embodied agents than a V3
venue ever did, and each of those agents is a mind that costs money to think. So
embodiment in V6 is not just "spawn a skeletal mesh" — it is a coupled
render-and-perception LOD pipeline that decides, per agent per frame, how much
body it gets _and_ how much cognition it is owed, so that fidelity and cost both
scale with what the player can actually see.

This page is the client side of that wire. It walks the UE5 module split, then
the embodiment stack proper — the near pawn, the avatar runtime (VRM and
MetaHuman through the vendored VRM4U plugin and the reused V3 avatar pipeline),
emotion-driven animation, the Mass Entity density LOD ladder, and the
StateTree-driven autonomous behavior that runs at the body when no model call is
affordable. V6 reuses V3's avatar/animation/audio substrate wholesale and adds
the agent-density and Ori-binding layer on top; the orientation map for the
whole system is [../V6_ARCHITECTURE.md](../V6_ARCHITECTURE.md).

## What ships, honestly

**The embodiment logic is real C++ and is tested.** The avatar runtime, the
emotion→animation resolver, the agent density/perception LOD planner, the
Ori-backed LOD-transition continuity check, the StateTree behavior selector, and
the Sequencer cinematics director are all implemented under `V6/ue/Source/` and
covered by `V6Tests` automation specs that the V6 completion run built and ran
on the on-box UnrealEngine-5.5.4 (the full V6 UE suite reported 30/30 green).
The load-bearing specs for this page are real and named:
`V6.Avatar.Runtime.AppearanceSeedCostumeAndAging`,
`V6.Animation.EmotionDrivenGaitPostureIdleAndFace`,
`V6.Agent.NearPawnBindsFullEmbodiment` / `...RejectsIncompleteEmbodiment`,
`V6.Agent.DensityLOD.Holds150AgentDrawBudget`,
`V6.Agent.PerceptionLOD.BoundsCognitionByVisibleAgents`,
`V6.Agent.LODBoundary.PreservesOriStateAcrossRenderLODs`, and
`V6.Agent.Behavior.SelectsPerCognitionTier` / `...FailsLoudWithoutTree`.

**V6 ships zero binary UE content — by deliberate decision, not omission.**
There are no `.uasset`/`.umap` files in the project; `V6/ue/Content/` is
`.gitkeep` placeholders. The six Orun districts are authored as _procedural C++
grounds_ (`V6World/V6OrunDistricts.cpp`), not authored levels, and the
embodiment modules here are the **runtime binding and resolution logic plus the
data contracts** that a skeletal mesh, an AnimBP, a MetaHuman master, or a VRM
body would be driven through — not the art itself. This is the same honest line
V3 draws between its real retarget tables and the avatars they gate
(`v3arch§"Avatar Pipeline"`): the pipeline is code; the bodies are content the
pipeline binds and validates.

**VRM4U is vendored, not hard-linked.** The avatar plugin is the third-party
[VRM4U](https://github.com/ruyo/VRM4U) importer/runtime loader, dropped in at
`V6/ue/Plugins/VRM4U/` as an untracked submodule with
`"EnabledByDefault": false`. Notably, `V6Avatar.Build.cs` does **not** list
VRM4U as a module dependency — it depends only on `Core`, `CoreUObject`,
`Engine`, `GameplayTags`, and `V6Core`. V6 reaches VRM bodies through _soft_
references (an `FSoftObjectPath VrmAssetPath`) and the named V3 retarget profile
`V3Avatar.Oshun60`, so the V6 C++ stays buildable and testable headless without
the plugin loaded. (On Linux the editor additionally needs VRM4U's
`Shaders/Private` case-shim symlink before it will boot — a build-time shim,
never committed.)

**Four modules are honestly thin.** The integration-boundary modules `V6Net` (32
lines), `V6Gameplay` (21), `V6OnlineServices` (31), and `V6Telemetry` (31) are
currently module-registration contracts; their heavy lifting lives in the Rust
services and the `libs/v6/*` packages (the wire protocol, for instance, is the
373-line proto3 schema in `libs/v6/egbe-protocol`, documented in the
[gateway page](./gateway-pixel-streaming-and-web-fallback.md)). The
embodiment-critical modules — `V6Avatar`, `V6Animation`, `V6Agent`, `V6Voice`
(1,585 lines), `V6Audio` (527), `V6Cinematics`, `V6UI`, `V6World` — carry real
domain types.

## The UE5 client module split

C++ is the primary gameplay language; Blueprint is reserved for designers, data
assets, and cinematic glue. The client is a high-fidelity _client_ of
authoritative Rust world-server state — it owns no frame-deterministic combat
(that belongs to the destination Aye realm on incarnation), and all client-side
prediction reconciles against the world server. The eighteen modules group into
four bands:

- **Foundation** — `V6Core` (the `AV6EgbeGameMode` / `AV6EgbePawn`, engine
  subsystems, save-game and V1-account bridge, the `UV6GameFeatureData`
  registration that lets districts and shard modes hot-load as Game Feature
  Plugins) and `V6World` (the client-side replicated shard model
  `UV6ClientWorldModel` and the procedural district grounds).
- **Embodiment** — `V6Avatar`, `V6Animation`, and `V6Agent`, the subject of the
  rest of this page.
- **Interaction & presence** — `V6Input` (Enhanced Input plus an OpenXR backend
  matrix and VR stewardship interaction), `V6Voice` (the Vac mic / intent /
  squad client), `V6Audio` (MetaSound and conversation-audio types), `V6UI`
  (token-themed CommonUI roster / dossier / Chronicle / HUD widgets with
  first-class `FV6AccessibilityDescriptor` metadata), `V6VFX`, and
  `V6Cinematics`.
- **Integration & tooling** — the four thin boundary modules above, plus
  `V6Persistence`, `V6Editor` (the Egbe Studio authoring / verification
  commandlets), and `V6Tests` (the automation harness and Gauntlet drivers).

## Agent embodiment: the near pawn

The richest form an Ori can take is a **near pawn**: `AV6AgentPawn`, an
`ACharacter` that composes a `UV6AgentComponent`, a `UAudioComponent` for agent
TTS, and a `USphereComponent` for the interaction radius. These are the Clotho
candidates — co-present, on-screen, addressable, full collision and movement.

Binding one is a fail-loud contract, not a best-effort spawn.
`UV6AgentComponent::BindNearAgent()` takes a `FV6AgentNearEmbodimentSpec` and
the live skeletal-mesh, voice, interaction, movement, and collision components,
and `BuildNearReadinessReport()` returns a `FV6AgentNearReadinessReport` whose
`EV6AgentNearBindingState` is exactly one of `Unbound`, `BoundIncomplete`, or
`RenderReady`. `RenderReady` requires _all_ capabilities present — an Ori id,
the V3 avatar pipeline binding (`V3AvatarPipelineRef` defaults to
`V3Avatar.VRMOrMetaHuman`), a full skeletal mesh, individual animation, voice,
expression channels, a renderable appearance seed, emotion animation, collision,
movement, and interaction. Anything missing is listed by name in
`MissingCapabilities`; the spec carries an `EV6AgentBodySource` of `Vrm`,
`MetaHuman`, or `Procedural`. This is what
`V6.Agent.NearPawnBindsFullEmbodiment` and its rejection twin guard — a
half-bound agent reports `BoundIncomplete` rather than rendering a broken body.

### Avatars: appearance seed, costume, and visible aging (`V6Avatar`)

`V6Avatar` is the runtime that turns an Ori's `FV6AvatarAppearanceSeed` into a
dressed, age-marked body. The seed names its `EV6AvatarRuntimeSource` (VRM or
MetaHuman), carries the soft `VrmAssetPath` / `MetaHumanBlueprintPath`, the
`V3Avatar.Oshun60` retarget profile, a list of `FV6AvatarCostumeSlotBinding`,
and a list of `FV6AvatarAgingMark`. The default wardrobe is three slots —
`body`, `outer` (the steward-wrap), and `accent` (the "ori-thread") — each
mapped to a named material vector+scalar parameter pair.

The component does real engine work, not bookkeeping.
`ApplyCostumeSlotsToMesh()` calls
`USkeletalMeshComponent::SetVectorParameterValueOnMaterials()` and
`SetScalarParameterValueOnMaterials()` to tint each slot, and
`ApplyAgingMarksToMesh()` calls `SetMorphTarget()` plus a material scalar to
weight each aging mark. **Visible aging** — V6's signature addition over V3 — is
driven from arc state: `ResolveLifeStageFromArc(ArcProgress, bTranscendent)`
maps a normalized arc into `NewlyKnown` (<0.18), `Young` (<0.42), `Adult`
(<0.78), `Elder`, or `Ancestor` (on transcendence), and the three default marks
— `adult-story-lines` (weight 0.42), `elder-silvering` (0.72), and
`ancestor-lumens` (1.0) — fade in by stage via
`FV6AvatarAgingMark::AppliesToStage()`. `BuildRuntimeBindingReport()` is again
fail-loud: it enumerates every missing capability and only an avatar with an Ori
id, a runtime asset, the V3 retarget profile, required costume slots, and
stage-appropriate aging marks reports `IsRenderable()`.
(`V6.Avatar.Runtime.AppearanceSeedCostumeAndAging`.)

### Emotion-driven animation (`V6Animation`)

The second V6 addition is that **mood is legible at a distance, before any
dialogue.** `V6Animation` maps eight `EV6OriEmotionState` values — `Calm`,
`Joy`, `Focus`, `Concern`, `Grief`, `Anger`, `Awe`, `Tired` — to gait, posture,
idle, and facial-curve profiles. `BuildDefaultEmotionPoseMap()` authors a full
`FV6EmotionAnimationPose` per emotion (grief, for instance, is
`gait.grief.weighted` + `posture.closed.lowered`, an 11° forward lean, and the
`V6Face_Grief` / `V6Face_LowerLids` curves), and
`ResolveEmotionDrivenAnimation()` takes the live `FV6EmotionAnimationInput`
(valence, arousal, confidence from the Ori) and blends it: arousal nudges the
gait-speed multiplier (clamped 0.25–1.75) and idle intensity, low confidence
lengthens the blend time, and the resolver computes a
`MoodLegibilityDistanceMeters` from posture lean, idle intensity, arousal, and
facial richness. `ValidateMoodLegibilityAtDistance()` enforces a real floor —
the blend must read at **≥ 12 m** — so an agent's emotional state is
silhouette-legible across a plaza. An unrecognized emotion falls back to the
`Calm` pose rather than producing nothing.
(`V6.Animation.EmotionDrivenGaitPostureIdleAndFace`.)

## The density LOD pipeline (Mass Entity)

This is the part of embodiment that is genuinely new at the V6 scale, and it is
where `V6Agent` depends on the full UE Mass framework (`MassEntity`, `MassLOD`,
`MassRepresentation`, `MassActors`, `MassSpawner`, …). Agents stream across four
render tiers, and — the load-bearing idea — **perception and cognition LOD are
slaved to render LOD**, so the world server only spends rich perception, and
Moirai only spends a full model call, on agents the player can actually see.

```mermaid
flowchart TD
    subgraph PLAN["UV6AgentDensityLODLibrary (V6Agent)"]
        IN["Agents: distance + importance + visible"] --> SORT["sort: visible ▸ importance ▸ distance"]
        SORT --> RENDER{"PlanDensityLOD<br/>per-platform caps + draw budget"}
    end
    RENDER -->|"≤ NearRadius, cap MaxNearPawns"| NEAR["NearPawn · AV6AgentPawn<br/>full skeletal mesh + AnimBP"]
    RENDER -->|"≤ MidRadius, cap MaxMidMass"| MID["MidMassInstanced · Mass StaticMeshInstance<br/>shared animation"]
    RENDER -->|"≤ FarRadius"| FAR["FarSilhouette · billboard / crowd shader"]
    RENDER -->|"beyond / not visible"| CULL["Culled"]

    NEAR --> PRICH["Rich perception → Clotho"]
    MID --> PCOARSE["Coarse perception → Lachesis"]
    FAR --> PSUM["Summary perception → Atropos"]
    CULL --> PDORM["Dormant → no cognition"]

    PRICH --> STREAM["PlanAppearanceAssetStreaming<br/>FullBody 48MB / Mid 12MB / Sil 2MB / 0MB"]
    PCOARSE --> STREAM
    PSUM --> STREAM

    NEAR -. "cross-LOD" .-> CONT["ValidateLODTransitionContinuity<br/>Ori durable state: identity · activity · rels · transform"]
    MID -. "cross-LOD" .-> CONT
    CONT --> SEAM["bSeamless = no popping"]
```

`UV6AgentDensityLODLibrary::PlanDensityLOD()` sorts agents by visibility, then
importance, then distance (with a lexical Ori-id tiebreak for determinism) and
greedily assigns `NearPawn` within `NearRadiusCm` (default 2,500 cm) up to
`MaxNearPawns`, `MidMassInstanced` within `MidRadiusCm` (9,000 cm) up to
`MaxMidMassAgents`, `FarSilhouette` within `FarRadiusCm` (25,000 cm), and
`Culled` beyond. Caps and the draw budget are **per platform**, read from the
`[V6.MassEntity]` config section with code defaults: desktop allows 64 near
pawns / 64 mid / a 340-draw-unit budget; mobile drops to 24 / 48 / 160; a Pixel
Streaming worker to 48 near / 280; the Tier-2 fallback floor to 16 / 32 / 100. A
near pawn costs 4 draw units, a Mass-instanced mid agent 1, and the entire far
silhouette batch 1 — so the `FV6AgentDensityLODSummary` can prove
`bWithinDrawBudget` (`V6.Agent.DensityLOD.Holds150AgentDrawBudget`,
`V6.Agent.DensityLOD.HoldsFestivalTierGatheringBudget`).

`PlanPerceptionLOD()` then walks the render assignments and derives, per agent,
the `EV6AgentPerceptionLOD` (`Rich` / `Coarse` / `Summary` / `Dormant`) and the
`EV6AgentCognitionTier` (`Clotho` / `Lachesis` / `Atropos` / `Dormant`) directly
from the render LOD — NearPawn→Rich→Clotho, Mid→Coarse→Lachesis,
Far→Summary→Atropos. Because every cognition tier is anchored to a render tier,
the summary can assert `bWithinCognitionBudget` _and_
`bRenderAndPerceptionLODsTrack`: total cognition is bounded by the visible-agent
count, which is the client-side half of V6's cost story
(`V6.Agent.PerceptionLOD.BoundsCognitionByVisibleAgents`,
`V6.Agent.PerceptionLOD.TracksRenderLOD`). `PlanAppearanceAssetStreaming()`
closes the loop on memory: FullBody ≈ 48 MB resident, the shared mid proxy ≈ 12
MB, a silhouette ≈ 2 MB, dormant 0, each checked against the residency budget
and flagged `bCanShedDetailWithoutPop`.

The Mass side is concrete: `UV6AgentMassVisualizationTrait` (a
`UMassMovableVisualizationTrait`) carries the mid instanced mesh, the far
silhouette billboard mesh, and the silhouette material, and
`RepresentationForRenderLOD()` maps mid/far to
`EMassRepresentationType::StaticMeshInstance`. Agent identity, Ori durable
state, render LOD, and perception LOD all ride as `FMassFragment`s
(`FV6AgentMassIdentityFragment`, `FV6AgentOriDurableStateFragment`, …) so they
survive the ECS.

### Crossing a LOD boundary without popping

The reason a player never _feels_ an agent drop from a full pawn to a Mass
instance is that durable agent state lives in the **Ori**, not the pawn.
`FV6AgentOriDurableState` carries the Ori id, the current activity (id / tag /
phase / progress), the relationship LOD set, and the authoritative transform,
with explicit `PreservesIdentityWith()` / `PreservesActivityWith()` /
`PreservesRelationshipsWith()` / `PreservesTransformWith()` comparators (the
last with a 1 cm tolerance). `ValidateLODTransitionContinuity()` takes a from/to
transition and returns a `FV6AgentLODTransitionContinuityReport` that is
`bSeamless` only when the LOD actually changed _and_ both sides are Ori-backed
_and_ identity, activity, relationships, and transform are all preserved;
otherwise it lists the `ContinuityBreaks` by name. That guarantee is exactly
what `V6.Agent.LODBoundary.PreservesOriStateAcrossRenderLODs` asserts.

## Autonomous behavior at the body (`V6Agent` StateTree)

A budget without a behavior is useless, so `V6Agent` carries a real AI path.
Each frame the system has a cognition tier for an agent;
`UV6AgentBehaviorComponent::EvaluateBehavior(tier)` writes that tier into an
`FV6AgentBehaviorBlackboard` and runs an authored `UStateTree` through a real
`FStateTreeExecutionContext`. The tree's states are gated by
`FV6AgentTierCondition` (an enter-condition that passes when the blackboard tier
matches the state's `MatchTier`), and the matching state's
`FV6AgentSetBehaviorTask` writes the selected `EV6AgentBehavior` —
`FullDeliberation` for Clotho, `CoarsePlanning` for Lachesis, `AmbientRoutine`
for Atropos, `Idle` for Dormant. Crucially it **fails loud**: with no usable
StateTree assigned, `EvaluateBehavior` returns `EV6AgentBehavior::None` rather
than fabricating a behavior, which is what
`V6.Agent.Behavior.FailsLoudWithoutTree` pins (and
`V6.Agent.Behavior.SelectsPerCognitionTier` proves the real selection path, the
spec compiling a StateTree in-process). This is the believability floor the
[cognition stack](./cognition-stack-and-agent-behavior.md) drops to when a model
call is unavailable or over budget.

## Presence, cinematics, and the client edge

Two more client subsystems carry embodiment beyond the live pawn.
**`V6Cinematics`** is the runtime Sequencer hook:
`UV6CinematicsDirector::PlayAuthoredSequence()` instantiates a real
`ULevelSequencePlayer` for an authored `ULevelSequence`, tracks it by tag, and
tears it down on `StopPlaybacksByTag()` / `StopAllPlaybacks()` — this is the
playback half of Clio Chronicle replays, incarnation ceremonies, and Yemaya
memorial reels. It is honestly scoped: the header states that offline
MovieRenderQueue render-to-file is _not_ driven here, because it needs a live
GPU render unavailable in a headless `-nullrhi` context; the director owns
real-time playback and never fakes a render
(`V6.Cinematics.Director.PlaysAndManagesAuthoredSequences`). **VR presence** is
`V6Input`'s VR stewardship interaction plus `V6UI`'s VR comfort options,
validated by `V6.XR.OpenXRBackendMatrix` and
`V6.XR.VRComfort.OptionsPersistenceReview`, so a steward can be embodied
alongside their company in VR with comfort options that persist.

Underneath all of it, the client is a peer on one wire. The near pawn, the Mass
crowd, and the silhouette field are all rendered from interest-managed
`egbe-protocol` agent state delivered through the Realtime Gateway; the density
LOD planner's near/mid/far bands are the client-side consumer of the protocol's
interest windows and visible-agent caps. Fidelity is the only thing that moves —
identity, presence, and the authoritative world do not.

## How it composes

The client is the visible end of the mind/body/memory split. The body it renders
is owned by the
[world server and shard continuum](./world-server-and-shard-continuum.md) —
agent transforms, perception, and the 20 Hz tick that feeds the density
planner's distance / visibility inputs. The frames and the wire that carry that
state to a native UE client, a Pixel Streaming worker, or the Tier-2 three.js
fallback are
[the gateway, Pixel Streaming, and the web fallback](./gateway-pixel-streaming-and-web-fallback.md),
whose interest windows and per-surface agent caps the density LOD ladder mirrors
one-for-one. And the player's voice into an agent's ear — mic capture in
`V6Voice`, intent parsing, agent TTS played back on the pawn's
`UAudioComponent`, and squad comms — is
[the Vac communication pipeline](./vac-communication-pipeline.md). Across all
three, the rule holds: the UE5 client makes the Ori _legible_ — its dress, its
age, its mood, its attention — but it is never authoritative over who the Ori
is.
