Skip to main content

oxihuman_export/
hip_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Houdini HIP scene stub export.
6
7/// HIP format type.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum HipFormat {
10    Hip,
11    Hiplc,
12    Hipnc,
13}
14
15impl HipFormat {
16    pub fn extension(&self) -> &'static str {
17        match self {
18            HipFormat::Hip => "hip",
19            HipFormat::Hiplc => "hiplc",
20            HipFormat::Hipnc => "hipnc",
21        }
22    }
23}
24
25/// A HIP node stub.
26#[derive(Debug, Clone)]
27pub struct HipNode {
28    pub name: String,
29    pub node_type: String,
30    pub parms: Vec<(String, String)>,
31}
32
33/// A HIP scene export stub.
34#[derive(Debug, Clone)]
35pub struct HipExport {
36    pub format: HipFormat,
37    pub hip_version: String,
38    pub nodes: Vec<HipNode>,
39}
40
41/// Create a new HIP export.
42pub fn new_hip_export(format: HipFormat) -> HipExport {
43    HipExport {
44        format,
45        hip_version: "20.0.506".to_string(),
46        nodes: Vec::new(),
47    }
48}
49
50/// Add a node to the HIP scene.
51pub fn hip_add_node(export: &mut HipExport, name: &str, node_type: &str) {
52    export.nodes.push(HipNode {
53        name: name.to_string(),
54        node_type: node_type.to_string(),
55        parms: Vec::new(),
56    });
57}
58
59/// Set a parameter on the last added node.
60pub fn hip_set_parm(export: &mut HipExport, key: &str, value: &str) {
61    if let Some(node) = export.nodes.last_mut() {
62        node.parms.push((key.to_string(), value.to_string()));
63    }
64}
65
66/// Return the node count.
67pub fn hip_node_count(export: &HipExport) -> usize {
68    export.nodes.len()
69}
70
71/// Validate the HIP export.
72pub fn validate_hip(export: &HipExport) -> bool {
73    !export.hip_version.is_empty()
74}
75
76/// Generate a stub HIP script string.
77pub fn hip_to_string(export: &HipExport) -> String {
78    let mut out = format!("# Houdini {} Scene\n", export.hip_version);
79    for node in &export.nodes {
80        out.push_str(&format!("opadd -e {} {}\n", node.node_type, node.name));
81        for (k, v) in &node.parms {
82            out.push_str(&format!("opparm {} {} ({})\n", node.name, k, v));
83        }
84    }
85    out
86}
87
88/// Estimate the HIP file size in bytes.
89pub fn hip_size_estimate(export: &HipExport) -> usize {
90    hip_to_string(export).len()
91}
92
93/// Find a node by name.
94pub fn hip_find_node<'a>(export: &'a HipExport, name: &str) -> Option<&'a HipNode> {
95    export.nodes.iter().find(|n| n.name == name)
96}
97
98/// Get the file extension for this format.
99pub fn hip_extension(export: &HipExport) -> &'static str {
100    export.format.extension()
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_new_hip_export() {
109        let exp = new_hip_export(HipFormat::Hip);
110        assert_eq!(exp.format, HipFormat::Hip);
111        assert_eq!(hip_node_count(&exp), 0);
112    }
113
114    #[test]
115    fn test_add_node() {
116        let mut exp = new_hip_export(HipFormat::Hip);
117        hip_add_node(&mut exp, "geo1", "geo");
118        assert_eq!(hip_node_count(&exp), 1);
119    }
120
121    #[test]
122    fn test_set_parm() {
123        let mut exp = new_hip_export(HipFormat::Hip);
124        hip_add_node(&mut exp, "geo1", "geo");
125        hip_set_parm(&mut exp, "tx", "1.0");
126        assert_eq!(exp.nodes[0].parms.len(), 1);
127    }
128
129    #[test]
130    fn test_validate() {
131        let exp = new_hip_export(HipFormat::Hiplc);
132        assert!(validate_hip(&exp));
133    }
134
135    #[test]
136    fn test_to_string() {
137        let mut exp = new_hip_export(HipFormat::Hip);
138        hip_add_node(&mut exp, "geo1", "geo");
139        let s = hip_to_string(&exp);
140        assert!(s.contains("geo1"));
141    }
142
143    #[test]
144    fn test_size_estimate() {
145        let exp = new_hip_export(HipFormat::Hip);
146        assert!(hip_size_estimate(&exp) > 0);
147    }
148
149    #[test]
150    fn test_find_node() {
151        let mut exp = new_hip_export(HipFormat::Hip);
152        hip_add_node(&mut exp, "mynode", "null");
153        let found = hip_find_node(&exp, "mynode");
154        assert!(found.is_some());
155    }
156
157    #[test]
158    fn test_extension() {
159        let exp = new_hip_export(HipFormat::Hipnc);
160        assert_eq!(hip_extension(&exp), "hipnc");
161    }
162
163    #[test]
164    fn test_format_extensions() {
165        assert_eq!(HipFormat::Hip.extension(), "hip");
166        assert_eq!(HipFormat::Hiplc.extension(), "hiplc");
167        assert_eq!(HipFormat::Hipnc.extension(), "hipnc");
168    }
169}