@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.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +231 -0
  3. package/native/android/Cargo.lock +1848 -0
  4. package/native/android/Cargo.toml +24 -0
  5. package/native/android/src/lib.rs +702 -0
  6. package/native/ios/Cargo.lock +1690 -0
  7. package/native/ios/Cargo.toml +32 -0
  8. package/native/ios/src/lib.rs +1267 -0
  9. package/native/linux/Cargo.lock +3279 -0
  10. package/native/linux/Cargo.toml +29 -0
  11. package/native/linux/src/lib.rs +1331 -0
  12. package/native/macos/Cargo.lock +3310 -0
  13. package/native/macos/Cargo.toml +46 -0
  14. package/native/macos/src/lib.rs +1302 -0
  15. package/native/shared/Cargo.lock +1899 -0
  16. package/native/shared/Cargo.toml +62 -0
  17. package/native/shared/assets/default_font.ttf +0 -0
  18. package/native/shared/build.rs +270 -0
  19. package/native/shared/shaders/common/clouds.wgsl +122 -0
  20. package/native/shared/shaders/common/fog.wgsl +16 -0
  21. package/native/shared/shaders/common/foliage_wind.wgsl +98 -0
  22. package/native/shared/shaders/common/imposter.wgsl +112 -0
  23. package/native/shared/shaders/common/pbr.wgsl +186 -0
  24. package/native/shared/shaders/common/shadows.wgsl +186 -0
  25. package/native/shared/shaders/common/sky.wgsl +8 -0
  26. package/native/shared/shaders/common/tonemap.wgsl +25 -0
  27. package/native/shared/shaders/impulse_field.wgsl +57 -0
  28. package/native/shared/shaders/material_abi.wgsl +383 -0
  29. package/native/shared/shaders/materials/test_minimal.wgsl +42 -0
  30. package/native/shared/src/anim_mixer.rs +61 -0
  31. package/native/shared/src/attach.rs +263 -0
  32. package/native/shared/src/audio/decode.rs +123 -0
  33. package/native/shared/src/audio/mod.rs +863 -0
  34. package/native/shared/src/audio/render.rs +892 -0
  35. package/native/shared/src/audio/spsc.rs +156 -0
  36. package/native/shared/src/audio/stream.rs +226 -0
  37. package/native/shared/src/custom_shaders.rs +104 -0
  38. package/native/shared/src/decals.rs +245 -0
  39. package/native/shared/src/drs.rs +211 -0
  40. package/native/shared/src/engine.rs +261 -0
  41. package/native/shared/src/ffi.rs +116 -0
  42. package/native/shared/src/ffi_core/assets.rs +388 -0
  43. package/native/shared/src/ffi_core/audio_ffi.rs +184 -0
  44. package/native/shared/src/ffi_core/draw.rs +334 -0
  45. package/native/shared/src/ffi_core/game_loop.rs +577 -0
  46. package/native/shared/src/ffi_core/input.rs +234 -0
  47. package/native/shared/src/ffi_core/mod.rs +127 -0
  48. package/native/shared/src/ffi_core/models.rs +1154 -0
  49. package/native/shared/src/ffi_core/ragdoll_ffi.rs +261 -0
  50. package/native/shared/src/ffi_core/scene.rs +626 -0
  51. package/native/shared/src/ffi_core/vfx.rs +212 -0
  52. package/native/shared/src/ffi_core/visual.rs +691 -0
  53. package/native/shared/src/frame_callbacks.rs +122 -0
  54. package/native/shared/src/geometry.rs +236 -0
  55. package/native/shared/src/handles.rs +182 -0
  56. package/native/shared/src/input.rs +448 -0
  57. package/native/shared/src/jolt_sys.rs +822 -0
  58. package/native/shared/src/lib.rs +55 -0
  59. package/native/shared/src/models.rs +1093 -0
  60. package/native/shared/src/models_gltf.rs +1280 -0
  61. package/native/shared/src/particles.rs +391 -0
  62. package/native/shared/src/physics_jolt.rs +1908 -0
  63. package/native/shared/src/picking.rs +298 -0
  64. package/native/shared/src/postfx.rs +345 -0
  65. package/native/shared/src/profiler.rs +492 -0
  66. package/native/shared/src/ragdoll.rs +474 -0
  67. package/native/shared/src/renderer/atmosphere_lut.rs +573 -0
  68. package/native/shared/src/renderer/brdf_lut.rs +154 -0
  69. package/native/shared/src/renderer/draw2d.rs +143 -0
  70. package/native/shared/src/renderer/formats.rs +822 -0
  71. package/native/shared/src/renderer/froxel.rs +421 -0
  72. package/native/shared/src/renderer/gi_bake.rs +653 -0
  73. package/native/shared/src/renderer/graph.rs +462 -0
  74. package/native/shared/src/renderer/hiz.rs +269 -0
  75. package/native/shared/src/renderer/hot_reload.rs +390 -0
  76. package/native/shared/src/renderer/impulse_field.rs +456 -0
  77. package/native/shared/src/renderer/lighting.rs +154 -0
  78. package/native/shared/src/renderer/material_instancing.rs +171 -0
  79. package/native/shared/src/renderer/material_pipeline.rs +700 -0
  80. package/native/shared/src/renderer/material_system.rs +1996 -0
  81. package/native/shared/src/renderer/material_system_tests.rs +601 -0
  82. package/native/shared/src/renderer/material_system_wasm.rs +41 -0
  83. package/native/shared/src/renderer/mod.rs +12556 -0
  84. package/native/shared/src/renderer/model_draw.rs +641 -0
  85. package/native/shared/src/renderer/occlusion.rs +429 -0
  86. package/native/shared/src/renderer/planar_pass.rs +593 -0
  87. package/native/shared/src/renderer/planar_reflection.rs +499 -0
  88. package/native/shared/src/renderer/post_pass.rs +249 -0
  89. package/native/shared/src/renderer/postfx_chain.rs +728 -0
  90. package/native/shared/src/renderer/pt_pass.rs +577 -0
  91. package/native/shared/src/renderer/scene_pass.rs +607 -0
  92. package/native/shared/src/renderer/shader_include.rs +205 -0
  93. package/native/shared/src/renderer/shader_library.rs +135 -0
  94. package/native/shared/src/renderer/shaders/ao.rs +570 -0
  95. package/native/shared/src/renderer/shaders/core.rs +1243 -0
  96. package/native/shared/src/renderer/shaders/env.rs +907 -0
  97. package/native/shared/src/renderer/shaders/gi.rs +810 -0
  98. package/native/shared/src/renderer/shaders/mod.rs +19 -0
  99. package/native/shared/src/renderer/shaders/post.rs +1558 -0
  100. package/native/shared/src/renderer/shaders/pt.rs +1859 -0
  101. package/native/shared/src/renderer/shaders/ssgi.rs +1586 -0
  102. package/native/shared/src/renderer/shadow_pass.rs +731 -0
  103. package/native/shared/src/renderer/ssgi_pass.rs +392 -0
  104. package/native/shared/src/renderer/ssr_pass.rs +188 -0
  105. package/native/shared/src/renderer/texture_store.rs +473 -0
  106. package/native/shared/src/renderer/transient.rs +591 -0
  107. package/native/shared/src/renderer/types.rs +941 -0
  108. package/native/shared/src/renderer/util.rs +152 -0
  109. package/native/shared/src/scene.rs +1362 -0
  110. package/native/shared/src/sdf_cache.rs +274 -0
  111. package/native/shared/src/shadows.rs +1036 -0
  112. package/native/shared/src/staging.rs +102 -0
  113. package/native/shared/src/string_header.rs +266 -0
  114. package/native/shared/src/text_renderer.rs +502 -0
  115. package/native/shared/src/textures.rs +197 -0
  116. package/native/tvos/Cargo.lock +1693 -0
  117. package/native/tvos/Cargo.toml +36 -0
  118. package/native/tvos/metal-patched/Cargo.toml +178 -0
  119. package/native/tvos/metal-patched/LICENSE-APACHE +201 -0
  120. package/native/tvos/metal-patched/LICENSE-MIT +25 -0
  121. package/native/tvos/metal-patched/src/acceleration_structure.rs +667 -0
  122. package/native/tvos/metal-patched/src/acceleration_structure_pass.rs +108 -0
  123. package/native/tvos/metal-patched/src/argument.rs +366 -0
  124. package/native/tvos/metal-patched/src/blitpass.rs +102 -0
  125. package/native/tvos/metal-patched/src/buffer.rs +71 -0
  126. package/native/tvos/metal-patched/src/capturedescriptor.rs +76 -0
  127. package/native/tvos/metal-patched/src/capturemanager.rs +113 -0
  128. package/native/tvos/metal-patched/src/commandbuffer.rs +192 -0
  129. package/native/tvos/metal-patched/src/commandqueue.rs +44 -0
  130. package/native/tvos/metal-patched/src/computepass.rs +107 -0
  131. package/native/tvos/metal-patched/src/constants.rs +152 -0
  132. package/native/tvos/metal-patched/src/counters.rs +119 -0
  133. package/native/tvos/metal-patched/src/depthstencil.rs +190 -0
  134. package/native/tvos/metal-patched/src/device.rs +2134 -0
  135. package/native/tvos/metal-patched/src/drawable.rs +39 -0
  136. package/native/tvos/metal-patched/src/encoder.rs +2041 -0
  137. package/native/tvos/metal-patched/src/heap.rs +281 -0
  138. package/native/tvos/metal-patched/src/indirect_encoder.rs +344 -0
  139. package/native/tvos/metal-patched/src/lib.rs +657 -0
  140. package/native/tvos/metal-patched/src/library.rs +902 -0
  141. package/native/tvos/metal-patched/src/mps.rs +575 -0
  142. package/native/tvos/metal-patched/src/pipeline/compute.rs +475 -0
  143. package/native/tvos/metal-patched/src/pipeline/mod.rs +71 -0
  144. package/native/tvos/metal-patched/src/pipeline/render.rs +762 -0
  145. package/native/tvos/metal-patched/src/renderpass.rs +443 -0
  146. package/native/tvos/metal-patched/src/resource.rs +182 -0
  147. package/native/tvos/metal-patched/src/sampler.rs +165 -0
  148. package/native/tvos/metal-patched/src/sync.rs +178 -0
  149. package/native/tvos/metal-patched/src/texture.rs +352 -0
  150. package/native/tvos/metal-patched/src/types.rs +90 -0
  151. package/native/tvos/metal-patched/src/vertexdescriptor.rs +250 -0
  152. package/native/tvos/src/audio_backend.rs +197 -0
  153. package/native/tvos/src/lib.rs +1891 -0
  154. package/native/visionos/Cargo.lock +1693 -0
  155. package/native/visionos/Cargo.toml +40 -0
  156. package/native/visionos/src/audio_backend.rs +197 -0
  157. package/native/visionos/src/lib.rs +1887 -0
  158. package/native/watchos/Cargo.lock +16 -0
  159. package/native/watchos/Cargo.toml +19 -0
  160. package/native/watchos/shaders/bloom_postfx.metal +99 -0
  161. package/native/watchos/src/BloomWatchApp.swift +1267 -0
  162. package/native/watchos/src/BloomWatchAudio.swift +179 -0
  163. package/native/watchos/src/audio.rs +55 -0
  164. package/native/watchos/src/draw_list.rs +229 -0
  165. package/native/watchos/src/ffi_stubs.rs +915 -0
  166. package/native/watchos/src/ffi_stubs_manual.rs +35 -0
  167. package/native/watchos/src/lib.rs +1124 -0
  168. package/native/watchos/src/models.rs +746 -0
  169. package/native/watchos/src/postfx.rs +95 -0
  170. package/native/watchos/src/scene.rs +534 -0
  171. package/native/watchos/src/textures.rs +184 -0
  172. package/native/web/Cargo.lock +1657 -0
  173. package/native/web/Cargo.toml +43 -0
  174. package/native/web/bloom_glue.js +695 -0
  175. package/native/web/build.sh +131 -0
  176. package/native/web/index.html +35 -0
  177. package/native/web/jolt_bridge.js +1519 -0
  178. package/native/web/src/input_ffi.rs +286 -0
  179. package/native/web/src/lib.rs +1796 -0
  180. package/native/web/src/material_ffi.rs +710 -0
  181. package/native/web/src/parity_ffi.rs +343 -0
  182. package/native/web/src/physics_ffi.rs +643 -0
  183. package/native/web/src/ragdoll_ffi.rs +250 -0
  184. package/native/web/src/render_settings.rs +98 -0
  185. package/native/windows/Cargo.lock +1815 -0
  186. package/native/windows/Cargo.toml +68 -0
  187. package/native/windows/src/lib.rs +1486 -0
  188. package/package.json +4279 -0
  189. package/src/audio/index.ts +315 -0
  190. package/src/core/colors.ts +63 -0
  191. package/src/core/index.ts +1206 -0
  192. package/src/core/keys.ts +63 -0
  193. package/src/core/types.ts +104 -0
  194. package/src/index.ts +171 -0
  195. package/src/math/index.ts +516 -0
  196. package/src/mobile/index.ts +294 -0
  197. package/src/models/index.ts +1258 -0
  198. package/src/physics/index.ts +1134 -0
  199. package/src/scene/index.ts +698 -0
  200. package/src/shapes/index.ts +120 -0
  201. package/src/text/index.ts +48 -0
  202. package/src/textures/index.ts +187 -0
  203. package/src/vfx/index.ts +191 -0
  204. package/src/world/index.ts +24 -0
  205. package/src/world/loader.ts +423 -0
  206. package/src/world/prefab.ts +217 -0
  207. package/src/world/render.ts +172 -0
  208. package/src/world/saver.ts +108 -0
  209. package/src/world/serialize.ts +301 -0
  210. package/src/world/terrain.ts +355 -0
  211. package/src/world/types.ts +160 -0
  212. package/src/world/validate.ts +319 -0
  213. package/src/world/version.ts +114 -0
