Commit graph

320 commits

Author SHA1 Message Date
Kelsi
b491ecb435 fix(editor): call WMORenderer::prepareRender before render
Mirror the M2 fix: the editor was skipping the WMO renderer's per-frame
state advance, so material UBO updates and frame ID tracking were stale
relative to the main game's render flow. Most visible effect is that
material setting toggles wouldn't propagate to the GPU.
2026-05-06 01:49:32 -07:00
Kelsi
2a2c217ae3 feat(editor): auto-save fires for any unsaved change (objects, NPCs, quests)
Auto-save was gated on terrainEditor.hasUnsavedChanges() so a session
where the user only edited NPCs or quests would lose data on crash.
Added autoSavePendingChanges_ flag flipped by every objectsDirty_ = true
site (and markObjectsDirty), cleared by quickSave. Auto-save now fires
on either dirty signal.
2026-05-06 01:48:34 -07:00
Kelsi
552e0d22e2 feat(editor): export creature/faction/item DBCs alongside zone DBCs
Spawned NPCs reference CreatureDisplayInfo, CreatureModelData, faction
templates, etc. Without exporting these the JSON DBC pack only covered
terrain data and exported zones couldn't resolve creature display IDs
on a clean install. Added: CreatureDisplayInfo, CreatureModelData,
CreatureType, CreatureFamily, FactionTemplate, Faction, ItemDisplayInfo.
2026-05-06 01:41:53 -07:00
Kelsi
4fd285b5c4 feat(editor): collectWMOTextures recurses into WMO doodad M2 textures
WMO buildings reference M2 doodads (chairs, candles, banners) via the
MODD chunk. Their textures live in those M2 files, not the WMO root.
Now collectWMOTextures walks every doodad name and collects M2 textures
recursively so exported buildings include all their interior decoration
textures.
2026-05-06 01:40:44 -07:00
Kelsi
f1d332825e feat(editor): export WMO textures with the zone
Placed WMO buildings reference textures (walls, floors, decorations) that
were not being exported alongside the WOB files. Added collectWMOTextures()
which loads the root WMO + all group files and gathers every texture path,
then folds these into the same PNG export pass that handles terrain and M2
textures. Exported zones now have every texture they need across all model
types.
2026-05-06 01:40:05 -07:00
Kelsi
9eae67d574 feat(editor): NPC scatter snaps each spawn to ground when enabled
The scatter tool spawned creatures all at the cursor's z-height which
made them hover when scattered over uneven terrain. Added a Snap to
Ground checkbox (defaults to on) that raycasts each scattered NPC and
places it on the actual surface.
2026-05-06 01:35:26 -07:00
Kelsi
8daa81eecb feat(editor): transform gizmo works on selected NPCs
Selected NPCs now display the move/rotate/scale gizmo and respond to
drag operations. Rotate uses only the yaw axis (NPCs have a single
orientation field, not full euler rotation). Move/scale work the same
way as objects.
2026-05-06 01:31:34 -07:00
Kelsi
b49cb344ac feat(editor): NPC context menu adds Snap to Ground and Face Camera
Right-click context menu on a selected NPC now mirrors the object menu:
Snap to Ground drops it to terrain elevation, Face Camera rotates the NPC
to face the current camera position. Also annotates Duplicate with the
Ctrl+D shortcut hint.
2026-05-06 01:30:27 -07:00
Kelsi
ec50c41044 fix(sql): convert NPC orientation from degrees to radians for AzerothCore
The editor's orientation field is stored in degrees (matches the UI slider
and the M2 renderer's glm::radians() call), but AzerothCore's creature.
orientation column expects radians. Without conversion every exported NPC
faces the wrong direction in-game (off by 57x).
2026-05-06 01:28:46 -07:00
Kelsi
09f573c6ee feat(editor): patrol UI shows loop length and per-point wait time editor
The patrol list now shows total loop distance and gives each waypoint a
draggable wait-time field (0-60s). Helps tune patrol pacing without re-saving
the JSON manually.
2026-05-06 01:27:50 -07:00
Kelsi
700416b2c7 feat(editor): scale slider in selected-NPC editor
Once an NPC is placed there was no way to resize it without removing and
re-placing. Added a Scale DragFloat in the selected-creature editor with
the same 0.1-50 range used for placed objects.
2026-05-06 01:23:42 -07:00
Kelsi
7c4afdbca4 feat(editor): double-click in NPC/object list flies camera to entry
Single-click selects, double-click also flies to. Removes the trip back to
the side panel to hit the Fly To button when navigating long lists.
2026-05-06 01:22:44 -07:00
Kelsi
f39869ef6a docs(editor): document new shortcuts in F1 Keyboard Shortcuts dialog
Adds Ctrl+D duplicate, Ctrl+Wheel rotation, NPC click-to-select, and
W-for-waypoint to the help text so users discover the new bindings.
2026-05-06 01:17:57 -07:00
Kelsi
7d88c5f538 feat(editor): Ctrl+D duplicates the selected object or NPC at (10,10) offset 2026-05-06 01:17:25 -07:00
Kelsi
53b2fc78fa feat(editor): patrol path visualization closes the loop back to start 2026-05-06 01:14:58 -07:00
Kelsi
f097763875 feat(editor): click on existing NPC marker selects it instead of placing duplicate
Plain left-click within 4u of an existing NPC now selects that NPC rather
than dropping a new spawn on top. Shift+click forces placement at the cursor
even if it overlaps an existing NPC, preserving the rapid-placement workflow.
2026-05-06 01:13:43 -07:00
Kelsi
7fcc923d2e feat(editor): W hotkey adds patrol waypoint at cursor in NPC mode
When an NPC with Patrol behavior is selected in NPC mode, pressing W
appends a waypoint at the current cursor position. Faster than clicking
the panel button for laying out long routes.
2026-05-06 01:12:51 -07:00
Kelsi
4b3375ac44 feat(editor): export NPC/M2 model textures as PNG with the zone
TextureExporter::collectUsedTextures only picked up terrain textures, so
exported zones were missing every texture referenced by NPC creature models
and placed M2 doodads. Added collectM2Textures() and unified the export
collection to include terrain + all referenced M2 paths, so the rendered
zone is fully self-contained in the PNG/WOM open formats.
2026-05-06 01:11:47 -07:00
Kelsi
732e58355a feat(editor): convert NPC creature models to WOM during zone export
Previously only placed M2 objects were converted to the WOM open format.
NPC creature models stayed as references to game M2/skin files, which
meant exported zones still depended on proprietary Blizzard assets to
render their NPCs. Now the exporter walks both placed objects and NPC
spawns and emits a WOM for every unique M2 path, making zones fully
self-contained.
2026-05-06 01:09:38 -07:00
Kelsi
35ad340ccc feat(editor): facing arrow on NPC markers + Ctrl+Wheel hint in object panel
NPC markers now show a yellow ground-triangle pointing in the orientation
direction so users can see facing without selecting. Object panel gained
the Ctrl+Wheel rotation hint to match the NPC panel.
2026-05-06 01:08:32 -07:00
Kelsi
b736c6b2e1 feat(wom): add WOM3 multi-batch format for material-aware models
WOM1/WOM2 had a single mesh with one texture, which lost the multi-submesh
structure of complex M2 models (body+hair+eyes+armor each need different
textures and blend modes).

