use anyhow::Result;
use crate::sdf::{self, Value, Variability};
use crate::usd::{Attribute, Prim, Relationship, Stage};
use super::impl_render_schema;
use super::tokens as tok;
use crate::schemas::common::get_typed;
#[derive(Clone, derive_more::Deref)]
pub struct RenderSettings(Prim);
impl RenderSettings {
pub fn define(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Self> {
Ok(Self(stage.define_prim(path)?.set_type_name(tok::T_RENDER_SETTINGS)?))
}
pub fn get(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Option<Self>> {
get_typed(stage, path, tok::T_RENDER_SETTINGS).map(|o| o.map(Self))
}
pub fn stage_settings_path(stage: &Stage) -> Result<Option<sdf::Path>> {
stage
.stage_metadata(tok::META_RENDER_SETTINGS_PRIM_PATH)?
.as_ref()
.and_then(Value::as_str)
.map(sdf::Path::new)
.transpose()
}
pub fn products_rel(&self) -> Relationship {
self.relationship(tok::REL_PRODUCTS)
}
pub fn create_products_rel(&self) -> Result<Relationship> {
Ok(self.create_relationship(tok::REL_PRODUCTS)?.set_custom(false)?)
}
pub fn included_purposes_attr(&self) -> Attribute {
self.attribute(tok::A_INCLUDED_PURPOSES)
}
pub fn create_included_purposes_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_INCLUDED_PURPOSES, "token[]")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
pub fn material_binding_purposes_attr(&self) -> Attribute {
self.attribute(tok::A_MATERIAL_BINDING_PURPOSES)
}
pub fn create_material_binding_purposes_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_MATERIAL_BINDING_PURPOSES, "token[]")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
pub fn rendering_color_space_attr(&self) -> Attribute {
self.attribute(tok::A_RENDERING_COLOR_SPACE)
}
pub fn create_rendering_color_space_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_RENDERING_COLOR_SPACE, "token")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
}
impl_render_schema!(settings_base RenderSettings);
#[derive(Clone, derive_more::Deref)]
pub struct RenderProduct(Prim);
impl RenderProduct {
pub fn define(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Self> {
Ok(Self(stage.define_prim(path)?.set_type_name(tok::T_RENDER_PRODUCT)?))
}
pub fn get(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Option<Self>> {
get_typed(stage, path, tok::T_RENDER_PRODUCT).map(|o| o.map(Self))
}
pub fn product_type_attr(&self) -> Attribute {
self.attribute(tok::A_PRODUCT_TYPE)
}
pub fn create_product_type_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_PRODUCT_TYPE, "token")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
pub fn product_name_attr(&self) -> Attribute {
self.attribute(tok::A_PRODUCT_NAME)
}
pub fn create_product_name_attr(&self) -> Result<Attribute> {
Ok(self.create_attribute(tok::A_PRODUCT_NAME, "token")?.set_custom(false)?)
}
pub fn ordered_vars_rel(&self) -> Relationship {
self.relationship(tok::REL_ORDERED_VARS)
}
pub fn create_ordered_vars_rel(&self) -> Result<Relationship> {
Ok(self.create_relationship(tok::REL_ORDERED_VARS)?.set_custom(false)?)
}
}
impl_render_schema!(settings_base RenderProduct);
#[derive(Clone, derive_more::Deref)]
pub struct RenderVar(Prim);
impl RenderVar {
pub fn define(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Self> {
Ok(Self(stage.define_prim(path)?.set_type_name(tok::T_RENDER_VAR)?))
}
pub fn get(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Option<Self>> {
get_typed(stage, path, tok::T_RENDER_VAR).map(|o| o.map(Self))
}
pub fn data_type_attr(&self) -> Attribute {
self.attribute(tok::A_DATA_TYPE)
}
pub fn create_data_type_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_DATA_TYPE, "token")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
pub fn source_name_attr(&self) -> Attribute {
self.attribute(tok::A_SOURCE_NAME)
}
pub fn create_source_name_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_SOURCE_NAME, "string")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
pub fn source_type_attr(&self) -> Attribute {
self.attribute(tok::A_SOURCE_TYPE)
}
pub fn create_source_type_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_SOURCE_TYPE, "token")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
}
impl_render_schema!(typed RenderVar);
#[derive(Clone, derive_more::Deref)]
pub struct RenderPass(Prim);
impl RenderPass {
pub fn define(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Self> {
Ok(Self(stage.define_prim(path)?.set_type_name(tok::T_RENDER_PASS)?))
}
pub fn get(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Option<Self>> {
get_typed(stage, path, tok::T_RENDER_PASS).map(|o| o.map(Self))
}
pub fn pass_type_attr(&self) -> Attribute {
self.attribute(tok::A_PASS_TYPE)
}
pub fn create_pass_type_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_PASS_TYPE, "token")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
pub fn command_attr(&self) -> Attribute {
self.attribute(tok::A_COMMAND)
}
pub fn create_command_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_COMMAND, "string[]")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
pub fn file_name_attr(&self) -> Attribute {
self.attribute(tok::A_FILE_NAME)
}
pub fn create_file_name_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_FILE_NAME, "asset")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
pub fn render_source_rel(&self) -> Relationship {
self.relationship(tok::REL_RENDER_SOURCE)
}
pub fn create_render_source_rel(&self) -> Result<Relationship> {
Ok(self.create_relationship(tok::REL_RENDER_SOURCE)?.set_custom(false)?)
}
pub fn input_passes_rel(&self) -> Relationship {
self.relationship(tok::REL_INPUT_PASSES)
}
pub fn create_input_passes_rel(&self) -> Result<Relationship> {
Ok(self.create_relationship(tok::REL_INPUT_PASSES)?.set_custom(false)?)
}
pub fn render_visibility_include_root_attr(&self) -> Attribute {
self.attribute(tok::A_RENDER_VISIBILITY_INCLUDE_ROOT)
}
pub fn create_render_visibility_include_root_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_RENDER_VISIBILITY_INCLUDE_ROOT, "bool")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
pub fn camera_visibility_include_root_attr(&self) -> Attribute {
self.attribute(tok::A_CAMERA_VISIBILITY_INCLUDE_ROOT)
}
pub fn create_camera_visibility_include_root_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_CAMERA_VISIBILITY_INCLUDE_ROOT, "bool")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
}
impl_render_schema!(typed RenderPass);
#[derive(Clone, derive_more::Deref)]
pub struct RenderDenoisePass(Prim);
impl RenderDenoisePass {
pub fn define(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Self> {
Ok(Self(
stage.define_prim(path)?.set_type_name(tok::T_RENDER_DENOISE_PASS)?,
))
}
pub fn get(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Option<Self>> {
get_typed(stage, path, tok::T_RENDER_DENOISE_PASS).map(|o| o.map(Self))
}
}
impl_render_schema!(typed RenderDenoisePass);
#[cfg(test)]
mod tests {
use super::*;
use crate::gf;
use crate::schemas::render::{AspectRatioConformPolicy, ProductType, RenderSettingsBase, SourceType};
#[test]
fn render_settings_roundtrip() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
let s = RenderSettings::define(&stage, "/Render/Settings")?;
s.create_resolution_attr()?.set(gf::vec2i(1280, 720))?;
s.create_aspect_ratio_conform_policy_attr()?
.set(AspectRatioConformPolicy::AdjustApertureWidth)?;
s.create_camera_rel()?.add_target(sdf::path("/World/Cam")?)?;
s.create_products_rel()?
.add_target(sdf::path("/Render/Products/beauty")?)?;
s.create_included_purposes_attr()?
.set(Value::TokenVec(vec!["default".into(), "render".into()]))?;
s.create_rendering_color_space_attr()?.set("lin_rec709".to_string())?;
let s = RenderSettings::get(&stage, "/Render/Settings")?.expect("RenderSettings");
assert_eq!(
s.resolution_attr().get::<Value>()?.and_then(|v| v.try_as_vec_2i()),
Some(gf::vec2i(1280, 720))
);
assert_eq!(
s.aspect_ratio_conform_policy_attr().get::<AspectRatioConformPolicy>()?,
Some(AspectRatioConformPolicy::AdjustApertureWidth)
);
assert_eq!(s.camera_rel().targets()?, vec![sdf::path("/World/Cam")?]);
assert_eq!(s.products_rel().targets()?, vec![sdf::path("/Render/Products/beauty")?]);
assert_eq!(
s.rendering_color_space_attr().get::<String>()?.as_deref(),
Some("lin_rec709")
);
stage.define_prim(sdf::path("/NotSettings")?)?.set_type_name("Scope")?;
assert!(RenderSettings::get(&stage, "/NotSettings")?.is_none());
Ok(())
}
#[test]
fn render_product_roundtrip() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
let p = RenderProduct::define(&stage, "/Render/Products/beauty")?;
p.create_product_type_attr()?.set(ProductType::Raster)?;
p.create_product_name_attr()?.set("beauty.exr".to_string())?;
p.create_resolution_attr()?.set(gf::vec2i(512, 512))?;
p.create_ordered_vars_rel()?
.add_target(sdf::path("/Render/Vars/color")?)?;
let p = RenderProduct::get(&stage, "/Render/Products/beauty")?.expect("RenderProduct");
assert_eq!(p.product_type_attr().get::<ProductType>()?, Some(ProductType::Raster));
assert_eq!(p.product_name_attr().get::<String>()?.as_deref(), Some("beauty.exr"));
assert_eq!(
p.resolution_attr().get::<Value>()?.and_then(|v| v.try_as_vec_2i()),
Some(gf::vec2i(512, 512))
);
assert_eq!(p.ordered_vars_rel().targets()?, vec![sdf::path("/Render/Vars/color")?]);
Ok(())
}
#[test]
fn render_var_roundtrip() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
let v = RenderVar::define(&stage, "/Render/Vars/N")?;
v.create_data_type_attr()?.set("normal3f".to_string())?;
v.create_source_name_attr()?.set("Nworld".to_string())?;
v.create_source_type_attr()?.set(SourceType::Primvar)?;
let v = RenderVar::get(&stage, "/Render/Vars/N")?.expect("RenderVar");
assert_eq!(v.data_type_attr().get::<String>()?.as_deref(), Some("normal3f"));
assert_eq!(v.source_name_attr().get::<String>()?.as_deref(), Some("Nworld"));
assert_eq!(v.source_type_attr().get::<SourceType>()?, Some(SourceType::Primvar));
Ok(())
}
#[test]
fn render_pass_roundtrip() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
let p = RenderPass::define(&stage, "/Render/Passes/beauty")?;
p.create_pass_type_attr()?.set("prman".to_string())?;
p.create_command_attr()?.set(Value::StringVec(vec![
"prman".into(),
"-t:0".into(),
"{fileName}".into(),
]))?;
p.create_file_name_attr()?
.set(Value::AssetPath("./beauty.rib".into()))?;
p.create_render_source_rel()?
.add_target(sdf::path("/Render/Settings")?)?;
p.create_camera_visibility_include_root_attr()?.set(false)?;
let p = RenderPass::get(&stage, "/Render/Passes/beauty")?.expect("RenderPass");
assert_eq!(p.pass_type_attr().get::<String>()?.as_deref(), Some("prman"));
assert_eq!(
p.command_attr().get::<Value>()?.and_then(|v| v.try_as_string_vec()),
Some(vec!["prman".to_string(), "-t:0".to_string(), "{fileName}".to_string()])
);
assert_eq!(p.render_source_rel().targets()?, vec![sdf::path("/Render/Settings")?]);
assert_eq!(p.camera_visibility_include_root_attr().get::<bool>()?, Some(false));
Ok(())
}
#[test]
fn denoise_pass_type_gate() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
RenderDenoisePass::define(&stage, "/Render/Denoise")?;
assert!(RenderDenoisePass::get(&stage, "/Render/Denoise")?.is_some());
assert!(RenderDenoisePass::get(&stage, "/Render/Settings")?.is_none());
Ok(())
}
}