@bornengine/engine 0.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +231 -0
- package/native/android/Cargo.lock +1848 -0
- package/native/android/Cargo.toml +24 -0
- package/native/android/src/lib.rs +702 -0
- package/native/ios/Cargo.lock +1690 -0
- package/native/ios/Cargo.toml +32 -0
- package/native/ios/src/lib.rs +1267 -0
- package/native/linux/Cargo.lock +3279 -0
- package/native/linux/Cargo.toml +29 -0
- package/native/linux/src/lib.rs +1331 -0
- package/native/macos/Cargo.lock +3310 -0
- package/native/macos/Cargo.toml +46 -0
- package/native/macos/src/lib.rs +1302 -0
- package/native/shared/Cargo.lock +1899 -0
- package/native/shared/Cargo.toml +62 -0
- package/native/shared/assets/default_font.ttf +0 -0
- package/native/shared/build.rs +270 -0
- package/native/shared/shaders/common/clouds.wgsl +122 -0
- package/native/shared/shaders/common/fog.wgsl +16 -0
- package/native/shared/shaders/common/foliage_wind.wgsl +98 -0
- package/native/shared/shaders/common/imposter.wgsl +112 -0
- package/native/shared/shaders/common/pbr.wgsl +186 -0
- package/native/shared/shaders/common/shadows.wgsl +186 -0
- package/native/shared/shaders/common/sky.wgsl +8 -0
- package/native/shared/shaders/common/tonemap.wgsl +25 -0
- package/native/shared/shaders/impulse_field.wgsl +57 -0
- package/native/shared/shaders/material_abi.wgsl +383 -0
- package/native/shared/shaders/materials/test_minimal.wgsl +42 -0
- package/native/shared/src/anim_mixer.rs +61 -0
- package/native/shared/src/attach.rs +263 -0
- package/native/shared/src/audio/decode.rs +123 -0
- package/native/shared/src/audio/mod.rs +863 -0
- package/native/shared/src/audio/render.rs +892 -0
- package/native/shared/src/audio/spsc.rs +156 -0
- package/native/shared/src/audio/stream.rs +226 -0
- package/native/shared/src/custom_shaders.rs +104 -0
- package/native/shared/src/decals.rs +245 -0
- package/native/shared/src/drs.rs +211 -0
- package/native/shared/src/engine.rs +261 -0
- package/native/shared/src/ffi.rs +116 -0
- package/native/shared/src/ffi_core/assets.rs +388 -0
- package/native/shared/src/ffi_core/audio_ffi.rs +184 -0
- package/native/shared/src/ffi_core/draw.rs +334 -0
- package/native/shared/src/ffi_core/game_loop.rs +577 -0
- package/native/shared/src/ffi_core/input.rs +234 -0
- package/native/shared/src/ffi_core/mod.rs +127 -0
- package/native/shared/src/ffi_core/models.rs +1154 -0
- package/native/shared/src/ffi_core/ragdoll_ffi.rs +261 -0
- package/native/shared/src/ffi_core/scene.rs +626 -0
- package/native/shared/src/ffi_core/vfx.rs +212 -0
- package/native/shared/src/ffi_core/visual.rs +691 -0
- package/native/shared/src/frame_callbacks.rs +122 -0
- package/native/shared/src/geometry.rs +236 -0
- package/native/shared/src/handles.rs +182 -0
- package/native/shared/src/input.rs +448 -0
- package/native/shared/src/jolt_sys.rs +822 -0
- package/native/shared/src/lib.rs +55 -0
- package/native/shared/src/models.rs +1093 -0
- package/native/shared/src/models_gltf.rs +1280 -0
- package/native/shared/src/particles.rs +391 -0
- package/native/shared/src/physics_jolt.rs +1908 -0
- package/native/shared/src/picking.rs +298 -0
- package/native/shared/src/postfx.rs +345 -0
- package/native/shared/src/profiler.rs +492 -0
- package/native/shared/src/ragdoll.rs +474 -0
- package/native/shared/src/renderer/atmosphere_lut.rs +573 -0
- package/native/shared/src/renderer/brdf_lut.rs +154 -0
- package/native/shared/src/renderer/draw2d.rs +143 -0
- package/native/shared/src/renderer/formats.rs +822 -0
- package/native/shared/src/renderer/froxel.rs +421 -0
- package/native/shared/src/renderer/gi_bake.rs +653 -0
- package/native/shared/src/renderer/graph.rs +462 -0
- package/native/shared/src/renderer/hiz.rs +269 -0
- package/native/shared/src/renderer/hot_reload.rs +390 -0
- package/native/shared/src/renderer/impulse_field.rs +456 -0
- package/native/shared/src/renderer/lighting.rs +154 -0
- package/native/shared/src/renderer/material_instancing.rs +171 -0
- package/native/shared/src/renderer/material_pipeline.rs +700 -0
- package/native/shared/src/renderer/material_system.rs +1996 -0
- package/native/shared/src/renderer/material_system_tests.rs +601 -0
- package/native/shared/src/renderer/material_system_wasm.rs +41 -0
- package/native/shared/src/renderer/mod.rs +12556 -0
- package/native/shared/src/renderer/model_draw.rs +641 -0
- package/native/shared/src/renderer/occlusion.rs +429 -0
- package/native/shared/src/renderer/planar_pass.rs +593 -0
- package/native/shared/src/renderer/planar_reflection.rs +499 -0
- package/native/shared/src/renderer/post_pass.rs +249 -0
- package/native/shared/src/renderer/postfx_chain.rs +728 -0
- package/native/shared/src/renderer/pt_pass.rs +577 -0
- package/native/shared/src/renderer/scene_pass.rs +607 -0
- package/native/shared/src/renderer/shader_include.rs +205 -0
- package/native/shared/src/renderer/shader_library.rs +135 -0
- package/native/shared/src/renderer/shaders/ao.rs +570 -0
- package/native/shared/src/renderer/shaders/core.rs +1243 -0
- package/native/shared/src/renderer/shaders/env.rs +907 -0
- package/native/shared/src/renderer/shaders/gi.rs +810 -0
- package/native/shared/src/renderer/shaders/mod.rs +19 -0
- package/native/shared/src/renderer/shaders/post.rs +1558 -0
- package/native/shared/src/renderer/shaders/pt.rs +1859 -0
- package/native/shared/src/renderer/shaders/ssgi.rs +1586 -0
- package/native/shared/src/renderer/shadow_pass.rs +731 -0
- package/native/shared/src/renderer/ssgi_pass.rs +392 -0
- package/native/shared/src/renderer/ssr_pass.rs +188 -0
- package/native/shared/src/renderer/texture_store.rs +473 -0
- package/native/shared/src/renderer/transient.rs +591 -0
- package/native/shared/src/renderer/types.rs +941 -0
- package/native/shared/src/renderer/util.rs +152 -0
- package/native/shared/src/scene.rs +1362 -0
- package/native/shared/src/sdf_cache.rs +274 -0
- package/native/shared/src/shadows.rs +1036 -0
- package/native/shared/src/staging.rs +102 -0
- package/native/shared/src/string_header.rs +266 -0
- package/native/shared/src/text_renderer.rs +502 -0
- package/native/shared/src/textures.rs +197 -0
- package/native/tvos/Cargo.lock +1693 -0
- package/native/tvos/Cargo.toml +36 -0
- package/native/tvos/metal-patched/Cargo.toml +178 -0
- package/native/tvos/metal-patched/LICENSE-APACHE +201 -0
- package/native/tvos/metal-patched/LICENSE-MIT +25 -0
- package/native/tvos/metal-patched/src/acceleration_structure.rs +667 -0
- package/native/tvos/metal-patched/src/acceleration_structure_pass.rs +108 -0
- package/native/tvos/metal-patched/src/argument.rs +366 -0
- package/native/tvos/metal-patched/src/blitpass.rs +102 -0
- package/native/tvos/metal-patched/src/buffer.rs +71 -0
- package/native/tvos/metal-patched/src/capturedescriptor.rs +76 -0
- package/native/tvos/metal-patched/src/capturemanager.rs +113 -0
- package/native/tvos/metal-patched/src/commandbuffer.rs +192 -0
- package/native/tvos/metal-patched/src/commandqueue.rs +44 -0
- package/native/tvos/metal-patched/src/computepass.rs +107 -0
- package/native/tvos/metal-patched/src/constants.rs +152 -0
- package/native/tvos/metal-patched/src/counters.rs +119 -0
- package/native/tvos/metal-patched/src/depthstencil.rs +190 -0
- package/native/tvos/metal-patched/src/device.rs +2134 -0
- package/native/tvos/metal-patched/src/drawable.rs +39 -0
- package/native/tvos/metal-patched/src/encoder.rs +2041 -0
- package/native/tvos/metal-patched/src/heap.rs +281 -0
- package/native/tvos/metal-patched/src/indirect_encoder.rs +344 -0
- package/native/tvos/metal-patched/src/lib.rs +657 -0
- package/native/tvos/metal-patched/src/library.rs +902 -0
- package/native/tvos/metal-patched/src/mps.rs +575 -0
- package/native/tvos/metal-patched/src/pipeline/compute.rs +475 -0
- package/native/tvos/metal-patched/src/pipeline/mod.rs +71 -0
- package/native/tvos/metal-patched/src/pipeline/render.rs +762 -0
- package/native/tvos/metal-patched/src/renderpass.rs +443 -0
- package/native/tvos/metal-patched/src/resource.rs +182 -0
- package/native/tvos/metal-patched/src/sampler.rs +165 -0
- package/native/tvos/metal-patched/src/sync.rs +178 -0
- package/native/tvos/metal-patched/src/texture.rs +352 -0
- package/native/tvos/metal-patched/src/types.rs +90 -0
- package/native/tvos/metal-patched/src/vertexdescriptor.rs +250 -0
- package/native/tvos/src/audio_backend.rs +197 -0
- package/native/tvos/src/lib.rs +1891 -0
- package/native/visionos/Cargo.lock +1693 -0
- package/native/visionos/Cargo.toml +40 -0
- package/native/visionos/src/audio_backend.rs +197 -0
- package/native/visionos/src/lib.rs +1887 -0
- package/native/watchos/Cargo.lock +16 -0
- package/native/watchos/Cargo.toml +19 -0
- package/native/watchos/shaders/bloom_postfx.metal +99 -0
- package/native/watchos/src/BloomWatchApp.swift +1267 -0
- package/native/watchos/src/BloomWatchAudio.swift +179 -0
- package/native/watchos/src/audio.rs +55 -0
- package/native/watchos/src/draw_list.rs +229 -0
- package/native/watchos/src/ffi_stubs.rs +915 -0
- package/native/watchos/src/ffi_stubs_manual.rs +35 -0
- package/native/watchos/src/lib.rs +1124 -0
- package/native/watchos/src/models.rs +746 -0
- package/native/watchos/src/postfx.rs +95 -0
- package/native/watchos/src/scene.rs +534 -0
- package/native/watchos/src/textures.rs +184 -0
- package/native/web/Cargo.lock +1657 -0
- package/native/web/Cargo.toml +43 -0
- package/native/web/bloom_glue.js +695 -0
- package/native/web/build.sh +131 -0
- package/native/web/index.html +35 -0
- package/native/web/jolt_bridge.js +1519 -0
- package/native/web/src/input_ffi.rs +286 -0
- package/native/web/src/lib.rs +1796 -0
- package/native/web/src/material_ffi.rs +710 -0
- package/native/web/src/parity_ffi.rs +343 -0
- package/native/web/src/physics_ffi.rs +643 -0
- package/native/web/src/ragdoll_ffi.rs +250 -0
- package/native/web/src/render_settings.rs +98 -0
- package/native/windows/Cargo.lock +1815 -0
- package/native/windows/Cargo.toml +68 -0
- package/native/windows/src/lib.rs +1486 -0
- package/package.json +4279 -0
- package/src/audio/index.ts +315 -0
- package/src/core/colors.ts +63 -0
- package/src/core/index.ts +1206 -0
- package/src/core/keys.ts +63 -0
- package/src/core/types.ts +104 -0
- package/src/index.ts +171 -0
- package/src/math/index.ts +516 -0
- package/src/mobile/index.ts +294 -0
- package/src/models/index.ts +1258 -0
- package/src/physics/index.ts +1134 -0
- package/src/scene/index.ts +698 -0
- package/src/shapes/index.ts +120 -0
- package/src/text/index.ts +48 -0
- package/src/textures/index.ts +187 -0
- package/src/vfx/index.ts +191 -0
- package/src/world/index.ts +24 -0
- package/src/world/loader.ts +423 -0
- package/src/world/prefab.ts +217 -0
- package/src/world/render.ts +172 -0
- package/src/world/saver.ts +108 -0
- package/src/world/serialize.ts +301 -0
- package/src/world/terrain.ts +355 -0
- package/src/world/types.ts +160 -0
- package/src/world/validate.ts +319 -0
- package/src/world/version.ts +114 -0
|
@@ -0,0 +1,1154 @@
|
|
|
1
|
+
//! 3D model + mesh surface (gated on the `models3d` cargo feature).
|
|
2
|
+
//!
|
|
3
|
+
//! Section of [`define_core_ffi!`](crate::define_core_ffi) — see
|
|
4
|
+
//! `ffi_core/mod.rs` for the architecture and the invoking-crate contract.
|
|
5
|
+
//! Internal: platform crates must invoke `define_core_ffi!()`, never the
|
|
6
|
+
//! section macros directly.
|
|
7
|
+
|
|
8
|
+
#[doc(hidden)]
|
|
9
|
+
#[macro_export]
|
|
10
|
+
macro_rules! __bloom_ffi_models {
|
|
11
|
+
() => {
|
|
12
|
+
|
|
13
|
+
// bloom_load_model [source: curated; gated: models3d]
|
|
14
|
+
#[cfg(feature = "models3d")]
|
|
15
|
+
#[no_mangle]
|
|
16
|
+
pub extern "C" fn bloom_load_model(path_ptr: *const u8) -> f64 {
|
|
17
|
+
$crate::ffi::guard("bloom_load_model", move || {
|
|
18
|
+
let path = $crate::string_header::str_from_header(path_ptr);
|
|
19
|
+
let path = bloom_resolve_asset_path(path);
|
|
20
|
+
match std::fs::read(path.as_ref()) {
|
|
21
|
+
Ok(data) => {
|
|
22
|
+
let mut eng = engine();
|
|
23
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
24
|
+
models.load_model_with_textures(&data, renderer)
|
|
25
|
+
}
|
|
26
|
+
Err(_) => 0.0,
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
#[cfg(not(feature = "models3d"))]
|
|
31
|
+
#[no_mangle]
|
|
32
|
+
pub extern "C" fn bloom_load_model(_path_ptr: *const u8) -> f64 {
|
|
33
|
+
$crate::ffi::feature_off_warn_once("bloom_load_model", "models3d");
|
|
34
|
+
0.0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// bloom_draw_model [source: linux; gated: models3d]
|
|
38
|
+
#[cfg(feature = "models3d")]
|
|
39
|
+
#[no_mangle]
|
|
40
|
+
pub extern "C" fn bloom_draw_model(handle: f64, x: f64, y: f64, z: f64, scale: f64, r: f64, g: f64, b: f64, a: f64) {
|
|
41
|
+
$crate::ffi::guard("bloom_draw_model", move || {
|
|
42
|
+
let mut eng = engine();
|
|
43
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
44
|
+
if let Some(model) = models.get(handle) {
|
|
45
|
+
let tint = [(r / 255.0) as f32, (g / 255.0) as f32, (b / 255.0) as f32, (a / 255.0) as f32];
|
|
46
|
+
let position = [x as f32, y as f32, z as f32];
|
|
47
|
+
let handle_bits = handle.to_bits();
|
|
48
|
+
if renderer.cache_model_if_static(handle_bits, &model.meshes) {
|
|
49
|
+
// Skinned models cache too now (bind-pose VB with raw
|
|
50
|
+
// joint indices, skinned in the scene VS) — routed to
|
|
51
|
+
// the skinned cached draw, which pops the staged pose
|
|
52
|
+
// ONCE for the whole model and shares the offset
|
|
53
|
+
// across every primitive (per-primitive pops starved
|
|
54
|
+
// multi-primitive models onto joint offset 0 —
|
|
55
|
+
// another model's matrices).
|
|
56
|
+
if renderer.is_model_skinned(handle_bits) {
|
|
57
|
+
renderer.draw_model_cached_skinned(handle_bits, position, scale as f32, tint);
|
|
58
|
+
} else {
|
|
59
|
+
renderer.draw_model_cached(handle_bits, position, scale as f32, tint);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
#[cfg(not(feature = "models3d"))]
|
|
66
|
+
#[no_mangle]
|
|
67
|
+
pub extern "C" fn bloom_draw_model(_handle: f64, _x: f64, _y: f64, _z: f64, _scale: f64, _r: f64, _g: f64, _b: f64, _a: f64) {
|
|
68
|
+
$crate::ffi::feature_off_warn_once("bloom_draw_model", "models3d");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// bloom_draw_model_rotated [source: linux; gated: models3d]
|
|
72
|
+
#[cfg(feature = "models3d")]
|
|
73
|
+
#[no_mangle]
|
|
74
|
+
pub extern "C" fn bloom_draw_model_rotated(
|
|
75
|
+
handle: f64, x: f64, y: f64, z: f64,
|
|
76
|
+
scale: f64, rot_y: f64,
|
|
77
|
+
color_packed_argb: f64,
|
|
78
|
+
) {
|
|
79
|
+
$crate::ffi::guard("bloom_draw_model_rotated", move || {
|
|
80
|
+
let bits = color_packed_argb as u32;
|
|
81
|
+
let a = ((bits >> 24) & 0xff) as f32 / 255.0;
|
|
82
|
+
let r = ((bits >> 16) & 0xff) as f32 / 255.0;
|
|
83
|
+
let g = ((bits >> 8) & 0xff) as f32 / 255.0;
|
|
84
|
+
let b = ( bits & 0xff) as f32 / 255.0;
|
|
85
|
+
let mut eng = engine();
|
|
86
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
87
|
+
if let Some(model) = models.get(handle) {
|
|
88
|
+
let position = [x as f32, y as f32, z as f32];
|
|
89
|
+
let scale = scale as f32;
|
|
90
|
+
let tint = [r, g, b, a];
|
|
91
|
+
let handle_bits = handle.to_bits();
|
|
92
|
+
// Static models go through the cached scene pipeline
|
|
93
|
+
// (alpha cutout, normal/MR maps, foliage wind +
|
|
94
|
+
// transmission, cutout shadows, planar reflections) —
|
|
95
|
+
// the old immediate path had none of that and used
|
|
96
|
+
// to render cutout foliage as opaque cards. Skinned
|
|
97
|
+
// models take the skinned cached draw and IGNORE the
|
|
98
|
+
// rotation — their joint matrices bake orientation,
|
|
99
|
+
// exactly as the old immediate fallback behaved.
|
|
100
|
+
if renderer.cache_model_if_static(handle_bits, &model.meshes) {
|
|
101
|
+
if renderer.is_model_skinned(handle_bits) {
|
|
102
|
+
renderer.draw_model_cached_skinned(
|
|
103
|
+
handle_bits, position, scale, tint,
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
renderer.draw_model_cached_rotated(
|
|
107
|
+
handle_bits, position, scale, rot_y as f32, tint,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
#[cfg(not(feature = "models3d"))]
|
|
115
|
+
#[no_mangle]
|
|
116
|
+
pub extern "C" fn bloom_draw_model_rotated(_handle: f64, _x: f64, _y: f64, _z: f64, _scale: f64, _rot_y: f64, _color_packed_argb: f64) {
|
|
117
|
+
$crate::ffi::feature_off_warn_once("bloom_draw_model_rotated", "models3d");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// bloom_draw_model_transform16 — EN-039. [gated: models3d]
|
|
121
|
+
//
|
|
122
|
+
// The immediate-mode twin of bloom_scene_set_transform16: a full
|
|
123
|
+
// column-major 4x4, so an immediate draw can finally pitch and roll.
|
|
124
|
+
// bloom_draw_model_rotated only takes a Y rotation, which is why the
|
|
125
|
+
// shooter's gun cannot tilt with the aim.
|
|
126
|
+
//
|
|
127
|
+
// The ticket proposed routing this through the mesh scratch on the
|
|
128
|
+
// grounds that "Perry can't pass 16 f64 args". It can:
|
|
129
|
+
// bloom_scene_set_transform16 already passes 17 and works. Spelling the
|
|
130
|
+
// matrix out keeps this STATELESS — no scratch to reset, no ordering
|
|
131
|
+
// hazard between a reset and a draw — which is the same reasoning
|
|
132
|
+
// recorded on the scene twin.
|
|
133
|
+
//
|
|
134
|
+
// SKINNED models are rejected rather than silently mis-drawn: their
|
|
135
|
+
// joint matrices already bake world orientation, so a caller-supplied
|
|
136
|
+
// model matrix would double-transform them. bloom_draw_model_rotated
|
|
137
|
+
// has the same carve-out (it ignores rot_y for skinned).
|
|
138
|
+
#[cfg(feature = "models3d")]
|
|
139
|
+
#[no_mangle]
|
|
140
|
+
#[allow(clippy::too_many_arguments)]
|
|
141
|
+
pub extern "C" fn bloom_draw_model_transform16(
|
|
142
|
+
handle: f64,
|
|
143
|
+
m0: f64, m1: f64, m2: f64, m3: f64,
|
|
144
|
+
m4: f64, m5: f64, m6: f64, m7: f64,
|
|
145
|
+
m8: f64, m9: f64, m10: f64, m11: f64,
|
|
146
|
+
m12: f64, m13: f64, m14: f64, m15: f64,
|
|
147
|
+
color_packed_argb: f64,
|
|
148
|
+
) {
|
|
149
|
+
$crate::ffi::guard("bloom_draw_model_transform16", move || {
|
|
150
|
+
let bits = color_packed_argb as u32;
|
|
151
|
+
let a = ((bits >> 24) & 0xff) as f32 / 255.0;
|
|
152
|
+
let r = ((bits >> 16) & 0xff) as f32 / 255.0;
|
|
153
|
+
let g = ((bits >> 8) & 0xff) as f32 / 255.0;
|
|
154
|
+
let b = ( bits & 0xff) as f32 / 255.0;
|
|
155
|
+
let s = [
|
|
156
|
+
m0, m1, m2, m3,
|
|
157
|
+
m4, m5, m6, m7,
|
|
158
|
+
m8, m9, m10, m11,
|
|
159
|
+
m12, m13, m14, m15,
|
|
160
|
+
];
|
|
161
|
+
let mut mat = [[0.0f32; 4]; 4];
|
|
162
|
+
for col in 0..4 {
|
|
163
|
+
for row in 0..4 {
|
|
164
|
+
mat[col][row] = s[col * 4 + row] as f32;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
let mut eng = engine();
|
|
168
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
169
|
+
if let Some(model) = models.get(handle) {
|
|
170
|
+
let handle_bits = handle.to_bits();
|
|
171
|
+
if renderer.cache_model_if_static(handle_bits, &model.meshes) {
|
|
172
|
+
if renderer.is_model_skinned(handle_bits) {
|
|
173
|
+
return; // see the note above
|
|
174
|
+
}
|
|
175
|
+
renderer.draw_model_cached_transform(
|
|
176
|
+
handle_bits, mat, [r, g, b, a],
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
#[cfg(not(feature = "models3d"))]
|
|
183
|
+
#[no_mangle]
|
|
184
|
+
#[allow(clippy::too_many_arguments)]
|
|
185
|
+
pub extern "C" fn bloom_draw_model_transform16(
|
|
186
|
+
_handle: f64,
|
|
187
|
+
_m0: f64, _m1: f64, _m2: f64, _m3: f64,
|
|
188
|
+
_m4: f64, _m5: f64, _m6: f64, _m7: f64,
|
|
189
|
+
_m8: f64, _m9: f64, _m10: f64, _m11: f64,
|
|
190
|
+
_m12: f64, _m13: f64, _m14: f64, _m15: f64,
|
|
191
|
+
_color_packed_argb: f64,
|
|
192
|
+
) {
|
|
193
|
+
$crate::ffi::feature_off_warn_once("bloom_draw_model_transform16", "models3d");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// bloom_unload_model [source: linux; gated: models3d]
|
|
197
|
+
#[cfg(feature = "models3d")]
|
|
198
|
+
#[no_mangle]
|
|
199
|
+
pub extern "C" fn bloom_unload_model(handle: f64) {
|
|
200
|
+
$crate::ffi::guard("bloom_unload_model", move || {
|
|
201
|
+
let mut eng = engine();
|
|
202
|
+
// Evict cached GPU meshes (keyed by handle bits) before the
|
|
203
|
+
// handle dies — without this the buffers leak and a slot-
|
|
204
|
+
// reusing future model would render the stale geometry.
|
|
205
|
+
eng.renderer.evict_model_cache(handle.to_bits());
|
|
206
|
+
eng.models.unload_model(handle);
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
#[cfg(not(feature = "models3d"))]
|
|
210
|
+
#[no_mangle]
|
|
211
|
+
pub extern "C" fn bloom_unload_model(_handle: f64) {
|
|
212
|
+
$crate::ffi::feature_off_warn_once("bloom_unload_model", "models3d");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// bloom_gen_mesh_cube [source: linux; gated: models3d]
|
|
216
|
+
#[cfg(feature = "models3d")]
|
|
217
|
+
#[no_mangle]
|
|
218
|
+
pub extern "C" fn bloom_gen_mesh_cube(w: f64, h: f64, d: f64) -> f64 {
|
|
219
|
+
$crate::ffi::guard("bloom_gen_mesh_cube", move || {
|
|
220
|
+
engine().models.gen_mesh_cube(w as f32, h as f32, d as f32)
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
#[cfg(not(feature = "models3d"))]
|
|
224
|
+
#[no_mangle]
|
|
225
|
+
pub extern "C" fn bloom_gen_mesh_cube(_w: f64, _h: f64, _d: f64) -> f64 {
|
|
226
|
+
$crate::ffi::feature_off_warn_once("bloom_gen_mesh_cube", "models3d");
|
|
227
|
+
0.0
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// bloom_gen_mesh_heightmap [source: linux; gated: models3d]
|
|
231
|
+
#[cfg(feature = "models3d")]
|
|
232
|
+
#[no_mangle]
|
|
233
|
+
pub extern "C" fn bloom_gen_mesh_heightmap(image_handle: f64, size_x: f64, size_y: f64, size_z: f64) -> f64 {
|
|
234
|
+
$crate::ffi::guard("bloom_gen_mesh_heightmap", move || {
|
|
235
|
+
let mut eng = engine();
|
|
236
|
+
if let Some(img) = eng.textures.images.get(image_handle) {
|
|
237
|
+
let data = img.data.clone();
|
|
238
|
+
let w = img.width;
|
|
239
|
+
let h = img.height;
|
|
240
|
+
eng.models.gen_mesh_heightmap(&data, w, h, size_x as f32, size_y as f32, size_z as f32)
|
|
241
|
+
} else {
|
|
242
|
+
0.0
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
#[cfg(not(feature = "models3d"))]
|
|
247
|
+
#[no_mangle]
|
|
248
|
+
pub extern "C" fn bloom_gen_mesh_heightmap(_image_handle: f64, _size_x: f64, _size_y: f64, _size_z: f64) -> f64 {
|
|
249
|
+
$crate::ffi::feature_off_warn_once("bloom_gen_mesh_heightmap", "models3d");
|
|
250
|
+
0.0
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// bloom_compile_material [source: macos]
|
|
254
|
+
#[no_mangle]
|
|
255
|
+
pub extern "C" fn bloom_compile_material(source_ptr: *const u8) -> f64 {
|
|
256
|
+
$crate::ffi::guard("bloom_compile_material", move || {
|
|
257
|
+
let source = $crate::string_header::str_from_header(source_ptr);
|
|
258
|
+
match engine().renderer.compile_material(source) {
|
|
259
|
+
Ok(handle) => handle as f64,
|
|
260
|
+
Err(e) => {
|
|
261
|
+
eprintln!("[material] compile failed: {:?}", e);
|
|
262
|
+
0.0
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// bloom_compile_material_refractive [source: macos]
|
|
269
|
+
#[no_mangle]
|
|
270
|
+
pub extern "C" fn bloom_compile_material_refractive(source_ptr: *const u8) -> f64 {
|
|
271
|
+
$crate::ffi::guard("bloom_compile_material_refractive", move || {
|
|
272
|
+
use $crate::renderer::material_pipeline::{FragmentProfile, Bucket};
|
|
273
|
+
let source = $crate::string_header::str_from_header(source_ptr);
|
|
274
|
+
match engine().renderer.compile_material_with_options(
|
|
275
|
+
source, FragmentProfile::Translucent, Bucket::Refractive, true, false,
|
|
276
|
+
) {
|
|
277
|
+
Ok(handle) => handle as f64,
|
|
278
|
+
Err(e) => { eprintln!("[refractive] compile failed: {:?}", e); 0.0 }
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// bloom_compile_material_transparent [source: macos]
|
|
284
|
+
#[no_mangle]
|
|
285
|
+
pub extern "C" fn bloom_compile_material_transparent(source_ptr: *const u8) -> f64 {
|
|
286
|
+
$crate::ffi::guard("bloom_compile_material_transparent", move || {
|
|
287
|
+
use $crate::renderer::material_pipeline::{FragmentProfile, Bucket};
|
|
288
|
+
let source = $crate::string_header::str_from_header(source_ptr);
|
|
289
|
+
match engine().renderer.compile_material_with_options(
|
|
290
|
+
source, FragmentProfile::Translucent, Bucket::Transparent, false, false,
|
|
291
|
+
) {
|
|
292
|
+
Ok(handle) => handle as f64,
|
|
293
|
+
Err(e) => { eprintln!("[material] compile failed: {:?}", e); 0.0 }
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// bloom_compile_material_additive [source: macos]
|
|
299
|
+
#[no_mangle]
|
|
300
|
+
pub extern "C" fn bloom_compile_material_additive(source_ptr: *const u8) -> f64 {
|
|
301
|
+
$crate::ffi::guard("bloom_compile_material_additive", move || {
|
|
302
|
+
use $crate::renderer::material_pipeline::{FragmentProfile, Bucket};
|
|
303
|
+
let source = $crate::string_header::str_from_header(source_ptr);
|
|
304
|
+
match engine().renderer.compile_material_with_options(
|
|
305
|
+
source, FragmentProfile::Translucent, Bucket::Additive, false, false,
|
|
306
|
+
) {
|
|
307
|
+
Ok(handle) => handle as f64,
|
|
308
|
+
Err(e) => { eprintln!("[material] compile failed: {:?}", e); 0.0 }
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// bloom_compile_material_cutout [source: macos]
|
|
314
|
+
#[no_mangle]
|
|
315
|
+
pub extern "C" fn bloom_compile_material_cutout(source_ptr: *const u8) -> f64 {
|
|
316
|
+
$crate::ffi::guard("bloom_compile_material_cutout", move || {
|
|
317
|
+
use $crate::renderer::material_pipeline::{FragmentProfile, Bucket};
|
|
318
|
+
let source = $crate::string_header::str_from_header(source_ptr);
|
|
319
|
+
match engine().renderer.compile_material_with_options(
|
|
320
|
+
source, FragmentProfile::Opaque, Bucket::Cutout, false, false,
|
|
321
|
+
) {
|
|
322
|
+
Ok(handle) => handle as f64,
|
|
323
|
+
Err(e) => { eprintln!("[material] compile failed: {:?}", e); 0.0 }
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// bloom_compile_material_instanced [source: macos]
|
|
329
|
+
#[no_mangle]
|
|
330
|
+
pub extern "C" fn bloom_compile_material_instanced(source_ptr: *const u8) -> f64 {
|
|
331
|
+
$crate::ffi::guard("bloom_compile_material_instanced", move || {
|
|
332
|
+
let source = $crate::string_header::str_from_header(source_ptr);
|
|
333
|
+
match engine().renderer.compile_material_instanced(source) {
|
|
334
|
+
Ok(handle) => handle as f64,
|
|
335
|
+
Err(e) => { eprintln!("[material] instanced compile failed: {:?}", e); 0.0 }
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// bloom_compile_material_instanced_bucket [EN-026/027]
|
|
341
|
+
// bucket: 0 = opaque, 1 = cutout, 2 = additive, 3 = transparent.
|
|
342
|
+
// reads_scene: bind the scene colour/depth snapshot (soft particles).
|
|
343
|
+
// The plain instanced compile above is hardcoded to opaque, which is
|
|
344
|
+
// right for grass and wrong for the two things that most want
|
|
345
|
+
// instancing: particles (additive) and decals (cutout).
|
|
346
|
+
#[no_mangle]
|
|
347
|
+
pub extern "C" fn bloom_compile_material_instanced_bucket(
|
|
348
|
+
source_ptr: *const u8, bucket: f64, reads_scene: f64,
|
|
349
|
+
) -> f64 {
|
|
350
|
+
$crate::ffi::guard("bloom_compile_material_instanced_bucket", move || {
|
|
351
|
+
let source = $crate::string_header::str_from_header(source_ptr);
|
|
352
|
+
match engine().renderer.compile_material_instanced_bucket(
|
|
353
|
+
source, bucket as u32, reads_scene != 0.0)
|
|
354
|
+
{
|
|
355
|
+
Ok(handle) => handle as f64,
|
|
356
|
+
Err(e) => { eprintln!("[material] instanced compile failed: {:?}", e); 0.0 }
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// bloom_submit_material_draw_instanced [source: linux; gated: models3d]
|
|
362
|
+
#[cfg(feature = "models3d")]
|
|
363
|
+
#[no_mangle]
|
|
364
|
+
pub extern "C" fn bloom_submit_material_draw_instanced(
|
|
365
|
+
material: f64, mesh_handle: f64, mesh_idx: f64,
|
|
366
|
+
instance_buffer: f64, instance_count: f64,
|
|
367
|
+
) {
|
|
368
|
+
$crate::ffi::guard("bloom_submit_material_draw_instanced", move || {
|
|
369
|
+
let mut eng = engine();
|
|
370
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
371
|
+
let handle_bits = mesh_handle.to_bits();
|
|
372
|
+
if let Some(model) = models.get(mesh_handle) {
|
|
373
|
+
renderer.cache_model_if_static(handle_bits, &model.meshes);
|
|
374
|
+
}
|
|
375
|
+
renderer.submit_material_draw_instanced(
|
|
376
|
+
material as u32,
|
|
377
|
+
handle_bits,
|
|
378
|
+
mesh_idx as usize,
|
|
379
|
+
instance_buffer as u32,
|
|
380
|
+
instance_count as u32,
|
|
381
|
+
);
|
|
382
|
+
})
|
|
383
|
+
}
|
|
384
|
+
#[cfg(not(feature = "models3d"))]
|
|
385
|
+
#[no_mangle]
|
|
386
|
+
pub extern "C" fn bloom_submit_material_draw_instanced(_material: f64, _mesh_handle: f64, _mesh_idx: f64, _instance_buffer: f64, _instance_count: f64) {
|
|
387
|
+
$crate::ffi::feature_off_warn_once("bloom_submit_material_draw_instanced", "models3d");
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// bloom_draw_material [source: linux; gated: models3d]
|
|
391
|
+
#[cfg(feature = "models3d")]
|
|
392
|
+
#[no_mangle]
|
|
393
|
+
pub extern "C" fn bloom_draw_material(
|
|
394
|
+
material: f64,
|
|
395
|
+
mesh_handle: f64,
|
|
396
|
+
mesh_idx: f64,
|
|
397
|
+
x: f64, y: f64, z: f64, scale: f64,
|
|
398
|
+
r: f64, g: f64, b: f64, a: f64,
|
|
399
|
+
) {
|
|
400
|
+
$crate::ffi::guard("bloom_draw_material", move || {
|
|
401
|
+
let mut eng = engine();
|
|
402
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
403
|
+
let handle_bits = mesh_handle.to_bits();
|
|
404
|
+
if let Some(model) = models.get(mesh_handle) {
|
|
405
|
+
renderer.cache_model_if_static(handle_bits, &model.meshes);
|
|
406
|
+
}
|
|
407
|
+
renderer.submit_material_draw(
|
|
408
|
+
material as u32,
|
|
409
|
+
handle_bits,
|
|
410
|
+
mesh_idx as usize,
|
|
411
|
+
[x as f32, y as f32, z as f32],
|
|
412
|
+
scale as f32,
|
|
413
|
+
[(r / 255.0) as f32, (g / 255.0) as f32, (b / 255.0) as f32, (a / 255.0) as f32],
|
|
414
|
+
);
|
|
415
|
+
})
|
|
416
|
+
}
|
|
417
|
+
#[cfg(not(feature = "models3d"))]
|
|
418
|
+
#[no_mangle]
|
|
419
|
+
pub extern "C" fn bloom_draw_material(_material: f64, _mesh_handle: f64, _mesh_idx: f64, _x: f64, _y: f64, _z: f64, _scale: f64, _r: f64, _g: f64, _b: f64, _a: f64) {
|
|
420
|
+
$crate::ffi::feature_off_warn_once("bloom_draw_material", "models3d");
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// bloom_load_model_animation [source: linux; gated: models3d]
|
|
424
|
+
#[cfg(feature = "models3d")]
|
|
425
|
+
#[no_mangle]
|
|
426
|
+
pub extern "C" fn bloom_load_model_animation(path_ptr: *const u8) -> f64 {
|
|
427
|
+
$crate::ffi::guard("bloom_load_model_animation", move || {
|
|
428
|
+
let path = $crate::string_header::str_from_header(path_ptr);
|
|
429
|
+
let path = bloom_resolve_asset_path(path);
|
|
430
|
+
match std::fs::read(path.as_ref()) {
|
|
431
|
+
Ok(data) => engine().models.load_model_animation(&data),
|
|
432
|
+
Err(_) => 0.0,
|
|
433
|
+
}
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
#[cfg(not(feature = "models3d"))]
|
|
437
|
+
#[no_mangle]
|
|
438
|
+
pub extern "C" fn bloom_load_model_animation(_path_ptr: *const u8) -> f64 {
|
|
439
|
+
$crate::ffi::feature_off_warn_once("bloom_load_model_animation", "models3d");
|
|
440
|
+
0.0
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// bloom_instantiate_animation [source: EN-055; gated: models3d]
|
|
444
|
+
// A fresh animation INSTANCE (own mixer/joint state) over an
|
|
445
|
+
// already-loaded clip set — clip data is Arc-shared, so N characters
|
|
446
|
+
// no longer cost N GLB re-parses at load.
|
|
447
|
+
#[cfg(feature = "models3d")]
|
|
448
|
+
#[no_mangle]
|
|
449
|
+
pub extern "C" fn bloom_instantiate_animation(src: f64) -> f64 {
|
|
450
|
+
$crate::ffi::guard("bloom_instantiate_animation", move || {
|
|
451
|
+
engine().models.instantiate_animation(src)
|
|
452
|
+
})
|
|
453
|
+
}
|
|
454
|
+
#[cfg(not(feature = "models3d"))]
|
|
455
|
+
#[no_mangle]
|
|
456
|
+
pub extern "C" fn bloom_instantiate_animation(_src: f64) -> f64 {
|
|
457
|
+
$crate::ffi::feature_off_warn_once("bloom_instantiate_animation", "models3d");
|
|
458
|
+
0.0
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// bloom_update_model_animation [source: linux; gated: models3d]
|
|
462
|
+
#[cfg(feature = "models3d")]
|
|
463
|
+
#[no_mangle]
|
|
464
|
+
pub extern "C" fn bloom_update_model_animation(handle: f64, anim_index: f64, time: f64, scale: f64, px: f64, py: f64, pz: f64, rot_y: f64) {
|
|
465
|
+
$crate::ffi::guard("bloom_update_model_animation", move || {
|
|
466
|
+
// Take a single Y-axis angle (radians) instead of sin/cos, so the
|
|
467
|
+
// engine reconstructs both with full precision + correct signs.
|
|
468
|
+
// Older callers that passed (rot_sin, rot_cos) hit a Perry-ARM64
|
|
469
|
+
// 9th-arg garbling bug AND a sqrt(1-sin²) workaround that lost
|
|
470
|
+
// the sign of cos — model rotation was correct only on half the
|
|
471
|
+
// circle. 8-arg signature dodges both issues. Matches macOS.
|
|
472
|
+
let rot_y_f = rot_y as f32;
|
|
473
|
+
let rot_sin = rot_y_f.sin();
|
|
474
|
+
let rot_cos = rot_y_f.cos();
|
|
475
|
+
let mut eng = engine();
|
|
476
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
477
|
+
models.update_model_animation(handle, anim_index as usize, time as f32);
|
|
478
|
+
if let Some(anim) = models.get_animation(handle) {
|
|
479
|
+
if !anim.joint_matrices.is_empty() {
|
|
480
|
+
// PT-7: the anim handle keys the prev-palette
|
|
481
|
+
// pairing for skinned motion vectors.
|
|
482
|
+
renderer.set_joint_matrices_scaled(handle.to_bits(), &anim.joint_matrices, scale as f32, [px as f32, py as f32, pz as f32], rot_sin, rot_cos);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
})
|
|
486
|
+
}
|
|
487
|
+
#[cfg(not(feature = "models3d"))]
|
|
488
|
+
#[no_mangle]
|
|
489
|
+
pub extern "C" fn bloom_update_model_animation(_handle: f64, _anim_index: f64, _time: f64, _scale: f64, _px: f64, _py: f64, _pz: f64, _rot_y: f64) {
|
|
490
|
+
$crate::ffi::feature_off_warn_once("bloom_update_model_animation", "models3d");
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// bloom_create_mesh [source: curated; gated: models3d]
|
|
494
|
+
#[cfg(feature = "models3d")]
|
|
495
|
+
#[no_mangle]
|
|
496
|
+
pub extern "C" fn bloom_create_mesh(vertex_ptr: *const f64, vertex_count: f64, index_ptr: *const f64, index_count: f64) -> f64 {
|
|
497
|
+
$crate::ffi::guard("bloom_create_mesh", move || {
|
|
498
|
+
// Perry arrays are pointers to inline f64 data (8-byte ArrayHeader
|
|
499
|
+
// skipped by the compiler before the call) — see perry-codegen
|
|
500
|
+
// lower_call/extern_func.rs. Reading f32/u32 here was a latent
|
|
501
|
+
// misread on windows/android/ios/tvos before the FFI unification.
|
|
502
|
+
if vertex_ptr.is_null() || index_ptr.is_null() { return 0.0; }
|
|
503
|
+
let vcount = vertex_count as usize;
|
|
504
|
+
let icount = index_count as usize;
|
|
505
|
+
let vertex_f64 = unsafe { std::slice::from_raw_parts(vertex_ptr, vcount * 12) };
|
|
506
|
+
let index_f64 = unsafe { std::slice::from_raw_parts(index_ptr, icount) };
|
|
507
|
+
let vertices: Vec<f32> = vertex_f64.iter().map(|&v| v as f32).collect();
|
|
508
|
+
let indices: Vec<u32> = index_f64.iter().map(|&v| v as u32).collect();
|
|
509
|
+
engine().models.create_mesh(&vertices, &indices)
|
|
510
|
+
})
|
|
511
|
+
}
|
|
512
|
+
#[cfg(not(feature = "models3d"))]
|
|
513
|
+
#[no_mangle]
|
|
514
|
+
pub extern "C" fn bloom_create_mesh(_vertex_ptr: *const f64, _vertex_count: f64, _index_ptr: *const f64, _index_count: f64) -> f64 {
|
|
515
|
+
$crate::ffi::feature_off_warn_once("bloom_create_mesh", "models3d");
|
|
516
|
+
0.0
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Array-free mesh upload (Perry 0.5.1171 rejects number[] -> i64 pointer
|
|
520
|
+
// params; see ModelManager::scratch_f32). Push the 12-float vertex
|
|
521
|
+
// records + u32 indices one scalar at a time (all-f64 ABI), then build.
|
|
522
|
+
#[cfg(feature = "models3d")]
|
|
523
|
+
#[no_mangle]
|
|
524
|
+
pub extern "C" fn bloom_mesh_scratch_reset() {
|
|
525
|
+
$crate::ffi::guard("bloom_mesh_scratch_reset", move || {
|
|
526
|
+
engine().models.mesh_scratch_reset();
|
|
527
|
+
})
|
|
528
|
+
}
|
|
529
|
+
#[cfg(not(feature = "models3d"))]
|
|
530
|
+
#[no_mangle]
|
|
531
|
+
pub extern "C" fn bloom_mesh_scratch_reset() {
|
|
532
|
+
$crate::ffi::feature_off_warn_once("bloom_mesh_scratch_reset", "models3d");
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
#[cfg(feature = "models3d")]
|
|
536
|
+
#[no_mangle]
|
|
537
|
+
pub extern "C" fn bloom_mesh_scratch_push_f32(v: f64) {
|
|
538
|
+
$crate::ffi::guard("bloom_mesh_scratch_push_f32", move || {
|
|
539
|
+
engine().models.mesh_scratch_push_f32(v as f32);
|
|
540
|
+
})
|
|
541
|
+
}
|
|
542
|
+
#[cfg(not(feature = "models3d"))]
|
|
543
|
+
#[no_mangle]
|
|
544
|
+
pub extern "C" fn bloom_mesh_scratch_push_f32(_v: f64) {
|
|
545
|
+
$crate::ffi::feature_off_warn_once("bloom_mesh_scratch_push_f32", "models3d");
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
#[cfg(feature = "models3d")]
|
|
549
|
+
#[no_mangle]
|
|
550
|
+
pub extern "C" fn bloom_mesh_scratch_push_u32(v: f64) {
|
|
551
|
+
$crate::ffi::guard("bloom_mesh_scratch_push_u32", move || {
|
|
552
|
+
engine().models.mesh_scratch_push_u32(v as u32);
|
|
553
|
+
})
|
|
554
|
+
}
|
|
555
|
+
#[cfg(not(feature = "models3d"))]
|
|
556
|
+
#[no_mangle]
|
|
557
|
+
pub extern "C" fn bloom_mesh_scratch_push_u32(_v: f64) {
|
|
558
|
+
$crate::ffi::feature_off_warn_once("bloom_mesh_scratch_push_u32", "models3d");
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
#[cfg(feature = "models3d")]
|
|
562
|
+
#[no_mangle]
|
|
563
|
+
pub extern "C" fn bloom_create_mesh_scratch(vertex_count: f64, index_count: f64) -> f64 {
|
|
564
|
+
$crate::ffi::guard("bloom_create_mesh_scratch", move || {
|
|
565
|
+
engine().models.create_mesh_from_scratch(vertex_count as u32, index_count as u32)
|
|
566
|
+
})
|
|
567
|
+
}
|
|
568
|
+
#[cfg(not(feature = "models3d"))]
|
|
569
|
+
#[no_mangle]
|
|
570
|
+
pub extern "C" fn bloom_create_mesh_scratch(_vertex_count: f64, _index_count: f64) -> f64 {
|
|
571
|
+
$crate::ffi::feature_off_warn_once("bloom_create_mesh_scratch", "models3d");
|
|
572
|
+
0.0
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Array-free scene-geometry upload. Same motivation as the mesh scratch
|
|
576
|
+
// above: `bloom_scene_update_geometry` takes `i64` pointers, which
|
|
577
|
+
// Perry cannot produce from a `number[]`. Push the vertex floats and
|
|
578
|
+
// u32 indices through the mesh scratch, then re-upload them into an
|
|
579
|
+
// existing scene node (the editor's terrain re-meshes every brush
|
|
580
|
+
// stroke this way).
|
|
581
|
+
#[cfg(feature = "models3d")]
|
|
582
|
+
#[no_mangle]
|
|
583
|
+
pub extern "C" fn bloom_scene_update_geometry_scratch(
|
|
584
|
+
handle: f64,
|
|
585
|
+
vertex_count: f64,
|
|
586
|
+
index_count: f64,
|
|
587
|
+
) {
|
|
588
|
+
$crate::ffi::guard("bloom_scene_update_geometry_scratch", move || {
|
|
589
|
+
let taken = engine()
|
|
590
|
+
.models
|
|
591
|
+
.take_scratch_geometry(vertex_count as u32, index_count as u32);
|
|
592
|
+
let (vert_floats, indices) = match taken {
|
|
593
|
+
Some(v) => v,
|
|
594
|
+
None => return,
|
|
595
|
+
};
|
|
596
|
+
let nv = vertex_count as usize;
|
|
597
|
+
let mut vertices = Vec::with_capacity(nv);
|
|
598
|
+
for i in 0..nv {
|
|
599
|
+
let base = i * 12;
|
|
600
|
+
vertices.push($crate::renderer::Vertex3D {
|
|
601
|
+
position: [vert_floats[base], vert_floats[base + 1], vert_floats[base + 2]],
|
|
602
|
+
normal: [vert_floats[base + 3], vert_floats[base + 4], vert_floats[base + 5]],
|
|
603
|
+
color: [
|
|
604
|
+
vert_floats[base + 6],
|
|
605
|
+
vert_floats[base + 7],
|
|
606
|
+
vert_floats[base + 8],
|
|
607
|
+
vert_floats[base + 9],
|
|
608
|
+
],
|
|
609
|
+
uv: [vert_floats[base + 10], vert_floats[base + 11]],
|
|
610
|
+
joints: [0.0; 4],
|
|
611
|
+
weights: [0.0; 4],
|
|
612
|
+
tangent: [0.0; 4],
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
engine().scene.update_geometry(handle, vertices, indices);
|
|
616
|
+
})
|
|
617
|
+
}
|
|
618
|
+
#[cfg(not(feature = "models3d"))]
|
|
619
|
+
#[no_mangle]
|
|
620
|
+
pub extern "C" fn bloom_scene_update_geometry_scratch(
|
|
621
|
+
_handle: f64,
|
|
622
|
+
_vertex_count: f64,
|
|
623
|
+
_index_count: f64,
|
|
624
|
+
) {
|
|
625
|
+
$crate::ffi::feature_off_warn_once("bloom_scene_update_geometry_scratch", "models3d");
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// bloom_get_model_mesh_count [source: linux; gated: models3d]
|
|
629
|
+
#[cfg(feature = "models3d")]
|
|
630
|
+
#[no_mangle]
|
|
631
|
+
pub extern "C" fn bloom_get_model_mesh_count(handle: f64) -> f64 {
|
|
632
|
+
$crate::ffi::guard("bloom_get_model_mesh_count", move || {
|
|
633
|
+
match engine().models.get(handle) {
|
|
634
|
+
Some(model) => model.meshes.len() as f64,
|
|
635
|
+
None => 0.0,
|
|
636
|
+
}
|
|
637
|
+
})
|
|
638
|
+
}
|
|
639
|
+
#[cfg(not(feature = "models3d"))]
|
|
640
|
+
#[no_mangle]
|
|
641
|
+
pub extern "C" fn bloom_get_model_mesh_count(_handle: f64) -> f64 {
|
|
642
|
+
$crate::ffi::feature_off_warn_once("bloom_get_model_mesh_count", "models3d");
|
|
643
|
+
0.0
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// bloom_get_model_material_count [source: linux; gated: models3d]
|
|
647
|
+
#[cfg(feature = "models3d")]
|
|
648
|
+
#[no_mangle]
|
|
649
|
+
pub extern "C" fn bloom_get_model_material_count(handle: f64) -> f64 {
|
|
650
|
+
$crate::ffi::guard("bloom_get_model_material_count", move || {
|
|
651
|
+
match engine().models.get(handle) {
|
|
652
|
+
Some(model) => model.meshes.len() as f64,
|
|
653
|
+
None => 0.0,
|
|
654
|
+
}
|
|
655
|
+
})
|
|
656
|
+
}
|
|
657
|
+
#[cfg(not(feature = "models3d"))]
|
|
658
|
+
#[no_mangle]
|
|
659
|
+
pub extern "C" fn bloom_get_model_material_count(_handle: f64) -> f64 {
|
|
660
|
+
$crate::ffi::feature_off_warn_once("bloom_get_model_material_count", "models3d");
|
|
661
|
+
0.0
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// bloom_get_model_bounds_min_x [source: linux; gated: models3d]
|
|
665
|
+
#[cfg(feature = "models3d")]
|
|
666
|
+
#[no_mangle]
|
|
667
|
+
pub extern "C" fn bloom_get_model_bounds_min_x(model_handle: f64) -> f64 {
|
|
668
|
+
$crate::ffi::guard("bloom_get_model_bounds_min_x", move || {
|
|
669
|
+
engine().models.get_bounds(model_handle).0[0] as f64
|
|
670
|
+
})
|
|
671
|
+
}
|
|
672
|
+
#[cfg(not(feature = "models3d"))]
|
|
673
|
+
#[no_mangle]
|
|
674
|
+
pub extern "C" fn bloom_get_model_bounds_min_x(_model_handle: f64) -> f64 {
|
|
675
|
+
$crate::ffi::feature_off_warn_once("bloom_get_model_bounds_min_x", "models3d");
|
|
676
|
+
0.0
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// bloom_get_model_bounds_min_y [source: linux; gated: models3d]
|
|
680
|
+
#[cfg(feature = "models3d")]
|
|
681
|
+
#[no_mangle]
|
|
682
|
+
pub extern "C" fn bloom_get_model_bounds_min_y(model_handle: f64) -> f64 {
|
|
683
|
+
$crate::ffi::guard("bloom_get_model_bounds_min_y", move || {
|
|
684
|
+
engine().models.get_bounds(model_handle).0[1] as f64
|
|
685
|
+
})
|
|
686
|
+
}
|
|
687
|
+
#[cfg(not(feature = "models3d"))]
|
|
688
|
+
#[no_mangle]
|
|
689
|
+
pub extern "C" fn bloom_get_model_bounds_min_y(_model_handle: f64) -> f64 {
|
|
690
|
+
$crate::ffi::feature_off_warn_once("bloom_get_model_bounds_min_y", "models3d");
|
|
691
|
+
0.0
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// bloom_get_model_bounds_min_z [source: linux; gated: models3d]
|
|
695
|
+
#[cfg(feature = "models3d")]
|
|
696
|
+
#[no_mangle]
|
|
697
|
+
pub extern "C" fn bloom_get_model_bounds_min_z(model_handle: f64) -> f64 {
|
|
698
|
+
$crate::ffi::guard("bloom_get_model_bounds_min_z", move || {
|
|
699
|
+
engine().models.get_bounds(model_handle).0[2] as f64
|
|
700
|
+
})
|
|
701
|
+
}
|
|
702
|
+
#[cfg(not(feature = "models3d"))]
|
|
703
|
+
#[no_mangle]
|
|
704
|
+
pub extern "C" fn bloom_get_model_bounds_min_z(_model_handle: f64) -> f64 {
|
|
705
|
+
$crate::ffi::feature_off_warn_once("bloom_get_model_bounds_min_z", "models3d");
|
|
706
|
+
0.0
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// bloom_get_model_bounds_max_x [source: linux; gated: models3d]
|
|
710
|
+
#[cfg(feature = "models3d")]
|
|
711
|
+
#[no_mangle]
|
|
712
|
+
pub extern "C" fn bloom_get_model_bounds_max_x(model_handle: f64) -> f64 {
|
|
713
|
+
$crate::ffi::guard("bloom_get_model_bounds_max_x", move || {
|
|
714
|
+
engine().models.get_bounds(model_handle).1[0] as f64
|
|
715
|
+
})
|
|
716
|
+
}
|
|
717
|
+
#[cfg(not(feature = "models3d"))]
|
|
718
|
+
#[no_mangle]
|
|
719
|
+
pub extern "C" fn bloom_get_model_bounds_max_x(_model_handle: f64) -> f64 {
|
|
720
|
+
$crate::ffi::feature_off_warn_once("bloom_get_model_bounds_max_x", "models3d");
|
|
721
|
+
0.0
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// bloom_get_model_bounds_max_y [source: linux; gated: models3d]
|
|
725
|
+
#[cfg(feature = "models3d")]
|
|
726
|
+
#[no_mangle]
|
|
727
|
+
pub extern "C" fn bloom_get_model_bounds_max_y(model_handle: f64) -> f64 {
|
|
728
|
+
$crate::ffi::guard("bloom_get_model_bounds_max_y", move || {
|
|
729
|
+
engine().models.get_bounds(model_handle).1[1] as f64
|
|
730
|
+
})
|
|
731
|
+
}
|
|
732
|
+
#[cfg(not(feature = "models3d"))]
|
|
733
|
+
#[no_mangle]
|
|
734
|
+
pub extern "C" fn bloom_get_model_bounds_max_y(_model_handle: f64) -> f64 {
|
|
735
|
+
$crate::ffi::feature_off_warn_once("bloom_get_model_bounds_max_y", "models3d");
|
|
736
|
+
0.0
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// bloom_get_model_bounds_max_z [source: linux; gated: models3d]
|
|
740
|
+
#[cfg(feature = "models3d")]
|
|
741
|
+
#[no_mangle]
|
|
742
|
+
pub extern "C" fn bloom_get_model_bounds_max_z(model_handle: f64) -> f64 {
|
|
743
|
+
$crate::ffi::guard("bloom_get_model_bounds_max_z", move || {
|
|
744
|
+
engine().models.get_bounds(model_handle).1[2] as f64
|
|
745
|
+
})
|
|
746
|
+
}
|
|
747
|
+
#[cfg(not(feature = "models3d"))]
|
|
748
|
+
#[no_mangle]
|
|
749
|
+
pub extern "C" fn bloom_get_model_bounds_max_z(_model_handle: f64) -> f64 {
|
|
750
|
+
$crate::ffi::feature_off_warn_once("bloom_get_model_bounds_max_z", "models3d");
|
|
751
|
+
0.0
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// bloom_gen_mesh_spline_ribbon [source: curated; gated: models3d]
|
|
755
|
+
#[cfg(feature = "models3d")]
|
|
756
|
+
#[no_mangle]
|
|
757
|
+
pub extern "C" fn bloom_gen_mesh_spline_ribbon(points_ptr: *const f64, point_count: f64, widths_ptr: *const f64, width_count: f64) -> f64 {
|
|
758
|
+
$crate::ffi::guard("bloom_gen_mesh_spline_ribbon", move || {
|
|
759
|
+
// Perry arrays are f64 buffers (see bloom_create_mesh note).
|
|
760
|
+
if points_ptr.is_null() || widths_ptr.is_null() { return 0.0; }
|
|
761
|
+
let n = point_count as usize;
|
|
762
|
+
let wn = width_count as usize;
|
|
763
|
+
let points_f64 = unsafe { std::slice::from_raw_parts(points_ptr, n * 3) };
|
|
764
|
+
let widths_f64 = unsafe { std::slice::from_raw_parts(widths_ptr, wn) };
|
|
765
|
+
let points: Vec<f32> = points_f64.iter().map(|&v| v as f32).collect();
|
|
766
|
+
let widths: Vec<f32> = widths_f64.iter().map(|&v| v as f32).collect();
|
|
767
|
+
engine().models.gen_mesh_spline_ribbon(&points, &widths)
|
|
768
|
+
})
|
|
769
|
+
}
|
|
770
|
+
#[cfg(not(feature = "models3d"))]
|
|
771
|
+
#[no_mangle]
|
|
772
|
+
pub extern "C" fn bloom_gen_mesh_spline_ribbon(_points_ptr: *const f64, _point_count: f64, _widths_ptr: *const f64, _width_count: f64) -> f64 {
|
|
773
|
+
$crate::ffi::feature_off_warn_once("bloom_gen_mesh_spline_ribbon", "models3d");
|
|
774
|
+
0.0
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Array-free spline ribbon. Same reason as the mesh scratch above: the
|
|
778
|
+
// pointer form is unreachable from TypeScript (Perry won't pass a
|
|
779
|
+
// `number[]` into an i64 param). Push `point_count * 3` position floats
|
|
780
|
+
// followed by `width_count` width floats into the mesh scratch, then
|
|
781
|
+
// call this.
|
|
782
|
+
#[cfg(feature = "models3d")]
|
|
783
|
+
#[no_mangle]
|
|
784
|
+
pub extern "C" fn bloom_gen_mesh_spline_ribbon_scratch(point_count: f64, width_count: f64) -> f64 {
|
|
785
|
+
$crate::ffi::guard("bloom_gen_mesh_spline_ribbon_scratch", move || {
|
|
786
|
+
let n = point_count as usize;
|
|
787
|
+
let wn = width_count as usize;
|
|
788
|
+
let (points, widths) = {
|
|
789
|
+
let eng = engine();
|
|
790
|
+
let scratch = eng.models.scratch_floats();
|
|
791
|
+
if n < 2 || wn == 0 || scratch.len() < n * 3 + wn {
|
|
792
|
+
return 0.0;
|
|
793
|
+
}
|
|
794
|
+
(
|
|
795
|
+
scratch[..n * 3].to_vec(),
|
|
796
|
+
scratch[n * 3..n * 3 + wn].to_vec(),
|
|
797
|
+
)
|
|
798
|
+
};
|
|
799
|
+
engine().models.gen_mesh_spline_ribbon(&points, &widths)
|
|
800
|
+
})
|
|
801
|
+
}
|
|
802
|
+
#[cfg(not(feature = "models3d"))]
|
|
803
|
+
#[no_mangle]
|
|
804
|
+
pub extern "C" fn bloom_gen_mesh_spline_ribbon_scratch(_point_count: f64, _width_count: f64) -> f64 {
|
|
805
|
+
$crate::ffi::feature_off_warn_once("bloom_gen_mesh_spline_ribbon_scratch", "models3d");
|
|
806
|
+
0.0
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// bloom_stage_model [source: linux; gated: models3d]
|
|
810
|
+
#[cfg(feature = "models3d")]
|
|
811
|
+
#[no_mangle]
|
|
812
|
+
pub extern "C" fn bloom_stage_model(path_ptr: *const u8) -> f64 {
|
|
813
|
+
$crate::ffi::guard("bloom_stage_model", move || {
|
|
814
|
+
let path = $crate::string_header::str_from_header(path_ptr);
|
|
815
|
+
let path = bloom_resolve_asset_path(path);
|
|
816
|
+
let data = match std::fs::read(path.as_ref()) {
|
|
817
|
+
Ok(d) => d,
|
|
818
|
+
Err(_) => return 0.0,
|
|
819
|
+
};
|
|
820
|
+
match $crate::models::load_gltf_staged(&data) {
|
|
821
|
+
Some(staged) => $crate::staging::stage_model(staged),
|
|
822
|
+
None => 0.0,
|
|
823
|
+
}
|
|
824
|
+
})
|
|
825
|
+
}
|
|
826
|
+
#[cfg(not(feature = "models3d"))]
|
|
827
|
+
#[no_mangle]
|
|
828
|
+
pub extern "C" fn bloom_stage_model(_path_ptr: *const u8) -> f64 {
|
|
829
|
+
$crate::ffi::feature_off_warn_once("bloom_stage_model", "models3d");
|
|
830
|
+
0.0
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// bloom_commit_model [source: linux; gated: models3d]
|
|
834
|
+
#[cfg(feature = "models3d")]
|
|
835
|
+
#[no_mangle]
|
|
836
|
+
pub extern "C" fn bloom_commit_model(staging_handle: f64) -> f64 {
|
|
837
|
+
$crate::ffi::guard("bloom_commit_model", move || {
|
|
838
|
+
let staged = match $crate::staging::take_model(staging_handle) {
|
|
839
|
+
Some(s) => s,
|
|
840
|
+
None => return 0.0,
|
|
841
|
+
};
|
|
842
|
+
let mut eng = engine();
|
|
843
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
844
|
+
// Normal maps must go through the kind-aware registration
|
|
845
|
+
// (linear space + LEADR mips) or the committed model shades
|
|
846
|
+
// visibly flatter than the same GLB through loadModel.
|
|
847
|
+
let mut tex_map: Vec<u32> = Vec::with_capacity(staged.textures.len());
|
|
848
|
+
for tex in &staged.textures {
|
|
849
|
+
tex_map.push(renderer.register_texture_kind(
|
|
850
|
+
tex.width, tex.height, &tex.data, tex.is_normal));
|
|
851
|
+
}
|
|
852
|
+
let mut model = staged.model;
|
|
853
|
+
// Remap EVERY texture slot, not just the base colour — this
|
|
854
|
+
// used to drop normal/MR/emissive/occlusion references on the
|
|
855
|
+
// floor (the round-5 "stale aux-texture remap" bug), which
|
|
856
|
+
// silently stripped the SH-046 character maps on any model
|
|
857
|
+
// loaded through the staged path.
|
|
858
|
+
let remap = |idx: &mut Option<u32>| {
|
|
859
|
+
if let Some(i) = *idx {
|
|
860
|
+
let s = i as usize;
|
|
861
|
+
*idx = if s > 0 && s <= tex_map.len() {
|
|
862
|
+
Some(tex_map[s - 1])
|
|
863
|
+
} else {
|
|
864
|
+
None
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
for mesh in &mut model.meshes {
|
|
869
|
+
remap(&mut mesh.texture_idx);
|
|
870
|
+
remap(&mut mesh.normal_texture_idx);
|
|
871
|
+
remap(&mut mesh.metallic_roughness_texture_idx);
|
|
872
|
+
remap(&mut mesh.emissive_texture_idx);
|
|
873
|
+
remap(&mut mesh.occlusion_texture_idx);
|
|
874
|
+
}
|
|
875
|
+
models.models.alloc(model)
|
|
876
|
+
})
|
|
877
|
+
}
|
|
878
|
+
#[cfg(not(feature = "models3d"))]
|
|
879
|
+
#[no_mangle]
|
|
880
|
+
pub extern "C" fn bloom_commit_model(_staging_handle: f64) -> f64 {
|
|
881
|
+
$crate::ffi::feature_off_warn_once("bloom_commit_model", "models3d");
|
|
882
|
+
0.0
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// bloom_compile_material_from_file [source: macos]
|
|
886
|
+
#[no_mangle]
|
|
887
|
+
pub extern "C" fn bloom_compile_material_from_file(
|
|
888
|
+
path_ptr: *const u8,
|
|
889
|
+
bucket_kind: f64,
|
|
890
|
+
) -> f64 {
|
|
891
|
+
$crate::ffi::guard("bloom_compile_material_from_file", move || {
|
|
892
|
+
use $crate::renderer::material_pipeline::{FragmentProfile, Bucket};
|
|
893
|
+
let path = $crate::string_header::str_from_header(path_ptr);
|
|
894
|
+
// Every other asset loader routes through the platform's
|
|
895
|
+
// resolve hook; this one read the raw relative path straight
|
|
896
|
+
// off the working directory, which is only the asset root on
|
|
897
|
+
// the desktop hosts. On iOS the CWD is not the app bundle, so
|
|
898
|
+
// every from-file material failed to canonicalize and the whole
|
|
899
|
+
// scene lost its shaders.
|
|
900
|
+
let path = bloom_resolve_asset_path(path);
|
|
901
|
+
let (profile, bucket, reads_scene) = match bucket_kind as u32 {
|
|
902
|
+
0 => (FragmentProfile::Opaque, Bucket::Opaque, false),
|
|
903
|
+
1 => (FragmentProfile::Translucent, Bucket::Transparent, false),
|
|
904
|
+
2 => (FragmentProfile::Translucent, Bucket::Refractive, true),
|
|
905
|
+
3 => (FragmentProfile::Translucent, Bucket::Additive, false),
|
|
906
|
+
4 => (FragmentProfile::Opaque, Bucket::Cutout, false),
|
|
907
|
+
_ => {
|
|
908
|
+
eprintln!("[material] from_file: unknown bucket_kind {bucket_kind}");
|
|
909
|
+
return 0.0;
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
match engine().renderer.compile_material_from_file(
|
|
913
|
+
std::path::Path::new(path.as_ref()), profile, bucket, reads_scene,
|
|
914
|
+
) {
|
|
915
|
+
Ok(handle) => handle as f64,
|
|
916
|
+
Err(e) => { eprintln!("[material] from_file failed: {e}"); 0.0 }
|
|
917
|
+
}
|
|
918
|
+
})
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// bloom_set_material_params_scratch — params arrive via the mesh
|
|
922
|
+
// scratch (bloom_mesh_scratch_reset + push_f32 × N) instead of the
|
|
923
|
+
// pointer param below, which Perry 0.5.x rejects for JS arrays.
|
|
924
|
+
// Same idiom as bloom_create_instance_buffer_scratch. ≤ 64 floats.
|
|
925
|
+
#[cfg(feature = "models3d")]
|
|
926
|
+
#[no_mangle]
|
|
927
|
+
pub extern "C" fn bloom_set_material_params_scratch(handle: f64, param_count: f64) {
|
|
928
|
+
$crate::ffi::guard("bloom_set_material_params_scratch", move || {
|
|
929
|
+
let mut eng = engine();
|
|
930
|
+
let count = param_count as usize;
|
|
931
|
+
if count > 64 {
|
|
932
|
+
eprintln!("[material] set_material_params_scratch: param_count {} > 64 (256-byte UBO cap)", count);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
936
|
+
if models.scratch_f32.len() < count { return; }
|
|
937
|
+
let mut bytes = vec![0u8; count * 4];
|
|
938
|
+
for i in 0..count {
|
|
939
|
+
bytes[i*4..i*4+4].copy_from_slice(&models.scratch_f32[i].to_le_bytes());
|
|
940
|
+
}
|
|
941
|
+
models.mesh_scratch_reset();
|
|
942
|
+
if let Err(e) = renderer.material_system.set_user_params(
|
|
943
|
+
&renderer.device, &renderer.queue,
|
|
944
|
+
handle as u32, &bytes,
|
|
945
|
+
) {
|
|
946
|
+
eprintln!("[material] set_material_params_scratch failed: {}", e);
|
|
947
|
+
}
|
|
948
|
+
})
|
|
949
|
+
}
|
|
950
|
+
#[cfg(not(feature = "models3d"))]
|
|
951
|
+
#[no_mangle]
|
|
952
|
+
pub extern "C" fn bloom_set_material_params_scratch(_handle: f64, _param_count: f64) {
|
|
953
|
+
$crate::ffi::feature_off_warn_once("bloom_set_material_params_scratch", "models3d");
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// bloom_set_material_params [source: linux; gated: models3d]
|
|
957
|
+
#[cfg(feature = "models3d")]
|
|
958
|
+
#[no_mangle]
|
|
959
|
+
pub extern "C" fn bloom_set_material_params(
|
|
960
|
+
handle: f64,
|
|
961
|
+
params_ptr: *const f64,
|
|
962
|
+
param_count: f64,
|
|
963
|
+
) {
|
|
964
|
+
$crate::ffi::guard("bloom_set_material_params", move || {
|
|
965
|
+
let count = param_count as usize;
|
|
966
|
+
if count > 64 {
|
|
967
|
+
eprintln!("[material] set_material_params: param_count {} > 64 (256-byte UBO cap)", count);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
let mut bytes = vec![0u8; count * 4];
|
|
971
|
+
if !params_ptr.is_null() && count > 0 {
|
|
972
|
+
let slots = unsafe { std::slice::from_raw_parts(params_ptr, count) };
|
|
973
|
+
for (i, &v) in slots.iter().enumerate() {
|
|
974
|
+
let f = v as f32;
|
|
975
|
+
bytes[i*4..i*4+4].copy_from_slice(&f.to_le_bytes());
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
let mut eng = engine();
|
|
979
|
+
let $crate::engine::EngineState { renderer, .. } = &mut *eng;
|
|
980
|
+
if let Err(e) = renderer.material_system.set_user_params(
|
|
981
|
+
&renderer.device, &renderer.queue,
|
|
982
|
+
handle as u32, &bytes,
|
|
983
|
+
) {
|
|
984
|
+
eprintln!("[material] set_material_params failed: {}", e);
|
|
985
|
+
}
|
|
986
|
+
})
|
|
987
|
+
}
|
|
988
|
+
#[cfg(not(feature = "models3d"))]
|
|
989
|
+
#[no_mangle]
|
|
990
|
+
pub extern "C" fn bloom_set_material_params(_handle: f64, _params_ptr: *const f64, _param_count: f64) {
|
|
991
|
+
$crate::ffi::feature_off_warn_once("bloom_set_material_params", "models3d");
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// ==================================================================
|
|
995
|
+
// EN-028 — animation mixer (crossfade + masked layer + root motion)
|
|
996
|
+
// EN-033 — bone sockets
|
|
997
|
+
//
|
|
998
|
+
// All numeric ABI, <= 8 f64 args per call (the 9th arg garbles on
|
|
999
|
+
// Perry/ARM64 — see bloom_update_model_animation's note).
|
|
1000
|
+
// ==================================================================
|
|
1001
|
+
|
|
1002
|
+
// bloom_anim_play [gated: models3d]
|
|
1003
|
+
#[cfg(feature = "models3d")]
|
|
1004
|
+
#[no_mangle]
|
|
1005
|
+
pub extern "C" fn bloom_anim_play(handle: f64, clip: f64, fade: f64, speed: f64, looping: f64) {
|
|
1006
|
+
$crate::ffi::guard("bloom_anim_play", move || {
|
|
1007
|
+
engine().models.anim_play(
|
|
1008
|
+
handle, clip as usize, fade as f32, speed as f32, looping != 0.0);
|
|
1009
|
+
})
|
|
1010
|
+
}
|
|
1011
|
+
#[cfg(not(feature = "models3d"))]
|
|
1012
|
+
#[no_mangle]
|
|
1013
|
+
pub extern "C" fn bloom_anim_play(_h: f64, _c: f64, _f: f64, _s: f64, _l: f64) {
|
|
1014
|
+
$crate::ffi::feature_off_warn_once("bloom_anim_play", "models3d");
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// bloom_anim_set_layer [gated: models3d]
|
|
1018
|
+
#[cfg(feature = "models3d")]
|
|
1019
|
+
#[no_mangle]
|
|
1020
|
+
pub extern "C" fn bloom_anim_set_layer(handle: f64, clip: f64, weight: f64, mask_root: f64, speed: f64, looping: f64) {
|
|
1021
|
+
$crate::ffi::guard("bloom_anim_set_layer", move || {
|
|
1022
|
+
engine().models.anim_set_layer(
|
|
1023
|
+
handle, clip as i32, weight as f32, mask_root as i32,
|
|
1024
|
+
speed as f32, looping != 0.0);
|
|
1025
|
+
})
|
|
1026
|
+
}
|
|
1027
|
+
#[cfg(not(feature = "models3d"))]
|
|
1028
|
+
#[no_mangle]
|
|
1029
|
+
pub extern "C" fn bloom_anim_set_layer(_h: f64, _c: f64, _w: f64, _m: f64, _s: f64, _l: f64) {
|
|
1030
|
+
$crate::ffi::feature_off_warn_once("bloom_anim_set_layer", "models3d");
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// bloom_anim_set_root_motion [gated: models3d]
|
|
1034
|
+
#[cfg(feature = "models3d")]
|
|
1035
|
+
#[no_mangle]
|
|
1036
|
+
pub extern "C" fn bloom_anim_set_root_motion(handle: f64, on: f64) {
|
|
1037
|
+
$crate::ffi::guard("bloom_anim_set_root_motion", move || {
|
|
1038
|
+
engine().models.anim_set_root_motion(handle, on != 0.0);
|
|
1039
|
+
})
|
|
1040
|
+
}
|
|
1041
|
+
#[cfg(not(feature = "models3d"))]
|
|
1042
|
+
#[no_mangle]
|
|
1043
|
+
pub extern "C" fn bloom_anim_set_root_motion(_h: f64, _o: f64) {
|
|
1044
|
+
$crate::ffi::feature_off_warn_once("bloom_anim_set_root_motion", "models3d");
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// bloom_anim_update [gated: models3d]
|
|
1048
|
+
// Advances every clock on the model by dt, rebuilds the blended pose,
|
|
1049
|
+
// and uploads the joint matrices — the mixer-driven replacement for
|
|
1050
|
+
// bloom_update_model_animation (which stays for single-clip callers).
|
|
1051
|
+
#[cfg(feature = "models3d")]
|
|
1052
|
+
#[no_mangle]
|
|
1053
|
+
pub extern "C" fn bloom_anim_update(handle: f64, dt: f64, scale: f64, px: f64, py: f64, pz: f64, rot_y: f64) {
|
|
1054
|
+
$crate::ffi::guard("bloom_anim_update", move || {
|
|
1055
|
+
let rot_y_f = rot_y as f32;
|
|
1056
|
+
let rot_sin = rot_y_f.sin();
|
|
1057
|
+
let rot_cos = rot_y_f.cos();
|
|
1058
|
+
let mut eng = engine();
|
|
1059
|
+
let $crate::engine::EngineState { models, renderer, .. } = &mut *eng;
|
|
1060
|
+
models.advance_and_update(handle, dt as f32);
|
|
1061
|
+
if let Some(anim) = models.get_animation(handle) {
|
|
1062
|
+
if !anim.joint_matrices.is_empty() {
|
|
1063
|
+
// PT-7: anim handle = prev-palette pairing key.
|
|
1064
|
+
renderer.set_joint_matrices_scaled(
|
|
1065
|
+
handle.to_bits(), &anim.joint_matrices, scale as f32,
|
|
1066
|
+
[px as f32, py as f32, pz as f32], rot_sin, rot_cos);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
})
|
|
1070
|
+
}
|
|
1071
|
+
#[cfg(not(feature = "models3d"))]
|
|
1072
|
+
#[no_mangle]
|
|
1073
|
+
pub extern "C" fn bloom_anim_update(_h: f64, _d: f64, _s: f64, _x: f64, _y: f64, _z: f64, _r: f64) {
|
|
1074
|
+
$crate::ffi::feature_off_warn_once("bloom_anim_update", "models3d");
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// bloom_anim_finished [gated: models3d]
|
|
1078
|
+
#[cfg(feature = "models3d")]
|
|
1079
|
+
#[no_mangle]
|
|
1080
|
+
pub extern "C" fn bloom_anim_finished(handle: f64) -> f64 {
|
|
1081
|
+
$crate::ffi::guard("bloom_anim_finished", move || {
|
|
1082
|
+
if engine().models.anim_finished(handle) { 1.0 } else { 0.0 }
|
|
1083
|
+
})
|
|
1084
|
+
}
|
|
1085
|
+
#[cfg(not(feature = "models3d"))]
|
|
1086
|
+
#[no_mangle]
|
|
1087
|
+
pub extern "C" fn bloom_anim_finished(_h: f64) -> f64 { 1.0 }
|
|
1088
|
+
|
|
1089
|
+
// bloom_anim_clip_duration [gated: models3d]
|
|
1090
|
+
#[cfg(feature = "models3d")]
|
|
1091
|
+
#[no_mangle]
|
|
1092
|
+
pub extern "C" fn bloom_anim_clip_duration(handle: f64, clip: f64) -> f64 {
|
|
1093
|
+
$crate::ffi::guard("bloom_anim_clip_duration", move || {
|
|
1094
|
+
engine().models.anim_clip_duration(handle, clip as usize) as f64
|
|
1095
|
+
})
|
|
1096
|
+
}
|
|
1097
|
+
#[cfg(not(feature = "models3d"))]
|
|
1098
|
+
#[no_mangle]
|
|
1099
|
+
pub extern "C" fn bloom_anim_clip_duration(_h: f64, _c: f64) -> f64 { 0.0 }
|
|
1100
|
+
|
|
1101
|
+
// bloom_anim_root_delta [gated: models3d] axis: 0=x 1=y 2=z
|
|
1102
|
+
#[cfg(feature = "models3d")]
|
|
1103
|
+
#[no_mangle]
|
|
1104
|
+
pub extern "C" fn bloom_anim_root_delta(handle: f64, axis: f64) -> f64 {
|
|
1105
|
+
$crate::ffi::guard("bloom_anim_root_delta", move || {
|
|
1106
|
+
let d = engine().models.anim_root_delta(handle);
|
|
1107
|
+
let i = axis as usize;
|
|
1108
|
+
if i < 3 { d[i] as f64 } else { 0.0 }
|
|
1109
|
+
})
|
|
1110
|
+
}
|
|
1111
|
+
#[cfg(not(feature = "models3d"))]
|
|
1112
|
+
#[no_mangle]
|
|
1113
|
+
pub extern "C" fn bloom_anim_root_delta(_h: f64, _a: f64) -> f64 { 0.0 }
|
|
1114
|
+
|
|
1115
|
+
// bloom_model_find_joint [gated: models3d]
|
|
1116
|
+
// Load-time only (it parses a string) — cache the index, per
|
|
1117
|
+
// perry-quirks #5.
|
|
1118
|
+
#[cfg(feature = "models3d")]
|
|
1119
|
+
#[no_mangle]
|
|
1120
|
+
pub extern "C" fn bloom_model_find_joint(handle: f64, name_ptr: *const u8) -> f64 {
|
|
1121
|
+
$crate::ffi::guard("bloom_model_find_joint", move || {
|
|
1122
|
+
let name = $crate::string_header::str_from_header(name_ptr);
|
|
1123
|
+
engine().models.find_joint(handle, &name) as f64
|
|
1124
|
+
})
|
|
1125
|
+
}
|
|
1126
|
+
#[cfg(not(feature = "models3d"))]
|
|
1127
|
+
#[no_mangle]
|
|
1128
|
+
pub extern "C" fn bloom_model_find_joint(_h: f64, _n: *const u8) -> f64 { -1.0 }
|
|
1129
|
+
|
|
1130
|
+
// bloom_model_joint_world [gated: models3d]
|
|
1131
|
+
// Model-space 4x4 of a joint, column-major, component 0..15.
|
|
1132
|
+
// Translation is components 12/13/14. The caller applies the same
|
|
1133
|
+
// (scale, position, yaw) it passed to bloom_anim_update to lift this
|
|
1134
|
+
// into world space — the engine deliberately does not, so a socket
|
|
1135
|
+
// costs no extra state.
|
|
1136
|
+
#[cfg(feature = "models3d")]
|
|
1137
|
+
#[no_mangle]
|
|
1138
|
+
pub extern "C" fn bloom_model_joint_world(handle: f64, joint: f64, comp: f64) -> f64 {
|
|
1139
|
+
$crate::ffi::guard("bloom_model_joint_world", move || {
|
|
1140
|
+
let j = joint as i64;
|
|
1141
|
+
let c = comp as usize;
|
|
1142
|
+
if j < 0 || c > 15 { return 0.0; }
|
|
1143
|
+
match engine().models.joint_world(handle, j as usize) {
|
|
1144
|
+
Some(m) => m[c / 4][c % 4] as f64,
|
|
1145
|
+
None => 0.0,
|
|
1146
|
+
}
|
|
1147
|
+
})
|
|
1148
|
+
}
|
|
1149
|
+
#[cfg(not(feature = "models3d"))]
|
|
1150
|
+
#[no_mangle]
|
|
1151
|
+
pub extern "C" fn bloom_model_joint_world(_h: f64, _j: f64, _c: f64) -> f64 { 0.0 }
|
|
1152
|
+
|
|
1153
|
+
};
|
|
1154
|
+
}
|