@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.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +231 -0
  3. package/native/android/Cargo.lock +1848 -0
  4. package/native/android/Cargo.toml +24 -0
  5. package/native/android/src/lib.rs +702 -0
  6. package/native/ios/Cargo.lock +1690 -0
  7. package/native/ios/Cargo.toml +32 -0
  8. package/native/ios/src/lib.rs +1267 -0
  9. package/native/linux/Cargo.lock +3279 -0
  10. package/native/linux/Cargo.toml +29 -0
  11. package/native/linux/src/lib.rs +1331 -0
  12. package/native/macos/Cargo.lock +3310 -0
  13. package/native/macos/Cargo.toml +46 -0
  14. package/native/macos/src/lib.rs +1302 -0
  15. package/native/shared/Cargo.lock +1899 -0
  16. package/native/shared/Cargo.toml +62 -0
  17. package/native/shared/assets/default_font.ttf +0 -0
  18. package/native/shared/build.rs +270 -0
  19. package/native/shared/shaders/common/clouds.wgsl +122 -0
  20. package/native/shared/shaders/common/fog.wgsl +16 -0
  21. package/native/shared/shaders/common/foliage_wind.wgsl +98 -0
  22. package/native/shared/shaders/common/imposter.wgsl +112 -0
  23. package/native/shared/shaders/common/pbr.wgsl +186 -0
  24. package/native/shared/shaders/common/shadows.wgsl +186 -0
  25. package/native/shared/shaders/common/sky.wgsl +8 -0
  26. package/native/shared/shaders/common/tonemap.wgsl +25 -0
  27. package/native/shared/shaders/impulse_field.wgsl +57 -0
  28. package/native/shared/shaders/material_abi.wgsl +383 -0
  29. package/native/shared/shaders/materials/test_minimal.wgsl +42 -0
  30. package/native/shared/src/anim_mixer.rs +61 -0
  31. package/native/shared/src/attach.rs +263 -0
  32. package/native/shared/src/audio/decode.rs +123 -0
  33. package/native/shared/src/audio/mod.rs +863 -0
  34. package/native/shared/src/audio/render.rs +892 -0
  35. package/native/shared/src/audio/spsc.rs +156 -0
  36. package/native/shared/src/audio/stream.rs +226 -0
  37. package/native/shared/src/custom_shaders.rs +104 -0
  38. package/native/shared/src/decals.rs +245 -0
  39. package/native/shared/src/drs.rs +211 -0
  40. package/native/shared/src/engine.rs +261 -0
  41. package/native/shared/src/ffi.rs +116 -0
  42. package/native/shared/src/ffi_core/assets.rs +388 -0
  43. package/native/shared/src/ffi_core/audio_ffi.rs +184 -0
  44. package/native/shared/src/ffi_core/draw.rs +334 -0
  45. package/native/shared/src/ffi_core/game_loop.rs +577 -0
  46. package/native/shared/src/ffi_core/input.rs +234 -0
  47. package/native/shared/src/ffi_core/mod.rs +127 -0
  48. package/native/shared/src/ffi_core/models.rs +1154 -0
  49. package/native/shared/src/ffi_core/ragdoll_ffi.rs +261 -0
  50. package/native/shared/src/ffi_core/scene.rs +626 -0
  51. package/native/shared/src/ffi_core/vfx.rs +212 -0
  52. package/native/shared/src/ffi_core/visual.rs +691 -0
  53. package/native/shared/src/frame_callbacks.rs +122 -0
  54. package/native/shared/src/geometry.rs +236 -0
  55. package/native/shared/src/handles.rs +182 -0
  56. package/native/shared/src/input.rs +448 -0
  57. package/native/shared/src/jolt_sys.rs +822 -0
  58. package/native/shared/src/lib.rs +55 -0
  59. package/native/shared/src/models.rs +1093 -0
  60. package/native/shared/src/models_gltf.rs +1280 -0
  61. package/native/shared/src/particles.rs +391 -0
  62. package/native/shared/src/physics_jolt.rs +1908 -0
  63. package/native/shared/src/picking.rs +298 -0
  64. package/native/shared/src/postfx.rs +345 -0
  65. package/native/shared/src/profiler.rs +492 -0
  66. package/native/shared/src/ragdoll.rs +474 -0
  67. package/native/shared/src/renderer/atmosphere_lut.rs +573 -0
  68. package/native/shared/src/renderer/brdf_lut.rs +154 -0
  69. package/native/shared/src/renderer/draw2d.rs +143 -0
  70. package/native/shared/src/renderer/formats.rs +822 -0
  71. package/native/shared/src/renderer/froxel.rs +421 -0
  72. package/native/shared/src/renderer/gi_bake.rs +653 -0
  73. package/native/shared/src/renderer/graph.rs +462 -0
  74. package/native/shared/src/renderer/hiz.rs +269 -0
  75. package/native/shared/src/renderer/hot_reload.rs +390 -0
  76. package/native/shared/src/renderer/impulse_field.rs +456 -0
  77. package/native/shared/src/renderer/lighting.rs +154 -0
  78. package/native/shared/src/renderer/material_instancing.rs +171 -0
  79. package/native/shared/src/renderer/material_pipeline.rs +700 -0
  80. package/native/shared/src/renderer/material_system.rs +1996 -0
  81. package/native/shared/src/renderer/material_system_tests.rs +601 -0
  82. package/native/shared/src/renderer/material_system_wasm.rs +41 -0
  83. package/native/shared/src/renderer/mod.rs +12556 -0
  84. package/native/shared/src/renderer/model_draw.rs +641 -0
  85. package/native/shared/src/renderer/occlusion.rs +429 -0
  86. package/native/shared/src/renderer/planar_pass.rs +593 -0
  87. package/native/shared/src/renderer/planar_reflection.rs +499 -0
  88. package/native/shared/src/renderer/post_pass.rs +249 -0
  89. package/native/shared/src/renderer/postfx_chain.rs +728 -0
  90. package/native/shared/src/renderer/pt_pass.rs +577 -0
  91. package/native/shared/src/renderer/scene_pass.rs +607 -0
  92. package/native/shared/src/renderer/shader_include.rs +205 -0
  93. package/native/shared/src/renderer/shader_library.rs +135 -0
  94. package/native/shared/src/renderer/shaders/ao.rs +570 -0
  95. package/native/shared/src/renderer/shaders/core.rs +1243 -0
  96. package/native/shared/src/renderer/shaders/env.rs +907 -0
  97. package/native/shared/src/renderer/shaders/gi.rs +810 -0
  98. package/native/shared/src/renderer/shaders/mod.rs +19 -0
  99. package/native/shared/src/renderer/shaders/post.rs +1558 -0
  100. package/native/shared/src/renderer/shaders/pt.rs +1859 -0
  101. package/native/shared/src/renderer/shaders/ssgi.rs +1586 -0
  102. package/native/shared/src/renderer/shadow_pass.rs +731 -0
  103. package/native/shared/src/renderer/ssgi_pass.rs +392 -0
  104. package/native/shared/src/renderer/ssr_pass.rs +188 -0
  105. package/native/shared/src/renderer/texture_store.rs +473 -0
  106. package/native/shared/src/renderer/transient.rs +591 -0
  107. package/native/shared/src/renderer/types.rs +941 -0
  108. package/native/shared/src/renderer/util.rs +152 -0
  109. package/native/shared/src/scene.rs +1362 -0
  110. package/native/shared/src/sdf_cache.rs +274 -0
  111. package/native/shared/src/shadows.rs +1036 -0
  112. package/native/shared/src/staging.rs +102 -0
  113. package/native/shared/src/string_header.rs +266 -0
  114. package/native/shared/src/text_renderer.rs +502 -0
  115. package/native/shared/src/textures.rs +197 -0
  116. package/native/tvos/Cargo.lock +1693 -0
  117. package/native/tvos/Cargo.toml +36 -0
  118. package/native/tvos/metal-patched/Cargo.toml +178 -0
  119. package/native/tvos/metal-patched/LICENSE-APACHE +201 -0
  120. package/native/tvos/metal-patched/LICENSE-MIT +25 -0
  121. package/native/tvos/metal-patched/src/acceleration_structure.rs +667 -0
  122. package/native/tvos/metal-patched/src/acceleration_structure_pass.rs +108 -0
  123. package/native/tvos/metal-patched/src/argument.rs +366 -0
  124. package/native/tvos/metal-patched/src/blitpass.rs +102 -0
  125. package/native/tvos/metal-patched/src/buffer.rs +71 -0
  126. package/native/tvos/metal-patched/src/capturedescriptor.rs +76 -0
  127. package/native/tvos/metal-patched/src/capturemanager.rs +113 -0
  128. package/native/tvos/metal-patched/src/commandbuffer.rs +192 -0
  129. package/native/tvos/metal-patched/src/commandqueue.rs +44 -0
  130. package/native/tvos/metal-patched/src/computepass.rs +107 -0
  131. package/native/tvos/metal-patched/src/constants.rs +152 -0
  132. package/native/tvos/metal-patched/src/counters.rs +119 -0
  133. package/native/tvos/metal-patched/src/depthstencil.rs +190 -0
  134. package/native/tvos/metal-patched/src/device.rs +2134 -0
  135. package/native/tvos/metal-patched/src/drawable.rs +39 -0
  136. package/native/tvos/metal-patched/src/encoder.rs +2041 -0
  137. package/native/tvos/metal-patched/src/heap.rs +281 -0
  138. package/native/tvos/metal-patched/src/indirect_encoder.rs +344 -0
  139. package/native/tvos/metal-patched/src/lib.rs +657 -0
  140. package/native/tvos/metal-patched/src/library.rs +902 -0
  141. package/native/tvos/metal-patched/src/mps.rs +575 -0
  142. package/native/tvos/metal-patched/src/pipeline/compute.rs +475 -0
  143. package/native/tvos/metal-patched/src/pipeline/mod.rs +71 -0
  144. package/native/tvos/metal-patched/src/pipeline/render.rs +762 -0
  145. package/native/tvos/metal-patched/src/renderpass.rs +443 -0
  146. package/native/tvos/metal-patched/src/resource.rs +182 -0
  147. package/native/tvos/metal-patched/src/sampler.rs +165 -0
  148. package/native/tvos/metal-patched/src/sync.rs +178 -0
  149. package/native/tvos/metal-patched/src/texture.rs +352 -0
  150. package/native/tvos/metal-patched/src/types.rs +90 -0
  151. package/native/tvos/metal-patched/src/vertexdescriptor.rs +250 -0
  152. package/native/tvos/src/audio_backend.rs +197 -0
  153. package/native/tvos/src/lib.rs +1891 -0
  154. package/native/visionos/Cargo.lock +1693 -0
  155. package/native/visionos/Cargo.toml +40 -0
  156. package/native/visionos/src/audio_backend.rs +197 -0
  157. package/native/visionos/src/lib.rs +1887 -0
  158. package/native/watchos/Cargo.lock +16 -0
  159. package/native/watchos/Cargo.toml +19 -0
  160. package/native/watchos/shaders/bloom_postfx.metal +99 -0
  161. package/native/watchos/src/BloomWatchApp.swift +1267 -0
  162. package/native/watchos/src/BloomWatchAudio.swift +179 -0
  163. package/native/watchos/src/audio.rs +55 -0
  164. package/native/watchos/src/draw_list.rs +229 -0
  165. package/native/watchos/src/ffi_stubs.rs +915 -0
  166. package/native/watchos/src/ffi_stubs_manual.rs +35 -0
  167. package/native/watchos/src/lib.rs +1124 -0
  168. package/native/watchos/src/models.rs +746 -0
  169. package/native/watchos/src/postfx.rs +95 -0
  170. package/native/watchos/src/scene.rs +534 -0
  171. package/native/watchos/src/textures.rs +184 -0
  172. package/native/web/Cargo.lock +1657 -0
  173. package/native/web/Cargo.toml +43 -0
  174. package/native/web/bloom_glue.js +695 -0
  175. package/native/web/build.sh +131 -0
  176. package/native/web/index.html +35 -0
  177. package/native/web/jolt_bridge.js +1519 -0
  178. package/native/web/src/input_ffi.rs +286 -0
  179. package/native/web/src/lib.rs +1796 -0
  180. package/native/web/src/material_ffi.rs +710 -0
  181. package/native/web/src/parity_ffi.rs +343 -0
  182. package/native/web/src/physics_ffi.rs +643 -0
  183. package/native/web/src/ragdoll_ffi.rs +250 -0
  184. package/native/web/src/render_settings.rs +98 -0
  185. package/native/windows/Cargo.lock +1815 -0
  186. package/native/windows/Cargo.toml +68 -0
  187. package/native/windows/src/lib.rs +1486 -0
  188. package/package.json +4279 -0
  189. package/src/audio/index.ts +315 -0
  190. package/src/core/colors.ts +63 -0
  191. package/src/core/index.ts +1206 -0
  192. package/src/core/keys.ts +63 -0
  193. package/src/core/types.ts +104 -0
  194. package/src/index.ts +171 -0
  195. package/src/math/index.ts +516 -0
  196. package/src/mobile/index.ts +294 -0
  197. package/src/models/index.ts +1258 -0
  198. package/src/physics/index.ts +1134 -0
  199. package/src/scene/index.ts +698 -0
  200. package/src/shapes/index.ts +120 -0
  201. package/src/text/index.ts +48 -0
  202. package/src/textures/index.ts +187 -0
  203. package/src/vfx/index.ts +191 -0
  204. package/src/world/index.ts +24 -0
  205. package/src/world/loader.ts +423 -0
  206. package/src/world/prefab.ts +217 -0
  207. package/src/world/render.ts +172 -0
  208. package/src/world/saver.ts +108 -0
  209. package/src/world/serialize.ts +301 -0
  210. package/src/world/terrain.ts +355 -0
  211. package/src/world/types.ts +160 -0
  212. package/src/world/validate.ts +319 -0
  213. package/src/world/version.ts +114 -0