WOM3 adds a Batch array: each batch has indexStart/indexCount + a textureIndex
into texturePaths + blendMode + flags. Loader is fully backward compatible:
WOM1/WOM2 files still load, and WOM3 with no batches block falls back to a
single full-mesh batch. fromM2 now extracts batches with materials, and toM2
emits matching M2 batches so the renderer can draw them correctly.
2026-05-06 01:07:00 -07:00
Kelsi
00c078a9af feat(editor): snapSelectedToGround now also snaps NPCs and their patrol waypoints 2026-05-06 01:03:50 -07:00
Kelsi
03a863abe1 refactor(wom): extract WOM->M2 conversion to shared helper
Adds WoweeModelLoader::toM2() and tryLoadByGamePath() to deduplicate the
identical conversion code that lived in editor_viewport for both objects
and NPCs. Cuts ~70 lines of duplicated logic and makes WOM->M2 reusable
across the codebase.
2026-05-06 01:02:56 -07:00
Kelsi
eb8f5a09b1 feat(editor): visualize patrol path of selected NPC as ribbon with waypoints
Adds setPatrolPath() that draws a multi-segment orange ribbon between the
NPC's spawn position and each waypoint, plus diamond markers at each point
(green for start = NPC home, white for waypoints). Renders only while the
NPC is selected and has a patrol path defined.
2026-05-06 00:58:30 -07:00
Kelsi
191ff9ec16 feat(editor): NPC orientation control + Ctrl+Wheel rotates placement preview
Added orientation slider in NPC panel with random button. Ctrl+Wheel now
rotates the placement preview (objects and NPCs) instead of zooming —
Shift makes the step finer (5 deg vs 15 deg). Ghost preview now shows
the actual orientation that the placed NPC will have.
2026-05-06 00:56:19 -07:00
Kelsi
1c3307a0b6 fix(editor): unload ghost preview model when path changes
setGhostPreview reused modelId 59999 for every preview, but loadModel
returns true without doing anything when the ID is already cached. So
selecting a new NPC kept the old ghost model in GPU memory and createInstance
used the stale model. Added M2Renderer::unloadModel public API and call it
from clearGhostPreview.
2026-05-06 00:51:22 -07:00
Kelsi
ca630c4e87 chore(editor): remove debug logging now that NPC rendering works 2026-05-06 00:48:41 -07:00
Kelsi
687923c885 fix(editor): call M2Renderer::prepareRender to allocate mega bone slots before render
Without prepareRender(), animated NPC creature instances had megaBoneOffset=0
which caused the render loop to skip them (filtered out at the bones check).
This is why all loaded NPC M2s produced 0 draw calls despite valid GPU buffers,
material sets, and instance creation. Matches the main game's render flow.
2026-05-06 00:47:21 -07:00
Kelsi
e97d6d0c23 fix(editor): remove nested upload batch, add instance creation diagnostics 2026-05-06 00:16:25 -07:00
Kelsi
a845723635 fix(editor): create M2 instances AFTER upload flush (isValid fix)
ROOT CAUSE: createInstance() checks mdlRef.isValid() which requires
vertexBuffer != VK_NULL_HANDLE. But vertex buffers are uploaded via
staging and only finalized by waitAllUploads(). Instances were being
created BEFORE the upload flush, so vertexBuffer was still null,
cachedIsValid was set to false, and all instances were skipped during
render (0 draws despite loaded models).

