Skip to content

Commit 31da996

Browse files
authored
Fix generated texture code for multi-texturing (#430)
Ensure the bind points are unique for all textures used. Fixes #421
1 parent 20aa419 commit 31da996

File tree

6 files changed

+31
-21
lines changed

6 files changed

+31
-21
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6262
- Fixed a bug with opaque effects not rendering.
6363
- Fixed a bug in `ParticleLayout` where some fields may not have been aligned according to the WGSL rules.
6464
- Added a workaround for a `wgpu` bug on macOS/Metal backend related to `ParticleLayout` alignment.
65+
- Fixed a bug where only 1 texture could be used due to incorrectly generated shader code. (#421)
6566

6667
### Removed
6768

@@ -78,7 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7879
- Removed `EffectAsset::ribbon_group` as well as `with_trails()` and `with_ribbons()`.
7980
Use the `Attribute::RIBBON_ID` instead to assign a per-particle ribbon ID.
8081
- Removed `ParticleEffectBundle`. Use `ParticleEffect` directly instead.
81-
- Removed `ParticleEffect::z_layer_2d`. Use the Z coordinate of the effect's `Tranform` to order effects.
82+
- Removed `ParticleEffect::z_layer_2d`. Use the Z coordinate of the effect's `Tranform` to order effects. (#423)
8283

8384
## [0.14.0] 2024-12-09
8485

assets/ramp.png

577 Bytes
Loading

examples/circle.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
2222
}
2323

2424
fn setup(
25+
asset_server: Res<AssetServer>,
2526
mut commands: Commands,
2627
mut images: ResMut<Assets<Image>>,
2728
mut effects: ResMut<Assets<EffectAsset>>,
@@ -40,6 +41,9 @@ fn setup(
4041
let anim_img = make_anim_img(sprite_size, sprite_grid_size, Vec3::new(0.1, 0.1, 0.1));
4142
let texture_handle = images.add(anim_img);
4243

44+
// Also use a serialized asset
45+
let texture_handle2 = asset_server.load("ramp.png");
46+
4347
// The sprites form a grid, with a total animation frame count equal to the
4448
// number of sprites.
4549
let frame_count = sprite_grid_size.x * sprite_grid_size.y;
@@ -105,9 +109,11 @@ fn setup(
105109
let update_sprite_index = SetAttributeModifier::new(Attribute::SPRITE_INDEX, sprite_index);
106110

107111
let texture_slot = writer.lit(0u32).expr();
112+
let texture_slot2 = writer.lit(1u32).expr();
108113

109114
let mut module = writer.finish();
110115
module.add_texture_slot("color");
116+
module.add_texture_slot("shape");
111117

112118
let effect = effects.add(
113119
EffectAsset::new(32768, Spawner::burst(32.0.into(), 8.0.into()), module)
@@ -121,6 +127,10 @@ fn setup(
121127
texture_slot: texture_slot,
122128
sample_mapping: ImageSampleMapping::ModulateOpacityFromR,
123129
})
130+
.render(ParticleTextureModifier {
131+
texture_slot: texture_slot2,
132+
sample_mapping: ImageSampleMapping::ModulateRGB,
133+
})
124134
.render(FlipbookModifier { sprite_grid_size })
125135
.render(ColorOverLifetimeModifier { gradient })
126136
.render(SizeOverLifetimeModifier {
@@ -150,7 +160,7 @@ fn setup(
150160
commands.spawn((
151161
ParticleEffect::new(effect),
152162
EffectMaterial {
153-
images: vec![texture_handle],
163+
images: vec![texture_handle, texture_handle2],
154164
},
155165
Name::new("effect"),
156166
));

src/lib.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1116,12 +1116,16 @@ fn append_spawn_events_{0}(particle_index: u32, count: u32) {{
11161116
texture_layout
11171117
);
11181118
let mut material_bindings_code = String::new();
1119+
let mut bind_index = 0;
11191120
for (slot, _) in texture_layout.layout.iter().enumerate() {
1121+
let tex_index = bind_index;
1122+
let sampler_index = bind_index + 1;
11201123
material_bindings_code.push_str(&format!(
1121-
"@group(2) @binding(0) var material_texture_{slot}: texture_2d<f32>;
1122-
@group(2) @binding(1) var material_sampler_{slot}: sampler;
1124+
"@group(2) @binding({tex_index}) var material_texture_{slot}: texture_2d<f32>;
1125+
@group(2) @binding({sampler_index}) var material_sampler_{slot}: sampler;
11231126
"
11241127
));
1128+
bind_index += 2;
11251129
}
11261130

11271131
(

src/modifier/output.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,18 @@ pub enum ImageSampleMapping {
4444
ModulateOpacityFromR,
4545
}
4646

47-
impl ToWgslString for ImageSampleMapping {
48-
fn to_wgsl_string(&self) -> String {
47+
impl ImageSampleMapping {
48+
/// Convert this mapping to shader code for the given texture slot.
49+
pub fn to_shader_code(&self, texture_slot: &str) -> String {
4950
match *self {
50-
ImageSampleMapping::Modulate => "color = color * texColor;",
51+
ImageSampleMapping::Modulate => format!("color = color * texColor{texture_slot};"),
5152
ImageSampleMapping::ModulateRGB => {
52-
"color = vec4<f32>(color.rgb * texColor.rgb, color.a);"
53+
format!("color = vec4<f32>(color.rgb * texColor{texture_slot}.rgb, color.a);")
54+
}
55+
ImageSampleMapping::ModulateOpacityFromR => {
56+
format!("color.a = color.a * texColor{texture_slot}.r;")
5357
}
54-
ImageSampleMapping::ModulateOpacityFromR => "color.a = color.a * texColor.r;",
5558
}
56-
.to_string()
5759
}
5860
}
5961

@@ -118,7 +120,7 @@ impl ParticleTextureModifier {
118120
) -> Result<String, ExprError> {
119121
let texture_slot = module.try_get(self.texture_slot)?;
120122
let texture_slot = texture_slot.eval(module, context)?;
121-
let sample_mapping = self.sample_mapping.to_wgsl_string();
123+
let sample_mapping = self.sample_mapping.to_shader_code(&texture_slot[..]);
122124

123125
let sample_mapping_name = format!("{:?}", self.sample_mapping);
124126

@@ -129,15 +131,15 @@ impl ParticleTextureModifier {
129131
let mut code = String::with_capacity(1024);
130132
code += &format!(
131133
" // ParticleTextureModifier
132-
var texColor: vec4<f32>;
134+
var texColor{texture_slot}: vec4<f32>;
133135
switch ({texture_slot}) {{\n"
134136
);
135137
let count = module.texture_layout().layout.len() as u32;
136138
for index in 0..count {
137139
let wgsl_index = index.to_wgsl_string();
138-
code += &format!(" case {wgsl_index}: {{ texColor = textureSample(material_texture_{index}, material_sampler_{index}, uv); }}\n");
140+
code += &format!(" case {wgsl_index}: {{ texColor{texture_slot} = textureSample(material_texture_{index}, material_sampler_{index}, uv); }}\n");
139141
}
140-
code += " default: {{ texColor = vec4<f32>(0.0); }}\n";
142+
code += &format!(" default: {{ texColor{texture_slot} = vec4<f32>(0.0); }}\n");
141143
code += &format!(
142144
" }}
143145
// Sample mapping: {sample_mapping_name}

src/render/mod.rs

-7
Original file line numberDiff line numberDiff line change
@@ -1992,13 +1992,6 @@ impl SpecializedRenderPipeline for ParticlesRenderPipeline {
19921992

19931993
if let Some(material_bind_group_layout) = self.get_material(&key.texture_layout) {
19941994
layout.push(material_bind_group_layout.clone());
1995-
// // @location(1) vertex_uv: vec2<f32>
1996-
// vertex_buffer_layout.attributes.push(VertexAttribute {
1997-
// format: VertexFormat::Float32x2,
1998-
// offset: 12,
1999-
// shader_location: 1,
2000-
// });
2001-
// vertex_buffer_layout.array_stride += 8;
20021995
}
20031996

20041997
// Key: LOCAL_SPACE_SIMULATION

0 commit comments

Comments
 (0)