@@ -0,0 +1,626 @@
1
+ //! Retained scene graph, picking, and screen projection.
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_scene {
11
+ () => {
12
+
13
+ // bloom_scene_create_node [source: macos]
14
+ #[no_mangle]
15
+ pub extern "C" fn bloom_scene_create_node() -> f64 {
16
+ $crate::ffi::guard("bloom_scene_create_node", move || {
17
+ engine().scene.create_node()
18
+ })
19
+ }
20
+
21
+ // bloom_scene_destroy_node [source: macos]
22
+ #[no_mangle]
23
+ pub extern "C" fn bloom_scene_destroy_node(handle: f64) {
24
+ $crate::ffi::guard("bloom_scene_destroy_node", move || {
25
+ engine().scene.destroy_node(handle);
26
+ })
27
+ }
28
+
29
+ // bloom_scene_set_visible [source: macos]
30
+ #[no_mangle]
31
+ pub extern "C" fn bloom_scene_set_visible(handle: f64, visible: f64) {
32
+ $crate::ffi::guard("bloom_scene_set_visible", move || {
33
+ engine().scene.set_visible(handle, visible != 0.0);
34
+ })
35
+ }
36
+
37
+ // bloom_scene_set_trs — position + Y-rotation + uniform scale as six
38
+ // f64 scalars. The full-matrix bloom_scene_set_transform passes a JS
39
+ // array into an i64 pointer param, which Perry 0.5.x rejects; this
40
+ // covers the common placement case without touching that ABI.
41
+ #[no_mangle]
42
+ pub extern "C" fn bloom_scene_set_trs(handle: f64, px: f64, py: f64, pz: f64, yaw: f64, scale: f64) {
43
+ $crate::ffi::guard("bloom_scene_set_trs", move || {
44
+ engine().scene.set_trs(handle, px as f32, py as f32, pz as f32, yaw as f32, scale as f32);
45
+ })
46
+ }
47
+
48
+ // bloom_scene_set_cast_shadow [source: macos]
49
+ #[no_mangle]
50
+ pub extern "C" fn bloom_scene_set_cast_shadow(handle: f64, cast: f64) {
51
+ $crate::ffi::guard("bloom_scene_set_cast_shadow", move || {
52
+ engine().scene.set_cast_shadow(handle, cast != 0.0);
53
+ })
54
+ }
55
+
56
+ // bloom_scene_set_gi_only — mark a node as a GI proxy: it feeds
57
+ // BLAS/TLAS, mesh cards, and the SDF clipmap (SSGI off-screen
58
+ // bounce) but is skipped by the main render, planar reflections,
59
+ // and the sun-shadow pass. For material-system games whose world
60
+ // never becomes scene nodes.
61
+ #[no_mangle]
62
+ pub extern "C" fn bloom_scene_set_gi_only(handle: f64, gi_only: f64) {
63
+ $crate::ffi::guard("bloom_scene_set_gi_only", move || {
64
+ engine().scene.set_gi_only(handle, gi_only != 0.0);
65
+ })
66
+ }
67
+
68
+ // bloom_scene_set_receive_shadow [source: macos]
69
+ #[no_mangle]
70
+ pub extern "C" fn bloom_scene_set_receive_shadow(handle: f64, receive: f64) {
71
+ $crate::ffi::guard("bloom_scene_set_receive_shadow", move || {
72
+ engine().scene.set_receive_shadow(handle, receive != 0.0);
73
+ })
74
+ }
75
+
76
+ // bloom_scene_set_parent [source: macos]
77
+ #[no_mangle]
78
+ pub extern "C" fn bloom_scene_set_parent(handle: f64, parent: f64) {
79
+ $crate::ffi::guard("bloom_scene_set_parent", move || {
80
+ engine().scene.set_parent(handle, parent);
81
+ })
82
+ }
83
+
84
+ // bloom_scene_set_transform [source: macos]
85
+ #[no_mangle]
86
+ pub extern "C" fn bloom_scene_set_transform(handle: f64, mat_ptr: *const f64) {
87
+ $crate::ffi::guard("bloom_scene_set_transform", move || {
88
+ if mat_ptr.is_null() { return; }
89
+ let slice = unsafe { std::slice::from_raw_parts(mat_ptr, 16) };
90
+ let mut mat = [[0.0f32; 4]; 4];
91
+ for col in 0..4 {
92
+ for row in 0..4 {
93
+ mat[col][row] = slice[col * 4 + row] as f32;
94
+ }
95
+ }
96
+ engine().scene.set_transform(handle, mat);
97
+ })
98
+ }
99
+
100
+ // bloom_scene_set_transform16 — all-f64 variant of set_transform.
101
+ //
102
+ // Perry 0.5.x refuses to pass a JS `number[]` into an `i64` pointer
103
+ // param ("Expected safe integer for native i64 parameter"), so the
104
+ // pointer-taking `bloom_scene_set_transform` above is unreachable from
105
+ // TypeScript. Meshes already dodge this via the scratch buffers; a
106
+ // 4x4 matrix is small enough to just spell out as 16 scalars, which
107
+ // keeps this stateless. Column-major, same layout as the ptr version.
108
+ #[no_mangle]
109
+ #[allow(clippy::too_many_arguments)]
110
+ pub extern "C" fn bloom_scene_set_transform16(
111
+ handle: f64,
112
+ m0: f64, m1: f64, m2: f64, m3: f64,
113
+ m4: f64, m5: f64, m6: f64, m7: f64,
114
+ m8: f64, m9: f64, m10: f64, m11: f64,
115
+ m12: f64, m13: f64, m14: f64, m15: f64,
116
+ ) {
117
+ $crate::ffi::guard("bloom_scene_set_transform16", move || {
118
+ let s = [
119
+ m0, m1, m2, m3,
120
+ m4, m5, m6, m7,
121
+ m8, m9, m10, m11,
122
+ m12, m13, m14, m15,
123
+ ];
124
+ let mut mat = [[0.0f32; 4]; 4];
125
+ for col in 0..4 {
126
+ for row in 0..4 {
127
+ mat[col][row] = s[col * 4 + row] as f32;
128
+ }
129
+ }
130
+ engine().scene.set_transform(handle, mat);
131
+ })
132
+ }
133
+
134
+ // bloom_scene_update_geometry [source: macos]
135
+ #[no_mangle]
136
+ pub extern "C" fn bloom_scene_update_geometry(
137
+ handle: f64,
138
+ vert_ptr: *const f64,
139
+ vert_count: f64,
140
+ idx_ptr: *const f64,
141
+ idx_count: f64,
142
+ ) {
143
+ $crate::ffi::guard("bloom_scene_update_geometry", move || {
144
+ if vert_ptr.is_null() || idx_ptr.is_null() { return; }
145
+ let nv = vert_count as usize;
146
+ let ni = idx_count as usize;
147
+
148
+ let vert_floats = unsafe { std::slice::from_raw_parts(vert_ptr, nv * 12) };
149
+ let idx_floats = unsafe { std::slice::from_raw_parts(idx_ptr, ni) };
150
+
151
+ let mut vertices = Vec::with_capacity(nv);
152
+ for i in 0..nv {
153
+ let base = i * 12;
154
+ vertices.push($crate::renderer::Vertex3D {
155
+ position: [vert_floats[base] as f32, vert_floats[base+1] as f32, vert_floats[base+2] as f32],
156
+ normal: [vert_floats[base+3] as f32, vert_floats[base+4] as f32, vert_floats[base+5] as f32],
157
+ color: [vert_floats[base+6] as f32, vert_floats[base+7] as f32, vert_floats[base+8] as f32, vert_floats[base+9] as f32],
158
+ uv: [vert_floats[base+10] as f32, vert_floats[base+11] as f32],
159
+ joints: [0.0; 4],
160
+ weights: [0.0; 4],
161
+ tangent: [0.0; 4],
162
+ });
163
+ }
164
+
165
+ let indices: Vec<u32> = idx_floats.iter().map(|&v| v as u32).collect();
166
+
167
+ engine().scene.update_geometry(handle, vertices, indices);
168
+ })
169
+ }
170
+
171
+ // bloom_scene_set_lod — reduced-detail variant for a node. Same
172
+ // 12-float vertex layout as bloom_scene_update_geometry; the base
173
+ // geometry is the finest level. max_coverage = screen-coverage
174
+ // threshold below which this level renders.
175
+ #[no_mangle]
176
+ pub extern "C" fn bloom_scene_set_lod(
177
+ handle: f64,
178
+ lod_index: f64,
179
+ vert_ptr: *const f64,
180
+ vert_count: f64,
181
+ idx_ptr: *const f64,
182
+ idx_count: f64,
183
+ max_coverage: f64,
184
+ ) {
185
+ $crate::ffi::guard("bloom_scene_set_lod", move || {
186
+ if vert_ptr.is_null() || idx_ptr.is_null() { return; }
187
+ let nv = vert_count as usize;
188
+ let ni = idx_count as usize;
189
+ let vert_floats = unsafe { std::slice::from_raw_parts(vert_ptr, nv * 12) };
190
+ let idx_floats = unsafe { std::slice::from_raw_parts(idx_ptr, ni) };
191
+ let mut vertices = Vec::with_capacity(nv);
192
+ for i in 0..nv {
193
+ let base = i * 12;
194
+ vertices.push($crate::renderer::Vertex3D {
195
+ position: [vert_floats[base] as f32, vert_floats[base+1] as f32, vert_floats[base+2] as f32],
196
+ normal: [vert_floats[base+3] as f32, vert_floats[base+4] as f32, vert_floats[base+5] as f32],
197
+ color: [vert_floats[base+6] as f32, vert_floats[base+7] as f32, vert_floats[base+8] as f32, vert_floats[base+9] as f32],
198
+ uv: [vert_floats[base+10] as f32, vert_floats[base+11] as f32],
199
+ joints: [0.0; 4],
200
+ weights: [0.0; 4],
201
+ tangent: [0.0; 4],
202
+ });
203
+ }
204
+ let indices: Vec<u32> = idx_floats.iter().map(|&v| v as u32).collect();
205
+ engine().scene.set_lod_geometry(handle, lod_index as usize, vertices, indices, max_coverage as f32);
206
+ })
207
+ }
208
+
209
+ // bloom_scene_attach_model_lod — model mesh as a reduced-detail
210
+ // variant (LOD counterpart of bloom_scene_attach_model).
211
+ #[cfg(feature = "models3d")]
212
+ #[no_mangle]
213
+ pub extern "C" fn bloom_scene_attach_model_lod(node_handle: f64, model_handle: f64, mesh_index: f64, lod_index: f64, max_coverage: f64) {
214
+ $crate::ffi::guard("bloom_scene_attach_model_lod", move || {
215
+ let mut eng = engine();
216
+ let mi = mesh_index as usize;
217
+ let model_data = match eng.models.models.get(model_handle) {
218
+ Some(md) => md,
219
+ None => return,
220
+ };
221
+ if mi >= model_data.meshes.len() { return; }
222
+ let mesh = &model_data.meshes[mi];
223
+ let vertices = mesh.vertices.clone();
224
+ let indices = mesh.indices.clone();
225
+ eng.scene.set_lod_geometry(node_handle, lod_index as usize, vertices, indices, max_coverage as f32);
226
+ })
227
+ }
228
+ #[cfg(not(feature = "models3d"))]
229
+ #[no_mangle]
230
+ pub extern "C" fn bloom_scene_attach_model_lod(_node_handle: f64, _model_handle: f64, _mesh_index: f64, _lod_index: f64, _max_coverage: f64) {
231
+ $crate::ffi::feature_off_warn_once("bloom_scene_attach_model_lod", "models3d");
232
+ }
233
+
234
+ // bloom_scene_set_material_color [source: macos]
235
+ #[no_mangle]
236
+ pub extern "C" fn bloom_scene_set_material_color(handle: f64, r: f64, g: f64, b: f64, a: f64) {
237
+ $crate::ffi::guard("bloom_scene_set_material_color", move || {
238
+ engine().scene.set_material_color(handle, r as f32, g as f32, b as f32, a as f32);
239
+ })
240
+ }
241
+
242
+ // bloom_scene_set_material_pbr [source: macos]
243
+ #[no_mangle]
244
+ pub extern "C" fn bloom_scene_set_material_pbr(handle: f64, roughness: f64, metalness: f64) {
245
+ $crate::ffi::guard("bloom_scene_set_material_pbr", move || {
246
+ engine().scene.set_material_pbr(handle, roughness as f32, metalness as f32);
247
+ })
248
+ }
249
+
250
+ // bloom_scene_set_material_texture [source: macos]
251
+ #[no_mangle]
252
+ pub extern "C" fn bloom_scene_set_material_texture(handle: f64, texture_idx: f64) {
253
+ $crate::ffi::guard("bloom_scene_set_material_texture", move || {
254
+ engine().scene.set_material_texture(handle, texture_idx as u32);
255
+ })
256
+ }
257
+
258
+ // bloom_scene_node_count [source: macos]
259
+ #[no_mangle]
260
+ pub extern "C" fn bloom_scene_node_count() -> f64 {
261
+ $crate::ffi::guard("bloom_scene_node_count", move || {
262
+ engine().scene.node_count() as f64
263
+ })
264
+ }
265
+
266
+ // bloom_scene_node_vertex_count [source: macos]
267
+ #[no_mangle]
268
+ pub extern "C" fn bloom_scene_node_vertex_count(handle: f64) -> f64 {
269
+ $crate::ffi::guard("bloom_scene_node_vertex_count", move || {
270
+ match engine().scene.nodes.get(handle) {
271
+ Some(node) => node.vertices.len() as f64,
272
+ None => -1.0,
273
+ }
274
+ })
275
+ }
276
+
277
+ // bloom_scene_node_index_count [source: macos]
278
+ #[no_mangle]
279
+ pub extern "C" fn bloom_scene_node_index_count(handle: f64) -> f64 {
280
+ $crate::ffi::guard("bloom_scene_node_index_count", move || {
281
+ match engine().scene.nodes.get(handle) {
282
+ Some(node) => node.indices.len() as f64,
283
+ None => -1.0,
284
+ }
285
+ })
286
+ }
287
+
288
+ // bloom_scene_pick_all [source: macos]
289
+ #[no_mangle]
290
+ pub extern "C" fn bloom_scene_pick_all(screen_x: f64, screen_y: f64, max_results: f64) -> f64 {
291
+ $crate::ffi::guard("bloom_scene_pick_all", move || {
292
+ let mut eng = engine();
293
+ let inv_vp = eng.renderer.inverse_vp_matrix();
294
+ let cam_pos = eng.renderer.camera_pos();
295
+ let w = eng.renderer.width() as f32;
296
+ let h = eng.renderer.height() as f32;
297
+ let (origin, direction) = $crate::picking::screen_to_ray(
298
+ screen_x as f32, screen_y as f32, w, h, &inv_vp, &cam_pos,
299
+ );
300
+ let results = $crate::picking::raycast_scene_all(&eng.scene, &origin, &direction, max_results as usize);
301
+ let count = results.len();
302
+ eng.last_pick_all = results;
303
+ count as f64
304
+ })
305
+ }
306
+
307
+ // bloom_pick_all_handle [source: macos]
308
+ #[no_mangle]
309
+ pub extern "C" fn bloom_pick_all_handle(index: f64) -> f64 {
310
+ $crate::ffi::guard("bloom_pick_all_handle", move || {
311
+ let i = index as usize;
312
+ engine().last_pick_all.get(i).map(|r| r.handle).unwrap_or(0.0)
313
+ })
314
+ }
315
+
316
+ // bloom_pick_all_distance [source: macos]
317
+ #[no_mangle]
318
+ pub extern "C" fn bloom_pick_all_distance(index: f64) -> f64 {
319
+ $crate::ffi::guard("bloom_pick_all_distance", move || {
320
+ let i = index as usize;
321
+ engine().last_pick_all.get(i).map(|r| r.distance as f64).unwrap_or(0.0)
322
+ })
323
+ }
324
+
325
+ // bloom_scene_set_material_water [source: macos]
326
+ #[no_mangle]
327
+ pub extern "C" fn bloom_scene_set_material_water(handle: f64, wave_amp: f64, wave_speed: f64, r: f64, g: f64, b: f64, a: f64) {
328
+ $crate::ffi::guard("bloom_scene_set_material_water", move || {
329
+ engine().scene.set_material_water(handle, wave_amp as f32, wave_speed as f32, r as f32, g as f32, b as f32, a as f32);
330
+ })
331
+ }
332
+
333
+ // bloom_scene_get_transform [source: macos]
334
+ #[no_mangle]
335
+ pub extern "C" fn bloom_scene_get_transform(handle: f64, index: f64) -> f64 {
336
+ $crate::ffi::guard("bloom_scene_get_transform", move || {
337
+ let mat = engine().scene.get_transform(handle);
338
+ let i = index as usize;
339
+ let col = i / 4;
340
+ let row = i % 4;
341
+ if col < 4 && row < 4 { mat[col][row] as f64 } else { 0.0 }
342
+ })
343
+ }
344
+
345
+ // bloom_scene_get_bounds_min_x [source: macos]
346
+ #[no_mangle]
347
+ pub extern "C" fn bloom_scene_get_bounds_min_x(handle: f64) -> f64 {
348
+ $crate::ffi::guard("bloom_scene_get_bounds_min_x", move || { engine().scene.get_bounds(handle).0[0] as f64 })
349
+ }
350
+
351
+ // bloom_scene_get_bounds_min_y [source: macos]
352
+ #[no_mangle]
353
+ pub extern "C" fn bloom_scene_get_bounds_min_y(handle: f64) -> f64 {
354
+ $crate::ffi::guard("bloom_scene_get_bounds_min_y", move || { engine().scene.get_bounds(handle).0[1] as f64 })
355
+ }
356
+
357
+ // bloom_scene_get_bounds_min_z [source: macos]
358
+ #[no_mangle]
359
+ pub extern "C" fn bloom_scene_get_bounds_min_z(handle: f64) -> f64 {
360
+ $crate::ffi::guard("bloom_scene_get_bounds_min_z", move || { engine().scene.get_bounds(handle).0[2] as f64 })
361
+ }
362
+
363
+ // bloom_scene_get_bounds_max_x [source: macos]
364
+ #[no_mangle]
365
+ pub extern "C" fn bloom_scene_get_bounds_max_x(handle: f64) -> f64 {
366
+ $crate::ffi::guard("bloom_scene_get_bounds_max_x", move || { engine().scene.get_bounds(handle).1[0] as f64 })
367
+ }
368
+
369
+ // bloom_scene_get_bounds_max_y [source: macos]
370
+ #[no_mangle]
371
+ pub extern "C" fn bloom_scene_get_bounds_max_y(handle: f64) -> f64 {
372
+ $crate::ffi::guard("bloom_scene_get_bounds_max_y", move || { engine().scene.get_bounds(handle).1[1] as f64 })
373
+ }
374
+
375
+ // bloom_scene_get_bounds_max_z [source: macos]
376
+ #[no_mangle]
377
+ pub extern "C" fn bloom_scene_get_bounds_max_z(handle: f64) -> f64 {
378
+ $crate::ffi::guard("bloom_scene_get_bounds_max_z", move || { engine().scene.get_bounds(handle).1[2] as f64 })
379
+ }
380
+
381
+ // bloom_scene_set_user_data [source: macos]
382
+ #[no_mangle]
383
+ pub extern "C" fn bloom_scene_set_user_data(handle: f64, data: f64) {
384
+ $crate::ffi::guard("bloom_scene_set_user_data", move || {
385
+ engine().scene.set_user_data(handle, data as i64);
386
+ })
387
+ }
388
+
389
+ // bloom_scene_get_user_data [source: macos]
390
+ #[no_mangle]
391
+ pub extern "C" fn bloom_scene_get_user_data(handle: f64) -> f64 {
392
+ $crate::ffi::guard("bloom_scene_get_user_data", move || {
393
+ engine().scene.get_user_data(handle) as f64
394
+ })
395
+ }
396
+
397
+ // bloom_scene_extrude_polygon [source: macos]
398
+ #[no_mangle]
399
+ pub extern "C" fn bloom_scene_extrude_polygon(
400
+ handle: f64,
401
+ polygon_ptr: *const f64,
402
+ polygon_count: f64,
403
+ depth: f64,
404
+ ) {
405
+ $crate::ffi::guard("bloom_scene_extrude_polygon", move || {
406
+ if polygon_ptr.is_null() { return; }
407
+ let n = polygon_count as usize;
408
+ let polygon = unsafe { std::slice::from_raw_parts(polygon_ptr, n * 2) };
409
+
410
+ let geo = $crate::geometry::extrude_polygon(polygon, &[], depth);
411
+ engine().scene.update_geometry(handle, geo.vertices, geo.indices);
412
+ })
413
+ }
414
+
415
+ // bloom_scene_subtract_box [source: macos]
416
+ #[no_mangle]
417
+ pub extern "C" fn bloom_scene_subtract_box(
418
+ handle: f64,
419
+ min_x: f64, min_y: f64, min_z: f64,
420
+ max_x: f64, max_y: f64, max_z: f64,
421
+ ) {
422
+ $crate::ffi::guard("bloom_scene_subtract_box", move || {
423
+ let mut eng = engine();
424
+ if let Some(node) = eng.scene.nodes.get(handle) {
425
+ let current = $crate::geometry::GeometryData {
426
+ vertices: node.vertices.clone(),
427
+ indices: node.indices.clone(),
428
+ };
429
+ let result = $crate::geometry::subtract_box(
430
+ &current,
431
+ [min_x as f32, min_y as f32, min_z as f32],
432
+ [max_x as f32, max_y as f32, max_z as f32],
433
+ );
434
+ eng.scene.update_geometry(handle, result.vertices, result.indices);
435
+ }
436
+ })
437
+ }
438
+
439
+ // bloom_scene_attach_model [source: linux; gated: models3d]
440
+ #[cfg(feature = "models3d")]
441
+ #[no_mangle]
442
+ pub extern "C" fn bloom_scene_attach_model(node_handle: f64, model_handle: f64, mesh_index: f64) {
443
+ $crate::ffi::guard("bloom_scene_attach_model", move || {
444
+ let mut eng = engine();
445
+ let mi = mesh_index as usize;
446
+
447
+ let model_data = match eng.models.models.get(model_handle) {
448
+ Some(md) => md,
449
+ None => return,
450
+ };
451
+ if mi >= model_data.meshes.len() { return; }
452
+ let mesh = &model_data.meshes[mi];
453
+
454
+ let vertices = mesh.vertices.clone();
455
+ let indices = mesh.indices.clone();
456
+ let base_color_tex = mesh.texture_idx;
457
+ let normal_tex = mesh.normal_texture_idx;
458
+ let mr_tex = mesh.metallic_roughness_texture_idx;
459
+ let emissive_tex = mesh.emissive_texture_idx;
460
+ let emissive_factor = mesh.emissive_factor;
461
+ let roughness_factor = mesh.roughness_factor;
462
+ let metallic_factor = mesh.metallic_factor;
463
+ let alpha_cutoff = mesh.alpha_cutoff;
464
+ eng.scene.update_geometry(node_handle, vertices, indices);
465
+
466
+ if let Some(tex_idx) = base_color_tex {
467
+ eng.scene.set_material_texture(node_handle, tex_idx);
468
+ }
469
+ if let Some(tex_idx) = normal_tex {
470
+ eng.scene.set_material_normal_texture(node_handle, tex_idx);
471
+ }
472
+ if let Some(tex_idx) = mr_tex {
473
+ eng.scene.set_material_metallic_roughness_texture(node_handle, tex_idx);
474
+ }
475
+ if let Some(tex_idx) = emissive_tex {
476
+ eng.scene.set_material_emissive_texture(node_handle, tex_idx);
477
+ }
478
+ eng.scene.set_material_emissive_factor(
479
+ node_handle,
480
+ emissive_factor[0],
481
+ emissive_factor[1],
482
+ emissive_factor[2],
483
+ );
484
+ // Carry the glTF material factors + MASK cutoff too —
485
+ // previously dropped, which left attached foliage opaque
486
+ // (solid cards) and every attached mesh at the default
487
+ // roughness 0.8 regardless of its authored material.
488
+ eng.scene.set_material_pbr(node_handle, roughness_factor, metallic_factor);
489
+ eng.scene.set_material_alpha_cutoff(node_handle, alpha_cutoff);
490
+ })
491
+ }
492
+ #[cfg(not(feature = "models3d"))]
493
+ #[no_mangle]
494
+ pub extern "C" fn bloom_scene_attach_model(_node_handle: f64, _model_handle: f64, _mesh_index: f64) {
495
+ $crate::ffi::feature_off_warn_once("bloom_scene_attach_model", "models3d");
496
+ }
497
+
498
+ // bloom_project_to_screen [source: macos]
499
+ #[no_mangle]
500
+ pub extern "C" fn bloom_project_to_screen(wx: f64, wy: f64, wz: f64) -> f64 {
501
+ $crate::ffi::guard("bloom_project_to_screen", move || {
502
+ let eng = engine();
503
+ let vp = eng.renderer.vp_matrix();
504
+ let w = eng.renderer.width() as f32;
505
+ let h = eng.renderer.height() as f32;
506
+
507
+ // Multiply by VP matrix
508
+ let x = wx as f32;
509
+ let y = wy as f32;
510
+ let z = wz as f32;
511
+ let clip_x = vp[0][0]*x + vp[1][0]*y + vp[2][0]*z + vp[3][0];
512
+ let clip_y = vp[0][1]*x + vp[1][1]*y + vp[2][1]*z + vp[3][1];
513
+ let clip_w = vp[0][3]*x + vp[1][3]*y + vp[2][3]*z + vp[3][3];
514
+
515
+ if clip_w <= 0.0 {
516
+ engine().last_project = (-9999.0, -9999.0);
517
+ return -9999.0;
518
+ }
519
+
520
+ // NDC to screen
521
+ let ndc_x = clip_x / clip_w;
522
+ let ndc_y = clip_y / clip_w;
523
+ let screen_x = ((ndc_x + 1.0) * 0.5 * w) as f64;
524
+ let screen_y = ((1.0 - ndc_y) * 0.5 * h) as f64;
525
+
526
+ engine().last_project = (screen_x, screen_y);
527
+ screen_x
528
+ })
529
+ }
530
+
531
+ // bloom_project_screen_y [source: macos]
532
+ #[no_mangle]
533
+ pub extern "C" fn bloom_project_screen_y() -> f64 {
534
+ $crate::ffi::guard("bloom_project_screen_y", move || {
535
+ engine().last_project.1
536
+ })
537
+ }
538
+
539
+ // bloom_scene_pick [source: macos]
540
+ #[no_mangle]
541
+ pub extern "C" fn bloom_scene_pick(screen_x: f64, screen_y: f64) -> f64 {
542
+ $crate::ffi::guard("bloom_scene_pick", move || {
543
+ let eng = engine();
544
+ let inv_vp = eng.renderer.inverse_vp_matrix();
545
+ let cam_pos = eng.renderer.camera_pos();
546
+ let w = eng.renderer.width() as f32;
547
+ let h = eng.renderer.height() as f32;
548
+
549
+ let (origin, direction) = $crate::picking::screen_to_ray(
550
+ screen_x as f32, screen_y as f32,
551
+ w, h, &inv_vp, &cam_pos,
552
+ );
553
+
554
+ let result = $crate::picking::raycast_scene(&eng.scene, &origin, &direction);
555
+ let hit = result.hit;
556
+ engine().last_pick = Some(result);
557
+ if hit { 1.0 } else { 0.0 }
558
+ })
559
+ }
560
+
561
+ // bloom_pick_hit_handle [source: macos]
562
+ #[no_mangle]
563
+ pub extern "C" fn bloom_pick_hit_handle() -> f64 {
564
+ $crate::ffi::guard("bloom_pick_hit_handle", move || {
565
+ engine().last_pick.as_ref().map(|r| r.handle).unwrap_or(0.0)
566
+ })
567
+ }
568
+
569
+ // bloom_pick_hit_distance [source: macos]
570
+ #[no_mangle]
571
+ pub extern "C" fn bloom_pick_hit_distance() -> f64 {
572
+ $crate::ffi::guard("bloom_pick_hit_distance", move || {
573
+ engine().last_pick.as_ref().map(|r| r.distance as f64).unwrap_or(0.0)
574
+ })
575
+ }
576
+
577
+ // bloom_pick_hit_x [source: macos]
578
+ #[no_mangle]
579
+ pub extern "C" fn bloom_pick_hit_x() -> f64 {
580
+ $crate::ffi::guard("bloom_pick_hit_x", move || {
581
+ engine().last_pick.as_ref().map(|r| r.point[0] as f64).unwrap_or(0.0)
582
+ })
583
+ }
584
+
585
+ // bloom_pick_hit_y [source: macos]
586
+ #[no_mangle]
587
+ pub extern "C" fn bloom_pick_hit_y() -> f64 {
588
+ $crate::ffi::guard("bloom_pick_hit_y", move || {
589
+ engine().last_pick.as_ref().map(|r| r.point[1] as f64).unwrap_or(0.0)
590
+ })
591
+ }
592
+
593
+ // bloom_pick_hit_z [source: macos]
594
+ #[no_mangle]
595
+ pub extern "C" fn bloom_pick_hit_z() -> f64 {
596
+ $crate::ffi::guard("bloom_pick_hit_z", move || {
597
+ engine().last_pick.as_ref().map(|r| r.point[2] as f64).unwrap_or(0.0)
598
+ })
599
+ }
600
+
601
+ // bloom_pick_hit_normal_x [source: macos]
602
+ #[no_mangle]
603
+ pub extern "C" fn bloom_pick_hit_normal_x() -> f64 {
604
+ $crate::ffi::guard("bloom_pick_hit_normal_x", move || {
605
+ engine().last_pick.as_ref().map(|r| r.normal[0] as f64).unwrap_or(0.0)
606
+ })
607
+ }
608
+
609
+ // bloom_pick_hit_normal_y [source: macos]
610
+ #[no_mangle]
611
+ pub extern "C" fn bloom_pick_hit_normal_y() -> f64 {
612
+ $crate::ffi::guard("bloom_pick_hit_normal_y", move || {
613
+ engine().last_pick.as_ref().map(|r| r.normal[1] as f64).unwrap_or(0.0)
614
+ })
615
+ }
616
+
617
+ // bloom_pick_hit_normal_z [source: macos]
618
+ #[no_mangle]
619
+ pub extern "C" fn bloom_pick_hit_normal_z() -> f64 {
620
+ $crate::ffi::guard("bloom_pick_hit_normal_z", move || {
621
+ engine().last_pick.as_ref().map(|r| r.normal[2] as f64).unwrap_or(0.0)
622
+ })
623
+ }
624
+
625
+ };
626
+ }