use anyhow::Result;
use crate::sdf::{Path, Value, Variability};
use crate::usd::{Prim, Stage};
use crate::schemas::lux::tokens::{
A_GUIDE_RADIUS, A_POLE_AXIS, A_TEXTURE_FILE, A_TEXTURE_FORMAT, REL_PORTALS, T_DOME_LIGHT, T_DOME_LIGHT_1,
};
use crate::schemas::lux::types::{PoleAxis, TextureFormat};
use super::common::{author_input_asset, author_rel_targets};
use super::light_api::LightApiSetters;
pub fn define_dome_light<'s>(stage: &'s Stage, path: impl Into<Path>) -> Result<DomeLightAuthor<'s>> {
let prim = stage.define_prim(path)?.set_type_name(T_DOME_LIGHT)?;
Ok(DomeLightAuthor { prim })
}
pub struct DomeLightAuthor<'s> {
prim: Prim<'s>,
}
impl<'s> DomeLightAuthor<'s> {
pub fn into_prim(self) -> Prim<'s> {
self.prim
}
pub fn set_texture_file(self, path: impl Into<String>) -> Result<Self> {
author_input_asset(self.prim.stage(), self.prim.path(), A_TEXTURE_FILE, path)?;
Ok(self)
}
pub fn set_texture_format(self, format: TextureFormat) -> Result<Self> {
author_token_input(
self.prim.stage(),
self.prim.path(),
A_TEXTURE_FORMAT,
format.as_token().to_string(),
)?;
Ok(self)
}
pub fn set_portals<I, P>(self, targets: I) -> Result<Self>
where
I: IntoIterator<Item = P>,
P: Into<Path>,
{
author_rel_targets(self.prim.stage(), self.prim.path(), REL_PORTALS, targets)?;
Ok(self)
}
pub fn set_guide_radius(self, radius: f32) -> Result<Self> {
author_scalar_float(self.prim.stage(), self.prim.path(), A_GUIDE_RADIUS, radius)?;
Ok(self)
}
}
impl<'s> LightApiSetters<'s> for DomeLightAuthor<'s> {
fn prim(&self) -> &Prim<'s> {
&self.prim
}
}
pub fn define_dome_light_1<'s>(stage: &'s Stage, path: impl Into<Path>) -> Result<DomeLight1Author<'s>> {
let prim = stage.define_prim(path)?.set_type_name(T_DOME_LIGHT_1)?;
Ok(DomeLight1Author { prim })
}
pub struct DomeLight1Author<'s> {
prim: Prim<'s>,
}
impl<'s> DomeLight1Author<'s> {
pub fn into_prim(self) -> Prim<'s> {
self.prim
}
pub fn set_texture_file(self, path: impl Into<String>) -> Result<Self> {
author_input_asset(self.prim.stage(), self.prim.path(), A_TEXTURE_FILE, path)?;
Ok(self)
}
pub fn set_texture_format(self, format: TextureFormat) -> Result<Self> {
author_token_input(
self.prim.stage(),
self.prim.path(),
A_TEXTURE_FORMAT,
format.as_token().to_string(),
)?;
Ok(self)
}
pub fn set_portals<I, P>(self, targets: I) -> Result<Self>
where
I: IntoIterator<Item = P>,
P: Into<Path>,
{
author_rel_targets(self.prim.stage(), self.prim.path(), REL_PORTALS, targets)?;
Ok(self)
}
pub fn set_guide_radius(self, radius: f32) -> Result<Self> {
author_scalar_float(self.prim.stage(), self.prim.path(), A_GUIDE_RADIUS, radius)?;
Ok(self)
}
pub fn set_pole_axis(self, axis: PoleAxis) -> Result<Self> {
author_uniform_token(
self.prim.stage(),
self.prim.path(),
A_POLE_AXIS,
axis.as_token().to_string(),
)?;
Ok(self)
}
}
impl<'s> LightApiSetters<'s> for DomeLight1Author<'s> {
fn prim(&self) -> &Prim<'s> {
&self.prim
}
}
fn author_token_input(stage: &Stage, prim: &Path, name: &str, value: String) -> Result<()> {
let attr_path = prim.append_property(name)?;
stage
.create_attribute(attr_path, "token")?
.set_custom(false)?
.set(Value::Token(value))?;
Ok(())
}
fn author_uniform_token(stage: &Stage, prim: &Path, name: &str, value: String) -> Result<()> {
let attr_path = prim.append_property(name)?;
stage
.create_attribute(attr_path, "token")?
.set_variability(Variability::Uniform)?
.set_custom(false)?
.set(Value::Token(value))?;
Ok(())
}
fn author_scalar_float(stage: &Stage, prim: &Path, name: &str, value: f32) -> Result<()> {
let attr_path = prim.append_property(name)?;
stage
.create_attribute(attr_path, "float")?
.set_custom(false)?
.set(Value::Float(value))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sdf;
#[test]
fn dome_light_roundtrip_with_texture_and_portals() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
define_dome_light(&stage, sdf::path("/Dome")?)?
.set_texture_file("./studio.hdr")?
.set_texture_format(TextureFormat::Latlong)?
.set_portals([sdf::path("/Dome/Portal")?])?
.set_guide_radius(50.0)?
.set_intensity(1.0)?;
let dome = crate::schemas::lux::read_dome_light(&stage, &sdf::path("/Dome")?)?.expect("DomeLight");
assert_eq!(dome.texture_file.as_deref(), Some("./studio.hdr"));
assert_eq!(dome.texture_format, TextureFormat::Latlong);
assert!((dome.guide_radius - 50.0).abs() < 1e-3);
assert_eq!(dome.portals, vec!["/Dome/Portal".to_string()]);
Ok(())
}
#[test]
fn dome_light_1_authors_pole_axis() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
define_dome_light_1(&stage, sdf::path("/Dome")?)?
.set_texture_file("./studio.hdr")?
.set_pole_axis(PoleAxis::Z)?;
let dome = crate::schemas::lux::read_dome_light(&stage, &sdf::path("/Dome")?)?.expect("DomeLight_1");
assert_eq!(dome.texture_file.as_deref(), Some("./studio.hdr"));
match stage.field::<sdf::Value>("/Dome.poleAxis", sdf::FieldKey::Default)? {
Some(sdf::Value::Token(t)) => assert_eq!(t, "Z"),
other => panic!("expected token 'Z' for poleAxis, got {other:?}"),
}
Ok(())
}
}