Scene FFI reachable from TS; water/rivers render; point lights become schema (v2)#90
Merged
Conversation
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).
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (12)
📝 WalkthroughWalkthroughAdds 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. ChangesNative FFI and Scratch Pipelines
World Schema and Rendering
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
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three related changes, all additive. The shooter compiles and links unchanged against every one of them.
1.
setSceneNodeTransform/updateSceneNodeGeometrywere unreachable from TypeScriptBoth take their arrays through
i64pointer params, and Perry 0.5.x refuses to pass anumber[]into ani64("Expected safe integer for native i64 parameter"). So the pointer-based entry points were dead from TS — as the doc comment onsetSceneNodeTransformalready 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-scalarsetSceneNodeTrs. 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
instantiateWorldpushed 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 bothinstantiateWorldand 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 whilesetSceneNodeWaterMaterialtakes 0-255; that conversion now lives in exactly one place.genMeshSplineRibbonturned out to be unreachable for the same i64 reason, so rivers were impossible regardless — fixed withbloom_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-levellights: 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 liftspoint_lightentities intoworld.lightson load. Old worlds keep working untouched.applyWorldLights(world)must be called every frame — the renderer clears its lighting block inbegin_frame, the same reason games re-apply sun and ambient. Called once at load, a world is lit for exactly one frame.validateWorldchecks lights (duplicate ids, kind, vectors, numbers).Note on
InstantiateResultWater/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 oneMapfield (repro table in the editor'sdocs/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
Bug Fixes