Skip to content

Scene FFI reachable from TS; water/rivers render; point lights become schema (v2)#90

Merged
proggeramlug merged 3 commits into
mainfrom
fix/scene-scalar-ffi
Jul 12, 2026
Merged

Scene FFI reachable from TS; water/rivers render; point lights become schema (v2)#90
proggeramlug merged 3 commits into
mainfrom
fix/scene-scalar-ffi

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Three related changes, all additive. The shooter compiles and links unchanged against every one of them.

1. setSceneNodeTransform / updateSceneNodeGeometry were unreachable from TypeScript

Both take their arrays through i64 pointer params, and Perry 0.5.x refuses to pass a number[] into an i64 ("Expected safe integer for native i64 parameter"). So the pointer-based entry points were dead from TS — as the doc comment on setSceneNodeTransform already admitted ("prefer setSceneNodeTrs until the scratch migration lands").

Meshes had long since routed around this via the bloom_mesh_scratch_* buffers; the scene-graph pair never got the same treatment because no game needed it — the shooter places nodes with the all-scalar setSceneNodeTrs. The world editor does need them: it applies full 4×4 matrices (arbitrary rotation, non-uniform scale — a boundary wall is 40×4×0.5) and re-meshes terrain on every brush stroke.

  • bloom_scene_set_transform16 — 16 matrix scalars, stateless, column-major.
  • bloom_scene_update_geometry_scratch — reuses the mesh scratch buffers.

The old pointer functions stay for ABI compatibility.

2. Water and rivers actually render

instantiateWorld pushed two "pending Q8/Q9" warnings instead of spawning water volumes and rivers. Everything needed had in fact shipped — the water material, the spline-ribbon generator — so the warnings were just stale.

New src/world/render.ts (spawnWaterVolume, spawnRiver) is called by both instantiateWorld and the editor's sync layer, so a river cannot render differently in the editor than in the game. The world schema stores colours as 0-1 floats while setSceneNodeWaterMaterial takes 0-255; that conversion now lives in exactly one place.

genMeshSplineRibbon turned out to be unreachable for the same i64 reason, so rivers were impossible regardless — fixed with bloom_gen_mesh_spline_ribbon_scratch.

3. Point lights become first-class schema (v2)

A light used to be an entity carrying userData.kind = "point_light" plus range/color/intensity strings — a private convention between one game and its baker. Anything that wasn't that game saw an entity with no model: the editor could not light its preview, and the next game would have re-invented the same convention.

Sun, ambient and fog were already first-class in environment. Point lights now sit beside them in a top-level lights: LightData[].

The line this draws, for the next schema question: lights are engine-universal — every renderer knows what a point light is — so they are schema. A spawner or a wave plan means nothing without the game, so it stays userData.

  • WORLD_SCHEMA_VERSION = 2, with a v1→v2 migration that lifts point_light entities into world.lights on load. Old worlds keep working untouched.
  • applyWorldLights(world) must be called every frame — the renderer clears its lighting block in begin_frame, the same reason games re-apply sun and ambient. Called once at load, a world is lit for exactly one frame.
  • validateWorld checks lights (duplicate ids, kind, vectors, numbers).

Note on InstantiateResult

