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, Hash)]
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 path(&self, session: &Session) -> Result<String> {
182 debug_assert!(self.is_valid(session)?, "Invalid {:?}", self);
183 crate::ffi::get_node_path(session, *self, None)
184 }
185
186 pub fn path_relative(
188 &self,
189 session: &Session,
190 to: impl Into<Option<NodeHandle>>,
191 ) -> Result<String> {
192 debug_assert!(self.is_valid(session)?, "Invalid {:?}", self);
193 crate::ffi::get_node_path(session, *self, to.into())
194 }
195
196 pub fn is_valid(&self, session: &Session) -> Result<bool> {
198 let info = self.info(session)?;
199 crate::ffi::is_node_valid(session, &info.0)
200 }
201
202 pub fn to_node(&self, session: &Session) -> Result<HoudiniNode> {
204 HoudiniNode::new(session.clone(), *self, None)
205 }
206
207 pub fn as_geometry_node(&self, session: &Session) -> Result<Option<Geometry>> {
209 let info = NodeInfo::new(session, *self)?;
210 match info.node_type() {
211 NodeType::Sop => Ok(Some(Geometry {
212 node: HoudiniNode::new(session.clone(), *self, Some(info))?,
213 info: GeoInfo::from_handle(*self, session)?,
214 })),
215 _ => Ok(None),
216 }
217 }
218
219 pub fn as_top_node(&self, session: &Session) -> Result<Option<TopNode>> {
221 let node = self.to_node(session)?;
222 match node.info.node_type() {
223 NodeType::Top => Ok(Some(TopNode { node })),
224 _ => Ok(None),
225 }
226 }
227}
228
229#[derive(Clone)]
230pub struct HoudiniNode {
232 pub handle: NodeHandle,
233 pub session: Session,
234 pub info: Arc<NodeInfo>,
235}
236
237impl PartialEq for HoudiniNode {
238 fn eq(&self, other: &Self) -> bool {
239 self.handle == other.handle && self.session == other.session
240 }
241}
242
243impl std::fmt::Debug for HoudiniNode {
244 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
245 f.debug_struct("HoudiniNode")
246 .field("id", &self.handle.0)
247 .field("type", &self.info.node_type())
248 .field(
249 "path",
250 &self.path().expect("[HoudiniNode::Debug] node path"),
251 )
252 .finish()
253 }
254}
255
256impl From<HoudiniNode> for NodeHandle {
257 fn from(n: HoudiniNode) -> Self {
258 n.handle
259 }
260}
261
262impl From<HoudiniNode> for Option<NodeHandle> {
263 fn from(n: HoudiniNode) -> Self {
264 Some(n.handle)
265 }
266}
267
268impl From<&HoudiniNode> for Option<NodeHandle> {
269 fn from(n: &HoudiniNode) -> Self {
270 Some(n.handle)
271 }
272}
273
274impl From<&HoudiniNode> for NodeHandle {
275 fn from(n: &HoudiniNode) -> Self {
276 n.handle
277 }
278}
279
280impl HoudiniNode {
281 pub(crate) fn new(
282 session: Session,
283 handle: NodeHandle,
284 info: Option<NodeInfo>,
285 ) -> Result<Self> {
286 let info = Arc::new(match info {
287 None => NodeInfo::new(&session, handle)?,
288 Some(i) => i,
289 });
290 Ok(HoudiniNode {
291 handle,
292 session,
293 info,
294 })
295 }
296
297 pub fn to_top_node(self) -> Option<TopNode> {
299 match self.info.node_type() {
300 NodeType::Top => Some(TopNode { node: self }),
301 _ => None,
302 }
303 }
304 pub fn delete(self) -> Result<()> {
306 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
307 crate::ffi::delete_node(self.handle, &self.session)
308 }
309
310 pub fn is_valid(&self) -> Result<bool> {
312 self.handle.is_valid(&self.session)
313 }
314
315 pub fn name(&self) -> Result<String> {
316 self.info.name()
317 }
318
319 pub fn path(&self) -> Result<String> {
321 self.handle.path(&self.session)
322 }
323
324 pub fn path_relative(&self, to: impl Into<Option<NodeHandle>>) -> Result<String> {
326 self.handle.path_relative(&self.session, to)
327 }
328
329 pub fn cook(&self) -> Result<()> {
331 debug!("Start cooking node: {}", self.path()?);
332 debug_assert!(self.is_valid()?);
333 crate::ffi::cook_node(self, &CookOptions::default())
334 }
335
336 pub fn cook_blocking(&self) -> Result<CookResult> {
340 debug!("Start cooking node: {}", self.path()?);
341 debug_assert!(self.is_valid()?);
342 crate::ffi::cook_node(self, &CookOptions::default())?;
343 self.session.cook()
344 }
345
346 pub fn cook_with_options(&self, options: &CookOptions, blocking: bool) -> Result<CookResult> {
348 debug!("Start cooking node: {}", self.path()?);
349 debug_assert!(self.is_valid()?);
350 crate::ffi::cook_node(self, options)?;
351 if blocking {
352 self.session.cook()
353 } else {
354 Ok(CookResult::Succeeded)
355 }
356 }
357
358 pub fn cook_count(
360 &self,
361 node_types: NodeType,
362 node_flags: NodeFlags,
363 recurse: bool,
364 ) -> Result<i32> {
365 debug_assert!(self.is_valid()?);
366 crate::ffi::get_total_cook_count(self, node_types, node_flags, recurse)
367 }
368
369 pub fn get_object_info(&self) -> Result<ObjectInfo<'_>> {
371 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
372 crate::ffi::get_object_info(&self.session, self.handle)
373 .map(|info| ObjectInfo(info, (&self.session).into()))
374 }
375
376 pub fn get_info(&self) -> Result<NodeInfo> {
378 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
379 self.handle.info(&self.session)
380 }
381
382 pub fn get_objects_info(&self) -> Result<Vec<ObjectInfo<'_>>> {
384 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
385 let parent = match self.info.node_type() {
386 NodeType::Obj => self.info.parent_id(),
387 _ => self.handle,
388 };
389 let infos = crate::ffi::get_composed_object_list(&self.session, parent)?;
390 Ok(infos
391 .into_iter()
392 .map(|inner| ObjectInfo(inner, (&self.session).into()))
393 .collect())
394 }
395
396 pub fn find_children_by_type(
398 &self,
399 types: NodeType,
400 flags: NodeFlags,
401 recursive: bool,
402 ) -> Result<Vec<NodeHandle>> {
403 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
404 get_child_node_list(&self.session, self, types, flags, recursive)
405 }
406
407 pub fn get_children(&self) -> Result<Vec<NodeHandle>> {
409 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
410 get_child_node_list(&self.session, self, NodeType::Any, NodeFlags::Any, false)
411 }
412
413 pub fn get_child_by_path(&self, relative_path: &str) -> Result<Option<HoudiniNode>> {
415 self.session
416 .get_node_from_path(relative_path, Some(self.handle))
417 }
418
419 pub fn get_instanced_object_ids(&self) -> Result<Vec<NodeHandle>> {
421 crate::ffi::get_instanced_object_ids(self)
422 }
423
424 pub fn find_child_node(
426 &self,
427 name: impl AsRef<str>,
428 recursive: bool,
429 ) -> Result<Option<HoudiniNode>> {
430 debug_assert!(self.is_valid()?);
431 if !recursive {
432 return self.get_child_by_path(name.as_ref());
433 }
434 for handle in self.find_children_by_type(NodeType::Any, NodeFlags::Any, recursive)? {
435 let info = handle.info(&self.session)?;
436 if info.name()? == name.as_ref() {
437 return Ok(Some(HoudiniNode::new(
438 self.session.clone(),
439 handle,
440 Some(info),
441 )?));
442 }
443 }
444 Ok(None)
445 }
446
447 pub fn get_sop_output_node(&self, index: i32) -> Result<NodeHandle> {
449 debug_assert!(self.is_valid()?);
450 crate::ffi::get_sop_output_node(&self.session, self.handle, index)
451 }
452
453 pub fn parent_node(&self) -> Option<NodeHandle> {
455 let handle = self.info.parent_id();
456 (handle.0 > -1).then_some(handle)
457 }
458
459 pub fn parameter(&self, name: &str) -> Result<Parameter> {
461 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
462 let parm_info = ParmInfo::from_parm_name(name, self)?;
463 Ok(Parameter::new(self.handle, parm_info))
464 }
465
466 pub fn parameter_with_tag(&self, tag: &str) -> Result<Option<Parameter>> {
468 let tag = CString::new(tag)?;
469 match crate::ffi::get_parm_with_tag(self, &tag)? {
470 -1 => Ok(None),
471 h => {
472 let parm_info =
473 ParmInfo::from_parm_handle(ParmHandle(h), self.handle, &self.session)?;
474 Ok(Some(Parameter::new(self.handle, parm_info)))
475 }
476 }
477 }
478
479 pub fn parameters(&self) -> Result<Vec<Parameter>> {
481 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
482 let infos = crate::ffi::get_parameters(self)?;
483 Ok(infos
484 .into_iter()
485 .map(|info| {
486 Parameter::new(self.handle, ParmInfo::new(info, self.session.clone(), None))
487 })
488 .collect())
489 }
490
491 pub fn asset_info(&self) -> Result<AssetInfo> {
493 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
494 Ok(AssetInfo(
495 crate::ffi::get_asset_info(self)?,
496 self.session.clone().into(),
497 ))
498 }
499 pub fn check_for_specific_error(&self, error_bits: i32) -> Result<ErrorCode> {
501 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
502 crate::ffi::check_for_specific_errors(self, error_bits)
503 }
504
505 pub fn get_composed_cook_result_string(&self, verbosity: StatusVerbosity) -> Result<String> {
507 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
508 unsafe { crate::ffi::get_composed_cook_result(self, verbosity) }
509 }
510
511 pub fn get_cook_result_string(&self, verbosity: StatusVerbosity) -> Result<String> {
513 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
514 let bytes = crate::ffi::get_node_cook_result(self, verbosity)?;
515 Ok(String::from_utf8_lossy(&bytes).to_string())
516 }
517 pub fn reset_simulation(&self) -> Result<()> {
519 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
520 crate::ffi::reset_simulation(self)
521 }
522
523 pub fn input_node(&self, idx: i32) -> Result<Option<HoudiniNode>> {
525 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
526 crate::ffi::query_node_input(self, idx).map(|idx| {
527 if idx == -1 {
528 None
529 } else {
530 HoudiniNode::new(self.session.clone(), NodeHandle(idx), None).ok()
531 }
532 })
533 }
534
535 pub fn rename(&self, new_name: impl AsRef<str>) -> Result<()> {
537 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
538 let name = CString::new(new_name.as_ref())?;
539 crate::ffi::rename_node(self, &name)
540 }
541
542 pub fn save_to_file(&self, file: impl AsRef<Path>) -> Result<()> {
544 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
545 let filename = CString::new(file.as_ref().to_string_lossy().to_string())?;
546 crate::ffi::save_node_to_file(self.handle, &self.session, &filename)
547 }
548
549 pub fn load_from_file(
551 session: &Session,
552 parent: impl Into<Option<NodeHandle>>,
553 label: &str,
554 cook: bool,
555 file: impl AsRef<OsStr>,
556 ) -> Result<HoudiniNode> {
557 debug_assert!(session.is_valid());
558 debug!("Loading node from file {:?}", file.as_ref());
559 let filename = CString::new(file.as_ref().to_string_lossy().to_string())?;
560 let label = CString::new(label)?;
561 let id = crate::ffi::load_node_from_file(parent.into(), session, &label, &filename, cook)?;
562 NodeHandle(id).to_node(session)
563 }
564
565 pub fn get_preset(&self, name: &str, preset_type: PresetType) -> Result<Vec<i8>> {
567 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
568 let name = CString::new(name)?;
569 crate::ffi::get_preset(&self.session, self.handle, &name, preset_type)
570 }
571
572 pub fn set_preset(&self, name: &str, preset_type: PresetType, data: &[i8]) -> Result<()> {
574 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
575 let name = CString::new(name)?;
576 crate::ffi::set_preset(&self.session, self.handle, &name, preset_type, data)
577 }
578
579 pub fn geometry(&self) -> Result<Option<Geometry>> {
582 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
583 match self.info.node_type() {
584 NodeType::Sop => Ok(Some(Geometry {
585 node: self.clone(),
586 info: GeoInfo::from_node(self)?,
587 })),
588 NodeType::Obj => {
589 let info = crate::ffi::get_geo_display_info(self).map(GeoInfo)?;
590 Ok(Some(Geometry {
591 node: info.node_id().to_node(&self.session)?,
592 info,
593 }))
594 }
595 _ => Ok(None),
596 }
597 }
598
599 pub fn find_top_networks(&self) -> Result<Vec<HoudiniNode>> {
601 find_networks_nodes(&self.session, NodeType::Top, self, true)
602 }
603
604 pub fn number_of_geo_outputs(&self) -> Result<i32> {
606 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
607 crate::ffi::get_output_geo_count(self)
608 }
609
610 pub fn get_output_names(&self) -> Result<Vec<String>> {
612 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
613 crate::ffi::get_output_names(self)
614 }
615
616 pub fn geometry_output_nodes(&self) -> Result<Vec<Geometry>> {
618 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
619 crate::ffi::get_output_geos(self).map(|vec| {
620 vec.into_iter()
621 .map(|inner| {
622 NodeHandle(inner.nodeId)
623 .to_node(&self.session)
624 .map(|node| Geometry {
625 node,
626 info: GeoInfo(inner),
627 })
628 })
629 .collect::<Result<Vec<_>>>()
630 })?
631 }
632
633 pub fn get_transform(
635 &self,
636 rst_order: Option<RSTOrder>,
637 relative_to: impl Into<Option<NodeHandle>>,
638 ) -> Result<Transform> {
639 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
640 crate::ffi::get_object_transform(
641 &self.session,
642 self.handle,
643 relative_to.into(),
644 rst_order.unwrap_or(RSTOrder::Default),
645 )
646 .map(Transform)
647 }
648
649 pub fn set_transform(&self, transform: &TransformEuler) -> Result<()> {
651 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
652 crate::ffi::set_object_transform(&self.session, self.handle, &transform.0)
653 }
654
655 pub fn set_transform_anim_curve(
657 &self,
658 component: TransformComponent,
659 keys: &[KeyFrame],
660 ) -> Result<()> {
661 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
662 let keys =
663 unsafe { std::mem::transmute::<&[KeyFrame], &[crate::ffi::raw::HAPI_Keyframe]>(keys) };
664 crate::ffi::set_transform_anim_curve(&self.session, self.handle, component, keys)
665 }
666
667 pub fn connect_input<H: Into<NodeHandle>>(
669 &self,
670 input_num: i32,
671 source: H,
672 output_num: i32,
673 ) -> Result<()> {
674 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
675 crate::ffi::connect_node_input(
676 &self.session,
677 self.handle,
678 input_num,
679 source.into(),
680 output_num,
681 )
682 }
683
684 pub fn output_connected_nodes(
686 &self,
687 output_index: i32,
688 search_subnets: bool,
689 ) -> Result<Vec<NodeHandle>> {
690 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
691 crate::ffi::query_node_output_connected_nodes(self, output_index, search_subnets)
692 }
693
694 pub fn disconnect_input(&self, input_index: i32) -> Result<()> {
696 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
697 crate::ffi::disconnect_node_input(self, input_index)
698 }
699
700 pub fn disconnect_outputs(&self, output_index: i32) -> Result<()> {
702 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
703 crate::ffi::disconnect_node_outputs(self, output_index)
704 }
705
706 pub fn set_display_flag(&self, on: bool) -> Result<()> {
708 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
709 crate::ffi::set_node_display(&self.session, self.handle, on)
710 }
711
712 pub fn get_input_name(&self, input_index: i32) -> Result<String> {
714 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
715 crate::ffi::get_node_input_name(self, input_index)
716 }
717
718 pub fn get_message_nodes(&self) -> Result<Vec<NodeHandle>> {
720 debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
721 crate::ffi::get_message_node_ids(self)
722 }
723}