@bloomengine/engine 0.4.2 → 0.4.4
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 +23 -2
- package/native/ios/Cargo.toml +8 -0
- package/native/macos/Cargo.toml +8 -0
- package/native/tvos/Cargo.lock +1 -0
- package/native/tvos/Cargo.toml +10 -0
- package/native/tvos/src/lib.rs +151 -0
- package/package.json +2 -2
- package/src/core/colors.ts +2 -2
package/README.md
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
Write TypeScript. Ship native games — and now the web too.
|
|
6
6
|
Bloom compiles your game to Metal, DirectX 12, Vulkan, OpenGL, and WebGPU — one codebase for every platform.
|
|
7
7
|
|
|
8
|
+
> **Inspired by [raylib](https://github.com/raysan5/raylib).** Bloom models its public
|
|
9
|
+
> API on raylib's — in our view one of the best API designs in gamedev. Bloom is an
|
|
10
|
+
> independent implementation, not a port — [how Bloom relates to raylib »](#how-bloom-relates-to-raylib)
|
|
11
|
+
|
|
8
12
|
## Install
|
|
9
13
|
|
|
10
14
|
```bash
|
|
@@ -37,7 +41,7 @@ initWindow(800, 450, "My Game");
|
|
|
37
41
|
|
|
38
42
|
while (!windowShouldClose()) {
|
|
39
43
|
beginDrawing();
|
|
40
|
-
clearBackground(Colors.
|
|
44
|
+
clearBackground(Colors.SNOW);
|
|
41
45
|
drawText("Hello, Bloom!", 190, 200, 20, Colors.DARKGRAY);
|
|
42
46
|
endDrawing();
|
|
43
47
|
}
|
|
@@ -53,7 +57,7 @@ import { initWindow, runGame, clearBackground, drawText, Colors } from "@bloomen
|
|
|
53
57
|
initWindow(800, 450, "My Game");
|
|
54
58
|
|
|
55
59
|
runGame((dt) => {
|
|
56
|
-
clearBackground(Colors.
|
|
60
|
+
clearBackground(Colors.SNOW);
|
|
57
61
|
drawText("Hello, Bloom!", 190, 200, 20, Colors.DARKGRAY);
|
|
58
62
|
});
|
|
59
63
|
```
|
|
@@ -73,6 +77,23 @@ cd dist/web && python3 -m http.server 8080
|
|
|
73
77
|
- **Unified 2D/3D** — Shapes, textures, text, 3D models, and audio in one engine.
|
|
74
78
|
- **Zero magic** — Explicit game loops, no hidden framework overhead.
|
|
75
79
|
|
|
80
|
+
## How Bloom relates to raylib
|
|
81
|
+
|
|
82
|
+
Bloom's public API is heavily inspired by [raylib](https://github.com/raysan5/raylib).
|
|
83
|
+
raylib's API is, in our opinion, one of the best in the gamedev space — a flat library
|
|
84
|
+
of plain functions, no classes, small enough to learn from a cheatsheet — so we model
|
|
85
|
+
ours on it. You'll recognize the shape immediately: `initWindow`, `beginDrawing`,
|
|
86
|
+
`clearBackground`, `drawText`, and modules named core / shapes / textures / text /
|
|
87
|
+
audio / models.
|
|
88
|
+
|
|
89
|
+
That's where the relationship ends. **Bloom's implementation is entirely independent —
|
|
90
|
+
it does not link against, embed, or call raylib.** Bloom compiles TypeScript directly to
|
|
91
|
+
native code via Perry, our LLVM-based AOT compiler, and renders through wgpu (Metal,
|
|
92
|
+
DirectX 12, Vulkan, OpenGL, WebGPU). It is not a port or a binding — just an engine that
|
|
93
|
+
admires raylib's API design. Thanks to
|
|
94
|
+
[Ramon Santamaria (@raysan5)](https://github.com/raysan5) and the raylib community for
|
|
95
|
+
setting the bar. ([full design rationale](docs/design-api.md))
|
|
96
|
+
|
|
76
97
|
## Modules
|
|
77
98
|
|
|
78
99
|
| Module | Import | Description |
|
package/native/ios/Cargo.toml
CHANGED
|
@@ -11,6 +11,14 @@ crate-type = ["staticlib"]
|
|
|
11
11
|
default = ["jolt"]
|
|
12
12
|
jolt = ["bloom-shared/jolt"]
|
|
13
13
|
|
|
14
|
+
# Match perry-runtime's panic strategy so the final perry-driven link
|
|
15
|
+
# doesn't see two copies of rust_eh_personality (and friends) from two
|
|
16
|
+
# independent Rust staticlibs. ld64.lld flags these as duplicate symbols;
|
|
17
|
+
# GNU ld silently tolerates them, which is why only Apple targets break.
|
|
18
|
+
# Mirrors the same setting in native/watchos/Cargo.toml.
|
|
19
|
+
[profile.release]
|
|
20
|
+
panic = "abort"
|
|
21
|
+
|
|
14
22
|
[dependencies]
|
|
15
23
|
bloom-shared = { path = "../shared", default-features = false, features = ["mp3"] }
|
|
16
24
|
objc2 = "0.6"
|
package/native/macos/Cargo.toml
CHANGED
|
@@ -15,6 +15,14 @@ crate-type = ["staticlib"]
|
|
|
15
15
|
default = ["jolt"]
|
|
16
16
|
jolt = ["bloom-shared/jolt"]
|
|
17
17
|
|
|
18
|
+
# Match perry-runtime's panic strategy so the final perry-driven link
|
|
19
|
+
# doesn't see two copies of rust_eh_personality (and friends) from two
|
|
20
|
+
# independent Rust staticlibs. ld64.lld flags these as duplicate symbols;
|
|
21
|
+
# GNU ld silently tolerates them, which is why only Apple targets break.
|
|
22
|
+
# Mirrors the same setting in native/watchos/Cargo.toml.
|
|
23
|
+
[profile.release]
|
|
24
|
+
panic = "abort"
|
|
25
|
+
|
|
18
26
|
[dependencies]
|
|
19
27
|
bloom-shared = { path = "../shared", default-features = false, features = ["mp3"] }
|
|
20
28
|
image = { version = "0.25", default-features = false, features = ["hdr"] }
|
package/native/tvos/Cargo.lock
CHANGED
package/native/tvos/Cargo.toml
CHANGED
|
@@ -11,12 +11,22 @@ crate-type = ["staticlib"]
|
|
|
11
11
|
default = ["jolt"]
|
|
12
12
|
jolt = ["bloom-shared/jolt"]
|
|
13
13
|
|
|
14
|
+
# Match perry-runtime's panic strategy so the final perry-driven link
|
|
15
|
+
# doesn't see two copies of rust_eh_personality (and friends) from two
|
|
16
|
+
# independent Rust staticlibs. ld64.lld flags these as duplicate symbols;
|
|
17
|
+
# GNU ld silently tolerates them, which is why only Apple targets break.
|
|
18
|
+
# Mirrors the same setting in native/watchos/Cargo.toml.
|
|
19
|
+
[profile.release]
|
|
20
|
+
panic = "abort"
|
|
21
|
+
|
|
14
22
|
[dependencies]
|
|
15
23
|
bloom-shared = { path = "../shared", default-features = false, features = ["mp3"] }
|
|
16
24
|
objc2 = "0.6"
|
|
17
25
|
objc2-foundation = { version = "0.3", features = ["NSDate", "NSRunLoop", "NSString", "NSThread", "NSObject"] }
|
|
18
26
|
raw-window-handle = "0.6"
|
|
19
27
|
wgpu = "29"
|
|
28
|
+
# Needed by bloom_set_env_clear_from_hdr (HDR env-map decode); mirrors macOS.
|
|
29
|
+
image = { version = "0.25", default-features = false, features = ["hdr"] }
|
|
20
30
|
|
|
21
31
|
[patch.crates-io]
|
|
22
32
|
metal = { path = "metal-patched" }
|
package/native/tvos/src/lib.rs
CHANGED
|
@@ -3177,3 +3177,154 @@ fn bloom_jolt_ffi_physics() -> &'static mut bloom_shared::physics_jolt::JoltPhys
|
|
|
3177
3177
|
|
|
3178
3178
|
#[cfg(feature = "jolt")]
|
|
3179
3179
|
bloom_shared::define_physics_ffi!();
|
|
3180
|
+
|
|
3181
|
+
// ============================================================
|
|
3182
|
+
// Screenshot + HDR env + Post-FX / resolution FFI
|
|
3183
|
+
// ------------------------------------------------------------
|
|
3184
|
+
// Ported from native/macos/src/lib.rs. These delegate to the shared
|
|
3185
|
+
// bloom_shared renderer (identical type used here), so they are real
|
|
3186
|
+
// implementations, not stubs. They were present on macOS/linux/windows
|
|
3187
|
+
// but missing on tvOS, which caused `ld64.lld: undefined symbol: _bloom_*`
|
|
3188
|
+
// link errors for any app using the post-processing API on tvOS.
|
|
3189
|
+
// ============================================================
|
|
3190
|
+
|
|
3191
|
+
/// Request a PNG screenshot of the next rendered frame. The capture happens
|
|
3192
|
+
/// during the next end_drawing(); used by bloom-diff / CI image regression.
|
|
3193
|
+
#[no_mangle]
|
|
3194
|
+
pub extern "C" fn bloom_take_screenshot(path_ptr: *const u8) {
|
|
3195
|
+
let path = str_from_header(path_ptr).to_string();
|
|
3196
|
+
let eng = engine();
|
|
3197
|
+
eng.renderer.screenshot_requested = true;
|
|
3198
|
+
eng.renderer.pending_screenshot_path = Some(path);
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
/// Load an HDR equirectangular environment map and upload it to the GPU.
|
|
3202
|
+
/// The file must be Radiance HDR (.hdr).
|
|
3203
|
+
#[no_mangle]
|
|
3204
|
+
pub extern "C" fn bloom_set_env_clear_from_hdr(path_ptr: *const u8) {
|
|
3205
|
+
use image::ImageDecoder;
|
|
3206
|
+
let path = str_from_header(path_ptr).to_string();
|
|
3207
|
+
let file = match std::fs::File::open(&path) {
|
|
3208
|
+
Ok(f) => f,
|
|
3209
|
+
Err(_) => return,
|
|
3210
|
+
};
|
|
3211
|
+
let decoder = match image::codecs::hdr::HdrDecoder::new(std::io::BufReader::new(file)) {
|
|
3212
|
+
Ok(d) => d,
|
|
3213
|
+
Err(_) => return,
|
|
3214
|
+
};
|
|
3215
|
+
let (w, h) = decoder.dimensions();
|
|
3216
|
+
let byte_len = (w as usize) * (h as usize) * 3 * 4;
|
|
3217
|
+
let mut buf = vec![0u8; byte_len];
|
|
3218
|
+
if decoder.read_image(&mut buf).is_err() {
|
|
3219
|
+
return;
|
|
3220
|
+
}
|
|
3221
|
+
let rgb_f32: Vec<f32> = buf
|
|
3222
|
+
.chunks_exact(4)
|
|
3223
|
+
.map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
|
|
3224
|
+
.collect();
|
|
3225
|
+
engine().renderer.load_env_from_hdr(w, h, &rgb_f32);
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
// --- Post-FX knobs (heuristic visual layer; default-off) ---
|
|
3229
|
+
|
|
3230
|
+
#[no_mangle]
|
|
3231
|
+
pub extern "C" fn bloom_set_fog(r: f64, g: f64, b: f64, density: f64, height_ref: f64, height_falloff: f64) {
|
|
3232
|
+
let r_ = engine();
|
|
3233
|
+
r_.renderer.set_fog_color(r as f32, g as f32, b as f32);
|
|
3234
|
+
r_.renderer.set_fog_density(density as f32);
|
|
3235
|
+
r_.renderer.set_fog_height_falloff(height_ref as f32, height_falloff as f32);
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
#[no_mangle]
|
|
3239
|
+
pub extern "C" fn bloom_set_chromatic_aberration(strength: f64) {
|
|
3240
|
+
engine().renderer.set_chromatic_aberration(strength as f32);
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
#[no_mangle]
|
|
3244
|
+
pub extern "C" fn bloom_set_vignette(strength: f64, softness: f64) {
|
|
3245
|
+
engine().renderer.set_vignette(strength as f32, softness as f32);
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
#[no_mangle]
|
|
3249
|
+
pub extern "C" fn bloom_set_film_grain(strength: f64) {
|
|
3250
|
+
engine().renderer.set_film_grain(strength as f32);
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
#[no_mangle]
|
|
3254
|
+
pub extern "C" fn bloom_set_sun_shafts(strength: f64, decay: f64, r: f64, g: f64, b: f64) {
|
|
3255
|
+
let eng = engine();
|
|
3256
|
+
eng.renderer.set_sun_shaft_strength(strength as f32);
|
|
3257
|
+
eng.renderer.set_sun_shaft_decay(decay as f32);
|
|
3258
|
+
eng.renderer.set_sun_shaft_color(r as f32, g as f32, b as f32);
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
#[no_mangle]
|
|
3262
|
+
pub extern "C" fn bloom_set_auto_exposure(on: f64) {
|
|
3263
|
+
engine().renderer.set_auto_exposure(on != 0.0);
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
#[no_mangle]
|
|
3267
|
+
pub extern "C" fn bloom_set_taa_enabled(on: f64) {
|
|
3268
|
+
engine().renderer.set_taa_enabled(on != 0.0);
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
#[no_mangle]
|
|
3272
|
+
pub extern "C" fn bloom_set_render_scale(scale: f64) {
|
|
3273
|
+
engine().renderer.set_render_scale(scale as f32);
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
#[no_mangle]
|
|
3277
|
+
pub extern "C" fn bloom_get_render_scale() -> f64 {
|
|
3278
|
+
engine().renderer.render_scale() as f64
|
|
3279
|
+
}
|
|
3280
|
+
|
|
3281
|
+
#[no_mangle]
|
|
3282
|
+
pub extern "C" fn bloom_set_upscale_mode(mode: f64) {
|
|
3283
|
+
engine().renderer.set_upscale_mode(mode as u32);
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
#[no_mangle]
|
|
3287
|
+
pub extern "C" fn bloom_set_cas_strength(strength: f64) {
|
|
3288
|
+
engine().renderer.set_cas_strength(strength as f32);
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
#[no_mangle]
|
|
3292
|
+
pub extern "C" fn bloom_get_physical_width() -> f64 {
|
|
3293
|
+
engine().renderer.physical_width() as f64
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
#[no_mangle]
|
|
3297
|
+
pub extern "C" fn bloom_get_physical_height() -> f64 {
|
|
3298
|
+
engine().renderer.physical_height() as f64
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
#[no_mangle]
|
|
3302
|
+
pub extern "C" fn bloom_set_auto_resolution(target_hz: f64, enabled: f64) {
|
|
3303
|
+
let eng = engine();
|
|
3304
|
+
if enabled != 0.0 {
|
|
3305
|
+
let current = eng.renderer.render_scale();
|
|
3306
|
+
eng.drs.enable(target_hz as f32, current);
|
|
3307
|
+
} else {
|
|
3308
|
+
eng.drs.disable();
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
#[no_mangle]
|
|
3313
|
+
pub extern "C" fn bloom_set_manual_exposure(value: f64) {
|
|
3314
|
+
engine().renderer.set_manual_exposure(value as f32);
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
#[no_mangle]
|
|
3318
|
+
pub extern "C" fn bloom_set_env_intensity(intensity: f64) {
|
|
3319
|
+
engine().renderer.set_env_intensity(intensity as f32);
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
#[no_mangle]
|
|
3323
|
+
pub extern "C" fn bloom_set_ssgi_enabled(enabled: f64) {
|
|
3324
|
+
engine().renderer.set_ssgi_enabled(enabled != 0.0);
|
|
3325
|
+
}
|
|
3326
|
+
|
|
3327
|
+
#[no_mangle]
|
|
3328
|
+
pub extern "C" fn bloom_set_ssgi_intensity(intensity: f64) {
|
|
3329
|
+
engine().renderer.set_ssgi_intensity(intensity as f32);
|
|
3330
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloomengine/engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Bloom Engine: native TypeScript game engine compiled by Perry",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -536,7 +536,7 @@
|
|
|
536
536
|
},
|
|
537
537
|
"linux": {
|
|
538
538
|
"crate": "native/linux/",
|
|
539
|
-
"lib": "
|
|
539
|
+
"lib": "libbloom_linux.a",
|
|
540
540
|
"libs": ["stdc++"],
|
|
541
541
|
"pkgConfig": ["x11", "xi", "alsa"]
|
|
542
542
|
},
|
package/src/core/colors.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Color as ColorType } from './types';
|
|
|
5
5
|
// emits a `_perry_fn_src_core_colors_ts__Color` symbol that examples
|
|
6
6
|
// importing `Color` from `bloom/core` can link against.
|
|
7
7
|
export const Color: Record<string, ColorType> = {
|
|
8
|
-
|
|
8
|
+
Snow: { r: 245, g: 245, b: 245, a: 255 },
|
|
9
9
|
White: { r: 255, g: 255, b: 255, a: 255 },
|
|
10
10
|
Black: { r: 0, g: 0, b: 0, a: 255 },
|
|
11
11
|
Red: { r: 230, g: 41, b: 55, a: 255 },
|
|
@@ -58,6 +58,6 @@ export const Colors: Record<string, ColorType> = {
|
|
|
58
58
|
BEIGE: Color.Beige,
|
|
59
59
|
MAGENTA: Color.Magenta,
|
|
60
60
|
VIOLET: Color.Violet,
|
|
61
|
-
|
|
61
|
+
SNOW: Color.Snow,
|
|
62
62
|
BLANK: Color.Blank,
|
|
63
63
|
};
|