Fix: split rebuildObjects into two phases:
1. Load all models (upload geometry to staging)
2. waitAllUploads + pollUploadBatches (finalize GPU buffers)
3. Create all instances (vertexBuffer is now valid, isValid() = true)

This matches the client's terrain_manager pattern where models are
loaded on background threads and instances created after finalization.
2026-05-06 00:11:44 -07:00
Kelsi
c9c4a15e9a fix(editor): remove per-model upload waits, single batch flush
Log showed: models=2, instances=3, draws=0 — models loaded and
instances created but zero draw calls. The M2 renderer skips
instances where cachedIsValid is false, which depends on the GPU
vertex buffer being valid after upload.

The per-model waitAllUploads/pollUploadBatches calls inside the
loading loop may have corrupted the upload batch context (started
at beginUploadBatch but flushed mid-loop). Now all models upload
in a single batch with one final waitAllUploads+pollUploadBatches
at the end of rebuildObjects.
2026-05-06 00:06:40 -07:00
Kelsi
c077ee2cee fix(editor): add per-frame M2 render diagnostics (models/instances/draws) 2026-05-06 00:02:02 -07:00
Kelsi
6f066dee48 fix(editor): debounce M2 rebuild to prevent clear+reload loop
ROOT CAUSE of NPC models not rendering: every NPC placement triggered
an immediate full clear+rebuild of ALL M2 models. During rapid clicking,
this created a destroy-reload cycle where models were cleared faster
than they could render — the log showed rebuild firing every ~200ms
with models loading OK but being destroyed before the next frame.

Fix: debounce rebuilds with a 0.5s timer. Multiple rapid placements
reset the timer, so the rebuild only fires once after the user stops
clicking. Models stay loaded and visible between placements.

Before: click → clear all → reload all → click → clear all → reload...
After:  click → click → click → (0.5s pause) → single rebuild
2026-05-05 23:59:51 -07:00
Kelsi
09e867eb07 fix(editor): move rebuildObjects AFTER beginFrame (instance SSBO fix)
ROOT CAUSE of NPCs not rendering: rebuildObjects() called createInstance()
BEFORE beginFrame(), causing instance SSBO writes to use the wrong frame
index. The M2 renderer writes instance transforms to a per-frame buffer
indexed by getCurrentFrame(), but the frame index isn't valid until after
beginFrame() returns.

This is the same bug documented in the project memory:
"M2 models not rendering (draws=0): update() was called before
beginFrame(), causing frame index mismatch in instance SSBO"

