@bornengine/engine 0.4.16 → 0.5.1

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 (62) hide show
  1. package/README.md +24 -14
  2. package/native/android/Cargo.lock +577 -28
  3. package/native/android/Cargo.toml +2 -0
  4. package/native/android/src/lib.rs +39 -3
  5. package/native/ios/Cargo.lock +573 -24
  6. package/native/ios/Cargo.toml +2 -0
  7. package/native/ios/src/lib.rs +72 -9
  8. package/native/ios/src/text_input.rs +48 -0
  9. package/native/linux/Cargo.lock +623 -35
  10. package/native/linux/Cargo.toml +3 -0
  11. package/native/macos/Cargo.lock +630 -35
  12. package/native/macos/Cargo.toml +3 -0
  13. package/native/shared/Cargo.lock +430 -27
  14. package/native/shared/Cargo.toml +5 -0
  15. package/native/shared/build.rs +41 -1
  16. package/native/shared/src/engine.rs +54 -2
  17. package/native/shared/src/ffi.rs +54 -1
  18. package/native/shared/src/ffi_core/mod.rs +2 -0
  19. package/native/shared/src/ffi_core/ui.rs +182 -0
  20. package/native/shared/src/input.rs +317 -12
  21. package/native/shared/src/lib.rs +1 -0
  22. package/native/shared/src/renderer/mod.rs +47 -201
  23. package/native/shared/src/renderer/texture_store.rs +5 -0
  24. package/native/shared/src/renderer/ui_pass.rs +742 -0
  25. package/native/shared/src/textures.rs +2 -0
  26. package/native/shared/src/ui/commands.rs +213 -0
  27. package/native/shared/src/ui/egui.rs +1282 -0
  28. package/native/shared/src/ui/imgui.rs +1056 -0
  29. package/native/shared/src/ui/input.rs +173 -0
  30. package/native/shared/src/ui/mod.rs +252 -0
  31. package/native/shared/src/ui/responses.rs +61 -0
  32. package/native/tvos/Cargo.lock +576 -27
  33. package/native/tvos/Cargo.toml +2 -0
  34. package/native/visionos/Cargo.lock +571 -22
  35. package/native/visionos/Cargo.toml +2 -0
  36. package/native/watchos/src/ffi_stubs.rs +27 -0
  37. package/native/web/Cargo.lock +346 -11
  38. package/native/web/Cargo.toml +6 -0
  39. package/native/web/bloom_glue.js +84 -11
  40. package/native/web/build.sh +150 -34
  41. package/native/web/src/lib.rs +2 -0
  42. package/native/web/src/ui_ffi.rs +148 -0
  43. package/native/windows/Cargo.lock +638 -38
  44. package/native/windows/Cargo.toml +3 -0
  45. package/native/windows/src/lib.rs +50 -1
  46. package/native/windows/src/windows_text.rs +69 -0
  47. package/package.json +91 -2
  48. package/src/debug-ui/index.ts +4 -0
  49. package/src/debug-ui/types.ts +1 -0
  50. package/src/game/adapters/audio-source-component.ts +67 -0
  51. package/src/game/adapters/rigid-body-component.ts +69 -0
  52. package/src/game/adapters/scene-node-component.ts +49 -0
  53. package/src/game/game-component.ts +89 -0
  54. package/src/game/game-object.ts +338 -0
  55. package/src/game/game-scene.ts +303 -0
  56. package/src/game/index.ts +7 -0
  57. package/src/game/transform.ts +315 -0
  58. package/src/index.ts +27 -0
  59. package/src/ui/api.ts +240 -0
  60. package/src/ui/index.ts +5 -0
  61. package/src/ui/opcodes.ts +66 -0
  62. package/src/ui/types.ts +82 -0
@@ -14,6 +14,8 @@ jolt = ["bloom-shared/jolt"]
14
14
  # unaffected; pure-2D games opt out via Perry feature forwarding.
15
15
  models3d = ["bloom-shared/models3d"]
16
16
  image-extras = ["bloom-shared/image-extras"]
17
+ hot-reload = ["bloom-shared/hot-reload"]
18
+ dev = ["hot-reload"]
17
19
 
18
20
  # Match perry-runtime's panic strategy so the final perry-driven link
