@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,710 @@
1
+ //! Material-system FFI surface for web: material compile/param/
2
+ //! draw-submission exports plus instance buffers, texture arrays and
3
+ //! reflection-probe wiring. Split from lib.rs (2000-line file policy).
4
+
5
+ use crate::engine;
6
+ use wasm_bindgen::prelude::*;
7
+
8
+ // ============================================================
9
+ // Phase 1c — material system FFI
10
+ // ============================================================
11
+
12
+ #[wasm_bindgen]
13
+ pub fn bloom_set_material_params(_handle: f64, _params_ptr: f64, _param_count: f64) {
14
+ // No-op: JS glue calls bloom_set_material_params_floats instead
15
+ }
16
+
17
+ #[wasm_bindgen]
18
+ pub fn bloom_set_material_params_floats(handle: f64, params: &[f32]) {
19
+ let count = params.len();
20
+ if count > 64 {
21
+ web_sys::console::error_1(&format!(
22
+ "[material] set_material_params: param_count {} > 64 (256-byte UBO cap)",
23
+ count
24
+ ).into());
25
+ return;
26
+ }
27
+ let mut bytes = vec![0u8; count * 4];
28
+ for (i, &v) in params.iter().enumerate() {
29
+ bytes[i*4..i*4+4].copy_from_slice(&v.to_le_bytes());
30
+ }
31
+ let eng = engine();
32
+ if let Err(e) = eng.renderer.material_system.set_user_params(
33
+ &eng.renderer.device, &eng.renderer.queue,
34
+ handle as u32, &bytes,
35
+ ) {
36
+ web_sys::console::error_1(&format!("[material] set_material_params failed: {}", e).into());
37
+ }
38
+ }
39
+
40
+ #[wasm_bindgen]
41
+ pub fn bloom_compile_material(_source: f64) -> f64 {
42
+ // No-op: JS glue calls bloom_compile_material_str instead
43
+ 0.0
44
+ }
45
+
46
+ #[wasm_bindgen]
47
+ pub fn bloom_compile_material_str(source: &str) -> f64 {
48
+ match engine().renderer.compile_material(source) {
49
+ Ok(handle) => handle as f64,
50
+ Err(e) => {
51
+ web_sys::console::error_1(&format!("[material] compile failed: {:?}", e).into());
52
+ 0.0
53
+ }
54
+ }
55
+ }
56
+
57
+ #[wasm_bindgen]
58
+ pub fn bloom_compile_material_refractive(_source: f64) -> f64 {
59
+ // No-op: JS glue calls bloom_compile_material_refractive_str instead
60
+ 0.0
61
+ }
62
+
63
+ #[wasm_bindgen]
64
+ pub fn bloom_compile_material_refractive_str(source: &str) -> f64 {
65
+ use bloom_shared::renderer::material_pipeline::{FragmentProfile, Bucket};
66
+ match engine().renderer.compile_material_with_options(
67
+ source, FragmentProfile::Translucent, Bucket::Refractive, true, false,
68
+ ) {
69
+ Ok(handle) => handle as f64,
70
+ Err(e) => {
71
+ web_sys::console::error_1(&format!("[refractive] compile failed: {:?}", e).into());
72
+ 0.0
73
+ }
74
+ }
75
+ }
76
+
77
+ #[wasm_bindgen]
78
+ pub fn bloom_compile_material_transparent(_source: f64) -> f64 {
79
+ // No-op: JS glue calls bloom_compile_material_transparent_str instead
80
+ 0.0
81
+ }
82
+
83
+ #[wasm_bindgen]
84
+ pub fn bloom_compile_material_transparent_str(source: &str) -> f64 {
85
+ use bloom_shared::renderer::material_pipeline::{FragmentProfile, Bucket};
86
+ match engine().renderer.compile_material_with_options(
87
+ source, FragmentProfile::Translucent, Bucket::Transparent, false, false,
88
+ ) {
89
+ Ok(handle) => handle as f64,
90
+ Err(e) => {
91
+ web_sys::console::error_1(&format!("[material] compile failed: {:?}", e).into());
92
+ 0.0
93
+ }
94
+ }
95
+ }
96
+
97
+ #[wasm_bindgen]
98
+ pub fn bloom_compile_material_additive(_source: f64) -> f64 {
99
+ // No-op: JS glue calls bloom_compile_material_additive_str instead
100
+ 0.0
101
+ }
102
+
103
+ #[wasm_bindgen]
104
+ pub fn bloom_compile_material_additive_str(source: &str) -> f64 {
105
+ use bloom_shared::renderer::material_pipeline::{FragmentProfile, Bucket};
106
+ match engine().renderer.compile_material_with_options(
107
+ source, FragmentProfile::Translucent, Bucket::Additive, false, false,
108
+ ) {
109
+ Ok(handle) => handle as f64,
110
+ Err(e) => {
111
+ web_sys::console::error_1(&format!("[material] compile failed: {:?}", e).into());
112
+ 0.0
113
+ }
114
+ }
115
+ }
116
+
117
+ #[wasm_bindgen]
118
+ pub fn bloom_compile_material_cutout(_source: f64) -> f64 {
119
+ // No-op: JS glue calls bloom_compile_material_cutout_str instead
120
+ 0.0
121
+ }
122
+
123
+ #[wasm_bindgen]
124
+ pub fn bloom_compile_material_cutout_str(source: &str) -> f64 {
125
+ use bloom_shared::renderer::material_pipeline::{FragmentProfile, Bucket};
126
+ match engine().renderer.compile_material_with_options(
127
+ source, FragmentProfile::Opaque, Bucket::Cutout, false, false,
128
+ ) {
129
+ Ok(handle) => handle as f64,
130
+ Err(e) => {
131
+ web_sys::console::error_1(&format!("[material] compile failed: {:?}", e).into());
132
+ 0.0
133
+ }
134
+ }
135
+ }
136
+
137
+ #[wasm_bindgen]
138
+ pub fn bloom_compile_material_instanced(_source: f64) -> f64 {
139
+ // No-op: JS glue calls bloom_compile_material_instanced_str instead
140
+ 0.0
141
+ }
142
+
143
+ #[wasm_bindgen]
144
+ pub fn bloom_compile_material_instanced_str(source: &str) -> f64 {
145
+ match engine().renderer.compile_material_instanced(source) {
146
+ Ok(handle) => handle as f64,
147
+ Err(e) => {
148
+ web_sys::console::error_1(&format!("[material] instanced compile failed: {:?}", e).into());
149
+ 0.0
150
+ }
151
+ }
152
+ }
153
+
154
+ #[wasm_bindgen]
155
+ pub fn bloom_create_instance_buffer(_data_ptr: f64, _instance_count: f64) -> f64 {
156
+ // No-op: JS glue calls bloom_create_instance_buffer_floats instead
157
+ 0.0
158
+ }
159
+
160
+ #[wasm_bindgen]
161
+ pub fn bloom_create_instance_buffer_floats(data: &[f32], instance_count: f64) -> f64 {
162
+ if instance_count <= 0.0 { return 0.0; }
163
+ let count = instance_count as u32;
164
+ engine().renderer.create_instance_buffer(data, count) as f64
165
+ }
166
+
167
+ #[wasm_bindgen]
168
+ pub fn bloom_submit_material_draw_instanced(
169
+ material: f64, mesh_handle: f64, mesh_idx: f64,
170
+ instance_buffer: f64, instance_count: f64,
171
+ ) {
172
+ let eng = engine();
173
+ let handle_bits = mesh_handle.to_bits();
174
+ if let Some(model) = eng.models.get(mesh_handle) {
175
+ eng.renderer.cache_model_if_static(handle_bits, &model.meshes);
176
+ }
177
+ eng.renderer.submit_material_draw_instanced(
178
+ material as u32,
179
+ handle_bits,
180
+ mesh_idx as usize,
181
+ instance_buffer as u32,
182
+ instance_count as u32,
183
+ );
184
+ }
185
+
186
+ #[wasm_bindgen]
187
+ pub fn bloom_destroy_instance_buffer(handle: f64) {
188
+ engine().renderer.destroy_instance_buffer(handle as u32);
189
+ }
190
+
191
+ /// EN-011 — create a planar reflection probe. See macOS lib.rs for the
192
+ /// full doc comment. Web ports the same FFI surface so a TypeScript
193
+ /// game targets one API across native + browser.
194
+ #[wasm_bindgen]
195
+ pub fn bloom_create_planar_reflection(
196
+ plane_y: f64, nx: f64, ny: f64, nz: f64, resolution: f64,
197
+ ) -> f64 {
198
+ engine().renderer.create_planar_reflection(
199
+ plane_y as f32,
200
+ [nx as f32, ny as f32, nz as f32],
201
+ resolution as u32,
202
+ ) as f64
203
+ }
204
+
205
+ /// EN-011 — link a material to a planar reflection probe. `probe = 0`
206
+ /// reverts the binding to the engine's default 1×1 black texture.
207
+ #[wasm_bindgen]
208
+ pub fn bloom_set_material_reflection_probe(
209
+ material: f64, probe: f64,
210
+ ) {
211
+ engine().renderer.set_material_reflection_probe(material as u32, probe as u32);
212
+ }
213
+
214
+ /// EN-014 — pointer-shaped variant exists only so the FFI manifest
215
+ /// validates against the Perry surface; JS glue calls
216
+ /// `bloom_create_texture_array_bytes` with a `Uint8Array` instead.
217
+ /// Same precedent as `bloom_create_instance_buffer` (see EN-001).
218
+ #[wasm_bindgen]
219
+ pub fn bloom_create_texture_array(
220
+ _data_ptr: f64, _data_len: f64, _width: f64, _height: f64, _layer_count: f64,
221
+ ) -> f64 {
222
+ 0.0
223
+ }
224
+
225
+ /// EN-014 — create a texture array from concatenated RGBA8 bytes.
226
+ /// JS glue passes a `Uint8Array` of `layer_count × width × height × 4`
227
+ /// bytes (each layer back-to-back). Layer count is capped at 16; the
228
+ /// rest are silently dropped. Returns a 1-based handle (0 on failure).
229
+ #[wasm_bindgen]
230
+ pub fn bloom_create_texture_array_bytes(
231
+ data: &[u8],
232
+ width: f64, height: f64, layer_count: f64,
233
+ ) -> f64 {
234
+ // EN-014 V2 — V1 forwards to _ex with default sRGB / no mips.
235
+ bloom_create_texture_array_ex_bytes(data, width, height, layer_count, 0.0, 1.0)
236
+ }
237
+
238
+ /// EN-014 V2 — pointer-shaped Ex variant exists only so the FFI manifest
239
+ /// validates against the Perry surface; JS glue uses the `_bytes` form.
240
+ #[wasm_bindgen]
241
+ pub fn bloom_create_texture_array_ex(
242
+ _data_ptr: f64, _data_len: f64, _width: f64, _height: f64,
243
+ _layer_count: f64, _format: f64, _mip_levels: f64,
244
+ ) -> f64 {
245
+ 0.0
246
+ }
247
+
248
+ /// EN-014 V2 — bytes form of `_ex`. See `MaterialSystem::create_texture_array_ex`
249
+ /// for `format` (0 = sRGB, 1 = linear) and `mip_levels` (1 = none, 0 =
250
+ /// auto-generate via point-sample copies) semantics.
251
+ #[wasm_bindgen]
252
+ pub fn bloom_create_texture_array_ex_bytes(
253
+ data: &[u8],
254
+ width: f64, height: f64, layer_count: f64,
255
+ format: f64, mip_levels: f64,
256
+ ) -> f64 {
257
+ let w = width as u32;
258
+ let h = height as u32;
259
+ if w == 0 || h == 0 { return 0.0; }
260
+ let layers_count = (layer_count as u32)
261
+ .min(bloom_shared::renderer::material_system::MAX_TEXTURE_ARRAY_LAYERS);
262
+ if layers_count == 0 { return 0.0; }
263
+ let layer_size = (w as usize) * (h as usize) * 4;
264
+ let mut layers: Vec<(&[u8], u32, u32)> = Vec::with_capacity(layers_count as usize);
265
+ for i in 0..(layers_count as usize) {
266
+ let start = i * layer_size;
267
+ let end = start + layer_size;
268
+ if end > data.len() { break; }
269
+ layers.push((&data[start..end], w, h));
270
+ }
271
+ engine().renderer.create_texture_array_ex(&layers, format as u32, mip_levels as u32) as f64
272
+ }
273
+
274
+ /// EN-014 — link a texture-array handle to a material slot
275
+ /// (0 = albedo / 1 = normal / 2 = MR). `array = 0` reverts to the
276
+ /// engine's 1×1×1 stub.
277
+ #[wasm_bindgen]
278
+ pub fn bloom_set_material_texture_array(
279
+ material: f64, slot: f64, array: f64,
280
+ ) {
281
+ engine().renderer.set_material_texture_array(
282
+ material as u32, slot as u32, array as u32,
283
+ );
284
+ }
285
+
286
+ /// EN-012 — set the shading model for a material (0=default lit,
287
+ /// 1=foliage, 2=subsurface V2 stub).
288
+ #[wasm_bindgen]
289
+ pub fn bloom_set_material_shading_model(
290
+ material: f64, model: f64,
291
+ ) {
292
+ engine().renderer.set_material_shading_model(material as u32, model as u32);
293
+ }
294
+
295
+ /// Whether a material's draws render into planar-reflection probes
296
+ /// (default true). Authoring control for content that is sub-pixel at
297
+ /// probe resolution (e.g. instanced grass).
298
+ #[wasm_bindgen]
299
+ pub fn bloom_set_material_probe_visible(
300
+ material: f64, visible: f64,
301
+ ) {
302
+ engine().renderer.set_material_probe_visible(material as u32, visible != 0.0);
303
+ }
304
+
305
+ /// EN-012 — set the foliage shading parameters for a material.
306
+ /// Only takes effect when shading_model == 1 (foliage).
307
+ #[wasm_bindgen]
308
+ pub fn bloom_set_material_foliage(
309
+ material: f64,
310
+ trans_r: f64, trans_g: f64, trans_b: f64,
311
+ trans_amount: f64, wrap_factor: f64,
312
+ ) {
313
+ engine().renderer.set_material_foliage(
314
+ material as u32,
315
+ [trans_r as f32, trans_g as f32, trans_b as f32],
316
+ trans_amount as f32, wrap_factor as f32,
317
+ );
318
+ }
319
+
320
+ #[wasm_bindgen]
321
+ pub fn bloom_compile_material_from_file(_path: f64, _bucket_kind: f64) -> f64 {
322
+ // No-op: web has no filesystem — JS glue would have to fetch + call
323
+ // bloom_compile_material_from_file_str instead.
324
+ 0.0
325
+ }
326
+
327
+ #[wasm_bindgen]
328
+ pub fn bloom_compile_material_from_file_str(path: &str, bucket_kind: f64) -> f64 {
329
+ use bloom_shared::renderer::material_pipeline::{FragmentProfile, Bucket};
330
+ let (profile, bucket, reads_scene) = match bucket_kind as u32 {
331
+ 0 => (FragmentProfile::Opaque, Bucket::Opaque, false),
332
+ 1 => (FragmentProfile::Translucent, Bucket::Transparent, false),
333
+ 2 => (FragmentProfile::Translucent, Bucket::Refractive, true),
334
+ 3 => (FragmentProfile::Translucent, Bucket::Additive, false),
335
+ 4 => (FragmentProfile::Opaque, Bucket::Cutout, false),
336
+ _ => {
337
+ web_sys::console::error_1(&format!(
338
+ "[material] from_file: unknown bucket_kind {}", bucket_kind
339
+ ).into());
340
+ return 0.0;
341
+ }
342
+ };
343
+ match engine().renderer.compile_material_from_file(
344
+ std::path::Path::new(path), profile, bucket, reads_scene,
345
+ ) {
346
+ Ok(handle) => handle as f64,
347
+ Err(e) => {
348
+ web_sys::console::error_1(&format!("[material] from_file failed: {}", e).into());
349
+ 0.0
350
+ }
351
+ }
352
+ }
353
+
354
+ /// EN-017 — stub: JS glue calls `bloom_set_post_pass_str` instead.
355
+ #[wasm_bindgen]
356
+ pub fn bloom_set_post_pass(_source: f64) -> f64 { 0.0 }
357
+
358
+ /// EN-017 — compile + install a fullscreen post-pass material on web.
359
+ /// See `bloom-macos::bloom_set_post_pass` for the full ABI. Returns
360
+ /// 1.0 on success, 0.0 on compile failure.
361
+ #[wasm_bindgen]
362
+ pub fn bloom_set_post_pass_str(source: &str) -> f64 {
363
+ match engine().renderer.set_post_pass(source) {
364
+ Ok(()) => 1.0,
365
+ Err(e) => {
366
+ web_sys::console::error_1(
367
+ &format!("[post_pass] compile failed: {:?}", e).into(),
368
+ );
369
+ 0.0
370
+ }
371
+ }
372
+ }
373
+
374
+ /// EN-017 — uninstall the active post-pass.
375
+ #[wasm_bindgen]
376
+ pub fn bloom_clear_post_pass() {
377
+ engine().renderer.clear_post_pass();
378
+ }
379
+
380
+ /// EN-017 V2 — stub: JS glue calls `bloom_add_post_pass_str` instead.
381
+ #[wasm_bindgen]
382
+ pub fn bloom_add_post_pass(_source: f64) -> f64 { 0.0 }
383
+
384
+ /// EN-017 V2 — append a fullscreen post-pass to the stack on web.
385
+ /// See `bloom-macos::bloom_add_post_pass` for the full ABI. Returns
386
+ /// 1-based handle on success, 0.0 on compile failure.
387
+ #[wasm_bindgen]
388
+ pub fn bloom_add_post_pass_str(source: &str) -> f64 {
389
+ match engine().renderer.add_post_pass(source) {
390
+ Ok(h) => h as f64,
391
+ Err(e) => {
392
+ web_sys::console::error_1(
393
+ &format!("[post_pass] compile failed: {:?}", e).into(),
394
+ );
395
+ 0.0
396
+ }
397
+ }
398
+ }
399
+
400
+ /// EN-017 V2 — wipe the entire post-pass stack.
401
+ #[wasm_bindgen]
402
+ pub fn bloom_clear_all_post_passes() {
403
+ engine().renderer.clear_all_post_passes();
404
+ }
405
+
406
+ #[wasm_bindgen]
407
+ pub fn bloom_draw_material(
408
+ material: f64,
409
+ mesh_handle: f64,
410
+ mesh_idx: f64,
411
+ x: f64, y: f64, z: f64, scale: f64,
412
+ r: f64, g: f64, b: f64, a: f64,
413
+ ) {
414
+ let eng = engine();
415
+ let handle_bits = mesh_handle.to_bits();
416
+ if let Some(model) = eng.models.get(mesh_handle) {
417
+ eng.renderer.cache_model_if_static(handle_bits, &model.meshes);
418
+ }
419
+ eng.renderer.submit_material_draw(
420
+ material as u32,
421
+ handle_bits,
422
+ mesh_idx as usize,
423
+ [x as f32, y as f32, z as f32],
424
+ scale as f32,
425
+ [(r / 255.0) as f32, (g / 255.0) as f32, (b / 255.0) as f32, (a / 255.0) as f32],
426
+ );
427
+ }
428
+
429
+ #[wasm_bindgen]
430
+ pub fn bloom_load_model_animation(_path: f64) -> f64 { 0.0 }
431
+
432
+ #[wasm_bindgen]
433
+ pub fn bloom_load_model_animation_bytes(data: &[u8]) -> f64 {
434
+ engine().models.load_model_animation(data)
435
+ }
436
+
437
+ #[wasm_bindgen]
438
+ pub fn bloom_instantiate_animation(src: f64) -> f64 {
439
+ engine().models.instantiate_animation(src)
440
+ }
441
+
442
+ #[wasm_bindgen]
443
+ #[allow(clippy::too_many_arguments)]
444
+ pub fn bloom_update_model_animation(
445
+ handle: f64, anim_index: f64, time: f64, scale: f64,
446
+ px: f64, py: f64, pz: f64, rot_y: f64,
447
+ ) {
448
+ // Mirrors the shared macro. This was an empty stub carrying the PRE-FIX
449
+ // 9-arg (rot_sin, rot_cos) signature that native abandoned — so the
450
+ // sampled-animation API silently did nothing on web (models frozen at
451
+ // bind pose) AND its arity disagreed with the manifest, shifting every
452
+ // argument. The engine takes a single Y angle and reconstructs sin/cos
453
+ // itself with full precision and correct signs.
454
+ let rot_y_f = rot_y as f32;
455
+ let rot_sin = rot_y_f.sin();
456
+ let rot_cos = rot_y_f.cos();
457
+ let eng = engine();
458
+ eng.models.update_model_animation(handle, anim_index as usize, time as f32);
459
+ if let Some(anim) = eng.models.get_animation(handle) {
460
+ if !anim.joint_matrices.is_empty() {
461
+ // PT-7: the anim handle keys the prev-palette pairing for
462
+ // skinned motion vectors.
463
+ eng.renderer.set_joint_matrices_scaled(
464
+ handle.to_bits(), &anim.joint_matrices, scale as f32,
465
+ [px as f32, py as f32, pz as f32], rot_sin, rot_cos,
466
+ );
467
+ }
468
+ }
469
+ }
470
+
471
+ #[wasm_bindgen]
472
+ pub fn bloom_get_model_mesh_count(handle: f64) -> f64 {
473
+ match engine().models.get(handle) {
474
+ Some(model) => model.meshes.len() as f64,
475
+ None => 0.0,
476
+ }
477
+ }
478
+
479
+ #[wasm_bindgen]
480
+ pub fn bloom_get_model_material_count(handle: f64) -> f64 {
481
+ match engine().models.get(handle) {
482
+ Some(model) => model.meshes.len() as f64,
483
+ None => 0.0,
484
+ }
485
+ }
486
+
487
+ // ============================================================
488
+ // EN-028 animation mixer / EN-033 sockets / EN-026 particles /
489
+ // EN-027 decals — web ports.
490
+ //
491
+ // The mixer and both VFX systems are pure CPU state in bloom-shared, so the
492
+ // web crate gets the real behaviour, not a stub: same ModelManager, same
493
+ // ParticleManager, same DecalManager. The only web-specific care is that the
494
+ // particle/decal *upload* path goes through the same material-system dynamic
495
+ // instance buffer, which WebGPU supports.
496
+ // ============================================================
497
+
498
+ #[wasm_bindgen]
499
+ pub fn bloom_anim_play(handle: f64, clip: f64, fade: f64, speed: f64, looping: f64) {
500
+ engine().models.anim_play(handle, clip as usize, fade as f32, speed as f32, looping != 0.0);
501
+ }
502
+
503
+ #[wasm_bindgen]
504
+ pub fn bloom_anim_set_layer(handle: f64, clip: f64, weight: f64, mask_root: f64, speed: f64, looping: f64) {
505
+ engine().models.anim_set_layer(handle, clip as i32, weight as f32, mask_root as i32, speed as f32, looping != 0.0);
506
+ }
507
+
508
+ #[wasm_bindgen]
509
+ pub fn bloom_anim_set_root_motion(handle: f64, on: f64) {
510
+ engine().models.anim_set_root_motion(handle, on != 0.0);
511
+ }
512
+
513
+ #[wasm_bindgen]
514
+ pub fn bloom_anim_update(handle: f64, dt: f64, scale: f64, px: f64, py: f64, pz: f64, rot_y: f64) {
515
+ let rot_y_f = rot_y as f32;
516
+ let eng = engine();
517
+ eng.models.advance_and_update(handle, dt as f32);
518
+ if let Some(anim) = eng.models.get_animation(handle) {
519
+ if !anim.joint_matrices.is_empty() {
520
+ // PT-7: the anim handle keys the prev-palette pairing for skinned
521
+ // motion vectors — the same key the shared FFI passes
522
+ // (ffi_core/models.rs). Without it this call does not compile: the
523
+ // web backend was left behind when `key` was added, which is what
524
+ // broke build-web on main.
525
+ eng.renderer.set_joint_matrices_scaled(
526
+ handle.to_bits(), &anim.joint_matrices, scale as f32,
527
+ [px as f32, py as f32, pz as f32], rot_y_f.sin(), rot_y_f.cos());
528
+ }
529
+ }
530
+ }
531
+
532
+ #[wasm_bindgen]
533
+ pub fn bloom_anim_finished(handle: f64) -> f64 {
534
+ if engine().models.anim_finished(handle) { 1.0 } else { 0.0 }
535
+ }
536
+
537
+ #[wasm_bindgen]
538
+ pub fn bloom_anim_clip_duration(handle: f64, clip: f64) -> f64 {
539
+ engine().models.anim_clip_duration(handle, clip as usize) as f64
540
+ }
541
+
542
+ #[wasm_bindgen]
543
+ pub fn bloom_anim_root_delta(handle: f64, axis: f64) -> f64 {
544
+ let d = engine().models.anim_root_delta(handle);
545
+ let i = axis as usize;
546
+ if i < 3 { d[i] as f64 } else { 0.0 }
547
+ }
548
+
549
+ #[wasm_bindgen]
550
+ pub fn bloom_model_find_joint(handle: f64, name: String) -> f64 {
551
+ engine().models.find_joint(handle, &name) as f64
552
+ }
553
+
554
+ #[wasm_bindgen]
555
+ pub fn bloom_model_joint_world(handle: f64, joint: f64, comp: f64) -> f64 {
556
+ let j = joint as i64;
557
+ let c = comp as usize;
558
+ if j < 0 || c > 15 { return 0.0; }
559
+ match engine().models.joint_world(handle, j as usize) {
560
+ Some(m) => m[c / 4][c % 4] as f64,
561
+ None => 0.0,
562
+ }
563
+ }
564
+
565
+ #[wasm_bindgen]
566
+ pub fn bloom_particles_create(capacity: f64) -> f64 {
567
+ let cap = (capacity as usize).clamp(1, 100_000);
568
+ let eng = engine();
569
+ let ib = eng.renderer.material_system.create_dynamic_instance_buffer(&eng.renderer.device, cap as u32);
570
+ eng.particles.create(cap, ib) as f64
571
+ }
572
+
573
+ #[wasm_bindgen]
574
+ pub fn bloom_particles_configure(sys: f64) {
575
+ let eng = engine();
576
+ let params: Vec<f32> = eng.models.scratch_f32.clone();
577
+ eng.models.mesh_scratch_reset();
578
+ if let Some(s) = eng.particles.get_mut(sys as u32) {
579
+ s.configure_from_slice(&params);
580
+ }
581
+ }
582
+
583
+ #[wasm_bindgen]
584
+ pub fn bloom_particles_emit(sys: f64, x: f64, y: f64, z: f64, dx: f64, dy: f64, dz: f64, count: f64) {
585
+ if let Some(s) = engine().particles.get_mut(sys as u32) {
586
+ s.emit([x as f32, y as f32, z as f32], [dx as f32, dy as f32, dz as f32], (count as usize).min(4096));
587
+ }
588
+ }
589
+
590
+ #[wasm_bindgen]
591
+ pub fn bloom_particles_update(sys: f64, dt: f64) -> f64 {
592
+ let eng = engine();
593
+ let (live, ib) = match eng.particles.get_mut(sys as u32) {
594
+ Some(s) => (s.update(dt as f32), s.instance_buffer),
595
+ None => return 0.0,
596
+ };
597
+ if live > 0 {
598
+ let packed: Vec<f32> = match eng.particles.get_mut(sys as u32) {
599
+ Some(s) => s.packed()[..(live as usize) * 12].to_vec(),
600
+ None => return 0.0,
601
+ };
602
+ eng.renderer.material_system.update_instance_buffer(&eng.renderer.queue, ib, &packed, live);
603
+ }
604
+ live as f64
605
+ }
606
+
607
+ #[wasm_bindgen]
608
+ pub fn bloom_particles_instance_buffer(sys: f64) -> f64 {
609
+ engine().particles.get_mut(sys as u32).map(|s| s.instance_buffer as f64).unwrap_or(0.0)
610
+ }
611
+
612
+ #[wasm_bindgen]
613
+ pub fn bloom_particles_clear(sys: f64) {
614
+ if let Some(s) = engine().particles.get_mut(sys as u32) { s.clear(); }
615
+ }
616
+
617
+ #[wasm_bindgen]
618
+ pub fn bloom_particles_live(sys: f64) -> f64 {
619
+ engine().particles.get_mut(sys as u32).map(|s| s.live as f64).unwrap_or(0.0)
620
+ }
621
+
622
+ #[wasm_bindgen]
623
+ pub fn bloom_decals_init(capacity: f64) -> f64 {
624
+ let cap = (capacity as usize).clamp(1, 8192);
625
+ let eng = engine();
626
+ let ib = eng.renderer.material_system.create_dynamic_instance_buffer(&eng.renderer.device, cap as u32);
627
+ eng.decals.init(cap, ib);
628
+ ib as f64
629
+ }
630
+
631
+ #[wasm_bindgen]
632
+ pub fn bloom_decals_spawn(x: f64, y: f64, z: f64, nx: f64, ny: f64, nz: f64, size: f64, roll: f64) {
633
+ engine().decals.spawn_styled(
634
+ [x as f32, y as f32, z as f32],
635
+ [nx as f32, ny as f32, nz as f32],
636
+ size as f32, roll as f32);
637
+ }
638
+
639
+ #[wasm_bindgen]
640
+ pub fn bloom_decals_set_style(frame: f64, r: f64, g: f64, b: f64, a: f64, life: f64, fade: f64) {
641
+ engine().decals.style = bloom_shared::decals::DecalStyle {
642
+ frame: frame as f32,
643
+ color: [r as f32, g as f32, b as f32, a as f32],
644
+ life: life as f32,
645
+ fade: fade as f32,
646
+ };
647
+ }
648
+
649
+ #[wasm_bindgen]
650
+ pub fn bloom_decals_update(dt: f64) -> f64 {
651
+ let eng = engine();
652
+ let live = eng.decals.update(dt as f32);
653
+ let ib = eng.decals.instance_buffer;
654
+ if live > 0 {
655
+ let packed: Vec<f32> = eng.decals.packed()[..(live as usize) * 12].to_vec();
656
+ eng.renderer.material_system.update_instance_buffer(&eng.renderer.queue, ib, &packed, live);
657
+ }
658
+ live as f64
659
+ }
660
+
661
+ #[wasm_bindgen]
662
+ pub fn bloom_decals_instance_buffer() -> f64 {
663
+ engine().decals.instance_buffer as f64
664
+ }
665
+
666
+ #[wasm_bindgen]
667
+ pub fn bloom_decals_clear() {
668
+ engine().decals.clear();
669
+ }
670
+
671
+ // EN-029 — audio buses / reverb / occlusion low-pass. All CPU DSP in
672
+ // bloom-shared's AudioMixer, so web gets the real thing.
673
+
674
+ #[wasm_bindgen]
675
+ pub fn bloom_set_sound_bus(handle: f64, bus: f64) {
676
+ engine().audio.set_sound_bus(handle, bus as u8);
677
+ }
678
+
679
+ #[wasm_bindgen]
680
+ pub fn bloom_set_sound_reverb_send(handle: f64, send: f64) {
681
+ engine().audio.set_sound_reverb_send(handle, send as f32);
682
+ }
683
+
684
+ #[wasm_bindgen]
685
+ pub fn bloom_set_sound_lowpass(handle: f64, cutoff: f64) {
686
+ engine().audio.set_sound_lowpass(handle, cutoff as f32);
687
+ }
688
+
689
+ #[wasm_bindgen]
690
+ pub fn bloom_set_bus_gain(bus: f64, gain: f64) {
691
+ engine().audio.set_bus_gain(bus as u8, gain as f32);
692
+ }
693
+
694
+ #[wasm_bindgen]
695
+ pub fn bloom_duck_bus(bus: f64, amount: f64, attack: f64, release: f64, hold: f64) {
696
+ engine().audio.duck_bus(bus as u8, amount as f32, attack as f32, release as f32, hold as f32);
697
+ }
698
+
699
+ #[wasm_bindgen]
700
+ pub fn bloom_set_reverb(size: f64, damp: f64, wet: f64) {
701
+ engine().audio.set_reverb(size as f32, damp as f32, wet as f32);
702
+ }
703
+
704
+ #[wasm_bindgen]
705
+ pub fn bloom_compile_material_instanced_bucket(source: String, bucket: f64, reads_scene: f64) -> f64 {
706
+ match engine().renderer.compile_material_instanced_bucket(&source, bucket as u32, reads_scene != 0.0) {
707
+ Ok(handle) => handle as f64,
708
+ Err(e) => { web_sys::console::error_1(&format!("[material] instanced compile failed: {:?}", e).into()); 0.0 }
709
+ }
710
+ }