Commit graph

7 commits

Author SHA1 Message Date
Kelsi
8f16a27253 feat(pipeline): add WOL preset variants for cave/dungeon/night
Three new single-keyframe WOL presets complement the
existing 4-keyframe day/night cycle from --gen-light:

  • --gen-light-cave    — dim cool ambient (0.05, 0.05, 0.07)
                          + heavy short-range fog (15..80)
                          for cave / mine interiors
  • --gen-light-dungeon — warm torchlit ambient (0.18, 0.14,
                          0.10) + medium fog (25..200) for
                          dungeon / crypt interiors
  • --gen-light-night   — cold blue ambient (0.06, 0.07, 0.12)
                          + moonlit directional + far fog
                          (80..500) for always-night zones

Each preset emits a single-keyframe WOL since enclosed /
fixed-time scenes don't vary with time-of-day. All three
share an emitLightPreset helper so adding more presets
(e.g. --gen-light-tundra, --gen-light-volcanic) is one
line of registration + a maker function.

All four WOL outputs validate clean under --validate-wol
(1 or 4 keyframe(s) valid).
2026-05-09 14:01:26 -07:00
Kelsi
dc29f7f135 feat(pipeline): add WOL validation + time-of-day sampling
Three additions to the Wowee Open Light format that landed
last commit:

  • WoweeLightLoader::sampleAtTime(light, timeMin) returns
    the linearly-interpolated keyframe at any time-of-day,
    correctly handling wrap-around between the last keyframe
    and the first (e.g. 21:00 blends from dusk toward
    midnight by going forward through 00:00).

  • --validate-wol <wol-base> [--json] walks every keyframe
    and reports structural problems: time bounds (must be
    [0, 1440)), strict-ascending sort order, fogEnd >
    fogStart, finite color components. Exit code 0 PASS /
    1 FAIL — CI-friendly.

  • --info-wol-at <wol-base> <HH:MM|minutes> samples the
    interpolated state at a specific time of day. Useful
    for previewing what the renderer would feed in at a
    given moment, debugging keyframe gaps, or previewing
    a sub-range of the cycle.

Smoke-tested: dawn-to-midnight blend at 03:00 yields a
plausible mid-fade ambient (0.18, 0.16, 0.15) and dusk-to-
midnight wrap at 21:00 yields the symmetric (0.19, 0.145,
0.14). The default 4-keyframe day/night cycle from
makeDefaultDayNight passes --validate-wol cleanly.
2026-05-09 13:54:57 -07:00
Kelsi
d58ee0af7d feat(pipeline): add Wowee Open Light (.wol) atmosphere format
New open replacement for WoW's Light.dbc / LightParams.dbc /
LightIntBand.dbc / LightFloatBand.dbc stack — a single .wol
file holds a list of time-of-day keyframes for one zone,
each capturing the ambient + directional + fog state at that
moment. The renderer interpolates between adjacent keyframes
by time-of-day.

Binary layout:
  magic[4] = "WOLA", version (uint32),
  nameLen + name bytes,
  keyframeCount + keyframes (each 13 floats + 1 uint32 time)

Per keyframe:
  • timeOfDayMin (0..1439 = minutes since midnight)
  • ambientColor.rgb, directionalColor.rgb, directionalDir.xyz
  • fogColor.rgb, fogStart, fogEnd

CLI:
  • --gen-light <wol-base> [zoneName] — emit a starter file
    with 4-keyframe day/night cycle (midnight/dawn/noon/dusk)
    using reasonable outdoor defaults
  • --info-wol <wol-base> [--json] — inspect: zone name +
    per-keyframe time-of-day + colors + fog distances

The 7th open-format addition to the Wowee pipeline:
  M2  → WOM (model)
  WMO → WOB (building)
  WMO collision → WOC
  ADT → WOT (terrain)
  DBC → JsonDBC
  BLP → PNG
  Light.dbc family → WOL  ← new

Smoke-tested round-trip: gen → info shows correct 4 keyframes
at 00:00 / 06:00 / 12:00 / 18:00 with the canonical color
ramps. JSON output for tooling integration.
2026-05-09 13:52:07 -07:00
Kelsi
3cf3b35885 refactor(editor): extract edge classification into cli_weld
The "loop over triangles, key edges by canonical-vertex pair,
count uses, classify boundary/manifold/non-manifold" pass was
duplicated across cli_mesh_info, cli_world_info, and the new
cli_audits watertight check. Hoist it into cli_weld as
classifyEdges(indices, canon) returning an EdgeStats struct
with boundary / manifold / nonManifold counters and a
watertight() convenience method.

All three callers verified byte-identical:
  • --info-mesh-stats firepit:    180 edges, watertight YES
  • --info-wob-stats cube:        18 manifold, watertight YES
  • --audit-watertight /tmp/...:  61 meshes, 12 failures, rc=12

About 60 more lines of duplication removed; classifyEdges +
buildWeldMap together form the complete reusable surface for
new weld/topology audit commands.
2026-05-09 11:15:31 -07:00
Kelsi
869124fa96 refactor(editor): extract vertex weld into shared cli_weld utility
Three callers were each open-coding the same quantize-and-
bucket pass over vertex positions: --info-mesh-stats,
--info-wob-stats, and --bake-wom-collision. Move the
implementation to cli_weld.{hpp,cpp} as buildWeldMap() and
have each caller pass a flat positions array.

Identical std::map-based exact-equality keying preserved
(unbalanced-hash collisions remain absent). All three call
sites verified to produce byte-identical output:

  • info-mesh-stats firepit: 240→80 verts, 0 boundary
  • info-wob-stats cube:     8→8 verts, watertight YES
  • bake-wom-collision tent: 18→6 verts, 8-tri WOC

About 75 lines of duplication removed; future weld-using
commands now opt in by including one header.
2026-05-09 11:05:54 -07:00
Kelsi
4112b6d257 feat(editor): --info-wob-stats + fix weld hash collision bug
Add --info-wob-stats reporting per-group + aggregate
triangle counts, surface area, edge analysis, and watertight
check for WOB buildings. Same flag surface as --info-mesh-stats
including --weld <eps> for true topological closure check
on per-face-vertex meshes.

Also fixes a correctness bug in the weld implementation
of --info-mesh-stats: the previous code used a 64-bit hash
of the quantized position as the equality key, which gave
false-positive collisions that incorrectly merged distinct
vertices. A unit cube's 8 corners collapsed to 2 positions
under the buggy hash. Replace with std::map keyed on the
actual quantized (qx, qy, qz) tuple so equality is exact.

Re-verified: cube 8→8 watertight YES; firepit 240→80
watertight YES (was wrongly reporting 56 unique with 48
non-manifold edges); tent_solid 18→6 watertight YES;
tent_fixed 21→9 with 5 boundary edges at the door perimeter
(correct — door is intentionally open).
2026-05-09 10:57:22 -07:00
Kelsi
5c350ee0af refactor(editor): extract WOB/WOT/WOC inspectors into cli_world_info.cpp
Moves the three open-format world-asset inspectors
(--info-wob, --info-wot, --info-woc) out of main.cpp into a
new cli_world_info.{hpp,cpp} module. Each prints a quick
structural summary (groups / portals / chunk counts /
triangles / bounds) without paying the full deserialization
cost a viewer would.

main.cpp shrinks by 144 lines (6,926 to 6,786). The
--copy-project handler that interleaved between --info-wob
and --info-wot stays inline -- it isn't an inspector and
belongs with project-mutation operations. All --json output
modes preserved.
2026-05-09 07:44:57 -07:00