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    /// Attenuation start
249    pub attenuation_start: f32,
250
251    /// Attenuation end
252    pub attenuation_end: f32,
253
254    /// Use attenuation
255    pub use_attenuation: bool,
256
257    /// Additional properties based on light type
258    pub properties: WmoLightProperties,
259}
260
261/// Type of WMO light
262#[derive(Debug, Clone, Copy, PartialEq, Eq)]
263pub enum WmoLightType {
264    /// Omnidirectional point light
265    Omni = 0,
266
267    /// Spotlight
268    Spot = 1,
269
270    /// Directional light
271    Directional = 2,
272
273    /// Ambient light
274    Ambient = 3,
275}
276
277impl WmoLightType {
278    /// Convert a raw value to a WmoLightType
279    pub fn from_raw(raw: u8) -> Option<Self> {
280        match raw {
281            0 => Some(Self::Omni),
282            1 => Some(Self::Spot),
283            2 => Some(Self::Directional),
284            3 => Some(Self::Ambient),
285            // Unknown light types default to Omni
286            _ => {
287                // Log warning but continue with default
288                tracing::warn!("Unknown light type {}, defaulting to Omni", raw);
289                Some(Self::Omni)
290            }
291        }
292    }
293}
294
295/// Additional light properties depending on light type
296#[derive(Debug, Clone)]
297pub enum WmoLightProperties {
298    /// Omni light properties (none)
299    Omni,
300
301    /// Spotlight properties
302    Spot {
303        /// Direction
304        direction: Vec3,
305
306        /// Hotspot angle (inner cone)
307        hotspot: f32,
308
309        /// Falloff angle (outer cone)
310        falloff: f32,
311    },
312
313    /// Directional light properties
314    Directional {
315        /// Direction
316        direction: Vec3,
317    },
318
319    /// Ambient light properties (none)
320    Ambient,
321}
322
323/// Represents a WMO doodad definition
324#[derive(Debug, Clone)]
325pub struct WmoDoodadDef {
326    /// Doodad name offset in MODN chunk
327    pub name_offset: u32,
328
329    /// Doodad position
330    pub position: Vec3,
331
332    /// Doodad orientation as quaternion
333    pub orientation: [f32; 4],
334
335    /// Doodad scale factor
336    pub scale: f32,
337
338    /// Doodad color
339    pub color: Color,
340
341    /// Doodad set index
342    pub set_index: u16,
343}
344
345/// Represents a WMO doodad set
346#[derive(Debug, Clone)]
347pub struct WmoDoodadSet {
348    /// Set name
349    pub name: String,
350
351    /// Start doodad index
352    pub start_doodad: u32,
353
354    /// Number of doodads in this set
355    pub n_doodads: u32,
356}
357
358/// Represents a convex volume plane (MCVP chunk)
359/// Added in Cataclysm for transport WMOs and world objects requiring collision
360/// Based on empirical analysis: typically 496 bytes in transport WMOs
361#[derive(Debug, Clone)]
362pub struct WmoConvexVolumePlane {
363    /// Plane normal vector
364    pub normal: Vec3,
365
366    /// Distance from origin along the normal
367    pub distance: f32,
368
369    /// Plane flags (usage unknown)
370    pub flags: u32,
371}
372
373/// Container for MCVP chunk data
374/// Found in Cataclysm+ WMOs, particularly transport objects like ships
375#[derive(Debug, Clone)]
376pub struct WmoConvexVolumePlanes {
377    /// List of convex volume planes
378    /// Each plane defines a clipping boundary for the WMO collision system
379    pub planes: Vec<WmoConvexVolumePlane>,
380}