@bloomengine/engine 0.4.9 → 0.4.10

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.
@@ -1,6 +1,6 @@
1
1
  use bloom_shared::engine::EngineState;
2
2
  use bloom_shared::renderer::Renderer;
3
- use bloom_shared::string_header::str_from_header;
3
+ use bloom_shared::string_header::{str_from_header, alloc_perry_string};
4
4
  use bloom_shared::audio::{parse_wav, parse_ogg, parse_mp3, SoundData};
5
5
 
6
6
  use objc2::encode::{Encode, Encoding, RefEncode};
@@ -3328,3 +3328,137 @@ pub extern "C" fn bloom_set_ssgi_enabled(enabled: f64) {
3328
3328
  pub extern "C" fn bloom_set_ssgi_intensity(intensity: f64) {
3329
3329
  engine().renderer.set_ssgi_intensity(intensity as f32);
3330
3330
  }
3331
+
3332
+ // ── Ported from native/macos for tvOS FFI parity (render textures, profiler, DOF/SSGI, splat) ──
3333
+ #[no_mangle]
3334
+ pub extern "C" fn bloom_begin_texture_mode(handle: f64) {
3335
+ let eng = engine();
3336
+ let (w, h, bg_idx) = match eng.textures.render_textures.get(handle) {
3337
+ Some(rt) => {
3338
+ let tex_handle = rt.texture_handle;
3339
+ match eng.textures.textures.get(tex_handle) {
3340
+ Some(td) => (rt.width, rt.height, td.bind_group_idx as usize),
3341
+ None => return,
3342
+ }
3343
+ }
3344
+ None => return,
3345
+ };
3346
+ if let Some(texture) = eng.renderer.get_texture_ref(bg_idx) {
3347
+ // We need to call begin_texture_mode with a reference to the texture,
3348
+ // but get_texture_ref borrows renderer immutably. Clone the texture view
3349
+ // data we need first, then call the mutable method.
3350
+ let color_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
3351
+ // Create depth texture for this RT.
3352
+ let depth_tex = eng.renderer.device.create_texture(&wgpu::TextureDescriptor {
3353
+ label: Some("rt_depth"), size: wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
3354
+ mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2,
3355
+ format: wgpu::TextureFormat::Depth32Float, usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
3356
+ view_formats: &[],
3357
+ });
3358
+ let depth_view = depth_tex.create_view(&wgpu::TextureViewDescriptor::default());
3359
+ eng.renderer.rt_color_view = Some(color_view);
3360
+ eng.renderer.rt_depth_view = Some(depth_view);
3361
+ eng.renderer.rt_depth_texture = Some(depth_tex);
3362
+ eng.renderer.rt_width = w;
3363
+ eng.renderer.rt_height = h;
3364
+ }
3365
+ }
3366
+
3367
+ #[no_mangle]
3368
+ pub extern "C" fn bloom_end_texture_mode() {
3369
+ engine().renderer.end_texture_mode();
3370
+ }
3371
+
3372
+ #[no_mangle]
3373
+ pub extern "C" fn bloom_get_render_texture_texture(handle: f64) -> f64 {
3374
+ engine().textures.get_render_texture_texture(handle)
3375
+ }
3376
+
3377
+ // Q1: Render texture FFI — create GPU textures for render-to-texture.
3378
+ #[no_mangle]
3379
+ pub extern "C" fn bloom_load_render_texture(width: f64, height: f64) -> f64 {
3380
+ let w = width as u32;
3381
+ let h = height as u32;
3382
+ let eng = engine();
3383
+ let rt_handle = eng.textures.load_render_texture(w, h);
3384
+
3385
+ // Create the GPU texture via the renderer's public method.
3386
+ let (bind_group_idx, _tex_vec_idx) = eng.renderer.create_render_texture(w, h);
3387
+
3388
+ // Register as a texture handle so drawTexture can sample it.
3389
+ let tex_handle = eng.textures.textures.alloc(bloom_shared::textures::TextureData {
3390
+ bind_group_idx, width: w, height: h,
3391
+ });
3392
+ eng.textures.set_render_texture_handle(rt_handle, tex_handle);
3393
+
3394
+ rt_handle
3395
+ }
3396
+
3397
+ #[no_mangle]
3398
+ pub extern "C" fn bloom_unload_render_texture(handle: f64) {
3399
+ engine().textures.unload_render_texture(handle);
3400
+ }
3401
+
3402
+ /// Phase 8 — frame history for the histogram. Header-prefixed string,
3403
+ /// one line per frame "<cpu_us>|<gpu_us>", oldest first, up to 120
3404
+ /// lines.
3405
+ #[no_mangle]
3406
+ pub extern "C" fn bloom_profiler_frame_history() -> *const u8 {
3407
+ let hist = engine().profiler.frame_history();
3408
+ let mut s = String::with_capacity(hist.len() * 24);
3409
+ for (cpu, gpu) in &hist {
3410
+ s.push_str(&format!("{:.2}|{:.2}\n", cpu, gpu));
3411
+ }
3412
+ alloc_perry_string(&s)
3413
+ }
3414
+
3415
+ #[no_mangle]
3416
+ pub extern "C" fn bloom_profiler_overlay_text() -> *const u8 {
3417
+ let snap = engine().profiler.snapshot();
3418
+ let mut s = String::with_capacity(snap.len() * 48);
3419
+ for (label, cpu, gpu) in &snap {
3420
+ s.push_str(label);
3421
+ s.push('|');
3422
+ s.push_str(&format!("{:.2}", cpu));
3423
+ s.push('|');
3424
+ match gpu {
3425
+ Some(g) => s.push_str(&format!("{:.2}", g)),
3426
+ None => s.push_str("-1"),
3427
+ }
3428
+ s.push('\n');
3429
+ }
3430
+ alloc_perry_string(&s)
3431
+ }
3432
+
3433
+ #[no_mangle]
3434
+ pub extern "C" fn bloom_set_dof(enabled: f64, focus_distance: f64, aperture: f64) {
3435
+ let r = &mut engine().renderer;
3436
+ r.set_dof_enabled(enabled != 0.0);
3437
+ r.set_dof_focus_distance(focus_distance as f32);
3438
+ r.set_dof_aperture(aperture as f32);
3439
+ }
3440
+
3441
+ #[no_mangle]
3442
+ pub extern "C" fn bloom_set_ssgi_radius(radius: f64) {
3443
+ engine().renderer.set_ssgi_radius(radius as f32);
3444
+ }
3445
+
3446
+ /// Phase 8 — formatted per-pass overlay text. Returns a Perry-style
3447
+ /// header-prefixed string (12-byte header: len, cap, flags, then
3448
+ /// UTF-8 bytes) with one line per pass sorted by CPU time descending:
3449
+ ///
3450
+ /// "<label>|<cpu_us>|<gpu_us_or_-1>\n..."
3451
+ ///
3452
+ /// The TS overlay splits on \n then | per line. Games call this once
3453
+ /// per overlay-draw frame.
3454
+ /// Phase 7 — submit a world-space impulse. The renderer's per-frame
3455
+ /// compute pass decays + accumulates submissions into a 256×256
3456
+ /// R32Float texture covering a 128 m centred square; materials that
3457
+ /// sample `impulse_tex` (group 4 binding 4) read the result. Up to 16
3458
+ /// splats per frame are accepted — overflow is dropped silently.
3459
+ #[no_mangle]
3460
+ pub extern "C" fn bloom_splat_impulse(x: f64, z: f64, radius: f64, strength: f64) {
3461
+ engine().renderer.impulse_field.submit_splat(
3462
+ x as f32, z as f32, radius as f32, strength as f32,
3463
+ );
3464
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloomengine/engine",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
4
4
  "description": "Bloom Engine: native TypeScript game engine compiled by Perry",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",