M13.2: Create VR Environment nests the scene as a collapsed 3D precomp in each face comp (lights copied)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-03 04:22:49 -04:00
parent 7ac5317ce9
commit e1296a57c2
2 changed files with 39 additions and 27 deletions

View File

@ -433,8 +433,8 @@ fn ae(v: [f64; 3]) -> Vec3 {
}
/// Composition ▸ VR ▸ Create VR Environment: renders the active comp's 3D scene in every
/// direction. Six face comps (a 90° camera each at `position`, default the comp centre, with a
/// copy of the scene's layers), a 3:2 cube map of them, and an equirectangular output comp
/// direction. Six face comps (a 90° camera each at `position`, default the comp centre, looking
/// at the scene nested as a collapsed 3D precomp), a 3:2 cube map of them, and an equirectangular output comp
/// (VR Converter, cube map → equirectangular). One undo step.
fn create_vr_environment(s: &mut Session, p: &Value) -> Result<Value> {
let cid = comp_id(s, p)?;
@ -462,30 +462,41 @@ fn create_vr_environment(s: &mut Session, p: &Value) -> Result<Value> {
id_of(&s.execute("layer.newCamera", json!({"comp": fc, "name": format!("{fname} Camera"), "angleOfView": 90.0, "type": "oneNode"}))?, "layer")?;
s.execute("prop.set", json!({"comp": fc, "layer": cam, "path": "transform/position", "value": center}))?;
s.execute("prop.set", json!({"comp": fc, "layer": cam, "path": "transform/orientation", "value": o}))?;
// The scene's layers (not its cameras), in the scene's coordinates, under the face
// camera.
let layers: Vec<effectcraft_project::Layer> = src.layers.iter().filter(|l| !l.is_camera()).cloned().collect();
s.edit("Create VR Environment", None, |proj, _| {
let mut next = proj.next_id;
let mut map = std::collections::BTreeMap::new();
let mut copies = vec![];
for l in &layers {
let mut c = l.clone();
super::edit::reid(&mut c, &mut next);
map.insert(l.id, c.id);
copies.push(c);
}
for c in &mut copies {
c.parent = c.parent.and_then(|p| map.get(&p).copied());
if let Some(m) = &mut c.track_matte {
m.layer = map.get(&m.layer).copied().unwrap_or(m.layer);
// The scene, nested as a collapsed 3D precomp whose layer maps the scene's
// coordinates onto the face comp's unchanged (anchor = position): its 3D layers are
// seen through the face camera and edits to the scene show in every face. Lights
// inside a collapsed precomp do not light it (the containing comp's do), so the
// scene's lights are copied next to it.
let pre = id_of(&s.execute("layer.addItem", json!({"comp": fc, "item": cid.0, "time": 0.0}))?, "layer")?;
let mid = [src.width as f64 / 2.0, src.height as f64 / 2.0, 0.0];
s.execute("layer.setSwitch", json!({"comp": fc, "layers": [pre], "switch": "threeD", "value": true}))?;
s.execute("layer.setSwitch", json!({"comp": fc, "layers": [pre], "switch": "collapse", "value": true}))?;
s.execute("prop.set", json!({"comp": fc, "layer": pre, "path": "transform/anchor", "value": mid}))?;
s.execute("prop.set", json!({"comp": fc, "layer": pre, "path": "transform/position", "value": mid}))?;
let lights: Vec<effectcraft_project::Layer> = src.layers.iter().filter(|l| l.is_light()).cloned().collect();
if !lights.is_empty() {
s.edit("Create VR Environment", None, |proj, _| {
let mut next = proj.next_id;
let mut map = std::collections::BTreeMap::new();
let mut copies = vec![];
for l in &lights {
let mut c = l.clone();
super::edit::reid(&mut c, &mut next);
map.insert(l.id, c.id);
copies.push(c);
}
}
proj.next_id = next;
let comp = proj.comp_mut(ItemId(fc)).ok_or(EngineError::NoComp)?;
comp.layers.extend(copies);
Ok(())
})?;
for c in &mut copies {
c.parent = c.parent.and_then(|p| map.get(&p).copied());
}
proj.next_id = next;
let comp = proj.comp_mut(ItemId(fc)).ok_or(EngineError::NoComp)?;
let at = comp.layers.len().saturating_sub(1);
for (k, c) in copies.into_iter().enumerate() {
comp.layers.insert(at + k, c);
}
Ok(())
})?;
}
faces.push(fc);
}
let cube = id_of(

View File

@ -476,8 +476,9 @@ fn create_vr_environment_and_extract_cubemap() {
let c = s.project.comp(ItemId(*f)).unwrap();
assert_eq!((c.width, c.height), (64, 64));
assert!(c.layers.iter().any(|l| l.is_camera()));
assert!(c.layers.iter().any(|l| l.name == aname && l.is_3d()), "the scene's layers");
let _ = main;
let nested = c.layers.iter().find(|l| matches!(l.source, effectcraft_project::LayerSource::Comp { item } if item == main)).expect("the scene, nested");
assert!(nested.switches.collapse && nested.is_3d(), "collapsed 3D precomp");
let _ = &aname;
}
let cube = s.project.comp(ItemId(r["cubeMap"].as_u64().unwrap())).unwrap();
assert_eq!((cube.width, cube.height, cube.layers.len()), (192, 128, 6));