Water/river handles are reported as arrays index-aligned with world.water / world.rivers, deliberately not Maps: the interface already carries one Map, and Perry 0.5.x miscompiles interfaces that declare more than one Map field (repro table in the editor's docs/perry-map-size-av.md).

Verification

Shooter builds and links clean. Editor's 62 self-tests pass, including the full v1→v2 migration path. Water and lights verified rendering on arena_02.

Summary by CodeRabbit

  • New Features

    • Added support for rendering and editing world water volumes and rivers.
    • Added top-level point lights to world data, including validation and migration of existing worlds.
    • Added shared world rendering utilities and exports.
    • Improved scene transform and geometry updates for native rendering.
    • Added spline ribbon mesh generation for rivers.
  • Bug Fixes

    • Water and river instances now load correctly and report their scene handles.
    • Empty or invalid mesh inputs are handled safely.

Ralph Kuepper added 3 commits July 11, 2026 16:29
…ble from TS

Both entry points take their arrays through i64 pointer params, which Perry
0.5.x refuses to accept from a `number[]` ("Expected safe integer for native
i64 parameter"). The pointer-based functions were therefore dead from
TypeScript, as the doc comment on setSceneNodeTransform already admitted.
Meshes had long since routed around this via the bloom_mesh_scratch_* buffers;
the scene-graph pair never got the same treatment because no game needed it —
the shooter places nodes with the all-scalar setSceneNodeTrs.

The world editor does need them: it applies full 4x4 matrices (arbitrary
rotation and non-uniform scale — a boundary wall is 40x4x0.5), and it re-meshes
terrain on every brush stroke.

Add two all-scalar entry points and route the wrappers through them:

- bloom_scene_set_transform16 — 16 matrix scalars, stateless, column-major.
- bloom_scene_update_geometry_scratch — reuses the mesh scratch buffers, with
  ModelManager::take_scratch_geometry to read them back out.

Additive: the old pointer functions stay for ABI compatibility, and the
shooter compiles and links unchanged.
instantiateWorld pushed two "pending Q8/Q9" warnings instead of spawning water
volumes and rivers. Everything needed had in fact shipped — the water material,
the spline-ribbon generator — so the warnings were just stale.

Add src/world/render.ts with spawnWaterVolume and spawnRiver, and call them
from instantiateWorld. The editor's sync layer calls the same two helpers, so a
river cannot render differently in the editor than in the game. The world
schema stores colours as 0-1 floats while setSceneNodeWaterMaterial takes
0-255; that conversion now lives in one place.

genMeshSplineRibbon turned out to be unreachable from TypeScript for the same
reason setSceneNodeTransform was — its arrays cross the FFI as i64 pointers.
Add bloom_gen_mesh_spline_ribbon_scratch, which reads the points and widths
back out of the mesh scratch buffers.

InstantiateResult reports waterHandles/riverHandles as arrays index-aligned
with world.water/world.rivers, deliberately not Maps: it already carries one
Map, and Perry 0.5.x miscompiles interfaces declaring more than one Map field.
A light used to be an entity carrying userData.kind = "point_light" plus
range/color/intensity strings — a private convention between one game and its
baker. Anything that wasn't that game saw an entity with no model: the editor
could not light its preview, and the next game would have re-invented the same
convention.

Sun, ambient, and fog were already first-class in `environment`. Point lights
now sit beside them in a top-level `lights: LightData[]`.

The line this draws, for the next schema question: lights are engine-universal
(every renderer knows what a point light is), so they are schema. A spawner or a
wave plan means nothing without the game, so it stays userData.

- WORLD_SCHEMA_VERSION = 2, with a v1 -> v2 migration that lifts point_light
  entities into world.lights on load. Old worlds keep working untouched.
- applyWorldLights(world) must be called every frame: the renderer clears its
  lighting block in begin_frame, the same reason games re-apply sun and ambient.
  Calling it once at load lights the world for exactly one frame.
- validateWorld checks lights (duplicate ids, kind, vectors, numbers).
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ca60fd5f-adf3-424c-b129-3a0e0a0015c3

📥 Commits

Reviewing files that changed from the base of the PR and between 5470d56 and 19bd1e1.

📒 Files selected for processing (12)
  • native/shared/src/ffi_core/models.rs
  • native/shared/src/ffi_core/scene.rs
  • native/shared/src/models.rs
  • package.json
  • src/models/index.ts
  • src/scene/index.ts
  • src/world/index.ts
  • src/world/loader.ts
  • src/world/render.ts
  • src/world/types.ts
  • src/world/validate.ts
  • src/world/version.ts

📝 Walkthrough

Walkthrough

Adds scratch-buffer FFI paths for geometry and spline ribbons, a scalar scene transform API, and world schema v2 support with point-light migration, validation, shared water/river rendering, and runtime instantiation.

Changes

Native FFI and Scratch Pipelines

Layer / File(s) Summary
Scratch buffer contracts and registrations
native/shared/src/models.rs, package.json, src/models/index.ts, src/scene/index.ts
Adds scratch-buffer accessors and registers the new native geometry, ribbon, and transform functions.
Scratch geometry and ribbon flows
native/shared/src/ffi_core/models.rs, src/models/index.ts, src/scene/index.ts
Routes spline ribbon generation and scene geometry updates through shared scratch buffers.
Scalar scene transform path
native/shared/src/ffi_core/scene.rs, src/scene/index.ts
Adds a 16-scalar column-major transform entry point and uses it for scene-node transforms.

World Schema and Rendering

Layer / File(s) Summary
World schema and light migration
src/world/types.ts, src/world/version.ts
Introduces schema v2 with top-level point lights and migrates legacy point-light entities into it.
World validation and defaults
src/world/validate.ts, src/world/loader.ts
Validates light records and updates empty-world defaults for schema v2 and the lights collection.
Shared world rendering helpers
src/world/render.ts, src/world/index.ts
Adds shared lighting, water-volume, river, and selection-rendering helpers and re-exports them.
Water and river world instantiation
src/world/loader.ts
Spawns water and river scene nodes during world loading, stores their handles, and records spawn failures.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorldLoader
  participant WorldRender
  participant Scene
  WorldLoader->>WorldRender: spawnWaterVolume or spawnRiver
  WorldRender->>Scene: create mesh and scene node
  WorldRender->>Scene: apply material and enable node
  WorldRender-->>WorldLoader: return scene-node handle
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/scene-scalar-ffi

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@proggeramlug proggeramlug merged commit ebf816f into main Jul 12, 2026
8 of 10 checks passed
@proggeramlug proggeramlug deleted the fix/scene-scalar-ffi branch July 12, 2026 07:27
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.

1 participant