Skip to content

cliffCatalog.ts has its own f64 sliderToLinear, disagreeing with the game-validated f32 form at 11 of 22 slider positions #324

Description

@wormeyman

src/noise/cliffs/cliffCatalog.ts exports its own sliderToLinear, computed
entirely in f64 and rounded once at the end. src/noise/eval/math.ts exports a
second one that rounds every operation to f32. cliffFields.ts is the only
consumer of the f64 copy; every other caller (moisture.ts, fulgoraScrap.ts,
fulgoraShared.ts) uses the f32 one.

The f32-per-operation form is the one that was measured against the game.
fulgora_grid is 175 - slider_to_linear(freq, -50, 50), sampled at five
slider positions on a real 2.1.14 surface: an f64 chain rounded once at the end
misses s = 3 by exactly one f32 ULP, and rounding every operation matches all
five. So the cliff copy is very likely wrong, and the port had to reproduce it.

Measured

The two forms compared after Math.fround, over the two ranges the cliff gate
reads. 11 of 22 slider positions differ.

range slider f64 form (cliffCatalog) f32 form (eval/math)
(-1, 1) 1/3 -0.61314719276545848 -0.61314713954925537
(-1, 1) 2/3 -0.22629438553091685 -0.22629439830780029
(-1, 1) 1.5 0.22629438553091674 0.22629439830780029
(-1, 1) 2 0.38685280723454163 0.38685274124145508
(-1, 1) 3 0.61314719276545837 0.61314713954925537
(-1.7, 1.7) 0.5 -0.65764977229872068 -0.65764969587326050
(-1.7, 1.7) 2/3 -0.38470045540255859 -0.38470047712326050
(-1.7, 1.7) 1 0 4.7683716530855236e-8
(-1.7, 1.7) 1.5 0.38470045540255859 0.38470059633255005
(-1.7, 1.7) 2 0.65764977229872090 0.65764981508255005
(-1.7, 1.7) 3 1.0423502277012793 1.0423500537872314

Largest gap 1.409e-7, at s = 1.5 on (-1.7, 1.7).

Why nothing has ever caught it

All three cliff fixtures (oracle-cliff-elevation, oracle-cliffiness,
oracle-rock-density) were captured at default settings. At s = 1 the two
forms agree exactly on (-1, 1) - both return 0.

They do not agree on (-1.7, 1.7), where the f32 form returns 4.768e-8
rather than 0. But the gate reads that range only inside

low_freq_lever = min(slider_to_linear(cliff_frequency, -1.7, 1.7),
                     slider_to_linear(cliff_richness,  -1,   1))

and at the defaults the min picks the (-1, 1) zero. So the one place the two
forms differ at the default is masked by an argument the min never chooses.

test/cliffCatalog.spec.ts asserts sliderToLinear(1, -1.7, 1.7) is 0 to 12
decimal places, which pins the f64 form - the game-validated f32 form would
fail that line.

What the Rust port did

crates/fmw-noise/src/cliffs/catalog.rs::cliff_slider_to_linear reproduces the
f64 form, deliberately, with a doc comment saying not to "fix" it there. A
unilateral change on the Rust side would read as a port bug in tier 2, which is
the whole point of having tier 2.

Settling it

Neither form is known to be the game's here. fulgora_grid is evidence about
slider_to_linear as an operation, which is a good prior but not a capture of
the cliff gate away from its defaults. A capture of cliffiness_nauvis at a
non-default nauvis_cliff frequency or richness would decide it directly.

Note the gate is discrete and forgiving: the closest of 2,048 fixture positions
sits 2.344e-4 from the cutoff, so a 1.4e-7 lever difference will not flip it.
cliff_elevation_nauvis does not read the lever at all. So a capture would need
to sample main_cliffiness or a sub-term rather than the gate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions