render: a 3D layer read outside its in/out range is transparent

solo_3d passed the layer to the 3D pipelines whatever the comp time, so an
instance (CC Composite referencing a model layer) rendered before the source
layer's in-point or after its out-point. Like any other layer reference, there
is nothing to read outside the range: return the transparent canvas.
This commit is contained in:
Minher0 committed 2026-10-25 13:21:57 +00:00
1 parent 82cebba68f
commit 601a15bc1f
2 files changed
+28 -1

No files matched your search

+22
View File
@@ -155,3 +155,25 @@ fn instance3d_needs_a_3d_layer() {
s.execute("layer.setSwitch", json!({"layers": [text], "switch": "threeD", "value": true})).unwrap();
assert!(s.execute_checked("layer.create", json!({"op": "instance3d", "layers": [text]})).is_err());
}
#[test]
fn instance3d_shows_nothing_outside_the_source_layer_range() {
let mut s = session();
let model = triangle_model(&mut s);
// The model's range ends at 1 s; the comp runs to 2 s.
let comp = cid(&s);
s.edit("trim", None, |p, _| {
p.comp_mut(comp).unwrap().layer_mut(LayerId(model)).unwrap().out_point = Tick::from_seconds_f64(1.0);
Ok(())
})
.unwrap();
s.execute("layer.select", json!({"layers": [model]})).unwrap();
let r = s.execute("layer.create", json!({"op": "instance3d"})).unwrap();
let _copy = r["layers"][0].as_u64().unwrap();
// Inside the model's range the copy shows it, transparent around it.
let inside = s.render(comp, Tick::from_seconds_f64(0.5), RenderOpts::default());
assert!(px(&inside, 320, 180)[3] > 0.9, "the copy shows the model: {:?}", px(&inside, 320, 180));
// After its out point the source has nothing to read: the copy is transparent.
let outside = s.render(comp, Tick::from_seconds_f64(1.5), RenderOpts::default());
assert!(outside.data.iter().all(|p| p[3] < 1e-3), "transparent after the source's out point: {:?}", px(&outside, 320, 180));
}
+6 -1
View File
@@ -1510,12 +1510,17 @@ impl<'a> Renderer<'a> {
let s = self.opts.scale;
let w = ((ctx.comp.width as f64 * s).ceil() as u32).max(1);
let h = ((ctx.comp.height as f64 * s).ceil() as u32).max(1);
let mut canvas = Image::new(w, h);
// Outside the layer's in/out points there is nothing to read: the copy is transparent
// there, like any other layer reference (the 3D pipelines don't gate on the range).
if !layer.is_active_at(ctx.time) {
return Buf { img: canvas, offset: [0.0; 2], scale: s };
}
let mut solo = layer.clone();
solo.track_matte = None;
solo.blend_mode = effectcraft_color::BlendMode::Normal;
solo.preserve_transparency = false;
solo.switches.video = true;
let mut canvas = Image::new(w, h);
three_d::compose::draw_run(self, ctx, &[&solo], &mut canvas);
Buf { img: canvas, offset: [0.0; 2], scale: s }
}