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    pub fn segment_color_len() -> usize {
5600        // SAFETY: a compile-time constant on the native side.
5601        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_segmentColor_size() }
5602    }
5603
5604    pub fn segment_color(&self, index: usize) -> crate::math::Vector3f {
5605        // SAFETY: the getter returns an interior pointer to a
5606        // layout-identical POD, copied out immediately. The
5607        // native side does not bounds-check, so callers stay
5608        // within `segment_color_len()`.
5609        unsafe {
5610            *(ffi::whiteout_mdx_MdxParticleEmitter2_get_segmentColor_at(self.raw.as_ptr(), index)
5611                as *const crate::math::Vector3f)
5612        }
5613    }
5614
5615    /// Alpha at start/middle/end
5616    pub fn segment_alpha_len() -> usize {
5617        // SAFETY: a compile-time constant on the native side.
5618        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_segmentAlpha_size() }
5619    }
5620
5621    pub fn segment_alpha(&self, index: usize) -> u8 {
5622        // SAFETY: scalar read. The native side does not bounds-
5623        // check, so callers stay within `segment_alpha_len()`.
5624        unsafe {
5625            ffi::whiteout_mdx_MdxParticleEmitter2_get_segmentAlpha_at(self.raw.as_ptr(), index)
5626        }
5627    }
5628
5629    pub fn set_segment_alpha(&mut self, index: usize, value: u8) {
5630        // SAFETY: as above.
5631        unsafe {
5632            ffi::whiteout_mdx_MdxParticleEmitter2_set_segmentAlpha_at(
5633                self.raw.as_ptr(),
5634                index,
5635                value,
5636            )
5637        }
5638    }
5639
5640    /// Scale at start/middle/end
5641    pub fn segment_scaling_len() -> usize {
5642        // SAFETY: a compile-time constant on the native side.
5643        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_segmentScaling_size() }
5644    }
5645
5646    pub fn segment_scaling(&self, index: usize) -> f32 {
5647        // SAFETY: scalar read. The native side does not bounds-
5648        // check, so callers stay within `segment_scaling_len()`.
5649        unsafe {
5650            ffi::whiteout_mdx_MdxParticleEmitter2_get_segmentScaling_at(self.raw.as_ptr(), index)
5651        }
5652    }
5653
5654    pub fn set_segment_scaling(&mut self, index: usize, value: f32) {
5655        // SAFETY: as above.
5656        unsafe {
5657            ffi::whiteout_mdx_MdxParticleEmitter2_set_segmentScaling_at(
5658                self.raw.as_ptr(),
5659                index,
5660                value,
5661            )
5662        }
5663    }
5664
5665    /// Head lifetime intervals
5666    pub fn head_interval_len() -> usize {
5667        // SAFETY: a compile-time constant on the native side.
5668        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_headInterval_size() }
5669    }
5670
5671    pub fn head_interval(&self, index: usize) -> u32 {
5672        // SAFETY: scalar read. The native side does not bounds-
5673        // check, so callers stay within `head_interval_len()`.
5674        unsafe {
5675            ffi::whiteout_mdx_MdxParticleEmitter2_get_headInterval_at(self.raw.as_ptr(), index)
5676        }
5677    }
5678
5679    pub fn set_head_interval(&mut self, index: usize, value: u32) {
5680        // SAFETY: as above.
5681        unsafe {
5682            ffi::whiteout_mdx_MdxParticleEmitter2_set_headInterval_at(
5683                self.raw.as_ptr(),
5684                index,
5685                value,
5686            )
5687        }
5688    }
5689
5690    /// Head decay intervals
5691    pub fn head_decay_interval_len() -> usize {
5692        // SAFETY: a compile-time constant on the native side.
5693        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_headDecayInterval_size() }
5694    }
5695
5696    pub fn head_decay_interval(&self, index: usize) -> u32 {
5697        // SAFETY: scalar read. The native side does not bounds-
5698        // check, so callers stay within `head_decay_interval_len()`.
5699        unsafe {
5700            ffi::whiteout_mdx_MdxParticleEmitter2_get_headDecayInterval_at(self.raw.as_ptr(), index)
5701        }
5702    }
5703
5704    pub fn set_head_decay_interval(&mut self, index: usize, value: u32) {
5705        // SAFETY: as above.
5706        unsafe {
5707            ffi::whiteout_mdx_MdxParticleEmitter2_set_headDecayInterval_at(
5708                self.raw.as_ptr(),
5709                index,
5710                value,
5711            )
5712        }
5713    }
5714
5715    /// Tail lifetime intervals
5716    pub fn tail_interval_len() -> usize {
5717        // SAFETY: a compile-time constant on the native side.
5718        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_tailInterval_size() }
5719    }
5720
5721    pub fn tail_interval(&self, index: usize) -> u32 {
5722        // SAFETY: scalar read. The native side does not bounds-
5723        // check, so callers stay within `tail_interval_len()`.
5724        unsafe {
5725            ffi::whiteout_mdx_MdxParticleEmitter2_get_tailInterval_at(self.raw.as_ptr(), index)
5726        }
5727    }
5728
5729    pub fn set_tail_interval(&mut self, index: usize, value: u32) {
5730        // SAFETY: as above.
5731        unsafe {
5732            ffi::whiteout_mdx_MdxParticleEmitter2_set_tailInterval_at(
5733                self.raw.as_ptr(),
5734                index,
5735                value,
5736            )
5737        }
5738    }
5739
5740    /// Tail decay intervals
5741    pub fn tail_decay_interval_len() -> usize {
5742        // SAFETY: a compile-time constant on the native side.
5743        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_tailDecayInterval_size() }
5744    }
5745
5746    pub fn tail_decay_interval(&self, index: usize) -> u32 {
5747        // SAFETY: scalar read. The native side does not bounds-
5748        // check, so callers stay within `tail_decay_interval_len()`.
5749        unsafe {
5750            ffi::whiteout_mdx_MdxParticleEmitter2_get_tailDecayInterval_at(self.raw.as_ptr(), index)
5751        }
5752    }
5753
5754    pub fn set_tail_decay_interval(&mut self, index: usize, value: u32) {
5755        // SAFETY: as above.
5756        unsafe {
5757            ffi::whiteout_mdx_MdxParticleEmitter2_set_tailDecayInterval_at(
5758                self.raw.as_ptr(),
5759                index,
5760                value,
5761            )
5762        }
5763    }
5764
5765    /// Texture index
5766    pub fn texture_id(&self) -> u32 {
5767        // SAFETY: plain scalar read through a live handle.
5768        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_textureId(self.raw.as_ptr()) }
5769    }
5770
5771    pub fn set_texture_id(&mut self, value: u32) {
5772        // SAFETY: plain scalar write through a live handle.
5773        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_textureId(self.raw.as_ptr(), value) }
5774    }
5775
5776    /// Squirt flag (burst mode)
5777    pub fn squirt(&self) -> u32 {
5778        // SAFETY: plain scalar read through a live handle.
5779        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_squirt(self.raw.as_ptr()) }
5780    }
5781
5782    pub fn set_squirt(&mut self, value: u32) {
5783        // SAFETY: plain scalar write through a live handle.
5784        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_squirt(self.raw.as_ptr(), value) }
5785    }
5786
5787    /// Rendering priority; signed
5788    pub fn priority_plane(&self) -> i32 {
5789        // SAFETY: plain scalar read through a live handle.
5790        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_priorityPlane(self.raw.as_ptr()) }
5791    }
5792
5793    pub fn set_priority_plane(&mut self, value: i32) {
5794        // SAFETY: plain scalar write through a live handle.
5795        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_priorityPlane(self.raw.as_ptr(), value) }
5796    }
5797
5798    /// Replaceable texture ID
5799    pub fn replaceable_id(&self) -> u32 {
5800        // SAFETY: plain scalar read through a live handle.
5801        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_get_replaceableId(self.raw.as_ptr()) }
5802    }
5803
5804    pub fn set_replaceable_id(&mut self, value: u32) {
5805        // SAFETY: plain scalar write through a live handle.
5806        unsafe { ffi::whiteout_mdx_MdxParticleEmitter2_set_replaceableId(self.raw.as_ptr(), value) }
5807    }
5808
5809    /// Speed animation
5810    /// Borrows the field in place — no copy, no allocation.
5811    pub fn speed_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5812        // SAFETY: an interior pointer into `self`, valid for this
5813        // borrow and never freed by the `Ref`.
5814        unsafe {
5815            crate::support::Ref::new(TrackF32 {
5816                raw: core::ptr::NonNull::new_unchecked(
5817                    ffi::whiteout_mdx_MdxParticleEmitter2_get_speedTracks(self.raw.as_ptr()),
5818                ),
5819            })
5820        }
5821    }
5822
5823    pub fn speed_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5824        // SAFETY: as above; `&mut self` guarantees exclusivity.
5825        unsafe {
5826            crate::support::RefMut::new(TrackF32 {
5827                raw: core::ptr::NonNull::new_unchecked(
5828                    ffi::whiteout_mdx_MdxParticleEmitter2_get_speedTracks(self.raw.as_ptr()),
5829                ),
5830            })
5831        }
5832    }
5833
5834    /// Variation animation
5835    /// Borrows the field in place — no copy, no allocation.
5836    pub fn variation_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5837        // SAFETY: an interior pointer into `self`, valid for this
5838        // borrow and never freed by the `Ref`.
5839        unsafe {
5840            crate::support::Ref::new(TrackF32 {
5841                raw: core::ptr::NonNull::new_unchecked(
5842                    ffi::whiteout_mdx_MdxParticleEmitter2_get_variationTracks(self.raw.as_ptr()),
5843                ),
5844            })
5845        }
5846    }
5847
5848    pub fn variation_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5849        // SAFETY: as above; `&mut self` guarantees exclusivity.
5850        unsafe {
5851            crate::support::RefMut::new(TrackF32 {
5852                raw: core::ptr::NonNull::new_unchecked(
5853                    ffi::whiteout_mdx_MdxParticleEmitter2_get_variationTracks(self.raw.as_ptr()),
5854                ),
5855            })
5856        }
5857    }
5858
5859    /// Latitude animation
5860    /// Borrows the field in place — no copy, no allocation.
5861    pub fn latitude_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5862        // SAFETY: an interior pointer into `self`, valid for this
5863        // borrow and never freed by the `Ref`.
5864        unsafe {
5865            crate::support::Ref::new(TrackF32 {
5866                raw: core::ptr::NonNull::new_unchecked(
5867                    ffi::whiteout_mdx_MdxParticleEmitter2_get_latitudeTracks(self.raw.as_ptr()),
5868                ),
5869            })
5870        }
5871    }
5872
5873    pub fn latitude_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5874        // SAFETY: as above; `&mut self` guarantees exclusivity.
5875        unsafe {
5876            crate::support::RefMut::new(TrackF32 {
5877                raw: core::ptr::NonNull::new_unchecked(
5878                    ffi::whiteout_mdx_MdxParticleEmitter2_get_latitudeTracks(self.raw.as_ptr()),
5879                ),
5880            })
5881        }
5882    }
5883
5884    /// Gravity animation
5885    /// Borrows the field in place — no copy, no allocation.
5886    pub fn gravity_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5887        // SAFETY: an interior pointer into `self`, valid for this
5888        // borrow and never freed by the `Ref`.
5889        unsafe {
5890            crate::support::Ref::new(TrackF32 {
5891                raw: core::ptr::NonNull::new_unchecked(
5892                    ffi::whiteout_mdx_MdxParticleEmitter2_get_gravityTracks(self.raw.as_ptr()),
5893                ),
5894            })
5895        }
5896    }
5897
5898    pub fn gravity_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5899        // SAFETY: as above; `&mut self` guarantees exclusivity.
5900        unsafe {
5901            crate::support::RefMut::new(TrackF32 {
5902                raw: core::ptr::NonNull::new_unchecked(
5903                    ffi::whiteout_mdx_MdxParticleEmitter2_get_gravityTracks(self.raw.as_ptr()),
5904                ),
5905            })
5906        }
5907    }
5908
5909    /// Emission rate animation
5910    /// Borrows the field in place — no copy, no allocation.
5911    pub fn emission_rate_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5912        // SAFETY: an interior pointer into `self`, valid for this
5913        // borrow and never freed by the `Ref`.
5914        unsafe {
5915            crate::support::Ref::new(TrackF32 {
5916                raw: core::ptr::NonNull::new_unchecked(
5917                    ffi::whiteout_mdx_MdxParticleEmitter2_get_emissionRateTracks(self.raw.as_ptr()),
5918                ),
5919            })
5920        }
5921    }
5922
5923    pub fn emission_rate_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5924        // SAFETY: as above; `&mut self` guarantees exclusivity.
5925        unsafe {
5926            crate::support::RefMut::new(TrackF32 {
5927                raw: core::ptr::NonNull::new_unchecked(
5928                    ffi::whiteout_mdx_MdxParticleEmitter2_get_emissionRateTracks(self.raw.as_ptr()),
5929                ),
5930            })
5931        }
5932    }
5933
5934    /// Length animation
5935    /// Borrows the field in place — no copy, no allocation.
5936    pub fn length_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5937        // SAFETY: an interior pointer into `self`, valid for this
5938        // borrow and never freed by the `Ref`.
5939        unsafe {
5940            crate::support::Ref::new(TrackF32 {
5941                raw: core::ptr::NonNull::new_unchecked(
5942                    ffi::whiteout_mdx_MdxParticleEmitter2_get_lengthTracks(self.raw.as_ptr()),
5943                ),
5944            })
5945        }
5946    }
5947
5948    pub fn length_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5949        // SAFETY: as above; `&mut self` guarantees exclusivity.
5950        unsafe {
5951            crate::support::RefMut::new(TrackF32 {
5952                raw: core::ptr::NonNull::new_unchecked(
5953                    ffi::whiteout_mdx_MdxParticleEmitter2_get_lengthTracks(self.raw.as_ptr()),
5954                ),
5955            })
5956        }
5957    }
5958
5959    /// Width animation
5960    /// Borrows the field in place — no copy, no allocation.
5961    pub fn width_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5962        // SAFETY: an interior pointer into `self`, valid for this
5963        // borrow and never freed by the `Ref`.
5964        unsafe {
5965            crate::support::Ref::new(TrackF32 {
5966                raw: core::ptr::NonNull::new_unchecked(
5967                    ffi::whiteout_mdx_MdxParticleEmitter2_get_widthTracks(self.raw.as_ptr()),
5968                ),
5969            })
5970        }
5971    }
5972
5973    pub fn width_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5974        // SAFETY: as above; `&mut self` guarantees exclusivity.
5975        unsafe {
5976            crate::support::RefMut::new(TrackF32 {
5977                raw: core::ptr::NonNull::new_unchecked(
5978                    ffi::whiteout_mdx_MdxParticleEmitter2_get_widthTracks(self.raw.as_ptr()),
5979                ),
5980            })
5981        }
5982    }
5983
5984    /// Visibility animation
5985    /// Borrows the field in place — no copy, no allocation.
5986    pub fn visibility_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
5987        // SAFETY: an interior pointer into `self`, valid for this
5988        // borrow and never freed by the `Ref`.
5989        unsafe {
5990            crate::support::Ref::new(TrackF32 {
5991                raw: core::ptr::NonNull::new_unchecked(
5992                    ffi::whiteout_mdx_MdxParticleEmitter2_get_visibilityTracks(self.raw.as_ptr()),
5993                ),
5994            })
5995        }
5996    }
5997
5998    pub fn visibility_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
5999        // SAFETY: as above; `&mut self` guarantees exclusivity.
6000        unsafe {
6001            crate::support::RefMut::new(TrackF32 {
6002                raw: core::ptr::NonNull::new_unchecked(
6003                    ffi::whiteout_mdx_MdxParticleEmitter2_get_visibilityTracks(self.raw.as_ptr()),
6004                ),
6005            })
6006        }
6007    }
6008}
6009
6010impl Default for ParticleEmitter2 {
6011    fn default() -> Self {
6012        Self::new()
6013    }
6014}
6015
6016/// Ribbon/trail emitter
6017///
6018/// Creates ribbon trails that follow the emitter's movement, like sword trails, missile contrails, etc.
6019pub struct RibbonEmitter {
6020    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxRibbonEmitter>,
6021}
6022
6023impl Drop for RibbonEmitter {
6024    fn drop(&mut self) {
6025        // SAFETY: `raw` came from a native constructor and Drop runs once.
6026        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_delete(self.raw.as_ptr()) }
6027    }
6028}
6029
6030impl RibbonEmitter {
6031    /// # Safety
6032    /// `raw` must be a live handle this value takes ownership of.
6033    #[allow(dead_code)] // used by whichever methods return this type
6034    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxRibbonEmitter) -> Option<Self> {
6035        core::ptr::NonNull::new(raw).map(|raw| RibbonEmitter { raw })
6036    }
6037}
6038
6039// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6040// is deliberately NOT implemented — the C++ types make no documented
6041// guarantee about concurrent use, and claiming one we haven't verified
6042// would be unsound. See `@bind thread_safe` in the plan.
6043unsafe impl Send for RibbonEmitter {}
6044
6045impl core::fmt::Debug for RibbonEmitter {
6046    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6047        f.debug_struct("RibbonEmitter").finish_non_exhaustive()
6048    }
6049}
6050
6051impl RibbonEmitter {
6052    /// # Panics
6053    /// Panics if the native allocation fails.
6054    pub fn new() -> Self {
6055        // SAFETY: the native constructor returns a live handle; a null here
6056        // means the library is unusable.
6057        unsafe {
6058            let raw = ffi::whiteout_mdx_MdxRibbonEmitter_new();
6059            Self::from_raw(raw).expect("native RibbonEmitter allocation failed")
6060        }
6061    }
6062
6063    /// Base node data
6064    /// Borrows the field in place — no copy, no allocation.
6065    pub fn node(&self) -> crate::support::Ref<'_, Node> {
6066        // SAFETY: an interior pointer into `self`, valid for this
6067        // borrow and never freed by the `Ref`.
6068        unsafe {
6069            crate::support::Ref::new(Node {
6070                raw: core::ptr::NonNull::new_unchecked(
6071                    ffi::whiteout_mdx_MdxRibbonEmitter_get_node(self.raw.as_ptr()),
6072                ),
6073            })
6074        }
6075    }
6076
6077    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
6078        // SAFETY: as above; `&mut self` guarantees exclusivity.
6079        unsafe {
6080            crate::support::RefMut::new(Node {
6081                raw: core::ptr::NonNull::new_unchecked(
6082                    ffi::whiteout_mdx_MdxRibbonEmitter_get_node(self.raw.as_ptr()),
6083                ),
6084            })
6085        }
6086    }
6087
6088    /// Height above attachment point
6089    pub fn height_above(&self) -> f32 {
6090        // SAFETY: plain scalar read through a live handle.
6091        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_heightAbove(self.raw.as_ptr()) }
6092    }
6093
6094    pub fn set_height_above(&mut self, value: f32) {
6095        // SAFETY: plain scalar write through a live handle.
6096        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_heightAbove(self.raw.as_ptr(), value) }
6097    }
6098
6099    /// Height below attachment point
6100    pub fn height_below(&self) -> f32 {
6101        // SAFETY: plain scalar read through a live handle.
6102        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_heightBelow(self.raw.as_ptr()) }
6103    }
6104
6105    pub fn set_height_below(&mut self, value: f32) {
6106        // SAFETY: plain scalar write through a live handle.
6107        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_heightBelow(self.raw.as_ptr(), value) }
6108    }
6109
6110    /// Ribbon opacity
6111    pub fn alpha(&self) -> f32 {
6112        // SAFETY: plain scalar read through a live handle.
6113        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_alpha(self.raw.as_ptr()) }
6114    }
6115
6116    pub fn set_alpha(&mut self, value: f32) {
6117        // SAFETY: plain scalar write through a live handle.
6118        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_alpha(self.raw.as_ptr(), value) }
6119    }
6120
6121    /// Ribbon color
6122    pub fn color(&self) -> crate::math::Vector3f {
6123        // SAFETY: the getter returns an interior pointer to a
6124        // layout-identical POD; we copy it out immediately.
6125        unsafe {
6126            *(ffi::whiteout_mdx_MdxRibbonEmitter_get_color(self.raw.as_ptr())
6127                as *const crate::math::Vector3f)
6128        }
6129    }
6130
6131    pub fn set_color(&mut self, value: crate::math::Vector3f) {
6132        // SAFETY: as above, in the other direction.
6133        unsafe {
6134            ffi::whiteout_mdx_MdxRibbonEmitter_set_color(
6135                self.raw.as_ptr(),
6136                &value as *const crate::math::Vector3f as *const _,
6137            )
6138        }
6139    }
6140
6141    /// Ribbon segment lifetime
6142    pub fn lifespan(&self) -> f32 {
6143        // SAFETY: plain scalar read through a live handle.
6144        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_lifespan(self.raw.as_ptr()) }
6145    }
6146
6147    pub fn set_lifespan(&mut self, value: f32) {
6148        // SAFETY: plain scalar write through a live handle.
6149        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_lifespan(self.raw.as_ptr(), value) }
6150    }
6151
6152    /// Texture slot in material
6153    pub fn texture_slot(&self) -> u32 {
6154        // SAFETY: plain scalar read through a live handle.
6155        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_textureSlot(self.raw.as_ptr()) }
6156    }
6157
6158    pub fn set_texture_slot(&mut self, value: u32) {
6159        // SAFETY: plain scalar write through a live handle.
6160        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_textureSlot(self.raw.as_ptr(), value) }
6161    }
6162
6163    /// Emission rate
6164    pub fn emission_rate(&self) -> u32 {
6165        // SAFETY: plain scalar read through a live handle.
6166        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_emissionRate(self.raw.as_ptr()) }
6167    }
6168
6169    pub fn set_emission_rate(&mut self, value: u32) {
6170        // SAFETY: plain scalar write through a live handle.
6171        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_emissionRate(self.raw.as_ptr(), value) }
6172    }
6173
6174    /// Texture atlas rows
6175    pub fn rows(&self) -> u32 {
6176        // SAFETY: plain scalar read through a live handle.
6177        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_rows(self.raw.as_ptr()) }
6178    }
6179
6180    pub fn set_rows(&mut self, value: u32) {
6181        // SAFETY: plain scalar write through a live handle.
6182        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_rows(self.raw.as_ptr(), value) }
6183    }
6184
6185    /// Texture atlas columns
6186    pub fn columns(&self) -> u32 {
6187        // SAFETY: plain scalar read through a live handle.
6188        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_columns(self.raw.as_ptr()) }
6189    }
6190
6191    pub fn set_columns(&mut self, value: u32) {
6192        // SAFETY: plain scalar write through a live handle.
6193        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_columns(self.raw.as_ptr(), value) }
6194    }
6195
6196    /// Material index
6197    pub fn material_id(&self) -> u32 {
6198        // SAFETY: plain scalar read through a live handle.
6199        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_materialId(self.raw.as_ptr()) }
6200    }
6201
6202    pub fn set_material_id(&mut self, value: u32) {
6203        // SAFETY: plain scalar write through a live handle.
6204        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_materialId(self.raw.as_ptr(), value) }
6205    }
6206
6207    /// Gravity effect
6208    pub fn gravity(&self) -> f32 {
6209        // SAFETY: plain scalar read through a live handle.
6210        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_get_gravity(self.raw.as_ptr()) }
6211    }
6212
6213    pub fn set_gravity(&mut self, value: f32) {
6214        // SAFETY: plain scalar write through a live handle.
6215        unsafe { ffi::whiteout_mdx_MdxRibbonEmitter_set_gravity(self.raw.as_ptr(), value) }
6216    }
6217
6218    /// Height above animation
6219    /// Borrows the field in place — no copy, no allocation.
6220    pub fn height_above_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6221        // SAFETY: an interior pointer into `self`, valid for this
6222        // borrow and never freed by the `Ref`.
6223        unsafe {
6224            crate::support::Ref::new(TrackF32 {
6225                raw: core::ptr::NonNull::new_unchecked(
6226                    ffi::whiteout_mdx_MdxRibbonEmitter_get_heightAboveTracks(self.raw.as_ptr()),
6227                ),
6228            })
6229        }
6230    }
6231
6232    pub fn height_above_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6233        // SAFETY: as above; `&mut self` guarantees exclusivity.
6234        unsafe {
6235            crate::support::RefMut::new(TrackF32 {
6236                raw: core::ptr::NonNull::new_unchecked(
6237                    ffi::whiteout_mdx_MdxRibbonEmitter_get_heightAboveTracks(self.raw.as_ptr()),
6238                ),
6239            })
6240        }
6241    }
6242
6243    /// Height below animation
6244    /// Borrows the field in place — no copy, no allocation.
6245    pub fn height_below_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6246        // SAFETY: an interior pointer into `self`, valid for this
6247        // borrow and never freed by the `Ref`.
6248        unsafe {
6249            crate::support::Ref::new(TrackF32 {
6250                raw: core::ptr::NonNull::new_unchecked(
6251                    ffi::whiteout_mdx_MdxRibbonEmitter_get_heightBelowTracks(self.raw.as_ptr()),
6252                ),
6253            })
6254        }
6255    }
6256
6257    pub fn height_below_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6258        // SAFETY: as above; `&mut self` guarantees exclusivity.
6259        unsafe {
6260            crate::support::RefMut::new(TrackF32 {
6261                raw: core::ptr::NonNull::new_unchecked(
6262                    ffi::whiteout_mdx_MdxRibbonEmitter_get_heightBelowTracks(self.raw.as_ptr()),
6263                ),
6264            })
6265        }
6266    }
6267
6268    /// Alpha animation
6269    /// Borrows the field in place — no copy, no allocation.
6270    pub fn alpha_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6271        // SAFETY: an interior pointer into `self`, valid for this
6272        // borrow and never freed by the `Ref`.
6273        unsafe {
6274            crate::support::Ref::new(TrackF32 {
6275                raw: core::ptr::NonNull::new_unchecked(
6276                    ffi::whiteout_mdx_MdxRibbonEmitter_get_alphaTracks(self.raw.as_ptr()),
6277                ),
6278            })
6279        }
6280    }
6281
6282    pub fn alpha_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6283        // SAFETY: as above; `&mut self` guarantees exclusivity.
6284        unsafe {
6285            crate::support::RefMut::new(TrackF32 {
6286                raw: core::ptr::NonNull::new_unchecked(
6287                    ffi::whiteout_mdx_MdxRibbonEmitter_get_alphaTracks(self.raw.as_ptr()),
6288                ),
6289            })
6290        }
6291    }
6292
6293    /// Color animation
6294    /// Borrows the field in place — no copy, no allocation.
6295    pub fn color_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
6296        // SAFETY: an interior pointer into `self`, valid for this
6297        // borrow and never freed by the `Ref`.
6298        unsafe {
6299            crate::support::Ref::new(TrackVector3f {
6300                raw: core::ptr::NonNull::new_unchecked(
6301                    ffi::whiteout_mdx_MdxRibbonEmitter_get_colorTracks(self.raw.as_ptr()),
6302                ),
6303            })
6304        }
6305    }
6306
6307    pub fn color_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
6308        // SAFETY: as above; `&mut self` guarantees exclusivity.
6309        unsafe {
6310            crate::support::RefMut::new(TrackVector3f {
6311                raw: core::ptr::NonNull::new_unchecked(
6312                    ffi::whiteout_mdx_MdxRibbonEmitter_get_colorTracks(self.raw.as_ptr()),
6313                ),
6314            })
6315        }
6316    }
6317
6318    /// Texture slot animation
6319    /// Borrows the field in place — no copy, no allocation.
6320    pub fn texture_slot_tracks(&self) -> crate::support::Ref<'_, TrackU32> {
6321        // SAFETY: an interior pointer into `self`, valid for this
6322        // borrow and never freed by the `Ref`.
6323        unsafe {
6324            crate::support::Ref::new(TrackU32 {
6325                raw: core::ptr::NonNull::new_unchecked(
6326                    ffi::whiteout_mdx_MdxRibbonEmitter_get_textureSlotTracks(self.raw.as_ptr()),
6327                ),
6328            })
6329        }
6330    }
6331
6332    pub fn texture_slot_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackU32> {
6333        // SAFETY: as above; `&mut self` guarantees exclusivity.
6334        unsafe {
6335            crate::support::RefMut::new(TrackU32 {
6336                raw: core::ptr::NonNull::new_unchecked(
6337                    ffi::whiteout_mdx_MdxRibbonEmitter_get_textureSlotTracks(self.raw.as_ptr()),
6338                ),
6339            })
6340        }
6341    }
6342
6343    /// Visibility animation
6344    /// Borrows the field in place — no copy, no allocation.
6345    pub fn visibility_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6346        // SAFETY: an interior pointer into `self`, valid for this
6347        // borrow and never freed by the `Ref`.
6348        unsafe {
6349            crate::support::Ref::new(TrackF32 {
6350                raw: core::ptr::NonNull::new_unchecked(
6351                    ffi::whiteout_mdx_MdxRibbonEmitter_get_visibilityTracks(self.raw.as_ptr()),
6352                ),
6353            })
6354        }
6355    }
6356
6357    pub fn visibility_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6358        // SAFETY: as above; `&mut self` guarantees exclusivity.
6359        unsafe {
6360            crate::support::RefMut::new(TrackF32 {
6361                raw: core::ptr::NonNull::new_unchecked(
6362                    ffi::whiteout_mdx_MdxRibbonEmitter_get_visibilityTracks(self.raw.as_ptr()),
6363                ),
6364            })
6365        }
6366    }
6367}
6368
6369impl Default for RibbonEmitter {
6370    fn default() -> Self {
6371        Self::new()
6372    }
6373}
6374
6375/// Animation event trigger
6376///
6377/// Event objects fire events at specific animation frames, used to trigger sounds, spawn effects, etc. synchronized with animations.
6378pub struct EventObject {
6379    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxEventObject>,
6380}
6381
6382impl Drop for EventObject {
6383    fn drop(&mut self) {
6384        // SAFETY: `raw` came from a native constructor and Drop runs once.
6385        unsafe { ffi::whiteout_mdx_MdxEventObject_delete(self.raw.as_ptr()) }
6386    }
6387}
6388
6389impl EventObject {
6390    /// # Safety
6391    /// `raw` must be a live handle this value takes ownership of.
6392    #[allow(dead_code)] // used by whichever methods return this type
6393    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxEventObject) -> Option<Self> {
6394        core::ptr::NonNull::new(raw).map(|raw| EventObject { raw })
6395    }
6396}
6397
6398// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6399// is deliberately NOT implemented — the C++ types make no documented
6400// guarantee about concurrent use, and claiming one we haven't verified
6401// would be unsound. See `@bind thread_safe` in the plan.
6402unsafe impl Send for EventObject {}
6403
6404impl core::fmt::Debug for EventObject {
6405    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6406        f.debug_struct("EventObject").finish_non_exhaustive()
6407    }
6408}
6409
6410impl EventObject {
6411    /// # Panics
6412    /// Panics if the native allocation fails.
6413    pub fn new() -> Self {
6414        // SAFETY: the native constructor returns a live handle; a null here
6415        // means the library is unusable.
6416        unsafe {
6417            let raw = ffi::whiteout_mdx_MdxEventObject_new();
6418            Self::from_raw(raw).expect("native EventObject allocation failed")
6419        }
6420    }
6421
6422    /// Base node data
6423    /// Borrows the field in place — no copy, no allocation.
6424    pub fn node(&self) -> crate::support::Ref<'_, Node> {
6425        // SAFETY: an interior pointer into `self`, valid for this
6426        // borrow and never freed by the `Ref`.
6427        unsafe {
6428            crate::support::Ref::new(Node {
6429                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxEventObject_get_node(
6430                    self.raw.as_ptr(),
6431                )),
6432            })
6433        }
6434    }
6435
6436    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
6437        // SAFETY: as above; `&mut self` guarantees exclusivity.
6438        unsafe {
6439            crate::support::RefMut::new(Node {
6440                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxEventObject_get_node(
6441                    self.raw.as_ptr(),
6442                )),
6443            })
6444        }
6445    }
6446
6447    /// Global sequence if looping
6448    pub fn global_sequence_id(&self) -> u32 {
6449        // SAFETY: plain scalar read through a live handle.
6450        unsafe { ffi::whiteout_mdx_MdxEventObject_get_globalSequenceId(self.raw.as_ptr()) }
6451    }
6452
6453    pub fn set_global_sequence_id(&mut self, value: u32) {
6454        // SAFETY: plain scalar write through a live handle.
6455        unsafe { ffi::whiteout_mdx_MdxEventObject_set_globalSequenceId(self.raw.as_ptr(), value) }
6456    }
6457
6458    /// Frame numbers when events fire
6459    /// Zero-copy view of the underlying `std::vector`.
6460    pub fn event_track_times(&self) -> &[u32] {
6461        // SAFETY: `_data`/`_count` describe one contiguous C++
6462        // allocation, borrowed for as long as `self` is.
6463        unsafe {
6464            let n = ffi::whiteout_mdx_MdxEventObject_get_eventTrackTimes_count(self.raw.as_ptr());
6465            let p = ffi::whiteout_mdx_MdxEventObject_get_eventTrackTimes_data(self.raw.as_ptr());
6466            if p.is_null() || n == 0 {
6467                &[]
6468            } else {
6469                core::slice::from_raw_parts(p, n)
6470            }
6471        }
6472    }
6473
6474    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
6475    pub fn event_track_times_mut(&mut self) -> &mut [u32] {
6476        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
6477        unsafe {
6478            let n = ffi::whiteout_mdx_MdxEventObject_get_eventTrackTimes_count(self.raw.as_ptr());
6479            let p = ffi::whiteout_mdx_MdxEventObject_get_eventTrackTimes_data(self.raw.as_ptr())
6480                as *mut u32;
6481            if p.is_null() || n == 0 {
6482                &mut []
6483            } else {
6484                core::slice::from_raw_parts_mut(p, n)
6485            }
6486        }
6487    }
6488
6489    pub fn set_event_track_times(&mut self, values: &[u32]) {
6490        // SAFETY: the native side copies `values` before returning.
6491        unsafe {
6492            ffi::whiteout_mdx_MdxEventObject_assign_eventTrackTimes(
6493                self.raw.as_ptr(),
6494                values.as_ptr() as *const _,
6495                values.len(),
6496            )
6497        }
6498    }
6499
6500    pub fn resize_event_track_times(&mut self, count: usize) {
6501        // SAFETY: reallocation is safe here precisely because
6502        // `&mut self` means no slice borrow is outstanding.
6503        unsafe { ffi::whiteout_mdx_MdxEventObject_resize_eventTrackTimes(self.raw.as_ptr(), count) }
6504    }
6505}
6506
6507impl Default for EventObject {
6508    fn default() -> Self {
6509        Self::new()
6510    }
6511}
6512
6513/// Camera definition
6514///
6515/// Cameras define viewpoints that can be used for portrait renders or in-game cutscenes.
6516pub struct Camera {
6517    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxCamera>,
6518}
6519
6520impl Drop for Camera {
6521    fn drop(&mut self) {
6522        // SAFETY: `raw` came from a native constructor and Drop runs once.
6523        unsafe { ffi::whiteout_mdx_MdxCamera_delete(self.raw.as_ptr()) }
6524    }
6525}
6526
6527impl Camera {
6528    /// # Safety
6529    /// `raw` must be a live handle this value takes ownership of.
6530    #[allow(dead_code)] // used by whichever methods return this type
6531    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxCamera) -> Option<Self> {
6532        core::ptr::NonNull::new(raw).map(|raw| Camera { raw })
6533    }
6534}
6535
6536// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6537// is deliberately NOT implemented — the C++ types make no documented
6538// guarantee about concurrent use, and claiming one we haven't verified
6539// would be unsound. See `@bind thread_safe` in the plan.
6540unsafe impl Send for Camera {}
6541
6542impl core::fmt::Debug for Camera {
6543    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6544        f.debug_struct("Camera").finish_non_exhaustive()
6545    }
6546}
6547
6548impl Camera {
6549    /// # Panics
6550    /// Panics if the native allocation fails.
6551    pub fn new() -> Self {
6552        // SAFETY: the native constructor returns a live handle; a null here
6553        // means the library is unusable.
6554        unsafe {
6555            let raw = ffi::whiteout_mdx_MdxCamera_new();
6556            Self::from_raw(raw).expect("native Camera allocation failed")
6557        }
6558    }
6559
6560    /// Camera name
6561    pub fn name(&self) -> String {
6562        // SAFETY: the native side hands over an owned CString.
6563        unsafe {
6564            crate::support::take_string(ffi::whiteout_mdx_MdxCamera_get_name(self.raw.as_ptr()))
6565        }
6566    }
6567
6568    pub fn set_name(&mut self, value: &str) {
6569        let value = std::ffi::CString::new(value).unwrap_or_default();
6570        // SAFETY: the pointer outlives the call.
6571        unsafe { ffi::whiteout_mdx_MdxCamera_set_name(self.raw.as_ptr(), value.as_ptr()) }
6572    }
6573
6574    /// Camera position
6575    pub fn position(&self) -> crate::math::Vector3f {
6576        // SAFETY: the getter returns an interior pointer to a
6577        // layout-identical POD; we copy it out immediately.
6578        unsafe {
6579            *(ffi::whiteout_mdx_MdxCamera_get_position(self.raw.as_ptr())
6580                as *const crate::math::Vector3f)
6581        }
6582    }
6583
6584    pub fn set_position(&mut self, value: crate::math::Vector3f) {
6585        // SAFETY: as above, in the other direction.
6586        unsafe {
6587            ffi::whiteout_mdx_MdxCamera_set_position(
6588                self.raw.as_ptr(),
6589                &value as *const crate::math::Vector3f as *const _,
6590            )
6591        }
6592    }
6593
6594    /// Field of view angle in radians
6595    pub fn field_of_view(&self) -> f32 {
6596        // SAFETY: plain scalar read through a live handle.
6597        unsafe { ffi::whiteout_mdx_MdxCamera_get_fieldOfView(self.raw.as_ptr()) }
6598    }
6599
6600    pub fn set_field_of_view(&mut self, value: f32) {
6601        // SAFETY: plain scalar write through a live handle.
6602        unsafe { ffi::whiteout_mdx_MdxCamera_set_fieldOfView(self.raw.as_ptr(), value) }
6603    }
6604
6605    /// Far clipping distance
6606    pub fn far_clipping_plane(&self) -> f32 {
6607        // SAFETY: plain scalar read through a live handle.
6608        unsafe { ffi::whiteout_mdx_MdxCamera_get_farClippingPlane(self.raw.as_ptr()) }
6609    }
6610
6611    pub fn set_far_clipping_plane(&mut self, value: f32) {
6612        // SAFETY: plain scalar write through a live handle.
6613        unsafe { ffi::whiteout_mdx_MdxCamera_set_farClippingPlane(self.raw.as_ptr(), value) }
6614    }
6615
6616    /// Near clipping distance
6617    pub fn near_clipping_plane(&self) -> f32 {
6618        // SAFETY: plain scalar read through a live handle.
6619        unsafe { ffi::whiteout_mdx_MdxCamera_get_nearClippingPlane(self.raw.as_ptr()) }
6620    }
6621
6622    pub fn set_near_clipping_plane(&mut self, value: f32) {
6623        // SAFETY: plain scalar write through a live handle.
6624        unsafe { ffi::whiteout_mdx_MdxCamera_set_nearClippingPlane(self.raw.as_ptr(), value) }
6625    }
6626
6627    /// Look-at target position
6628    pub fn target_position(&self) -> crate::math::Vector3f {
6629        // SAFETY: the getter returns an interior pointer to a
6630        // layout-identical POD; we copy it out immediately.
6631        unsafe {
6632            *(ffi::whiteout_mdx_MdxCamera_get_targetPosition(self.raw.as_ptr())
6633                as *const crate::math::Vector3f)
6634        }
6635    }
6636
6637    pub fn set_target_position(&mut self, value: crate::math::Vector3f) {
6638        // SAFETY: as above, in the other direction.
6639        unsafe {
6640            ffi::whiteout_mdx_MdxCamera_set_targetPosition(
6641                self.raw.as_ptr(),
6642                &value as *const crate::math::Vector3f as *const _,
6643            )
6644        }
6645    }
6646
6647    /// Position animation
6648    /// Borrows the field in place — no copy, no allocation.
6649    pub fn position_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
6650        // SAFETY: an interior pointer into `self`, valid for this
6651        // borrow and never freed by the `Ref`.
6652        unsafe {
6653            crate::support::Ref::new(TrackVector3f {
6654                raw: core::ptr::NonNull::new_unchecked(
6655                    ffi::whiteout_mdx_MdxCamera_get_positionTracks(self.raw.as_ptr()),
6656                ),
6657            })
6658        }
6659    }
6660
6661    pub fn position_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
6662        // SAFETY: as above; `&mut self` guarantees exclusivity.
6663        unsafe {
6664            crate::support::RefMut::new(TrackVector3f {
6665                raw: core::ptr::NonNull::new_unchecked(
6666                    ffi::whiteout_mdx_MdxCamera_get_positionTracks(self.raw.as_ptr()),
6667                ),
6668            })
6669        }
6670    }
6671
6672    /// Target rotation animation
6673    /// Borrows the field in place — no copy, no allocation.
6674    pub fn target_rotation_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
6675        // SAFETY: an interior pointer into `self`, valid for this
6676        // borrow and never freed by the `Ref`.
6677        unsafe {
6678            crate::support::Ref::new(TrackF32 {
6679                raw: core::ptr::NonNull::new_unchecked(
6680                    ffi::whiteout_mdx_MdxCamera_get_targetRotationTracks(self.raw.as_ptr()),
6681                ),
6682            })
6683        }
6684    }
6685
6686    pub fn target_rotation_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
6687        // SAFETY: as above; `&mut self` guarantees exclusivity.
6688        unsafe {
6689            crate::support::RefMut::new(TrackF32 {
6690                raw: core::ptr::NonNull::new_unchecked(
6691                    ffi::whiteout_mdx_MdxCamera_get_targetRotationTracks(self.raw.as_ptr()),
6692                ),
6693            })
6694        }
6695    }
6696
6697    /// Target position animation
6698    /// Borrows the field in place — no copy, no allocation.
6699    pub fn target_position_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
6700        // SAFETY: an interior pointer into `self`, valid for this
6701        // borrow and never freed by the `Ref`.
6702        unsafe {
6703            crate::support::Ref::new(TrackVector3f {
6704                raw: core::ptr::NonNull::new_unchecked(
6705                    ffi::whiteout_mdx_MdxCamera_get_targetPositionTracks(self.raw.as_ptr()),
6706                ),
6707            })
6708        }
6709    }
6710
6711    pub fn target_position_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
6712        // SAFETY: as above; `&mut self` guarantees exclusivity.
6713        unsafe {
6714            crate::support::RefMut::new(TrackVector3f {
6715                raw: core::ptr::NonNull::new_unchecked(
6716                    ffi::whiteout_mdx_MdxCamera_get_targetPositionTracks(self.raw.as_ptr()),
6717                ),
6718            })
6719        }
6720    }
6721}
6722
6723impl Default for Camera {
6724    fn default() -> Self {
6725        Self::new()
6726    }
6727}
6728
6729/// Collision volume for physics
6730///
6731/// Collision shapes define simplified geometry for collision detection.
6732pub struct CollisionShape {
6733    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxCollisionShape>,
6734}
6735
6736impl Drop for CollisionShape {
6737    fn drop(&mut self) {
6738        // SAFETY: `raw` came from a native constructor and Drop runs once.
6739        unsafe { ffi::whiteout_mdx_MdxCollisionShape_delete(self.raw.as_ptr()) }
6740    }
6741}
6742
6743impl CollisionShape {
6744    /// # Safety
6745    /// `raw` must be a live handle this value takes ownership of.
6746    #[allow(dead_code)] // used by whichever methods return this type
6747    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxCollisionShape) -> Option<Self> {
6748        core::ptr::NonNull::new(raw).map(|raw| CollisionShape { raw })
6749    }
6750}
6751
6752// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6753// is deliberately NOT implemented — the C++ types make no documented
6754// guarantee about concurrent use, and claiming one we haven't verified
6755// would be unsound. See `@bind thread_safe` in the plan.
6756unsafe impl Send for CollisionShape {}
6757
6758impl core::fmt::Debug for CollisionShape {
6759    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6760        f.debug_struct("CollisionShape").finish_non_exhaustive()
6761    }
6762}
6763
6764impl CollisionShape {
6765    /// # Panics
6766    /// Panics if the native allocation fails.
6767    pub fn new() -> Self {
6768        // SAFETY: the native constructor returns a live handle; a null here
6769        // means the library is unusable.
6770        unsafe {
6771            let raw = ffi::whiteout_mdx_MdxCollisionShape_new();
6772            Self::from_raw(raw).expect("native CollisionShape allocation failed")
6773        }
6774    }
6775
6776    /// Base node data
6777    /// Borrows the field in place — no copy, no allocation.
6778    pub fn node(&self) -> crate::support::Ref<'_, Node> {
6779        // SAFETY: an interior pointer into `self`, valid for this
6780        // borrow and never freed by the `Ref`.
6781        unsafe {
6782            crate::support::Ref::new(Node {
6783                raw: core::ptr::NonNull::new_unchecked(
6784                    ffi::whiteout_mdx_MdxCollisionShape_get_node(self.raw.as_ptr()),
6785                ),
6786            })
6787        }
6788    }
6789
6790    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
6791        // SAFETY: as above; `&mut self` guarantees exclusivity.
6792        unsafe {
6793            crate::support::RefMut::new(Node {
6794                raw: core::ptr::NonNull::new_unchecked(
6795                    ffi::whiteout_mdx_MdxCollisionShape_get_node(self.raw.as_ptr()),
6796                ),
6797            })
6798        }
6799    }
6800
6801    /// Shape type
6802    pub fn type_(&self) -> CollisionShapeShapeType {
6803        // SAFETY: scalar read; the discriminant is validated below.
6804        unsafe { ffi::whiteout_mdx_MdxCollisionShape_get_type(self.raw.as_ptr()) }
6805            .try_into()
6806            .expect("unknown enum discriminant from the native library")
6807    }
6808
6809    pub fn set_type_(&mut self, value: CollisionShapeShapeType) {
6810        // SAFETY: scalar write through a live handle.
6811        unsafe { ffi::whiteout_mdx_MdxCollisionShape_set_type(self.raw.as_ptr(), value as i32) }
6812    }
6813
6814    /// Shape vertices (box only)
6815    /// Zero-copy view of the underlying `std::vector`.
6816    pub fn vertices(&self) -> &[crate::math::Vector3f] {
6817        // SAFETY: `_data`/`_count` describe one contiguous C++
6818        // allocation, borrowed for as long as `self` is.
6819        unsafe {
6820            let n = ffi::whiteout_mdx_MdxCollisionShape_get_vertices_count(self.raw.as_ptr());
6821            let p = ffi::whiteout_mdx_MdxCollisionShape_get_vertices_data(self.raw.as_ptr())
6822                as *const crate::math::Vector3f;
6823            if p.is_null() || n == 0 {
6824                &[]
6825            } else {
6826                core::slice::from_raw_parts(p, n)
6827            }
6828        }
6829    }
6830
6831    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
6832    pub fn vertices_mut(&mut self) -> &mut [crate::math::Vector3f] {
6833        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
6834        unsafe {
6835            let n = ffi::whiteout_mdx_MdxCollisionShape_get_vertices_count(self.raw.as_ptr());
6836            let p = ffi::whiteout_mdx_MdxCollisionShape_get_vertices_data(self.raw.as_ptr())
6837                as *const crate::math::Vector3f as *mut crate::math::Vector3f;
6838            if p.is_null() || n == 0 {
6839                &mut []
6840            } else {
6841                core::slice::from_raw_parts_mut(p, n)
6842            }
6843        }
6844    }
6845
6846    pub fn set_vertices(&mut self, values: &[crate::math::Vector3f]) {
6847        // SAFETY: the native side copies `values` before returning.
6848        unsafe {
6849            ffi::whiteout_mdx_MdxCollisionShape_assign_vertices(
6850                self.raw.as_ptr(),
6851                values.as_ptr() as *const _,
6852                values.len(),
6853            )
6854        }
6855    }
6856
6857    pub fn resize_vertices(&mut self, count: usize) {
6858        // SAFETY: reallocation is safe here precisely because
6859        // `&mut self` means no slice borrow is outstanding.
6860        unsafe { ffi::whiteout_mdx_MdxCollisionShape_resize_vertices(self.raw.as_ptr(), count) }
6861    }
6862
6863    /// Radius (sphere/cylinder only)
6864    pub fn radius(&self) -> f32 {
6865        // SAFETY: plain scalar read through a live handle.
6866        unsafe { ffi::whiteout_mdx_MdxCollisionShape_get_radius(self.raw.as_ptr()) }
6867    }
6868
6869    pub fn set_radius(&mut self, value: f32) {
6870        // SAFETY: plain scalar write through a live handle.
6871        unsafe { ffi::whiteout_mdx_MdxCollisionShape_set_radius(self.raw.as_ptr(), value) }
6872    }
6873}
6874
6875impl Default for CollisionShape {
6876    fn default() -> Self {
6877        Self::new()
6878    }
6879}
6880
6881/// Facial animation effect (Reforged)
6882///
6883/// Holds a name and a path to the FaceFX setup file used for a character's face.
6884pub struct FaceEffect {
6885    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxFaceEffect>,
6886}
6887
6888impl Drop for FaceEffect {
6889    fn drop(&mut self) {
6890        // SAFETY: `raw` came from a native constructor and Drop runs once.
6891        unsafe { ffi::whiteout_mdx_MdxFaceEffect_delete(self.raw.as_ptr()) }
6892    }
6893}
6894
6895impl FaceEffect {
6896    /// # Safety
6897    /// `raw` must be a live handle this value takes ownership of.
6898    #[allow(dead_code)] // used by whichever methods return this type
6899    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxFaceEffect) -> Option<Self> {
6900        core::ptr::NonNull::new(raw).map(|raw| FaceEffect { raw })
6901    }
6902}
6903
6904// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6905// is deliberately NOT implemented — the C++ types make no documented
6906// guarantee about concurrent use, and claiming one we haven't verified
6907// would be unsound. See `@bind thread_safe` in the plan.
6908unsafe impl Send for FaceEffect {}
6909
6910impl core::fmt::Debug for FaceEffect {
6911    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6912        f.debug_struct("FaceEffect").finish_non_exhaustive()
6913    }
6914}
6915
6916impl FaceEffect {
6917    /// # Panics
6918    /// Panics if the native allocation fails.
6919    pub fn new() -> Self {
6920        // SAFETY: the native constructor returns a live handle; a null here
6921        // means the library is unusable.
6922        unsafe {
6923            let raw = ffi::whiteout_mdx_MdxFaceEffect_new();
6924            Self::from_raw(raw).expect("native FaceEffect allocation failed")
6925        }
6926    }
6927
6928    /// Section name
6929    pub fn name(&self) -> String {
6930        // SAFETY: the native side hands over an owned CString.
6931        unsafe {
6932            crate::support::take_string(ffi::whiteout_mdx_MdxFaceEffect_get_name(self.raw.as_ptr()))
6933        }
6934    }
6935
6936    pub fn set_name(&mut self, value: &str) {
6937        let value = std::ffi::CString::new(value).unwrap_or_default();
6938        // SAFETY: the pointer outlives the call.
6939        unsafe { ffi::whiteout_mdx_MdxFaceEffect_set_name(self.raw.as_ptr(), value.as_ptr()) }
6940    }
6941
6942    /// Path to facial animation data
6943    pub fn path(&self) -> String {
6944        // SAFETY: the native side hands over an owned CString.
6945        unsafe {
6946            crate::support::take_string(ffi::whiteout_mdx_MdxFaceEffect_get_path(self.raw.as_ptr()))
6947        }
6948    }
6949
6950    pub fn set_path(&mut self, value: &str) {
6951        let value = std::ffi::CString::new(value).unwrap_or_default();
6952        // SAFETY: the pointer outlives the call.
6953        unsafe { ffi::whiteout_mdx_MdxFaceEffect_set_path(self.raw.as_ptr(), value.as_ptr()) }
6954    }
6955}
6956
6957impl Default for FaceEffect {
6958    fn default() -> Self {
6959        Self::new()
6960    }
6961}
6962
6963/// PopcornFX particle emitter (Reforged)
6964///
6965/// Advanced particle system using PopcornFX technology in Warcraft III: Reforged. Color (RGB) and alpha are stored as separate fields with independent animation tracks.
6966pub struct CornEmitter {
6967    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxCornEmitter>,
6968}
6969
6970impl Drop for CornEmitter {
6971    fn drop(&mut self) {
6972        // SAFETY: `raw` came from a native constructor and Drop runs once.
6973        unsafe { ffi::whiteout_mdx_MdxCornEmitter_delete(self.raw.as_ptr()) }
6974    }
6975}
6976
6977impl CornEmitter {
6978    /// # Safety
6979    /// `raw` must be a live handle this value takes ownership of.
6980    #[allow(dead_code)] // used by whichever methods return this type
6981    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxCornEmitter) -> Option<Self> {
6982        core::ptr::NonNull::new(raw).map(|raw| CornEmitter { raw })
6983    }
6984}
6985
6986// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
6987// is deliberately NOT implemented — the C++ types make no documented
6988// guarantee about concurrent use, and claiming one we haven't verified
6989// would be unsound. See `@bind thread_safe` in the plan.
6990unsafe impl Send for CornEmitter {}
6991
6992impl core::fmt::Debug for CornEmitter {
6993    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6994        f.debug_struct("CornEmitter").finish_non_exhaustive()
6995    }
6996}
6997
6998impl CornEmitter {
6999    /// # Panics
7000    /// Panics if the native allocation fails.
7001    pub fn new() -> Self {
7002        // SAFETY: the native constructor returns a live handle; a null here
7003        // means the library is unusable.
7004        unsafe {
7005            let raw = ffi::whiteout_mdx_MdxCornEmitter_new();
7006            Self::from_raw(raw).expect("native CornEmitter allocation failed")
7007        }
7008    }
7009
7010    /// Base node data
7011    /// Borrows the field in place — no copy, no allocation.
7012    pub fn node(&self) -> crate::support::Ref<'_, Node> {
7013        // SAFETY: an interior pointer into `self`, valid for this
7014        // borrow and never freed by the `Ref`.
7015        unsafe {
7016            crate::support::Ref::new(Node {
7017                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxCornEmitter_get_node(
7018                    self.raw.as_ptr(),
7019                )),
7020            })
7021        }
7022    }
7023
7024    pub fn node_mut(&mut self) -> crate::support::RefMut<'_, Node> {
7025        // SAFETY: as above; `&mut self` guarantees exclusivity.
7026        unsafe {
7027            crate::support::RefMut::new(Node {
7028                raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_mdx_MdxCornEmitter_get_node(
7029                    self.raw.as_ptr(),
7030                )),
7031            })
7032        }
7033    }
7034
7035    /// Particle lifetime (default)
7036    pub fn life_span(&self) -> f32 {
7037        // SAFETY: plain scalar read through a live handle.
7038        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_lifeSpan(self.raw.as_ptr()) }
7039    }
7040
7041    pub fn set_life_span(&mut self, value: f32) {
7042        // SAFETY: plain scalar write through a live handle.
7043        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_lifeSpan(self.raw.as_ptr(), value) }
7044    }
7045
7046    /// Emission rate (default)
7047    pub fn emission_rate(&self) -> f32 {
7048        // SAFETY: plain scalar read through a live handle.
7049        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_emissionRate(self.raw.as_ptr()) }
7050    }
7051
7052    pub fn set_emission_rate(&mut self, value: f32) {
7053        // SAFETY: plain scalar write through a live handle.
7054        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_emissionRate(self.raw.as_ptr(), value) }
7055    }
7056
7057    /// Particle speed (default)
7058    pub fn speed(&self) -> f32 {
7059        // SAFETY: plain scalar read through a live handle.
7060        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_speed(self.raw.as_ptr()) }
7061    }
7062
7063    pub fn set_speed(&mut self, value: f32) {
7064        // SAFETY: plain scalar write through a live handle.
7065        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_speed(self.raw.as_ptr(), value) }
7066    }
7067
7068    /// Particle color (RGB)
7069    pub fn color(&self) -> crate::math::Vector3f {
7070        // SAFETY: the getter returns an interior pointer to a
7071        // layout-identical POD; we copy it out immediately.
7072        unsafe {
7073            *(ffi::whiteout_mdx_MdxCornEmitter_get_color(self.raw.as_ptr())
7074                as *const crate::math::Vector3f)
7075        }
7076    }
7077
7078    pub fn set_color(&mut self, value: crate::math::Vector3f) {
7079        // SAFETY: as above, in the other direction.
7080        unsafe {
7081            ffi::whiteout_mdx_MdxCornEmitter_set_color(
7082                self.raw.as_ptr(),
7083                &value as *const crate::math::Vector3f as *const _,
7084            )
7085        }
7086    }
7087
7088    /// Particle alpha (default)
7089    pub fn alpha(&self) -> f32 {
7090        // SAFETY: plain scalar read through a live handle.
7091        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_alpha(self.raw.as_ptr()) }
7092    }
7093
7094    pub fn set_alpha(&mut self, value: f32) {
7095        // SAFETY: plain scalar write through a live handle.
7096        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_alpha(self.raw.as_ptr(), value) }
7097    }
7098
7099    /// Replaceable texture ID
7100    pub fn replaceable_id(&self) -> u32 {
7101        // SAFETY: plain scalar read through a live handle.
7102        unsafe { ffi::whiteout_mdx_MdxCornEmitter_get_replaceableId(self.raw.as_ptr()) }
7103    }
7104
7105    pub fn set_replaceable_id(&mut self, value: u32) {
7106        // SAFETY: plain scalar write through a live handle.
7107        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_replaceableId(self.raw.as_ptr(), value) }
7108    }
7109
7110    /// Path to PopcornFX effect
7111    pub fn path(&self) -> String {
7112        // SAFETY: the native side hands over an owned CString.
7113        unsafe {
7114            crate::support::take_string(ffi::whiteout_mdx_MdxCornEmitter_get_path(
7115                self.raw.as_ptr(),
7116            ))
7117        }
7118    }
7119
7120    pub fn set_path(&mut self, value: &str) {
7121        let value = std::ffi::CString::new(value).unwrap_or_default();
7122        // SAFETY: the pointer outlives the call.
7123        unsafe { ffi::whiteout_mdx_MdxCornEmitter_set_path(self.raw.as_ptr(), value.as_ptr()) }
7124    }
7125
7126    /// Animation visibility guide
7127    pub fn anim_visibility_guide(&self) -> String {
7128        // SAFETY: the native side hands over an owned CString.
7129        unsafe {
7130            crate::support::take_string(ffi::whiteout_mdx_MdxCornEmitter_get_animVisibilityGuide(
7131                self.raw.as_ptr(),
7132            ))
7133        }
7134    }
7135
7136    pub fn set_anim_visibility_guide(&mut self, value: &str) {
7137        let value = std::ffi::CString::new(value).unwrap_or_default();
7138        // SAFETY: the pointer outlives the call.
7139        unsafe {
7140            ffi::whiteout_mdx_MdxCornEmitter_set_animVisibilityGuide(
7141                self.raw.as_ptr(),
7142                value.as_ptr(),
7143            )
7144        }
7145    }
7146
7147    /// Lifespan animation
7148    /// Borrows the field in place — no copy, no allocation.
7149    pub fn life_span_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7150        // SAFETY: an interior pointer into `self`, valid for this
7151        // borrow and never freed by the `Ref`.
7152        unsafe {
7153            crate::support::Ref::new(TrackF32 {
7154                raw: core::ptr::NonNull::new_unchecked(
7155                    ffi::whiteout_mdx_MdxCornEmitter_get_lifeSpanTracks(self.raw.as_ptr()),
7156                ),
7157            })
7158        }
7159    }
7160
7161    pub fn life_span_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7162        // SAFETY: as above; `&mut self` guarantees exclusivity.
7163        unsafe {
7164            crate::support::RefMut::new(TrackF32 {
7165                raw: core::ptr::NonNull::new_unchecked(
7166                    ffi::whiteout_mdx_MdxCornEmitter_get_lifeSpanTracks(self.raw.as_ptr()),
7167                ),
7168            })
7169        }
7170    }
7171
7172    /// Emission rate animation
7173    /// Borrows the field in place — no copy, no allocation.
7174    pub fn emission_rate_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7175        // SAFETY: an interior pointer into `self`, valid for this
7176        // borrow and never freed by the `Ref`.
7177        unsafe {
7178            crate::support::Ref::new(TrackF32 {
7179                raw: core::ptr::NonNull::new_unchecked(
7180                    ffi::whiteout_mdx_MdxCornEmitter_get_emissionRateTracks(self.raw.as_ptr()),
7181                ),
7182            })
7183        }
7184    }
7185
7186    pub fn emission_rate_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7187        // SAFETY: as above; `&mut self` guarantees exclusivity.
7188        unsafe {
7189            crate::support::RefMut::new(TrackF32 {
7190                raw: core::ptr::NonNull::new_unchecked(
7191                    ffi::whiteout_mdx_MdxCornEmitter_get_emissionRateTracks(self.raw.as_ptr()),
7192                ),
7193            })
7194        }
7195    }
7196
7197    /// Speed animation
7198    /// Borrows the field in place — no copy, no allocation.
7199    pub fn speed_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7200        // SAFETY: an interior pointer into `self`, valid for this
7201        // borrow and never freed by the `Ref`.
7202        unsafe {
7203            crate::support::Ref::new(TrackF32 {
7204                raw: core::ptr::NonNull::new_unchecked(
7205                    ffi::whiteout_mdx_MdxCornEmitter_get_speedTracks(self.raw.as_ptr()),
7206                ),
7207            })
7208        }
7209    }
7210
7211    pub fn speed_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7212        // SAFETY: as above; `&mut self` guarantees exclusivity.
7213        unsafe {
7214            crate::support::RefMut::new(TrackF32 {
7215                raw: core::ptr::NonNull::new_unchecked(
7216                    ffi::whiteout_mdx_MdxCornEmitter_get_speedTracks(self.raw.as_ptr()),
7217                ),
7218            })
7219        }
7220    }
7221
7222    /// Color animation
7223    /// Borrows the field in place — no copy, no allocation.
7224    pub fn color_tracks(&self) -> crate::support::Ref<'_, TrackVector3f> {
7225        // SAFETY: an interior pointer into `self`, valid for this
7226        // borrow and never freed by the `Ref`.
7227        unsafe {
7228            crate::support::Ref::new(TrackVector3f {
7229                raw: core::ptr::NonNull::new_unchecked(
7230                    ffi::whiteout_mdx_MdxCornEmitter_get_colorTracks(self.raw.as_ptr()),
7231                ),
7232            })
7233        }
7234    }
7235
7236    pub fn color_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackVector3f> {
7237        // SAFETY: as above; `&mut self` guarantees exclusivity.
7238        unsafe {
7239            crate::support::RefMut::new(TrackVector3f {
7240                raw: core::ptr::NonNull::new_unchecked(
7241                    ffi::whiteout_mdx_MdxCornEmitter_get_colorTracks(self.raw.as_ptr()),
7242                ),
7243            })
7244        }
7245    }
7246
7247    /// Alpha animation
7248    /// Borrows the field in place — no copy, no allocation.
7249    pub fn alpha_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7250        // SAFETY: an interior pointer into `self`, valid for this
7251        // borrow and never freed by the `Ref`.
7252        unsafe {
7253            crate::support::Ref::new(TrackF32 {
7254                raw: core::ptr::NonNull::new_unchecked(
7255                    ffi::whiteout_mdx_MdxCornEmitter_get_alphaTracks(self.raw.as_ptr()),
7256                ),
7257            })
7258        }
7259    }
7260
7261    pub fn alpha_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7262        // SAFETY: as above; `&mut self` guarantees exclusivity.
7263        unsafe {
7264            crate::support::RefMut::new(TrackF32 {
7265                raw: core::ptr::NonNull::new_unchecked(
7266                    ffi::whiteout_mdx_MdxCornEmitter_get_alphaTracks(self.raw.as_ptr()),
7267                ),
7268            })
7269        }
7270    }
7271
7272    /// Visibility animation
7273    /// Borrows the field in place — no copy, no allocation.
7274    pub fn visibility_tracks(&self) -> crate::support::Ref<'_, TrackF32> {
7275        // SAFETY: an interior pointer into `self`, valid for this
7276        // borrow and never freed by the `Ref`.
7277        unsafe {
7278            crate::support::Ref::new(TrackF32 {
7279                raw: core::ptr::NonNull::new_unchecked(
7280                    ffi::whiteout_mdx_MdxCornEmitter_get_visibilityTracks(self.raw.as_ptr()),
7281                ),
7282            })
7283        }
7284    }
7285
7286    pub fn visibility_tracks_mut(&mut self) -> crate::support::RefMut<'_, TrackF32> {
7287        // SAFETY: as above; `&mut self` guarantees exclusivity.
7288        unsafe {
7289            crate::support::RefMut::new(TrackF32 {
7290                raw: core::ptr::NonNull::new_unchecked(
7291                    ffi::whiteout_mdx_MdxCornEmitter_get_visibilityTracks(self.raw.as_ptr()),
7292                ),
7293            })
7294        }
7295    }
7296}
7297
7298impl Default for CornEmitter {
7299    fn default() -> Self {
7300        Self::new()
7301    }
7302}
7303
7304/// Parser for MDX model files
7305///
7306/// The Parser reads binary MDX files and converts them into the Model structure. It supports multiple parsing modes and can handle version differences.
7307///
7308/// Uses the PImpl (Pointer to Implementation) idiom to hide implementation details.
7309pub struct Parser {
7310    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxParser>,
7311}
7312
7313impl Drop for Parser {
7314    fn drop(&mut self) {
7315        // SAFETY: `raw` came from a native constructor and Drop runs once.
7316        unsafe { ffi::whiteout_mdx_MdxParser_delete(self.raw.as_ptr()) }
7317    }
7318}
7319
7320impl Parser {
7321    /// # Safety
7322    /// `raw` must be a live handle this value takes ownership of.
7323    #[allow(dead_code)] // used by whichever methods return this type
7324    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxParser) -> Option<Self> {
7325        core::ptr::NonNull::new(raw).map(|raw| Parser { raw })
7326    }
7327}
7328
7329// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7330// is deliberately NOT implemented — the C++ types make no documented
7331// guarantee about concurrent use, and claiming one we haven't verified
7332// would be unsound. See `@bind thread_safe` in the plan.
7333unsafe impl Send for Parser {}
7334
7335impl core::fmt::Debug for Parser {
7336    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7337        f.debug_struct("Parser").finish_non_exhaustive()
7338    }
7339}
7340
7341impl Parser {
7342    /// # Panics
7343    /// Panics if the native allocation fails.
7344    pub fn new() -> Self {
7345        // SAFETY: the native constructor returns a live handle; a null here
7346        // means the library is unusable.
7347        unsafe {
7348            let raw = ffi::whiteout_mdx_MdxParser_new();
7349            Self::from_raw(raw).expect("native Parser allocation failed")
7350        }
7351    }
7352
7353    /// Parse an MDX file from disk
7354    ///
7355    /// The format is detected from the file extension: `.mdl` for text MDL format, `.mdx` (or any other extension) for binary MDX format.
7356    ///
7357    /// @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
7358    pub fn parse_file(&mut self, file_path: &str) -> Option<Model> {
7359        let file_path_cstr = std::ffi::CString::new(file_path).unwrap_or_default();
7360        // SAFETY: handle is live for the duration of the call.
7361        unsafe {
7362            Model::from_raw(ffi::whiteout_mdx_MdxParser_parse(
7363                self.raw.as_ptr(),
7364                file_path_cstr.as_ptr(),
7365            ))
7366        }
7367    }
7368
7369    /// 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
7370    pub fn parse(&mut self, buffer: &[u8], format: MDLXFormat) -> Option<Model> {
7371        // SAFETY: handle is live for the duration of the call.
7372        unsafe {
7373            Model::from_raw(ffi::whiteout_mdx_MdxParser_parse_buffer_format(
7374                self.raw.as_ptr(),
7375                buffer.as_ptr(),
7376                buffer.len(),
7377                format as i32,
7378            ))
7379        }
7380    }
7381
7382    /// Check if parsing encountered any issues @return True if there were warnings or recoverable errors
7383    pub fn has_issues(&self) -> bool {
7384        // SAFETY: handle is live for the duration of the call.
7385        unsafe { ffi::whiteout_mdx_MdxParser_hasIssues(self.raw.as_ptr()) != 0 }
7386    }
7387
7388    /// Get list of issues encountered during parsing @return Vector of issue description strings
7389    pub fn issues(&self) -> Vec<String> {
7390        // SAFETY: index stays below the reported count.
7391        unsafe {
7392            let n = ffi::whiteout_mdx_MdxParser_getIssues_count(self.raw.as_ptr());
7393            (0..n)
7394                .map(|i| {
7395                    crate::support::take_string(ffi::whiteout_mdx_MdxParser_getIssues_at(
7396                        self.raw.as_ptr(),
7397                        i,
7398                    ))
7399                })
7400                .collect()
7401        }
7402    }
7403}
7404
7405impl Default for Parser {
7406    fn default() -> Self {
7407        Self::new()
7408    }
7409}
7410
7411/// Writer for MDX/MDL model files
7412///
7413/// 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.
7414///
7415/// The format is selected either by file extension (.mdl → text, .mdx → binary) or by an explicit MDLXFormat parameter.
7416///
7417/// Uses the PImpl (Pointer to Implementation) idiom to hide implementation details.
7418pub struct Writer {
7419    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxWriter>,
7420}
7421
7422impl Drop for Writer {
7423    fn drop(&mut self) {
7424        // SAFETY: `raw` came from a native constructor and Drop runs once.
7425        unsafe { ffi::whiteout_mdx_MdxWriter_delete(self.raw.as_ptr()) }
7426    }
7427}
7428
7429impl Writer {
7430    /// # Safety
7431    /// `raw` must be a live handle this value takes ownership of.
7432    #[allow(dead_code)] // used by whichever methods return this type
7433    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxWriter) -> Option<Self> {
7434        core::ptr::NonNull::new(raw).map(|raw| Writer { raw })
7435    }
7436}
7437
7438// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7439// is deliberately NOT implemented — the C++ types make no documented
7440// guarantee about concurrent use, and claiming one we haven't verified
7441// would be unsound. See `@bind thread_safe` in the plan.
7442unsafe impl Send for Writer {}
7443
7444impl core::fmt::Debug for Writer {
7445    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7446        f.debug_struct("Writer").finish_non_exhaustive()
7447    }
7448}
7449
7450impl Writer {
7451    /// # Panics
7452    /// Panics if the native allocation fails.
7453    pub fn new() -> Self {
7454        // SAFETY: the native constructor returns a live handle; a null here
7455        // means the library is unusable.
7456        unsafe {
7457            let raw = ffi::whiteout_mdx_MdxWriter_new();
7458            Self::from_raw(raw).expect("native Writer allocation failed")
7459        }
7460    }
7461
7462    /// Write a model file to disk
7463    ///
7464    /// The format is detected from the file extension: `.mdl` for text MDL format, `.mdx` (or any other extension) for binary MDX format.
7465    ///
7466    /// @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
7467    pub fn write_file(&mut self, file_path: &str, mdlx: &Model, mdl_format: MdlFormat) {
7468        let file_path_cstr = std::ffi::CString::new(file_path).unwrap_or_default();
7469        // SAFETY: handle is live for the duration of the call.
7470        unsafe {
7471            ffi::whiteout_mdx_MdxWriter_write(
7472                self.raw.as_ptr(),
7473                file_path_cstr.as_ptr(),
7474                mdlx.raw.as_ptr(),
7475                mdl_format as i32,
7476            );
7477        }
7478    }
7479
7480    /// 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
7481    pub fn write(&mut self, mdx: &Model, format: MDLXFormat, mdl_format: MdlFormat) -> Bytes {
7482        // SAFETY: handle is live for the duration of the call.
7483        unsafe {
7484            Bytes::from_raw(ffi::whiteout_mdx_MdxWriter_write_mdx_format_mdlFormat(
7485                self.raw.as_ptr(),
7486                mdx.raw.as_ptr(),
7487                format as i32,
7488                mdl_format as i32,
7489            ))
7490            .unwrap_or_else(Bytes::empty)
7491        }
7492    }
7493}
7494
7495impl Default for Writer {
7496    fn default() -> Self {
7497        Self::new()
7498    }
7499}
7500
7501/// Generic animation track with keyframe data
7502///
7503/// Tracks store animation data as a series of keyframes. The interpolation type determines how values between keyframes are calculated.
7504///
7505/// @tparam T The value type (f32, Vector3f, Vector4f, u32, etc.)
7506pub struct TrackVector3f {
7507    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTrackVector3f>,
7508}
7509
7510impl Drop for TrackVector3f {
7511    fn drop(&mut self) {
7512        // SAFETY: `raw` came from a native constructor and Drop runs once.
7513        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_delete(self.raw.as_ptr()) }
7514    }
7515}
7516
7517impl TrackVector3f {
7518    /// # Safety
7519    /// `raw` must be a live handle this value takes ownership of.
7520    #[allow(dead_code)] // used by whichever methods return this type
7521    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTrackVector3f) -> Option<Self> {
7522        core::ptr::NonNull::new(raw).map(|raw| TrackVector3f { raw })
7523    }
7524}
7525
7526// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7527// is deliberately NOT implemented — the C++ types make no documented
7528// guarantee about concurrent use, and claiming one we haven't verified
7529// would be unsound. See `@bind thread_safe` in the plan.
7530unsafe impl Send for TrackVector3f {}
7531
7532impl core::fmt::Debug for TrackVector3f {
7533    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7534        f.debug_struct("TrackVector3f").finish_non_exhaustive()
7535    }
7536}
7537
7538impl TrackVector3f {
7539    /// # Panics
7540    /// Panics if the native allocation fails.
7541    pub fn new() -> Self {
7542        // SAFETY: the native constructor returns a live handle; a null here
7543        // means the library is unusable.
7544        unsafe {
7545            let raw = ffi::whiteout_mdx_MdxTrackVector3f_new();
7546            Self::from_raw(raw).expect("native TrackVector3f allocation failed")
7547        }
7548    }
7549
7550    /// Whether this track contains animation data
7551    pub fn is_used(&self) -> bool {
7552        // SAFETY: plain scalar read through a live handle.
7553        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_get_isUsed(self.raw.as_ptr()) != 0 }
7554    }
7555
7556    pub fn set_is_used(&mut self, value: bool) {
7557        // SAFETY: plain scalar write through a live handle.
7558        unsafe {
7559            ffi::whiteout_mdx_MdxTrackVector3f_set_isUsed(
7560                self.raw.as_ptr(),
7561                if value { 1 } else { 0 },
7562            )
7563        }
7564    }
7565
7566    /// Interpolation type
7567    pub fn interpolation_type(&self) -> InterpolationType {
7568        // SAFETY: scalar read; the discriminant is validated below.
7569        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_get_interpolationType(self.raw.as_ptr()) }
7570            .try_into()
7571            .expect("unknown enum discriminant from the native library")
7572    }
7573
7574    pub fn set_interpolation_type(&mut self, value: InterpolationType) {
7575        // SAFETY: scalar write through a live handle.
7576        unsafe {
7577            ffi::whiteout_mdx_MdxTrackVector3f_set_interpolationType(
7578                self.raw.as_ptr(),
7579                value as i32,
7580            )
7581        }
7582    }
7583
7584    /// Global sequence ID if applicable
7585    pub fn global_sequence_id(&self) -> u32 {
7586        // SAFETY: plain scalar read through a live handle.
7587        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_get_globalSequenceId(self.raw.as_ptr()) }
7588    }
7589
7590    pub fn set_global_sequence_id(&mut self, value: u32) {
7591        // SAFETY: plain scalar write through a live handle.
7592        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_set_globalSequenceId(self.raw.as_ptr(), value) }
7593    }
7594
7595    /// Number of keyframes
7596    pub fn key_count(&self) -> usize {
7597        // SAFETY: plain scalar read through a live handle.
7598        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_get_keyCount(self.raw.as_ptr()) }
7599    }
7600
7601    pub fn set_key_count(&mut self, value: usize) {
7602        // SAFETY: plain scalar write through a live handle.
7603        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_set_keyCount(self.raw.as_ptr(), value) }
7604    }
7605
7606    /// Keyframe timestamps (in frames)
7607    /// Zero-copy view of the underlying `std::vector`.
7608    pub fn timestamps(&self) -> &[u32] {
7609        // SAFETY: `_data`/`_count` describe one contiguous C++
7610        // allocation, borrowed for as long as `self` is.
7611        unsafe {
7612            let n = ffi::whiteout_mdx_MdxTrackVector3f_get_timestamps_count(self.raw.as_ptr());
7613            let p = ffi::whiteout_mdx_MdxTrackVector3f_get_timestamps_data(self.raw.as_ptr());
7614            if p.is_null() || n == 0 {
7615                &[]
7616            } else {
7617                core::slice::from_raw_parts(p, n)
7618            }
7619        }
7620    }
7621
7622    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
7623    pub fn timestamps_mut(&mut self) -> &mut [u32] {
7624        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
7625        unsafe {
7626            let n = ffi::whiteout_mdx_MdxTrackVector3f_get_timestamps_count(self.raw.as_ptr());
7627            let p = ffi::whiteout_mdx_MdxTrackVector3f_get_timestamps_data(self.raw.as_ptr())
7628                as *mut u32;
7629            if p.is_null() || n == 0 {
7630                &mut []
7631            } else {
7632                core::slice::from_raw_parts_mut(p, n)
7633            }
7634        }
7635    }
7636
7637    pub fn set_timestamps(&mut self, values: &[u32]) {
7638        // SAFETY: the native side copies `values` before returning.
7639        unsafe {
7640            ffi::whiteout_mdx_MdxTrackVector3f_assign_timestamps(
7641                self.raw.as_ptr(),
7642                values.as_ptr() as *const _,
7643                values.len(),
7644            )
7645        }
7646    }
7647
7648    pub fn resize_timestamps(&mut self, count: usize) {
7649        // SAFETY: reallocation is safe here precisely because
7650        // `&mut self` means no slice borrow is outstanding.
7651        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_resize_timestamps(self.raw.as_ptr(), count) }
7652    }
7653
7654    /// Raw keyframe data
7655    /// Zero-copy view of the underlying `std::vector`.
7656    pub fn keys(&self) -> &[crate::math::Vector3f] {
7657        // SAFETY: `_data`/`_count` describe one contiguous C++
7658        // allocation, borrowed for as long as `self` is.
7659        unsafe {
7660            let n = ffi::whiteout_mdx_MdxTrackVector3f_get_keys_count(self.raw.as_ptr());
7661            let p = ffi::whiteout_mdx_MdxTrackVector3f_get_keys_data(self.raw.as_ptr())
7662                as *const crate::math::Vector3f;
7663            if p.is_null() || n == 0 {
7664                &[]
7665            } else {
7666                core::slice::from_raw_parts(p, n)
7667            }
7668        }
7669    }
7670
7671    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
7672    pub fn keys_mut(&mut self) -> &mut [crate::math::Vector3f] {
7673        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
7674        unsafe {
7675            let n = ffi::whiteout_mdx_MdxTrackVector3f_get_keys_count(self.raw.as_ptr());
7676            let p = ffi::whiteout_mdx_MdxTrackVector3f_get_keys_data(self.raw.as_ptr())
7677                as *const crate::math::Vector3f as *mut crate::math::Vector3f;
7678            if p.is_null() || n == 0 {
7679                &mut []
7680            } else {
7681                core::slice::from_raw_parts_mut(p, n)
7682            }
7683        }
7684    }
7685
7686    pub fn set_keys(&mut self, values: &[crate::math::Vector3f]) {
7687        // SAFETY: the native side copies `values` before returning.
7688        unsafe {
7689            ffi::whiteout_mdx_MdxTrackVector3f_assign_keys(
7690                self.raw.as_ptr(),
7691                values.as_ptr() as *const _,
7692                values.len(),
7693            )
7694        }
7695    }
7696
7697    pub fn resize_keys(&mut self, count: usize) {
7698        // SAFETY: reallocation is safe here precisely because
7699        // `&mut self` means no slice borrow is outstanding.
7700        unsafe { ffi::whiteout_mdx_MdxTrackVector3f_resize_keys(self.raw.as_ptr(), count) }
7701    }
7702}
7703
7704impl Default for TrackVector3f {
7705    fn default() -> Self {
7706        Self::new()
7707    }
7708}
7709
7710/// Generic animation track with keyframe data
7711///
7712/// Tracks store animation data as a series of keyframes. The interpolation type determines how values between keyframes are calculated.
7713///
7714/// @tparam T The value type (f32, Vector3f, Vector4f, u32, etc.)
7715pub struct TrackQuaternion {
7716    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTrackQuaternion>,
7717}
7718
7719impl Drop for TrackQuaternion {
7720    fn drop(&mut self) {
7721        // SAFETY: `raw` came from a native constructor and Drop runs once.
7722        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_delete(self.raw.as_ptr()) }
7723    }
7724}
7725
7726impl TrackQuaternion {
7727    /// # Safety
7728    /// `raw` must be a live handle this value takes ownership of.
7729    #[allow(dead_code)] // used by whichever methods return this type
7730    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTrackQuaternion) -> Option<Self> {
7731        core::ptr::NonNull::new(raw).map(|raw| TrackQuaternion { raw })
7732    }
7733}
7734
7735// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7736// is deliberately NOT implemented — the C++ types make no documented
7737// guarantee about concurrent use, and claiming one we haven't verified
7738// would be unsound. See `@bind thread_safe` in the plan.
7739unsafe impl Send for TrackQuaternion {}
7740
7741impl core::fmt::Debug for TrackQuaternion {
7742    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7743        f.debug_struct("TrackQuaternion").finish_non_exhaustive()
7744    }
7745}
7746
7747impl TrackQuaternion {
7748    /// # Panics
7749    /// Panics if the native allocation fails.
7750    pub fn new() -> Self {
7751        // SAFETY: the native constructor returns a live handle; a null here
7752        // means the library is unusable.
7753        unsafe {
7754            let raw = ffi::whiteout_mdx_MdxTrackQuaternion_new();
7755            Self::from_raw(raw).expect("native TrackQuaternion allocation failed")
7756        }
7757    }
7758
7759    /// Whether this track contains animation data
7760    pub fn is_used(&self) -> bool {
7761        // SAFETY: plain scalar read through a live handle.
7762        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_get_isUsed(self.raw.as_ptr()) != 0 }
7763    }
7764
7765    pub fn set_is_used(&mut self, value: bool) {
7766        // SAFETY: plain scalar write through a live handle.
7767        unsafe {
7768            ffi::whiteout_mdx_MdxTrackQuaternion_set_isUsed(
7769                self.raw.as_ptr(),
7770                if value { 1 } else { 0 },
7771            )
7772        }
7773    }
7774
7775    /// Interpolation type
7776    pub fn interpolation_type(&self) -> InterpolationType {
7777        // SAFETY: scalar read; the discriminant is validated below.
7778        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_get_interpolationType(self.raw.as_ptr()) }
7779            .try_into()
7780            .expect("unknown enum discriminant from the native library")
7781    }
7782
7783    pub fn set_interpolation_type(&mut self, value: InterpolationType) {
7784        // SAFETY: scalar write through a live handle.
7785        unsafe {
7786            ffi::whiteout_mdx_MdxTrackQuaternion_set_interpolationType(
7787                self.raw.as_ptr(),
7788                value as i32,
7789            )
7790        }
7791    }
7792
7793    /// Global sequence ID if applicable
7794    pub fn global_sequence_id(&self) -> u32 {
7795        // SAFETY: plain scalar read through a live handle.
7796        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_get_globalSequenceId(self.raw.as_ptr()) }
7797    }
7798
7799    pub fn set_global_sequence_id(&mut self, value: u32) {
7800        // SAFETY: plain scalar write through a live handle.
7801        unsafe {
7802            ffi::whiteout_mdx_MdxTrackQuaternion_set_globalSequenceId(self.raw.as_ptr(), value)
7803        }
7804    }
7805
7806    /// Number of keyframes
7807    pub fn key_count(&self) -> usize {
7808        // SAFETY: plain scalar read through a live handle.
7809        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_get_keyCount(self.raw.as_ptr()) }
7810    }
7811
7812    pub fn set_key_count(&mut self, value: usize) {
7813        // SAFETY: plain scalar write through a live handle.
7814        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_set_keyCount(self.raw.as_ptr(), value) }
7815    }
7816
7817    /// Keyframe timestamps (in frames)
7818    /// Zero-copy view of the underlying `std::vector`.
7819    pub fn timestamps(&self) -> &[u32] {
7820        // SAFETY: `_data`/`_count` describe one contiguous C++
7821        // allocation, borrowed for as long as `self` is.
7822        unsafe {
7823            let n = ffi::whiteout_mdx_MdxTrackQuaternion_get_timestamps_count(self.raw.as_ptr());
7824            let p = ffi::whiteout_mdx_MdxTrackQuaternion_get_timestamps_data(self.raw.as_ptr());
7825            if p.is_null() || n == 0 {
7826                &[]
7827            } else {
7828                core::slice::from_raw_parts(p, n)
7829            }
7830        }
7831    }
7832
7833    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
7834    pub fn timestamps_mut(&mut self) -> &mut [u32] {
7835        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
7836        unsafe {
7837            let n = ffi::whiteout_mdx_MdxTrackQuaternion_get_timestamps_count(self.raw.as_ptr());
7838            let p = ffi::whiteout_mdx_MdxTrackQuaternion_get_timestamps_data(self.raw.as_ptr())
7839                as *mut u32;
7840            if p.is_null() || n == 0 {
7841                &mut []
7842            } else {
7843                core::slice::from_raw_parts_mut(p, n)
7844            }
7845        }
7846    }
7847
7848    pub fn set_timestamps(&mut self, values: &[u32]) {
7849        // SAFETY: the native side copies `values` before returning.
7850        unsafe {
7851            ffi::whiteout_mdx_MdxTrackQuaternion_assign_timestamps(
7852                self.raw.as_ptr(),
7853                values.as_ptr() as *const _,
7854                values.len(),
7855            )
7856        }
7857    }
7858
7859    pub fn resize_timestamps(&mut self, count: usize) {
7860        // SAFETY: reallocation is safe here precisely because
7861        // `&mut self` means no slice borrow is outstanding.
7862        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_resize_timestamps(self.raw.as_ptr(), count) }
7863    }
7864
7865    /// Raw keyframe data
7866    /// Zero-copy view of the underlying `std::vector`.
7867    pub fn keys(&self) -> &[crate::math::Quaternion] {
7868        // SAFETY: `_data`/`_count` describe one contiguous C++
7869        // allocation, borrowed for as long as `self` is.
7870        unsafe {
7871            let n = ffi::whiteout_mdx_MdxTrackQuaternion_get_keys_count(self.raw.as_ptr());
7872            let p = ffi::whiteout_mdx_MdxTrackQuaternion_get_keys_data(self.raw.as_ptr())
7873                as *const crate::math::Quaternion;
7874            if p.is_null() || n == 0 {
7875                &[]
7876            } else {
7877                core::slice::from_raw_parts(p, n)
7878            }
7879        }
7880    }
7881
7882    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
7883    pub fn keys_mut(&mut self) -> &mut [crate::math::Quaternion] {
7884        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
7885        unsafe {
7886            let n = ffi::whiteout_mdx_MdxTrackQuaternion_get_keys_count(self.raw.as_ptr());
7887            let p = ffi::whiteout_mdx_MdxTrackQuaternion_get_keys_data(self.raw.as_ptr())
7888                as *const crate::math::Quaternion
7889                as *mut crate::math::Quaternion;
7890            if p.is_null() || n == 0 {
7891                &mut []
7892            } else {
7893                core::slice::from_raw_parts_mut(p, n)
7894            }
7895        }
7896    }
7897
7898    pub fn set_keys(&mut self, values: &[crate::math::Quaternion]) {
7899        // SAFETY: the native side copies `values` before returning.
7900        unsafe {
7901            ffi::whiteout_mdx_MdxTrackQuaternion_assign_keys(
7902                self.raw.as_ptr(),
7903                values.as_ptr() as *const _,
7904                values.len(),
7905            )
7906        }
7907    }
7908
7909    pub fn resize_keys(&mut self, count: usize) {
7910        // SAFETY: reallocation is safe here precisely because
7911        // `&mut self` means no slice borrow is outstanding.
7912        unsafe { ffi::whiteout_mdx_MdxTrackQuaternion_resize_keys(self.raw.as_ptr(), count) }
7913    }
7914}
7915
7916impl Default for TrackQuaternion {
7917    fn default() -> Self {
7918        Self::new()
7919    }
7920}
7921
7922/// Generic animation track with keyframe data
7923///
7924/// Tracks store animation data as a series of keyframes. The interpolation type determines how values between keyframes are calculated.
7925///
7926/// @tparam T The value type (f32, Vector3f, Vector4f, u32, etc.)
7927pub struct TrackU32 {
7928    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTrackU32>,
7929}
7930
7931impl Drop for TrackU32 {
7932    fn drop(&mut self) {
7933        // SAFETY: `raw` came from a native constructor and Drop runs once.
7934        unsafe { ffi::whiteout_mdx_MdxTrackU32_delete(self.raw.as_ptr()) }
7935    }
7936}
7937
7938impl TrackU32 {
7939    /// # Safety
7940    /// `raw` must be a live handle this value takes ownership of.
7941    #[allow(dead_code)] // used by whichever methods return this type
7942    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTrackU32) -> Option<Self> {
7943        core::ptr::NonNull::new(raw).map(|raw| TrackU32 { raw })
7944    }
7945}
7946
7947// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
7948// is deliberately NOT implemented — the C++ types make no documented
7949// guarantee about concurrent use, and claiming one we haven't verified
7950// would be unsound. See `@bind thread_safe` in the plan.
7951unsafe impl Send for TrackU32 {}
7952
7953impl core::fmt::Debug for TrackU32 {
7954    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7955        f.debug_struct("TrackU32").finish_non_exhaustive()
7956    }
7957}
7958
7959impl TrackU32 {
7960    /// # Panics
7961    /// Panics if the native allocation fails.
7962    pub fn new() -> Self {
7963        // SAFETY: the native constructor returns a live handle; a null here
7964        // means the library is unusable.
7965        unsafe {
7966            let raw = ffi::whiteout_mdx_MdxTrackU32_new();
7967            Self::from_raw(raw).expect("native TrackU32 allocation failed")
7968        }
7969    }
7970
7971    /// Whether this track contains animation data
7972    pub fn is_used(&self) -> bool {
7973        // SAFETY: plain scalar read through a live handle.
7974        unsafe { ffi::whiteout_mdx_MdxTrackU32_get_isUsed(self.raw.as_ptr()) != 0 }
7975    }
7976
7977    pub fn set_is_used(&mut self, value: bool) {
7978        // SAFETY: plain scalar write through a live handle.
7979        unsafe {
7980            ffi::whiteout_mdx_MdxTrackU32_set_isUsed(self.raw.as_ptr(), if value { 1 } else { 0 })
7981        }
7982    }
7983
7984    /// Interpolation type
7985    pub fn interpolation_type(&self) -> InterpolationType {
7986        // SAFETY: scalar read; the discriminant is validated below.
7987        unsafe { ffi::whiteout_mdx_MdxTrackU32_get_interpolationType(self.raw.as_ptr()) }
7988            .try_into()
7989            .expect("unknown enum discriminant from the native library")
7990    }
7991
7992    pub fn set_interpolation_type(&mut self, value: InterpolationType) {
7993        // SAFETY: scalar write through a live handle.
7994        unsafe {
7995            ffi::whiteout_mdx_MdxTrackU32_set_interpolationType(self.raw.as_ptr(), value as i32)
7996        }
7997    }
7998
7999    /// Global sequence ID if applicable
8000    pub fn global_sequence_id(&self) -> u32 {
8001        // SAFETY: plain scalar read through a live handle.
8002        unsafe { ffi::whiteout_mdx_MdxTrackU32_get_globalSequenceId(self.raw.as_ptr()) }
8003    }
8004
8005    pub fn set_global_sequence_id(&mut self, value: u32) {
8006        // SAFETY: plain scalar write through a live handle.
8007        unsafe { ffi::whiteout_mdx_MdxTrackU32_set_globalSequenceId(self.raw.as_ptr(), value) }
8008    }
8009
8010    /// Number of keyframes
8011    pub fn key_count(&self) -> usize {
8012        // SAFETY: plain scalar read through a live handle.
8013        unsafe { ffi::whiteout_mdx_MdxTrackU32_get_keyCount(self.raw.as_ptr()) }
8014    }
8015
8016    pub fn set_key_count(&mut self, value: usize) {
8017        // SAFETY: plain scalar write through a live handle.
8018        unsafe { ffi::whiteout_mdx_MdxTrackU32_set_keyCount(self.raw.as_ptr(), value) }
8019    }
8020
8021    /// Keyframe timestamps (in frames)
8022    /// Zero-copy view of the underlying `std::vector`.
8023    pub fn timestamps(&self) -> &[u32] {
8024        // SAFETY: `_data`/`_count` describe one contiguous C++
8025        // allocation, borrowed for as long as `self` is.
8026        unsafe {
8027            let n = ffi::whiteout_mdx_MdxTrackU32_get_timestamps_count(self.raw.as_ptr());
8028            let p = ffi::whiteout_mdx_MdxTrackU32_get_timestamps_data(self.raw.as_ptr());
8029            if p.is_null() || n == 0 {
8030                &[]
8031            } else {
8032                core::slice::from_raw_parts(p, n)
8033            }
8034        }
8035    }
8036
8037    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
8038    pub fn timestamps_mut(&mut self) -> &mut [u32] {
8039        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
8040        unsafe {
8041            let n = ffi::whiteout_mdx_MdxTrackU32_get_timestamps_count(self.raw.as_ptr());
8042            let p =
8043                ffi::whiteout_mdx_MdxTrackU32_get_timestamps_data(self.raw.as_ptr()) as *mut u32;
8044            if p.is_null() || n == 0 {
8045                &mut []
8046            } else {
8047                core::slice::from_raw_parts_mut(p, n)
8048            }
8049        }
8050    }
8051
8052    pub fn set_timestamps(&mut self, values: &[u32]) {
8053        // SAFETY: the native side copies `values` before returning.
8054        unsafe {
8055            ffi::whiteout_mdx_MdxTrackU32_assign_timestamps(
8056                self.raw.as_ptr(),
8057                values.as_ptr() as *const _,
8058                values.len(),
8059            )
8060        }
8061    }
8062
8063    pub fn resize_timestamps(&mut self, count: usize) {
8064        // SAFETY: reallocation is safe here precisely because
8065        // `&mut self` means no slice borrow is outstanding.
8066        unsafe { ffi::whiteout_mdx_MdxTrackU32_resize_timestamps(self.raw.as_ptr(), count) }
8067    }
8068
8069    /// Raw keyframe data
8070    /// Zero-copy view of the underlying `std::vector`.
8071    pub fn keys(&self) -> &[u32] {
8072        // SAFETY: `_data`/`_count` describe one contiguous C++
8073        // allocation, borrowed for as long as `self` is.
8074        unsafe {
8075            let n = ffi::whiteout_mdx_MdxTrackU32_get_keys_count(self.raw.as_ptr());
8076            let p = ffi::whiteout_mdx_MdxTrackU32_get_keys_data(self.raw.as_ptr());
8077            if p.is_null() || n == 0 {
8078                &[]
8079            } else {
8080                core::slice::from_raw_parts(p, n)
8081            }
8082        }
8083    }
8084
8085    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
8086    pub fn keys_mut(&mut self) -> &mut [u32] {
8087        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
8088        unsafe {
8089            let n = ffi::whiteout_mdx_MdxTrackU32_get_keys_count(self.raw.as_ptr());
8090            let p = ffi::whiteout_mdx_MdxTrackU32_get_keys_data(self.raw.as_ptr()) as *mut u32;
8091            if p.is_null() || n == 0 {
8092                &mut []
8093            } else {
8094                core::slice::from_raw_parts_mut(p, n)
8095            }
8096        }
8097    }
8098
8099    pub fn set_keys(&mut self, values: &[u32]) {
8100        // SAFETY: the native side copies `values` before returning.
8101        unsafe {
8102            ffi::whiteout_mdx_MdxTrackU32_assign_keys(
8103                self.raw.as_ptr(),
8104                values.as_ptr() as *const _,
8105                values.len(),
8106            )
8107        }
8108    }
8109
8110    pub fn resize_keys(&mut self, count: usize) {
8111        // SAFETY: reallocation is safe here precisely because
8112        // `&mut self` means no slice borrow is outstanding.
8113        unsafe { ffi::whiteout_mdx_MdxTrackU32_resize_keys(self.raw.as_ptr(), count) }
8114    }
8115}
8116
8117impl Default for TrackU32 {
8118    fn default() -> Self {
8119        Self::new()
8120    }
8121}
8122
8123/// Generic animation track with keyframe data
8124///
8125/// Tracks store animation data as a series of keyframes. The interpolation type determines how values between keyframes are calculated.
8126///
8127/// @tparam T The value type (f32, Vector3f, Vector4f, u32, etc.)
8128pub struct TrackF32 {
8129    pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MdxTrackF32>,
8130}
8131
8132impl Drop for TrackF32 {
8133    fn drop(&mut self) {
8134        // SAFETY: `raw` came from a native constructor and Drop runs once.
8135        unsafe { ffi::whiteout_mdx_MdxTrackF32_delete(self.raw.as_ptr()) }
8136    }
8137}
8138
8139impl TrackF32 {
8140    /// # Safety
8141    /// `raw` must be a live handle this value takes ownership of.
8142    #[allow(dead_code)] // used by whichever methods return this type
8143    pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MdxTrackF32) -> Option<Self> {
8144        core::ptr::NonNull::new(raw).map(|raw| TrackF32 { raw })
8145    }
8146}
8147
8148// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
8149// is deliberately NOT implemented — the C++ types make no documented
8150// guarantee about concurrent use, and claiming one we haven't verified
8151// would be unsound. See `@bind thread_safe` in the plan.
8152unsafe impl Send for TrackF32 {}
8153
8154impl core::fmt::Debug for TrackF32 {
8155    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
8156        f.debug_struct("TrackF32").finish_non_exhaustive()
8157    }
8158}
8159
8160impl TrackF32 {
8161    /// # Panics
8162    /// Panics if the native allocation fails.
8163    pub fn new() -> Self {
8164        // SAFETY: the native constructor returns a live handle; a null here
8165        // means the library is unusable.
8166        unsafe {
8167            let raw = ffi::whiteout_mdx_MdxTrackF32_new();
8168            Self::from_raw(raw).expect("native TrackF32 allocation failed")
8169        }
8170    }
8171
8172    /// Whether this track contains animation data
8173    pub fn is_used(&self) -> bool {
8174        // SAFETY: plain scalar read through a live handle.
8175        unsafe { ffi::whiteout_mdx_MdxTrackF32_get_isUsed(self.raw.as_ptr()) != 0 }
8176    }
8177
8178    pub fn set_is_used(&mut self, value: bool) {
8179        // SAFETY: plain scalar write through a live handle.
8180        unsafe {
8181            ffi::whiteout_mdx_MdxTrackF32_set_isUsed(self.raw.as_ptr(), if value { 1 } else { 0 })
8182        }
8183    }
8184
8185    /// Interpolation type
8186    pub fn interpolation_type(&self) -> InterpolationType {
8187        // SAFETY: scalar read; the discriminant is validated below.
8188        unsafe { ffi::whiteout_mdx_MdxTrackF32_get_interpolationType(self.raw.as_ptr()) }
8189            .try_into()
8190            .expect("unknown enum discriminant from the native library")
8191    }
8192
8193    pub fn set_interpolation_type(&mut self, value: InterpolationType) {
8194        // SAFETY: scalar write through a live handle.
8195        unsafe {
8196            ffi::whiteout_mdx_MdxTrackF32_set_interpolationType(self.raw.as_ptr(), value as i32)
8197        }
8198    }
8199
8200    /// Global sequence ID if applicable
8201    pub fn global_sequence_id(&self) -> u32 {
8202        // SAFETY: plain scalar read through a live handle.
8203        unsafe { ffi::whiteout_mdx_MdxTrackF32_get_globalSequenceId(self.raw.as_ptr()) }
8204    }
8205
8206    pub fn set_global_sequence_id(&mut self, value: u32) {
8207        // SAFETY: plain scalar write through a live handle.
8208        unsafe { ffi::whiteout_mdx_MdxTrackF32_set_globalSequenceId(self.raw.as_ptr(), value) }
8209    }
8210
8211    /// Number of keyframes
8212    pub fn key_count(&self) -> usize {
8213        // SAFETY: plain scalar read through a live handle.
8214        unsafe { ffi::whiteout_mdx_MdxTrackF32_get_keyCount(self.raw.as_ptr()) }
8215    }
8216
8217    pub fn set_key_count(&mut self, value: usize) {
8218        // SAFETY: plain scalar write through a live handle.
8219        unsafe { ffi::whiteout_mdx_MdxTrackF32_set_keyCount(self.raw.as_ptr(), value) }
8220    }
8221
8222    /// Keyframe timestamps (in frames)
8223    /// Zero-copy view of the underlying `std::vector`.
8224    pub fn timestamps(&self) -> &[u32] {
8225        // SAFETY: `_data`/`_count` describe one contiguous C++
8226        // allocation, borrowed for as long as `self` is.
8227        unsafe {
8228            let n = ffi::whiteout_mdx_MdxTrackF32_get_timestamps_count(self.raw.as_ptr());
8229            let p = ffi::whiteout_mdx_MdxTrackF32_get_timestamps_data(self.raw.as_ptr());
8230            if p.is_null() || n == 0 {
8231                &[]
8232            } else {
8233                core::slice::from_raw_parts(p, n)
8234            }
8235        }
8236    }
8237
8238    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
8239    pub fn timestamps_mut(&mut self) -> &mut [u32] {
8240        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
8241        unsafe {
8242            let n = ffi::whiteout_mdx_MdxTrackF32_get_timestamps_count(self.raw.as_ptr());
8243            let p =
8244                ffi::whiteout_mdx_MdxTrackF32_get_timestamps_data(self.raw.as_ptr()) as *mut u32;
8245            if p.is_null() || n == 0 {
8246                &mut []
8247            } else {
8248                core::slice::from_raw_parts_mut(p, n)
8249            }
8250        }
8251    }
8252
8253    pub fn set_timestamps(&mut self, values: &[u32]) {
8254        // SAFETY: the native side copies `values` before returning.
8255        unsafe {
8256            ffi::whiteout_mdx_MdxTrackF32_assign_timestamps(
8257                self.raw.as_ptr(),
8258                values.as_ptr() as *const _,
8259                values.len(),
8260            )
8261        }
8262    }
8263
8264    pub fn resize_timestamps(&mut self, count: usize) {
8265        // SAFETY: reallocation is safe here precisely because
8266        // `&mut self` means no slice borrow is outstanding.
8267        unsafe { ffi::whiteout_mdx_MdxTrackF32_resize_timestamps(self.raw.as_ptr(), count) }
8268    }
8269
8270    /// Raw keyframe data
8271    /// Zero-copy view of the underlying `std::vector`.
8272    pub fn keys(&self) -> &[f32] {
8273        // SAFETY: `_data`/`_count` describe one contiguous C++
8274        // allocation, borrowed for as long as `self` is.
8275        unsafe {
8276            let n = ffi::whiteout_mdx_MdxTrackF32_get_keys_count(self.raw.as_ptr());
8277            let p = ffi::whiteout_mdx_MdxTrackF32_get_keys_data(self.raw.as_ptr());
8278            if p.is_null() || n == 0 {
8279                &[]
8280            } else {
8281                core::slice::from_raw_parts(p, n)
8282            }
8283        }
8284    }
8285
8286    /// Zero-copy mutable view. Resize first — the borrow forbids it after.
8287    pub fn keys_mut(&mut self) -> &mut [f32] {
8288        // SAFETY: as above; `&mut self` rules out aliasing and resizing.
8289        unsafe {
8290            let n = ffi::whiteout_mdx_MdxTrackF32_get_keys_count(self.raw.as_ptr());
8291            let p = ffi::whiteout_mdx_MdxTrackF32_get_keys_data(self.raw.as_ptr()) as *mut f32;
8292            if p.is_null() || n == 0 {
8293                &mut []
8294            } else {
8295                core::slice::from_raw_parts_mut(p, n)
8296            }
8297        }
8298    }
8299
8300    pub fn set_keys(&mut self, values: &[f32]) {
8301        // SAFETY: the native side copies `values` before returning.
8302        unsafe {
8303            ffi::whiteout_mdx_MdxTrackF32_assign_keys(
8304                self.raw.as_ptr(),
8305                values.as_ptr() as *const _,
8306                values.len(),
8307            )
8308        }
8309    }
8310
8311    pub fn resize_keys(&mut self, count: usize) {
8312        // SAFETY: reallocation is safe here precisely because
8313        // `&mut self` means no slice borrow is outstanding.
8314        unsafe { ffi::whiteout_mdx_MdxTrackF32_resize_keys(self.raw.as_ptr(), count) }
8315    }
8316}
8317
8318impl Default for TrackF32 {
8319    fn default() -> Self {
8320        Self::new()
8321    }
8322}
8323
8324#[doc(hidden)]
8325pub mod ffi {
8326    #![allow(missing_debug_implementations)]
8327
8328    #[allow(unused_imports)]
8329    use crate::support::{RawBytes, RawCString};
8330
8331    #[repr(C)]
8332    pub struct whiteout_MdxExtent {
8333        _private: [u8; 0],
8334    }
8335    #[repr(C)]
8336    pub struct whiteout_MdxModel {
8337        _private: [u8; 0],
8338    }
8339    #[repr(C)]
8340    pub struct whiteout_MdxSequence {
8341        _private: [u8; 0],
8342    }
8343    #[repr(C)]
8344    pub struct whiteout_MdxTexture {
8345        _private: [u8; 0],
8346    }
8347    #[repr(C)]
8348    pub struct whiteout_MdxSound {
8349        _private: [u8; 0],
8350    }
8351    #[repr(C)]
8352    pub struct whiteout_MdxNode {
8353        _private: [u8; 0],
8354    }
8355    #[repr(C)]
8356    pub struct whiteout_MdxSoundEmitter {
8357        _private: [u8; 0],
8358    }
8359    #[repr(C)]
8360    pub struct whiteout_MdxLayer {
8361        _private: [u8; 0],
8362    }
8363    #[repr(C)]
8364    pub struct whiteout_MdxLayerSubTexture {
8365        _private: [u8; 0],
8366    }
8367    #[repr(C)]
8368    pub struct whiteout_MdxMaterial {
8369        _private: [u8; 0],
8370    }
8371    #[repr(C)]
8372    pub struct whiteout_MdxTextureAnimation {
8373        _private: [u8; 0],
8374    }
8375    #[repr(C)]
8376    pub struct whiteout_MdxGeoset {
8377        _private: [u8; 0],
8378    }
8379    #[repr(C)]
8380    pub struct whiteout_MdxGeosetAnimation {
8381        _private: [u8; 0],
8382    }
8383    #[repr(C)]
8384    pub struct whiteout_MdxBone {
8385        _private: [u8; 0],
8386    }
8387    #[repr(C)]
8388    pub struct whiteout_MdxLight {
8389        _private: [u8; 0],
8390    }
8391    #[repr(C)]
8392    pub struct whiteout_MdxHelper {
8393        _private: [u8; 0],
8394    }
8395    #[repr(C)]
8396    pub struct whiteout_MdxAttachment {
8397        _private: [u8; 0],
8398    }
8399    #[repr(C)]
8400    pub struct whiteout_MdxParticleEmitter {
8401        _private: [u8; 0],
8402    }
8403    #[repr(C)]
8404    pub struct whiteout_MdxParticleEmitter2 {
8405        _private: [u8; 0],
8406    }
8407    #[repr(C)]
8408    pub struct whiteout_MdxRibbonEmitter {
8409        _private: [u8; 0],
8410    }
8411    #[repr(C)]
8412    pub struct whiteout_MdxEventObject {
8413        _private: [u8; 0],
8414    }
8415    #[repr(C)]
8416    pub struct whiteout_MdxCamera {
8417        _private: [u8; 0],
8418    }
8419    #[repr(C)]
8420    pub struct whiteout_MdxCollisionShape {
8421        _private: [u8; 0],
8422    }
8423    #[repr(C)]
8424    pub struct whiteout_MdxFaceEffect {
8425        _private: [u8; 0],
8426    }
8427    #[repr(C)]
8428    pub struct whiteout_MdxCornEmitter {
8429        _private: [u8; 0],
8430    }
8431    #[repr(C)]
8432    pub struct whiteout_MdxParser {
8433        _private: [u8; 0],
8434    }
8435    #[repr(C)]
8436    pub struct whiteout_MdxWriter {
8437        _private: [u8; 0],
8438    }
8439    #[repr(C)]
8440    pub struct whiteout_MdxTrackVector3f {
8441        _private: [u8; 0],
8442    }
8443    #[repr(C)]
8444    pub struct whiteout_MdxTrackQuaternion {
8445        _private: [u8; 0],
8446    }
8447    #[repr(C)]
8448    pub struct whiteout_MdxTrackU32 {
8449        _private: [u8; 0],
8450    }
8451    #[repr(C)]
8452    pub struct whiteout_MdxTrackF32 {
8453        _private: [u8; 0],
8454    }
8455
8456    extern "C" {
8457        // Extent
8458        pub fn whiteout_mdx_MdxExtent_new() -> *mut whiteout_MdxExtent;
8459        pub fn whiteout_mdx_MdxExtent_delete(self_: *mut whiteout_MdxExtent);
8460        pub fn whiteout_mdx_MdxExtent_get_boundsRadius(self_: *mut whiteout_MdxExtent) -> f32;
8461        pub fn whiteout_mdx_MdxExtent_set_boundsRadius(self_: *mut whiteout_MdxExtent, value: f32);
8462        pub fn whiteout_mdx_MdxExtent_get_minimum(
8463            self_: *mut whiteout_MdxExtent,
8464        ) -> *mut core::ffi::c_void;
8465        pub fn whiteout_mdx_MdxExtent_set_minimum(
8466            self_: *mut whiteout_MdxExtent,
8467            value: *const core::ffi::c_void,
8468        );
8469        pub fn whiteout_mdx_MdxExtent_get_maximum(
8470            self_: *mut whiteout_MdxExtent,
8471        ) -> *mut core::ffi::c_void;
8472        pub fn whiteout_mdx_MdxExtent_set_maximum(
8473            self_: *mut whiteout_MdxExtent,
8474            value: *const core::ffi::c_void,
8475        );
8476        // Model
8477        pub fn whiteout_mdx_MdxModel_new() -> *mut whiteout_MdxModel;
8478        pub fn whiteout_mdx_MdxModel_delete(self_: *mut whiteout_MdxModel);
8479        pub fn whiteout_mdx_MdxModel_get_version(self_: *mut whiteout_MdxModel) -> u32;
8480        pub fn whiteout_mdx_MdxModel_set_version(self_: *mut whiteout_MdxModel, value: u32);
8481        pub fn whiteout_mdx_MdxModel_get_modelName(self_: *mut whiteout_MdxModel) -> RawCString;
8482        pub fn whiteout_mdx_MdxModel_set_modelName(
8483            self_: *mut whiteout_MdxModel,
8484            value: *const core::ffi::c_char,
8485        );
8486        pub fn whiteout_mdx_MdxModel_get_animationFileName(
8487            self_: *mut whiteout_MdxModel,
8488        ) -> RawCString;
8489        pub fn whiteout_mdx_MdxModel_set_animationFileName(
8490            self_: *mut whiteout_MdxModel,
8491            value: *const core::ffi::c_char,
8492        );
8493        pub fn whiteout_mdx_MdxModel_get_modelExtent(
8494            self_: *mut whiteout_MdxModel,
8495        ) -> *mut whiteout_MdxExtent;
8496        pub fn whiteout_mdx_MdxModel_set_modelExtent(
8497            self_: *mut whiteout_MdxModel,
8498            value: *const whiteout_MdxExtent,
8499        );
8500        pub fn whiteout_mdx_MdxModel_get_blendTime(self_: *mut whiteout_MdxModel) -> u32;
8501        pub fn whiteout_mdx_MdxModel_set_blendTime(self_: *mut whiteout_MdxModel, value: u32);
8502        pub fn whiteout_mdx_MdxModel_get_globalSequences_count(
8503            self_: *mut whiteout_MdxModel,
8504        ) -> usize;
8505        pub fn whiteout_mdx_MdxModel_resize_globalSequences(
8506            self_: *mut whiteout_MdxModel,
8507            count: usize,
8508        );
8509        pub fn whiteout_mdx_MdxModel_get_globalSequences_data(
8510            self_: *mut whiteout_MdxModel,
8511        ) -> *const u32;
8512        pub fn whiteout_mdx_MdxModel_assign_globalSequences(
8513            self_: *mut whiteout_MdxModel,
8514            data: *const u32,
8515            count: usize,
8516        );
8517        pub fn whiteout_mdx_MdxModel_get_sequences_count(self_: *mut whiteout_MdxModel) -> usize;
8518        pub fn whiteout_mdx_MdxModel_resize_sequences(self_: *mut whiteout_MdxModel, count: usize);
8519        pub fn whiteout_mdx_MdxModel_get_sequences_at(
8520            self_: *mut whiteout_MdxModel,
8521            index: usize,
8522        ) -> *mut whiteout_MdxSequence;
8523        pub fn whiteout_mdx_MdxModel_get_textures_count(self_: *mut whiteout_MdxModel) -> usize;
8524        pub fn whiteout_mdx_MdxModel_resize_textures(self_: *mut whiteout_MdxModel, count: usize);
8525        pub fn whiteout_mdx_MdxModel_get_textures_at(
8526            self_: *mut whiteout_MdxModel,
8527            index: usize,
8528        ) -> *mut whiteout_MdxTexture;
8529        pub fn whiteout_mdx_MdxModel_get_sounds_count(self_: *mut whiteout_MdxModel) -> usize;
8530        pub fn whiteout_mdx_MdxModel_resize_sounds(self_: *mut whiteout_MdxModel, count: usize);
8531        pub fn whiteout_mdx_MdxModel_get_sounds_at(
8532            self_: *mut whiteout_MdxModel,
8533            index: usize,
8534        ) -> *mut whiteout_MdxSound;
8535        pub fn whiteout_mdx_MdxModel_get_soundEmitters_count(
8536            self_: *mut whiteout_MdxModel,
8537        ) -> usize;
8538        pub fn whiteout_mdx_MdxModel_resize_soundEmitters(
8539            self_: *mut whiteout_MdxModel,
8540            count: usize,
8541        );
8542        pub fn whiteout_mdx_MdxModel_get_soundEmitters_at(
8543            self_: *mut whiteout_MdxModel,
8544            index: usize,
8545        ) -> *mut whiteout_MdxSoundEmitter;
8546        pub fn whiteout_mdx_MdxModel_get_materials_count(self_: *mut whiteout_MdxModel) -> usize;
8547        pub fn whiteout_mdx_MdxModel_resize_materials(self_: *mut whiteout_MdxModel, count: usize);
8548        pub fn whiteout_mdx_MdxModel_get_materials_at(
8549            self_: *mut whiteout_MdxModel,
8550            index: usize,
8551        ) -> *mut whiteout_MdxMaterial;
8552        pub fn whiteout_mdx_MdxModel_get_textureAnimations_count(
8553            self_: *mut whiteout_MdxModel,
8554        ) -> usize;
8555        pub fn whiteout_mdx_MdxModel_resize_textureAnimations(
8556            self_: *mut whiteout_MdxModel,
8557            count: usize,
8558        );
8559        pub fn whiteout_mdx_MdxModel_get_textureAnimations_at(
8560            self_: *mut whiteout_MdxModel,
8561            index: usize,
8562        ) -> *mut whiteout_MdxTextureAnimation;
8563        pub fn whiteout_mdx_MdxModel_get_geosets_count(self_: *mut whiteout_MdxModel) -> usize;
8564        pub fn whiteout_mdx_MdxModel_resize_geosets(self_: *mut whiteout_MdxModel, count: usize);
8565        pub fn whiteout_mdx_MdxModel_get_geosets_at(
8566            self_: *mut whiteout_MdxModel,
8567            index: usize,
8568        ) -> *mut whiteout_MdxGeoset;
8569        pub fn whiteout_mdx_MdxModel_get_geosetAnimations_count(
8570            self_: *mut whiteout_MdxModel,
8571        ) -> usize;
8572        pub fn whiteout_mdx_MdxModel_resize_geosetAnimations(
8573            self_: *mut whiteout_MdxModel,
8574            count: usize,
8575        );
8576        pub fn whiteout_mdx_MdxModel_get_geosetAnimations_at(
8577            self_: *mut whiteout_MdxModel,
8578            index: usize,
8579        ) -> *mut whiteout_MdxGeosetAnimation;
8580        pub fn whiteout_mdx_MdxModel_get_bones_count(self_: *mut whiteout_MdxModel) -> usize;
8581        pub fn whiteout_mdx_MdxModel_resize_bones(self_: *mut whiteout_MdxModel, count: usize);
8582        pub fn whiteout_mdx_MdxModel_get_bones_at(
8583            self_: *mut whiteout_MdxModel,
8584            index: usize,
8585        ) -> *mut whiteout_MdxBone;
8586        pub fn whiteout_mdx_MdxModel_get_helpers_count(self_: *mut whiteout_MdxModel) -> usize;
8587        pub fn whiteout_mdx_MdxModel_resize_helpers(self_: *mut whiteout_MdxModel, count: usize);
8588        pub fn whiteout_mdx_MdxModel_get_helpers_at(
8589            self_: *mut whiteout_MdxModel,
8590            index: usize,
8591        ) -> *mut whiteout_MdxHelper;
8592        pub fn whiteout_mdx_MdxModel_get_attachments_count(self_: *mut whiteout_MdxModel) -> usize;
8593        pub fn whiteout_mdx_MdxModel_resize_attachments(
8594            self_: *mut whiteout_MdxModel,
8595            count: usize,
8596        );
8597        pub fn whiteout_mdx_MdxModel_get_attachments_at(
8598            self_: *mut whiteout_MdxModel,
8599            index: usize,
8600        ) -> *mut whiteout_MdxAttachment;
8601        pub fn whiteout_mdx_MdxModel_get_pivotPoints_count(self_: *mut whiteout_MdxModel) -> usize;
8602        pub fn whiteout_mdx_MdxModel_resize_pivotPoints(
8603            self_: *mut whiteout_MdxModel,
8604            count: usize,
8605        );
8606        pub fn whiteout_mdx_MdxModel_get_pivotPoints_data(
8607            self_: *mut whiteout_MdxModel,
8608        ) -> *const f32;
8609        pub fn whiteout_mdx_MdxModel_assign_pivotPoints(
8610            self_: *mut whiteout_MdxModel,
8611            data: *const f32,
8612            count: usize,
8613        );
8614        pub fn whiteout_mdx_MdxModel_get_lights_count(self_: *mut whiteout_MdxModel) -> usize;
8615        pub fn whiteout_mdx_MdxModel_resize_lights(self_: *mut whiteout_MdxModel, count: usize);
8616        pub fn whiteout_mdx_MdxModel_get_lights_at(
8617            self_: *mut whiteout_MdxModel,
8618            index: usize,
8619        ) -> *mut whiteout_MdxLight;
8620        pub fn whiteout_mdx_MdxModel_get_particleEmitters_count(
8621            self_: *mut whiteout_MdxModel,
8622        ) -> usize;
8623        pub fn whiteout_mdx_MdxModel_resize_particleEmitters(
8624            self_: *mut whiteout_MdxModel,
8625            count: usize,
8626        );
8627        pub fn whiteout_mdx_MdxModel_get_particleEmitters_at(
8628            self_: *mut whiteout_MdxModel,
8629            index: usize,
8630        ) -> *mut whiteout_MdxParticleEmitter;
8631        pub fn whiteout_mdx_MdxModel_get_particleEmitters2_count(
8632            self_: *mut whiteout_MdxModel,
8633        ) -> usize;
8634        pub fn whiteout_mdx_MdxModel_resize_particleEmitters2(
8635            self_: *mut whiteout_MdxModel,
8636            count: usize,
8637        );
8638        pub fn whiteout_mdx_MdxModel_get_particleEmitters2_at(
8639            self_: *mut whiteout_MdxModel,
8640            index: usize,
8641        ) -> *mut whiteout_MdxParticleEmitter2;
8642        pub fn whiteout_mdx_MdxModel_get_ribbonEmitters_count(
8643            self_: *mut whiteout_MdxModel,
8644        ) -> usize;
8645        pub fn whiteout_mdx_MdxModel_resize_ribbonEmitters(
8646            self_: *mut whiteout_MdxModel,
8647            count: usize,
8648        );
8649        pub fn whiteout_mdx_MdxModel_get_ribbonEmitters_at(
8650            self_: *mut whiteout_MdxModel,
8651            index: usize,
8652        ) -> *mut whiteout_MdxRibbonEmitter;
8653        pub fn whiteout_mdx_MdxModel_get_cornEmitters_count(self_: *mut whiteout_MdxModel)
8654            -> usize;
8655        pub fn whiteout_mdx_MdxModel_resize_cornEmitters(
8656            self_: *mut whiteout_MdxModel,
8657            count: usize,
8658        );
8659        pub fn whiteout_mdx_MdxModel_get_cornEmitters_at(
8660            self_: *mut whiteout_MdxModel,
8661            index: usize,
8662        ) -> *mut whiteout_MdxCornEmitter;
8663        pub fn whiteout_mdx_MdxModel_get_eventObjects_count(self_: *mut whiteout_MdxModel)
8664            -> usize;
8665        pub fn whiteout_mdx_MdxModel_resize_eventObjects(
8666            self_: *mut whiteout_MdxModel,
8667            count: usize,
8668        );
8669        pub fn whiteout_mdx_MdxModel_get_eventObjects_at(
8670            self_: *mut whiteout_MdxModel,
8671            index: usize,
8672        ) -> *mut whiteout_MdxEventObject;
8673        pub fn whiteout_mdx_MdxModel_get_cameras_count(self_: *mut whiteout_MdxModel) -> usize;
8674        pub fn whiteout_mdx_MdxModel_resize_cameras(self_: *mut whiteout_MdxModel, count: usize);
8675        pub fn whiteout_mdx_MdxModel_get_cameras_at(
8676            self_: *mut whiteout_MdxModel,
8677            index: usize,
8678        ) -> *mut whiteout_MdxCamera;
8679        pub fn whiteout_mdx_MdxModel_get_collisionShapes_count(
8680            self_: *mut whiteout_MdxModel,
8681        ) -> usize;
8682        pub fn whiteout_mdx_MdxModel_resize_collisionShapes(
8683            self_: *mut whiteout_MdxModel,
8684            count: usize,
8685        );
8686        pub fn whiteout_mdx_MdxModel_get_collisionShapes_at(
8687            self_: *mut whiteout_MdxModel,
8688            index: usize,
8689        ) -> *mut whiteout_MdxCollisionShape;
8690        pub fn whiteout_mdx_MdxModel_get_faceEffects_count(self_: *mut whiteout_MdxModel) -> usize;
8691        pub fn whiteout_mdx_MdxModel_resize_faceEffects(
8692            self_: *mut whiteout_MdxModel,
8693            count: usize,
8694        );
8695        pub fn whiteout_mdx_MdxModel_get_faceEffects_at(
8696            self_: *mut whiteout_MdxModel,
8697            index: usize,
8698        ) -> *mut whiteout_MdxFaceEffect;
8699        // Sequence
8700        pub fn whiteout_mdx_MdxSequence_new() -> *mut whiteout_MdxSequence;
8701        pub fn whiteout_mdx_MdxSequence_delete(self_: *mut whiteout_MdxSequence);
8702        pub fn whiteout_mdx_MdxSequence_get_name(self_: *mut whiteout_MdxSequence) -> RawCString;
8703        pub fn whiteout_mdx_MdxSequence_set_name(
8704            self_: *mut whiteout_MdxSequence,
8705            value: *const core::ffi::c_char,
8706        );
8707        pub fn whiteout_mdx_MdxSequence_get_intervalStart(self_: *mut whiteout_MdxSequence) -> u32;
8708        pub fn whiteout_mdx_MdxSequence_set_intervalStart(
8709            self_: *mut whiteout_MdxSequence,
8710            value: u32,
8711        );
8712        pub fn whiteout_mdx_MdxSequence_get_intervalEnd(self_: *mut whiteout_MdxSequence) -> u32;
8713        pub fn whiteout_mdx_MdxSequence_set_intervalEnd(
8714            self_: *mut whiteout_MdxSequence,
8715            value: u32,
8716        );
8717        pub fn whiteout_mdx_MdxSequence_get_moveSpeed(self_: *mut whiteout_MdxSequence) -> f32;
8718        pub fn whiteout_mdx_MdxSequence_set_moveSpeed(self_: *mut whiteout_MdxSequence, value: f32);
8719        pub fn whiteout_mdx_MdxSequence_get_flags(self_: *mut whiteout_MdxSequence) -> i32;
8720        pub fn whiteout_mdx_MdxSequence_set_flags(self_: *mut whiteout_MdxSequence, value: i32);
8721        pub fn whiteout_mdx_MdxSequence_get_rarity(self_: *mut whiteout_MdxSequence) -> f32;
8722        pub fn whiteout_mdx_MdxSequence_set_rarity(self_: *mut whiteout_MdxSequence, value: f32);
8723        pub fn whiteout_mdx_MdxSequence_get_syncPoint(self_: *mut whiteout_MdxSequence) -> u32;
8724        pub fn whiteout_mdx_MdxSequence_set_syncPoint(self_: *mut whiteout_MdxSequence, value: u32);
8725        pub fn whiteout_mdx_MdxSequence_get_extent(
8726            self_: *mut whiteout_MdxSequence,
8727        ) -> *mut whiteout_MdxExtent;
8728        pub fn whiteout_mdx_MdxSequence_set_extent(
8729            self_: *mut whiteout_MdxSequence,
8730            value: *const whiteout_MdxExtent,
8731        );
8732        // Texture
8733        pub fn whiteout_mdx_MdxTexture_new() -> *mut whiteout_MdxTexture;
8734        pub fn whiteout_mdx_MdxTexture_delete(self_: *mut whiteout_MdxTexture);
8735        pub fn whiteout_mdx_MdxTexture_get_replaceableId(self_: *mut whiteout_MdxTexture) -> u32;
8736        pub fn whiteout_mdx_MdxTexture_set_replaceableId(
8737            self_: *mut whiteout_MdxTexture,
8738            value: u32,
8739        );
8740        pub fn whiteout_mdx_MdxTexture_get_fileName(self_: *mut whiteout_MdxTexture) -> RawCString;
8741        pub fn whiteout_mdx_MdxTexture_set_fileName(
8742            self_: *mut whiteout_MdxTexture,
8743            value: *const core::ffi::c_char,
8744        );
8745        pub fn whiteout_mdx_MdxTexture_get_flags(self_: *mut whiteout_MdxTexture) -> i32;
8746        pub fn whiteout_mdx_MdxTexture_set_flags(self_: *mut whiteout_MdxTexture, value: i32);
8747        // Sound
8748        pub fn whiteout_mdx_MdxSound_new() -> *mut whiteout_MdxSound;
8749        pub fn whiteout_mdx_MdxSound_delete(self_: *mut whiteout_MdxSound);
8750        pub fn whiteout_mdx_MdxSound_get_soundFile(self_: *mut whiteout_MdxSound) -> RawCString;
8751        pub fn whiteout_mdx_MdxSound_set_soundFile(
8752            self_: *mut whiteout_MdxSound,
8753            value: *const core::ffi::c_char,
8754        );
8755        pub fn whiteout_mdx_MdxSound_get_maximumDistance(self_: *mut whiteout_MdxSound) -> f32;
8756        pub fn whiteout_mdx_MdxSound_set_maximumDistance(self_: *mut whiteout_MdxSound, value: f32);
8757        pub fn whiteout_mdx_MdxSound_get_minimumDistance(self_: *mut whiteout_MdxSound) -> f32;
8758        pub fn whiteout_mdx_MdxSound_set_minimumDistance(self_: *mut whiteout_MdxSound, value: f32);
8759        pub fn whiteout_mdx_MdxSound_get_soundChannel(self_: *mut whiteout_MdxSound) -> u32;
8760        pub fn whiteout_mdx_MdxSound_set_soundChannel(self_: *mut whiteout_MdxSound, value: u32);
8761        // Node
8762        pub fn whiteout_mdx_MdxNode_new() -> *mut whiteout_MdxNode;
8763        pub fn whiteout_mdx_MdxNode_delete(self_: *mut whiteout_MdxNode);
8764        pub fn whiteout_mdx_MdxNode_get_name(self_: *mut whiteout_MdxNode) -> RawCString;
8765        pub fn whiteout_mdx_MdxNode_set_name(
8766            self_: *mut whiteout_MdxNode,
8767            value: *const core::ffi::c_char,
8768        );
8769        pub fn whiteout_mdx_MdxNode_get_objectId(self_: *mut whiteout_MdxNode) -> u32;
8770        pub fn whiteout_mdx_MdxNode_set_objectId(self_: *mut whiteout_MdxNode, value: u32);
8771        pub fn whiteout_mdx_MdxNode_get_parentId(self_: *mut whiteout_MdxNode) -> u32;
8772        pub fn whiteout_mdx_MdxNode_set_parentId(self_: *mut whiteout_MdxNode, value: u32);
8773        pub fn whiteout_mdx_MdxNode_get_flags(self_: *mut whiteout_MdxNode) -> i32;
8774        pub fn whiteout_mdx_MdxNode_set_flags(self_: *mut whiteout_MdxNode, value: i32);
8775        pub fn whiteout_mdx_MdxNode_get_type(self_: *mut whiteout_MdxNode) -> i32;
8776        pub fn whiteout_mdx_MdxNode_set_type(self_: *mut whiteout_MdxNode, value: i32);
8777        pub fn whiteout_mdx_MdxNode_get_nodeFamilyId(self_: *mut whiteout_MdxNode) -> u32;
8778        pub fn whiteout_mdx_MdxNode_set_nodeFamilyId(self_: *mut whiteout_MdxNode, value: u32);
8779        pub fn whiteout_mdx_MdxNode_get_translationTracks(
8780            self_: *mut whiteout_MdxNode,
8781        ) -> *mut whiteout_MdxTrackVector3f;
8782        pub fn whiteout_mdx_MdxNode_set_translationTracks(
8783            self_: *mut whiteout_MdxNode,
8784            value: *const whiteout_MdxTrackVector3f,
8785        );
8786        pub fn whiteout_mdx_MdxNode_get_rotationTracks(
8787            self_: *mut whiteout_MdxNode,
8788        ) -> *mut whiteout_MdxTrackQuaternion;
8789        pub fn whiteout_mdx_MdxNode_set_rotationTracks(
8790            self_: *mut whiteout_MdxNode,
8791            value: *const whiteout_MdxTrackQuaternion,
8792        );
8793        pub fn whiteout_mdx_MdxNode_get_scalingTracks(
8794            self_: *mut whiteout_MdxNode,
8795        ) -> *mut whiteout_MdxTrackVector3f;
8796        pub fn whiteout_mdx_MdxNode_set_scalingTracks(
8797            self_: *mut whiteout_MdxNode,
8798            value: *const whiteout_MdxTrackVector3f,
8799        );
8800        // SoundEmitter
8801        pub fn whiteout_mdx_MdxSoundEmitter_new() -> *mut whiteout_MdxSoundEmitter;
8802        pub fn whiteout_mdx_MdxSoundEmitter_delete(self_: *mut whiteout_MdxSoundEmitter);
8803        pub fn whiteout_mdx_MdxSoundEmitter_get_node(
8804            self_: *mut whiteout_MdxSoundEmitter,
8805        ) -> *mut whiteout_MdxNode;
8806        pub fn whiteout_mdx_MdxSoundEmitter_set_node(
8807            self_: *mut whiteout_MdxSoundEmitter,
8808            value: *const whiteout_MdxNode,
8809        );
8810        pub fn whiteout_mdx_MdxSoundEmitter_get_soundTrack(
8811            self_: *mut whiteout_MdxSoundEmitter,
8812        ) -> *mut whiteout_MdxTrackU32;
8813        pub fn whiteout_mdx_MdxSoundEmitter_set_soundTrack(
8814            self_: *mut whiteout_MdxSoundEmitter,
8815            value: *const whiteout_MdxTrackU32,
8816        );
8817        // Layer
8818        pub fn whiteout_mdx_MdxLayer_new() -> *mut whiteout_MdxLayer;
8819        pub fn whiteout_mdx_MdxLayer_delete(self_: *mut whiteout_MdxLayer);
8820        pub fn whiteout_mdx_MdxLayer_get_filterMode(self_: *mut whiteout_MdxLayer) -> i32;
8821        pub fn whiteout_mdx_MdxLayer_set_filterMode(self_: *mut whiteout_MdxLayer, value: i32);
8822        pub fn whiteout_mdx_MdxLayer_get_shadingFlags(self_: *mut whiteout_MdxLayer) -> i32;
8823        pub fn whiteout_mdx_MdxLayer_set_shadingFlags(self_: *mut whiteout_MdxLayer, value: i32);
8824        pub fn whiteout_mdx_MdxLayer_get_textureId(self_: *mut whiteout_MdxLayer) -> u32;
8825        pub fn whiteout_mdx_MdxLayer_set_textureId(self_: *mut whiteout_MdxLayer, value: u32);
8826        pub fn whiteout_mdx_MdxLayer_get_textureAnimationId(self_: *mut whiteout_MdxLayer) -> u32;
8827        pub fn whiteout_mdx_MdxLayer_set_textureAnimationId(
8828            self_: *mut whiteout_MdxLayer,
8829            value: u32,
8830        );
8831        pub fn whiteout_mdx_MdxLayer_get_coordId(self_: *mut whiteout_MdxLayer) -> u32;
8832        pub fn whiteout_mdx_MdxLayer_set_coordId(self_: *mut whiteout_MdxLayer, value: u32);
8833        pub fn whiteout_mdx_MdxLayer_get_alpha(self_: *mut whiteout_MdxLayer) -> f32;
8834        pub fn whiteout_mdx_MdxLayer_set_alpha(self_: *mut whiteout_MdxLayer, value: f32);
8835        pub fn whiteout_mdx_MdxLayer_get_emissiveGain(self_: *mut whiteout_MdxLayer) -> f32;
8836        pub fn whiteout_mdx_MdxLayer_set_emissiveGain(self_: *mut whiteout_MdxLayer, value: f32);
8837        pub fn whiteout_mdx_MdxLayer_get_fresnelColor(
8838            self_: *mut whiteout_MdxLayer,
8839        ) -> *mut core::ffi::c_void;
8840        pub fn whiteout_mdx_MdxLayer_set_fresnelColor(
8841            self_: *mut whiteout_MdxLayer,
8842            value: *const core::ffi::c_void,
8843        );
8844        pub fn whiteout_mdx_MdxLayer_get_fresnelOpacity(self_: *mut whiteout_MdxLayer) -> f32;
8845        pub fn whiteout_mdx_MdxLayer_set_fresnelOpacity(self_: *mut whiteout_MdxLayer, value: f32);
8846        pub fn whiteout_mdx_MdxLayer_get_fresnelTeamColor(self_: *mut whiteout_MdxLayer) -> f32;
8847        pub fn whiteout_mdx_MdxLayer_set_fresnelTeamColor(
8848            self_: *mut whiteout_MdxLayer,
8849            value: f32,
8850        );
8851        pub fn whiteout_mdx_MdxLayer_get_shader(self_: *mut whiteout_MdxLayer) -> i32;
8852        pub fn whiteout_mdx_MdxLayer_set_shader(self_: *mut whiteout_MdxLayer, value: i32);
8853        pub fn whiteout_mdx_MdxLayer_get_isHd(self_: *mut whiteout_MdxLayer) -> i32;
8854        pub fn whiteout_mdx_MdxLayer_set_isHd(self_: *mut whiteout_MdxLayer, value: i32);
8855        pub fn whiteout_mdx_MdxLayer_get_subTextures_count(self_: *mut whiteout_MdxLayer) -> usize;
8856        pub fn whiteout_mdx_MdxLayer_resize_subTextures(
8857            self_: *mut whiteout_MdxLayer,
8858            count: usize,
8859        );
8860        pub fn whiteout_mdx_MdxLayer_get_subTextures_at(
8861            self_: *mut whiteout_MdxLayer,
8862            index: usize,
8863        ) -> *mut whiteout_MdxLayerSubTexture;
8864        pub fn whiteout_mdx_MdxLayer_get_textureIdTracks(
8865            self_: *mut whiteout_MdxLayer,
8866        ) -> *mut whiteout_MdxTrackU32;
8867        pub fn whiteout_mdx_MdxLayer_set_textureIdTracks(
8868            self_: *mut whiteout_MdxLayer,
8869            value: *const whiteout_MdxTrackU32,
8870        );
8871        pub fn whiteout_mdx_MdxLayer_get_alphaTracks(
8872            self_: *mut whiteout_MdxLayer,
8873        ) -> *mut whiteout_MdxTrackF32;
8874        pub fn whiteout_mdx_MdxLayer_set_alphaTracks(
8875            self_: *mut whiteout_MdxLayer,
8876            value: *const whiteout_MdxTrackF32,
8877        );
8878        pub fn whiteout_mdx_MdxLayer_get_emissiveGainTracks(
8879            self_: *mut whiteout_MdxLayer,
8880        ) -> *mut whiteout_MdxTrackF32;
8881        pub fn whiteout_mdx_MdxLayer_set_emissiveGainTracks(
8882            self_: *mut whiteout_MdxLayer,
8883            value: *const whiteout_MdxTrackF32,
8884        );
8885        pub fn whiteout_mdx_MdxLayer_get_fresnelColorTracks(
8886            self_: *mut whiteout_MdxLayer,
8887        ) -> *mut whiteout_MdxTrackVector3f;
8888        pub fn whiteout_mdx_MdxLayer_set_fresnelColorTracks(
8889            self_: *mut whiteout_MdxLayer,
8890            value: *const whiteout_MdxTrackVector3f,
8891        );
8892        pub fn whiteout_mdx_MdxLayer_get_fresnelAlphaTracks(
8893            self_: *mut whiteout_MdxLayer,
8894        ) -> *mut whiteout_MdxTrackF32;
8895        pub fn whiteout_mdx_MdxLayer_set_fresnelAlphaTracks(
8896            self_: *mut whiteout_MdxLayer,
8897            value: *const whiteout_MdxTrackF32,
8898        );
8899        pub fn whiteout_mdx_MdxLayer_get_fresnelTeamColorTracks(
8900            self_: *mut whiteout_MdxLayer,
8901        ) -> *mut whiteout_MdxTrackF32;
8902        pub fn whiteout_mdx_MdxLayer_set_fresnelTeamColorTracks(
8903            self_: *mut whiteout_MdxLayer,
8904            value: *const whiteout_MdxTrackF32,
8905        );
8906        // LayerSubTexture
8907        pub fn whiteout_mdx_MdxLayerSubTexture_new() -> *mut whiteout_MdxLayerSubTexture;
8908        pub fn whiteout_mdx_MdxLayerSubTexture_delete(self_: *mut whiteout_MdxLayerSubTexture);
8909        pub fn whiteout_mdx_MdxLayerSubTexture_get_textureId(
8910            self_: *mut whiteout_MdxLayerSubTexture,
8911        ) -> u32;
8912        pub fn whiteout_mdx_MdxLayerSubTexture_set_textureId(
8913            self_: *mut whiteout_MdxLayerSubTexture,
8914            value: u32,
8915        );
8916        pub fn whiteout_mdx_MdxLayerSubTexture_get_slot(
8917            self_: *mut whiteout_MdxLayerSubTexture,
8918        ) -> i32;
8919        pub fn whiteout_mdx_MdxLayerSubTexture_set_slot(
8920            self_: *mut whiteout_MdxLayerSubTexture,
8921            value: i32,
8922        );
8923        pub fn whiteout_mdx_MdxLayerSubTexture_get_tracks(
8924            self_: *mut whiteout_MdxLayerSubTexture,
8925        ) -> *mut whiteout_MdxTrackU32;
8926        pub fn whiteout_mdx_MdxLayerSubTexture_set_tracks(
8927            self_: *mut whiteout_MdxLayerSubTexture,
8928            value: *const whiteout_MdxTrackU32,
8929        );
8930        // Material
8931        pub fn whiteout_mdx_MdxMaterial_new() -> *mut whiteout_MdxMaterial;
8932        pub fn whiteout_mdx_MdxMaterial_delete(self_: *mut whiteout_MdxMaterial);
8933        pub fn whiteout_mdx_MdxMaterial_get_priorityPlane(self_: *mut whiteout_MdxMaterial) -> i32;
8934        pub fn whiteout_mdx_MdxMaterial_set_priorityPlane(
8935            self_: *mut whiteout_MdxMaterial,
8936            value: i32,
8937        );
8938        pub fn whiteout_mdx_MdxMaterial_get_flags(self_: *mut whiteout_MdxMaterial) -> i32;
8939        pub fn whiteout_mdx_MdxMaterial_set_flags(self_: *mut whiteout_MdxMaterial, value: i32);
8940        pub fn whiteout_mdx_MdxMaterial_get_shader(self_: *mut whiteout_MdxMaterial) -> RawCString;
8941        pub fn whiteout_mdx_MdxMaterial_set_shader(
8942            self_: *mut whiteout_MdxMaterial,
8943            value: *const core::ffi::c_char,
8944        );
8945        pub fn whiteout_mdx_MdxMaterial_get_layers_count(self_: *mut whiteout_MdxMaterial)
8946            -> usize;
8947        pub fn whiteout_mdx_MdxMaterial_resize_layers(
8948            self_: *mut whiteout_MdxMaterial,
8949            count: usize,
8950        );
8951        pub fn whiteout_mdx_MdxMaterial_get_layers_at(
8952            self_: *mut whiteout_MdxMaterial,
8953            index: usize,
8954        ) -> *mut whiteout_MdxLayer;
8955        // TextureAnimation
8956        pub fn whiteout_mdx_MdxTextureAnimation_new() -> *mut whiteout_MdxTextureAnimation;
8957        pub fn whiteout_mdx_MdxTextureAnimation_delete(self_: *mut whiteout_MdxTextureAnimation);
8958        pub fn whiteout_mdx_MdxTextureAnimation_get_translationTracks(
8959            self_: *mut whiteout_MdxTextureAnimation,
8960        ) -> *mut whiteout_MdxTrackVector3f;
8961        pub fn whiteout_mdx_MdxTextureAnimation_set_translationTracks(
8962            self_: *mut whiteout_MdxTextureAnimation,
8963            value: *const whiteout_MdxTrackVector3f,
8964        );
8965        pub fn whiteout_mdx_MdxTextureAnimation_get_rotationTracks(
8966            self_: *mut whiteout_MdxTextureAnimation,
8967        ) -> *mut whiteout_MdxTrackQuaternion;
8968        pub fn whiteout_mdx_MdxTextureAnimation_set_rotationTracks(
8969            self_: *mut whiteout_MdxTextureAnimation,
8970            value: *const whiteout_MdxTrackQuaternion,
8971        );
8972        pub fn whiteout_mdx_MdxTextureAnimation_get_scalingTracks(
8973            self_: *mut whiteout_MdxTextureAnimation,
8974        ) -> *mut whiteout_MdxTrackVector3f;
8975        pub fn whiteout_mdx_MdxTextureAnimation_set_scalingTracks(
8976            self_: *mut whiteout_MdxTextureAnimation,
8977            value: *const whiteout_MdxTrackVector3f,
8978        );
8979        // Geoset
8980        pub fn whiteout_mdx_MdxGeoset_new() -> *mut whiteout_MdxGeoset;
8981        pub fn whiteout_mdx_MdxGeoset_delete(self_: *mut whiteout_MdxGeoset);
8982        pub fn whiteout_mdx_MdxGeoset_get_vertexPositions_count(
8983            self_: *mut whiteout_MdxGeoset,
8984        ) -> usize;
8985        pub fn whiteout_mdx_MdxGeoset_resize_vertexPositions(
8986            self_: *mut whiteout_MdxGeoset,
8987            count: usize,
8988        );
8989        pub fn whiteout_mdx_MdxGeoset_get_vertexPositions_data(
8990            self_: *mut whiteout_MdxGeoset,
8991        ) -> *const f32;
8992        pub fn whiteout_mdx_MdxGeoset_assign_vertexPositions(
8993            self_: *mut whiteout_MdxGeoset,
8994            data: *const f32,
8995            count: usize,
8996        );
8997        pub fn whiteout_mdx_MdxGeoset_get_vertexNormals_count(
8998            self_: *mut whiteout_MdxGeoset,
8999        ) -> usize;
9000        pub fn whiteout_mdx_MdxGeoset_resize_vertexNormals(
9001            self_: *mut whiteout_MdxGeoset,
9002            count: usize,
9003        );
9004        pub fn whiteout_mdx_MdxGeoset_get_vertexNormals_data(
9005            self_: *mut whiteout_MdxGeoset,
9006        ) -> *const f32;
9007        pub fn whiteout_mdx_MdxGeoset_assign_vertexNormals(
9008            self_: *mut whiteout_MdxGeoset,
9009            data: *const f32,
9010            count: usize,
9011        );
9012        pub fn whiteout_mdx_MdxGeoset_get_faceTypeGroups_count(
9013            self_: *mut whiteout_MdxGeoset,
9014        ) -> usize;
9015        pub fn whiteout_mdx_MdxGeoset_resize_faceTypeGroups(
9016            self_: *mut whiteout_MdxGeoset,
9017            count: usize,
9018        );
9019        pub fn whiteout_mdx_MdxGeoset_get_faceTypeGroups_data(
9020            self_: *mut whiteout_MdxGeoset,
9021        ) -> *const u32;
9022        pub fn whiteout_mdx_MdxGeoset_assign_faceTypeGroups(
9023            self_: *mut whiteout_MdxGeoset,
9024            data: *const u32,
9025            count: usize,
9026        );
9027        pub fn whiteout_mdx_MdxGeoset_get_faceGroups_count(self_: *mut whiteout_MdxGeoset)
9028            -> usize;
9029        pub fn whiteout_mdx_MdxGeoset_resize_faceGroups(
9030            self_: *mut whiteout_MdxGeoset,
9031            count: usize,
9032        );
9033        pub fn whiteout_mdx_MdxGeoset_get_faceGroups_data(
9034            self_: *mut whiteout_MdxGeoset,
9035        ) -> *const u32;
9036        pub fn whiteout_mdx_MdxGeoset_assign_faceGroups(
9037            self_: *mut whiteout_MdxGeoset,
9038            data: *const u32,
9039            count: usize,
9040        );
9041        pub fn whiteout_mdx_MdxGeoset_get_faces_count(self_: *mut whiteout_MdxGeoset) -> usize;
9042        pub fn whiteout_mdx_MdxGeoset_resize_faces(self_: *mut whiteout_MdxGeoset, count: usize);
9043        pub fn whiteout_mdx_MdxGeoset_get_faces_data(self_: *mut whiteout_MdxGeoset) -> *const u16;
9044        pub fn whiteout_mdx_MdxGeoset_assign_faces(
9045            self_: *mut whiteout_MdxGeoset,
9046            data: *const u16,
9047            count: usize,
9048        );
9049        pub fn whiteout_mdx_MdxGeoset_get_vertexGroups_count(
9050            self_: *mut whiteout_MdxGeoset,
9051        ) -> usize;
9052        pub fn whiteout_mdx_MdxGeoset_resize_vertexGroups(
9053            self_: *mut whiteout_MdxGeoset,
9054            count: usize,
9055        );
9056        pub fn whiteout_mdx_MdxGeoset_get_vertexGroups_data(
9057            self_: *mut whiteout_MdxGeoset,
9058        ) -> *const u8;
9059        pub fn whiteout_mdx_MdxGeoset_assign_vertexGroups(
9060            self_: *mut whiteout_MdxGeoset,
9061            data: *const u8,
9062            count: usize,
9063        );
9064        pub fn whiteout_mdx_MdxGeoset_get_matrixGroups_count(
9065            self_: *mut whiteout_MdxGeoset,
9066        ) -> usize;
9067        pub fn whiteout_mdx_MdxGeoset_resize_matrixGroups(
9068            self_: *mut whiteout_MdxGeoset,
9069            count: usize,
9070        );
9071        pub fn whiteout_mdx_MdxGeoset_get_matrixGroups_data(
9072            self_: *mut whiteout_MdxGeoset,
9073        ) -> *const u32;
9074        pub fn whiteout_mdx_MdxGeoset_assign_matrixGroups(
9075            self_: *mut whiteout_MdxGeoset,
9076            data: *const u32,
9077            count: usize,
9078        );
9079        pub fn whiteout_mdx_MdxGeoset_get_matrixIndices_count(
9080            self_: *mut whiteout_MdxGeoset,
9081        ) -> usize;
9082        pub fn whiteout_mdx_MdxGeoset_resize_matrixIndices(
9083            self_: *mut whiteout_MdxGeoset,
9084            count: usize,
9085        );
9086        pub fn whiteout_mdx_MdxGeoset_get_matrixIndices_data(
9087            self_: *mut whiteout_MdxGeoset,
9088        ) -> *const u32;
9089        pub fn whiteout_mdx_MdxGeoset_assign_matrixIndices(
9090            self_: *mut whiteout_MdxGeoset,
9091            data: *const u32,
9092            count: usize,
9093        );
9094        pub fn whiteout_mdx_MdxGeoset_get_materialId(self_: *mut whiteout_MdxGeoset) -> u32;
9095        pub fn whiteout_mdx_MdxGeoset_set_materialId(self_: *mut whiteout_MdxGeoset, value: u32);
9096        pub fn whiteout_mdx_MdxGeoset_get_selectionGroup(self_: *mut whiteout_MdxGeoset) -> u32;
9097        pub fn whiteout_mdx_MdxGeoset_set_selectionGroup(
9098            self_: *mut whiteout_MdxGeoset,
9099            value: u32,
9100        );
9101        pub fn whiteout_mdx_MdxGeoset_get_selectionFlags(self_: *mut whiteout_MdxGeoset) -> u32;
9102        pub fn whiteout_mdx_MdxGeoset_set_selectionFlags(
9103            self_: *mut whiteout_MdxGeoset,
9104            value: u32,
9105        );
9106        pub fn whiteout_mdx_MdxGeoset_get_lod(self_: *mut whiteout_MdxGeoset) -> u32;
9107        pub fn whiteout_mdx_MdxGeoset_set_lod(self_: *mut whiteout_MdxGeoset, value: u32);
9108        pub fn whiteout_mdx_MdxGeoset_get_lodName(self_: *mut whiteout_MdxGeoset) -> RawCString;
9109        pub fn whiteout_mdx_MdxGeoset_set_lodName(
9110            self_: *mut whiteout_MdxGeoset,
9111            value: *const core::ffi::c_char,
9112        );
9113        pub fn whiteout_mdx_MdxGeoset_get_extent(
9114            self_: *mut whiteout_MdxGeoset,
9115        ) -> *mut whiteout_MdxExtent;
9116        pub fn whiteout_mdx_MdxGeoset_set_extent(
9117            self_: *mut whiteout_MdxGeoset,
9118            value: *const whiteout_MdxExtent,
9119        );
9120        pub fn whiteout_mdx_MdxGeoset_get_sequenceExtents_count(
9121            self_: *mut whiteout_MdxGeoset,
9122        ) -> usize;
9123        pub fn whiteout_mdx_MdxGeoset_resize_sequenceExtents(
9124            self_: *mut whiteout_MdxGeoset,
9125            count: usize,
9126        );
9127        pub fn whiteout_mdx_MdxGeoset_get_sequenceExtents_at(
9128            self_: *mut whiteout_MdxGeoset,
9129            index: usize,
9130        ) -> *mut whiteout_MdxExtent;
9131        pub fn whiteout_mdx_MdxGeoset_get_tangents_count(self_: *mut whiteout_MdxGeoset) -> usize;
9132        pub fn whiteout_mdx_MdxGeoset_resize_tangents(self_: *mut whiteout_MdxGeoset, count: usize);
9133        pub fn whiteout_mdx_MdxGeoset_get_tangents_data(
9134            self_: *mut whiteout_MdxGeoset,
9135        ) -> *const f32;
9136        pub fn whiteout_mdx_MdxGeoset_assign_tangents(
9137            self_: *mut whiteout_MdxGeoset,
9138            data: *const f32,
9139            count: usize,
9140        );
9141        pub fn whiteout_mdx_MdxGeoset_get_skinData_count(self_: *mut whiteout_MdxGeoset) -> usize;
9142        pub fn whiteout_mdx_MdxGeoset_resize_skinData(self_: *mut whiteout_MdxGeoset, count: usize);
9143        pub fn whiteout_mdx_MdxGeoset_get_skinData_data(
9144            self_: *mut whiteout_MdxGeoset,
9145        ) -> *const u8;
9146        pub fn whiteout_mdx_MdxGeoset_assign_skinData(
9147            self_: *mut whiteout_MdxGeoset,
9148            data: *const u8,
9149            count: usize,
9150        );
9151        pub fn whiteout_mdx_MdxGeoset_get_textureCoordinateSets_count(
9152            self_: *mut whiteout_MdxGeoset,
9153        ) -> usize;
9154        pub fn whiteout_mdx_MdxGeoset_get_textureCoordinateSets_inner_count(
9155            self_: *mut whiteout_MdxGeoset,
9156            outer: usize,
9157        ) -> usize;
9158        pub fn whiteout_mdx_MdxGeoset_resize_textureCoordinateSets(
9159            self_: *mut whiteout_MdxGeoset,
9160            count: usize,
9161        );
9162        pub fn whiteout_mdx_MdxGeoset_resize_textureCoordinateSets_inner(
9163            self_: *mut whiteout_MdxGeoset,
9164            outer: usize,
9165            count: usize,
9166        );
9167        pub fn whiteout_mdx_MdxGeoset_get_textureCoordinateSets_inner_data(
9168            self_: *mut whiteout_MdxGeoset,
9169            outer: usize,
9170        ) -> *const f32;
9171        pub fn whiteout_mdx_MdxGeoset_assign_textureCoordinateSets_inner(
9172            self_: *mut whiteout_MdxGeoset,
9173            outer: usize,
9174            data: *const f32,
9175            count: usize,
9176        );
9177        // GeosetAnimation
9178        pub fn whiteout_mdx_MdxGeosetAnimation_new() -> *mut whiteout_MdxGeosetAnimation;
9179        pub fn whiteout_mdx_MdxGeosetAnimation_delete(self_: *mut whiteout_MdxGeosetAnimation);
9180        pub fn whiteout_mdx_MdxGeosetAnimation_get_alpha(
9181            self_: *mut whiteout_MdxGeosetAnimation,
9182        ) -> f32;
9183        pub fn whiteout_mdx_MdxGeosetAnimation_set_alpha(
9184            self_: *mut whiteout_MdxGeosetAnimation,
9185            value: f32,
9186        );
9187        pub fn whiteout_mdx_MdxGeosetAnimation_get_flags(
9188            self_: *mut whiteout_MdxGeosetAnimation,
9189        ) -> i32;
9190        pub fn whiteout_mdx_MdxGeosetAnimation_set_flags(
9191            self_: *mut whiteout_MdxGeosetAnimation,
9192            value: i32,
9193        );
9194        pub fn whiteout_mdx_MdxGeosetAnimation_get_color(
9195            self_: *mut whiteout_MdxGeosetAnimation,
9196        ) -> *mut core::ffi::c_void;
9197        pub fn whiteout_mdx_MdxGeosetAnimation_set_color(
9198            self_: *mut whiteout_MdxGeosetAnimation,
9199            value: *const core::ffi::c_void,
9200        );
9201        pub fn whiteout_mdx_MdxGeosetAnimation_get_geosetId(
9202            self_: *mut whiteout_MdxGeosetAnimation,
9203        ) -> u32;
9204        pub fn whiteout_mdx_MdxGeosetAnimation_set_geosetId(
9205            self_: *mut whiteout_MdxGeosetAnimation,
9206            value: u32,
9207        );
9208        pub fn whiteout_mdx_MdxGeosetAnimation_get_alphaTracks(
9209            self_: *mut whiteout_MdxGeosetAnimation,
9210        ) -> *mut whiteout_MdxTrackF32;
9211        pub fn whiteout_mdx_MdxGeosetAnimation_set_alphaTracks(
9212            self_: *mut whiteout_MdxGeosetAnimation,
9213            value: *const whiteout_MdxTrackF32,
9214        );
9215        pub fn whiteout_mdx_MdxGeosetAnimation_get_colorTracks(
9216            self_: *mut whiteout_MdxGeosetAnimation,
9217        ) -> *mut whiteout_MdxTrackVector3f;
9218        pub fn whiteout_mdx_MdxGeosetAnimation_set_colorTracks(
9219            self_: *mut whiteout_MdxGeosetAnimation,
9220            value: *const whiteout_MdxTrackVector3f,
9221        );
9222        // Bone
9223        pub fn whiteout_mdx_MdxBone_new() -> *mut whiteout_MdxBone;
9224        pub fn whiteout_mdx_MdxBone_delete(self_: *mut whiteout_MdxBone);
9225        pub fn whiteout_mdx_MdxBone_get_node(self_: *mut whiteout_MdxBone)
9226            -> *mut whiteout_MdxNode;
9227        pub fn whiteout_mdx_MdxBone_set_node(
9228            self_: *mut whiteout_MdxBone,
9229            value: *const whiteout_MdxNode,
9230        );
9231        pub fn whiteout_mdx_MdxBone_get_geosetId(self_: *mut whiteout_MdxBone) -> u32;
9232        pub fn whiteout_mdx_MdxBone_set_geosetId(self_: *mut whiteout_MdxBone, value: u32);
9233        pub fn whiteout_mdx_MdxBone_get_geosetAnimationId(self_: *mut whiteout_MdxBone) -> u32;
9234        pub fn whiteout_mdx_MdxBone_set_geosetAnimationId(self_: *mut whiteout_MdxBone, value: u32);
9235        // Light
9236        pub fn whiteout_mdx_MdxLight_new() -> *mut whiteout_MdxLight;
9237        pub fn whiteout_mdx_MdxLight_delete(self_: *mut whiteout_MdxLight);
9238        pub fn whiteout_mdx_MdxLight_get_node(
9239            self_: *mut whiteout_MdxLight,
9240        ) -> *mut whiteout_MdxNode;
9241        pub fn whiteout_mdx_MdxLight_set_node(
9242            self_: *mut whiteout_MdxLight,
9243            value: *const whiteout_MdxNode,
9244        );
9245        pub fn whiteout_mdx_MdxLight_get_type(self_: *mut whiteout_MdxLight) -> i32;
9246        pub fn whiteout_mdx_MdxLight_set_type(self_: *mut whiteout_MdxLight, value: i32);
9247        pub fn whiteout_mdx_MdxLight_get_attenuationStart(self_: *mut whiteout_MdxLight) -> f32;
9248        pub fn whiteout_mdx_MdxLight_set_attenuationStart(
9249            self_: *mut whiteout_MdxLight,
9250            value: f32,
9251        );
9252        pub fn whiteout_mdx_MdxLight_get_attenuationEnd(self_: *mut whiteout_MdxLight) -> f32;
9253        pub fn whiteout_mdx_MdxLight_set_attenuationEnd(self_: *mut whiteout_MdxLight, value: f32);
9254        pub fn whiteout_mdx_MdxLight_get_color(
9255            self_: *mut whiteout_MdxLight,
9256        ) -> *mut core::ffi::c_void;
9257        pub fn whiteout_mdx_MdxLight_set_color(
9258            self_: *mut whiteout_MdxLight,
9259            value: *const core::ffi::c_void,
9260        );
9261        pub fn whiteout_mdx_MdxLight_get_intensity(self_: *mut whiteout_MdxLight) -> f32;
9262        pub fn whiteout_mdx_MdxLight_set_intensity(self_: *mut whiteout_MdxLight, value: f32);
9263        pub fn whiteout_mdx_MdxLight_get_ambientColor(
9264            self_: *mut whiteout_MdxLight,
9265        ) -> *mut core::ffi::c_void;
9266        pub fn whiteout_mdx_MdxLight_set_ambientColor(
9267            self_: *mut whiteout_MdxLight,
9268            value: *const core::ffi::c_void,
9269        );
9270        pub fn whiteout_mdx_MdxLight_get_ambientIntensity(self_: *mut whiteout_MdxLight) -> f32;
9271        pub fn whiteout_mdx_MdxLight_set_ambientIntensity(
9272            self_: *mut whiteout_MdxLight,
9273            value: f32,
9274        );
9275        pub fn whiteout_mdx_MdxLight_get_shadowIntensity(self_: *mut whiteout_MdxLight) -> f32;
9276        pub fn whiteout_mdx_MdxLight_set_shadowIntensity(self_: *mut whiteout_MdxLight, value: f32);
9277        pub fn whiteout_mdx_MdxLight_get_attenuationStartTracks(
9278            self_: *mut whiteout_MdxLight,
9279        ) -> *mut whiteout_MdxTrackF32;
9280        pub fn whiteout_mdx_MdxLight_set_attenuationStartTracks(
9281            self_: *mut whiteout_MdxLight,
9282            value: *const whiteout_MdxTrackF32,
9283        );
9284        pub fn whiteout_mdx_MdxLight_get_attenuationEndTracks(
9285            self_: *mut whiteout_MdxLight,
9286        ) -> *mut whiteout_MdxTrackF32;
9287        pub fn whiteout_mdx_MdxLight_set_attenuationEndTracks(
9288            self_: *mut whiteout_MdxLight,
9289            value: *const whiteout_MdxTrackF32,
9290        );
9291        pub fn whiteout_mdx_MdxLight_get_colorTracks(
9292            self_: *mut whiteout_MdxLight,
9293        ) -> *mut whiteout_MdxTrackVector3f;
9294        pub fn whiteout_mdx_MdxLight_set_colorTracks(
9295            self_: *mut whiteout_MdxLight,
9296            value: *const whiteout_MdxTrackVector3f,
9297        );
9298        pub fn whiteout_mdx_MdxLight_get_intensityTracks(
9299            self_: *mut whiteout_MdxLight,
9300        ) -> *mut whiteout_MdxTrackF32;
9301        pub fn whiteout_mdx_MdxLight_set_intensityTracks(
9302            self_: *mut whiteout_MdxLight,
9303            value: *const whiteout_MdxTrackF32,
9304        );
9305        pub fn whiteout_mdx_MdxLight_get_ambientIntensityTracks(
9306            self_: *mut whiteout_MdxLight,
9307        ) -> *mut whiteout_MdxTrackF32;
9308        pub fn whiteout_mdx_MdxLight_set_ambientIntensityTracks(
9309            self_: *mut whiteout_MdxLight,
9310            value: *const whiteout_MdxTrackF32,
9311        );
9312        pub fn whiteout_mdx_MdxLight_get_ambientColorTracks(
9313            self_: *mut whiteout_MdxLight,
9314        ) -> *mut whiteout_MdxTrackVector3f;
9315        pub fn whiteout_mdx_MdxLight_set_ambientColorTracks(
9316            self_: *mut whiteout_MdxLight,
9317            value: *const whiteout_MdxTrackVector3f,
9318        );
9319        pub fn whiteout_mdx_MdxLight_get_visibilityTracks(
9320            self_: *mut whiteout_MdxLight,
9321        ) -> *mut whiteout_MdxTrackF32;
9322        pub fn whiteout_mdx_MdxLight_set_visibilityTracks(
9323            self_: *mut whiteout_MdxLight,
9324            value: *const whiteout_MdxTrackF32,
9325        );
9326        pub fn whiteout_mdx_MdxLight_get_shadowIntensityTracks(
9327            self_: *mut whiteout_MdxLight,
9328        ) -> *mut whiteout_MdxTrackF32;
9329        pub fn whiteout_mdx_MdxLight_set_shadowIntensityTracks(
9330            self_: *mut whiteout_MdxLight,
9331            value: *const whiteout_MdxTrackF32,
9332        );
9333        // Helper
9334        pub fn whiteout_mdx_MdxHelper_new() -> *mut whiteout_MdxHelper;
9335        pub fn whiteout_mdx_MdxHelper_delete(self_: *mut whiteout_MdxHelper);
9336        pub fn whiteout_mdx_MdxHelper_get_node(
9337            self_: *mut whiteout_MdxHelper,
9338        ) -> *mut whiteout_MdxNode;
9339        pub fn whiteout_mdx_MdxHelper_set_node(
9340            self_: *mut whiteout_MdxHelper,
9341            value: *const whiteout_MdxNode,
9342        );
9343        // Attachment
9344        pub fn whiteout_mdx_MdxAttachment_new() -> *mut whiteout_MdxAttachment;
9345        pub fn whiteout_mdx_MdxAttachment_delete(self_: *mut whiteout_MdxAttachment);
9346        pub fn whiteout_mdx_MdxAttachment_get_node(
9347            self_: *mut whiteout_MdxAttachment,
9348        ) -> *mut whiteout_MdxNode;
9349        pub fn whiteout_mdx_MdxAttachment_set_node(
9350            self_: *mut whiteout_MdxAttachment,
9351            value: *const whiteout_MdxNode,
9352        );
9353        pub fn whiteout_mdx_MdxAttachment_get_path(
9354            self_: *mut whiteout_MdxAttachment,
9355        ) -> RawCString;
9356        pub fn whiteout_mdx_MdxAttachment_set_path(
9357            self_: *mut whiteout_MdxAttachment,
9358            value: *const core::ffi::c_char,
9359        );
9360        pub fn whiteout_mdx_MdxAttachment_get_attachmentId(
9361            self_: *mut whiteout_MdxAttachment,
9362        ) -> u32;
9363        pub fn whiteout_mdx_MdxAttachment_set_attachmentId(
9364            self_: *mut whiteout_MdxAttachment,
9365            value: u32,
9366        );
9367        pub fn whiteout_mdx_MdxAttachment_get_visibilityTracks(
9368            self_: *mut whiteout_MdxAttachment,
9369        ) -> *mut whiteout_MdxTrackF32;
9370        pub fn whiteout_mdx_MdxAttachment_set_visibilityTracks(
9371            self_: *mut whiteout_MdxAttachment,
9372            value: *const whiteout_MdxTrackF32,
9373        );
9374        // ParticleEmitter
9375        pub fn whiteout_mdx_MdxParticleEmitter_new() -> *mut whiteout_MdxParticleEmitter;
9376        pub fn whiteout_mdx_MdxParticleEmitter_delete(self_: *mut whiteout_MdxParticleEmitter);
9377        pub fn whiteout_mdx_MdxParticleEmitter_get_node(
9378            self_: *mut whiteout_MdxParticleEmitter,
9379        ) -> *mut whiteout_MdxNode;
9380        pub fn whiteout_mdx_MdxParticleEmitter_set_node(
9381            self_: *mut whiteout_MdxParticleEmitter,
9382            value: *const whiteout_MdxNode,
9383        );
9384        pub fn whiteout_mdx_MdxParticleEmitter_get_emissionRate(
9385            self_: *mut whiteout_MdxParticleEmitter,
9386        ) -> f32;
9387        pub fn whiteout_mdx_MdxParticleEmitter_set_emissionRate(
9388            self_: *mut whiteout_MdxParticleEmitter,
9389            value: f32,
9390        );
9391        pub fn whiteout_mdx_MdxParticleEmitter_get_gravity(
9392            self_: *mut whiteout_MdxParticleEmitter,
9393        ) -> f32;
9394        pub fn whiteout_mdx_MdxParticleEmitter_set_gravity(
9395            self_: *mut whiteout_MdxParticleEmitter,
9396            value: f32,
9397        );
9398        pub fn whiteout_mdx_MdxParticleEmitter_get_longitude(
9399            self_: *mut whiteout_MdxParticleEmitter,
9400        ) -> f32;
9401        pub fn whiteout_mdx_MdxParticleEmitter_set_longitude(
9402            self_: *mut whiteout_MdxParticleEmitter,
9403            value: f32,
9404        );
9405        pub fn whiteout_mdx_MdxParticleEmitter_get_latitude(
9406            self_: *mut whiteout_MdxParticleEmitter,
9407        ) -> f32;
9408        pub fn whiteout_mdx_MdxParticleEmitter_set_latitude(
9409            self_: *mut whiteout_MdxParticleEmitter,
9410            value: f32,
9411        );
9412        pub fn whiteout_mdx_MdxParticleEmitter_get_spawnModelFileName(
9413            self_: *mut whiteout_MdxParticleEmitter,
9414        ) -> RawCString;
9415        pub fn whiteout_mdx_MdxParticleEmitter_set_spawnModelFileName(
9416            self_: *mut whiteout_MdxParticleEmitter,
9417            value: *const core::ffi::c_char,
9418        );
9419        pub fn whiteout_mdx_MdxParticleEmitter_get_lifespan(
9420            self_: *mut whiteout_MdxParticleEmitter,
9421        ) -> f32;
9422        pub fn whiteout_mdx_MdxParticleEmitter_set_lifespan(
9423            self_: *mut whiteout_MdxParticleEmitter,
9424            value: f32,
9425        );
9426        pub fn whiteout_mdx_MdxParticleEmitter_get_initialVelocity(
9427            self_: *mut whiteout_MdxParticleEmitter,
9428        ) -> f32;
9429        pub fn whiteout_mdx_MdxParticleEmitter_set_initialVelocity(
9430            self_: *mut whiteout_MdxParticleEmitter,
9431            value: f32,
9432        );
9433        pub fn whiteout_mdx_MdxParticleEmitter_get_emissionRateTracks(
9434            self_: *mut whiteout_MdxParticleEmitter,
9435        ) -> *mut whiteout_MdxTrackF32;
9436        pub fn whiteout_mdx_MdxParticleEmitter_set_emissionRateTracks(
9437            self_: *mut whiteout_MdxParticleEmitter,
9438            value: *const whiteout_MdxTrackF32,
9439        );
9440        pub fn whiteout_mdx_MdxParticleEmitter_get_gravityTracks(
9441            self_: *mut whiteout_MdxParticleEmitter,
9442        ) -> *mut whiteout_MdxTrackF32;
9443        pub fn whiteout_mdx_MdxParticleEmitter_set_gravityTracks(
9444            self_: *mut whiteout_MdxParticleEmitter,
9445            value: *const whiteout_MdxTrackF32,
9446        );
9447        pub fn whiteout_mdx_MdxParticleEmitter_get_longitudeTracks(
9448            self_: *mut whiteout_MdxParticleEmitter,
9449        ) -> *mut whiteout_MdxTrackF32;
9450        pub fn whiteout_mdx_MdxParticleEmitter_set_longitudeTracks(
9451            self_: *mut whiteout_MdxParticleEmitter,
9452            value: *const whiteout_MdxTrackF32,
9453        );
9454        pub fn whiteout_mdx_MdxParticleEmitter_get_latitudeTracks(
9455            self_: *mut whiteout_MdxParticleEmitter,
9456        ) -> *mut whiteout_MdxTrackF32;
9457        pub fn whiteout_mdx_MdxParticleEmitter_set_latitudeTracks(
9458            self_: *mut whiteout_MdxParticleEmitter,
9459            value: *const whiteout_MdxTrackF32,
9460        );
9461        pub fn whiteout_mdx_MdxParticleEmitter_get_lifespanTracks(
9462            self_: *mut whiteout_MdxParticleEmitter,
9463        ) -> *mut whiteout_MdxTrackF32;
9464        pub fn whiteout_mdx_MdxParticleEmitter_set_lifespanTracks(
9465            self_: *mut whiteout_MdxParticleEmitter,
9466            value: *const whiteout_MdxTrackF32,
9467        );
9468        pub fn whiteout_mdx_MdxParticleEmitter_get_speedTracks(
9469            self_: *mut whiteout_MdxParticleEmitter,
9470        ) -> *mut whiteout_MdxTrackF32;
9471        pub fn whiteout_mdx_MdxParticleEmitter_set_speedTracks(
9472            self_: *mut whiteout_MdxParticleEmitter,
9473            value: *const whiteout_MdxTrackF32,
9474        );
9475        pub fn whiteout_mdx_MdxParticleEmitter_get_visibilityTracks(
9476            self_: *mut whiteout_MdxParticleEmitter,
9477        ) -> *mut whiteout_MdxTrackF32;
9478        pub fn whiteout_mdx_MdxParticleEmitter_set_visibilityTracks(
9479            self_: *mut whiteout_MdxParticleEmitter,
9480            value: *const whiteout_MdxTrackF32,
9481        );
9482        // ParticleEmitter2
9483        pub fn whiteout_mdx_MdxParticleEmitter2_new() -> *mut whiteout_MdxParticleEmitter2;
9484        pub fn whiteout_mdx_MdxParticleEmitter2_delete(self_: *mut whiteout_MdxParticleEmitter2);
9485        pub fn whiteout_mdx_MdxParticleEmitter2_get_node(
9486            self_: *mut whiteout_MdxParticleEmitter2,
9487        ) -> *mut whiteout_MdxNode;
9488        pub fn whiteout_mdx_MdxParticleEmitter2_set_node(
9489            self_: *mut whiteout_MdxParticleEmitter2,
9490            value: *const whiteout_MdxNode,
9491        );
9492        pub fn whiteout_mdx_MdxParticleEmitter2_get_speed(
9493            self_: *mut whiteout_MdxParticleEmitter2,
9494        ) -> f32;
9495        pub fn whiteout_mdx_MdxParticleEmitter2_set_speed(
9496            self_: *mut whiteout_MdxParticleEmitter2,
9497            value: f32,
9498        );
9499        pub fn whiteout_mdx_MdxParticleEmitter2_get_variation(
9500            self_: *mut whiteout_MdxParticleEmitter2,
9501        ) -> f32;
9502        pub fn whiteout_mdx_MdxParticleEmitter2_set_variation(
9503            self_: *mut whiteout_MdxParticleEmitter2,
9504            value: f32,
9505        );
9506        pub fn whiteout_mdx_MdxParticleEmitter2_get_latitude(
9507            self_: *mut whiteout_MdxParticleEmitter2,
9508        ) -> f32;
9509        pub fn whiteout_mdx_MdxParticleEmitter2_set_latitude(
9510            self_: *mut whiteout_MdxParticleEmitter2,
9511            value: f32,
9512        );
9513        pub fn whiteout_mdx_MdxParticleEmitter2_get_gravity(
9514            self_: *mut whiteout_MdxParticleEmitter2,
9515        ) -> f32;
9516        pub fn whiteout_mdx_MdxParticleEmitter2_set_gravity(
9517            self_: *mut whiteout_MdxParticleEmitter2,
9518            value: f32,
9519        );
9520        pub fn whiteout_mdx_MdxParticleEmitter2_get_lifespan(
9521            self_: *mut whiteout_MdxParticleEmitter2,
9522        ) -> f32;
9523        pub fn whiteout_mdx_MdxParticleEmitter2_set_lifespan(
9524            self_: *mut whiteout_MdxParticleEmitter2,
9525            value: f32,
9526        );
9527        pub fn whiteout_mdx_MdxParticleEmitter2_get_emissionRate(
9528            self_: *mut whiteout_MdxParticleEmitter2,
9529        ) -> f32;
9530        pub fn whiteout_mdx_MdxParticleEmitter2_set_emissionRate(
9531            self_: *mut whiteout_MdxParticleEmitter2,
9532            value: f32,
9533        );
9534        pub fn whiteout_mdx_MdxParticleEmitter2_get_length(
9535            self_: *mut whiteout_MdxParticleEmitter2,
9536        ) -> f32;
9537        pub fn whiteout_mdx_MdxParticleEmitter2_set_length(
9538            self_: *mut whiteout_MdxParticleEmitter2,
9539            value: f32,
9540        );
9541        pub fn whiteout_mdx_MdxParticleEmitter2_get_width(
9542            self_: *mut whiteout_MdxParticleEmitter2,
9543        ) -> f32;
9544        pub fn whiteout_mdx_MdxParticleEmitter2_set_width(
9545            self_: *mut whiteout_MdxParticleEmitter2,
9546            value: f32,
9547        );
9548        pub fn whiteout_mdx_MdxParticleEmitter2_get_filterMode(
9549            self_: *mut whiteout_MdxParticleEmitter2,
9550        ) -> u32;
9551        pub fn whiteout_mdx_MdxParticleEmitter2_set_filterMode(
9552            self_: *mut whiteout_MdxParticleEmitter2,
9553            value: u32,
9554        );
9555        pub fn whiteout_mdx_MdxParticleEmitter2_get_rows(
9556            self_: *mut whiteout_MdxParticleEmitter2,
9557        ) -> u32;
9558        pub fn whiteout_mdx_MdxParticleEmitter2_set_rows(
9559            self_: *mut whiteout_MdxParticleEmitter2,
9560            value: u32,
9561        );
9562        pub fn whiteout_mdx_MdxParticleEmitter2_get_columns(
9563            self_: *mut whiteout_MdxParticleEmitter2,
9564        ) -> u32;
9565        pub fn whiteout_mdx_MdxParticleEmitter2_set_columns(
9566            self_: *mut whiteout_MdxParticleEmitter2,
9567            value: u32,
9568        );
9569        pub fn whiteout_mdx_MdxParticleEmitter2_get_headOrTail(
9570            self_: *mut whiteout_MdxParticleEmitter2,
9571        ) -> u32;
9572        pub fn whiteout_mdx_MdxParticleEmitter2_set_headOrTail(
9573            self_: *mut whiteout_MdxParticleEmitter2,
9574            value: u32,
9575        );
9576        pub fn whiteout_mdx_MdxParticleEmitter2_get_tailLength(
9577            self_: *mut whiteout_MdxParticleEmitter2,
9578        ) -> f32;
9579        pub fn whiteout_mdx_MdxParticleEmitter2_set_tailLength(
9580            self_: *mut whiteout_MdxParticleEmitter2,
9581            value: f32,
9582        );
9583        pub fn whiteout_mdx_MdxParticleEmitter2_get_time(
9584            self_: *mut whiteout_MdxParticleEmitter2,
9585        ) -> f32;
9586        pub fn whiteout_mdx_MdxParticleEmitter2_set_time(
9587            self_: *mut whiteout_MdxParticleEmitter2,
9588            value: f32,
9589        );
9590        pub fn whiteout_mdx_MdxParticleEmitter2_segmentColor_size() -> usize;
9591        pub fn whiteout_mdx_MdxParticleEmitter2_get_segmentColor_at(
9592            self_: *mut whiteout_MdxParticleEmitter2,
9593            index: usize,
9594        ) -> *mut core::ffi::c_void;
9595        pub fn whiteout_mdx_MdxParticleEmitter2_segmentAlpha_size() -> usize;
9596        pub fn whiteout_mdx_MdxParticleEmitter2_get_segmentAlpha_at(
9597            self_: *mut whiteout_MdxParticleEmitter2,
9598            index: usize,
9599        ) -> u8;
9600        pub fn whiteout_mdx_MdxParticleEmitter2_set_segmentAlpha_at(
9601            self_: *mut whiteout_MdxParticleEmitter2,
9602            index: usize,
9603            value: u8,
9604        );
9605        pub fn whiteout_mdx_MdxParticleEmitter2_segmentScaling_size() -> usize;
9606        pub fn whiteout_mdx_MdxParticleEmitter2_get_segmentScaling_at(
9607            self_: *mut whiteout_MdxParticleEmitter2,
9608            index: usize,
9609        ) -> f32;
9610        pub fn whiteout_mdx_MdxParticleEmitter2_set_segmentScaling_at(
9611            self_: *mut whiteout_MdxParticleEmitter2,
9612            index: usize,
9613            value: f32,
9614        );
9615        pub fn whiteout_mdx_MdxParticleEmitter2_headInterval_size() -> usize;
9616        pub fn whiteout_mdx_MdxParticleEmitter2_get_headInterval_at(
9617            self_: *mut whiteout_MdxParticleEmitter2,
9618            index: usize,
9619        ) -> u32;
9620        pub fn whiteout_mdx_MdxParticleEmitter2_set_headInterval_at(
9621            self_: *mut whiteout_MdxParticleEmitter2,
9622            index: usize,
9623            value: u32,
9624        );
9625        pub fn whiteout_mdx_MdxParticleEmitter2_headDecayInterval_size() -> usize;
9626        pub fn whiteout_mdx_MdxParticleEmitter2_get_headDecayInterval_at(
9627            self_: *mut whiteout_MdxParticleEmitter2,
9628            index: usize,
9629        ) -> u32;
9630        pub fn whiteout_mdx_MdxParticleEmitter2_set_headDecayInterval_at(
9631            self_: *mut whiteout_MdxParticleEmitter2,
9632            index: usize,
9633            value: u32,
9634        );
9635        pub fn whiteout_mdx_MdxParticleEmitter2_tailInterval_size() -> usize;
9636        pub fn whiteout_mdx_MdxParticleEmitter2_get_tailInterval_at(
9637            self_: *mut whiteout_MdxParticleEmitter2,
9638            index: usize,
9639        ) -> u32;
9640        pub fn whiteout_mdx_MdxParticleEmitter2_set_tailInterval_at(
9641            self_: *mut whiteout_MdxParticleEmitter2,
9642            index: usize,
9643            value: u32,
9644        );
9645        pub fn whiteout_mdx_MdxParticleEmitter2_tailDecayInterval_size() -> usize;
9646        pub fn whiteout_mdx_MdxParticleEmitter2_get_tailDecayInterval_at(
9647            self_: *mut whiteout_MdxParticleEmitter2,
9648            index: usize,
9649        ) -> u32;
9650        pub fn whiteout_mdx_MdxParticleEmitter2_set_tailDecayInterval_at(
9651            self_: *mut whiteout_MdxParticleEmitter2,
9652            index: usize,
9653            value: u32,
9654        );
9655        pub fn whiteout_mdx_MdxParticleEmitter2_get_textureId(
9656            self_: *mut whiteout_MdxParticleEmitter2,
9657        ) -> u32;
9658        pub fn whiteout_mdx_MdxParticleEmitter2_set_textureId(
9659            self_: *mut whiteout_MdxParticleEmitter2,
9660            value: u32,
9661        );
9662        pub fn whiteout_mdx_MdxParticleEmitter2_get_squirt(
9663            self_: *mut whiteout_MdxParticleEmitter2,
9664        ) -> u32;
9665        pub fn whiteout_mdx_MdxParticleEmitter2_set_squirt(
9666            self_: *mut whiteout_MdxParticleEmitter2,
9667            value: u32,
9668        );
9669        pub fn whiteout_mdx_MdxParticleEmitter2_get_priorityPlane(
9670            self_: *mut whiteout_MdxParticleEmitter2,
9671        ) -> i32;
9672        pub fn whiteout_mdx_MdxParticleEmitter2_set_priorityPlane(
9673            self_: *mut whiteout_MdxParticleEmitter2,
9674            value: i32,
9675        );
9676        pub fn whiteout_mdx_MdxParticleEmitter2_get_replaceableId(
9677            self_: *mut whiteout_MdxParticleEmitter2,
9678        ) -> u32;
9679        pub fn whiteout_mdx_MdxParticleEmitter2_set_replaceableId(
9680            self_: *mut whiteout_MdxParticleEmitter2,
9681            value: u32,
9682        );
9683        pub fn whiteout_mdx_MdxParticleEmitter2_get_speedTracks(
9684            self_: *mut whiteout_MdxParticleEmitter2,
9685        ) -> *mut whiteout_MdxTrackF32;
9686        pub fn whiteout_mdx_MdxParticleEmitter2_set_speedTracks(
9687            self_: *mut whiteout_MdxParticleEmitter2,
9688            value: *const whiteout_MdxTrackF32,
9689        );
9690        pub fn whiteout_mdx_MdxParticleEmitter2_get_variationTracks(
9691            self_: *mut whiteout_MdxParticleEmitter2,
9692        ) -> *mut whiteout_MdxTrackF32;
9693        pub fn whiteout_mdx_MdxParticleEmitter2_set_variationTracks(
9694            self_: *mut whiteout_MdxParticleEmitter2,
9695            value: *const whiteout_MdxTrackF32,
9696        );
9697        pub fn whiteout_mdx_MdxParticleEmitter2_get_latitudeTracks(
9698            self_: *mut whiteout_MdxParticleEmitter2,
9699        ) -> *mut whiteout_MdxTrackF32;
9700        pub fn whiteout_mdx_MdxParticleEmitter2_set_latitudeTracks(
9701            self_: *mut whiteout_MdxParticleEmitter2,
9702            value: *const whiteout_MdxTrackF32,
9703        );
9704        pub fn whiteout_mdx_MdxParticleEmitter2_get_gravityTracks(
9705            self_: *mut whiteout_MdxParticleEmitter2,
9706        ) -> *mut whiteout_MdxTrackF32;
9707        pub fn whiteout_mdx_MdxParticleEmitter2_set_gravityTracks(
9708            self_: *mut whiteout_MdxParticleEmitter2,
9709            value: *const whiteout_MdxTrackF32,
9710        );
9711        pub fn whiteout_mdx_MdxParticleEmitter2_get_emissionRateTracks(
9712            self_: *mut whiteout_MdxParticleEmitter2,
9713        ) -> *mut whiteout_MdxTrackF32;
9714        pub fn whiteout_mdx_MdxParticleEmitter2_set_emissionRateTracks(
9715            self_: *mut whiteout_MdxParticleEmitter2,
9716            value: *const whiteout_MdxTrackF32,
9717        );
9718        pub fn whiteout_mdx_MdxParticleEmitter2_get_lengthTracks(
9719            self_: *mut whiteout_MdxParticleEmitter2,
9720        ) -> *mut whiteout_MdxTrackF32;
9721        pub fn whiteout_mdx_MdxParticleEmitter2_set_lengthTracks(
9722            self_: *mut whiteout_MdxParticleEmitter2,
9723            value: *const whiteout_MdxTrackF32,
9724        );
9725        pub fn whiteout_mdx_MdxParticleEmitter2_get_widthTracks(
9726            self_: *mut whiteout_MdxParticleEmitter2,
9727        ) -> *mut whiteout_MdxTrackF32;
9728        pub fn whiteout_mdx_MdxParticleEmitter2_set_widthTracks(
9729            self_: *mut whiteout_MdxParticleEmitter2,
9730            value: *const whiteout_MdxTrackF32,
9731        );
9732        pub fn whiteout_mdx_MdxParticleEmitter2_get_visibilityTracks(
9733            self_: *mut whiteout_MdxParticleEmitter2,
9734        ) -> *mut whiteout_MdxTrackF32;
9735        pub fn whiteout_mdx_MdxParticleEmitter2_set_visibilityTracks(
9736            self_: *mut whiteout_MdxParticleEmitter2,
9737            value: *const whiteout_MdxTrackF32,
9738        );
9739        // RibbonEmitter
9740        pub fn whiteout_mdx_MdxRibbonEmitter_new() -> *mut whiteout_MdxRibbonEmitter;
9741        pub fn whiteout_mdx_MdxRibbonEmitter_delete(self_: *mut whiteout_MdxRibbonEmitter);
9742        pub fn whiteout_mdx_MdxRibbonEmitter_get_node(
9743            self_: *mut whiteout_MdxRibbonEmitter,
9744        ) -> *mut whiteout_MdxNode;
9745        pub fn whiteout_mdx_MdxRibbonEmitter_set_node(
9746            self_: *mut whiteout_MdxRibbonEmitter,
9747            value: *const whiteout_MdxNode,
9748        );
9749        pub fn whiteout_mdx_MdxRibbonEmitter_get_heightAbove(
9750            self_: *mut whiteout_MdxRibbonEmitter,
9751        ) -> f32;
9752        pub fn whiteout_mdx_MdxRibbonEmitter_set_heightAbove(
9753            self_: *mut whiteout_MdxRibbonEmitter,
9754            value: f32,
9755        );
9756        pub fn whiteout_mdx_MdxRibbonEmitter_get_heightBelow(
9757            self_: *mut whiteout_MdxRibbonEmitter,
9758        ) -> f32;
9759        pub fn whiteout_mdx_MdxRibbonEmitter_set_heightBelow(
9760            self_: *mut whiteout_MdxRibbonEmitter,
9761            value: f32,
9762        );
9763        pub fn whiteout_mdx_MdxRibbonEmitter_get_alpha(
9764            self_: *mut whiteout_MdxRibbonEmitter,
9765        ) -> f32;
9766        pub fn whiteout_mdx_MdxRibbonEmitter_set_alpha(
9767            self_: *mut whiteout_MdxRibbonEmitter,
9768            value: f32,
9769        );
9770        pub fn whiteout_mdx_MdxRibbonEmitter_get_color(
9771            self_: *mut whiteout_MdxRibbonEmitter,
9772        ) -> *mut core::ffi::c_void;
9773        pub fn whiteout_mdx_MdxRibbonEmitter_set_color(
9774            self_: *mut whiteout_MdxRibbonEmitter,
9775            value: *const core::ffi::c_void,
9776        );
9777        pub fn whiteout_mdx_MdxRibbonEmitter_get_lifespan(
9778            self_: *mut whiteout_MdxRibbonEmitter,
9779        ) -> f32;
9780        pub fn whiteout_mdx_MdxRibbonEmitter_set_lifespan(
9781            self_: *mut whiteout_MdxRibbonEmitter,
9782            value: f32,
9783        );
9784        pub fn whiteout_mdx_MdxRibbonEmitter_get_textureSlot(
9785            self_: *mut whiteout_MdxRibbonEmitter,
9786        ) -> u32;
9787        pub fn whiteout_mdx_MdxRibbonEmitter_set_textureSlot(
9788            self_: *mut whiteout_MdxRibbonEmitter,
9789            value: u32,
9790        );
9791        pub fn whiteout_mdx_MdxRibbonEmitter_get_emissionRate(
9792            self_: *mut whiteout_MdxRibbonEmitter,
9793        ) -> u32;
9794        pub fn whiteout_mdx_MdxRibbonEmitter_set_emissionRate(
9795            self_: *mut whiteout_MdxRibbonEmitter,
9796            value: u32,
9797        );
9798        pub fn whiteout_mdx_MdxRibbonEmitter_get_rows(self_: *mut whiteout_MdxRibbonEmitter)
9799            -> u32;
9800        pub fn whiteout_mdx_MdxRibbonEmitter_set_rows(
9801            self_: *mut whiteout_MdxRibbonEmitter,
9802            value: u32,
9803        );
9804        pub fn whiteout_mdx_MdxRibbonEmitter_get_columns(
9805            self_: *mut whiteout_MdxRibbonEmitter,
9806        ) -> u32;
9807        pub fn whiteout_mdx_MdxRibbonEmitter_set_columns(
9808            self_: *mut whiteout_MdxRibbonEmitter,
9809            value: u32,
9810        );
9811        pub fn whiteout_mdx_MdxRibbonEmitter_get_materialId(
9812            self_: *mut whiteout_MdxRibbonEmitter,
9813        ) -> u32;
9814        pub fn whiteout_mdx_MdxRibbonEmitter_set_materialId(
9815            self_: *mut whiteout_MdxRibbonEmitter,
9816            value: u32,
9817        );
9818        pub fn whiteout_mdx_MdxRibbonEmitter_get_gravity(
9819            self_: *mut whiteout_MdxRibbonEmitter,
9820        ) -> f32;
9821        pub fn whiteout_mdx_MdxRibbonEmitter_set_gravity(
9822            self_: *mut whiteout_MdxRibbonEmitter,
9823            value: f32,
9824        );
9825        pub fn whiteout_mdx_MdxRibbonEmitter_get_heightAboveTracks(
9826            self_: *mut whiteout_MdxRibbonEmitter,
9827        ) -> *mut whiteout_MdxTrackF32;
9828        pub fn whiteout_mdx_MdxRibbonEmitter_set_heightAboveTracks(
9829            self_: *mut whiteout_MdxRibbonEmitter,
9830            value: *const whiteout_MdxTrackF32,
9831        );
9832        pub fn whiteout_mdx_MdxRibbonEmitter_get_heightBelowTracks(
9833            self_: *mut whiteout_MdxRibbonEmitter,
9834        ) -> *mut whiteout_MdxTrackF32;
9835        pub fn whiteout_mdx_MdxRibbonEmitter_set_heightBelowTracks(
9836            self_: *mut whiteout_MdxRibbonEmitter,
9837            value: *const whiteout_MdxTrackF32,
9838        );
9839        pub fn whiteout_mdx_MdxRibbonEmitter_get_alphaTracks(
9840            self_: *mut whiteout_MdxRibbonEmitter,
9841        ) -> *mut whiteout_MdxTrackF32;
9842        pub fn whiteout_mdx_MdxRibbonEmitter_set_alphaTracks(
9843            self_: *mut whiteout_MdxRibbonEmitter,
9844            value: *const whiteout_MdxTrackF32,
9845        );
9846        pub fn whiteout_mdx_MdxRibbonEmitter_get_colorTracks(
9847            self_: *mut whiteout_MdxRibbonEmitter,
9848        ) -> *mut whiteout_MdxTrackVector3f;
9849        pub fn whiteout_mdx_MdxRibbonEmitter_set_colorTracks(
9850            self_: *mut whiteout_MdxRibbonEmitter,
9851            value: *const whiteout_MdxTrackVector3f,
9852        );
9853        pub fn whiteout_mdx_MdxRibbonEmitter_get_textureSlotTracks(
9854            self_: *mut whiteout_MdxRibbonEmitter,
9855        ) -> *mut whiteout_MdxTrackU32;
9856        pub fn whiteout_mdx_MdxRibbonEmitter_set_textureSlotTracks(
9857            self_: *mut whiteout_MdxRibbonEmitter,
9858            value: *const whiteout_MdxTrackU32,
9859        );
9860        pub fn whiteout_mdx_MdxRibbonEmitter_get_visibilityTracks(
9861            self_: *mut whiteout_MdxRibbonEmitter,
9862        ) -> *mut whiteout_MdxTrackF32;
9863        pub fn whiteout_mdx_MdxRibbonEmitter_set_visibilityTracks(
9864            self_: *mut whiteout_MdxRibbonEmitter,
9865            value: *const whiteout_MdxTrackF32,
9866        );
9867        // EventObject
9868        pub fn whiteout_mdx_MdxEventObject_new() -> *mut whiteout_MdxEventObject;
9869        pub fn whiteout_mdx_MdxEventObject_delete(self_: *mut whiteout_MdxEventObject);
9870        pub fn whiteout_mdx_MdxEventObject_get_node(
9871            self_: *mut whiteout_MdxEventObject,
9872        ) -> *mut whiteout_MdxNode;
9873        pub fn whiteout_mdx_MdxEventObject_set_node(
9874            self_: *mut whiteout_MdxEventObject,
9875            value: *const whiteout_MdxNode,
9876        );
9877        pub fn whiteout_mdx_MdxEventObject_get_globalSequenceId(
9878            self_: *mut whiteout_MdxEventObject,
9879        ) -> u32;
9880        pub fn whiteout_mdx_MdxEventObject_set_globalSequenceId(
9881            self_: *mut whiteout_MdxEventObject,
9882            value: u32,
9883        );
9884        pub fn whiteout_mdx_MdxEventObject_get_eventTrackTimes_count(
9885            self_: *mut whiteout_MdxEventObject,
9886        ) -> usize;
9887        pub fn whiteout_mdx_MdxEventObject_resize_eventTrackTimes(
9888            self_: *mut whiteout_MdxEventObject,
9889            count: usize,
9890        );
9891        pub fn whiteout_mdx_MdxEventObject_get_eventTrackTimes_data(
9892            self_: *mut whiteout_MdxEventObject,
9893        ) -> *const u32;
9894        pub fn whiteout_mdx_MdxEventObject_assign_eventTrackTimes(
9895            self_: *mut whiteout_MdxEventObject,
9896            data: *const u32,
9897            count: usize,
9898        );
9899        // Camera
9900        pub fn whiteout_mdx_MdxCamera_new() -> *mut whiteout_MdxCamera;
9901        pub fn whiteout_mdx_MdxCamera_delete(self_: *mut whiteout_MdxCamera);
9902        pub fn whiteout_mdx_MdxCamera_get_name(self_: *mut whiteout_MdxCamera) -> RawCString;
9903        pub fn whiteout_mdx_MdxCamera_set_name(
9904            self_: *mut whiteout_MdxCamera,
9905            value: *const core::ffi::c_char,
9906        );
9907        pub fn whiteout_mdx_MdxCamera_get_position(
9908            self_: *mut whiteout_MdxCamera,
9909        ) -> *mut core::ffi::c_void;
9910        pub fn whiteout_mdx_MdxCamera_set_position(
9911            self_: *mut whiteout_MdxCamera,
9912            value: *const core::ffi::c_void,
9913        );
9914        pub fn whiteout_mdx_MdxCamera_get_fieldOfView(self_: *mut whiteout_MdxCamera) -> f32;
9915        pub fn whiteout_mdx_MdxCamera_set_fieldOfView(self_: *mut whiteout_MdxCamera, value: f32);
9916        pub fn whiteout_mdx_MdxCamera_get_farClippingPlane(self_: *mut whiteout_MdxCamera) -> f32;
9917        pub fn whiteout_mdx_MdxCamera_set_farClippingPlane(
9918            self_: *mut whiteout_MdxCamera,
9919            value: f32,
9920        );
9921        pub fn whiteout_mdx_MdxCamera_get_nearClippingPlane(self_: *mut whiteout_MdxCamera) -> f32;
9922        pub fn whiteout_mdx_MdxCamera_set_nearClippingPlane(
9923            self_: *mut whiteout_MdxCamera,
9924            value: f32,
9925        );
9926        pub fn whiteout_mdx_MdxCamera_get_targetPosition(
9927            self_: *mut whiteout_MdxCamera,
9928        ) -> *mut core::ffi::c_void;
9929        pub fn whiteout_mdx_MdxCamera_set_targetPosition(
9930            self_: *mut whiteout_MdxCamera,
9931            value: *const core::ffi::c_void,
9932        );
9933        pub fn whiteout_mdx_MdxCamera_get_positionTracks(
9934            self_: *mut whiteout_MdxCamera,
9935        ) -> *mut whiteout_MdxTrackVector3f;
9936        pub fn whiteout_mdx_MdxCamera_set_positionTracks(
9937            self_: *mut whiteout_MdxCamera,
9938            value: *const whiteout_MdxTrackVector3f,
9939        );
9940        pub fn whiteout_mdx_MdxCamera_get_targetRotationTracks(
9941            self_: *mut whiteout_MdxCamera,
9942        ) -> *mut whiteout_MdxTrackF32;
9943        pub fn whiteout_mdx_MdxCamera_set_targetRotationTracks(
9944            self_: *mut whiteout_MdxCamera,
9945            value: *const whiteout_MdxTrackF32,
9946        );
9947        pub fn whiteout_mdx_MdxCamera_get_targetPositionTracks(
9948            self_: *mut whiteout_MdxCamera,
9949        ) -> *mut whiteout_MdxTrackVector3f;
9950        pub fn whiteout_mdx_MdxCamera_set_targetPositionTracks(
9951            self_: *mut whiteout_MdxCamera,
9952            value: *const whiteout_MdxTrackVector3f,
9953        );
9954        // CollisionShape
9955        pub fn whiteout_mdx_MdxCollisionShape_new() -> *mut whiteout_MdxCollisionShape;
9956        pub fn whiteout_mdx_MdxCollisionShape_delete(self_: *mut whiteout_MdxCollisionShape);
9957        pub fn whiteout_mdx_MdxCollisionShape_get_node(
9958            self_: *mut whiteout_MdxCollisionShape,
9959        ) -> *mut whiteout_MdxNode;
9960        pub fn whiteout_mdx_MdxCollisionShape_set_node(
9961            self_: *mut whiteout_MdxCollisionShape,
9962            value: *const whiteout_MdxNode,
9963        );
9964        pub fn whiteout_mdx_MdxCollisionShape_get_type(
9965            self_: *mut whiteout_MdxCollisionShape,
9966        ) -> i32;
9967        pub fn whiteout_mdx_MdxCollisionShape_set_type(
9968            self_: *mut whiteout_MdxCollisionShape,
9969            value: i32,
9970        );
9971        pub fn whiteout_mdx_MdxCollisionShape_get_vertices_count(
9972            self_: *mut whiteout_MdxCollisionShape,
9973        ) -> usize;
9974        pub fn whiteout_mdx_MdxCollisionShape_resize_vertices(
9975            self_: *mut whiteout_MdxCollisionShape,
9976            count: usize,
9977        );
9978        pub fn whiteout_mdx_MdxCollisionShape_get_vertices_data(
9979            self_: *mut whiteout_MdxCollisionShape,
9980        ) -> *const f32;
9981        pub fn whiteout_mdx_MdxCollisionShape_assign_vertices(
9982            self_: *mut whiteout_MdxCollisionShape,
9983            data: *const f32,
9984            count: usize,
9985        );
9986        pub fn whiteout_mdx_MdxCollisionShape_get_radius(
9987            self_: *mut whiteout_MdxCollisionShape,
9988        ) -> f32;
9989        pub fn whiteout_mdx_MdxCollisionShape_set_radius(
9990            self_: *mut whiteout_MdxCollisionShape,
9991            value: f32,
9992        );
9993        // FaceEffect
9994        pub fn whiteout_mdx_MdxFaceEffect_new() -> *mut whiteout_MdxFaceEffect;
9995        pub fn whiteout_mdx_MdxFaceEffect_delete(self_: *mut whiteout_MdxFaceEffect);
9996        pub fn whiteout_mdx_MdxFaceEffect_get_name(
9997            self_: *mut whiteout_MdxFaceEffect,
9998        ) -> RawCString;
9999        pub fn whiteout_mdx_MdxFaceEffect_set_name(
10000            self_: *mut whiteout_MdxFaceEffect,
10001            value: *const core::ffi::c_char,
10002        );
10003        pub fn whiteout_mdx_MdxFaceEffect_get_path(
10004            self_: *mut whiteout_MdxFaceEffect,
10005        ) -> RawCString;
10006        pub fn whiteout_mdx_MdxFaceEffect_set_path(
10007            self_: *mut whiteout_MdxFaceEffect,
10008            value: *const core::ffi::c_char,
10009        );
10010        // CornEmitter
10011        pub fn whiteout_mdx_MdxCornEmitter_new() -> *mut whiteout_MdxCornEmitter;
10012        pub fn whiteout_mdx_MdxCornEmitter_delete(self_: *mut whiteout_MdxCornEmitter);
10013        pub fn whiteout_mdx_MdxCornEmitter_get_node(
10014            self_: *mut whiteout_MdxCornEmitter,
10015        ) -> *mut whiteout_MdxNode;
10016        pub fn whiteout_mdx_MdxCornEmitter_set_node(
10017            self_: *mut whiteout_MdxCornEmitter,
10018            value: *const whiteout_MdxNode,
10019        );
10020        pub fn whiteout_mdx_MdxCornEmitter_get_lifeSpan(self_: *mut whiteout_MdxCornEmitter)
10021            -> f32;
10022        pub fn whiteout_mdx_MdxCornEmitter_set_lifeSpan(
10023            self_: *mut whiteout_MdxCornEmitter,
10024            value: f32,
10025        );
10026        pub fn whiteout_mdx_MdxCornEmitter_get_emissionRate(
10027            self_: *mut whiteout_MdxCornEmitter,
10028        ) -> f32;
10029        pub fn whiteout_mdx_MdxCornEmitter_set_emissionRate(
10030            self_: *mut whiteout_MdxCornEmitter,
10031            value: f32,
10032        );
10033        pub fn whiteout_mdx_MdxCornEmitter_get_speed(self_: *mut whiteout_MdxCornEmitter) -> f32;
10034        pub fn whiteout_mdx_MdxCornEmitter_set_speed(
10035            self_: *mut whiteout_MdxCornEmitter,
10036            value: f32,
10037        );
10038        pub fn whiteout_mdx_MdxCornEmitter_get_color(
10039            self_: *mut whiteout_MdxCornEmitter,
10040        ) -> *mut core::ffi::c_void;
10041        pub fn whiteout_mdx_MdxCornEmitter_set_color(
10042            self_: *mut whiteout_MdxCornEmitter,
10043            value: *const core::ffi::c_void,
10044        );
10045        pub fn whiteout_mdx_MdxCornEmitter_get_alpha(self_: *mut whiteout_MdxCornEmitter) -> f32;
10046        pub fn whiteout_mdx_MdxCornEmitter_set_alpha(
10047            self_: *mut whiteout_MdxCornEmitter,
10048            value: f32,
10049        );
10050        pub fn whiteout_mdx_MdxCornEmitter_get_replaceableId(
10051            self_: *mut whiteout_MdxCornEmitter,
10052        ) -> u32;
10053        pub fn whiteout_mdx_MdxCornEmitter_set_replaceableId(
10054            self_: *mut whiteout_MdxCornEmitter,
10055            value: u32,
10056        );
10057        pub fn whiteout_mdx_MdxCornEmitter_get_path(
10058            self_: *mut whiteout_MdxCornEmitter,
10059        ) -> RawCString;
10060        pub fn whiteout_mdx_MdxCornEmitter_set_path(
10061            self_: *mut whiteout_MdxCornEmitter,
10062            value: *const core::ffi::c_char,
10063        );
10064        pub fn whiteout_mdx_MdxCornEmitter_get_animVisibilityGuide(
10065            self_: *mut whiteout_MdxCornEmitter,
10066        ) -> RawCString;
10067        pub fn whiteout_mdx_MdxCornEmitter_set_animVisibilityGuide(
10068            self_: *mut whiteout_MdxCornEmitter,
10069            value: *const core::ffi::c_char,
10070        );
10071        pub fn whiteout_mdx_MdxCornEmitter_get_lifeSpanTracks(
10072            self_: *mut whiteout_MdxCornEmitter,
10073        ) -> *mut whiteout_MdxTrackF32;
10074        pub fn whiteout_mdx_MdxCornEmitter_set_lifeSpanTracks(
10075            self_: *mut whiteout_MdxCornEmitter,
10076            value: *const whiteout_MdxTrackF32,
10077        );
10078        pub fn whiteout_mdx_MdxCornEmitter_get_emissionRateTracks(
10079            self_: *mut whiteout_MdxCornEmitter,
10080        ) -> *mut whiteout_MdxTrackF32;
10081        pub fn whiteout_mdx_MdxCornEmitter_set_emissionRateTracks(
10082            self_: *mut whiteout_MdxCornEmitter,
10083            value: *const whiteout_MdxTrackF32,
10084        );
10085        pub fn whiteout_mdx_MdxCornEmitter_get_speedTracks(
10086            self_: *mut whiteout_MdxCornEmitter,
10087        ) -> *mut whiteout_MdxTrackF32;
10088        pub fn whiteout_mdx_MdxCornEmitter_set_speedTracks(
10089            self_: *mut whiteout_MdxCornEmitter,
10090            value: *const whiteout_MdxTrackF32,
10091        );
10092        pub fn whiteout_mdx_MdxCornEmitter_get_colorTracks(
10093            self_: *mut whiteout_MdxCornEmitter,
10094        ) -> *mut whiteout_MdxTrackVector3f;
10095        pub fn whiteout_mdx_MdxCornEmitter_set_colorTracks(
10096            self_: *mut whiteout_MdxCornEmitter,
10097            value: *const whiteout_MdxTrackVector3f,
10098        );
10099        pub fn whiteout_mdx_MdxCornEmitter_get_alphaTracks(
10100            self_: *mut whiteout_MdxCornEmitter,
10101        ) -> *mut whiteout_MdxTrackF32;
10102        pub fn whiteout_mdx_MdxCornEmitter_set_alphaTracks(
10103            self_: *mut whiteout_MdxCornEmitter,
10104            value: *const whiteout_MdxTrackF32,
10105        );
10106        pub fn whiteout_mdx_MdxCornEmitter_get_visibilityTracks(
10107            self_: *mut whiteout_MdxCornEmitter,
10108        ) -> *mut whiteout_MdxTrackF32;
10109        pub fn whiteout_mdx_MdxCornEmitter_set_visibilityTracks(
10110            self_: *mut whiteout_MdxCornEmitter,
10111            value: *const whiteout_MdxTrackF32,
10112        );
10113        // Parser
10114        pub fn whiteout_mdx_MdxParser_new() -> *mut whiteout_MdxParser;
10115        pub fn whiteout_mdx_MdxParser_new_upgradeMode(
10116            _0: *mut core::ffi::c_void,
10117        ) -> *mut whiteout_MdxParser;
10118        pub fn whiteout_mdx_MdxParser_delete(self_: *mut whiteout_MdxParser);
10119        pub fn whiteout_mdx_MdxParser_parse(
10120            self_: *mut whiteout_MdxParser,
10121            file_path: *const core::ffi::c_char,
10122        ) -> *mut whiteout_MdxModel;
10123        pub fn whiteout_mdx_MdxParser_parse_buffer_format(
10124            self_: *mut whiteout_MdxParser,
10125            buffer: *const u8,
10126            buffer_size: usize,
10127            format: i32,
10128        ) -> *mut whiteout_MdxModel;
10129        pub fn whiteout_mdx_MdxParser_hasIssues(self_: *mut whiteout_MdxParser) -> i32;
10130        pub fn whiteout_mdx_MdxParser_getIssues_count(self_: *mut whiteout_MdxParser) -> usize;
10131        pub fn whiteout_mdx_MdxParser_getIssues_at(
10132            self_: *mut whiteout_MdxParser,
10133            index: usize,
10134        ) -> RawCString;
10135        // Writer
10136        pub fn whiteout_mdx_MdxWriter_new() -> *mut whiteout_MdxWriter;
10137        pub fn whiteout_mdx_MdxWriter_delete(self_: *mut whiteout_MdxWriter);
10138        pub fn whiteout_mdx_MdxWriter_write(
10139            self_: *mut whiteout_MdxWriter,
10140            file_path: *const core::ffi::c_char,
10141            mdlx: *mut whiteout_MdxModel,
10142            mdl_format: i32,
10143        );
10144        pub fn whiteout_mdx_MdxWriter_write_mdx_format_mdlFormat(
10145            self_: *mut whiteout_MdxWriter,
10146            mdx: *mut whiteout_MdxModel,
10147            format: i32,
10148            mdl_format: i32,
10149        ) -> RawBytes;
10150        // TrackVector3f
10151        pub fn whiteout_mdx_MdxTrackVector3f_new() -> *mut whiteout_MdxTrackVector3f;
10152        pub fn whiteout_mdx_MdxTrackVector3f_delete(self_: *mut whiteout_MdxTrackVector3f);
10153        pub fn whiteout_mdx_MdxTrackVector3f_get_isUsed(
10154            self_: *mut whiteout_MdxTrackVector3f,
10155        ) -> i32;
10156        pub fn whiteout_mdx_MdxTrackVector3f_set_isUsed(
10157            self_: *mut whiteout_MdxTrackVector3f,
10158            value: i32,
10159        );
10160        pub fn whiteout_mdx_MdxTrackVector3f_get_interpolationType(
10161            self_: *mut whiteout_MdxTrackVector3f,
10162        ) -> i32;
10163        pub fn whiteout_mdx_MdxTrackVector3f_set_interpolationType(
10164            self_: *mut whiteout_MdxTrackVector3f,
10165            value: i32,
10166        );
10167        pub fn whiteout_mdx_MdxTrackVector3f_get_globalSequenceId(
10168            self_: *mut whiteout_MdxTrackVector3f,
10169        ) -> u32;
10170        pub fn whiteout_mdx_MdxTrackVector3f_set_globalSequenceId(
10171            self_: *mut whiteout_MdxTrackVector3f,
10172            value: u32,
10173        );
10174        pub fn whiteout_mdx_MdxTrackVector3f_get_keyCount(
10175            self_: *mut whiteout_MdxTrackVector3f,
10176        ) -> usize;
10177        pub fn whiteout_mdx_MdxTrackVector3f_set_keyCount(
10178            self_: *mut whiteout_MdxTrackVector3f,
10179            value: usize,
10180        );
10181        pub fn whiteout_mdx_MdxTrackVector3f_get_timestamps_count(
10182            self_: *mut whiteout_MdxTrackVector3f,
10183        ) -> usize;
10184        pub fn whiteout_mdx_MdxTrackVector3f_resize_timestamps(
10185            self_: *mut whiteout_MdxTrackVector3f,
10186            count: usize,
10187        );
10188        pub fn whiteout_mdx_MdxTrackVector3f_get_timestamps_data(
10189            self_: *mut whiteout_MdxTrackVector3f,
10190        ) -> *const u32;
10191        pub fn whiteout_mdx_MdxTrackVector3f_assign_timestamps(
10192            self_: *mut whiteout_MdxTrackVector3f,
10193            data: *const u32,
10194            count: usize,
10195        );
10196        pub fn whiteout_mdx_MdxTrackVector3f_get_keys_count(
10197            self_: *mut whiteout_MdxTrackVector3f,
10198        ) -> usize;
10199        pub fn whiteout_mdx_MdxTrackVector3f_resize_keys(
10200            self_: *mut whiteout_MdxTrackVector3f,
10201            count: usize,
10202        );
10203        pub fn whiteout_mdx_MdxTrackVector3f_get_keys_data(
10204            self_: *mut whiteout_MdxTrackVector3f,
10205        ) -> *const f32;
10206        pub fn whiteout_mdx_MdxTrackVector3f_assign_keys(
10207            self_: *mut whiteout_MdxTrackVector3f,
10208            data: *const f32,
10209            count: usize,
10210        );
10211        // TrackQuaternion
10212        pub fn whiteout_mdx_MdxTrackQuaternion_new() -> *mut whiteout_MdxTrackQuaternion;
10213        pub fn whiteout_mdx_MdxTrackQuaternion_delete(self_: *mut whiteout_MdxTrackQuaternion);
10214        pub fn whiteout_mdx_MdxTrackQuaternion_get_isUsed(
10215            self_: *mut whiteout_MdxTrackQuaternion,
10216        ) -> i32;
10217        pub fn whiteout_mdx_MdxTrackQuaternion_set_isUsed(
10218            self_: *mut whiteout_MdxTrackQuaternion,
10219            value: i32,
10220        );
10221        pub fn whiteout_mdx_MdxTrackQuaternion_get_interpolationType(
10222            self_: *mut whiteout_MdxTrackQuaternion,
10223        ) -> i32;
10224        pub fn whiteout_mdx_MdxTrackQuaternion_set_interpolationType(
10225            self_: *mut whiteout_MdxTrackQuaternion,
10226            value: i32,
10227        );
10228        pub fn whiteout_mdx_MdxTrackQuaternion_get_globalSequenceId(
10229            self_: *mut whiteout_MdxTrackQuaternion,
10230        ) -> u32;
10231        pub fn whiteout_mdx_MdxTrackQuaternion_set_globalSequenceId(
10232            self_: *mut whiteout_MdxTrackQuaternion,
10233            value: u32,
10234        );
10235        pub fn whiteout_mdx_MdxTrackQuaternion_get_keyCount(
10236            self_: *mut whiteout_MdxTrackQuaternion,
10237        ) -> usize;
10238        pub fn whiteout_mdx_MdxTrackQuaternion_set_keyCount(
10239            self_: *mut whiteout_MdxTrackQuaternion,
10240            value: usize,
10241        );
10242        pub fn whiteout_mdx_MdxTrackQuaternion_get_timestamps_count(
10243            self_: *mut whiteout_MdxTrackQuaternion,
10244        ) -> usize;
10245        pub fn whiteout_mdx_MdxTrackQuaternion_resize_timestamps(
10246            self_: *mut whiteout_MdxTrackQuaternion,
10247            count: usize,
10248        );
10249        pub fn whiteout_mdx_MdxTrackQuaternion_get_timestamps_data(
10250            self_: *mut whiteout_MdxTrackQuaternion,
10251        ) -> *const u32;
10252        pub fn whiteout_mdx_MdxTrackQuaternion_assign_timestamps(
10253            self_: *mut whiteout_MdxTrackQuaternion,
10254            data: *const u32,
10255            count: usize,
10256        );
10257        pub fn whiteout_mdx_MdxTrackQuaternion_get_keys_count(
10258            self_: *mut whiteout_MdxTrackQuaternion,
10259        ) -> usize;
10260        pub fn whiteout_mdx_MdxTrackQuaternion_resize_keys(
10261            self_: *mut whiteout_MdxTrackQuaternion,
10262            count: usize,
10263        );
10264        pub fn whiteout_mdx_MdxTrackQuaternion_get_keys_data(
10265            self_: *mut whiteout_MdxTrackQuaternion,
10266        ) -> *const f32;
10267        pub fn whiteout_mdx_MdxTrackQuaternion_assign_keys(
10268            self_: *mut whiteout_MdxTrackQuaternion,
10269            data: *const f32,
10270            count: usize,
10271        );
10272        // TrackU32
10273        pub fn whiteout_mdx_MdxTrackU32_new() -> *mut whiteout_MdxTrackU32;
10274        pub fn whiteout_mdx_MdxTrackU32_delete(self_: *mut whiteout_MdxTrackU32);
10275        pub fn whiteout_mdx_MdxTrackU32_get_isUsed(self_: *mut whiteout_MdxTrackU32) -> i32;
10276        pub fn whiteout_mdx_MdxTrackU32_set_isUsed(self_: *mut whiteout_MdxTrackU32, value: i32);
10277        pub fn whiteout_mdx_MdxTrackU32_get_interpolationType(
10278            self_: *mut whiteout_MdxTrackU32,
10279        ) -> i32;
10280        pub fn whiteout_mdx_MdxTrackU32_set_interpolationType(
10281            self_: *mut whiteout_MdxTrackU32,
10282            value: i32,
10283        );
10284        pub fn whiteout_mdx_MdxTrackU32_get_globalSequenceId(
10285            self_: *mut whiteout_MdxTrackU32,
10286        ) -> u32;
10287        pub fn whiteout_mdx_MdxTrackU32_set_globalSequenceId(
10288            self_: *mut whiteout_MdxTrackU32,
10289            value: u32,
10290        );
10291        pub fn whiteout_mdx_MdxTrackU32_get_keyCount(self_: *mut whiteout_MdxTrackU32) -> usize;
10292        pub fn whiteout_mdx_MdxTrackU32_set_keyCount(
10293            self_: *mut whiteout_MdxTrackU32,
10294            value: usize,
10295        );
10296        pub fn whiteout_mdx_MdxTrackU32_get_timestamps_count(
10297            self_: *mut whiteout_MdxTrackU32,
10298        ) -> usize;
10299        pub fn whiteout_mdx_MdxTrackU32_resize_timestamps(
10300            self_: *mut whiteout_MdxTrackU32,
10301            count: usize,
10302        );
10303        pub fn whiteout_mdx_MdxTrackU32_get_timestamps_data(
10304            self_: *mut whiteout_MdxTrackU32,
10305        ) -> *const u32;
10306        pub fn whiteout_mdx_MdxTrackU32_assign_timestamps(
10307            self_: *mut whiteout_MdxTrackU32,
10308            data: *const u32,
10309            count: usize,
10310        );
10311        pub fn whiteout_mdx_MdxTrackU32_get_keys_count(self_: *mut whiteout_MdxTrackU32) -> usize;
10312        pub fn whiteout_mdx_MdxTrackU32_resize_keys(self_: *mut whiteout_MdxTrackU32, count: usize);
10313        pub fn whiteout_mdx_MdxTrackU32_get_keys_data(
10314            self_: *mut whiteout_MdxTrackU32,
10315        ) -> *const u32;
10316        pub fn whiteout_mdx_MdxTrackU32_assign_keys(
10317            self_: *mut whiteout_MdxTrackU32,
10318            data: *const u32,
10319            count: usize,
10320        );
10321        // TrackF32
10322        pub fn whiteout_mdx_MdxTrackF32_new() -> *mut whiteout_MdxTrackF32;
10323        pub fn whiteout_mdx_MdxTrackF32_delete(self_: *mut whiteout_MdxTrackF32);
10324        pub fn whiteout_mdx_MdxTrackF32_get_isUsed(self_: *mut whiteout_MdxTrackF32) -> i32;
10325        pub fn whiteout_mdx_MdxTrackF32_set_isUsed(self_: *mut whiteout_MdxTrackF32, value: i32);
10326        pub fn whiteout_mdx_MdxTrackF32_get_interpolationType(
10327            self_: *mut whiteout_MdxTrackF32,
10328        ) -> i32;
10329        pub fn whiteout_mdx_MdxTrackF32_set_interpolationType(
10330            self_: *mut whiteout_MdxTrackF32,
10331            value: i32,
10332        );
10333        pub fn whiteout_mdx_MdxTrackF32_get_globalSequenceId(
10334            self_: *mut whiteout_MdxTrackF32,
10335        ) -> u32;
10336        pub fn whiteout_mdx_MdxTrackF32_set_globalSequenceId(
10337            self_: *mut whiteout_MdxTrackF32,
10338            value: u32,
10339        );
10340        pub fn whiteout_mdx_MdxTrackF32_get_keyCount(self_: *mut whiteout_MdxTrackF32) -> usize;
10341        pub fn whiteout_mdx_MdxTrackF32_set_keyCount(
10342            self_: *mut whiteout_MdxTrackF32,
10343            value: usize,
10344        );
10345        pub fn whiteout_mdx_MdxTrackF32_get_timestamps_count(
10346            self_: *mut whiteout_MdxTrackF32,
10347        ) -> usize;
10348        pub fn whiteout_mdx_MdxTrackF32_resize_timestamps(
10349            self_: *mut whiteout_MdxTrackF32,
10350            count: usize,
10351        );
10352        pub fn whiteout_mdx_MdxTrackF32_get_timestamps_data(
10353            self_: *mut whiteout_MdxTrackF32,
10354        ) -> *const u32;
10355        pub fn whiteout_mdx_MdxTrackF32_assign_timestamps(
10356            self_: *mut whiteout_MdxTrackF32,
10357            data: *const u32,
10358            count: usize,
10359        );
10360        pub fn whiteout_mdx_MdxTrackF32_get_keys_count(self_: *mut whiteout_MdxTrackF32) -> usize;
10361        pub fn whiteout_mdx_MdxTrackF32_resize_keys(self_: *mut whiteout_MdxTrackF32, count: usize);
10362        pub fn whiteout_mdx_MdxTrackF32_get_keys_data(
10363            self_: *mut whiteout_MdxTrackF32,
10364        ) -> *const f32;
10365        pub fn whiteout_mdx_MdxTrackF32_assign_keys(
10366            self_: *mut whiteout_MdxTrackF32,
10367            data: *const f32,
10368            count: usize,
10369        );
10370    }
10371}