mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-31 05:27:08 -06:00
layer.create: the instance3d op - a 2D copy of a 3D model layer (#2100)
Layer > Create > Create 3D Layer Instance, as After Effects (public docs):
3D model layers take no masks or effects, so the command puts a comp-sized
solid at the top of the layer stack with CC Composite referencing the
selected model, and switches the model off (back on to keep its shadow) -
a 2D copy of its render, through the comp's camera, that 2D effects can be
applied to.
- CC Composite (ec.channel.cccomposite) gains an optional Layer parameter:
with a layer chosen it takes the original's place; In Front replaces the
solid's pixels, alpha included, the other Composite Original modes blend
with them. The GPU compositor runs such an instance on the CPU.
- FxHost serves a referenced model or primitive layer as After Effects
does - the layer rendered on its own through the comp's camera
(Renderer::solo_3d); the owner keeps no cache entry for it (it moves with
the camera and lights, without a trace in its own properties).
- layer.create {op: instance3d} skips layers that are not 3D model or
primitive layers (the ones that take no effects); one undo step, the copy
selected, the model's video off.
Tests: tests_instance3d.rs (behaviour, pixels, undo/redo, serde, 2D effects
on the copy), a CC Composite layer-parameter unit test; docs/effects.md
regenerated (3033 parameters).
This commit is contained in:
+7
@@ -68,6 +68,13 @@ impl Dependencies<'_> {
|
||||
if self.path.len() > crate::MAX_FX_DEPTH || self.path.contains(&id) {
|
||||
return None;
|
||||
}
|
||||
// A referenced 3D model or primitive layer renders through the comp's camera and lights
|
||||
// (an effect layer parameter reads it that way — Create 3D Layer Instance's CC
|
||||
// Composite): those move its pixels without a trace in its own properties, so the owner
|
||||
// keeps no cache entry for it.
|
||||
if layer.source.is_model() {
|
||||
return None;
|
||||
}
|
||||
self.remaining = self.remaining.checked_sub(1)?;
|
||||
if (self.other_times || super::reads_other_times(layer)) && super::has_expression(&layer.props) {
|
||||
return None;
|
||||
|
||||
@@ -277,6 +277,14 @@ impl EffectHost for FxHost<'_, '_, '_> {
|
||||
return None;
|
||||
}
|
||||
let sub = self.r.nested();
|
||||
// A 3D model or primitive layer has no 2D source of its own: an effect referencing one
|
||||
// reads it as After Effects does — the layer rendered on its own through the comp's
|
||||
// camera (Create 3D Layer Instance's CC Composite), in the layer's effect space (the
|
||||
// comp, centred on the layer's anchor).
|
||||
if other.source.is_model() {
|
||||
let buf = sub.solo_3d(self.ctx, other);
|
||||
return Some(self.pixels(other, buf, false));
|
||||
}
|
||||
// The cached layer buffer is shared; effects get their own copy.
|
||||
let buf = if masks_and_effects { (*sub.content_buf(self.ctx, other)?).clone() } else { sub.source(self.ctx, other)? };
|
||||
Some(self.pixels(other, buf, true))
|
||||
@@ -1493,6 +1501,25 @@ impl<'a> Renderer<'a> {
|
||||
self.source(ctx, layer)
|
||||
}
|
||||
|
||||
/// A 3D model or primitive layer rendered on its own, through the comp's camera: what an
|
||||
/// effect layer parameter reads when it references one, as After Effects, which renders 3D
|
||||
/// layers through the active camera. The comp's pixels at this render's scale; the layer's
|
||||
/// blend mode, track matte and video switch don't apply (a source read, not a composite —
|
||||
/// the solo clone of a 3D track matte, see `three_d::adv`).
|
||||
fn solo_3d(&self, ctx: &EvalCtx, layer: &Layer) -> Buf {
|
||||
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 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 }
|
||||
}
|
||||
|
||||
/// Layer pixels after source → masks → effects (no layer styles).
|
||||
pub fn content_buf(&self, ctx: &EvalCtx, layer: &Layer) -> Option<Arc<Buf>> {
|
||||
self.content_buf_timed(ctx, layer, None).map(|(b, _)| b)
|
||||
|
||||
Reference in New Issue
Block a user