Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions native/shared/src/ffi_core/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,59 @@ macro_rules! __bloom_ffi_models {
0.0
}

// Array-free scene-geometry upload. Same motivation as the mesh scratch
// above: `bloom_scene_update_geometry` takes `i64` pointers, which
// Perry cannot produce from a `number[]`. Push the vertex floats and
// u32 indices through the mesh scratch, then re-upload them into an
// existing scene node (the editor's terrain re-meshes every brush
// stroke this way).
#[cfg(feature = "models3d")]
#[no_mangle]
pub extern "C" fn bloom_scene_update_geometry_scratch(
handle: f64,
vertex_count: f64,
index_count: f64,
) {
$crate::ffi::guard("bloom_scene_update_geometry_scratch", move || {
let taken = engine()
.models
.take_scratch_geometry(vertex_count as u32, index_count as u32);
let (vert_floats, indices) = match taken {
Some(v) => v,
None => return,
};
let nv = vertex_count as usize;
let mut vertices = Vec::with_capacity(nv);
for i in 0..nv {
let base = i * 12;
vertices.push($crate::renderer::Vertex3D {
position: [vert_floats[base], vert_floats[base + 1], vert_floats[base + 2]],
normal: [vert_floats[base + 3], vert_floats[base + 4], vert_floats[base + 5]],
color: [
vert_floats[base + 6],
vert_floats[base + 7],
vert_floats[base + 8],
vert_floats[base + 9],
],
uv: [vert_floats[base + 10], vert_floats[base + 11]],
joints: [0.0; 4],
weights: [0.0; 4],
tangent: [0.0; 4],
});
}
engine().scene.update_geometry(handle, vertices, indices);
})
}
#[cfg(not(feature = "models3d"))]
#[no_mangle]
pub extern "C" fn bloom_scene_update_geometry_scratch(
_handle: f64,
_vertex_count: f64,
_index_count: f64,
) {
$crate::ffi::feature_off_warn_once("bloom_scene_update_geometry_scratch", "models3d");
}

// bloom_get_model_mesh_count [source: linux; gated: models3d]
#[cfg(feature = "models3d")]
#[no_mangle]
Expand Down Expand Up @@ -599,6 +652,33 @@ macro_rules! __bloom_ffi_models {
0.0
}

// Array-free spline ribbon. Same reason as the mesh scratch above: the
// pointer form is unreachable from TypeScript (Perry won't pass a
// `number[]` into an i64 param). Push `point_count * 3` position floats
// followed by `width_count` width floats into the mesh scratch, then
// call this.
#[cfg(feature = "models3d")]
#[no_mangle]
pub extern "C" fn bloom_gen_mesh_spline_ribbon_scratch(point_count: f64, width_count: f64) -> f64 {
$crate::ffi::guard("bloom_gen_mesh_spline_ribbon_scratch", move || {
let n = point_count as usize;
let wn = width_count as usize;
let scratch = engine().models.scratch_floats();
if n < 2 || wn == 0 || scratch.len() < n * 3 + wn {
return 0.0;
}
let points: Vec<f32> = scratch[..n * 3].to_vec();
let widths: Vec<f32> = scratch[n * 3..n * 3 + wn].to_vec();
engine().models.gen_mesh_spline_ribbon(&points, &widths)
})
}
#[cfg(not(feature = "models3d"))]
#[no_mangle]
pub extern "C" fn bloom_gen_mesh_spline_ribbon_scratch(_point_count: f64, _width_count: f64) -> f64 {
$crate::ffi::feature_off_warn_once("bloom_gen_mesh_spline_ribbon_scratch", "models3d");
0.0
}

// bloom_stage_model [source: linux; gated: models3d]
#[cfg(feature = "models3d")]
#[no_mangle]
Expand Down
34 changes: 34 additions & 0 deletions native/shared/src/ffi_core/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,40 @@ macro_rules! __bloom_ffi_scene {
})
}

// bloom_scene_set_transform16 — all-f64 variant of set_transform.
//
// Perry 0.5.x refuses to pass a JS `number[]` into an `i64` pointer
// param ("Expected safe integer for native i64 parameter"), so the
// pointer-taking `bloom_scene_set_transform` above is unreachable from
// TypeScript. Meshes already dodge this via the scratch buffers; a
// 4x4 matrix is small enough to just spell out as 16 scalars, which
// keeps this stateless. Column-major, same layout as the ptr version.
#[no_mangle]
#[allow(clippy::too_many_arguments)]
pub extern "C" fn bloom_scene_set_transform16(
handle: f64,
m0: f64, m1: f64, m2: f64, m3: f64,
m4: f64, m5: f64, m6: f64, m7: f64,
m8: f64, m9: f64, m10: f64, m11: f64,
m12: f64, m13: f64, m14: f64, m15: f64,
) {
$crate::ffi::guard("bloom_scene_set_transform16", move || {
let s = [
m0, m1, m2, m3,
m4, m5, m6, m7,
m8, m9, m10, m11,
m12, m13, m14, m15,
];
let mut mat = [[0.0f32; 4]; 4];
for col in 0..4 {
for row in 0..4 {
mat[col][row] = s[col * 4 + row] as f32;
}
}
engine().scene.set_transform(handle, mat);
})
}

