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:
Minher0
2026-10-25 12:46:58 +00:00
parent 991904888a
commit 82cebba68f
8 changed files with 302 additions and 8 deletions
+7
View File
@@ -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;