rbxm 0.3.0

Reader for Roblox model files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
//! Base data types used primarily by Instance properties.

use crate::model::{FontStyle, FontWeight, Property};
use crate::tree::TreeKey;

use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use core::ops::{Add, Deref, DerefMut, Div, Mul, Sub};

/// A set of named attributes for an instance. Wrapper type for a mapping of
/// string to property,
#[derive(Clone, Default)]
pub struct Attributes {
    pub(crate) backing: BTreeMap<String, Property>,
}

impl Deref for Attributes {
    type Target = BTreeMap<String, Property>;

    fn deref(&self) -> &Self::Target {
        &self.backing
    }
}

impl DerefMut for Attributes {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.backing
    }
}

impl fmt::Debug for Attributes {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self.backing)
    }
}

/// A dimensional component representing a scale and an offset
///
#[doc = doc_link!("datatype/UDim")]
#[derive(Debug, Clone, Default)]
pub struct UDim {
    /// The scale of this component
    pub scale: f32,
    /// The offset of this component
    pub offset: i32,
}

impl UDim {
    /// Create a new `UDim` from components
    #[must_use]
    pub const fn new(scale: f32, offset: i32) -> UDim {
        UDim { scale, offset }
    }
}

/// A type of coordinate representing a scale and an offset in XY space, most often
/// used in GUI objects
///
#[doc = doc_link!("datatype/UDim2")]
#[derive(Debug, Clone, Default)]
pub struct UDim2 {
    /// The x component of this coordinate
    pub x: UDim,
    /// The y component of this coordinate
    pub y: UDim,
}

impl UDim2 {
    /// Create a new `UDim2` from an x, y pair of [`UDims`](UDim)
    #[must_use]
    pub const fn new(x: UDim, y: UDim) -> UDim2 {
        UDim2 { x, y }
    }

    /// Create a new `UDim2` from x and y scale and offset components.
    #[must_use]
    pub const fn new_components(x_scale: f32, x_offset: i32, y_scale: f32, y_offset: i32) -> UDim2 {
        UDim2 {
            x: UDim::new(x_scale, x_offset),
            y: UDim::new(y_scale, y_offset),
        }
    }
}

/// A ray in 3D space, from origin extending along a unit axis
///
#[doc = doc_link!("datatype/Ray")]
#[derive(Debug, Clone, Default)]
pub struct Ray {
    /// The origin point
    pub origin: Vector3,
    /// The unit direction
    pub direction: Vector3,
}

impl Ray {
    /// Create a new ray from components. The direction vector will be converted to a unit vector
    /// if necessary.
    #[must_use]
    pub fn new(origin: Vector3, direction: Vector3) -> Ray {
        Ray {
            origin,
            direction: direction.unit(),
        }
    }
}

/// A set of faces an Instance is applied to
///
#[doc = doc_link!("datatype/Faces")]
#[derive(Debug, Clone, Default)]
pub struct Faces {
    /// Applied to front face
    pub front: bool,
    /// Applied to back face
    pub back: bool,
    /// Applied to top face
    pub top: bool,
    /// Applied to bottom face
    pub bottom: bool,
    /// Applied to left face
    pub left: bool,
    /// Applied to right face
    pub right: bool,
}

impl Faces {
    /// Get a value with no faces selected
    #[must_use]
    pub const fn none() -> Faces {
        Faces {
            front: false,
            back: false,
            top: false,
            bottom: false,
            left: false,
            right: false,
        }
    }

    /// Get a value with the front and back faces selected
    #[must_use]
    pub const fn front_back() -> Faces {
        Faces {
            front: true,
            back: true,
            ..Faces::none()
        }
    }

    /// Get a value with the top and bottom faces selected
    #[must_use]
    pub const fn top_bottom() -> Faces {
        Faces {
            top: true,
            bottom: true,
            ..Faces::none()
        }
    }

    /// Get a value with the left and right faces selected
    #[must_use]
    pub const fn left_right() -> Faces {
        Faces {
            left: true,
            right: true,
            ..Faces::none()
        }
    }

    /// Get a value with all faces selected
    #[must_use]
    pub const fn all() -> Faces {
        Faces {
            front: true,
            back: true,
            top: true,
            bottom: true,
            left: true,
            right: true,
        }
    }
}

/// A set of XYZ axes an Instance is applied to
///
#[doc = doc_link!("datatype/Axes")]
#[derive(Debug, Clone, Default)]
pub struct Axes {
    /// Applied to X axis
    pub x: bool,
    /// Applied to Y axis
    pub y: bool,
    /// Applied to Z axis
    pub z: bool,
}

