1use std::path::Path;
10use std::str::FromStr;
11use std::sync::Arc;
12use std::{ffi::CString, ffi::OsStr, fmt::Formatter};
13
14use log::debug;
15
16use crate::pdg::TopNode;
17pub use crate::{
18 errors::Result,
19 ffi::{AssetInfo, GeoInfo, KeyFrame, NodeInfo, ObjectInfo, ParmInfo},
20 geometry::Geometry,
21 parameter::*,
22 session::{CookResult, Session},
23};
24pub use crate::{
25 ffi::raw::{
26 ErrorCode, NodeFlags, NodeType, PresetType, RSTOrder, State, StatusType, StatusVerbosity,
27 TransformComponent,
28 },
29 ffi::{CookOptions, Transform, TransformEuler},
30};
31
32impl std::fmt::Display for NodeType {
33 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34 f.write_str(match *self {
35 NodeType::Sop => "Sop",
36 NodeType::Obj => "Obj",
37 NodeType::Rop => "Rop",
38 NodeType::Dop => "Dop",
39 NodeType::Cop => "Cop",
40 NodeType::Shop => "Shop",
41 NodeType::Vop => "Vop",
42 NodeType::Chop => "Chop",
43 _ => "Unknown",
44 })
45 }
46}
47
48#[derive(Debug, Copy, Clone)]
50#[non_exhaustive]
51pub enum ManagerType {
52 Obj,
53 Chop,
54 Cop,
55 Rop,
56 Top,
57}
58
59impl FromStr for ManagerType {
60 type Err = crate::HapiError;
61
62 fn from_str(val: &str) -> Result<Self> {
63 match val {
64 "Cop2" => Ok(Self::Cop),
65 "Chop" => Ok(Self::Chop),
66 "Top" => Ok(Self::Top),
67 "Object" => Ok(Self::Obj),
68 "Driver" => Ok(Self::Rop),
69 v => Err(crate::HapiError::internal(format!(
70 "Unknown ManagerType::{v}"
71 ))),
72 }
73 }
74}
75
76impl From<ManagerType> for NodeType {
77 fn from(value: ManagerType) -> Self {
78 use ManagerType::*;
79 match value {
80 Obj => NodeType::Obj,
81 Chop => NodeType::Chop,
82 Cop => NodeType::Cop,
83 Rop => NodeType::Rop,
84 Top => NodeType::Top,
85 }
86 }
87}
88
89fn get_child_node_list(
91 session: &Session,
92 parent: impl Into<NodeHandle>,
93 types: NodeType,
94 flags: NodeFlags,
95 recursive: bool,
96) -> Result<Vec<NodeHandle>> {
97 let ids =
98 crate::ffi::get_compose_child_node_list(session, parent.into(), types, flags, recursive)?;
99 Ok(ids.into_iter().map(NodeHandle).collect())
100}
101
102fn find_networks_nodes(
104 session: &Session,
105 types: NodeType,
106 parent: impl Into<NodeHandle>,
107 recursive: bool,
108) -> Result<Vec<HoudiniNode>> {
109 debug_assert!(session.is_valid());
110 get_child_node_list(session, parent, types, NodeFlags::Network, recursive).map(|vec| {
111 vec.into_iter()
112 .map(|handle| handle.to_node(session))
113 .collect::<Result<Vec<_>>>()
114 })?
115}
116
117#[derive(Debug, Clone)]
118pub struct ManagerNode {
120 pub session: Session,
121 pub handle: NodeHandle,
122 pub node_type: ManagerType,
123}
124
125impl ManagerNode {
126 pub fn find_network_nodes(&self, types: NodeType) -> Result<Vec<HoudiniNode>> {
128 find_networks_nodes(&self.session, types, self.handle, true)
129 }
130
131 pub fn get_children(&self) -> Result<Vec<NodeHandle>> {
133 get_child_node_list(
134 &self.session,
135 self.handle,
136 NodeType::from(self.node_type),
137 NodeFlags::Any,
138 false,
139 )
140 }
141}
142
143#[repr(transparent)]
144#[derive(Debug, Copy, Clone, Eq, PartialEq)]
145pub struct NodeHandle(pub(crate) crate::ffi::raw::HAPI_NodeId);
149
150impl From<NodeHandle> for crate::ffi::raw::HAPI_NodeId {
151 fn from(h: NodeHandle) -> Self {
152 h.0
153 }
154}
155
156impl AsRef<NodeHandle> for HoudiniNode {
157 fn as_ref(&self) -> &NodeHandle {
158 &self.handle
159 }
160}
161
162impl AsRef<NodeHandle> for NodeHandle {
163 fn as_ref(&self) -> &NodeHandle {
164 self
165 }
166}
167
168impl Default for NodeHandle {
169 fn default() -> Self {
170 NodeHandle(-1)
171 }
172}
173
174impl NodeHandle {
175 pub fn info(&self, session: &Session) -> Result<NodeInfo> {
177 NodeInfo::new(session, *self)
178 }
179
180 pub fn is_valid(&self, session: &Session) -> Result<bool> {
182 let info = self.info(session)?;
183 crate::ffi::is_node_valid(session, &info.0)
184 }
185
186 pub fn to_node(&self, session: &Session) -> Result<HoudiniNode> {
188 HoudiniNode::new(session.clone(), *self, None)
189 }
190
191 pub fn as_geometry_node(&self, session: &Session) -> Result<Option<Geometry>> {
193 let info = NodeInfo::new(session, *self)?;
194 match info.node_type() {
195 NodeType::Sop => Ok(Some(Geometry {
196 node: HoudiniNode::new(session.clone(), *self, Some(info))?,
197 info: GeoInfo::from_handle(*self, session)?,
198 })),
199 _ => Ok(None),
200 }
201 }
202
203 pub fn as_top_node(&self, session: &Session) -> Result<Option<TopNode>> {
205 let node = self.to_node(session)?;
206 match node.info.node_type() {
207 NodeType::Top => Ok(Some(TopNode { node })),
208 _ => Ok(None),
209 }
210 }
211}
212
213#[derive(Clone)]
214pub struct HoudiniNode {
216 pub handle: NodeHandle,
217 pub session: Session,
218 pub info: Arc<NodeInfo>,
219}
220
221impl PartialEq for HoudiniNode {
222 fn eq(&self, other: &Self) -> bool {
223 self.handle == other.handle && self.session == other.session
224 }
225}
226
227impl std::fmt::Debug for HoudiniNode {
228 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
229 f.debug_struct("HoudiniNode")
230 .field("id", &self.handle.0)
231 .field("type", &self.info.node_type())
232 .field(
233 "path",
234 &self.path().expect("[HoudiniNode::Debug] node path"),
235 )
236 .finish()
237 }
238}
239
240impl From<HoudiniNode> for NodeHandle {
241 fn from(n: HoudiniNode) -> Self {
242 n.handle
243 }
244}
245
246impl From<HoudiniNode> for Option<NodeHandle> {
247 fn from(n: HoudiniNode) -> Self {
248 Some(n.handle)
249 }
250}
251
252impl From<&HoudiniNode> for Option<NodeHandle> {
253 fn from(n: &HoudiniNode) -> Self {
254 Some(n.handle)
255 }
256}
257
258impl From<&HoudiniNode> for NodeHandle {
259 fn from(n: &HoudiniNode) -> Self {
260 n.handle
261 }
262}
263
264impl HoudiniNode {
265 pub(crate) fn new(
266 session: Session,
267 handle: NodeHandle,
268 info: Option<NodeInfo>,
269 ) -> Result<Self> {
270 let info = Arc::new(match info {
271 None => NodeInfo::new(&session, handle)?,
272 Some(i) => i,
273 });
274 Ok(HoudiniNode {
275 handle,
276 session,
277 info,
278 })
279 }
280
281 pub fn to_top_node(self) -> Option<TopNode> {
283 match self.info.node_type() {
284 NodeType::Top => Some(TopNode { node: self }),
285 _ => None,
286 }
287 }
288 pub fn delete(self) -> Result<()> {
290 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
291 crate::ffi::delete_node(self.handle, &self.session)
292 }
293
294 pub fn is_valid(&self) -> Result<bool> {
296 self.handle.is_valid(&self.session)
297 }
298
299 pub fn name(&self) -> Result<String> {
300 self.info.name()
301 }
302
303 pub fn path(&self) -> Result<String> {
305 debug_assert!(self.is_valid()?, "Invalid node: {}", self.name()?);
306 crate::ffi::get_node_path(&self.session, self.handle, None)
307 }
308
309 pub fn path_relative(&self, to: impl Into<Option<NodeHandle>>) -> Result<String> {
311 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
312 crate::ffi::get_node_path(&self.session, self.handle, to.into())
313 }
314
315 pub fn cook(&self) -> Result<()> {
317 debug!("Start cooking node: {}", self.path()?);
318 debug_assert!(self.is_valid()?);
319 crate::ffi::cook_node(self, &CookOptions::default())
320 }
321
322 pub fn cook_blocking(&self) -> Result<CookResult> {
326 debug!("Start cooking node: {}", self.path()?);
327 debug_assert!(self.is_valid()?);
328 crate::ffi::cook_node(self, &CookOptions::default())?;
329 self.session.cook()
330 }
331
332 pub fn cook_with_options(&self, options: &CookOptions, blocking: bool) -> Result<CookResult> {
334 debug!("Start cooking node: {}", self.path()?);
335 debug_assert!(self.is_valid()?);
336 crate::ffi::cook_node(self, options)?;
337 if blocking {
338 self.session.cook()
339 } else {
340 Ok(CookResult::Succeeded)
341 }
342 }
343
344 pub fn cook_count(
346 &self,
347 node_types: NodeType,
348 node_flags: NodeFlags,
349 recurse: bool,
350 ) -> Result<i32> {
351 debug_assert!(self.is_valid()?);
352 crate::ffi::get_total_cook_count(self, node_types, node_flags, recurse)
353 }
354
355 pub fn get_object_info(&self) -> Result<ObjectInfo<'_>> {
357 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
358 crate::ffi::get_object_info(&self.session, self.handle)
359 .map(|info| ObjectInfo(info, (&self.session).into()))
360 }
361
362 pub fn get_info(&self) -> Result<NodeInfo> {
364 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
365 self.handle.info(&self.session)
366 }
367
368 pub fn get_objects_info(&self) -> Result<Vec<ObjectInfo>> {
370 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
371 let parent = match self.info.node_type() {
372 NodeType::Obj => self.info.parent_id(),
373 _ => self.handle,
374 };
375 let infos = crate::ffi::get_composed_object_list(&self.session, parent)?;
376 Ok(infos
377 .into_iter()
378 .map(|inner| ObjectInfo(inner, (&self.session).into()))
379 .collect())
380 }
381
382 pub fn find_children_by_type(
384 &self,
385 types: NodeType,
386 flags: NodeFlags,
387 recursive: bool,
388 ) -> Result<Vec<NodeHandle>> {
389 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
390 get_child_node_list(&self.session, self, types, flags, recursive)
391 }
392
393 pub fn get_children(&self) -> Result<Vec<NodeHandle>> {
395 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
396 get_child_node_list(&self.session, self, NodeType::Any, NodeFlags::Any, false)
397 }
398
399 pub fn get_child_by_path(&self, relative_path: &str) -> Result<Option<HoudiniNode>> {
401 self.session
402 .get_node_from_path(relative_path, Some(self.handle))
403 }
404
405 pub fn get_instanced_object_ids(&self) -> Result<Vec<NodeHandle>> {
407 crate::ffi::get_instanced_object_ids(self)
408 }
409
410 pub fn find_child_node(
412 &self,
413 name: impl AsRef<str>,
414 recursive: bool,
415 ) -> Result<Option<HoudiniNode>> {
416 debug_assert!(self.is_valid()?);
417 if !recursive {
418 return self.get_child_by_path(name.as_ref());
419 }
420 for handle in self.find_children_by_type(NodeType::Any, NodeFlags::Any, recursive)? {
421 let info = handle.info(&self.session)?;
422 if info.name()? == name.as_ref() {
423 return Ok(Some(HoudiniNode::new(
424 self.session.clone(),
425 handle,
426 Some(info),
427 )?));
428 }
429 }
430 Ok(None)
431 }
432
433 pub fn get_sop_output_node(&self, index: i32) -> Result<NodeHandle> {
435 debug_assert!(self.is_valid()?);
436 crate::ffi::get_sop_output_node(&self.session, self.handle, index)
437 }
438
439 pub fn parent_node(&self) -> Option<NodeHandle> {
441 let handle = self.info.parent_id();
442 (handle.0 > -1).then_some(handle)
443 }
444
445 pub fn parameter(&self, name: &str) -> Result<Parameter> {
447 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
448 let parm_info = ParmInfo::from_parm_name(name, self)?;
449 Ok(Parameter::new(self.handle, parm_info))
450 }
451
452 pub fn parameter_with_tag(&self, tag: &str) -> Result<Option<Parameter>> {
454 let tag = CString::new(tag)?;
455 match crate::ffi::get_parm_with_tag(self, &tag)? {
456 -1 => Ok(None),
457 h => {
458 let parm_info =
459 ParmInfo::from_parm_handle(ParmHandle(h), self.handle, &self.session)?;
460 Ok(Some(Parameter::new(self.handle, parm_info)))
461 }
462 }
463 }
464
465 pub fn parameters(&self) -> Result<Vec<Parameter>> {
467 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
468 let infos = crate::ffi::get_parameters(self)?;
469 Ok(infos
470 .into_iter()
471 .map(|info| {
472 Parameter::new(self.handle, ParmInfo::new(info, self.session.clone(), None))
473 })
474 .collect())
475 }
476
477 pub fn asset_info(&self) -> Result<AssetInfo> {
479 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
480 Ok(AssetInfo(
481 crate::ffi::get_asset_info(self)?,
482 self.session.clone().into(),
483 ))
484 }
485 pub fn check_for_specific_error(&self, error_bits: i32) -> Result<ErrorCode> {
487 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
488 crate::ffi::check_for_specific_errors(self, error_bits)
489 }
490
491 pub fn get_composed_cook_result_string(&self, verbosity: StatusVerbosity) -> Result<String> {
493 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
494 unsafe { crate::ffi::get_composed_cook_result(self, verbosity) }
495 }
496
497 pub fn get_cook_result_string(&self, verbosity: StatusVerbosity) -> Result<String> {
499 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
500 let bytes = crate::ffi::get_node_cook_result(self, verbosity)?;
501 Ok(String::from_utf8_lossy(&bytes).to_string())
502 }
503 pub fn reset_simulation(&self) -> Result<()> {
505 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
506 crate::ffi::reset_simulation(self)
507 }
508
509 pub fn input_node(&self, idx: i32) -> Result<Option<HoudiniNode>> {
511 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
512 crate::ffi::query_node_input(self, idx).map(|idx| {
513 if idx == -1 {
514 None
515 } else {
516 HoudiniNode::new(self.session.clone(), NodeHandle(idx), None).ok()
517 }
518 })
519 }
520
521 pub fn rename(&self, new_name: impl AsRef<str>) -> Result<()> {
523 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
524 let name = CString::new(new_name.as_ref())?;
525 crate::ffi::rename_node(self, &name)
526 }
527
528 pub fn save_to_file(&self, file: impl AsRef<Path>) -> Result<()> {
530 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
531 let filename = CString::new(file.as_ref().to_string_lossy().to_string())?;
532 crate::ffi::save_node_to_file(self.handle, &self.session, &filename)
533 }
534
535 pub fn load_from_file(
537 session: &Session,
538 parent: impl Into<Option<NodeHandle>>,
539 label: &str,
540 cook: bool,
541 file: impl AsRef<OsStr>,
542 ) -> Result<HoudiniNode> {
543 debug_assert!(session.is_valid());
544 debug!("Loading node from file {:?}", file.as_ref());
545 let filename = CString::new(file.as_ref().to_string_lossy().to_string())?;
546 let label = CString::new(label)?;
547 let id = crate::ffi::load_node_from_file(parent.into(), session, &label, &filename, cook)?;
548 NodeHandle(id).to_node(session)
549 }
550
551 pub fn get_preset(&self, name: &str, preset_type: PresetType) -> Result<Vec<i8>> {
553 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
554 let name = CString::new(name)?;
555 crate::ffi::get_preset(&self.session, self.handle, &name, preset_type)
556 }
557
558 pub fn set_preset(&self, name: &str, preset_type: PresetType, data: &[i8]) -> Result<()> {
560 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
561 let name = CString::new(name)?;
562 crate::ffi::set_preset(&self.session, self.handle, &name, preset_type, data)
563 }
564
565 pub fn geometry(&self) -> Result<Option<Geometry>> {
568 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
569 match self.info.node_type() {
570 NodeType::Sop => Ok(Some(Geometry {
571 node: self.clone(),
572 info: GeoInfo::from_node(self)?,
573 })),
574 NodeType::Obj => {
575 let info = crate::ffi::get_geo_display_info(self).map(GeoInfo)?;
576 Ok(Some(Geometry {
577 node: info.node_id().to_node(&self.session)?,
578 info,
579 }))
580 }
581 _ => Ok(None),
582 }
583 }
584
585 pub fn find_top_networks(&self) -> Result<Vec<HoudiniNode>> {
587 find_networks_nodes(&self.session, NodeType::Top, self, true)
588 }
589
590 pub fn number_of_geo_outputs(&self) -> Result<i32> {
592 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
593 crate::ffi::get_output_geo_count(self)
594 }
595
596 pub fn get_output_names(&self) -> Result<Vec<String>> {
598 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
599 crate::ffi::get_output_names(self)
600 }
601
602 pub fn geometry_output_nodes(&self) -> Result<Vec<Geometry>> {
604 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
605 crate::ffi::get_output_geos(self).map(|vec| {
606 vec.into_iter()
607 .map(|inner| {
608 NodeHandle(inner.nodeId)
609 .to_node(&self.session)
610 .map(|node| Geometry {
611 node,
612 info: GeoInfo(inner),
613 })
614 })
615 .collect::<Result<Vec<_>>>()
616 })?
617 }
618
619 pub fn get_transform(
621 &self,
622 rst_order: Option<RSTOrder>,
623 relative_to: impl Into<Option<NodeHandle>>,
624 ) -> Result<Transform> {
625 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
626 crate::ffi::get_object_transform(
627 &self.session,
628 self.handle,
629 relative_to.into(),
630 rst_order.unwrap_or(RSTOrder::Default),
631 )
632 .map(Transform)
633 }
634
635 pub fn set_transform(&self, transform: &TransformEuler) -> Result<()> {
637 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
638 crate::ffi::set_object_transform(&self.session, self.handle, &transform.0)
639 }
640
641 pub fn set_transform_anim_curve(
643 &self,
644 component: TransformComponent,
645 keys: &[KeyFrame],
646 ) -> Result<()> {
647 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
648 let keys =
649 unsafe { std::mem::transmute::<&[KeyFrame], &[crate::ffi::raw::HAPI_Keyframe]>(keys) };
650 crate::ffi::set_transform_anim_curve(&self.session, self.handle, component, keys)
651 }
652
653 pub fn connect_input<H: Into<NodeHandle>>(
655 &self,
656 input_num: i32,
657 source: H,
658 output_num: i32,
659 ) -> Result<()> {
660 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
661 crate::ffi::connect_node_input(
662 &self.session,
663 self.handle,
664 input_num,
665 source.into(),
666 output_num,
667 )
668 }
669
670 pub fn output_connected_nodes(
672 &self,
673 output_index: i32,
674 search_subnets: bool,
675 ) -> Result<Vec<NodeHandle>> {
676 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
677 crate::ffi::query_node_output_connected_nodes(self, output_index, search_subnets)
678 }
679
680 pub fn disconnect_input(&self, input_index: i32) -> Result<()> {
682 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
683 crate::ffi::disconnect_node_input(self, input_index)
684 }
685
686 pub fn disconnect_outputs(&self, output_index: i32) -> Result<()> {
688 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
689 crate::ffi::disconnect_node_outputs(self, output_index)
690 }
691
692 pub fn set_display_flag(&self, on: bool) -> Result<()> {
694 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
695 crate::ffi::set_node_display(&self.session, self.handle, on)
696 }
697
698 pub fn get_input_name(&self, input_index: i32) -> Result<String> {
700 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
701 crate::ffi::get_node_input_name(self, input_index)
702 }
703
704 pub fn get_message_nodes(&self) -> Result<Vec<NodeHandle>> {
706 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
707 crate::ffi::get_message_node_ids(self)
708 }
709}