@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,593 @@
|
|
|
1
|
+
//! EN-011 planar-reflection probe pass: mirrored PerView build,
|
|
2
|
+
//! per-probe bind-group cache, mirrored-frustum culling and the two
|
|
3
|
+
//! probe render passes (materials + cached models). Split from
|
|
4
|
+
//! renderer/mod.rs (2000-line file policy — same pattern as
|
|
5
|
+
//! shadow_pass.rs / model_draw.rs / scene_pass.rs).
|
|
6
|
+
|
|
7
|
+
use super::*;
|
|
8
|
+
|
|
9
|
+
impl Renderer {
|
|
10
|
+
/// EN-011 — render every registered probe's RT for this frame.
|
|
11
|
+
/// Called from `end_frame_with_scene` BEFORE the main material
|
|
12
|
+
/// pass so the probe textures are ready when materials sample
|
|
13
|
+
/// them.
|
|
14
|
+
///
|
|
15
|
+
/// For each probe:
|
|
16
|
+
/// 1. Build the mirrored PerView (camera reflected across the
|
|
17
|
+
/// probe's plane; same projection as the main camera).
|
|
18
|
+
/// 2. Upload the mirrored PerView to the probe's UBO.
|
|
19
|
+
/// 3. Begin a render pass against the probe's RT + depth (clear
|
|
20
|
+
/// colour to fog, depth to 1.0).
|
|
21
|
+
/// 4. Walk material_system.commands and dispatch each non-
|
|
22
|
+
/// excluded draw with the mirrored per-view bind group.
|
|
23
|
+
///
|
|
24
|
+
/// V1 cull list: excludes any material whose handle equals one
|
|
25
|
+
/// of the materials linked to a probe (so the water plane itself
|
|
26
|
+
/// doesn't reflect). Future revisions can expand this with a
|
|
27
|
+
/// hardcoded foliage / particle bucket filter via the bucket
|
|
28
|
+
/// metadata on each compiled pipeline.
|
|
29
|
+
pub fn dispatch_planar_reflections(
|
|
30
|
+
&mut self,
|
|
31
|
+
encoder: &mut wgpu::CommandEncoder,
|
|
32
|
+
scene: &crate::scene::SceneGraph,
|
|
33
|
+
profiler: &mut crate::profiler::Profiler,
|
|
34
|
+
) {
|
|
35
|
+
if self.planar_probes.iter().all(|p| p.is_none()) { return; }
|
|
36
|
+
// Scene-graph nodes render into the probe too (they share the
|
|
37
|
+
// Vertex3D layout and the scene material bind-group layout), so a
|
|
38
|
+
// fully retained-mode game gets real water reflections as well.
|
|
39
|
+
let scene_draws = scene.reflect_draw_list();
|
|
40
|
+
if self.material_system.commands.is_empty()
|
|
41
|
+
&& self.model_draw_commands.is_empty()
|
|
42
|
+
&& scene_draws.is_empty() { return; }
|
|
43
|
+
|
|
44
|
+
// EN-011 — lazily build the single-target reflection pipeline + buffers
|
|
45
|
+
// used to render cached models (trees/house) into the probe with a
|
|
46
|
+
// mirrored VP. Owned layouts: g0 dynamic per-draw model uniform, g1
|
|
47
|
+
// sun/ambient; g2 reuses the scene material layout for base colour.
|
|
48
|
+
const REFLECT_STRIDE: u64 = 256;
|
|
49
|
+
const REFLECT_MAX_DRAWS: usize = 1024;
|
|
50
|
+
if self.reflect_scene_pipeline.is_none() {
|
|
51
|
+
let model_dyn_layout = self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
52
|
+
label: Some("reflect_model_dyn_layout"),
|
|
53
|
+
entries: &[wgpu::BindGroupLayoutEntry {
|
|
54
|
+
binding: 0, visibility: wgpu::ShaderStages::VERTEX,
|
|
55
|
+
ty: wgpu::BindingType::Buffer {
|
|
56
|
+
ty: wgpu::BufferBindingType::Uniform,
|
|
57
|
+
has_dynamic_offset: true,
|
|
58
|
+
min_binding_size: std::num::NonZeroU64::new(128),
|
|
59
|
+
},
|
|
60
|
+
count: None,
|
|
61
|
+
}],
|
|
62
|
+
});
|
|
63
|
+
let shadow_tex_entry = |binding: u32| wgpu::BindGroupLayoutEntry {
|
|
64
|
+
binding, visibility: wgpu::ShaderStages::FRAGMENT,
|
|
65
|
+
ty: wgpu::BindingType::Texture {
|
|
66
|
+
sample_type: wgpu::TextureSampleType::Depth,
|
|
67
|
+
view_dimension: wgpu::TextureViewDimension::D2,
|
|
68
|
+
multisampled: false,
|
|
69
|
+
},
|
|
70
|
+
count: None,
|
|
71
|
+
};
|
|
72
|
+
let light_layout = self.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
73
|
+
label: Some("reflect_light_layout"),
|
|
74
|
+
entries: &[
|
|
75
|
+
wgpu::BindGroupLayoutEntry {
|
|
76
|
+
binding: 0, visibility: wgpu::ShaderStages::FRAGMENT,
|
|
77
|
+
ty: wgpu::BindingType::Buffer {
|
|
78
|
+
ty: wgpu::BufferBindingType::Uniform,
|
|
79
|
+
has_dynamic_offset: false, min_binding_size: None,
|
|
80
|
+
},
|
|
81
|
+
count: None,
|
|
82
|
+
},
|
|
83
|
+
// Shadow cascades + comparison sampler so the mirrored
|
|
84
|
+
// scene is sun-shadowed like the real one (the probe
|
|
85
|
+
// previously rendered everything fully lit, which made
|
|
86
|
+
// water reflections disagree with the scene above them).
|
|
87
|
+
shadow_tex_entry(1),
|
|
88
|
+
shadow_tex_entry(2),
|
|
89
|
+
shadow_tex_entry(3),
|
|
90
|
+
wgpu::BindGroupLayoutEntry {
|
|
91
|
+
binding: 4, visibility: wgpu::ShaderStages::FRAGMENT,
|
|
92
|
+
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
|
|
93
|
+
count: None,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
let shader = self.device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
98
|
+
label: Some("reflect_scene_shader"),
|
|
99
|
+
source: wgpu::ShaderSource::Wgsl(REFLECT_SCENE_WGSL.into()),
|
|
100
|
+
});
|
|
101
|
+
let pl = self.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
102
|
+
label: Some("reflect_scene_pl"),
|
|
103
|
+
bind_group_layouts: &[Some(&model_dyn_layout), Some(&light_layout), Some(&self.scene_material_layout)],
|
|
104
|
+
immediate_size: 0,
|
|
105
|
+
});
|
|
106
|
+
let pipeline = self.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
107
|
+
label: Some("reflect_scene_pipeline"),
|
|
108
|
+
layout: Some(&pl),
|
|
109
|
+
vertex: wgpu::VertexState {
|
|
110
|
+
module: &shader, entry_point: Some("vs_reflect"),
|
|
111
|
+
buffers: &[Vertex3D::desc()], compilation_options: Default::default(),
|
|
112
|
+
},
|
|
113
|
+
fragment: Some(wgpu::FragmentState {
|
|
114
|
+
module: &shader, entry_point: Some("fs_reflect"),
|
|
115
|
+
targets: &[Some(wgpu::ColorTargetState {
|
|
116
|
+
format: HDR_FORMAT, blend: None, write_mask: wgpu::ColorWrites::ALL,
|
|
117
|
+
})],
|
|
118
|
+
compilation_options: Default::default(),
|
|
119
|
+
}),
|
|
120
|
+
primitive: wgpu::PrimitiveState {
|
|
121
|
+
topology: wgpu::PrimitiveTopology::TriangleList,
|
|
122
|
+
cull_mode: None, ..Default::default()
|
|
123
|
+
},
|
|
124
|
+
depth_stencil: Some(wgpu::DepthStencilState {
|
|
125
|
+
format: DEPTH_FORMAT, depth_write_enabled: Some(true),
|
|
126
|
+
depth_compare: Some(wgpu::CompareFunction::Less),
|
|
127
|
+
stencil: Default::default(), bias: Default::default(),
|
|
128
|
+
}),
|
|
129
|
+
multisample: Default::default(), multiview_mask: None, cache: None,
|
|
130
|
+
});
|
|
131
|
+
let model_buf = self.device.create_buffer(&wgpu::BufferDescriptor {
|
|
132
|
+
label: Some("reflect_model_buf"),
|
|
133
|
+
size: REFLECT_STRIDE * REFLECT_MAX_DRAWS as u64,
|
|
134
|
+
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
135
|
+
mapped_at_creation: false,
|
|
136
|
+
});
|
|
137
|
+
let model_bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
138
|
+
label: Some("reflect_model_bg"), layout: &model_dyn_layout,
|
|
139
|
+
entries: &[wgpu::BindGroupEntry {
|
|
140
|
+
binding: 0,
|
|
141
|
+
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
|
142
|
+
buffer: &model_buf, offset: 0, size: std::num::NonZeroU64::new(128),
|
|
143
|
+
}),
|
|
144
|
+
}],
|
|
145
|
+
});
|
|
146
|
+
// sun_dir + sun_color + ambient + cam_pos + shadow_splits (5 vec4)
|
|
147
|
+
// + 3 cascade mat4s = 80 + 192 = 272 bytes.
|
|
148
|
+
let light_buf = self.device.create_buffer(&wgpu::BufferDescriptor {
|
|
149
|
+
label: Some("reflect_light_buf"), size: 272,
|
|
150
|
+
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
151
|
+
mapped_at_creation: false,
|
|
152
|
+
});
|
|
153
|
+
let light_bg = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
154
|
+
label: Some("reflect_light_bg"), layout: &light_layout,
|
|
155
|
+
entries: &[
|
|
156
|
+
wgpu::BindGroupEntry { binding: 0, resource: light_buf.as_entire_binding() },
|
|
157
|
+
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(&self.shadow_map.depth_views[0]) },
|
|
158
|
+
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(&self.shadow_map.depth_views[1]) },
|
|
159
|
+
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(&self.shadow_map.depth_views[2]) },
|
|
160
|
+
wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::Sampler(&self.shadow_map.sampler) },
|
|
161
|
+
],
|
|
162
|
+
});
|
|
163
|
+
self.reflect_scene_pipeline = Some(pipeline);
|
|
164
|
+
self.reflect_model_buf = Some(model_buf);
|
|
165
|
+
self.reflect_model_bg = Some(model_bg);
|
|
166
|
+
self.reflect_light_buf = Some(light_buf);
|
|
167
|
+
self.reflect_light_bg = Some(light_bg);
|
|
168
|
+
}
|
|
169
|
+
// Sun/ambient + shadow data for the reflection shading (same values
|
|
170
|
+
// as the main pass, so the mirrored scene is lit AND shadowed like
|
|
171
|
+
// the real one). shadow_splits.w carries the shadows-enabled flag —
|
|
172
|
+
// in THIS struct .w is free (no LOD-bias tenant).
|
|
173
|
+
{
|
|
174
|
+
let ld = self.lighting_uniforms.light_dir;
|
|
175
|
+
let lc = self.lighting_uniforms.light_color;
|
|
176
|
+
let amb = self.lighting_uniforms.ambient;
|
|
177
|
+
let cam = self.current_camera_pos;
|
|
178
|
+
let sp = self.lighting_uniforms.shadow_cascade_splits;
|
|
179
|
+
let shadows_flag = if self.shadow_map.enabled { 1.0 } else { 0.0 };
|
|
180
|
+
let mut light_data = [0.0f32; 68];
|
|
181
|
+
light_data[0..4].copy_from_slice(&[ld[0], ld[1], ld[2], ld[3]]);
|
|
182
|
+
light_data[4..8].copy_from_slice(&[lc[0], lc[1], lc[2], 0.0]);
|
|
183
|
+
light_data[8..12].copy_from_slice(&[amb[0], amb[1], amb[2], amb[3]]);
|
|
184
|
+
light_data[12..16].copy_from_slice(&[cam[0], cam[1], cam[2], 0.0]);
|
|
185
|
+
light_data[16..20].copy_from_slice(&[sp[0], sp[1], sp[2], shadows_flag]);
|
|
186
|
+
let vps = &self.lighting_uniforms.shadow_cascade_vps;
|
|
187
|
+
for c in 0..3 {
|
|
188
|
+
for col in 0..4 {
|
|
189
|
+
for row in 0..4 {
|
|
190
|
+
light_data[20 + c * 16 + col * 4 + row] = vps[c][col][row];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if let Some(buf) = &self.reflect_light_buf {
|
|
195
|
+
self.queue.write_buffer(buf, 0, bytemuck::cast_slice(&light_data));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Build the V1 exclude set: every material linked to any
|
|
200
|
+
// probe. The water material itself shouldn't appear in its
|
|
201
|
+
// own reflection (it'd black-on-black self-occlude the
|
|
202
|
+
// surface).
|
|
203
|
+
let mut excluded: std::collections::HashSet<material_system::MaterialHandle> =
|
|
204
|
+
std::collections::HashSet::new();
|
|
205
|
+
for (i, probe_link) in self.material_system.material_reflection_probe.iter().enumerate() {
|
|
206
|
+
if probe_link.is_some() {
|
|
207
|
+
excluded.insert((i + 1) as material_system::MaterialHandle);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Cache main-pass per-view inputs once outside the loop.
|
|
212
|
+
let main_view = self.current_view_matrix;
|
|
213
|
+
let proj = self.current_proj_matrix;
|
|
214
|
+
let cam_pos = self.current_camera_pos;
|
|
215
|
+
|
|
216
|
+
// Snapshot the existing PerView uniforms by reconstructing
|
|
217
|
+
// the same struct material_system_begin_frame writes — we
|
|
218
|
+
// need a fresh copy per probe to swap view/view_proj.
|
|
219
|
+
let base_per_view = material_system::PerViewUniforms {
|
|
220
|
+
view: main_view,
|
|
221
|
+
proj,
|
|
222
|
+
view_proj: self.current_vp_matrix,
|
|
223
|
+
// EN-022 fix: velocity reference (prev unjittered VP +
|
|
224
|
+
// current jitter), so material shaders computing
|
|
225
|
+
// `prev_view_proj * world` get true zero velocity on
|
|
226
|
+
// static geometry instead of TAA jitter-delta noise.
|
|
227
|
+
prev_view_proj: self.velocity_ref_vp,
|
|
228
|
+
inv_proj: self.current_inv_proj_matrix,
|
|
229
|
+
camera_pos: [
|
|
230
|
+
cam_pos[0], cam_pos[1], cam_pos[2],
|
|
231
|
+
self.lighting_uniforms.camera_pos[3],
|
|
232
|
+
],
|
|
233
|
+
camera_dir: [0.0, 0.0, -1.0, 70.0_f32.to_radians()],
|
|
234
|
+
ambient: self.lighting_uniforms.ambient,
|
|
235
|
+
fog: [self.fog_color[0], self.fog_color[1], self.fog_color[2], self.fog_density],
|
|
236
|
+
sun_dir: self.lighting_uniforms.light_dir,
|
|
237
|
+
sun_color: self.lighting_uniforms.light_color,
|
|
238
|
+
dir_light_count: self.lighting_uniforms.dir_light_count,
|
|
239
|
+
dir_lights: std::array::from_fn(|i| material_system::PerViewDirLight {
|
|
240
|
+
direction: self.lighting_uniforms.dir_lights[i].direction,
|
|
241
|
+
color: self.lighting_uniforms.dir_lights[i].color,
|
|
242
|
+
}),
|
|
243
|
+
point_light_count: self.lighting_uniforms.point_light_count,
|
|
244
|
+
point_lights: std::array::from_fn(|i| material_system::PerViewPointLight {
|
|
245
|
+
position: self.lighting_uniforms.point_lights[i].position,
|
|
246
|
+
color: self.lighting_uniforms.point_lights[i].color,
|
|
247
|
+
}),
|
|
248
|
+
shadow_splits: self.lighting_uniforms.shadow_cascade_splits,
|
|
249
|
+
shadow_view: self.lighting_uniforms.shadow_view_matrix,
|
|
250
|
+
shadow_cascades: self.lighting_uniforms.shadow_cascade_vps,
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// Iterate probes by index — we mutate `material_system`'s
|
|
254
|
+
// commands view while iterating, so collect the work first.
|
|
255
|
+
let probe_count = self.planar_probes.len();
|
|
256
|
+
for i in 0..probe_count {
|
|
257
|
+
let (plane_y, normal, color_view, depth_view,
|
|
258
|
+
aux_material_view, aux_velocity_view, aux_albedo_view) =
|
|
259
|
+
match &self.planar_probes[i] {
|
|
260
|
+
Some(p) => (p.plane_y, p.normal, p.color_view.clone(), p.depth_view.clone(),
|
|
261
|
+
p.aux_material_view.clone(), p.aux_velocity_view.clone(),
|
|
262
|
+
p.aux_albedo_view.clone()),
|
|
263
|
+
None => continue,
|
|
264
|
+
};
|
|
265
|
+
let view_buf = match self.planar_probe_view_buffers[i].as_ref() {
|
|
266
|
+
Some(b) => b, None => continue,
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// Mirror the camera + recompute view_proj for the probe.
|
|
270
|
+
let mirror_view = planar_reflection::mirrored_view(main_view, plane_y, normal);
|
|
271
|
+
let mirror_cam = planar_reflection::mirrored_camera_pos(cam_pos, plane_y, normal);
|
|
272
|
+
|
|
273
|
+
// EN-011 V2 — oblique near-plane clip. Replace the
|
|
274
|
+
// projection's near plane with the water plane (in
|
|
275
|
+
// mirror-eye-space) so geometry below the plane is
|
|
276
|
+
// clipped at the rasterizer instead of polluting the
|
|
277
|
+
// reflection edge.
|
|
278
|
+
//
|
|
279
|
+
// World-space plane equation: `N · p + d_w = 0` with
|
|
280
|
+
// kept-side `N · p + d_w > 0` (above water). For a
|
|
281
|
+
// horizontal mirror at world y = plane_y with normal
|
|
282
|
+
// +Y, d_w = -plane_y so the kept side is y > plane_y.
|
|
283
|
+
// Transformed via mirror-view's inverse-transpose, the
|
|
284
|
+
// eye-space plane defines the same physical half-space
|
|
285
|
+
// (the side the kept geometry lives on after the
|
|
286
|
+
// reflection has rolled it through the view).
|
|
287
|
+
let d_w = -(normal[0] * 0.0 + normal[1] * plane_y + normal[2] * 0.0);
|
|
288
|
+
let plane_world = [normal[0], normal[1], normal[2], d_w];
|
|
289
|
+
let plane_eye = planar_reflection::world_plane_to_eye_space(mirror_view, plane_world);
|
|
290
|
+
let mirror_proj = planar_reflection::oblique_proj(proj, plane_eye);
|
|
291
|
+
let mirror_vp = mat4_multiply(mirror_proj, mirror_view);
|
|
292
|
+
|
|
293
|
+
let mut per_view = base_per_view;
|
|
294
|
+
per_view.view = mirror_view;
|
|
295
|
+
per_view.proj = mirror_proj;
|
|
296
|
+
per_view.view_proj = mirror_vp;
|
|
297
|
+
per_view.inv_proj = planar_reflection::inv_proj_for(mirror_proj);
|
|
298
|
+
per_view.camera_pos[0] = mirror_cam[0];
|
|
299
|
+
per_view.camera_pos[1] = mirror_cam[1];
|
|
300
|
+
per_view.camera_pos[2] = mirror_cam[2];
|
|
301
|
+
// prev_view_proj stays as the main camera's previous VP
|
|
302
|
+
// — TAA reprojection isn't meaningful for the reflection
|
|
303
|
+
// probe (we don't temporally accumulate it), so this is
|
|
304
|
+
// benign.
|
|
305
|
+
self.queue.write_buffer(view_buf, 0, bytemuck::bytes_of(&per_view));
|
|
306
|
+
|
|
307
|
+
// EN-011 V2 — rebuild the per-probe PerView bind group with
|
|
308
|
+
// the live env / BRDF / shadow views. V1 bound 1×1 stub
|
|
309
|
+
// textures here, which left mirrored draws lit by a flat
|
|
310
|
+
// grey IBL (no specular reflections, no sun shadow) — the
|
|
311
|
+
// reflection painting the lit scene differently from the
|
|
312
|
+
// main pass made the surface look "off". Rebuilding once
|
|
313
|
+
// per probe per frame is cheap (a single bind-group
|
|
314
|
+
// create); it also picks up any env hot-load that
|
|
315
|
+
// happens between frames without needing explicit dirty
|
|
316
|
+
// tracking on the probe side.
|
|
317
|
+
//
|
|
318
|
+
// The sky env (binding 1) and env_diffuse (binding 3)
|
|
319
|
+
// default to the renderer's 1×1 grey fallback when no HDR
|
|
320
|
+
// is loaded — same default the main pass uses, so the
|
|
321
|
+
// reflection's IBL stays consistent pre/post
|
|
322
|
+
// `load_env_from_hdr`. The sky_texture's view doesn't sit
|
|
323
|
+
// on a struct field (it's owned by `sky_bind_group`), so
|
|
324
|
+
// we build fresh views here each frame; that's a cheap
|
|
325
|
+
// Arc bump on the underlying wgpu Texture.
|
|
326
|
+
// Cached per-probe PerView bind group — inputs (env, BRDF,
|
|
327
|
+
// shadow views, the per-probe UBO) are all stable objects;
|
|
328
|
+
// env (re)loads and probe creation clear the cache slot.
|
|
329
|
+
if self.planar_probe_view_bgs[i].is_none() {
|
|
330
|
+
let sky_view_owned: Option<wgpu::TextureView> = self.sky_texture
|
|
331
|
+
.as_ref()
|
|
332
|
+
.map(|t| t.create_view(&Default::default()));
|
|
333
|
+
let env_view: &wgpu::TextureView = sky_view_owned
|
|
334
|
+
.as_ref()
|
|
335
|
+
.unwrap_or(&self.scene_env_default_view);
|
|
336
|
+
let diffuse_view_owned: Option<wgpu::TextureView> = self.env_diffuse_texture
|
|
337
|
+
.as_ref()
|
|
338
|
+
.map(|t| t.create_view(&Default::default()));
|
|
339
|
+
let env_diffuse_view: &wgpu::TextureView = diffuse_view_owned
|
|
340
|
+
.as_ref()
|
|
341
|
+
.unwrap_or(&self.scene_env_default_view);
|
|
342
|
+
self.planar_probe_view_bgs[i] = Some(self.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
343
|
+
label: Some("planar_probe_per_view_bg_live"),
|
|
344
|
+
layout: &self.material_system.layouts.per_view,
|
|
345
|
+
entries: &[
|
|
346
|
+
wgpu::BindGroupEntry { binding: 0, resource: view_buf.as_entire_binding() },
|
|
347
|
+
// env (specular) tex + sampler
|
|
348
|
+
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(env_view) },
|
|
349
|
+
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::Sampler(&self.env_sampler) },
|
|
350
|
+
// env diffuse tex
|
|
351
|
+
wgpu::BindGroupEntry { binding: 3, resource: wgpu::BindingResource::TextureView(env_diffuse_view) },
|
|
352
|
+
// BRDF LUT tex + sampler
|
|
353
|
+
wgpu::BindGroupEntry { binding: 4, resource: wgpu::BindingResource::TextureView(&self.brdf_lut_view) },
|
|
354
|
+
wgpu::BindGroupEntry { binding: 5, resource: wgpu::BindingResource::Sampler(&self.brdf_lut_sampler) },
|
|
355
|
+
// 3 shadow cascades — same depth views the main
|
|
356
|
+
// pass binds, so the reflection picks up sun
|
|
357
|
+
// shadows without re-rendering the cascades.
|
|
358
|
+
wgpu::BindGroupEntry { binding: 6, resource: wgpu::BindingResource::TextureView(&self.shadow_map.depth_views[0]) },
|
|
359
|
+
wgpu::BindGroupEntry { binding: 7, resource: wgpu::BindingResource::TextureView(&self.shadow_map.depth_views[1]) },
|
|
360
|
+
wgpu::BindGroupEntry { binding: 8, resource: wgpu::BindingResource::TextureView(&self.shadow_map.depth_views[2]) },
|
|
361
|
+
wgpu::BindGroupEntry { binding: 9, resource: wgpu::BindingResource::Sampler(&self.shadow_map.sampler) },
|
|
362
|
+
],
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
let probe_view_bg = self.planar_probe_view_bgs[i].as_ref().unwrap();
|
|
366
|
+
|
|
367
|
+
// Stage each surviving draw's [mirror_mvp, model] into ONE
|
|
368
|
+
// batched upload (this was ~450 individual 128 B
|
|
369
|
+
// `write_buffer` calls per probe per frame), and record the
|
|
370
|
+
// draw list. Draws are culled against the MIRRORED frustum —
|
|
371
|
+
// the oblique near plane doubles as the water-plane clip, so
|
|
372
|
+
// below-plane geometry culls here too. Scene-graph nodes
|
|
373
|
+
// append after the cached models in the same slot space —
|
|
374
|
+
// creation order decides who survives the cap, so games
|
|
375
|
+
// should create hero geometry before filler (grass).
|
|
376
|
+
let mirror_planes = crate::scene::extract_frustum_planes(&mirror_vp);
|
|
377
|
+
let mut reflect_draws: Vec<(u64, usize, u32)> = Vec::new();
|
|
378
|
+
let mut node_slots: Vec<(usize, u32)> = Vec::new();
|
|
379
|
+
if let Some(model_buf) = &self.reflect_model_buf {
|
|
380
|
+
let stride = REFLECT_STRIDE as usize;
|
|
381
|
+
let mut staged: Vec<u8> = Vec::with_capacity(stride * 128);
|
|
382
|
+
for cmd in self.model_draw_commands.iter() {
|
|
383
|
+
// REFLECT_SCENE_WGSL can't skin — a skinned draw would
|
|
384
|
+
// mirror its bind pose at the origin. Skinned models
|
|
385
|
+
// (enemies) were never reflected on the old immediate
|
|
386
|
+
// path either, so skipping preserves that.
|
|
387
|
+
if cmd.skinned { continue; }
|
|
388
|
+
let slot = reflect_draws.len();
|
|
389
|
+
if slot >= REFLECT_MAX_DRAWS { break; }
|
|
390
|
+
let Some(Some(meshes)) = self.model_gpu_cache.get(&cmd.cache_handle) else { continue };
|
|
391
|
+
if cmd.mesh_idx >= meshes.len() { continue; }
|
|
392
|
+
let mesh = &meshes[cmd.mesh_idx];
|
|
393
|
+
let (wmin, wmax) =
|
|
394
|
+
transform_aabb(&cmd.model, mesh.local_min, mesh.local_max);
|
|
395
|
+
if wmin[0] <= wmax[0]
|
|
396
|
+
&& crate::scene::aabb_outside_frustum(&mirror_planes, wmin, wmax)
|
|
397
|
+
{
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
let mirror_mvp = mat4_multiply(mirror_vp, cmd.model);
|
|
401
|
+
let base = staged.len();
|
|
402
|
+
staged.resize(base + stride, 0);
|
|
403
|
+
staged[base..base + 64].copy_from_slice(bytemuck::bytes_of(&mirror_mvp));
|
|
404
|
+
staged[base + 64..base + 128].copy_from_slice(bytemuck::bytes_of(&cmd.model));
|
|
405
|
+
reflect_draws.push((cmd.cache_handle, cmd.mesh_idx, slot as u32));
|
|
406
|
+
}
|
|
407
|
+
for (i, (_vb, _ib, _ic, _bg, model, wmin, wmax)) in scene_draws.iter().enumerate() {
|
|
408
|
+
let slot = reflect_draws.len() + node_slots.len();
|
|
409
|
+
if slot >= REFLECT_MAX_DRAWS { break; }
|
|
410
|
+
if wmin[0] <= wmax[0]
|
|
411
|
+
&& crate::scene::aabb_outside_frustum(&mirror_planes, *wmin, *wmax)
|
|
412
|
+
{
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
let mirror_mvp = mat4_multiply(mirror_vp, *model);
|
|
416
|
+
let base = staged.len();
|
|
417
|
+
staged.resize(base + stride, 0);
|
|
418
|
+
staged[base..base + 64].copy_from_slice(bytemuck::bytes_of(&mirror_mvp));
|
|
419
|
+
staged[base + 64..base + 128].copy_from_slice(bytemuck::bytes_of(model));
|
|
420
|
+
node_slots.push((i, slot as u32));
|
|
421
|
+
}
|
|
422
|
+
if !staged.is_empty() {
|
|
423
|
+
self.queue.write_buffer(model_buf, 0, &staged);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Clear the probe to transparent black. Geometry fragments write
|
|
428
|
+
// alpha 1, so the water shader can blend the probe over its analytic
|
|
429
|
+
// sky by alpha (a=0 → no reflected geometry → show the sky dome).
|
|
430
|
+
let clear_color = wgpu::Color { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
|
|
431
|
+
|
|
432
|
+
let view_bg = probe_view_bg;
|
|
433
|
+
let cache = &self.model_gpu_cache;
|
|
434
|
+
let mat_sys = &self.material_system;
|
|
435
|
+
let refl_pipeline = self.reflect_scene_pipeline.as_ref();
|
|
436
|
+
let refl_model_bg = self.reflect_model_bg.as_ref();
|
|
437
|
+
let refl_light_bg = self.reflect_light_bg.as_ref();
|
|
438
|
+
// Pass A — user materials. Opaque-profile material pipelines
|
|
439
|
+
// (and their `_reflection` siblings) target the full opaque
|
|
440
|
+
// G-buffer layout, so this pass presents the same four
|
|
441
|
+
// attachments; the three aux targets are probe-resolution
|
|
442
|
+
// dummies cleared here and discarded at store. Only the hdr
|
|
443
|
+
// attachment (and depth, which pass B tests against) is kept.
|
|
444
|
+
{
|
|
445
|
+
let aux_ops = wgpu::Operations {
|
|
446
|
+
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
|
447
|
+
store: wgpu::StoreOp::Discard,
|
|
448
|
+
};
|
|
449
|
+
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
450
|
+
label: Some("bloom_planar_reflection_materials"),
|
|
451
|
+
color_attachments: &[
|
|
452
|
+
Some(wgpu::RenderPassColorAttachment {
|
|
453
|
+
view: &color_view,
|
|
454
|
+
resolve_target: None,
|
|
455
|
+
depth_slice: None,
|
|
456
|
+
ops: wgpu::Operations {
|
|
457
|
+
load: wgpu::LoadOp::Clear(clear_color),
|
|
458
|
+
store: wgpu::StoreOp::Store,
|
|
459
|
+
},
|
|
460
|
+
}),
|
|
461
|
+
Some(wgpu::RenderPassColorAttachment {
|
|
462
|
+
view: &aux_material_view,
|
|
463
|
+
resolve_target: None,
|
|
464
|
+
depth_slice: None,
|
|
465
|
+
ops: aux_ops,
|
|
466
|
+
}),
|
|
467
|
+
Some(wgpu::RenderPassColorAttachment {
|
|
468
|
+
view: &aux_velocity_view,
|
|
469
|
+
resolve_target: None,
|
|
470
|
+
depth_slice: None,
|
|
471
|
+
ops: aux_ops,
|
|
472
|
+
}),
|
|
473
|
+
Some(wgpu::RenderPassColorAttachment {
|
|
474
|
+
view: &aux_albedo_view,
|
|
475
|
+
resolve_target: None,
|
|
476
|
+
depth_slice: None,
|
|
477
|
+
ops: aux_ops,
|
|
478
|
+
}),
|
|
479
|
+
],
|
|
480
|
+
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
|
481
|
+
view: &depth_view,
|
|
482
|
+
depth_ops: Some(wgpu::Operations {
|
|
483
|
+
load: wgpu::LoadOp::Clear(1.0),
|
|
484
|
+
store: wgpu::StoreOp::Store,
|
|
485
|
+
}),
|
|
486
|
+
stencil_ops: None,
|
|
487
|
+
}),
|
|
488
|
+
timestamp_writes: profiler.pass_timestamp_writes("planar_probe_materials"),
|
|
489
|
+
occlusion_query_set: None,
|
|
490
|
+
multiview_mask: None,
|
|
491
|
+
});
|
|
492
|
+
mat_sys.dispatch_with_view(
|
|
493
|
+
&mut pass, view_bg,
|
|
494
|
+
// Excluded: probe-linked materials (the water plane
|
|
495
|
+
// itself) and anything flagged not-probe-visible
|
|
496
|
+
// (authoring control — e.g. sub-pixel instanced
|
|
497
|
+
// grass in a 512² probe).
|
|
498
|
+
|handle| !excluded.contains(&handle)
|
|
499
|
+
&& mat_sys.material_probe_visible(handle),
|
|
500
|
+
// EN-011 V2 — swap to each material's sibling
|
|
501
|
+
// pipeline with cull_mode flipped Front→Back.
|
|
502
|
+
// Reflection mirrors world-space, which inverts
|
|
503
|
+
// triangle winding; without the flip, single-
|
|
504
|
+
// sided opaque geometry renders inside-out in
|
|
505
|
+
// the probe's RT. Cutout materials have
|
|
506
|
+
// `reflection_pipeline = None` and gracefully fall
|
|
507
|
+
// back to the main pipeline (no cull change needed
|
|
508
|
+
// since they're already double-sided); translucent-
|
|
509
|
+
// profile materials are skipped inside (their
|
|
510
|
+
// single-target pipelines can't render into this
|
|
511
|
+
// 4-target pass).
|
|
512
|
+
true,
|
|
513
|
+
// Instance tiles cull against the MIRRORED frustum.
|
|
514
|
+
Some(&mirror_planes),
|
|
515
|
+
|handle, idx| {
|
|
516
|
+
if let Some(Some(meshes)) = cache.get(&handle) {
|
|
517
|
+
if idx < meshes.len() {
|
|
518
|
+
let mesh = &meshes[idx];
|
|
519
|
+
return Some((
|
|
520
|
+
&mesh.vb, &mesh.ib, mesh.index_count,
|
|
521
|
+
mesh.local_min, mesh.local_max,
|
|
522
|
+
));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
None
|
|
526
|
+
},
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Pass B — cached models (trees/house/foliage) + scene-graph
|
|
531
|
+
// nodes with the single-target REFLECT_SCENE pipeline. Loads
|
|
532
|
+
// pass A's color + depth so the two batches depth-test
|
|
533
|
+
// against each other.
|
|
534
|
+
{
|
|
535
|
+
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
536
|
+
label: Some("bloom_planar_reflection_models"),
|
|
537
|
+
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
538
|
+
view: &color_view,
|
|
539
|
+
resolve_target: None,
|
|
540
|
+
depth_slice: None,
|
|
541
|
+
ops: wgpu::Operations {
|
|
542
|
+
load: wgpu::LoadOp::Load,
|
|
543
|
+
store: wgpu::StoreOp::Store,
|
|
544
|
+
},
|
|
545
|
+
})],
|
|
546
|
+
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
|
547
|
+
view: &depth_view,
|
|
548
|
+
depth_ops: Some(wgpu::Operations {
|
|
549
|
+
load: wgpu::LoadOp::Load,
|
|
550
|
+
store: wgpu::StoreOp::Store,
|
|
551
|
+
}),
|
|
552
|
+
stencil_ops: None,
|
|
553
|
+
}),
|
|
554
|
+
timestamp_writes: profiler.pass_timestamp_writes("planar_probe_models"),
|
|
555
|
+
occlusion_query_set: None,
|
|
556
|
+
multiview_mask: None,
|
|
557
|
+
});
|
|
558
|
+
if let (Some(rp), Some(rmbg), Some(rlbg)) =
|
|
559
|
+
(refl_pipeline, refl_model_bg, refl_light_bg)
|
|
560
|
+
{
|
|
561
|
+
if !reflect_draws.is_empty() || !node_slots.is_empty() {
|
|
562
|
+
pass.set_pipeline(rp);
|
|
563
|
+
pass.set_bind_group(1, rlbg, &[]);
|
|
564
|
+
for (handle, midx, slot) in &reflect_draws {
|
|
565
|
+
if let Some(Some(meshes)) = cache.get(handle) {
|
|
566
|
+
if *midx < meshes.len() {
|
|
567
|
+
let mesh = &meshes[*midx];
|
|
568
|
+
pass.set_bind_group(0, rmbg, &[*slot * REFLECT_STRIDE as u32]);
|
|
569
|
+
pass.set_bind_group(2, &mesh.material_bg, &[]);
|
|
570
|
+
pass.set_vertex_buffer(0, mesh.vb.slice(..));
|
|
571
|
+
pass.set_index_buffer(mesh.ib.slice(..), wgpu::IndexFormat::Uint32);
|
|
572
|
+
pass.draw_indexed(0..mesh.index_count, 0, 0..1);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
// Scene-graph nodes: same pipeline — node geometry is
|
|
577
|
+
// Vertex3D and node material bind groups share the
|
|
578
|
+
// scene material layout the pipeline's g2 expects.
|
|
579
|
+
for (i, slot) in &node_slots {
|
|
580
|
+
let (vb, ib, index_count, mat_bg, _model, _wmin, _wmax) =
|
|
581
|
+
&scene_draws[*i];
|
|
582
|
+
pass.set_bind_group(0, rmbg, &[*slot * REFLECT_STRIDE as u32]);
|
|
583
|
+
pass.set_bind_group(2, *mat_bg, &[]);
|
|
584
|
+
pass.set_vertex_buffer(0, vb.slice(..));
|
|
585
|
+
pass.set_index_buffer(ib.slice(..), wgpu::IndexFormat::Uint32);
|
|
586
|
+
pass.draw_indexed(0..*index_count, 0, 0..1);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|