impl Axes {
    /// Get a value with no axes selected
    #[must_use]
    pub const fn none() -> Axes {
        Axes {
            x: false,
            y: false,
            z: false,
        }
    }

    /// Get a value with all axes selected
    #[must_use]
    pub const fn all() -> Axes {
        Axes {
            x: true,
            y: true,
            z: true,
        }
    }
}

/// A color for an Instance, picked from a static palette. This type is deprecated, and only used
/// in a few places.
///
#[doc = doc_link!("datatype/BrickColor")]
#[derive(Debug, Clone, Default)]
pub struct BrickColor {
    /// The palette index of this BrickColor
    pub index: i32,
}

/// A 2D vector, most often used in GUI
///
#[doc = doc_link!("datatype/Vector2")]
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Vector2 {
    /// X component
    pub x: f32,
    /// Y component
    pub y: f32,
}

impl Vector2 {
    /// Zero-vector, all components zeroed
    pub const ZERO: Vector2 = Vector2::new(0.0, 0.0);
    /// Unit vector in the X direction
    pub const UNIT_X: Vector2 = Vector2::new(1.0, 0.0);
    /// Unit vector in the Y direction
    pub const UNIT_Y: Vector2 = Vector2::new(0.0, 1.0);
    /// One-vector, all components are 1.0.
    pub const ONE: Vector2 = Vector2::new(1.0, 1.0);

    /// Create a new `Vector2` from components
    #[must_use]
    pub const fn new(x: f32, y: f32) -> Vector2 {
        Vector2 { x, y }
    }

    /// Get the length of this vector
    #[must_use]
    #[inline]
    pub fn len(&self) -> f32 {
        num::Float::sqrt(self.len_squared())
    }

    /// Get the length squared of this vector
    #[must_use]
    #[inline]
    pub fn len_squared(&self) -> f32 {
        self.x * self.x + self.y * self.y
    }
}

impl Add for Vector2 {
    type Output = Vector2;

    fn add(self, rhs: Self) -> Self::Output {
        Vector2 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
        }
    }
}

impl Sub for Vector2 {
    type Output = Vector2;

    fn sub(self, rhs: Self) -> Self::Output {
        Vector2 {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
        }
    }
}

impl Mul for Vector2 {
    type Output = Vector2;

    fn mul(self, rhs: Self) -> Self::Output {
        Vector2 {
            x: self.x * rhs.x,
            y: self.y * rhs.y,
        }
    }
}

impl Div for Vector2 {
    type Output = Vector2;

    fn div(self, rhs: Self) -> Self::Output {
        Vector2 {
            x: self.x / rhs.x,
            y: self.y / rhs.y,
        }
    }
}

/// A 3D vector, used for most physical things
///
#[doc = doc_link!("datatype/Vector3")]
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Vector3 {
    /// X component
    pub x: f32,
    /// Y component
    pub y: f32,
    /// Z component
    pub z: f32,
}

impl Vector3 {
    /// Zero-vector, all components zeroed
    pub const ZERO: Vector3 = Vector3::new(0.0, 0.0, 0.0);
    /// Unit vector in the X direction
    pub const UNIT_X: Vector3 = Vector3::new(1.0, 0.0, 0.0);
    /// Unit vector in the Y direction
    pub const UNIT_Y: Vector3 = Vector3::new(0.0, 1.0, 0.0);
    /// Unit vector in the Z direction
    pub const UNIT_Z: Vector3 = Vector3::new(0.0, 0.0, 1.0);
    /// One-vector, all components are 1.0.
    pub const ONE: Vector3 = Vector3::new(1.0, 1.0, 1.0);

    /// Create a new `Vector3` from components
    #[must_use]
    pub const fn new(x: f32, y: f32, z: f32) -> Vector3 {
        Vector3 { x, y, z }
    }

    /// Get the length of this vector
    #[must_use]
    #[inline]
    pub fn len(&self) -> f32 {
        num::Float::sqrt(self.len_squared())
    }

    /// Get the length squared of this vector
    #[must_use]
    #[inline]
    pub fn len_squared(&self) -> f32 {
        self.x * self.x + self.y * self.y + self.z * self.z
    }

    /// Get the vector pointing the same direction as this vector, with a length of one, also known
    /// as a unit vector.
    #[must_use]
    pub fn unit(&self) -> Vector3 {
        let len = self.len();
        Vector3 {
            x: self.x / len,
            y: self.y / len,
            z: self.z / len,
        }
    }
}

impl Add for Vector3 {
    type Output = Vector3;

    fn add(self, rhs: Self) -> Self::Output {
        Vector3 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
        }
    }
}

