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