Skip to content

Commit 83e1775

Browse files
authored
SetPositionBoxModifier (#551)
Create a `SetPositionBoxModifier` to initialize (or move) the particles randomly inside or at the surface of a cube.
1 parent 675257a commit 83e1775

3 files changed

Lines changed: 150 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
55

66
## Unreleased
77

8+
### Added
9+
- Added `SetPositionBoxModifier`
10+
811
### Changed
912

1013
- Batch same-effect instances. This greatly improves performance when using many instances

examples/init.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,22 @@ fn setup(
157157
cube.clone(),
158158
mat.clone(),
159159
);
160+
161+
spawn_effect(
162+
&mut commands,
163+
"SetPositionBoxModifier".to_string(),
164+
3.,
165+
Transform::from_translation(Vec3::new(0., 10., 0.)),
166+
effects.add(base_effect("SetPositionBoxModifier", |writer| {
167+
SetPositionBoxModifier {
168+
center: writer.lit(Vec3::ZERO).expr(),
169+
extent: writer.lit(Vec3::splat(5.)).expr(),
170+
dimension: ShapeDimension::Volume,
171+
}
172+
})),
173+
cube.clone(),
174+
mat.clone(),
175+
);
160176
}
161177

162178
fn rotate_effect(time: Res<Time>, mut query: Query<(&RotateSpeed, &mut Transform)>) {

src/modifier/position.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,134 @@ impl Modifier for SetPositionCone3dModifier {
343343
Ok(())
344344
}
345345
}
346+
347+
/// A modifier to set the position of particles on a box.
348+
///
349+
/// The box is axis aligned.
350+
///
351+
/// Particles are moved somewhere inside the volume or on the surface of a
352+
/// box defined by its extent.
353+
///
354+
/// # Attributes
355+
///
356+
/// This modifier requires the following particle attributes:
357+
/// - [`Attribute::POSITION`]
358+
#[derive(Clone, Copy, Hash, Reflect)]
359+
pub struct SetPositionBoxModifier {
360+
/// The box center, relative to the emitter position.
361+
///
362+
/// Expression type: `Vec3`
363+
pub center: ExprHandle,
364+
/// The extent of the box.
365+
///
366+
/// Expression type: `Vec3`
367+
pub extent: ExprHandle,
368+
/// The shape dimension to set the position to.
369+
pub dimension: ShapeDimension,
370+
}
371+
372+
impl SetPositionBoxModifier {
373+
fn eval(
374+
&self,
375+
module: &mut Module,
376+
context: &mut dyn EvalContext,
377+
) -> Result<String, ExprError> {
378+
let func_id = calc_func_id(self);
379+
let func_name = format!("set_position_box_{0:016X}", func_id);
380+
381+
context.make_fn(
382+
&func_name,
383+
"particle: ptr<function, Particle>",
384+
module,
385+
&mut |m: &mut Module, ctx: &mut dyn EvalContext| -> Result<String, ExprError> {
386+
let center = ctx.eval(m, self.center)?;
387+
let extent = ctx.eval(m, self.extent)?;
388+
389+
let code = match self.dimension {
390+
ShapeDimension::Surface => {
391+
format!(
392+
r#" let center = {};
393+
let extent = {};
394+
395+
let face = frand();
396+
let rand1 = frand() - 0.5;
397+
let rand2 = frand() - 0.5;
398+
let fixed = 0.5;
399+
400+
var x: f32;
401+
var y: f32;
402+
var z: f32;
403+
404+
if face < (1. / 6.) {{
405+
x = rand1 * extent.x;
406+
y = fixed * extent.y;
407+
z = rand2 * extent.z;
408+
}} else if face < (2. / 6.) {{
409+
x = rand1 * extent.x;
410+
y = -fixed * extent.y;
411+
z = rand2 * extent.z;
412+
}} else if face < (3. / 6.) {{
413+
x = fixed * extent.x;
414+
y = rand1 * extent.y;
415+
z = rand2 * extent.z;
416+
}} else if face < (4. / 6.) {{
417+
x = -fixed * extent.x;
418+
y = rand1 * extent.y;
419+
z = rand2 * extent.z;
420+
}} else if face < (5. / 6.) {{
421+
x = rand1 * extent.x;
422+
y = rand2 * extent.y;
423+
z = fixed * extent.z;
424+
}} else {{
425+
x = rand1 * extent.x;
426+
y = rand2 * extent.y;
427+
z = -fixed * extent.z;
428+
}}
429+
(*particle).{} = center + vec3(x, y, z);
430+
"#,
431+
center,
432+
extent,
433+
Attribute::POSITION.name()
434+
)
435+
}
436+
ShapeDimension::Volume => format!(
437+
r#" let center = {};
438+
let extent = {};
439+
let mult = frand3() - 0.5;
440+
(*particle).{} = center + extent * mult;
441+
"#,
442+
center,
443+
extent,
444+
Attribute::POSITION.name()
445+
),
446+
};
447+
448+
Ok(code)
449+
},
450+
)?;
451+
452+
let code = format!("{}(&particle);\n", func_name);
453+
454+
Ok(code)
455+
}
456+
}
457+
458+
impl Modifier for SetPositionBoxModifier {
459+
fn context(&self) -> ModifierContext {
460+
ModifierContext::Init | ModifierContext::Update
461+
}
462+
463+
fn attributes(&self) -> &[Attribute] {
464+
&[Attribute::POSITION]
465+
}
466+
467+
fn boxed_clone(&self) -> BoxedModifier {
468+
Box::new(*self)
469+
}
470+
471+
fn apply(&self, module: &mut Module, context: &mut ShaderWriter) -> Result<(), ExprError> {
472+
let code = self.eval(module, context)?;
473+
context.main_code += &code;
474+
Ok(())
475+
}
476+
}

0 commit comments

Comments
 (0)