@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.
- package/LICENSE +21 -0
- package/README.md +231 -0
- package/native/android/Cargo.lock +1848 -0
- package/native/android/Cargo.toml +24 -0
- package/native/android/src/lib.rs +702 -0
- package/native/ios/Cargo.lock +1690 -0
- package/native/ios/Cargo.toml +32 -0
- package/native/ios/src/lib.rs +1267 -0
- package/native/linux/Cargo.lock +3279 -0
- package/native/linux/Cargo.toml +29 -0
- package/native/linux/src/lib.rs +1331 -0
- package/native/macos/Cargo.lock +3310 -0
- package/native/macos/Cargo.toml +46 -0
- package/native/macos/src/lib.rs +1302 -0
- package/native/shared/Cargo.lock +1899 -0
- package/native/shared/Cargo.toml +62 -0
- package/native/shared/assets/default_font.ttf +0 -0
- package/native/shared/build.rs +270 -0
- package/native/shared/shaders/common/clouds.wgsl +122 -0
- package/native/shared/shaders/common/fog.wgsl +16 -0
- package/native/shared/shaders/common/foliage_wind.wgsl +98 -0
- package/native/shared/shaders/common/imposter.wgsl +112 -0
- package/native/shared/shaders/common/pbr.wgsl +186 -0
- package/native/shared/shaders/common/shadows.wgsl +186 -0
- package/native/shared/shaders/common/sky.wgsl +8 -0
- package/native/shared/shaders/common/tonemap.wgsl +25 -0
- package/native/shared/shaders/impulse_field.wgsl +57 -0
- package/native/shared/shaders/material_abi.wgsl +383 -0
- package/native/shared/shaders/materials/test_minimal.wgsl +42 -0
- package/native/shared/src/anim_mixer.rs +61 -0
- package/native/shared/src/attach.rs +263 -0
- package/native/shared/src/audio/decode.rs +123 -0
- package/native/shared/src/audio/mod.rs +863 -0
- package/native/shared/src/audio/render.rs +892 -0
- package/native/shared/src/audio/spsc.rs +156 -0
- package/native/shared/src/audio/stream.rs +226 -0
- package/native/shared/src/custom_shaders.rs +104 -0
- package/native/shared/src/decals.rs +245 -0
- package/native/shared/src/drs.rs +211 -0
- package/native/shared/src/engine.rs +261 -0
- package/native/shared/src/ffi.rs +116 -0
- package/native/shared/src/ffi_core/assets.rs +388 -0
- package/native/shared/src/ffi_core/audio_ffi.rs +184 -0
- package/native/shared/src/ffi_core/draw.rs +334 -0
- package/native/shared/src/ffi_core/game_loop.rs +577 -0
- package/native/shared/src/ffi_core/input.rs +234 -0
- package/native/shared/src/ffi_core/mod.rs +127 -0
- package/native/shared/src/ffi_core/models.rs +1154 -0
- package/native/shared/src/ffi_core/ragdoll_ffi.rs +261 -0
- package/native/shared/src/ffi_core/scene.rs +626 -0
- package/native/shared/src/ffi_core/vfx.rs +212 -0
- package/native/shared/src/ffi_core/visual.rs +691 -0
- package/native/shared/src/frame_callbacks.rs +122 -0
- package/native/shared/src/geometry.rs +236 -0
- package/native/shared/src/handles.rs +182 -0
- package/native/shared/src/input.rs +448 -0
- package/native/shared/src/jolt_sys.rs +822 -0
- package/native/shared/src/lib.rs +55 -0
- package/native/shared/src/models.rs +1093 -0
- package/native/shared/src/models_gltf.rs +1280 -0
- package/native/shared/src/particles.rs +391 -0
- package/native/shared/src/physics_jolt.rs +1908 -0
- package/native/shared/src/picking.rs +298 -0
- package/native/shared/src/postfx.rs +345 -0
- package/native/shared/src/profiler.rs +492 -0
- package/native/shared/src/ragdoll.rs +474 -0
- package/native/shared/src/renderer/atmosphere_lut.rs +573 -0
- package/native/shared/src/renderer/brdf_lut.rs +154 -0
- package/native/shared/src/renderer/draw2d.rs +143 -0
- package/native/shared/src/renderer/formats.rs +822 -0
- package/native/shared/src/renderer/froxel.rs +421 -0
- package/native/shared/src/renderer/gi_bake.rs +653 -0
- package/native/shared/src/renderer/graph.rs +462 -0
- package/native/shared/src/renderer/hiz.rs +269 -0
- package/native/shared/src/renderer/hot_reload.rs +390 -0
- package/native/shared/src/renderer/impulse_field.rs +456 -0
- package/native/shared/src/renderer/lighting.rs +154 -0
- package/native/shared/src/renderer/material_instancing.rs +171 -0
- package/native/shared/src/renderer/material_pipeline.rs +700 -0
- package/native/shared/src/renderer/material_system.rs +1996 -0
- package/native/shared/src/renderer/material_system_tests.rs +601 -0
- package/native/shared/src/renderer/material_system_wasm.rs +41 -0
- package/native/shared/src/renderer/mod.rs +12556 -0
- package/native/shared/src/renderer/model_draw.rs +641 -0
- package/native/shared/src/renderer/occlusion.rs +429 -0
- package/native/shared/src/renderer/planar_pass.rs +593 -0
- package/native/shared/src/renderer/planar_reflection.rs +499 -0
- package/native/shared/src/renderer/post_pass.rs +249 -0
- package/native/shared/src/renderer/postfx_chain.rs +728 -0
- package/native/shared/src/renderer/pt_pass.rs +577 -0
- package/native/shared/src/renderer/scene_pass.rs +607 -0
- package/native/shared/src/renderer/shader_include.rs +205 -0
- package/native/shared/src/renderer/shader_library.rs +135 -0
- package/native/shared/src/renderer/shaders/ao.rs +570 -0
- package/native/shared/src/renderer/shaders/core.rs +1243 -0
- package/native/shared/src/renderer/shaders/env.rs +907 -0
- package/native/shared/src/renderer/shaders/gi.rs +810 -0
- package/native/shared/src/renderer/shaders/mod.rs +19 -0
- package/native/shared/src/renderer/shaders/post.rs +1558 -0
- package/native/shared/src/renderer/shaders/pt.rs +1859 -0
- package/native/shared/src/renderer/shaders/ssgi.rs +1586 -0
- package/native/shared/src/renderer/shadow_pass.rs +731 -0
- package/native/shared/src/renderer/ssgi_pass.rs +392 -0
- package/native/shared/src/renderer/ssr_pass.rs +188 -0
- package/native/shared/src/renderer/texture_store.rs +473 -0
- package/native/shared/src/renderer/transient.rs +591 -0
- package/native/shared/src/renderer/types.rs +941 -0
- package/native/shared/src/renderer/util.rs +152 -0
- package/native/shared/src/scene.rs +1362 -0
- package/native/shared/src/sdf_cache.rs +274 -0
- package/native/shared/src/shadows.rs +1036 -0
- package/native/shared/src/staging.rs +102 -0
- package/native/shared/src/string_header.rs +266 -0
- package/native/shared/src/text_renderer.rs +502 -0
- package/native/shared/src/textures.rs +197 -0
- package/native/tvos/Cargo.lock +1693 -0
- package/native/tvos/Cargo.toml +36 -0
- package/native/tvos/metal-patched/Cargo.toml +178 -0
- package/native/tvos/metal-patched/LICENSE-APACHE +201 -0
- package/native/tvos/metal-patched/LICENSE-MIT +25 -0
- package/native/tvos/metal-patched/src/acceleration_structure.rs +667 -0
- package/native/tvos/metal-patched/src/acceleration_structure_pass.rs +108 -0
- package/native/tvos/metal-patched/src/argument.rs +366 -0
- package/native/tvos/metal-patched/src/blitpass.rs +102 -0
- package/native/tvos/metal-patched/src/buffer.rs +71 -0
- package/native/tvos/metal-patched/src/capturedescriptor.rs +76 -0
- package/native/tvos/metal-patched/src/capturemanager.rs +113 -0
- package/native/tvos/metal-patched/src/commandbuffer.rs +192 -0
- package/native/tvos/metal-patched/src/commandqueue.rs +44 -0
- package/native/tvos/metal-patched/src/computepass.rs +107 -0
- package/native/tvos/metal-patched/src/constants.rs +152 -0
- package/native/tvos/metal-patched/src/counters.rs +119 -0
- package/native/tvos/metal-patched/src/depthstencil.rs +190 -0
- package/native/tvos/metal-patched/src/device.rs +2134 -0
- package/native/tvos/metal-patched/src/drawable.rs +39 -0
- package/native/tvos/metal-patched/src/encoder.rs +2041 -0
- package/native/tvos/metal-patched/src/heap.rs +281 -0
- package/native/tvos/metal-patched/src/indirect_encoder.rs +344 -0
- package/native/tvos/metal-patched/src/lib.rs +657 -0
- package/native/tvos/metal-patched/src/library.rs +902 -0
- package/native/tvos/metal-patched/src/mps.rs +575 -0
- package/native/tvos/metal-patched/src/pipeline/compute.rs +475 -0
- package/native/tvos/metal-patched/src/pipeline/mod.rs +71 -0
- package/native/tvos/metal-patched/src/pipeline/render.rs +762 -0
- package/native/tvos/metal-patched/src/renderpass.rs +443 -0
- package/native/tvos/metal-patched/src/resource.rs +182 -0
- package/native/tvos/metal-patched/src/sampler.rs +165 -0
- package/native/tvos/metal-patched/src/sync.rs +178 -0
- package/native/tvos/metal-patched/src/texture.rs +352 -0
- package/native/tvos/metal-patched/src/types.rs +90 -0
- package/native/tvos/metal-patched/src/vertexdescriptor.rs +250 -0
- package/native/tvos/src/audio_backend.rs +197 -0
- package/native/tvos/src/lib.rs +1891 -0
- package/native/visionos/Cargo.lock +1693 -0
- package/native/visionos/Cargo.toml +40 -0
- package/native/visionos/src/audio_backend.rs +197 -0
- package/native/visionos/src/lib.rs +1887 -0
- package/native/watchos/Cargo.lock +16 -0
- package/native/watchos/Cargo.toml +19 -0
- package/native/watchos/shaders/bloom_postfx.metal +99 -0
- package/native/watchos/src/BloomWatchApp.swift +1267 -0
- package/native/watchos/src/BloomWatchAudio.swift +179 -0
- package/native/watchos/src/audio.rs +55 -0
- package/native/watchos/src/draw_list.rs +229 -0
- package/native/watchos/src/ffi_stubs.rs +915 -0
- package/native/watchos/src/ffi_stubs_manual.rs +35 -0
- package/native/watchos/src/lib.rs +1124 -0
- package/native/watchos/src/models.rs +746 -0
- package/native/watchos/src/postfx.rs +95 -0
- package/native/watchos/src/scene.rs +534 -0
- package/native/watchos/src/textures.rs +184 -0
- package/native/web/Cargo.lock +1657 -0
- package/native/web/Cargo.toml +43 -0
- package/native/web/bloom_glue.js +695 -0
- package/native/web/build.sh +131 -0
- package/native/web/index.html +35 -0
- package/native/web/jolt_bridge.js +1519 -0
- package/native/web/src/input_ffi.rs +286 -0
- package/native/web/src/lib.rs +1796 -0
- package/native/web/src/material_ffi.rs +710 -0
- package/native/web/src/parity_ffi.rs +343 -0
- package/native/web/src/physics_ffi.rs +643 -0
- package/native/web/src/ragdoll_ffi.rs +250 -0
- package/native/web/src/render_settings.rs +98 -0
- package/native/windows/Cargo.lock +1815 -0
- package/native/windows/Cargo.toml +68 -0
- package/native/windows/src/lib.rs +1486 -0
- package/package.json +4279 -0
- package/src/audio/index.ts +315 -0
- package/src/core/colors.ts +63 -0
- package/src/core/index.ts +1206 -0
- package/src/core/keys.ts +63 -0
- package/src/core/types.ts +104 -0
- package/src/index.ts +171 -0
- package/src/math/index.ts +516 -0
- package/src/mobile/index.ts +294 -0
- package/src/models/index.ts +1258 -0
- package/src/physics/index.ts +1134 -0
- package/src/scene/index.ts +698 -0
- package/src/shapes/index.ts +120 -0
- package/src/text/index.ts +48 -0
- package/src/textures/index.ts +187 -0
- package/src/vfx/index.ts +191 -0
- package/src/world/index.ts +24 -0
- package/src/world/loader.ts +423 -0
- package/src/world/prefab.ts +217 -0
- package/src/world/render.ts +172 -0
- package/src/world/saver.ts +108 -0
- package/src/world/serialize.ts +301 -0
- package/src/world/terrain.ts +355 -0
- package/src/world/types.ts +160 -0
- package/src/world/validate.ts +319 -0
- package/src/world/version.ts +114 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/// Platform-agnostic input state.
|
|
2
|
+
|
|
3
|
+
const MAX_KEYS: usize = 512;
|
|
4
|
+
const MAX_MOUSE_BUTTONS: usize = 8;
|
|
5
|
+
const MAX_GAMEPAD_AXES: usize = 8;
|
|
6
|
+
const MAX_GAMEPAD_BUTTONS: usize = 16;
|
|
7
|
+
const MAX_TOUCH_POINTS: usize = 10;
|
|
8
|
+
|
|
9
|
+
pub struct TouchPoint {
|
|
10
|
+
pub x: f64,
|
|
11
|
+
pub y: f64,
|
|
12
|
+
pub active: bool,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pub struct InputState {
|
|
16
|
+
keys_pressed: [bool; MAX_KEYS],
|
|
17
|
+
keys_down: [bool; MAX_KEYS],
|
|
18
|
+
keys_released: [bool; MAX_KEYS],
|
|
19
|
+
prev_keys_down: [bool; MAX_KEYS],
|
|
20
|
+
|
|
21
|
+
// Injected keys (bloom_inject_key_down/up) are staged here and applied at the
|
|
22
|
+
// top of begin_frame, rather than written straight into keys_down.
|
|
23
|
+
//
|
|
24
|
+
// They HAVE to be, and the reason is the frame order: a real key arrives in
|
|
25
|
+
// poll_events(), which runs BEFORE begin_frame(), so the edge
|
|
26
|
+
// (keys_down && !prev_keys_down) is computed for it. An injected key is set
|
|
27
|
+
// from the game loop — AFTER begin_frame — so end_frame() copied it into
|
|
28
|
+
// prev_keys_down before begin_frame ever got to look at it, and the edge was
|
|
29
|
+
// never computed at all. is_key_down() saw the key; is_key_pressed() never
|
|
30
|
+
// did. That silently made the injection API useless for anything
|
|
31
|
+
// edge-triggered, which is to say every menu and every UI in every game
|
|
32
|
+
// built on this engine.
|
|
33
|
+
pending_key_down: [bool; MAX_KEYS],
|
|
34
|
+
pending_key_up: [bool; MAX_KEYS],
|
|
35
|
+
|
|
36
|
+
pub mouse_x: f64,
|
|
37
|
+
pub mouse_y: f64,
|
|
38
|
+
pub mouse_delta_x: f64,
|
|
39
|
+
pub mouse_delta_y: f64,
|
|
40
|
+
prev_mouse_x: f64,
|
|
41
|
+
prev_mouse_y: f64,
|
|
42
|
+
raw_delta_x: f64,
|
|
43
|
+
raw_delta_y: f64,
|
|
44
|
+
/// Scroll wheel accumulator. Platform event loops call `accumulate_mouse_wheel`
|
|
45
|
+
/// when a scroll event arrives; games/editors call `consume_mouse_wheel` once
|
|
46
|
+
/// per frame to read the total delta (and reset it to 0).
|
|
47
|
+
mouse_wheel_y: f64,
|
|
48
|
+
/// Digital Crown rotation accumulator (watchOS). Platforms push rotation
|
|
49
|
+
/// deltas via `accumulate_crown_rotation`; games call `consume_crown_rotation`
|
|
50
|
+
/// once per frame. Units are radians, positive = clockwise (away from user).
|
|
51
|
+
crown_rotation: f64,
|
|
52
|
+
/// Character input ring buffer for text-entry support (E3b). Platform
|
|
53
|
+
/// event loops push Unicode codepoints here on key-down; editors pop them
|
|
54
|
+
/// one at a time via `pop_char`.
|
|
55
|
+
char_queue: [u32; 32],
|
|
56
|
+
char_queue_head: usize,
|
|
57
|
+
char_queue_tail: usize,
|
|
58
|
+
/// Cursor shape requested by the editor. The platform event loop polls
|
|
59
|
+
/// this and applies the appropriate native cursor.
|
|
60
|
+
/// 0=default, 1=hand, 2=move, 3=text, 4=resize_h, 5=resize_v, 6=crosshair
|
|
61
|
+
pub cursor_shape: u32,
|
|
62
|
+
pub cursor_disabled: bool,
|
|
63
|
+
mouse_pressed: [bool; MAX_MOUSE_BUTTONS],
|
|
64
|
+
mouse_down: [bool; MAX_MOUSE_BUTTONS],
|
|
65
|
+
mouse_released: [bool; MAX_MOUSE_BUTTONS],
|
|
66
|
+
prev_mouse_down: [bool; MAX_MOUSE_BUTTONS],
|
|
67
|
+
// Platform event loops can drain both halves of a click before the next
|
|
68
|
+
// frame begins. Keep the transitions independently so neither edge is
|
|
69
|
+
// lost when the final button state is released again.
|
|
70
|
+
pending_mouse_pressed: [bool; MAX_MOUSE_BUTTONS],
|
|
71
|
+
pending_mouse_released: [bool; MAX_MOUSE_BUTTONS],
|
|
72
|
+
|
|
73
|
+
// Gamepad
|
|
74
|
+
pub gamepad_available: bool,
|
|
75
|
+
gamepad_axes: [f32; MAX_GAMEPAD_AXES],
|
|
76
|
+
gamepad_buttons_down: [bool; MAX_GAMEPAD_BUTTONS],
|
|
77
|
+
gamepad_buttons_pressed: [bool; MAX_GAMEPAD_BUTTONS],
|
|
78
|
+
gamepad_buttons_released: [bool; MAX_GAMEPAD_BUTTONS],
|
|
79
|
+
prev_gamepad_buttons: [bool; MAX_GAMEPAD_BUTTONS],
|
|
80
|
+
pub gamepad_axis_count: usize,
|
|
81
|
+
/// EN-031 rumble: (low-frequency motor, high-frequency motor, seconds
|
|
82
|
+
/// left), all 0 = motors off. The FFI writes this; each platform's input
|
|
83
|
+
/// poll drives its own vibration API from it and decrements the timer.
|
|
84
|
+
/// Keeping the *state* shared and only the driving per-platform is what
|
|
85
|
+
/// stops the six platform crates from drifting again.
|
|
86
|
+
pub rumble: [f32; 3],
|
|
87
|
+
|
|
88
|
+
// Touch
|
|
89
|
+
pub touch_points: [TouchPoint; MAX_TOUCH_POINTS],
|
|
90
|
+
pub touch_count: usize,
|
|
91
|
+
// Release-deferral: an ACTION_UP that arrives in the same frame as its
|
|
92
|
+
// ACTION_DOWN would otherwise zero out `touch_count` before the game has a
|
|
93
|
+
// chance to poll. Mark the slot here instead and clear at end_frame, so a
|
|
94
|
+
// fast tap is still visible for one full frame.
|
|
95
|
+
touch_pending_release: [bool; MAX_TOUCH_POINTS],
|
|
96
|
+
|
|
97
|
+
// OS key auto-repeat, surfaced as its OWN edge (isKeyRepeated) so that
|
|
98
|
+
// isKeyPressed semantics never change for games — a held jump key must
|
|
99
|
+
// not machine-gun. Text/caret navigation (arrows, Home/End, Delete) is
|
|
100
|
+
// what consumes these. The platform layer queues repeats as they arrive;
|
|
101
|
+
// begin_frame publishes them for exactly one frame.
|
|
102
|
+
keys_repeated: [bool; MAX_KEYS],
|
|
103
|
+
repeat_pending: [bool; MAX_KEYS],
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
impl InputState {
|
|
107
|
+
pub fn new() -> Self {
|
|
108
|
+
const EMPTY_TOUCH: TouchPoint = TouchPoint { x: 0.0, y: 0.0, active: false };
|
|
109
|
+
Self {
|
|
110
|
+
keys_pressed: [false; MAX_KEYS],
|
|
111
|
+
keys_down: [false; MAX_KEYS],
|
|
112
|
+
keys_released: [false; MAX_KEYS],
|
|
113
|
+
prev_keys_down: [false; MAX_KEYS],
|
|
114
|
+
pending_key_down: [false; MAX_KEYS],
|
|
115
|
+
pending_key_up: [false; MAX_KEYS],
|
|
116
|
+
mouse_x: 0.0,
|
|
117
|
+
mouse_y: 0.0,
|
|
118
|
+
mouse_delta_x: 0.0,
|
|
119
|
+
mouse_delta_y: 0.0,
|
|
120
|
+
prev_mouse_x: 0.0,
|
|
121
|
+
prev_mouse_y: 0.0,
|
|
122
|
+
raw_delta_x: 0.0,
|
|
123
|
+
raw_delta_y: 0.0,
|
|
124
|
+
mouse_wheel_y: 0.0,
|
|
125
|
+
crown_rotation: 0.0,
|
|
126
|
+
char_queue: [0; 32],
|
|
127
|
+
char_queue_head: 0,
|
|
128
|
+
char_queue_tail: 0,
|
|
129
|
+
cursor_shape: 0,
|
|
130
|
+
cursor_disabled: false,
|
|
131
|
+
mouse_pressed: [false; MAX_MOUSE_BUTTONS],
|
|
132
|
+
mouse_down: [false; MAX_MOUSE_BUTTONS],
|
|
133
|
+
mouse_released: [false; MAX_MOUSE_BUTTONS],
|
|
134
|
+
prev_mouse_down: [false; MAX_MOUSE_BUTTONS],
|
|
135
|
+
pending_mouse_pressed: [false; MAX_MOUSE_BUTTONS],
|
|
136
|
+
pending_mouse_released: [false; MAX_MOUSE_BUTTONS],
|
|
137
|
+
gamepad_available: false,
|
|
138
|
+
gamepad_axes: [0.0; MAX_GAMEPAD_AXES],
|
|
139
|
+
gamepad_buttons_down: [false; MAX_GAMEPAD_BUTTONS],
|
|
140
|
+
gamepad_buttons_pressed: [false; MAX_GAMEPAD_BUTTONS],
|
|
141
|
+
gamepad_buttons_released: [false; MAX_GAMEPAD_BUTTONS],
|
|
142
|
+
prev_gamepad_buttons: [false; MAX_GAMEPAD_BUTTONS],
|
|
143
|
+
gamepad_axis_count: 0,
|
|
144
|
+
rumble: [0.0; 3],
|
|
145
|
+
touch_points: [EMPTY_TOUCH; MAX_TOUCH_POINTS],
|
|
146
|
+
touch_count: 0,
|
|
147
|
+
touch_pending_release: [false; MAX_TOUCH_POINTS],
|
|
148
|
+
keys_repeated: [false; MAX_KEYS],
|
|
149
|
+
repeat_pending: [false; MAX_KEYS],
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Platform layer: an OS auto-repeat arrived for `key`. Published as a
|
|
154
|
+
/// one-frame isKeyRepeated edge by the next begin_frame.
|
|
155
|
+
pub fn queue_key_repeat(&mut self, key: usize) {
|
|
156
|
+
if key < MAX_KEYS {
|
|
157
|
+
self.repeat_pending[key] = true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
pub fn is_key_repeated(&self, key: usize) -> bool {
|
|
162
|
+
key < MAX_KEYS && self.keys_repeated[key]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
pub fn begin_frame(&mut self) {
|
|
166
|
+
// Apply injected keys BEFORE the edge computation below, so an injection
|
|
167
|
+
// made during the previous frame reads exactly like a real key press:
|
|
168
|
+
// down this frame, not-down last frame => keys_pressed. See the note on
|
|
169
|
+
// pending_key_down.
|
|
170
|
+
for i in 0..MAX_KEYS {
|
|
171
|
+
if self.pending_key_down[i] {
|
|
172
|
+
self.keys_down[i] = true;
|
|
173
|
+
self.pending_key_down[i] = false;
|
|
174
|
+
}
|
|
175
|
+
if self.pending_key_up[i] {
|
|
176
|
+
self.keys_down[i] = false;
|
|
177
|
+
self.pending_key_up[i] = false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if self.cursor_disabled {
|
|
181
|
+
self.mouse_delta_x = self.raw_delta_x;
|
|
182
|
+
self.mouse_delta_y = self.raw_delta_y;
|
|
183
|
+
self.raw_delta_x = 0.0;
|
|
184
|
+
self.raw_delta_y = 0.0;
|
|
185
|
+
} else {
|
|
186
|
+
self.mouse_delta_x = self.mouse_x - self.prev_mouse_x;
|
|
187
|
+
self.mouse_delta_y = self.mouse_y - self.prev_mouse_y;
|
|
188
|
+
}
|
|
189
|
+
for i in 0..MAX_KEYS {
|
|
190
|
+
self.keys_pressed[i] = self.keys_down[i] && !self.prev_keys_down[i];
|
|
191
|
+
self.keys_released[i] = !self.keys_down[i] && self.prev_keys_down[i];
|
|
192
|
+
self.keys_repeated[i] = self.repeat_pending[i];
|
|
193
|
+
self.repeat_pending[i] = false;
|
|
194
|
+
}
|
|
195
|
+
for i in 0..MAX_MOUSE_BUTTONS {
|
|
196
|
+
self.mouse_pressed[i] =
|
|
197
|
+
self.pending_mouse_pressed[i] || (self.mouse_down[i] && !self.prev_mouse_down[i]);
|
|
198
|
+
self.mouse_released[i] =
|
|
199
|
+
self.pending_mouse_released[i] || (!self.mouse_down[i] && self.prev_mouse_down[i]);
|
|
200
|
+
self.pending_mouse_pressed[i] = false;
|
|
201
|
+
self.pending_mouse_released[i] = false;
|
|
202
|
+
}
|
|
203
|
+
for i in 0..MAX_GAMEPAD_BUTTONS {
|
|
204
|
+
self.gamepad_buttons_pressed[i] = self.gamepad_buttons_down[i] && !self.prev_gamepad_buttons[i];
|
|
205
|
+
self.gamepad_buttons_released[i] = !self.gamepad_buttons_down[i] && self.prev_gamepad_buttons[i];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
pub fn end_frame(&mut self) {
|
|
210
|
+
self.prev_keys_down = self.keys_down;
|
|
211
|
+
self.prev_mouse_down = self.mouse_down;
|
|
212
|
+
self.prev_gamepad_buttons = self.gamepad_buttons_down;
|
|
213
|
+
self.prev_mouse_x = self.mouse_x;
|
|
214
|
+
self.prev_mouse_y = self.mouse_y;
|
|
215
|
+
let mut flushed = false;
|
|
216
|
+
for i in 0..MAX_TOUCH_POINTS {
|
|
217
|
+
if self.touch_pending_release[i] {
|
|
218
|
+
self.touch_points[i].active = false;
|
|
219
|
+
self.touch_pending_release[i] = false;
|
|
220
|
+
flushed = true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if flushed {
|
|
224
|
+
self.touch_count = self.touch_points.iter().filter(|t| t.active).count();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Keyboard
|
|
229
|
+
/// Real key events, delivered from the platform's message pump. These run
|
|
230
|
+
/// before begin_frame(), so writing keys_down directly is correct here.
|
|
231
|
+
pub fn set_key_down(&mut self, key: usize) { if key < MAX_KEYS { self.keys_down[key] = true; } }
|
|
232
|
+
pub fn set_key_up(&mut self, key: usize) { if key < MAX_KEYS { self.keys_down[key] = false; } }
|
|
233
|
+
|
|
234
|
+
/// Synthetic key events (bloom_inject_key_down/up), which callers raise from
|
|
235
|
+
/// inside the game loop. Staged, not written — see pending_key_down.
|
|
236
|
+
pub fn inject_key_down(&mut self, key: usize) {
|
|
237
|
+
if key < MAX_KEYS { self.pending_key_down[key] = true; self.pending_key_up[key] = false; }
|
|
238
|
+
}
|
|
239
|
+
pub fn inject_key_up(&mut self, key: usize) {
|
|
240
|
+
if key < MAX_KEYS { self.pending_key_up[key] = true; self.pending_key_down[key] = false; }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
pub fn is_key_pressed(&self, key: usize) -> bool { key < MAX_KEYS && self.keys_pressed[key] }
|
|
244
|
+
pub fn is_key_down(&self, key: usize) -> bool { key < MAX_KEYS && self.keys_down[key] }
|
|
245
|
+
pub fn is_key_released(&self, key: usize) -> bool { key < MAX_KEYS && self.keys_released[key] }
|
|
246
|
+
|
|
247
|
+
// Mouse
|
|
248
|
+
pub fn set_mouse_position(&mut self, x: f64, y: f64) { self.mouse_x = x; self.mouse_y = y; }
|
|
249
|
+
pub fn accumulate_mouse_delta(&mut self, dx: f64, dy: f64) { self.raw_delta_x += dx; self.raw_delta_y += dy; }
|
|
250
|
+
pub fn clear_mouse_delta(&mut self) { self.raw_delta_x = 0.0; self.raw_delta_y = 0.0; self.mouse_delta_x = 0.0; self.mouse_delta_y = 0.0; }
|
|
251
|
+
pub fn accumulate_mouse_wheel(&mut self, dy: f64) { self.mouse_wheel_y += dy; }
|
|
252
|
+
/// Returns the accumulated scroll delta since the last call, and resets it.
|
|
253
|
+
/// Games/editors call this once per frame.
|
|
254
|
+
pub fn consume_mouse_wheel(&mut self) -> f64 {
|
|
255
|
+
let v = self.mouse_wheel_y;
|
|
256
|
+
self.mouse_wheel_y = 0.0;
|
|
257
|
+
v
|
|
258
|
+
}
|
|
259
|
+
/// Push a Unicode codepoint into the character input queue. Called by
|
|
260
|
+
/// platform event loops when a key-down event produces a printable char.
|
|
261
|
+
pub fn push_char(&mut self, c: u32) {
|
|
262
|
+
let next = (self.char_queue_head + 1) % self.char_queue.len();
|
|
263
|
+
if next != self.char_queue_tail {
|
|
264
|
+
self.char_queue[self.char_queue_head] = c;
|
|
265
|
+
self.char_queue_head = next;
|
|
266
|
+
}
|
|
267
|
+
// If the queue is full, silently drop the character.
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/// Pop the next queued character. Returns 0 when the queue is empty.
|
|
271
|
+
/// Called once per frame by the editor's text-input widget.
|
|
272
|
+
pub fn pop_char(&mut self) -> u32 {
|
|
273
|
+
if self.char_queue_tail == self.char_queue_head {
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
let c = self.char_queue[self.char_queue_tail];
|
|
277
|
+
self.char_queue_tail = (self.char_queue_tail + 1) % self.char_queue.len();
|
|
278
|
+
c
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
pub fn set_mouse_button_down(&mut self, button: usize) {
|
|
282
|
+
if button < MAX_MOUSE_BUTTONS {
|
|
283
|
+
if !self.mouse_down[button] {
|
|
284
|
+
self.pending_mouse_pressed[button] = true;
|
|
285
|
+
}
|
|
286
|
+
self.mouse_down[button] = true;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
pub fn set_mouse_button_up(&mut self, button: usize) {
|
|
290
|
+
if button < MAX_MOUSE_BUTTONS {
|
|
291
|
+
if self.mouse_down[button] {
|
|
292
|
+
self.pending_mouse_released[button] = true;
|
|
293
|
+
}
|
|
294
|
+
self.mouse_down[button] = false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
pub fn is_mouse_button_pressed(&self, button: usize) -> bool { button < MAX_MOUSE_BUTTONS && self.mouse_pressed[button] }
|
|
299
|
+
pub fn is_mouse_button_down(&self, button: usize) -> bool { button < MAX_MOUSE_BUTTONS && self.mouse_down[button] }
|
|
300
|
+
pub fn is_mouse_button_released(&self, button: usize) -> bool { button < MAX_MOUSE_BUTTONS && self.mouse_released[button] }
|
|
301
|
+
|
|
302
|
+
// Gamepad
|
|
303
|
+
pub fn set_gamepad_axis(&mut self, axis: usize, value: f32) {
|
|
304
|
+
if axis < MAX_GAMEPAD_AXES { self.gamepad_axes[axis] = value; }
|
|
305
|
+
}
|
|
306
|
+
pub fn set_gamepad_button_down(&mut self, button: usize) {
|
|
307
|
+
if button < MAX_GAMEPAD_BUTTONS { self.gamepad_buttons_down[button] = true; }
|
|
308
|
+
}
|
|
309
|
+
pub fn set_gamepad_button_up(&mut self, button: usize) {
|
|
310
|
+
if button < MAX_GAMEPAD_BUTTONS { self.gamepad_buttons_down[button] = false; }
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/// Clear polled gamepad axes + buttons at the top of a hardware poll.
|
|
314
|
+
/// A poller only ever SETS the currently-pressed buttons/axes, so without
|
|
315
|
+
/// this a released button would stay `down` forever (begin_frame does not
|
|
316
|
+
/// reset gamepad state — the edge detector reads down-vs-prev). Call this
|
|
317
|
+
/// once per poll, only when a real controller is present, so it never
|
|
318
|
+
/// clobbers the inject path on frames with no pad connected.
|
|
319
|
+
pub fn reset_gamepad(&mut self) {
|
|
320
|
+
self.gamepad_axes = [0.0; MAX_GAMEPAD_AXES];
|
|
321
|
+
self.gamepad_buttons_down = [false; MAX_GAMEPAD_BUTTONS];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
pub fn is_gamepad_available(&self) -> bool { self.gamepad_available }
|
|
325
|
+
pub fn get_gamepad_axis(&self, axis: usize) -> f32 {
|
|
326
|
+
if axis < MAX_GAMEPAD_AXES { self.gamepad_axes[axis] } else { 0.0 }
|
|
327
|
+
}
|
|
328
|
+
pub fn is_gamepad_button_pressed(&self, button: usize) -> bool {
|
|
329
|
+
button < MAX_GAMEPAD_BUTTONS && self.gamepad_buttons_pressed[button]
|
|
330
|
+
}
|
|
331
|
+
pub fn is_gamepad_button_down(&self, button: usize) -> bool {
|
|
332
|
+
button < MAX_GAMEPAD_BUTTONS && self.gamepad_buttons_down[button]
|
|
333
|
+
}
|
|
334
|
+
pub fn is_gamepad_button_released(&self, button: usize) -> bool {
|
|
335
|
+
button < MAX_GAMEPAD_BUTTONS && self.gamepad_buttons_released[button]
|
|
336
|
+
}
|
|
337
|
+
pub fn get_gamepad_axis_count(&self) -> usize { self.gamepad_axis_count }
|
|
338
|
+
|
|
339
|
+
// Touch
|
|
340
|
+
pub fn set_touch(&mut self, index: usize, x: f64, y: f64, active: bool) {
|
|
341
|
+
if index < MAX_TOUCH_POINTS {
|
|
342
|
+
self.touch_points[index] = TouchPoint { x, y, active };
|
|
343
|
+
if active {
|
|
344
|
+
self.touch_pending_release[index] = false;
|
|
345
|
+
}
|
|
346
|
+
self.touch_count = self.touch_points.iter().filter(|t| t.active).count();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/// Deferred release: keeps the slot active for the current frame and
|
|
350
|
+
/// clears it at `end_frame`. Call from platform event loops on ACTION_UP
|
|
351
|
+
/// so a DOWN+UP that falls inside one frame remains visible to the game.
|
|
352
|
+
pub fn release_touch(&mut self, index: usize, x: f64, y: f64) {
|
|
353
|
+
if index < MAX_TOUCH_POINTS {
|
|
354
|
+
self.touch_points[index].x = x;
|
|
355
|
+
self.touch_points[index].y = y;
|
|
356
|
+
self.touch_pending_release[index] = true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
pub fn get_touch_x(&self, index: usize) -> f64 {
|
|
360
|
+
if index < MAX_TOUCH_POINTS { self.touch_points[index].x } else { 0.0 }
|
|
361
|
+
}
|
|
362
|
+
pub fn get_touch_y(&self, index: usize) -> f64 {
|
|
363
|
+
if index < MAX_TOUCH_POINTS { self.touch_points[index].y } else { 0.0 }
|
|
364
|
+
}
|
|
365
|
+
pub fn get_touch_count(&self) -> usize { self.touch_count }
|
|
366
|
+
/// Whether touch *slot* `index` currently holds a live finger.
|
|
367
|
+
///
|
|
368
|
+
/// `get_touch_count` counts active fingers, but `touch_points` is indexed
|
|
369
|
+
/// by slot, and slots go sparse as soon as a finger lifts out of order
|
|
370
|
+
/// (lift slot 0 while slot 1 is still held → count == 1, live finger at
|
|
371
|
+
/// slot 1). Scanning `0..get_touch_count()` therefore reads a released
|
|
372
|
+
/// slot's stale coordinates as if they were a live touch. Games that track
|
|
373
|
+
/// per-finger state must iterate `0..MAX_TOUCH_POINTS` and skip slots this
|
|
374
|
+
/// returns false for.
|
|
375
|
+
pub fn is_touch_active(&self, index: usize) -> bool {
|
|
376
|
+
index < MAX_TOUCH_POINTS && self.touch_points[index].active
|
|
377
|
+
}
|
|
378
|
+
pub fn max_touch_points(&self) -> usize { MAX_TOUCH_POINTS }
|
|
379
|
+
|
|
380
|
+
// Digital Crown (watchOS)
|
|
381
|
+
pub fn accumulate_crown_rotation(&mut self, delta: f64) { self.crown_rotation += delta; }
|
|
382
|
+
pub fn consume_crown_rotation(&mut self) -> f64 {
|
|
383
|
+
let v = self.crown_rotation;
|
|
384
|
+
self.crown_rotation = 0.0;
|
|
385
|
+
v
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Any input
|
|
389
|
+
pub fn is_any_input_pressed(&self) -> bool {
|
|
390
|
+
self.keys_pressed.iter().any(|&k| k)
|
|
391
|
+
|| self.mouse_pressed.iter().any(|&m| m)
|
|
392
|
+
|| self.gamepad_buttons_pressed.iter().any(|&g| g)
|
|
393
|
+
|| self.touch_count > 0
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
#[cfg(test)]
|
|
398
|
+
mod tests {
|
|
399
|
+
use super::InputState;
|
|
400
|
+
|
|
401
|
+
#[test]
|
|
402
|
+
fn mouse_edges_are_published_for_one_frame() {
|
|
403
|
+
let mut input = InputState::new();
|
|
404
|
+
|
|
405
|
+
input.set_mouse_button_down(0);
|
|
406
|
+
input.begin_frame();
|
|
407
|
+
assert!(input.is_mouse_button_pressed(0));
|
|
408
|
+
assert!(input.is_mouse_button_down(0));
|
|
409
|
+
assert!(!input.is_mouse_button_released(0));
|
|
410
|
+
|
|
411
|
+
input.end_frame();
|
|
412
|
+
input.begin_frame();
|
|
413
|
+
assert!(!input.is_mouse_button_pressed(0));
|
|
414
|
+
assert!(input.is_mouse_button_down(0));
|
|
415
|
+
assert!(!input.is_mouse_button_released(0));
|
|
416
|
+
|
|
417
|
+
input.set_mouse_button_up(0);
|
|
418
|
+
input.begin_frame();
|
|
419
|
+
assert!(!input.is_mouse_button_pressed(0));
|
|
420
|
+
assert!(!input.is_mouse_button_down(0));
|
|
421
|
+
assert!(input.is_mouse_button_released(0));
|
|
422
|
+
|
|
423
|
+
input.end_frame();
|
|
424
|
+
input.begin_frame();
|
|
425
|
+
assert!(!input.is_mouse_button_pressed(0));
|
|
426
|
+
assert!(!input.is_mouse_button_down(0));
|
|
427
|
+
assert!(!input.is_mouse_button_released(0));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
#[test]
|
|
431
|
+
fn fast_mouse_click_publishes_both_edges() {
|
|
432
|
+
let mut input = InputState::new();
|
|
433
|
+
|
|
434
|
+
input.set_mouse_button_down(0);
|
|
435
|
+
input.set_mouse_button_up(0);
|
|
436
|
+
input.begin_frame();
|
|
437
|
+
|
|
438
|
+
assert!(input.is_mouse_button_pressed(0));
|
|
439
|
+
assert!(!input.is_mouse_button_down(0));
|
|
440
|
+
assert!(input.is_mouse_button_released(0));
|
|
441
|
+
|
|
442
|
+
input.end_frame();
|
|
443
|
+
input.begin_frame();
|
|
444
|
+
assert!(!input.is_mouse_button_pressed(0));
|
|
445
|
+
assert!(!input.is_mouse_button_down(0));
|
|
446
|
+
assert!(!input.is_mouse_button_released(0));
|
|
447
|
+
}
|
|
448
|
+
}
|