using AcDream.App.Rendering.Wb; using AcDream.App.UI; using AcDream.Core.Lighting; using AcDream.Core.World; namespace AcDream.App.Rendering; /// /// CC6b-MOUNT: narrow seam mirroring IPaperdollDollRenderer so /// 's rebuild/render logic can be /// exercised with a fake in tests without a live GPU device. /// internal interface IChargenPreviewRenderer { void SetPreview(WorldEntity? entity); uint Render(int width, int height); } /// /// Chargen-specific facade over the shared private creature viewport /// () — CC6a's foundation half of /// the campaign plan's "chargen preview renderer" deliverable. Mirrors /// 's shape exactly, with a /// heading-capable in place of the /// paperdoll's fixed one. /// /// /// NOT wired here (CC6b page-mount half, after CC4 merges per the /// campaign's parallelism contract): mounting into the authored /// Appearance/Summary viewport ids (0x100003bb / 0x10000406) /// and binding the spin/color-wheel/rotate/zoom widgets to /// // /// . This class is a standalone, /// composition-root-agnostic renderer — nothing in /// AcDream.App/UI/Layout/ or RetailUiRuntime.cs references it /// yet. /// /// /// /// CC6b (pre-mount half): the preview now HAS a real live idle loop /// (, retail's m_didAnimation DID /// at 30fps via set_sequence_animation) instead of the CC6a-only held /// rest pose — TS-83 is retired. still accepts a /// static WorldEntity for callers that only want /// ChargenPreviewEntityBuilder.TryBuild's unchanged rest-pose /// snapshot; a caller that wants the animated preview constructs a /// from /// ChargenPreviewEntityBuilder.TryBuildAnimated and passes its /// Entity here once — the animator mutates that SAME entity's /// MeshRefs in place every Tick, and Render reads it /// fresh (no re-SetPreview needed per frame; see /// WorldEntity.MeshRefs's own "mutable so the animation tick can /// replace it each frame" doc comment). /// /// internal sealed class ChargenPreviewRenderer : IUiViewportRenderer, IChargenPreviewRenderer, 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, ChargenPreviewCamera? camera = null) { // CC6b-MOUNT: when a caller supplies its own camera instance (the // page-mount composition, which needs a SETTABLE camera for // ChargenPreviewZoomController to tween — see // ChargenPreviewController's own doc comment), wrap that exact // instance instead of building a private, unreachable one. _camera = camera is not null ? new ChargenPreviewViewportCamera(camera) : new ChargenPreviewViewportCamera(heritageId); _renderer = new PrivateEntityViewportRenderer( scope, device, frames, dispatcher, lightUbo, textureLifetime, meshAdapter, ChargenPreviewEntityBuilder.PreviewRenderId, _camera, "chargen preview"); } public bool TextureIsBottomUp => _renderer.TextureIsBottomUp; /// /// Re-derives the fixed per-heritage camera eye /// () — call whenever /// the selected heritage changes, BEFORE the next . /// 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(); }