feat(chargen): Campaign CC slice CC6a — index→ObjDesc factory + preview renderer foundation
Delivers the CC6a foundation half of the chargen 3D preview: the missing index->ObjDesc appearance factory the campaign plan's acdream-seams section named, plus a static-pose offscreen renderer following PrivateEntityViewportRenderer's proven paperdoll/appraisal architecture. Page mount, spin/color-wheel controls, and rotate/zoom behavior stay out of scope per the CC4-parallel worktree contract (CC6b, after CC4 merges). Core (src/AcDream.Core/CharGen/, pure, no Chorizite on public surfaces): ChargenAppearanceFactory.TryCompose ports gmCG3DView::Update @0x004EE9D0's ObjDesc rebuild in its exact decompiled order - base body, hair style, clothing in retail's own Headgear/Trousers/Shirt/Footwear order (not the UI tab order or the wire's field order, both of which differ), eyes (bald-aware), nose, mouth, then the unconditional skin subpalette, hair color, eye color. ChargenPalSetMath ports PalSet::GetPaletteID's shade-to-index formula, cross-checked three ways (decomp control flow, ACE's PaletteSet.GetPaletteID "Taken from acclient.c" citation, ACViewer's identical slider math). ChargenPalSet/ChargenClothingTable are pure projections behind IChargenPalSetSource/IChargenClothingTableSource so the factory itself never touches a dat. Content (src/AcDream.Content/CharGen/): ChargenAppearanceCatalog is the cached dat-backed implementation of those two source interfaces, mirroring ChargenTableReader's no-leak discipline. App (src/AcDream.App/Rendering/): ChargenPreviewRenderer is a third facade over PrivateEntityViewportRenderer beside PaperdollViewportRenderer and CreatureAppraisalViewportRenderer - no existing rendering file touched. ChargenPreviewCamera carries the four retail-verbatim per-heritage eye profiles from gmCGAppearancePage::Update @0x0047E8F0 (cross-checked against ZoomIn/ZoomOut's identical literals) plus the recovered rotation (3.0 s/revolution) and zoom-tween (0.6 s, reconstructed from the decompiler's garbled float literals - the plan's own "measure if it matters" note is resolved, not garbled beyond recovery). Rotation applies to the character model, not the camera, per gmCGAppearancePage::DoRotation. ChargenPreviewEntityBuilder resolves Setup/GfxObj/Surface/Animation itself (there is no live entity yet), reusing DatLiveEntityProjectionMaterializer's surface-override algorithm and RetailPaperdollPoseApplicator's held-pose technique, generalized to chargen's per-heritage rest-pose DID. Two register rows filed: TS-83 (the plan-named CC6a static-pose-vs-retail- idle-loop staging, CC6b to retire) and TS-82 (measured, not assumed - the un-ported clothing Setup-substitution fallback chain costs nothing for the 9 standard heritages with clothing UI, but Undead's default gear choices genuinely lack ClothingBaseEffects coverage for Undead's own body Setup). Tests: ChargenPalSetMathTests, ChargenAppearanceFactoryTests (hand-built fixtures), ChargenAppearanceCatalogInstalledDatTests (installed-DAT sweep, all 26 heritage/gender combinations, zero missing PalSet/ClothingTable ids), ChargenPreviewCameraTests, ChargenPreviewEntityBuilderTests (installed-DAT-gated, proves a real 34-part Aluvian mesh resolves). Core.Tests 4767/1 skip, Content.Tests 146/0, App.Tests 5121/6 skips - all pre-existing skips, zero failures, full solution Release build green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3a6b7e3115
commit
55bfd9ca82
16 changed files with 2131 additions and 2 deletions
80
src/AcDream.App/Rendering/ChargenPreviewRenderer.cs
Normal file
80
src/AcDream.App/Rendering/ChargenPreviewRenderer.cs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.Core.Lighting;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
||||
/// <summary>
|
||||
/// Chargen-specific facade over the shared private creature viewport
|
||||
/// (<see cref="PrivateEntityViewportRenderer"/>) — CC6a's foundation half of
|
||||
/// the campaign plan's "chargen preview renderer" deliverable. Mirrors
|
||||
/// <see cref="PaperdollViewportRenderer"/>'s shape exactly, with a
|
||||
/// heading-capable <see cref="ChargenPreviewViewportCamera"/> in place of the
|
||||
/// paperdoll's fixed one.
|
||||
///
|
||||
/// <para>
|
||||
/// <b>NOT wired here (CC6b, after CC4 merges per the campaign's parallelism
|
||||
/// contract):</b> mounting into the authored Appearance/Summary viewport ids
|
||||
/// (<c>0x100003bb</c> / <c>0x10000406</c>), spin/color-wheel controls, and
|
||||
/// the rotate/zoom buttons. This class is a standalone, composition-root-
|
||||
/// agnostic renderer — nothing in <c>AcDream.App/UI/Layout/</c> or
|
||||
/// <c>RetailUiRuntime.cs</c> references it yet.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// <b>Register row (staged deviation, retired by CC6b):</b> retail plays a
|
||||
/// live 30fps idle loop in the preview
|
||||
/// (<c>gmCG3DView</c>'s <c>m_didAnimation</c>/<c>m_didAnimArray</c>,
|
||||
/// <c>set_sequence_animation</c>, distinct from the STATIC
|
||||
/// <c>m_didAnimationRest</c> this class's entity builder uses). CC6a holds
|
||||
/// the static rest-pose final frame only — see
|
||||
/// <c>docs/architecture/retail-divergence-register.md</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
internal sealed class ChargenPreviewRenderer :
|
||||
IUiViewportRenderer,
|
||||
IDisposable
|
||||
{
|
||||
private readonly PrivateEntityViewportRenderer _renderer;
|
||||
private readonly ChargenPreviewViewportCamera _camera;
|
||||
|
||||
internal ChargenPreviewRenderer(
|
||||
IWorldPassScope scope,
|
||||
AcDream.App.Rendering.Gpu.IGpuDevice device,
|
||||
ICurrentGpuFrameSource frames,
|
||||
WbDrawDispatcher dispatcher,
|
||||
SceneLightingUboBinding lightUbo,
|
||||
IEntityTextureLifetime textureLifetime,
|
||||
IWbMeshAdapter meshAdapter,
|
||||
uint heritageId = 0u)
|
||||
{
|
||||
_camera = new ChargenPreviewViewportCamera(heritageId);
|
||||
_renderer = new PrivateEntityViewportRenderer(
|
||||
scope,
|
||||
device,
|
||||
frames,
|
||||
dispatcher,
|
||||
lightUbo,
|
||||
textureLifetime,
|
||||
meshAdapter,
|
||||
ChargenPreviewEntityBuilder.PreviewRenderId,
|
||||
_camera,
|
||||
"chargen preview");
|
||||
}
|
||||
|
||||
public bool TextureIsBottomUp => _renderer.TextureIsBottomUp;
|
||||
|
||||
/// <summary>
|
||||
/// Re-derives the fixed per-heritage camera eye
|
||||
/// (<see cref="ChargenPreviewCamera.ResolveDefaultEye"/>) — call whenever
|
||||
/// the selected heritage changes, BEFORE the next <see cref="Render"/>.
|
||||
/// </summary>
|
||||
public void SetHeritage(uint heritageId) => _camera.SetHeritage(heritageId);
|
||||
|
||||
public void SetPreview(WorldEntity? entity) => _renderer.SetEntity(entity);
|
||||
|
||||
public uint Render(int width, int height) => _renderer.Render(width, height);
|
||||
|
||||
public void Dispose() => _renderer.Dispose();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue