# Model preview Ported from AVA2's render-target window (`EterLib/CRenderTarget*`, `GrpRenderTargetTexture.*`, `UserInterface/CPythonRenderTargetModule.cpp`, `root/uirendertarget.py`). The preview shows: - a wearable item on a character of every race that may wear it; - a monster, with its motions. It is client only; the server is not involved. ## Entry points | Where | How | |---|---| | Inventory | hold X and left-click the item | | NPC shop | hold X and left-click the item | | Wiki detail | the "模型预览" line, on weapon / armor / costume rows that have a model, and on every monster | Without X the original click path runs unchanged. The inventory wrapper chains onto `mt_mobile_inventory`'s, because `mt_preview` is installed after it. There is no touch entry other than the wiki. ## Window (`ScriptLib/ModelPreviewScript.inc`, module `mt_preview`) ### Item mode - `ItemSpec(vnum)` accepts: - a weapon other than an arrow → `SetWeapon`; - a body armor or a body costume → `SetArmor`; - a hair costume → `SetHair`, which reads value3. - The races are those not excluded by `ITEM_ANTIFLAG_{MALE,FEMALE,WARRIOR,ASSASSIN,SURA,SHAMAN}`, ordered warrior, assassin, sura, shaman, with male first. The window starts on the player's own race if it is allowed. - `<` / `>` step through the allowed races. ### Mob mode - `SelectModel(vnum)`. - Motion buttons: 待机 1, 攻击 13, 受击 5, 死亡 11, 技能1–4 33–36 (`InterceptOnceMotion`). ### Controls - Dragging with the left button turns the camera: absolute yaw/pitch from the press point, pitch clamped to ±60°. - 放大 / 缩小 change the zoom in 5 % steps, clamped to −8..+12. Windows have no wheel event. - 自动旋转 spins the model. - 复位 rebuilds the model, which also resets the camera. ### Lifetime - Render target index 1 is created at 300×330 when the window opens. - It is removed on close or Esc, and on every re-open before it is created again. `GameWindow.Close` also calls `renderTarget.Destroy()`. - The host frees the GPU image 120 frames after its last use. ## Native side - `renderTarget` Python module (`port/UserInterface/PythonRenderTargetModule.cpp`) → `CRenderTargetManager` → `CRenderTarget`, which owns a `CInstanceBase` and a `CGraphicRenderTargetTexture`. - `CPythonApplication` calls `DeformModels` / `UpdateModels` / `RenderModels` around the character manager. ### Port differences from AVA2 - Player races (0..7) are created as `TYPE_PC`. 40250 reserves the armor/weapon/hair parts only for PCs, so AVA2's `TYPE_NPC` crashed on the first `ChangeArmor`. - The model sits at the world origin, where culling hides it. Instead of AVA2's `SetAlwaysRender`, it is shown again before `Deform` and `Render`. Its text tail is detached. - The background quad is drawn inside the render target, in `RenderModel`. `RenderBackgrounds` is a no-op. - Wiki affects are not ported. - The texture has 1 mip level. ### Host (Vulkan) - A colour render target is `D3DFMT_X8R8G8B8` / `A8R8G8B8` with `D3DUSAGE_RENDERTARGET`, named `rt::x:c`. - It gets its own RGBA8 offscreen pass (pipeline key bits 29–30: 0 none, 1 shadow, 2 colour) and is cleared to black. - Shadow targets keep the previous path. - Side effect: SnowEnvironment's blur targets now take the colour path. Snow blur is off by default. ## Verification - `tests/wiki/test_model_preview.py` covers: - `ItemSpec` races and slots; - race stepping and fallback; - motions and zoom clamps; - drag; - reset; - the view drawing the target; - no live target after close, Esc, or 5 re-opens; - the inventory and shop wrappers; - wiki links and module order. - ctest 49/49. - Live, channel5 13040, macOS client, 2026-10-16: - armor 11299 on a warrior; - sword 19 after a 90°/20° turn; - wild dog 101 with the attack motion; - boss 691 zoomed in with spin. - The main scene was unchanged. - 120 open/close cycles (30 paced, plus 60 in one frame) did not grow RSS: 668 MB before, 593 MB after.