Models loaded correctly (log confirmed 2192v 7926i 8b) but instances
were invisible because their transform data was written to the wrong
frame buffer.

Fix: move the rebuild block after beginFrame(), alongside update().
2026-05-05 23:53:52 -07:00
Kelsi
f1133cdfa7 fix(editor): NPC diagnostics use WARNING level (INFO invisible in Release)
Release builds set default log level to WARNING — all the NPC loading
diagnostic messages were at LOG_INFO/LOG_DEBUG and completely invisible.
This is why the log showed zero NPC messages despite NPCs being placed.

All NPC loading messages now use LOG_WARNING:
- "NPC rebuild: N creatures to load" — confirms rebuild loop runs
- "NPC M2 OK: path (Nv Ni Nb)" — model loaded successfully
- "NPC loaded from WOM: path" — WOM format used
- "NPC model file not found: path" — file missing
- "NPC model invalid: path" — parse failed
- "NPC M2 loadModel failed: path" — GPU upload failed
- "NPC has empty modelPath: name" — no model selected
2026-05-05 23:49:46 -07:00
Kelsi
5506fd155b fix(editor): all NPC loading messages now WARNING level (were DEBUG)
NPC model loading diagnostics were at LOG_DEBUG level which doesn't
appear in the default log output. Changed all NPC model loading
messages to LOG_WARNING/LOG_INFO:
- "NPC model file not found" now WARNING (was DEBUG, invisible)
- "NPC has empty modelPath" new WARNING for missing model selection
- "Loading N NPC models..." at loop entry to confirm rebuild runs
- "NPC M2 loaded" at INFO level shows successful loads

This will reveal exactly where the NPC rendering pipeline fails.
2026-05-05 23:25:57 -07:00
Kelsi
ef04159b46 fix(editor): upgrade NPC model loading diagnostics to WARNING level
Changed NPC model invalid and load success messages from LOG_DEBUG to
LOG_WARNING/LOG_INFO so they appear in the log output. This helps
diagnose why specific creature models fail to render — the log will
show vertex/index/batch counts for each load attempt.
2026-05-05 23:06:34 -07:00
Kelsi
d773109b50 feat(editor): WMO-only instance loading (Dire Maul, dungeons, raids)
Many WoW instances (Dire Maul, Blackrock Depths, etc.) are WMO-only
maps with no ADT terrain tiles. The editor now handles these:

- loadWMOInstance(): reads WDT, detects WDTF_GLOBAL_WMO flag, loads
  the root WMO path from MWMO chunk, places it as an object with
  correct position/rotation from MODF chunk
- Automatic fallback: when loadADT fails to find tiles, tries
  WMO-only loading before showing error
- Load dialog: "Find Tile" detects WMO-only instances and shows
  "WMO-only instance — click Load to open" instead of "not found"
- Camera positioned near the WMO for immediate editing
- Blank terrain floor generated as ground reference
2026-05-05 22:44:38 -07:00
Kelsi
072d0f78c2 fix(editor): begin upload batch before M2 model loading in rebuild
rebuildObjects() calls clearObjects() which clears the M2 renderer,
but didn't start a new upload batch before loading models. The M2
renderer needs an active upload batch context to upload vertex/index
buffers to the GPU. Without it, loadModel may silently fail.

Now calls vkCtx_->beginUploadBatch() after clear and before the
model loading loop. Also adds diagnostic logging when loadModel
fails for NPCs (shows vertex/index/batch counts).
2026-05-05 22:40:58 -07:00
Kelsi
98223995fc fix(editor): placed objects also use WOM format + always load skin
Applied same two fixes from NPC renderer to placed object renderer:
1. Check WOM open format before M2 fallback (custom_zones/output dirs)
2. Always load skin file regardless of initial isValid state

Both placed objects (M2 doodads from ADT import or manual placement)
and NPC creatures now have consistent WOM→M2 fallback pipeline with
proper skin file loading.
2026-05-05 22:29:31 -07:00
Kelsi
3f5b030a4a feat(editor): NPC models load from WOM open format before M2 fallback
NPC creature renderer now checks for WOM open format models in
custom_zones/models/ and output/models/ before falling back to M2
game data files. This completes the WOM integration — both placed
objects (via terrain_manager) and NPC creatures (via editor viewport)
can render from the novel open format.

WOM→M2Model conversion includes bone weights, textures, render batch,
and material setup — same pipeline as the client-side WOM loader.

