rust_raylib/
model.rs

1use std::{ffi::CString, mem::ManuallyDrop};
2
3use static_assertions::{assert_eq_align, assert_eq_size};
4
5use crate::{
6    color::Color,
7    ffi,
8    math::{BoundingBox, Matrix, Transform, Vector2, Vector3, Vector4},
9    shader::Shader,
10    texture::{Image, Texture2D},
11};
12
13pub use crate::ffi::MaterialMapIndex;
14
15/// Mesh, vertex data and vao/vbo
16#[derive(Debug)]
17#[repr(transparent)]
18pub struct Mesh {
19    pub(crate) raw: ffi::Mesh,
20}
21
22impl Mesh {
23    /// Vertex positions (XYZ - 3 components per vertex) (shader-location = 0)
24    #[inline]
25    pub fn vertices(&self) -> &[Vector3] {
26        unsafe {
27            std::slice::from_raw_parts(self.raw.vertices as *const _, self.raw.vertexCount as _)
28        }
29    }
30
31    /// Vertex positions (XYZ - 3 components per vertex) (shader-location = 0)
32    #[inline]
33    pub fn vertices_mut(&mut self) -> &mut [Vector3] {
34        unsafe {
35            std::slice::from_raw_parts_mut(self.raw.vertices as *mut _, self.raw.vertexCount as _)
36        }
37    }
38
39    /// Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
40    #[inline]
41    pub fn texcoords(&self) -> &[Vector2] {
42        unsafe {
43            std::slice::from_raw_parts(self.raw.texcoords as *const _, self.raw.vertexCount as _)
44        }
45    }
46
47    /// Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1)
48    #[inline]
49    pub fn texcoords_mut(&mut self) -> &mut [Vector2] {
50        unsafe {
51            std::slice::from_raw_parts_mut(self.raw.texcoords as *mut _, self.raw.vertexCount as _)
52        }
53    }
54
55    /// Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5)
56    #[inline]
57    pub fn texcoords2(&self) -> &[Vector2] {
58        unsafe {
59            std::slice::from_raw_parts(self.raw.texcoords as *const _, self.raw.vertexCount as _)
60        }
61    }
62
63    /// Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5)
64    #[inline]
65    pub fn texcoords2_mut(&mut self) -> &mut [Vector2] {
66        unsafe {
67            std::slice::from_raw_parts_mut(self.raw.texcoords as *mut _, self.raw.vertexCount as _)
68        }
69    }
70
71    /// Vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
72    #[inline]
73    pub fn normals(&self) -> &[Vector3] {
74        unsafe {
75            std::slice::from_raw_parts(self.raw.normals as *const _, self.raw.vertexCount as _)
76        }
77    }
78
79    /// Vertex normals (XYZ - 3 components per vertex) (shader-location = 2)
80    #[inline]
81    pub fn normals_mut(&mut self) -> &mut [Vector3] {
82        unsafe {
83            std::slice::from_raw_parts_mut(self.raw.normals as *mut _, self.raw.vertexCount as _)
84        }
85    }
86
87    /// Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
88    #[inline]
89    pub fn tangents(&self) -> &[Vector4] {
90        unsafe {
91            std::slice::from_raw_parts(self.raw.tangents as *const _, self.raw.vertexCount as _)
92        }
93    }
94
95    /// Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
96    #[inline]
97    pub fn tangents_mut(&mut self) -> &mut [Vector4] {
98        unsafe {
99            std::slice::from_raw_parts_mut(self.raw.tangents as *mut _, self.raw.vertexCount as _)
100        }
101    }
102
103    /// Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
104    #[inline]
105    pub fn colors(&self) -> &[Color] {
106        unsafe {
107            std::slice::from_raw_parts(self.raw.colors as *const _, self.raw.vertexCount as _)
108        }
109    }
110
111    /// Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
112    #[inline]
113    pub fn colors_mut(&mut self) -> &mut [Color] {
114        unsafe {
115            std::slice::from_raw_parts_mut(self.raw.colors as *mut _, self.raw.vertexCount as _)
116        }
117    }
118
119    /// Vertex indices (in case vertex data comes indexed)
120    #[inline]
121    pub fn indices(&self) -> &[u16] {
122        unsafe {
123            std::slice::from_raw_parts(self.raw.indices as *const _, self.raw.triangleCount as _)
124        }
125    }
126
127    /// Vertex indices (in case vertex data comes indexed)
128    #[inline]
129    pub fn indices_mut(&mut self) -> &mut [u16] {
130        unsafe {
131            std::slice::from_raw_parts_mut(self.raw.indices as *mut _, self.raw.triangleCount as _)
132        }
133    }
134
135    /// Upload mesh vertex data in GPU and provide VAO/VBO ids
136    #[inline]
137    pub fn upload(&mut self, dynamic: bool) {
138        unsafe { ffi::UploadMesh(&mut self.raw as *mut _, dynamic) }
139    }
140
141    /// Update mesh vertex data in GPU for a specific buffer index
142    #[inline]
143    pub fn update_buffer(&self, index: u32, data: &[u8], offset: u32) {
144        unsafe {
145            ffi::UpdateMeshBuffer(
146                self.raw.clone(),
147                index as _,
148                data.as_ptr() as *const _,
149                data.len() as _,
150                offset as _,
151            )
152        }
153    }
154
155    /// Export mesh data to file, returns true on success
156    #[inline]
157    pub fn export(&self, file_name: &str) -> bool {
158        let file_name = CString::new(file_name).unwrap();
159
160        unsafe { ffi::ExportMesh(self.raw.clone(), file_name.as_ptr()) }
161    }
162
163    /// Compute mesh bounding box limits
164    #[inline]
165    pub fn get_bounding_box(&self) -> BoundingBox {
166        unsafe { ffi::GetMeshBoundingBox(self.raw.clone()).into() }
167    }
168
169    /// Compute mesh tangents
170    #[inline]
171    pub fn generate_tangents(&mut self) {
172        unsafe { ffi::GenMeshTangents(&mut self.raw as *mut _) }
173    }
174
175    /// Generate polygonal mesh
176    #[inline]
177    pub fn generate_polygon(sides: u32, radius: f32) -> Self {
178        Self {
179            raw: unsafe { ffi::GenMeshPoly(sides as _, radius) },
180        }
181    }
182
183    /// Generate plane mesh (with subdivisions)
184    #[inline]
185    pub fn generate_plane(width: f32, length: f32, res_x: u32, res_z: u32) -> Self {
186        Self {
187            raw: unsafe { ffi::GenMeshPlane(width, length, res_x as _, res_z as _) },
188        }
189    }
190
191    /// Generate cuboid mesh
192    #[inline]
193    pub fn generate_cube(width: f32, height: f32, length: f32) -> Self {
194        Self {
195            raw: unsafe { ffi::GenMeshCube(width, height, length) },
196        }
197    }
198
199    /// Generate sphere mesh (standard sphere)
200    #[inline]
201    pub fn generate_sphere(radius: f32, rings: u32, slices: u32) -> Self {
202        Self {
203            raw: unsafe { ffi::GenMeshSphere(radius, rings as _, slices as _) },
204        }
205    }
206
207    /// Generate half-sphere mesh (no bottom cap)
208    #[inline]
209    pub fn generate_hemisphere(radius: f32, rings: u32, slices: u32) -> Self {
210        Self {
211            raw: unsafe { ffi::GenMeshHemiSphere(radius, rings as _, slices as _) },
212        }
213    }
214
215    /// Generate cylinder mesh
216    #[inline]
217    pub fn generate_cylinder(radius: f32, height: f32, slices: u32) -> Self {
218        Self {
219            raw: unsafe { ffi::GenMeshCylinder(radius, height, slices as _) },
220        }
221    }
222
223    /// Generate cone/pyramid mesh
224    #[inline]
225    pub fn generate_cone(radius: f32, height: f32, slices: u32) -> Self {
226        Self {
227            raw: unsafe { ffi::GenMeshCone(radius, height, slices as _) },
228        }
229    }
230
231    /// Generate torus mesh
232    #[inline]
233    pub fn generate_torus(radius: f32, size: f32, rad_seg: u32, sides: u32) -> Self {
234        Self {
235            raw: unsafe { ffi::GenMeshTorus(radius, size, rad_seg as _, sides as _) },
236        }
237    }
238
239    /// Generate trefoil knot mesh
240    #[inline]
241    pub fn generate_knot(radius: f32, size: f32, rad_seg: u32, sides: u32) -> Self {
242        Self {
243            raw: unsafe { ffi::GenMeshKnot(radius, size, rad_seg as _, sides as _) },
244        }
245    }
246
247    /// Generate heightmap mesh from image data
248    #[inline]
249    pub fn generate_heightmap(heightmap: &Image, size: Vector3) -> Self {
250        Self {
251            raw: unsafe { ffi::GenMeshHeightmap(heightmap.raw.clone(), size.into()) },
252        }
253    }
254
255    /// Generate cubes-based map mesh from image data
256    #[inline]
257    pub fn generate_cubicmap(cubicmap: &Image, cube_size: Vector3) -> Self {
258        Self {
259            raw: unsafe { ffi::GenMeshCubicmap(cubicmap.raw.clone(), cube_size.into()) },
260        }
261    }
262
263    /// Get the 'raw' ffi type
264    /// Take caution when cloning so it doesn't outlive the original
265    #[inline]
266    pub fn as_raw(&self) -> &ffi::Mesh {
267        &self.raw
268    }
269
270    /// Get the 'raw' ffi type
271    /// Take caution when cloning so it doesn't outlive the original
272    #[inline]
273    pub fn as_raw_mut(&mut self) -> &mut ffi::Mesh {
274        &mut self.raw
275    }
276
277    /// Convert a 'raw' ffi object to a safe wrapper
278    ///
279    /// # Safety
280    /// * The raw object must be correctly initialized
281    /// * The raw object should be unique. Otherwise, make sure its clones don't outlive the newly created object.
282    #[inline]
283    pub unsafe fn from_raw(raw: ffi::Mesh) -> Self {
284        Self { raw }
285    }
286}
287
288impl Drop for Mesh {
289    #[inline]
290    fn drop(&mut self) {
291        unsafe { ffi::UnloadMesh(self.raw.clone()) }
292    }
293}
294
295/// Model, meshes, materials and animation data
296#[derive(Debug)]
297#[repr(transparent)]
298pub struct Model {
299    pub(crate) raw: ffi::Model,
300}
301
302impl Model {
303    /// Local transform matrix
304    #[inline]
305    pub fn transform(&self) -> Matrix {
306        self.raw.transform.clone().into()
307    }
308
309    /// Local transform matrix
310    #[inline]
311    pub fn set_transform(&mut self, mat: Matrix) {
312        self.raw.transform = mat.into();
313    }
314
315    /// Meshes array
316    ///
317    /// Note that calling `ManuallyDrop::drop` on the returned values is a *very very bad* idea.
318    #[inline]
319    pub fn meshes(&self) -> &[ManuallyDrop<Mesh>] {
320        unsafe { std::slice::from_raw_parts(self.raw.meshes as *const _, self.raw.meshCount as _) }
321    }
322
323    /// Meshes array
324    ///
325    /// Note that calling `ManuallyDrop::drop` on the returned values is a *very very bad* idea.
326    #[inline]
327    pub fn meshes_mut(&mut self) -> &mut [ManuallyDrop<Mesh>] {
328        unsafe {
329            std::slice::from_raw_parts_mut(self.raw.meshes as *mut _, self.raw.meshCount as _)
330        }
331    }
332
333    /// Materials array
334    ///
335    /// Note that calling `ManuallyDrop::drop` on the returned values is a *very very bad* idea.
336    #[inline]
337    pub fn materials(&self) -> &[ManuallyDrop<Material>] {
338        unsafe {
339            std::slice::from_raw_parts(self.raw.materials as *const _, self.raw.materialCount as _)
340        }
341    }
342
343    /// Materials array
344    ///
345    /// Note that calling `ManuallyDrop::drop` on the returned values is a *very very bad* idea.
346    #[inline]
347    pub fn materials_mut(&mut self) -> &mut [ManuallyDrop<Material>] {
348        unsafe {
349            std::slice::from_raw_parts_mut(
350                self.raw.materials as *mut _,
351                self.raw.materialCount as _,
352            )
353        }
354    }
355
356    /// Bones information (skeleton)
357    #[inline]
358    pub fn bones(&self) -> &[ffi::BoneInfo] {
359        unsafe { std::slice::from_raw_parts(self.raw.bones as *const _, self.raw.boneCount as _) }
360    }
361
362    /// Bones information (skeleton)
363    #[inline]
364    pub fn bones_mut(&mut self) -> &mut [ffi::BoneInfo] {
365        unsafe { std::slice::from_raw_parts_mut(self.raw.bones, self.raw.boneCount as _) }
366    }
367
368    /// Bones base transformation (pose)
369    #[inline]
370    pub fn bind_pose(&self) -> &[Transform] {
371        unsafe {
372            std::slice::from_raw_parts(self.raw.bindPose as *const _, self.raw.boneCount as _)
373        }
374    }
375
376    /// Bones base transformation (pose)
377    #[inline]
378    pub fn bind_pose_mut(&mut self) -> &mut [Transform] {
379        unsafe {
380            std::slice::from_raw_parts_mut(self.raw.bindPose as *mut _, self.raw.boneCount as _)
381        }
382    }
383
384    /// Load model from files (meshes and materials)
385    #[inline]
386    pub fn from_file(file_name: &str) -> Option<Self> {
387        let file_name = CString::new(file_name).unwrap();
388
389        let raw = unsafe { ffi::LoadModel(file_name.as_ptr()) };
390
391        if unsafe { ffi::IsModelReady(raw.clone()) } {
392            Some(Self { raw })
393        } else {
394            None
395        }
396    }
397
398    /// Load model from generated mesh (default material)
399    #[inline]
400    pub fn from_mesh(mesh: Mesh) -> Self {
401        let mesh = ManuallyDrop::new(mesh);
402
403        Self {
404            raw: unsafe { ffi::LoadModelFromMesh(mesh.raw.clone()) },
405        }
406    }
407
408    /// Compute model bounding box limits (considers all meshes)
409    #[inline]
410    pub fn get_bounding_box(&self) -> BoundingBox {
411        unsafe { ffi::GetModelBoundingBox(self.raw.clone()).into() }
412    }
413
414    /// Set material for a mesh
415    #[inline]
416    pub fn set_mesh_material(&mut self, mesh_id: u32, material_id: u32) {
417        unsafe {
418            ffi::SetModelMeshMaterial(&mut self.raw as *mut _, mesh_id as _, material_id as _)
419        }
420    }
421
422    /// Update model animation pose
423    #[inline]
424    pub fn update_animation(&self, anim: &ModelAnimation, frame: u32) {
425        unsafe { ffi::UpdateModelAnimation(self.raw.clone(), anim.raw.clone(), frame as _) }
426    }
427
428    /// Check model animation skeleton match
429    #[inline]
430    pub fn is_animation_valid(&self, anim: &ModelAnimation) -> bool {
431        unsafe { ffi::IsModelAnimationValid(self.raw.clone(), anim.raw.clone()) }
432    }
433
434    /// Get the 'raw' ffi type
435    /// Take caution when cloning so it doesn't outlive the original
436    #[inline]
437    pub fn as_raw(&self) -> &ffi::Model {
438        &self.raw
439    }
440
441    /// Get the 'raw' ffi type
442    /// Take caution when cloning so it doesn't outlive the original
443    #[inline]
444    pub fn as_raw_mut(&mut self) -> &mut ffi::Model {
445        &mut self.raw
446    }
447
448    /// Convert a 'raw' ffi object to a safe wrapper
449    ///
450    /// # Safety
451    /// * The raw object must be correctly initialized
452    /// * The raw object should be unique. Otherwise, make sure its clones don't outlive the newly created object.
453    #[inline]
454    pub unsafe fn from_raw(raw: ffi::Model) -> Self {
455        Self { raw }
456    }
457}
458
459impl Drop for Model {
460    #[inline]
461    fn drop(&mut self) {
462        unsafe { ffi::UnloadModel(self.raw.clone()) }
463    }
464}
465
466/// Material map
467#[repr(C)]
468#[derive(Debug)]
469pub struct MaterialMap {
470    /// Material map texture
471    pub texture: ManuallyDrop<Texture2D>,
472    /// Material map color
473    pub color: Color,
474    /// Material map value
475    pub value: f32,
476}
477
478assert_eq_size!(MaterialMap, ffi::MaterialMap);
479assert_eq_align!(MaterialMap, ffi::MaterialMap);
480
481/// Material, includes shader and maps
482#[derive(Debug)]
483#[repr(transparent)]
484pub struct Material {
485    pub(crate) raw: ffi::Material,
486}
487
488impl Material {
489    /// Material shader
490    #[inline]
491    pub fn shader(&self) -> &ManuallyDrop<Shader> {
492        unsafe { std::mem::transmute(&self.raw.shader) }
493    }
494
495    /// Material shader
496    #[inline]
497    pub fn shader_mut(&mut self) -> &mut ManuallyDrop<Shader> {
498        unsafe { std::mem::transmute(&mut self.raw.shader) }
499    }
500
501    /// Material maps array
502    #[inline]
503    pub fn maps(&self) -> &[MaterialMap] {
504        unsafe { std::slice::from_raw_parts(self.raw.maps as *const _, ffi::MAX_MATERIAL_MAPS) }
505    }
506
507    /// Material maps array
508    #[inline]
509    pub fn maps_mut(&mut self) -> &mut [MaterialMap] {
510        unsafe { std::slice::from_raw_parts_mut(self.raw.maps as *mut _, ffi::MAX_MATERIAL_MAPS) }
511    }
512
513    /// Material generic parameters (if required)
514    #[inline]
515    pub fn params(&self) -> &[f32; 4] {
516        &self.raw.params
517    }
518
519    /// Material generic parameters (if required)
520    #[inline]
521    pub fn params_mut(&mut self) -> &mut [f32; 4] {
522        &mut self.raw.params
523    }
524
525    /// Load materials from model file
526    #[inline]
527    pub fn from_file(file_name: &str) -> Vec<Self> {
528        let file_name = CString::new(file_name).unwrap();
529        let mut count: i32 = 0;
530
531        let mats = unsafe { ffi::LoadMaterials(file_name.as_ptr(), &mut count as *mut _) };
532
533        let mut vec = Vec::new();
534
535        for i in 0..(count as usize) {
536            let mat = unsafe { mats.add(i).read() };
537
538            if unsafe { ffi::IsMaterialReady(mat.clone()) } {
539                vec.push(Self { raw: mat });
540            }
541        }
542
543        vec
544    }
545
546    /// Set texture for a material map type
547    #[inline]
548    pub fn set_texture(&mut self, map_type: MaterialMapIndex, texture: Texture2D) {
549        let texture = ManuallyDrop::new(texture);
550
551        unsafe {
552            ffi::SetMaterialTexture(&mut self.raw as *mut _, map_type as _, texture.raw.clone());
553        }
554    }
555
556    /// Get the 'raw' ffi type
557    /// Take caution when cloning so it doesn't outlive the original
558    #[inline]
559    pub fn as_raw(&self) -> &ffi::Material {
560        &self.raw
561    }
562
563    /// Get the 'raw' ffi type
564    /// Take caution when cloning so it doesn't outlive the original
565    #[inline]
566    pub fn as_raw_mut(&mut self) -> &mut ffi::Material {
567        &mut self.raw
568    }
569
570    /// Convert a 'raw' ffi object to a safe wrapper
571    ///
572    /// # Safety
573    /// * The raw object must be correctly initialized
574    /// * The raw object should be unique. Otherwise, make sure its clones don't outlive the newly created object.
575    #[inline]
576    pub unsafe fn from_raw(raw: ffi::Material) -> Self {
577        Self { raw }
578    }
579}
580
581impl Default for Material {
582    #[inline]
583    fn default() -> Self {
584        Self {
585            raw: unsafe { ffi::LoadMaterialDefault() },
586        }
587    }
588}
589
590impl Drop for Material {
591    #[inline]
592    fn drop(&mut self) {
593        unsafe { ffi::UnloadMaterial(self.raw.clone()) }
594    }
595}
596
597/// Model animation
598#[derive(Debug)]
599#[repr(transparent)]
600pub struct ModelAnimation {
601    raw: ffi::ModelAnimation,
602}
603
604impl ModelAnimation {
605    /// Bones information (skeleton)
606    #[inline]
607    pub fn bones(&self) -> &[ffi::BoneInfo] {
608        unsafe { std::slice::from_raw_parts(self.raw.bones as *const _, self.raw.boneCount as _) }
609    }
610
611    /// Bones information (skeleton)
612    #[inline]
613    pub fn bones_mut(&mut self) -> &mut [ffi::BoneInfo] {
614        unsafe { std::slice::from_raw_parts_mut(self.raw.bones, self.raw.boneCount as _) }
615    }
616
617    /// Poses array by frame
618    #[inline]
619    pub fn frame_poses(&self) -> Vec<&[Transform]> {
620        let mut vec = Vec::new();
621
622        for i in 0..(self.raw.frameCount as usize) {
623            vec.push(unsafe {
624                std::slice::from_raw_parts(
625                    self.raw.framePoses.add(i).read() as *const _,
626                    self.raw.boneCount as _,
627                )
628            })
629        }
630
631        vec
632    }
633
634    /// Poses array by frame
635    #[inline]
636    pub fn frame_poses_mut(&mut self) -> Vec<&mut [Transform]> {
637        let mut vec = Vec::new();
638
639        for i in 0..(self.raw.frameCount as usize) {
640            vec.push(unsafe {
641                std::slice::from_raw_parts_mut(
642                    self.raw.framePoses.add(i).read() as *mut _,
643                    self.raw.boneCount as _,
644                )
645            })
646        }
647
648        vec
649    }
650
651    /// Load model animations from file
652    #[inline]
653    pub fn from_file(file_name: &str) -> Vec<Self> {
654        let file_name = CString::new(file_name).unwrap();
655        let mut count: u32 = 0;
656
657        let anims = unsafe { ffi::LoadModelAnimations(file_name.as_ptr(), &mut count as *mut _) };
658
659        let mut vec = Vec::new();
660
661        for i in 0..(count as usize) {
662            vec.push(ModelAnimation {
663                raw: unsafe { anims.add(i).read() },
664            })
665        }
666
667        unsafe {
668            ffi::UnloadModelAnimations(anims, count);
669        }
670
671        vec
672    }
673
674    /// Get the 'raw' ffi type
675    /// Take caution when cloning so it doesn't outlive the original
676    #[inline]
677    pub fn as_raw(&self) -> &ffi::ModelAnimation {
678        &self.raw
679    }
680
681    /// Get the 'raw' ffi type
682    /// Take caution when cloning so it doesn't outlive the original
683    #[inline]
684    pub fn as_raw_mut(&mut self) -> &mut ffi::ModelAnimation {
685        &mut self.raw
686    }
687
688    /// Convert a 'raw' ffi object to a safe wrapper
689    ///
690    /// # Safety
691    /// * The raw object must be correctly initialized
692    /// * The raw object should be unique. Otherwise, make sure its clones don't outlive the newly created object.
693    #[inline]
694    pub unsafe fn from_raw(raw: ffi::ModelAnimation) -> Self {
695        Self { raw }
696    }
697}
698
699impl Drop for ModelAnimation {
700    #[inline]
701    fn drop(&mut self) {
702        unsafe { ffi::UnloadModelAnimation(self.raw.clone()) }
703    }
704}