Skip to content

RagnarokOnline: layer toggles, fog/light/sky overrides, music picker#913

Open
malvarezcastillo wants to merge 19 commits into
magcius:mainfrom
malvarezcastillo:ragnarok-follow-up
Open

RagnarokOnline: layer toggles, fog/light/sky overrides, music picker#913
malvarezcastillo wants to merge 19 commits into
magcius:mainfrom
malvarezcastillo:ragnarok-follow-up

Conversation

@malvarezcastillo
Copy link
Copy Markdown
Contributor

@malvarezcastillo malvarezcastillo commented May 25, 2026

Warning

The contributed code was written/generated largely by Opus 4.7

Follow-up to #911. Adds a Layers panel, fog/sun/sky overrides with share-URL persistence, and a music track picker.

@malvarezcastillo malvarezcastillo marked this pull request as ready for review May 25, 2026 08:54
@magcius
Copy link
Copy Markdown
Owner

magcius commented May 25, 2026

Please remove music picker and color picker to start with.

Comment thread src/ui.ts Outdated
export const VR_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" height="20" fill="white"><g><path d="M29,8H3A1,1,0,0,0,2,9V23a1,1,0,0,0,1,1H13a1,1,0,0,0,1-.83l.66-4A1.36,1.36,0,0,1,16,18a1.38,1.38,0,0,1,1.36,1.26L18,23.17A1,1,0,0,0,19,24H29a1,1,0,0,0,1-1V9A1,1,0,0,0,29,8ZM8.5,19A3.5,3.5,0,1,1,12,15.5,3.5,3.5,0,0,1,8.5,19Zm15,0A3.5,3.5,0,1,1,27,15.5,3.5,3.5,0,0,1,23.5,19Z"/></g></svg>`;
export const CUTSCENE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 142.448 142.448" height="20" fill="white"><g><path d="M142.411,68.9C141.216,31.48,110.968,1.233,73.549,0.038c-20.361-0.646-39.41,7.104-53.488,21.639 C6.527,35.65-0.584,54.071,0.038,73.549c1.194,37.419,31.442,67.667,68.861,68.861c0.779,0.025,1.551,0.037,2.325,0.037 c19.454,0,37.624-7.698,51.163-21.676C135.921,106.799,143.033,88.377,142.411,68.9z M111.613,110.336 c-10.688,11.035-25.032,17.112-40.389,17.112c-0.614,0-1.228-0.01-1.847-0.029c-29.532-0.943-53.404-24.815-54.348-54.348 c-0.491-15.382,5.122-29.928,15.806-40.958c10.688-11.035,25.032-17.112,40.389-17.112c0.614,0,1.228,0.01,1.847,0.029 c29.532,0.943,53.404,24.815,54.348,54.348C127.91,84.76,122.296,99.306,111.613,110.336z"></path> <path d="M94.585,67.086L63.001,44.44c-3.369-2.416-8.059-0.008-8.059,4.138v45.293 c0,4.146,4.69,6.554,8.059,4.138l31.583-22.647C97.418,73.331,97.418,69.118,94.585,67.086z"></path> </g></svg>`
export const EYE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" fill="none"><path d="M15.0007 12C15.0007 13.6569 13.6576 15 12.0007 15C10.3439 15 9.00073 13.6569 9.00073 12C9.00073 10.3431 10.3439 9 12.0007 9C13.6576 9 15.0007 10.3431 15.0007 12Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M12.0012 5C7.52354 5 3.73326 7.94288 2.45898 12C3.73324 16.0571 7.52354 19 12.0012 19C16.4788 19 20.2691 16.0571 21.5434 12C20.2691 7.94291 16.4788 5 12.0012 5Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
export const MUSIC_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="20" fill="white"><path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/></svg>`;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs an attribution in README.md. If it was AI-generated, that is a hard reject.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://raw.githubusercontent.com/google/material-design-icons/3.0.1/image/svg/production/ic_music_note_24px.svg

It's apache 2.0 but doesn't really matter since I'm collapsing all the submenus into one

The 400 @classic scene entries cluttered the map list while differing
only in entity manifest (NPCs, mobs, warps) from their renewal aliases.
Replace them with a single "Pre-Renewal Era" checkbox in the Layers
panel that swaps the entity layer in place without reloading the scene.

Era is a runtime selector backed by a ?era=classic query param. Toggling
refetches <id>@<era>.json, tears down sprite/shadow/dust/label/warp-portal
GPU state, and rebuilds with the new manifest. Camera, time of day,
layer toggles all stay put.
@malvarezcastillo malvarezcastillo marked this pull request as draft May 25, 2026 17:43
…s in DataShare

Adjacent maps reuse the same terrain BMPs, model RSMs, and water animation
frames. Routing those loads through a DataShare-backed cache eliminates the
fetch + decode + parse work on revisits and dedupes concurrent requests to
the same asset. The cache holds only CPU data (decoded RGBA / parsed meshes);
GfxTextures still live on the per-scene renderer and are recreated each load.
Previously each 8x8 GND lightmap tile became a layer of a 2DArray texture,
which capped maps at MAX_ARRAY_TEXTURE_LAYERS (256 guaranteed by WebGL2; up
to 2048 in practice). Large maps (WoE castles, prontera, dense outdoor
fields) blew past that and failed to allocate the texture. Pack tiles into
a square 2D atlas instead; the GND's reserved per-tile border means bilinear
sampling stays inside each tile, so the change is invisible to the renderer
beyond the UV remap. Vertex shrinks from 36 to 32 bytes (lightmap UV drops
the layer index).
… maps

Hash-diffing the pre-renewal kRO 2009 GRF against the current iRO renewal
GRF surfaced 22 maps where Gravity actually rebuilt RSW/GND/GAT geometry
(morocc, izlude, alberta, rachel, ...). Every model the pre-renewal RSWs
reference is still in the renewal GRF, so the existing model/texture pool
covers them; only the .rsw/.gnd/.gat triplets need staging as
<id>@classic-suffixed files.

regen-maps.ts now picks up <id>@classic.rsw siblings in the staged maps
dir and emits dedicated scene entries with a "(Pre-Renewal)" name suffix
and era: "classic". The scene loader overrides the RSW-embedded gnd
filename for these scenes so the era-suffixed GND loads instead of the
bare renewal one, and uses the bare base id for intra-map warp matching.

The global "Pre-Renewal Era" toggle is hidden on dedicated @classic
scenes because the scene id already pins the era. For the other ~378
entity-divergent maps with byte-identical geometry, the toggle continues
to do a live entity swap.
The "re-" prefix was misleading: there is no separate initial-generation
step, every invocation produces the same manifest from the staged inputs.
Renaming to gen-maps to match the verb used by the other tools in this dir
(extract-, audit-, validate-).
Manifest generation moved into its own tool (tools/gen-maps.ts) some
commits ago, but the original parseMapNameTable + writeMapManifest
implementation in extract.ts stuck around. Removing it along with the
ASSET_MAP_NAME_TABLE and OUT_MAP_MANIFEST constants it referenced.
The original asset dump predates Episode 15+ content (verus, lasagna,
ilusion, rockridge, ...) so hundreds of staged maps reference textures
and models that never made it into data/RagnarokOnline_raw/assets/. The
modern data.grf ships them, but it's the v0x300 "Event Horizon" variant
the existing iro-grf/ Python reader doesn't understand.

tools/grf.ts is a minimal TS reader for the v0x300 layout (u64 file-table
offset, lowercased CP949 paths, no per-entry encryption flags). It's a
small library, not a CLI.

tools/extract-from-grf.ts uses it to walk every staged map, parse the
referenced texture/model paths from its RSW/GND, and pull anything
missing out of the GRF into the assets/ tree. Re-runnable; only copies
what's actually absent. Run before tools/extract.ts when bringing in new
maps so the CDN-staging pass has a complete source tree.
- render.ts: hoist EntityLayerBundle out of the import block; use clamp
  from MathHelpers in serializeSaveState; document the fog/light magic
  constants. Name the arrival-framing camera offset; drop the unused
  `= null` defaults from the renderer constructor (every callsite already
  passes every argument).
- scenes.ts: fold the model and water-frame loaders into
  RagnarokSharedCache so the cross-scene cache owns all three asset
  kinds consistently; tighten a couple of let-without-type spots. Use
  clamp from MathHelpers for the fog density bound.
- entity.ts: drop the redundant `// default "npc"` comment on
  EntityPlacement.kind; the default is encoded by the `?? "npc"`
  coalesce on the render side.
- grf.ts: make the path field private.
- gnd.ts / extract.ts / extract-from-grf.ts: trim the case-sensitive
  CDN explanations to one canonical comment in gnd.ts; drop the
  macOS-specific note from the GRF top-up tool.
# Conflicts:
#	src/RagnarokOnline/render.ts
#	src/RagnarokOnline/scenes.ts
Each surface stored one ARGB tint but the orig engine paints each top-
quad corner with the surface color of the cell whose top-left lands on
that corner (self / right / down / down-right), and walls borrow the
matching neighbor colors. We were flooding the surface color across all
4 corners, so every tile edge showed a hard seam (visible as the
staircase rim around dark sinkholes in moc_fild21 etc.). Sample the
neighbors per corner so the GPU blends across boundaries.
@malvarezcastillo malvarezcastillo force-pushed the ragnarok-follow-up branch 4 times, most recently from fd4a576 to 3484ba1 Compare May 25, 2026 21:09
Comment thread src/RagnarokOnline/tools/extract.ts Outdated
Comment on lines +22 to +26
// data/RagnarokOnline_raw/assets/
// Bulk-extracted GRF contents. Use tools/iro-grf/ (Python) for the iRO
// GRF; any standard reader works for legacy/. Layout mirrors the GRF.
// Stages 2 and 5 read this (data/maps, graphics/{texture,model,sprite},
// data/misc/{mapnametable,fogparametertable}.txt).
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You now have a grf parser directly in here so do we still need the Python iro-grf extractor?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we dont, I'm going to modify read_grf.py with a --bulk-extract mode on Stage 1 so there's a single extractor.

