LibNUI Model Viewer

A ModelScene-backed 3D model viewer widget for LibNUI.

LibNUI ModelViewer

A ModelScene-backed 3D model viewer widget for LibNUI. Extracted from core LibNUI because it is a useful but uncommon component — only addons that actually present a 3D character / transmog viewer need to depend on it.

It registers itself as ui.Model (a.k.a. the LibNUI.Model global) on the shared LibNUI widget table, so consumers use it exactly like any other LibNUI widget.

Dependencies

  • LibNAddOn
  • LibNUI

Setup

List it in your .toc so it loads before your addon — its widget must exist on ui by the time your files run:

## Dependencies: LibNAddOn, LibNUI, LibNUI-ModelViewer

The widget is then available on the shared LibNUI table — as the LibNUI.Model global, or as ns.ui.Model inside a LibNAddOn-based addon:

---@type LibNUI
local ui = ns.ui
local Model = ui.Model

Model

Inherits Frame (backed by a ModelScene). A 3D viewer that borrows Blizzard's dressup scene (id 596) for camera + lighting + a skinnable player actor: set a body via a unit, an arbitrary race+gender (creature display ID), or a unit re-rendered as another race, then TryOn transmog appearance sources. Left-drag spins the model (actor yaw), right-drag pans the camera, and the mouse wheel zooms — pan and zoom glide smoothly (the camera eases them itself), and a left-drag flick keeps the model spinning and coasts to a stop. The dragged angle is preserved when you re-skin the actor (DisplayInfo/Unit), so swapping the body doesn't snap it back to front-facing; pan/zoom live on the camera and likewise persist across re-skins. Note a creature display only textures if it's a baked display (carries its own textures) — a bare base/ChrModel display renders white, since the engine can only composite textures for the active player's own race. All models render through one player-sized actor, so races at their natural size come out wildly inconsistent; use Aggressiveness to normalize every model toward a common (~human-male) size, with Scale as a user multiplier on top.

Constructor options

Option Type Description
rotateSpeed number Radians of yaw per screen pixel dragged (default 0.01)
facing number Initial yaw (radians) applied on load so the model faces the camera; re-skinned models pose side-on by default (default -math.rad(88))
minZoom number Closest the wheel may zoom in, in scene units (default 2). The borrowed scene pins zoom to 6–10, so this widens the usable range
maxZoom number Farthest the wheel may zoom out, in scene units (default 16)
spinFriction number How fast a left-drag flick decays to a stop (DeltaLerp amount per ideal frame; default 0.05 ≈ a 1s glide). Lower = longer coast
spinTracking number How closely the tracked throw speed follows the cursor while dragging (default 0.5). Smooths out spikes so carefully placing the model doesn't fling it on release

Methods

Method Description
DisplayInfo(id, useCustomizations?) Skin the actor with a creature display ID. useCustomizations defaults false — a baked display carries its own race+gender textures; passing true overlays the active player's customizations (only textures the player's own race)
Unit(token, customRaceID?, useNativeForm?) Skin from a unit (e.g. "player"), optionally rendered as another race (customRaceID = chrRaceID, keeps the unit's gender). useNativeForm (default true) is the unit's native vs altered form (Worgen human, Dracthyr visage) — only effective when the unit has an alternate form. autoDress is off
TryOn(source) Put on an item link or itemModifiedAppearanceID (sourceID)
Undress() / Dress() Strip / re-equip the actor's gear
UndressSlot(slot) Strip one equipment slot (inventory slot id) off the model, leaving the rest — the removal half of a paper-doll per-slot toggle (TryOn re-adds). Doesn't touch the remembered Outfit; re-set that so an async re-skin honors the change
Outfit(sources) Remember a transmog outfit (list of sourceIDs) and re-apply it after every async model (re)load. Use this instead of one-shot TryOn/Undress when re-skinning, since the load otherwise resets the actor to its baked default. Call before DisplayInfo/Unit.

Setting an outfit removes the slots the previous one occupied that the new one doesn't, so swapping to a shorter or different look in place takes the dropped pieces off — an empty list undresses every slot the last outfit held. Per-slot SlotTransmog overrides are left alone (ClearSlotTransmog owns those). Unchanged pieces are never touched, so nothing flickers. Use ForgetOutfit() when you want to drop the remembered list without undressing anything — e.g. before driving every slot through SlotTransmog yourself. The list is copied, so mutating your own copy afterwards doesn't change what the next model load re-applies
ForgetOutfit() Drop the remembered Outfit list without touching the actor — the model keeps rendering what it shows, but no re-skin re-applies the old list. Use it before driving every slot through SlotTransmog yourself; Outfit({}) is not the same thing, since an empty list now undresses every slot the previous outfit held
SlotTransmog(slot, appearanceID, opts?) Precisely set one slot's transmog, including the extras a bare TryOn can't express — an enchant illusion (opts.illusionID, weapon slots), a secondary appearance (opts.secondaryAppearanceID — split shoulders, or a Legion artifact's paired off-hand), and opts.ignoreChildItems (default true). Routes through SetItemTransmogInfo (the primitive Blizzard's dressing room uses), composes with Outfit/TryOn (re-applied last for its slot after each async re-skin), and remembers the override across re-skins.

appearanceID 0 does not bare the slot. It is NoTransmogID, which records "no override for this slot" rather than "wear nothing there" — the actor keeps whatever it was last given, so the piece goes on rendering. To genuinely empty a slot, use ClearSlotTransmog(slot) followed by UndressSlot(slot). Illusion preview: SlotTransmog(INVSLOT_MAINHAND, hostWeaponSourceID, { illusionID = sid }).

Two weapons at once (dual-wield, or a Titan's Grip pair of two-handers) work: the two weapon slots are undressed, then re-applied last in a fixed off-hand-then-main-hand order behind the actor's ResetNextHandSlot(), and the off hand is finally read back and re-placed by explicit hand if the main-hand write took it — which the engine does to any weapon in the off hand, though not to a shield or holdable. opts.secondaryAppearanceID is slot-dependent: on the shoulders it is a genuine second appearance (split shoulders), but on the main hand it is a discriminator, not an appearance-1 (Constants.Transmog.MainHandTransmogIsIndividualWeapon) for an ordinary weapon, 0 (MainHandTransmogIsPairedWeapon) for a Legion artifact whose off-hand is derived from it. Omitting it is not neutral: it defaults to NoTransmogID, which is also 0 = paired, so an ordinary main-hand weapon comes out flagged as half of a pair it has nothing to do with. Pass -1 for an ordinary weapon. This is a mislabelling trap and not the reason the off-hand gets clobbered — that happens whatever the discriminator says, and the read-back repair above handles it either way
ClearSlotTransmog(slot) Forget a slot's SlotTransmog override so later re-skins no longer re-apply it (the base Outfit/TryOn look governs that slot again). Doesn't restrip in place — follow with a fresh Outfit/Dress
Aggressiveness(n) Set the bounding-box normalization strength (0 = the model's natural size, default; 1 = forced to ~human-male size). A mid value (e.g. 0.5) keeps races consistent while preserving some racial size character. Remembered and re-applied after each async re-skin
Scale(n) Set the user scale multiplier on top of the normalized size (1 = the normalized size). Remembered and re-asserted every frame, so it survives an async re-skin's scale reset regardless of load timing
Spin(v) Get/set the model's rotation speed in radians/sec. A non-zero value spins it with the same inertia as a mouse flick (handy for a showcase spin); Spin(0) halts. No-arg reads the live speed
ResetView() Restore the view to its load defaults: the facing yaw (cancelling any spin), the scene's natural zoom, and no pan. Undoes the user's drag-rotate / right-drag pan / wheel-zoom. The user scale multiplier (Scale) is a separate control and is left untouched

The LibNUI Model Viewer Team

profile avatar
  • 20
    Projects
  • 6.0K
    Downloads

More from nazurakiView all

  • Warbandeer_Collected project image

    Warbandeer_Collected

    A transmog set tracker for your whole warband.

    • 93
    • July 28, 2026
  • Warbandeer project image

    Warbandeer

    Warband roster that tracks gear, weeklies, professions, achievements, and more across all your characters.

    • 1.4K
    • July 28, 2026
  • LibNUI project image

    LibNUI

    A library of UI classes wrapping Blizzard's frames and adding convenience and quality of life features.

    • 1.5K
    • July 28, 2026
  • LibNAddOn project image

    LibNAddOn

    Bootstrapper for addons. Provides common setup functionality such as database initialization, slash command handling, addon compartment click handling, settings setup and handling.

    • 1.2K
    • July 28, 2026
  • Warbandeer_Collected project image

    Warbandeer_Collected

    A transmog set tracker for your whole warband.

    • 93
    • July 28, 2026
  • Warbandeer project image

    Warbandeer

    Warband roster that tracks gear, weeklies, professions, achievements, and more across all your characters.

    • 1.4K
    • July 28, 2026
  • LibNUI project image

    LibNUI

    A library of UI classes wrapping Blizzard's frames and adding convenience and quality of life features.

    • 1.5K
    • July 28, 2026
  • LibNAddOn project image

    LibNAddOn

    Bootstrapper for addons. Provides common setup functionality such as database initialization, slash command handling, addon compartment click handling, settings setup and handling.

    • 1.2K
    • July 28, 2026