Loading priority: WOM (open format) → M2 + skin (game data)
2026-05-05 22:27:35 -07:00
Kelsi
47bfe35b26 fix(editor): always load M2 skin file for NPC models
WotLK M2 models store geometry in separate .skin files. The NPC
renderer was only loading skin files when M2Loader::load() returned
invalid (empty vertices). But some M2 files have vertices in the
header yet need the skin file for indices, batches, and submeshes.

Now always attempts to load the skin file regardless of initial
isValid() state. This fixes creature models not rendering even
when the M2 and skin files exist on disk.

Also improved debug logging to show vertex/index counts when models
fail to load, making it easier to diagnose remaining issues.
2026-05-05 22:26:10 -07:00
Kelsi
92787901d8 feat(editor): tile count button in ADT load dialog
"Count" button next to "Find Tile" scans all 64x64 tile coordinates
and shows how many ADT tiles exist for the selected map. Helps users
understand the scope of a map before loading individual tiles.
2026-05-05 22:23:18 -07:00
Kelsi
cc7ec8e497 fix(editor): clear previous state on ADT load, fix model orientations
Two bugs fixed:
- Loading a new ADT tile now clears all previous objects, NPCs, quests,
  path state, and terrain before loading. Was accumulating old state
  across multiple loads
- ADT doodad/WMO rotation conversion now matches client's transform:
  renderRotX = -adtRotZ, renderRotY = -adtRotX, renderRotZ = adtRotY+180
  Was copying raw ADT rotations without coordinate system conversion,
  causing models to appear at wrong orientations
2026-05-05 22:19:18 -07:00
Kelsi
e287cc9a78 fix(editor): shrink NPC markers, add show/hide toggle
- NPC markers reduced from 30-unit poles to 8-unit poles (was
  overwhelming and obscuring terrain). Base 1.5u, diamond top 1u
- "Show Position Markers" checkbox in NPC panel to toggle visibility
- Markers hidden when checkbox unchecked — useful when M2 creature
  models are rendering and markers are redundant
- Marker alpha reduced for less visual noise
2026-05-05 22:11:53 -07:00
Kelsi
d84ad82e26 fix(editor): recompute chunk positions after ADT coord override
When loading ADT tiles, the editor overrides terrain_.coord with the
filename-derived tile coordinates (instanced maps have arbitrary
internal values). But it wasn't recomputing the per-chunk world
positions to match, causing terrain to render at wrong coordinates.

Now recalculates all 256 chunk positions from the corrected tile
coordinates using the standard WoW formula:
  chunkX = (32 - tileX) * 533.33 - cx * 33.33
  chunkY = (32 - tileY) * 533.33 - cy * 33.33

This fixes terrain appearing at the wrong location in the editor
when loading instanced maps or tiles with mismatched internal coords.
2026-05-05 22:05:43 -07:00
Kelsi
9d14bc19cb fix(editor): log when NPC model files not found (helps debug rendering) 2026-05-05 21:59:51 -07:00
Kelsi
1db1166bec fix(editor): NPC markers now always render (pipeline binding bug)
NPC position markers were silently not rendering when no object was
selected because they relied on the gizmo's pipeline being bound, but
the gizmo only binds its pipeline when active. Now explicitly binds
the water pipeline (same pos+color vertex format with alpha blend)
before drawing NPC markers, ensuring they always appear regardless of
gizmo state.

Note: M2 creature models still require extracted game data files to
render. When model files aren't found, the colored markers (poles with
diamond tops) provide reliable visual feedback for NPC positions.
2026-05-05 21:58:32 -07:00
Kelsi
fde1fa8129 fix(editor): batch convert CLI has visible output and proper exit codes
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
- --convert-m2 and --convert-wmo now print progress and results to
  stdout (was LOG_INFO to log file only, invisible to user)
- Failures return exit code 1 (was always 0, breaking scripting)
- Success output shows vertex/bone counts (M2) or group count (WMO)
- Error messages go to stderr for proper pipe handling
2026-05-05 16:57:06 -07:00
Kelsi
1337a963a0 fix(editor): CLI output visible on stdout (was silent LOG_INFO)
--help, --version, and --list-zones now print to stdout via printf
instead of LOG_INFO which writes to log file only. Users running
CLI commands would see no output at all — critical first-impression
bug for new users.

Also updated --version format list to include WOC.
2026-05-05 16:54:03 -07:00