Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
1// Shared current-frame water-surface deformation. This module is the single
2// source of truth for forward rendering and later motion-vector consumers.
4#define_import_path bevy_aqua_core::deform
6#import bevy_pbr::{forward_io::Vertex, mesh_functions}
7#import aqua::cascade::{BodyParams, DEBUG_MODE_BEAUTY, DEBUG_MODE_FAR_TIER, MIN_NORMAL_Y, SAFE_LENGTH_SQUARED, begin_invocation, cascade_layout, effective_flow, far_tier_weight, field_params, lod_count, owning_body, sample_displacement, sample_field_flow, sample_field_level, set_effective_flow, set_effective_time, snap_and_transition, surface}
8#import aqua::waves::displace::{far_displacement, far_normal_cross, sample_fft_normal_cross}
9#import bevy_aqua_core::river::river_analytic_displacement
11// The full current-frame result is intentionally independent of a render-stage
12// output. `sample_xz` is the stable location used to evaluate deformation;
13// `undisplaced_xz` preserves the forward material's existing varying contract.
14struct DeformationResult {
15 world_position: vec4<f32>,
16 base_world_position: vec3<f32>,
17 world_normal: vec3<f32>,
18 sample_xz: vec2<f32>,
19 undisplaced_xz: vec2<f32>,
20 lod: u32,
21 sample_alpha: f32,
22 displacement: vec3<f32>,
23 wave_height: f32,
24 body_slot: u32,
25 body_params: BodyParams,
26 body_level: f32,
27 bounded: bool,
28 river: bool,
29 river_flow: vec4<f32>,
32fn deformation_safe_normalize(value: vec3<f32>, fallback: vec3<f32>) -> vec3<f32> {
33 let length_squared = dot(value, value);
34 let normalized = value * inverseSqrt(max(length_squared, SAFE_LENGTH_SQUARED));
35 return select(fallback, normalized, length_squared > SAFE_LENGTH_SQUARED);
38fn deform_current(vertex: Vertex, time: f32) -> DeformationResult {
39 var result: DeformationResult;
40 let world_from_local = mesh_functions::get_world_from_local(vertex.instance_index);
41 var world_position = mesh_functions::mesh_position_local_to_world(
42 world_from_local,
43 vec4(vertex.position, 1.0),
44 );
45 let tile_lod = min(mesh_functions::get_tag(vertex.instance_index), lod_count() - 1u);
46 var slot = 0u;
47 var params = field_params.bodies[0];
48 var vertex_level = 0.0;
49 if field_params.info.x > 0.5 {
50 let field = sample_field_level(world_position.xz);
51 slot = u32(field.y + 0.5);
52 params = owning_body(slot);
53 vertex_level = field.x;
54 }
55 let bounded = slot > 0u;
56 begin_invocation(bounded, params);
57 set_effective_time(time);
58 // River bodies advect wave content with their baked local current;
59 // everything else keeps the global uniform current.
60 var vertex_river_flow = vec4(0.0, 0.0, 1024.0, 0.0);
61 if bounded {
62 vertex_river_flow = sample_field_flow(world_position.xz);
63 }
64 set_effective_flow(select(
65 surface.advection.xy,
66 vertex_river_flow.xy,
67 bounded && params.flags.y > 0.5,
68 ));
70 result.body_slot = slot;
71 result.body_params = params;
72 result.body_level = vertex_level;
73 result.bounded = bounded;
74 result.river = bounded && params.flags.y > 0.5;
75 result.river_flow = vertex_river_flow;
77 // River bodies synthesize their own closed-form waves along the local
78 // current instead of sampling the shared ocean cascade.
79 if result.river {
80 let transitioned = snap_and_transition(
81 world_position.xz,
82 world_from_local[3].xz,
83 cascade_layout.cascades[tile_lod],
84 );
85 let river_xz = transitioned.xy;
86 vertex_river_flow = sample_field_flow(river_xz);
87 effective_flow = vertex_river_flow.xy;
88 let analytic = river_analytic_displacement(
89 river_xz,
90 vertex_river_flow,
91 time,
92 );
93 let epsilon = 0.35;
94 let height_x = river_analytic_displacement(
95 river_xz + vec2(epsilon, 0.0),
96 vertex_river_flow,
97 time,
98 ).x;
99 let height_z = river_analytic_displacement(
100 river_xz + vec2(0.0, epsilon),
101 vertex_river_flow,
102 time,
103 ).x;
104 let displaced = vec4(
105 river_xz.x,
106 vertex_level + analytic.x,
107 river_xz.y,
108 1.0,
109 );
110 result.world_position = displaced;
111 result.base_world_position = world_position.xyz;
112 result.world_normal = deformation_safe_normalize(
113 vec3(
114 -(height_x - analytic.x) / epsilon,
115 1.0,
116 -(height_z - analytic.x) / epsilon,
117 ),
118 vec3(0.0, 1.0, 0.0),
119 );
120 result.sample_xz = river_xz;
121 result.undisplaced_xz = world_position.xz;
122 result.lod = 0u;
123 result.sample_alpha = 1.0;
124 result.displacement = displaced.xyz - world_position.xyz;
125 result.wave_height = analytic.x;
126 result.river_flow = vertex_river_flow;
127 return result;
128 }
130 if bounded {
131 let transitioned = snap_and_transition(
132 world_position.xz,
133 world_from_local[3].xz,
134 cascade_layout.cascades[tile_lod],
135 );
136 let displaced = vec4(transitioned.x, vertex_level, transitioned.y, 1.0);
137 result.world_position = displaced;
138 result.base_world_position = displaced.xyz;
139 result.world_normal = vec3(0.0, 1.0, 0.0);
140 result.sample_xz = transitioned.xy;
141 result.undisplaced_xz = world_position.xz;
142 result.lod = 0u;
143 result.sample_alpha = 1.0;
144 result.displacement = vec3(0.0);
145 result.wave_height = 0.0;
146 return result;
147 }
149 let transitioned = select(
150 snap_and_transition(
151 world_position.xz,
152 world_from_local[3].xz,
153 cascade_layout.cascades[tile_lod],
154 ),
155 vec3(world_position.x, world_position.z, 1.0),
156 bounded,
157 );
158 world_position = vec4(
159 transitioned.x,
160 world_position.y,
161 transitioned.y,
162 world_position.w,
163 );
164 // Crest fades wave content that projects below the minimum useful
165 // wavelength before the coarse vertex grid can alias it.
166 let detail_lod = clamp(cascade_layout.center.z, 0.0, f32(lod_count() - 1u));
167 let minimum_lod = u32(floor(detail_lod));
168 let lod = max(tile_lod, minimum_lod);
169 let detail_alpha = fract(detail_lod);
170 var sample_alpha = select(transitioned.z, 1.0, bounded);
171 if tile_lod < minimum_lod {
172 sample_alpha = select(detail_alpha, 1.0, bounded);
173 } else if tile_lod == minimum_lod {
174 sample_alpha = select(max(sample_alpha, detail_alpha), 1.0, bounded);
175 }
176 let mode = u32(round(surface.debug.x));
177 let far_diagnostic = mode == DEBUG_MODE_FAR_TIER;
178 let base_world_position = world_position.xyz;
179 let far_tier = select(
180 0.0,
181 far_tier_weight(base_world_position),
182 mode == DEBUG_MODE_BEAUTY || far_diagnostic,
183 );
184 var displacement: vec3<f32>;
185 var normal_cross: vec3<f32>;
186 if far_tier >= 1.0 {
187 displacement = far_displacement(transitioned.xy);
188 normal_cross = far_normal_cross(transitioned.xy);
189 } else {
190 let near_displacement = sample_displacement(transitioned.xy, lod, sample_alpha);
191 displacement = near_displacement;
192 if surface.reflection.x > 0.5 {
193 normal_cross = sample_fft_normal_cross(transitioned.xy, lod, sample_alpha);
194 } else {
195 let texel_width = cascade_layout.cascades[lod].texel_width;
196 let displacement_x = sample_displacement(
197 transitioned.xy + vec2(texel_width, 0.0),
198 lod,
199 sample_alpha,
200 );
201 let displacement_z = sample_displacement(
202 transitioned.xy + vec2(0.0, texel_width),
203 lod,
204 sample_alpha,
205 );
206 let tangent_x = vec3(texel_width, 0.0, 0.0)
207 + displacement_x - near_displacement;
208 let tangent_z = vec3(0.0, 0.0, texel_width)
209 + displacement_z - near_displacement;
210 normal_cross = cross(tangent_z, tangent_x);
211 }
212 normal_cross.y = max(normal_cross.y, MIN_NORMAL_Y);
213 if far_tier > 0.0 {
214 displacement = mix(
215 near_displacement,
216 far_displacement(transitioned.xy),
217 far_tier,
218 );
219 normal_cross = deformation_safe_normalize(
220 mix(
221 deformation_safe_normalize(normal_cross, vec3(0.0, 1.0, 0.0)),
222 far_normal_cross(transitioned.xy),
223 far_tier,
224 ),
225 vec3(0.0, 1.0, 0.0),
226 );
227 }
228 }
229 normal_cross.y = max(normal_cross.y, MIN_NORMAL_Y);
230 world_position = vec4(base_world_position + displacement, world_position.w);
232 result.world_position = world_position;
233 result.base_world_position = base_world_position;
234 result.world_normal = deformation_safe_normalize(normal_cross, vec3(0.0, 1.0, 0.0));
235 result.sample_xz = transitioned.xy;
236 result.undisplaced_xz = transitioned.xy;
237 result.lod = lod;
238 result.sample_alpha = sample_alpha;
239 result.displacement = displacement;
240 result.wave_height = displacement.y;
241 return result;
Contents