@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,286 @@
1
+ //! Input FFI surface for web: keyboard/mouse/gamepad/touch getters plus
2
+ //! the injection entry points the JS glue calls from DOM event
3
+ //! listeners. Split from lib.rs (2000-line file policy).
4
+
5
+ use crate::engine;
6
+ use wasm_bindgen::prelude::*;
7
+
8
+ // ============================================================
9
+ // Input - Keyboard
10
+ // ============================================================
11
+
12
+ #[wasm_bindgen]
13
+ pub fn bloom_is_key_pressed(key: f64) -> f64 {
14
+ if engine().input.is_key_pressed(key as usize) { 1.0 } else { 0.0 }
15
+ }
16
+
17
+ #[wasm_bindgen]
18
+ pub fn bloom_is_key_down(key: f64) -> f64 {
19
+ if engine().input.is_key_down(key as usize) { 1.0 } else { 0.0 }
20
+ }
21
+
22
+ #[wasm_bindgen]
23
+ pub fn bloom_is_key_released(key: f64) -> f64 {
24
+ if engine().input.is_key_released(key as usize) { 1.0 } else { 0.0 }
25
+ }
26
+
27
+ #[wasm_bindgen]
28
+ pub fn bloom_is_key_repeated(key: f64) -> f64 {
29
+ if engine().input.is_key_repeated(key as usize) { 1.0 } else { 0.0 }
30
+ }
31
+
32
+ // ============================================================
33
+ // Input - Mouse
34
+ // ============================================================
35
+
36
+ #[wasm_bindgen]
37
+ pub fn bloom_get_mouse_x() -> f64 {
38
+ engine().input.mouse_x
39
+ }
40
+
41
+ #[wasm_bindgen]
42
+ pub fn bloom_get_mouse_y() -> f64 {
43
+ engine().input.mouse_y
44
+ }
45
+
46
+ #[wasm_bindgen]
47
+ pub fn bloom_is_mouse_button_pressed(btn: f64) -> f64 {
48
+ if engine().input.is_mouse_button_pressed(btn as usize) { 1.0 } else { 0.0 }
49
+ }
50
+
51
+ #[wasm_bindgen]
52
+ pub fn bloom_is_mouse_button_down(btn: f64) -> f64 {
53
+ if engine().input.is_mouse_button_down(btn as usize) { 1.0 } else { 0.0 }
54
+ }
55
+
56
+ #[wasm_bindgen]
57
+ pub fn bloom_is_mouse_button_released(btn: f64) -> f64 {
58
+ if engine().input.is_mouse_button_released(btn as usize) { 1.0 } else { 0.0 }
59
+ }
60
+
61
+ #[wasm_bindgen]
62
+ pub fn bloom_get_mouse_delta_x() -> f64 {
63
+ engine().input.mouse_delta_x
64
+ }
65
+
66
+ #[wasm_bindgen]
67
+ pub fn bloom_get_mouse_delta_y() -> f64 {
68
+ engine().input.mouse_delta_y
69
+ }
70
+
71
+ // Accumulated scroll wheel delta since the last call. Reading consumes the
72
+ // value. Used by the editor's orbit camera and any scrollable UI panel.
73
+ #[wasm_bindgen]
74
+ pub fn bloom_get_mouse_wheel() -> f64 {
75
+ engine().input.consume_mouse_wheel()
76
+ }
77
+
78
+ #[wasm_bindgen]
79
+ pub fn bloom_get_char_pressed() -> f64 {
80
+ engine().input.pop_char() as f64
81
+ }
82
+
83
+ // Model bounds accessors. Return the axis-aligned bounding box of a loaded
84
+ // model in model-local coordinates.
85
+ #[wasm_bindgen]
86
+ pub fn bloom_get_model_bounds_min_x(model_handle: f64) -> f64 {
87
+ engine().models.get_bounds(model_handle).0[0] as f64
88
+ }
89
+ #[wasm_bindgen]
90
+ pub fn bloom_get_model_bounds_min_y(model_handle: f64) -> f64 {
91
+ engine().models.get_bounds(model_handle).0[1] as f64
92
+ }
93
+ #[wasm_bindgen]
94
+ pub fn bloom_get_model_bounds_min_z(model_handle: f64) -> f64 {
95
+ engine().models.get_bounds(model_handle).0[2] as f64
96
+ }
97
+ #[wasm_bindgen]
98
+ pub fn bloom_get_model_bounds_max_x(model_handle: f64) -> f64 {
99
+ engine().models.get_bounds(model_handle).1[0] as f64
100
+ }
101
+ #[wasm_bindgen]
102
+ pub fn bloom_get_model_bounds_max_y(model_handle: f64) -> f64 {
103
+ engine().models.get_bounds(model_handle).1[1] as f64
104
+ }
105
+ #[wasm_bindgen]
106
+ pub fn bloom_get_model_bounds_max_z(model_handle: f64) -> f64 {
107
+ engine().models.get_bounds(model_handle).1[2] as f64
108
+ }
109
+
110
+ // ============================================================
111
+ // Input - Gamepad
112
+ // ============================================================
113
+
114
+ // EN-063 — arity MUST match the manifest (and the shared macro): these
115
+ // carried a leading `gamepad` param the manifest never declared, so the
116
+ // game's single argument landed in it and the real one arrived undefined
117
+ // -> NaN -> `as usize` = 0. Every axis read axis 0 and every button read
118
+ // button 0 — silently, on the platform with no debugger. This is verbatim
119
+ // the argument-shift class ffi_core/mod.rs says the shared macro exists to
120
+ // prevent; the web crate hand-mirrors it and drifted.
121
+ #[wasm_bindgen]
122
+ pub fn bloom_is_gamepad_available() -> f64 {
123
+ if engine().input.is_gamepad_available() { 1.0 } else { 0.0 }
124
+ }
125
+
126
+ #[wasm_bindgen]
127
+ pub fn bloom_get_gamepad_axis(axis: f64) -> f64 {
128
+ engine().input.get_gamepad_axis(axis as usize) as f64
129
+ }
130
+
131
+ #[wasm_bindgen]
132
+ pub fn bloom_is_gamepad_button_pressed(button: f64) -> f64 {
133
+ if engine().input.is_gamepad_button_pressed(button as usize) { 1.0 } else { 0.0 }
134
+ }
135
+
136
+ #[wasm_bindgen]
137
+ pub fn bloom_is_gamepad_button_down(button: f64) -> f64 {
138
+ if engine().input.is_gamepad_button_down(button as usize) { 1.0 } else { 0.0 }
139
+ }
140
+
141
+ #[wasm_bindgen]
142
+ pub fn bloom_is_gamepad_button_released(button: f64) -> f64 {
143
+ if engine().input.is_gamepad_button_released(button as usize) { 1.0 } else { 0.0 }
144
+ }
145
+
146
+ #[wasm_bindgen]
147
+ pub fn bloom_get_gamepad_axis_count() -> f64 {
148
+ engine().input.get_gamepad_axis_count() as f64
149
+ }
150
+
151
+ // ============================================================
152
+ // Input - Touch
153
+ // ============================================================
154
+
155
+ // EN-063 — take the slot index, like the manifest and the shared macro.
156
+ // Hardcoding slot 0 meant every finger reported the FIRST finger's
157
+ // coordinates while `is_touch_active(i)` honoured the index: on a phone
158
+ // both thumbsticks tracked one thumb.
159
+ #[wasm_bindgen]
160
+ pub fn bloom_get_touch_x(index: f64) -> f64 {
161
+ engine().input.get_touch_x(index as usize)
162
+ }
163
+
164
+ #[wasm_bindgen]
165
+ pub fn bloom_get_touch_y(index: f64) -> f64 {
166
+ engine().input.get_touch_y(index as usize)
167
+ }
168
+
169
+ #[wasm_bindgen]
170
+ pub fn bloom_get_touch_count() -> f64 {
171
+ engine().input.get_touch_count() as f64
172
+ }
173
+
174
+ #[wasm_bindgen]
175
+ pub fn bloom_is_touch_active(index: f64) -> f64 {
176
+ if engine().input.is_touch_active(index as usize) { 1.0 } else { 0.0 }
177
+ }
178
+
179
+ #[wasm_bindgen]
180
+ pub fn bloom_get_max_touch_points() -> f64 {
181
+ engine().input.max_touch_points() as f64
182
+ }
183
+
184
+ // ============================================================
185
+ // Input injection (called from JS event listeners)
186
+ // ============================================================
187
+
188
+ // Injection goes through inject_key_* (staged, applied at the top of
189
+ // begin_frame), NOT set_key_* (which writes keys_down directly). A direct
190
+ // write made outside the poll phase is folded into `prev` before the edge
191
+ // is computed, so `isKeyPressed` never fires — the exact bug input.rs's
192
+ // pending_key_down staging was added to cure. Mirrors the shared macro.
193
+ #[wasm_bindgen]
194
+ pub fn bloom_inject_key_down(key: f64) {
195
+ engine().input.inject_key_down(key as usize);
196
+ }
197
+
198
+ #[wasm_bindgen]
199
+ pub fn bloom_inject_key_up(key: f64) {
200
+ engine().input.inject_key_up(key as usize);
201
+ }
202
+
203
+ // EN-063: mouse / touch / wheel / char injection. DOM event handlers run in
204
+ // their own tasks, which on a single-threaded wasm host always land BETWEEN
205
+ // frames — i.e. before the next begin_frame(), exactly where a native
206
+ // message pump writes. So the immediate setters are correct here (edges are
207
+ // computed at begin_frame from prev-frame state); only same-gap taps need
208
+ // care, which the JS glue handles by deferring the matching -up one frame.
209
+
210
+ #[wasm_bindgen]
211
+ pub fn bloom_inject_mouse_move(x: f64, y: f64) {
212
+ engine().input.set_mouse_position(x, y);
213
+ }
214
+
215
+ /// Relative movement (pointer-lock `movementX/Y`). Feeds the raw-delta
216
+ /// accumulator that `begin_frame` prefers while the cursor is disabled —
217
+ /// the camera-orbit path.
218
+ #[wasm_bindgen]
219
+ pub fn bloom_inject_mouse_delta(dx: f64, dy: f64) {
220
+ engine().input.accumulate_mouse_delta(dx, dy);
221
+ }
222
+
223
+ #[wasm_bindgen]
224
+ pub fn bloom_inject_mouse_button_down(button: f64) {
225
+ engine().input.set_mouse_button_down(button as usize);
226
+ }
227
+
228
+ #[wasm_bindgen]
229
+ pub fn bloom_inject_mouse_button_up(button: f64) {
230
+ engine().input.set_mouse_button_up(button as usize);
231
+ }
232
+
233
+ #[wasm_bindgen]
234
+ pub fn bloom_inject_mouse_wheel(dy: f64) {
235
+ engine().input.accumulate_mouse_wheel(dy);
236
+ }
237
+
238
+ #[wasm_bindgen]
239
+ pub fn bloom_inject_touch(index: f64, x: f64, y: f64, active: f64) {
240
+ engine().input.set_touch(index as usize, x, y, active != 0.0);
241
+ }
242
+
243
+ /// Deferred release: keeps the slot active for the current frame and clears
244
+ /// it at end_frame, so a touch that begins and ends inside one frame is
245
+ /// still seen by the game.
246
+ #[wasm_bindgen]
247
+ pub fn bloom_inject_touch_release(index: f64, x: f64, y: f64) {
248
+ engine().input.release_touch(index as usize, x, y);
249
+ }
250
+
251
+ #[wasm_bindgen]
252
+ pub fn bloom_inject_char(codepoint: f64) {
253
+ engine().input.push_char(codepoint as u32);
254
+ }
255
+
256
+ #[wasm_bindgen]
257
+ pub fn bloom_inject_gamepad_axis(axis: f64, value: f64) {
258
+ engine().input.set_gamepad_axis(axis as usize, value as f32);
259
+ }
260
+
261
+ #[wasm_bindgen]
262
+ pub fn bloom_inject_gamepad_button_down(button: f64) {
263
+ engine().input.set_gamepad_button_down(button as usize);
264
+ }
265
+
266
+ #[wasm_bindgen]
267
+ pub fn bloom_inject_gamepad_button_up(button: f64) {
268
+ engine().input.set_gamepad_button_up(button as usize);
269
+ }
270
+
271
+
272
+ /// EN-031 — gamepad rumble. The Gamepad API exposes vibrationActuator, but
273
+ /// only behind a user-gesture requirement and with patchy support, so the web
274
+ /// port records the request (keeping the symbol and the state consistent with
275
+ /// native) without driving a motor. Wire `playEffect` here when the browser
276
+ /// story settles.
277
+ #[wasm_bindgen]
278
+ pub fn bloom_gamepad_rumble(low: f64, high: f64, seconds: f64) {
279
+ let inp = &mut engine().input;
280
+ inp.rumble = [
281
+ (low as f32).clamp(0.0, 1.0),
282
+ (high as f32).clamp(0.0, 1.0),
283
+ (seconds as f32).clamp(0.0, 10.0),
284
+ ];
285
+ }
286
+