mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Engine: adding a 3D model switches a Classic 3D comp to Advanced 3D (#264)
Imported GLB/glTF models were invisible in the Composition viewer while
text showed. Only the Advanced 3D renderer draws model and primitive
layers, and new compositions (and New Comp from Selection) are Classic
3D by default, but adding a model layer left the comp's renderer alone,
so the model drew nothing on every backend. It wasn't the reporter's
GPU: on this machine the viewer showed nothing for a GLB in a new comp
with the GPU compositor (Vulkan) and with Mercury Software Only, and
the model in both once the comp used Advanced 3D.
As After Effects documents ("Advanced 3D gets automatically selected
when you add a 3D model to the composition"), layer.newModel /
layer.addItem of a model and layer.newPrimitive now switch a Classic
3D comp to Advanced 3D inside the same edit (one undo step, Geometry
Options added to its text and shape layers like Composition Settings
does) and toast that the comp changed renderer. Switching a comp that
has model layers back to Classic 3D toasts why they disappear.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b0fa9f1943
commit
6d20cc2151
2 files changed
+37
-12
No files matched your search
@@ -61,6 +61,9 @@ fn comp_renderer(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
sync_geometry_options(proj, cid);
|
||||
Ok(())
|
||||
})?;
|
||||
if r == Renderer::Classic3D && s.project.comp(cid).is_some_and(|c| c.layers.iter().any(|l| l.source.is_model())) {
|
||||
s.toast("Classic 3D doesn't draw 3D model layers: they show with the Advanced 3D renderer");
|
||||
}
|
||||
Ok(json!({"renderer": renderer_id(r)}))
|
||||
}
|
||||
|
||||
@@ -71,6 +74,25 @@ fn renderer_id(r: Renderer) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
/// Only the Advanced 3D renderer draws model and primitive layers, so adding one switches a
|
||||
/// Classic 3D comp to it, like After Effects (inside the adding edit: one undo step). Whether
|
||||
/// the renderer changed.
|
||||
fn use_advanced_3d(proj: &mut effectcraft_project::Project, cid: ItemId) -> Result<bool> {
|
||||
let c = proj.comp_mut(cid).ok_or(EngineError::NoComp)?;
|
||||
let switched = c.renderer != Renderer::Advanced3D;
|
||||
c.renderer = Renderer::Advanced3D;
|
||||
sync_geometry_options(proj, cid);
|
||||
Ok(switched)
|
||||
}
|
||||
|
||||
/// Tell the user when [`use_advanced_3d`] changed the comp's renderer.
|
||||
fn report_switch(s: &mut Session, cid: ItemId, switched: bool) {
|
||||
if switched {
|
||||
let name = s.project.item(cid).map_or("the composition", |i| i.name.as_str()).to_string();
|
||||
s.toast(format!("\"{name}\" now uses the Advanced 3D renderer, which draws 3D models"));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- model layers
|
||||
|
||||
/// Set a property's value at comp time `t` (adds a key when it is animated).
|
||||
@@ -121,7 +143,7 @@ pub(crate) fn new_model(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let start = fr.snap_nearest(f_p(p, "time").map(Tick::from_seconds_f64).unwrap_or(Tick::ZERO));
|
||||
let index = index_p(p, "layer.newModel")?;
|
||||
let position = position_p(p, "layer.newModel")?;
|
||||
let id = s.edit("New 3D Model Layer", None, |proj, st| {
|
||||
let (id, switched) = s.edit("New 3D Model Layer", None, |proj, st| {
|
||||
let mut l = build::layer(proj, &comp, &name, LayerSource::Model { item }, (comp.width, comp.height), None);
|
||||
let g = build::model_geometry_options(&mut Ids(&mut proj.next_id), scale, &clips);
|
||||
if let Some(slot) = l.props.children.iter_mut().find(|c| c.match_id() == "geometryOptions") {
|
||||
@@ -131,9 +153,11 @@ pub(crate) fn new_model(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
l.in_point = start;
|
||||
l.out_point = comp.duration.max(start + fr.frame_duration());
|
||||
place(&mut l, position);
|
||||
insert_layer_at(proj, st, cid, l, index)
|
||||
let id = insert_layer_at(proj, st, cid, l, index)?;
|
||||
Ok((id, use_advanced_3d(proj, cid)?))
|
||||
})?;
|
||||
Ok(json!({"layer": id.0, "item": item.0}))
|
||||
report_switch(s, cid, switched);
|
||||
Ok(json!({"layer": id.0, "item": item.0, "advanced3d": true}))
|
||||
}
|
||||
|
||||
fn kind_p(p: &Value, cmd: &str) -> Result<PrimitiveKind> {
|
||||
@@ -187,7 +211,7 @@ fn new_primitive(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let comp = s.project.comp(cid).ok_or(EngineError::NoComp)?.clone();
|
||||
let name = str_p(p, "name").map(str::to_string).unwrap_or_else(|| kind.label().to_string());
|
||||
let t = super::time_p(s, p, Some(&comp));
|
||||
let id = s.edit(&format!("New {}", kind.label()), None, |proj, st| {
|
||||
let (id, switched) = s.edit(&format!("New {}", kind.label()), None, |proj, st| {
|
||||
let mut l = build::layer(proj, &comp, &name, LayerSource::Primitive { kind }, (comp.width, comp.height), None);
|
||||
for k in ["width", "height", "depth", "radius", "tubeRadius", "segments", "rings"] {
|
||||
if let Some(v) = f_p(p, k) {
|
||||
@@ -199,11 +223,11 @@ fn new_primitive(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
set_at(&mut l, "transform/position", t, KV::Vec3([g(0), g(1), g(2)]));
|
||||
}
|
||||
apply_material(&mut l, p, t, "layer.newPrimitive")?;
|
||||
insert_layer(proj, st, cid, l)
|
||||
let id = insert_layer(proj, st, cid, l)?;
|
||||
Ok((id, use_advanced_3d(proj, cid)?))
|
||||
})?;
|
||||
// Primitives are drawn by the Advanced 3D renderer.
|
||||
let advanced = s.project.comp(cid).is_some_and(|c| c.renderer == Renderer::Advanced3D);
|
||||
Ok(json!({"layer": id.0, "kind": kind.label(), "advanced3d": advanced}))
|
||||
report_switch(s, cid, switched);
|
||||
Ok(json!({"layer": id.0, "kind": kind.label(), "advanced3d": true}))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- environment layer
|
||||
@@ -352,7 +376,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"3D Model Layer",
|
||||
[],
|
||||
None,
|
||||
"{item?: id or name of a 3D model footage item, path?: a .gltf, .glb or .obj file to import, name?, time? (s), index? (1-based stack position), position? ([x, y] comp px)}",
|
||||
"{item?: id or name of a 3D model footage item, path?: a .gltf, .glb or .obj file to import, name?, time? (s), index? (1-based stack position), position? ([x, y] comp px)}; switches a Classic 3D comp to Advanced 3D",
|
||||
has_comp,
|
||||
new_model
|
||||
),
|
||||
@@ -361,7 +385,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"3D Primitive",
|
||||
[],
|
||||
None,
|
||||
"{kind: cube|sphere|plane|torus|cone|cylinder, name?, width?, height?, depth?, radius?, tubeRadius?, segments?, rings?, position? [x,y,z], baseColor?, metallic?, roughness?, emissive?, castsShadows?, acceptsShadows?, acceptsLights?}",
|
||||
"{kind: cube|sphere|plane|torus|cone|cylinder, name?, width?, height?, depth?, radius?, tubeRadius?, segments?, rings?, position? [x,y,z], baseColor?, metallic?, roughness?, emissive?, castsShadows?, acceptsShadows?, acceptsLights?}; switches a Classic 3D comp to Advanced 3D",
|
||||
has_comp,
|
||||
new_primitive
|
||||
),
|
||||
@@ -370,7 +394,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"New 3D Primitive",
|
||||
[],
|
||||
None,
|
||||
"{kind: cube|sphere|plane|torus|cone|cylinder, name?, width?, height?, depth?, radius?, tubeRadius?, segments?, rings?, position? [x,y,z], baseColor?, metallic?, roughness?, emissive?, castsShadows?, acceptsShadows?, acceptsLights?}",
|
||||
"{kind: cube|sphere|plane|torus|cone|cylinder, name?, width?, height?, depth?, radius?, tubeRadius?, segments?, rings?, position? [x,y,z], baseColor?, metallic?, roughness?, emissive?, castsShadows?, acceptsShadows?, acceptsLights?}; switches a Classic 3D comp to Advanced 3D",
|
||||
has_comp,
|
||||
new_primitive
|
||||
),
|
||||
|
||||
@@ -100,7 +100,8 @@ time), primitive layers (Layer ▸ New ▸ Cube/Sphere/Plane/Torus/Cone/Cylinder
|
||||
Geometry Options), text and shape layers with Geometry Options ▸ Extrusion Depth (glyph and
|
||||
fill outlines flattened, triangulated with holes and extruded with Angular/Concave/Convex
|
||||
bevels, per character so per-character 3D carries over), and a textured card for every other
|
||||
3D layer. Shading is physically based in linear light (glTF metallic-roughness; Cook–Torrance
|
||||
3D layer. Classic 3D doesn't draw model or primitive layers, so adding one switches a Classic 3D
|
||||
comp to Advanced 3D in the same undo step, as in After Effects. Shading is physically based in linear light (glTF metallic-roughness; Cook–Torrance
|
||||
GGX specular, Smith–Schlick geometry, Schlick Fresnel, Lambert diffuse, after Karis 2013):
|
||||
parallel, spot and point lights with After Effects' falloffs, ambient lights, image-based
|
||||
light from an Environment light whose Source (or the comp's Environment Layer, Layer ▸
|
||||
|
||||
Reference in new issue
Block a user