@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,388 @@
1
+ //! Texture/image/font/file loading, staging, screenshots.
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_assets {
11
+ () => {
12
+
13
+ // bloom_take_screenshot [source: macos]
14
+ #[no_mangle]
15
+ pub extern "C" fn bloom_take_screenshot(path_ptr: *const u8) {
16
+ $crate::ffi::guard("bloom_take_screenshot", move || {
17
+ let path = $crate::string_header::str_from_header(path_ptr).to_string();
18
+ eprintln!("bloom: screenshot requested -> '{}'", path);
19
+ let mut eng = engine();
20
+ eng.renderer.screenshot_requested = true;
21
+ eng.renderer.pending_screenshot_path = Some(path);
22
+ })
23
+ }
24
+
25
+ // bloom_load_font [source: macos]
26
+ #[no_mangle]
27
+ pub extern "C" fn bloom_load_font(path_ptr: *const u8, _size: f64) -> f64 {
28
+ $crate::ffi::guard("bloom_load_font", move || {
29
+ let path = $crate::string_header::str_from_header(path_ptr);
30
+ let path: &str = &bloom_resolve_asset_path(path);
31
+ match std::fs::read(path) {
32
+ Ok(data) => engine().text.load_font(&data) as f64,
33
+ Err(_) => 0.0,
34
+ }
35
+ })
36
+ }
37
+
38
+ // bloom_unload_font [source: macos]
39
+ #[no_mangle]
40
+ pub extern "C" fn bloom_unload_font(font_handle: f64) {
41
+ $crate::ffi::guard("bloom_unload_font", move || {
42
+ engine().text.unload_font(font_handle as usize);
43
+ })
44
+ }
45
+
46
+ // bloom_load_sound [source: curated]
47
+ #[no_mangle]
48
+ pub extern "C" fn bloom_load_sound(path_ptr: *const u8) -> f64 {
49
+ $crate::ffi::guard("bloom_load_sound", move || {
50
+ let path = $crate::string_header::str_from_header(path_ptr);
51
+ let path: &str = &bloom_resolve_asset_path(path);
52
+ match std::fs::read(path) {
53
+ Ok(data) => match $crate::audio::decode_audio(path, &data) {
54
+ Some(s) => engine().audio.load_sound(s),
55
+ None => 0.0,
56
+ },
57
+ Err(_) => 0.0,
58
+ }
59
+ })
60
+ }
61
+
62
+ // bloom_load_texture [source: curated]
63
+ #[no_mangle]
64
+ pub extern "C" fn bloom_load_texture(path_ptr: *const u8) -> f64 {
65
+ $crate::ffi::guard("bloom_load_texture", move || {
66
+ let path = $crate::string_header::str_from_header(path_ptr);
67
+ let path: &str = &bloom_resolve_asset_path(path);
68
+ match std::fs::read(path) {
69
+ Ok(data) => {
70
+ let mut eng = engine();
71
+ let $crate::engine::EngineState { ref mut textures, ref mut renderer, .. } = *eng;
72
+ textures.load_texture(renderer, &data)
73
+ }
74
+ Err(_) => 0.0,
75
+ }
76
+ })
77
+ }
78
+
79
+ // bloom_unload_texture [source: curated]
80
+ #[no_mangle]
81
+ pub extern "C" fn bloom_unload_texture(handle: f64) {
82
+ $crate::ffi::guard("bloom_unload_texture", move || {
83
+ let mut eng = engine();
84
+ let $crate::engine::EngineState { ref mut textures, ref mut renderer, .. } = *eng;
85
+ textures.unload_texture(handle, renderer);
86
+ })
87
+ }
88
+
89
+ // bloom_get_texture_width [source: macos]
90
+ #[no_mangle]
91
+ pub extern "C" fn bloom_get_texture_width(handle: f64) -> f64 {
92
+ $crate::ffi::guard("bloom_get_texture_width", move || {
93
+ let eng = engine();
94
+ eng.textures.get(handle).map(|t| t.width as f64).unwrap_or(0.0)
95
+ })
96
+ }
97
+
98
+ // bloom_get_texture_height [source: macos]
99
+ #[no_mangle]
100
+ pub extern "C" fn bloom_get_texture_height(handle: f64) -> f64 {
101
+ $crate::ffi::guard("bloom_get_texture_height", move || {
102
+ let eng = engine();
103
+ eng.textures.get(handle).map(|t| t.height as f64).unwrap_or(0.0)
104
+ })
105
+ }
106
+
107
+ // bloom_load_image [source: macos]
108
+ #[no_mangle]
109
+ pub extern "C" fn bloom_load_image(path_ptr: *const u8) -> f64 {
110
+ $crate::ffi::guard("bloom_load_image", move || {
111
+ let path = $crate::string_header::str_from_header(path_ptr);
112
+ let path: &str = &bloom_resolve_asset_path(path);
113
+ match std::fs::read(path) {
114
+ Ok(data) => engine().textures.load_image(&data),
115
+ Err(_) => 0.0,
116
+ }
117
+ })
118
+ }
119
+
120
+ // bloom_image_resize [source: macos]
121
+ #[no_mangle]
122
+ pub extern "C" fn bloom_image_resize(handle: f64, w: f64, h: f64) {
123
+ $crate::ffi::guard("bloom_image_resize", move || {
124
+ engine().textures.image_resize(handle, w as u32, h as u32);
125
+ })
126
+ }
127
+
128
+ // bloom_image_crop [source: macos]
129
+ #[no_mangle]
130
+ pub extern "C" fn bloom_image_crop(handle: f64, x: f64, y: f64, w: f64, h: f64) {
131
+ $crate::ffi::guard("bloom_image_crop", move || {
132
+ engine().textures.image_crop(handle, x as u32, y as u32, w as u32, h as u32);
133
+ })
134
+ }
135
+
136
+ // bloom_image_flip_h [source: macos]
137
+ #[no_mangle]
138
+ pub extern "C" fn bloom_image_flip_h(handle: f64) {
139
+ $crate::ffi::guard("bloom_image_flip_h", move || {
140
+ engine().textures.image_flip_h(handle);
141
+ })
142
+ }
143
+
144
+ // bloom_image_flip_v [source: macos]
145
+ #[no_mangle]
146
+ pub extern "C" fn bloom_image_flip_v(handle: f64) {
147
+ $crate::ffi::guard("bloom_image_flip_v", move || {
148
+ engine().textures.image_flip_v(handle);
149
+ })
150
+ }
151
+
152
+ // bloom_load_texture_from_image [source: curated]
153
+ #[no_mangle]
154
+ pub extern "C" fn bloom_load_texture_from_image(handle: f64) -> f64 {
155
+ $crate::ffi::guard("bloom_load_texture_from_image", move || {
156
+ let mut eng = engine();
157
+ let $crate::engine::EngineState { ref mut textures, ref mut renderer, .. } = *eng;
158
+ textures.load_texture_from_image(handle, renderer)
159
+ })
160
+ }
161
+
162
+ // bloom_gen_texture_mipmaps [source: macos]
163
+ #[no_mangle]
164
+ pub extern "C" fn bloom_gen_texture_mipmaps(_handle: f64) {
165
+ $crate::ffi::guard("bloom_gen_texture_mipmaps", move || {
166
+ // Mipmap generation is handled by the GPU texture creation pipeline
167
+ // This is a no-op for now as wgpu handles mipmaps internally
168
+ })
169
+ }
170
+
171
+ // bloom_load_shader [source: macos]
172
+ #[no_mangle]
173
+ pub extern "C" fn bloom_load_shader(source_ptr: *const u8) -> f64 {
174
+ $crate::ffi::guard("bloom_load_shader", move || {
175
+ let source = $crate::string_header::str_from_header(source_ptr);
176
+ engine().renderer.load_custom_shader(source) as f64
177
+ })
178
+ }
179
+
180
+ // bloom_load_music [source: curated]
181
+ #[no_mangle]
182
+ pub extern "C" fn bloom_load_music(path_ptr: *const u8) -> f64 {
183
+ $crate::ffi::guard("bloom_load_music", move || {
184
+ let path = $crate::string_header::str_from_header(path_ptr);
185
+ let path: &str = &bloom_resolve_asset_path(path);
186
+ match std::fs::read(path) {
187
+ // Streams OGG/MP3 from the compressed bytes (background
188
+ // decode worker); WAV and wasm32 fully decode.
189
+ Ok(data) => engine().audio.load_music_bytes(path, data),
190
+ Err(_) => 0.0,
191
+ }
192
+ })
193
+ }
194
+
195
+ // bloom_write_file [source: macos]
196
+ #[no_mangle]
197
+ pub extern "C" fn bloom_write_file(path_ptr: *const u8, data_ptr: *const u8) -> f64 {
198
+ $crate::ffi::guard("bloom_write_file", move || {
199
+ let path = $crate::string_header::str_from_header(path_ptr);
200
+ let path: &str = &bloom_resolve_asset_path(path);
201
+ // A string that failed ABI validation must NOT be written as "" and
202
+ // reported as a success. That is not a save, it is a deletion with a
203
+ // thumbs-up, and it is exactly what happened to every world the
204
+ // editor ever saved.
205
+ let data = match $crate::string_header::try_str_from_header(data_ptr) {
206
+ Some(d) => d,
207
+ None => return 0.0,
208
+ };
209
+ match std::fs::write(path, data.as_bytes()) {
210
+ Ok(_) => 1.0,
211
+ Err(_) => 0.0,
212
+ }
213
+ })
214
+ }
215
+
216
+ // bloom_launch_process [EN-048]
217
+ //
218
+ // Perry's `child_process.spawn` COMPILES and then does nothing — it returns a
219
+ // child with an undefined pid and no process is started. So a tool that wants
220
+ // to launch another program (the editor's play-in-editor: save the level, run
221
+ // the game on it) has nowhere to go.
222
+ //
223
+ // Fire-and-forget by design. The caller is a GUI that must not block on, or
224
+ // die with, the thing it launched: close the game, and you are back in the
225
+ // editor with your undo history intact.
226
+ //
227
+ // `args` is newline-separated. Not shell-escaped and not shell-interpreted —
228
+ // there is no shell here, which is also why there is nothing to inject into.
229
+ #[no_mangle]
230
+ pub extern "C" fn bloom_launch_process(
231
+ cmd_ptr: *const u8, args_ptr: *const u8, cwd_ptr: *const u8,
232
+ ) -> f64 {
233
+ $crate::ffi::guard("bloom_launch_process", move || {
234
+ let cmd = $crate::string_header::str_from_header(cmd_ptr);
235
+ if cmd.is_empty() { return 0.0; }
236
+ let args = $crate::string_header::str_from_header(args_ptr);
237
+ let cwd = $crate::string_header::str_from_header(cwd_ptr);
238
+
239
+ // Resolve the program against `cwd` when it is a bare name.
240
+ //
241
+ // Rust's `Command::current_dir` sets the CHILD's working directory --
242
+ // it does NOT affect how the program is FOUND, which happens in the
243
+ // parent's context. So launching "main.exe" with cwd "<project>"
244
+ // fails with "program not found" even though main.exe is sitting
245
+ // right there in <project>. Which is exactly what it did.
246
+ let bare = !cmd.chars().any(|ch| ch == '/' || ch == std::path::MAIN_SEPARATOR);
247
+ let resolved: std::path::PathBuf = if !cwd.is_empty() && bare {
248
+ std::path::Path::new(cwd).join(cmd)
249
+ } else {
250
+ std::path::PathBuf::from(cmd)
251
+ };
252
+ let mut c = std::process::Command::new(&resolved);
253
+ for a in args.split('\n') {
254
+ if !a.is_empty() { c.arg(a); }
255
+ }
256
+ if !cwd.is_empty() { c.current_dir(cwd); }
257
+ // Detach: we never wait on it, and we do not want its output in ours.
258
+ c.stdin(std::process::Stdio::null())
259
+ .stdout(std::process::Stdio::null())
260
+ .stderr(std::process::Stdio::null());
261
+ let r = c.spawn();
262
+ match r {
263
+ Ok(child) => child.id() as f64,
264
+ Err(_) => 0.0,
265
+ }
266
+ })
267
+ }
268
+
269
+ // bloom_file_exists [source: macos]
270
+ #[no_mangle]
271
+ pub extern "C" fn bloom_file_exists(path_ptr: *const u8) -> f64 {
272
+ $crate::ffi::guard("bloom_file_exists", move || {
273
+ let path = $crate::string_header::str_from_header(path_ptr);
274
+ let path: &str = &bloom_resolve_asset_path(path);
275
+ if std::path::Path::new(path).exists() { 1.0 } else { 0.0 }
276
+ })
277
+ }
278
+
279
+ // bloom_read_file [source: curated]
280
+ #[no_mangle]
281
+ pub extern "C" fn bloom_read_file(path_ptr: *const u8) -> *const u8 {
282
+ $crate::ffi::guard("bloom_read_file", move || {
283
+ let path = $crate::string_header::str_from_header(path_ptr);
284
+ let path: &str = &bloom_resolve_asset_path(path);
285
+ match std::fs::read_to_string(path) {
286
+ Ok(contents) => $crate::string_header::alloc_perry_string(&contents),
287
+ Err(_) => $crate::string_header::alloc_perry_string(""),
288
+ }
289
+ })
290
+ }
291
+
292
+ // bloom_load_render_texture [source: macos]
293
+ #[no_mangle]
294
+ pub extern "C" fn bloom_load_render_texture(width: f64, height: f64) -> f64 {
295
+ $crate::ffi::guard("bloom_load_render_texture", move || {
296
+ let w = width as u32;
297
+ let h = height as u32;
298
+ let mut eng = engine();
299
+ let rt_handle = eng.textures.load_render_texture(w, h);
300
+
301
+ // Create the GPU texture via the renderer's public method.
302
+ let (bind_group_idx, _tex_vec_idx) = eng.renderer.create_render_texture(w, h);
303
+
304
+ // Register as a texture handle so drawTexture can sample it.
305
+ let tex_handle = eng.textures.textures.alloc($crate::textures::TextureData {
306
+ bind_group_idx, width: w, height: h,
307
+ });
308
+ eng.textures.set_render_texture_handle(rt_handle, tex_handle);
309
+
310
+ rt_handle
311
+ })
312
+ }
313
+
314
+ // bloom_unload_render_texture [source: macos]
315
+ #[no_mangle]
316
+ pub extern "C" fn bloom_unload_render_texture(handle: f64) {
317
+ $crate::ffi::guard("bloom_unload_render_texture", move || {
318
+ engine().textures.unload_render_texture(handle);
319
+ })
320
+ }
321
+
322
+ // bloom_stage_texture [source: macos]
323
+ #[no_mangle]
324
+ pub extern "C" fn bloom_stage_texture(path_ptr: *const u8) -> f64 {
325
+ $crate::ffi::guard("bloom_stage_texture", move || {
326
+ let path = $crate::string_header::str_from_header(path_ptr);
327
+ let path: &str = &bloom_resolve_asset_path(path);
328
+ match std::fs::read(path) {
329
+ Ok(data) => $crate::staging::decode_and_stage_texture(&data),
330
+ Err(_) => 0.0,
331
+ }
332
+ })
333
+ }
334
+
335
+ // bloom_stage_sound [source: curated]
336
+ #[no_mangle]
337
+ pub extern "C" fn bloom_stage_sound(path_ptr: *const u8) -> f64 {
338
+ $crate::ffi::guard("bloom_stage_sound", move || {
339
+ let path = $crate::string_header::str_from_header(path_ptr);
340
+ let path: &str = &bloom_resolve_asset_path(path);
341
+ let data = match std::fs::read(path) { Ok(d) => d, Err(_) => return 0.0 };
342
+ match $crate::audio::decode_audio(path, &data) {
343
+ Some(s) => $crate::staging::stage_sound(s),
344
+ None => 0.0,
345
+ }
346
+ })
347
+ }
348
+
349
+ // bloom_commit_texture [source: macos]
350
+ #[no_mangle]
351
+ pub extern "C" fn bloom_commit_texture(staging_handle: f64) -> f64 {
352
+ $crate::ffi::guard("bloom_commit_texture", move || {
353
+ let staged = match $crate::staging::take_texture(staging_handle) {
354
+ Some(s) => s,
355
+ None => return 0.0,
356
+ };
357
+ let mut eng = engine();
358
+ let bind_group_idx = eng.renderer.register_texture(staged.width, staged.height, &staged.data);
359
+ eng.textures.textures.alloc($crate::textures::TextureData {
360
+ bind_group_idx, width: staged.width, height: staged.height,
361
+ })
362
+ })
363
+ }
364
+
365
+ // bloom_commit_sound [source: macos]
366
+ #[no_mangle]
367
+ pub extern "C" fn bloom_commit_sound(staging_handle: f64) -> f64 {
368
+ $crate::ffi::guard("bloom_commit_sound", move || {
369
+ match $crate::staging::take_sound(staging_handle) {
370
+ Some(sd) => engine().audio.load_sound(sd),
371
+ None => 0.0,
372
+ }
373
+ })
374
+ }
375
+
376
+ // bloom_commit_music [source: macos]
377
+ #[no_mangle]
378
+ pub extern "C" fn bloom_commit_music(staging_handle: f64) -> f64 {
379
+ $crate::ffi::guard("bloom_commit_music", move || {
380
+ match $crate::staging::take_sound(staging_handle) {
381
+ Some(sd) => engine().audio.load_music(sd),
382
+ None => 0.0,
383
+ }
384
+ })
385
+ }
386
+
387
+ };
388
+ }
@@ -0,0 +1,184 @@
1
+ //! Sound and music playback control.
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_audio_ffi {
11
+ () => {
12
+
13
+ // bloom_play_sound [source: macos]
14
+ #[no_mangle]
15
+ pub extern "C" fn bloom_play_sound(handle: f64) {
16
+ $crate::ffi::guard("bloom_play_sound", move || {
17
+ engine().audio.play_sound(handle);
18
+ })
19
+ }
20
+
21
+ // bloom_stop_sound [source: macos]
22
+ #[no_mangle]
23
+ pub extern "C" fn bloom_stop_sound(handle: f64) {
24
+ $crate::ffi::guard("bloom_stop_sound", move || {
25
+ engine().audio.stop_sound(handle);
26
+ })
27
+ }
28
+
29
+ // bloom_play_sound_3d [source: macos]
30
+ #[no_mangle]
31
+ pub extern "C" fn bloom_play_sound_3d(handle: f64, x: f64, y: f64, z: f64) {
32
+ $crate::ffi::guard("bloom_play_sound_3d", move || {
33
+ engine().audio.play_sound_3d(handle, x as f32, y as f32, z as f32);
34
+ })
35
+ }
36
+
37
+ // bloom_play_music [source: macos]
38
+ #[no_mangle]
39
+ pub extern "C" fn bloom_play_music(handle: f64) {
40
+ $crate::ffi::guard("bloom_play_music", move || {
41
+ engine().audio.play_music(handle);
42
+ })
43
+ }
44
+
45
+ // bloom_stop_music [source: macos]
46
+ #[no_mangle]
47
+ pub extern "C" fn bloom_stop_music(handle: f64) {
48
+ $crate::ffi::guard("bloom_stop_music", move || {
49
+ engine().audio.stop_music(handle);
50
+ })
51
+ }
52
+
53
+ // bloom_is_music_playing [source: macos]
54
+ #[no_mangle]
55
+ pub extern "C" fn bloom_is_music_playing(handle: f64) -> f64 {
56
+ $crate::ffi::guard("bloom_is_music_playing", move || {
57
+ if engine().audio.is_music_playing(handle) { 1.0 } else { 0.0 }
58
+ })
59
+ }
60
+
61
+ // ---- EN-029: buses, reverb send, occlusion low-pass -------------
62
+ //
63
+ // The mixer was master + per-voice gain and nothing else. These three
64
+ // additions are what separate "sounds are playing" from "the space
65
+ // sounds like a place": a bus you can duck, a tail you can send to, and
66
+ // a filter that makes a wall audible.
67
+
68
+ // bloom_set_sound_bus — 0 = SFX, 1 = music, 2 = UI.
69
+ #[no_mangle]
70
+ pub extern "C" fn bloom_set_sound_bus(handle: f64, bus: f64) {
71
+ $crate::ffi::guard("bloom_set_sound_bus", move || {
72
+ engine().audio.set_sound_bus(handle, bus as u8);
73
+ })
74
+ }
75
+
76
+ // bloom_set_sound_reverb_send — 0..1.
77
+ #[no_mangle]
78
+ pub extern "C" fn bloom_set_sound_reverb_send(handle: f64, send: f64) {
79
+ $crate::ffi::guard("bloom_set_sound_reverb_send", move || {
80
+ engine().audio.set_sound_reverb_send(handle, send as f32);
81
+ })
82
+ }
83
+
84
+ // bloom_set_sound_lowpass — cutoff Hz; 0 = bypass.
85
+ #[no_mangle]
86
+ pub extern "C" fn bloom_set_sound_lowpass(handle: f64, cutoff: f64) {
87
+ $crate::ffi::guard("bloom_set_sound_lowpass", move || {
88
+ engine().audio.set_sound_lowpass(handle, cutoff as f32);
89
+ })
90
+ }
91
+
92
+ // bloom_set_bus_gain
93
+ #[no_mangle]
94
+ pub extern "C" fn bloom_set_bus_gain(bus: f64, gain: f64) {
95
+ $crate::ffi::guard("bloom_set_bus_gain", move || {
96
+ engine().audio.set_bus_gain(bus as u8, gain as f32);
97
+ })
98
+ }
99
+
100
+ // bloom_duck_bus — momentary attenuation with attack/hold/release.
101
+ #[no_mangle]
102
+ pub extern "C" fn bloom_duck_bus(bus: f64, amount: f64, attack: f64, release: f64, hold: f64) {
103
+ $crate::ffi::guard("bloom_duck_bus", move || {
104
+ engine().audio.duck_bus(
105
+ bus as u8, amount as f32, attack as f32, release as f32, hold as f32);
106
+ })
107
+ }
108
+
109
+ // bloom_set_reverb — size / damp / wet, all 0..1. wet = 0 bypasses the
110
+ // whole reverb path, so it costs nothing until a game asks for it.
111
+ #[no_mangle]
112
+ pub extern "C" fn bloom_set_reverb(size: f64, damp: f64, wet: f64) {
113
+ $crate::ffi::guard("bloom_set_reverb", move || {
114
+ engine().audio.set_reverb(size as f32, damp as f32, wet as f32);
115
+ })
116
+ }
117
+
118
+ // ---- EN-062: live spatial voices ---------------------------------
119
+ //
120
+ // bloom_play_sound_3d is fire-and-forget: it cannot loop and it cannot
121
+ // move. These six calls are the emitter API — a voice id you hold onto
122
+ // and steer. A river you walk along, wind in a treeline, a creature
123
+ // circling closer: all of them are one looping voice plus a per-frame
124
+ // position/volume ride.
125
+
126
+ // bloom_play_sound_3d_ex — returns the voice id (0 = unknown sound).
127
+ // looping != 0 loops until bloom_voice_stop. ref_dist = full-volume
128
+ // range, rolloff = fall-off steepness (1,1 == the classic 1/d),
129
+ // max_dist = cull range (<= 0 = never cull).
130
+ #[no_mangle]
131
+ pub extern "C" fn bloom_play_sound_3d_ex(
132
+ handle: f64, x: f64, y: f64, z: f64,
133
+ looping: f64, ref_dist: f64, max_dist: f64, rolloff: f64,
134
+ ) -> f64 {
135
+ $crate::ffi::guard("bloom_play_sound_3d_ex", move || {
136
+ engine().audio.play_sound_3d_ex(
137
+ handle, x as f32, y as f32, z as f32,
138
+ looping != 0.0, ref_dist as f32, max_dist as f32, rolloff as f32)
139
+ })
140
+ }
141
+
142
+ // bloom_voice_set_position — move a live voice (doppler falls out of
143
+ // the motion automatically).
144
+ #[no_mangle]
145
+ pub extern "C" fn bloom_voice_set_position(voice: f64, x: f64, y: f64, z: f64) {
146
+ $crate::ffi::guard("bloom_voice_set_position", move || {
147
+ engine().audio.set_voice_position(voice, x as f32, y as f32, z as f32);
148
+ })
149
+ }
150
+
151
+ // bloom_voice_stop — click-free: fades over one mix block, then drops.
152
+ #[no_mangle]
153
+ pub extern "C" fn bloom_voice_stop(voice: f64) {
154
+ $crate::ffi::guard("bloom_voice_stop", move || {
155
+ engine().audio.stop_voice(voice);
156
+ })
157
+ }
158
+
159
+ // bloom_voice_set_volume
160
+ #[no_mangle]
161
+ pub extern "C" fn bloom_voice_set_volume(voice: f64, volume: f64) {
162
+ $crate::ffi::guard("bloom_voice_set_volume", move || {
163
+ engine().audio.set_voice_volume(voice, volume as f32);
164
+ })
165
+ }
166
+
167
+ // bloom_voice_set_pitch — 0.25..4 playback-rate multiplier.
168
+ #[no_mangle]
169
+ pub extern "C" fn bloom_voice_set_pitch(voice: f64, pitch: f64) {
170
+ $crate::ffi::guard("bloom_voice_set_pitch", move || {
171
+ engine().audio.set_voice_pitch(voice, pitch as f32);
172
+ })
173
+ }
174
+
175
+ // bloom_voice_set_lowpass — per-VOICE occlusion (Hz; 0 = bypass).
176
+ #[no_mangle]
177
+ pub extern "C" fn bloom_voice_set_lowpass(voice: f64, cutoff: f64) {
178
+ $crate::ffi::guard("bloom_voice_set_lowpass", move || {
179
+ engine().audio.set_voice_lowpass(voice, cutoff as f32);
180
+ })
181
+ }
182
+
183
+ };
184
+ }