bevy/crates/bevy_mikktspace/src/lib.rs
James Liu 56bcbb0975
Forbid unsafe in most crates in the engine (#12684)
# Objective
Resolves #3824. `unsafe` code should be the exception, not the norm in
Rust. It's obviously needed for various use cases as it's interfacing
with platforms and essentially running the borrow checker at runtime in
the ECS, but the touted benefits of Bevy is that we are able to heavily
leverage Rust's safety, and we should be holding ourselves accountable
to that by minimizing our unsafe footprint.

## Solution
Deny `unsafe_code` workspace wide. Add explicit exceptions for the
following crates, and forbid it in almost all of the others.

* bevy_ecs - Obvious given how much unsafe is needed to achieve
performant results
* bevy_ptr - Works with raw pointers, even more low level than bevy_ecs.
 * bevy_render - due to needing to integrate with wgpu
 * bevy_window - due to needing to integrate with raw_window_handle
* bevy_utils - Several unsafe utilities used by bevy_ecs. Ideally moved
into bevy_ecs instead of made publicly usable.
 * bevy_reflect - Required for the unsafe type casting it's doing.
 * bevy_transform - for the parallel transform propagation
 * bevy_gizmos  - For the SystemParam impls it has.
* bevy_assets - To support reflection. Might not be required, not 100%
sure yet.
* bevy_mikktspace - due to being a conversion from a C library. Pending
safe rewrite.
* bevy_dynamic_plugin - Inherently unsafe due to the dynamic loading
nature.

Several uses of unsafe were rewritten, as they did not need to be using
them:

* bevy_text - a case of `Option::unchecked` could be rewritten as a
normal for loop and match instead of an iterator.
* bevy_color - the Pod/Zeroable implementations were replaceable with
bytemuck's derive macros.
2024-03-27 03:30:08 +00:00

99 lines
3.1 KiB
Rust

#![allow(
unsafe_op_in_unsafe_fn,
clippy::all,
clippy::undocumented_unsafe_blocks,
clippy::ptr_cast_constness
)]
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
)]
use glam::{Vec2, Vec3};
mod generated;
/// The interface by which mikktspace interacts with your geometry.
pub trait Geometry {
/// Returns the number of faces.
fn num_faces(&self) -> usize;
/// Returns the number of vertices of a face.
fn num_vertices_of_face(&self, face: usize) -> usize;
/// Returns the position of a vertex.
fn position(&self, face: usize, vert: usize) -> [f32; 3];
/// Returns the normal of a vertex.
fn normal(&self, face: usize, vert: usize) -> [f32; 3];
/// Returns the texture coordinate of a vertex.
fn tex_coord(&self, face: usize, vert: usize) -> [f32; 2];
/// Sets the generated tangent for a vertex.
/// Leave this function unimplemented if you are implementing
/// `set_tangent_encoded`.
fn set_tangent(
&mut self,
tangent: [f32; 3],
_bi_tangent: [f32; 3],
_f_mag_s: f32,
_f_mag_t: f32,
bi_tangent_preserves_orientation: bool,
face: usize,
vert: usize,
) {
let sign = if bi_tangent_preserves_orientation {
1.0
} else {
-1.0
};
self.set_tangent_encoded([tangent[0], tangent[1], tangent[2], sign], face, vert);
}
/// Sets the generated tangent for a vertex with its bi-tangent encoded as the 'W' (4th)
/// component in the tangent. The 'W' component marks if the bi-tangent is flipped. This
/// is called by the default implementation of `set_tangent`; therefore, this function will
/// not be called by the crate unless `set_tangent` is unimplemented.
fn set_tangent_encoded(&mut self, _tangent: [f32; 4], _face: usize, _vert: usize) {}
}
/// Generates tangents for the input geometry.
///
/// # Errors
///
/// Returns `false` if the geometry is unsuitable for tangent generation including,
/// but not limited to, lack of vertices.
#[allow(unsafe_code)]
pub fn generate_tangents<I: Geometry>(geometry: &mut I) -> bool {
unsafe { generated::genTangSpace(geometry, 180.0) }
}
fn get_position<I: Geometry>(geometry: &mut I, index: usize) -> Vec3 {
let (face, vert) = index_to_face_vert(index);
geometry.position(face, vert).into()
}
fn get_tex_coord<I: Geometry>(geometry: &mut I, index: usize) -> Vec3 {
let (face, vert) = index_to_face_vert(index);
let tex_coord: Vec2 = geometry.tex_coord(face, vert).into();
let val = tex_coord.extend(1.0);
val
}
fn get_normal<I: Geometry>(geometry: &mut I, index: usize) -> Vec3 {
let (face, vert) = index_to_face_vert(index);
geometry.normal(face, vert).into()
}
fn index_to_face_vert(index: usize) -> (usize, usize) {
(index >> 2, index & 0x3)
}
fn face_vert_to_index(face: usize, vert: usize) -> usize {
face << 2 | vert & 0x3
}