mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-02 06:27:11 -06:00
Merge branch 'worktree-agent-aba044eaf08eded94'
# Conflicts: # crates/render/src/three_d/compose.rs
This commit is contained in:
@@ -53,6 +53,9 @@ pub trait FootageSource: Send + Sync {
|
||||
/// Set the decoded-frame cache budget in bytes (Settings ▸ Memory & CPU); sources without
|
||||
/// a cache ignore it.
|
||||
fn set_cache_budget(&self, _bytes: usize) {}
|
||||
/// Settings ▸ Disk ▸ Conformed Audio Folder: where decoded audio is kept between reads
|
||||
/// (`None` = off); sources that don't decode audio ignore it.
|
||||
fn set_conform_folder(&self, _folder: Option<std::path::PathBuf>) {}
|
||||
/// The decoded-frame cache budget in bytes, if the source has a cache.
|
||||
fn cache_budget(&self) -> Option<usize> {
|
||||
None
|
||||
@@ -303,6 +306,19 @@ pub struct RenderOpts {
|
||||
pub roi: Option<[f64; 4]>,
|
||||
/// Which proxies stand in for footage and compositions (Render Settings ▸ Proxy Use).
|
||||
pub proxy: effectcraft_project::render_queue::ProxyUse,
|
||||
/// Switches Affect Nested Comps (Settings ▸ General): a precomp layer's Quality and Motion
|
||||
/// Blur switches also limit the layers of the nested comp (Draft/Wireframe or motion blur off
|
||||
/// propagate down; they never raise a nested layer's own setting).
|
||||
pub nested_switches: bool,
|
||||
/// Realtime Shadows in Draft (Settings ▸ 3D): draft renders (`draft`) still cast shadows.
|
||||
pub draft_shadows: bool,
|
||||
}
|
||||
|
||||
impl RenderOpts {
|
||||
/// Whether 3D layers cast shadows in this render.
|
||||
pub fn shadows(&self) -> bool {
|
||||
!self.draft || self.draft_shadows
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RenderOpts {
|
||||
@@ -315,6 +331,8 @@ impl Default for RenderOpts {
|
||||
view: None,
|
||||
roi: None,
|
||||
backend: Backend::Cpu,
|
||||
nested_switches: true,
|
||||
draft_shadows: true,
|
||||
proxy: effectcraft_project::render_queue::ProxyUse::CurrentSettings,
|
||||
}
|
||||
}
|
||||
@@ -342,6 +360,9 @@ pub struct Renderer<'a> {
|
||||
opacity_mul: f32,
|
||||
/// Collapsed precomp: nested 3D layers join the parent's 3D space.
|
||||
pub(crate) collapse3d: Option<Collapse3d<'a>>,
|
||||
/// Switches inherited from the precomp layers this comp is rendered through (see
|
||||
/// [`RenderOpts::nested_switches`]): the worst quality and whether motion blur is allowed.
|
||||
pub(crate) inherited: Option<(Quality, bool)>,
|
||||
}
|
||||
|
||||
/// How a collapsed precomp's 3D layers are placed in the parent (see `Renderer::collapse_into`).
|
||||
@@ -385,9 +406,39 @@ impl<'a> Renderer<'a> {
|
||||
outer: None,
|
||||
opacity_mul: 1.0,
|
||||
collapse3d: None,
|
||||
inherited: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A layer's Quality switch, limited by the precomp layers above it when
|
||||
/// [`RenderOpts::nested_switches`] is on.
|
||||
pub fn quality(&self, layer: &Layer) -> Quality {
|
||||
let rank = |q: Quality| match q {
|
||||
Quality::Best => 0,
|
||||
Quality::Draft => 1,
|
||||
Quality::Wireframe => 2,
|
||||
};
|
||||
match self.inherited {
|
||||
Some((q, _)) if rank(q) > rank(layer.switches.quality) => q,
|
||||
_ => layer.switches.quality,
|
||||
}
|
||||
}
|
||||
|
||||
/// A layer's Motion Blur switch, limited by the precomp layers above it.
|
||||
pub(crate) fn layer_motion_blur(&self, layer: &Layer) -> bool {
|
||||
layer.switches.motion_blur && self.inherited.is_none_or(|(_, mb)| mb)
|
||||
}
|
||||
|
||||
/// The renderer for the nested comp of precomp layer `layer`: its switches pass down when
|
||||
/// [`RenderOpts::nested_switches`] is on.
|
||||
fn nested_through(&self, layer: &Layer) -> Renderer<'a> {
|
||||
let mut sub = self.nested();
|
||||
if self.opts.nested_switches {
|
||||
sub.inherited = Some((self.quality(layer), self.layer_motion_blur(layer)));
|
||||
}
|
||||
sub
|
||||
}
|
||||
|
||||
/// A renderer for nested work (precomps, layers read by effects): one level deeper.
|
||||
fn nested(&self) -> Renderer<'a> {
|
||||
Renderer { depth: self.depth + 1, outer: None, opacity_mul: 1.0, collapse3d: None, ..*self }
|
||||
@@ -769,7 +820,7 @@ impl<'a> Renderer<'a> {
|
||||
// values.
|
||||
let ov = essential_overrides(ctx, layer);
|
||||
let tmp = if ov.is_empty() { None } else { effectcraft_project::essential::with_overrides(self.project, *item, &ov) };
|
||||
let base = self.nested();
|
||||
let base = self.nested_through(layer);
|
||||
let sub = match &tmp {
|
||||
Some(p) => Renderer { project: p, ..base },
|
||||
None => base,
|
||||
@@ -1058,7 +1109,7 @@ impl<'a> Renderer<'a> {
|
||||
}
|
||||
|
||||
fn sampling(&self, layer: &Layer) -> effectcraft_raster::Sampling {
|
||||
if layer.switches.quality == Quality::Draft {
|
||||
if self.quality(layer) == Quality::Draft {
|
||||
// Draft quality: no interpolation (nearest neighbour), as After Effects' Draft.
|
||||
effectcraft_raster::Sampling::Nearest
|
||||
} else if self.opts.draft {
|
||||
@@ -1103,7 +1154,7 @@ impl<'a> Renderer<'a> {
|
||||
|
||||
/// Whether a layer is motion blurred.
|
||||
fn mb_on(&self, ctx: &EvalCtx, layer: &Layer) -> bool {
|
||||
self.opts.motion_blur && ctx.comp.enable_motion_blur && layer.switches.motion_blur
|
||||
self.opts.motion_blur && ctx.comp.enable_motion_blur && self.layer_motion_blur(layer)
|
||||
}
|
||||
|
||||
/// Motion-blur sub-samples for a layer buffer (1 = no motion blur). As in After Effects, a 2D
|
||||
@@ -1227,7 +1278,7 @@ impl<'a> Renderer<'a> {
|
||||
/// Draw a 2D layer into `canvas`; returns the region it may have changed.
|
||||
fn draw_layer(&self, ctx: &EvalCtx, layer: &Layer, canvas: &mut Image, blank: bool) -> Region {
|
||||
let opacity = ctx.opacity(layer) as f32 * self.opacity_mul;
|
||||
if layer.switches.quality == Quality::Wireframe && !layer.switches.adjustment {
|
||||
if self.quality(layer) == Quality::Wireframe && !layer.switches.adjustment {
|
||||
self.draw_wireframe(ctx, layer, canvas);
|
||||
return Region::Full;
|
||||
}
|
||||
|
||||
@@ -855,7 +855,14 @@ pub(crate) fn build_at(r: &Renderer, ctx: &EvalCtx, sub: Option<&EvalCtx>, run:
|
||||
{
|
||||
add_environment(&mut b, w, h, d, k, rot);
|
||||
}
|
||||
super::raster::shadow_maps(&mut b.s, r.opts.draft);
|
||||
if r.opts.shadows() {
|
||||
super::raster::shadow_maps(&mut b.s, r.opts.draft);
|
||||
} else {
|
||||
// Settings ▸ 3D ▸ Realtime Shadows in Draft off: draft renders skip shadows.
|
||||
for l in &mut b.s.lights {
|
||||
l.shadow = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Camera matrices.
|
||||
let s = r.opts.scale;
|
||||
|
||||
@@ -163,7 +163,7 @@ pub(crate) fn camera_for(r: &Renderer, ctx: &EvalCtx) -> CameraState {
|
||||
|
||||
/// Number of motion-blur sub-samples for a layer.
|
||||
fn mb_samples(r: &Renderer, ctx: &EvalCtx, layer: &Layer) -> usize {
|
||||
if r.opts.motion_blur && ctx.comp.enable_motion_blur && layer.switches.motion_blur {
|
||||
if r.opts.motion_blur && ctx.comp.enable_motion_blur && r.layer_motion_blur(layer) {
|
||||
if r.opts.draft { 4 } else { ctx.comp.motion_blur_samples.clamp(2, 64) as usize }
|
||||
} else {
|
||||
1
|
||||
@@ -366,9 +366,9 @@ pub(crate) fn draw_run(r: &Renderer, ctx: &EvalCtx, run: &[&Layer], canvas: &mut
|
||||
}
|
||||
let out = (canvas.width, canvas.height);
|
||||
let g = gather(r, ctx, run, out);
|
||||
let casters: Vec<&Item> = g.items.iter().chain(g.extra.iter()).filter(|i| i.mat.casts_shadows != 0 && !i.geos.is_empty()).collect();
|
||||
let casters: Vec<&Item> = g.items.iter().chain(g.extra.iter()).filter(|i| r.opts.shadows() && i.mat.casts_shadows != 0 && !i.geos.is_empty()).collect();
|
||||
composite(canvas, &g.items, &g.lights, &casters, run[0].id.0 as u32);
|
||||
for l in run.iter().filter(|l| l.switches.quality == effectcraft_project::Quality::Wireframe) {
|
||||
for l in run.iter().filter(|l| r.quality(l) == effectcraft_project::Quality::Wireframe) {
|
||||
r.draw_layer(ctx, l, canvas, false);
|
||||
}
|
||||
}
|
||||
@@ -387,7 +387,7 @@ fn gather<'a>(r: &Renderer<'a>, ctx: &EvalCtx<'a>, run: &[&'a Layer], out: (u32,
|
||||
let mut items: Vec<Item> = run
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.filter(|(_, l)| ctx.opacity(l) > 0.0 && l.switches.quality != effectcraft_project::Quality::Wireframe)
|
||||
.filter(|(_, l)| ctx.opacity(l) > 0.0 && r.quality(l) != effectcraft_project::Quality::Wireframe)
|
||||
.flat_map_iter(|(i, l)| {
|
||||
// Stack order with room for a collapsed precomp's layers in between.
|
||||
let i = i * 1024;
|
||||
@@ -682,7 +682,7 @@ pub struct Plane3d {
|
||||
/// Prepare a run of 3D layers for an accelerator. `None` when the run must be drawn on the CPU
|
||||
/// (adjustment, wireframe or environment background layers, Advanced 3D).
|
||||
pub(crate) fn gpu_run(r: &Renderer, ctx: &EvalCtx, run: &[&Layer], out: (u32, u32)) -> Option<Run3d> {
|
||||
if run.is_empty() || run.iter().any(|l| l.switches.adjustment || l.environment_background || l.switches.quality == effectcraft_project::Quality::Wireframe)
|
||||
if run.is_empty() || run.iter().any(|l| l.switches.adjustment || l.environment_background || r.quality(l) == effectcraft_project::Quality::Wireframe)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
@@ -711,7 +711,7 @@ pub(crate) fn gpu_run(r: &Renderer, ctx: &EvalCtx, run: &[&Layer], out: (u32, u3
|
||||
order: it.order,
|
||||
})
|
||||
.collect();
|
||||
let casters = planes.iter().enumerate().filter(|(_, p)| p.mat.casts_shadows != 0 && !p.geos.is_empty()).map(|(i, _)| i).collect();
|
||||
let casters = planes.iter().enumerate().filter(|(_, p)| r.opts.shadows() && p.mat.casts_shadows != 0 && !p.geos.is_empty()).map(|(i, _)| i).collect();
|
||||
Some(Run3d { planes, casters, lights: g.lights, seed: run[0].id.0 as u32 })
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user