19
21
  # doesn't see two copies of rust_eh_personality (and friends) from two
@@ -1,10 +1,6 @@
1
- // `static mut` is intentional throughout this FFI surface — Perry calls
2
- // us from a single OS thread (the UIKit main run-loop), so the engine
3
- // singleton + view/window scratch state never race. The 2024 lint
4
- // flagging `&UI_VIEW`-style accesses is a real concern in
5
- // multi-threaded code, but inapplicable here. Suppress at the crate
6
- // root to avoid a dozen noise lines in every build. Mirrors
7
- // native/macos/src/lib.rs.
1
+ // The iOS FFI uses legacy `static mut` state. UIKit text callbacks run on the
2
+ // main thread while the game loop runs separately, so text/key events cross to
3
+ // the game thread through the synchronized queue in `text_input`.
8
4
  #![allow(static_mut_refs)]
9
5
 
10
6
  use bloom_shared::engine::EngineState;
@@ -18,9 +14,11 @@ use objc2::{msg_send, sel};
18
14
 
19
15
  use raw_window_handle::{RawDisplayHandle, RawWindowHandle, UiKitDisplayHandle, UiKitWindowHandle};
20
16
 
21
- use std::ffi::c_void;
17
+ use std::ffi::{c_void, CStr};
22
18
  use std::sync::OnceLock;
23
19
 
20
+ mod text_input;
21
+
24
22
  static mut ENGINE: OnceLock<EngineState> = OnceLock::new();
25
23
  static mut UI_WINDOW: Option<Retained<AnyObject>> = None;
26
24
  static mut UI_VIEW: Option<Retained<AnyObject>> = None;
@@ -138,6 +136,30 @@ unsafe extern "C" fn bloom_touches_cancelled(_this: *mut c_void, _sel: Sel, touc
138
136
  handle_touches(touches, TouchPhase::Ended);
139
137
  }
140
138
 
139
+ unsafe extern "C" fn bloom_can_become_first_responder(_this: *const c_void, _sel: Sel) -> Bool {
140
+ Bool::YES
141
+ }
142
+
143
+ unsafe extern "C" fn bloom_text_input_has_text(_this: *const c_void, _sel: Sel) -> Bool {
144
+ Bool::YES
145
+ }
146
+
147
+ unsafe extern "C" fn bloom_text_input_insert_text(
148
+ _this: *mut c_void,
149
+ _sel: Sel,
150
+ text: *const AnyObject,
151
+ ) {
152
+ if text.is_null() { return; }
153
+ let utf8: *const std::ffi::c_char = msg_send![text, UTF8String];
154
+ if utf8.is_null() { return; }
155
+ let text = CStr::from_ptr(utf8).to_string_lossy().into_owned();
156
+ text_input::enqueue(text_input::TextInputEvent::Insert(text));
157
+ }
158
+
159
+ unsafe extern "C" fn bloom_text_input_delete_backward(_this: *mut c_void, _sel: Sel) {
160
+ text_input::enqueue(text_input::TextInputEvent::DeleteBackward);
161
+ }
162
+
141
163
  enum TouchPhase { Began, Moved, Ended }
142
164
 
143
165
  unsafe fn handle_touches(touches: *const AnyObject, phase: TouchPhase) {
@@ -226,6 +248,21 @@ fn register_metal_view_class() {
226
248
  class_addMethod(cls, sel!(touchesEnded:withEvent:), bloom_touches_ended as *const c_void, touch_types);
227
249
  class_addMethod(cls, sel!(touchesCancelled:withEvent:), bloom_touches_cancelled as *const c_void, touch_types);
228
250
 
251
+ extern "C" { fn objc_getProtocol(name: *const u8) -> *const c_void; }
252
+ let keyboard_protocol = objc_getProtocol(b"UIKeyInput\0".as_ptr());
253
+ if !keyboard_protocol.is_null() {
254
+ class_addProtocol(cls, keyboard_protocol);
255
+ class_addMethod(
256
+ cls,
257
+ sel!(canBecomeFirstResponder),
258
+ bloom_can_become_first_responder as *const c_void,
259
+ b"B16@0:8\0".as_ptr(),
260
+ );
261
+ class_addMethod(cls, sel!(hasText), bloom_text_input_has_text as *const c_void, b"B16@0:8\0".as_ptr());
262
+ class_addMethod(cls, sel!(insertText:), bloom_text_input_insert_text as *const c_void, b"v24@0:8@16\0".as_ptr());
263
+ class_addMethod(cls, sel!(deleteBackward), bloom_text_input_delete_backward as *const c_void, b"v16@0:8\0".as_ptr());
264
+ }
265
+
229
266
  objc_registerClassPair(cls);
230
267
  }
231
268
  }
