1#import bevy_render::view::View 2#import bevy_hanabi::vfx_common::{ 3 IndirectBuffer, SimParams, Spawner, BatchInfo, 4 seed, tau, pcg_hash, to_float01, frand, frand2, frand3, frand4, 5 rand_uniform_f, rand_uniform_vec2, rand_uniform_vec3, rand_uniform_vec4, 6 rand_normal_f, rand_normal_vec2, rand_normal_vec3, rand_normal_vec4, proj 15struct ParticleBuffer { 16 particles: array<Particle>, 20 @builtin(position) position: vec4<f32>, 21 @location(0) color: vec4<f32>, 23 @location(1) uv: vec2<f32>, 26 @location(2) normal: vec3<f32>, 28#ifdef NEEDS_PARTICLE_FRAGMENT 29 @location(3) slab_particle_index: u32, 33@group(0) @binding(0) var<uniform> view: View; 34@group(0) @binding(1) var<uniform> sim_params : SimParams; 36@group(1) @binding(0) var<storage, read> particle_buffer : ParticleBuffer; 37@group(1) @binding(1) var<storage, read> indirect_buffer : IndirectBuffer; 40@group(2) @binding(0) var<storage, read> spawners : array<Spawner>; 41@group(2) @binding(1) var<storage, read> prefix_sum : array<u32>; 42@group(2) @binding(2) var<storage, read> batch_info : BatchInfo; 47fn get_camera_position_effect_space() -> vec3<f32> { 48 let view_pos = view.world_from_view[3].xyz; 49#ifdef LOCAL_SPACE_SIMULATION 50 let inverse_transform = transpose( 52 spawners[batch_info.base_effect + effect_location.effect_index].inverse_transform[0].xyz, 53 spawners[batch_info.base_effect + effect_location.effect_index].inverse_transform[1].xyz, 54 spawners[batch_info.base_effect + effect_location.effect_index].inverse_transform[2].xyz, 57 return inverse_transform * view_pos; 63fn get_camera_rotation_effect_space() -> mat3x3<f32> { 64 let view_rot = mat3x3(view.world_from_view[0].xyz, view.world_from_view[1].xyz, view.world_from_view[2].xyz); 65#ifdef LOCAL_SPACE_SIMULATION 66 let inverse_transform = transpose( 68 spawners[batch_info.base_effect + effect_location.effect_index].inverse_transform[0].xyz, 69 spawners[batch_info.base_effect + effect_location.effect_index].inverse_transform[1].xyz, 70 spawners[batch_info.base_effect + effect_location.effect_index].inverse_transform[2].xyz, 73 return inverse_transform * view_rot; 80fn unpack_compressed_transform(compressed_transform: mat3x4<f32>) -> mat4x4<f32> { 83 compressed_transform[0], 84 compressed_transform[1], 85 compressed_transform[2], 86 vec4<f32>(0.0, 0.0, 0.0, 1.0) 92fn unpack_compressed_transform_3x3_transpose(compressed_transform: mat3x4<f32>) -> mat3x3<f32> { 94 compressed_transform[0].xyz, 95 compressed_transform[1].xyz, 96 compressed_transform[2].xyz, 104fn transform_position_simulation_to_world(sim_position: vec3<f32>) -> vec4<f32> { 105#ifdef LOCAL_SPACE_SIMULATION 106 let transform = unpack_compressed_transform(spawners[batch_info.base_effect + effect_location.effect_index].transform); 107 return transform * vec4<f32>(sim_position, 1.0); 109 return vec4<f32>(sim_position, 1.0); 113fn transform_normal_simulation_to_world(sim_normal: vec3<f32>) -> vec3<f32> { 114#ifdef LOCAL_SPACE_SIMULATION 118 let transform = unpack_compressed_transform_3x3_transpose(spawners[batch_info.base_effect + effect_location.effect_index].inverse_transform); 119 return transform * sim_normal; 131fn transform_position_simulation_to_clip(sim_position: vec3<f32>) -> vec4<f32> { 132 return view.clip_from_world * transform_position_simulation_to_world(sim_position); 135fn inverse_transpose_mat3(m: mat3x3<f32>) -> mat3x3<f32> { 136 let tmp0 = cross(m[1], m[2]); 137 let tmp1 = cross(m[2], m[0]); 138 let tmp2 = cross(m[0], m[1]); 139 let inv_det = 1.0 / dot(m[2], tmp2); 140 return mat3x3<f32>(tmp0 * inv_det, tmp1 * inv_det, tmp2 * inv_det); 146struct EffectLocation { 165fn find_location_from_particle(slab_particle_index: u32) -> EffectLocation { 166 var lo = batch_info.prefix_sum_offset; 167 var hi = lo + batch_info.prefix_sum_count; 170 let mid = (hi + lo) >> 1u; 171 let base_particle = prefix_sum[mid]; 172 if (slab_particle_index >= base_particle) { 174 } else if (slab_particle_index < base_particle) { 178 if (num_iter >= 100) { 179 return EffectLocation(0xDEADBEEFu, 0xDEADBEEFu, 0xDEADBEEFu); 182 let base_particle = batch_info.base_particle + prefix_sum[lo - 1u]; 183 let effect_index = lo - 1u - batch_info.prefix_sum_offset; 184 let update_index = slab_particle_index - base_particle; 185 return EffectLocation(effect_index, base_particle, update_index); 192var<private> effect_location : EffectLocation; 194var<private> effect_metadata_index: u32; 199 @builtin(instance_index) instance_index: u32, 200 @location(0) vertex_position: vec3<f32>, 202 @location(1) vertex_uv: vec2<f32>, 205 @location(2) vertex_normal: vec3<f32>, 213 let slab_particle_index = batch_info.base_particle + instance_index; 216 effect_location = find_location_from_particle(slab_particle_index); 217 let spawner = &spawners[batch_info.base_effect + effect_location.effect_index]; 218 effect_metadata_index = (*spawner).effect_metadata_index; 219 let base_particle = effect_location.base_particle; 222 let indirect_read_index = (*spawner).render_indirect_read_index; 223 let particle_index = indirect_buffer.rows[base_particle + instance_index].particle_index[indirect_read_index]; 224 var particle = particle_buffer.particles[base_particle + particle_index]; 226 var out: VertexOutput; 228#ifdef NEEDS_PARTICLE_FRAGMENT 229 out.slab_particle_index = base_particle + particle_index; 230#endif // NEEDS_PARTICLE_FRAGMENT 234 if (instance_index == 0) { 235 out.position = vec4(0.0); 240 let prev_index = indirect_buffer.rows[base_particle + instance_index - 1u].particle_index[indirect_read_index]; 241 let prev_particle = particle_buffer.particles[base_particle + prev_index]; 245 if (prev_particle.ribbon_id != particle.ribbon_id) { 246 out.position = vec4(0.0); 255 let row_count = {{FLIPBOOK_ROW_COUNT}}; 256 let ij = vec2<f32>(f32(particle.sprite_index % row_count), f32(particle.sprite_index / row_count)); 257 uv = (ij + uv) * {{FLIPBOOK_SCALE}}; 267 var delta = particle.position - prev_particle.position; 269 axis_x = normalize(delta); 270 axis_y = normalize(cross(axis_x, axis_z)); 271 axis_z = cross(axis_x, axis_y); 273 position = mix(particle.position, prev_particle.position, 0.5); 274 size = vec3(length(delta), size.y, 1.0); 279 let vpos = vertex_position * size; 280 let sim_position = position + axis_x * vpos.x + axis_y * vpos.y + axis_z * vpos.z; 281 out.position = transform_position_simulation_to_clip(sim_position); 286 let normal = inverse_transpose_mat3(mat3x3(axis_x, axis_y, axis_z)) * vertex_normal; 287 out.normal = transform_normal_simulation_to_world(normal); 288#endif // NEEDS_NORMAL 294fn fragment(in: VertexOutput) -> @location(0) vec4<f32> { 297 var alpha_cutoff: f32 = {{ALPHA_CUTOFF}}; 299 var color = in.color; 304 var normal = in.normal; 306#ifdef NEEDS_PARTICLE_FRAGMENT 307 var particle = particle_buffer.particles[in.slab_particle_index]; 308#endif // NEEDS_PARTICLE_FRAGMENT 310{{FRAGMENT_MODIFIERS}} 313 if color.a >= alpha_cutoff {