1use std::collections::HashMap;
4use std::io::{Seek, Read};
5
6use glam::{Affine3A, Vec3A, Vec4};
7use smallvec::SmallVec;
8use thiserror::Error;
9
10use crate::pxml::{self, Value, Element};
11
12
13pub fn from_reader<R: Read + Seek>(reader: R) -> Result<Box<Visual>, DeError> {
18
19    let root_elt = pxml::from_reader(reader)?;
20    
21    let root_node_value = root_elt.get_child("node").ok_or(DeError::MissingRootNode)?;
22    let root_node_elt = root_node_value.as_element().ok_or(DeError::MissingRootNode)?;
23    let root_node = read_node(root_node_elt).ok_or(DeError::InvalidNode)?;
24
25    let bb_elt = root_elt
26        .get_child("boundingBox").ok_or(DeError::MissingBoundingBox)?
27        .as_element().ok_or(DeError::MissingBoundingBox)?;
28
29    let bb_min = bb_elt
30        .get_child("min").ok_or(DeError::MissingBoundingBox)?
31        .as_vec3().ok_or(DeError::MissingBoundingBox)?;
32
33    let bb_max = bb_elt
34        .get_child("max").ok_or(DeError::MissingBoundingBox)?
35        .as_vec3().ok_or(DeError::MissingBoundingBox)?;
36
37    let geometry_size = root_elt
38        .get_child("geometrySize").ok_or(DeError::MissingGeometrySize)?
39        .as_integer().ok_or(DeError::MissingGeometrySize)? as u32;
40
41    let min_uv_density = root_elt
42        .get_child("minUVDensity").ok_or(DeError::MissingUvDensity)?
43        .as_float().ok_or(DeError::MissingUvDensity)?;
44
45    let mut render_sets = SmallVec::new();
46    for child in root_elt.iter_children("renderSet") {
47        if let Value::Element(child_elt) = child {
48            render_sets.push(read_render_set(&**&child_elt).ok_or(DeError::InvalidRenderSet)?);
49        }
50    }
51
52    Ok(Box::new(Visual {
53        root_node,
54        render_sets,
55        bb_min,
56        bb_max,
57        geometry_size,
58        min_uv_density,
59    }))
60
61}
62
63
64fn read_node(element: &Element) -> Option<Node> {
65    
66    let identifier = element.get_child("identifier")?.as_string()?;
67    let transform = element.get_child("transform")?.as_affine3()?;
68    
69    let mut children = Vec::new();
70    for child in element.iter_children("node") {
71        if let Value::Element(child_elt) = child {
72            children.push(read_node(&**child_elt)?);
73        }
74    }
75
76    Some(Node {
77        identifier: identifier.clone(),
78        transform,
79        children,
80    })
81    
82}
83
84
85fn read_render_set(element: &Element) -> Option<RenderSet> {
86
87    let node = element.get_child("node")?.as_string()?;
88    let treat_as_world_space_object = element.get_child("treatAsWorldSpaceObject")?.as_boolean()?;
89
90    let geometry_elt = element.get_child("geometry")?.as_element()?;
91    let geometry_vertices = geometry_elt.get_child("vertices")?.as_string()?;
92    let geometry_indices = geometry_elt.get_child("primitive")?.as_string()?;
93
94    let mut primitive_groups = SmallVec::new();
95    for group_val in geometry_elt.iter_children("primitiveGroup") {
96        if let Value::Element(group_elt) = group_val {
97            
98            let group_index = group_elt.value.as_integer()? as u32;
99            let group_origin = group_elt.get_child("groupOrigin")?.as_vec3()?;
100
101            let mat_elt = group_elt.get_child("material")?.as_element()?;
102            let mat_identifier = mat_elt.get_child("identifier")?.as_string()?;
103            let mat_collision_flags = mat_elt.get_child("collisionFlags")?.as_integer()? as u32;
104            let mat_kind = mat_elt.get_child("materialKind")?.as_integer()? as u32;
105            let mat_fx = mat_elt.get_child("fx")?.as_string()?;
106
107            let mut mat_properties = HashMap::new();
108            for prop_val in mat_elt.iter_children("property") {
109                if let Value::Element(prop_elt) = prop_val {
110
111                    let prop_name = prop_elt.value.as_string()?;
112                    let prop_value = if let Some(val) = prop_elt.get_child("Texture") {
113                        MaterialProperty::Texture(val.as_string()?.clone())
114                    } else if let Some(val) = prop_elt.get_child("Bool") {
115                        MaterialProperty::Boolean(val.as_boolean()?)
116                    } else if let Some(val) = prop_elt.get_child("Int") {
117                        MaterialProperty::Integer(val.as_integer()?)
118                    } else if let Some(val) = prop_elt.get_child("Float") {
119                        let n = match val {
122                            &Value::Integer(n) => n as f32,
123                            &Value::Float(n) => n,
124                            Value::String(s) => s.parse().ok()?,
125                            _ => return None
126                        };
127                        MaterialProperty::Float(n)
128                    } else if let Some(val) = prop_elt.get_child("Vector4") {
129                        let mut raw = val.as_string()?
131                            .splitn(4, ' ')
132                            .filter_map(|s| s.parse::<f32>().ok());
133                        let x = raw.next()?;
134                        let y = raw.next()?;
135                        let z = raw.next()?;
136                        let w = raw.next()?;
137                        MaterialProperty::Vec4(Vec4::new(x, y, z, w))
138                    } else {
139                        return None;
140                    };
141
142                    mat_properties.insert(prop_name.clone(), prop_value);
143                    
144                }
145            }
146
147            primitive_groups.push(PrimitiveGroup {
148                index: group_index,
149                origin: group_origin,
150                material: Material { 
151                    identifier: mat_identifier.clone(), 
152                    properties: mat_properties,
153                    collision_flags: mat_collision_flags,
154                    material_kind: mat_kind,
155                    fx: mat_fx.clone(),
156                },
157            })
158
159        }
160    }
161
162    Some(RenderSet { 
163        node: node.clone(), 
164        geometry: Geometry { 
165            vertices_section: geometry_vertices.clone(), 
166            indices_section: geometry_indices.clone(), 
167            primitive_groups,
168        }, 
169        treat_as_world_space_object,
170    })
171
172}
173
174
175#[derive(Debug)]
177pub struct Visual {
178    pub root_node: Node,
180    pub render_sets: SmallVec<[RenderSet; 4]>,
182    pub bb_min: Vec3A,
184    pub bb_max: Vec3A,
186    pub geometry_size: u32,
188    pub min_uv_density: f32,
190}
191
192#[derive(Debug)]
194pub struct Node {
195    pub identifier: String,
197    pub transform: Affine3A,
199    pub children: Vec<Node>,
201}
202
203#[derive(Debug)]
205pub struct RenderSet {
206    pub node: String,
208    pub geometry: Geometry,
210    pub treat_as_world_space_object: bool,
212}
213
214#[derive(Debug)]
216pub struct Geometry {
217    pub vertices_section: String,
219    pub indices_section: String,
221    pub primitive_groups: SmallVec<[PrimitiveGroup; 1]>,
223}
224
225#[derive(Debug)]
227pub struct PrimitiveGroup {
228    pub index: u32,
230    pub origin: Vec3A,
232    pub material: Material,
234}
235
236#[derive(Debug)]
237pub struct Material {
238    pub identifier: String,
239    pub properties: HashMap<String, MaterialProperty>,
240    pub collision_flags: u32,
241    pub material_kind: u32,
242    pub fx: String,
243}
244
245#[derive(Debug)]
246pub enum MaterialProperty {
247    Texture(String),
249    Boolean(bool),
251    Integer(i64),
253    Float(f32),
255    Vec4(Vec4),
257}
258
259#[derive(Debug, Error)]
261pub enum DeError {
262    #[error("the root node is missing")]
264    MissingRootNode,
265    #[error("a node is missing either identifier or transform")]
267    InvalidNode,
268    #[error("values are missing in a render set")]
270    InvalidRenderSet,
271    #[error("the bounding box is missing or invalid")]
273    MissingBoundingBox,
274    #[error("the geometry size is missing or invalid")]
276    MissingGeometrySize,
277    #[error("the uv density is missing or invalid")]
279    MissingUvDensity,
280    #[error("pxml error: {0}")]
282    Pxml(#[from] pxml::DeError),
283}