@@ -890,6 +927,33 @@ pub extern "C" fn bloom_begin_drawing() {
890
927
  // No run loop pumping needed — UIApplicationMain handles the main run loop
891
928
  // on its own thread. The game runs on the game thread.
892
929
 
930
+ // UIKit text callbacks run on the main thread. Apply their queued events
931
+ // here so only the game thread mutates EngineState for text input.
932
+ for event in text_input::drain() {
933
+ let eng = engine();
934
+ match event {
935
+ text_input::TextInputEvent::Insert(text) => eng.input.push_ui_text(text),
936
+ text_input::TextInputEvent::DeleteBackward => {
937
+ eng.input.inject_key_down(8);
938
+ eng.input.inject_key_up(8);
939
+ }
940
+ }
941
+ }
942
+
943
+ // Apply UI text-focus transitions before beginning the next input snapshot.
944
+ match engine().ui.take_keyboard_request() {
945
+ Some(show) => unsafe {
946
+ if let Some(view) = &UI_VIEW {
947
+ let view_ptr = Retained::as_ptr(view);
948
+ let selector = if show { sel!(becomeFirstResponder) } else { sel!(resignFirstResponder) };
949
+ let _: () = msg_send![view_ptr, performSelectorOnMainThread: selector
950
+ withObject: std::ptr::null::<AnyObject>()
951
+ waitUntilDone: Bool::NO];
952
+ }
953
+ },
954
+ None => {}
955
+ }
956
+
893
957
  // Poll a connected controller before either begin_frame path below.
894
958
  poll_game_controllers();
895
959
 
@@ -1264,4 +1328,3 @@ fn bloom_jolt_ffi_physics() -> &'static mut bloom_shared::physics_jolt::JoltPhys
1264
1328
 
1265
1329
  #[cfg(feature = "jolt")]
1266
1330
  bloom_shared::define_physics_ffi!();
1267
-
@@ -0,0 +1,48 @@
1
+ use std::collections::VecDeque;
2
+ use std::sync::{Mutex, MutexGuard, OnceLock};
3
+
4
+ #[derive(Debug, Eq, PartialEq)]
5
+ pub enum TextInputEvent {
6
+ Insert(String),
7
+ DeleteBackward,
8
+ }
9
+
10
+ static PENDING_EVENTS: OnceLock<Mutex<VecDeque<TextInputEvent>>> = OnceLock::new();
11
+
12
+ fn pending_events() -> MutexGuard<'static, VecDeque<TextInputEvent>> {
13
+ PENDING_EVENTS
14
+ .get_or_init(|| Mutex::new(VecDeque::new()))
15
+ .lock()
16
+ .unwrap_or_else(std::sync::PoisonError::into_inner)
17
+ }
18
+
19
+ pub fn enqueue(event: TextInputEvent) {
20
+ pending_events().push_back(event);
21
+ }
22
+
23
+ pub fn drain() -> Vec<TextInputEvent> {
24
+ pending_events().drain(..).collect()
25
+ }
26
+
27
+ #[cfg(test)]
28
+ mod tests {
29
+ use super::{drain, enqueue, TextInputEvent};
30
+
31
+ #[test]
32
+ fn text_events_cross_to_the_game_thread_once_and_in_order() {
33
+ let producer = std::thread::spawn(|| {
34
+ enqueue(TextInputEvent::Insert("é🙂".to_owned()));
35
+ enqueue(TextInputEvent::DeleteBackward);
36
+ });
37
+ producer.join().unwrap();
38
+
39
+ assert_eq!(
40
+ drain(),
41
+ [
42
+ TextInputEvent::Insert("é🙂".to_owned()),
43
+ TextInputEvent::DeleteBackward,
44
+ ]
45
+ );
46
+ assert!(drain().is_empty());
47
+ }
48
+ }