use crate::core::databuf::DataBuf;
use crate::core::math::BoundingBox;
use crate::core::math::Matrix;
use crate::core::math::Transform;
use crate::core::math::{Vector2, Vector3, Vector4};
use crate::core::texture::Image;
use crate::core::{RaylibHandle, RaylibThread};
use crate::ffi::Color;
use crate::{
consts,
error::{
AllocationError, GenMeshError, InvalidMeshError, LoadMaterialError, LoadModelAnimError,
LoadModelError, SetMaterialError,
},
ffi,
};
use std::ffi::CString;
use std::os::raw::c_void;
fn no_drop<T>(_thing: T) {}
make_thin_wrapper!(
Model,
ffi::Model,
ffi::UnloadModel
);
make_thin_wrapper!(WeakModel, ffi::Model, no_drop);
make_thin_wrapper!(
Mesh,
ffi::Mesh,
|mesh: ffi::Mesh| ffi::UnloadMesh(mesh)
);
make_thin_wrapper!(WeakMesh, ffi::Mesh, no_drop);
make_thin_wrapper!(
Material,
ffi::Material,
ffi::UnloadMaterial
);
make_thin_wrapper!(WeakMaterial, ffi::Material, no_drop);
make_thin_wrapper!(
BoneInfo,
ffi::BoneInfo,
no_drop
);
make_thin_wrapper!(
ModelAnimation,
ffi::ModelAnimation,
no_drop
);
make_thin_wrapper!(WeakModelAnimation, ffi::ModelAnimation, no_drop);
make_thin_wrapper!(
MaterialMap,
ffi::MaterialMap,
no_drop
);
impl Clone for WeakModel {
fn clone(&self) -> WeakModel {
WeakModel(self.0)
}
}
impl Clone for WeakMesh {
fn clone(&self) -> WeakMesh {
WeakMesh(self.0)
}
}
impl Clone for WeakMaterial {
fn clone(&self) -> WeakMaterial {
WeakMaterial(self.0)
}
}
impl Clone for WeakModelAnimation {
fn clone(&self) -> WeakModelAnimation {
WeakModelAnimation(self.0)
}
}
#[derive(Debug)]
pub struct ModelAnimations {
ptr: *mut ffi::ModelAnimation,
count: usize,
}
impl ModelAnimations {
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.count
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.count == 0
}
#[inline]
#[must_use]
pub fn as_slice(&self) -> &[ModelAnimation] {
unsafe { std::slice::from_raw_parts(self.ptr as *const ModelAnimation, self.count) }
}
#[inline]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [ModelAnimation] {
unsafe { std::slice::from_raw_parts_mut(self.ptr as *mut ModelAnimation, self.count) }
}
}
impl std::ops::Deref for ModelAnimations {
type Target = [ModelAnimation];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl std::ops::DerefMut for ModelAnimations {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
impl Drop for ModelAnimations {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { ffi::UnloadModelAnimations(self.ptr, self.count as i32) }
}
}
}
impl RaylibHandle {
pub fn load_model(
&mut self,
_: &RaylibThread,
filename: &str,
) -> Result<Model, LoadModelError> {
let c_filename = CString::new(filename).unwrap();
let m = unsafe { ffi::LoadModel(c_filename.as_ptr()) };
if m.meshes.is_null()
&& m.materials.is_null()
&& m.skeleton.bones.is_null()
&& m.skeleton.bindPose.is_null()
{
return Err(LoadModelError::LoadFromFileFailed {
path: filename.into(),
});
}
Ok(Model(m))
}
pub fn load_model_from_mesh(
&mut self,
_: &RaylibThread,
mesh: WeakMesh,
) -> Result<Model, LoadModelError> {
let m = unsafe { ffi::LoadModelFromMesh(mesh.0) };
if m.meshes.is_null() || m.materials.is_null() {
return Err(LoadModelError::LoadFromMeshFailed);
}
Ok(Model(m))
}
pub fn load_model_animations(
&mut self,
_: &RaylibThread,
filename: &str,
) -> Result<ModelAnimations, LoadModelAnimError> {
let c_filename = CString::new(filename).unwrap();
let mut m_size = 0;
let m_ptr = unsafe { ffi::LoadModelAnimations(c_filename.as_ptr(), &mut m_size) };
if m_ptr.is_null() || m_size <= 0 {
return Err(LoadModelAnimError::NoAnimationsLoaded {
path: filename.into(),
});
}
Ok(ModelAnimations {
ptr: m_ptr,
count: m_size as usize,
})
}
#[inline]
pub fn update_model_animation(
&mut self,
_: &RaylibThread,
mut model: impl AsMut<ffi::Model>,
anim: impl AsRef<ffi::ModelAnimation>,
frame: f32,
) {
unsafe {
ffi::UpdateModelAnimation(*model.as_mut(), *anim.as_ref(), frame);
}
}
#[inline]
#[allow(clippy::too_many_arguments)] pub fn update_model_animation_ex(
&mut self,
_: &RaylibThread,
mut model: impl AsMut<ffi::Model>,
anim_a: impl AsRef<ffi::ModelAnimation>,
frame_a: f32,
anim_b: impl AsRef<ffi::ModelAnimation>,
frame_b: f32,
blend: f32,
) {
unsafe {
ffi::UpdateModelAnimationEx(
*model.as_mut(),
*anim_a.as_ref(),
frame_a,
*anim_b.as_ref(),
frame_b,
blend,
);
}
}
}
impl RaylibModel for WeakModel {}
impl RaylibModel for Model {}
impl Model {
pub unsafe fn make_weak(self) -> WeakModel {
let m = WeakModel(self.0);
std::mem::forget(self);
m
}
}
pub trait RaylibModel: AsRef<ffi::Model> + AsMut<ffi::Model> {
#[inline]
#[must_use]
fn transform(&self) -> &Matrix {
unsafe { std::mem::transmute(&self.as_ref().transform) }
}
#[inline]
fn set_transform(&mut self, mat: &Matrix) {
self.as_mut().transform = *mat;
}
#[inline]
#[must_use]
fn meshes(&self) -> &[WeakMesh] {
unsafe {
std::slice::from_raw_parts(
self.as_ref().meshes as *const WeakMesh,
self.as_ref().meshCount as usize,
)
}
}
#[inline]
#[must_use]
fn meshes_mut(&mut self) -> &mut [WeakMesh] {
unsafe {
std::slice::from_raw_parts_mut(
self.as_mut().meshes as *mut WeakMesh,
self.as_mut().meshCount as usize,
)
}
}
#[inline]
#[must_use]
fn materials(&self) -> &[WeakMaterial] {
unsafe {
std::slice::from_raw_parts(
self.as_ref().materials as *const WeakMaterial,
self.as_ref().materialCount as usize,
)
}
}
#[inline]
#[must_use]
fn materials_mut(&mut self) -> &mut [WeakMaterial] {
unsafe {
std::slice::from_raw_parts_mut(
self.as_mut().materials as *mut WeakMaterial,
self.as_mut().materialCount as usize,
)
}
}
#[inline]
#[must_use]
fn bones(&self) -> Option<&[BoneInfo]> {
if self.as_ref().skeleton.bones.is_null() {
return None;
}
Some(unsafe {
std::slice::from_raw_parts(
self.as_ref().skeleton.bones as *const BoneInfo,
self.as_ref().skeleton.boneCount as usize,
)
})
}
#[inline]
#[must_use]
fn bones_mut(&mut self) -> Option<&mut [BoneInfo]> {
if self.as_ref().skeleton.bones.is_null() {
return None;
}
Some(unsafe {
std::slice::from_raw_parts_mut(
self.as_mut().skeleton.bones as *mut BoneInfo,
self.as_mut().skeleton.boneCount as usize,
)
})
}
#[inline]
#[must_use]
fn bind_pose(&self) -> Option<&Transform> {
if self.as_ref().skeleton.bindPose.is_null() {
return None;
}
Some(unsafe { &*(self.as_ref().skeleton.bindPose as *const Transform) })
}
#[inline]
#[must_use]
fn bind_pose_mut(&mut self) -> Option<&mut Transform> {
if self.as_ref().skeleton.bindPose.is_null() {
return None;
}
Some(unsafe { &mut *(self.as_mut().skeleton.bindPose as *mut Transform) })
}
#[inline]
#[must_use]
fn is_model_animation_valid(&self, anim: &ModelAnimation) -> bool {
unsafe { ffi::IsModelAnimationValid(*self.as_ref(), anim.0) }
}
#[inline]
#[must_use]
fn is_model_valid(&self) -> bool {
unsafe { ffi::IsModelValid(*self.as_ref()) }
}
#[inline]
#[must_use]
fn get_model_bounding_box(&self) -> BoundingBox {
unsafe { BoundingBox::from(ffi::GetModelBoundingBox(*self.as_ref())) }
}
#[inline]
fn set_model_mesh_material(
&mut self,
mesh_id: i32,
material_id: i32,
) -> Result<(), SetMaterialError> {
if mesh_id >= self.as_ref().meshCount {
Err(SetMaterialError::MeshIdOutOfBounds)
} else if material_id >= self.as_ref().materialCount {
Err(SetMaterialError::MaterialIdOutOfBounds)
} else {
unsafe { ffi::SetModelMeshMaterial(self.as_mut(), mesh_id, material_id) };
Ok(())
}
}
}
impl RaylibMesh for WeakMesh {}
impl RaylibMesh for Mesh {}
impl Mesh {
pub unsafe fn make_weak(self) -> WeakMesh {
let m = WeakMesh(self.0);
std::mem::forget(self);
m
}
}
pub trait RaylibMesh: AsRef<ffi::Mesh> + AsMut<ffi::Mesh> {
#[inline]
unsafe fn upload(&mut self, dynamic: bool) {
unsafe { ffi::UploadMesh(self.as_mut(), dynamic) };
}
#[inline]
unsafe fn update_buffer<A>(&mut self, index: i32, data: &[u8], offset: i32) {
unsafe {
ffi::UpdateMeshBuffer(
*self.as_ref(),
index,
data.as_ptr() as *const c_void,
data.len() as i32,
offset,
)
};
}
#[inline]
#[must_use]
fn vertices(&self) -> &[Vector3] {
let m = self.as_ref();
if m.vertices.is_null() || m.vertexCount == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(m.vertices as *const Vector3, m.vertexCount as usize) }
}
#[inline]
#[must_use]
fn vertices_mut(&mut self) -> &mut [Vector3] {
let m = self.as_mut();
if m.vertices.is_null() || m.vertexCount == 0 {
return &mut [];
}
unsafe {
std::slice::from_raw_parts_mut(m.vertices as *mut Vector3, m.vertexCount as usize)
}
}
#[inline]
#[must_use]
fn normals(&self) -> &[Vector3] {
let m = self.as_ref();
if m.normals.is_null() || m.vertexCount == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(m.normals as *const Vector3, m.vertexCount as usize) }
}
#[inline]
#[must_use]
fn normals_mut(&mut self) -> &mut [Vector3] {
let m = self.as_mut();
if m.normals.is_null() || m.vertexCount == 0 {
return &mut [];
}
unsafe { std::slice::from_raw_parts_mut(m.normals as *mut Vector3, m.vertexCount as usize) }
}
#[inline]
#[must_use]
fn texcoords(&self) -> &[Vector2] {
let m = self.as_ref();
if m.texcoords.is_null() || m.vertexCount == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(m.texcoords as *const Vector2, m.vertexCount as usize) }
}
#[inline]
#[must_use]
fn texcoords_mut(&mut self) -> &mut [Vector2] {
let m = self.as_mut();
if m.texcoords.is_null() || m.vertexCount == 0 {
return &mut [];
}
unsafe {
std::slice::from_raw_parts_mut(m.texcoords as *mut Vector2, m.vertexCount as usize)
}
}
#[inline]
#[must_use]
fn texcoords2(&self) -> &[Vector2] {
let m = self.as_ref();
if m.texcoords2.is_null() || m.vertexCount == 0 {
return &[];
}
unsafe {
std::slice::from_raw_parts(m.texcoords2 as *const Vector2, m.vertexCount as usize)
}
}
#[inline]
#[must_use]
fn texcoords2_mut(&mut self) -> &mut [Vector2] {
let m = self.as_mut();
if m.texcoords2.is_null() || m.vertexCount == 0 {
return &mut [];
}
unsafe {
std::slice::from_raw_parts_mut(m.texcoords2 as *mut Vector2, m.vertexCount as usize)
}
}
#[inline]
#[must_use]
fn tangents(&self) -> &[Vector3] {
let m = self.as_ref();
if m.tangents.is_null() || m.vertexCount == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(m.tangents as *const Vector3, m.vertexCount as usize) }
}
#[inline]
#[must_use]
fn tangents_mut(&mut self) -> &mut [Vector3] {
let m = self.as_mut();
if m.tangents.is_null() || m.vertexCount == 0 {
return &mut [];
}
unsafe {
std::slice::from_raw_parts_mut(m.tangents as *mut Vector3, m.vertexCount as usize)
}
}
#[inline]
#[must_use]
fn colors(&self) -> &[Color] {
let m = self.as_ref();
if m.colors.is_null() || m.vertexCount == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(m.colors as *const Color, m.vertexCount as usize) }
}
#[inline]
#[must_use]
fn colors_mut(&mut self) -> &mut [Color] {
let m = self.as_mut();
if m.colors.is_null() || m.vertexCount == 0 {
return &mut [];
}
unsafe { std::slice::from_raw_parts_mut(m.colors as *mut Color, m.vertexCount as usize) }
}
#[inline]
#[must_use]
fn indices(&self) -> &[u16] {
let m = self.as_ref();
if m.indices.is_null() || m.triangleCount == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(m.indices as *const u16, m.triangleCount as usize * 3) }
}
#[inline]
#[must_use]
fn indices_mut(&mut self) -> &mut [u16] {
let m = self.as_mut();
if m.indices.is_null() || m.triangleCount == 0 {
return &mut [];
}
unsafe { std::slice::from_raw_parts_mut(m.indices, m.triangleCount as usize * 3) }
}
#[inline]
#[must_use]
fn gen_mesh_poly(_: &RaylibThread, sides: i32, radius: f32) -> Mesh {
unsafe { Mesh(ffi::GenMeshPoly(sides, radius)) }
}
#[inline]
#[must_use]
fn gen_mesh_plane(_: &RaylibThread, width: f32, length: f32, res_x: i32, res_z: i32) -> Mesh {
unsafe { Mesh(ffi::GenMeshPlane(width, length, res_x, res_z)) }
}
#[inline]
#[must_use]
fn gen_mesh_cube(_: &RaylibThread, width: f32, height: f32, length: f32) -> Mesh {
unsafe { Mesh(ffi::GenMeshCube(width, height, length)) }
}
#[inline]
#[must_use]
fn gen_mesh_sphere(_: &RaylibThread, radius: f32, rings: i32, slices: i32) -> Mesh {
unsafe { Mesh(ffi::GenMeshSphere(radius, rings, slices)) }
}
#[inline]
#[must_use]
fn gen_mesh_hemisphere(_: &RaylibThread, radius: f32, rings: i32, slices: i32) -> Mesh {
unsafe { Mesh(ffi::GenMeshHemiSphere(radius, rings, slices)) }
}
#[inline]
#[must_use]
fn gen_mesh_cylinder(_: &RaylibThread, radius: f32, height: f32, slices: i32) -> Mesh {
unsafe { Mesh(ffi::GenMeshCylinder(radius, height, slices)) }
}
#[inline]
#[must_use]
fn gen_mesh_torus(_: &RaylibThread, radius: f32, size: f32, rad_seg: i32, sides: i32) -> Mesh {
unsafe { Mesh(ffi::GenMeshTorus(radius, size, rad_seg, sides)) }
}
#[inline]
#[must_use]
fn gen_mesh_knot(_: &RaylibThread, radius: f32, size: f32, rad_seg: i32, sides: i32) -> Mesh {
unsafe { Mesh(ffi::GenMeshKnot(radius, size, rad_seg, sides)) }
}
#[inline]
#[must_use]
fn gen_mesh_heightmap(_: &RaylibThread, heightmap: &Image, size: impl Into<Vector3>) -> Mesh {
unsafe { Mesh(ffi::GenMeshHeightmap(heightmap.0, size.into())) }
}
#[inline]
#[must_use]
fn gen_mesh_cubicmap(
_: &RaylibThread,
cubicmap: &Image,
cube_size: impl Into<Vector3>,
) -> Mesh {
unsafe { Mesh(ffi::GenMeshCubicmap(cubicmap.0, cube_size.into())) }
}
#[inline]
#[must_use]
fn gen_mesh_cone(_: &RaylibThread, radius: f32, height: f32, slices: i32) -> Mesh {
unsafe { Mesh(ffi::GenMeshCone(radius, height, slices)) }
}
#[inline]
#[must_use]
fn get_mesh_bounding_box(&self) -> BoundingBox {
unsafe { ffi::GetMeshBoundingBox(*self.as_ref()).into() }
}
#[inline]
fn gen_mesh_tangents(&mut self, _: &RaylibThread) {
unsafe {
ffi::GenMeshTangents(self.as_mut());
}
}
#[inline]
fn export(&self, filename: &str) {
let c_filename = CString::new(filename).unwrap();
unsafe {
ffi::ExportMesh(*self.as_ref(), c_filename.as_ptr());
}
}
#[inline]
fn export_as_code(&self, filename: &str) {
let c_filename = CString::new(filename).unwrap();
unsafe {
ffi::ExportMeshAsCode(*self.as_ref(), c_filename.as_ptr());
}
}
}
impl Material {
#[inline]
pub unsafe fn make_weak(self) -> WeakMaterial {
let m = WeakMaterial(self.0);
std::mem::forget(self);
m
}
pub fn load_materials(filename: &str) -> Result<Vec<Material>, LoadMaterialError> {
let c_filename = CString::new(filename).unwrap();
let mut m_size = 0;
let m_ptr = unsafe { ffi::LoadMaterials(c_filename.as_ptr(), &mut m_size) };
if m_size <= 0 {
return Err(LoadMaterialError::NoneLoaded {
path: filename.into(),
});
}
let mut m_vec = Vec::with_capacity(m_size as usize);
for i in 0..m_size {
unsafe {
m_vec.push(Material(*m_ptr.offset(i as isize)));
}
}
unsafe {
ffi::MemFree(m_ptr as *mut ::std::os::raw::c_void);
}
Ok(m_vec)
}
}
impl RaylibMaterial for WeakMaterial {}
impl RaylibMaterial for Material {}
pub trait RaylibMaterial: AsRef<ffi::Material> + AsMut<ffi::Material> {
#[must_use]
#[inline]
fn shader(&self) -> &crate::shaders::WeakShader {
unsafe { std::mem::transmute(&self.as_ref().shader) }
}
#[must_use]
#[inline]
fn shader_mut(&mut self) -> &mut crate::shaders::WeakShader {
unsafe { std::mem::transmute(&mut self.as_mut().shader) }
}
#[must_use]
#[inline]
fn maps(&self) -> &[MaterialMap] {
unsafe {
std::slice::from_raw_parts(
self.as_ref().maps as *const MaterialMap,
consts::MAX_MATERIAL_MAPS as usize,
)
}
}
#[must_use]
#[inline]
fn maps_mut(&mut self) -> &mut [MaterialMap] {
unsafe {
std::slice::from_raw_parts_mut(
self.as_mut().maps as *mut MaterialMap,
consts::MAX_MATERIAL_MAPS as usize,
)
}
}
#[inline]
fn set_material_texture(
&mut self,
map_type: crate::consts::MaterialMapIndex,
texture: impl AsRef<ffi::Texture2D>,
) {
unsafe {
ffi::SetMaterialTexture(self.as_mut(), (map_type as u32) as i32, *texture.as_ref())
}
}
#[inline]
#[must_use]
fn is_material_valid(&mut self) -> bool {
unsafe { ffi::IsMaterialValid(*self.as_ref()) }
}
}
#[derive(Debug, Clone)]
pub struct FramePoseIter<'a> {
iter: std::slice::Iter<'a, Option<&'a [Transform]>>,
bone_count: usize,
}
impl<'a> FramePoseIter<'a> {
#[must_use]
unsafe fn new(
frame_poses: *mut *mut ffi::Transform,
frame_count: usize,
bone_count: usize,
) -> Self {
assert!(!frame_poses.is_null(), "frame pose array cannot be null");
assert!(frame_poses.is_aligned(), "frame pose array must be aligned");
let frame_poses = frame_poses.cast::<Option<&'a [Transform]>>();
let iter = unsafe { std::slice::from_raw_parts(frame_poses, frame_count) }.iter();
Self { iter, bone_count }
}
fn func(tf: &Option<&'a [Transform]>, bone_count: usize) -> &'a [Transform] {
unsafe {
std::slice::from_raw_parts(
tf.expect("frame pose transform cannot be null").as_ptr(),
bone_count,
)
}
}
}
impl<'a> Iterator for FramePoseIter<'a> {
type Item = &'a [Transform];
fn next(&mut self) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter.next().map(move |tf| Self::func(tf, bone_count))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
#[inline]
fn count(self) -> usize {
self.len()
}
fn last(self) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter.last().map(move |tf| Self::func(tf, bone_count))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter.nth(n).map(move |tf| Self::func(tf, bone_count))
}
}
impl DoubleEndedIterator for FramePoseIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter
.next_back()
.map(move |tf| Self::func(tf, bone_count))
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter
.nth_back(n)
.map(move |tf| Self::func(tf, bone_count))
}
}
impl ExactSizeIterator for FramePoseIter<'_> {
#[inline]
fn len(&self) -> usize {
self.iter.len()
}
}
#[derive(Debug)]
pub struct FramePoseIterMut<'a> {
iter: std::slice::IterMut<'a, Option<&'a mut [Transform]>>,
bone_count: usize,
}
impl<'a> FramePoseIterMut<'a> {
unsafe fn new(
frame_poses: *mut *mut ffi::Transform,
frame_count: usize,
bone_count: usize,
) -> Self {
assert!(!frame_poses.is_null(), "frame pose array cannot be null");
assert!(frame_poses.is_aligned(), "frame pose array must be aligned");
let frame_poses = frame_poses.cast::<Option<&'a mut [Transform]>>();
let iter = unsafe { std::slice::from_raw_parts_mut(frame_poses, frame_count) }.iter_mut();
Self { iter, bone_count }
}
fn func(tf: &mut Option<&'a mut [Transform]>, bone_count: usize) -> &'a mut [Transform] {
unsafe {
std::slice::from_raw_parts_mut(
tf.as_mut()
.expect("frame pose transform cannot be null")
.as_mut_ptr(),
bone_count,
)
}
}
}
impl<'a> Iterator for FramePoseIterMut<'a> {
type Item = &'a mut [Transform];
fn next(&mut self) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter.next().map(move |tf| Self::func(tf, bone_count))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
#[inline]
fn count(self) -> usize {
self.len()
}
fn last(self) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter.last().map(move |tf| Self::func(tf, bone_count))
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter.nth(n).map(move |tf| Self::func(tf, bone_count))
}
}
impl DoubleEndedIterator for FramePoseIterMut<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter
.next_back()
.map(move |tf| Self::func(tf, bone_count))
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
let bone_count = self.bone_count;
self.iter
.nth_back(n)
.map(move |tf| Self::func(tf, bone_count))
}
}
impl ExactSizeIterator for FramePoseIterMut<'_> {
#[inline]
fn len(&self) -> usize {
self.iter.len()
}
}
impl RaylibModelAnimation for ModelAnimation {}
impl RaylibModelAnimation for WeakModelAnimation {}
impl ModelAnimation {
#[inline]
#[must_use]
pub unsafe fn make_weak(self) -> WeakModelAnimation {
let m = WeakModelAnimation(self.0);
std::mem::forget(self);
m
}
}
pub trait RaylibModelAnimation: AsRef<ffi::ModelAnimation> + AsMut<ffi::ModelAnimation> {
#[must_use]
fn frame_poses(&self) -> Vec<&[Transform]> {
let anim = self.as_ref();
let mut top = Vec::with_capacity(anim.keyframeCount as usize);
for i in 0..anim.keyframeCount {
top.push(unsafe {
std::slice::from_raw_parts(
*(anim.keyframePoses.offset(i as isize) as *const *const Transform),
anim.boneCount as usize,
)
});
}
top
}
#[must_use]
fn frame_poses_iter(&self) -> FramePoseIter<'_> {
let anim = self.as_ref();
unsafe {
FramePoseIter::new(
anim.keyframePoses,
anim.keyframeCount as usize,
anim.boneCount as usize,
)
}
}
#[must_use]
fn frame_poses_mut(&mut self) -> Vec<&mut [Transform]> {
let anim = self.as_ref();
let mut top = Vec::with_capacity(anim.keyframeCount as usize);
for i in 0..anim.keyframeCount {
top.push(unsafe {
std::slice::from_raw_parts_mut(
*(anim.keyframePoses.offset(i as isize) as *mut *mut Transform),
anim.boneCount as usize,
)
});
}
top
}
#[must_use]
fn frame_poses_iter_mut(&mut self) -> FramePoseIterMut<'_> {
let anim = self.as_ref();
unsafe {
FramePoseIterMut::new(
anim.keyframePoses,
anim.keyframeCount as usize,
anim.boneCount as usize,
)
}
}
}
impl MaterialMap {
#[inline]
#[must_use]
pub fn texture(&self) -> &crate::texture::WeakTexture2D {
unsafe { std::mem::transmute(&self.0.texture) }
}
#[inline]
#[must_use]
pub fn texture_mut(&mut self) -> &mut crate::texture::WeakTexture2D {
unsafe { std::mem::transmute(&mut self.0.texture) }
}
#[inline]
#[must_use]
pub fn color(&self) -> &Color {
unsafe { std::mem::transmute(&self.0.color) }
}
#[inline]
#[must_use]
pub fn color_mut(&mut self) -> &mut Color {
unsafe { std::mem::transmute(&mut self.0.color) }
}
#[inline]
#[must_use]
pub fn value(&self) -> &f32 {
unsafe { std::mem::transmute(&self.0.value) }
}
#[inline]
#[must_use]
pub fn value_mut(&mut self) -> &mut f32 {
unsafe { std::mem::transmute(&mut self.0.value) }
}
}
impl RaylibHandle {
#[inline]
#[must_use]
pub fn load_material_default(&self, _: &RaylibThread) -> WeakMaterial {
WeakMaterial(unsafe { ffi::LoadMaterialDefault() })
}
#[inline]
pub unsafe fn unload_material(&mut self, _: &RaylibThread, material: WeakMaterial) {
unsafe { ffi::UnloadMaterial(*material.as_ref()) }
}
#[inline]
pub unsafe fn unload_model(&mut self, _: &RaylibThread, model: WeakModel) {
unsafe { ffi::UnloadModel(*model.as_ref()) }
}
#[inline]
pub unsafe fn unload_mesh(&mut self, _: &RaylibThread, mesh: WeakMesh) {
unsafe { ffi::UnloadMesh(*mesh.as_ref()) }
}
}
#[derive(Debug, Clone)]
#[must_use]
pub struct MeshBuilder<'a> {
vertices: &'a [Vector3],
texcoords: &'a [Vector2],
texcoords2: Option<&'a [Vector2]>,
normals: Option<&'a [Vector3]>,
tangents: Option<&'a [Vector4]>,
colors: Option<&'a [Color]>,
indices: Option<&'a [u16]>,
}
impl Mesh {
#[inline]
pub fn gen_mesh<'a>(vertices: &'a [Vector3], texcoords: &'a [Vector2]) -> MeshBuilder<'a> {
MeshBuilder::new(vertices, texcoords)
}
}
fn slice_to_rl_ptr<'a, T: Copy + 'a, U: 'a>(
data: Option<&'a [T]>,
) -> Result<*mut U, AllocationError> {
Ok(match data {
Some(data) => {
assert!(
std::mem::size_of_val(data) >= std::mem::size_of::<U>(),
"should not cast to a larger type",
);
assert!(
(std::mem::size_of_val(data) % std::mem::size_of::<U>()) == 0,
"should not cast to a type whose size does not evenly divide the source",
);
assert!(
(std::mem::align_of::<T>() >= std::mem::align_of::<U>()),
"should not cast to a type with wider alignment than that of the source",
);
assert!(
(std::mem::align_of::<T>() % std::mem::align_of::<U>()) == 0,
"should not cast to a type whose alignment does not evenly divide the source alignment",
);
DataBuf::<[T]>::alloc_from_copy(data)?
.into_inner()
.into_inner()
.as_ptr()
.cast::<U>()
}
None => std::ptr::null_mut(),
})
}
impl<'a> MeshBuilder<'a> {
pub fn new(vertices: &'a [Vector3], texcoords: &'a [Vector2]) -> Self {
Self {
vertices,
texcoords,
texcoords2: None,
normals: None,
tangents: None,
colors: None,
indices: None,
}
}
#[inline]
pub fn texcoords2(&mut self, texcoords2: &'a [Vector2]) -> &mut Self {
assert!(
self.texcoords2.is_none(),
"texcoords2() should be called no more than once on the same MeshBuilder",
);
self.texcoords2 = Some(texcoords2);
self
}
#[inline]
pub fn normals(&mut self, normals: &'a [Vector3]) -> &mut Self {
assert!(
self.normals.is_none(),
"normals() should be called no more than once on the same MeshBuilder",
);
self.normals = Some(normals);
self
}
#[inline]
pub fn tangents(&mut self, tangents: &'a [Vector4]) -> &mut Self {
assert!(
self.tangents.is_none(),
"tangents() should be called no more than once on the same MeshBuilder",
);
self.tangents = Some(tangents);
self
}
#[inline]
pub fn colors(&mut self, colors: &'a [Color]) -> &mut Self {
assert!(
self.colors.is_none(),
"colors() should be called no more than once on the same MeshBuilder",
);
self.colors = Some(colors);
self
}
#[inline]
pub fn indices(&mut self, indices: &'a [u16]) -> &mut Self {
assert!(
self.indices.is_none(),
"indices() should be called no more than once on the same MeshBuilder",
);
self.indices = Some(indices);
self
}
fn check_mesh(&self) -> Result<(usize, usize), InvalidMeshError> {
let vertex_count = self.vertices.len();
let triangle_vertex_count = self.indices.map_or(vertex_count, <[_]>::len);
let triangle_count = triangle_vertex_count / 3;
let triangle_count_rem = triangle_vertex_count % 3;
if triangle_count_rem != 0 {
Err(InvalidMeshError::TrianglePointMiscount)
} else if self.texcoords.len() != vertex_count {
Err(InvalidMeshError::TexcoordsMiscount)
} else if self.texcoords2.is_some_and(|x| x.len() != vertex_count) {
Err(InvalidMeshError::Texcoords2Miscount)
} else if self.normals.is_some_and(|x| x.len() != vertex_count) {
Err(InvalidMeshError::NormalsMiscount)
} else if self.tangents.is_some_and(|x| x.len() != vertex_count) {
Err(InvalidMeshError::TangentsMiscount)
} else if self.colors.is_some_and(|x| x.len() != vertex_count) {
Err(InvalidMeshError::ColorsMiscount)
} else if match self.indices {
Some(indices) => {
let vertex_count = vertex_count
.try_into()
.map_err(InvalidMeshError::VertexUnindexible)?;
indices.iter().any(|&x| x >= vertex_count)
}
None => false,
} {
Err(InvalidMeshError::IndexOutOfBounds)
} else {
Ok((vertex_count, triangle_count))
}
}
pub fn build(&self, _thread: &RaylibThread) -> Result<Mesh, GenMeshError> {
let (vertex_count, triangle_count) = self.check_mesh()?;
let raw_mesh = ffi::Mesh {
vertexCount: vertex_count.try_into().unwrap(),
triangleCount: triangle_count.try_into().unwrap(),
vertices: slice_to_rl_ptr(Some(self.vertices))?,
texcoords: slice_to_rl_ptr(Some(self.texcoords))?,
texcoords2: slice_to_rl_ptr(self.texcoords2)?,
normals: slice_to_rl_ptr(self.normals)?,
tangents: slice_to_rl_ptr(self.tangents)?,
colors: slice_to_rl_ptr(self.colors)?,
indices: slice_to_rl_ptr(self.indices)?,
..Default::default()
};
let mut mesh = unsafe { Mesh::from_raw(raw_mesh) };
unsafe {
mesh.upload(false);
}
Ok(mesh)
}
}
#[cfg(test)]
mod mesh_soundness {
use super::*;
#[test]
fn null_field_accessors_are_empty_not_ub() {
let ffi_mesh: ffi::Mesh = unsafe { std::mem::zeroed() };
let m = WeakMesh(ffi_mesh);
assert!(
m.vertices().is_empty(),
"vertices() on null ptr must be empty"
);
assert!(
m.normals().is_empty(),
"normals() on null ptr must be empty"
);
assert!(
m.texcoords().is_empty(),
"texcoords() on null ptr must be empty"
);
assert!(
m.tangents().is_empty(),
"tangents() on null ptr must be empty"
);
assert!(m.colors().is_empty(), "colors() on null ptr must be empty");
assert!(
m.indices().is_empty(),
"indices() on null ptr must be empty"
);
}
}