impl Sub for Vector3 {
    type Output = Vector3;

    fn sub(self, rhs: Self) -> Self::Output {
        Vector3 {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
        }
    }
}

impl Mul for Vector3 {
    type Output = Vector3;

    fn mul(self, rhs: Self) -> Self::Output {
        Vector3 {
            x: self.x * rhs.x,
            y: self.y * rhs.y,
            z: self.z * rhs.z,
        }
    }
}

impl Div for Vector3 {
    type Output = Vector3;

    fn div(self, rhs: Self) -> Self::Output {
        Vector3 {
            x: self.x / rhs.x,
            y: self.y / rhs.y,
            z: self.z / rhs.z,
        }
    }
}

/// A representation of a point in space plus a rotation. This is basically a [`Vector3`] and a
/// rotation matrix.
///
#[doc = doc_link!("datatype/CFrame")]
#[derive(Debug, Clone)]
pub struct CFrame {
    /// The position in space this CFrame represents
    pub position: Vector3,
    /// The rotation angle this CFrame represents
    pub angle: [[f32; 3]; 3],
}

impl CFrame {
    /// Create a new `CFrame` from components
    #[must_use]
    pub const fn new(position: Vector3, angle: [[f32; 3]; 3]) -> CFrame {
        CFrame { position, angle }
    }
}

impl Default for CFrame {
    fn default() -> Self {
        CFrame {
            position: Vector3::default(),
            angle: [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]],
        }
    }
}

/// A reference to another instance in the model, which may be null
#[derive(Debug, Clone)]
pub enum InstanceRef {
    /// A null instance reference
    Null,
    /// A reference to another instance in the model
    Item(TreeKey),
}

/// A 3D vector, with an underlying unsigned integer datatype.
///
#[doc = doc_link!("datatype/Vector3int16")]
#[derive(Debug, Clone, Default)]
pub struct Vector3Int16 {
    /// X component
    pub x: i16,
    /// Y component
    pub y: i16,
    /// Z component
    pub z: i16,
}

/// A keypoint in a [`NumberSequence`], a value at a time, and the amount of variance that might
/// occur at that time.
///
#[doc = doc_link!("datatype/NumberSequenceKeypoint")]
#[derive(Debug, Clone, Default)]
pub struct NumberKeypoint {
    /// The time of this keypoint
    pub time: f32,
    /// The value at the given time
    pub value: f32,
    /// The possible variance of this value
    pub envelope: f32,
}

/// A sequence of values, often used for particles or over-time effects
///
#[doc = doc_link!("datatype/NumberSequence")]
#[derive(Debug, Clone, Default)]
pub struct NumberSequence {
    /// The keypoints contained in this sequence
    pub keypoints: Vec<NumberKeypoint>,
}

/// A keypoint in a [`ColorSequence`], a color value at a time, and the amount of variance that
/// might occur at that time.
///
#[doc = doc_link!("datatype/ColorSequenceKeypoint")]
#[derive(Debug, Clone, Default)]
pub struct ColorKeypoint {
    /// The time of this keypoint
    pub time: f32,
    /// The color value at the given time
    pub color: Color3,
    /// The possible variance of this value
    pub envelope: f32,
}

/// A sequence of color values, often used for particles or over-time effects
///
#[doc = doc_link!("datatype/ColorSequence")]
#[derive(Debug, Clone, Default)]
pub struct ColorSequence {
    /// The keypoints contained in this sequence
    pub keypoints: Vec<ColorKeypoint>,
}

/// A range of possible values
///
#[doc = doc_link!("datatype/NumberRange")]
#[derive(Debug, Clone, Default)]
pub struct NumberRange {
    /// Low point of the range
    pub low: f32,
    /// High point of the range
    pub high: f32,
}

/// A rectangle in a 2D plane
///
#[doc = doc_link!("datatype/Rect")]
#[derive(Debug, Clone, Default)]
pub struct Rect {
    /// The top-left corner
    pub top_left: Vector2,
    /// The bottom-right corner
    pub bottom_right: Vector2,
}

/// A color with floating point RGB components, in the range of \[0-1\].
///
#[doc = doc_link!("datatype/Color3")]
#[derive(Debug, Clone, Default)]
pub struct Color3 {
    /// Red component
    pub r: f32,
    /// Green component
    pub g: f32,
    /// Blue component
    pub b: f32,
}