@@ -0,0 +1,62 @@
1
+ [package]
2
+ name = "bloom-shared"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [features]
7
+ default = ["mp3", "jolt", "hot-reload", "models3d", "image-extras"]
8
+ mp3 = ["dep:minimp3"]
9
+ jolt = []
10
+ # EN-008 — gate the `notify` filesystem watcher behind a compile-time
11
+ # feature. Default-on for the dev workflow; ship game binaries with
12
+ # --no-default-features (or --features mp3,jolt) to drop notify from
13
+ # the dep tree entirely.
14
+ hot-reload = ["dep:notify"]
15
+ web = ["dep:web-time"]
16
+ # EN-014 — 3D model loading (glTF + DDS) and the ModelManager. Pure-2D
17
+ # games disable this via Perry's `[native-library."<pkg>"]` feature
18
+ # forwarding to drop gltf/gltf_json/image_dds from the binary entirely.
19
+ # Default-on so existing 3D games are unaffected.
20
+ models3d = ["dep:gltf", "dep:image_dds"]
21
+ # EN-014 — image codecs beyond PNG. `image::load_from_memory` is
22
+ # format-agnostic, so disabling this only changes which file formats
23
+ # decode at runtime; PNG always works.
24
+ # image_dds rides along: cooked BC7 .dds textures are "image formats
25
+ # beyond PNG" in the same sense as jpeg/bmp/tga (decode-only here; the
26
+ # encoder lives in tools/bloom-cook).
27
+ image-extras = ["image/jpeg", "image/bmp", "image/tga", "image/hdr", "dep:image_dds"]
28
+
29
+ [build-dependencies]
30
+ cmake = "0.1"
31
+
32
+ [dependencies]
33
+ wgpu = "29"
34
+ raw-window-handle = "0.6"
35
+ fontdue = "0.9"
36
+ libc = "0.2"
37
+ bytemuck = { version = "1", features = ["derive"] }
38
+ half = "2"
39
+ image = { version = "0.25", default-features = false, features = ["png"] }
40
+ image_dds = { version = "0.7", default-features = false, features = ["ddsfile", "image"], optional = true }
41
+ gltf = { version = "1", features = ["KHR_materials_pbrSpecularGlossiness", "KHR_materials_transmission"], optional = true }
42
+ lewton = "0.10"
43
+ minimp3 = { version = "0.5", optional = true }
44
+ earcutr = "0.4"
45
+ web-time = { version = "0.2", optional = true }
46
+ # Phase 6 — hot-reload watcher for material WGSL files. Native only;
47
+ # wasm doesn't have a filesystem to watch (gated in code via
48
+ # cfg(not(target_arch = "wasm32"))). EN-008 made it opt-in via the
49
+ # `hot-reload` cargo feature so shipped game builds can drop notify.
50
+ [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
51
+ notify = { version = "6", optional = true }
52
+
53
+ # EN-063 — the engine's error channel on web. `log_error`'s eprintln! is a
54
+ # black hole in a browser (no stderr), so every engine warning and error —
55
+ # missing texture-array layers, surface-acquire failures, feature-off
56
+ # warnings — was silently dropped on the one platform whose users can't
57
+ # attach a debugger. Route them to console.error instead.
58
+ [target.'cfg(target_arch = "wasm32")'.dependencies]
59
+ web-sys = { version = "0.3", features = ["console"] }
60
+
61
+ [dev-dependencies]
62
+ pollster = "0.4"
@@ -0,0 +1,270 @@
1
+ //! Build script for bloom-shared.
2
+ //!
3
+ //! Only does work when the `jolt` feature is enabled. In that case it builds
4
+ //! the `bloom_jolt` C++ shim (and JoltPhysics, via its own CMakeLists) via the
5
+ //! `cmake` crate and emits link directives so rustc picks up both archives.
6
+
7
+ fn main() {
8
+ // Always re-run when these change.
9
+ println!("cargo:rerun-if-changed=build.rs");
10
+ println!("cargo:rerun-if-env-changed=CARGO_FEATURE_JOLT");
11
+
12
+ if std::env::var_os("CARGO_FEATURE_JOLT").is_none() {
13
+ return;
14
+ }
15
+
16
+ // The Jolt C++ shim cannot target WebAssembly via the normal cmake path —
17
+ // Emscripten + Jolt is a separate build pipeline (see JoltPhysics.js).
18
+ // On wasm32 the web crate routes bloom_physics_* FFI to JoltPhysics.js
19
+ // directly via wasm_bindgen; no static library is needed here.
20
+ if std::env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("wasm32") {
21
+ return;
22
+ }
23
+
24
+ build_jolt();
25
+ }
26
+
27
+ #[cfg(not(feature = "jolt"))]
28
+ fn build_jolt() {}
29
+
30
+ #[cfg(feature = "jolt")]
31
+ fn build_jolt() {
32
+ use std::path::PathBuf;
33
+
34
+ // Locate native/third_party/bloom_jolt relative to this crate.
35
+ let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
36
+ let shim_dir = manifest_dir
37
+ .parent()
38
+ .unwrap()
39
+ .join("third_party")
40
+ .join("bloom_jolt");
41
+
42
+ let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
43
+ let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
44
+
45
+ println!("cargo:rerun-if-env-changed=BLOOM_JOLT_PREBUILT_DIR");
46
+ println!("cargo:rerun-if-env-changed=BLOOM_JOLT_FROM_SOURCE");
47
+
48
+ // Prebuilt fast path — if `@bornengine/jolt-prebuilt` is available
49
+ // (via env var or a node_modules sibling), link its archives and
50
+ // skip the multi-minute cmake build of JoltPhysics entirely. The
51
+ // env var BLOOM_JOLT_FROM_SOURCE=1 forces the cmake fallback even
52
+ // when prebuilts are present, useful for hacking on the C++ shim.
53
+ let from_source = std::env::var_os("BLOOM_JOLT_FROM_SOURCE").is_some();
54
+ if !from_source {
55
+ if let Some(prebuilt_dir) = find_prebuilt_dir(&manifest_dir, &target_os, &target_arch) {
56
+ link_prebuilt(&prebuilt_dir, &target_os);
57
+ emit_cxx_runtime_link(&target_os);
58
+ return;
59
+ }
60
+ }
61
+
62
+ if !shim_dir.join("CMakeLists.txt").exists() {
63
+ panic!(
64
+ "bloom_jolt shim not found at {}; did the third_party submodules init?",
65
+ shim_dir.display()
66
+ );
67
+ }
68
+
69
+ println!(
70
+ "cargo:rerun-if-changed={}",
71
+ shim_dir.join("CMakeLists.txt").display()
72
+ );
73
+ println!(
74
+ "cargo:rerun-if-changed={}",
75
+ shim_dir.join("include/bloom_jolt.h").display()
76
+ );
77
+ println!(
78
+ "cargo:rerun-if-changed={}",
79
+ shim_dir.join("src/bloom_jolt.cpp").display()
80
+ );
81
+
82
+ // Build into a stable, short path next to the shim itself rather than the
83
+ // per-build OUT_DIR. Two reasons:
84
+ // 1. Cargo wraps OUT_DIR with the `\\?\` long-path prefix on Windows
85
+ // whenever the absolute path approaches MAX_PATH; MSBuild and cl.exe
86
+ // both choke on `\\?\`-prefixed paths in subtle ways (echo'd build
87
+ // events fail with MSB3073, cl.exe rewrites the file to `\\testfile`
88
+ // and reports C1083).
89
+ // 2. The Jolt build is expensive and identical across every cargo target
90
+ // hash — caching it once per profile lets `cargo clean` not nuke a
91
+ // multi-minute compile.
92
+ let dst = shim_dir
93
+ .join("build")
94
+ .join(format!("{}-{}", target_os, target_arch));
95
+
96
+ let lib_ext = if target_os == "windows" { "lib" } else { "a" };
97
+ let lib_prefix = if target_os == "windows" { "" } else { "lib" };
98
+ let bloom_jolt_lib = dst
99
+ .join("lib")
100
+ .join(format!("{}bloom_jolt.{}", lib_prefix, lib_ext));
101
+ let jolt_lib = dst
102
+ .join("lib")
103
+ .join(format!("{}Jolt.{}", lib_prefix, lib_ext));
104
+
105
+ if !(bloom_jolt_lib.exists() && jolt_lib.exists()) {
106
+ let mut cfg = cmake::Config::new(&shim_dir);
107
+ cfg.out_dir(&dst)
108
+ .profile("Release")
109
+ .define("CMAKE_BUILD_TYPE", "Release");
110
+ if target_os == "windows" {
111
+ // perry links the prebuilt Jolt archive with lld-link, which
112
+ // cannot read MSVC `/GL` (whole-program-optimization) object
113
+ // files — they're LTCG intermediates, not native COFF, and the
114
+ // link fails with "is not a native COFF file. Recompile without
115
+ // /GL?". Turn off Jolt's interprocedural optimization so the
116
+ // archive holds ordinary COFF objects perry can consume. (Costs
117
+ // a little Jolt codegen perf; physics is not the bottleneck.)
118
+ cfg.define("INTERPROCEDURAL_OPTIMIZATION", "OFF")
119
+ .define("CMAKE_INTERPROCEDURAL_OPTIMIZATION", "OFF");
120
+ }
121
+ let _ = cfg.build();
122
+ }
123
+
124
+ println!(
125
+ "cargo:rustc-link-search=native={}",
126
+ dst.join("lib").display()
127
+ );
128
+ println!("cargo:rustc-link-lib=static=bloom_jolt");
129
+ println!("cargo:rustc-link-lib=static=Jolt");
130
+
131
+ emit_cxx_runtime_link(&target_os);
132
+ }
133
+
134
+ /// Locate a BornEngine or legacy Bloom Engine Jolt prebuilt package's lib dir for the
135
+ /// active build target. Returns `Some(dir)` only when both expected
136
+ /// archives are present — a half-shipped package falls through to the
137
+ /// cmake build instead of producing a confusing linker error.
138
+ ///
139
+ /// Resolution order:
140
+ /// 1. `BLOOM_JOLT_PREBUILT_DIR` env var — points at a directory
141
+ /// containing per-target subdirs (`<dir>/<os>-<arch>/lib*.a`).
142
+ /// Used for local spike testing and for CI matrix jobs that
143
+ /// stage archives outside `node_modules`.
144
+ /// 2. Walk up from `CARGO_MANIFEST_DIR` looking for either
145
+ /// `node_modules/@bornengine/jolt-prebuilt/lib/<os>-<arch>/` or
146
+ /// its legacy `@bloomengine/jolt-prebuilt` equivalent. The BornEngine
147
+ /// scope is preferred, while existing Bloom Engine installs keep working.
148
+ #[cfg(feature = "jolt")]
149
+ fn find_prebuilt_dir(
150
+ manifest_dir: &std::path::Path,
151
+ target_os: &str,
152
+ target_arch: &str,
153
+ ) -> Option<std::path::PathBuf> {
154
+ // Map Rust's target_arch to the npm/Node convention used in this
155
+ // package's directory layout. Keep this in sync with the matrix in
156
+ // npm/jolt-prebuilt/README.md.
157
+ let arch_token = match target_arch {
158
+ "aarch64" => "arm64",
159
+ "x86_64" => "x64",
160
+ "arm" => "armv7",
161
+ other => other,
162
+ };
163
+ // Apple simulator targets share `target_os`/`target_arch` with their
164
+ // device counterpart (e.g. aarch64-apple-tvos-sim vs -tvos both report
165
+ // os=tvos arch=aarch64) and are only distinguished by the "sim" ABI.
166
+ // jolt-prebuilt ships a separate `<os>-<arch>-sim` archive built for the
167
+ // simulator platform; without this suffix we'd link the device archive
168
+ // into a simulator build and ld would reject the platform mismatch.
169
+ let sim_suffix = if std::env::var("CARGO_CFG_TARGET_ABI").as_deref() == Ok("sim") {
170
+ "-sim"
171
+ } else {
172
+ ""
173
+ };
174
+ let target_token = format!("{}-{}{}", target_os, arch_token, sim_suffix);
175
+
176
+ let candidates = std::iter::empty::<std::path::PathBuf>()
177
+ .chain(
178
+ std::env::var_os("BLOOM_JOLT_PREBUILT_DIR")
179
+ .map(|v| std::path::PathBuf::from(v).join(&target_token)),
180
+ )
181
+ .chain(walk_up_for_node_modules(manifest_dir).flat_map(|nm| {
182
+ ["@bornengine", "@bloomengine"].map(|scope| {
183
+ nm.join(scope)
184
+ .join("jolt-prebuilt")
185
+ .join("lib")
186
+ .join(&target_token)
187
+ })
188
+ }));
189
+
190
+ let (lib_prefix, lib_ext) = if target_os == "windows" {
191
+ ("", "lib")
192
+ } else {
193
+ ("lib", "a")
194
+ };
195
+
196
+ for dir in candidates {
197
+ let bloom_jolt = dir.join(format!("{}bloom_jolt.{}", lib_prefix, lib_ext));
198
+ let jolt = dir.join(format!("{}Jolt.{}", lib_prefix, lib_ext));
199
+ if bloom_jolt.exists() && jolt.exists() {
200
+ return Some(dir);
201
+ }
202
+ }
203
+ None
204
+ }
205
+
206
+ /// Iterator over every ancestor `node_modules/` reachable from
207
+ /// `start`. Yields each directory once, parent-first, so a closer
208
+ /// prebuilt installation wins over a higher one (matching node's
209
+ /// resolution algorithm).
210
+ #[cfg(feature = "jolt")]
211
+ fn walk_up_for_node_modules(
212
+ start: &std::path::Path,
213
+ ) -> impl Iterator<Item = std::path::PathBuf> + '_ {
214
+ let mut next: Option<&std::path::Path> = Some(start);
215
+ std::iter::from_fn(move || {
216
+ while let Some(cur) = next {
217
+ next = cur.parent();
218
+ let candidate = cur.join("node_modules");
219
+ if candidate.is_dir() {
220
+ return Some(candidate);
221
+ }
222
+ }
223
+ None
224
+ })
225
+ }
226
+
227
+ #[cfg(feature = "jolt")]
228
+ fn link_prebuilt(dir: &std::path::Path, target_os: &str) {
229
+ // Rerun if any archive in the prebuilt dir changes — covers the
230
+ // case where a new release of jolt-prebuilt drops in new bytes.
231
+ let (lib_prefix, lib_ext) = if target_os == "windows" {
232
+ ("", "lib")
233
+ } else {
234
+ ("lib", "a")
235
+ };
236
+ println!(
237
+ "cargo:rerun-if-changed={}",
238
+ dir.join(format!("{}bloom_jolt.{}", lib_prefix, lib_ext))
239
+ .display()
240
+ );
241
+ println!(
242
+ "cargo:rerun-if-changed={}",
243
+ dir.join(format!("{}Jolt.{}", lib_prefix, lib_ext))
244
+ .display()
245
+ );
246
+
247
+ println!("cargo:rustc-link-search=native={}", dir.display());
248
+ println!("cargo:rustc-link-lib=static=bloom_jolt");
249
+ println!("cargo:rustc-link-lib=static=Jolt");
250
+ }
251
+
252
+ /// Emit the C++ standard library link directive that matches the
253
+ /// archives we just linked. Required because the static archives pull
254
+ /// in libc++ / libstdc++ symbols that the Rust toolchain doesn't
255
+ /// resolve on its own.
256
+ #[cfg(feature = "jolt")]
257
+ fn emit_cxx_runtime_link(target_os: &str) {
258
+ match target_os {
259
+ "macos" | "ios" | "tvos" | "watchos" => {
260
+ println!("cargo:rustc-link-lib=dylib=c++");
261
+ }
262
+ "linux" | "android" => {
263
+ println!("cargo:rustc-link-lib=dylib=stdc++");
264
+ }
265
+ "windows" => {
266
+ // MSVC toolchain handles libc++ automatically; MinGW would need stdc++.
267
+ }
268
+ _ => {}
269
+ }
270
+ }
@@ -0,0 +1,122 @@
1
+ // One cloud deck — shared by the sky that draws the clouds and the ground that
2
+ // takes their shadow.
3
+ //
4
+ // WHY THIS IS ONE FILE. These used to be two unrelated noise fields that had
5
+ // never been reconciled, and the result did not survive looking at:
6
+ //
7
+ // - The sky's puffs were fBm over a plane pinned to the CAMERA, so they slid
8
+ // along with the player instead of hanging over the world.
9
+ // - The ground's "cloud shadow" was a *different*, much finer field on a
10
+ // different noise, drifting ~80x faster (20 m/s against the sky's 0.25).
11
+ //
12
+ // So the shadow racing across the grass had no cloud above it, and the cloud
13
+ // overhead cast nothing. Worse, only the materials that happened to carry a copy
14
+ // of the ground function darkened at all: in the shooter that was the grass, but
15
+ // not the terrain under it, nor the trees standing in it — a cloud shadow that
16
+ // crosses the field and ignores the forest in the middle of it.
17
+ //
18
+ // Now there is ONE field, in WORLD space, and the shadow you are standing in
19
+ // belongs to the cloud you can look up and see.
20
+ //
21
+ // THE MODEL. The deck is a horizontal plane at `p.y` metres. A view ray and a
22
+ // sun ray are both intersected with it; whatever they hit is the same cloud.
23
+ // That is the entire trick, and it is why the apparent size of a cloud in the
24
+ // sky and the size of its shadow on the ground are no longer independently
25
+ // tunable — they are the same number seen from two directions, which is the
26
+ // point.
27
+
28
+ fn cloud_hash(p: vec2<f32>) -> f32 {
29
+ return fract(sin(dot(p, vec2<f32>(127.1, 311.7))) * 43758.5453);
30
+ }
31
+
32
+ fn cloud_noise(p: vec2<f32>) -> f32 {
33
+ let i = floor(p);
34
+ let f = fract(p);
35
+ let uu = f * f * (3.0 - 2.0 * f);
36
+ let a = cloud_hash(i);
37
+ let b = cloud_hash(i + vec2<f32>(1.0, 0.0));
38
+ let c = cloud_hash(i + vec2<f32>(0.0, 1.0));
39
+ let d = cloud_hash(i + vec2<f32>(1.0, 1.0));
40
+ return mix(mix(a, b, uu.x), mix(c, d, uu.x), uu.y);
41
+ }
42
+
43
+ fn cloud_fbm(p0: vec2<f32>) -> f32 {
44
+ var s = 0.0;
45
+ var amp = 0.5;
46
+ var q = p0;
47
+ for (var i = 0; i < 5; i = i + 1) {
48
+ s = s + amp * cloud_noise(q);
49
+ q = q * 2.03;
50
+ amp = amp * 0.5;
51
+ }
52
+ return s;
53
+ }
54
+
55
+ // Cloud params, passed by each caller from whichever uniform block it happens to
56
+ // have (the sky pass and the material ABI do not share one, so this file takes
57
+ // them explicitly rather than reaching for a global):
58
+ //
59
+ // x = shadow strength 0 = the world ignores the clouds entirely (default)
60
+ // y = deck height world metres
61
+ // z = feature scale noise units per metre — 1/z is the cloud size
62
+ // w = drift speed metres/second
63
+ //
64
+ // The drift DIRECTION is the wind vector — the same one that bends the grass —
65
+ // so the clouds travel the way the foliage under them is leaning. A game that
66
+ // never set a wind still gets a slow default heading rather than a frozen sky.
67
+ fn cloud_drift(wind_xy: vec2<f32>, t: f32, speed: f32) -> vec2<f32> {
68
+ var d = wind_xy;
69
+ if (dot(d, d) < 1e-6) { d = vec2<f32>(1.0, 0.25); }
70
+ return normalize(d) * speed * t;
71
+ }
72
+
73
+ // Coverage of the deck at a point ON the deck. 0 = clear sky, 1 = solid cloud.
74
+ fn cloud_density(deck_xz: vec2<f32>, wind_xy: vec2<f32>, t: f32, cp: vec4<f32>) -> f32 {
75
+ let p = (deck_xz - cloud_drift(wind_xy, t, cp.w)) * cp.z;
76
+ // The threshold is high on purpose: it buys mostly-open sky with the
77
+ // occasional real cloud, instead of a permanent grey smear. A low threshold
78
+ // here is what makes a procedural deck read as fog.
79
+ return smoothstep(0.56, 1.04, cloud_fbm(p));
80
+ }
81
+
82
+ // Where a ray from `origin` along `dir` pierces the deck.
83
+ fn cloud_deck_hit(origin: vec3<f32>, dir: vec3<f32>, deck_y: f32) -> vec2<f32> {
84
+ let t = (deck_y - origin.y) / dir.y;
85
+ return origin.xz + dir.xz * t;
86
+ }
87
+
88
+ // --- the two consumers -------------------------------------------------------
89
+
90
+ // SKY: coverage along a view ray. Returns (coverage, sun-alignment) — the caller
91
+ // colours the cloud from the second component so puffs facing the sun burn white
92
+ // and the ones facing away stay cool grey.
93
+ fn cloud_cover_view(cam: vec3<f32>, dir: vec3<f32>, sun_dir: vec3<f32>,
94
+ wind_xy: vec2<f32>, t: f32, cp: vec4<f32>) -> vec2<f32> {
95
+ if (dir.y <= 0.02) { return vec2<f32>(0.0, 0.0); }
96
+ var cov = cloud_density(cloud_deck_hit(cam, dir, cp.y), wind_xy, t, cp);
97
+ // Toward the horizon a view ray runs so far through the deck that modelling
98
+ // it as an infinitely thin plane stops being defensible — fade out instead
99
+ // of drawing the smear the maths would give.
100
+ let horizon_fade = smoothstep(0.03, 0.24, dir.y);
101
+ // Thin the deck right around the sun so the disk still burns through.
102
+ let near_sun = smoothstep(0.90, 0.999, dot(dir, sun_dir));
103
+ cov = cov * horizon_fade * (1.0 - near_sun * 0.8) * 0.9;
104
+ let sun_amt = clamp(dot(dir, sun_dir) * 0.5 + 0.5, 0.0, 1.0);
105
+ return vec2<f32>(cov, sun_amt);
106
+ }
107
+
108
+ // GROUND: how much sun a world point keeps. 1.0 = full sun, lower = under cloud.
109
+ // Multiply this into direct sunlight only — a cloud blocks the sun, it does not
110
+ // stop the sky from being blue, and folding it into ambient too is what makes
111
+ // cloud shadows read as flat grey paint rather than as shade.
112
+ fn cloud_shadow_at(world_pos: vec3<f32>, sun_dir: vec3<f32>,
113
+ wind_xy: vec2<f32>, t: f32, cp: vec4<f32>) -> f32 {
114
+ if (cp.x <= 0.0) { return 1.0; }
115
+ // Sun on the horizon: the shadow ray runs nearly parallel to the deck and
116
+ // the intersection shoots off to infinity, so the noise lookup lands a
117
+ // kilometre away and swims. Fade the whole effect out as the sun sets.
118
+ let up = sun_dir.y;
119
+ if (up <= 0.02) { return 1.0; }
120
+ let cov = cloud_density(cloud_deck_hit(world_pos, sun_dir, cp.y), wind_xy, t, cp);
121
+ return 1.0 - cov * cp.x * smoothstep(0.02, 0.20, up);
122
+ }
@@ -0,0 +1,16 @@
1
+ // Distance fog — standard exponential-squared falloff.
2
+ //
3
+ // Depends on material_abi.wgsl for view.fog (rgb + density).
4
+
5
+ // Apply fog to a lit fragment. `view_distance` is the world-space
6
+ // distance from the camera to the fragment. `color` is the lit HDR
7
+ // colour before fog.
8
+ fn apply_fog(color: vec3<f32>, view_distance: f32) -> vec3<f32> {
9
+ let density = view.fog.w;
10
+ if (density <= 0.0) { return color; }
11
+ // exp² is tighter near the camera and softer at distance than a plain
12
+ // exp — matches what most atmospherics use.
13
+ let f = exp(-density * view_distance * density * view_distance);
14
+ let factor = clamp(f, 0.0, 1.0);
15
+ return mix(view.fog.rgb, color, factor);
16
+ }
@@ -0,0 +1,98 @@
1
+ // Hierarchical foliage wind — one field, shared by the scene pass that draws the
2
+ // tree and the shadow pass that casts it.
3
+ //
4
+ // WHY THREE LAYERS. A tree does not move as one thing. The trunk leans slowly
5
+ // under the whole wind load, the branches swing at their own rate, and the
6
+ // leaves flutter fast at the tips. Driving all of it from a single sine is what
7
+ // makes procedural foliage read as rippling cloth instead of wood.
8
+ //
9
+ // What was here before did less than that: the engine swayed *alpha-cut
10
+ // materials only*, so leaf cards fluttered and every trunk in every game was
11
+ // perfectly rigid — and the shadow shaders applied no wind at all, so the leaves
12
+ // moved while their shadows stayed nailed to the ground.
13
+ //
14
+ // WHERE THE WEIGHTS COME FROM. SH-013 planned to author them into vertex colours
15
+ // (R = bend, G = branch, B = flutter). That is the right answer for hand-modelled
16
+ // trees, but ours are procedurally generated, so the regions are known exactly and
17
+ // can simply be derived from where the vertex sits relative to the tree's base:
18
+ //
19
+ // trunk bend proportional to height^2 -- a cantilever: the crown travels, the roots do not
20
+ // branch sway proportional to reach out from the trunk axis
21
+ // leaf flutter cutout cards only, small and fast
22
+ //
23
+ // So: no new vertex attribute, no GLB re-bake, no COLOR_0 (which the scene shader
24
+ // already spends on albedo tint), and it works on any foliage model the game flags.
25
+ //
26
+ // The offset is returned in WORLD space and added after the model transform.
27
+ // Displacing in local space would let each tree's per-instance yaw rotate the
28
+ // wind with it, and a stand of trees would bend in a dozen different directions.
29
+
30
+ // `rel` is the vertex's WORLD-space offset from its model origin (so it is
31
+ // already scaled and rotated -- the coefficients below are in metres and mean the
32
+ // same thing for a sapling and a giant).
33
+ // `wind` is the global wind vec4: xy = direction in the XZ plane, z = amplitude,
34
+ // w = elapsed seconds.
35
+ // `amount` scales the whole effect per draw; 0 = this is not foliage, don't move
36
+ // it. `is_leaf` is 1.0 for cutout cards, 0.0 for wood.
37
+ fn foliage_wind_world(rel: vec3<f32>, model_origin: vec3<f32>,
38
+ wind: vec4<f32>, amount: f32, is_leaf: f32) -> vec3<f32> {
39
+ if (amount <= 0.0 || wind.z <= 0.0) { return vec3<f32>(0.0, 0.0, 0.0); }
40
+
41
+ let dir = vec3<f32>(wind.x, 0.0, wind.y);
42
+ let amp = wind.z * amount;
43
+ let t = wind.w;
44
+
45
+ // Per-tree phase from the model's world origin. Without it a stand of trees
46
+ // sways in lockstep, which is the single most obvious tell of fake foliage.
47
+ let ph = model_origin.x * 0.137 + model_origin.z * 0.241;
48
+
49
+ let h = max(rel.y, 0.0);
50
+ let reach = length(rel.xz);
51
+
52
+ // 1. TRUNK BEND — cantilever, so travel grows with the SQUARE of height: the
53
+ // base is planted, the crown swings. Slow (~0.15 Hz). This is the motion
54
+ // you read from 30 m away, and it is the one that did not exist at all.
55
+ // Coefficients are tuned so amount = 1.0 is a real tree in a lazy breeze:
56
+ // with the shooter's wind amplitude (0.10) a 4 m crown travels ~35 cm.
57
+ let bend = amp * 0.22 * h * h * sin(t * 0.95 + ph);
58
+
59
+ // 2. BRANCH SWAY — how far the vertex reaches out from the trunk axis. Medium
60
+ // (~0.4 Hz), phase-offset by azimuth so opposite limbs do not swing together.
61
+ let azim = atan2(rel.z, rel.x);
62
+ // Gated by height as well as reach: without it the flare at the base of the
63
+ // trunk has some reach and would shuffle its own roots.
64
+ let swing = amp * 0.70 * reach * clamp(h * 0.5, 0.0, 1.0)
65
+ * sin(t * 2.4 + ph + azim * 1.7);
66
+
67
+ // 3. LEAF FLUTTER — cutout cards only. Fast (~1 Hz), small, and keyed on the
68
+ // vertex's own position so neighbouring cards break up rather than shimmer
69
+ // as a sheet.
70
+ let fl = amp * 0.60 * is_leaf * sin(t * 6.0 + ph + h * 3.1 + reach * 5.0);
71
+
72
+ var o = dir * (bend + swing + fl);
73
+ // Leaves twist rather than only sliding downwind; and a leaning crown dips a
74
+ // little, because the tip is swinging on an arc, not a rail.
75
+ o.y = o.y + fl * 0.45 - bend * 0.15;
76
+ return o;
77
+ }
78
+
79
+ // Convenience wrapper: local-space vertex in, wind-displaced local-space vertex
80
+ // out. Both the scene pass and the shadow pass want exactly this, and they must
81
+ // agree exactly or a tree detaches from its own shadow.
82
+ //
83
+ // The offset is computed in WORLD space (see above) and then brought back into
84
+ // local space by inverting the model's linear part. That inverse is exact for the
85
+ // transforms these draws use -- rotation plus UNIFORM scale -- because then
86
+ // M^-1 = M^T / s^2.
87
+ fn foliage_wind_local(local_pos: vec3<f32>, model: mat4x4<f32>,
88
+ wind: vec4<f32>, amount: f32, is_leaf: f32) -> vec3<f32> {
89
+ if (amount <= 0.0 || wind.z <= 0.0) { return local_pos; }
90
+ let origin = model[3].xyz;
91
+ let rel = (model * vec4<f32>(local_pos, 1.0)).xyz - origin;
92
+ let wo = foliage_wind_world(rel, origin, wind, amount, is_leaf);
93
+ let c0 = model[0].xyz;
94
+ let s2 = max(dot(c0, c0), 1e-8);
95
+ return local_pos + vec3<f32>(dot(model[0].xyz, wo),
96
+ dot(model[1].xyz, wo),
97
+ dot(model[2].xyz, wo)) / s2;
98
+ }