@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,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"
|
|
Binary file
|
|
@@ -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
|
+
}
|