probe_maps.py was an early prep script that nothing in the unified pipeline needs so it needs to be removed

Comment thread src/RagnarokOnline/tools/extract.ts Outdated
Comment on lines +28 to +34
// data/RagnarokOnline_raw/iro_effecttool/
// iRO effecttool .lub/.lua dump (per-map particle emitter specs). Side-
// channel client dump, not in the GRF.
//
// data/RagnarokOnline_raw/iro_eff_textures_all/
// Pre-extracted effect textures the .lub emitters reference. Stage 3
// copies the referenced subset into data/RagnarokOnline/textures/effect/.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the process to prepare these?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a subset of the iRO GRF's data/texture/effect/ tree, originally extracted separately.

so the call here is to fold the path back into the shared assets/graphics/texture/effect/ (same place Stage 2 reads textures from) and dropping the separate dir

Comment thread src/RagnarokOnline/tools/extract.ts Outdated
Comment on lines +41 to +43
// data/RagnarokOnline_raw/iro_tables/mapnametable.txt
// iRO English map name table. Stage 5 prefers this over the kRO Korean
// fallback at assets/data/misc/mapnametable.txt (which ships in the GRF).
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this retrieved from? Is it not in the iRO GRF?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in the iRO GRF at data\mapnametable.txt, modifying Stage 5 so it now pulls it through the inlined Grf reader, and iro_tables/ is dropped from the inputs list.

Comment thread src/RagnarokOnline/tools/extract.ts
The five stage scripts and the extract-all orchestrator collapse into
src/RagnarokOnline/tools/extract.ts. Bare CLI args are the map-id
filter; --only=stage1,stage2 (repeatable) restricts the run. The
previously-missing extract-from-grf stage is now wired in.
@malvarezcastillo malvarezcastillo marked this pull request as ready for review May 25, 2026 21:22
Stage 1 --bulk-extract dumps the whole iRO GRF (replaces tools/iro-grf/).
Stage 5 reads the iRO mapnametable from the GRF directly. Stage 3 takes
effect textures from assets/graphics/texture/effect alongside Stage 2.
Header documents the mingw + wine build for the Granny baked outputs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants