Skip to main content

wow_wmo/
wmo_types.rs

1use std::collections::HashMap;
2
3use crate::types::{BoundingBox, Color, Vec3};
4use crate::version::WmoVersion;
5use crate::wmo_group_types::WmoGroupFlags;
6use bitflags::bitflags;
7
8/// Represents a WMO root file
9#[derive(Debug)]
10pub struct WmoRoot {
11    /// WMO version
12    pub version: WmoVersion,
13
14    /// List of materials
15    pub materials: Vec<WmoMaterial>,
16
17    /// List of groups
18    pub groups: Vec<WmoGroupInfo>,
19
20    /// List of portals
21    pub portals: Vec<WmoPortal>,
22
23    /// List of portal references
24    pub portal_references: Vec<WmoPortalReference>,
25
26    /// List of visible block lists
27    pub visible_block_lists: Vec<Vec<u16>>,
28
29    /// List of lights
30    pub lights: Vec<WmoLight>,
31
32    /// List of doodad definitions
33    pub doodad_defs: Vec<WmoDoodadDef>,
34
35    /// List of doodad sets
36    pub doodad_sets: Vec<WmoDoodadSet>,
37
38    /// Global bounding box
39    pub bounding_box: BoundingBox,
40
41    /// List of textures
42    pub textures: Vec<String>,
43
44    /// Map of texture offsets to indices in the textures vector
45    pub texture_offset_index_map: HashMap<u32, u32>,
46
47    /// Model header info
48    pub header: WmoHeader,
49
50    /// Skybox model path, if any
51    pub skybox: Option<String>,
52
53    /// Convex volume planes (Cataclysm+, transport WMOs)
54    /// Contains collision geometry for advanced physics interactions
55    pub convex_volume_planes: Option<WmoConvexVolumePlanes>,
56}
57
58/// WMO header information
59#[derive(Debug, Clone)]
60pub struct WmoHeader {
61    /// Number of materials
62    pub n_materials: u32,
63
64    /// Number of groups
65    pub n_groups: u32,
66
67    /// Number of portals
68    pub n_portals: u32,
69
70    /// Number of lights
71    pub n_lights: u32,
72
73    /// Number of doodad names
74    pub n_doodad_names: u32,
75
76    /// Number of doodad defs
77    pub n_doodad_defs: u32,
78
79    /// Number of doodad sets
80    pub n_doodad_sets: u32,
81
82    /// Global WMO flags
83    pub flags: WmoFlags,
84
85    /// Ambient color
86    pub ambient_color: Color,
87}
88
89bitflags! {
90    /// Global WMO flags
91    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
92    pub struct WmoFlags: u32 {
93        /// Contains vertex colors
94        const HAS_VERTEX_COLORS = 0x01;
95        /// Outdoor
96        const OUTDOOR = 0x02;
97        /// Does not cast shadows on terrain
98        const NO_TERRAIN_SHADOWS = 0x04;
99        /// Contains groups with liquids
100        const HAS_LIQUIDS = 0x08;
101        /// Indoor map
102        const INDOOR_MAP = 0x10;
103        /// Contains skybox (introduced in WotLK)
104        const HAS_SKYBOX = 0x20;
105        /// Needs rendering during a special pass
106        const SPECIAL_PASS = 0x40;
107        /// Uses scene graph for rendering
108        const USE_SCENE_GRAPH = 0x80;
109        /// Shows minimap in outdoor
110        const SHOW_MINIMAP_OUTDOOR = 0x100;
111        /// Mount allowed inside this WMO
112        const MOUNT_ALLOWED = 0x200;
113    }
114}
115
116/// Represents a WMO material
117#[derive(Debug, Clone)]
118pub struct WmoMaterial {
119    /// Material flags
120    pub flags: WmoMaterialFlags,
121
122    /// Texture 1 index in MOTX chunk
123    pub shader: u32,
124
125    /// Blend mode
126    pub blend_mode: u32,
127
128    /// Texture 1 offset in MOTX chunk
129    pub texture1: u32,
130
131    /// Emissive color
132    pub emissive_color: Color,
133
134    /// Sidn color
135    pub sidn_color: Color,
136
137    /// Framebuffer blend
138    pub framebuffer_blend: Color,
139
140    /// Texture 2 offset in MOTX chunk
141    pub texture2: u32,
142
143    /// Diffuse color
144    pub diffuse_color: Color,
145
146    /// Ground type
147    pub ground_type: u32,
148}
149
150bitflags! {
151    /// WMO material flags
152    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
153    pub struct WmoMaterialFlags: u32 {
154        /// Unlit
155        const UNLIT = 0x01;
156        /// Unfogged
157        const UNFOGGED = 0x02;
158        /// Two-sided
159        const TWO_SIDED = 0x04;
160        /// Exterior light
161        const EXTERIOR_LIGHT = 0x08;
162        /// Window light
163        const WINDOW_LIGHT = 0x10;
164        /// Use clamp s addressing
165        const CLAMP_S = 0x20;
166        /// Use clamp t addressing
167        const CLAMP_T = 0x40;
168        /// Unused 1
169        const UNUSED1 = 0x80;
170        /// Shadow batch 1
171        const SHADOW_BATCH_1 = 0x100;
172        /// Shadow batch 2
173        const SHADOW_BATCH_2 = 0x200;
174        /// Unused 2
175        const UNUSED2 = 0x400;
176        /// Unused 3
177        const UNUSED3 = 0x800;
178    }
179}
180
181impl WmoMaterial {
182    pub fn get_texture1_index(&self, texture_offset_index_map: &HashMap<u32, u32>) -> u32 {
183        texture_offset_index_map
184            .get(&self.texture1)
185            .copied()
186            .unwrap()
187    }
188
189    pub fn get_texture2_index(&self, texture_offset_index_map: &HashMap<u32, u32>) -> u32 {
190        texture_offset_index_map
191            .get(&self.texture2)
192            .copied()
193            .unwrap()
194    }
195}
196
197/// Represents information about a WMO group
198#[derive(Debug, Clone)]
199pub struct WmoGroupInfo {
200    /// Group flags
201    pub flags: WmoGroupFlags,
202
203    /// Bounding box
204    pub bounding_box: BoundingBox,
205
206    /// Group name (often contains a numeric index)
207    pub name: String,
208}
209
210/// Represents a WMO portal
211#[derive(Debug, Clone)]
212pub struct WmoPortal {
213    /// Portal vertices
214    pub vertices: Vec<Vec3>,
215
216    /// Normal vector
217    pub normal: Vec3,
218}
219
220/// Represents a WMO portal reference
221#[derive(Debug, Clone)]
222pub struct WmoPortalReference {
223    /// Portal index
224    pub portal_index: u16,
225
226    /// Group index
227    pub group_index: u16,
228
229    /// Side (0 or 1)
230    pub side: u16,
231}
232
233/// Represents a WMO light
234#[derive(Debug, Clone)]
235pub struct WmoLight {
236    /// Light type
237    pub light_type: WmoLightType,
238
239    /// Light position
240    pub position: Vec3,
241
242    /// Light color
243    pub color: Color,
244
245    /// Light intensity
246    pub intensity: f32,
247
248    /// Rotation quaternion (x, y, z, w) from on-disk C4Quaternion at +0x18.
249    /// Used by spot and directional lights for orientation.
250    pub rotation: [f32; 4],
251
252    /// Attenuation start distance (at +0x28 on disk)
253    pub attenuation_start: f32,
254
255    /// Attenuation end distance (at +0x2C on disk)
256    pub attenuation_end: f32,
257
258    /// Use attenuation
259    pub use_attenuation: bool,
260
261    /// Additional properties based on light type
262    pub properties: WmoLightProperties,
263}
264
265/// Type of WMO light
266#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum WmoLightType {
268    /// Omnidirectional point light
269    Omni = 0,
270
271    /// Spotlight
272    Spot = 1,
273
274    /// Directional light
275    Directional = 2,
276
277    /// Ambient light
278    Ambient = 3,
279}
280
281impl WmoLightType {
282    /// Convert a raw value to a WmoLightType
283    pub fn from_raw(raw: u8) -> Option<Self> {
284        match raw {
285            0 => Some(Self::Omni),
286            1 => Some(Self::Spot),
287            2 => Some(Self::Directional),
288            3 => Some(Self::Ambient),
289            // Unknown light types default to Omni
290            _ => {
291                // Log warning but continue with default
292                tracing::warn!("Unknown light type {}, defaulting to Omni", raw);
293                Some(Self::Omni)
294            }
295        }
296    }
297}
298
299/// Additional light properties depending on light type
300#[derive(Debug, Clone)]
301pub enum WmoLightProperties {
302    /// Omni light properties (none)
303    Omni,
304
305    /// Spotlight properties
306    Spot {
307        /// Direction
308        direction: Vec3,
309
310        /// Hotspot angle (inner cone)
311        hotspot: f32,
312
313        /// Falloff angle (outer cone)
314        falloff: f32,
315    },
316
317    /// Directional light properties
318    Directional {
319        /// Direction
320        direction: Vec3,
321    },
322
323    /// Ambient light properties (none)
324    Ambient,
325}
326
327/// Represents a WMO doodad definition
328#[derive(Debug, Clone)]
329pub struct WmoDoodadDef {
330    /// Doodad name offset in MODN chunk
331    pub name_offset: u32,
332
333    /// Doodad position
334    pub position: Vec3,
335
336    /// Doodad orientation as quaternion
337    pub orientation: [f32; 4],
338
339    /// Doodad scale factor
340    pub scale: f32,
341
342    /// Doodad color
343    pub color: Color,
344
345    /// Doodad set index
346    pub set_index: u16,
347}
348
349/// Represents a WMO doodad set
350#[derive(Debug, Clone)]
351pub struct WmoDoodadSet {
352    /// Set name
353    pub name: String,
354
355    /// Start doodad index
356    pub start_doodad: u32,
357
358    /// Number of doodads in this set
359    pub n_doodads: u32,
360}
361
362/// Represents a convex volume plane (MCVP chunk)
363/// Added in Cataclysm for transport WMOs and world objects requiring collision
364/// Based on empirical analysis: typically 496 bytes in transport WMOs
365#[derive(Debug, Clone)]
366pub struct WmoConvexVolumePlane {
367    /// Plane normal vector
368    pub normal: Vec3,
369
370    /// Distance from origin along the normal
371    pub distance: f32,
372
373    /// Plane flags (usage unknown)
374    pub flags: u32,
375}
376
377/// Container for MCVP chunk data
378/// Found in Cataclysm+ WMOs, particularly transport objects like ships
379#[derive(Debug, Clone)]
380pub struct WmoConvexVolumePlanes {
381    /// List of convex volume planes
382    /// Each plane defines a clipping boundary for the WMO collision system
383    pub planes: Vec<WmoConvexVolumePlane>,
384}