// bloom_scene_update_geometry [source: macos]
#[no_mangle]
pub extern "C" fn bloom_scene_update_geometry(
Expand Down
26 changes: 26 additions & 0 deletions native/shared/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,32 @@ impl ModelManager {
self.create_mesh(&verts, &inds)
}

/// Read-only view of the f32 scratch buffer, for consumers that lay their
/// own data out in it (the spline ribbon packs positions then widths).
pub fn scratch_floats(&self) -> &[f32] {
&self.scratch_f32
}

/// Take the scratch buffers as raw vertex floats + indices, for callers
/// that build something other than a Model out of them (the scene graph's
/// `update_geometry`). Same 12-floats-per-vertex layout as
/// `create_mesh_from_scratch`; returns None if the scratch is short.
pub fn take_scratch_geometry(
&self,
vertex_count: u32,
index_count: u32,
) -> Option<(Vec<f32>, Vec<u32>)> {
let need_f = vertex_count as usize * 12;
let need_u = index_count as usize;
if vertex_count == 0 || self.scratch_f32.len() < need_f || self.scratch_u32.len() < need_u {
return None;
}
Some((
self.scratch_f32[..need_f].to_vec(),
self.scratch_u32[..need_u].to_vec(),
))
}

pub fn load_model(&mut self, file_data: &[u8]) -> f64 {
match load_gltf(file_data) {
Some(model) => self.models.alloc(model),
Expand Down
40 changes: 40 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1979,6 +1979,38 @@
],
"returns": "void"
},
{
"name": "bloom_scene_set_transform16",
"params": [
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64",
"f64"
],
"returns": "void"
},
{
"name": "bloom_scene_update_geometry_scratch",
"params": [
"f64",
"f64",
"f64"
],
"returns": "void"
},
{
"name": "bloom_scene_set_lod",
"params": [
Expand Down Expand Up @@ -2131,6 +2163,14 @@
],
"returns": "f64"
},
{
"name": "bloom_gen_mesh_spline_ribbon_scratch",
"params": [
"f64",
"f64"
],
"returns": "f64"
},
{
"name": "bloom_load_render_texture",
"params": [
Expand Down
20 changes: 18 additions & 2 deletions src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ declare function bloom_set_directional_light(dx: number, dy: number, dz: number,
declare function bloom_set_procedural_sky(enabled: number, rayleighDensity: number, mieDensity: number, groundAlbedo: number): void;
declare function bloom_set_sun_direction(dx: number, dy: number, dz: number, intensity: number): void;
declare function bloom_gen_mesh_spline_ribbon(pointsPtr: number, pointCount: number, widthsPtr: number, widthCount: number): number;
declare function bloom_gen_mesh_spline_ribbon_scratch(pointCount: number, widthCount: number): number;
declare function bloom_get_model_mesh_count(handle: number): number;
declare function bloom_get_model_material_count(handle: number): number;
declare function bloom_get_model_bounds_min_x(handle: number): number;
Expand Down Expand Up @@ -201,9 +202,24 @@ export function unloadModel(model: Model): void {
* `widths` has one width per control point.
* Returns a Model whose mesh is a smooth triangle-strip ribbon.
*/
/**
* Build a ribbon mesh that follows a Catmull-Rom spline through `points`
* (flat x,y,z triples), with a per-point half-width from `widths`.
*
* Goes through the mesh scratch buffers — positions first, then widths — for
* the same reason as `createMesh`: Perry 0.5.x will not pass a `number[]` into
* an `i64` pointer param, which made the pointer form unreachable from TS.
*/
export function genMeshSplineRibbon(points: number[], widths: number[]): Model {
const pointCount = points.length / 3;
const handle = bloom_gen_mesh_spline_ribbon(points as any, pointCount, widths as any, widths.length);
const pointCount = Math.floor(points.length / 3);
const widthCount = widths.length;
if (pointCount < 2 || widthCount === 0) return makeModel(0);

bloom_mesh_scratch_reset();
for (let i = 0; i < pointCount * 3; i++) bloom_mesh_scratch_push_f32(points[i]);
for (let i = 0; i < widthCount; i++) bloom_mesh_scratch_push_f32(widths[i]);

const handle = bloom_gen_mesh_spline_ribbon_scratch(pointCount, widthCount);
return makeModel(handle);
}

Expand Down
60 changes: 47 additions & 13 deletions src/scene/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ declare function bloom_scene_set_cast_shadow(handle: number, cast: number): void
declare function bloom_scene_set_receive_shadow(handle: number, receive: number): void;
declare function bloom_scene_set_parent(handle: number, parent: number): void;
declare function bloom_scene_set_transform(handle: number, matrix: number): void;
// All-scalar transform + geometry entry points. Perry 0.5.x cannot pass a
// `number[]` into the i64 pointer params above, so these are the ones the
// wrappers actually call.
declare function bloom_scene_set_transform16(
handle: number,
m0: number, m1: number, m2: number, m3: number,
m4: number, m5: number, m6: number, m7: number,
m8: number, m9: number, m10: number, m11: number,
m12: number, m13: number, m14: number, m15: number,
): void;
declare function bloom_scene_update_geometry_scratch(
handle: number,
vertexCount: number,
indexCount: number,
): void;
declare function bloom_mesh_scratch_reset(): void;
declare function bloom_mesh_scratch_push_f32(v: number): void;
declare function bloom_mesh_scratch_push_u32(v: number): void;
declare function bloom_scene_set_trs(handle: number, px: number, py: number, pz: number, yaw: number, scale: number): void;
declare function bloom_scene_update_geometry(
handle: number,
Expand Down Expand Up @@ -203,14 +221,23 @@ export function setSceneNodeParent(handle: SceneNodeHandle, parent: SceneNodeHan
* Set the 4x4 transform matrix for a scene node.
* Matrix is in column-major order (same as Three.js/glm).
*
* NOTE: this crosses the FFI as an array-into-i64-pointer, which Perry
* 0.5.x rejects at runtime ("Expected safe integer for native i64
* parameter"). Until the scratch-buffer migration lands, prefer
* `setSceneNodeTrs` for position/yaw/scale placement — it is all-scalar
* and works on every Perry.
* Takes a column-major 16-element matrix, so arbitrary rotation and
* non-uniform scale are expressible (`setSceneNodeTrs` covers only
* position + yaw + uniform scale).
*
* The matrix crosses the FFI as 16 scalars rather than a pointer: Perry
* 0.5.x refuses to pass a `number[]` into an `i64` pointer param
* ("Expected safe integer for native i64 parameter"), which made the old
* pointer-based entry point unreachable from TypeScript.
*/
export function setSceneNodeTransform(handle: SceneNodeHandle, matrix: number[]): void {
bloom_scene_set_transform(handle, matrix as any);
bloom_scene_set_transform16(
handle,
matrix[0], matrix[1], matrix[2], matrix[3],
matrix[4], matrix[5], matrix[6], matrix[7],
matrix[8], matrix[9], matrix[10], matrix[11],
matrix[12], matrix[13], matrix[14], matrix[15],
);
}

/**
Expand All @@ -233,19 +260,26 @@ export function setSceneNodeTrs(
* @param vertices — Flat array of vertex data. Each vertex has 12 floats:
* [x, y, z, nx, ny, nz, r, g, b, a, u, v]
* @param indices — Flat array of triangle indices (3 per triangle).
*
* Uploads through the mesh scratch buffers (one scalar per FFI call) for the
* same reason as `createMesh`: Perry 0.5.x rejects `number[]` in an `i64`
* pointer param. Cost is linear in the mesh size, so this is for
* edit-time re-meshing (terrain sculpting), not per-frame streaming.
*/
export function updateSceneNodeGeometry(
handle: SceneNodeHandle,
vertices: number[],
indices: number[],
): void {
bloom_scene_update_geometry(
handle,
vertices as any,
vertices.length / 12,
indices as any,
indices.length,
);
const vertexCount = Math.floor(vertices.length / 12);
const indexCount = indices.length;
if (vertexCount === 0 || indexCount === 0) return;

bloom_mesh_scratch_reset();
const floatCount = vertexCount * 12;
for (let i = 0; i < floatCount; i++) bloom_mesh_scratch_push_f32(vertices[i]);
for (let i = 0; i < indexCount; i++) bloom_mesh_scratch_push_u32(indices[i]);
bloom_scene_update_geometry_scratch(handle, vertexCount, indexCount);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/world/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// - version.ts schema version + migration
// - validate.ts runtime schema validation
// - loader.ts loadWorld / instantiateWorld (reads JSON, spawns scene nodes)
// - render.ts spawnWaterVolume / spawnRiver (shared by games + editor)
// - saver.ts saveWorld / savePrefab (writes JSON)
// - prefab.ts loadPrefab / expandPrefab / cycle detection
// - terrain.ts buildHeightmapMesh / sampleHeight / raycastTerrain / defaultTerrain
Expand All @@ -17,6 +18,7 @@ export * from './types';
export * from './version';
export * from './validate';
export * from './loader';
export * from './render';
export * from './saver';
export * from './prefab';
export * from './terrain';
Loading
Loading