Effects: a host evaluates one effect parameter at another layer time (#1373)

EffectHost::param_at(effect_index, id, layer_time) evaluates a single
parameter (by its flattened id, such as birthRate or group/param) of the
effect at that index in the layer's stack. A simulation can then sample
one parameter at every step without flattening the whole effect each
time.

The effect index is passed in, rather than read from the renderer's
running-effect index as params_at does: GPU-batched effects never update
that index, so a batched effect would read another effect's parameters.
The renderer's implementation walks the effect group by match id and
evaluates just that property (keyframes and expressions) at the comp
time of the layer time.
This commit is contained in:
tylerjenningsw
2026-10-13 20:45:20 -04:00
parent 549e9d8251
commit 7b0771fe5a
2 changed files with 28 additions and 0 deletions
+21
View File
@@ -343,6 +343,27 @@ impl EffectHost for FxHost<'_, '_, '_> {
Some(effectcraft_effects::flatten_params(g, &mut |pr| ctx.value(self.layer, pr)))
}
fn param_at(&self, effect_index: usize, id: &str, layer_time: f64) -> Option<effectcraft_keyframe::Value> {
// Only the one property: a simulation samples it at every step.
let mut g = self.layer.effects()?.groups().nth(effect_index)?;
let mut path = id.split('/').peekable();
while let Some(m) = path.next() {
if path.peek().is_none() {
let p = g.children.iter().find_map(|c| match c {
effectcraft_project::Node::Prop(p) if p.match_id == m => Some(p),
_ => None,
})?;
let ctx = self.ctx.at(self.layer.comp_time(Tick::from_seconds_f64(layer_time)));
return Some(ctx.value(self.layer, p));
}
g = g.children.iter().find_map(|c| match c {
effectcraft_project::Node::Group(sg) if sg.match_id == m => Some(sg),
_ => None,
})?;
}
None
}
fn comp_scene(&self) -> Option<effectcraft_effects::CompScene> {
Some(comp_scene(self.ctx, self.layer)?.rebased(self.origin))
}