impl Color3 {
    /// Create a new `Color3` from components
    ///
    /// # Panics
    ///
    /// In debug mode, if any component isn't within the range `[0.0, 1.0]`.
    #[must_use]
    pub fn new(r: f32, g: f32, b: f32) -> Color3 {
        debug_assert!((0.0..=1.0).contains(&r), "r value not in (0.0..=1.0)");
        debug_assert!((0.0..=1.0).contains(&g), "g value not in (0.0..=1.0)");
        debug_assert!((0.0..=1.0).contains(&b), "b value not in (0.0..=1.0)");
        Color3 { r, g, b }
    }
}

impl From<Color3Uint8> for Color3 {
    fn from(col: Color3Uint8) -> Self {
        Color3 {
            r: col.r as f32 / 255.,
            g: col.g as f32 / 255.,
            b: col.b as f32 / 255.,
        }
    }
}

/// A color with u8 RGB components, spanning the whole byte range. This isn't actually exposed
/// to the lua engine, instead shown as a Color3
#[derive(Debug, Clone, Default)]
pub struct Color3Uint8 {
    /// Red component
    pub r: u8,
    /// Green component
    pub g: u8,
    /// Blue component
    pub b: u8,
}

impl Color3Uint8 {
    /// Create a new `Color3Uint8` from an (r, g, b) set
    #[must_use]
    pub const fn new(r: u8, g: u8, b: u8) -> Color3Uint8 {
        Color3Uint8 { r, g, b }
    }
}

impl From<Color3> for Color3Uint8 {
    fn from(col: Color3) -> Self {
        Color3Uint8 {
            r: (col.r * 255.) as u8,
            g: (col.g * 255.) as u8,
            b: (col.b * 255.) as u8,
        }
    }
}

impl From<(u8, u8, u8)> for Color3Uint8 {
    fn from((r, g, b): (u8, u8, u8)) -> Self {
        Color3Uint8 { r, g, b }
    }
}

/// A pivot-point representing the 'center of mass' of a model, which it rotates around
#[derive(Debug, Clone, Default)]
pub struct Pivot {
    /// The position and orientation of the pivot in space
    pub cframe: CFrame,
    /// Unknown extra data
    // FIXME(CraftSpider)
    pub unknown: u8,
}

/// A set of physical properties, possibly user defined or not
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum PhysicalProperties {
    /// No custom physical properties, use default from part material
    Default,
    /// Custom physical properties
    Custom {
        /// The density of this part
        density: f32,
        /// The elasticity of collisions
        elasticity: f32,
        /// How much this parts elasticity affects the collision
        elasticity_weight: f32,
        /// The friction of collisions
        friction: f32,
        /// How much this parts friction affects the collision
        friction_weight: f32,
    },
}

impl Default for PhysicalProperties {
    fn default() -> Self {
        PhysicalProperties::Default
    }
}

/// A font property
#[derive(Debug, Clone)]
pub struct FontFace {
    /// The font family, such as Calibri or Times
    pub family: String,
    /// The font weight - from thin to heavy
    pub weight: FontWeight,
    /// The font style, regular or italic
    pub style: FontStyle,
    /// The cached font-face ID
    pub cached_face_id: String,
}

/// A full triangle mesh, used for collision or display
#[cfg(feature = "mesh-format")]
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum TriMesh {
    /// Default tri-mesh data
    Default,
    /// A box mesh, no special collision
    Box,
    /// A convex-hull based mesh
    Hull {
        /// Total volume of the mesh
        volume: f32,
        /// The center of gravity for the whole mesh
        center_of_gravity: Vector3,
        /// The inertia tensor of the whole mesh
        inertia_tensor: [[f32; 3]; 3],
        /// The set of convex hulls that make up this mesh
        meshes: Vec<ConvexHull>,
    },
}

#[cfg(feature = "mesh-format")]
impl Default for TriMesh {
    fn default() -> Self {
        TriMesh::Default
    }
}

/// A single convex hull, with relevant data
#[cfg(feature = "mesh-format")]
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ConvexHull {
    /// Unknown, possibly triangle indices
    // FIXME(CraftSpider)
    pub unknown_1: Vec<u8>,
    /// Unknown, possibly transform offsets
    // FIXME(CraftSpider)
    pub unknown_2: Vec<u8>,
    /// Triangle vertices
    pub vertices: Vec<Vector3>,
    /// Face indices into the vertex list
    pub faces: Vec<(usize, usize, usize)>,
}

#[cfg(feature = "mesh-format")]
impl ConvexHull {
    /// Create a new ConvexHull from a set of vertices and faces
    pub fn new(vertices: Vec<Vector3>, faces: Vec<(usize, usize, usize)>) -> ConvexHull {
        ConvexHull {
            unknown_1: Vec::new(),
            unknown_2: Vec::new(),
            vertices,
            faces,
        }
    }
}