Skip to main content

plasma_prp/core/
scene_node.rs

1//! plSceneNode — groups scene objects and drawables per page.
2//!
3//! Each page has one plSceneNode that holds references to all
4//! scene objects and drawables in that page.
5//!
6//! C++ ref: plScene/plSceneNode.h/.cpp
7
8use std::io::Read;
9
10use anyhow::Result;
11
12use crate::resource::prp::PlasmaRead;
13
14use super::uoid::{Uoid, read_key_uoid};
15
16/// Parsed plSceneNode data.
17///
18/// Note: plSceneNode inherits from hsKeyedObject (NOT plSynchedObject).
19#[derive(Debug, Clone)]
20pub struct SceneNodeData {
21    pub self_key: Option<Uoid>,
22    pub scene_objects: Vec<Option<Uoid>>,
23    pub pool_objects: Vec<Option<Uoid>>,
24}
25
26impl SceneNodeData {
27    /// Read a plSceneNode from a stream (after creatable class index).
28    pub fn read(reader: &mut impl Read) -> Result<Self> {
29        // hsKeyedObject::Read — just the self-key (no plSynchedObject!)
30        let self_key = read_key_uoid(reader)?;
31
32        // Scene objects
33        let num_scene_objects = reader.read_u32()?;
34        let mut scene_objects = Vec::with_capacity(num_scene_objects as usize);
35        for _ in 0..num_scene_objects {
36            scene_objects.push(read_key_uoid(reader)?);
37        }
38
39        // Pool (generic) objects
40        let num_pool = reader.read_u32()?;
41        let mut pool_objects = Vec::with_capacity(num_pool as usize);
42        for _ in 0..num_pool {
43            pool_objects.push(read_key_uoid(reader)?);
44        }
45
46        Ok(Self {
47            self_key,
48            scene_objects,
49            pool_objects,
50        })
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use crate::core::class_index::ClassIndex;
58    use crate::resource::prp::PrpPage;
59    use std::io::Cursor;
60    use std::path::Path;
61
62    #[test]
63    fn test_parse_cleft_scene_node() {
64        let path = Path::new("../../Plasma/staging/client/dat/Cleft_District_Cleft.prp");
65        if !path.exists() {
66            eprintln!("Skipping test: {:?} not found", path);
67            return;
68        }
69
70        let page = PrpPage::from_file(path).unwrap();
71        let node_keys: Vec<_> = page.keys_of_type(ClassIndex::PL_SCENE_NODE);
72
73        assert!(!node_keys.is_empty(), "Should have at least one scene node");
74
75        for key in &node_keys {
76            if let Some(data) = page.object_data(key) {
77                let mut cursor = Cursor::new(data);
78                let _ = cursor.read_i16().unwrap();
79
80                let node = SceneNodeData::read(&mut cursor).unwrap();
81                eprintln!(
82                    "SceneNode '{}': {} scene objects, {} pool objects",
83                    key.object_name,
84                    node.scene_objects.len(),
85                    node.pool_objects.len()
86                );
87                assert!(
88                    !node.scene_objects.is_empty(),
89                    "Cleft scene node should have scene objects"
90                );
91            }
92        }
93    }
94}