use anyhow::Result;
use crate::sdf::{self, Variability};
use crate::usd::{Attribute, Prim, Stage};
use super::impl_ui_schema;
use super::tokens as tok;
use crate::schemas::common::{get_typed, get_with_api};
#[derive(Clone, derive_more::Deref)]
pub struct Backdrop(Prim);
impl Backdrop {
pub fn define(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Self> {
Ok(Self(stage.define_prim(path)?.set_type_name(tok::T_BACKDROP)?))
}
pub fn get(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Option<Self>> {
get_typed(stage, path, tok::T_BACKDROP).map(|o| o.map(Self))
}
pub fn description_attr(&self) -> Attribute {
self.attribute(tok::A_DESCRIPTION)
}
pub fn create_description_attr(&self) -> Result<Attribute> {
Ok(self
.create_attribute(tok::A_DESCRIPTION, "token")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
}
impl_ui_schema!(typed Backdrop);
#[derive(Clone, derive_more::Deref)]
pub struct SceneGraphPrimAPI(Prim);
impl SceneGraphPrimAPI {
pub fn apply(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Self> {
Ok(Self(
stage
.override_prim(path)?
.add_applied_schema(tok::API_SCENE_GRAPH_PRIM)?,
))
}
pub fn get(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Option<Self>> {
get_with_api(stage, path, &[tok::API_SCENE_GRAPH_PRIM]).map(|o| o.map(Self))
}
pub fn display_name_attr(&self) -> Attribute {
self.attribute(tok::A_DISPLAY_NAME)
}
pub fn create_display_name_attr(&self) -> Result<Attribute> {
self.uniform_token(tok::A_DISPLAY_NAME)
}
pub fn display_group_attr(&self) -> Attribute {
self.attribute(tok::A_DISPLAY_GROUP)
}
pub fn create_display_group_attr(&self) -> Result<Attribute> {
self.uniform_token(tok::A_DISPLAY_GROUP)
}
fn uniform_token(&self, name: &str) -> Result<Attribute> {
Ok(self
.create_attribute(name, "token")?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
}
impl_ui_schema!(single_api SceneGraphPrimAPI);
#[derive(Clone, derive_more::Deref)]
pub struct NodeGraphNodeAPI(Prim);
impl NodeGraphNodeAPI {
pub fn apply(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Self> {
Ok(Self(
stage.override_prim(path)?.add_applied_schema(tok::API_NODEGRAPH_NODE)?,
))
}
pub fn get(stage: &Stage, path: impl Into<sdf::Path>) -> Result<Option<Self>> {
get_with_api(stage, path, &[tok::API_NODEGRAPH_NODE]).map(|o| o.map(Self))
}
pub fn pos_attr(&self) -> Attribute {
self.attribute(tok::A_NODE_POS)
}
pub fn create_pos_attr(&self) -> Result<Attribute> {
self.uniform(tok::A_NODE_POS, "float2")
}
pub fn size_attr(&self) -> Attribute {
self.attribute(tok::A_NODE_SIZE)
}
pub fn create_size_attr(&self) -> Result<Attribute> {
self.uniform(tok::A_NODE_SIZE, "float2")
}
pub fn stacking_order_attr(&self) -> Attribute {
self.attribute(tok::A_NODE_STACKING_ORDER)
}
pub fn create_stacking_order_attr(&self) -> Result<Attribute> {
self.uniform(tok::A_NODE_STACKING_ORDER, "int")
}
pub fn display_color_attr(&self) -> Attribute {
self.attribute(tok::A_NODE_DISPLAY_COLOR)
}
pub fn create_display_color_attr(&self) -> Result<Attribute> {
self.uniform(tok::A_NODE_DISPLAY_COLOR, "color3f")
}
pub fn icon_attr(&self) -> Attribute {
self.attribute(tok::A_NODE_ICON)
}
pub fn create_icon_attr(&self) -> Result<Attribute> {
self.uniform(tok::A_NODE_ICON, "asset")
}
pub fn expansion_state_attr(&self) -> Attribute {
self.attribute(tok::A_NODE_EXPANSION_STATE)
}
pub fn create_expansion_state_attr(&self) -> Result<Attribute> {
self.uniform(tok::A_NODE_EXPANSION_STATE, "token")
}
pub fn doc_uri_attr(&self) -> Attribute {
self.attribute(tok::A_NODE_DOC_URI)
}
pub fn create_doc_uri_attr(&self) -> Result<Attribute> {
self.uniform(tok::A_NODE_DOC_URI, "string")
}
fn uniform(&self, name: &str, type_name: &str) -> Result<Attribute> {
Ok(self
.create_attribute(name, type_name)?
.set_custom(false)?
.set_variability(Variability::Uniform)?)
}
}
impl_ui_schema!(single_api NodeGraphNodeAPI);
#[cfg(test)]
mod tests {
use super::*;
use crate::gf;
use crate::schemas::ui::ExpansionState;
use crate::usd::SchemaBase;
#[test]
fn scene_graph_prim_roundtrip() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
stage.define_prim(sdf::path("/World/Mesh")?)?.set_type_name("Mesh")?;
let sg = SceneGraphPrimAPI::apply(&stage, "/World/Mesh")?;
sg.create_display_name_attr()?.set("Hero Mesh".to_string())?;
sg.create_display_group_attr()?.set("Characters".to_string())?;
let p = SceneGraphPrimAPI::get(&stage, "/World/Mesh")?.expect("SceneGraphPrimAPI");
assert_eq!(p.display_name_attr().get::<String>()?.as_deref(), Some("Hero Mesh"));
assert_eq!(p.display_group_attr().get::<String>()?.as_deref(), Some("Characters"));
stage.define_prim(sdf::path("/Bare")?)?.set_type_name("Scope")?;
assert!(SceneGraphPrimAPI::get(&stage, "/Bare")?.is_none());
Ok(())
}
#[test]
fn nodegraph_node_roundtrip() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
stage.define_prim(sdf::path("/Mat/Shader")?)?.set_type_name("Shader")?;
let n = NodeGraphNodeAPI::apply(&stage, "/Mat/Shader")?;
n.create_pos_attr()?.set(gf::vec2f(12.0, 34.0))?;
n.create_size_attr()?.set(gf::vec2f(180.0, 90.0))?;
n.create_stacking_order_attr()?.set(3)?;
n.create_display_color_attr()?.set(gf::vec3f(0.2, 0.4, 0.8))?;
n.create_icon_attr()?.set(sdf::Value::AssetPath("./node.png".into()))?;
n.create_expansion_state_attr()?.set(ExpansionState::Minimized)?;
n.create_doc_uri_attr()?.set("https://example.com/node".to_string())?;
let n = NodeGraphNodeAPI::get(&stage, "/Mat/Shader")?.expect("NodeGraphNodeAPI");
assert_eq!(n.pos_attr().get::<gf::Vec2f>()?, Some(gf::vec2f(12.0, 34.0)));
assert_eq!(n.size_attr().get::<gf::Vec2f>()?, Some(gf::vec2f(180.0, 90.0)));
assert_eq!(n.stacking_order_attr().get::<i32>()?, Some(3));
assert_eq!(
n.display_color_attr().get::<gf::Vec3f>()?,
Some(gf::vec3f(0.2, 0.4, 0.8))
);
assert_eq!(
n.expansion_state_attr().get::<ExpansionState>()?,
Some(ExpansionState::Minimized)
);
assert_eq!(
n.doc_uri_attr().get::<String>()?.as_deref(),
Some("https://example.com/node")
);
Ok(())
}
#[test]
fn backdrop_roundtrip() -> Result<()> {
let stage = Stage::builder().in_memory("anon.usda")?;
Backdrop::define(&stage, "/Mat/Note")?
.create_description_attr()?
.set("lighting nodes".to_string())?;
let b = Backdrop::get(&stage, "/Mat/Note")?.expect("Backdrop");
assert_eq!(b.description_attr().get::<String>()?.as_deref(), Some("lighting nodes"));
assert!(b.is_concrete());
Ok(())
}
}