@bornengine/engine 0.4.16 → 0.5.0

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 +216 -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
@@ -15,6 +15,7 @@ mod planar_pass;
15
15
  mod material_instancing;
16
16
  mod postfx_chain;
17
17
  mod scene_pass;
18
+ mod ui_pass;
18
19
  mod gi_bake;
19
20
  mod froxel;
20
21
  mod lighting;
@@ -287,6 +288,8 @@ pub struct Renderer {
287
288
  pub logical_width: u32,
288
289
  pub logical_height: u32,
289
290
 
291
+ ui_renderer: ui_pass::UiRendererState,
292
+
290
293
  // Pipelines
291
294
  pipeline_2d: wgpu::RenderPipeline,
292
295
  pipeline_3d: wgpu::RenderPipeline,
@@ -6333,6 +6336,8 @@ impl Renderer {
6333
6336
  ..Default::default()
6334
6337
  });
6335
6338
 
6339
+ let ui_renderer = ui_pass::UiRendererState::new(&device, output_format);
6340
+
6336
6341
  let mut renderer = Self {
6337
6342
  headless_target: if surface.is_none() {
6338
6343
  Some(device.create_texture(&wgpu::TextureDescriptor {
@@ -6361,6 +6366,7 @@ impl Renderer {
6361
6366
  output_format,
6362
6367
  logical_width,
6363
6368
  logical_height,
6369
+ ui_renderer,
6364
6370
  pipeline_2d,
6365
6371
  pipeline_3d,
6366
6372
  uniform_buffers,
@@ -9753,203 +9759,15 @@ impl Renderer {
9753
9759
  self.material_per_view_bg_live = true;
9754
9760
  }
9755
9761
 
9756
- pub fn end_frame(&mut self) {
9757
- if !self.material_per_view_bg_live {
9758
- self.refresh_material_per_view_bg();
9759
- }
9760
- // Flush pending joint matrices to GPU right before rendering
9761
- self.flush_joint_matrices();
9762
- // One pooled upload for every cached-model draw's uniforms.
9763
- self.flush_model_uniforms();
9764
-
9765
- // Q1: If rendering to a texture, use the RT view. Otherwise use the surface.
9766
- // We take ownership of the RT views (via Option::take) to avoid holding a
9767
- // borrow on `self` while the rest of end_frame mutates it.
9768
- let rt_color = self.rt_color_view.take();
9769
- let rt_depth = self.rt_depth_view.take();
9770
- let using_rt = rt_color.is_some();
9771
-
9772
- let surface_output = if using_rt {
9773
- None
9774
- } else {
9775
- match self.acquire_frame() {
9776
- Some(t) => Some(t),
9777
- None => {
9778
- // Swapchain lost+reconfigured. Restore RT views if set.
9779
- self.rt_color_view = rt_color;
9780
- self.rt_depth_view = rt_depth;
9781
- return;
9782
- }
9783
- }
9784
- };
9785
-
9786
- let view: wgpu::TextureView;
9787
- let owned_depth_view: wgpu::TextureView;
9788
-
9789
- if let Some(ref rt_view) = rt_color {
9790
- view = rt_view.clone();
9791
- owned_depth_view = rt_depth.as_ref().unwrap().clone();
9792
- } else {
9793
- view = self.frame_texture(surface_output.as_ref().unwrap()).create_view(&wgpu::TextureViewDescriptor {
9794
- format: Some(self.output_format),
9795
- ..Default::default()
9796
- });
9797
- owned_depth_view = self.depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
9798
- }
9799
-
9800
- // Restore RT views so they persist across frames.
9801
- self.rt_color_view = rt_color;
9802
- self.rt_depth_view = rt_depth;
9803
-
9804
- let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
9805
- label: Some("bloom_encoder"),
9806
- });
9807
-
9808
- // Upload 2D data to persistent GPU buffers
9809
- let has_2d = !self.vertices_2d.is_empty();
9810
- if has_2d {
9811
- let vb_size = std::mem::size_of_val(self.vertices_2d.as_slice());
9812
- let ib_size = std::mem::size_of_val(self.indices_2d.as_slice());
9813
- self.ensure_buffer_capacity_2d(vb_size, ib_size);
9814
- self.queue.write_buffer(&self.persistent_vb_2d, 0, bytemuck::cast_slice(&self.vertices_2d));
9815
- self.queue.write_buffer(&self.persistent_ib_2d, 0, bytemuck::cast_slice(&self.indices_2d));
9816
- }
9817
-
9818
- // Upload 3D data to persistent GPU buffers
9819
- let has_3d = !self.vertices_3d.is_empty();
9820
- if has_3d {
9821
- let vb_size = std::mem::size_of_val(self.vertices_3d.as_slice());
9822
- let ib_size = std::mem::size_of_val(self.indices_3d.as_slice());
9823
- self.ensure_buffer_capacity_3d(vb_size, ib_size);
9824
- self.queue.write_buffer(&self.persistent_vb_3d, 0, bytemuck::cast_slice(&self.vertices_3d));
9825
- self.queue.write_buffer(&self.persistent_ib_3d, 0, bytemuck::cast_slice(&self.indices_3d));
9826
- }
9827
-
9828
- {
9829
- // Only attach a depth target when we're drawing 3D. pipeline_2d is
9830
- // depth-less; on some mobile Vulkan drivers (Adreno) pairing a
9831
- // depth-less pipeline with a pass that carries a depth attachment
9832
- // discards all draws silently. Matches the overlay_2d pass in
9833
- // end_frame_with_scene, which also omits depth.
9834
- let depth_attachment = if has_3d {
9835
- Some(wgpu::RenderPassDepthStencilAttachment {
9836
- view: &owned_depth_view,
9837
- depth_ops: Some(wgpu::Operations {
9838
- load: wgpu::LoadOp::Clear(1.0),
9839
- store: wgpu::StoreOp::Store,
9840
- }),
9841
- stencil_ops: None,
9842
- })
9843
- } else {
9844
- None
9845
- };
9846
- let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
9847
- label: Some("bloom_pass"),
9848
- color_attachments: &[Some(wgpu::RenderPassColorAttachment {
9849
- view: &view,
9850
- resolve_target: None,
9851
- depth_slice: None,
9852
- ops: wgpu::Operations {
9853
- load: wgpu::LoadOp::Clear(self.clear_color),
9854
- store: wgpu::StoreOp::Store,
9855
- },
9856
- })],
9857
- depth_stencil_attachment: depth_attachment,
9858
- timestamp_writes: None,
9859
- occlusion_query_set: None,
9860
- multiview_mask: None,
9861
- });
9862
-
9863
- // Draw 3D geometry first (with depth testing), batched by texture
9864
- if has_3d {
9865
- pass.set_pipeline(&self.pipeline_3d);
9866
- pass.set_bind_group(0, &self.uniform_bind_group_3d, &[]);
9867
- pass.set_bind_group(1, &self.lighting_bind_group, &[]);
9868
- pass.set_bind_group(3, &self.joint_bind_group, &[]);
9869
- pass.set_vertex_buffer(0, self.persistent_vb_3d.slice(..));
9870
- pass.set_index_buffer(self.persistent_ib_3d.slice(..), wgpu::IndexFormat::Uint32);
9871
-
9872
- if self.draw_calls_3d.is_empty() {
9873
- // No draw calls tracked — draw all with white texture (backward compat)
9874
- pass.set_bind_group(2, &self.texture_bind_groups[0], &[]);
9875
- pass.draw_indexed(0..self.indices_3d.len() as u32, 0, 0..1);
9876
- } else {
9877
- let num_calls = self.draw_calls_3d.len();
9878
- for i in 0..num_calls {
9879
- let call = &self.draw_calls_3d[i];
9880
- let next_start = if i + 1 < num_calls {
9881
- self.draw_calls_3d[i + 1].index_start
9882
- } else {
9883
- self.indices_3d.len() as u32
9884
- };
9885
- let count = next_start - call.index_start;
9886
- if count == 0 { continue; }
9887
- let tex_idx = call.texture_idx as usize;
9888
- if tex_idx < self.texture_bind_groups.len() {
9889
- pass.set_bind_group(2, &self.texture_bind_groups[tex_idx], &[]);
9890
- } else {
9891
- pass.set_bind_group(2, &self.texture_bind_groups[0], &[]);
9892
- }
9893
- pass.draw_indexed(call.index_start..next_start, 0, 0..1);
9894
- }
9895
- }
9896
- }
9897
-
9898
- // Draw cached models (static models with GPU-resident buffers).
9899
- // Use the scene pipeline so PBR-style material bindings (base
9900
- // color + normal map) apply — drawModel should behave the same
9901
- // as attachModelToNode for PBR purposes.
9902
- if !self.model_draw_commands.is_empty() {
9903
- pass.set_pipeline(&self.scene_pipeline);
9904
- pass.set_bind_group(1, &self.lighting_bind_group, &[]);
9905
- pass.set_bind_group(3, &self.joint_bind_group, &[]);
9906
-
9907
- for cmd in &self.model_draw_commands {
9908
- if let Some(Some(meshes)) = self.model_gpu_cache.get(&cmd.cache_handle) {
9909
- if cmd.mesh_idx < meshes.len() {
9910
- let mesh = &meshes[cmd.mesh_idx];
9911
- pass.set_bind_group(0, &self.model_uniform_bind_groups[cmd.uniform_slot], &[]);
9912
- pass.set_bind_group(2, &mesh.material_bg, &[]);
9913
- pass.set_vertex_buffer(0, mesh.vb.slice(..));
9914
- pass.set_index_buffer(mesh.ib.slice(..), wgpu::IndexFormat::Uint32);
9915
- pass.draw_indexed(0..mesh.index_count, 0, 0..1);
9916
- }
9917
- }
9918
- }
9919
- }
9920
-
9921
- // Draw 2D geometry (no depth testing, always passes)
9922
- if has_2d {
9923
- pass.set_pipeline(&self.pipeline_2d);
9924
- pass.set_vertex_buffer(0, self.persistent_vb_2d.slice(..));
9925
- pass.set_index_buffer(self.persistent_ib_2d.slice(..), wgpu::IndexFormat::Uint32);
9926
-
9927
- let num_calls = self.draw_calls_2d.len();
9928
- for i in 0..num_calls {
9929
- let call = &self.draw_calls_2d[i];
9930
- let next_start = if i + 1 < num_calls {
9931
- self.draw_calls_2d[i + 1].index_start
9932
- } else {
9933
- self.indices_2d.len() as u32
9934
- };
9935
- let count = next_start - call.index_start;
9936
- if count == 0 { continue; }
9937
-
9938
- pass.set_bind_group(0, &self.uniform_bind_groups[call.uniform_idx as usize], &[]);
9939
- if (call.texture_idx as usize) < self.texture_bind_groups.len() {
9940
- pass.set_bind_group(1, &self.texture_bind_groups[call.texture_idx as usize], &[]);
9941
- }
9942
- pass.draw_indexed(call.index_start..next_start, 0, 0..1);
9943
- }
9944
- }
9945
- }
9946
-
9947
- self.queue.submit(std::iter::once(encoder.finish()));
9948
- if let Some(out) = surface_output { self.present_frame(out); }
9949
- }
9950
-
9951
- /// Like end_frame, but also renders retained scene graph nodes.
9952
- pub fn end_frame_with_scene(&mut self, scene: &mut crate::scene::SceneGraph, profiler: &mut crate::profiler::Profiler) {
9762
+ #[allow(unused_variables)]
9763
+ fn end_frame_with_scene_internal(
9764
+ &mut self,
9765
+ scene: &mut crate::scene::SceneGraph,
9766
+ profiler: &mut crate::profiler::Profiler,
9767
+ ui: Option<&mut crate::ui::UiSystem>,
9768
+ dt: f64,
9769
+ native_texture_indices: HashMap<u64, u32>,
9770
+ ) {
9953
9771
  if !self.material_per_view_bg_live {
9954
9772
  self.refresh_material_per_view_bg();
9955
9773
  }
@@ -10567,6 +10385,21 @@ impl Renderer {
10567
10385
  profiler.end("overlay_2d");
10568
10386
  }
10569
10387
 
10388
+ let ui_command_buffers =
10389
+ self.encode_egui_pass(&mut encoder, &view, using_rt || self.surface.is_none());
10390
+
10391
+ #[cfg(feature = "debug-ui")]
10392
+ if let Some(ui) = ui {
10393
+ self.encode_dear_imgui_pass(
10394
+ &mut encoder,
10395
+ &view,
10396
+ using_rt || self.surface.is_none(),
10397
+ ui,
10398
+ dt,
10399
+ &native_texture_indices,
10400
+ );
10401
+ }
10402
+
10570
10403
  profiler.resolve(&mut encoder);
10571
10404
 
10572
10405
  // If screenshot requested, copy rendered texture to staging buffer before submitting.
@@ -10609,7 +10442,11 @@ impl Renderer {
10609
10442
  wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
10610
10443
  );
10611
10444
 
10612
- self.queue.submit(std::iter::once(encoder.finish()));
10445
+ self.queue.submit(
10446
+ ui_command_buffers
10447
+ .into_iter()
10448
+ .chain(std::iter::once(encoder.finish())),
10449
+ );
10613
10450
 
10614
10451
  // Read back pixels synchronously
10615
10452
  let slice = staging.slice(..);
@@ -10660,7 +10497,11 @@ impl Renderer {
10660
10497
  self.screenshot_requested = false;
10661
10498
  } else {
10662
10499
  profiler.begin("queue_submit");
10663
- self.queue.submit(std::iter::once(encoder.finish()));
10500
+ self.queue.submit(
10501
+ ui_command_buffers
10502
+ .into_iter()
10503
+ .chain(std::iter::once(encoder.finish())),
10504
+ );
10664
10505
  profiler.end("queue_submit");
10665
10506
  }
10666
10507
 
@@ -10682,9 +10523,15 @@ impl Renderer {
10682
10523
 
10683
10524
  #[cfg(target_arch = "wasm32")]
10684
10525
  {
10685
- self.queue.submit(std::iter::once(encoder.finish()));
10526
+ self.queue.submit(
10527
+ ui_command_buffers
10528
+ .into_iter()
10529
+ .chain(std::iter::once(encoder.finish())),
10530
+ );
10686
10531
  }
10687
10532
 
10533
+ self.free_pending_egui_textures();
10534
+
10688
10535
  profiler.begin("swap_present");
10689
10536
  if let Some(out) = output {
10690
10537
  self.present_frame(out);
@@ -12553,4 +12400,3 @@ impl Renderer {
12553
12400
  self.material_system.update_frame_uniforms(&self.queue, &per_frame, &per_view);
12554
12401
  }
12555
12402
  }
12556
-
@@ -75,6 +75,9 @@ impl Renderer {
75
75
  self.textures[i] = texture;
76
76
  self.texture_bind_groups[i] = bind_group;
77
77
  self.texture_sizes[i] = (width, height);
78
+ self.refresh_egui_texture_index(idx);
79
+ #[cfg(feature = "debug-ui")]
80
+ self.refresh_dear_imgui_texture_index(idx);
78
81
  }
79
82
 
80
83
  /// Register a texture with optional normal-map preprocessing.
@@ -328,6 +331,8 @@ impl Renderer {
328
331
  self.textures[i] = white;
329
332
  self.texture_bind_groups[i] = bind_group;
330
333
  self.texture_sizes[i] = (0, 0);
334
+ #[cfg(feature = "debug-ui")]
335
+ self.refresh_dear_imgui_texture_index(idx);
331
336
  }
332
337
 
333
338
  /// Drop a model's cached GPU meshes. Must be called when the model is