Skip to main content

whiteout/
mdx.rs

1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright (c) 2026 Fernando Sahmkow
3// AUTOGENERATED by tools/codegen/emit_rust.py — do not edit.
4// Regenerate via:  python -m tools.codegen.codegen mdx --backend rust
5
6#![allow(clippy::too_many_arguments)]
7
8// Which of these a module needs depends on its shapes; the modules that
9// have no span accessors would otherwise trip the unused-import lint.
10#[allow(unused_imports)]
11use crate::support::{BorrowedSlice, Bytes};
12
13#[repr(i32)]
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
15pub enum InterpolationType {
16    None = 0,
17    Linear = 1,
18    Hermite = 2,
19    Bezier = 3,
20}
21
22impl TryFrom<i32> for InterpolationType {
23    type Error = crate::Error;
24    fn try_from(v: i32) -> Result<Self, crate::Error> {
25        match v {
26            0 => Ok(InterpolationType::None),
27            1 => Ok(InterpolationType::Linear),
28            2 => Ok(InterpolationType::Hermite),
29            3 => Ok(InterpolationType::Bezier),
30            other => Err(crate::Error::UnknownEnum {
31                name: "InterpolationType",
32                value: other,
33            }),
34        }
35    }
36}
37
38/// Sequence playback flags
39#[repr(i32)]
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
41pub enum SequenceFlag {
42    None = 0,
43    /// Sequence plays once instead of looping
44    NonLooping = 1,
45}
46
47impl TryFrom<i32> for SequenceFlag {
48    type Error = crate::Error;
49    fn try_from(v: i32) -> Result<Self, crate::Error> {
50        match v {
51            0 => Ok(SequenceFlag::None),
52            1 => Ok(SequenceFlag::NonLooping),
53            other => Err(crate::Error::UnknownEnum {
54                name: "SequenceFlag",
55                value: other,
56            }),
57        }
58    }
59}
60
61/// Texture wrap flags
62#[repr(i32)]
63#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
64pub enum TextureFlag {
65    None = 0,
66    /// Wrap texture horizontally
67    WrapWidth = 1,
68    /// Wrap texture vertically
69    WrapHeight = 2,
70}
71
72impl TryFrom<i32> for TextureFlag {
73    type Error = crate::Error;
74    fn try_from(v: i32) -> Result<Self, crate::Error> {
75        match v {
76            0 => Ok(TextureFlag::None),
77            1 => Ok(TextureFlag::WrapWidth),
78            2 => Ok(TextureFlag::WrapHeight),
79            other => Err(crate::Error::UnknownEnum {
80                name: "TextureFlag",
81                value: other,
82            }),
83        }
84    }
85}
86
87/// Type of node in the hierarchy
88#[repr(i32)]
89#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
90pub enum NodeType {
91    /// Skeleton bone
92    Bone = 0,
93    /// Light source
94    Light = 1,
95    /// Helper/attachment point
96    Helper = 2,
97    /// Equipment attachment
98    Attachment = 3,
99    /// Particle emitter v1
100    ParticleEmitter = 4,
101    /// Particle emitter v2
102    ParticleEmitter2 = 5,
103    /// Ribbon trail emitter
104    RibbonEmitter = 6,
105    /// Event trigger
106    EventObject = 7,
107    /// Camera
108    Camera = 8,
109    /// Collision volume
110    CollisionShape = 9,
111    /// Face effect (Reforged)
112    FaceEffect = 10,
113    /// PopcornFX emitter (Reforged)
114    CornEmitter = 11,
115}
116
117impl TryFrom<i32> for NodeType {
118    type Error = crate::Error;
119    fn try_from(v: i32) -> Result<Self, crate::Error> {
120        match v {
121            0 => Ok(NodeType::Bone),
122            1 => Ok(NodeType::Light),
123            2 => Ok(NodeType::Helper),
124            3 => Ok(NodeType::Attachment),
125            4 => Ok(NodeType::ParticleEmitter),
126            5 => Ok(NodeType::ParticleEmitter2),
127            6 => Ok(NodeType::RibbonEmitter),
128            7 => Ok(NodeType::EventObject),
129            8 => Ok(NodeType::Camera),
130            9 => Ok(NodeType::CollisionShape),
131            10 => Ok(NodeType::FaceEffect),
132            11 => Ok(NodeType::CornEmitter),
133            other => Err(crate::Error::UnknownEnum {
134                name: "NodeType",
135                value: other,
136            }),
137        }
138    }
139}
140
141/// Flags controlling node behavior and rendering
142/// Bit flags. Combine with `|`, test with [`NodeFlag::contains`].
143#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
144pub struct NodeFlag(pub i32);
145
146impl NodeFlag {
147    pub const NONE: Self = Self(0);
148    /// Don't inherit parent translation
149    pub const DONT_INHERIT_TRANSLATION: Self = Self(1);
150    /// Don't inherit parent scaling
151    pub const DONT_INHERIT_SCALING: Self = Self(2);
152    /// Don't inherit parent rotation
153    pub const DONT_INHERIT_ROTATION: Self = Self(4);
154    /// Always face camera
155    pub const BILLBOARDED: Self = Self(8);
156    /// Billboard but lock X axis
157    pub const BILLBOARDED_LOCK_X: Self = Self(16);
158    /// Billboard but lock Y axis
159    pub const BILLBOARDED_LOCK_Y: Self = Self(32);
160    /// Billboard but lock Z axis
161    pub const BILLBOARDED_LOCK_Z: Self = Self(64);
162    /// Anchored to camera
163    pub const CAMERA_ANCHORED: Self = Self(128);
164    /// This is a bone
165    pub const BONE: Self = Self(256);
166    /// This is a light
167    pub const LIGHT: Self = Self(512);
168    /// This is an event object
169    pub const EVENT_OBJECT: Self = Self(1024);
170    /// This is an attachment
171    pub const ATTACHMENT: Self = Self(2048);
172    /// This is a particle emitter
173    pub const PARTICLE_EMITTER: Self = Self(4096);
174    /// This is a collision shape
175    pub const COLLISION_SHAPE: Self = Self(8192);
176    /// This is a ribbon emitter
177    pub const RIBBON_EMITTER: Self = Self(16384);
178    /// Unshaded (PE2/Popcorn) / EmitterUsesMdl (PE)
179    pub const UNSHADED: Self = Self(32768);
180    /// Particle emitter uses MDL model
181    pub const EMITTER_USES_MDL: Self = Self(32768);
182    /// Sort primitives (PE2/Popcorn)
183    pub const SORT_PRIMITIVES: Self = Self(65536);
184    /// Alias of SortPrimitives
185    pub const SORT_PRIMS_FAR_Z: Self = Self(65536);
186    /// Particle emitter uses TGA (PE only)
187    pub const EMITTER_USES_TGA: Self = Self(65536);
188    /// Line-shaped emitter (PE2)
189    pub const LINE_EMITTER: Self = Self(131072);
190    /// Unfogged (Popcorn emitter)
191    pub const POPCORN_UNFOGGED: Self = Self(131072);
192    /// Not affected by fog (PE2)
193    pub const UNFOGGED: Self = Self(262144);
194    /// Particle scaling (Popcorn emitter)
195    pub const POPCORN_SCALING: Self = Self(262144);
196    /// Use model space coordinates
197    pub const MODEL_SPACE: Self = Self(524288);
198    /// XY quad billboarding
199    pub const XY_QUAD: Self = Self(1048576);
200
201    #[inline]
202    pub const fn contains(self, other: Self) -> bool {
203        (self.0 & other.0) == other.0
204    }
205
206    #[inline]
207    pub const fn is_empty(self) -> bool {
208        self.0 == 0
209    }
210}
211
212impl core::ops::BitOr for NodeFlag {
213    type Output = Self;
214    #[inline]
215    fn bitor(self, rhs: Self) -> Self {
216        Self(self.0 | rhs.0)
217    }
218}
219
220impl core::ops::BitAnd for NodeFlag {
221    type Output = Self;
222    #[inline]
223    fn bitand(self, rhs: Self) -> Self {
224        Self(self.0 & rhs.0)
225    }
226}
227
228impl core::ops::Not for NodeFlag {
229    type Output = Self;
230    #[inline]
231    fn not(self) -> Self {
232        Self(!self.0)
233    }
234}
235
236impl core::fmt::Debug for NodeFlag {
237    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
238        write!(f, "NodeFlag({:#x})", self.0)
239    }
240}
241
242/// Blending/filter mode for the layer
243#[repr(i32)]
244#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
245pub enum LayerFilterMode {
246    /// No blending
247    None = 0,
248    /// Alpha test transparency
249    Transparent = 1,
250    /// Alpha blending
251    Blend = 2,
252    /// Additive blending
253    Additive = 3,
254    /// Additive alpha blending
255    AddAlpha = 4,
256    /// Modulate (multiply) blending
257    Modulate = 5,
258    /// Modulate 2x (brighten)
259    Modulate2x = 6,
260    Count = 7,
261}
262
263impl TryFrom<i32> for LayerFilterMode {
264    type Error = crate::Error;
265    fn try_from(v: i32) -> Result<Self, crate::Error> {
266        match v {
267            0 => Ok(LayerFilterMode::None),
268            1 => Ok(LayerFilterMode::Transparent),
269            2 => Ok(LayerFilterMode::Blend),
270            3 => Ok(LayerFilterMode::Additive),
271            4 => Ok(LayerFilterMode::AddAlpha),
272            5 => Ok(LayerFilterMode::Modulate),
273            6 => Ok(LayerFilterMode::Modulate2x),
274            7 => Ok(LayerFilterMode::Count),
275            other => Err(crate::Error::UnknownEnum {
276                name: "LayerFilterMode",
277                value: other,
278            }),
279        }
280    }
281}
282
283#[repr(i32)]
284#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
285pub enum LayerShaderType {
286    SD = 0,
287    HD = 1,
288    SDOnHD = 2,
289    Terrain = 3,
290    Water = 4,
291    Fog = 5,
292    Foliage = 6,
293    FoliagePush = 7,
294    Sprite = 8,
295    DebugTexture = 9,
296    DepthOfField = 10,
297    BloomCombine = 11,
298    BloomExtract = 12,
299    GaussianBlur = 13,
300    Tonemap = 14,
301    Movie = 15,
302    FFXCMAAEdge0 = 16,
303    FFXCMAAEdge1 = 17,
304    FFXCMAAEdgeCombine = 18,
305    FFXCMAAProcessAndApply = 19,
306    PopcornFX = 20,
307    ConeIndicator = 21,
308    CliffBlightMiscTerrain = 22,
309    Distortion = 23,
310    Crystal = 24,
311    Imgui = 25,
312}
313
314impl TryFrom<i32> for LayerShaderType {
315    type Error = crate::Error;
316    fn try_from(v: i32) -> Result<Self, crate::Error> {
317        match v {
318            0 => Ok(LayerShaderType::SD),
319            1 => Ok(LayerShaderType::HD),
320            2 => Ok(LayerShaderType::SDOnHD),
321            3 => Ok(LayerShaderType::Terrain),
322            4 => Ok(LayerShaderType::Water),
323            5 => Ok(LayerShaderType::Fog),
324            6 => Ok(LayerShaderType::Foliage),
325            7 => Ok(LayerShaderType::FoliagePush),
326            8 => Ok(LayerShaderType::Sprite),
327            9 => Ok(LayerShaderType::DebugTexture),
328            10 => Ok(LayerShaderType::DepthOfField),
329            11 => Ok(LayerShaderType::BloomCombine),
330            12 => Ok(LayerShaderType::BloomExtract),
331            13 => Ok(LayerShaderType::GaussianBlur),
332            14 => Ok(LayerShaderType::Tonemap),
333            15 => Ok(LayerShaderType::Movie),
334            16 => Ok(LayerShaderType::FFXCMAAEdge0),
335            17 => Ok(LayerShaderType::FFXCMAAEdge1),
336            18 => Ok(LayerShaderType::FFXCMAAEdgeCombine),
337            19 => Ok(LayerShaderType::FFXCMAAProcessAndApply),
338            20 => Ok(LayerShaderType::PopcornFX),
339            21 => Ok(LayerShaderType::ConeIndicator),
340            22 => Ok(LayerShaderType::CliffBlightMiscTerrain),
341            23 => Ok(LayerShaderType::Distortion),
342            24 => Ok(LayerShaderType::Crystal),
343            25 => Ok(LayerShaderType::Imgui),
344            other => Err(crate::Error::UnknownEnum {
345                name: "LayerShaderType",
346                value: other,
347            }),
348        }
349    }
350}
351
352/// Shader and rendering flags
353/// Bit flags. Combine with `|`, test with [`LayerShadingFlag::contains`].
354#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
355pub struct LayerShadingFlag(pub i32);
356
357impl LayerShadingFlag {
358    pub const NONE: Self = Self(0);
359    /// Not affected by lighting
360    pub const UNSHADED: Self = Self(1);
361    /// Spherical environment mapping
362    pub const SPHERE_ENV_MAP: Self = Self(2);
363    /// Texture U-wrap
364    pub const WRAP_WIDTH: Self = Self(4);
365    /// Texture V-wrap
366    pub const WRAP_HEIGHT: Self = Self(8);
367    /// Render both sides of polygons
368    pub const TWO_SIDED: Self = Self(16);
369    /// Not affected by fog
370    pub const UNFOGGED: Self = Self(32);
371    /// Disable depth testing
372    pub const NO_DEPTH_TEST: Self = Self(64);
373    /// Don't write to depth buffer
374    pub const NO_DEPTH_SET: Self = Self(128);
375    /// Reforged: bypass lighting pipeline
376    pub const UNLIT: Self = Self(256);
377
378    #[inline]
379    pub const fn contains(self, other: Self) -> bool {
380        (self.0 & other.0) == other.0
381    }
382
383    #[inline]
384    pub const fn is_empty(self) -> bool {
385        self.0 == 0
386    }
387}
388
389impl core::ops::BitOr for LayerShadingFlag {
390    type Output = Self;
391    #[inline]
392    fn bitor(self, rhs: Self) -> Self {
393        Self(self.0 | rhs.0)
394    }
395}
396
397impl core::ops::BitAnd for LayerShadingFlag {
398    type Output = Self;
399    #[inline]
400    fn bitand(self, rhs: Self) -> Self {
401        Self(self.0 & rhs.0)
402    }
403}
404
405impl core::ops::Not for LayerShadingFlag {
406    type Output = Self;
407    #[inline]
408    fn not(self) -> Self {
409        Self(!self.0)
410    }
411}
412
413impl core::fmt::Debug for LayerShadingFlag {
414    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
415        write!(f, "LayerShadingFlag({:#x})", self.0)
416    }
417}
418
419#[repr(i32)]
420#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
421pub enum LayerSlotType {
422    DiffuseMap = 0,
423    NormalMap = 1,
424    ORMMap = 2,
425    EmissiveMap = 3,
426    TeamColor = 4,
427    EnvironmentMap = 5,
428    Unknown = 6,
429}
430
431impl TryFrom<i32> for LayerSlotType {
432    type Error = crate::Error;
433    fn try_from(v: i32) -> Result<Self, crate::Error> {
434        match v {
435            0 => Ok(LayerSlotType::DiffuseMap),
436            1 => Ok(LayerSlotType::NormalMap),
437            2 => Ok(LayerSlotType::ORMMap),
438            3 => Ok(LayerSlotType::EmissiveMap),
439            4 => Ok(LayerSlotType::TeamColor),
440            5 => Ok(LayerSlotType::EnvironmentMap),
441            6 => Ok(LayerSlotType::Unknown),
442            other => Err(crate::Error::UnknownEnum {
443                name: "LayerSlotType",
444                value: other,
445            }),
446        }
447    }
448}
449
450/// Material-level flags
451/// Bit flags. Combine with `|`, test with [`MaterialFlag::contains`].
452#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
453pub struct MaterialFlag(pub i32);
454
455impl MaterialFlag {
456    pub const NONE: Self = Self(0);
457    /// Use constant color (no per-vertex tinting)
458    pub const CONSTANT_COLOR: Self = Self(1);
459    /// Render both sides of polygons
460    pub const TWO_SIDED: Self = Self(2);
461    /// Not affected by fog
462    pub const UNFOGGED: Self = Self(4);
463    /// Sort by near-Z
464    pub const SORT_PRIMS_NEAR_Z: Self = Self(8);
465    /// Sort by far-Z; alias: SortPrimitives
466    pub const SORT_PRIMS_FAR_Z: Self = Self(16);
467    /// Legacy alias for SortPrimsFarZ (HiveWorkshop name)
468    pub const SORT_PRIMITIVES: Self = Self(16);
469    /// Force full-resolution textures
470    pub const FULL_RESOLUTION: Self = Self(32);
471
472    #[inline]
473    pub const fn contains(self, other: Self) -> bool {
474        (self.0 & other.0) == other.0
475    }
476
477    #[inline]
478    pub const fn is_empty(self) -> bool {
479        self.0 == 0
480    }
481}
482
483impl core::ops::BitOr for MaterialFlag {
484    type Output = Self;
485    #[inline]
486    fn bitor(self, rhs: Self) -> Self {
487        Self(self.0 | rhs.0)
488    }
489}
490
491impl core::ops::BitAnd for MaterialFlag {
492    type Output = Self;
493    #[inline]
494    fn bitand(self, rhs: Self) -> Self {
495        Self(self.0 & rhs.0)
496    }
497}
498
499impl core::ops::Not for MaterialFlag {
500    type Output = Self;
501    #[inline]
502    fn not(self) -> Self {
503        Self(!self.0)
504    }
505}
506
507impl core::fmt::Debug for MaterialFlag {
508    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
509        write!(f, "MaterialFlag({:#x})", self.0)
510    }
511}
512
513/// Geoset animation flags
514#[repr(i32)]
515#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
516pub enum GeosetAnimationFlag {
517    None = 0,
518    /// Geoset casts a drop shadow
519    DropShadow = 1,
520    /// Use the per-geoset color/alpha values (else inherit)
521    Color = 2,
522}
523
524impl TryFrom<i32> for GeosetAnimationFlag {
525    type Error = crate::Error;
526    fn try_from(v: i32) -> Result<Self, crate::Error> {
527        match v {
528            0 => Ok(GeosetAnimationFlag::None),
529            1 => Ok(GeosetAnimationFlag::DropShadow),
530            2 => Ok(GeosetAnimationFlag::Color),
531            other => Err(crate::Error::UnknownEnum {
532                name: "GeosetAnimationFlag",
533                value: other,
534            }),
535        }
536    }
537}
538
539/// Type of light source
540#[repr(i32)]
541#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
542pub enum LightType {
543    /// Point light (radiates in all directions)
544    Omni = 0,
545    /// Directional light (like sunlight)
546    Directional = 1,
547    /// Ambient light (affects everything equally)
548    Ambient = 2,
549}
550
551impl TryFrom<i32> for LightType {
552    type Error = crate::Error;
553    fn try_from(v: i32) -> Result<Self, crate::Error> {
554        match v {
555            0 => Ok(LightType::Omni),
556            1 => Ok(LightType::Directional),
557            2 => Ok(LightType::Ambient),
558            other => Err(crate::Error::UnknownEnum {
559                name: "LightType",
560                value: other,
561            }),
562        }
563    }
564}
565
566#[repr(i32)]
567#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
568pub enum CollisionShapeShapeType {
569    Box = 0,
570    Plane = 1,
571    Sphere = 2,
572    Cylinder = 3,
573}
574
575impl TryFrom<i32> for CollisionShapeShapeType {
576    type Error = crate::Error;
577    fn try_from(v: i32) -> Result<Self, crate::Error> {
578        match v {
579            0 => Ok(CollisionShapeShapeType::Box),
580            1 => Ok(CollisionShapeShapeType::Plane),
581            2 => Ok(CollisionShapeShapeType::Sphere),
582            3 => Ok(CollisionShapeShapeType::Cylinder),
583            other => Err(crate::Error::UnknownEnum {
584                name: "CollisionShapeShapeType",
585                value: other,
586            }),
587        }
588    }
589}
590
591/// Source format selector for buffer-based parsing.
592#[repr(i32)]
593#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
594pub enum MDLXFormat {
595    /// Binary MDX format
596    MDX = 0,
597    /// Text MDL format
598    MDL = 1,
599}
600
601impl TryFrom<i32> for MDLXFormat {
602    type Error = crate::Error;
603    fn try_from(v: i32) -> Result<Self, crate::Error> {
604        match v {
605            0 => Ok(MDLXFormat::MDX),
606            1 => Ok(MDLXFormat::MDL),
607            other => Err(crate::Error::UnknownEnum {
608                name: "MDLXFormat",
609                value: other,
610            }),
611        }
612    }
613}
614
615/// Version handling mode
616#[repr(i32)]
617#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
618pub enum UpgradeMode {
619    /// Automatically upgrade older versions to latest format
620    UpgradeOldVersions = 0,
621    /// Keep original version and structure as-is
622    PreserveOriginal = 1,
623}
624
625impl TryFrom<i32> for UpgradeMode {
626    type Error = crate::Error;
627    fn try_from(v: i32) -> Result<Self, crate::Error> {
628        match v {
629            0 => Ok(UpgradeMode::UpgradeOldVersions),
630            1 => Ok(UpgradeMode::PreserveOriginal),
631            other => Err(crate::Error::UnknownEnum {
632                name: "UpgradeMode",
633                value: other,
634            }),
635        }
636    }
637}
638
639/// Selects which MDL text dialect Writer / writeModelToMdl emits.
640///
641/// Only affects the .mdl text output path; MDX binary is unchanged.
642#[repr(i32)]
643#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
644pub enum MdlFormat {
645    /// Engine-faithful dialect — matches what Warcraft III's MDL writer produces. HD layers use a per-layer `Shader "name",` directive and `static TextureID id <= slot,` for sub-textures. Files written in this dialect parse cleanly through the in-game MDL reader. (Default.)
646    WarcraftIII = 0,
647    /// HiveWorkshop community dialect — uses `ShaderTypeId N,` to mark HD layers and named slot properties (`NormalTextureID`, `ORMTextureID`, `EmissiveTextureID`, `TeamColorTextureID`, `ReflectionsTextureID`). Compatible with HiveWorkshop tools (Retera Model Studio, Magos, MdlVis, etc.).
648    Hiveworkshop = 1,
649}
650
651impl TryFrom<i32> for MdlFormat {
652    type Error = crate::Error;
653    fn try_from(v: i32) -> Result<Self, crate::Error> {
654        match v {
655            0 => Ok(MdlFormat::WarcraftIII),
656            1 => Ok(MdlFormat::Hiveworkshop),
657            other => Err(crate::Error::UnknownEnum {
658                name: "MdlFormat",
659                value: other,
660            }),
661        }
662    }
663}
664
665pub struct Extent {
666    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxExtent>,
667}
668
669impl Drop for Extent {
670    fn drop(&mut self) {
671        // SAFETY: `raw` came from a native constructor and Drop runs once.
672        unsafe { ffi::whiteout_mdx_MdxExtent_delete(self.raw.as_ptr()) }
673    }
674}
675
676impl Extent {
677    /// # Safety
678    /// `raw` must be a live handle this value takes ownership of.
679    #[allow(dead_code)] // used by whichever methods return this type
680    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxExtent) -> Option<Self> {
681        core::ptr::NonNull::new(raw).map(|raw| Extent { raw })
682    }
683}
684
685// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
686// is deliberately NOT implemented — the C++ types make no documented
687// guarantee about concurrent use, and claiming one we haven't verified
688// would be unsound. See `@bind thread_safe` in the plan.
689unsafe impl Send for Extent {}
690
691impl core::fmt::Debug for Extent {
692    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
693        f.debug_struct("Extent").finish_non_exhaustive()
694    }
695}
696
697impl Extent {
698    /// # Panics
699    /// Panics if the native allocation fails.
700    pub fn new() -> Self {
701        // SAFETY: the native constructor returns a live handle; a null here
702        // means the library is unusable.
703        unsafe {
704            let raw = ffi::whiteout_mdx_MdxExtent_new();
705            Self::from_raw(raw).expect("native Extent allocation failed")
706        }
707    }
708
709    pub fn bounds_radius(&self) -> f32 {
710        // SAFETY: plain scalar read through a live handle.
711        unsafe { ffi::whiteout_mdx_MdxExtent_get_boundsRadius(self.raw.as_ptr()) }
712    }
713
714    pub fn set_bounds_radius(&mut self, value: f32) {
715        // SAFETY: plain scalar write through a live handle.
716        unsafe { ffi::whiteout_mdx_MdxExtent_set_boundsRadius(self.raw.as_ptr(), value) }
717    }
718
719    pub fn minimum(&self) -> crate::math::Vector3f {
720        // SAFETY: the getter returns an interior pointer to a
721        // layout-identical POD; we copy it out immediately.
722        unsafe {
723            *(ffi::whiteout_mdx_MdxExtent_get_minimum(self.raw.as_ptr())
724                as *const crate::math::Vector3f)
725        }
726    }
727
728    pub fn set_minimum(&mut self, value: crate::math::Vector3f) {
729        // SAFETY: as above, in the other direction.
730        unsafe {
731            ffi::whiteout_mdx_MdxExtent_set_minimum(
732                self.raw.as_ptr(),
733                &value as *const crate::math::Vector3f as *const _,
734            )
735        }
736    }
737
738    pub fn maximum(&self) -> crate::math::Vector3f {
739        // SAFETY: the getter returns an interior pointer to a
740        // layout-identical POD; we copy it out immediately.
741        unsafe {
742            *(ffi::whiteout_mdx_MdxExtent_get_maximum(self.raw.as_ptr())
743                as *const crate::math::Vector3f)
744        }
745    }
746
747    pub fn set_maximum(&mut self, value: crate::math::Vector3f) {
748        // SAFETY: as above, in the other direction.
749        unsafe {
750            ffi::whiteout_mdx_MdxExtent_set_maximum(
751                self.raw.as_ptr(),
752                &value as *const crate::math::Vector3f as *const _,
753            )
754        }
755    }
756}
757
758impl Default for Extent {
759    fn default() -> Self {
760        Self::new()
761    }
762}
763
764/// Complete MDX model file structure
765///
766/// This structure represents an entire MDX model, including all geometry, materials, animations, and metadata. It can represent models from Warcraft III Classic (version 800) through Reforged (version 1200).
767///
768/// The structure is organized into chunks, each containing a specific type of data. Not all chunks are required - optional chunks can have empty vectors.
769pub struct Model {
770    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxModel>,
771}
772
773impl Drop for Model {
774    fn drop(&mut self) {
775        // SAFETY: `raw` came from a native constructor and Drop runs once.
776        unsafe { ffi::whiteout_mdx_MdxModel_delete(self.raw.as_ptr()) }
777    }
778}
779
780impl Model {
781    /// # Safety
782    /// `raw` must be a live handle this value takes ownership of.
783    #[allow(dead_code)] // used by whichever methods return this type
784    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxModel) -> Option<Self> {
785        core::ptr::NonNull::new(raw).map(|raw| Model { raw })
786    }
787}
788
789// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
790// is deliberately NOT implemented — the C++ types make no documented
791// guarantee about concurrent use, and claiming one we haven't verified
792// would be unsound. See `@bind thread_safe` in the plan.
793unsafe impl Send for Model {}
794
795impl core::fmt::Debug for Model {
796    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
797        f.debug_struct("Model").finish_non_exhaustive()
798    }
799}
800
801impl Model {
802    /// # Panics
803    /// Panics if the native allocation fails.
804    pub fn new() -> Self {
805        // SAFETY: the native constructor returns a live handle; a null here
806        // means the library is unusable.
807        unsafe {
808            let raw = ffi::whiteout_mdx_MdxModel_new();
809            Self::from_raw(raw).expect("native Model allocation failed")
810        }
811    }
812
813    /// MDX version: 800 (Classic), 900, 1000, 1100, 1200 (Reforged)
814    pub fn version(&self) -> u32 {
815        // SAFETY: plain scalar read through a live handle.
816        unsafe { ffi::whiteout_mdx_MdxModel_get_version(self.raw.as_ptr()) }
817    }
818
819    pub fn set_version(&mut self, value: u32) {
820        // SAFETY: plain scalar write through a live handle.
821        unsafe { ffi::whiteout_mdx_MdxModel_set_version(self.raw.as_ptr(), value) }
822    }
823
824    /// Name of the model
825    pub fn model_name(&self) -> String {
826        // SAFETY: the native side hands over an owned CString.
827        unsafe {
828            crate::support::take_string(ffi::whiteout_mdx_MdxModel_get_modelName(self.raw.as_ptr()))
829        }
830    }
831
832    pub fn set_model_name(&mut self, value: &str) {
833        let value = std::ffi::CString::new(value).unwrap_or_default();
834        // SAFETY: the pointer outlives the call.
835        unsafe { ffi::whiteout_mdx_MdxModel_set_modelName(self.raw.as_ptr(), value.as_ptr()) }
836    }
837
838    /// Path to external animation file (rarely used)
839    pub fn animation_file_name(&self) -> String {
840        // SAFETY: the native side hands over an owned CString.
841        unsafe {
842            crate::support::take_string(ffi::whiteout_mdx_MdxModel_get_animationFileName(
843                self.raw.as_ptr(),
844            ))
845        }
846    }
847
848    pub fn set_animation_file_name(&mut self, value: &str) {
849        let value = std::ffi::CString::new(value).unwrap_or_default();
850        // SAFETY: the pointer outlives the call.
851        unsafe {
852            ffi::whiteout_mdx_MdxModel_set_animationFileName(self.raw.as_ptr(), value.as_ptr())
853        }
854    }
855
856    /// Bounding volume for the entire model
857    /// Borrows the field in place — no copy, no allocation.
858    pub fn model_extent(&self) -> crate::support::Ref<'_, Extent> {
859        // SAFETY: an interior pointer into `self`, valid for this
860        // borrow and never freed by the `Ref`.
861        unsafe {
862            crate::support::Ref::new(Extent {
863                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_modelExtent(
864                    self.raw.as_ptr(),
865                )),
866            })
867        }
868    }
869
870    pub fn model_extent_mut(&mut self) -> crate::support::RefMut<'_, Extent> {
871        // SAFETY: as above; `&mut self` guarantees exclusivity.
872        unsafe {
873            crate::support::RefMut::new(Extent {
874                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_modelExtent(
875                    self.raw.as_ptr(),
876                )),
877            })
878        }
879    }
880
881    /// Time in milliseconds to blend between animations
882    pub fn blend_time(&self) -> u32 {
883        // SAFETY: plain scalar read through a live handle.
884        unsafe { ffi::whiteout_mdx_MdxModel_get_blendTime(self.raw.as_ptr()) }
885    }
886
887    pub fn set_blend_time(&mut self, value: u32) {
888        // SAFETY: plain scalar write through a live handle.
889        unsafe { ffi::whiteout_mdx_MdxModel_set_blendTime(self.raw.as_ptr(), value) }
890    }
891
892    /// Global looping animation durations
893    /// Zero-copy view of the underlying `std::vector`.
894    pub fn global_sequences(&self) -> &[u32] {
895        // SAFETY: `_data`/`_count` describe one contiguous C++
896        // allocation, borrowed for as long as `self` is.
897        unsafe {
898            let n = ffi::whiteout_mdx_MdxModel_get_globalSequences_count(self.raw.as_ptr());
899            let p = ffi::whiteout_mdx_MdxModel_get_globalSequences_data(self.raw.as_ptr());
900            if p.is_null() || n == 0 {
901                &[]
902            } else {
903                core::slice::from_raw_parts(p, n)
904            }
905        }
906    }
907
908    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
909    pub fn global_sequences_mut(&mut self) -> &mut [u32] {
910        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
911        unsafe {
912            let n = ffi::whiteout_mdx_MdxModel_get_globalSequences_count(self.raw.as_ptr());
913            let p =
914                ffi::whiteout_mdx_MdxModel_get_globalSequences_data(self.raw.as_ptr()) as *mut u32;
915            if p.is_null() || n == 0 {
916                &mut []
917            } else {
918                core::slice::from_raw_parts_mut(p, n)
919            }
920        }
921    }
922
923    pub fn set_global_sequences(&mut self, values: &[u32]) {
924        // SAFETY: the native side copies `values` before returning.
925        unsafe {
926            ffi::whiteout_mdx_MdxModel_assign_globalSequences(
927                self.raw.as_ptr(),
928                values.as_ptr() as *const _,
929                values.len(),
930            )
931        }
932    }
933
934    pub fn resize_global_sequences(&mut self, count: usize) {
935        // SAFETY: reallocation is safe here precisely because
936        // `&mut self` means no slice borrow is outstanding.
937        unsafe { ffi::whiteout_mdx_MdxModel_resize_globalSequences(self.raw.as_ptr(), count) }
938    }
939
940    /// Named animation sequences
941    pub fn sequences_len(&self) -> usize {
942        // SAFETY: scalar read through a live handle.
943        unsafe { ffi::whiteout_mdx_MdxModel_get_sequences_count(self.raw.as_ptr()) }
944    }
945
946    /// Borrows element `index` in place. `None` when out of range.
947    pub fn sequences(&self, index: usize) -> Option<crate::support::Ref<'_, Sequence>> {
948        if index >= self.sequences_len() {
949            return None;
950        }
951        // SAFETY: index checked above; the pointer is interior to `self`.
952        unsafe {
953            Some(crate::support::Ref::new(Sequence {
954                raw: core::ptr::NonNull::new_unchecked(
955                    ffi::whiteout_mdx_MdxModel_get_sequences_at(self.raw.as_ptr(), index),
956                ),
957            }))
958        }
959    }
960
961    pub fn sequences_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Sequence>> {
962        if index >= self.sequences_len() {
963            return None;
964        }
965        // SAFETY: as above; `&mut self` guarantees exclusivity.
966        unsafe {
967            Some(crate::support::RefMut::new(Sequence {
968                raw: core::ptr::NonNull::new_unchecked(
969                    ffi::whiteout_mdx_MdxModel_get_sequences_at(self.raw.as_ptr(), index),
970                ),
971            }))
972        }
973    }
974
975    /// Iterate the elements, borrowing each in turn.
976    pub fn sequences_iter(
977        &self,
978    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Sequence>> {
979        (0..self.sequences_len()).map(move |i| self.sequences(i).expect("index below len"))
980    }
981
982    pub fn resize_sequences(&mut self, count: usize) {
983        // SAFETY: exclusive access, so no borrow is outstanding.
984        unsafe { ffi::whiteout_mdx_MdxModel_resize_sequences(self.raw.as_ptr(), count) }
985    }
986
987    /// Texture paths and settings
988    pub fn textures_len(&self) -> usize {
989        // SAFETY: scalar read through a live handle.
990        unsafe { ffi::whiteout_mdx_MdxModel_get_textures_count(self.raw.as_ptr()) }
991    }
992
993    /// Borrows element `index` in place. `None` when out of range.
994    pub fn textures(&self, index: usize) -> Option<crate::support::Ref<'_, Texture>> {
995        if index >= self.textures_len() {
996            return None;
997        }
998        // SAFETY: index checked above; the pointer is interior to `self`.
999        unsafe {
1000            Some(crate::support::Ref::new(Texture {
1001                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_textures_at(
1002                    self.raw.as_ptr(),
1003                    index,
1004                )),
1005            }))
1006        }
1007    }
1008
1009    pub fn textures_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Texture>> {
1010        if index >= self.textures_len() {
1011            return None;
1012        }
1013        // SAFETY: as above; `&mut self` guarantees exclusivity.
1014        unsafe {
1015            Some(crate::support::RefMut::new(Texture {
1016                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_textures_at(
1017                    self.raw.as_ptr(),
1018                    index,
1019                )),
1020            }))
1021        }
1022    }
1023
1024    /// Iterate the elements, borrowing each in turn.
1025    pub fn textures_iter(&self) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Texture>> {
1026        (0..self.textures_len()).map(move |i| self.textures(i).expect("index below len"))
1027    }
1028
1029    pub fn resize_textures(&mut self, count: usize) {
1030        // SAFETY: exclusive access, so no borrow is outstanding.
1031        unsafe { ffi::whiteout_mdx_MdxModel_resize_textures(self.raw.as_ptr(), count) }
1032    }
1033
1034    /// Sounds (deprecated)
1035    pub fn sounds_len(&self) -> usize {
1036        // SAFETY: scalar read through a live handle.
1037        unsafe { ffi::whiteout_mdx_MdxModel_get_sounds_count(self.raw.as_ptr()) }
1038    }
1039
1040    /// Borrows element `index` in place. `None` when out of range.
1041    pub fn sounds(&self, index: usize) -> Option<crate::support::Ref<'_, Sound>> {
1042        if index >= self.sounds_len() {
1043            return None;
1044        }
1045        // SAFETY: index checked above; the pointer is interior to `self`.
1046        unsafe {
1047            Some(crate::support::Ref::new(Sound {
1048                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_sounds_at(
1049                    self.raw.as_ptr(),
1050                    index,
1051                )),
1052            }))
1053        }
1054    }
1055
1056    pub fn sounds_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Sound>> {
1057        if index >= self.sounds_len() {
1058            return None;
1059        }
1060        // SAFETY: as above; `&mut self` guarantees exclusivity.
1061        unsafe {
1062            Some(crate::support::RefMut::new(Sound {
1063                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_sounds_at(
1064                    self.raw.as_ptr(),
1065                    index,
1066                )),
1067            }))
1068        }
1069    }
1070
1071    /// Iterate the elements, borrowing each in turn.
1072    pub fn sounds_iter(&self) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Sound>> {
1073        (0..self.sounds_len()).map(move |i| self.sounds(i).expect("index below len"))
1074    }
1075
1076    pub fn resize_sounds(&mut self, count: usize) {
1077        // SAFETY: exclusive access, so no borrow is outstanding.
1078        unsafe { ffi::whiteout_mdx_MdxModel_resize_sounds(self.raw.as_ptr(), count) }
1079    }
1080
1081    /// Sound emitters (deprecated)
1082    pub fn sound_emitters_len(&self) -> usize {
1083        // SAFETY: scalar read through a live handle.
1084        unsafe { ffi::whiteout_mdx_MdxModel_get_soundEmitters_count(self.raw.as_ptr()) }
1085    }
1086
1087    /// Borrows element `index` in place. `None` when out of range.
1088    pub fn sound_emitters(&self, index: usize) -> Option<crate::support::Ref<'_, SoundEmitter>> {
1089        if index >= self.sound_emitters_len() {
1090            return None;
1091        }
1092        // SAFETY: index checked above; the pointer is interior to `self`.
1093        unsafe {
1094            Some(crate::support::Ref::new(SoundEmitter {
1095                raw: core::ptr::NonNull::new_unchecked(
1096                    ffi::whiteout_mdx_MdxModel_get_soundEmitters_at(self.raw.as_ptr(), index),
1097                ),
1098            }))
1099        }
1100    }
1101
1102    pub fn sound_emitters_mut(
1103        &mut self,
1104        index: usize,
1105    ) -> Option<crate::support::RefMut<'_, SoundEmitter>> {
1106        if index >= self.sound_emitters_len() {
1107            return None;
1108        }
1109        // SAFETY: as above; `&mut self` guarantees exclusivity.
1110        unsafe {
1111            Some(crate::support::RefMut::new(SoundEmitter {
1112                raw: core::ptr::NonNull::new_unchecked(
1113                    ffi::whiteout_mdx_MdxModel_get_soundEmitters_at(self.raw.as_ptr(), index),
1114                ),
1115            }))
1116        }
1117    }
1118
1119    /// Iterate the elements, borrowing each in turn.
1120    pub fn sound_emitters_iter(
1121        &self,
1122    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, SoundEmitter>> {
1123        (0..self.sound_emitters_len())
1124            .map(move |i| self.sound_emitters(i).expect("index below len"))
1125    }
1126
1127    pub fn resize_sound_emitters(&mut self, count: usize) {
1128        // SAFETY: exclusive access, so no borrow is outstanding.
1129        unsafe { ffi::whiteout_mdx_MdxModel_resize_soundEmitters(self.raw.as_ptr(), count) }
1130    }
1131
1132    /// Material definitions
1133    pub fn materials_len(&self) -> usize {
1134        // SAFETY: scalar read through a live handle.
1135        unsafe { ffi::whiteout_mdx_MdxModel_get_materials_count(self.raw.as_ptr()) }
1136    }
1137
1138    /// Borrows element `index` in place. `None` when out of range.
1139    pub fn materials(&self, index: usize) -> Option<crate::support::Ref<'_, Material>> {
1140        if index >= self.materials_len() {
1141            return None;
1142        }
1143        // SAFETY: index checked above; the pointer is interior to `self`.
1144        unsafe {
1145            Some(crate::support::Ref::new(Material {
1146                raw: core::ptr::NonNull::new_unchecked(
1147                    ffi::whiteout_mdx_MdxModel_get_materials_at(self.raw.as_ptr(), index),
1148                ),
1149            }))
1150        }
1151    }
1152
1153    pub fn materials_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Material>> {
1154        if index >= self.materials_len() {
1155            return None;
1156        }
1157        // SAFETY: as above; `&mut self` guarantees exclusivity.
1158        unsafe {
1159            Some(crate::support::RefMut::new(Material {
1160                raw: core::ptr::NonNull::new_unchecked(
1161                    ffi::whiteout_mdx_MdxModel_get_materials_at(self.raw.as_ptr(), index),
1162                ),
1163            }))
1164        }
1165    }
1166
1167    /// Iterate the elements, borrowing each in turn.
1168    pub fn materials_iter(
1169        &self,
1170    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Material>> {
1171        (0..self.materials_len()).map(move |i| self.materials(i).expect("index below len"))
1172    }
1173
1174    pub fn resize_materials(&mut self, count: usize) {
1175        // SAFETY: exclusive access, so no borrow is outstanding.
1176        unsafe { ffi::whiteout_mdx_MdxModel_resize_materials(self.raw.as_ptr(), count) }
1177    }
1178
1179    /// UV animation data
1180    pub fn texture_animations_len(&self) -> usize {
1181        // SAFETY: scalar read through a live handle.
1182        unsafe { ffi::whiteout_mdx_MdxModel_get_textureAnimations_count(self.raw.as_ptr()) }
1183    }
1184
1185    /// Borrows element `index` in place. `None` when out of range.
1186    pub fn texture_animations(
1187        &self,
1188        index: usize,
1189    ) -> Option<crate::support::Ref<'_, TextureAnimation>> {
1190        if index >= self.texture_animations_len() {
1191            return None;
1192        }
1193        // SAFETY: index checked above; the pointer is interior to `self`.
1194        unsafe {
1195            Some(crate::support::Ref::new(TextureAnimation {
1196                raw: core::ptr::NonNull::new_unchecked(
1197                    ffi::whiteout_mdx_MdxModel_get_textureAnimations_at(self.raw.as_ptr(), index),
1198                ),
1199            }))
1200        }
1201    }
1202
1203    pub fn texture_animations_mut(
1204        &mut self,
1205        index: usize,
1206    ) -> Option<crate::support::RefMut<'_, TextureAnimation>> {
1207        if index >= self.texture_animations_len() {
1208            return None;
1209        }
1210        // SAFETY: as above; `&mut self` guarantees exclusivity.
1211        unsafe {
1212            Some(crate::support::RefMut::new(TextureAnimation {
1213                raw: core::ptr::NonNull::new_unchecked(
1214                    ffi::whiteout_mdx_MdxModel_get_textureAnimations_at(self.raw.as_ptr(), index),
1215                ),
1216            }))
1217        }
1218    }
1219
1220    /// Iterate the elements, borrowing each in turn.
1221    pub fn texture_animations_iter(
1222        &self,
1223    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, TextureAnimation>> {
1224        (0..self.texture_animations_len())
1225            .map(move |i| self.texture_animations(i).expect("index below len"))
1226    }
1227
1228    pub fn resize_texture_animations(&mut self, count: usize) {
1229        // SAFETY: exclusive access, so no borrow is outstanding.
1230        unsafe { ffi::whiteout_mdx_MdxModel_resize_textureAnimations(self.raw.as_ptr(), count) }
1231    }
1232
1233    /// Mesh geometry
1234    pub fn geosets_len(&self) -> usize {
1235        // SAFETY: scalar read through a live handle.
1236        unsafe { ffi::whiteout_mdx_MdxModel_get_geosets_count(self.raw.as_ptr()) }
1237    }
1238
1239    /// Borrows element `index` in place. `None` when out of range.
1240    pub fn geosets(&self, index: usize) -> Option<crate::support::Ref<'_, Geoset>> {
1241        if index >= self.geosets_len() {
1242            return None;
1243        }
1244        // SAFETY: index checked above; the pointer is interior to `self`.
1245        unsafe {
1246            Some(crate::support::Ref::new(Geoset {
1247                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_geosets_at(
1248                    self.raw.as_ptr(),
1249                    index,
1250                )),
1251            }))
1252        }
1253    }
1254
1255    pub fn geosets_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Geoset>> {
1256        if index >= self.geosets_len() {
1257            return None;
1258        }
1259        // SAFETY: as above; `&mut self` guarantees exclusivity.
1260        unsafe {
1261            Some(crate::support::RefMut::new(Geoset {
1262                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_geosets_at(
1263                    self.raw.as_ptr(),
1264                    index,
1265                )),
1266            }))
1267        }
1268    }
1269
1270    /// Iterate the elements, borrowing each in turn.
1271    pub fn geosets_iter(&self) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Geoset>> {
1272        (0..self.geosets_len()).map(move |i| self.geosets(i).expect("index below len"))
1273    }
1274
1275    pub fn resize_geosets(&mut self, count: usize) {
1276        // SAFETY: exclusive access, so no borrow is outstanding.
1277        unsafe { ffi::whiteout_mdx_MdxModel_resize_geosets(self.raw.as_ptr(), count) }
1278    }
1279
1280    /// Mesh visibility/color animation
1281    pub fn geoset_animations_len(&self) -> usize {
1282        // SAFETY: scalar read through a live handle.
1283        unsafe { ffi::whiteout_mdx_MdxModel_get_geosetAnimations_count(self.raw.as_ptr()) }
1284    }
1285
1286    /// Borrows element `index` in place. `None` when out of range.
1287    pub fn geoset_animations(
1288        &self,
1289        index: usize,
1290    ) -> Option<crate::support::Ref<'_, GeosetAnimation>> {
1291        if index >= self.geoset_animations_len() {
1292            return None;
1293        }
1294        // SAFETY: index checked above; the pointer is interior to `self`.
1295        unsafe {
1296            Some(crate::support::Ref::new(GeosetAnimation {
1297                raw: core::ptr::NonNull::new_unchecked(
1298                    ffi::whiteout_mdx_MdxModel_get_geosetAnimations_at(self.raw.as_ptr(), index),
1299                ),
1300            }))
1301        }
1302    }
1303
1304    pub fn geoset_animations_mut(
1305        &mut self,
1306        index: usize,
1307    ) -> Option<crate::support::RefMut<'_, GeosetAnimation>> {
1308        if index >= self.geoset_animations_len() {
1309            return None;
1310        }
1311        // SAFETY: as above; `&mut self` guarantees exclusivity.
1312        unsafe {
1313            Some(crate::support::RefMut::new(GeosetAnimation {
1314                raw: core::ptr::NonNull::new_unchecked(
1315                    ffi::whiteout_mdx_MdxModel_get_geosetAnimations_at(self.raw.as_ptr(), index),
1316                ),
1317            }))
1318        }
1319    }
1320
1321    /// Iterate the elements, borrowing each in turn.
1322    pub fn geoset_animations_iter(
1323        &self,
1324    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, GeosetAnimation>> {
1325        (0..self.geoset_animations_len())
1326            .map(move |i| self.geoset_animations(i).expect("index below len"))
1327    }
1328
1329    pub fn resize_geoset_animations(&mut self, count: usize) {
1330        // SAFETY: exclusive access, so no borrow is outstanding.
1331        unsafe { ffi::whiteout_mdx_MdxModel_resize_geosetAnimations(self.raw.as_ptr(), count) }
1332    }
1333
1334    /// Skeleton bones
1335    pub fn bones_len(&self) -> usize {
1336        // SAFETY: scalar read through a live handle.
1337        unsafe { ffi::whiteout_mdx_MdxModel_get_bones_count(self.raw.as_ptr()) }
1338    }
1339
1340    /// Borrows element `index` in place. `None` when out of range.
1341    pub fn bones(&self, index: usize) -> Option<crate::support::Ref<'_, Bone>> {
1342        if index >= self.bones_len() {
1343            return None;
1344        }
1345        // SAFETY: index checked above; the pointer is interior to `self`.
1346        unsafe {
1347            Some(crate::support::Ref::new(Bone {
1348                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_bones_at(
1349                    self.raw.as_ptr(),
1350                    index,
1351                )),
1352            }))
1353        }
1354    }
1355
1356    pub fn bones_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Bone>> {
1357        if index >= self.bones_len() {
1358            return None;
1359        }
1360        // SAFETY: as above; `&mut self` guarantees exclusivity.
1361        unsafe {
1362            Some(crate::support::RefMut::new(Bone {
1363                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_bones_at(
1364                    self.raw.as_ptr(),
1365                    index,
1366                )),
1367            }))
1368        }
1369    }
1370
1371    /// Iterate the elements, borrowing each in turn.
1372    pub fn bones_iter(&self) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Bone>> {
1373        (0..self.bones_len()).map(move |i| self.bones(i).expect("index below len"))
1374    }
1375
1376    pub fn resize_bones(&mut self, count: usize) {
1377        // SAFETY: exclusive access, so no borrow is outstanding.
1378        unsafe { ffi::whiteout_mdx_MdxModel_resize_bones(self.raw.as_ptr(), count) }
1379    }
1380
1381    /// Helper/attachment nodes
1382    pub fn helpers_len(&self) -> usize {
1383        // SAFETY: scalar read through a live handle.
1384        unsafe { ffi::whiteout_mdx_MdxModel_get_helpers_count(self.raw.as_ptr()) }
1385    }
1386
1387    /// Borrows element `index` in place. `None` when out of range.
1388    pub fn helpers(&self, index: usize) -> Option<crate::support::Ref<'_, Helper>> {
1389        if index >= self.helpers_len() {
1390            return None;
1391        }
1392        // SAFETY: index checked above; the pointer is interior to `self`.
1393        unsafe {
1394            Some(crate::support::Ref::new(Helper {
1395                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_helpers_at(
1396                    self.raw.as_ptr(),
1397                    index,
1398                )),
1399            }))
1400        }
1401    }
1402
1403    pub fn helpers_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Helper>> {
1404        if index >= self.helpers_len() {
1405            return None;
1406        }
1407        // SAFETY: as above; `&mut self` guarantees exclusivity.
1408        unsafe {
1409            Some(crate::support::RefMut::new(Helper {
1410                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_helpers_at(
1411                    self.raw.as_ptr(),
1412                    index,
1413                )),
1414            }))
1415        }
1416    }
1417
1418    /// Iterate the elements, borrowing each in turn.
1419    pub fn helpers_iter(&self) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Helper>> {
1420        (0..self.helpers_len()).map(move |i| self.helpers(i).expect("index below len"))
1421    }
1422
1423    pub fn resize_helpers(&mut self, count: usize) {
1424        // SAFETY: exclusive access, so no borrow is outstanding.
1425        unsafe { ffi::whiteout_mdx_MdxModel_resize_helpers(self.raw.as_ptr(), count) }
1426    }
1427
1428    /// Equipment attachment points
1429    pub fn attachments_len(&self) -> usize {
1430        // SAFETY: scalar read through a live handle.
1431        unsafe { ffi::whiteout_mdx_MdxModel_get_attachments_count(self.raw.as_ptr()) }
1432    }
1433
1434    /// Borrows element `index` in place. `None` when out of range.
1435    pub fn attachments(&self, index: usize) -> Option<crate::support::Ref<'_, Attachment>> {
1436        if index >= self.attachments_len() {
1437            return None;
1438        }
1439        // SAFETY: index checked above; the pointer is interior to `self`.
1440        unsafe {
1441            Some(crate::support::Ref::new(Attachment {
1442                raw: core::ptr::NonNull::new_unchecked(
1443                    ffi::whiteout_mdx_MdxModel_get_attachments_at(self.raw.as_ptr(), index),
1444                ),
1445            }))
1446        }
1447    }
1448
1449    pub fn attachments_mut(
1450        &mut self,
1451        index: usize,
1452    ) -> Option<crate::support::RefMut<'_, Attachment>> {
1453        if index >= self.attachments_len() {
1454            return None;
1455        }
1456        // SAFETY: as above; `&mut self` guarantees exclusivity.
1457        unsafe {
1458            Some(crate::support::RefMut::new(Attachment {
1459                raw: core::ptr::NonNull::new_unchecked(
1460                    ffi::whiteout_mdx_MdxModel_get_attachments_at(self.raw.as_ptr(), index),
1461                ),
1462            }))
1463        }
1464    }
1465
1466    /// Iterate the elements, borrowing each in turn.
1467    pub fn attachments_iter(
1468        &self,
1469    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Attachment>> {
1470        (0..self.attachments_len()).map(move |i| self.attachments(i).expect("index below len"))
1471    }
1472
1473    pub fn resize_attachments(&mut self, count: usize) {
1474        // SAFETY: exclusive access, so no borrow is outstanding.
1475        unsafe { ffi::whiteout_mdx_MdxModel_resize_attachments(self.raw.as_ptr(), count) }
1476    }
1477
1478    /// Pivot points for nodes
1479    /// Zero-copy view of the underlying `std::vector`.
1480    pub fn pivot_points(&self) -> &[crate::math::Vector3f] {
1481        // SAFETY: `_data`/`_count` describe one contiguous C++
1482        // allocation, borrowed for as long as `self` is.
1483        unsafe {
1484            let n = ffi::whiteout_mdx_MdxModel_get_pivotPoints_count(self.raw.as_ptr());
1485            let p = ffi::whiteout_mdx_MdxModel_get_pivotPoints_data(self.raw.as_ptr())
1486                as *const crate::math::Vector3f;
1487            if p.is_null() || n == 0 {
1488                &[]
1489            } else {
1490                core::slice::from_raw_parts(p, n)
1491            }
1492        }
1493    }
1494
1495    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
1496    pub fn pivot_points_mut(&mut self) -> &mut [crate::math::Vector3f] {
1497        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
1498        unsafe {
1499            let n = ffi::whiteout_mdx_MdxModel_get_pivotPoints_count(self.raw.as_ptr());
1500            let p = ffi::whiteout_mdx_MdxModel_get_pivotPoints_data(self.raw.as_ptr())
1501                as *const crate::math::Vector3f as *mut crate::math::Vector3f;
1502            if p.is_null() || n == 0 {
1503                &mut []
1504            } else {
1505                core::slice::from_raw_parts_mut(p, n)
1506            }
1507        }
1508    }
1509
1510    pub fn set_pivot_points(&mut self, values: &[crate::math::Vector3f]) {
1511        // SAFETY: the native side copies `values` before returning.
1512        unsafe {
1513            ffi::whiteout_mdx_MdxModel_assign_pivotPoints(
1514                self.raw.as_ptr(),
1515                values.as_ptr() as *const _,
1516                values.len(),
1517            )
1518        }
1519    }
1520
1521    pub fn resize_pivot_points(&mut self, count: usize) {
1522        // SAFETY: reallocation is safe here precisely because
1523        // `&mut self` means no slice borrow is outstanding.
1524        unsafe { ffi::whiteout_mdx_MdxModel_resize_pivotPoints(self.raw.as_ptr(), count) }
1525    }
1526
1527    /// Light sources
1528    pub fn lights_len(&self) -> usize {
1529        // SAFETY: scalar read through a live handle.
1530        unsafe { ffi::whiteout_mdx_MdxModel_get_lights_count(self.raw.as_ptr()) }
1531    }
1532
1533    /// Borrows element `index` in place. `None` when out of range.
1534    pub fn lights(&self, index: usize) -> Option<crate::support::Ref<'_, Light>> {
1535        if index >= self.lights_len() {
1536            return None;
1537        }
1538        // SAFETY: index checked above; the pointer is interior to `self`.
1539        unsafe {
1540            Some(crate::support::Ref::new(Light {
1541                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_lights_at(
1542                    self.raw.as_ptr(),
1543                    index,
1544                )),
1545            }))
1546        }
1547    }
1548
1549    pub fn lights_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Light>> {
1550        if index >= self.lights_len() {
1551            return None;
1552        }
1553        // SAFETY: as above; `&mut self` guarantees exclusivity.
1554        unsafe {
1555            Some(crate::support::RefMut::new(Light {
1556                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_lights_at(
1557                    self.raw.as_ptr(),
1558                    index,
1559                )),
1560            }))
1561        }
1562    }
1563
1564    /// Iterate the elements, borrowing each in turn.
1565    pub fn lights_iter(&self) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Light>> {
1566        (0..self.lights_len()).map(move |i| self.lights(i).expect("index below len"))
1567    }
1568
1569    pub fn resize_lights(&mut self, count: usize) {
1570        // SAFETY: exclusive access, so no borrow is outstanding.
1571        unsafe { ffi::whiteout_mdx_MdxModel_resize_lights(self.raw.as_ptr(), count) }
1572    }
1573
1574    /// Particle emitters v1
1575    pub fn particle_emitters_len(&self) -> usize {
1576        // SAFETY: scalar read through a live handle.
1577        unsafe { ffi::whiteout_mdx_MdxModel_get_particleEmitters_count(self.raw.as_ptr()) }
1578    }
1579
1580    /// Borrows element `index` in place. `None` when out of range.
1581    pub fn particle_emitters(
1582        &self,
1583        index: usize,
1584    ) -> Option<crate::support::Ref<'_, ParticleEmitter>> {
1585        if index >= self.particle_emitters_len() {
1586            return None;
1587        }
1588        // SAFETY: index checked above; the pointer is interior to `self`.
1589        unsafe {
1590            Some(crate::support::Ref::new(ParticleEmitter {
1591                raw: core::ptr::NonNull::new_unchecked(
1592                    ffi::whiteout_mdx_MdxModel_get_particleEmitters_at(self.raw.as_ptr(), index),
1593                ),
1594            }))
1595        }
1596    }
1597
1598    pub fn particle_emitters_mut(
1599        &mut self,
1600        index: usize,
1601    ) -> Option<crate::support::RefMut<'_, ParticleEmitter>> {
1602        if index >= self.particle_emitters_len() {
1603            return None;
1604        }
1605        // SAFETY: as above; `&mut self` guarantees exclusivity.
1606        unsafe {
1607            Some(crate::support::RefMut::new(ParticleEmitter {
1608                raw: core::ptr::NonNull::new_unchecked(
1609                    ffi::whiteout_mdx_MdxModel_get_particleEmitters_at(self.raw.as_ptr(), index),
1610                ),
1611            }))
1612        }
1613    }
1614
1615    /// Iterate the elements, borrowing each in turn.
1616    pub fn particle_emitters_iter(
1617        &self,
1618    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, ParticleEmitter>> {
1619        (0..self.particle_emitters_len())
1620            .map(move |i| self.particle_emitters(i).expect("index below len"))
1621    }
1622
1623    pub fn resize_particle_emitters(&mut self, count: usize) {
1624        // SAFETY: exclusive access, so no borrow is outstanding.
1625        unsafe { ffi::whiteout_mdx_MdxModel_resize_particleEmitters(self.raw.as_ptr(), count) }
1626    }
1627
1628    /// Particle emitters v2
1629    pub fn particle_emitters_2_len(&self) -> usize {
1630        // SAFETY: scalar read through a live handle.
1631        unsafe { ffi::whiteout_mdx_MdxModel_get_particleEmitters2_count(self.raw.as_ptr()) }
1632    }
1633
1634    /// Borrows element `index` in place. `None` when out of range.
1635    pub fn particle_emitters_2(
1636        &self,
1637        index: usize,
1638    ) -> Option<crate::support::Ref<'_, ParticleEmitter2>> {
1639        if index >= self.particle_emitters_2_len() {
1640            return None;
1641        }
1642        // SAFETY: index checked above; the pointer is interior to `self`.
1643        unsafe {
1644            Some(crate::support::Ref::new(ParticleEmitter2 {
1645                raw: core::ptr::NonNull::new_unchecked(
1646                    ffi::whiteout_mdx_MdxModel_get_particleEmitters2_at(self.raw.as_ptr(), index),
1647                ),
1648            }))
1649        }
1650    }
1651
1652    pub fn particle_emitters_2_mut(
1653        &mut self,
1654        index: usize,
1655    ) -> Option<crate::support::RefMut<'_, ParticleEmitter2>> {
1656        if index >= self.particle_emitters_2_len() {
1657            return None;
1658        }
1659        // SAFETY: as above; `&mut self` guarantees exclusivity.
1660        unsafe {
1661            Some(crate::support::RefMut::new(ParticleEmitter2 {
1662                raw: core::ptr::NonNull::new_unchecked(
1663                    ffi::whiteout_mdx_MdxModel_get_particleEmitters2_at(self.raw.as_ptr(), index),
1664                ),
1665            }))
1666        }
1667    }
1668
1669    /// Iterate the elements, borrowing each in turn.
1670    pub fn particle_emitters_2_iter(
1671        &self,
1672    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, ParticleEmitter2>> {
1673        (0..self.particle_emitters_2_len())
1674            .map(move |i| self.particle_emitters_2(i).expect("index below len"))
1675    }
1676
1677    pub fn resize_particle_emitters_2(&mut self, count: usize) {
1678        // SAFETY: exclusive access, so no borrow is outstanding.
1679        unsafe { ffi::whiteout_mdx_MdxModel_resize_particleEmitters2(self.raw.as_ptr(), count) }
1680    }
1681
1682    /// Ribbon trail effects
1683    pub fn ribbon_emitters_len(&self) -> usize {
1684        // SAFETY: scalar read through a live handle.
1685        unsafe { ffi::whiteout_mdx_MdxModel_get_ribbonEmitters_count(self.raw.as_ptr()) }
1686    }
1687
1688    /// Borrows element `index` in place. `None` when out of range.
1689    pub fn ribbon_emitters(&self, index: usize) -> Option<crate::support::Ref<'_, RibbonEmitter>> {
1690        if index >= self.ribbon_emitters_len() {
1691            return None;
1692        }
1693        // SAFETY: index checked above; the pointer is interior to `self`.
1694        unsafe {
1695            Some(crate::support::Ref::new(RibbonEmitter {
1696                raw: core::ptr::NonNull::new_unchecked(
1697                    ffi::whiteout_mdx_MdxModel_get_ribbonEmitters_at(self.raw.as_ptr(), index),
1698                ),
1699            }))
1700        }
1701    }
1702
1703    pub fn ribbon_emitters_mut(
1704        &mut self,
1705        index: usize,
1706    ) -> Option<crate::support::RefMut<'_, RibbonEmitter>> {
1707        if index >= self.ribbon_emitters_len() {
1708            return None;
1709        }
1710        // SAFETY: as above; `&mut self` guarantees exclusivity.
1711        unsafe {
1712            Some(crate::support::RefMut::new(RibbonEmitter {
1713                raw: core::ptr::NonNull::new_unchecked(
1714                    ffi::whiteout_mdx_MdxModel_get_ribbonEmitters_at(self.raw.as_ptr(), index),
1715                ),
1716            }))
1717        }
1718    }
1719
1720    /// Iterate the elements, borrowing each in turn.
1721    pub fn ribbon_emitters_iter(
1722        &self,
1723    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, RibbonEmitter>> {
1724        (0..self.ribbon_emitters_len())
1725            .map(move |i| self.ribbon_emitters(i).expect("index below len"))
1726    }
1727
1728    pub fn resize_ribbon_emitters(&mut self, count: usize) {
1729        // SAFETY: exclusive access, so no borrow is outstanding.
1730        unsafe { ffi::whiteout_mdx_MdxModel_resize_ribbonEmitters(self.raw.as_ptr(), count) }
1731    }
1732
1733    /// PopcornFX emitters (Reforged)
1734    pub fn corn_emitters_len(&self) -> usize {
1735        // SAFETY: scalar read through a live handle.
1736        unsafe { ffi::whiteout_mdx_MdxModel_get_cornEmitters_count(self.raw.as_ptr()) }
1737    }
1738
1739    /// Borrows element `index` in place. `None` when out of range.
1740    pub fn corn_emitters(&self, index: usize) -> Option<crate::support::Ref<'_, CornEmitter>> {
1741        if index >= self.corn_emitters_len() {
1742            return None;
1743        }
1744        // SAFETY: index checked above; the pointer is interior to `self`.
1745        unsafe {
1746            Some(crate::support::Ref::new(CornEmitter {
1747                raw: core::ptr::NonNull::new_unchecked(
1748                    ffi::whiteout_mdx_MdxModel_get_cornEmitters_at(self.raw.as_ptr(), index),
1749                ),
1750            }))
1751        }
1752    }
1753
1754    pub fn corn_emitters_mut(
1755        &mut self,
1756        index: usize,
1757    ) -> Option<crate::support::RefMut<'_, CornEmitter>> {
1758        if index >= self.corn_emitters_len() {
1759            return None;
1760        }
1761        // SAFETY: as above; `&mut self` guarantees exclusivity.
1762        unsafe {
1763            Some(crate::support::RefMut::new(CornEmitter {
1764                raw: core::ptr::NonNull::new_unchecked(
1765                    ffi::whiteout_mdx_MdxModel_get_cornEmitters_at(self.raw.as_ptr(), index),
1766                ),
1767            }))
1768        }
1769    }
1770
1771    /// Iterate the elements, borrowing each in turn.
1772    pub fn corn_emitters_iter(
1773        &self,
1774    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, CornEmitter>> {
1775        (0..self.corn_emitters_len()).map(move |i| self.corn_emitters(i).expect("index below len"))
1776    }
1777
1778    pub fn resize_corn_emitters(&mut self, count: usize) {
1779        // SAFETY: exclusive access, so no borrow is outstanding.
1780        unsafe { ffi::whiteout_mdx_MdxModel_resize_cornEmitters(self.raw.as_ptr(), count) }
1781    }
1782
1783    /// Animation event triggers
1784    pub fn event_objects_len(&self) -> usize {
1785        // SAFETY: scalar read through a live handle.
1786        unsafe { ffi::whiteout_mdx_MdxModel_get_eventObjects_count(self.raw.as_ptr()) }
1787    }
1788
1789    /// Borrows element `index` in place. `None` when out of range.
1790    pub fn event_objects(&self, index: usize) -> Option<crate::support::Ref<'_, EventObject>> {
1791        if index >= self.event_objects_len() {
1792            return None;
1793        }
1794        // SAFETY: index checked above; the pointer is interior to `self`.
1795        unsafe {
1796            Some(crate::support::Ref::new(EventObject {
1797                raw: core::ptr::NonNull::new_unchecked(
1798                    ffi::whiteout_mdx_MdxModel_get_eventObjects_at(self.raw.as_ptr(), index),
1799                ),
1800            }))
1801        }
1802    }
1803
1804    pub fn event_objects_mut(
1805        &mut self,
1806        index: usize,
1807    ) -> Option<crate::support::RefMut<'_, EventObject>> {
1808        if index >= self.event_objects_len() {
1809            return None;
1810        }
1811        // SAFETY: as above; `&mut self` guarantees exclusivity.
1812        unsafe {
1813            Some(crate::support::RefMut::new(EventObject {
1814                raw: core::ptr::NonNull::new_unchecked(
1815                    ffi::whiteout_mdx_MdxModel_get_eventObjects_at(self.raw.as_ptr(), index),
1816                ),
1817            }))
1818        }
1819    }
1820
1821    /// Iterate the elements, borrowing each in turn.
1822    pub fn event_objects_iter(
1823        &self,
1824    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, EventObject>> {
1825        (0..self.event_objects_len()).map(move |i| self.event_objects(i).expect("index below len"))
1826    }
1827
1828    pub fn resize_event_objects(&mut self, count: usize) {
1829        // SAFETY: exclusive access, so no borrow is outstanding.
1830        unsafe { ffi::whiteout_mdx_MdxModel_resize_eventObjects(self.raw.as_ptr(), count) }
1831    }
1832
1833    /// Camera definitions
1834    pub fn cameras_len(&self) -> usize {
1835        // SAFETY: scalar read through a live handle.
1836        unsafe { ffi::whiteout_mdx_MdxModel_get_cameras_count(self.raw.as_ptr()) }
1837    }
1838
1839    /// Borrows element `index` in place. `None` when out of range.
1840    pub fn cameras(&self, index: usize) -> Option<crate::support::Ref<'_, Camera>> {
1841        if index >= self.cameras_len() {
1842            return None;
1843        }
1844        // SAFETY: index checked above; the pointer is interior to `self`.
1845        unsafe {
1846            Some(crate::support::Ref::new(Camera {
1847                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_cameras_at(
1848                    self.raw.as_ptr(),
1849                    index,
1850                )),
1851            }))
1852        }
1853    }
1854
1855    pub fn cameras_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Camera>> {
1856        if index >= self.cameras_len() {
1857            return None;
1858        }
1859        // SAFETY: as above; `&mut self` guarantees exclusivity.
1860        unsafe {
1861            Some(crate::support::RefMut::new(Camera {
1862                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxModel_get_cameras_at(
1863                    self.raw.as_ptr(),
1864                    index,
1865                )),
1866            }))
1867        }
1868    }
1869
1870    /// Iterate the elements, borrowing each in turn.
1871    pub fn cameras_iter(&self) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Camera>> {
1872        (0..self.cameras_len()).map(move |i| self.cameras(i).expect("index below len"))
1873    }
1874
1875    pub fn resize_cameras(&mut self, count: usize) {
1876        // SAFETY: exclusive access, so no borrow is outstanding.
1877        unsafe { ffi::whiteout_mdx_MdxModel_resize_cameras(self.raw.as_ptr(), count) }
1878    }
1879
1880    /// Collision geometry
1881    pub fn collision_shapes_len(&self) -> usize {
1882        // SAFETY: scalar read through a live handle.
1883        unsafe { ffi::whiteout_mdx_MdxModel_get_collisionShapes_count(self.raw.as_ptr()) }
1884    }
1885
1886    /// Borrows element `index` in place. `None` when out of range.
1887    pub fn collision_shapes(
1888        &self,
1889        index: usize,
1890    ) -> Option<crate::support::Ref<'_, CollisionShape>> {
1891        if index >= self.collision_shapes_len() {
1892            return None;
1893        }
1894        // SAFETY: index checked above; the pointer is interior to `self`.
1895        unsafe {
1896            Some(crate::support::Ref::new(CollisionShape {
1897                raw: core::ptr::NonNull::new_unchecked(
1898                    ffi::whiteout_mdx_MdxModel_get_collisionShapes_at(self.raw.as_ptr(), index),
1899                ),
1900            }))
1901        }
1902    }
1903
1904    pub fn collision_shapes_mut(
1905        &mut self,
1906        index: usize,
1907    ) -> Option<crate::support::RefMut<'_, CollisionShape>> {
1908        if index >= self.collision_shapes_len() {
1909            return None;
1910        }
1911        // SAFETY: as above; `&mut self` guarantees exclusivity.
1912        unsafe {
1913            Some(crate::support::RefMut::new(CollisionShape {
1914                raw: core::ptr::NonNull::new_unchecked(
1915                    ffi::whiteout_mdx_MdxModel_get_collisionShapes_at(self.raw.as_ptr(), index),
1916                ),
1917            }))
1918        }
1919    }
1920
1921    /// Iterate the elements, borrowing each in turn.
1922    pub fn collision_shapes_iter(
1923        &self,
1924    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, CollisionShape>> {
1925        (0..self.collision_shapes_len())
1926            .map(move |i| self.collision_shapes(i).expect("index below len"))
1927    }
1928
1929    pub fn resize_collision_shapes(&mut self, count: usize) {
1930        // SAFETY: exclusive access, so no borrow is outstanding.
1931        unsafe { ffi::whiteout_mdx_MdxModel_resize_collisionShapes(self.raw.as_ptr(), count) }
1932    }
1933
1934    /// Facial effects (Reforged)
1935    pub fn face_effects_len(&self) -> usize {
1936        // SAFETY: scalar read through a live handle.
1937        unsafe { ffi::whiteout_mdx_MdxModel_get_faceEffects_count(self.raw.as_ptr()) }
1938    }
1939
1940    /// Borrows element `index` in place. `None` when out of range.
1941    pub fn face_effects(&self, index: usize) -> Option<crate::support::Ref<'_, FaceEffect>> {
1942        if index >= self.face_effects_len() {
1943            return None;
1944        }
1945        // SAFETY: index checked above; the pointer is interior to `self`.
1946        unsafe {
1947            Some(crate::support::Ref::new(FaceEffect {
1948                raw: core::ptr::NonNull::new_unchecked(
1949                    ffi::whiteout_mdx_MdxModel_get_faceEffects_at(self.raw.as_ptr(), index),
1950                ),
1951            }))
1952        }
1953    }
1954
1955    pub fn face_effects_mut(
1956        &mut self,
1957        index: usize,
1958    ) -> Option<crate::support::RefMut<'_, FaceEffect>> {
1959        if index >= self.face_effects_len() {
1960            return None;
1961        }
1962        // SAFETY: as above; `&mut self` guarantees exclusivity.
1963        unsafe {
1964            Some(crate::support::RefMut::new(FaceEffect {
1965                raw: core::ptr::NonNull::new_unchecked(
1966                    ffi::whiteout_mdx_MdxModel_get_faceEffects_at(self.raw.as_ptr(), index),
1967                ),
1968            }))
1969        }
1970    }
1971
1972    /// Iterate the elements, borrowing each in turn.
1973    pub fn face_effects_iter(
1974        &self,
1975    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, FaceEffect>> {
1976        (0..self.face_effects_len()).map(move |i| self.face_effects(i).expect("index below len"))
1977    }
1978
1979    pub fn resize_face_effects(&mut self, count: usize) {
1980        // SAFETY: exclusive access, so no borrow is outstanding.
1981        unsafe { ffi::whiteout_mdx_MdxModel_resize_faceEffects(self.raw.as_ptr(), count) }
1982    }
1983}
1984
1985impl Default for Model {
1986    fn default() -> Self {
1987        Self::new()
1988    }
1989}
1990
1991/// Animation sequence definition
1992///
1993/// Sequences define named animation clips with specific frame ranges. Examples: "Stand", "Walk", "Attack", "Death"
1994pub struct Sequence {
1995    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxSequence>,
1996}
1997
1998impl Drop for Sequence {
1999    fn drop(&mut self) {
2000        // SAFETY: `raw` came from a native constructor and Drop runs once.
2001        unsafe { ffi::whiteout_mdx_MdxSequence_delete(self.raw.as_ptr()) }
2002    }
2003}
2004
2005impl Sequence {
2006    /// # Safety
2007    /// `raw` must be a live handle this value takes ownership of.
2008    #[allow(dead_code)] // used by whichever methods return this type
2009    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxSequence) -> Option<Self> {
2010        core::ptr::NonNull::new(raw).map(|raw| Sequence { raw })
2011    }
2012}
2013
2014// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
2015// is deliberately NOT implemented — the C++ types make no documented
2016// guarantee about concurrent use, and claiming one we haven't verified
2017// would be unsound. See `@bind thread_safe` in the plan.
2018unsafe impl Send for Sequence {}
2019
2020impl core::fmt::Debug for Sequence {
2021    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2022        f.debug_struct("Sequence").finish_non_exhaustive()
2023    }
2024}
2025
2026impl Sequence {
2027    /// # Panics
2028    /// Panics if the native allocation fails.
2029    pub fn new() -> Self {
2030        // SAFETY: the native constructor returns a live handle; a null here
2031        // means the library is unusable.
2032        unsafe {
2033            let raw = ffi::whiteout_mdx_MdxSequence_new();
2034            Self::from_raw(raw).expect("native Sequence allocation failed")
2035        }
2036    }
2037
2038    /// Sequence name (e.g., "Stand", "Walk")
2039    pub fn name(&self) -> String {
2040        // SAFETY: the native side hands over an owned CString.
2041        unsafe {
2042            crate::support::take_string(ffi::whiteout_mdx_MdxSequence_get_name(self.raw.as_ptr()))
2043        }
2044    }
2045
2046    pub fn set_name(&mut self, value: &str) {
2047        let value = std::ffi::CString::new(value).unwrap_or_default();
2048        // SAFETY: the pointer outlives the call.
2049        unsafe { ffi::whiteout_mdx_MdxSequence_set_name(self.raw.as_ptr(), value.as_ptr()) }
2050    }
2051
2052    /// Starting frame number
2053    pub fn interval_start(&self) -> u32 {
2054        // SAFETY: plain scalar read through a live handle.
2055        unsafe { ffi::whiteout_mdx_MdxSequence_get_intervalStart(self.raw.as_ptr()) }
2056    }
2057
2058    pub fn set_interval_start(&mut self, value: u32) {
2059        // SAFETY: plain scalar write through a live handle.
2060        unsafe { ffi::whiteout_mdx_MdxSequence_set_intervalStart(self.raw.as_ptr(), value) }
2061    }
2062
2063    /// Ending frame number (exclusive)
2064    pub fn interval_end(&self) -> u32 {
2065        // SAFETY: plain scalar read through a live handle.
2066        unsafe { ffi::whiteout_mdx_MdxSequence_get_intervalEnd(self.raw.as_ptr()) }
2067    }
2068
2069    pub fn set_interval_end(&mut self, value: u32) {
2070        // SAFETY: plain scalar write through a live handle.
2071        unsafe { ffi::whiteout_mdx_MdxSequence_set_intervalEnd(self.raw.as_ptr(), value) }
2072    }
2073
2074    /// Movement speed during this animation
2075    pub fn move_speed(&self) -> f32 {
2076        // SAFETY: plain scalar read through a live handle.
2077        unsafe { ffi::whiteout_mdx_MdxSequence_get_moveSpeed(self.raw.as_ptr()) }
2078    }
2079
2080    pub fn set_move_speed(&mut self, value: f32) {
2081        // SAFETY: plain scalar write through a live handle.
2082        unsafe { ffi::whiteout_mdx_MdxSequence_set_moveSpeed(self.raw.as_ptr(), value) }
2083    }
2084
2085    /// Playback flags
2086    pub fn flags(&self) -> SequenceFlag {
2087        // SAFETY: scalar read; the discriminant is validated below.
2088        unsafe { ffi::whiteout_mdx_MdxSequence_get_flags(self.raw.as_ptr()) }
2089            .try_into()
2090            .expect("unknown enum discriminant from the native library")
2091    }
2092
2093    pub fn set_flags(&mut self, value: SequenceFlag) {
2094        // SAFETY: scalar write through a live handle.
2095        unsafe { ffi::whiteout_mdx_MdxSequence_set_flags(self.raw.as_ptr(), value as i32) }
2096    }
2097
2098    /// Rarity factor for variation sequences
2099    pub fn rarity(&self) -> f32 {
2100        // SAFETY: plain scalar read through a live handle.
2101        unsafe { ffi::whiteout_mdx_MdxSequence_get_rarity(self.raw.as_ptr()) }
2102    }
2103
2104    pub fn set_rarity(&mut self, value: f32) {
2105        // SAFETY: plain scalar write through a live handle.
2106        unsafe { ffi::whiteout_mdx_MdxSequence_set_rarity(self.raw.as_ptr(), value) }
2107    }
2108
2109    /// Sync point for blending
2110    pub fn sync_point(&self) -> u32 {
2111        // SAFETY: plain scalar read through a live handle.
2112        unsafe { ffi::whiteout_mdx_MdxSequence_get_syncPoint(self.raw.as_ptr()) }
2113    }
2114
2115    pub fn set_sync_point(&mut self, value: u32) {
2116        // SAFETY: plain scalar write through a live handle.
2117        unsafe { ffi::whiteout_mdx_MdxSequence_set_syncPoint(self.raw.as_ptr(), value) }
2118    }
2119
2120    /// Bounding volume for this sequence
2121    /// Borrows the field in place — no copy, no allocation.
2122    pub fn extent(&self) -> crate::support::Ref<'_, Extent> {
2123        // SAFETY: an interior pointer into `self`, valid for this
2124        // borrow and never freed by the `Ref`.
2125        unsafe {
2126            crate::support::Ref::new(Extent {
2127                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxSequence_get_extent(
2128                    self.raw.as_ptr(),
2129                )),
2130            })
2131        }
2132    }
2133
2134    pub fn extent_mut(&mut self) -> crate::support::RefMut<'_, Extent> {
2135        // SAFETY: as above; `&mut self` guarantees exclusivity.
2136        unsafe {
2137            crate::support::RefMut::new(Extent {
2138                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxSequence_get_extent(
2139                    self.raw.as_ptr(),
2140                )),
2141            })
2142        }
2143    }
2144}
2145
2146impl Default for Sequence {
2147    fn default() -> Self {
2148        Self::new()
2149    }
2150}
2151
2152/// Texture definition
2153///
2154/// Defines a texture file path and its properties.
2155pub struct Texture {
2156    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTexture>,
2157}
2158
2159impl Drop for Texture {
2160    fn drop(&mut self) {
2161        // SAFETY: `raw` came from a native constructor and Drop runs once.
2162        unsafe { ffi::whiteout_mdx_MdxTexture_delete(self.raw.as_ptr()) }
2163    }
2164}
2165
2166impl Texture {
2167    /// # Safety
2168    /// `raw` must be a live handle this value takes ownership of.
2169    #[allow(dead_code)] // used by whichever methods return this type
2170    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTexture) -> Option<Self> {
2171        core::ptr::NonNull::new(raw).map(|raw| Texture { raw })
2172    }
2173}
2174
2175// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
2176// is deliberately NOT implemented — the C++ types make no documented
2177// guarantee about concurrent use, and claiming one we haven't verified
2178// would be unsound. See `@bind thread_safe` in the plan.
2179unsafe impl Send for Texture {}
2180
2181impl core::fmt::Debug for Texture {
2182    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2183        f.debug_struct("Texture").finish_non_exhaustive()
2184    }
2185}
2186
2187impl Texture {
2188    /// # Panics
2189    /// Panics if the native allocation fails.
2190    pub fn new() -> Self {
2191        // SAFETY: the native constructor returns a live handle; a null here
2192        // means the library is unusable.
2193        unsafe {
2194            let raw = ffi::whiteout_mdx_MdxTexture_new();
2195            Self::from_raw(raw).expect("native Texture allocation failed")
2196        }
2197    }
2198
2199    pub fn replaceable_id(&self) -> u32 {
2200        // SAFETY: plain scalar read through a live handle.
2201        unsafe { ffi::whiteout_mdx_MdxTexture_get_replaceableId(self.raw.as_ptr()) }
2202    }
2203
2204    pub fn set_replaceable_id(&mut self, value: u32) {
2205        // SAFETY: plain scalar write through a live handle.
2206        unsafe { ffi::whiteout_mdx_MdxTexture_set_replaceableId(self.raw.as_ptr(), value) }
2207    }
2208
2209    /// Path to texture file (BLP, DDS or TGA)
2210    pub fn file_name(&self) -> String {
2211        // SAFETY: the native side hands over an owned CString.
2212        unsafe {
2213            crate::support::take_string(ffi::whiteout_mdx_MdxTexture_get_fileName(
2214                self.raw.as_ptr(),
2215            ))
2216        }
2217    }
2218
2219    pub fn set_file_name(&mut self, value: &str) {
2220        let value = std::ffi::CString::new(value).unwrap_or_default();
2221        // SAFETY: the pointer outlives the call.
2222        unsafe { ffi::whiteout_mdx_MdxTexture_set_fileName(self.raw.as_ptr(), value.as_ptr()) }
2223    }
2224
2225    /// Texture wrap flags
2226    pub fn flags(&self) -> SequenceFlag {
2227        // SAFETY: scalar read; the discriminant is validated below.
2228        unsafe { ffi::whiteout_mdx_MdxTexture_get_flags(self.raw.as_ptr()) }
2229            .try_into()
2230            .expect("unknown enum discriminant from the native library")
2231    }
2232
2233    pub fn set_flags(&mut self, value: SequenceFlag) {
2234        // SAFETY: scalar write through a live handle.
2235        unsafe { ffi::whiteout_mdx_MdxTexture_set_flags(self.raw.as_ptr(), value as i32) }
2236    }
2237}
2238
2239impl Default for Texture {
2240    fn default() -> Self {
2241        Self::new()
2242    }
2243}
2244
2245/// Sound definition (deprecated, not used in shipped models)
2246///
2247/// Sounds define audio file paths and playback properties. While readers for this chunk exist in the internal game code, no known shipped model contains SNDS data.
2248///
2249/// Binary layout: 56 bytes per entry
2250pub struct Sound {
2251    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxSound>,
2252}
2253
2254impl Drop for Sound {
2255    fn drop(&mut self) {
2256        // SAFETY: `raw` came from a native constructor and Drop runs once.
2257        unsafe { ffi::whiteout_mdx_MdxSound_delete(self.raw.as_ptr()) }
2258    }
2259}
2260
2261impl Sound {
2262    /// # Safety
2263    /// `raw` must be a live handle this value takes ownership of.
2264    #[allow(dead_code)] // used by whichever methods return this type
2265    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxSound) -> Option<Self> {
2266        core::ptr::NonNull::new(raw).map(|raw| Sound { raw })
2267    }
2268}
2269
2270// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
2271// is deliberately NOT implemented — the C++ types make no documented
2272// guarantee about concurrent use, and claiming one we haven't verified
2273// would be unsound. See `@bind thread_safe` in the plan.
2274unsafe impl Send for Sound {}
2275
2276impl core::fmt::Debug for Sound {
2277    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2278        f.debug_struct("Sound").finish_non_exhaustive()
2279    }
2280}
2281
2282impl Sound {
2283    /// # Panics
2284    /// Panics if the native allocation fails.
2285    pub fn new() -> Self {
2286        // SAFETY: the native constructor returns a live handle; a null here
2287        // means the library is unusable.
2288        unsafe {
2289            let raw = ffi::whiteout_mdx_MdxSound_new();
2290            Self::from_raw(raw).expect("native Sound allocation failed")
2291        }
2292    }
2293
2294    /// Path to sound file (44 bytes, null-terminated)
2295    pub fn sound_file(&self) -> String {
2296        // SAFETY: the native side hands over an owned CString.
2297        unsafe {
2298            crate::support::take_string(ffi::whiteout_mdx_MdxSound_get_soundFile(self.raw.as_ptr()))
2299        }
2300    }
2301
2302    pub fn set_sound_file(&mut self, value: &str) {
2303        let value = std::ffi::CString::new(value).unwrap_or_default();
2304        // SAFETY: the pointer outlives the call.
2305        unsafe { ffi::whiteout_mdx_MdxSound_set_soundFile(self.raw.as_ptr(), value.as_ptr()) }
2306    }
2307
2308    /// Maximum audible distance
2309    pub fn maximum_distance(&self) -> f32 {
2310        // SAFETY: plain scalar read through a live handle.
2311        unsafe { ffi::whiteout_mdx_MdxSound_get_maximumDistance(self.raw.as_ptr()) }
2312    }
2313
2314    pub fn set_maximum_distance(&mut self, value: f32) {
2315        // SAFETY: plain scalar write through a live handle.
2316        unsafe { ffi::whiteout_mdx_MdxSound_set_maximumDistance(self.raw.as_ptr(), value) }
2317    }
2318
2319    /// Minimum distance (full volume)
2320    pub fn minimum_distance(&self) -> f32 {
2321        // SAFETY: plain scalar read through a live handle.
2322        unsafe { ffi::whiteout_mdx_MdxSound_get_minimumDistance(self.raw.as_ptr()) }
2323    }
2324
2325    pub fn set_minimum_distance(&mut self, value: f32) {
2326        // SAFETY: plain scalar write through a live handle.
2327        unsafe { ffi::whiteout_mdx_MdxSound_set_minimumDistance(self.raw.as_ptr(), value) }
2328    }
2329
2330    /// Sound channel identifier
2331    pub fn sound_channel(&self) -> u32 {
2332        // SAFETY: plain scalar read through a live handle.
2333        unsafe { ffi::whiteout_mdx_MdxSound_get_soundChannel(self.raw.as_ptr()) }
2334    }
2335
2336    pub fn set_sound_channel(&mut self, value: u32) {
2337        // SAFETY: plain scalar write through a live handle.
2338        unsafe { ffi::whiteout_mdx_MdxSound_set_soundChannel(self.raw.as_ptr(), value) }
2339    }
2340}
2341
2342impl Default for Sound {
2343    fn default() -> Self {
2344        Self::new()
2345    }
2346}
2347
2348/// Base node in the model hierarchy
2349///
2350/// Nodes form the skeleton and attachment system of the model. Each node can have a parent creating a transformation hierarchy. Nodes can be: - Bones (for skeletal animation) - Helpers (attachment points) - Lights - Particle emitters - And other special objects
2351///
2352/// Nodes contain transformation animation tracks (translation, rotation, scaling).
2353pub struct Node {
2354    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxNode>,
2355}
2356
2357impl Drop for Node {
2358    fn drop(&mut self) {
2359        // SAFETY: `raw` came from a native constructor and Drop runs once.
2360        unsafe { ffi::whiteout_mdx_MdxNode_delete(self.raw.as_ptr()) }
2361    }
2362}
2363
2364impl Node {
2365    /// # Safety
2366    /// `raw` must be a live handle this value takes ownership of.
2367    #[allow(dead_code)] // used by whichever methods return this type
2368    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxNode) -> Option<Self> {
2369        core::ptr::NonNull::new(raw).map(|raw| Node { raw })
2370    }
2371}
2372
2373// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
2374// is deliberately NOT implemented — the C++ types make no documented
2375// guarantee about concurrent use, and claiming one we haven't verified
2376// would be unsound. See `@bind thread_safe` in the plan.
2377unsafe impl Send for Node {}
2378
2379impl core::fmt::Debug for Node {
2380    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2381        f.debug_struct("Node").finish_non_exhaustive()
2382    }
2383}
2384
2385impl Node {
2386    /// # Panics
2387    /// Panics if the native allocation fails.
2388    pub fn new() -> Self {
2389        // SAFETY: the native constructor returns a live handle; a null here
2390        // means the library is unusable.
2391        unsafe {
2392            let raw = ffi::whiteout_mdx_MdxNode_new();
2393            Self::from_raw(raw).expect("native Node allocation failed")
2394        }
2395    }
2396
2397    /// Node name (for debugging/reference)
2398    pub fn name(&self) -> String {
2399        // SAFETY: the native side hands over an owned CString.
2400        unsafe {
2401            crate::support::take_string(ffi::whiteout_mdx_MdxNode_get_name(self.raw.as_ptr()))
2402        }
2403    }
2404
2405    pub fn set_name(&mut self, value: &str) {
2406        let value = std::ffi::CString::new(value).unwrap_or_default();
2407        // SAFETY: the pointer outlives the call.
2408        unsafe { ffi::whiteout_mdx_MdxNode_set_name(self.raw.as_ptr(), value.as_ptr()) }
2409    }
2410
2411    /// Unique ID for this node
2412    pub fn object_id(&self) -> u32 {
2413        // SAFETY: plain scalar read through a live handle.
2414        unsafe { ffi::whiteout_mdx_MdxNode_get_objectId(self.raw.as_ptr()) }
2415    }
2416
2417    pub fn set_object_id(&mut self, value: u32) {
2418        // SAFETY: plain scalar write through a live handle.
2419        unsafe { ffi::whiteout_mdx_MdxNode_set_objectId(self.raw.as_ptr(), value) }
2420    }
2421
2422    /// Parent node ID or NO_PARENT
2423    pub fn parent_id(&self) -> u32 {
2424        // SAFETY: plain scalar read through a live handle.
2425        unsafe { ffi::whiteout_mdx_MdxNode_get_parentId(self.raw.as_ptr()) }
2426    }
2427
2428    pub fn set_parent_id(&mut self, value: u32) {
2429        // SAFETY: plain scalar write through a live handle.
2430        unsafe { ffi::whiteout_mdx_MdxNode_set_parentId(self.raw.as_ptr(), value) }
2431    }
2432
2433    /// Combination of NodeFlag values
2434    pub fn flags(&self) -> NodeFlag {
2435        // SAFETY: scalar read; a flag set accepts any bits.
2436        NodeFlag(unsafe { ffi::whiteout_mdx_MdxNode_get_flags(self.raw.as_ptr()) })
2437    }
2438
2439    pub fn set_flags(&mut self, value: NodeFlag) {
2440        // SAFETY: scalar write through a live handle.
2441        unsafe { ffi::whiteout_mdx_MdxNode_set_flags(self.raw.as_ptr(), value.0) }
2442    }
2443
2444    /// Type of this node
2445    pub fn type_(&self) -> NodeType {
2446        // SAFETY: scalar read; the discriminant is validated below.
2447        unsafe { ffi::whiteout_mdx_MdxNode_get_type(self.raw.as_ptr()) }
2448            .try_into()
2449            .expect("unknown enum discriminant from the native library")
2450    }
2451
2452    pub fn set_type_(&mut self, value: NodeType) {
2453        // SAFETY: scalar write through a live handle.
2454        unsafe { ffi::whiteout_mdx_MdxNode_set_type(self.raw.as_ptr(), value as i32) }
2455    }
2456
2457    /// Used to link related nodes of the same type
2458    pub fn node_family_id(&self) -> u32 {
2459        // SAFETY: plain scalar read through a live handle.
2460        unsafe { ffi::whiteout_mdx_MdxNode_get_nodeFamilyId(self.raw.as_ptr()) }
2461    }
2462
2463    pub fn set_node_family_id(&mut self, value: u32) {
2464        // SAFETY: plain scalar write through a live handle.
2465        unsafe { ffi::whiteout_mdx_MdxNode_set_nodeFamilyId(self.raw.as_ptr(), value) }
2466    }
2467
2468    /// Position animation
2469    /// Borrows the field in place — no copy, no allocation.
2470    pub fn translation_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
2471        // SAFETY: an interior pointer into `self`, valid for this
2472        // borrow and never freed by the `Ref`.
2473        unsafe {
2474            crate::support::Ref::new(TrackVector3f {
2475                raw: core::ptr::NonNull::new_unchecked(
2476                    ffi::whiteout_mdx_MdxNode_get_translationTracks(self.raw.as_ptr()),
2477                ),
2478            })
2479        }
2480    }
2481
2482    pub fn translation_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
2483        // SAFETY: as above; `&mut self` guarantees exclusivity.
2484        unsafe {
2485            crate::support::RefMut::new(TrackVector3f {
2486                raw: core::ptr::NonNull::new_unchecked(
2487                    ffi::whiteout_mdx_MdxNode_get_translationTracks(self.raw.as_ptr()),
2488                ),
2489            })
2490        }
2491    }
2492
2493    /// Rotation animation (quaternion XYZW)
2494    /// Borrows the field in place — no copy, no allocation.
2495    pub fn rotation_tracks(&self) -> crate::support::Ref<'_, TrackQuaternion> {
2496        // SAFETY: an interior pointer into `self`, valid for this
2497        // borrow and never freed by the `Ref`.
2498        unsafe {
2499            crate::support::Ref::new(TrackQuaternion {
2500                raw: core::ptr::NonNull::new_unchecked(
2501                    ffi::whiteout_mdx_MdxNode_get_rotationTracks(self.raw.as_ptr()),
2502                ),
2503            })
2504        }
2505    }
2506
2507    pub fn rotation_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackQuaternion> {
2508        // SAFETY: as above; `&mut self` guarantees exclusivity.
2509        unsafe {
2510            crate::support::RefMut::new(TrackQuaternion {
2511                raw: core::ptr::NonNull::new_unchecked(
2512                    ffi::whiteout_mdx_MdxNode_get_rotationTracks(self.raw.as_ptr()),
2513                ),
2514            })
2515        }
2516    }
2517
2518    /// Scale animation
2519    /// Borrows the field in place — no copy, no allocation.
2520    pub fn scaling_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
2521        // SAFETY: an interior pointer into `self`, valid for this
2522        // borrow and never freed by the `Ref`.
2523        unsafe {
2524            crate::support::Ref::new(TrackVector3f {
2525                raw: core::ptr::NonNull::new_unchecked(
2526                    ffi::whiteout_mdx_MdxNode_get_scalingTracks(self.raw.as_ptr()),
2527                ),
2528            })
2529        }
2530    }
2531
2532    pub fn scaling_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
2533        // SAFETY: as above; `&mut self` guarantees exclusivity.
2534        unsafe {
2535            crate::support::RefMut::new(TrackVector3f {
2536                raw: core::ptr::NonNull::new_unchecked(
2537                    ffi::whiteout_mdx_MdxNode_get_scalingTracks(self.raw.as_ptr()),
2538                ),
2539            })
2540        }
2541    }
2542}
2543
2544impl Default for Node {
2545    fn default() -> Self {
2546        Self::new()
2547    }
2548}
2549
2550/// Sound emitter attached to the model hierarchy (deprecated)
2551///
2552/// Sound emitters are positioned nodes that can emit sounds at specific animation frames via KSEK tracks. While readers exist in the internal game code, no known shipped model contains SNEM data.
2553pub struct SoundEmitter {
2554    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxSoundEmitter>,
2555}
2556
2557impl Drop for SoundEmitter {
2558    fn drop(&mut self) {
2559        // SAFETY: `raw` came from a native constructor and Drop runs once.
2560        unsafe { ffi::whiteout_mdx_MdxSoundEmitter_delete(self.raw.as_ptr()) }
2561    }
2562}
2563
2564impl SoundEmitter {
2565    /// # Safety
2566    /// `raw` must be a live handle this value takes ownership of.
2567    #[allow(dead_code)] // used by whichever methods return this type
2568    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxSoundEmitter) -> Option<Self> {
2569        core::ptr::NonNull::new(raw).map(|raw| SoundEmitter { raw })
2570    }
2571}
2572
2573// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
2574// is deliberately NOT implemented — the C++ types make no documented
2575// guarantee about concurrent use, and claiming one we haven't verified
2576// would be unsound. See `@bind thread_safe` in the plan.
2577unsafe impl Send for SoundEmitter {}
2578
2579impl core::fmt::Debug for SoundEmitter {
2580    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2581        f.debug_struct("SoundEmitter").finish_non_exhaustive()
2582    }
2583}
2584
2585impl SoundEmitter {
2586    /// # Panics
2587    /// Panics if the native allocation fails.
2588    pub fn new() -> Self {
2589        // SAFETY: the native constructor returns a live handle; a null here
2590        // means the library is unusable.
2591        unsafe {
2592            let raw = ffi::whiteout_mdx_MdxSoundEmitter_new();
2593            Self::from_raw(raw).expect("native SoundEmitter allocation failed")
2594        }
2595    }
2596
2597    /// Base node data with transform
2598    /// Borrows the field in place — no copy, no allocation.
2599    pub fn node(&self) -> crate::support::Ref<'_, Node> {
2600        // SAFETY: an interior pointer into `self`, valid for this
2601        // borrow and never freed by the `Ref`.
2602        unsafe {
2603            crate::support::Ref::new(Node {
2604                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxSoundEmitter_get_node(
2605                    self.raw.as_ptr(),
2606                )),
2607            })
2608        }
2609    }
2610
2611    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
2612        // SAFETY: as above; `&mut self` guarantees exclusivity.
2613        unsafe {
2614            crate::support::RefMut::new(Node {
2615                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxSoundEmitter_get_node(
2616                    self.raw.as_ptr(),
2617                )),
2618            })
2619        }
2620    }
2621
2622    /// KSEK - sound event track (u32 values)
2623    /// Borrows the field in place — no copy, no allocation.
2624    pub fn sound_track(&self) -> crate::support::Ref<'_, TrackU32> {
2625        // SAFETY: an interior pointer into `self`, valid for this
2626        // borrow and never freed by the `Ref`.
2627        unsafe {
2628            crate::support::Ref::new(TrackU32 {
2629                raw: core::ptr::NonNull::new_unchecked(
2630                    ffi::whiteout_mdx_MdxSoundEmitter_get_soundTrack(self.raw.as_ptr()),
2631                ),
2632            })
2633        }
2634    }
2635
2636    pub fn sound_track_mut(&mut self) -> crate::support::RefMut<'_, TrackU32> {
2637        // SAFETY: as above; `&mut self` guarantees exclusivity.
2638        unsafe {
2639            crate::support::RefMut::new(TrackU32 {
2640                raw: core::ptr::NonNull::new_unchecked(
2641                    ffi::whiteout_mdx_MdxSoundEmitter_get_soundTrack(self.raw.as_ptr()),
2642                ),
2643            })
2644        }
2645    }
2646}
2647
2648impl Default for SoundEmitter {
2649    fn default() -> Self {
2650        Self::new()
2651    }
2652}
2653
2654/// Material rendering layer
2655///
2656/// Materials can have multiple layers, each with its own texture and blending mode. Layers control how textures are combined and rendered.
2657pub struct Layer {
2658    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxLayer>,
2659}
2660
2661impl Drop for Layer {
2662    fn drop(&mut self) {
2663        // SAFETY: `raw` came from a native constructor and Drop runs once.
2664        unsafe { ffi::whiteout_mdx_MdxLayer_delete(self.raw.as_ptr()) }
2665    }
2666}
2667
2668impl Layer {
2669    /// # Safety
2670    /// `raw` must be a live handle this value takes ownership of.
2671    #[allow(dead_code)] // used by whichever methods return this type
2672    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxLayer) -> Option<Self> {
2673        core::ptr::NonNull::new(raw).map(|raw| Layer { raw })
2674    }
2675}
2676
2677// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
2678// is deliberately NOT implemented — the C++ types make no documented
2679// guarantee about concurrent use, and claiming one we haven't verified
2680// would be unsound. See `@bind thread_safe` in the plan.
2681unsafe impl Send for Layer {}
2682
2683impl core::fmt::Debug for Layer {
2684    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2685        f.debug_struct("Layer").finish_non_exhaustive()
2686    }
2687}
2688
2689impl Layer {
2690    /// # Panics
2691    /// Panics if the native allocation fails.
2692    pub fn new() -> Self {
2693        // SAFETY: the native constructor returns a live handle; a null here
2694        // means the library is unusable.
2695        unsafe {
2696            let raw = ffi::whiteout_mdx_MdxLayer_new();
2697            Self::from_raw(raw).expect("native Layer allocation failed")
2698        }
2699    }
2700
2701    /// Blending mode
2702    pub fn filter_mode(&self) -> LayerFilterMode {
2703        // SAFETY: scalar read; the discriminant is validated below.
2704        unsafe { ffi::whiteout_mdx_MdxLayer_get_filterMode(self.raw.as_ptr()) }
2705            .try_into()
2706            .expect("unknown enum discriminant from the native library")
2707    }
2708
2709    pub fn set_filter_mode(&mut self, value: LayerFilterMode) {
2710        // SAFETY: scalar write through a live handle.
2711        unsafe { ffi::whiteout_mdx_MdxLayer_set_filterMode(self.raw.as_ptr(), value as i32) }
2712    }
2713
2714    /// Rendering flags
2715    pub fn shading_flags(&self) -> LayerShadingFlag {
2716        // SAFETY: scalar read; a flag set accepts any bits.
2717        LayerShadingFlag(unsafe { ffi::whiteout_mdx_MdxLayer_get_shadingFlags(self.raw.as_ptr()) })
2718    }
2719
2720    pub fn set_shading_flags(&mut self, value: LayerShadingFlag) {
2721        // SAFETY: scalar write through a live handle.
2722        unsafe { ffi::whiteout_mdx_MdxLayer_set_shadingFlags(self.raw.as_ptr(), value.0) }
2723    }
2724
2725    /// Texture index (versions 800-1100)
2726    pub fn texture_id(&self) -> u32 {
2727        // SAFETY: plain scalar read through a live handle.
2728        unsafe { ffi::whiteout_mdx_MdxLayer_get_textureId(self.raw.as_ptr()) }
2729    }
2730
2731    pub fn set_texture_id(&mut self, value: u32) {
2732        // SAFETY: plain scalar write through a live handle.
2733        unsafe { ffi::whiteout_mdx_MdxLayer_set_textureId(self.raw.as_ptr(), value) }
2734    }
2735
2736    /// Texture animation index
2737    pub fn texture_animation_id(&self) -> u32 {
2738        // SAFETY: plain scalar read through a live handle.
2739        unsafe { ffi::whiteout_mdx_MdxLayer_get_textureAnimationId(self.raw.as_ptr()) }
2740    }
2741
2742    pub fn set_texture_animation_id(&mut self, value: u32) {
2743        // SAFETY: plain scalar write through a live handle.
2744        unsafe { ffi::whiteout_mdx_MdxLayer_set_textureAnimationId(self.raw.as_ptr(), value) }
2745    }
2746
2747    /// Texture coordinate set index
2748    pub fn coord_id(&self) -> u32 {
2749        // SAFETY: plain scalar read through a live handle.
2750        unsafe { ffi::whiteout_mdx_MdxLayer_get_coordId(self.raw.as_ptr()) }
2751    }
2752
2753    pub fn set_coord_id(&mut self, value: u32) {
2754        // SAFETY: plain scalar write through a live handle.
2755        unsafe { ffi::whiteout_mdx_MdxLayer_set_coordId(self.raw.as_ptr(), value) }
2756    }
2757
2758    /// Layer opacity
2759    pub fn alpha(&self) -> f32 {
2760        // SAFETY: plain scalar read through a live handle.
2761        unsafe { ffi::whiteout_mdx_MdxLayer_get_alpha(self.raw.as_ptr()) }
2762    }
2763
2764    pub fn set_alpha(&mut self, value: f32) {
2765        // SAFETY: plain scalar write through a live handle.
2766        unsafe { ffi::whiteout_mdx_MdxLayer_set_alpha(self.raw.as_ptr(), value) }
2767    }
2768
2769    /// Emissive light intensity (default 1.0)
2770    pub fn emissive_gain(&self) -> f32 {
2771        // SAFETY: plain scalar read through a live handle.
2772        unsafe { ffi::whiteout_mdx_MdxLayer_get_emissiveGain(self.raw.as_ptr()) }
2773    }
2774
2775    pub fn set_emissive_gain(&mut self, value: f32) {
2776        // SAFETY: plain scalar write through a live handle.
2777        unsafe { ffi::whiteout_mdx_MdxLayer_set_emissiveGain(self.raw.as_ptr(), value) }
2778    }
2779
2780    /// Fresnel effect color
2781    pub fn fresnel_color(&self) -> crate::math::Vector3f {
2782        // SAFETY: the getter returns an interior pointer to a
2783        // layout-identical POD; we copy it out immediately.
2784        unsafe {
2785            *(ffi::whiteout_mdx_MdxLayer_get_fresnelColor(self.raw.as_ptr())
2786                as *const crate::math::Vector3f)
2787        }
2788    }
2789
2790    pub fn set_fresnel_color(&mut self, value: crate::math::Vector3f) {
2791        // SAFETY: as above, in the other direction.
2792        unsafe {
2793            ffi::whiteout_mdx_MdxLayer_set_fresnelColor(
2794                self.raw.as_ptr(),
2795                &value as *const crate::math::Vector3f as *const _,
2796            )
2797        }
2798    }
2799
2800    /// Fresnel effect opacity
2801    pub fn fresnel_opacity(&self) -> f32 {
2802        // SAFETY: plain scalar read through a live handle.
2803        unsafe { ffi::whiteout_mdx_MdxLayer_get_fresnelOpacity(self.raw.as_ptr()) }
2804    }
2805
2806    pub fn set_fresnel_opacity(&mut self, value: f32) {
2807        // SAFETY: plain scalar write through a live handle.
2808        unsafe { ffi::whiteout_mdx_MdxLayer_set_fresnelOpacity(self.raw.as_ptr(), value) }
2809    }
2810
2811    /// Fresnel team color factor
2812    pub fn fresnel_team_color(&self) -> f32 {
2813        // SAFETY: plain scalar read through a live handle.
2814        unsafe { ffi::whiteout_mdx_MdxLayer_get_fresnelTeamColor(self.raw.as_ptr()) }
2815    }
2816
2817    pub fn set_fresnel_team_color(&mut self, value: f32) {
2818        // SAFETY: plain scalar write through a live handle.
2819        unsafe { ffi::whiteout_mdx_MdxLayer_set_fresnelTeamColor(self.raw.as_ptr(), value) }
2820    }
2821
2822    /// Shader to use for this layer
2823    pub fn shader(&self) -> LayerShaderType {
2824        // SAFETY: scalar read; the discriminant is validated below.
2825        unsafe { ffi::whiteout_mdx_MdxLayer_get_shader(self.raw.as_ptr()) }
2826            .try_into()
2827            .expect("unknown enum discriminant from the native library")
2828    }
2829
2830    pub fn set_shader(&mut self, value: LayerShaderType) {
2831        // SAFETY: scalar write through a live handle.
2832        unsafe { ffi::whiteout_mdx_MdxLayer_set_shader(self.raw.as_ptr(), value as i32) }
2833    }
2834
2835    /// True if using Reforged HD shading
2836    pub fn is_hd(&self) -> bool {
2837        // SAFETY: plain scalar read through a live handle.
2838        unsafe { ffi::whiteout_mdx_MdxLayer_get_isHd(self.raw.as_ptr()) != 0 }
2839    }
2840
2841    pub fn set_is_hd(&mut self, value: bool) {
2842        // SAFETY: plain scalar write through a live handle.
2843        unsafe { ffi::whiteout_mdx_MdxLayer_set_isHd(self.raw.as_ptr(), if value { 1 } else { 0 }) }
2844    }
2845
2846    /// Multi-texture support (version 1200+)
2847    pub fn sub_textures_len(&self) -> usize {
2848        // SAFETY: scalar read through a live handle.
2849        unsafe { ffi::whiteout_mdx_MdxLayer_get_subTextures_count(self.raw.as_ptr()) }
2850    }
2851
2852    /// Borrows element `index` in place. `None` when out of range.
2853    pub fn sub_textures(&self, index: usize) -> Option<crate::support::Ref<'_, LayerSubTexture>> {
2854        if index >= self.sub_textures_len() {
2855            return None;
2856        }
2857        // SAFETY: index checked above; the pointer is interior to `self`.
2858        unsafe {
2859            Some(crate::support::Ref::new(LayerSubTexture {
2860                raw: core::ptr::NonNull::new_unchecked(
2861                    ffi::whiteout_mdx_MdxLayer_get_subTextures_at(self.raw.as_ptr(), index),
2862                ),
2863            }))
2864        }
2865    }
2866
2867    pub fn sub_textures_mut(
2868        &mut self,
2869        index: usize,
2870    ) -> Option<crate::support::RefMut<'_, LayerSubTexture>> {
2871        if index >= self.sub_textures_len() {
2872            return None;
2873        }
2874        // SAFETY: as above; `&mut self` guarantees exclusivity.
2875        unsafe {
2876            Some(crate::support::RefMut::new(LayerSubTexture {
2877                raw: core::ptr::NonNull::new_unchecked(
2878                    ffi::whiteout_mdx_MdxLayer_get_subTextures_at(self.raw.as_ptr(), index),
2879                ),
2880            }))
2881        }
2882    }
2883
2884    /// Iterate the elements, borrowing each in turn.
2885    pub fn sub_textures_iter(
2886        &self,
2887    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, LayerSubTexture>> {
2888        (0..self.sub_textures_len()).map(move |i| self.sub_textures(i).expect("index below len"))
2889    }
2890
2891    pub fn resize_sub_textures(&mut self, count: usize) {
2892        // SAFETY: exclusive access, so no borrow is outstanding.
2893        unsafe { ffi::whiteout_mdx_MdxLayer_resize_subTextures(self.raw.as_ptr(), count) }
2894    }
2895
2896    /// Texture ID animation (versions 800-1100)
2897    /// Borrows the field in place — no copy, no allocation.
2898    pub fn texture_id_tracks(&self) -> crate::support::Ref<'_, TrackU32> {
2899        // SAFETY: an interior pointer into `self`, valid for this
2900        // borrow and never freed by the `Ref`.
2901        unsafe {
2902            crate::support::Ref::new(TrackU32 {
2903                raw: core::ptr::NonNull::new_unchecked(
2904                    ffi::whiteout_mdx_MdxLayer_get_textureIdTracks(self.raw.as_ptr()),
2905                ),
2906            })
2907        }
2908    }
2909
2910    pub fn texture_id_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackU32> {
2911        // SAFETY: as above; `&mut self` guarantees exclusivity.
2912        unsafe {
2913            crate::support::RefMut::new(TrackU32 {
2914                raw: core::ptr::NonNull::new_unchecked(
2915                    ffi::whiteout_mdx_MdxLayer_get_textureIdTracks(self.raw.as_ptr()),
2916                ),
2917            })
2918        }
2919    }
2920
2921    /// Alpha animation
2922    /// Borrows the field in place — no copy, no allocation.
2923    pub fn alpha_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
2924        // SAFETY: an interior pointer into `self`, valid for this
2925        // borrow and never freed by the `Ref`.
2926        unsafe {
2927            crate::support::Ref::new(TrackF32 {
2928                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxLayer_get_alphaTracks(
2929                    self.raw.as_ptr(),
2930                )),
2931            })
2932        }
2933    }
2934
2935    pub fn alpha_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
2936        // SAFETY: as above; `&mut self` guarantees exclusivity.
2937        unsafe {
2938            crate::support::RefMut::new(TrackF32 {
2939                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxLayer_get_alphaTracks(
2940                    self.raw.as_ptr(),
2941                )),
2942            })
2943        }
2944    }
2945
2946    /// Emissive gain animation
2947    /// Borrows the field in place — no copy, no allocation.
2948    pub fn emissive_gain_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
2949        // SAFETY: an interior pointer into `self`, valid for this
2950        // borrow and never freed by the `Ref`.
2951        unsafe {
2952            crate::support::Ref::new(TrackF32 {
2953                raw: core::ptr::NonNull::new_unchecked(
2954                    ffi::whiteout_mdx_MdxLayer_get_emissiveGainTracks(self.raw.as_ptr()),
2955                ),
2956            })
2957        }
2958    }
2959
2960    pub fn emissive_gain_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
2961        // SAFETY: as above; `&mut self` guarantees exclusivity.
2962        unsafe {
2963            crate::support::RefMut::new(TrackF32 {
2964                raw: core::ptr::NonNull::new_unchecked(
2965                    ffi::whiteout_mdx_MdxLayer_get_emissiveGainTracks(self.raw.as_ptr()),
2966                ),
2967            })
2968        }
2969    }
2970
2971    /// Fresnel color animation
2972    /// Borrows the field in place — no copy, no allocation.
2973    pub fn fresnel_color_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
2974        // SAFETY: an interior pointer into `self`, valid for this
2975        // borrow and never freed by the `Ref`.
2976        unsafe {
2977            crate::support::Ref::new(TrackVector3f {
2978                raw: core::ptr::NonNull::new_unchecked(
2979                    ffi::whiteout_mdx_MdxLayer_get_fresnelColorTracks(self.raw.as_ptr()),
2980                ),
2981            })
2982        }
2983    }
2984
2985    pub fn fresnel_color_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
2986        // SAFETY: as above; `&mut self` guarantees exclusivity.
2987        unsafe {
2988            crate::support::RefMut::new(TrackVector3f {
2989                raw: core::ptr::NonNull::new_unchecked(
2990                    ffi::whiteout_mdx_MdxLayer_get_fresnelColorTracks(self.raw.as_ptr()),
2991                ),
2992            })
2993        }
2994    }
2995
2996    /// Fresnel alpha animation
2997    /// Borrows the field in place — no copy, no allocation.
2998    pub fn fresnel_alpha_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
2999        // SAFETY: an interior pointer into `self`, valid for this
3000        // borrow and never freed by the `Ref`.
3001        unsafe {
3002            crate::support::Ref::new(TrackF32 {
3003                raw: core::ptr::NonNull::new_unchecked(
3004                    ffi::whiteout_mdx_MdxLayer_get_fresnelAlphaTracks(self.raw.as_ptr()),
3005                ),
3006            })
3007        }
3008    }
3009
3010    pub fn fresnel_alpha_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
3011        // SAFETY: as above; `&mut self` guarantees exclusivity.
3012        unsafe {
3013            crate::support::RefMut::new(TrackF32 {
3014                raw: core::ptr::NonNull::new_unchecked(
3015                    ffi::whiteout_mdx_MdxLayer_get_fresnelAlphaTracks(self.raw.as_ptr()),
3016                ),
3017            })
3018        }
3019    }
3020
3021    /// Fresnel team color animation
3022    /// Borrows the field in place — no copy, no allocation.
3023    pub fn fresnel_team_color_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
3024        // SAFETY: an interior pointer into `self`, valid for this
3025        // borrow and never freed by the `Ref`.
3026        unsafe {
3027            crate::support::Ref::new(TrackF32 {
3028                raw: core::ptr::NonNull::new_unchecked(
3029                    ffi::whiteout_mdx_MdxLayer_get_fresnelTeamColorTracks(self.raw.as_ptr()),
3030                ),
3031            })
3032        }
3033    }
3034
3035    pub fn fresnel_team_color_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
3036        // SAFETY: as above; `&mut self` guarantees exclusivity.
3037        unsafe {
3038            crate::support::RefMut::new(TrackF32 {
3039                raw: core::ptr::NonNull::new_unchecked(
3040                    ffi::whiteout_mdx_MdxLayer_get_fresnelTeamColorTracks(self.raw.as_ptr()),
3041                ),
3042            })
3043        }
3044    }
3045}
3046
3047impl Default for Layer {
3048    fn default() -> Self {
3049        Self::new()
3050    }
3051}
3052
3053/// Sub-texture definition (Reforged multi-texture)
3054pub struct LayerSubTexture {
3055    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxLayerSubTexture>,
3056}
3057
3058impl Drop for LayerSubTexture {
3059    fn drop(&mut self) {
3060        // SAFETY: `raw` came from a native constructor and Drop runs once.
3061        unsafe { ffi::whiteout_mdx_MdxLayerSubTexture_delete(self.raw.as_ptr()) }
3062    }
3063}
3064
3065impl LayerSubTexture {
3066    /// # Safety
3067    /// `raw` must be a live handle this value takes ownership of.
3068    #[allow(dead_code)] // used by whichever methods return this type
3069    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxLayerSubTexture) -> Option<Self> {
3070        core::ptr::NonNull::new(raw).map(|raw| LayerSubTexture { raw })
3071    }
3072}
3073
3074// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
3075// is deliberately NOT implemented — the C++ types make no documented
3076// guarantee about concurrent use, and claiming one we haven't verified
3077// would be unsound. See `@bind thread_safe` in the plan.
3078unsafe impl Send for LayerSubTexture {}
3079
3080impl core::fmt::Debug for LayerSubTexture {
3081    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3082        f.debug_struct("LayerSubTexture").finish_non_exhaustive()
3083    }
3084}
3085
3086impl LayerSubTexture {
3087    /// # Panics
3088    /// Panics if the native allocation fails.
3089    pub fn new() -> Self {
3090        // SAFETY: the native constructor returns a live handle; a null here
3091        // means the library is unusable.
3092        unsafe {
3093            let raw = ffi::whiteout_mdx_MdxLayerSubTexture_new();
3094            Self::from_raw(raw).expect("native LayerSubTexture allocation failed")
3095        }
3096    }
3097
3098    /// Index into texture array
3099    pub fn texture_id(&self) -> u32 {
3100        // SAFETY: plain scalar read through a live handle.
3101        unsafe { ffi::whiteout_mdx_MdxLayerSubTexture_get_textureId(self.raw.as_ptr()) }
3102    }
3103
3104    pub fn set_texture_id(&mut self, value: u32) {
3105        // SAFETY: plain scalar write through a live handle.
3106        unsafe { ffi::whiteout_mdx_MdxLayerSubTexture_set_textureId(self.raw.as_ptr(), value) }
3107    }
3108
3109    /// Texture slot number
3110    pub fn slot(&self) -> LayerSlotType {
3111        // SAFETY: scalar read; the discriminant is validated below.
3112        unsafe { ffi::whiteout_mdx_MdxLayerSubTexture_get_slot(self.raw.as_ptr()) }
3113            .try_into()
3114            .expect("unknown enum discriminant from the native library")
3115    }
3116
3117    pub fn set_slot(&mut self, value: LayerSlotType) {
3118        // SAFETY: scalar write through a live handle.
3119        unsafe { ffi::whiteout_mdx_MdxLayerSubTexture_set_slot(self.raw.as_ptr(), value as i32) }
3120    }
3121
3122    /// Texture ID animation
3123    /// Borrows the field in place — no copy, no allocation.
3124    pub fn tracks(&self) -> crate::support::Ref<'_, TrackU32> {
3125        // SAFETY: an interior pointer into `self`, valid for this
3126        // borrow and never freed by the `Ref`.
3127        unsafe {
3128            crate::support::Ref::new(TrackU32 {
3129                raw: core::ptr::NonNull::new_unchecked(
3130                    ffi::whiteout_mdx_MdxLayerSubTexture_get_tracks(self.raw.as_ptr()),
3131                ),
3132            })
3133        }
3134    }
3135
3136    pub fn tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackU32> {
3137        // SAFETY: as above; `&mut self` guarantees exclusivity.
3138        unsafe {
3139            crate::support::RefMut::new(TrackU32 {
3140                raw: core::ptr::NonNull::new_unchecked(
3141                    ffi::whiteout_mdx_MdxLayerSubTexture_get_tracks(self.raw.as_ptr()),
3142                ),
3143            })
3144        }
3145    }
3146}
3147
3148impl Default for LayerSubTexture {
3149    fn default() -> Self {
3150        Self::new()
3151    }
3152}
3153
3154/// Material definition with rendering properties
3155///
3156/// Materials define how surfaces are rendered. Each material contains one or more layers that specify textures and blending modes.
3157pub struct Material {
3158    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxMaterial>,
3159}
3160
3161impl Drop for Material {
3162    fn drop(&mut self) {
3163        // SAFETY: `raw` came from a native constructor and Drop runs once.
3164        unsafe { ffi::whiteout_mdx_MdxMaterial_delete(self.raw.as_ptr()) }
3165    }
3166}
3167
3168impl Material {
3169    /// # Safety
3170    /// `raw` must be a live handle this value takes ownership of.
3171    #[allow(dead_code)] // used by whichever methods return this type
3172    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxMaterial) -> Option<Self> {
3173        core::ptr::NonNull::new(raw).map(|raw| Material { raw })
3174    }
3175}
3176
3177// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
3178// is deliberately NOT implemented — the C++ types make no documented
3179// guarantee about concurrent use, and claiming one we haven't verified
3180// would be unsound. See `@bind thread_safe` in the plan.
3181unsafe impl Send for Material {}
3182
3183impl core::fmt::Debug for Material {
3184    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3185        f.debug_struct("Material").finish_non_exhaustive()
3186    }
3187}
3188
3189impl Material {
3190    /// # Panics
3191    /// Panics if the native allocation fails.
3192    pub fn new() -> Self {
3193        // SAFETY: the native constructor returns a live handle; a null here
3194        // means the library is unusable.
3195        unsafe {
3196            let raw = ffi::whiteout_mdx_MdxMaterial_new();
3197            Self::from_raw(raw).expect("native Material allocation failed")
3198        }
3199    }
3200
3201    /// Rendering priority (higher = render last); signed
3202    pub fn priority_plane(&self) -> i32 {
3203        // SAFETY: plain scalar read through a live handle.
3204        unsafe { ffi::whiteout_mdx_MdxMaterial_get_priorityPlane(self.raw.as_ptr()) }
3205    }
3206
3207    pub fn set_priority_plane(&mut self, value: i32) {
3208        // SAFETY: plain scalar write through a live handle.
3209        unsafe { ffi::whiteout_mdx_MdxMaterial_set_priorityPlane(self.raw.as_ptr(), value) }
3210    }
3211
3212    /// Material flags
3213    pub fn flags(&self) -> SequenceFlag {
3214        // SAFETY: scalar read; the discriminant is validated below.
3215        unsafe { ffi::whiteout_mdx_MdxMaterial_get_flags(self.raw.as_ptr()) }
3216            .try_into()
3217            .expect("unknown enum discriminant from the native library")
3218    }
3219
3220    pub fn set_flags(&mut self, value: SequenceFlag) {
3221        // SAFETY: scalar write through a live handle.
3222        unsafe { ffi::whiteout_mdx_MdxMaterial_set_flags(self.raw.as_ptr(), value as i32) }
3223    }
3224
3225    /// Shader name (Reforged)
3226    pub fn shader(&self) -> String {
3227        // SAFETY: the native side hands over an owned CString.
3228        unsafe {
3229            crate::support::take_string(ffi::whiteout_mdx_MdxMaterial_get_shader(self.raw.as_ptr()))
3230        }
3231    }
3232
3233    pub fn set_shader(&mut self, value: &str) {
3234        let value = std::ffi::CString::new(value).unwrap_or_default();
3235        // SAFETY: the pointer outlives the call.
3236        unsafe { ffi::whiteout_mdx_MdxMaterial_set_shader(self.raw.as_ptr(), value.as_ptr()) }
3237    }
3238
3239    /// Rendering layers
3240    pub fn layers_len(&self) -> usize {
3241        // SAFETY: scalar read through a live handle.
3242        unsafe { ffi::whiteout_mdx_MdxMaterial_get_layers_count(self.raw.as_ptr()) }
3243    }
3244
3245    /// Borrows element `index` in place. `None` when out of range.
3246    pub fn layers(&self, index: usize) -> Option<crate::support::Ref<'_, Layer>> {
3247        if index >= self.layers_len() {
3248            return None;
3249        }
3250        // SAFETY: index checked above; the pointer is interior to `self`.
3251        unsafe {
3252            Some(crate::support::Ref::new(Layer {
3253                raw: core::ptr::NonNull::new_unchecked(
3254                    ffi::whiteout_mdx_MdxMaterial_get_layers_at(self.raw.as_ptr(), index),
3255                ),
3256            }))
3257        }
3258    }
3259
3260    pub fn layers_mut(&mut self, index: usize) -> Option<crate::support::RefMut<'_, Layer>> {
3261        if index >= self.layers_len() {
3262            return None;
3263        }
3264        // SAFETY: as above; `&mut self` guarantees exclusivity.
3265        unsafe {
3266            Some(crate::support::RefMut::new(Layer {
3267                raw: core::ptr::NonNull::new_unchecked(
3268                    ffi::whiteout_mdx_MdxMaterial_get_layers_at(self.raw.as_ptr(), index),
3269                ),
3270            }))
3271        }
3272    }
3273
3274    /// Iterate the elements, borrowing each in turn.
3275    pub fn layers_iter(&self) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Layer>> {
3276        (0..self.layers_len()).map(move |i| self.layers(i).expect("index below len"))
3277    }
3278
3279    pub fn resize_layers(&mut self, count: usize) {
3280        // SAFETY: exclusive access, so no borrow is outstanding.
3281        unsafe { ffi::whiteout_mdx_MdxMaterial_resize_layers(self.raw.as_ptr(), count) }
3282    }
3283}
3284
3285impl Default for Material {
3286    fn default() -> Self {
3287        Self::new()
3288    }
3289}
3290
3291/// UV coordinate animation
3292///
3293/// Texture animations transform UV coordinates over time, creating effects like scrolling water, rotating symbols, etc.
3294pub struct TextureAnimation {
3295    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTextureAnimation>,
3296}
3297
3298impl Drop for TextureAnimation {
3299    fn drop(&mut self) {
3300        // SAFETY: `raw` came from a native constructor and Drop runs once.
3301        unsafe { ffi::whiteout_mdx_MdxTextureAnimation_delete(self.raw.as_ptr()) }
3302    }
3303}
3304
3305impl TextureAnimation {
3306    /// # Safety
3307    /// `raw` must be a live handle this value takes ownership of.
3308    #[allow(dead_code)] // used by whichever methods return this type
3309    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTextureAnimation) -> Option<Self> {
3310        core::ptr::NonNull::new(raw).map(|raw| TextureAnimation { raw })
3311    }
3312}
3313
3314// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
3315// is deliberately NOT implemented — the C++ types make no documented
3316// guarantee about concurrent use, and claiming one we haven't verified
3317// would be unsound. See `@bind thread_safe` in the plan.
3318unsafe impl Send for TextureAnimation {}
3319
3320impl core::fmt::Debug for TextureAnimation {
3321    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3322        f.debug_struct("TextureAnimation").finish_non_exhaustive()
3323    }
3324}
3325
3326impl TextureAnimation {
3327    /// # Panics
3328    /// Panics if the native allocation fails.
3329    pub fn new() -> Self {
3330        // SAFETY: the native constructor returns a live handle; a null here
3331        // means the library is unusable.
3332        unsafe {
3333            let raw = ffi::whiteout_mdx_MdxTextureAnimation_new();
3334            Self::from_raw(raw).expect("native TextureAnimation allocation failed")
3335        }
3336    }
3337
3338    /// UV translation animation
3339    /// Borrows the field in place — no copy, no allocation.
3340    pub fn translation_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
3341        // SAFETY: an interior pointer into `self`, valid for this
3342        // borrow and never freed by the `Ref`.
3343        unsafe {
3344            crate::support::Ref::new(TrackVector3f {
3345                raw: core::ptr::NonNull::new_unchecked(
3346                    ffi::whiteout_mdx_MdxTextureAnimation_get_translationTracks(self.raw.as_ptr()),
3347                ),
3348            })
3349        }
3350    }
3351
3352    pub fn translation_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
3353        // SAFETY: as above; `&mut self` guarantees exclusivity.
3354        unsafe {
3355            crate::support::RefMut::new(TrackVector3f {
3356                raw: core::ptr::NonNull::new_unchecked(
3357                    ffi::whiteout_mdx_MdxTextureAnimation_get_translationTracks(self.raw.as_ptr()),
3358                ),
3359            })
3360        }
3361    }
3362
3363    /// UV rotation animation (quaternion XYZW)
3364    /// Borrows the field in place — no copy, no allocation.
3365    pub fn rotation_tracks(&self) -> crate::support::Ref<'_, TrackQuaternion> {
3366        // SAFETY: an interior pointer into `self`, valid for this
3367        // borrow and never freed by the `Ref`.
3368        unsafe {
3369            crate::support::Ref::new(TrackQuaternion {
3370                raw: core::ptr::NonNull::new_unchecked(
3371                    ffi::whiteout_mdx_MdxTextureAnimation_get_rotationTracks(self.raw.as_ptr()),
3372                ),
3373            })
3374        }
3375    }
3376
3377    pub fn rotation_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackQuaternion> {
3378        // SAFETY: as above; `&mut self` guarantees exclusivity.
3379        unsafe {
3380            crate::support::RefMut::new(TrackQuaternion {
3381                raw: core::ptr::NonNull::new_unchecked(
3382                    ffi::whiteout_mdx_MdxTextureAnimation_get_rotationTracks(self.raw.as_ptr()),
3383                ),
3384            })
3385        }
3386    }
3387
3388    /// UV scaling animation
3389    /// Borrows the field in place — no copy, no allocation.
3390    pub fn scaling_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
3391        // SAFETY: an interior pointer into `self`, valid for this
3392        // borrow and never freed by the `Ref`.
3393        unsafe {
3394            crate::support::Ref::new(TrackVector3f {
3395                raw: core::ptr::NonNull::new_unchecked(
3396                    ffi::whiteout_mdx_MdxTextureAnimation_get_scalingTracks(self.raw.as_ptr()),
3397                ),
3398            })
3399        }
3400    }
3401
3402    pub fn scaling_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
3403        // SAFETY: as above; `&mut self` guarantees exclusivity.
3404        unsafe {
3405            crate::support::RefMut::new(TrackVector3f {
3406                raw: core::ptr::NonNull::new_unchecked(
3407                    ffi::whiteout_mdx_MdxTextureAnimation_get_scalingTracks(self.raw.as_ptr()),
3408                ),
3409            })
3410        }
3411    }
3412}
3413
3414impl Default for TextureAnimation {
3415    fn default() -> Self {
3416        Self::new()
3417    }
3418}
3419
3420/// Mesh geometry
3421///
3422/// A geoset is a complete mesh with vertices, normals, faces, and skinning data. Models can have multiple geosets with different materials or LOD levels.
3423pub struct Geoset {
3424    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxGeoset>,
3425}
3426
3427impl Drop for Geoset {
3428    fn drop(&mut self) {
3429        // SAFETY: `raw` came from a native constructor and Drop runs once.
3430        unsafe { ffi::whiteout_mdx_MdxGeoset_delete(self.raw.as_ptr()) }
3431    }
3432}
3433
3434impl Geoset {
3435    /// # Safety
3436    /// `raw` must be a live handle this value takes ownership of.
3437    #[allow(dead_code)] // used by whichever methods return this type
3438    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxGeoset) -> Option<Self> {
3439        core::ptr::NonNull::new(raw).map(|raw| Geoset { raw })
3440    }
3441}
3442
3443// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
3444// is deliberately NOT implemented — the C++ types make no documented
3445// guarantee about concurrent use, and claiming one we haven't verified
3446// would be unsound. See `@bind thread_safe` in the plan.
3447unsafe impl Send for Geoset {}
3448
3449impl core::fmt::Debug for Geoset {
3450    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3451        f.debug_struct("Geoset").finish_non_exhaustive()
3452    }
3453}
3454
3455impl Geoset {
3456    /// # Panics
3457    /// Panics if the native allocation fails.
3458    pub fn new() -> Self {
3459        // SAFETY: the native constructor returns a live handle; a null here
3460        // means the library is unusable.
3461        unsafe {
3462            let raw = ffi::whiteout_mdx_MdxGeoset_new();
3463            Self::from_raw(raw).expect("native Geoset allocation failed")
3464        }
3465    }
3466
3467    /// Vertex positions
3468    /// Zero-copy view of the underlying `std::vector`.
3469    pub fn vertex_positions(&self) -> &[crate::math::Vector3f] {
3470        // SAFETY: `_data`/`_count` describe one contiguous C++
3471        // allocation, borrowed for as long as `self` is.
3472        unsafe {
3473            let n = ffi::whiteout_mdx_MdxGeoset_get_vertexPositions_count(self.raw.as_ptr());
3474            let p = ffi::whiteout_mdx_MdxGeoset_get_vertexPositions_data(self.raw.as_ptr())
3475                as *const crate::math::Vector3f;
3476            if p.is_null() || n == 0 {
3477                &[]
3478            } else {
3479                core::slice::from_raw_parts(p, n)
3480            }
3481        }
3482    }
3483
3484    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
3485    pub fn vertex_positions_mut(&mut self) -> &mut [crate::math::Vector3f] {
3486        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
3487        unsafe {
3488            let n = ffi::whiteout_mdx_MdxGeoset_get_vertexPositions_count(self.raw.as_ptr());
3489            let p = ffi::whiteout_mdx_MdxGeoset_get_vertexPositions_data(self.raw.as_ptr())
3490                as *const crate::math::Vector3f as *mut crate::math::Vector3f;
3491            if p.is_null() || n == 0 {
3492                &mut []
3493            } else {
3494                core::slice::from_raw_parts_mut(p, n)
3495            }
3496        }
3497    }
3498
3499    pub fn set_vertex_positions(&mut self, values: &[crate::math::Vector3f]) {
3500        // SAFETY: the native side copies `values` before returning.
3501        unsafe {
3502            ffi::whiteout_mdx_MdxGeoset_assign_vertexPositions(
3503                self.raw.as_ptr(),
3504                values.as_ptr() as *const _,
3505                values.len(),
3506            )
3507        }
3508    }
3509
3510    pub fn resize_vertex_positions(&mut self, count: usize) {
3511        // SAFETY: reallocation is safe here precisely because
3512        // `&mut self` means no slice borrow is outstanding.
3513        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_vertexPositions(self.raw.as_ptr(), count) }
3514    }
3515
3516    /// Vertex normals
3517    /// Zero-copy view of the underlying `std::vector`.
3518    pub fn vertex_normals(&self) -> &[crate::math::Vector3f] {
3519        // SAFETY: `_data`/`_count` describe one contiguous C++
3520        // allocation, borrowed for as long as `self` is.
3521        unsafe {
3522            let n = ffi::whiteout_mdx_MdxGeoset_get_vertexNormals_count(self.raw.as_ptr());
3523            let p = ffi::whiteout_mdx_MdxGeoset_get_vertexNormals_data(self.raw.as_ptr())
3524                as *const crate::math::Vector3f;
3525            if p.is_null() || n == 0 {
3526                &[]
3527            } else {
3528                core::slice::from_raw_parts(p, n)
3529            }
3530        }
3531    }
3532
3533    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
3534    pub fn vertex_normals_mut(&mut self) -> &mut [crate::math::Vector3f] {
3535        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
3536        unsafe {
3537            let n = ffi::whiteout_mdx_MdxGeoset_get_vertexNormals_count(self.raw.as_ptr());
3538            let p = ffi::whiteout_mdx_MdxGeoset_get_vertexNormals_data(self.raw.as_ptr())
3539                as *const crate::math::Vector3f as *mut crate::math::Vector3f;
3540            if p.is_null() || n == 0 {
3541                &mut []
3542            } else {
3543                core::slice::from_raw_parts_mut(p, n)
3544            }
3545        }
3546    }
3547
3548    pub fn set_vertex_normals(&mut self, values: &[crate::math::Vector3f]) {
3549        // SAFETY: the native side copies `values` before returning.
3550        unsafe {
3551            ffi::whiteout_mdx_MdxGeoset_assign_vertexNormals(
3552                self.raw.as_ptr(),
3553                values.as_ptr() as *const _,
3554                values.len(),
3555            )
3556        }
3557    }
3558
3559    pub fn resize_vertex_normals(&mut self, count: usize) {
3560        // SAFETY: reallocation is safe here precisely because
3561        // `&mut self` means no slice borrow is outstanding.
3562        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_vertexNormals(self.raw.as_ptr(), count) }
3563    }
3564
3565    /// Face type groups (4 = triangles)
3566    /// Zero-copy view of the underlying `std::vector`.
3567    pub fn face_type_groups(&self) -> &[u32] {
3568        // SAFETY: `_data`/`_count` describe one contiguous C++
3569        // allocation, borrowed for as long as `self` is.
3570        unsafe {
3571            let n = ffi::whiteout_mdx_MdxGeoset_get_faceTypeGroups_count(self.raw.as_ptr());
3572            let p = ffi::whiteout_mdx_MdxGeoset_get_faceTypeGroups_data(self.raw.as_ptr());
3573            if p.is_null() || n == 0 {
3574                &[]
3575            } else {
3576                core::slice::from_raw_parts(p, n)
3577            }
3578        }
3579    }
3580
3581    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
3582    pub fn face_type_groups_mut(&mut self) -> &mut [u32] {
3583        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
3584        unsafe {
3585            let n = ffi::whiteout_mdx_MdxGeoset_get_faceTypeGroups_count(self.raw.as_ptr());
3586            let p =
3587                ffi::whiteout_mdx_MdxGeoset_get_faceTypeGroups_data(self.raw.as_ptr()) as *mut u32;
3588            if p.is_null() || n == 0 {
3589                &mut []
3590            } else {
3591                core::slice::from_raw_parts_mut(p, n)
3592            }
3593        }
3594    }
3595
3596    pub fn set_face_type_groups(&mut self, values: &[u32]) {
3597        // SAFETY: the native side copies `values` before returning.
3598        unsafe {
3599            ffi::whiteout_mdx_MdxGeoset_assign_faceTypeGroups(
3600                self.raw.as_ptr(),
3601                values.as_ptr() as *const _,
3602                values.len(),
3603            )
3604        }
3605    }
3606
3607    pub fn resize_face_type_groups(&mut self, count: usize) {
3608        // SAFETY: reallocation is safe here precisely because
3609        // `&mut self` means no slice borrow is outstanding.
3610        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_faceTypeGroups(self.raw.as_ptr(), count) }
3611    }
3612
3613    /// Number of indices per group
3614    /// Zero-copy view of the underlying `std::vector`.
3615    pub fn face_groups(&self) -> &[u32] {
3616        // SAFETY: `_data`/`_count` describe one contiguous C++
3617        // allocation, borrowed for as long as `self` is.
3618        unsafe {
3619            let n = ffi::whiteout_mdx_MdxGeoset_get_faceGroups_count(self.raw.as_ptr());
3620            let p = ffi::whiteout_mdx_MdxGeoset_get_faceGroups_data(self.raw.as_ptr());
3621            if p.is_null() || n == 0 {
3622                &[]
3623            } else {
3624                core::slice::from_raw_parts(p, n)
3625            }
3626        }
3627    }
3628
3629    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
3630    pub fn face_groups_mut(&mut self) -> &mut [u32] {
3631        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
3632        unsafe {
3633            let n = ffi::whiteout_mdx_MdxGeoset_get_faceGroups_count(self.raw.as_ptr());
3634            let p = ffi::whiteout_mdx_MdxGeoset_get_faceGroups_data(self.raw.as_ptr()) as *mut u32;
3635            if p.is_null() || n == 0 {
3636                &mut []
3637            } else {
3638                core::slice::from_raw_parts_mut(p, n)
3639            }
3640        }
3641    }
3642
3643    pub fn set_face_groups(&mut self, values: &[u32]) {
3644        // SAFETY: the native side copies `values` before returning.
3645        unsafe {
3646            ffi::whiteout_mdx_MdxGeoset_assign_faceGroups(
3647                self.raw.as_ptr(),
3648                values.as_ptr() as *const _,
3649                values.len(),
3650            )
3651        }
3652    }
3653
3654    pub fn resize_face_groups(&mut self, count: usize) {
3655        // SAFETY: reallocation is safe here precisely because
3656        // `&mut self` means no slice borrow is outstanding.
3657        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_faceGroups(self.raw.as_ptr(), count) }
3658    }
3659
3660    /// Vertex indices (triangles)
3661    /// Zero-copy view of the underlying `std::vector`.
3662    pub fn faces(&self) -> &[u16] {
3663        // SAFETY: `_data`/`_count` describe one contiguous C++
3664        // allocation, borrowed for as long as `self` is.
3665        unsafe {
3666            let n = ffi::whiteout_mdx_MdxGeoset_get_faces_count(self.raw.as_ptr());
3667            let p = ffi::whiteout_mdx_MdxGeoset_get_faces_data(self.raw.as_ptr());
3668            if p.is_null() || n == 0 {
3669                &[]
3670            } else {
3671                core::slice::from_raw_parts(p, n)
3672            }
3673        }
3674    }
3675
3676    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
3677    pub fn faces_mut(&mut self) -> &mut [u16] {
3678        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
3679        unsafe {
3680            let n = ffi::whiteout_mdx_MdxGeoset_get_faces_count(self.raw.as_ptr());
3681            let p = ffi::whiteout_mdx_MdxGeoset_get_faces_data(self.raw.as_ptr()) as *mut u16;
3682            if p.is_null() || n == 0 {
3683                &mut []
3684            } else {
3685                core::slice::from_raw_parts_mut(p, n)
3686            }
3687        }
3688    }
3689
3690    pub fn set_faces(&mut self, values: &[u16]) {
3691        // SAFETY: the native side copies `values` before returning.
3692        unsafe {
3693            ffi::whiteout_mdx_MdxGeoset_assign_faces(
3694                self.raw.as_ptr(),
3695                values.as_ptr() as *const _,
3696                values.len(),
3697            )
3698        }
3699    }
3700
3701    pub fn resize_faces(&mut self, count: usize) {
3702        // SAFETY: reallocation is safe here precisely because
3703        // `&mut self` means no slice borrow is outstanding.
3704        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_faces(self.raw.as_ptr(), count) }
3705    }
3706
3707    /// Bone groups per vertex
3708    /// Zero-copy view of the underlying `std::vector`.
3709    pub fn vertex_groups(&self) -> &[u8] {
3710        // SAFETY: `_data`/`_count` describe one contiguous C++
3711        // allocation, borrowed for as long as `self` is.
3712        unsafe {
3713            let n = ffi::whiteout_mdx_MdxGeoset_get_vertexGroups_count(self.raw.as_ptr());
3714            let p = ffi::whiteout_mdx_MdxGeoset_get_vertexGroups_data(self.raw.as_ptr());
3715            if p.is_null() || n == 0 {
3716                &[]
3717            } else {
3718                core::slice::from_raw_parts(p, n)
3719            }
3720        }
3721    }
3722
3723    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
3724    pub fn vertex_groups_mut(&mut self) -> &mut [u8] {
3725        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
3726        unsafe {
3727            let n = ffi::whiteout_mdx_MdxGeoset_get_vertexGroups_count(self.raw.as_ptr());
3728            let p = ffi::whiteout_mdx_MdxGeoset_get_vertexGroups_data(self.raw.as_ptr()) as *mut u8;
3729            if p.is_null() || n == 0 {
3730                &mut []
3731            } else {
3732                core::slice::from_raw_parts_mut(p, n)
3733            }
3734        }
3735    }
3736
3737    pub fn set_vertex_groups(&mut self, values: &[u8]) {
3738        // SAFETY: the native side copies `values` before returning.
3739        unsafe {
3740            ffi::whiteout_mdx_MdxGeoset_assign_vertexGroups(
3741                self.raw.as_ptr(),
3742                values.as_ptr() as *const _,
3743                values.len(),
3744            )
3745        }
3746    }
3747
3748    pub fn resize_vertex_groups(&mut self, count: usize) {
3749        // SAFETY: reallocation is safe here precisely because
3750        // `&mut self` means no slice borrow is outstanding.
3751        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_vertexGroups(self.raw.as_ptr(), count) }
3752    }
3753
3754    /// Number of matrices per group
3755    /// Zero-copy view of the underlying `std::vector`.
3756    pub fn matrix_groups(&self) -> &[u32] {
3757        // SAFETY: `_data`/`_count` describe one contiguous C++
3758        // allocation, borrowed for as long as `self` is.
3759        unsafe {
3760            let n = ffi::whiteout_mdx_MdxGeoset_get_matrixGroups_count(self.raw.as_ptr());
3761            let p = ffi::whiteout_mdx_MdxGeoset_get_matrixGroups_data(self.raw.as_ptr());
3762            if p.is_null() || n == 0 {
3763                &[]
3764            } else {
3765                core::slice::from_raw_parts(p, n)
3766            }
3767        }
3768    }
3769
3770    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
3771    pub fn matrix_groups_mut(&mut self) -> &mut [u32] {
3772        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
3773        unsafe {
3774            let n = ffi::whiteout_mdx_MdxGeoset_get_matrixGroups_count(self.raw.as_ptr());
3775            let p =
3776                ffi::whiteout_mdx_MdxGeoset_get_matrixGroups_data(self.raw.as_ptr()) as *mut u32;
3777            if p.is_null() || n == 0 {
3778                &mut []
3779            } else {
3780                core::slice::from_raw_parts_mut(p, n)
3781            }
3782        }
3783    }
3784
3785    pub fn set_matrix_groups(&mut self, values: &[u32]) {
3786        // SAFETY: the native side copies `values` before returning.
3787        unsafe {
3788            ffi::whiteout_mdx_MdxGeoset_assign_matrixGroups(
3789                self.raw.as_ptr(),
3790                values.as_ptr() as *const _,
3791                values.len(),
3792            )
3793        }
3794    }
3795
3796    pub fn resize_matrix_groups(&mut self, count: usize) {
3797        // SAFETY: reallocation is safe here precisely because
3798        // `&mut self` means no slice borrow is outstanding.
3799        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_matrixGroups(self.raw.as_ptr(), count) }
3800    }
3801
3802    /// Bone indices
3803    /// Zero-copy view of the underlying `std::vector`.
3804    pub fn matrix_indices(&self) -> &[u32] {
3805        // SAFETY: `_data`/`_count` describe one contiguous C++
3806        // allocation, borrowed for as long as `self` is.
3807        unsafe {
3808            let n = ffi::whiteout_mdx_MdxGeoset_get_matrixIndices_count(self.raw.as_ptr());
3809            let p = ffi::whiteout_mdx_MdxGeoset_get_matrixIndices_data(self.raw.as_ptr());
3810            if p.is_null() || n == 0 {
3811                &[]
3812            } else {
3813                core::slice::from_raw_parts(p, n)
3814            }
3815        }
3816    }
3817
3818    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
3819    pub fn matrix_indices_mut(&mut self) -> &mut [u32] {
3820        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
3821        unsafe {
3822            let n = ffi::whiteout_mdx_MdxGeoset_get_matrixIndices_count(self.raw.as_ptr());
3823            let p =
3824                ffi::whiteout_mdx_MdxGeoset_get_matrixIndices_data(self.raw.as_ptr()) as *mut u32;
3825            if p.is_null() || n == 0 {
3826                &mut []
3827            } else {
3828                core::slice::from_raw_parts_mut(p, n)
3829            }
3830        }
3831    }
3832
3833    pub fn set_matrix_indices(&mut self, values: &[u32]) {
3834        // SAFETY: the native side copies `values` before returning.
3835        unsafe {
3836            ffi::whiteout_mdx_MdxGeoset_assign_matrixIndices(
3837                self.raw.as_ptr(),
3838                values.as_ptr() as *const _,
3839                values.len(),
3840            )
3841        }
3842    }
3843
3844    pub fn resize_matrix_indices(&mut self, count: usize) {
3845        // SAFETY: reallocation is safe here precisely because
3846        // `&mut self` means no slice borrow is outstanding.
3847        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_matrixIndices(self.raw.as_ptr(), count) }
3848    }
3849
3850    /// Material index
3851    pub fn material_id(&self) -> u32 {
3852        // SAFETY: plain scalar read through a live handle.
3853        unsafe { ffi::whiteout_mdx_MdxGeoset_get_materialId(self.raw.as_ptr()) }
3854    }
3855
3856    pub fn set_material_id(&mut self, value: u32) {
3857        // SAFETY: plain scalar write through a live handle.
3858        unsafe { ffi::whiteout_mdx_MdxGeoset_set_materialId(self.raw.as_ptr(), value) }
3859    }
3860
3861    /// Selection group (for editor)
3862    pub fn selection_group(&self) -> u32 {
3863        // SAFETY: plain scalar read through a live handle.
3864        unsafe { ffi::whiteout_mdx_MdxGeoset_get_selectionGroup(self.raw.as_ptr()) }
3865    }
3866
3867    pub fn set_selection_group(&mut self, value: u32) {
3868        // SAFETY: plain scalar write through a live handle.
3869        unsafe { ffi::whiteout_mdx_MdxGeoset_set_selectionGroup(self.raw.as_ptr(), value) }
3870    }
3871
3872    /// Selection flags
3873    pub fn selection_flags(&self) -> u32 {
3874        // SAFETY: plain scalar read through a live handle.
3875        unsafe { ffi::whiteout_mdx_MdxGeoset_get_selectionFlags(self.raw.as_ptr()) }
3876    }
3877
3878    pub fn set_selection_flags(&mut self, value: u32) {
3879        // SAFETY: plain scalar write through a live handle.
3880        unsafe { ffi::whiteout_mdx_MdxGeoset_set_selectionFlags(self.raw.as_ptr(), value) }
3881    }
3882
3883    /// Level of detail index
3884    pub fn lod(&self) -> u32 {
3885        // SAFETY: plain scalar read through a live handle.
3886        unsafe { ffi::whiteout_mdx_MdxGeoset_get_lod(self.raw.as_ptr()) }
3887    }
3888
3889    pub fn set_lod(&mut self, value: u32) {
3890        // SAFETY: plain scalar write through a live handle.
3891        unsafe { ffi::whiteout_mdx_MdxGeoset_set_lod(self.raw.as_ptr(), value) }
3892    }
3893
3894    /// LOD name
3895    pub fn lod_name(&self) -> String {
3896        // SAFETY: the native side hands over an owned CString.
3897        unsafe {
3898            crate::support::take_string(ffi::whiteout_mdx_MdxGeoset_get_lodName(self.raw.as_ptr()))
3899        }
3900    }
3901
3902    pub fn set_lod_name(&mut self, value: &str) {
3903        let value = std::ffi::CString::new(value).unwrap_or_default();
3904        // SAFETY: the pointer outlives the call.
3905        unsafe { ffi::whiteout_mdx_MdxGeoset_set_lodName(self.raw.as_ptr(), value.as_ptr()) }
3906    }
3907
3908    /// Bounding volume
3909    /// Borrows the field in place — no copy, no allocation.
3910    pub fn extent(&self) -> crate::support::Ref<'_, Extent> {
3911        // SAFETY: an interior pointer into `self`, valid for this
3912        // borrow and never freed by the `Ref`.
3913        unsafe {
3914            crate::support::Ref::new(Extent {
3915                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxGeoset_get_extent(
3916                    self.raw.as_ptr(),
3917                )),
3918            })
3919        }
3920    }
3921
3922    pub fn extent_mut(&mut self) -> crate::support::RefMut<'_, Extent> {
3923        // SAFETY: as above; `&mut self` guarantees exclusivity.
3924        unsafe {
3925            crate::support::RefMut::new(Extent {
3926                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxGeoset_get_extent(
3927                    self.raw.as_ptr(),
3928                )),
3929            })
3930        }
3931    }
3932
3933    /// Per-sequence bounding volumes
3934    pub fn sequence_extents_len(&self) -> usize {
3935        // SAFETY: scalar read through a live handle.
3936        unsafe { ffi::whiteout_mdx_MdxGeoset_get_sequenceExtents_count(self.raw.as_ptr()) }
3937    }
3938
3939    /// Borrows element `index` in place. `None` when out of range.
3940    pub fn sequence_extents(&self, index: usize) -> Option<crate::support::Ref<'_, Extent>> {
3941        if index >= self.sequence_extents_len() {
3942            return None;
3943        }
3944        // SAFETY: index checked above; the pointer is interior to `self`.
3945        unsafe {
3946            Some(crate::support::Ref::new(Extent {
3947                raw: core::ptr::NonNull::new_unchecked(
3948                    ffi::whiteout_mdx_MdxGeoset_get_sequenceExtents_at(self.raw.as_ptr(), index),
3949                ),
3950            }))
3951        }
3952    }
3953
3954    pub fn sequence_extents_mut(
3955        &mut self,
3956        index: usize,
3957    ) -> Option<crate::support::RefMut<'_, Extent>> {
3958        if index >= self.sequence_extents_len() {
3959            return None;
3960        }
3961        // SAFETY: as above; `&mut self` guarantees exclusivity.
3962        unsafe {
3963            Some(crate::support::RefMut::new(Extent {
3964                raw: core::ptr::NonNull::new_unchecked(
3965                    ffi::whiteout_mdx_MdxGeoset_get_sequenceExtents_at(self.raw.as_ptr(), index),
3966                ),
3967            }))
3968        }
3969    }
3970
3971    /// Iterate the elements, borrowing each in turn.
3972    pub fn sequence_extents_iter(
3973        &self,
3974    ) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Extent>> {
3975        (0..self.sequence_extents_len())
3976            .map(move |i| self.sequence_extents(i).expect("index below len"))
3977    }
3978
3979    pub fn resize_sequence_extents(&mut self, count: usize) {
3980        // SAFETY: exclusive access, so no borrow is outstanding.
3981        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_sequenceExtents(self.raw.as_ptr(), count) }
3982    }
3983
3984    /// Tangent vectors (for normal mapping)
3985    /// Zero-copy view of the underlying `std::vector`.
3986    pub fn tangents(&self) -> &[crate::math::Vector4f] {
3987        // SAFETY: `_data`/`_count` describe one contiguous C++
3988        // allocation, borrowed for as long as `self` is.
3989        unsafe {
3990            let n = ffi::whiteout_mdx_MdxGeoset_get_tangents_count(self.raw.as_ptr());
3991            let p = ffi::whiteout_mdx_MdxGeoset_get_tangents_data(self.raw.as_ptr())
3992                as *const crate::math::Vector4f;
3993            if p.is_null() || n == 0 {
3994                &[]
3995            } else {
3996                core::slice::from_raw_parts(p, n)
3997            }
3998        }
3999    }
4000
4001    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
4002    pub fn tangents_mut(&mut self) -> &mut [crate::math::Vector4f] {
4003        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
4004        unsafe {
4005            let n = ffi::whiteout_mdx_MdxGeoset_get_tangents_count(self.raw.as_ptr());
4006            let p = ffi::whiteout_mdx_MdxGeoset_get_tangents_data(self.raw.as_ptr())
4007                as *const crate::math::Vector4f as *mut crate::math::Vector4f;
4008            if p.is_null() || n == 0 {
4009                &mut []
4010            } else {
4011                core::slice::from_raw_parts_mut(p, n)
4012            }
4013        }
4014    }
4015
4016    pub fn set_tangents(&mut self, values: &[crate::math::Vector4f]) {
4017        // SAFETY: the native side copies `values` before returning.
4018        unsafe {
4019            ffi::whiteout_mdx_MdxGeoset_assign_tangents(
4020                self.raw.as_ptr(),
4021                values.as_ptr() as *const _,
4022                values.len(),
4023            )
4024        }
4025    }
4026
4027    pub fn resize_tangents(&mut self, count: usize) {
4028        // SAFETY: reallocation is safe here precisely because
4029        // `&mut self` means no slice borrow is outstanding.
4030        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_tangents(self.raw.as_ptr(), count) }
4031    }
4032
4033    /// Bone indices and weights
4034    /// Zero-copy view of the underlying `std::vector`.
4035    pub fn skin_data(&self) -> &[u8] {
4036        // SAFETY: `_data`/`_count` describe one contiguous C++
4037        // allocation, borrowed for as long as `self` is.
4038        unsafe {
4039            let n = ffi::whiteout_mdx_MdxGeoset_get_skinData_count(self.raw.as_ptr());
4040            let p = ffi::whiteout_mdx_MdxGeoset_get_skinData_data(self.raw.as_ptr());
4041            if p.is_null() || n == 0 {
4042                &[]
4043            } else {
4044                core::slice::from_raw_parts(p, n)
4045            }
4046        }
4047    }
4048
4049    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
4050    pub fn skin_data_mut(&mut self) -> &mut [u8] {
4051        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
4052        unsafe {
4053            let n = ffi::whiteout_mdx_MdxGeoset_get_skinData_count(self.raw.as_ptr());
4054            let p = ffi::whiteout_mdx_MdxGeoset_get_skinData_data(self.raw.as_ptr()) as *mut u8;
4055            if p.is_null() || n == 0 {
4056                &mut []
4057            } else {
4058                core::slice::from_raw_parts_mut(p, n)
4059            }
4060        }
4061    }
4062
4063    pub fn set_skin_data(&mut self, values: &[u8]) {
4064        // SAFETY: the native side copies `values` before returning.
4065        unsafe {
4066            ffi::whiteout_mdx_MdxGeoset_assign_skinData(
4067                self.raw.as_ptr(),
4068                values.as_ptr() as *const _,
4069                values.len(),
4070            )
4071        }
4072    }
4073
4074    pub fn resize_skin_data(&mut self, count: usize) {
4075        // SAFETY: reallocation is safe here precisely because
4076        // `&mut self` means no slice borrow is outstanding.
4077        unsafe { ffi::whiteout_mdx_MdxGeoset_resize_skinData(self.raw.as_ptr(), count) }
4078    }
4079
4080    /// UV coordinates (multiple sets)
4081    /// Number of inner sequences.
4082    pub fn texture_coordinate_sets_len(&self) -> usize {
4083        // SAFETY: scalar read through a live handle.
4084        unsafe { ffi::whiteout_mdx_MdxGeoset_get_textureCoordinateSets_count(self.raw.as_ptr()) }
4085    }
4086
4087    /// Zero-copy view of inner sequence `outer`. Empty when out of range.
4088    ///
4089    /// Each inner vector is contiguous on its own, but the outer one
4090    /// is a vector of vectors — so this borrows per sequence rather
4091    /// than handing back one flat slice.
4092    pub fn texture_coordinate_sets(&self, outer: usize) -> &[crate::math::Vector2f] {
4093        if outer >= self.texture_coordinate_sets_len() {
4094            return &[];
4095        }
4096        // SAFETY: index checked; the pointer borrows `self`.
4097        unsafe {
4098            let n = ffi::whiteout_mdx_MdxGeoset_get_textureCoordinateSets_inner_count(
4099                self.raw.as_ptr(),
4100                outer,
4101            );
4102            let p = ffi::whiteout_mdx_MdxGeoset_get_textureCoordinateSets_inner_data(
4103                self.raw.as_ptr(),
4104                outer,
4105            ) as *const crate::math::Vector2f;
4106            if p.is_null() || n == 0 {
4107                &[]
4108            } else {
4109                core::slice::from_raw_parts(p, n)
4110            }
4111        }
4112    }
4113
4114    pub fn texture_coordinate_sets_mut(&mut self, outer: usize) -> &mut [crate::math::Vector2f] {
4115        if outer >= self.texture_coordinate_sets_len() {
4116            return &mut [];
4117        }
4118        // SAFETY: as above; `&mut self` rules out aliasing.
4119        unsafe {
4120            let n = ffi::whiteout_mdx_MdxGeoset_get_textureCoordinateSets_inner_count(
4121                self.raw.as_ptr(),
4122                outer,
4123            );
4124            let p = ffi::whiteout_mdx_MdxGeoset_get_textureCoordinateSets_inner_data(
4125                self.raw.as_ptr(),
4126                outer,
4127            ) as *const crate::math::Vector2f as *mut crate::math::Vector2f;
4128            if p.is_null() || n == 0 {
4129                &mut []
4130            } else {
4131                core::slice::from_raw_parts_mut(p, n)
4132            }
4133        }
4134    }
4135
4136    pub fn set_texture_coordinate_sets(&mut self, outer: usize, values: &[crate::math::Vector2f]) {
4137        // SAFETY: the native side copies `values` before returning.
4138        unsafe {
4139            ffi::whiteout_mdx_MdxGeoset_assign_textureCoordinateSets_inner(
4140                self.raw.as_ptr(),
4141                outer,
4142                values.as_ptr() as *const _,
4143                values.len(),
4144            )
4145        }
4146    }
4147
4148    /// Resize the outer sequence. Do this before taking any borrow.
4149    pub fn resize_texture_coordinate_sets(&mut self, count: usize) {
4150        // SAFETY: exclusive access, so no borrow is outstanding.
4151        unsafe {
4152            ffi::whiteout_mdx_MdxGeoset_resize_textureCoordinateSets(self.raw.as_ptr(), count)
4153        }
4154    }
4155
4156    pub fn resize_texture_coordinate_sets_inner(&mut self, outer: usize, count: usize) {
4157        // SAFETY: as above.
4158        unsafe {
4159            ffi::whiteout_mdx_MdxGeoset_resize_textureCoordinateSets_inner(
4160                self.raw.as_ptr(),
4161                outer,
4162                count,
4163            )
4164        }
4165    }
4166}
4167
4168impl Default for Geoset {
4169    fn default() -> Self {
4170        Self::new()
4171    }
4172}
4173
4174/// Geoset visibility and color animation
4175///
4176/// Geoset animations control the visibility and color tinting of meshes.
4177pub struct GeosetAnimation {
4178    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxGeosetAnimation>,
4179}
4180
4181impl Drop for GeosetAnimation {
4182    fn drop(&mut self) {
4183        // SAFETY: `raw` came from a native constructor and Drop runs once.
4184        unsafe { ffi::whiteout_mdx_MdxGeosetAnimation_delete(self.raw.as_ptr()) }
4185    }
4186}
4187
4188impl GeosetAnimation {
4189    /// # Safety
4190    /// `raw` must be a live handle this value takes ownership of.
4191    #[allow(dead_code)] // used by whichever methods return this type
4192    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxGeosetAnimation) -> Option<Self> {
4193        core::ptr::NonNull::new(raw).map(|raw| GeosetAnimation { raw })
4194    }
4195}
4196
4197// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
4198// is deliberately NOT implemented — the C++ types make no documented
4199// guarantee about concurrent use, and claiming one we haven't verified
4200// would be unsound. See `@bind thread_safe` in the plan.
4201unsafe impl Send for GeosetAnimation {}
4202
4203impl core::fmt::Debug for GeosetAnimation {
4204    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4205        f.debug_struct("GeosetAnimation").finish_non_exhaustive()
4206    }
4207}
4208
4209impl GeosetAnimation {
4210    /// # Panics
4211    /// Panics if the native allocation fails.
4212    pub fn new() -> Self {
4213        // SAFETY: the native constructor returns a live handle; a null here
4214        // means the library is unusable.
4215        unsafe {
4216            let raw = ffi::whiteout_mdx_MdxGeosetAnimation_new();
4217            Self::from_raw(raw).expect("native GeosetAnimation allocation failed")
4218        }
4219    }
4220
4221    /// Base alpha value
4222    pub fn alpha(&self) -> f32 {
4223        // SAFETY: plain scalar read through a live handle.
4224        unsafe { ffi::whiteout_mdx_MdxGeosetAnimation_get_alpha(self.raw.as_ptr()) }
4225    }
4226
4227    pub fn set_alpha(&mut self, value: f32) {
4228        // SAFETY: plain scalar write through a live handle.
4229        unsafe { ffi::whiteout_mdx_MdxGeosetAnimation_set_alpha(self.raw.as_ptr(), value) }
4230    }
4231
4232    /// Animation flags
4233    pub fn flags(&self) -> SequenceFlag {
4234        // SAFETY: scalar read; the discriminant is validated below.
4235        unsafe { ffi::whiteout_mdx_MdxGeosetAnimation_get_flags(self.raw.as_ptr()) }
4236            .try_into()
4237            .expect("unknown enum discriminant from the native library")
4238    }
4239
4240    pub fn set_flags(&mut self, value: SequenceFlag) {
4241        // SAFETY: scalar write through a live handle.
4242        unsafe { ffi::whiteout_mdx_MdxGeosetAnimation_set_flags(self.raw.as_ptr(), value as i32) }
4243    }
4244
4245    /// Base color tint
4246    pub fn color(&self) -> crate::math::Vector3f {
4247        // SAFETY: the getter returns an interior pointer to a
4248        // layout-identical POD; we copy it out immediately.
4249        unsafe {
4250            *(ffi::whiteout_mdx_MdxGeosetAnimation_get_color(self.raw.as_ptr())
4251                as *const crate::math::Vector3f)
4252        }
4253    }
4254
4255    pub fn set_color(&mut self, value: crate::math::Vector3f) {
4256        // SAFETY: as above, in the other direction.
4257        unsafe {
4258            ffi::whiteout_mdx_MdxGeosetAnimation_set_color(
4259                self.raw.as_ptr(),
4260                &value as *const crate::math::Vector3f as *const _,
4261            )
4262        }
4263    }
4264
4265    /// Target geoset index
4266    pub fn geoset_id(&self) -> u32 {
4267        // SAFETY: plain scalar read through a live handle.
4268        unsafe { ffi::whiteout_mdx_MdxGeosetAnimation_get_geosetId(self.raw.as_ptr()) }
4269    }
4270
4271    pub fn set_geoset_id(&mut self, value: u32) {
4272        // SAFETY: plain scalar write through a live handle.
4273        unsafe { ffi::whiteout_mdx_MdxGeosetAnimation_set_geosetId(self.raw.as_ptr(), value) }
4274    }
4275
4276    /// Alpha animation
4277    /// Borrows the field in place — no copy, no allocation.
4278    pub fn alpha_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
4279        // SAFETY: an interior pointer into `self`, valid for this
4280        // borrow and never freed by the `Ref`.
4281        unsafe {
4282            crate::support::Ref::new(TrackF32 {
4283                raw: core::ptr::NonNull::new_unchecked(
4284                    ffi::whiteout_mdx_MdxGeosetAnimation_get_alphaTracks(self.raw.as_ptr()),
4285                ),
4286            })
4287        }
4288    }
4289
4290    pub fn alpha_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
4291        // SAFETY: as above; `&mut self` guarantees exclusivity.
4292        unsafe {
4293            crate::support::RefMut::new(TrackF32 {
4294                raw: core::ptr::NonNull::new_unchecked(
4295                    ffi::whiteout_mdx_MdxGeosetAnimation_get_alphaTracks(self.raw.as_ptr()),
4296                ),
4297            })
4298        }
4299    }
4300
4301    /// Color animation
4302    /// Borrows the field in place — no copy, no allocation.
4303    pub fn color_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
4304        // SAFETY: an interior pointer into `self`, valid for this
4305        // borrow and never freed by the `Ref`.
4306        unsafe {
4307            crate::support::Ref::new(TrackVector3f {
4308                raw: core::ptr::NonNull::new_unchecked(
4309                    ffi::whiteout_mdx_MdxGeosetAnimation_get_colorTracks(self.raw.as_ptr()),
4310                ),
4311            })
4312        }
4313    }
4314
4315    pub fn color_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
4316        // SAFETY: as above; `&mut self` guarantees exclusivity.
4317        unsafe {
4318            crate::support::RefMut::new(TrackVector3f {
4319                raw: core::ptr::NonNull::new_unchecked(
4320                    ffi::whiteout_mdx_MdxGeosetAnimation_get_colorTracks(self.raw.as_ptr()),
4321                ),
4322            })
4323        }
4324    }
4325}
4326
4327impl Default for GeosetAnimation {
4328    fn default() -> Self {
4329        Self::new()
4330    }
4331}
4332
4333/// Skeleton bone for skinned animation
4334///
4335/// Bones  form the skeleton that deforms mesh geometry. Each bone is a node in the hierarchy and can affect one or more geosets.
4336pub struct Bone {
4337    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxBone>,
4338}
4339
4340impl Drop for Bone {
4341    fn drop(&mut self) {
4342        // SAFETY: `raw` came from a native constructor and Drop runs once.
4343        unsafe { ffi::whiteout_mdx_MdxBone_delete(self.raw.as_ptr()) }
4344    }
4345}
4346
4347impl Bone {
4348    /// # Safety
4349    /// `raw` must be a live handle this value takes ownership of.
4350    #[allow(dead_code)] // used by whichever methods return this type
4351    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxBone) -> Option<Self> {
4352        core::ptr::NonNull::new(raw).map(|raw| Bone { raw })
4353    }
4354}
4355
4356// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
4357// is deliberately NOT implemented — the C++ types make no documented
4358// guarantee about concurrent use, and claiming one we haven't verified
4359// would be unsound. See `@bind thread_safe` in the plan.
4360unsafe impl Send for Bone {}
4361
4362impl core::fmt::Debug for Bone {
4363    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4364        f.debug_struct("Bone").finish_non_exhaustive()
4365    }
4366}
4367
4368impl Bone {
4369    /// # Panics
4370    /// Panics if the native allocation fails.
4371    pub fn new() -> Self {
4372        // SAFETY: the native constructor returns a live handle; a null here
4373        // means the library is unusable.
4374        unsafe {
4375            let raw = ffi::whiteout_mdx_MdxBone_new();
4376            Self::from_raw(raw).expect("native Bone allocation failed")
4377        }
4378    }
4379
4380    /// Base node data with transform
4381    /// Borrows the field in place — no copy, no allocation.
4382    pub fn node(&self) -> crate::support::Ref<'_, Node> {
4383        // SAFETY: an interior pointer into `self`, valid for this
4384        // borrow and never freed by the `Ref`.
4385        unsafe {
4386            crate::support::Ref::new(Node {
4387                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxBone_get_node(
4388                    self.raw.as_ptr(),
4389                )),
4390            })
4391        }
4392    }
4393
4394    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
4395        // SAFETY: as above; `&mut self` guarantees exclusivity.
4396        unsafe {
4397            crate::support::RefMut::new(Node {
4398                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxBone_get_node(
4399                    self.raw.as_ptr(),
4400                )),
4401            })
4402        }
4403    }
4404
4405    /// Geoset this bone affects
4406    pub fn geoset_id(&self) -> u32 {
4407        // SAFETY: plain scalar read through a live handle.
4408        unsafe { ffi::whiteout_mdx_MdxBone_get_geosetId(self.raw.as_ptr()) }
4409    }
4410
4411    pub fn set_geoset_id(&mut self, value: u32) {
4412        // SAFETY: plain scalar write through a live handle.
4413        unsafe { ffi::whiteout_mdx_MdxBone_set_geosetId(self.raw.as_ptr(), value) }
4414    }
4415
4416    /// Geoset animation index
4417    pub fn geoset_animation_id(&self) -> u32 {
4418        // SAFETY: plain scalar read through a live handle.
4419        unsafe { ffi::whiteout_mdx_MdxBone_get_geosetAnimationId(self.raw.as_ptr()) }
4420    }
4421
4422    pub fn set_geoset_animation_id(&mut self, value: u32) {
4423        // SAFETY: plain scalar write through a live handle.
4424        unsafe { ffi::whiteout_mdx_MdxBone_set_geosetAnimationId(self.raw.as_ptr(), value) }
4425    }
4426}
4427
4428impl Default for Bone {
4429    fn default() -> Self {
4430        Self::new()
4431    }
4432}
4433
4434/// Light source
4435///
4436/// Lights can be attached to bones to move with animations. They affect how the model is rendered in the game engine.
4437pub struct Light {
4438    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxLight>,
4439}
4440
4441impl Drop for Light {
4442    fn drop(&mut self) {
4443        // SAFETY: `raw` came from a native constructor and Drop runs once.
4444        unsafe { ffi::whiteout_mdx_MdxLight_delete(self.raw.as_ptr()) }
4445    }
4446}
4447
4448impl Light {
4449    /// # Safety
4450    /// `raw` must be a live handle this value takes ownership of.
4451    #[allow(dead_code)] // used by whichever methods return this type
4452    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxLight) -> Option<Self> {
4453        core::ptr::NonNull::new(raw).map(|raw| Light { raw })
4454    }
4455}
4456
4457// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
4458// is deliberately NOT implemented — the C++ types make no documented
4459// guarantee about concurrent use, and claiming one we haven't verified
4460// would be unsound. See `@bind thread_safe` in the plan.
4461unsafe impl Send for Light {}
4462
4463impl core::fmt::Debug for Light {
4464    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4465        f.debug_struct("Light").finish_non_exhaustive()
4466    }
4467}
4468
4469impl Light {
4470    /// # Panics
4471    /// Panics if the native allocation fails.
4472    pub fn new() -> Self {
4473        // SAFETY: the native constructor returns a live handle; a null here
4474        // means the library is unusable.
4475        unsafe {
4476            let raw = ffi::whiteout_mdx_MdxLight_new();
4477            Self::from_raw(raw).expect("native Light allocation failed")
4478        }
4479    }
4480
4481    /// Base node data
4482    /// Borrows the field in place — no copy, no allocation.
4483    pub fn node(&self) -> crate::support::Ref<'_, Node> {
4484        // SAFETY: an interior pointer into `self`, valid for this
4485        // borrow and never freed by the `Ref`.
4486        unsafe {
4487            crate::support::Ref::new(Node {
4488                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxLight_get_node(
4489                    self.raw.as_ptr(),
4490                )),
4491            })
4492        }
4493    }
4494
4495    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
4496        // SAFETY: as above; `&mut self` guarantees exclusivity.
4497        unsafe {
4498            crate::support::RefMut::new(Node {
4499                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxLight_get_node(
4500                    self.raw.as_ptr(),
4501                )),
4502            })
4503        }
4504    }
4505
4506    /// Type of light
4507    pub fn type_(&self) -> LightType {
4508        // SAFETY: scalar read; the discriminant is validated below.
4509        unsafe { ffi::whiteout_mdx_MdxLight_get_type(self.raw.as_ptr()) }
4510            .try_into()
4511            .expect("unknown enum discriminant from the native library")
4512    }
4513
4514    pub fn set_type_(&mut self, value: LightType) {
4515        // SAFETY: scalar write through a live handle.
4516        unsafe { ffi::whiteout_mdx_MdxLight_set_type(self.raw.as_ptr(), value as i32) }
4517    }
4518
4519    /// Distance where attenuation begins
4520    pub fn attenuation_start(&self) -> f32 {
4521        // SAFETY: plain scalar read through a live handle.
4522        unsafe { ffi::whiteout_mdx_MdxLight_get_attenuationStart(self.raw.as_ptr()) }
4523    }
4524
4525    pub fn set_attenuation_start(&mut self, value: f32) {
4526        // SAFETY: plain scalar write through a live handle.
4527        unsafe { ffi::whiteout_mdx_MdxLight_set_attenuationStart(self.raw.as_ptr(), value) }
4528    }
4529
4530    /// Distance where light reaches zero
4531    pub fn attenuation_end(&self) -> f32 {
4532        // SAFETY: plain scalar read through a live handle.
4533        unsafe { ffi::whiteout_mdx_MdxLight_get_attenuationEnd(self.raw.as_ptr()) }
4534    }
4535
4536    pub fn set_attenuation_end(&mut self, value: f32) {
4537        // SAFETY: plain scalar write through a live handle.
4538        unsafe { ffi::whiteout_mdx_MdxLight_set_attenuationEnd(self.raw.as_ptr(), value) }
4539    }
4540
4541    /// Light color (RGB)
4542    pub fn color(&self) -> crate::math::Vector3f {
4543        // SAFETY: the getter returns an interior pointer to a
4544        // layout-identical POD; we copy it out immediately.
4545        unsafe {
4546            *(ffi::whiteout_mdx_MdxLight_get_color(self.raw.as_ptr())
4547                as *const crate::math::Vector3f)
4548        }
4549    }
4550
4551    pub fn set_color(&mut self, value: crate::math::Vector3f) {
4552        // SAFETY: as above, in the other direction.
4553        unsafe {
4554            ffi::whiteout_mdx_MdxLight_set_color(
4555                self.raw.as_ptr(),
4556                &value as *const crate::math::Vector3f as *const _,
4557            )
4558        }
4559    }
4560
4561    /// Light intensity
4562    pub fn intensity(&self) -> f32 {
4563        // SAFETY: plain scalar read through a live handle.
4564        unsafe { ffi::whiteout_mdx_MdxLight_get_intensity(self.raw.as_ptr()) }
4565    }
4566
4567    pub fn set_intensity(&mut self, value: f32) {
4568        // SAFETY: plain scalar write through a live handle.
4569        unsafe { ffi::whiteout_mdx_MdxLight_set_intensity(self.raw.as_ptr(), value) }
4570    }
4571
4572    /// Ambient light color
4573    pub fn ambient_color(&self) -> crate::math::Vector3f {
4574        // SAFETY: the getter returns an interior pointer to a
4575        // layout-identical POD; we copy it out immediately.
4576        unsafe {
4577            *(ffi::whiteout_mdx_MdxLight_get_ambientColor(self.raw.as_ptr())
4578                as *const crate::math::Vector3f)
4579        }
4580    }
4581
4582    pub fn set_ambient_color(&mut self, value: crate::math::Vector3f) {
4583        // SAFETY: as above, in the other direction.
4584        unsafe {
4585            ffi::whiteout_mdx_MdxLight_set_ambientColor(
4586                self.raw.as_ptr(),
4587                &value as *const crate::math::Vector3f as *const _,
4588            )
4589        }
4590    }
4591
4592    /// Ambient intensity
4593    pub fn ambient_intensity(&self) -> f32 {
4594        // SAFETY: plain scalar read through a live handle.
4595        unsafe { ffi::whiteout_mdx_MdxLight_get_ambientIntensity(self.raw.as_ptr()) }
4596    }
4597
4598    pub fn set_ambient_intensity(&mut self, value: f32) {
4599        // SAFETY: plain scalar write through a live handle.
4600        unsafe { ffi::whiteout_mdx_MdxLight_set_ambientIntensity(self.raw.as_ptr(), value) }
4601    }
4602
4603    /// Shadow darkness (Reforged)
4604    pub fn shadow_intensity(&self) -> f32 {
4605        // SAFETY: plain scalar read through a live handle.
4606        unsafe { ffi::whiteout_mdx_MdxLight_get_shadowIntensity(self.raw.as_ptr()) }
4607    }
4608
4609    pub fn set_shadow_intensity(&mut self, value: f32) {
4610        // SAFETY: plain scalar write through a live handle.
4611        unsafe { ffi::whiteout_mdx_MdxLight_set_shadowIntensity(self.raw.as_ptr(), value) }
4612    }
4613
4614    /// Attenuation start animation
4615    /// Borrows the field in place — no copy, no allocation.
4616    pub fn attenuation_start_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
4617        // SAFETY: an interior pointer into `self`, valid for this
4618        // borrow and never freed by the `Ref`.
4619        unsafe {
4620            crate::support::Ref::new(TrackF32 {
4621                raw: core::ptr::NonNull::new_unchecked(
4622                    ffi::whiteout_mdx_MdxLight_get_attenuationStartTracks(self.raw.as_ptr()),
4623                ),
4624            })
4625        }
4626    }
4627
4628    pub fn attenuation_start_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
4629        // SAFETY: as above; `&mut self` guarantees exclusivity.
4630        unsafe {
4631            crate::support::RefMut::new(TrackF32 {
4632                raw: core::ptr::NonNull::new_unchecked(
4633                    ffi::whiteout_mdx_MdxLight_get_attenuationStartTracks(self.raw.as_ptr()),
4634                ),
4635            })
4636        }
4637    }
4638
4639    /// Attenuation end animation
4640    /// Borrows the field in place — no copy, no allocation.
4641    pub fn attenuation_end_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
4642        // SAFETY: an interior pointer into `self`, valid for this
4643        // borrow and never freed by the `Ref`.
4644        unsafe {
4645            crate::support::Ref::new(TrackF32 {
4646                raw: core::ptr::NonNull::new_unchecked(
4647                    ffi::whiteout_mdx_MdxLight_get_attenuationEndTracks(self.raw.as_ptr()),
4648                ),
4649            })
4650        }
4651    }
4652
4653    pub fn attenuation_end_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
4654        // SAFETY: as above; `&mut self` guarantees exclusivity.
4655        unsafe {
4656            crate::support::RefMut::new(TrackF32 {
4657                raw: core::ptr::NonNull::new_unchecked(
4658                    ffi::whiteout_mdx_MdxLight_get_attenuationEndTracks(self.raw.as_ptr()),
4659                ),
4660            })
4661        }
4662    }
4663
4664    /// Color animation
4665    /// Borrows the field in place — no copy, no allocation.
4666    pub fn color_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
4667        // SAFETY: an interior pointer into `self`, valid for this
4668        // borrow and never freed by the `Ref`.
4669        unsafe {
4670            crate::support::Ref::new(TrackVector3f {
4671                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxLight_get_colorTracks(
4672                    self.raw.as_ptr(),
4673                )),
4674            })
4675        }
4676    }
4677
4678    pub fn color_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
4679        // SAFETY: as above; `&mut self` guarantees exclusivity.
4680        unsafe {
4681            crate::support::RefMut::new(TrackVector3f {
4682                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxLight_get_colorTracks(
4683                    self.raw.as_ptr(),
4684                )),
4685            })
4686        }
4687    }
4688
4689    /// Intensity animation
4690    /// Borrows the field in place — no copy, no allocation.
4691    pub fn intensity_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
4692        // SAFETY: an interior pointer into `self`, valid for this
4693        // borrow and never freed by the `Ref`.
4694        unsafe {
4695            crate::support::Ref::new(TrackF32 {
4696                raw: core::ptr::NonNull::new_unchecked(
4697                    ffi::whiteout_mdx_MdxLight_get_intensityTracks(self.raw.as_ptr()),
4698                ),
4699            })
4700        }
4701    }
4702
4703    pub fn intensity_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
4704        // SAFETY: as above; `&mut self` guarantees exclusivity.
4705        unsafe {
4706            crate::support::RefMut::new(TrackF32 {
4707                raw: core::ptr::NonNull::new_unchecked(
4708                    ffi::whiteout_mdx_MdxLight_get_intensityTracks(self.raw.as_ptr()),
4709                ),
4710            })
4711        }
4712    }
4713
4714    /// Ambient intensity animation
4715    /// Borrows the field in place — no copy, no allocation.
4716    pub fn ambient_intensity_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
4717        // SAFETY: an interior pointer into `self`, valid for this
4718        // borrow and never freed by the `Ref`.
4719        unsafe {
4720            crate::support::Ref::new(TrackF32 {
4721                raw: core::ptr::NonNull::new_unchecked(
4722                    ffi::whiteout_mdx_MdxLight_get_ambientIntensityTracks(self.raw.as_ptr()),
4723                ),
4724            })
4725        }
4726    }
4727
4728    pub fn ambient_intensity_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
4729        // SAFETY: as above; `&mut self` guarantees exclusivity.
4730        unsafe {
4731            crate::support::RefMut::new(TrackF32 {
4732                raw: core::ptr::NonNull::new_unchecked(
4733                    ffi::whiteout_mdx_MdxLight_get_ambientIntensityTracks(self.raw.as_ptr()),
4734                ),
4735            })
4736        }
4737    }
4738
4739    /// Ambient color animation
4740    /// Borrows the field in place — no copy, no allocation.
4741    pub fn ambient_color_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
4742        // SAFETY: an interior pointer into `self`, valid for this
4743        // borrow and never freed by the `Ref`.
4744        unsafe {
4745            crate::support::Ref::new(TrackVector3f {
4746                raw: core::ptr::NonNull::new_unchecked(
4747                    ffi::whiteout_mdx_MdxLight_get_ambientColorTracks(self.raw.as_ptr()),
4748                ),
4749            })
4750        }
4751    }
4752
4753    pub fn ambient_color_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
4754        // SAFETY: as above; `&mut self` guarantees exclusivity.
4755        unsafe {
4756            crate::support::RefMut::new(TrackVector3f {
4757                raw: core::ptr::NonNull::new_unchecked(
4758                    ffi::whiteout_mdx_MdxLight_get_ambientColorTracks(self.raw.as_ptr()),
4759                ),
4760            })
4761        }
4762    }
4763
4764    /// Visibility animation
4765    /// Borrows the field in place — no copy, no allocation.
4766    pub fn visibility_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
4767        // SAFETY: an interior pointer into `self`, valid for this
4768        // borrow and never freed by the `Ref`.
4769        unsafe {
4770            crate::support::Ref::new(TrackF32 {
4771                raw: core::ptr::NonNull::new_unchecked(
4772                    ffi::whiteout_mdx_MdxLight_get_visibilityTracks(self.raw.as_ptr()),
4773                ),
4774            })
4775        }
4776    }
4777
4778    pub fn visibility_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
4779        // SAFETY: as above; `&mut self` guarantees exclusivity.
4780        unsafe {
4781            crate::support::RefMut::new(TrackF32 {
4782                raw: core::ptr::NonNull::new_unchecked(
4783                    ffi::whiteout_mdx_MdxLight_get_visibilityTracks(self.raw.as_ptr()),
4784                ),
4785            })
4786        }
4787    }
4788
4789    /// Shadow intensity animation (Reforged)
4790    /// Borrows the field in place — no copy, no allocation.
4791    pub fn shadow_intensity_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
4792        // SAFETY: an interior pointer into `self`, valid for this
4793        // borrow and never freed by the `Ref`.
4794        unsafe {
4795            crate::support::Ref::new(TrackF32 {
4796                raw: core::ptr::NonNull::new_unchecked(
4797                    ffi::whiteout_mdx_MdxLight_get_shadowIntensityTracks(self.raw.as_ptr()),
4798                ),
4799            })
4800        }
4801    }
4802
4803    pub fn shadow_intensity_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
4804        // SAFETY: as above; `&mut self` guarantees exclusivity.
4805        unsafe {
4806            crate::support::RefMut::new(TrackF32 {
4807                raw: core::ptr::NonNull::new_unchecked(
4808                    ffi::whiteout_mdx_MdxLight_get_shadowIntensityTracks(self.raw.as_ptr()),
4809                ),
4810            })
4811        }
4812    }
4813}
4814
4815impl Default for Light {
4816    fn default() -> Self {
4817        Self::new()
4818    }
4819}
4820
4821/// Helper node (attachment point)
4822///
4823/// Helpers are simple nodes used as attachment points for effects, weapons, or other objects. They don't render anything themselves.
4824pub struct Helper {
4825    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxHelper>,
4826}
4827
4828impl Drop for Helper {
4829    fn drop(&mut self) {
4830        // SAFETY: `raw` came from a native constructor and Drop runs once.
4831        unsafe { ffi::whiteout_mdx_MdxHelper_delete(self.raw.as_ptr()) }
4832    }
4833}
4834
4835impl Helper {
4836    /// # Safety
4837    /// `raw` must be a live handle this value takes ownership of.
4838    #[allow(dead_code)] // used by whichever methods return this type
4839    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxHelper) -> Option<Self> {
4840        core::ptr::NonNull::new(raw).map(|raw| Helper { raw })
4841    }
4842}
4843
4844// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
4845// is deliberately NOT implemented — the C++ types make no documented
4846// guarantee about concurrent use, and claiming one we haven't verified
4847// would be unsound. See `@bind thread_safe` in the plan.
4848unsafe impl Send for Helper {}
4849
4850impl core::fmt::Debug for Helper {
4851    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4852        f.debug_struct("Helper").finish_non_exhaustive()
4853    }
4854}
4855
4856impl Helper {
4857    /// # Panics
4858    /// Panics if the native allocation fails.
4859    pub fn new() -> Self {
4860        // SAFETY: the native constructor returns a live handle; a null here
4861        // means the library is unusable.
4862        unsafe {
4863            let raw = ffi::whiteout_mdx_MdxHelper_new();
4864            Self::from_raw(raw).expect("native Helper allocation failed")
4865        }
4866    }
4867
4868    /// Base node data with transform
4869    /// Borrows the field in place — no copy, no allocation.
4870    pub fn node(&self) -> crate::support::Ref<'_, Node> {
4871        // SAFETY: an interior pointer into `self`, valid for this
4872        // borrow and never freed by the `Ref`.
4873        unsafe {
4874            crate::support::Ref::new(Node {
4875                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxHelper_get_node(
4876                    self.raw.as_ptr(),
4877                )),
4878            })
4879        }
4880    }
4881
4882    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
4883        // SAFETY: as above; `&mut self` guarantees exclusivity.
4884        unsafe {
4885            crate::support::RefMut::new(Node {
4886                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxHelper_get_node(
4887                    self.raw.as_ptr(),
4888                )),
4889            })
4890        }
4891    }
4892}
4893
4894impl Default for Helper {
4895    fn default() -> Self {
4896        Self::new()
4897    }
4898}
4899
4900/// Attachment point for external models
4901///
4902/// Attachments define points where other models (like weapons or shields) can be attached to this model.
4903pub struct Attachment {
4904    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxAttachment>,
4905}
4906
4907impl Drop for Attachment {
4908    fn drop(&mut self) {
4909        // SAFETY: `raw` came from a native constructor and Drop runs once.
4910        unsafe { ffi::whiteout_mdx_MdxAttachment_delete(self.raw.as_ptr()) }
4911    }
4912}
4913
4914impl Attachment {
4915    /// # Safety
4916    /// `raw` must be a live handle this value takes ownership of.
4917    #[allow(dead_code)] // used by whichever methods return this type
4918    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxAttachment) -> Option<Self> {
4919        core::ptr::NonNull::new(raw).map(|raw| Attachment { raw })
4920    }
4921}
4922
4923// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
4924// is deliberately NOT implemented — the C++ types make no documented
4925// guarantee about concurrent use, and claiming one we haven't verified
4926// would be unsound. See `@bind thread_safe` in the plan.
4927unsafe impl Send for Attachment {}
4928
4929impl core::fmt::Debug for Attachment {
4930    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4931        f.debug_struct("Attachment").finish_non_exhaustive()
4932    }
4933}
4934
4935impl Attachment {
4936    /// # Panics
4937    /// Panics if the native allocation fails.
4938    pub fn new() -> Self {
4939        // SAFETY: the native constructor returns a live handle; a null here
4940        // means the library is unusable.
4941        unsafe {
4942            let raw = ffi::whiteout_mdx_MdxAttachment_new();
4943            Self::from_raw(raw).expect("native Attachment allocation failed")
4944        }
4945    }
4946
4947    /// Base node data
4948    /// Borrows the field in place — no copy, no allocation.
4949    pub fn node(&self) -> crate::support::Ref<'_, Node> {
4950        // SAFETY: an interior pointer into `self`, valid for this
4951        // borrow and never freed by the `Ref`.
4952        unsafe {
4953            crate::support::Ref::new(Node {
4954                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxAttachment_get_node(
4955                    self.raw.as_ptr(),
4956                )),
4957            })
4958        }
4959    }
4960
4961    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
4962        // SAFETY: as above; `&mut self` guarantees exclusivity.
4963        unsafe {
4964            crate::support::RefMut::new(Node {
4965                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxAttachment_get_node(
4966                    self.raw.as_ptr(),
4967                )),
4968            })
4969        }
4970    }
4971
4972    /// Path to attached model
4973    pub fn path(&self) -> String {
4974        // SAFETY: the native side hands over an owned CString.
4975        unsafe {
4976            crate::support::take_string(ffi::whiteout_mdx_MdxAttachment_get_path(self.raw.as_ptr()))
4977        }
4978    }
4979
4980    pub fn set_path(&mut self, value: &str) {
4981        let value = std::ffi::CString::new(value).unwrap_or_default();
4982        // SAFETY: the pointer outlives the call.
4983        unsafe { ffi::whiteout_mdx_MdxAttachment_set_path(self.raw.as_ptr(), value.as_ptr()) }
4984    }
4985
4986    /// Attachment slot ID
4987    pub fn attachment_id(&self) -> u32 {
4988        // SAFETY: plain scalar read through a live handle.
4989        unsafe { ffi::whiteout_mdx_MdxAttachment_get_attachmentId(self.raw.as_ptr()) }
4990    }
4991
4992    pub fn set_attachment_id(&mut self, value: u32) {
4993        // SAFETY: plain scalar write through a live handle.
4994        unsafe { ffi::whiteout_mdx_MdxAttachment_set_attachmentId(self.raw.as_ptr(), value) }
4995    }
4996
4997    /// Visibility animation
4998    /// Borrows the field in place — no copy, no allocation.
4999    pub fn visibility_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5000        // SAFETY: an interior pointer into `self`, valid for this
5001        // borrow and never freed by the `Ref`.
5002        unsafe {
5003            crate::support::Ref::new(TrackF32 {
5004                raw: core::ptr::NonNull::new_unchecked(
5005                    ffi::whiteout_mdx_MdxAttachment_get_visibilityTracks(self.raw.as_ptr()),
5006                ),
5007            })
5008        }
5009    }
5010
5011    pub fn visibility_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5012        // SAFETY: as above; `&mut self` guarantees exclusivity.
5013        unsafe {
5014            crate::support::RefMut::new(TrackF32 {
5015                raw: core::ptr::NonNull::new_unchecked(
5016                    ffi::whiteout_mdx_MdxAttachment_get_visibilityTracks(self.raw.as_ptr()),
5017                ),
5018            })
5019        }
5020    }
5021}
5022
5023impl Default for Attachment {
5024    fn default() -> Self {
5025        Self::new()
5026    }
5027}
5028
5029/// Particle emitter version 1 (uses external model)
5030///
5031/// Legacy particle emitter that spawns copies of an external model file. Used for effects like footprints, blood splatter, etc.
5032pub struct ParticleEmitter {
5033    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxParticleEmitter>,
5034}
5035
5036impl Drop for ParticleEmitter {
5037    fn drop(&mut self) {
5038        // SAFETY: `raw` came from a native constructor and Drop runs once.
5039        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_delete(self.raw.as_ptr()) }
5040    }
5041}
5042
5043impl ParticleEmitter {
5044    /// # Safety
5045    /// `raw` must be a live handle this value takes ownership of.
5046    #[allow(dead_code)] // used by whichever methods return this type
5047    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxParticleEmitter) -> Option<Self> {
5048        core::ptr::NonNull::new(raw).map(|raw| ParticleEmitter { raw })
5049    }
5050}
5051
5052// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
5053// is deliberately NOT implemented — the C++ types make no documented
5054// guarantee about concurrent use, and claiming one we haven't verified
5055// would be unsound. See `@bind thread_safe` in the plan.
5056unsafe impl Send for ParticleEmitter {}
5057
5058impl core::fmt::Debug for ParticleEmitter {
5059    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5060        f.debug_struct("ParticleEmitter").finish_non_exhaustive()
5061    }
5062}
5063
5064impl ParticleEmitter {
5065    /// # Panics
5066    /// Panics if the native allocation fails.
5067    pub fn new() -> Self {
5068        // SAFETY: the native constructor returns a live handle; a null here
5069        // means the library is unusable.
5070        unsafe {
5071            let raw = ffi::whiteout_mdx_MdxParticleEmitter_new();
5072            Self::from_raw(raw).expect("native ParticleEmitter allocation failed")
5073        }
5074    }
5075
5076    /// Base node data
5077    /// Borrows the field in place — no copy, no allocation.
5078    pub fn node(&self) -> crate::support::Ref<'_, Node> {
5079        // SAFETY: an interior pointer into `self`, valid for this
5080        // borrow and never freed by the `Ref`.
5081        unsafe {
5082            crate::support::Ref::new(Node {
5083                raw: core::ptr::NonNull::new_unchecked(
5084                    ffi::whiteout_mdx_MdxParticleEmitter_get_node(self.raw.as_ptr()),
5085                ),
5086            })
5087        }
5088    }
5089
5090    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
5091        // SAFETY: as above; `&mut self` guarantees exclusivity.
5092        unsafe {
5093            crate::support::RefMut::new(Node {
5094                raw: core::ptr::NonNull::new_unchecked(
5095                    ffi::whiteout_mdx_MdxParticleEmitter_get_node(self.raw.as_ptr()),
5096                ),
5097            })
5098        }
5099    }
5100
5101    /// Particles per second
5102    pub fn emission_rate(&self) -> f32 {
5103        // SAFETY: plain scalar read through a live handle.
5104        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_get_emissionRate(self.raw.as_ptr()) }
5105    }
5106
5107    pub fn set_emission_rate(&mut self, value: f32) {
5108        // SAFETY: plain scalar write through a live handle.
5109        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_set_emissionRate(self.raw.as_ptr(), value) }
5110    }
5111
5112    /// Gravity force
5113    pub fn gravity(&self) -> f32 {
5114        // SAFETY: plain scalar read through a live handle.
5115        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_get_gravity(self.raw.as_ptr()) }
5116    }
5117
5118    pub fn set_gravity(&mut self, value: f32) {
5119        // SAFETY: plain scalar write through a live handle.
5120        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_set_gravity(self.raw.as_ptr(), value) }
5121    }
5122
5123    /// Emission longitude angle
5124    pub fn longitude(&self) -> f32 {
5125        // SAFETY: plain scalar read through a live handle.
5126        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_get_longitude(self.raw.as_ptr()) }
5127    }
5128
5129    pub fn set_longitude(&mut self, value: f32) {
5130        // SAFETY: plain scalar write through a live handle.
5131        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_set_longitude(self.raw.as_ptr(), value) }
5132    }
5133
5134    /// Emission latitude angle
5135    pub fn latitude(&self) -> f32 {
5136        // SAFETY: plain scalar read through a live handle.
5137        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_get_latitude(self.raw.as_ptr()) }
5138    }
5139
5140    pub fn set_latitude(&mut self, value: f32) {
5141        // SAFETY: plain scalar write through a live handle.
5142        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_set_latitude(self.raw.as_ptr(), value) }
5143    }
5144
5145    /// Model to spawn as particles
5146    pub fn spawn_model_file_name(&self) -> String {
5147        // SAFETY: the native side hands over an owned CString.
5148        unsafe {
5149            crate::support::take_string(
5150                ffi::whiteout_mdx_MdxParticleEmitter_get_spawnModelFileName(self.raw.as_ptr()),
5151            )
5152        }
5153    }
5154
5155    pub fn set_spawn_model_file_name(&mut self, value: &str) {
5156        let value = std::ffi::CString::new(value).unwrap_or_default();
5157        // SAFETY: the pointer outlives the call.
5158        unsafe {
5159            ffi::whiteout_mdx_MdxParticleEmitter_set_spawnModelFileName(
5160                self.raw.as_ptr(),
5161                value.as_ptr(),
5162            )
5163        }
5164    }
5165
5166    /// Particle lifetime in seconds
5167    pub fn lifespan(&self) -> f32 {
5168        // SAFETY: plain scalar read through a live handle.
5169        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_get_lifespan(self.raw.as_ptr()) }
5170    }
5171
5172    pub fn set_lifespan(&mut self, value: f32) {
5173        // SAFETY: plain scalar write through a live handle.
5174        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_set_lifespan(self.raw.as_ptr(), value) }
5175    }
5176
5177    /// Initial particle speed
5178    pub fn initial_velocity(&self) -> f32 {
5179        // SAFETY: plain scalar read through a live handle.
5180        unsafe { ffi::whiteout_mdx_MdxParticleEmitter_get_initialVelocity(self.raw.as_ptr()) }
5181    }
5182
5183    pub fn set_initial_velocity(&mut self, value: f32) {
5184        // SAFETY: plain scalar write through a live handle.
5185        unsafe {
5186            ffi::whiteout_mdx_MdxParticleEmitter_set_initialVelocity(self.raw.as_ptr(), value)
5187        }
5188    }
5189
5190    /// Emission rate animation
5191    /// Borrows the field in place — no copy, no allocation.
5192    pub fn emission_rate_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5193        // SAFETY: an interior pointer into `self`, valid for this
5194        // borrow and never freed by the `Ref`.
5195        unsafe {
5196            crate::support::Ref::new(TrackF32 {
5197                raw: core::ptr::NonNull::new_unchecked(
5198                    ffi::whiteout_mdx_MdxParticleEmitter_get_emissionRateTracks(self.raw.as_ptr()),
5199                ),
5200            })
5201        }
5202    }
5203
5204    pub fn emission_rate_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5205        // SAFETY: as above; `&mut self` guarantees exclusivity.
5206        unsafe {
5207            crate::support::RefMut::new(TrackF32 {
5208                raw: core::ptr::NonNull::new_unchecked(
5209                    ffi::whiteout_mdx_MdxParticleEmitter_get_emissionRateTracks(self.raw.as_ptr()),
5210                ),
5211            })
5212        }
5213    }
5214
5215    /// Gravity animation
5216    /// Borrows the field in place — no copy, no allocation.
5217    pub fn gravity_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5218        // SAFETY: an interior pointer into `self`, valid for this
5219        // borrow and never freed by the `Ref`.
5220        unsafe {
5221            crate::support::Ref::new(TrackF32 {
5222                raw: core::ptr::NonNull::new_unchecked(
5223                    ffi::whiteout_mdx_MdxParticleEmitter_get_gravityTracks(self.raw.as_ptr()),
5224                ),
5225            })
5226        }
5227    }
5228
5229    pub fn gravity_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5230        // SAFETY: as above; `&mut self` guarantees exclusivity.
5231        unsafe {
5232            crate::support::RefMut::new(TrackF32 {
5233                raw: core::ptr::NonNull::new_unchecked(
5234                    ffi::whiteout_mdx_MdxParticleEmitter_get_gravityTracks(self.raw.as_ptr()),
5235                ),
5236            })
5237        }
5238    }
5239
5240    /// Longitude animation
5241    /// Borrows the field in place — no copy, no allocation.
5242    pub fn longitude_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5243        // SAFETY: an interior pointer into `self`, valid for this
5244        // borrow and never freed by the `Ref`.
5245        unsafe {
5246            crate::support::Ref::new(TrackF32 {
5247                raw: core::ptr::NonNull::new_unchecked(
5248                    ffi::whiteout_mdx_MdxParticleEmitter_get_longitudeTracks(self.raw.as_ptr()),
5249                ),
5250            })
5251        }
5252    }
5253
5254    pub fn longitude_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5255        // SAFETY: as above; `&mut self` guarantees exclusivity.
5256        unsafe {
5257            crate::support::RefMut::new(TrackF32 {
5258                raw: core::ptr::NonNull::new_unchecked(
5259                    ffi::whiteout_mdx_MdxParticleEmitter_get_longitudeTracks(self.raw.as_ptr()),
5260                ),
5261            })
5262        }
5263    }
5264
5265    /// Latitude animation
5266    /// Borrows the field in place — no copy, no allocation.
5267    pub fn latitude_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5268        // SAFETY: an interior pointer into `self`, valid for this
5269        // borrow and never freed by the `Ref`.
5270        unsafe {
5271            crate::support::Ref::new(TrackF32 {
5272                raw: core::ptr::NonNull::new_unchecked(
5273                    ffi::whiteout_mdx_MdxParticleEmitter_get_latitudeTracks(self.raw.as_ptr()),
5274                ),
5275            })
5276        }
5277    }
5278
5279    pub fn latitude_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5280        // SAFETY: as above; `&mut self` guarantees exclusivity.
5281        unsafe {
5282            crate::support::RefMut::new(TrackF32 {
5283                raw: core::ptr::NonNull::new_unchecked(
5284                    ffi::whiteout_mdx_MdxParticleEmitter_get_latitudeTracks(self.raw.as_ptr()),
5285                ),
5286            })
5287        }
5288    }
5289
5290    /// Lifespan animation
5291    /// Borrows the field in place — no copy, no allocation.
5292    pub fn lifespan_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5293        // SAFETY: an interior pointer into `self`, valid for this
5294        // borrow and never freed by the `Ref`.
5295        unsafe {
5296            crate::support::Ref::new(TrackF32 {
5297                raw: core::ptr::NonNull::new_unchecked(
5298                    ffi::whiteout_mdx_MdxParticleEmitter_get_lifespanTracks(self.raw.as_ptr()),
5299                ),
5300            })
5301        }
5302    }
5303
5304    pub fn lifespan_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5305        // SAFETY: as above; `&mut self` guarantees exclusivity.
5306        unsafe {
5307            crate::support::RefMut::new(TrackF32 {
5308                raw: core::ptr::NonNull::new_unchecked(
5309                    ffi::whiteout_mdx_MdxParticleEmitter_get_lifespanTracks(self.raw.as_ptr()),
5310                ),
5311            })
5312        }
5313    }
5314
5315    /// Speed animation
5316    /// Borrows the field in place — no copy, no allocation.
5317    pub fn speed_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5318        // SAFETY: an interior pointer into `self`, valid for this
5319        // borrow and never freed by the `Ref`.
5320        unsafe {
5321            crate::support::Ref::new(TrackF32 {
5322                raw: core::ptr::NonNull::new_unchecked(
5323                    ffi::whiteout_mdx_MdxParticleEmitter_get_speedTracks(self.raw.as_ptr()),
5324                ),
5325            })
5326        }
5327    }
5328
5329    pub fn speed_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5330        // SAFETY: as above; `&mut self` guarantees exclusivity.
5331        unsafe {
5332            crate::support::RefMut::new(TrackF32 {
5333                raw: core::ptr::NonNull::new_unchecked(
5334                    ffi::whiteout_mdx_MdxParticleEmitter_get_speedTracks(self.raw.as_ptr()),
5335                ),
5336            })
5337        }
5338    }
5339
5340    /// Visibility animation
5341    /// Borrows the field in place — no copy, no allocation.
5342    pub fn visibility_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5343        // SAFETY: an interior pointer into `self`, valid for this
5344        // borrow and never freed by the `Ref`.
5345        unsafe {
5346            crate::support::Ref::new(TrackF32 {
5347                raw: core::ptr::NonNull::new_unchecked(
5348                    ffi::whiteout_mdx_MdxParticleEmitter_get_visibilityTracks(self.raw.as_ptr()),
5349                ),
5350            })
5351        }
5352    }
5353
5354    pub fn visibility_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5355        // SAFETY: as above; `&mut self` guarantees exclusivity.
5356        unsafe {
5357            crate::support::RefMut::new(TrackF32 {
5358                raw: core::ptr::NonNull::new_unchecked(
5359                    ffi::whiteout_mdx_MdxParticleEmitter_get_visibilityTracks(self.raw.as_ptr()),
5360                ),
5361            })
5362        }
5363    }
5364}
5365
5366impl Default for ParticleEmitter {
5367    fn default() -> Self {
5368        Self::new()
5369    }
5370}
5371
5372/// Particle emitter version 2 (sprite-based)
5373///
5374/// More advanced particle system that uses sprite textures. Supports various particle shapes, blending modes, and animation over the particle lifetime. Used for fire, smoke, magic effects, etc.
5375pub struct ParticleEmitter2 {
5376    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxParticleEmitter2>,
5377}
5378
5379impl Drop for ParticleEmitter2 {
5380    fn drop(&mut self) {
5381        // SAFETY: `raw` came from a native constructor and Drop runs once.
5382        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_delete(self.raw.as_ptr()) }
5383    }
5384}
5385
5386impl ParticleEmitter2 {
5387    /// # Safety
5388    /// `raw` must be a live handle this value takes ownership of.
5389    #[allow(dead_code)] // used by whichever methods return this type
5390    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxParticleEmitter2) -> Option<Self> {
5391        core::ptr::NonNull::new(raw).map(|raw| ParticleEmitter2 { raw })
5392    }
5393}
5394
5395// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
5396// is deliberately NOT implemented — the C++ types make no documented
5397// guarantee about concurrent use, and claiming one we haven't verified
5398// would be unsound. See `@bind thread_safe` in the plan.
5399unsafe impl Send for ParticleEmitter2 {}
5400
5401impl core::fmt::Debug for ParticleEmitter2 {
5402    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5403        f.debug_struct("ParticleEmitter2").finish_non_exhaustive()
5404    }
5405}
5406
5407impl ParticleEmitter2 {
5408    /// # Panics
5409    /// Panics if the native allocation fails.
5410    pub fn new() -> Self {
5411        // SAFETY: the native constructor returns a live handle; a null here
5412        // means the library is unusable.
5413        unsafe {
5414            let raw = ffi::whiteout_mdx_MdxParticleEmitter2_new();
5415            Self::from_raw(raw).expect("native ParticleEmitter2 allocation failed")
5416        }
5417    }
5418
5419    /// Base node data
5420    /// Borrows the field in place — no copy, no allocation.
5421    pub fn node(&self) -> crate::support::Ref<'_, Node> {
5422        // SAFETY: an interior pointer into `self`, valid for this
5423        // borrow and never freed by the `Ref`.
5424        unsafe {
5425            crate::support::Ref::new(Node {
5426                raw: core::ptr::NonNull::new_unchecked(
5427                    ffi::whiteout_mdx_MdxParticleEmitter2_get_node(self.raw.as_ptr()),
5428                ),
5429            })
5430        }
5431    }
5432
5433    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
5434        // SAFETY: as above; `&mut self` guarantees exclusivity.
5435        unsafe {
5436            crate::support::RefMut::new(Node {
5437                raw: core::ptr::NonNull::new_unchecked(
5438                    ffi::whiteout_mdx_MdxParticleEmitter2_get_node(self.raw.as_ptr()),
5439                ),
5440            })
5441        }
5442    }
5443
5444    /// Particle speed
5445    pub fn speed(&self) -> f32 {
5446        // SAFETY: plain scalar read through a live handle.
5447        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_speed(self.raw.as_ptr()) }
5448    }
5449
5450    pub fn set_speed(&mut self, value: f32) {
5451        // SAFETY: plain scalar write through a live handle.
5452        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_speed(self.raw.as_ptr(), value) }
5453    }
5454
5455    /// Speed variation (randomness)
5456    pub fn variation(&self) -> f32 {
5457        // SAFETY: plain scalar read through a live handle.
5458        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_variation(self.raw.as_ptr()) }
5459    }
5460
5461    pub fn set_variation(&mut self, value: f32) {
5462        // SAFETY: plain scalar write through a live handle.
5463        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_variation(self.raw.as_ptr(), value) }
5464    }
5465
5466    /// Emission cone latitude
5467    pub fn latitude(&self) -> f32 {
5468        // SAFETY: plain scalar read through a live handle.
5469        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_latitude(self.raw.as_ptr()) }
5470    }
5471
5472    pub fn set_latitude(&mut self, value: f32) {
5473        // SAFETY: plain scalar write through a live handle.
5474        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_latitude(self.raw.as_ptr(), value) }
5475    }
5476
5477    /// Gravity acceleration
5478    pub fn gravity(&self) -> f32 {
5479        // SAFETY: plain scalar read through a live handle.
5480        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_gravity(self.raw.as_ptr()) }
5481    }
5482
5483    pub fn set_gravity(&mut self, value: f32) {
5484        // SAFETY: plain scalar write through a live handle.
5485        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_gravity(self.raw.as_ptr(), value) }
5486    }
5487
5488    /// Particle lifetime in seconds
5489    pub fn lifespan(&self) -> f32 {
5490        // SAFETY: plain scalar read through a live handle.
5491        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_lifespan(self.raw.as_ptr()) }
5492    }
5493
5494    pub fn set_lifespan(&mut self, value: f32) {
5495        // SAFETY: plain scalar write through a live handle.
5496        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_lifespan(self.raw.as_ptr(), value) }
5497    }
5498
5499    /// Particles per second
5500    pub fn emission_rate(&self) -> f32 {
5501        // SAFETY: plain scalar read through a live handle.
5502        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_emissionRate(self.raw.as_ptr()) }
5503    }
5504
5505    pub fn set_emission_rate(&mut self, value: f32) {
5506        // SAFETY: plain scalar write through a live handle.
5507        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_emissionRate(self.raw.as_ptr(), value) }
5508    }
5509
5510    /// Particle length (for tail effect)
5511    pub fn length(&self) -> f32 {
5512        // SAFETY: plain scalar read through a live handle.
5513        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_length(self.raw.as_ptr()) }
5514    }
5515
5516    pub fn set_length(&mut self, value: f32) {
5517        // SAFETY: plain scalar write through a live handle.
5518        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_length(self.raw.as_ptr(), value) }
5519    }
5520
5521    /// Particle width
5522    pub fn width(&self) -> f32 {
5523        // SAFETY: plain scalar read through a live handle.
5524        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_width(self.raw.as_ptr()) }
5525    }
5526
5527    pub fn set_width(&mut self, value: f32) {
5528        // SAFETY: plain scalar write through a live handle.
5529        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_width(self.raw.as_ptr(), value) }
5530    }
5531
5532    /// Blending mode
5533    pub fn filter_mode(&self) -> u32 {
5534        // SAFETY: plain scalar read through a live handle.
5535        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_filterMode(self.raw.as_ptr()) }
5536    }
5537
5538    pub fn set_filter_mode(&mut self, value: u32) {
5539        // SAFETY: plain scalar write through a live handle.
5540        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_filterMode(self.raw.as_ptr(), value) }
5541    }
5542
5543    /// Texture atlas rows
5544    pub fn rows(&self) -> u32 {
5545        // SAFETY: plain scalar read through a live handle.
5546        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_rows(self.raw.as_ptr()) }
5547    }
5548
5549    pub fn set_rows(&mut self, value: u32) {
5550        // SAFETY: plain scalar write through a live handle.
5551        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_rows(self.raw.as_ptr(), value) }
5552    }
5553
5554    /// Texture atlas columns
5555    pub fn columns(&self) -> u32 {
5556        // SAFETY: plain scalar read through a live handle.
5557        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_columns(self.raw.as_ptr()) }
5558    }
5559
5560    pub fn set_columns(&mut self, value: u32) {
5561        // SAFETY: plain scalar write through a live handle.
5562        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_columns(self.raw.as_ptr(), value) }
5563    }
5564
5565    /// Head/tail flags
5566    pub fn head_or_tail(&self) -> u32 {
5567        // SAFETY: plain scalar read through a live handle.
5568        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_headOrTail(self.raw.as_ptr()) }
5569    }
5570
5571    pub fn set_head_or_tail(&mut self, value: u32) {
5572        // SAFETY: plain scalar write through a live handle.
5573        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_headOrTail(self.raw.as_ptr(), value) }
5574    }
5575
5576    /// Tail particle length
5577    pub fn tail_length(&self) -> f32 {
5578        // SAFETY: plain scalar read through a live handle.
5579        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_tailLength(self.raw.as_ptr()) }
5580    }
5581
5582    pub fn set_tail_length(&mut self, value: f32) {
5583        // SAFETY: plain scalar write through a live handle.
5584        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_tailLength(self.raw.as_ptr(), value) }
5585    }
5586
5587    /// Middle time for segment animation
5588    pub fn time(&self) -> f32 {
5589        // SAFETY: plain scalar read through a live handle.
5590        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_time(self.raw.as_ptr()) }
5591    }
5592
5593    pub fn set_time(&mut self, value: f32) {
5594        // SAFETY: plain scalar write through a live handle.
5595        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_time(self.raw.as_ptr(), value) }
5596    }
5597
5598    /// Color at start/middle/end
5599    /// Number of elements — a fixed-size C++ array.
5600    pub const fn segment_color_len() -> usize {
5601        3
5602    }
5603
5604    /// # Panics
5605    /// If `index >= 3`, matching Rust slice indexing. The
5606    /// native accessor does no bounds checking of its own, so
5607    /// this check is what keeps the method safe to call.
5608    pub fn segment_color(&self, index: usize) -> crate::math::Vector3f {
5609        assert!(
5610            index < 3,
5611            "segment_color index {index} out of range (len 3)"
5612        );
5613        // SAFETY: index checked above; the getter returns an
5614        // interior pointer to a layout-identical POD, copied
5615        // out immediately.
5616        unsafe {
5617            *(ffi::whiteout_mdx_MdxParticleEmitter2_get_segmentColor_at(self.raw.as_ptr(), index)
5618                as *const crate::math::Vector3f)
5619        }
5620    }
5621
5622    /// Alpha at start/middle/end
5623    /// Number of elements — a fixed-size C++ array.
5624    pub const fn segment_alpha_len() -> usize {
5625        3
5626    }
5627
5628    /// # Panics
5629    /// If `index >= 3`, matching Rust slice indexing.
5630    pub fn segment_alpha(&self, index: usize) -> u8 {
5631        assert!(
5632            index < 3,
5633            "segment_alpha index {index} out of range (len 3)"
5634        );
5635        // SAFETY: index checked above; plain scalar read.
5636        unsafe {
5637            ffi::whiteout_mdx_MdxParticleEmitter2_get_segmentAlpha_at(self.raw.as_ptr(), index)
5638        }
5639    }
5640
5641    /// # Panics
5642    /// If `index >= 3`.
5643    pub fn set_segment_alpha(&mut self, index: usize, value: u8) {
5644        assert!(
5645            index < 3,
5646            "segment_alpha index {index} out of range (len 3)"
5647        );
5648        // SAFETY: index checked above.
5649        unsafe {
5650            ffi::whiteout_mdx_MdxParticleEmitter2_set_segmentAlpha_at(
5651                self.raw.as_ptr(),
5652                index,
5653                value,
5654            )
5655        }
5656    }
5657
5658    /// Scale at start/middle/end
5659    /// Number of elements — a fixed-size C++ array.
5660    pub const fn segment_scaling_len() -> usize {
5661        3
5662    }
5663
5664    /// # Panics
5665    /// If `index >= 3`, matching Rust slice indexing.
5666    pub fn segment_scaling(&self, index: usize) -> f32 {
5667        assert!(
5668            index < 3,
5669            "segment_scaling index {index} out of range (len 3)"
5670        );
5671        // SAFETY: index checked above; plain scalar read.
5672        unsafe {
5673            ffi::whiteout_mdx_MdxParticleEmitter2_get_segmentScaling_at(self.raw.as_ptr(), index)
5674        }
5675    }
5676
5677    /// # Panics
5678    /// If `index >= 3`.
5679    pub fn set_segment_scaling(&mut self, index: usize, value: f32) {
5680        assert!(
5681            index < 3,
5682            "segment_scaling index {index} out of range (len 3)"
5683        );
5684        // SAFETY: index checked above.
5685        unsafe {
5686            ffi::whiteout_mdx_MdxParticleEmitter2_set_segmentScaling_at(
5687                self.raw.as_ptr(),
5688                index,
5689                value,
5690            )
5691        }
5692    }
5693
5694    /// Head lifetime intervals
5695    /// Number of elements — a fixed-size C++ array.
5696    pub const fn head_interval_len() -> usize {
5697        3
5698    }
5699
5700    /// # Panics
5701    /// If `index >= 3`, matching Rust slice indexing.
5702    pub fn head_interval(&self, index: usize) -> u32 {
5703        assert!(
5704            index < 3,
5705            "head_interval index {index} out of range (len 3)"
5706        );
5707        // SAFETY: index checked above; plain scalar read.
5708        unsafe {
5709            ffi::whiteout_mdx_MdxParticleEmitter2_get_headInterval_at(self.raw.as_ptr(), index)
5710        }
5711    }
5712
5713    /// # Panics
5714    /// If `index >= 3`.
5715    pub fn set_head_interval(&mut self, index: usize, value: u32) {
5716        assert!(
5717            index < 3,
5718            "head_interval index {index} out of range (len 3)"
5719        );
5720        // SAFETY: index checked above.
5721        unsafe {
5722            ffi::whiteout_mdx_MdxParticleEmitter2_set_headInterval_at(
5723                self.raw.as_ptr(),
5724                index,
5725                value,
5726            )
5727        }
5728    }
5729
5730    /// Head decay intervals
5731    /// Number of elements — a fixed-size C++ array.
5732    pub const fn head_decay_interval_len() -> usize {
5733        3
5734    }
5735
5736    /// # Panics
5737    /// If `index >= 3`, matching Rust slice indexing.
5738    pub fn head_decay_interval(&self, index: usize) -> u32 {
5739        assert!(
5740            index < 3,
5741            "head_decay_interval index {index} out of range (len 3)"
5742        );
5743        // SAFETY: index checked above; plain scalar read.
5744        unsafe {
5745            ffi::whiteout_mdx_MdxParticleEmitter2_get_headDecayInterval_at(self.raw.as_ptr(), index)
5746        }
5747    }
5748
5749    /// # Panics
5750    /// If `index >= 3`.
5751    pub fn set_head_decay_interval(&mut self, index: usize, value: u32) {
5752        assert!(
5753            index < 3,
5754            "head_decay_interval index {index} out of range (len 3)"
5755        );
5756        // SAFETY: index checked above.
5757        unsafe {
5758            ffi::whiteout_mdx_MdxParticleEmitter2_set_headDecayInterval_at(
5759                self.raw.as_ptr(),
5760                index,
5761                value,
5762            )
5763        }
5764    }
5765
5766    /// Tail lifetime intervals
5767    /// Number of elements — a fixed-size C++ array.
5768    pub const fn tail_interval_len() -> usize {
5769        3
5770    }
5771
5772    /// # Panics
5773    /// If `index >= 3`, matching Rust slice indexing.
5774    pub fn tail_interval(&self, index: usize) -> u32 {
5775        assert!(
5776            index < 3,
5777            "tail_interval index {index} out of range (len 3)"
5778        );
5779        // SAFETY: index checked above; plain scalar read.
5780        unsafe {
5781            ffi::whiteout_mdx_MdxParticleEmitter2_get_tailInterval_at(self.raw.as_ptr(), index)
5782        }
5783    }
5784
5785    /// # Panics
5786    /// If `index >= 3`.
5787    pub fn set_tail_interval(&mut self, index: usize, value: u32) {
5788        assert!(
5789            index < 3,
5790            "tail_interval index {index} out of range (len 3)"
5791        );
5792        // SAFETY: index checked above.
5793        unsafe {
5794            ffi::whiteout_mdx_MdxParticleEmitter2_set_tailInterval_at(
5795                self.raw.as_ptr(),
5796                index,
5797                value,
5798            )
5799        }
5800    }
5801
5802    /// Tail decay intervals
5803    /// Number of elements — a fixed-size C++ array.
5804    pub const fn tail_decay_interval_len() -> usize {
5805        3
5806    }
5807
5808    /// # Panics
5809    /// If `index >= 3`, matching Rust slice indexing.
5810    pub fn tail_decay_interval(&self, index: usize) -> u32 {
5811        assert!(
5812            index < 3,
5813            "tail_decay_interval index {index} out of range (len 3)"
5814        );
5815        // SAFETY: index checked above; plain scalar read.
5816        unsafe {
5817            ffi::whiteout_mdx_MdxParticleEmitter2_get_tailDecayInterval_at(self.raw.as_ptr(), index)
5818        }
5819    }
5820
5821    /// # Panics
5822    /// If `index >= 3`.
5823    pub fn set_tail_decay_interval(&mut self, index: usize, value: u32) {
5824        assert!(
5825            index < 3,
5826            "tail_decay_interval index {index} out of range (len 3)"
5827        );
5828        // SAFETY: index checked above.
5829        unsafe {
5830            ffi::whiteout_mdx_MdxParticleEmitter2_set_tailDecayInterval_at(
5831                self.raw.as_ptr(),
5832                index,
5833                value,
5834            )
5835        }
5836    }
5837
5838    /// Texture index
5839    pub fn texture_id(&self) -> u32 {
5840        // SAFETY: plain scalar read through a live handle.
5841        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_textureId(self.raw.as_ptr()) }
5842    }
5843
5844    pub fn set_texture_id(&mut self, value: u32) {
5845        // SAFETY: plain scalar write through a live handle.
5846        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_textureId(self.raw.as_ptr(), value) }
5847    }
5848
5849    /// Squirt flag (burst mode)
5850    pub fn squirt(&self) -> u32 {
5851        // SAFETY: plain scalar read through a live handle.
5852        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_squirt(self.raw.as_ptr()) }
5853    }
5854
5855    pub fn set_squirt(&mut self, value: u32) {
5856        // SAFETY: plain scalar write through a live handle.
5857        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_squirt(self.raw.as_ptr(), value) }
5858    }
5859
5860    /// Rendering priority; signed
5861    pub fn priority_plane(&self) -> i32 {
5862        // SAFETY: plain scalar read through a live handle.
5863        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_priorityPlane(self.raw.as_ptr()) }
5864    }
5865
5866    pub fn set_priority_plane(&mut self, value: i32) {
5867        // SAFETY: plain scalar write through a live handle.
5868        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_priorityPlane(self.raw.as_ptr(), value) }
5869    }
5870
5871    /// Replaceable texture ID
5872    pub fn replaceable_id(&self) -> u32 {
5873        // SAFETY: plain scalar read through a live handle.
5874        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_replaceableId(self.raw.as_ptr()) }
5875    }
5876
5877    pub fn set_replaceable_id(&mut self, value: u32) {
5878        // SAFETY: plain scalar write through a live handle.
5879        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_replaceableId(self.raw.as_ptr(), value) }
5880    }
5881
5882    /// Speed animation
5883    /// Borrows the field in place — no copy, no allocation.
5884    pub fn speed_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5885        // SAFETY: an interior pointer into `self`, valid for this
5886        // borrow and never freed by the `Ref`.
5887        unsafe {
5888            crate::support::Ref::new(TrackF32 {
5889                raw: core::ptr::NonNull::new_unchecked(
5890                    ffi::whiteout_mdx_MdxParticleEmitter2_get_speedTracks(self.raw.as_ptr()),
5891                ),
5892            })
5893        }
5894    }
5895
5896    pub fn speed_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5897        // SAFETY: as above; `&mut self` guarantees exclusivity.
5898        unsafe {
5899            crate::support::RefMut::new(TrackF32 {
5900                raw: core::ptr::NonNull::new_unchecked(
5901                    ffi::whiteout_mdx_MdxParticleEmitter2_get_speedTracks(self.raw.as_ptr()),
5902                ),
5903            })
5904        }
5905    }
5906
5907    /// Variation animation
5908    /// Borrows the field in place — no copy, no allocation.
5909    pub fn variation_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5910        // SAFETY: an interior pointer into `self`, valid for this
5911        // borrow and never freed by the `Ref`.
5912        unsafe {
5913            crate::support::Ref::new(TrackF32 {
5914                raw: core::ptr::NonNull::new_unchecked(
5915                    ffi::whiteout_mdx_MdxParticleEmitter2_get_variationTracks(self.raw.as_ptr()),
5916                ),
5917            })
5918        }
5919    }
5920
5921    pub fn variation_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5922        // SAFETY: as above; `&mut self` guarantees exclusivity.
5923        unsafe {
5924            crate::support::RefMut::new(TrackF32 {
5925                raw: core::ptr::NonNull::new_unchecked(
5926                    ffi::whiteout_mdx_MdxParticleEmitter2_get_variationTracks(self.raw.as_ptr()),
5927                ),
5928            })
5929        }
5930    }
5931
5932    /// Latitude animation
5933    /// Borrows the field in place — no copy, no allocation.
5934    pub fn latitude_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5935        // SAFETY: an interior pointer into `self`, valid for this
5936        // borrow and never freed by the `Ref`.
5937        unsafe {
5938            crate::support::Ref::new(TrackF32 {
5939                raw: core::ptr::NonNull::new_unchecked(
5940                    ffi::whiteout_mdx_MdxParticleEmitter2_get_latitudeTracks(self.raw.as_ptr()),
5941                ),
5942            })
5943        }
5944    }
5945
5946    pub fn latitude_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5947        // SAFETY: as above; `&mut self` guarantees exclusivity.
5948        unsafe {
5949            crate::support::RefMut::new(TrackF32 {
5950                raw: core::ptr::NonNull::new_unchecked(
5951                    ffi::whiteout_mdx_MdxParticleEmitter2_get_latitudeTracks(self.raw.as_ptr()),
5952                ),
5953            })
5954        }
5955    }
5956
5957    /// Gravity animation
5958    /// Borrows the field in place — no copy, no allocation.
5959    pub fn gravity_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5960        // SAFETY: an interior pointer into `self`, valid for this
5961        // borrow and never freed by the `Ref`.
5962        unsafe {
5963            crate::support::Ref::new(TrackF32 {
5964                raw: core::ptr::NonNull::new_unchecked(
5965                    ffi::whiteout_mdx_MdxParticleEmitter2_get_gravityTracks(self.raw.as_ptr()),
5966                ),
5967            })
5968        }
5969    }
5970
5971    pub fn gravity_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5972        // SAFETY: as above; `&mut self` guarantees exclusivity.
5973        unsafe {
5974            crate::support::RefMut::new(TrackF32 {
5975                raw: core::ptr::NonNull::new_unchecked(
5976                    ffi::whiteout_mdx_MdxParticleEmitter2_get_gravityTracks(self.raw.as_ptr()),
5977                ),
5978            })
5979        }
5980    }
5981
5982    /// Emission rate animation
5983    /// Borrows the field in place — no copy, no allocation.
5984    pub fn emission_rate_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5985        // SAFETY: an interior pointer into `self`, valid for this
5986        // borrow and never freed by the `Ref`.
5987        unsafe {
5988            crate::support::Ref::new(TrackF32 {
5989                raw: core::ptr::NonNull::new_unchecked(
5990                    ffi::whiteout_mdx_MdxParticleEmitter2_get_emissionRateTracks(self.raw.as_ptr()),
5991                ),
5992            })
5993        }
5994    }
5995
5996    pub fn emission_rate_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5997        // SAFETY: as above; `&mut self` guarantees exclusivity.
5998        unsafe {
5999            crate::support::RefMut::new(TrackF32 {
6000                raw: core::ptr::NonNull::new_unchecked(
6001                    ffi::whiteout_mdx_MdxParticleEmitter2_get_emissionRateTracks(self.raw.as_ptr()),
6002                ),
6003            })
6004        }
6005    }
6006
6007    /// Length animation
6008    /// Borrows the field in place — no copy, no allocation.
6009    pub fn length_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6010        // SAFETY: an interior pointer into `self`, valid for this
6011        // borrow and never freed by the `Ref`.
6012        unsafe {
6013            crate::support::Ref::new(TrackF32 {
6014                raw: core::ptr::NonNull::new_unchecked(
6015                    ffi::whiteout_mdx_MdxParticleEmitter2_get_lengthTracks(self.raw.as_ptr()),
6016                ),
6017            })
6018        }
6019    }
6020
6021    pub fn length_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6022        // SAFETY: as above; `&mut self` guarantees exclusivity.
6023        unsafe {
6024            crate::support::RefMut::new(TrackF32 {
6025                raw: core::ptr::NonNull::new_unchecked(
6026                    ffi::whiteout_mdx_MdxParticleEmitter2_get_lengthTracks(self.raw.as_ptr()),
6027                ),
6028            })
6029        }
6030    }
6031
6032    /// Width animation
6033    /// Borrows the field in place — no copy, no allocation.
6034    pub fn width_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6035        // SAFETY: an interior pointer into `self`, valid for this
6036        // borrow and never freed by the `Ref`.
6037        unsafe {
6038            crate::support::Ref::new(TrackF32 {
6039                raw: core::ptr::NonNull::new_unchecked(
6040                    ffi::whiteout_mdx_MdxParticleEmitter2_get_widthTracks(self.raw.as_ptr()),
6041                ),
6042            })
6043        }
6044    }
6045
6046    pub fn width_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6047        // SAFETY: as above; `&mut self` guarantees exclusivity.
6048        unsafe {
6049            crate::support::RefMut::new(TrackF32 {
6050                raw: core::ptr::NonNull::new_unchecked(
6051                    ffi::whiteout_mdx_MdxParticleEmitter2_get_widthTracks(self.raw.as_ptr()),
6052                ),
6053            })
6054        }
6055    }
6056
6057    /// Visibility animation
6058    /// Borrows the field in place — no copy, no allocation.
6059    pub fn visibility_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6060        // SAFETY: an interior pointer into `self`, valid for this
6061        // borrow and never freed by the `Ref`.
6062        unsafe {
6063            crate::support::Ref::new(TrackF32 {
6064                raw: core::ptr::NonNull::new_unchecked(
6065                    ffi::whiteout_mdx_MdxParticleEmitter2_get_visibilityTracks(self.raw.as_ptr()),
6066                ),
6067            })
6068        }
6069    }
6070
6071    pub fn visibility_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6072        // SAFETY: as above; `&mut self` guarantees exclusivity.
6073        unsafe {
6074            crate::support::RefMut::new(TrackF32 {
6075                raw: core::ptr::NonNull::new_unchecked(
6076                    ffi::whiteout_mdx_MdxParticleEmitter2_get_visibilityTracks(self.raw.as_ptr()),
6077                ),
6078            })
6079        }
6080    }
6081}
6082
6083impl Default for ParticleEmitter2 {
6084    fn default() -> Self {
6085        Self::new()
6086    }
6087}
6088
6089/// Ribbon/trail emitter
6090///
6091/// Creates ribbon trails that follow the emitter's movement, like sword trails, missile contrails, etc.
6092pub struct RibbonEmitter {
6093    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxRibbonEmitter>,
6094}
6095
6096impl Drop for RibbonEmitter {
6097    fn drop(&mut self) {
6098        // SAFETY: `raw` came from a native constructor and Drop runs once.
6099        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_delete(self.raw.as_ptr()) }
6100    }
6101}
6102
6103impl RibbonEmitter {
6104    /// # Safety
6105    /// `raw` must be a live handle this value takes ownership of.
6106    #[allow(dead_code)] // used by whichever methods return this type
6107    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxRibbonEmitter) -> Option<Self> {
6108        core::ptr::NonNull::new(raw).map(|raw| RibbonEmitter { raw })
6109    }
6110}
6111
6112// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6113// is deliberately NOT implemented — the C++ types make no documented
6114// guarantee about concurrent use, and claiming one we haven't verified
6115// would be unsound. See `@bind thread_safe` in the plan.
6116unsafe impl Send for RibbonEmitter {}
6117
6118impl core::fmt::Debug for RibbonEmitter {
6119    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6120        f.debug_struct("RibbonEmitter").finish_non_exhaustive()
6121    }
6122}
6123
6124impl RibbonEmitter {
6125    /// # Panics
6126    /// Panics if the native allocation fails.
6127    pub fn new() -> Self {
6128        // SAFETY: the native constructor returns a live handle; a null here
6129        // means the library is unusable.
6130        unsafe {
6131            let raw = ffi::whiteout_mdx_MdxRibbonEmitter_new();
6132            Self::from_raw(raw).expect("native RibbonEmitter allocation failed")
6133        }
6134    }
6135
6136    /// Base node data
6137    /// Borrows the field in place — no copy, no allocation.
6138    pub fn node(&self) -> crate::support::Ref<'_, Node> {
6139        // SAFETY: an interior pointer into `self`, valid for this
6140        // borrow and never freed by the `Ref`.
6141        unsafe {
6142            crate::support::Ref::new(Node {
6143                raw: core::ptr::NonNull::new_unchecked(
6144                    ffi::whiteout_mdx_MdxRibbonEmitter_get_node(self.raw.as_ptr()),
6145                ),
6146            })
6147        }
6148    }
6149
6150    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
6151        // SAFETY: as above; `&mut self` guarantees exclusivity.
6152        unsafe {
6153            crate::support::RefMut::new(Node {
6154                raw: core::ptr::NonNull::new_unchecked(
6155                    ffi::whiteout_mdx_MdxRibbonEmitter_get_node(self.raw.as_ptr()),
6156                ),
6157            })
6158        }
6159    }
6160
6161    /// Height above attachment point
6162    pub fn height_above(&self) -> f32 {
6163        // SAFETY: plain scalar read through a live handle.
6164        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_heightAbove(self.raw.as_ptr()) }
6165    }
6166
6167    pub fn set_height_above(&mut self, value: f32) {
6168        // SAFETY: plain scalar write through a live handle.
6169        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_heightAbove(self.raw.as_ptr(), value) }
6170    }
6171
6172    /// Height below attachment point
6173    pub fn height_below(&self) -> f32 {
6174        // SAFETY: plain scalar read through a live handle.
6175        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_heightBelow(self.raw.as_ptr()) }
6176    }
6177
6178    pub fn set_height_below(&mut self, value: f32) {
6179        // SAFETY: plain scalar write through a live handle.
6180        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_heightBelow(self.raw.as_ptr(), value) }
6181    }
6182
6183    /// Ribbon opacity
6184    pub fn alpha(&self) -> f32 {
6185        // SAFETY: plain scalar read through a live handle.
6186        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_alpha(self.raw.as_ptr()) }
6187    }
6188
6189    pub fn set_alpha(&mut self, value: f32) {
6190        // SAFETY: plain scalar write through a live handle.
6191        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_alpha(self.raw.as_ptr(), value) }
6192    }
6193
6194    /// Ribbon color
6195    pub fn color(&self) -> crate::math::Vector3f {
6196        // SAFETY: the getter returns an interior pointer to a
6197        // layout-identical POD; we copy it out immediately.
6198        unsafe {
6199            *(ffi::whiteout_mdx_MdxRibbonEmitter_get_color(self.raw.as_ptr())
6200                as *const crate::math::Vector3f)
6201        }
6202    }
6203
6204    pub fn set_color(&mut self, value: crate::math::Vector3f) {
6205        // SAFETY: as above, in the other direction.
6206        unsafe {
6207            ffi::whiteout_mdx_MdxRibbonEmitter_set_color(
6208                self.raw.as_ptr(),
6209                &value as *const crate::math::Vector3f as *const _,
6210            )
6211        }
6212    }
6213
6214    /// Ribbon segment lifetime
6215    pub fn lifespan(&self) -> f32 {
6216        // SAFETY: plain scalar read through a live handle.
6217        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_lifespan(self.raw.as_ptr()) }
6218    }
6219
6220    pub fn set_lifespan(&mut self, value: f32) {
6221        // SAFETY: plain scalar write through a live handle.
6222        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_lifespan(self.raw.as_ptr(), value) }
6223    }
6224
6225    /// Texture slot in material
6226    pub fn texture_slot(&self) -> u32 {
6227        // SAFETY: plain scalar read through a live handle.
6228        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_textureSlot(self.raw.as_ptr()) }
6229    }
6230
6231    pub fn set_texture_slot(&mut self, value: u32) {
6232        // SAFETY: plain scalar write through a live handle.
6233        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_textureSlot(self.raw.as_ptr(), value) }
6234    }
6235
6236    /// Emission rate
6237    pub fn emission_rate(&self) -> u32 {
6238        // SAFETY: plain scalar read through a live handle.
6239        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_emissionRate(self.raw.as_ptr()) }
6240    }
6241
6242    pub fn set_emission_rate(&mut self, value: u32) {
6243        // SAFETY: plain scalar write through a live handle.
6244        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_emissionRate(self.raw.as_ptr(), value) }
6245    }
6246
6247    /// Texture atlas rows
6248    pub fn rows(&self) -> u32 {
6249        // SAFETY: plain scalar read through a live handle.
6250        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_rows(self.raw.as_ptr()) }
6251    }
6252
6253    pub fn set_rows(&mut self, value: u32) {
6254        // SAFETY: plain scalar write through a live handle.
6255        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_rows(self.raw.as_ptr(), value) }
6256    }
6257
6258    /// Texture atlas columns
6259    pub fn columns(&self) -> u32 {
6260        // SAFETY: plain scalar read through a live handle.
6261        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_columns(self.raw.as_ptr()) }
6262    }
6263
6264    pub fn set_columns(&mut self, value: u32) {
6265        // SAFETY: plain scalar write through a live handle.
6266        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_columns(self.raw.as_ptr(), value) }
6267    }
6268
6269    /// Material index
6270    pub fn material_id(&self) -> u32 {
6271        // SAFETY: plain scalar read through a live handle.
6272        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_materialId(self.raw.as_ptr()) }
6273    }
6274
6275    pub fn set_material_id(&mut self, value: u32) {
6276        // SAFETY: plain scalar write through a live handle.
6277        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_materialId(self.raw.as_ptr(), value) }
6278    }
6279
6280    /// Gravity effect
6281    pub fn gravity(&self) -> f32 {
6282        // SAFETY: plain scalar read through a live handle.
6283        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_gravity(self.raw.as_ptr()) }
6284    }
6285
6286    pub fn set_gravity(&mut self, value: f32) {
6287        // SAFETY: plain scalar write through a live handle.
6288        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_gravity(self.raw.as_ptr(), value) }
6289    }
6290
6291    /// Height above animation
6292    /// Borrows the field in place — no copy, no allocation.
6293    pub fn height_above_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6294        // SAFETY: an interior pointer into `self`, valid for this
6295        // borrow and never freed by the `Ref`.
6296        unsafe {
6297            crate::support::Ref::new(TrackF32 {
6298                raw: core::ptr::NonNull::new_unchecked(
6299                    ffi::whiteout_mdx_MdxRibbonEmitter_get_heightAboveTracks(self.raw.as_ptr()),
6300                ),
6301            })
6302        }
6303    }
6304
6305    pub fn height_above_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6306        // SAFETY: as above; `&mut self` guarantees exclusivity.
6307        unsafe {
6308            crate::support::RefMut::new(TrackF32 {
6309                raw: core::ptr::NonNull::new_unchecked(
6310                    ffi::whiteout_mdx_MdxRibbonEmitter_get_heightAboveTracks(self.raw.as_ptr()),
6311                ),
6312            })
6313        }
6314    }
6315
6316    /// Height below animation
6317    /// Borrows the field in place — no copy, no allocation.
6318    pub fn height_below_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6319        // SAFETY: an interior pointer into `self`, valid for this
6320        // borrow and never freed by the `Ref`.
6321        unsafe {
6322            crate::support::Ref::new(TrackF32 {
6323                raw: core::ptr::NonNull::new_unchecked(
6324                    ffi::whiteout_mdx_MdxRibbonEmitter_get_heightBelowTracks(self.raw.as_ptr()),
6325                ),
6326            })
6327        }
6328    }
6329
6330    pub fn height_below_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6331        // SAFETY: as above; `&mut self` guarantees exclusivity.
6332        unsafe {
6333            crate::support::RefMut::new(TrackF32 {
6334                raw: core::ptr::NonNull::new_unchecked(
6335                    ffi::whiteout_mdx_MdxRibbonEmitter_get_heightBelowTracks(self.raw.as_ptr()),
6336                ),
6337            })
6338        }
6339    }
6340
6341    /// Alpha animation
6342    /// Borrows the field in place — no copy, no allocation.
6343    pub fn alpha_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6344        // SAFETY: an interior pointer into `self`, valid for this
6345        // borrow and never freed by the `Ref`.
6346        unsafe {
6347            crate::support::Ref::new(TrackF32 {
6348                raw: core::ptr::NonNull::new_unchecked(
6349                    ffi::whiteout_mdx_MdxRibbonEmitter_get_alphaTracks(self.raw.as_ptr()),
6350                ),
6351            })
6352        }
6353    }
6354
6355    pub fn alpha_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6356        // SAFETY: as above; `&mut self` guarantees exclusivity.
6357        unsafe {
6358            crate::support::RefMut::new(TrackF32 {
6359                raw: core::ptr::NonNull::new_unchecked(
6360                    ffi::whiteout_mdx_MdxRibbonEmitter_get_alphaTracks(self.raw.as_ptr()),
6361                ),
6362            })
6363        }
6364    }
6365
6366    /// Color animation
6367    /// Borrows the field in place — no copy, no allocation.
6368    pub fn color_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
6369        // SAFETY: an interior pointer into `self`, valid for this
6370        // borrow and never freed by the `Ref`.
6371        unsafe {
6372            crate::support::Ref::new(TrackVector3f {
6373                raw: core::ptr::NonNull::new_unchecked(
6374                    ffi::whiteout_mdx_MdxRibbonEmitter_get_colorTracks(self.raw.as_ptr()),
6375                ),
6376            })
6377        }
6378    }
6379
6380    pub fn color_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
6381        // SAFETY: as above; `&mut self` guarantees exclusivity.
6382        unsafe {
6383            crate::support::RefMut::new(TrackVector3f {
6384                raw: core::ptr::NonNull::new_unchecked(
6385                    ffi::whiteout_mdx_MdxRibbonEmitter_get_colorTracks(self.raw.as_ptr()),
6386                ),
6387            })
6388        }
6389    }
6390
6391    /// Texture slot animation
6392    /// Borrows the field in place — no copy, no allocation.
6393    pub fn texture_slot_tracks(&self) -> crate::support::Ref<'_, TrackU32> {
6394        // SAFETY: an interior pointer into `self`, valid for this
6395        // borrow and never freed by the `Ref`.
6396        unsafe {
6397            crate::support::Ref::new(TrackU32 {
6398                raw: core::ptr::NonNull::new_unchecked(
6399                    ffi::whiteout_mdx_MdxRibbonEmitter_get_textureSlotTracks(self.raw.as_ptr()),
6400                ),
6401            })
6402        }
6403    }
6404
6405    pub fn texture_slot_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackU32> {
6406        // SAFETY: as above; `&mut self` guarantees exclusivity.
6407        unsafe {
6408            crate::support::RefMut::new(TrackU32 {
6409                raw: core::ptr::NonNull::new_unchecked(
6410                    ffi::whiteout_mdx_MdxRibbonEmitter_get_textureSlotTracks(self.raw.as_ptr()),
6411                ),
6412            })
6413        }
6414    }
6415
6416    /// Visibility animation
6417    /// Borrows the field in place — no copy, no allocation.
6418    pub fn visibility_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6419        // SAFETY: an interior pointer into `self`, valid for this
6420        // borrow and never freed by the `Ref`.
6421        unsafe {
6422            crate::support::Ref::new(TrackF32 {
6423                raw: core::ptr::NonNull::new_unchecked(
6424                    ffi::whiteout_mdx_MdxRibbonEmitter_get_visibilityTracks(self.raw.as_ptr()),
6425                ),
6426            })
6427        }
6428    }
6429
6430    pub fn visibility_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6431        // SAFETY: as above; `&mut self` guarantees exclusivity.
6432        unsafe {
6433            crate::support::RefMut::new(TrackF32 {
6434                raw: core::ptr::NonNull::new_unchecked(
6435                    ffi::whiteout_mdx_MdxRibbonEmitter_get_visibilityTracks(self.raw.as_ptr()),
6436                ),
6437            })
6438        }
6439    }
6440}
6441
6442impl Default for RibbonEmitter {
6443    fn default() -> Self {
6444        Self::new()
6445    }
6446}
6447
6448/// Animation event trigger
6449///
6450/// Event objects fire events at specific animation frames, used to trigger sounds, spawn effects, etc. synchronized with animations.
6451pub struct EventObject {
6452    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxEventObject>,
6453}
6454
6455impl Drop for EventObject {
6456    fn drop(&mut self) {
6457        // SAFETY: `raw` came from a native constructor and Drop runs once.
6458        unsafe { ffi::whiteout_mdx_MdxEventObject_delete(self.raw.as_ptr()) }
6459    }
6460}
6461
6462impl EventObject {
6463    /// # Safety
6464    /// `raw` must be a live handle this value takes ownership of.
6465    #[allow(dead_code)] // used by whichever methods return this type
6466    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxEventObject) -> Option<Self> {
6467        core::ptr::NonNull::new(raw).map(|raw| EventObject { raw })
6468    }
6469}
6470
6471// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6472// is deliberately NOT implemented — the C++ types make no documented
6473// guarantee about concurrent use, and claiming one we haven't verified
6474// would be unsound. See `@bind thread_safe` in the plan.
6475unsafe impl Send for EventObject {}
6476
6477impl core::fmt::Debug for EventObject {
6478    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6479        f.debug_struct("EventObject").finish_non_exhaustive()
6480    }
6481}
6482
6483impl EventObject {
6484    /// # Panics
6485    /// Panics if the native allocation fails.
6486    pub fn new() -> Self {
6487        // SAFETY: the native constructor returns a live handle; a null here
6488        // means the library is unusable.
6489        unsafe {
6490            let raw = ffi::whiteout_mdx_MdxEventObject_new();
6491            Self::from_raw(raw).expect("native EventObject allocation failed")
6492        }
6493    }
6494
6495    /// Base node data
6496    /// Borrows the field in place — no copy, no allocation.
6497    pub fn node(&self) -> crate::support::Ref<'_, Node> {
6498        // SAFETY: an interior pointer into `self`, valid for this
6499        // borrow and never freed by the `Ref`.
6500        unsafe {
6501            crate::support::Ref::new(Node {
6502                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxEventObject_get_node(
6503                    self.raw.as_ptr(),
6504                )),
6505            })
6506        }
6507    }
6508
6509    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
6510        // SAFETY: as above; `&mut self` guarantees exclusivity.
6511        unsafe {
6512            crate::support::RefMut::new(Node {
6513                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxEventObject_get_node(
6514                    self.raw.as_ptr(),
6515                )),
6516            })
6517        }
6518    }
6519
6520    /// Global sequence if looping
6521    pub fn global_sequence_id(&self) -> u32 {
6522        // SAFETY: plain scalar read through a live handle.
6523        unsafe { ffi::whiteout_mdx_MdxEventObject_get_globalSequenceId(self.raw.as_ptr()) }
6524    }
6525
6526    pub fn set_global_sequence_id(&mut self, value: u32) {
6527        // SAFETY: plain scalar write through a live handle.
6528        unsafe { ffi::whiteout_mdx_MdxEventObject_set_globalSequenceId(self.raw.as_ptr(), value) }
6529    }
6530
6531    /// Frame numbers when events fire
6532    /// Zero-copy view of the underlying `std::vector`.
6533    pub fn event_track_times(&self) -> &[u32] {
6534        // SAFETY: `_data`/`_count` describe one contiguous C++
6535        // allocation, borrowed for as long as `self` is.
6536        unsafe {
6537            let n = ffi::whiteout_mdx_MdxEventObject_get_eventTrackTimes_count(self.raw.as_ptr());
6538            let p = ffi::whiteout_mdx_MdxEventObject_get_eventTrackTimes_data(self.raw.as_ptr());
6539            if p.is_null() || n == 0 {
6540                &[]
6541            } else {
6542                core::slice::from_raw_parts(p, n)
6543            }
6544        }
6545    }
6546
6547    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
6548    pub fn event_track_times_mut(&mut self) -> &mut [u32] {
6549        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
6550        unsafe {
6551            let n = ffi::whiteout_mdx_MdxEventObject_get_eventTrackTimes_count(self.raw.as_ptr());
6552            let p = ffi::whiteout_mdx_MdxEventObject_get_eventTrackTimes_data(self.raw.as_ptr())
6553                as *mut u32;
6554            if p.is_null() || n == 0 {
6555                &mut []
6556            } else {
6557                core::slice::from_raw_parts_mut(p, n)
6558            }
6559        }
6560    }
6561
6562    pub fn set_event_track_times(&mut self, values: &[u32]) {
6563        // SAFETY: the native side copies `values` before returning.
6564        unsafe {
6565            ffi::whiteout_mdx_MdxEventObject_assign_eventTrackTimes(
6566                self.raw.as_ptr(),
6567                values.as_ptr() as *const _,
6568                values.len(),
6569            )
6570        }
6571    }
6572
6573    pub fn resize_event_track_times(&mut self, count: usize) {
6574        // SAFETY: reallocation is safe here precisely because
6575        // `&mut self` means no slice borrow is outstanding.
6576        unsafe { ffi::whiteout_mdx_MdxEventObject_resize_eventTrackTimes(self.raw.as_ptr(), count) }
6577    }
6578}
6579
6580impl Default for EventObject {
6581    fn default() -> Self {
6582        Self::new()
6583    }
6584}
6585
6586/// Camera definition
6587///
6588/// Cameras define viewpoints that can be used for portrait renders or in-game cutscenes.
6589pub struct Camera {
6590    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxCamera>,
6591}
6592
6593impl Drop for Camera {
6594    fn drop(&mut self) {
6595        // SAFETY: `raw` came from a native constructor and Drop runs once.
6596        unsafe { ffi::whiteout_mdx_MdxCamera_delete(self.raw.as_ptr()) }
6597    }
6598}
6599
6600impl Camera {
6601    /// # Safety
6602    /// `raw` must be a live handle this value takes ownership of.
6603    #[allow(dead_code)] // used by whichever methods return this type
6604    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxCamera) -> Option<Self> {
6605        core::ptr::NonNull::new(raw).map(|raw| Camera { raw })
6606    }
6607}
6608
6609// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6610// is deliberately NOT implemented — the C++ types make no documented
6611// guarantee about concurrent use, and claiming one we haven't verified
6612// would be unsound. See `@bind thread_safe` in the plan.
6613unsafe impl Send for Camera {}
6614
6615impl core::fmt::Debug for Camera {
6616    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6617        f.debug_struct("Camera").finish_non_exhaustive()
6618    }
6619}
6620
6621impl Camera {
6622    /// # Panics
6623    /// Panics if the native allocation fails.
6624    pub fn new() -> Self {
6625        // SAFETY: the native constructor returns a live handle; a null here
6626        // means the library is unusable.
6627        unsafe {
6628            let raw = ffi::whiteout_mdx_MdxCamera_new();
6629            Self::from_raw(raw).expect("native Camera allocation failed")
6630        }
6631    }
6632
6633    /// Camera name
6634    pub fn name(&self) -> String {
6635        // SAFETY: the native side hands over an owned CString.
6636        unsafe {
6637            crate::support::take_string(ffi::whiteout_mdx_MdxCamera_get_name(self.raw.as_ptr()))
6638        }
6639    }
6640
6641    pub fn set_name(&mut self, value: &str) {
6642        let value = std::ffi::CString::new(value).unwrap_or_default();
6643        // SAFETY: the pointer outlives the call.
6644        unsafe { ffi::whiteout_mdx_MdxCamera_set_name(self.raw.as_ptr(), value.as_ptr()) }
6645    }
6646
6647    /// Camera position
6648    pub fn position(&self) -> crate::math::Vector3f {
6649        // SAFETY: the getter returns an interior pointer to a
6650        // layout-identical POD; we copy it out immediately.
6651        unsafe {
6652            *(ffi::whiteout_mdx_MdxCamera_get_position(self.raw.as_ptr())
6653                as *const crate::math::Vector3f)
6654        }
6655    }
6656
6657    pub fn set_position(&mut self, value: crate::math::Vector3f) {
6658        // SAFETY: as above, in the other direction.
6659        unsafe {
6660            ffi::whiteout_mdx_MdxCamera_set_position(
6661                self.raw.as_ptr(),
6662                &value as *const crate::math::Vector3f as *const _,
6663            )
6664        }
6665    }
6666
6667    /// Field of view angle in radians
6668    pub fn field_of_view(&self) -> f32 {
6669        // SAFETY: plain scalar read through a live handle.
6670        unsafe { ffi::whiteout_mdx_MdxCamera_get_fieldOfView(self.raw.as_ptr()) }
6671    }
6672
6673    pub fn set_field_of_view(&mut self, value: f32) {
6674        // SAFETY: plain scalar write through a live handle.
6675        unsafe { ffi::whiteout_mdx_MdxCamera_set_fieldOfView(self.raw.as_ptr(), value) }
6676    }
6677
6678    /// Far clipping distance
6679    pub fn far_clipping_plane(&self) -> f32 {
6680        // SAFETY: plain scalar read through a live handle.
6681        unsafe { ffi::whiteout_mdx_MdxCamera_get_farClippingPlane(self.raw.as_ptr()) }
6682    }
6683
6684    pub fn set_far_clipping_plane(&mut self, value: f32) {
6685        // SAFETY: plain scalar write through a live handle.
6686        unsafe { ffi::whiteout_mdx_MdxCamera_set_farClippingPlane(self.raw.as_ptr(), value) }
6687    }
6688
6689    /// Near clipping distance
6690    pub fn near_clipping_plane(&self) -> f32 {
6691        // SAFETY: plain scalar read through a live handle.
6692        unsafe { ffi::whiteout_mdx_MdxCamera_get_nearClippingPlane(self.raw.as_ptr()) }
6693    }
6694
6695    pub fn set_near_clipping_plane(&mut self, value: f32) {
6696        // SAFETY: plain scalar write through a live handle.
6697        unsafe { ffi::whiteout_mdx_MdxCamera_set_nearClippingPlane(self.raw.as_ptr(), value) }
6698    }
6699
6700    /// Look-at target position
6701    pub fn target_position(&self) -> crate::math::Vector3f {
6702        // SAFETY: the getter returns an interior pointer to a
6703        // layout-identical POD; we copy it out immediately.
6704        unsafe {
6705            *(ffi::whiteout_mdx_MdxCamera_get_targetPosition(self.raw.as_ptr())
6706                as *const crate::math::Vector3f)
6707        }
6708    }
6709
6710    pub fn set_target_position(&mut self, value: crate::math::Vector3f) {
6711        // SAFETY: as above, in the other direction.
6712        unsafe {
6713            ffi::whiteout_mdx_MdxCamera_set_targetPosition(
6714                self.raw.as_ptr(),
6715                &value as *const crate::math::Vector3f as *const _,
6716            )
6717        }
6718    }
6719
6720    /// Position animation
6721    /// Borrows the field in place — no copy, no allocation.
6722    pub fn position_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
6723        // SAFETY: an interior pointer into `self`, valid for this
6724        // borrow and never freed by the `Ref`.
6725        unsafe {
6726            crate::support::Ref::new(TrackVector3f {
6727                raw: core::ptr::NonNull::new_unchecked(
6728                    ffi::whiteout_mdx_MdxCamera_get_positionTracks(self.raw.as_ptr()),
6729                ),
6730            })
6731        }
6732    }
6733
6734    pub fn position_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
6735        // SAFETY: as above; `&mut self` guarantees exclusivity.
6736        unsafe {
6737            crate::support::RefMut::new(TrackVector3f {
6738                raw: core::ptr::NonNull::new_unchecked(
6739                    ffi::whiteout_mdx_MdxCamera_get_positionTracks(self.raw.as_ptr()),
6740                ),
6741            })
6742        }
6743    }
6744
6745    /// Target rotation animation
6746    /// Borrows the field in place — no copy, no allocation.
6747    pub fn target_rotation_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6748        // SAFETY: an interior pointer into `self`, valid for this
6749        // borrow and never freed by the `Ref`.
6750        unsafe {
6751            crate::support::Ref::new(TrackF32 {
6752                raw: core::ptr::NonNull::new_unchecked(
6753                    ffi::whiteout_mdx_MdxCamera_get_targetRotationTracks(self.raw.as_ptr()),
6754                ),
6755            })
6756        }
6757    }
6758
6759    pub fn target_rotation_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6760        // SAFETY: as above; `&mut self` guarantees exclusivity.
6761        unsafe {
6762            crate::support::RefMut::new(TrackF32 {
6763                raw: core::ptr::NonNull::new_unchecked(
6764                    ffi::whiteout_mdx_MdxCamera_get_targetRotationTracks(self.raw.as_ptr()),
6765                ),
6766            })
6767        }
6768    }
6769
6770    /// Target position animation
6771    /// Borrows the field in place — no copy, no allocation.
6772    pub fn target_position_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
6773        // SAFETY: an interior pointer into `self`, valid for this
6774        // borrow and never freed by the `Ref`.
6775        unsafe {
6776            crate::support::Ref::new(TrackVector3f {
6777                raw: core::ptr::NonNull::new_unchecked(
6778                    ffi::whiteout_mdx_MdxCamera_get_targetPositionTracks(self.raw.as_ptr()),
6779                ),
6780            })
6781        }
6782    }
6783
6784    pub fn target_position_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
6785        // SAFETY: as above; `&mut self` guarantees exclusivity.
6786        unsafe {
6787            crate::support::RefMut::new(TrackVector3f {
6788                raw: core::ptr::NonNull::new_unchecked(
6789                    ffi::whiteout_mdx_MdxCamera_get_targetPositionTracks(self.raw.as_ptr()),
6790                ),
6791            })
6792        }
6793    }
6794}
6795
6796impl Default for Camera {
6797    fn default() -> Self {
6798        Self::new()
6799    }
6800}
6801
6802/// Collision volume for physics
6803///
6804/// Collision shapes define simplified geometry for collision detection.
6805pub struct CollisionShape {
6806    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxCollisionShape>,
6807}
6808
6809impl Drop for CollisionShape {
6810    fn drop(&mut self) {
6811        // SAFETY: `raw` came from a native constructor and Drop runs once.
6812        unsafe { ffi::whiteout_mdx_MdxCollisionShape_delete(self.raw.as_ptr()) }
6813    }
6814}
6815
6816impl CollisionShape {
6817    /// # Safety
6818    /// `raw` must be a live handle this value takes ownership of.
6819    #[allow(dead_code)] // used by whichever methods return this type
6820    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxCollisionShape) -> Option<Self> {
6821        core::ptr::NonNull::new(raw).map(|raw| CollisionShape { raw })
6822    }
6823}
6824
6825// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6826// is deliberately NOT implemented — the C++ types make no documented
6827// guarantee about concurrent use, and claiming one we haven't verified
6828// would be unsound. See `@bind thread_safe` in the plan.
6829unsafe impl Send for CollisionShape {}
6830
6831impl core::fmt::Debug for CollisionShape {
6832    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6833        f.debug_struct("CollisionShape").finish_non_exhaustive()
6834    }
6835}
6836
6837impl CollisionShape {
6838    /// # Panics
6839    /// Panics if the native allocation fails.
6840    pub fn new() -> Self {
6841        // SAFETY: the native constructor returns a live handle; a null here
6842        // means the library is unusable.
6843        unsafe {
6844            let raw = ffi::whiteout_mdx_MdxCollisionShape_new();
6845            Self::from_raw(raw).expect("native CollisionShape allocation failed")
6846        }
6847    }
6848
6849    /// Base node data
6850    /// Borrows the field in place — no copy, no allocation.
6851    pub fn node(&self) -> crate::support::Ref<'_, Node> {
6852        // SAFETY: an interior pointer into `self`, valid for this
6853        // borrow and never freed by the `Ref`.
6854        unsafe {
6855            crate::support::Ref::new(Node {
6856                raw: core::ptr::NonNull::new_unchecked(
6857                    ffi::whiteout_mdx_MdxCollisionShape_get_node(self.raw.as_ptr()),
6858                ),
6859            })
6860        }
6861    }
6862
6863    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
6864        // SAFETY: as above; `&mut self` guarantees exclusivity.
6865        unsafe {
6866            crate::support::RefMut::new(Node {
6867                raw: core::ptr::NonNull::new_unchecked(
6868                    ffi::whiteout_mdx_MdxCollisionShape_get_node(self.raw.as_ptr()),
6869                ),
6870            })
6871        }
6872    }
6873
6874    /// Shape type
6875    pub fn type_(&self) -> CollisionShapeShapeType {
6876        // SAFETY: scalar read; the discriminant is validated below.
6877        unsafe { ffi::whiteout_mdx_MdxCollisionShape_get_type(self.raw.as_ptr()) }
6878            .try_into()
6879            .expect("unknown enum discriminant from the native library")
6880    }
6881
6882    pub fn set_type_(&mut self, value: CollisionShapeShapeType) {
6883        // SAFETY: scalar write through a live handle.
6884        unsafe { ffi::whiteout_mdx_MdxCollisionShape_set_type(self.raw.as_ptr(), value as i32) }
6885    }
6886
6887    /// Shape vertices (box only)
6888    /// Zero-copy view of the underlying `std::vector`.
6889    pub fn vertices(&self) -> &[crate::math::Vector3f] {
6890        // SAFETY: `_data`/`_count` describe one contiguous C++
6891        // allocation, borrowed for as long as `self` is.
6892        unsafe {
6893            let n = ffi::whiteout_mdx_MdxCollisionShape_get_vertices_count(self.raw.as_ptr());
6894            let p = ffi::whiteout_mdx_MdxCollisionShape_get_vertices_data(self.raw.as_ptr())
6895                as *const crate::math::Vector3f;
6896            if p.is_null() || n == 0 {
6897                &[]
6898            } else {
6899                core::slice::from_raw_parts(p, n)
6900            }
6901        }
6902    }
6903
6904    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
6905    pub fn vertices_mut(&mut self) -> &mut [crate::math::Vector3f] {
6906        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
6907        unsafe {
6908            let n = ffi::whiteout_mdx_MdxCollisionShape_get_vertices_count(self.raw.as_ptr());
6909            let p = ffi::whiteout_mdx_MdxCollisionShape_get_vertices_data(self.raw.as_ptr())
6910                as *const crate::math::Vector3f as *mut crate::math::Vector3f;
6911            if p.is_null() || n == 0 {
6912                &mut []
6913            } else {
6914                core::slice::from_raw_parts_mut(p, n)
6915            }
6916        }
6917    }
6918
6919    pub fn set_vertices(&mut self, values: &[crate::math::Vector3f]) {
6920        // SAFETY: the native side copies `values` before returning.
6921        unsafe {
6922            ffi::whiteout_mdx_MdxCollisionShape_assign_vertices(
6923                self.raw.as_ptr(),
6924                values.as_ptr() as *const _,
6925                values.len(),
6926            )
6927        }
6928    }
6929
6930    pub fn resize_vertices(&mut self, count: usize) {
6931        // SAFETY: reallocation is safe here precisely because
6932        // `&mut self` means no slice borrow is outstanding.
6933        unsafe { ffi::whiteout_mdx_MdxCollisionShape_resize_vertices(self.raw.as_ptr(), count) }
6934    }
6935
6936    /// Radius (sphere/cylinder only)
6937    pub fn radius(&self) -> f32 {
6938        // SAFETY: plain scalar read through a live handle.
6939        unsafe { ffi::whiteout_mdx_MdxCollisionShape_get_radius(self.raw.as_ptr()) }
6940    }
6941
6942    pub fn set_radius(&mut self, value: f32) {
6943        // SAFETY: plain scalar write through a live handle.
6944        unsafe { ffi::whiteout_mdx_MdxCollisionShape_set_radius(self.raw.as_ptr(), value) }
6945    }
6946}
6947
6948impl Default for CollisionShape {
6949    fn default() -> Self {
6950        Self::new()
6951    }
6952}
6953
6954/// Facial animation effect (Reforged)
6955///
6956/// Holds a name and a path to the FaceFX setup file used for a character's face.
6957pub struct FaceEffect {
6958    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxFaceEffect>,
6959}
6960
6961impl Drop for FaceEffect {
6962    fn drop(&mut self) {
6963        // SAFETY: `raw` came from a native constructor and Drop runs once.
6964        unsafe { ffi::whiteout_mdx_MdxFaceEffect_delete(self.raw.as_ptr()) }
6965    }
6966}
6967
6968impl FaceEffect {
6969    /// # Safety
6970    /// `raw` must be a live handle this value takes ownership of.
6971    #[allow(dead_code)] // used by whichever methods return this type
6972    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxFaceEffect) -> Option<Self> {
6973        core::ptr::NonNull::new(raw).map(|raw| FaceEffect { raw })
6974    }
6975}
6976
6977// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6978// is deliberately NOT implemented — the C++ types make no documented
6979// guarantee about concurrent use, and claiming one we haven't verified
6980// would be unsound. See `@bind thread_safe` in the plan.
6981unsafe impl Send for FaceEffect {}
6982
6983impl core::fmt::Debug for FaceEffect {
6984    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6985        f.debug_struct("FaceEffect").finish_non_exhaustive()
6986    }
6987}
6988
6989impl FaceEffect {
6990    /// # Panics
6991    /// Panics if the native allocation fails.
6992    pub fn new() -> Self {
6993        // SAFETY: the native constructor returns a live handle; a null here
6994        // means the library is unusable.
6995        unsafe {
6996            let raw = ffi::whiteout_mdx_MdxFaceEffect_new();
6997            Self::from_raw(raw).expect("native FaceEffect allocation failed")
6998        }
6999    }
7000
7001    /// Section name
7002    pub fn name(&self) -> String {
7003        // SAFETY: the native side hands over an owned CString.
7004        unsafe {
7005            crate::support::take_string(ffi::whiteout_mdx_MdxFaceEffect_get_name(self.raw.as_ptr()))
7006        }
7007    }
7008
7009    pub fn set_name(&mut self, value: &str) {
7010        let value = std::ffi::CString::new(value).unwrap_or_default();
7011        // SAFETY: the pointer outlives the call.
7012        unsafe { ffi::whiteout_mdx_MdxFaceEffect_set_name(self.raw.as_ptr(), value.as_ptr()) }
7013    }
7014
7015    /// Path to facial animation data
7016    pub fn path(&self) -> String {
7017        // SAFETY: the native side hands over an owned CString.
7018        unsafe {
7019            crate::support::take_string(ffi::whiteout_mdx_MdxFaceEffect_get_path(self.raw.as_ptr()))
7020        }
7021    }
7022
7023    pub fn set_path(&mut self, value: &str) {
7024        let value = std::ffi::CString::new(value).unwrap_or_default();
7025        // SAFETY: the pointer outlives the call.
7026        unsafe { ffi::whiteout_mdx_MdxFaceEffect_set_path(self.raw.as_ptr(), value.as_ptr()) }
7027    }
7028}
7029
7030impl Default for FaceEffect {
7031    fn default() -> Self {
7032        Self::new()
7033    }
7034}
7035
7036/// PopcornFX particle emitter (Reforged)
7037///
7038/// Advanced particle system using PopcornFX technology in Warcraft III: Reforged. Color (RGB) and alpha are stored as separate fields with independent animation tracks.
7039pub struct CornEmitter {
7040    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxCornEmitter>,
7041}
7042
7043impl Drop for CornEmitter {
7044    fn drop(&mut self) {
7045        // SAFETY: `raw` came from a native constructor and Drop runs once.
7046        unsafe { ffi::whiteout_mdx_MdxCornEmitter_delete(self.raw.as_ptr()) }
7047    }
7048}
7049
7050impl CornEmitter {
7051    /// # Safety
7052    /// `raw` must be a live handle this value takes ownership of.
7053    #[allow(dead_code)] // used by whichever methods return this type
7054    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxCornEmitter) -> Option<Self> {
7055        core::ptr::NonNull::new(raw).map(|raw| CornEmitter { raw })
7056    }
7057}
7058
7059// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7060// is deliberately NOT implemented — the C++ types make no documented
7061// guarantee about concurrent use, and claiming one we haven't verified
7062// would be unsound. See `@bind thread_safe` in the plan.
7063unsafe impl Send for CornEmitter {}
7064
7065impl core::fmt::Debug for CornEmitter {
7066    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7067        f.debug_struct("CornEmitter").finish_non_exhaustive()
7068    }
7069}
7070
7071impl CornEmitter {
7072    /// # Panics
7073    /// Panics if the native allocation fails.
7074    pub fn new() -> Self {
7075        // SAFETY: the native constructor returns a live handle; a null here
7076        // means the library is unusable.
7077        unsafe {
7078            let raw = ffi::whiteout_mdx_MdxCornEmitter_new();
7079            Self::from_raw(raw).expect("native CornEmitter allocation failed")
7080        }
7081    }
7082
7083    /// Base node data
7084    /// Borrows the field in place — no copy, no allocation.
7085    pub fn node(&self) -> crate::support::Ref<'_, Node> {
7086        // SAFETY: an interior pointer into `self`, valid for this
7087        // borrow and never freed by the `Ref`.
7088        unsafe {
7089            crate::support::Ref::new(Node {
7090                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxCornEmitter_get_node(
7091                    self.raw.as_ptr(),
7092                )),
7093            })
7094        }
7095    }
7096
7097    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
7098        // SAFETY: as above; `&mut self` guarantees exclusivity.
7099        unsafe {
7100            crate::support::RefMut::new(Node {
7101                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxCornEmitter_get_node(
7102                    self.raw.as_ptr(),
7103                )),
7104            })
7105        }
7106    }
7107
7108    /// Particle lifetime (default)
7109    pub fn life_span(&self) -> f32 {
7110        // SAFETY: plain scalar read through a live handle.
7111        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_lifeSpan(self.raw.as_ptr()) }
7112    }
7113
7114    pub fn set_life_span(&mut self, value: f32) {
7115        // SAFETY: plain scalar write through a live handle.
7116        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_lifeSpan(self.raw.as_ptr(), value) }
7117    }
7118
7119    /// Emission rate (default)
7120    pub fn emission_rate(&self) -> f32 {
7121        // SAFETY: plain scalar read through a live handle.
7122        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_emissionRate(self.raw.as_ptr()) }
7123    }
7124
7125    pub fn set_emission_rate(&mut self, value: f32) {
7126        // SAFETY: plain scalar write through a live handle.
7127        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_emissionRate(self.raw.as_ptr(), value) }
7128    }
7129
7130    /// Particle speed (default)
7131    pub fn speed(&self) -> f32 {
7132        // SAFETY: plain scalar read through a live handle.
7133        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_speed(self.raw.as_ptr()) }
7134    }
7135
7136    pub fn set_speed(&mut self, value: f32) {
7137        // SAFETY: plain scalar write through a live handle.
7138        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_speed(self.raw.as_ptr(), value) }
7139    }
7140
7141    /// Particle color (RGB)
7142    pub fn color(&self) -> crate::math::Vector3f {
7143        // SAFETY: the getter returns an interior pointer to a
7144        // layout-identical POD; we copy it out immediately.
7145        unsafe {
7146            *(ffi::whiteout_mdx_MdxCornEmitter_get_color(self.raw.as_ptr())
7147                as *const crate::math::Vector3f)
7148        }
7149    }
7150
7151    pub fn set_color(&mut self, value: crate::math::Vector3f) {
7152        // SAFETY: as above, in the other direction.
7153        unsafe {
7154            ffi::whiteout_mdx_MdxCornEmitter_set_color(
7155                self.raw.as_ptr(),
7156                &value as *const crate::math::Vector3f as *const _,
7157            )
7158        }
7159    }
7160
7161    /// Particle alpha (default)
7162    pub fn alpha(&self) -> f32 {
7163        // SAFETY: plain scalar read through a live handle.
7164        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_alpha(self.raw.as_ptr()) }
7165    }
7166
7167    pub fn set_alpha(&mut self, value: f32) {
7168        // SAFETY: plain scalar write through a live handle.
7169        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_alpha(self.raw.as_ptr(), value) }
7170    }
7171
7172    /// Replaceable texture ID
7173    pub fn replaceable_id(&self) -> u32 {
7174        // SAFETY: plain scalar read through a live handle.
7175        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_replaceableId(self.raw.as_ptr()) }
7176    }
7177
7178    pub fn set_replaceable_id(&mut self, value: u32) {
7179        // SAFETY: plain scalar write through a live handle.
7180        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_replaceableId(self.raw.as_ptr(), value) }
7181    }
7182
7183    /// Path to PopcornFX effect
7184    pub fn path(&self) -> String {
7185        // SAFETY: the native side hands over an owned CString.
7186        unsafe {
7187            crate::support::take_string(ffi::whiteout_mdx_MdxCornEmitter_get_path(
7188                self.raw.as_ptr(),
7189            ))
7190        }
7191    }
7192
7193    pub fn set_path(&mut self, value: &str) {
7194        let value = std::ffi::CString::new(value).unwrap_or_default();
7195        // SAFETY: the pointer outlives the call.
7196        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_path(self.raw.as_ptr(), value.as_ptr()) }
7197    }
7198
7199    /// Animation visibility guide
7200    pub fn anim_visibility_guide(&self) -> String {
7201        // SAFETY: the native side hands over an owned CString.
7202        unsafe {
7203            crate::support::take_string(ffi::whiteout_mdx_MdxCornEmitter_get_animVisibilityGuide(
7204                self.raw.as_ptr(),
7205            ))
7206        }
7207    }
7208
7209    pub fn set_anim_visibility_guide(&mut self, value: &str) {
7210        let value = std::ffi::CString::new(value).unwrap_or_default();
7211        // SAFETY: the pointer outlives the call.
7212        unsafe {
7213            ffi::whiteout_mdx_MdxCornEmitter_set_animVisibilityGuide(
7214                self.raw.as_ptr(),
7215                value.as_ptr(),
7216            )
7217        }
7218    }
7219
7220    /// Lifespan animation
7221    /// Borrows the field in place — no copy, no allocation.
7222    pub fn life_span_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7223        // SAFETY: an interior pointer into `self`, valid for this
7224        // borrow and never freed by the `Ref`.
7225        unsafe {
7226            crate::support::Ref::new(TrackF32 {
7227                raw: core::ptr::NonNull::new_unchecked(
7228                    ffi::whiteout_mdx_MdxCornEmitter_get_lifeSpanTracks(self.raw.as_ptr()),
7229                ),
7230            })
7231        }
7232    }
7233
7234    pub fn life_span_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7235        // SAFETY: as above; `&mut self` guarantees exclusivity.
7236        unsafe {
7237            crate::support::RefMut::new(TrackF32 {
7238                raw: core::ptr::NonNull::new_unchecked(
7239                    ffi::whiteout_mdx_MdxCornEmitter_get_lifeSpanTracks(self.raw.as_ptr()),
7240                ),
7241            })
7242        }
7243    }
7244
7245    /// Emission rate animation
7246    /// Borrows the field in place — no copy, no allocation.
7247    pub fn emission_rate_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7248        // SAFETY: an interior pointer into `self`, valid for this
7249        // borrow and never freed by the `Ref`.
7250        unsafe {
7251            crate::support::Ref::new(TrackF32 {
7252                raw: core::ptr::NonNull::new_unchecked(
7253                    ffi::whiteout_mdx_MdxCornEmitter_get_emissionRateTracks(self.raw.as_ptr()),
7254                ),
7255            })
7256        }
7257    }
7258
7259    pub fn emission_rate_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7260        // SAFETY: as above; `&mut self` guarantees exclusivity.
7261        unsafe {
7262            crate::support::RefMut::new(TrackF32 {
7263                raw: core::ptr::NonNull::new_unchecked(
7264                    ffi::whiteout_mdx_MdxCornEmitter_get_emissionRateTracks(self.raw.as_ptr()),
7265                ),
7266            })
7267        }
7268    }
7269
7270    /// Speed animation
7271    /// Borrows the field in place — no copy, no allocation.
7272    pub fn speed_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7273        // SAFETY: an interior pointer into `self`, valid for this
7274        // borrow and never freed by the `Ref`.
7275        unsafe {
7276            crate::support::Ref::new(TrackF32 {
7277                raw: core::ptr::NonNull::new_unchecked(
7278                    ffi::whiteout_mdx_MdxCornEmitter_get_speedTracks(self.raw.as_ptr()),
7279                ),
7280            })
7281        }
7282    }
7283
7284    pub fn speed_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7285        // SAFETY: as above; `&mut self` guarantees exclusivity.
7286        unsafe {
7287            crate::support::RefMut::new(TrackF32 {
7288                raw: core::ptr::NonNull::new_unchecked(
7289                    ffi::whiteout_mdx_MdxCornEmitter_get_speedTracks(self.raw.as_ptr()),
7290                ),
7291            })
7292        }
7293    }
7294
7295    /// Color animation
7296    /// Borrows the field in place — no copy, no allocation.
7297    pub fn color_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
7298        // SAFETY: an interior pointer into `self`, valid for this
7299        // borrow and never freed by the `Ref`.
7300        unsafe {
7301            crate::support::Ref::new(TrackVector3f {
7302                raw: core::ptr::NonNull::new_unchecked(
7303                    ffi::whiteout_mdx_MdxCornEmitter_get_colorTracks(self.raw.as_ptr()),
7304                ),
7305            })
7306        }
7307    }
7308
7309    pub fn color_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
7310        // SAFETY: as above; `&mut self` guarantees exclusivity.
7311        unsafe {
7312            crate::support::RefMut::new(TrackVector3f {
7313                raw: core::ptr::NonNull::new_unchecked(
7314                    ffi::whiteout_mdx_MdxCornEmitter_get_colorTracks(self.raw.as_ptr()),
7315                ),
7316            })
7317        }
7318    }
7319
7320    /// Alpha animation
7321    /// Borrows the field in place — no copy, no allocation.
7322    pub fn alpha_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7323        // SAFETY: an interior pointer into `self`, valid for this
7324        // borrow and never freed by the `Ref`.
7325        unsafe {
7326            crate::support::Ref::new(TrackF32 {
7327                raw: core::ptr::NonNull::new_unchecked(
7328                    ffi::whiteout_mdx_MdxCornEmitter_get_alphaTracks(self.raw.as_ptr()),
7329                ),
7330            })
7331        }
7332    }
7333
7334    pub fn alpha_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7335        // SAFETY: as above; `&mut self` guarantees exclusivity.
7336        unsafe {
7337            crate::support::RefMut::new(TrackF32 {
7338                raw: core::ptr::NonNull::new_unchecked(
7339                    ffi::whiteout_mdx_MdxCornEmitter_get_alphaTracks(self.raw.as_ptr()),
7340                ),
7341            })
7342        }
7343    }
7344
7345    /// Visibility animation
7346    /// Borrows the field in place — no copy, no allocation.
7347    pub fn visibility_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7348        // SAFETY: an interior pointer into `self`, valid for this
7349        // borrow and never freed by the `Ref`.
7350        unsafe {
7351            crate::support::Ref::new(TrackF32 {
7352                raw: core::ptr::NonNull::new_unchecked(
7353                    ffi::whiteout_mdx_MdxCornEmitter_get_visibilityTracks(self.raw.as_ptr()),
7354                ),
7355            })
7356        }
7357    }
7358
7359    pub fn visibility_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7360        // SAFETY: as above; `&mut self` guarantees exclusivity.
7361        unsafe {
7362            crate::support::RefMut::new(TrackF32 {
7363                raw: core::ptr::NonNull::new_unchecked(
7364                    ffi::whiteout_mdx_MdxCornEmitter_get_visibilityTracks(self.raw.as_ptr()),
7365                ),
7366            })
7367        }
7368    }
7369}
7370
7371impl Default for CornEmitter {
7372    fn default() -> Self {
7373        Self::new()
7374    }
7375}
7376
7377/// Parser for MDX model files
7378///
7379/// The Parser reads binary MDX files and converts them into the Model structure. It supports multiple parsing modes and can handle version differences.
7380///
7381/// Uses the PImpl (Pointer to Implementation) idiom to hide implementation details.
7382pub struct Parser {
7383    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxParser>,
7384}
7385
7386impl Drop for Parser {
7387    fn drop(&mut self) {
7388        // SAFETY: `raw` came from a native constructor and Drop runs once.
7389        unsafe { ffi::whiteout_mdx_MdxParser_delete(self.raw.as_ptr()) }
7390    }
7391}
7392
7393impl Parser {
7394    /// # Safety
7395    /// `raw` must be a live handle this value takes ownership of.
7396    #[allow(dead_code)] // used by whichever methods return this type
7397    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxParser) -> Option<Self> {
7398        core::ptr::NonNull::new(raw).map(|raw| Parser { raw })
7399    }
7400}
7401
7402// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7403// is deliberately NOT implemented — the C++ types make no documented
7404// guarantee about concurrent use, and claiming one we haven't verified
7405// would be unsound. See `@bind thread_safe` in the plan.
7406unsafe impl Send for Parser {}
7407
7408impl core::fmt::Debug for Parser {
7409    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7410        f.debug_struct("Parser").finish_non_exhaustive()
7411    }
7412}
7413
7414impl Parser {
7415    /// # Panics
7416    /// Panics if the native allocation fails.
7417    pub fn new() -> Self {
7418        // SAFETY: the native constructor returns a live handle; a null here
7419        // means the library is unusable.
7420        unsafe {
7421            let raw = ffi::whiteout_mdx_MdxParser_new();
7422            Self::from_raw(raw).expect("native Parser allocation failed")
7423        }
7424    }
7425
7426    /// Parse an MDX file from disk
7427    ///
7428    /// The format is detected from the file extension: `.mdl` for text MDL format, `.mdx` (or any other extension) for binary MDX format.
7429    ///
7430    /// @param filePath Path to the MDX/MDL file @return Parsed MDX file data @throws std::runtime_error If file cannot be opened or parsing fails in strict mode
7431    pub fn parse_file(&mut self, file_path: &str) -> Option<Model> {
7432        let file_path_cstr = std::ffi::CString::new(file_path).unwrap_or_default();
7433        // SAFETY: handle is live for the duration of the call.
7434        unsafe {
7435            Model::from_raw(ffi::whiteout_mdx_MdxParser_parse(
7436                self.raw.as_ptr(),
7437                file_path_cstr.as_ptr(),
7438            ))
7439        }
7440    }
7441
7442    /// Parse an MDX/MDL file from memory buffer @param buffer Memory buffer containing MDX/MDL data @param format Source format (MDX binary or MDL text) @return Parsed MDX file data @throws std::runtime_error If parsing fails in strict mode
7443    pub fn parse(&mut self, buffer: &[u8], format: MDLXFormat) -> Option<Model> {
7444        // SAFETY: handle is live for the duration of the call.
7445        unsafe {
7446            Model::from_raw(ffi::whiteout_mdx_MdxParser_parse_buffer_format(
7447                self.raw.as_ptr(),
7448                buffer.as_ptr(),
7449                buffer.len(),
7450                format as i32,
7451            ))
7452        }
7453    }
7454
7455    /// Check if parsing encountered any issues @return True if there were warnings or recoverable errors
7456    pub fn has_issues(&self) -> bool {
7457        // SAFETY: handle is live for the duration of the call.
7458        unsafe { ffi::whiteout_mdx_MdxParser_hasIssues(self.raw.as_ptr()) != 0 }
7459    }
7460
7461    /// Get list of issues encountered during parsing @return Vector of issue description strings
7462    pub fn issues(&self) -> Vec<String> {
7463        // SAFETY: index stays below the reported count.
7464        unsafe {
7465            let n = ffi::whiteout_mdx_MdxParser_getIssues_count(self.raw.as_ptr());
7466            (0..n)
7467                .map(|i| {
7468                    crate::support::take_string(ffi::whiteout_mdx_MdxParser_getIssues_at(
7469                        self.raw.as_ptr(),
7470                        i,
7471                    ))
7472                })
7473                .collect()
7474        }
7475    }
7476}
7477
7478impl Default for Parser {
7479    fn default() -> Self {
7480        Self::new()
7481    }
7482}
7483
7484/// Writer for MDX/MDL model files
7485///
7486/// The Writer takes a Model structure and writes it to disk in binary MDX format or text MDL format. It automatically handles chunk serialization and size calculation for MDX, and text formatting for MDL.
7487///
7488/// The format is selected either by file extension (.mdl → text, .mdx → binary) or by an explicit MDLXFormat parameter.
7489///
7490/// Uses the PImpl (Pointer to Implementation) idiom to hide implementation details.
7491pub struct Writer {
7492    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxWriter>,
7493}
7494
7495impl Drop for Writer {
7496    fn drop(&mut self) {
7497        // SAFETY: `raw` came from a native constructor and Drop runs once.
7498        unsafe { ffi::whiteout_mdx_MdxWriter_delete(self.raw.as_ptr()) }
7499    }
7500}
7501
7502impl Writer {
7503    /// # Safety
7504    /// `raw` must be a live handle this value takes ownership of.
7505    #[allow(dead_code)] // used by whichever methods return this type
7506    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxWriter) -> Option<Self> {
7507        core::ptr::NonNull::new(raw).map(|raw| Writer { raw })
7508    }
7509}
7510
7511// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7512// is deliberately NOT implemented — the C++ types make no documented
7513// guarantee about concurrent use, and claiming one we haven't verified
7514// would be unsound. See `@bind thread_safe` in the plan.
7515unsafe impl Send for Writer {}
7516
7517impl core::fmt::Debug for Writer {
7518    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7519        f.debug_struct("Writer").finish_non_exhaustive()
7520    }
7521}
7522
7523impl Writer {
7524    /// # Panics
7525    /// Panics if the native allocation fails.
7526    pub fn new() -> Self {
7527        // SAFETY: the native constructor returns a live handle; a null here
7528        // means the library is unusable.
7529        unsafe {
7530            let raw = ffi::whiteout_mdx_MdxWriter_new();
7531            Self::from_raw(raw).expect("native Writer allocation failed")
7532        }
7533    }
7534
7535    /// Write a model file to disk
7536    ///
7537    /// The format is detected from the file extension: `.mdl` for text MDL format, `.mdx` (or any other extension) for binary MDX format.
7538    ///
7539    /// @param filePath  Path where the file should be written @param mdlx      Model data to write @param mdlFormat MDL text dialect (only used when writing .mdl); defaults to WarcraftIII (engine-faithful). @throws std::runtime_error If file cannot be created or written
7540    pub fn write_file(&mut self, file_path: &str, mdlx: &Model, mdl_format: MdlFormat) {
7541        let file_path_cstr = std::ffi::CString::new(file_path).unwrap_or_default();
7542        // SAFETY: handle is live for the duration of the call.
7543        unsafe {
7544            ffi::whiteout_mdx_MdxWriter_write(
7545                self.raw.as_ptr(),
7546                file_path_cstr.as_ptr(),
7547                mdlx.raw.as_ptr(),
7548                mdl_format as i32,
7549            );
7550        }
7551    }
7552
7553    /// Write a model to a byte buffer @param mdx       Model data to write @param format    Output format (MDX binary or MDL text, defaults to MDX) @param mdlFormat MDL text dialect (only used when format == MDL); defaults to WarcraftIII (engine-faithful). @return Vector containing the binary MDX data or UTF-8 MDL text
7554    pub fn write(&mut self, mdx: &Model, format: MDLXFormat, mdl_format: MdlFormat) -> Bytes {
7555        // SAFETY: handle is live for the duration of the call.
7556        unsafe {
7557            Bytes::from_raw(ffi::whiteout_mdx_MdxWriter_write_mdx_format_mdlFormat(
7558                self.raw.as_ptr(),
7559                mdx.raw.as_ptr(),
7560                format as i32,
7561                mdl_format as i32,
7562            ))
7563            .unwrap_or_else(Bytes::empty)
7564        }
7565    }
7566}
7567
7568impl Default for Writer {
7569    fn default() -> Self {
7570        Self::new()
7571    }
7572}
7573
7574/// Generic animation track with keyframe data
7575///
7576/// Tracks store animation data as a series of keyframes. The interpolation type determines how values between keyframes are calculated.
7577///
7578/// @tparam T The value type (f32, Vector3f, Vector4f, u32, etc.)
7579pub struct TrackVector3f {
7580    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTrackVector3f>,
7581}
7582
7583impl Drop for TrackVector3f {
7584    fn drop(&mut self) {
7585        // SAFETY: `raw` came from a native constructor and Drop runs once.
7586        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_delete(self.raw.as_ptr()) }
7587    }
7588}
7589
7590impl TrackVector3f {
7591    /// # Safety
7592    /// `raw` must be a live handle this value takes ownership of.
7593    #[allow(dead_code)] // used by whichever methods return this type
7594    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTrackVector3f) -> Option<Self> {
7595        core::ptr::NonNull::new(raw).map(|raw| TrackVector3f { raw })
7596    }
7597}
7598
7599// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7600// is deliberately NOT implemented — the C++ types make no documented
7601// guarantee about concurrent use, and claiming one we haven't verified
7602// would be unsound. See `@bind thread_safe` in the plan.
7603unsafe impl Send for TrackVector3f {}
7604
7605impl core::fmt::Debug for TrackVector3f {
7606    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7607        f.debug_struct("TrackVector3f").finish_non_exhaustive()
7608    }
7609}
7610
7611impl TrackVector3f {
7612    /// # Panics
7613    /// Panics if the native allocation fails.
7614    pub fn new() -> Self {
7615        // SAFETY: the native constructor returns a live handle; a null here
7616        // means the library is unusable.
7617        unsafe {
7618            let raw = ffi::whiteout_mdx_MdxTrackVector3f_new();
7619            Self::from_raw(raw).expect("native TrackVector3f allocation failed")
7620        }
7621    }
7622
7623    /// Whether this track contains animation data
7624    pub fn is_used(&self) -> bool {
7625        // SAFETY: plain scalar read through a live handle.
7626        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_get_isUsed(self.raw.as_ptr()) != 0 }
7627    }
7628
7629    pub fn set_is_used(&mut self, value: bool) {
7630        // SAFETY: plain scalar write through a live handle.
7631        unsafe {
7632            ffi::whiteout_mdx_MdxTrackVector3f_set_isUsed(
7633                self.raw.as_ptr(),
7634                if value { 1 } else { 0 },
7635            )
7636        }
7637    }
7638
7639    /// Interpolation type
7640    pub fn interpolation_type(&self) -> InterpolationType {
7641        // SAFETY: scalar read; the discriminant is validated below.
7642        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_get_interpolationType(self.raw.as_ptr()) }
7643            .try_into()
7644            .expect("unknown enum discriminant from the native library")
7645    }
7646
7647    pub fn set_interpolation_type(&mut self, value: InterpolationType) {
7648        // SAFETY: scalar write through a live handle.
7649        unsafe {
7650            ffi::whiteout_mdx_MdxTrackVector3f_set_interpolationType(
7651                self.raw.as_ptr(),
7652                value as i32,
7653            )
7654        }
7655    }
7656
7657    /// Global sequence ID if applicable
7658    pub fn global_sequence_id(&self) -> u32 {
7659        // SAFETY: plain scalar read through a live handle.
7660        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_get_globalSequenceId(self.raw.as_ptr()) }
7661    }
7662
7663    pub fn set_global_sequence_id(&mut self, value: u32) {
7664        // SAFETY: plain scalar write through a live handle.
7665        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_set_globalSequenceId(self.raw.as_ptr(), value) }
7666    }
7667
7668    /// Number of keyframes
7669    pub fn key_count(&self) -> usize {
7670        // SAFETY: plain scalar read through a live handle.
7671        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_get_keyCount(self.raw.as_ptr()) }
7672    }
7673
7674    pub fn set_key_count(&mut self, value: usize) {
7675        // SAFETY: plain scalar write through a live handle.
7676        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_set_keyCount(self.raw.as_ptr(), value) }
7677    }
7678
7679    /// Keyframe timestamps (in frames)
7680    /// Zero-copy view of the underlying `std::vector`.
7681    pub fn timestamps(&self) -> &[u32] {
7682        // SAFETY: `_data`/`_count` describe one contiguous C++
7683        // allocation, borrowed for as long as `self` is.
7684        unsafe {
7685            let n = ffi::whiteout_mdx_MdxTrackVector3f_get_timestamps_count(self.raw.as_ptr());
7686            let p = ffi::whiteout_mdx_MdxTrackVector3f_get_timestamps_data(self.raw.as_ptr());
7687            if p.is_null() || n == 0 {
7688                &[]
7689            } else {
7690                core::slice::from_raw_parts(p, n)
7691            }
7692        }
7693    }
7694
7695    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
7696    pub fn timestamps_mut(&mut self) -> &mut [u32] {
7697        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
7698        unsafe {
7699            let n = ffi::whiteout_mdx_MdxTrackVector3f_get_timestamps_count(self.raw.as_ptr());
7700            let p = ffi::whiteout_mdx_MdxTrackVector3f_get_timestamps_data(self.raw.as_ptr())
7701                as *mut u32;
7702            if p.is_null() || n == 0 {
7703                &mut []
7704            } else {
7705                core::slice::from_raw_parts_mut(p, n)
7706            }
7707        }
7708    }
7709
7710    pub fn set_timestamps(&mut self, values: &[u32]) {
7711        // SAFETY: the native side copies `values` before returning.
7712        unsafe {
7713            ffi::whiteout_mdx_MdxTrackVector3f_assign_timestamps(
7714                self.raw.as_ptr(),
7715                values.as_ptr() as *const _,
7716                values.len(),
7717            )
7718        }
7719    }
7720
7721    pub fn resize_timestamps(&mut self, count: usize) {
7722        // SAFETY: reallocation is safe here precisely because
7723        // `&mut self` means no slice borrow is outstanding.
7724        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_resize_timestamps(self.raw.as_ptr(), count) }
7725    }
7726
7727    /// Raw keyframe data
7728    /// Zero-copy view of the underlying `std::vector`.
7729    pub fn keys(&self) -> &[crate::math::Vector3f] {
7730        // SAFETY: `_data`/`_count` describe one contiguous C++
7731        // allocation, borrowed for as long as `self` is.
7732        unsafe {
7733            let n = ffi::whiteout_mdx_MdxTrackVector3f_get_keys_count(self.raw.as_ptr());
7734            let p = ffi::whiteout_mdx_MdxTrackVector3f_get_keys_data(self.raw.as_ptr())
7735                as *const crate::math::Vector3f;
7736            if p.is_null() || n == 0 {
7737                &[]
7738            } else {
7739                core::slice::from_raw_parts(p, n)
7740            }
7741        }
7742    }
7743
7744    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
7745    pub fn keys_mut(&mut self) -> &mut [crate::math::Vector3f] {
7746        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
7747        unsafe {
7748            let n = ffi::whiteout_mdx_MdxTrackVector3f_get_keys_count(self.raw.as_ptr());
7749            let p = ffi::whiteout_mdx_MdxTrackVector3f_get_keys_data(self.raw.as_ptr())
7750                as *const crate::math::Vector3f as *mut crate::math::Vector3f;
7751            if p.is_null() || n == 0 {
7752                &mut []
7753            } else {
7754                core::slice::from_raw_parts_mut(p, n)
7755            }
7756        }
7757    }
7758
7759    pub fn set_keys(&mut self, values: &[crate::math::Vector3f]) {
7760        // SAFETY: the native side copies `values` before returning.
7761        unsafe {
7762            ffi::whiteout_mdx_MdxTrackVector3f_assign_keys(
7763                self.raw.as_ptr(),
7764                values.as_ptr() as *const _,
7765                values.len(),
7766            )
7767        }
7768    }
7769
7770    pub fn resize_keys(&mut self, count: usize) {
7771        // SAFETY: reallocation is safe here precisely because
7772        // `&mut self` means no slice borrow is outstanding.
7773        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_resize_keys(self.raw.as_ptr(), count) }
7774    }
7775}
7776
7777impl Default for TrackVector3f {
7778    fn default() -> Self {
7779        Self::new()
7780    }
7781}
7782
7783/// Generic animation track with keyframe data
7784///
7785/// Tracks store animation data as a series of keyframes. The interpolation type determines how values between keyframes are calculated.
7786///
7787/// @tparam T The value type (f32, Vector3f, Vector4f, u32, etc.)
7788pub struct TrackQuaternion {
7789    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTrackQuaternion>,
7790}
7791
7792impl Drop for TrackQuaternion {
7793    fn drop(&mut self) {
7794        // SAFETY: `raw` came from a native constructor and Drop runs once.
7795        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_delete(self.raw.as_ptr()) }
7796    }
7797}
7798
7799impl TrackQuaternion {
7800    /// # Safety
7801    /// `raw` must be a live handle this value takes ownership of.
7802    #[allow(dead_code)] // used by whichever methods return this type
7803    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTrackQuaternion) -> Option<Self> {
7804        core::ptr::NonNull::new(raw).map(|raw| TrackQuaternion { raw })
7805    }
7806}
7807
7808// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7809// is deliberately NOT implemented — the C++ types make no documented
7810// guarantee about concurrent use, and claiming one we haven't verified
7811// would be unsound. See `@bind thread_safe` in the plan.
7812unsafe impl Send for TrackQuaternion {}
7813
7814impl core::fmt::Debug for TrackQuaternion {
7815    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7816        f.debug_struct("TrackQuaternion").finish_non_exhaustive()
7817    }
7818}
7819
7820impl TrackQuaternion {
7821    /// # Panics
7822    /// Panics if the native allocation fails.
7823    pub fn new() -> Self {
7824        // SAFETY: the native constructor returns a live handle; a null here
7825        // means the library is unusable.
7826        unsafe {
7827            let raw = ffi::whiteout_mdx_MdxTrackQuaternion_new();
7828            Self::from_raw(raw).expect("native TrackQuaternion allocation failed")
7829        }
7830    }
7831
7832    /// Whether this track contains animation data
7833    pub fn is_used(&self) -> bool {
7834        // SAFETY: plain scalar read through a live handle.
7835        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_get_isUsed(self.raw.as_ptr()) != 0 }
7836    }
7837
7838    pub fn set_is_used(&mut self, value: bool) {
7839        // SAFETY: plain scalar write through a live handle.
7840        unsafe {
7841            ffi::whiteout_mdx_MdxTrackQuaternion_set_isUsed(
7842                self.raw.as_ptr(),
7843                if value { 1 } else { 0 },
7844            )
7845        }
7846    }
7847
7848    /// Interpolation type
7849    pub fn interpolation_type(&self) -> InterpolationType {
7850        // SAFETY: scalar read; the discriminant is validated below.
7851        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_get_interpolationType(self.raw.as_ptr()) }
7852            .try_into()
7853            .expect("unknown enum discriminant from the native library")
7854    }
7855
7856    pub fn set_interpolation_type(&mut self, value: InterpolationType) {
7857        // SAFETY: scalar write through a live handle.
7858        unsafe {
7859            ffi::whiteout_mdx_MdxTrackQuaternion_set_interpolationType(
7860                self.raw.as_ptr(),
7861                value as i32,
7862            )
7863        }
7864    }
7865
7866    /// Global sequence ID if applicable
7867    pub fn global_sequence_id(&self) -> u32 {
7868        // SAFETY: plain scalar read through a live handle.
7869        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_get_globalSequenceId(self.raw.as_ptr()) }
7870    }
7871
7872    pub fn set_global_sequence_id(&mut self, value: u32) {
7873        // SAFETY: plain scalar write through a live handle.
7874        unsafe {
7875            ffi::whiteout_mdx_MdxTrackQuaternion_set_globalSequenceId(self.raw.as_ptr(), value)
7876        }
7877    }
7878
7879    /// Number of keyframes
7880    pub fn key_count(&self) -> usize {
7881        // SAFETY: plain scalar read through a live handle.
7882        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_get_keyCount(self.raw.as_ptr()) }
7883    }
7884
7885    pub fn set_key_count(&mut self, value: usize) {
7886        // SAFETY: plain scalar write through a live handle.
7887        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_set_keyCount(self.raw.as_ptr(), value) }
7888    }
7889
7890    /// Keyframe timestamps (in frames)
7891    /// Zero-copy view of the underlying `std::vector`.
7892    pub fn timestamps(&self) -> &[u32] {
7893        // SAFETY: `_data`/`_count` describe one contiguous C++
7894        // allocation, borrowed for as long as `self` is.
7895        unsafe {
7896            let n = ffi::whiteout_mdx_MdxTrackQuaternion_get_timestamps_count(self.raw.as_ptr());
7897            let p = ffi::whiteout_mdx_MdxTrackQuaternion_get_timestamps_data(self.raw.as_ptr());
7898            if p.is_null() || n == 0 {
7899                &[]
7900            } else {
7901                core::slice::from_raw_parts(p, n)
7902            }
7903        }
7904    }
7905
7906    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
7907    pub fn timestamps_mut(&mut self) -> &mut [u32] {
7908        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
7909        unsafe {
7910            let n = ffi::whiteout_mdx_MdxTrackQuaternion_get_timestamps_count(self.raw.as_ptr());
7911            let p = ffi::whiteout_mdx_MdxTrackQuaternion_get_timestamps_data(self.raw.as_ptr())
7912                as *mut u32;
7913            if p.is_null() || n == 0 {
7914                &mut []
7915            } else {
7916                core::slice::from_raw_parts_mut(p, n)
7917            }
7918        }
7919    }
7920
7921    pub fn set_timestamps(&mut self, values: &[u32]) {
7922        // SAFETY: the native side copies `values` before returning.
7923        unsafe {
7924            ffi::whiteout_mdx_MdxTrackQuaternion_assign_timestamps(
7925                self.raw.as_ptr(),
7926                values.as_ptr() as *const _,
7927                values.len(),
7928            )
7929        }
7930    }
7931
7932    pub fn resize_timestamps(&mut self, count: usize) {
7933        // SAFETY: reallocation is safe here precisely because
7934        // `&mut self` means no slice borrow is outstanding.
7935        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_resize_timestamps(self.raw.as_ptr(), count) }
7936    }
7937
7938    /// Raw keyframe data
7939    /// Zero-copy view of the underlying `std::vector`.
7940    pub fn keys(&self) -> &[crate::math::Quaternion] {
7941        // SAFETY: `_data`/`_count` describe one contiguous C++
7942        // allocation, borrowed for as long as `self` is.
7943        unsafe {
7944            let n = ffi::whiteout_mdx_MdxTrackQuaternion_get_keys_count(self.raw.as_ptr());
7945            let p = ffi::whiteout_mdx_MdxTrackQuaternion_get_keys_data(self.raw.as_ptr())
7946                as *const crate::math::Quaternion;
7947            if p.is_null() || n == 0 {
7948                &[]
7949            } else {
7950                core::slice::from_raw_parts(p, n)
7951            }
7952        }
7953    }
7954
7955    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
7956    pub fn keys_mut(&mut self) -> &mut [crate::math::Quaternion] {
7957        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
7958        unsafe {
7959            let n = ffi::whiteout_mdx_MdxTrackQuaternion_get_keys_count(self.raw.as_ptr());
7960            let p = ffi::whiteout_mdx_MdxTrackQuaternion_get_keys_data(self.raw.as_ptr())
7961                as *const crate::math::Quaternion
7962                as *mut crate::math::Quaternion;
7963            if p.is_null() || n == 0 {
7964                &mut []
7965            } else {
7966                core::slice::from_raw_parts_mut(p, n)
7967            }
7968        }
7969    }
7970
7971    pub fn set_keys(&mut self, values: &[crate::math::Quaternion]) {
7972        // SAFETY: the native side copies `values` before returning.
7973        unsafe {
7974            ffi::whiteout_mdx_MdxTrackQuaternion_assign_keys(
7975                self.raw.as_ptr(),
7976                values.as_ptr() as *const _,
7977                values.len(),
7978            )
7979        }
7980    }
7981
7982    pub fn resize_keys(&mut self, count: usize) {
7983        // SAFETY: reallocation is safe here precisely because
7984        // `&mut self` means no slice borrow is outstanding.
7985        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_resize_keys(self.raw.as_ptr(), count) }
7986    }
7987}
7988
7989impl Default for TrackQuaternion {
7990    fn default() -> Self {
7991        Self::new()
7992    }
7993}
7994
7995/// Generic animation track with keyframe data
7996///
7997/// Tracks store animation data as a series of keyframes. The interpolation type determines how values between keyframes are calculated.
7998///
7999/// @tparam T The value type (f32, Vector3f, Vector4f, u32, etc.)
8000pub struct TrackU32 {
8001    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTrackU32>,
8002}
8003
8004impl Drop for TrackU32 {
8005    fn drop(&mut self) {
8006        // SAFETY: `raw` came from a native constructor and Drop runs once.
8007        unsafe { ffi::whiteout_mdx_MdxTrackU32_delete(self.raw.as_ptr()) }
8008    }
8009}
8010
8011impl TrackU32 {
8012    /// # Safety
8013    /// `raw` must be a live handle this value takes ownership of.
8014    #[allow(dead_code)] // used by whichever methods return this type
8015    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTrackU32) -> Option<Self> {
8016        core::ptr::NonNull::new(raw).map(|raw| TrackU32 { raw })
8017    }
8018}
8019
8020// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
8021// is deliberately NOT implemented — the C++ types make no documented
8022// guarantee about concurrent use, and claiming one we haven't verified
8023// would be unsound. See `@bind thread_safe` in the plan.
8024unsafe impl Send for TrackU32 {}
8025
8026impl core::fmt::Debug for TrackU32 {
8027    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
8028        f.debug_struct("TrackU32").finish_non_exhaustive()
8029    }
8030}
8031
8032impl TrackU32 {
8033    /// # Panics
8034    /// Panics if the native allocation fails.
8035    pub fn new() -> Self {
8036        // SAFETY: the native constructor returns a live handle; a null here
8037        // means the library is unusable.
8038        unsafe {
8039            let raw = ffi::whiteout_mdx_MdxTrackU32_new();
8040            Self::from_raw(raw).expect("native TrackU32 allocation failed")
8041        }
8042    }
8043
8044    /// Whether this track contains animation data
8045    pub fn is_used(&self) -> bool {
8046        // SAFETY: plain scalar read through a live handle.
8047        unsafe { ffi::whiteout_mdx_MdxTrackU32_get_isUsed(self.raw.as_ptr()) != 0 }
8048    }
8049
8050    pub fn set_is_used(&mut self, value: bool) {
8051        // SAFETY: plain scalar write through a live handle.
8052        unsafe {
8053            ffi::whiteout_mdx_MdxTrackU32_set_isUsed(self.raw.as_ptr(), if value { 1 } else { 0 })
8054        }
8055    }
8056
8057    /// Interpolation type
8058    pub fn interpolation_type(&self) -> InterpolationType {
8059        // SAFETY: scalar read; the discriminant is validated below.
8060        unsafe { ffi::whiteout_mdx_MdxTrackU32_get_interpolationType(self.raw.as_ptr()) }
8061            .try_into()
8062            .expect("unknown enum discriminant from the native library")
8063    }
8064
8065    pub fn set_interpolation_type(&mut self, value: InterpolationType) {
8066        // SAFETY: scalar write through a live handle.
8067        unsafe {
8068            ffi::whiteout_mdx_MdxTrackU32_set_interpolationType(self.raw.as_ptr(), value as i32)
8069        }
8070    }
8071
8072    /// Global sequence ID if applicable
8073    pub fn global_sequence_id(&self) -> u32 {
8074        // SAFETY: plain scalar read through a live handle.
8075        unsafe { ffi::whiteout_mdx_MdxTrackU32_get_globalSequenceId(self.raw.as_ptr()) }
8076    }
8077
8078    pub fn set_global_sequence_id(&mut self, value: u32) {
8079        // SAFETY: plain scalar write through a live handle.
8080        unsafe { ffi::whiteout_mdx_MdxTrackU32_set_globalSequenceId(self.raw.as_ptr(), value) }
8081    }
8082
8083    /// Number of keyframes
8084    pub fn key_count(&self) -> usize {
8085        // SAFETY: plain scalar read through a live handle.
8086        unsafe { ffi::whiteout_mdx_MdxTrackU32_get_keyCount(self.raw.as_ptr()) }
8087    }
8088
8089    pub fn set_key_count(&mut self, value: usize) {
8090        // SAFETY: plain scalar write through a live handle.
8091        unsafe { ffi::whiteout_mdx_MdxTrackU32_set_keyCount(self.raw.as_ptr(), value) }
8092    }
8093
8094    /// Keyframe timestamps (in frames)
8095    /// Zero-copy view of the underlying `std::vector`.
8096    pub fn timestamps(&self) -> &[u32] {
8097        // SAFETY: `_data`/`_count` describe one contiguous C++
8098        // allocation, borrowed for as long as `self` is.
8099        unsafe {
8100            let n = ffi::whiteout_mdx_MdxTrackU32_get_timestamps_count(self.raw.as_ptr());
8101            let p = ffi::whiteout_mdx_MdxTrackU32_get_timestamps_data(self.raw.as_ptr());
8102            if p.is_null() || n == 0 {
8103                &[]
8104            } else {
8105                core::slice::from_raw_parts(p, n)
8106            }
8107        }
8108    }
8109
8110    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
8111    pub fn timestamps_mut(&mut self) -> &mut [u32] {
8112        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
8113        unsafe {
8114            let n = ffi::whiteout_mdx_MdxTrackU32_get_timestamps_count(self.raw.as_ptr());
8115            let p =
8116                ffi::whiteout_mdx_MdxTrackU32_get_timestamps_data(self.raw.as_ptr()) as *mut u32;
8117            if p.is_null() || n == 0 {
8118                &mut []
8119            } else {
8120                core::slice::from_raw_parts_mut(p, n)
8121            }
8122        }
8123    }
8124
8125    pub fn set_timestamps(&mut self, values: &[u32]) {
8126        // SAFETY: the native side copies `values` before returning.
8127        unsafe {
8128            ffi::whiteout_mdx_MdxTrackU32_assign_timestamps(
8129                self.raw.as_ptr(),
8130                values.as_ptr() as *const _,
8131                values.len(),
8132            )
8133        }
8134    }
8135
8136    pub fn resize_timestamps(&mut self, count: usize) {
8137        // SAFETY: reallocation is safe here precisely because
8138        // `&mut self` means no slice borrow is outstanding.
8139        unsafe { ffi::whiteout_mdx_MdxTrackU32_resize_timestamps(self.raw.as_ptr(), count) }
8140    }
8141
8142    /// Raw keyframe data
8143    /// Zero-copy view of the underlying `std::vector`.
8144    pub fn keys(&self) -> &[u32] {
8145        // SAFETY: `_data`/`_count` describe one contiguous C++
8146        // allocation, borrowed for as long as `self` is.
8147        unsafe {
8148            let n = ffi::whiteout_mdx_MdxTrackU32_get_keys_count(self.raw.as_ptr());
8149            let p = ffi::whiteout_mdx_MdxTrackU32_get_keys_data(self.raw.as_ptr());
8150            if p.is_null() || n == 0 {
8151                &[]
8152            } else {
8153                core::slice::from_raw_parts(p, n)
8154            }
8155        }
8156    }
8157
8158    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
8159    pub fn keys_mut(&mut self) -> &mut [u32] {
8160        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
8161        unsafe {
8162            let n = ffi::whiteout_mdx_MdxTrackU32_get_keys_count(self.raw.as_ptr());
8163            let p = ffi::whiteout_mdx_MdxTrackU32_get_keys_data(self.raw.as_ptr()) as *mut u32;
8164            if p.is_null() || n == 0 {
8165                &mut []
8166            } else {
8167                core::slice::from_raw_parts_mut(p, n)
8168            }
8169        }
8170    }
8171
8172    pub fn set_keys(&mut self, values: &[u32]) {
8173        // SAFETY: the native side copies `values` before returning.
8174        unsafe {
8175            ffi::whiteout_mdx_MdxTrackU32_assign_keys(
8176                self.raw.as_ptr(),
8177                values.as_ptr() as *const _,
8178                values.len(),
8179            )
8180        }
8181    }
8182
8183    pub fn resize_keys(&mut self, count: usize) {
8184        // SAFETY: reallocation is safe here precisely because
8185        // `&mut self` means no slice borrow is outstanding.
8186        unsafe { ffi::whiteout_mdx_MdxTrackU32_resize_keys(self.raw.as_ptr(), count) }
8187    }
8188}
8189
8190impl Default for TrackU32 {
8191    fn default() -> Self {
8192        Self::new()
8193    }
8194}
8195
8196/// Generic animation track with keyframe data
8197///
8198/// Tracks store animation data as a series of keyframes. The interpolation type determines how values between keyframes are calculated.
8199///
8200/// @tparam T The value type (f32, Vector3f, Vector4f, u32, etc.)
8201pub struct TrackF32 {
8202    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTrackF32>,
8203}
8204
8205impl Drop for TrackF32 {
8206    fn drop(&mut self) {
8207        // SAFETY: `raw` came from a native constructor and Drop runs once.
8208        unsafe { ffi::whiteout_mdx_MdxTrackF32_delete(self.raw.as_ptr()) }
8209    }
8210}
8211
8212impl TrackF32 {
8213    /// # Safety
8214    /// `raw` must be a live handle this value takes ownership of.
8215    #[allow(dead_code)] // used by whichever methods return this type
8216    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTrackF32) -> Option<Self> {
8217        core::ptr::NonNull::new(raw).map(|raw| TrackF32 { raw })
8218    }
8219}
8220
8221// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
8222// is deliberately NOT implemented — the C++ types make no documented
8223// guarantee about concurrent use, and claiming one we haven't verified
8224// would be unsound. See `@bind thread_safe` in the plan.
8225unsafe impl Send for TrackF32 {}
8226
8227impl core::fmt::Debug for TrackF32 {
8228    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
8229        f.debug_struct("TrackF32").finish_non_exhaustive()
8230    }
8231}
8232
8233impl TrackF32 {
8234    /// # Panics
8235    /// Panics if the native allocation fails.
8236    pub fn new() -> Self {
8237        // SAFETY: the native constructor returns a live handle; a null here
8238        // means the library is unusable.
8239        unsafe {
8240            let raw = ffi::whiteout_mdx_MdxTrackF32_new();
8241            Self::from_raw(raw).expect("native TrackF32 allocation failed")
8242        }
8243    }
8244
8245    /// Whether this track contains animation data
8246    pub fn is_used(&self) -> bool {
8247        // SAFETY: plain scalar read through a live handle.
8248        unsafe { ffi::whiteout_mdx_MdxTrackF32_get_isUsed(self.raw.as_ptr()) != 0 }
8249    }
8250
8251    pub fn set_is_used(&mut self, value: bool) {
8252        // SAFETY: plain scalar write through a live handle.
8253        unsafe {
8254            ffi::whiteout_mdx_MdxTrackF32_set_isUsed(self.raw.as_ptr(), if value { 1 } else { 0 })
8255        }
8256    }
8257
8258    /// Interpolation type
8259    pub fn interpolation_type(&self) -> InterpolationType {
8260        // SAFETY: scalar read; the discriminant is validated below.
8261        unsafe { ffi::whiteout_mdx_MdxTrackF32_get_interpolationType(self.raw.as_ptr()) }
8262            .try_into()
8263            .expect("unknown enum discriminant from the native library")
8264    }
8265
8266    pub fn set_interpolation_type(&mut self, value: InterpolationType) {
8267        // SAFETY: scalar write through a live handle.
8268        unsafe {
8269            ffi::whiteout_mdx_MdxTrackF32_set_interpolationType(self.raw.as_ptr(), value as i32)
8270        }
8271    }
8272
8273    /// Global sequence ID if applicable
8274    pub fn global_sequence_id(&self) -> u32 {
8275        // SAFETY: plain scalar read through a live handle.
8276        unsafe { ffi::whiteout_mdx_MdxTrackF32_get_globalSequenceId(self.raw.as_ptr()) }
8277    }
8278
8279    pub fn set_global_sequence_id(&mut self, value: u32) {
8280        // SAFETY: plain scalar write through a live handle.
8281        unsafe { ffi::whiteout_mdx_MdxTrackF32_set_globalSequenceId(self.raw.as_ptr(), value) }
8282    }
8283
8284    /// Number of keyframes
8285    pub fn key_count(&self) -> usize {
8286        // SAFETY: plain scalar read through a live handle.
8287        unsafe { ffi::whiteout_mdx_MdxTrackF32_get_keyCount(self.raw.as_ptr()) }
8288    }
8289
8290    pub fn set_key_count(&mut self, value: usize) {
8291        // SAFETY: plain scalar write through a live handle.
8292        unsafe { ffi::whiteout_mdx_MdxTrackF32_set_keyCount(self.raw.as_ptr(), value) }
8293    }
8294
8295    /// Keyframe timestamps (in frames)
8296    /// Zero-copy view of the underlying `std::vector`.
8297    pub fn timestamps(&self) -> &[u32] {
8298        // SAFETY: `_data`/`_count` describe one contiguous C++
8299        // allocation, borrowed for as long as `self` is.
8300        unsafe {
8301            let n = ffi::whiteout_mdx_MdxTrackF32_get_timestamps_count(self.raw.as_ptr());
8302            let p = ffi::whiteout_mdx_MdxTrackF32_get_timestamps_data(self.raw.as_ptr());
8303            if p.is_null() || n == 0 {
8304                &[]
8305            } else {
8306                core::slice::from_raw_parts(p, n)
8307            }
8308        }
8309    }
8310
8311    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
8312    pub fn timestamps_mut(&mut self) -> &mut [u32] {
8313        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
8314        unsafe {
8315            let n = ffi::whiteout_mdx_MdxTrackF32_get_timestamps_count(self.raw.as_ptr());
8316            let p =
8317                ffi::whiteout_mdx_MdxTrackF32_get_timestamps_data(self.raw.as_ptr()) as *mut u32;
8318            if p.is_null() || n == 0 {
8319                &mut []
8320            } else {
8321                core::slice::from_raw_parts_mut(p, n)
8322            }
8323        }
8324    }
8325
8326    pub fn set_timestamps(&mut self, values: &[u32]) {
8327        // SAFETY: the native side copies `values` before returning.
8328        unsafe {
8329            ffi::whiteout_mdx_MdxTrackF32_assign_timestamps(
8330                self.raw.as_ptr(),
8331                values.as_ptr() as *const _,
8332                values.len(),
8333            )
8334        }
8335    }
8336
8337    pub fn resize_timestamps(&mut self, count: usize) {
8338        // SAFETY: reallocation is safe here precisely because
8339        // `&mut self` means no slice borrow is outstanding.
8340        unsafe { ffi::whiteout_mdx_MdxTrackF32_resize_timestamps(self.raw.as_ptr(), count) }
8341    }
8342
8343    /// Raw keyframe data
8344    /// Zero-copy view of the underlying `std::vector`.
8345    pub fn keys(&self) -> &[f32] {
8346        // SAFETY: `_data`/`_count` describe one contiguous C++
8347        // allocation, borrowed for as long as `self` is.
8348        unsafe {
8349            let n = ffi::whiteout_mdx_MdxTrackF32_get_keys_count(self.raw.as_ptr());
8350            let p = ffi::whiteout_mdx_MdxTrackF32_get_keys_data(self.raw.as_ptr());
8351            if p.is_null() || n == 0 {
8352                &[]
8353            } else {
8354                core::slice::from_raw_parts(p, n)
8355            }
8356        }
8357    }
8358
8359    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
8360    pub fn keys_mut(&mut self) -> &mut [f32] {
8361        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
8362        unsafe {
8363            let n = ffi::whiteout_mdx_MdxTrackF32_get_keys_count(self.raw.as_ptr());
8364            let p = ffi::whiteout_mdx_MdxTrackF32_get_keys_data(self.raw.as_ptr()) as *mut f32;
8365            if p.is_null() || n == 0 {
8366                &mut []
8367            } else {
8368                core::slice::from_raw_parts_mut(p, n)
8369            }
8370        }
8371    }
8372
8373    pub fn set_keys(&mut self, values: &[f32]) {
8374        // SAFETY: the native side copies `values` before returning.
8375        unsafe {
8376            ffi::whiteout_mdx_MdxTrackF32_assign_keys(
8377                self.raw.as_ptr(),
8378                values.as_ptr() as *const _,
8379                values.len(),
8380            )
8381        }
8382    }
8383
8384    pub fn resize_keys(&mut self, count: usize) {
8385        // SAFETY: reallocation is safe here precisely because
8386        // `&mut self` means no slice borrow is outstanding.
8387        unsafe { ffi::whiteout_mdx_MdxTrackF32_resize_keys(self.raw.as_ptr(), count) }
8388    }
8389}
8390
8391impl Default for TrackF32 {
8392    fn default() -> Self {
8393        Self::new()
8394    }
8395}
8396
8397#[doc(hidden)]
8398pub mod ffi {
8399    #![allow(missing_debug_implementations)]
8400
8401    #[allow(unused_imports)]
8402    use crate::support::{RawBytes, RawCString};
8403
8404    #[repr(C)]
8405    pub struct whiteout_MdxExtent {
8406        _private: [u8; 0],
8407    }
8408    #[repr(C)]
8409    pub struct whiteout_MdxModel {
8410        _private: [u8; 0],
8411    }
8412    #[repr(C)]
8413    pub struct whiteout_MdxSequence {
8414        _private: [u8; 0],
8415    }
8416    #[repr(C)]
8417    pub struct whiteout_MdxTexture {
8418        _private: [u8; 0],
8419    }
8420    #[repr(C)]
8421    pub struct whiteout_MdxSound {
8422        _private: [u8; 0],
8423    }
8424    #[repr(C)]
8425    pub struct whiteout_MdxNode {
8426        _private: [u8; 0],
8427    }
8428    #[repr(C)]
8429    pub struct whiteout_MdxSoundEmitter {
8430        _private: [u8; 0],
8431    }
8432    #[repr(C)]
8433    pub struct whiteout_MdxLayer {
8434        _private: [u8; 0],
8435    }
8436    #[repr(C)]
8437    pub struct whiteout_MdxLayerSubTexture {
8438        _private: [u8; 0],
8439    }
8440    #[repr(C)]
8441    pub struct whiteout_MdxMaterial {
8442        _private: [u8; 0],
8443    }
8444    #[repr(C)]
8445    pub struct whiteout_MdxTextureAnimation {
8446        _private: [u8; 0],
8447    }
8448    #[repr(C)]
8449    pub struct whiteout_MdxGeoset {
8450        _private: [u8; 0],
8451    }
8452    #[repr(C)]
8453    pub struct whiteout_MdxGeosetAnimation {
8454        _private: [u8; 0],
8455    }
8456    #[repr(C)]
8457    pub struct whiteout_MdxBone {
8458        _private: [u8; 0],
8459    }
8460    #[repr(C)]
8461    pub struct whiteout_MdxLight {
8462        _private: [u8; 0],
8463    }
8464    #[repr(C)]
8465    pub struct whiteout_MdxHelper {
8466        _private: [u8; 0],
8467    }
8468    #[repr(C)]
8469    pub struct whiteout_MdxAttachment {
8470        _private: [u8; 0],
8471    }
8472    #[repr(C)]
8473    pub struct whiteout_MdxParticleEmitter {
8474        _private: [u8; 0],
8475    }
8476    #[repr(C)]
8477    pub struct whiteout_MdxParticleEmitter2 {
8478        _private: [u8; 0],
8479    }
8480    #[repr(C)]
8481    pub struct whiteout_MdxRibbonEmitter {
8482        _private: [u8; 0],
8483    }
8484    #[repr(C)]
8485    pub struct whiteout_MdxEventObject {
8486        _private: [u8; 0],
8487    }
8488    #[repr(C)]
8489    pub struct whiteout_MdxCamera {
8490        _private: [u8; 0],
8491    }
8492    #[repr(C)]
8493    pub struct whiteout_MdxCollisionShape {
8494        _private: [u8; 0],
8495    }
8496    #[repr(C)]
8497    pub struct whiteout_MdxFaceEffect {
8498        _private: [u8; 0],
8499    }
8500    #[repr(C)]
8501    pub struct whiteout_MdxCornEmitter {
8502        _private: [u8; 0],
8503    }
8504    #[repr(C)]
8505    pub struct whiteout_MdxParser {
8506        _private: [u8; 0],
8507    }
8508    #[repr(C)]
8509    pub struct whiteout_MdxWriter {
8510        _private: [u8; 0],
8511    }
8512    #[repr(C)]
8513    pub struct whiteout_MdxTrackVector3f {
8514        _private: [u8; 0],
8515    }
8516    #[repr(C)]
8517    pub struct whiteout_MdxTrackQuaternion {
8518        _private: [u8; 0],
8519    }
8520    #[repr(C)]
8521    pub struct whiteout_MdxTrackU32 {
8522        _private: [u8; 0],
8523    }
8524    #[repr(C)]
8525    pub struct whiteout_MdxTrackF32 {
8526        _private: [u8; 0],
8527    }
8528
8529    extern "C" {
8530        // Extent
8531        pub fn whiteout_mdx_MdxExtent_new() -> *mut whiteout_MdxExtent;
8532        pub fn whiteout_mdx_MdxExtent_delete(self_: *mut whiteout_MdxExtent);
8533        pub fn whiteout_mdx_MdxExtent_get_boundsRadius(self_: *mut whiteout_MdxExtent) -> f32;
8534        pub fn whiteout_mdx_MdxExtent_set_boundsRadius(self_: *mut whiteout_MdxExtent, value: f32);
8535        pub fn whiteout_mdx_MdxExtent_get_minimum(
8536            self_: *mut whiteout_MdxExtent,
8537        ) -> *mut core::ffi::c_void;
8538        pub fn whiteout_mdx_MdxExtent_set_minimum(
8539            self_: *mut whiteout_MdxExtent,
8540            value: *const core::ffi::c_void,
8541        );
8542        pub fn whiteout_mdx_MdxExtent_get_maximum(
8543            self_: *mut whiteout_MdxExtent,
8544        ) -> *mut core::ffi::c_void;
8545        pub fn whiteout_mdx_MdxExtent_set_maximum(
8546            self_: *mut whiteout_MdxExtent,
8547            value: *const core::ffi::c_void,
8548        );
8549        // Model
8550        pub fn whiteout_mdx_MdxModel_new() -> *mut whiteout_MdxModel;
8551        pub fn whiteout_mdx_MdxModel_delete(self_: *mut whiteout_MdxModel);
8552        pub fn whiteout_mdx_MdxModel_get_version(self_: *mut whiteout_MdxModel) -> u32;
8553        pub fn whiteout_mdx_MdxModel_set_version(self_: *mut whiteout_MdxModel, value: u32);
8554        pub fn whiteout_mdx_MdxModel_get_modelName(self_: *mut whiteout_MdxModel) -> RawCString;
8555        pub fn whiteout_mdx_MdxModel_set_modelName(
8556            self_: *mut whiteout_MdxModel,
8557            value: *const core::ffi::c_char,
8558        );
8559        pub fn whiteout_mdx_MdxModel_get_animationFileName(
8560            self_: *mut whiteout_MdxModel,
8561        ) -> RawCString;
8562        pub fn whiteout_mdx_MdxModel_set_animationFileName(
8563            self_: *mut whiteout_MdxModel,
8564            value: *const core::ffi::c_char,
8565        );
8566        pub fn whiteout_mdx_MdxModel_get_modelExtent(
8567            self_: *mut whiteout_MdxModel,
8568        ) -> *mut whiteout_MdxExtent;
8569        pub fn whiteout_mdx_MdxModel_set_modelExtent(
8570            self_: *mut whiteout_MdxModel,
8571            value: *const whiteout_MdxExtent,
8572        );
8573        pub fn whiteout_mdx_MdxModel_get_blendTime(self_: *mut whiteout_MdxModel) -> u32;
8574        pub fn whiteout_mdx_MdxModel_set_blendTime(self_: *mut whiteout_MdxModel, value: u32);
8575        pub fn whiteout_mdx_MdxModel_get_globalSequences_count(
8576            self_: *mut whiteout_MdxModel,
8577        ) -> usize;
8578        pub fn whiteout_mdx_MdxModel_resize_globalSequences(
8579            self_: *mut whiteout_MdxModel,
8580            count: usize,
8581        );
8582        pub fn whiteout_mdx_MdxModel_get_globalSequences_data(
8583            self_: *mut whiteout_MdxModel,
8584        ) -> *const u32;
8585        pub fn whiteout_mdx_MdxModel_assign_globalSequences(
8586            self_: *mut whiteout_MdxModel,
8587            data: *const u32,
8588            count: usize,
8589        );
8590        pub fn whiteout_mdx_MdxModel_get_sequences_count(self_: *mut whiteout_MdxModel) -> usize;
8591        pub fn whiteout_mdx_MdxModel_resize_sequences(self_: *mut whiteout_MdxModel, count: usize);
8592        pub fn whiteout_mdx_MdxModel_get_sequences_at(
8593            self_: *mut whiteout_MdxModel,
8594            index: usize,
8595        ) -> *mut whiteout_MdxSequence;
8596        pub fn whiteout_mdx_MdxModel_get_textures_count(self_: *mut whiteout_MdxModel) -> usize;
8597        pub fn whiteout_mdx_MdxModel_resize_textures(self_: *mut whiteout_MdxModel, count: usize);
8598        pub fn whiteout_mdx_MdxModel_get_textures_at(
8599            self_: *mut whiteout_MdxModel,
8600            index: usize,
8601        ) -> *mut whiteout_MdxTexture;
8602        pub fn whiteout_mdx_MdxModel_get_sounds_count(self_: *mut whiteout_MdxModel) -> usize;
8603        pub fn whiteout_mdx_MdxModel_resize_sounds(self_: *mut whiteout_MdxModel, count: usize);
8604        pub fn whiteout_mdx_MdxModel_get_sounds_at(
8605            self_: *mut whiteout_MdxModel,
8606            index: usize,
8607        ) -> *mut whiteout_MdxSound;
8608        pub fn whiteout_mdx_MdxModel_get_soundEmitters_count(
8609            self_: *mut whiteout_MdxModel,
8610        ) -> usize;
8611        pub fn whiteout_mdx_MdxModel_resize_soundEmitters(
8612            self_: *mut whiteout_MdxModel,
8613            count: usize,
8614        );
8615        pub fn whiteout_mdx_MdxModel_get_soundEmitters_at(
8616            self_: *mut whiteout_MdxModel,
8617            index: usize,
8618        ) -> *mut whiteout_MdxSoundEmitter;
8619        pub fn whiteout_mdx_MdxModel_get_materials_count(self_: *mut whiteout_MdxModel) -> usize;
8620        pub fn whiteout_mdx_MdxModel_resize_materials(self_: *mut whiteout_MdxModel, count: usize);
8621        pub fn whiteout_mdx_MdxModel_get_materials_at(
8622            self_: *mut whiteout_MdxModel,
8623            index: usize,
8624        ) -> *mut whiteout_MdxMaterial;
8625        pub fn whiteout_mdx_MdxModel_get_textureAnimations_count(
8626            self_: *mut whiteout_MdxModel,
8627        ) -> usize;
8628        pub fn whiteout_mdx_MdxModel_resize_textureAnimations(
8629            self_: *mut whiteout_MdxModel,
8630            count: usize,
8631        );
8632        pub fn whiteout_mdx_MdxModel_get_textureAnimations_at(
8633            self_: *mut whiteout_MdxModel,
8634            index: usize,
8635        ) -> *mut whiteout_MdxTextureAnimation;
8636        pub fn whiteout_mdx_MdxModel_get_geosets_count(self_: *mut whiteout_MdxModel) -> usize;
8637        pub fn whiteout_mdx_MdxModel_resize_geosets(self_: *mut whiteout_MdxModel, count: usize);
8638        pub fn whiteout_mdx_MdxModel_get_geosets_at(
8639            self_: *mut whiteout_MdxModel,
8640            index: usize,
8641        ) -> *mut whiteout_MdxGeoset;
8642        pub fn whiteout_mdx_MdxModel_get_geosetAnimations_count(
8643            self_: *mut whiteout_MdxModel,
8644        ) -> usize;
8645        pub fn whiteout_mdx_MdxModel_resize_geosetAnimations(
8646            self_: *mut whiteout_MdxModel,
8647            count: usize,
8648        );
8649        pub fn whiteout_mdx_MdxModel_get_geosetAnimations_at(
8650            self_: *mut whiteout_MdxModel,
8651            index: usize,
8652        ) -> *mut whiteout_MdxGeosetAnimation;
8653        pub fn whiteout_mdx_MdxModel_get_bones_count(self_: *mut whiteout_MdxModel) -> usize;
8654        pub fn whiteout_mdx_MdxModel_resize_bones(self_: *mut whiteout_MdxModel, count: usize);
8655        pub fn whiteout_mdx_MdxModel_get_bones_at(
8656            self_: *mut whiteout_MdxModel,
8657            index: usize,
8658        ) -> *mut whiteout_MdxBone;
8659        pub fn whiteout_mdx_MdxModel_get_helpers_count(self_: *mut whiteout_MdxModel) -> usize;
8660        pub fn whiteout_mdx_MdxModel_resize_helpers(self_: *mut whiteout_MdxModel, count: usize);
8661        pub fn whiteout_mdx_MdxModel_get_helpers_at(
8662            self_: *mut whiteout_MdxModel,
8663            index: usize,
8664        ) -> *mut whiteout_MdxHelper;
8665        pub fn whiteout_mdx_MdxModel_get_attachments_count(self_: *mut whiteout_MdxModel) -> usize;
8666        pub fn whiteout_mdx_MdxModel_resize_attachments(
8667            self_: *mut whiteout_MdxModel,
8668            count: usize,
8669        );
8670        pub fn whiteout_mdx_MdxModel_get_attachments_at(
8671            self_: *mut whiteout_MdxModel,
8672            index: usize,
8673        ) -> *mut whiteout_MdxAttachment;
8674        pub fn whiteout_mdx_MdxModel_get_pivotPoints_count(self_: *mut whiteout_MdxModel) -> usize;
8675        pub fn whiteout_mdx_MdxModel_resize_pivotPoints(
8676            self_: *mut whiteout_MdxModel,
8677            count: usize,
8678        );
8679        pub fn whiteout_mdx_MdxModel_get_pivotPoints_data(
8680            self_: *mut whiteout_MdxModel,
8681        ) -> *const f32;
8682        pub fn whiteout_mdx_MdxModel_assign_pivotPoints(
8683            self_: *mut whiteout_MdxModel,
8684            data: *const f32,
8685            count: usize,
8686        );
8687        pub fn whiteout_mdx_MdxModel_get_lights_count(self_: *mut whiteout_MdxModel) -> usize;
8688        pub fn whiteout_mdx_MdxModel_resize_lights(self_: *mut whiteout_MdxModel, count: usize);
8689        pub fn whiteout_mdx_MdxModel_get_lights_at(
8690            self_: *mut whiteout_MdxModel,
8691            index: usize,
8692        ) -> *mut whiteout_MdxLight;
8693        pub fn whiteout_mdx_MdxModel_get_particleEmitters_count(
8694            self_: *mut whiteout_MdxModel,
8695        ) -> usize;
8696        pub fn whiteout_mdx_MdxModel_resize_particleEmitters(
8697            self_: *mut whiteout_MdxModel,
8698            count: usize,
8699        );
8700        pub fn whiteout_mdx_MdxModel_get_particleEmitters_at(
8701            self_: *mut whiteout_MdxModel,
8702            index: usize,
8703        ) -> *mut whiteout_MdxParticleEmitter;
8704        pub fn whiteout_mdx_MdxModel_get_particleEmitters2_count(
8705            self_: *mut whiteout_MdxModel,
8706        ) -> usize;
8707        pub fn whiteout_mdx_MdxModel_resize_particleEmitters2(
8708            self_: *mut whiteout_MdxModel,
8709            count: usize,
8710        );
8711        pub fn whiteout_mdx_MdxModel_get_particleEmitters2_at(
8712            self_: *mut whiteout_MdxModel,
8713            index: usize,
8714        ) -> *mut whiteout_MdxParticleEmitter2;
8715        pub fn whiteout_mdx_MdxModel_get_ribbonEmitters_count(
8716            self_: *mut whiteout_MdxModel,
8717        ) -> usize;
8718        pub fn whiteout_mdx_MdxModel_resize_ribbonEmitters(
8719            self_: *mut whiteout_MdxModel,
8720            count: usize,
8721        );
8722        pub fn whiteout_mdx_MdxModel_get_ribbonEmitters_at(
8723            self_: *mut whiteout_MdxModel,
8724            index: usize,
8725        ) -> *mut whiteout_MdxRibbonEmitter;
8726        pub fn whiteout_mdx_MdxModel_get_cornEmitters_count(self_: *mut whiteout_MdxModel)
8727            -> usize;
8728        pub fn whiteout_mdx_MdxModel_resize_cornEmitters(
8729            self_: *mut whiteout_MdxModel,
8730            count: usize,
8731        );
8732        pub fn whiteout_mdx_MdxModel_get_cornEmitters_at(
8733            self_: *mut whiteout_MdxModel,
8734            index: usize,
8735        ) -> *mut whiteout_MdxCornEmitter;
8736        pub fn whiteout_mdx_MdxModel_get_eventObjects_count(self_: *mut whiteout_MdxModel)
8737            -> usize;
8738        pub fn whiteout_mdx_MdxModel_resize_eventObjects(
8739            self_: *mut whiteout_MdxModel,
8740            count: usize,
8741        );
8742        pub fn whiteout_mdx_MdxModel_get_eventObjects_at(
8743            self_: *mut whiteout_MdxModel,
8744            index: usize,
8745        ) -> *mut whiteout_MdxEventObject;
8746        pub fn whiteout_mdx_MdxModel_get_cameras_count(self_: *mut whiteout_MdxModel) -> usize;
8747        pub fn whiteout_mdx_MdxModel_resize_cameras(self_: *mut whiteout_MdxModel, count: usize);
8748        pub fn whiteout_mdx_MdxModel_get_cameras_at(
8749            self_: *mut whiteout_MdxModel,
8750            index: usize,
8751        ) -> *mut whiteout_MdxCamera;
8752        pub fn whiteout_mdx_MdxModel_get_collisionShapes_count(
8753            self_: *mut whiteout_MdxModel,
8754        ) -> usize;
8755        pub fn whiteout_mdx_MdxModel_resize_collisionShapes(
8756            self_: *mut whiteout_MdxModel,
8757            count: usize,
8758        );
8759        pub fn whiteout_mdx_MdxModel_get_collisionShapes_at(
8760            self_: *mut whiteout_MdxModel,
8761            index: usize,
8762        ) -> *mut whiteout_MdxCollisionShape;
8763        pub fn whiteout_mdx_MdxModel_get_faceEffects_count(self_: *mut whiteout_MdxModel) -> usize;
8764        pub fn whiteout_mdx_MdxModel_resize_faceEffects(
8765            self_: *mut whiteout_MdxModel,
8766            count: usize,
8767        );
8768        pub fn whiteout_mdx_MdxModel_get_faceEffects_at(
8769            self_: *mut whiteout_MdxModel,
8770            index: usize,
8771        ) -> *mut whiteout_MdxFaceEffect;
8772        // Sequence
8773        pub fn whiteout_mdx_MdxSequence_new() -> *mut whiteout_MdxSequence;
8774        pub fn whiteout_mdx_MdxSequence_delete(self_: *mut whiteout_MdxSequence);
8775        pub fn whiteout_mdx_MdxSequence_get_name(self_: *mut whiteout_MdxSequence) -> RawCString;
8776        pub fn whiteout_mdx_MdxSequence_set_name(
8777            self_: *mut whiteout_MdxSequence,
8778            value: *const core::ffi::c_char,
8779        );
8780        pub fn whiteout_mdx_MdxSequence_get_intervalStart(self_: *mut whiteout_MdxSequence) -> u32;
8781        pub fn whiteout_mdx_MdxSequence_set_intervalStart(
8782            self_: *mut whiteout_MdxSequence,
8783            value: u32,
8784        );
8785        pub fn whiteout_mdx_MdxSequence_get_intervalEnd(self_: *mut whiteout_MdxSequence) -> u32;
8786        pub fn whiteout_mdx_MdxSequence_set_intervalEnd(
8787            self_: *mut whiteout_MdxSequence,
8788            value: u32,
8789        );
8790        pub fn whiteout_mdx_MdxSequence_get_moveSpeed(self_: *mut whiteout_MdxSequence) -> f32;
8791        pub fn whiteout_mdx_MdxSequence_set_moveSpeed(self_: *mut whiteout_MdxSequence, value: f32);
8792        pub fn whiteout_mdx_MdxSequence_get_flags(self_: *mut whiteout_MdxSequence) -> i32;
8793        pub fn whiteout_mdx_MdxSequence_set_flags(self_: *mut whiteout_MdxSequence, value: i32);
8794        pub fn whiteout_mdx_MdxSequence_get_rarity(self_: *mut whiteout_MdxSequence) -> f32;
8795        pub fn whiteout_mdx_MdxSequence_set_rarity(self_: *mut whiteout_MdxSequence, value: f32);
8796        pub fn whiteout_mdx_MdxSequence_get_syncPoint(self_: *mut whiteout_MdxSequence) -> u32;
8797        pub fn whiteout_mdx_MdxSequence_set_syncPoint(self_: *mut whiteout_MdxSequence, value: u32);
8798        pub fn whiteout_mdx_MdxSequence_get_extent(
8799            self_: *mut whiteout_MdxSequence,
8800        ) -> *mut whiteout_MdxExtent;
8801        pub fn whiteout_mdx_MdxSequence_set_extent(
8802            self_: *mut whiteout_MdxSequence,
8803            value: *const whiteout_MdxExtent,
8804        );
8805        // Texture
8806        pub fn whiteout_mdx_MdxTexture_new() -> *mut whiteout_MdxTexture;
8807        pub fn whiteout_mdx_MdxTexture_delete(self_: *mut whiteout_MdxTexture);
8808        pub fn whiteout_mdx_MdxTexture_get_replaceableId(self_: *mut whiteout_MdxTexture) -> u32;
8809        pub fn whiteout_mdx_MdxTexture_set_replaceableId(
8810            self_: *mut whiteout_MdxTexture,
8811            value: u32,
8812        );
8813        pub fn whiteout_mdx_MdxTexture_get_fileName(self_: *mut whiteout_MdxTexture) -> RawCString;
8814        pub fn whiteout_mdx_MdxTexture_set_fileName(
8815            self_: *mut whiteout_MdxTexture,
8816            value: *const core::ffi::c_char,
8817        );
8818        pub fn whiteout_mdx_MdxTexture_get_flags(self_: *mut whiteout_MdxTexture) -> i32;
8819        pub fn whiteout_mdx_MdxTexture_set_flags(self_: *mut whiteout_MdxTexture, value: i32);
8820        // Sound
8821        pub fn whiteout_mdx_MdxSound_new() -> *mut whiteout_MdxSound;
8822        pub fn whiteout_mdx_MdxSound_delete(self_: *mut whiteout_MdxSound);
8823        pub fn whiteout_mdx_MdxSound_get_soundFile(self_: *mut whiteout_MdxSound) -> RawCString;
8824        pub fn whiteout_mdx_MdxSound_set_soundFile(
8825            self_: *mut whiteout_MdxSound,
8826            value: *const core::ffi::c_char,
8827        );
8828        pub fn whiteout_mdx_MdxSound_get_maximumDistance(self_: *mut whiteout_MdxSound) -> f32;
8829        pub fn whiteout_mdx_MdxSound_set_maximumDistance(self_: *mut whiteout_MdxSound, value: f32);
8830        pub fn whiteout_mdx_MdxSound_get_minimumDistance(self_: *mut whiteout_MdxSound) -> f32;
8831        pub fn whiteout_mdx_MdxSound_set_minimumDistance(self_: *mut whiteout_MdxSound, value: f32);
8832        pub fn whiteout_mdx_MdxSound_get_soundChannel(self_: *mut whiteout_MdxSound) -> u32;
8833        pub fn whiteout_mdx_MdxSound_set_soundChannel(self_: *mut whiteout_MdxSound, value: u32);
8834        // Node
8835        pub fn whiteout_mdx_MdxNode_new() -> *mut whiteout_MdxNode;
8836        pub fn whiteout_mdx_MdxNode_delete(self_: *mut whiteout_MdxNode);
8837        pub fn whiteout_mdx_MdxNode_get_name(self_: *mut whiteout_MdxNode) -> RawCString;
8838        pub fn whiteout_mdx_MdxNode_set_name(
8839            self_: *mut whiteout_MdxNode,
8840            value: *const core::ffi::c_char,
8841        );
8842        pub fn whiteout_mdx_MdxNode_get_objectId(self_: *mut whiteout_MdxNode) -> u32;
8843        pub fn whiteout_mdx_MdxNode_set_objectId(self_: *mut whiteout_MdxNode, value: u32);
8844        pub fn whiteout_mdx_MdxNode_get_parentId(self_: *mut whiteout_MdxNode) -> u32;
8845        pub fn whiteout_mdx_MdxNode_set_parentId(self_: *mut whiteout_MdxNode, value: u32);
8846        pub fn whiteout_mdx_MdxNode_get_flags(self_: *mut whiteout_MdxNode) -> i32;
8847        pub fn whiteout_mdx_MdxNode_set_flags(self_: *mut whiteout_MdxNode, value: i32);
8848        pub fn whiteout_mdx_MdxNode_get_type(self_: *mut whiteout_MdxNode) -> i32;
8849        pub fn whiteout_mdx_MdxNode_set_type(self_: *mut whiteout_MdxNode, value: i32);
8850        pub fn whiteout_mdx_MdxNode_get_nodeFamilyId(self_: *mut whiteout_MdxNode) -> u32;
8851        pub fn whiteout_mdx_MdxNode_set_nodeFamilyId(self_: *mut whiteout_MdxNode, value: u32);
8852        pub fn whiteout_mdx_MdxNode_get_translationTracks(
8853            self_: *mut whiteout_MdxNode,
8854        ) -> *mut whiteout_MdxTrackVector3f;
8855        pub fn whiteout_mdx_MdxNode_set_translationTracks(
8856            self_: *mut whiteout_MdxNode,
8857            value: *const whiteout_MdxTrackVector3f,
8858        );
8859        pub fn whiteout_mdx_MdxNode_get_rotationTracks(
8860            self_: *mut whiteout_MdxNode,
8861        ) -> *mut whiteout_MdxTrackQuaternion;
8862        pub fn whiteout_mdx_MdxNode_set_rotationTracks(
8863            self_: *mut whiteout_MdxNode,
8864            value: *const whiteout_MdxTrackQuaternion,
8865        );
8866        pub fn whiteout_mdx_MdxNode_get_scalingTracks(
8867            self_: *mut whiteout_MdxNode,
8868        ) -> *mut whiteout_MdxTrackVector3f;
8869        pub fn whiteout_mdx_MdxNode_set_scalingTracks(
8870            self_: *mut whiteout_MdxNode,
8871            value: *const whiteout_MdxTrackVector3f,
8872        );
8873        // SoundEmitter
8874        pub fn whiteout_mdx_MdxSoundEmitter_new() -> *mut whiteout_MdxSoundEmitter;
8875        pub fn whiteout_mdx_MdxSoundEmitter_delete(self_: *mut whiteout_MdxSoundEmitter);
8876        pub fn whiteout_mdx_MdxSoundEmitter_get_node(
8877            self_: *mut whiteout_MdxSoundEmitter,
8878        ) -> *mut whiteout_MdxNode;
8879        pub fn whiteout_mdx_MdxSoundEmitter_set_node(
8880            self_: *mut whiteout_MdxSoundEmitter,
8881            value: *const whiteout_MdxNode,
8882        );
8883        pub fn whiteout_mdx_MdxSoundEmitter_get_soundTrack(
8884            self_: *mut whiteout_MdxSoundEmitter,
8885        ) -> *mut whiteout_MdxTrackU32;
8886        pub fn whiteout_mdx_MdxSoundEmitter_set_soundTrack(
8887            self_: *mut whiteout_MdxSoundEmitter,
8888            value: *const whiteout_MdxTrackU32,
8889        );
8890        // Layer
8891        pub fn whiteout_mdx_MdxLayer_new() -> *mut whiteout_MdxLayer;
8892        pub fn whiteout_mdx_MdxLayer_delete(self_: *mut whiteout_MdxLayer);
8893        pub fn whiteout_mdx_MdxLayer_get_filterMode(self_: *mut whiteout_MdxLayer) -> i32;
8894        pub fn whiteout_mdx_MdxLayer_set_filterMode(self_: *mut whiteout_MdxLayer, value: i32);
8895        pub fn whiteout_mdx_MdxLayer_get_shadingFlags(self_: *mut whiteout_MdxLayer) -> i32;
8896        pub fn whiteout_mdx_MdxLayer_set_shadingFlags(self_: *mut whiteout_MdxLayer, value: i32);
8897        pub fn whiteout_mdx_MdxLayer_get_textureId(self_: *mut whiteout_MdxLayer) -> u32;
8898        pub fn whiteout_mdx_MdxLayer_set_textureId(self_: *mut whiteout_MdxLayer, value: u32);
8899        pub fn whiteout_mdx_MdxLayer_get_textureAnimationId(self_: *mut whiteout_MdxLayer) -> u32;
8900        pub fn whiteout_mdx_MdxLayer_set_textureAnimationId(
8901            self_: *mut whiteout_MdxLayer,
8902            value: u32,
8903        );
8904        pub fn whiteout_mdx_MdxLayer_get_coordId(self_: *mut whiteout_MdxLayer) -> u32;
8905        pub fn whiteout_mdx_MdxLayer_set_coordId(self_: *mut whiteout_MdxLayer, value: u32);
8906        pub fn whiteout_mdx_MdxLayer_get_alpha(self_: *mut whiteout_MdxLayer) -> f32;
8907        pub fn whiteout_mdx_MdxLayer_set_alpha(self_: *mut whiteout_MdxLayer, value: f32);
8908        pub fn whiteout_mdx_MdxLayer_get_emissiveGain(self_: *mut whiteout_MdxLayer) -> f32;
8909        pub fn whiteout_mdx_MdxLayer_set_emissiveGain(self_: *mut whiteout_MdxLayer, value: f32);
8910        pub fn whiteout_mdx_MdxLayer_get_fresnelColor(
8911            self_: *mut whiteout_MdxLayer,
8912        ) -> *mut core::ffi::c_void;
8913        pub fn whiteout_mdx_MdxLayer_set_fresnelColor(
8914            self_: *mut whiteout_MdxLayer,
8915            value: *const core::ffi::c_void,
8916        );
8917        pub fn whiteout_mdx_MdxLayer_get_fresnelOpacity(self_: *mut whiteout_MdxLayer) -> f32;
8918        pub fn whiteout_mdx_MdxLayer_set_fresnelOpacity(self_: *mut whiteout_MdxLayer, value: f32);
8919        pub fn whiteout_mdx_MdxLayer_get_fresnelTeamColor(self_: *mut whiteout_MdxLayer) -> f32;
8920        pub fn whiteout_mdx_MdxLayer_set_fresnelTeamColor(
8921            self_: *mut whiteout_MdxLayer,
8922            value: f32,
8923        );
8924        pub fn whiteout_mdx_MdxLayer_get_shader(self_: *mut whiteout_MdxLayer) -> i32;
8925        pub fn whiteout_mdx_MdxLayer_set_shader(self_: *mut whiteout_MdxLayer, value: i32);
8926        pub fn whiteout_mdx_MdxLayer_get_isHd(self_: *mut whiteout_MdxLayer) -> i32;
8927        pub fn whiteout_mdx_MdxLayer_set_isHd(self_: *mut whiteout_MdxLayer, value: i32);
8928        pub fn whiteout_mdx_MdxLayer_get_subTextures_count(self_: *mut whiteout_MdxLayer) -> usize;
8929        pub fn whiteout_mdx_MdxLayer_resize_subTextures(
8930            self_: *mut whiteout_MdxLayer,
8931            count: usize,
8932        );
8933        pub fn whiteout_mdx_MdxLayer_get_subTextures_at(
8934            self_: *mut whiteout_MdxLayer,
8935            index: usize,
8936        ) -> *mut whiteout_MdxLayerSubTexture;
8937        pub fn whiteout_mdx_MdxLayer_get_textureIdTracks(
8938            self_: *mut whiteout_MdxLayer,
8939        ) -> *mut whiteout_MdxTrackU32;
8940        pub fn whiteout_mdx_MdxLayer_set_textureIdTracks(
8941            self_: *mut whiteout_MdxLayer,
8942            value: *const whiteout_MdxTrackU32,
8943        );
8944        pub fn whiteout_mdx_MdxLayer_get_alphaTracks(
8945            self_: *mut whiteout_MdxLayer,
8946        ) -> *mut whiteout_MdxTrackF32;
8947        pub fn whiteout_mdx_MdxLayer_set_alphaTracks(
8948            self_: *mut whiteout_MdxLayer,
8949            value: *const whiteout_MdxTrackF32,
8950        );
8951        pub fn whiteout_mdx_MdxLayer_get_emissiveGainTracks(
8952            self_: *mut whiteout_MdxLayer,
8953        ) -> *mut whiteout_MdxTrackF32;
8954        pub fn whiteout_mdx_MdxLayer_set_emissiveGainTracks(
8955            self_: *mut whiteout_MdxLayer,
8956            value: *const whiteout_MdxTrackF32,
8957        );
8958        pub fn whiteout_mdx_MdxLayer_get_fresnelColorTracks(
8959            self_: *mut whiteout_MdxLayer,
8960        ) -> *mut whiteout_MdxTrackVector3f;
8961        pub fn whiteout_mdx_MdxLayer_set_fresnelColorTracks(
8962            self_: *mut whiteout_MdxLayer,
8963            value: *const whiteout_MdxTrackVector3f,
8964        );
8965        pub fn whiteout_mdx_MdxLayer_get_fresnelAlphaTracks(
8966            self_: *mut whiteout_MdxLayer,
8967        ) -> *mut whiteout_MdxTrackF32;
8968        pub fn whiteout_mdx_MdxLayer_set_fresnelAlphaTracks(
8969            self_: *mut whiteout_MdxLayer,
8970            value: *const whiteout_MdxTrackF32,
8971        );
8972        pub fn whiteout_mdx_MdxLayer_get_fresnelTeamColorTracks(
8973            self_: *mut whiteout_MdxLayer,
8974        ) -> *mut whiteout_MdxTrackF32;
8975        pub fn whiteout_mdx_MdxLayer_set_fresnelTeamColorTracks(
8976            self_: *mut whiteout_MdxLayer,
8977            value: *const whiteout_MdxTrackF32,
8978        );
8979        // LayerSubTexture
8980        pub fn whiteout_mdx_MdxLayerSubTexture_new() -> *mut whiteout_MdxLayerSubTexture;
8981        pub fn whiteout_mdx_MdxLayerSubTexture_delete(self_: *mut whiteout_MdxLayerSubTexture);
8982        pub fn whiteout_mdx_MdxLayerSubTexture_get_textureId(
8983            self_: *mut whiteout_MdxLayerSubTexture,
8984        ) -> u32;
8985        pub fn whiteout_mdx_MdxLayerSubTexture_set_textureId(
8986            self_: *mut whiteout_MdxLayerSubTexture,
8987            value: u32,
8988        );
8989        pub fn whiteout_mdx_MdxLayerSubTexture_get_slot(
8990            self_: *mut whiteout_MdxLayerSubTexture,
8991        ) -> i32;
8992        pub fn whiteout_mdx_MdxLayerSubTexture_set_slot(
8993            self_: *mut whiteout_MdxLayerSubTexture,
8994            value: i32,
8995        );
8996        pub fn whiteout_mdx_MdxLayerSubTexture_get_tracks(
8997            self_: *mut whiteout_MdxLayerSubTexture,
8998        ) -> *mut whiteout_MdxTrackU32;
8999        pub fn whiteout_mdx_MdxLayerSubTexture_set_tracks(
9000            self_: *mut whiteout_MdxLayerSubTexture,
9001            value: *const whiteout_MdxTrackU32,
9002        );
9003        // Material
9004        pub fn whiteout_mdx_MdxMaterial_new() -> *mut whiteout_MdxMaterial;
9005        pub fn whiteout_mdx_MdxMaterial_delete(self_: *mut whiteout_MdxMaterial);
9006        pub fn whiteout_mdx_MdxMaterial_get_priorityPlane(self_: *mut whiteout_MdxMaterial) -> i32;
9007        pub fn whiteout_mdx_MdxMaterial_set_priorityPlane(
9008            self_: *mut whiteout_MdxMaterial,
9009            value: i32,
9010        );
9011        pub fn whiteout_mdx_MdxMaterial_get_flags(self_: *mut whiteout_MdxMaterial) -> i32;
9012        pub fn whiteout_mdx_MdxMaterial_set_flags(self_: *mut whiteout_MdxMaterial, value: i32);
9013        pub fn whiteout_mdx_MdxMaterial_get_shader(self_: *mut whiteout_MdxMaterial) -> RawCString;
9014        pub fn whiteout_mdx_MdxMaterial_set_shader(
9015            self_: *mut whiteout_MdxMaterial,
9016            value: *const core::ffi::c_char,
9017        );
9018        pub fn whiteout_mdx_MdxMaterial_get_layers_count(self_: *mut whiteout_MdxMaterial)
9019            -> usize;
9020        pub fn whiteout_mdx_MdxMaterial_resize_layers(
9021            self_: *mut whiteout_MdxMaterial,
9022            count: usize,
9023        );
9024        pub fn whiteout_mdx_MdxMaterial_get_layers_at(
9025            self_: *mut whiteout_MdxMaterial,
9026            index: usize,
9027        ) -> *mut whiteout_MdxLayer;
9028        // TextureAnimation
9029        pub fn whiteout_mdx_MdxTextureAnimation_new() -> *mut whiteout_MdxTextureAnimation;
9030        pub fn whiteout_mdx_MdxTextureAnimation_delete(self_: *mut whiteout_MdxTextureAnimation);
9031        pub fn whiteout_mdx_MdxTextureAnimation_get_translationTracks(
9032            self_: *mut whiteout_MdxTextureAnimation,
9033        ) -> *mut whiteout_MdxTrackVector3f;
9034        pub fn whiteout_mdx_MdxTextureAnimation_set_translationTracks(
9035            self_: *mut whiteout_MdxTextureAnimation,
9036            value: *const whiteout_MdxTrackVector3f,
9037        );
9038        pub fn whiteout_mdx_MdxTextureAnimation_get_rotationTracks(
9039            self_: *mut whiteout_MdxTextureAnimation,
9040        ) -> *mut whiteout_MdxTrackQuaternion;
9041        pub fn whiteout_mdx_MdxTextureAnimation_set_rotationTracks(
9042            self_: *mut whiteout_MdxTextureAnimation,
9043            value: *const whiteout_MdxTrackQuaternion,
9044        );
9045        pub fn whiteout_mdx_MdxTextureAnimation_get_scalingTracks(
9046            self_: *mut whiteout_MdxTextureAnimation,
9047        ) -> *mut whiteout_MdxTrackVector3f;
9048        pub fn whiteout_mdx_MdxTextureAnimation_set_scalingTracks(
9049            self_: *mut whiteout_MdxTextureAnimation,
9050            value: *const whiteout_MdxTrackVector3f,
9051        );
9052        // Geoset
9053        pub fn whiteout_mdx_MdxGeoset_new() -> *mut whiteout_MdxGeoset;
9054        pub fn whiteout_mdx_MdxGeoset_delete(self_: *mut whiteout_MdxGeoset);
9055        pub fn whiteout_mdx_MdxGeoset_get_vertexPositions_count(
9056            self_: *mut whiteout_MdxGeoset,
9057        ) -> usize;
9058        pub fn whiteout_mdx_MdxGeoset_resize_vertexPositions(
9059            self_: *mut whiteout_MdxGeoset,
9060            count: usize,
9061        );
9062        pub fn whiteout_mdx_MdxGeoset_get_vertexPositions_data(
9063            self_: *mut whiteout_MdxGeoset,
9064        ) -> *const f32;
9065        pub fn whiteout_mdx_MdxGeoset_assign_vertexPositions(
9066            self_: *mut whiteout_MdxGeoset,
9067            data: *const f32,
9068            count: usize,
9069        );
9070        pub fn whiteout_mdx_MdxGeoset_get_vertexNormals_count(
9071            self_: *mut whiteout_MdxGeoset,
9072        ) -> usize;
9073        pub fn whiteout_mdx_MdxGeoset_resize_vertexNormals(
9074            self_: *mut whiteout_MdxGeoset,
9075            count: usize,
9076        );
9077        pub fn whiteout_mdx_MdxGeoset_get_vertexNormals_data(
9078            self_: *mut whiteout_MdxGeoset,
9079        ) -> *const f32;
9080        pub fn whiteout_mdx_MdxGeoset_assign_vertexNormals(
9081            self_: *mut whiteout_MdxGeoset,
9082            data: *const f32,
9083            count: usize,
9084        );
9085        pub fn whiteout_mdx_MdxGeoset_get_faceTypeGroups_count(
9086            self_: *mut whiteout_MdxGeoset,
9087        ) -> usize;
9088        pub fn whiteout_mdx_MdxGeoset_resize_faceTypeGroups(
9089            self_: *mut whiteout_MdxGeoset,
9090            count: usize,
9091        );
9092        pub fn whiteout_mdx_MdxGeoset_get_faceTypeGroups_data(
9093            self_: *mut whiteout_MdxGeoset,
9094        ) -> *const u32;
9095        pub fn whiteout_mdx_MdxGeoset_assign_faceTypeGroups(
9096            self_: *mut whiteout_MdxGeoset,
9097            data: *const u32,
9098            count: usize,
9099        );
9100        pub fn whiteout_mdx_MdxGeoset_get_faceGroups_count(self_: *mut whiteout_MdxGeoset)
9101            -> usize;
9102        pub fn whiteout_mdx_MdxGeoset_resize_faceGroups(
9103            self_: *mut whiteout_MdxGeoset,
9104            count: usize,
9105        );
9106        pub fn whiteout_mdx_MdxGeoset_get_faceGroups_data(
9107            self_: *mut whiteout_MdxGeoset,
9108        ) -> *const u32;
9109        pub fn whiteout_mdx_MdxGeoset_assign_faceGroups(
9110            self_: *mut whiteout_MdxGeoset,
9111            data: *const u32,
9112            count: usize,
9113        );
9114        pub fn whiteout_mdx_MdxGeoset_get_faces_count(self_: *mut whiteout_MdxGeoset) -> usize;
9115        pub fn whiteout_mdx_MdxGeoset_resize_faces(self_: *mut whiteout_MdxGeoset, count: usize);
9116        pub fn whiteout_mdx_MdxGeoset_get_faces_data(self_: *mut whiteout_MdxGeoset) -> *const u16;
9117        pub fn whiteout_mdx_MdxGeoset_assign_faces(
9118            self_: *mut whiteout_MdxGeoset,
9119            data: *const u16,
9120            count: usize,
9121        );
9122        pub fn whiteout_mdx_MdxGeoset_get_vertexGroups_count(
9123            self_: *mut whiteout_MdxGeoset,
9124        ) -> usize;
9125        pub fn whiteout_mdx_MdxGeoset_resize_vertexGroups(
9126            self_: *mut whiteout_MdxGeoset,
9127            count: usize,
9128        );
9129        pub fn whiteout_mdx_MdxGeoset_get_vertexGroups_data(
9130            self_: *mut whiteout_MdxGeoset,
9131        ) -> *const u8;
9132        pub fn whiteout_mdx_MdxGeoset_assign_vertexGroups(
9133            self_: *mut whiteout_MdxGeoset,
9134            data: *const u8,
9135            count: usize,
9136        );
9137        pub fn whiteout_mdx_MdxGeoset_get_matrixGroups_count(
9138            self_: *mut whiteout_MdxGeoset,
9139        ) -> usize;
9140        pub fn whiteout_mdx_MdxGeoset_resize_matrixGroups(
9141            self_: *mut whiteout_MdxGeoset,
9142            count: usize,
9143        );
9144        pub fn whiteout_mdx_MdxGeoset_get_matrixGroups_data(
9145            self_: *mut whiteout_MdxGeoset,
9146        ) -> *const u32;
9147        pub fn whiteout_mdx_MdxGeoset_assign_matrixGroups(
9148            self_: *mut whiteout_MdxGeoset,
9149            data: *const u32,
9150            count: usize,
9151        );
9152        pub fn whiteout_mdx_MdxGeoset_get_matrixIndices_count(
9153            self_: *mut whiteout_MdxGeoset,
9154        ) -> usize;
9155        pub fn whiteout_mdx_MdxGeoset_resize_matrixIndices(
9156            self_: *mut whiteout_MdxGeoset,
9157            count: usize,
9158        );
9159        pub fn whiteout_mdx_MdxGeoset_get_matrixIndices_data(
9160            self_: *mut whiteout_MdxGeoset,
9161        ) -> *const u32;
9162        pub fn whiteout_mdx_MdxGeoset_assign_matrixIndices(
9163            self_: *mut whiteout_MdxGeoset,
9164            data: *const u32,
9165            count: usize,
9166        );
9167        pub fn whiteout_mdx_MdxGeoset_get_materialId(self_: *mut whiteout_MdxGeoset) -> u32;
9168        pub fn whiteout_mdx_MdxGeoset_set_materialId(self_: *mut whiteout_MdxGeoset, value: u32);
9169        pub fn whiteout_mdx_MdxGeoset_get_selectionGroup(self_: *mut whiteout_MdxGeoset) -> u32;
9170        pub fn whiteout_mdx_MdxGeoset_set_selectionGroup(
9171            self_: *mut whiteout_MdxGeoset,
9172            value: u32,
9173        );
9174        pub fn whiteout_mdx_MdxGeoset_get_selectionFlags(self_: *mut whiteout_MdxGeoset) -> u32;
9175        pub fn whiteout_mdx_MdxGeoset_set_selectionFlags(
9176            self_: *mut whiteout_MdxGeoset,
9177            value: u32,
9178        );
9179        pub fn whiteout_mdx_MdxGeoset_get_lod(self_: *mut whiteout_MdxGeoset) -> u32;
9180        pub fn whiteout_mdx_MdxGeoset_set_lod(self_: *mut whiteout_MdxGeoset, value: u32);
9181        pub fn whiteout_mdx_MdxGeoset_get_lodName(self_: *mut whiteout_MdxGeoset) -> RawCString;
9182        pub fn whiteout_mdx_MdxGeoset_set_lodName(
9183            self_: *mut whiteout_MdxGeoset,
9184            value: *const core::ffi::c_char,
9185        );
9186        pub fn whiteout_mdx_MdxGeoset_get_extent(
9187            self_: *mut whiteout_MdxGeoset,
9188        ) -> *mut whiteout_MdxExtent;
9189        pub fn whiteout_mdx_MdxGeoset_set_extent(
9190            self_: *mut whiteout_MdxGeoset,
9191            value: *const whiteout_MdxExtent,
9192        );
9193        pub fn whiteout_mdx_MdxGeoset_get_sequenceExtents_count(
9194            self_: *mut whiteout_MdxGeoset,
9195        ) -> usize;
9196        pub fn whiteout_mdx_MdxGeoset_resize_sequenceExtents(
9197            self_: *mut whiteout_MdxGeoset,
9198            count: usize,
9199        );
9200        pub fn whiteout_mdx_MdxGeoset_get_sequenceExtents_at(
9201            self_: *mut whiteout_MdxGeoset,
9202            index: usize,
9203        ) -> *mut whiteout_MdxExtent;
9204        pub fn whiteout_mdx_MdxGeoset_get_tangents_count(self_: *mut whiteout_MdxGeoset) -> usize;
9205        pub fn whiteout_mdx_MdxGeoset_resize_tangents(self_: *mut whiteout_MdxGeoset, count: usize);
9206        pub fn whiteout_mdx_MdxGeoset_get_tangents_data(
9207            self_: *mut whiteout_MdxGeoset,
9208        ) -> *const f32;
9209        pub fn whiteout_mdx_MdxGeoset_assign_tangents(
9210            self_: *mut whiteout_MdxGeoset,
9211            data: *const f32,
9212            count: usize,
9213        );
9214        pub fn whiteout_mdx_MdxGeoset_get_skinData_count(self_: *mut whiteout_MdxGeoset) -> usize;
9215        pub fn whiteout_mdx_MdxGeoset_resize_skinData(self_: *mut whiteout_MdxGeoset, count: usize);
9216        pub fn whiteout_mdx_MdxGeoset_get_skinData_data(
9217            self_: *mut whiteout_MdxGeoset,
9218        ) -> *const u8;
9219        pub fn whiteout_mdx_MdxGeoset_assign_skinData(
9220            self_: *mut whiteout_MdxGeoset,
9221            data: *const u8,
9222            count: usize,
9223        );
9224        pub fn whiteout_mdx_MdxGeoset_get_textureCoordinateSets_count(
9225            self_: *mut whiteout_MdxGeoset,
9226        ) -> usize;
9227        pub fn whiteout_mdx_MdxGeoset_get_textureCoordinateSets_inner_count(
9228            self_: *mut whiteout_MdxGeoset,
9229            outer: usize,
9230        ) -> usize;
9231        pub fn whiteout_mdx_MdxGeoset_resize_textureCoordinateSets(
9232            self_: *mut whiteout_MdxGeoset,
9233            count: usize,
9234        );
9235        pub fn whiteout_mdx_MdxGeoset_resize_textureCoordinateSets_inner(
9236            self_: *mut whiteout_MdxGeoset,
9237            outer: usize,
9238            count: usize,
9239        );
9240        pub fn whiteout_mdx_MdxGeoset_get_textureCoordinateSets_inner_data(
9241            self_: *mut whiteout_MdxGeoset,
9242            outer: usize,
9243        ) -> *const f32;
9244        pub fn whiteout_mdx_MdxGeoset_assign_textureCoordinateSets_inner(
9245            self_: *mut whiteout_MdxGeoset,
9246            outer: usize,
9247            data: *const f32,
9248            count: usize,
9249        );
9250        // GeosetAnimation
9251        pub fn whiteout_mdx_MdxGeosetAnimation_new() -> *mut whiteout_MdxGeosetAnimation;
9252        pub fn whiteout_mdx_MdxGeosetAnimation_delete(self_: *mut whiteout_MdxGeosetAnimation);
9253        pub fn whiteout_mdx_MdxGeosetAnimation_get_alpha(
9254            self_: *mut whiteout_MdxGeosetAnimation,
9255        ) -> f32;
9256        pub fn whiteout_mdx_MdxGeosetAnimation_set_alpha(
9257            self_: *mut whiteout_MdxGeosetAnimation,
9258            value: f32,
9259        );
9260        pub fn whiteout_mdx_MdxGeosetAnimation_get_flags(
9261            self_: *mut whiteout_MdxGeosetAnimation,
9262        ) -> i32;
9263        pub fn whiteout_mdx_MdxGeosetAnimation_set_flags(
9264            self_: *mut whiteout_MdxGeosetAnimation,
9265            value: i32,
9266        );
9267        pub fn whiteout_mdx_MdxGeosetAnimation_get_color(
9268            self_: *mut whiteout_MdxGeosetAnimation,
9269        ) -> *mut core::ffi::c_void;
9270        pub fn whiteout_mdx_MdxGeosetAnimation_set_color(
9271            self_: *mut whiteout_MdxGeosetAnimation,
9272            value: *const core::ffi::c_void,
9273        );
9274        pub fn whiteout_mdx_MdxGeosetAnimation_get_geosetId(
9275            self_: *mut whiteout_MdxGeosetAnimation,
9276        ) -> u32;
9277        pub fn whiteout_mdx_MdxGeosetAnimation_set_geosetId(
9278            self_: *mut whiteout_MdxGeosetAnimation,
9279            value: u32,
9280        );
9281        pub fn whiteout_mdx_MdxGeosetAnimation_get_alphaTracks(
9282            self_: *mut whiteout_MdxGeosetAnimation,
9283        ) -> *mut whiteout_MdxTrackF32;
9284        pub fn whiteout_mdx_MdxGeosetAnimation_set_alphaTracks(
9285            self_: *mut whiteout_MdxGeosetAnimation,
9286            value: *const whiteout_MdxTrackF32,
9287        );
9288        pub fn whiteout_mdx_MdxGeosetAnimation_get_colorTracks(
9289            self_: *mut whiteout_MdxGeosetAnimation,
9290        ) -> *mut whiteout_MdxTrackVector3f;
9291        pub fn whiteout_mdx_MdxGeosetAnimation_set_colorTracks(
9292            self_: *mut whiteout_MdxGeosetAnimation,
9293            value: *const whiteout_MdxTrackVector3f,
9294        );
9295        // Bone
9296        pub fn whiteout_mdx_MdxBone_new() -> *mut whiteout_MdxBone;
9297        pub fn whiteout_mdx_MdxBone_delete(self_: *mut whiteout_MdxBone);
9298        pub fn whiteout_mdx_MdxBone_get_node(self_: *mut whiteout_MdxBone)
9299            -> *mut whiteout_MdxNode;
9300        pub fn whiteout_mdx_MdxBone_set_node(
9301            self_: *mut whiteout_MdxBone,
9302            value: *const whiteout_MdxNode,
9303        );
9304        pub fn whiteout_mdx_MdxBone_get_geosetId(self_: *mut whiteout_MdxBone) -> u32;
9305        pub fn whiteout_mdx_MdxBone_set_geosetId(self_: *mut whiteout_MdxBone, value: u32);
9306        pub fn whiteout_mdx_MdxBone_get_geosetAnimationId(self_: *mut whiteout_MdxBone) -> u32;
9307        pub fn whiteout_mdx_MdxBone_set_geosetAnimationId(self_: *mut whiteout_MdxBone, value: u32);
9308        // Light
9309        pub fn whiteout_mdx_MdxLight_new() -> *mut whiteout_MdxLight;
9310        pub fn whiteout_mdx_MdxLight_delete(self_: *mut whiteout_MdxLight);
9311        pub fn whiteout_mdx_MdxLight_get_node(
9312            self_: *mut whiteout_MdxLight,
9313        ) -> *mut whiteout_MdxNode;
9314        pub fn whiteout_mdx_MdxLight_set_node(
9315            self_: *mut whiteout_MdxLight,
9316            value: *const whiteout_MdxNode,
9317        );
9318        pub fn whiteout_mdx_MdxLight_get_type(self_: *mut whiteout_MdxLight) -> i32;
9319        pub fn whiteout_mdx_MdxLight_set_type(self_: *mut whiteout_MdxLight, value: i32);
9320        pub fn whiteout_mdx_MdxLight_get_attenuationStart(self_: *mut whiteout_MdxLight) -> f32;
9321        pub fn whiteout_mdx_MdxLight_set_attenuationStart(
9322            self_: *mut whiteout_MdxLight,
9323            value: f32,
9324        );
9325        pub fn whiteout_mdx_MdxLight_get_attenuationEnd(self_: *mut whiteout_MdxLight) -> f32;
9326        pub fn whiteout_mdx_MdxLight_set_attenuationEnd(self_: *mut whiteout_MdxLight, value: f32);
9327        pub fn whiteout_mdx_MdxLight_get_color(
9328            self_: *mut whiteout_MdxLight,
9329        ) -> *mut core::ffi::c_void;
9330        pub fn whiteout_mdx_MdxLight_set_color(
9331            self_: *mut whiteout_MdxLight,
9332            value: *const core::ffi::c_void,
9333        );
9334        pub fn whiteout_mdx_MdxLight_get_intensity(self_: *mut whiteout_MdxLight) -> f32;
9335        pub fn whiteout_mdx_MdxLight_set_intensity(self_: *mut whiteout_MdxLight, value: f32);
9336        pub fn whiteout_mdx_MdxLight_get_ambientColor(
9337            self_: *mut whiteout_MdxLight,
9338        ) -> *mut core::ffi::c_void;
9339        pub fn whiteout_mdx_MdxLight_set_ambientColor(
9340            self_: *mut whiteout_MdxLight,
9341            value: *const core::ffi::c_void,
9342        );
9343        pub fn whiteout_mdx_MdxLight_get_ambientIntensity(self_: *mut whiteout_MdxLight) -> f32;
9344        pub fn whiteout_mdx_MdxLight_set_ambientIntensity(
9345            self_: *mut whiteout_MdxLight,
9346            value: f32,
9347        );
9348        pub fn whiteout_mdx_MdxLight_get_shadowIntensity(self_: *mut whiteout_MdxLight) -> f32;
9349        pub fn whiteout_mdx_MdxLight_set_shadowIntensity(self_: *mut whiteout_MdxLight, value: f32);
9350        pub fn whiteout_mdx_MdxLight_get_attenuationStartTracks(
9351            self_: *mut whiteout_MdxLight,
9352        ) -> *mut whiteout_MdxTrackF32;
9353        pub fn whiteout_mdx_MdxLight_set_attenuationStartTracks(
9354            self_: *mut whiteout_MdxLight,
9355            value: *const whiteout_MdxTrackF32,
9356        );
9357        pub fn whiteout_mdx_MdxLight_get_attenuationEndTracks(
9358            self_: *mut whiteout_MdxLight,
9359        ) -> *mut whiteout_MdxTrackF32;
9360        pub fn whiteout_mdx_MdxLight_set_attenuationEndTracks(
9361            self_: *mut whiteout_MdxLight,
9362            value: *const whiteout_MdxTrackF32,
9363        );
9364        pub fn whiteout_mdx_MdxLight_get_colorTracks(
9365            self_: *mut whiteout_MdxLight,
9366        ) -> *mut whiteout_MdxTrackVector3f;
9367        pub fn whiteout_mdx_MdxLight_set_colorTracks(
9368            self_: *mut whiteout_MdxLight,
9369            value: *const whiteout_MdxTrackVector3f,
9370        );
9371        pub fn whiteout_mdx_MdxLight_get_intensityTracks(
9372            self_: *mut whiteout_MdxLight,
9373        ) -> *mut whiteout_MdxTrackF32;
9374        pub fn whiteout_mdx_MdxLight_set_intensityTracks(
9375            self_: *mut whiteout_MdxLight,
9376            value: *const whiteout_MdxTrackF32,
9377        );
9378        pub fn whiteout_mdx_MdxLight_get_ambientIntensityTracks(
9379            self_: *mut whiteout_MdxLight,
9380        ) -> *mut whiteout_MdxTrackF32;
9381        pub fn whiteout_mdx_MdxLight_set_ambientIntensityTracks(
9382            self_: *mut whiteout_MdxLight,
9383            value: *const whiteout_MdxTrackF32,
9384        );
9385        pub fn whiteout_mdx_MdxLight_get_ambientColorTracks(
9386            self_: *mut whiteout_MdxLight,
9387        ) -> *mut whiteout_MdxTrackVector3f;
9388        pub fn whiteout_mdx_MdxLight_set_ambientColorTracks(
9389            self_: *mut whiteout_MdxLight,
9390            value: *const whiteout_MdxTrackVector3f,
9391        );
9392        pub fn whiteout_mdx_MdxLight_get_visibilityTracks(
9393            self_: *mut whiteout_MdxLight,
9394        ) -> *mut whiteout_MdxTrackF32;
9395        pub fn whiteout_mdx_MdxLight_set_visibilityTracks(
9396            self_: *mut whiteout_MdxLight,
9397            value: *const whiteout_MdxTrackF32,
9398        );
9399        pub fn whiteout_mdx_MdxLight_get_shadowIntensityTracks(
9400            self_: *mut whiteout_MdxLight,
9401        ) -> *mut whiteout_MdxTrackF32;
9402        pub fn whiteout_mdx_MdxLight_set_shadowIntensityTracks(
9403            self_: *mut whiteout_MdxLight,
9404            value: *const whiteout_MdxTrackF32,
9405        );
9406        // Helper
9407        pub fn whiteout_mdx_MdxHelper_new() -> *mut whiteout_MdxHelper;
9408        pub fn whiteout_mdx_MdxHelper_delete(self_: *mut whiteout_MdxHelper);
9409        pub fn whiteout_mdx_MdxHelper_get_node(
9410            self_: *mut whiteout_MdxHelper,
9411        ) -> *mut whiteout_MdxNode;
9412        pub fn whiteout_mdx_MdxHelper_set_node(
9413            self_: *mut whiteout_MdxHelper,
9414            value: *const whiteout_MdxNode,
9415        );
9416        // Attachment
9417        pub fn whiteout_mdx_MdxAttachment_new() -> *mut whiteout_MdxAttachment;
9418        pub fn whiteout_mdx_MdxAttachment_delete(self_: *mut whiteout_MdxAttachment);
9419        pub fn whiteout_mdx_MdxAttachment_get_node(
9420            self_: *mut whiteout_MdxAttachment,
9421        ) -> *mut whiteout_MdxNode;
9422        pub fn whiteout_mdx_MdxAttachment_set_node(
9423            self_: *mut whiteout_MdxAttachment,
9424            value: *const whiteout_MdxNode,
9425        );
9426        pub fn whiteout_mdx_MdxAttachment_get_path(
9427            self_: *mut whiteout_MdxAttachment,
9428        ) -> RawCString;
9429        pub fn whiteout_mdx_MdxAttachment_set_path(
9430            self_: *mut whiteout_MdxAttachment,
9431            value: *const core::ffi::c_char,
9432        );
9433        pub fn whiteout_mdx_MdxAttachment_get_attachmentId(
9434            self_: *mut whiteout_MdxAttachment,
9435        ) -> u32;
9436        pub fn whiteout_mdx_MdxAttachment_set_attachmentId(
9437            self_: *mut whiteout_MdxAttachment,
9438            value: u32,
9439        );
9440        pub fn whiteout_mdx_MdxAttachment_get_visibilityTracks(
9441            self_: *mut whiteout_MdxAttachment,
9442        ) -> *mut whiteout_MdxTrackF32;
9443        pub fn whiteout_mdx_MdxAttachment_set_visibilityTracks(
9444            self_: *mut whiteout_MdxAttachment,
9445            value: *const whiteout_MdxTrackF32,
9446        );
9447        // ParticleEmitter
9448        pub fn whiteout_mdx_MdxParticleEmitter_new() -> *mut whiteout_MdxParticleEmitter;
9449        pub fn whiteout_mdx_MdxParticleEmitter_delete(self_: *mut whiteout_MdxParticleEmitter);
9450        pub fn whiteout_mdx_MdxParticleEmitter_get_node(
9451            self_: *mut whiteout_MdxParticleEmitter,
9452        ) -> *mut whiteout_MdxNode;
9453        pub fn whiteout_mdx_MdxParticleEmitter_set_node(
9454            self_: *mut whiteout_MdxParticleEmitter,
9455            value: *const whiteout_MdxNode,
9456        );
9457        pub fn whiteout_mdx_MdxParticleEmitter_get_emissionRate(
9458            self_: *mut whiteout_MdxParticleEmitter,
9459        ) -> f32;
9460        pub fn whiteout_mdx_MdxParticleEmitter_set_emissionRate(
9461            self_: *mut whiteout_MdxParticleEmitter,
9462            value: f32,
9463        );
9464        pub fn whiteout_mdx_MdxParticleEmitter_get_gravity(
9465            self_: *mut whiteout_MdxParticleEmitter,
9466        ) -> f32;
9467        pub fn whiteout_mdx_MdxParticleEmitter_set_gravity(
9468            self_: *mut whiteout_MdxParticleEmitter,
9469            value: f32,
9470        );
9471        pub fn whiteout_mdx_MdxParticleEmitter_get_longitude(
9472            self_: *mut whiteout_MdxParticleEmitter,
9473        ) -> f32;
9474        pub fn whiteout_mdx_MdxParticleEmitter_set_longitude(
9475            self_: *mut whiteout_MdxParticleEmitter,
9476            value: f32,
9477        );
9478        pub fn whiteout_mdx_MdxParticleEmitter_get_latitude(
9479            self_: *mut whiteout_MdxParticleEmitter,
9480        ) -> f32;
9481        pub fn whiteout_mdx_MdxParticleEmitter_set_latitude(
9482            self_: *mut whiteout_MdxParticleEmitter,
9483            value: f32,
9484        );
9485        pub fn whiteout_mdx_MdxParticleEmitter_get_spawnModelFileName(
9486            self_: *mut whiteout_MdxParticleEmitter,
9487        ) -> RawCString;
9488        pub fn whiteout_mdx_MdxParticleEmitter_set_spawnModelFileName(
9489            self_: *mut whiteout_MdxParticleEmitter,
9490            value: *const core::ffi::c_char,
9491        );
9492        pub fn whiteout_mdx_MdxParticleEmitter_get_lifespan(
9493            self_: *mut whiteout_MdxParticleEmitter,
9494        ) -> f32;
9495        pub fn whiteout_mdx_MdxParticleEmitter_set_lifespan(
9496            self_: *mut whiteout_MdxParticleEmitter,
9497            value: f32,
9498        );
9499        pub fn whiteout_mdx_MdxParticleEmitter_get_initialVelocity(
9500            self_: *mut whiteout_MdxParticleEmitter,
9501        ) -> f32;
9502        pub fn whiteout_mdx_MdxParticleEmitter_set_initialVelocity(
9503            self_: *mut whiteout_MdxParticleEmitter,
9504            value: f32,
9505        );
9506        pub fn whiteout_mdx_MdxParticleEmitter_get_emissionRateTracks(
9507            self_: *mut whiteout_MdxParticleEmitter,
9508        ) -> *mut whiteout_MdxTrackF32;
9509        pub fn whiteout_mdx_MdxParticleEmitter_set_emissionRateTracks(
9510            self_: *mut whiteout_MdxParticleEmitter,
9511            value: *const whiteout_MdxTrackF32,
9512        );
9513        pub fn whiteout_mdx_MdxParticleEmitter_get_gravityTracks(
9514            self_: *mut whiteout_MdxParticleEmitter,
9515        ) -> *mut whiteout_MdxTrackF32;
9516        pub fn whiteout_mdx_MdxParticleEmitter_set_gravityTracks(
9517            self_: *mut whiteout_MdxParticleEmitter,
9518            value: *const whiteout_MdxTrackF32,
9519        );
9520        pub fn whiteout_mdx_MdxParticleEmitter_get_longitudeTracks(
9521            self_: *mut whiteout_MdxParticleEmitter,
9522        ) -> *mut whiteout_MdxTrackF32;
9523        pub fn whiteout_mdx_MdxParticleEmitter_set_longitudeTracks(
9524            self_: *mut whiteout_MdxParticleEmitter,
9525            value: *const whiteout_MdxTrackF32,
9526        );
9527        pub fn whiteout_mdx_MdxParticleEmitter_get_latitudeTracks(
9528            self_: *mut whiteout_MdxParticleEmitter,
9529        ) -> *mut whiteout_MdxTrackF32;
9530        pub fn whiteout_mdx_MdxParticleEmitter_set_latitudeTracks(
9531            self_: *mut whiteout_MdxParticleEmitter,
9532            value: *const whiteout_MdxTrackF32,
9533        );
9534        pub fn whiteout_mdx_MdxParticleEmitter_get_lifespanTracks(
9535            self_: *mut whiteout_MdxParticleEmitter,
9536        ) -> *mut whiteout_MdxTrackF32;
9537        pub fn whiteout_mdx_MdxParticleEmitter_set_lifespanTracks(
9538            self_: *mut whiteout_MdxParticleEmitter,
9539            value: *const whiteout_MdxTrackF32,
9540        );
9541        pub fn whiteout_mdx_MdxParticleEmitter_get_speedTracks(
9542            self_: *mut whiteout_MdxParticleEmitter,
9543        ) -> *mut whiteout_MdxTrackF32;
9544        pub fn whiteout_mdx_MdxParticleEmitter_set_speedTracks(
9545            self_: *mut whiteout_MdxParticleEmitter,
9546            value: *const whiteout_MdxTrackF32,
9547        );
9548        pub fn whiteout_mdx_MdxParticleEmitter_get_visibilityTracks(
9549            self_: *mut whiteout_MdxParticleEmitter,
9550        ) -> *mut whiteout_MdxTrackF32;
9551        pub fn whiteout_mdx_MdxParticleEmitter_set_visibilityTracks(
9552            self_: *mut whiteout_MdxParticleEmitter,
9553            value: *const whiteout_MdxTrackF32,
9554        );
9555        // ParticleEmitter2
9556        pub fn whiteout_mdx_MdxParticleEmitter2_new() -> *mut whiteout_MdxParticleEmitter2;
9557        pub fn whiteout_mdx_MdxParticleEmitter2_delete(self_: *mut whiteout_MdxParticleEmitter2);
9558        pub fn whiteout_mdx_MdxParticleEmitter2_get_node(
9559            self_: *mut whiteout_MdxParticleEmitter2,
9560        ) -> *mut whiteout_MdxNode;
9561        pub fn whiteout_mdx_MdxParticleEmitter2_set_node(
9562            self_: *mut whiteout_MdxParticleEmitter2,
9563            value: *const whiteout_MdxNode,
9564        );
9565        pub fn whiteout_mdx_MdxParticleEmitter2_get_speed(
9566            self_: *mut whiteout_MdxParticleEmitter2,
9567        ) -> f32;
9568        pub fn whiteout_mdx_MdxParticleEmitter2_set_speed(
9569            self_: *mut whiteout_MdxParticleEmitter2,
9570            value: f32,
9571        );
9572        pub fn whiteout_mdx_MdxParticleEmitter2_get_variation(
9573            self_: *mut whiteout_MdxParticleEmitter2,
9574        ) -> f32;
9575        pub fn whiteout_mdx_MdxParticleEmitter2_set_variation(
9576            self_: *mut whiteout_MdxParticleEmitter2,
9577            value: f32,
9578        );
9579        pub fn whiteout_mdx_MdxParticleEmitter2_get_latitude(
9580            self_: *mut whiteout_MdxParticleEmitter2,
9581        ) -> f32;
9582        pub fn whiteout_mdx_MdxParticleEmitter2_set_latitude(
9583            self_: *mut whiteout_MdxParticleEmitter2,
9584            value: f32,
9585        );
9586        pub fn whiteout_mdx_MdxParticleEmitter2_get_gravity(
9587            self_: *mut whiteout_MdxParticleEmitter2,
9588        ) -> f32;
9589        pub fn whiteout_mdx_MdxParticleEmitter2_set_gravity(
9590            self_: *mut whiteout_MdxParticleEmitter2,
9591            value: f32,
9592        );
9593        pub fn whiteout_mdx_MdxParticleEmitter2_get_lifespan(
9594            self_: *mut whiteout_MdxParticleEmitter2,
9595        ) -> f32;
9596        pub fn whiteout_mdx_MdxParticleEmitter2_set_lifespan(
9597            self_: *mut whiteout_MdxParticleEmitter2,
9598            value: f32,
9599        );
9600        pub fn whiteout_mdx_MdxParticleEmitter2_get_emissionRate(
9601            self_: *mut whiteout_MdxParticleEmitter2,
9602        ) -> f32;
9603        pub fn whiteout_mdx_MdxParticleEmitter2_set_emissionRate(
9604            self_: *mut whiteout_MdxParticleEmitter2,
9605            value: f32,
9606        );
9607        pub fn whiteout_mdx_MdxParticleEmitter2_get_length(
9608            self_: *mut whiteout_MdxParticleEmitter2,
9609        ) -> f32;
9610        pub fn whiteout_mdx_MdxParticleEmitter2_set_length(
9611            self_: *mut whiteout_MdxParticleEmitter2,
9612            value: f32,
9613        );
9614        pub fn whiteout_mdx_MdxParticleEmitter2_get_width(
9615            self_: *mut whiteout_MdxParticleEmitter2,
9616        ) -> f32;
9617        pub fn whiteout_mdx_MdxParticleEmitter2_set_width(
9618            self_: *mut whiteout_MdxParticleEmitter2,
9619            value: f32,
9620        );
9621        pub fn whiteout_mdx_MdxParticleEmitter2_get_filterMode(
9622            self_: *mut whiteout_MdxParticleEmitter2,
9623        ) -> u32;
9624        pub fn whiteout_mdx_MdxParticleEmitter2_set_filterMode(
9625            self_: *mut whiteout_MdxParticleEmitter2,
9626            value: u32,
9627        );
9628        pub fn whiteout_mdx_MdxParticleEmitter2_get_rows(
9629            self_: *mut whiteout_MdxParticleEmitter2,
9630        ) -> u32;
9631        pub fn whiteout_mdx_MdxParticleEmitter2_set_rows(
9632            self_: *mut whiteout_MdxParticleEmitter2,
9633            value: u32,
9634        );
9635        pub fn whiteout_mdx_MdxParticleEmitter2_get_columns(
9636            self_: *mut whiteout_MdxParticleEmitter2,
9637        ) -> u32;
9638        pub fn whiteout_mdx_MdxParticleEmitter2_set_columns(
9639            self_: *mut whiteout_MdxParticleEmitter2,
9640            value: u32,
9641        );
9642        pub fn whiteout_mdx_MdxParticleEmitter2_get_headOrTail(
9643            self_: *mut whiteout_MdxParticleEmitter2,
9644        ) -> u32;
9645        pub fn whiteout_mdx_MdxParticleEmitter2_set_headOrTail(
9646            self_: *mut whiteout_MdxParticleEmitter2,
9647            value: u32,
9648        );
9649        pub fn whiteout_mdx_MdxParticleEmitter2_get_tailLength(
9650            self_: *mut whiteout_MdxParticleEmitter2,
9651        ) -> f32;
9652        pub fn whiteout_mdx_MdxParticleEmitter2_set_tailLength(
9653            self_: *mut whiteout_MdxParticleEmitter2,
9654            value: f32,
9655        );
9656        pub fn whiteout_mdx_MdxParticleEmitter2_get_time(
9657            self_: *mut whiteout_MdxParticleEmitter2,
9658        ) -> f32;
9659        pub fn whiteout_mdx_MdxParticleEmitter2_set_time(
9660            self_: *mut whiteout_MdxParticleEmitter2,
9661            value: f32,
9662        );
9663        pub fn whiteout_mdx_MdxParticleEmitter2_segmentColor_size() -> usize;
9664        pub fn whiteout_mdx_MdxParticleEmitter2_get_segmentColor_at(
9665            self_: *mut whiteout_MdxParticleEmitter2,
9666            index: usize,
9667        ) -> *mut core::ffi::c_void;
9668        pub fn whiteout_mdx_MdxParticleEmitter2_segmentAlpha_size() -> usize;
9669        pub fn whiteout_mdx_MdxParticleEmitter2_get_segmentAlpha_at(
9670            self_: *mut whiteout_MdxParticleEmitter2,
9671            index: usize,
9672        ) -> u8;
9673        pub fn whiteout_mdx_MdxParticleEmitter2_set_segmentAlpha_at(
9674            self_: *mut whiteout_MdxParticleEmitter2,
9675            index: usize,
9676            value: u8,
9677        );
9678        pub fn whiteout_mdx_MdxParticleEmitter2_segmentScaling_size() -> usize;
9679        pub fn whiteout_mdx_MdxParticleEmitter2_get_segmentScaling_at(
9680            self_: *mut whiteout_MdxParticleEmitter2,
9681            index: usize,
9682        ) -> f32;
9683        pub fn whiteout_mdx_MdxParticleEmitter2_set_segmentScaling_at(
9684            self_: *mut whiteout_MdxParticleEmitter2,
9685            index: usize,
9686            value: f32,
9687        );
9688        pub fn whiteout_mdx_MdxParticleEmitter2_headInterval_size() -> usize;
9689        pub fn whiteout_mdx_MdxParticleEmitter2_get_headInterval_at(
9690            self_: *mut whiteout_MdxParticleEmitter2,
9691            index: usize,
9692        ) -> u32;
9693        pub fn whiteout_mdx_MdxParticleEmitter2_set_headInterval_at(
9694            self_: *mut whiteout_MdxParticleEmitter2,
9695            index: usize,
9696            value: u32,
9697        );
9698        pub fn whiteout_mdx_MdxParticleEmitter2_headDecayInterval_size() -> usize;
9699        pub fn whiteout_mdx_MdxParticleEmitter2_get_headDecayInterval_at(
9700            self_: *mut whiteout_MdxParticleEmitter2,
9701            index: usize,
9702        ) -> u32;
9703        pub fn whiteout_mdx_MdxParticleEmitter2_set_headDecayInterval_at(
9704            self_: *mut whiteout_MdxParticleEmitter2,
9705            index: usize,
9706            value: u32,
9707        );
9708        pub fn whiteout_mdx_MdxParticleEmitter2_tailInterval_size() -> usize;
9709        pub fn whiteout_mdx_MdxParticleEmitter2_get_tailInterval_at(
9710            self_: *mut whiteout_MdxParticleEmitter2,
9711            index: usize,
9712        ) -> u32;
9713        pub fn whiteout_mdx_MdxParticleEmitter2_set_tailInterval_at(
9714            self_: *mut whiteout_MdxParticleEmitter2,
9715            index: usize,
9716            value: u32,
9717        );
9718        pub fn whiteout_mdx_MdxParticleEmitter2_tailDecayInterval_size() -> usize;
9719        pub fn whiteout_mdx_MdxParticleEmitter2_get_tailDecayInterval_at(
9720            self_: *mut whiteout_MdxParticleEmitter2,
9721            index: usize,
9722        ) -> u32;
9723        pub fn whiteout_mdx_MdxParticleEmitter2_set_tailDecayInterval_at(
9724            self_: *mut whiteout_MdxParticleEmitter2,
9725            index: usize,
9726            value: u32,
9727        );
9728        pub fn whiteout_mdx_MdxParticleEmitter2_get_textureId(
9729            self_: *mut whiteout_MdxParticleEmitter2,
9730        ) -> u32;
9731        pub fn whiteout_mdx_MdxParticleEmitter2_set_textureId(
9732            self_: *mut whiteout_MdxParticleEmitter2,
9733            value: u32,
9734        );
9735        pub fn whiteout_mdx_MdxParticleEmitter2_get_squirt(
9736            self_: *mut whiteout_MdxParticleEmitter2,
9737        ) -> u32;
9738        pub fn whiteout_mdx_MdxParticleEmitter2_set_squirt(
9739            self_: *mut whiteout_MdxParticleEmitter2,
9740            value: u32,
9741        );
9742        pub fn whiteout_mdx_MdxParticleEmitter2_get_priorityPlane(
9743            self_: *mut whiteout_MdxParticleEmitter2,
9744        ) -> i32;
9745        pub fn whiteout_mdx_MdxParticleEmitter2_set_priorityPlane(
9746            self_: *mut whiteout_MdxParticleEmitter2,
9747            value: i32,
9748        );
9749        pub fn whiteout_mdx_MdxParticleEmitter2_get_replaceableId(
9750            self_: *mut whiteout_MdxParticleEmitter2,
9751        ) -> u32;
9752        pub fn whiteout_mdx_MdxParticleEmitter2_set_replaceableId(
9753            self_: *mut whiteout_MdxParticleEmitter2,
9754            value: u32,
9755        );
9756        pub fn whiteout_mdx_MdxParticleEmitter2_get_speedTracks(
9757            self_: *mut whiteout_MdxParticleEmitter2,
9758        ) -> *mut whiteout_MdxTrackF32;
9759        pub fn whiteout_mdx_MdxParticleEmitter2_set_speedTracks(
9760            self_: *mut whiteout_MdxParticleEmitter2,
9761            value: *const whiteout_MdxTrackF32,
9762        );
9763        pub fn whiteout_mdx_MdxParticleEmitter2_get_variationTracks(
9764            self_: *mut whiteout_MdxParticleEmitter2,
9765        ) -> *mut whiteout_MdxTrackF32;
9766        pub fn whiteout_mdx_MdxParticleEmitter2_set_variationTracks(
9767            self_: *mut whiteout_MdxParticleEmitter2,
9768            value: *const whiteout_MdxTrackF32,
9769        );
9770        pub fn whiteout_mdx_MdxParticleEmitter2_get_latitudeTracks(
9771            self_: *mut whiteout_MdxParticleEmitter2,
9772        ) -> *mut whiteout_MdxTrackF32;
9773        pub fn whiteout_mdx_MdxParticleEmitter2_set_latitudeTracks(
9774            self_: *mut whiteout_MdxParticleEmitter2,
9775            value: *const whiteout_MdxTrackF32,
9776        );
9777        pub fn whiteout_mdx_MdxParticleEmitter2_get_gravityTracks(
9778            self_: *mut whiteout_MdxParticleEmitter2,
9779        ) -> *mut whiteout_MdxTrackF32;
9780        pub fn whiteout_mdx_MdxParticleEmitter2_set_gravityTracks(
9781            self_: *mut whiteout_MdxParticleEmitter2,
9782            value: *const whiteout_MdxTrackF32,
9783        );
9784        pub fn whiteout_mdx_MdxParticleEmitter2_get_emissionRateTracks(
9785            self_: *mut whiteout_MdxParticleEmitter2,
9786        ) -> *mut whiteout_MdxTrackF32;
9787        pub fn whiteout_mdx_MdxParticleEmitter2_set_emissionRateTracks(
9788            self_: *mut whiteout_MdxParticleEmitter2,
9789            value: *const whiteout_MdxTrackF32,
9790        );
9791        pub fn whiteout_mdx_MdxParticleEmitter2_get_lengthTracks(
9792            self_: *mut whiteout_MdxParticleEmitter2,
9793        ) -> *mut whiteout_MdxTrackF32;
9794        pub fn whiteout_mdx_MdxParticleEmitter2_set_lengthTracks(
9795            self_: *mut whiteout_MdxParticleEmitter2,
9796            value: *const whiteout_MdxTrackF32,
9797        );
9798        pub fn whiteout_mdx_MdxParticleEmitter2_get_widthTracks(
9799            self_: *mut whiteout_MdxParticleEmitter2,
9800        ) -> *mut whiteout_MdxTrackF32;
9801        pub fn whiteout_mdx_MdxParticleEmitter2_set_widthTracks(
9802            self_: *mut whiteout_MdxParticleEmitter2,
9803            value: *const whiteout_MdxTrackF32,
9804        );
9805        pub fn whiteout_mdx_MdxParticleEmitter2_get_visibilityTracks(
9806            self_: *mut whiteout_MdxParticleEmitter2,
9807        ) -> *mut whiteout_MdxTrackF32;
9808        pub fn whiteout_mdx_MdxParticleEmitter2_set_visibilityTracks(
9809            self_: *mut whiteout_MdxParticleEmitter2,
9810            value: *const whiteout_MdxTrackF32,
9811        );
9812        // RibbonEmitter
9813        pub fn whiteout_mdx_MdxRibbonEmitter_new() -> *mut whiteout_MdxRibbonEmitter;
9814        pub fn whiteout_mdx_MdxRibbonEmitter_delete(self_: *mut whiteout_MdxRibbonEmitter);
9815        pub fn whiteout_mdx_MdxRibbonEmitter_get_node(
9816            self_: *mut whiteout_MdxRibbonEmitter,
9817        ) -> *mut whiteout_MdxNode;
9818        pub fn whiteout_mdx_MdxRibbonEmitter_set_node(
9819            self_: *mut whiteout_MdxRibbonEmitter,
9820            value: *const whiteout_MdxNode,
9821        );
9822        pub fn whiteout_mdx_MdxRibbonEmitter_get_heightAbove(
9823            self_: *mut whiteout_MdxRibbonEmitter,
9824        ) -> f32;
9825        pub fn whiteout_mdx_MdxRibbonEmitter_set_heightAbove(
9826            self_: *mut whiteout_MdxRibbonEmitter,
9827            value: f32,
9828        );
9829        pub fn whiteout_mdx_MdxRibbonEmitter_get_heightBelow(
9830            self_: *mut whiteout_MdxRibbonEmitter,
9831        ) -> f32;
9832        pub fn whiteout_mdx_MdxRibbonEmitter_set_heightBelow(
9833            self_: *mut whiteout_MdxRibbonEmitter,
9834            value: f32,
9835        );
9836        pub fn whiteout_mdx_MdxRibbonEmitter_get_alpha(
9837            self_: *mut whiteout_MdxRibbonEmitter,
9838        ) -> f32;
9839        pub fn whiteout_mdx_MdxRibbonEmitter_set_alpha(
9840            self_: *mut whiteout_MdxRibbonEmitter,
9841            value: f32,
9842        );
9843        pub fn whiteout_mdx_MdxRibbonEmitter_get_color(
9844            self_: *mut whiteout_MdxRibbonEmitter,
9845        ) -> *mut core::ffi::c_void;
9846        pub fn whiteout_mdx_MdxRibbonEmitter_set_color(
9847            self_: *mut whiteout_MdxRibbonEmitter,
9848            value: *const core::ffi::c_void,
9849        );
9850        pub fn whiteout_mdx_MdxRibbonEmitter_get_lifespan(
9851            self_: *mut whiteout_MdxRibbonEmitter,
9852        ) -> f32;
9853        pub fn whiteout_mdx_MdxRibbonEmitter_set_lifespan(
9854            self_: *mut whiteout_MdxRibbonEmitter,
9855            value: f32,
9856        );
9857        pub fn whiteout_mdx_MdxRibbonEmitter_get_textureSlot(
9858            self_: *mut whiteout_MdxRibbonEmitter,
9859        ) -> u32;
9860        pub fn whiteout_mdx_MdxRibbonEmitter_set_textureSlot(
9861            self_: *mut whiteout_MdxRibbonEmitter,
9862            value: u32,
9863        );
9864        pub fn whiteout_mdx_MdxRibbonEmitter_get_emissionRate(
9865            self_: *mut whiteout_MdxRibbonEmitter,
9866        ) -> u32;
9867        pub fn whiteout_mdx_MdxRibbonEmitter_set_emissionRate(
9868            self_: *mut whiteout_MdxRibbonEmitter,
9869            value: u32,
9870        );
9871        pub fn whiteout_mdx_MdxRibbonEmitter_get_rows(self_: *mut whiteout_MdxRibbonEmitter)
9872            -> u32;
9873        pub fn whiteout_mdx_MdxRibbonEmitter_set_rows(
9874            self_: *mut whiteout_MdxRibbonEmitter,
9875            value: u32,
9876        );
9877        pub fn whiteout_mdx_MdxRibbonEmitter_get_columns(
9878            self_: *mut whiteout_MdxRibbonEmitter,
9879        ) -> u32;
9880        pub fn whiteout_mdx_MdxRibbonEmitter_set_columns(
9881            self_: *mut whiteout_MdxRibbonEmitter,
9882            value: u32,
9883        );
9884        pub fn whiteout_mdx_MdxRibbonEmitter_get_materialId(
9885            self_: *mut whiteout_MdxRibbonEmitter,
9886        ) -> u32;
9887        pub fn whiteout_mdx_MdxRibbonEmitter_set_materialId(
9888            self_: *mut whiteout_MdxRibbonEmitter,
9889            value: u32,
9890        );
9891        pub fn whiteout_mdx_MdxRibbonEmitter_get_gravity(
9892            self_: *mut whiteout_MdxRibbonEmitter,
9893        ) -> f32;
9894        pub fn whiteout_mdx_MdxRibbonEmitter_set_gravity(
9895            self_: *mut whiteout_MdxRibbonEmitter,
9896            value: f32,
9897        );
9898        pub fn whiteout_mdx_MdxRibbonEmitter_get_heightAboveTracks(
9899            self_: *mut whiteout_MdxRibbonEmitter,
9900        ) -> *mut whiteout_MdxTrackF32;
9901        pub fn whiteout_mdx_MdxRibbonEmitter_set_heightAboveTracks(
9902            self_: *mut whiteout_MdxRibbonEmitter,
9903            value: *const whiteout_MdxTrackF32,
9904        );
9905        pub fn whiteout_mdx_MdxRibbonEmitter_get_heightBelowTracks(
9906            self_: *mut whiteout_MdxRibbonEmitter,
9907        ) -> *mut whiteout_MdxTrackF32;
9908        pub fn whiteout_mdx_MdxRibbonEmitter_set_heightBelowTracks(
9909            self_: *mut whiteout_MdxRibbonEmitter,
9910            value: *const whiteout_MdxTrackF32,
9911        );
9912        pub fn whiteout_mdx_MdxRibbonEmitter_get_alphaTracks(
9913            self_: *mut whiteout_MdxRibbonEmitter,
9914        ) -> *mut whiteout_MdxTrackF32;
9915        pub fn whiteout_mdx_MdxRibbonEmitter_set_alphaTracks(
9916            self_: *mut whiteout_MdxRibbonEmitter,
9917            value: *const whiteout_MdxTrackF32,
9918        );
9919        pub fn whiteout_mdx_MdxRibbonEmitter_get_colorTracks(
9920            self_: *mut whiteout_MdxRibbonEmitter,
9921        ) -> *mut whiteout_MdxTrackVector3f;
9922        pub fn whiteout_mdx_MdxRibbonEmitter_set_colorTracks(
9923            self_: *mut whiteout_MdxRibbonEmitter,
9924            value: *const whiteout_MdxTrackVector3f,
9925        );
9926        pub fn whiteout_mdx_MdxRibbonEmitter_get_textureSlotTracks(
9927            self_: *mut whiteout_MdxRibbonEmitter,
9928        ) -> *mut whiteout_MdxTrackU32;
9929        pub fn whiteout_mdx_MdxRibbonEmitter_set_textureSlotTracks(
9930            self_: *mut whiteout_MdxRibbonEmitter,
9931            value: *const whiteout_MdxTrackU32,
9932        );
9933        pub fn whiteout_mdx_MdxRibbonEmitter_get_visibilityTracks(
9934            self_: *mut whiteout_MdxRibbonEmitter,
9935        ) -> *mut whiteout_MdxTrackF32;
9936        pub fn whiteout_mdx_MdxRibbonEmitter_set_visibilityTracks(
9937            self_: *mut whiteout_MdxRibbonEmitter,
9938            value: *const whiteout_MdxTrackF32,
9939        );
9940        // EventObject
9941        pub fn whiteout_mdx_MdxEventObject_new() -> *mut whiteout_MdxEventObject;
9942        pub fn whiteout_mdx_MdxEventObject_delete(self_: *mut whiteout_MdxEventObject);
9943        pub fn whiteout_mdx_MdxEventObject_get_node(
9944            self_: *mut whiteout_MdxEventObject,
9945        ) -> *mut whiteout_MdxNode;
9946        pub fn whiteout_mdx_MdxEventObject_set_node(
9947            self_: *mut whiteout_MdxEventObject,
9948            value: *const whiteout_MdxNode,
9949        );
9950        pub fn whiteout_mdx_MdxEventObject_get_globalSequenceId(
9951            self_: *mut whiteout_MdxEventObject,
9952        ) -> u32;
9953        pub fn whiteout_mdx_MdxEventObject_set_globalSequenceId(
9954            self_: *mut whiteout_MdxEventObject,
9955            value: u32,
9956        );
9957        pub fn whiteout_mdx_MdxEventObject_get_eventTrackTimes_count(
9958            self_: *mut whiteout_MdxEventObject,
9959        ) -> usize;
9960        pub fn whiteout_mdx_MdxEventObject_resize_eventTrackTimes(
9961            self_: *mut whiteout_MdxEventObject,
9962            count: usize,
9963        );
9964        pub fn whiteout_mdx_MdxEventObject_get_eventTrackTimes_data(
9965            self_: *mut whiteout_MdxEventObject,
9966        ) -> *const u32;
9967        pub fn whiteout_mdx_MdxEventObject_assign_eventTrackTimes(
9968            self_: *mut whiteout_MdxEventObject,
9969            data: *const u32,
9970            count: usize,
9971        );
9972        // Camera
9973        pub fn whiteout_mdx_MdxCamera_new() -> *mut whiteout_MdxCamera;
9974        pub fn whiteout_mdx_MdxCamera_delete(self_: *mut whiteout_MdxCamera);
9975        pub fn whiteout_mdx_MdxCamera_get_name(self_: *mut whiteout_MdxCamera) -> RawCString;
9976        pub fn whiteout_mdx_MdxCamera_set_name(
9977            self_: *mut whiteout_MdxCamera,
9978            value: *const core::ffi::c_char,
9979        );
9980        pub fn whiteout_mdx_MdxCamera_get_position(
9981            self_: *mut whiteout_MdxCamera,
9982        ) -> *mut core::ffi::c_void;
9983        pub fn whiteout_mdx_MdxCamera_set_position(
9984            self_: *mut whiteout_MdxCamera,
9985            value: *const core::ffi::c_void,
9986        );
9987        pub fn whiteout_mdx_MdxCamera_get_fieldOfView(self_: *mut whiteout_MdxCamera) -> f32;
9988        pub fn whiteout_mdx_MdxCamera_set_fieldOfView(self_: *mut whiteout_MdxCamera, value: f32);
9989        pub fn whiteout_mdx_MdxCamera_get_farClippingPlane(self_: *mut whiteout_MdxCamera) -> f32;
9990        pub fn whiteout_mdx_MdxCamera_set_farClippingPlane(
9991            self_: *mut whiteout_MdxCamera,
9992            value: f32,
9993        );
9994        pub fn whiteout_mdx_MdxCamera_get_nearClippingPlane(self_: *mut whiteout_MdxCamera) -> f32;
9995        pub fn whiteout_mdx_MdxCamera_set_nearClippingPlane(
9996            self_: *mut whiteout_MdxCamera,
9997            value: f32,
9998        );
9999        pub fn whiteout_mdx_MdxCamera_get_targetPosition(
10000            self_: *mut whiteout_MdxCamera,
10001        ) -> *mut core::ffi::c_void;
10002        pub fn whiteout_mdx_MdxCamera_set_targetPosition(
10003            self_: *mut whiteout_MdxCamera,
10004            value: *const core::ffi::c_void,
10005        );
10006        pub fn whiteout_mdx_MdxCamera_get_positionTracks(
10007            self_: *mut whiteout_MdxCamera,
10008        ) -> *mut whiteout_MdxTrackVector3f;
10009        pub fn whiteout_mdx_MdxCamera_set_positionTracks(
10010            self_: *mut whiteout_MdxCamera,
10011            value: *const whiteout_MdxTrackVector3f,
10012        );
10013        pub fn whiteout_mdx_MdxCamera_get_targetRotationTracks(
10014            self_: *mut whiteout_MdxCamera,
10015        ) -> *mut whiteout_MdxTrackF32;
10016        pub fn whiteout_mdx_MdxCamera_set_targetRotationTracks(
10017            self_: *mut whiteout_MdxCamera,
10018            value: *const whiteout_MdxTrackF32,
10019        );
10020        pub fn whiteout_mdx_MdxCamera_get_targetPositionTracks(
10021            self_: *mut whiteout_MdxCamera,
10022        ) -> *mut whiteout_MdxTrackVector3f;
10023        pub fn whiteout_mdx_MdxCamera_set_targetPositionTracks(
10024            self_: *mut whiteout_MdxCamera,
10025            value: *const whiteout_MdxTrackVector3f,
10026        );
10027        // CollisionShape
10028        pub fn whiteout_mdx_MdxCollisionShape_new() -> *mut whiteout_MdxCollisionShape;
10029        pub fn whiteout_mdx_MdxCollisionShape_delete(self_: *mut whiteout_MdxCollisionShape);
10030        pub fn whiteout_mdx_MdxCollisionShape_get_node(
10031            self_: *mut whiteout_MdxCollisionShape,
10032        ) -> *mut whiteout_MdxNode;
10033        pub fn whiteout_mdx_MdxCollisionShape_set_node(
10034            self_: *mut whiteout_MdxCollisionShape,
10035            value: *const whiteout_MdxNode,
10036        );
10037        pub fn whiteout_mdx_MdxCollisionShape_get_type(
10038            self_: *mut whiteout_MdxCollisionShape,
10039        ) -> i32;
10040        pub fn whiteout_mdx_MdxCollisionShape_set_type(
10041            self_: *mut whiteout_MdxCollisionShape,
10042            value: i32,
10043        );
10044        pub fn whiteout_mdx_MdxCollisionShape_get_vertices_count(
10045            self_: *mut whiteout_MdxCollisionShape,
10046        ) -> usize;
10047        pub fn whiteout_mdx_MdxCollisionShape_resize_vertices(
10048            self_: *mut whiteout_MdxCollisionShape,
10049            count: usize,
10050        );
10051        pub fn whiteout_mdx_MdxCollisionShape_get_vertices_data(
10052            self_: *mut whiteout_MdxCollisionShape,
10053        ) -> *const f32;
10054        pub fn whiteout_mdx_MdxCollisionShape_assign_vertices(
10055            self_: *mut whiteout_MdxCollisionShape,
10056            data: *const f32,
10057            count: usize,
10058        );
10059        pub fn whiteout_mdx_MdxCollisionShape_get_radius(
10060            self_: *mut whiteout_MdxCollisionShape,
10061        ) -> f32;
10062        pub fn whiteout_mdx_MdxCollisionShape_set_radius(
10063            self_: *mut whiteout_MdxCollisionShape,
10064            value: f32,
10065        );
10066        // FaceEffect
10067        pub fn whiteout_mdx_MdxFaceEffect_new() -> *mut whiteout_MdxFaceEffect;
10068        pub fn whiteout_mdx_MdxFaceEffect_delete(self_: *mut whiteout_MdxFaceEffect);
10069        pub fn whiteout_mdx_MdxFaceEffect_get_name(
10070            self_: *mut whiteout_MdxFaceEffect,
10071        ) -> RawCString;
10072        pub fn whiteout_mdx_MdxFaceEffect_set_name(
10073            self_: *mut whiteout_MdxFaceEffect,
10074            value: *const core::ffi::c_char,
10075        );
10076        pub fn whiteout_mdx_MdxFaceEffect_get_path(
10077            self_: *mut whiteout_MdxFaceEffect,
10078        ) -> RawCString;
10079        pub fn whiteout_mdx_MdxFaceEffect_set_path(
10080            self_: *mut whiteout_MdxFaceEffect,
10081            value: *const core::ffi::c_char,
10082        );
10083        // CornEmitter
10084        pub fn whiteout_mdx_MdxCornEmitter_new() -> *mut whiteout_MdxCornEmitter;
10085        pub fn whiteout_mdx_MdxCornEmitter_delete(self_: *mut whiteout_MdxCornEmitter);
10086        pub fn whiteout_mdx_MdxCornEmitter_get_node(
10087            self_: *mut whiteout_MdxCornEmitter,
10088        ) -> *mut whiteout_MdxNode;
10089        pub fn whiteout_mdx_MdxCornEmitter_set_node(
10090            self_: *mut whiteout_MdxCornEmitter,
10091            value: *const whiteout_MdxNode,
10092        );
10093        pub fn whiteout_mdx_MdxCornEmitter_get_lifeSpan(self_: *mut whiteout_MdxCornEmitter)
10094            -> f32;
10095        pub fn whiteout_mdx_MdxCornEmitter_set_lifeSpan(
10096            self_: *mut whiteout_MdxCornEmitter,
10097            value: f32,
10098        );
10099        pub fn whiteout_mdx_MdxCornEmitter_get_emissionRate(
10100            self_: *mut whiteout_MdxCornEmitter,
10101        ) -> f32;
10102        pub fn whiteout_mdx_MdxCornEmitter_set_emissionRate(
10103            self_: *mut whiteout_MdxCornEmitter,
10104            value: f32,
10105        );
10106        pub fn whiteout_mdx_MdxCornEmitter_get_speed(self_: *mut whiteout_MdxCornEmitter) -> f32;
10107        pub fn whiteout_mdx_MdxCornEmitter_set_speed(
10108            self_: *mut whiteout_MdxCornEmitter,
10109            value: f32,
10110        );
10111        pub fn whiteout_mdx_MdxCornEmitter_get_color(
10112            self_: *mut whiteout_MdxCornEmitter,
10113        ) -> *mut core::ffi::c_void;
10114        pub fn whiteout_mdx_MdxCornEmitter_set_color(
10115            self_: *mut whiteout_MdxCornEmitter,
10116            value: *const core::ffi::c_void,
10117        );
10118        pub fn whiteout_mdx_MdxCornEmitter_get_alpha(self_: *mut whiteout_MdxCornEmitter) -> f32;
10119        pub fn whiteout_mdx_MdxCornEmitter_set_alpha(
10120            self_: *mut whiteout_MdxCornEmitter,
10121            value: f32,
10122        );
10123        pub fn whiteout_mdx_MdxCornEmitter_get_replaceableId(
10124            self_: *mut whiteout_MdxCornEmitter,
10125        ) -> u32;
10126        pub fn whiteout_mdx_MdxCornEmitter_set_replaceableId(
10127            self_: *mut whiteout_MdxCornEmitter,
10128            value: u32,
10129        );
10130        pub fn whiteout_mdx_MdxCornEmitter_get_path(
10131            self_: *mut whiteout_MdxCornEmitter,
10132        ) -> RawCString;
10133        pub fn whiteout_mdx_MdxCornEmitter_set_path(
10134            self_: *mut whiteout_MdxCornEmitter,
10135            value: *const core::ffi::c_char,
10136        );
10137        pub fn whiteout_mdx_MdxCornEmitter_get_animVisibilityGuide(
10138            self_: *mut whiteout_MdxCornEmitter,
10139        ) -> RawCString;
10140        pub fn whiteout_mdx_MdxCornEmitter_set_animVisibilityGuide(
10141            self_: *mut whiteout_MdxCornEmitter,
10142            value: *const core::ffi::c_char,
10143        );
10144        pub fn whiteout_mdx_MdxCornEmitter_get_lifeSpanTracks(
10145            self_: *mut whiteout_MdxCornEmitter,
10146        ) -> *mut whiteout_MdxTrackF32;
10147        pub fn whiteout_mdx_MdxCornEmitter_set_lifeSpanTracks(
10148            self_: *mut whiteout_MdxCornEmitter,
10149            value: *const whiteout_MdxTrackF32,
10150        );
10151        pub fn whiteout_mdx_MdxCornEmitter_get_emissionRateTracks(
10152            self_: *mut whiteout_MdxCornEmitter,
10153        ) -> *mut whiteout_MdxTrackF32;
10154        pub fn whiteout_mdx_MdxCornEmitter_set_emissionRateTracks(
10155            self_: *mut whiteout_MdxCornEmitter,
10156            value: *const whiteout_MdxTrackF32,
10157        );
10158        pub fn whiteout_mdx_MdxCornEmitter_get_speedTracks(
10159            self_: *mut whiteout_MdxCornEmitter,
10160        ) -> *mut whiteout_MdxTrackF32;
10161        pub fn whiteout_mdx_MdxCornEmitter_set_speedTracks(
10162            self_: *mut whiteout_MdxCornEmitter,
10163            value: *const whiteout_MdxTrackF32,
10164        );
10165        pub fn whiteout_mdx_MdxCornEmitter_get_colorTracks(
10166            self_: *mut whiteout_MdxCornEmitter,
10167        ) -> *mut whiteout_MdxTrackVector3f;
10168        pub fn whiteout_mdx_MdxCornEmitter_set_colorTracks(
10169            self_: *mut whiteout_MdxCornEmitter,
10170            value: *const whiteout_MdxTrackVector3f,
10171        );
10172        pub fn whiteout_mdx_MdxCornEmitter_get_alphaTracks(
10173            self_: *mut whiteout_MdxCornEmitter,
10174        ) -> *mut whiteout_MdxTrackF32;
10175        pub fn whiteout_mdx_MdxCornEmitter_set_alphaTracks(
10176            self_: *mut whiteout_MdxCornEmitter,
10177            value: *const whiteout_MdxTrackF32,
10178        );
10179        pub fn whiteout_mdx_MdxCornEmitter_get_visibilityTracks(
10180            self_: *mut whiteout_MdxCornEmitter,
10181        ) -> *mut whiteout_MdxTrackF32;
10182        pub fn whiteout_mdx_MdxCornEmitter_set_visibilityTracks(
10183            self_: *mut whiteout_MdxCornEmitter,
10184            value: *const whiteout_MdxTrackF32,
10185        );
10186        // Parser
10187        pub fn whiteout_mdx_MdxParser_new() -> *mut whiteout_MdxParser;
10188        pub fn whiteout_mdx_MdxParser_new_upgradeMode(
10189            _0: *mut core::ffi::c_void,
10190        ) -> *mut whiteout_MdxParser;
10191        pub fn whiteout_mdx_MdxParser_delete(self_: *mut whiteout_MdxParser);
10192        pub fn whiteout_mdx_MdxParser_parse(
10193            self_: *mut whiteout_MdxParser,
10194            file_path: *const core::ffi::c_char,
10195        ) -> *mut whiteout_MdxModel;
10196        pub fn whiteout_mdx_MdxParser_parse_buffer_format(
10197            self_: *mut whiteout_MdxParser,
10198            buffer: *const u8,
10199            buffer_size: usize,
10200            format: i32,
10201        ) -> *mut whiteout_MdxModel;
10202        pub fn whiteout_mdx_MdxParser_hasIssues(self_: *mut whiteout_MdxParser) -> i32;
10203        pub fn whiteout_mdx_MdxParser_getIssues_count(self_: *mut whiteout_MdxParser) -> usize;
10204        pub fn whiteout_mdx_MdxParser_getIssues_at(
10205            self_: *mut whiteout_MdxParser,
10206            index: usize,
10207        ) -> RawCString;
10208        // Writer
10209        pub fn whiteout_mdx_MdxWriter_new() -> *mut whiteout_MdxWriter;
10210        pub fn whiteout_mdx_MdxWriter_delete(self_: *mut whiteout_MdxWriter);
10211        pub fn whiteout_mdx_MdxWriter_write(
10212            self_: *mut whiteout_MdxWriter,
10213            file_path: *const core::ffi::c_char,
10214            mdlx: *mut whiteout_MdxModel,
10215            mdl_format: i32,
10216        );
10217        pub fn whiteout_mdx_MdxWriter_write_mdx_format_mdlFormat(
10218            self_: *mut whiteout_MdxWriter,
10219            mdx: *mut whiteout_MdxModel,
10220            format: i32,
10221            mdl_format: i32,
10222        ) -> RawBytes;
10223        // TrackVector3f
10224        pub fn whiteout_mdx_MdxTrackVector3f_new() -> *mut whiteout_MdxTrackVector3f;
10225        pub fn whiteout_mdx_MdxTrackVector3f_delete(self_: *mut whiteout_MdxTrackVector3f);
10226        pub fn whiteout_mdx_MdxTrackVector3f_get_isUsed(
10227            self_: *mut whiteout_MdxTrackVector3f,
10228        ) -> i32;
10229        pub fn whiteout_mdx_MdxTrackVector3f_set_isUsed(
10230            self_: *mut whiteout_MdxTrackVector3f,
10231            value: i32,
10232        );
10233        pub fn whiteout_mdx_MdxTrackVector3f_get_interpolationType(
10234            self_: *mut whiteout_MdxTrackVector3f,
10235        ) -> i32;
10236        pub fn whiteout_mdx_MdxTrackVector3f_set_interpolationType(
10237            self_: *mut whiteout_MdxTrackVector3f,
10238            value: i32,
10239        );
10240        pub fn whiteout_mdx_MdxTrackVector3f_get_globalSequenceId(
10241            self_: *mut whiteout_MdxTrackVector3f,
10242        ) -> u32;
10243        pub fn whiteout_mdx_MdxTrackVector3f_set_globalSequenceId(
10244            self_: *mut whiteout_MdxTrackVector3f,
10245            value: u32,
10246        );
10247        pub fn whiteout_mdx_MdxTrackVector3f_get_keyCount(
10248            self_: *mut whiteout_MdxTrackVector3f,
10249        ) -> usize;
10250        pub fn whiteout_mdx_MdxTrackVector3f_set_keyCount(
10251            self_: *mut whiteout_MdxTrackVector3f,
10252            value: usize,
10253        );
10254        pub fn whiteout_mdx_MdxTrackVector3f_get_timestamps_count(
10255            self_: *mut whiteout_MdxTrackVector3f,
10256        ) -> usize;
10257        pub fn whiteout_mdx_MdxTrackVector3f_resize_timestamps(
10258            self_: *mut whiteout_MdxTrackVector3f,
10259            count: usize,
10260        );
10261        pub fn whiteout_mdx_MdxTrackVector3f_get_timestamps_data(
10262            self_: *mut whiteout_MdxTrackVector3f,
10263        ) -> *const u32;
10264        pub fn whiteout_mdx_MdxTrackVector3f_assign_timestamps(
10265            self_: *mut whiteout_MdxTrackVector3f,
10266            data: *const u32,
10267            count: usize,
10268        );
10269        pub fn whiteout_mdx_MdxTrackVector3f_get_keys_count(
10270            self_: *mut whiteout_MdxTrackVector3f,
10271        ) -> usize;
10272        pub fn whiteout_mdx_MdxTrackVector3f_resize_keys(
10273            self_: *mut whiteout_MdxTrackVector3f,
10274            count: usize,
10275        );
10276        pub fn whiteout_mdx_MdxTrackVector3f_get_keys_data(
10277            self_: *mut whiteout_MdxTrackVector3f,
10278        ) -> *const f32;
10279        pub fn whiteout_mdx_MdxTrackVector3f_assign_keys(
10280            self_: *mut whiteout_MdxTrackVector3f,
10281            data: *const f32,
10282            count: usize,
10283        );
10284        // TrackQuaternion
10285        pub fn whiteout_mdx_MdxTrackQuaternion_new() -> *mut whiteout_MdxTrackQuaternion;
10286        pub fn whiteout_mdx_MdxTrackQuaternion_delete(self_: *mut whiteout_MdxTrackQuaternion);
10287        pub fn whiteout_mdx_MdxTrackQuaternion_get_isUsed(
10288            self_: *mut whiteout_MdxTrackQuaternion,
10289        ) -> i32;
10290        pub fn whiteout_mdx_MdxTrackQuaternion_set_isUsed(
10291            self_: *mut whiteout_MdxTrackQuaternion,
10292            value: i32,
10293        );
10294        pub fn whiteout_mdx_MdxTrackQuaternion_get_interpolationType(
10295            self_: *mut whiteout_MdxTrackQuaternion,
10296        ) -> i32;
10297        pub fn whiteout_mdx_MdxTrackQuaternion_set_interpolationType(
10298            self_: *mut whiteout_MdxTrackQuaternion,
10299            value: i32,
10300        );
10301        pub fn whiteout_mdx_MdxTrackQuaternion_get_globalSequenceId(
10302            self_: *mut whiteout_MdxTrackQuaternion,
10303        ) -> u32;
10304        pub fn whiteout_mdx_MdxTrackQuaternion_set_globalSequenceId(
10305            self_: *mut whiteout_MdxTrackQuaternion,
10306            value: u32,
10307        );
10308        pub fn whiteout_mdx_MdxTrackQuaternion_get_keyCount(
10309            self_: *mut whiteout_MdxTrackQuaternion,
10310        ) -> usize;
10311        pub fn whiteout_mdx_MdxTrackQuaternion_set_keyCount(
10312            self_: *mut whiteout_MdxTrackQuaternion,
10313            value: usize,
10314        );
10315        pub fn whiteout_mdx_MdxTrackQuaternion_get_timestamps_count(
10316            self_: *mut whiteout_MdxTrackQuaternion,
10317        ) -> usize;
10318        pub fn whiteout_mdx_MdxTrackQuaternion_resize_timestamps(
10319            self_: *mut whiteout_MdxTrackQuaternion,
10320            count: usize,
10321        );
10322        pub fn whiteout_mdx_MdxTrackQuaternion_get_timestamps_data(
10323            self_: *mut whiteout_MdxTrackQuaternion,
10324        ) -> *const u32;
10325        pub fn whiteout_mdx_MdxTrackQuaternion_assign_timestamps(
10326            self_: *mut whiteout_MdxTrackQuaternion,
10327            data: *const u32,
10328            count: usize,
10329        );
10330        pub fn whiteout_mdx_MdxTrackQuaternion_get_keys_count(
10331            self_: *mut whiteout_MdxTrackQuaternion,
10332        ) -> usize;
10333        pub fn whiteout_mdx_MdxTrackQuaternion_resize_keys(
10334            self_: *mut whiteout_MdxTrackQuaternion,
10335            count: usize,
10336        );
10337        pub fn whiteout_mdx_MdxTrackQuaternion_get_keys_data(
10338            self_: *mut whiteout_MdxTrackQuaternion,
10339        ) -> *const f32;
10340        pub fn whiteout_mdx_MdxTrackQuaternion_assign_keys(
10341            self_: *mut whiteout_MdxTrackQuaternion,
10342            data: *const f32,
10343            count: usize,
10344        );
10345        // TrackU32
10346        pub fn whiteout_mdx_MdxTrackU32_new() -> *mut whiteout_MdxTrackU32;
10347        pub fn whiteout_mdx_MdxTrackU32_delete(self_: *mut whiteout_MdxTrackU32);
10348        pub fn whiteout_mdx_MdxTrackU32_get_isUsed(self_: *mut whiteout_MdxTrackU32) -> i32;
10349        pub fn whiteout_mdx_MdxTrackU32_set_isUsed(self_: *mut whiteout_MdxTrackU32, value: i32);
10350        pub fn whiteout_mdx_MdxTrackU32_get_interpolationType(
10351            self_: *mut whiteout_MdxTrackU32,
10352        ) -> i32;
10353        pub fn whiteout_mdx_MdxTrackU32_set_interpolationType(
10354            self_: *mut whiteout_MdxTrackU32,
10355            value: i32,
10356        );
10357        pub fn whiteout_mdx_MdxTrackU32_get_globalSequenceId(
10358            self_: *mut whiteout_MdxTrackU32,
10359        ) -> u32;
10360        pub fn whiteout_mdx_MdxTrackU32_set_globalSequenceId(
10361            self_: *mut whiteout_MdxTrackU32,
10362            value: u32,
10363        );
10364        pub fn whiteout_mdx_MdxTrackU32_get_keyCount(self_: *mut whiteout_MdxTrackU32) -> usize;
10365        pub fn whiteout_mdx_MdxTrackU32_set_keyCount(
10366            self_: *mut whiteout_MdxTrackU32,
10367            value: usize,
10368        );
10369        pub fn whiteout_mdx_MdxTrackU32_get_timestamps_count(
10370            self_: *mut whiteout_MdxTrackU32,
10371        ) -> usize;
10372        pub fn whiteout_mdx_MdxTrackU32_resize_timestamps(
10373            self_: *mut whiteout_MdxTrackU32,
10374            count: usize,
10375        );
10376        pub fn whiteout_mdx_MdxTrackU32_get_timestamps_data(
10377            self_: *mut whiteout_MdxTrackU32,
10378        ) -> *const u32;
10379        pub fn whiteout_mdx_MdxTrackU32_assign_timestamps(
10380            self_: *mut whiteout_MdxTrackU32,
10381            data: *const u32,
10382            count: usize,
10383        );
10384        pub fn whiteout_mdx_MdxTrackU32_get_keys_count(self_: *mut whiteout_MdxTrackU32) -> usize;
10385        pub fn whiteout_mdx_MdxTrackU32_resize_keys(self_: *mut whiteout_MdxTrackU32, count: usize);
10386        pub fn whiteout_mdx_MdxTrackU32_get_keys_data(
10387            self_: *mut whiteout_MdxTrackU32,
10388        ) -> *const u32;
10389        pub fn whiteout_mdx_MdxTrackU32_assign_keys(
10390            self_: *mut whiteout_MdxTrackU32,
10391            data: *const u32,
10392            count: usize,
10393        );
10394        // TrackF32
10395        pub fn whiteout_mdx_MdxTrackF32_new() -> *mut whiteout_MdxTrackF32;
10396        pub fn whiteout_mdx_MdxTrackF32_delete(self_: *mut whiteout_MdxTrackF32);
10397        pub fn whiteout_mdx_MdxTrackF32_get_isUsed(self_: *mut whiteout_MdxTrackF32) -> i32;
10398        pub fn whiteout_mdx_MdxTrackF32_set_isUsed(self_: *mut whiteout_MdxTrackF32, value: i32);
10399        pub fn whiteout_mdx_MdxTrackF32_get_interpolationType(
10400            self_: *mut whiteout_MdxTrackF32,
10401        ) -> i32;
10402        pub fn whiteout_mdx_MdxTrackF32_set_interpolationType(
10403            self_: *mut whiteout_MdxTrackF32,
10404            value: i32,
10405        );
10406        pub fn whiteout_mdx_MdxTrackF32_get_globalSequenceId(
10407            self_: *mut whiteout_MdxTrackF32,
10408        ) -> u32;
10409        pub fn whiteout_mdx_MdxTrackF32_set_globalSequenceId(
10410            self_: *mut whiteout_MdxTrackF32,
10411            value: u32,
10412        );
10413        pub fn whiteout_mdx_MdxTrackF32_get_keyCount(self_: *mut whiteout_MdxTrackF32) -> usize;
10414        pub fn whiteout_mdx_MdxTrackF32_set_keyCount(
10415            self_: *mut whiteout_MdxTrackF32,
10416            value: usize,
10417        );
10418        pub fn whiteout_mdx_MdxTrackF32_get_timestamps_count(
10419            self_: *mut whiteout_MdxTrackF32,
10420        ) -> usize;
10421        pub fn whiteout_mdx_MdxTrackF32_resize_timestamps(
10422            self_: *mut whiteout_MdxTrackF32,
10423            count: usize,
10424        );
10425        pub fn whiteout_mdx_MdxTrackF32_get_timestamps_data(
10426            self_: *mut whiteout_MdxTrackF32,
10427        ) -> *const u32;
10428        pub fn whiteout_mdx_MdxTrackF32_assign_timestamps(
10429            self_: *mut whiteout_MdxTrackF32,
10430            data: *const u32,
10431            count: usize,
10432        );
10433        pub fn whiteout_mdx_MdxTrackF32_get_keys_count(self_: *mut whiteout_MdxTrackF32) -> usize;
10434        pub fn whiteout_mdx_MdxTrackF32_resize_keys(self_: *mut whiteout_MdxTrackF32, count: usize);
10435        pub fn whiteout_mdx_MdxTrackF32_get_keys_data(
10436            self_: *mut whiteout_MdxTrackF32,
10437        ) -> *const f32;
10438        pub fn whiteout_mdx_MdxTrackF32_assign_keys(
10439            self_: *mut whiteout_MdxTrackF32,
10440            data: *const f32,
10441            count: usize,
10442        );
10443    }
10444}