Mirrors the WOL JSON pair from the previous batch — same
hand-edit authoring loop for the binary .wow weather format:
• --export-wow-json <wow-base> [out.json]
Dumps a .wow to a human-readable JSON sidecar
(defaults to <base>.wow.json). Each entry includes
BOTH the raw typeId (0..6) and the human-friendly
type name ("clear" / "rain" / "snow" / "storm" /
"sandstorm" / "fog" / "blizzard").
• --import-wow-json <json-path> [out-base]
Reads a JSON sidecar and writes back binary .wow.
Accepts either typeId int OR type-name string
(typeId wins if both present). Schema mismatches
fail with a clear message.
Workflow: --gen-weather-* → --export-wow-json → hand-edit
weights / durations / intensities → --import-wow-json →
use in runtime.
Round-trip verified: elwynn.wow → elwynn.wow.json →
elwynn_rt.wow shows byte-identical entries via --info-wow.
Both atmosphere formats now have full JSON authoring support:
WOL: --export-wol-json / --import-wol-json
WOW: --export-wow-json / --import-wow-json
Convenience composite that drops both atmosphere.wol AND
atmosphere.wow into <zoneDir> in a single invocation, using
a paired light/weather preset:
--preset default → makeDefaultDayNight + makeTemperate
--preset arctic → makeNight + makeArctic
--preset desert → makeDefaultDayNight + makeDesert
--preset stormy → makeDefaultDayNight + makeStormy
--preset cave → makeCave + makeTemperate
The preset pairs are curated so the lighting and weather
match the zone climate (arctic uses always-night WOL +
arctic WOW; cave uses dim cave WOL + temperate WOW since
cave weather is rarely visible).
Useful as the canonical "give me a working atmosphere setup
for a fresh zone in one command" entrypoint. Both files
land at <zoneDir>/atmosphere.{wol,wow} where the runtime
loader can find them by convention.
Smoke-tested both default and arctic presets — both produce
files that pass --validate-wol and --validate-wow cleanly.
Two new commands enable a hand-edit authoring loop for the
binary .wol format:
• --export-wol-json <wol-base> [out.json]
Dumps a .wol to a human-readable JSON sidecar
(defaults to <base>.wol.json). Preserves every
keyframe's time, ambient/directional/fog colors,
directional vector, and fog distances.
• --import-wol-json <json-path> [out-base]
Reads a JSON sidecar and writes back binary .wol.
Validates schema strictly — missing keyframes /
wrong field types fail with a clear error message.
Workflow: --gen-light → --export-wol-json → hand-edit values
in any text editor → --import-wol-json → use in renderer.
Round-trip verified byte-for-byte identical on the existing
sunny.wol fixture: re-import produces the same 4 keyframes
with the same colors, fog distances, and zone name.
Mirrors --validate-wol but for the .wow weather format. Walks
every entry and reports structural problems:
• unknown weather typeId (above the Blizzard sentinel)
• intensity bounds outside [0, 1] or min > max
• non-positive weight (would zero a row in weighted-random
selection and warp probabilities)
• zero or inverted duration bounds
• non-finite floats
Returns exit code 0 on PASS / 1 on FAIL — CI-friendly. JSON
output via --json for tooling.
All four built-in presets (--gen-weather-temperate / arctic /
desert / stormy) validate clean. The WOW format now has
parity with WOL on the inspect/validate front:
WOL: --info-wol / --info-wol-at / --validate-wol
WOW: --info-wow / --validate-wow
Milestone: kArgRequired now at 440 documented flags.
8th open-format addition to the Wowee pipeline. Replaces
WoW's WeatherTypes.dbc / WeatherEffect logic with a single
binary file holding a list of weather states for one zone,
each tagged with intensity bounds, a probability weight,
and duration bounds. The renderer / runtime samples one
entry at a time using weighted-random selection, drives
it for a uniform-random duration in [min, max] sec, then
re-rolls.
• Types: Clear / Rain / Snow / Storm / Sandstorm / Fog /
Blizzard (extensible enum).
• Binary format: magic "WOWA", version 1, name, N entries
each storing (typeId, minIntensity, maxIntensity, weight,
minDurationSec, maxDurationSec).
CLI:
• --info-wow <wow-base> [--json] — inspect a WOW
• --gen-weather-temperate — clear + rain + fog (forest)
• --gen-weather-arctic — snow + blizzard + fog (tundra)
• --gen-weather-desert — clear + sandstorm (dunes)
• --gen-weather-stormy — rain + storm + occasional clear
The 8th open format complementing the rest:
M2 → WOM | WMO → WOB | WMO collision → WOC | ADT → WOT
DBC → JsonDBC | BLP → PNG | Light.dbc → WOL | WeatherTypes.dbc → WOW
Smoke-tested all 4 presets + JSON output. Each preset reads
back identically with the expected entry count and weight
distribution.
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).
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.
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.
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.
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.
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).
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.