@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.
- package/README.md +24 -14
- package/native/android/Cargo.lock +577 -28
- package/native/android/Cargo.toml +2 -0
- package/native/android/src/lib.rs +39 -3
- package/native/ios/Cargo.lock +573 -24
- package/native/ios/Cargo.toml +2 -0
- package/native/ios/src/lib.rs +72 -9
- package/native/ios/src/text_input.rs +48 -0
- package/native/linux/Cargo.lock +623 -35
- package/native/linux/Cargo.toml +3 -0
- package/native/macos/Cargo.lock +630 -35
- package/native/macos/Cargo.toml +3 -0
- package/native/shared/Cargo.lock +430 -27
- package/native/shared/Cargo.toml +5 -0
- package/native/shared/build.rs +41 -1
- package/native/shared/src/engine.rs +54 -2
- package/native/shared/src/ffi.rs +54 -1
- package/native/shared/src/ffi_core/mod.rs +2 -0
- package/native/shared/src/ffi_core/ui.rs +182 -0
- package/native/shared/src/input.rs +317 -12
- package/native/shared/src/lib.rs +1 -0
- package/native/shared/src/renderer/mod.rs +47 -201
- package/native/shared/src/renderer/texture_store.rs +5 -0
- package/native/shared/src/renderer/ui_pass.rs +742 -0
- package/native/shared/src/textures.rs +2 -0
- package/native/shared/src/ui/commands.rs +213 -0
- package/native/shared/src/ui/egui.rs +1282 -0
- package/native/shared/src/ui/imgui.rs +1056 -0
- package/native/shared/src/ui/input.rs +173 -0
- package/native/shared/src/ui/mod.rs +252 -0
- package/native/shared/src/ui/responses.rs +61 -0
- package/native/tvos/Cargo.lock +576 -27
- package/native/tvos/Cargo.toml +2 -0
- package/native/visionos/Cargo.lock +571 -22
- package/native/visionos/Cargo.toml +2 -0
- package/native/watchos/src/ffi_stubs.rs +27 -0
- package/native/web/Cargo.lock +346 -11
- package/native/web/Cargo.toml +6 -0
- package/native/web/bloom_glue.js +84 -11
- package/native/web/build.sh +150 -34
- package/native/web/src/lib.rs +2 -0
- package/native/web/src/ui_ffi.rs +148 -0
- package/native/windows/Cargo.lock +638 -38
- package/native/windows/Cargo.toml +3 -0
- package/native/windows/src/lib.rs +50 -1
- package/native/windows/src/windows_text.rs +69 -0
- package/package.json +91 -2
- package/src/debug-ui/index.ts +4 -0
- package/src/debug-ui/types.ts +1 -0
- package/src/game/adapters/audio-source-component.ts +67 -0
- package/src/game/adapters/rigid-body-component.ts +69 -0
- package/src/game/adapters/scene-node-component.ts +49 -0
- package/src/game/game-component.ts +89 -0
- package/src/game/game-object.ts +338 -0
- package/src/game/game-scene.ts +303 -0
- package/src/game/index.ts +7 -0
- package/src/game/transform.ts +315 -0
- package/src/index.ts +27 -0
- package/src/ui/api.ts +240 -0
- package/src/ui/index.ts +5 -0
- package/src/ui/opcodes.ts +66 -0
- package/src/ui/types.ts +82 -0
package/native/ios/Cargo.toml
CHANGED
|
@@ -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
|
package/native/ios/src/lib.rs
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
// `static mut`
|
|
2
|
-
//
|
|
3
|
-
//
|
|
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
|
+
}
|