diff --git a/Cargo.toml b/Cargo.toml
index 24a552a5..c6691e02 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,9 @@
# \___\/ ==\______\/\_____\__\/ ==\______/_____,´ /==\_____\___\/==\_______\/
# \_____\,´
+# On nightly only
+#cargo-features = ["panic-immediate-abort"]
+
[package]
name = "retrofire"
description = "90s style software 3D renderer and graphics tools."
@@ -67,12 +70,21 @@ debug = 1
[profile.bench]
opt-level = 3
codegen-units = 1
-lto = "thin"
+lto = "fat"
[profile.dev]
opt-level = 1
split-debuginfo = "unpacked"
+[profile.min]
+inherits = "release"
+opt-level = 3
+codegen-units = 1
+lto = "fat"
+debug = 0
+strip = true
+panic = "abort"
+#panic = "immediate-abort" # On nightly only
[[bench]]
name = "fill"
diff --git a/benches/clip.rs b/benches/clip.rs
index c2450ff3..d970a778 100644
--- a/benches/clip.rs
+++ b/benches/clip.rs
@@ -14,12 +14,13 @@ use retrofire_core::{
//#[global_allocator]
//static ALLOC: AllocProfiler = AllocProfiler::system();
-#[divan::bench(args = [1, 10, 100, 1000, 10_000])]
+#[divan::bench(args = [1, 10, 100, 1000, 10_000], min_time = 1)]
fn clip_mixed(b: Bencher, n: usize) {
let rng = &mut DefaultRng::default();
let pts = pt3(-10.0, -10.0, -10.0)..pt3(10.0, 10.0, 10.0);
let proj = orthographic(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0));
+ let mut out = Vec::with_capacity(n);
b.with_inputs(|| {
repeat_with(|| {
let vs = array::from_fn(|_| {
@@ -32,18 +33,19 @@ fn clip_mixed(b: Bencher, n: usize) {
})
.input_counter(|tris| ItemsCount::of_iter(tris))
.bench_local_values(|tris| {
- let mut out = Vec::new();
+ out.clear();
view_frustum::clip(tris.as_slice(), &mut out);
- out
+ out.len()
})
}
-#[divan::bench(args = [1, 10, 100, 1000, 10_000])]
+#[divan::bench(args = [1, 10, 100, 1000, 10_000], min_time = 1)]
fn clip_all_inside(b: Bencher, n: usize) {
let rng = &mut DefaultRng::default();
let pts = pt3(-1.0, -1.0, -1.0)..pt3(1.0, 1.0, 1.0);
let proj = orthographic(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0));
+ let mut out = Vec::with_capacity(n);
b.with_inputs(|| {
repeat_with(|| {
let vs = array::from_fn(|_| {
@@ -56,18 +58,19 @@ fn clip_all_inside(b: Bencher, n: usize) {
})
.input_counter(|tris| ItemsCount::of_iter(tris))
.bench_local_values(|tris| {
- let mut out = Vec::new();
+ out.clear();
view_frustum::clip(tris.as_slice(), &mut out);
- out
+ out.len()
})
}
-#[divan::bench(args = [1, 10, 100, 1000, 10_000])]
+#[divan::bench(args = [1, 10, 100, 1000, 10_000], min_time = 1)]
fn clip_all_outside(b: Bencher, n: usize) {
let mut rng = DEFAULT_RNG;
let pts = pt3(2.0, -10.0, -10.0)..pt3(10.0, 10.0, 10.0);
let proj = orthographic(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0));
+ let mut out = Vec::with_capacity(n);
b.with_inputs(|| {
repeat_with(|| {
let vs = ([pts.start; 3]..[pts.end; 3])
@@ -80,9 +83,9 @@ fn clip_all_outside(b: Bencher, n: usize) {
})
.input_counter(|tris| ItemsCount::of_iter(tris))
.bench_local_values(|tris| {
- let mut out = Vec::with_capacity(tris.len());
+ out.clear();
view_frustum::clip(tris.as_slice(), &mut out);
- out
+ out.len()
})
}
diff --git a/benches/fill.rs b/benches/fill.rs
index a6f38133..2bee07fa 100644
--- a/benches/fill.rs
+++ b/benches/fill.rs
@@ -56,10 +56,8 @@ fn gouraud(b: Bencher, sz: f32) {
});
});
- let buf = Buf2::new_from(
- (1024, 1024),
- buf.data().into_iter().map(|c| c.to_color3()),
- );
+ let buf =
+ Buf2::new_from((1024, 1024), buf.data().iter().map(|c| c.to_color3()));
save_ppm("benches_fill_color.ppm", buf).unwrap();
}
diff --git a/core/examples/hello_tri.rs b/core/examples/hello_tri.rs
index 3e33b97e..8f4ddf17 100644
--- a/core/examples/hello_tri.rs
+++ b/core/examples/hello_tri.rs
@@ -1,5 +1,5 @@
+use retrofire_core::prelude::*;
use retrofire_core::render::{Model, render, shader};
-use retrofire_core::{prelude::*, util::*};
fn main() {
let verts = [
@@ -53,6 +53,7 @@ fn main() {
}
#[cfg(feature = "std")]
{
+ use retrofire_core::util::pnm;
pnm::save_ppm("triangle.ppm", framebuf).unwrap();
}
}
diff --git a/core/src/geom/mesh.rs b/core/src/geom/mesh.rs
index f04a50cb..9cba28ba 100644
--- a/core/src/geom/mesh.rs
+++ b/core/src/geom/mesh.rs
@@ -92,6 +92,7 @@ impl Mesh {
}
/// Returns a mesh with the faces and vertices of both `self` and `other`.
+ #[must_use]
pub fn merge(mut self, Self { faces, verts }: Self) -> Self {
let n = self.verts.len();
self.verts.extend(verts);
@@ -107,7 +108,7 @@ fn assert_indices_in_bounds(faces: &[Tri], len: usize) {
assert!(
vs.iter().all(|&j| j < len),
"vertex index out of bounds at faces[{i}]: {vs:?}"
- )
+ );
}
}
@@ -164,6 +165,7 @@ impl Builder {
///
/// # Panics
/// If any of the vertex indices in `faces` ≥ `verts.len()`.
+ #[must_use]
pub fn build(self) -> Mesh {
// Sanity checks done by new()
Mesh::new(self.mesh.faces, self.mesh.verts)
@@ -175,6 +177,7 @@ impl Builder {
///
/// This is an eager operation, that is, only vertices *currently*
/// added to the builder are transformed.
+ #[must_use]
pub fn transform(self, tf: &Mat4) -> Self {
self.warp(|v| vertex(tf.apply(&v.pos), v.attrib))
}
@@ -184,6 +187,7 @@ impl Builder {
/// This method can be used for various nonlinear transformations such as
/// twisting or dilation. This is an eager operation, that is, only vertices
/// *currently* added to the builder are transformed.
+ #[must_use]
pub fn warp(mut self, f: impl FnMut(Vertex3) -> Vertex3) -> Self {
self.mesh.verts = self.mesh.verts.into_iter().map(f).collect();
self
@@ -202,6 +206,7 @@ impl Builder {
/// This is an eager operation, that is, only vertices *currently* added
/// to the builder are transformed. The attribute type of the result is
/// `Normal3`; the vertex type it accepts is changed accordingly.
+ #[must_use]
pub fn with_vertex_normals(self) -> Builder {
let Mesh { verts, faces } = self.mesh;
diff --git a/core/src/geom/prim.rs b/core/src/geom/prim.rs
index 196d5e72..88701990 100644
--- a/core/src/geom/prim.rs
+++ b/core/src/geom/prim.rs
@@ -467,6 +467,7 @@ impl Plane3 {
///
/// assert_eq!(::new(0.0, 0.0, 1.0, 2.0).project(pt), pt3(1.0, 2.0, 2.0));
/// ```
+ #[must_use]
pub fn project(&self, pt: Point3) -> Point3 {
// The vector that projects pt on the plane is parallel with the plane
// normal and its length is the distance of pt from the plane.
@@ -672,9 +673,9 @@ impl Line2 {
///
/// # Panics
/// If the vector (a, b) is not unit-length.
- pub fn new(a: f32, b: f32, c: f32) -> Self {
+ pub const fn new(a: f32, b: f32, c: f32) -> Self {
// TODO This method can't itself normalize because const
- assert!((a * a + b * b - 1.0).abs() < 1e-6, "non-unit normal");
+ assert!((a * a + b * b - 1.0).abs() < 1e-5, "non-unit normal",);
Self(Vector::new([a, b, -c]))
}
@@ -720,7 +721,7 @@ impl Line2 {
/// Returns the coefficients [a, b, c] of the line equation ax + by = c.
pub const fn coeffs(&self) -> [f32; 3] {
- return self.0.0;
+ self.0.0
}
}
@@ -1071,21 +1072,21 @@ mod tests {
let mut l: Line2;
l = Line2::new(1.0, 0.0, 0.0); // x = 0
- assert_eq!(format!("{:?}", l), "Line(x = 0)");
+ assert_eq!(format!("{l:?}"), "Line(x = 0)");
l = Line2::from_points(pt2(2.0, 0.0), pt2(2.0, -1.0));
assert_eq!(l.coeffs(), [1.0, 0.0, -2.0]);
- assert_eq!(format!("{:?}", l), "Line(x = 2)");
+ assert_eq!(format!("{l:?}"), "Line(x = 2)");
l = Line2::new(1.0, 0.0, 2.0); // x = 2
- assert_eq!(format!("{:?}", l), "Line(x = 2)");
+ assert_eq!(format!("{l:?}"), "Line(x = 2)");
l = Line2::new(0.0, 1.0, 0.0); // y = 0
- assert_eq!(format!("{:?}", l), "Line(y = 0)");
+ assert_eq!(format!("{l:?}"), "Line(y = 0)");
l = Line2::from_points(pt2(0.0, -3.0), pt2(1.0, -3.0)); // y = -3
assert_eq!(l.slope_intercept(), Some((0.0, -3.0)));
- assert_eq!(format!("{:?}", l), "Line(y = -3)");
+ assert_eq!(format!("{l:?}"), "Line(y = -3)");
l = Line2::new(0.0, 1.0, -3.0); // y = -3
assert_eq!(format!("{:?}", l), "Line(y = -3)");
diff --git a/core/src/lib.rs b/core/src/lib.rs
index 11d2a072..0a5b3e3c 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -41,6 +41,7 @@
//! # Example
//!
//! ```
+#![allow(clippy::needless_doctest_main)]
#![doc = include_str!("../examples/hello_tri.rs")]
//! ```
diff --git a/core/src/math.rs b/core/src/math.rs
index d90a9aae..3250aead 100644
--- a/core/src/math.rs
+++ b/core/src/math.rs
@@ -1,7 +1,7 @@
//! Linear algebra and other useful mathematics.
//!
//! Includes [vectors][self::vec], [matrices][mat], [colors][color],
-//! [angles][angle], [Bezier splines][spline] and [pseudo-random numbers][rand],
+//! [angles][angle], [splines][spline] and [pseudo-random numbers][rand],
//! as well as support for custom [varying][vary] types and utilities such as
//! approximate equality comparisons.
//!
diff --git a/core/src/math/angle.rs b/core/src/math/angle.rs
index e3aeaff0..fdf93d0f 100644
--- a/core/src/math/angle.rs
+++ b/core/src/math/angle.rs
@@ -77,6 +77,7 @@ pub const fn turns(a: f32) -> Angle {
/// # Panics
/// If `x` is outside the range [-1.0, 1.0].
#[cfg(feature = "fp")]
+#[inline]
pub fn asin(x: f32) -> Angle {
assert!(-1.0 <= x && x <= 1.0);
Angle(f32::asin(x))
@@ -96,6 +97,7 @@ pub fn asin(x: f32) -> Angle {
/// # Panics
/// If `x` is outside the range [-1.0, 1.0].
#[cfg(feature = "fp")]
+#[inline]
pub fn acos(x: f32) -> Angle {
Angle(f32::acos(x))
}
@@ -113,11 +115,13 @@ pub fn acos(x: f32) -> Angle {
/// assert_eq!(atan2(-3.0, 0.0), degs(-90.0));
/// ```
#[cfg(feature = "fp")]
+#[inline]
pub fn atan2(y: f32, x: f32) -> Angle {
Angle(f32::atan2(y, x))
}
/// Returns a polar coordinate vector with azimuth `az` and radius `r`.
+#[inline]
pub const fn polar(r: f32, az: Angle) -> PolarVec {
Vector::new([r, az.to_rads()])
}
@@ -126,6 +130,7 @@ pub const fn polar(r: f32, az: Angle) -> PolarVec {
/// altitude `alt`, and radius `r`.
///
/// An altitude of +90° corresponds to straight up and -90° to straight down.
+#[inline]
pub const fn spherical(r: f32, az: Angle, alt: Angle) -> SphericalVec {
Vector::new([r, az.to_rads(), alt.to_rads()])
}
@@ -183,11 +188,13 @@ impl Angle {
/// Returns the minimum of `self` and `other`.
#[inline]
+ #[must_use]
pub const fn min(self, other: Self) -> Self {
Self(self.0.min(other.0))
}
/// Returns the maximum of `self` and `other`.
#[inline]
+ #[must_use]
pub const fn max(self, other: Self) -> Self {
Self(self.0.max(other.0))
}
@@ -413,6 +420,7 @@ impl Vec2 {
/// // A negative x and zero y maps to straight angle azimuth
/// assert_approx_eq!(vec2(-1.0, 0.0).to_polar().az(), degs(180.0));
/// ```
+ #[inline]
pub fn to_polar(&self) -> PolarVec {
polar(self.len(), self.atan())
}
@@ -426,8 +434,8 @@ impl Vec3 {
/// * `r` equals `self.len()`
/// * `az`is the angle between `self` and the xy-plane in the range
/// (-180°, 180°] such that positive `z` maps to *negative* `az`, and
- /// * `alt` is the angle between `self` and the xz-plane in the
- /// range [-90°, 90°] such that positive `y` maps to positive `alt`.
+ /// * `alt` is the angle between `self` and the xz-plane in the range
+ /// [-90°, 90°] such that positive `y` maps to positive `alt`.
///
/// # Examples
/// ```
@@ -631,7 +639,7 @@ impl DivAssign for Angle {
impl From> for Vec2 {
/// Converts a polar vector into the equivalent Cartesian vector.
///
- /// See [PolarVec::to_cart] for more information.
+ /// See [`PolarVec::to_cart`] for more information.
#[inline]
fn from(p: PolarVec) -> Self {
p.to_cart()
@@ -642,7 +650,7 @@ impl From> for Vec2 {
impl From> for PolarVec {
/// Converts a Cartesian 2-vector into the equivalent polar vector.
///
- /// See [Vec2::to_polar] for more information.
+ /// See [`Vec2::to_polar`] for more information.
#[inline]
fn from(v: Vec2) -> Self {
v.to_polar()
@@ -653,7 +661,7 @@ impl From> for PolarVec {
impl From> for Vec3 {
/// Converts a spherical coordinate vector to a Euclidean 3-vector.
///
- /// See [SphericalVec::to_cart] for more information.
+ /// See [`SphericalVec::to_cart`] for more information.
#[inline]
fn from(v: SphericalVec) -> Self {
v.to_cart()
@@ -664,7 +672,7 @@ impl From> for Vec3 {
impl From> for SphericalVec {
/// Converts a Cartesian 3-vector into the equivalent spherical vector.
///
- /// See [Vec3::to_spherical] for more information.
+ /// See [`Vec3::to_spherical`] for more information.
#[inline]
fn from(v: Vec3) -> Self {
v.to_spherical()
diff --git a/core/src/math/color.rs b/core/src/math/color.rs
index eab9acab..225c7262 100644
--- a/core/src/math/color.rs
+++ b/core/src/math/color.rs
@@ -139,6 +139,7 @@ impl Color<[f32; N], Sp> {
// A generic clamp for Sc: Ord would conflict with this one. There is
// currently no clean way to support both floats and impl Ord types.
// However, ColorX should have its own inherent impls.
+ #[inline]
#[must_use]
pub fn clamp(&self, min: &Self, max: &Self) -> Self {
array::from_fn(|i| self[i].clamp(min[i], max[i])).into()
@@ -313,7 +314,7 @@ impl Color3f {
(r - g) / d + 4.0
};
let h = h / 6.0;
- let l = (max + min) / 2.0;
+ let l = min.midpoint(max);
let s = if l == 0.0 || l == 1.0 {
0.0
} else {
@@ -330,6 +331,7 @@ impl Color3f {
impl Color4f {
/// Returns `self` as RGB, discarding the alpha channel.
+ #[inline]
pub const fn to_rgb(self) -> Color3f {
let [r, g, b, _] = self.0;
rgb(r, g, b)
@@ -421,7 +423,7 @@ impl Color3 {
let rgb = hcx_to_rgb(h / _1, c, x, 0);
rgb.map(|ch| {
let ch = ch + m;
- debug_assert!(0 <= ch && ch < _1, "channel oob: {:?}", ch);
+ debug_assert!(0 <= ch && ch < _1, "channel oob: {ch:?}");
ch as u8
})
.into()
@@ -473,11 +475,13 @@ fn hcx_to_rgb(h: i32, c: T, x: T, z: T) -> [T; 3] {
impl Color4 {
/// Returns `self` as HSL, discarding the alpha channel.
+ #[inline]
pub const fn to_hsl(self) -> Color3 {
let [h, s, l, _] = self.0;
hsl(h, s, l)
}
/// Returns the RGBA color equivalent to `self`.
+ #[inline]
pub fn to_rgba(self) -> Color4 {
let [r, g, b] = self.to_hsl().to_rgb().0;
rgba(r, g, b, self.a())
@@ -485,11 +489,13 @@ impl Color4 {
}
impl Color4f {
/// Returns `self` as HSL, discarding the alpha channel.
+ #[inline]
pub fn to_hsl(self) -> Color3f {
let [h, s, l, _] = self.0;
hsl(h, s, l)
}
/// Returns the RGBA color equivalent to `self`.
+ #[inline]
pub fn to_rgba(self) -> Color4f {
let [r, g, b] = self.to_hsl().to_rgb().0;
rgba(r, g, b, self.a())
@@ -498,14 +504,17 @@ impl Color4f {
impl Color<[Ch; N], Rgb> {
/// Returns the red component of `self`.
+ #[inline]
pub const fn r(&self) -> Ch {
self.0[0]
}
/// Returns the green component of `self`.
+ #[inline]
pub const fn g(&self) -> Ch {
self.0[1]
}
/// Returns the blue component of `self`.
+ #[inline]
pub const fn b(&self) -> Ch {
self.0[2]
}
@@ -513,18 +522,22 @@ impl Color<[Ch; N], Rgb> {
impl Color<[Ch; N], Rgba> {
/// Returns the red component of `self`.
+ #[inline]
pub const fn r(&self) -> Ch {
self.0[0]
}
/// Returns the green component of `self`.
+ #[inline]
pub const fn g(&self) -> Ch {
self.0[1]
}
/// Returns the blue component of `self`.
+ #[inline]
pub const fn b(&self) -> Ch {
self.0[2]
}
/// Returns the alpha component of `self`.
+ #[inline]
pub const fn a(&self) -> Ch {
self.0[3]
}
@@ -532,14 +545,17 @@ impl Color<[Ch; N], Rgba> {
impl Color<[Ch; N], Hsl> {
/// Returns the hue component of `self`.
+ #[inline]
pub const fn h(&self) -> Ch {
self.0[0]
}
/// Returns the saturation component of `self`.
+ #[inline]
pub const fn s(&self) -> Ch {
self.0[1]
}
/// Returns the luminance component of `self`.
+ #[inline]
pub const fn l(&self) -> Ch {
self.0[2]
}
@@ -550,18 +566,22 @@ where
Ch: Copy,
{
/// Returns the hue component of `self`.
+ #[inline]
pub const fn h(&self) -> Ch {
self.0[0]
}
/// Returns the saturation component of `self`.
+ #[inline]
pub const fn s(&self) -> Ch {
self.0[1]
}
/// Returns the luminance component of `self`.
+ #[inline]
pub const fn l(&self) -> Ch {
self.0[2]
}
/// Returns the alpha component of `self`.
+ #[inline]
pub const fn a(&self) -> Ch {
self.0[3]
}
@@ -578,13 +598,15 @@ impl Affine for Color<[u8; DIM], Sp> {
const DIM: usize = DIM;
+ #[inline]
fn add(&self, other: &Self::Diff) -> Self {
array::from_fn(|i| {
let sum = i32::from(self.0[i]) + other.0[i];
- sum.clamp(0, u8::MAX as i32) as u8
+ sum.clamp(0, u8::MAX.into()) as u8
})
.into()
}
+ #[inline]
fn sub(&self, other: &Self) -> Self::Diff {
array::from_fn(|i| i32::from(self.0[i]) - i32::from(other.0[i])).into()
}
@@ -616,6 +638,7 @@ where
type Scalar = f32;
/// Returns the all-zeroes color (black).
+ #[inline]
fn zero() -> Self {
[0.0; DIM].into()
}
@@ -636,6 +659,7 @@ impl ZDiv for Color<[Sc; N], Sp> where Sc: ZDiv + Copy {
impl Copy for Color {}
impl Clone for Color {
+ #[inline]
fn clone(&self) -> Self {
self.0.clone().into()
}
@@ -648,6 +672,7 @@ impl Debug for Color {
}
impl Default for Color {
+ #[inline]
fn default() -> Self {
R::default().into()
}
@@ -656,6 +681,7 @@ impl Default for Color {
impl Eq for Color {}
impl PartialEq for Color {
+ #[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
@@ -705,7 +731,7 @@ where
{
#[inline]
fn sub_assign(&mut self, rhs: D) {
- *self += rhs.neg();
+ self.add_assign(rhs.neg());
}
}
@@ -728,7 +754,7 @@ where
#[inline]
fn mul_assign(&mut self, rhs: Self) {
for (a, b) in zip(&mut self.0, rhs.0) {
- *a = (&*a).mul(b)
+ *a = Linear::mul(a, b);
}
}
}
diff --git a/core/src/math/grad.rs b/core/src/math/grad.rs
index 2df68f17..a2b73cf7 100644
--- a/core/src/math/grad.rs
+++ b/core/src/math/grad.rs
@@ -63,9 +63,9 @@ impl Gradient2 {
Kind::Radial(p0, r) => p.distance(&p0) / r,
#[cfg(feature = "fp")]
Kind::Conical(p0) => {
+ use super::float::f32;
let angle = (p - p0).atan();
// map negative angles to positive
- use super::float::f32;
f32::rem_euclid(angle.to_turns(), 1.0)
}
};
diff --git a/core/src/math/mat.rs b/core/src/math/mat.rs
index 48426425..837daa8c 100644
--- a/core/src/math/mat.rs
+++ b/core/src/math/mat.rs
@@ -210,9 +210,9 @@ const fn transpose(a: &mut [[Sc; N]; N]) {
let tmp = a[i][j];
a[i][j] = a[j][i];
a[j][i] = tmp;
- j += 1
+ j += 1;
}
- i += 1
+ i += 1;
}
}
@@ -279,7 +279,7 @@ where
array::from_fn(|j| array::from_fn(|i| dot(&lhs[j], &rhs[i])))
}
- let mut other = other.0.clone();
+ let mut other = other.0;
transpose(&mut other);
do_compose(&self.0, &other).into()
}
@@ -519,6 +519,11 @@ impl Mat3 {
Some(Mat3::new(res))
}
+ /// TODO
+ ///
+ /// # Panics
+ /// If the matrix is singular or near-singular.
+ #[must_use]
pub fn inverse(&self) -> Mat3 {
self.checked_inverse()
.expect("matrix cannot be singular or near-singular")
@@ -660,14 +665,6 @@ impl Mat4 {
#[must_use]
pub fn inverse(&self) -> Mat4 {
use super::float::f32;
- if cfg!(debug_assertions) {
- let det = self.determinant();
- assert!(
- !det.approx_eq(&0.0),
- "a singular, near-singular, or non-finite matrix does not \
- have a well-defined inverse (determinant = {det})"
- );
- }
// Elementary row operation subtracting one row,
// multiplied by a scalar, from another
@@ -685,6 +682,15 @@ impl Mat4 {
m.0.swap(r, s);
}
+ if cfg!(debug_assertions) {
+ let det = self.determinant();
+ assert!(
+ !det.approx_eq(&0.0),
+ "a singular, near-singular, or non-finite matrix does not \
+ have a well-defined inverse (determinant = {det})"
+ );
+ }
+
// This algorithm attempts to reduce `this` to the identity matrix
// by simultaneously applying elementary row operations to it and
// another matrix `inv` which starts as the identity matrix. Once
@@ -1484,11 +1490,11 @@ mod tests {
fn matrix_debug() {
assert_eq!(
alloc::format!("{MAT:?}"),
- r#"Matrix[
+ r"Matrix[
[ 0.0, 1.0, 2.0]
[10.0, 11.0, 12.0]
[20.0, 21.0, 22.0]
-]"#
+]"
);
}
}
@@ -1756,12 +1762,12 @@ mod tests {
fn matrix_debug() {
assert_eq!(
alloc::format!("{MAT:?}"),
- r#"Matrix[
+ r"Matrix[
[ 0.0, 1.0, 2.0, 3.0]
[10.0, 11.0, 12.0, 13.0]
[20.0, 21.0, 22.0, 23.0]
[30.0, 31.0, 32.0, 33.0]
-]"#
+]"
);
}
}
diff --git a/core/src/math/point.rs b/core/src/math/point.rs
index 0cc6572d..ed2c4eca 100644
--- a/core/src/math/point.rs
+++ b/core/src/math/point.rs
@@ -281,9 +281,9 @@ where
Sc: ZDiv + Copy,
{
#[inline]
- fn z_div(mut self, z: f32) -> Self {
+ fn z_div_recip(mut self, recip_z: f32) -> Self {
for c in &mut self.0 {
- *c = c.z_div(z);
+ *c = c.z_div_recip(recip_z);
}
self
}
@@ -308,12 +308,14 @@ impl ApproxEq for Point<[Sc; N], Sp> {
impl Copy for Point {}
impl Clone for Point {
+ #[inline]
fn clone(&self) -> Self {
Self(self.0.clone(), Pd)
}
}
impl Default for Point {
+ #[inline]
fn default() -> Self {
Self(R::default(), Pd)
}
@@ -329,6 +331,7 @@ impl Debug for Point {
impl Eq for Point {}
impl PartialEq for Point {
+ #[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
@@ -479,7 +482,7 @@ where
#[inline]
fn div(self, rhs: f32) -> Self {
- self * rhs.recip()
+ self.mul(1.0 / rhs)
}
}
impl DivAssign for Point
@@ -505,13 +508,14 @@ mod tests {
const pt2: fn(f32, f32) -> Point2 = super::pt2;
const pt3: fn(f32, f32, f32) -> Point3 = super::pt3;
+
#[test]
fn vector_addition() {
assert_eq!(pt2(1.0, 2.0) + vec2(-2.0, 3.0), pt2(-1.0, 5.0));
assert_eq!(
pt3(1.0, 2.0, 3.0) + vec3(-2.0, 3.0, 1.0),
pt3(-1.0, 5.0, 4.0)
- )
+ );
}
#[test]
fn vector_subtraction() {
@@ -519,7 +523,7 @@ mod tests {
assert_eq!(
pt3(1.0, 2.0, 3.0) - vec3(-2.0, 3.0, 1.0),
pt3(3.0, -1.0, 2.0)
- )
+ );
}
#[test]
fn point_subtraction() {
@@ -527,7 +531,7 @@ mod tests {
assert_eq!(
pt3(1.0, 2.0, 3.0) - pt3(-2.0, 3.0, 1.0),
vec3(3.0, -1.0, 2.0)
- )
+ );
}
#[test]
fn scalar_multiplication() {
diff --git a/core/src/math/rand.rs b/core/src/math/rand.rs
index cb37df7c..decb7b45 100644
--- a/core/src/math/rand.rs
+++ b/core/src/math/rand.rs
@@ -118,7 +118,7 @@ struct Samples(D, R);
impl Xorshift64 {
/// A random 64-bit prime, used to initialize the generator returned by
/// [`Xorshift64::default()`].
- pub const DEFAULT_SEED: u64 = 378682147834061;
+ pub const DEFAULT_SEED: u64 = 378_682_147_834_061;
/// Returns a new `Xorshift64` seeded by the given number.
///
@@ -163,10 +163,11 @@ impl Xorshift64 {
/// ```
#[cfg(feature = "std")]
pub fn from_time() -> Self {
- let t = std::time::SystemTime::UNIX_EPOCH
+ let d = std::time::SystemTime::UNIX_EPOCH
.elapsed()
- .unwrap();
- Self(t.as_micros() as u64)
+ // If for some strange reason the system time < epoch...
+ .unwrap_or_else(|e| e.duration());
+ Self(d.as_micros() as u64)
}
/// Returns 64 bits of pseudo-randomness.
@@ -174,6 +175,7 @@ impl Xorshift64 {
/// Successive calls to this function (with the same `self`) will yield
/// every value in the interval [1, 264) exactly once before
/// starting to repeat the sequence.
+ #[inline]
pub const fn next_bits(&mut self) -> u64 {
let Self(x) = self;
*x ^= *x << 13;
@@ -193,6 +195,7 @@ impl Iterator for Samples {
/// Returns the next pseudorandom sample from this iterator.
///
/// This method never returns `None`.
+ #[inline]
fn next(&mut self) -> Option {
Some(self.0.sample(self.1))
}
@@ -220,6 +223,7 @@ impl Default for Xorshift64 {
impl Distrib for &D {
type Sample = D::Sample;
+ #[inline]
fn sample(&self, rng: &mut DefaultRng) -> Self::Sample {
(*self).sample(rng)
}
@@ -357,6 +361,7 @@ where
{
type Sample = S;
+ #[inline]
fn sample(&self, rng: &mut DefaultRng) -> Self::Sample {
Uniform(self.clone()).sample(rng)
}
@@ -406,6 +411,7 @@ where
/// assert_eq!(int_pairs.next(), Some([1, 0]));
/// assert_eq!(int_pairs.next(), Some([3, 1]));
/// ```
+ #[inline]
fn sample(&self, rng: &mut DefaultRng) -> [T; N] {
let Range { start, end } = self.0;
array::from_fn(|i| Uniform(start[i]..end[i]).sample(rng))
@@ -592,6 +598,7 @@ impl Distrib for Bernoulli {
/// let bools = array::from_fn(|_| bern.sample(rng));
/// assert_eq!(bools, [true, true, false, true, false, true]);
/// ```
+ #[inline]
fn sample(&self, rng: &mut DefaultRng) -> bool {
Uniform(0.0f32..1.0).sample(rng) < self.0
}
@@ -601,6 +608,7 @@ impl Distrib for (D, E) {
type Sample = (D::Sample, E::Sample);
/// Returns a pair of samples, sampled from two separate distributions.
+ #[inline]
fn sample(&self, rng: &mut DefaultRng) -> Self::Sample {
(self.0.sample(rng), self.1.sample(rng))
}
@@ -616,7 +624,7 @@ mod tests {
const COUNT: usize = 1000;
fn rng() -> DefaultRng {
- Default::default()
+ DefaultRng::default()
}
#[test]
diff --git a/core/src/math/spline.rs b/core/src/math/spline.rs
index 54f6873b..1546c95c 100644
--- a/core/src/math/spline.rs
+++ b/core/src/math/spline.rs
@@ -1,4 +1,7 @@
//! Bézier curves and splines.
+
+#![allow(clippy::just_underscores_and_digits)]
+
use alloc::vec::Vec;
use core::{array::from_fn, fmt::Debug, marker::PhantomData};
@@ -243,6 +246,7 @@ impl CubicBezier {
/// the curve beyond the control points.
///
/// [1]: https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm
+ #[inline]
pub fn eval(&self, t: f32) -> T {
let [p0, p1, p2, p3] = &self.0;
let p01 = p0.lerp(p1, t);
@@ -262,6 +266,7 @@ where
/// numerically stable than [`Self::eval`]. Values of *t* outside the
/// interval [0, 1] are accepted and extrapolate the curve beyond the
/// control points.
+ #[inline]
pub fn fast_eval(&self, t: f32) -> T {
// Add a linear combination of the three coefficients
// to `p0` to get the result
@@ -346,6 +351,7 @@ where
///
/// Values of *t* outside the interval [0, 1] are accepted and extrapolate
/// the curve beyond the control points.
+ #[inline]
pub fn eval(&self, t: f32) -> P {
let Self([p0, p1], [d0, d1]) = self;
let [_0, t1, t2, t3] = [1.0, t, t * t, t * t * t];
@@ -366,7 +372,7 @@ where
// = (1 - b2) * p0 + b2 * p1
// = p0 + b2 * (p1 - p0)
- p0.add(&p1.sub(&p0).mul(b2)) // Affine part
+ p0.add(&p1.sub(p0).mul(b2)) // Affine part
.add(&d0.mul(b1).add(&d1.mul(b3))) // Linear part
}
@@ -398,7 +404,7 @@ where
// Only vectors as expected:
// b2·(p1 - p0) + b1·d0 + b3·d1
- p1.sub(&p0)
+ p1.sub(p0)
.mul(b2)
.add(&d0.mul(b1).add(&d1.mul(b3)))
}
@@ -442,13 +448,14 @@ where
.into_iter()
.flat_map(|Ray(p, d)| [p.add(&d.neg()), p.clone(), p.add(&d)])
.collect();
- Self::new(pts[1..pts.len() - 1].into_iter().cloned())
+ Self::new(pts[1..pts.len() - 1].iter().cloned())
}
/// Returns the point of `self` at the given *t* value.
///
/// Values of *t* outside the interval [0, 1] are accepted and extrapolate
/// the curve beyond the control points.
+ #[inline]
pub fn eval(&self, t: f32) -> T {
let (u, seg) = self.segment(t);
seg.fast_eval(u)
@@ -458,6 +465,7 @@ where
///
/// Values of *t* outside the interval [0, 1] are accepted and extrapolate
/// the curve beyond the control points.
+ #[inline]
pub fn velocity(&self, t: f32) -> T::Diff {
let (u, seg) = self.segment(t);
seg.velocity(u)
@@ -482,7 +490,6 @@ where
let num_segs = (self.0.len() - 1) / 3;
// Rescale from [0, 1] to [0, num_segs]
let t = t * num_segs as f32;
- use super::float::f32;
// Calculate the segment index.
let seg_i = (t as usize).min(num_segs - 1);
// The leftover part is the local t value. This is the fractional part
@@ -542,6 +549,7 @@ where
///
/// Values of *t* outside the interval [0, 1] are accepted and extrapolate
/// the curve beyond the control points.
+ #[inline]
pub fn eval(&self, t: f32) -> T {
let (u, seg) = self.segment(t);
seg.eval(u)
@@ -552,6 +560,7 @@ where
///
/// Values of *t* outside the interval [0, 1] are accepted and extrapolate
/// the curve beyond the control points.
+ #[inline]
pub fn velocity(&self, t: f32) -> T::Diff {
let (u, seg) = self.segment(t);
seg.velocity(u)
@@ -569,6 +578,10 @@ where
-0.5, 1.5, -1.5, 0.5;
];
+ /// TODO
+ ///
+ /// # Panics
+ /// If `pts` has fewer than four points.
pub fn new(pts: impl IntoIterator- ) -> Self {
let pts: Vec<_> = pts.into_iter().collect();
assert!(
@@ -582,6 +595,7 @@ where
///
/// Values of *t* outside the interval [0, 1] are accepted and extrapolate
/// the curve beyond the control points.
+ #[inline]
pub fn eval(&self, t: f32) -> T {
let (t, [p0, p1, p2, p3]) = crb_segment(&self.0, t);
let [_0, t1, t2, t3] = [1.0, t, t * t, t * t * t];
@@ -599,9 +613,9 @@ where
// = P0 - b1·P0 - b2·P0 - b3·P0 + b1·P1 + b2·P2 + b3·P3
// = P0 + b1·(P1 - P0) + b2·(P2 - P0) + b3·(P3 - P0)
- let v01 = p1.sub(&p0).mul(b1);
- let v02 = &p2.sub(&p0).mul(b2);
- let v03 = p3.sub(&p0).mul(b3);
+ let v01 = p1.sub(p0).mul(b1);
+ let v02 = p2.sub(p0).mul(b2);
+ let v03 = p3.sub(p0).mul(b3);
p0.add(&v01.add(&v02).add(&v03).mul(1.0 / 2.0))
}
@@ -629,9 +643,9 @@ where
// = b1·P1 + b2·P2 + b3·P3 - b1·P0 - b2·P0 - b3·P0
// = b1·(P1 - P0) + b2·(P2 - P0) + b3·(P3 - P0)
- let v01 = p1.sub(&p0).mul(b1);
- let v02 = p2.sub(&p0).mul(b2);
- let v03 = p3.sub(&p0).mul(b3);
+ let v01 = p1.sub(p0).mul(b1);
+ let v02 = p2.sub(p0).mul(b2);
+ let v03 = p3.sub(p0).mul(b3);
v01.add(&v02).add(&v03).mul(1.0 / 2.0)
}
}
@@ -651,6 +665,10 @@ where
]
};
+ /// TODO
+ ///
+ /// # Panics
+ /// If `pts` has fewer than four points.
pub fn new(pts: impl IntoIterator
- ) -> Self {
let pts: Vec<_> = pts.into_iter().collect();
assert!(pts.len() >= 4, "a B-spline requires at least four points");
@@ -670,9 +688,9 @@ where
let b2 = 1.0 + 3.0 * t1 + 3.0 * t2 - 3.0 * t3;
let b3 = t3;
- let v01 = p1.sub(&p0).mul(b1);
- let v02 = p2.sub(&p0).mul(b2);
- let v03 = p3.sub(&p0).mul(b3);
+ let v01 = p1.sub(p0).mul(b1);
+ let v02 = p2.sub(p0).mul(b2);
+ let v03 = p3.sub(p0).mul(b3);
p0.add(&v01.add(&v02).add(&v03).mul(1.0 / 6.0))
}
@@ -700,15 +718,16 @@ where
// = b1·P1 + b2·P2 + b3·P3 - b1·P0 - b2·P0 - b3·P0
// = b1·(P1 - P0) + b2·(P2 - P0) + b3·(P3 - P0)
- let v01 = p1.sub(&p0).mul(b1);
- let v02 = p2.sub(&p0).mul(b2);
- let v03 = p3.sub(&p0).mul(b3);
+ let v01 = p1.sub(p0).mul(b1);
+ let v02 = p2.sub(p0).mul(b2);
+ let v03 = p3.sub(p0).mul(b3);
v01.add(&v02).add(&v03).mul(1.0 / 6.0)
}
}
/// Returns the curve segment and local *t* value corresponding to
/// the global *t* value of a Catmull–Rom or B-spline.
+#[inline]
fn crb_segment(pts: &[T], t: f32) -> (f32, &[T; 4]) {
let t = 1.0 + t * (pts.len() as f32 - 3.0);
@@ -742,6 +761,7 @@ impl Euclidean {
/// Returns the point of `self` at distance *s* from the start,
/// as measured along the curve.
+ #[inline]
pub fn eval(&self, s: f32) -> T
where
Spl: Parametric,
@@ -780,6 +800,7 @@ impl Parametric for CubicBezier
where
T: Affine + Clone> + Clone,
{
+ #[inline]
fn eval(&self, t: f32) -> T {
self.fast_eval(t)
}
@@ -789,6 +810,7 @@ impl Parametric for CubicHermite
where
T: Affine + Clone> + Clone,
{
+ #[inline]
fn eval(&self, t: f32) -> T {
self.eval(t)
}
@@ -798,6 +820,7 @@ impl Parametric for BezierSpline
where
T: Affine + Clone> + Clone,
{
+ #[inline]
fn eval(&self, t: f32) -> T {
self.eval(t)
}
@@ -807,6 +830,7 @@ impl Parametric for HermiteSpline
where
T: Affine + Clone> + Clone,
{
+ #[inline]
fn eval(&self, t: f32) -> T {
self.eval(t)
}
@@ -816,6 +840,7 @@ impl Parametric for CatmullRomSpline
where
T: Affine + Clone> + Clone,
{
+ #[inline]
fn eval(&self, t: f32) -> T {
self.eval(t)
}
@@ -825,6 +850,7 @@ impl Parametric for BSpline
where
T: Affine + Clone> + Clone,
{
+ #[inline]
fn eval(&self, t: f32) -> T {
self.eval(t)
}
@@ -834,6 +860,7 @@ impl Parametric for Euclidean
where
Spl: Parametric,
{
+ #[inline]
fn eval(&self, s: f32) -> T {
self.eval(s)
}
diff --git a/core/src/math/vary.rs b/core/src/math/vary.rs
index ec9fe73a..9a57b8b3 100644
--- a/core/src/math/vary.rs
+++ b/core/src/math/vary.rs
@@ -8,8 +8,22 @@ use core::mem;
use super::Lerp;
pub trait ZDiv: Sized {
+ /// Performs a z-division.
+ ///
+ /// The default implementation calls [`z_div_recip`][Self::z_div_recip]
+ /// with the reciprocal of `z`.
+ #[must_use]
+ fn z_div(self, z: f32) -> Self {
+ self.z_div_recip(z.recip())
+ }
+ /// Performs a z-division by multiplying by the reciprocal of z.
+ ///
+ /// This method should be preferred to [`z_div`][Self::z_div] when the same
+ /// (reciprocal) z can be reused for several z-divisions.
+ ///
+ /// The default implementation is a no-op and simply returns `self`.
#[must_use]
- fn z_div(self, _z: f32) -> Self {
+ fn z_div_recip(self, _recip_z: f32) -> Self {
self
}
}
@@ -127,15 +141,15 @@ impl Vary for (T, U) {
}
impl ZDiv for (T, U) {
#[inline]
- fn z_div(self, z: f32) -> Self {
- (self.0.z_div(z), self.1.z_div(z))
+ fn z_div_recip(self, recip_z: f32) -> Self {
+ (self.0.z_div_recip(recip_z), self.1.z_div_recip(recip_z))
}
}
impl ZDiv for f32 {
#[inline]
- fn z_div(self, z: f32) -> Self {
- self / z
+ fn z_div_recip(self, recip_z: f32) -> Self {
+ self * recip_z
}
}
diff --git a/core/src/math/vec.rs b/core/src/math/vec.rs
index b0067e21..048b8a3c 100644
--- a/core/src/math/vec.rs
+++ b/core/src/math/vec.rs
@@ -1,13 +1,9 @@
//! Real and projective vectors.
//!
//! TODO
+//!
+//!
-use super::{
- Affine, ApproxEq, Linear, Point,
- space::{Proj3, Real},
- vary::ZDiv,
-};
-use crate::math::space::Hom;
use core::{
array,
fmt::{Debug, Formatter},
@@ -17,15 +13,21 @@ use core::{
ops::{AddAssign, DivAssign, MulAssign, SubAssign},
};
+use super::{
+ Affine, ApproxEq, Linear, Point,
+ space::{Hom, Proj3, Real},
+ vary::ZDiv,
+};
+
//
// Types
//
/// A generic vector type. Represents an element of a vector space.
///
-/// or a module,
-/// a generalization of a vector space where the scalars can be integers
-/// (technically, the scalar type can be any *ring*-like type).
+// or a module,
+// a generalization of a vector space where the scalars can be integers
+// (technically, the scalar type can be any *ring*-like type).
///
/// # Type parameters
/// * `Repr`: Representation of the scalar components of the vector,
@@ -313,6 +315,7 @@ where
///
/// assert_eq!(v.reflect(axis), vec3(2.0, 3.0, 1.0));
/// ```
+ #[must_use]
pub fn reflect(self, axis: Self) -> Self
where
Sc: Div,
@@ -356,7 +359,7 @@ where
if !a.mul(y).approx_eq(&b.mul(x)) {
return false;
}
- (x, y) = (a, b)
+ (x, y) = (a, b);
}
true
}
@@ -668,9 +671,9 @@ where
Sc: ZDiv + Copy,
{
#[inline]
- fn z_div(mut self, z: f32) -> Self {
+ fn z_div_recip(mut self, recip_z: f32) -> Self {
for c in &mut self.0 {
- *c = c.z_div(z);
+ *c = c.z_div_recip(recip_z);
}
self
}
@@ -695,6 +698,7 @@ impl ApproxEq for Vector<[Sc; N], Sp> {
impl Copy for Vector {}
impl Clone for Vector {
+ #[inline]
fn clone(&self) -> Self {
Self::new(self.0.clone())
}
@@ -702,6 +706,7 @@ impl Clone for Vector {
// Limited to Cartesian vectors because Spherical/PolarVec have own impl
impl Default for Vector> {
+ #[inline]
fn default() -> Self {
Self::new(R::default())
}
@@ -710,6 +715,7 @@ impl Default for Vector> {
impl Eq for Vector {}
impl PartialEq for Vector {
+ #[inline]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
diff --git a/core/src/render.rs b/core/src/render.rs
index f6d8c6f0..806a0342 100644
--- a/core/src/render.rs
+++ b/core/src/render.rs
@@ -52,7 +52,7 @@ pub mod text;
/// Renderable geometric primitive.
pub trait Render {
- /// The type of this primitive in clip space
+ /// The type of this primitive in clip space.
type Clip;
/// The type for which `Clip` is implemented.
diff --git a/core/src/render/batch.rs b/core/src/render/batch.rs
index bf917455..10da4af9 100644
--- a/core/src/render/batch.rs
+++ b/core/src/render/batch.rs
@@ -153,7 +153,7 @@ impl Batch, Vtx, Uni, Shd, Tgt, Ctx> {
let prims = prims.into_iter().map(|e| Edge(e.0 + n, e.1 + n));
self.verts.extend(verts);
- self.prims.extend(prims)
+ self.prims.extend(prims);
}
}
diff --git a/core/src/render/cam.rs b/core/src/render/cam.rs
index c3fc9aa7..2c1c7c81 100644
--- a/core/src/render/cam.rs
+++ b/core/src/render/cam.rs
@@ -154,6 +154,7 @@ impl Camera<()> {
}
/// Sets the world-to-view transform of this camera.
+ #[must_use]
pub fn transform(self, tf: T) -> Camera {
let Self { dims, project, viewport, .. } = self;
Camera {
@@ -167,6 +168,7 @@ impl Camera<()> {
impl Camera {
/// Sets the viewport bounds of this camera.
+ #[must_use]
pub fn viewport(self, bounds: impl Into>) -> Self {
let (w, h) = self.dims;
@@ -196,6 +198,7 @@ impl Camera {
/// # Panics
/// * If any parameter value is non-positive.
/// * If `near_far` is an empty range.
+ #[must_use]
pub fn perspective(mut self, fov: Fov, near_far: Range) -> Self {
let aspect = self.dims.0 as f32 / self.dims.1 as f32;
@@ -204,6 +207,7 @@ impl Camera {
}
/// Sets up orthographic projection.
+ #[must_use]
pub fn orthographic(mut self, bounds: Range) -> Self {
self.project = orthographic(bounds.start, bounds.end);
self
@@ -226,6 +230,7 @@ impl Camera {
}
/// Renders the given geometry from the viewpoint of this camera.
+ #[allow(clippy::too_many_arguments)]
pub fn render(
&self,
prims: impl AsRef<[Prim]>,
@@ -371,9 +376,8 @@ impl PitchYawRoll {
/// Adjusts the orientation of the camera by the given delta angles.
pub fn rotate(&mut self, pitch: Angle, yaw: Angle, roll: Angle) {
- self.orient = self
- .orient
- .compose(&rotate_pyr(pitch, yaw, roll).to())
+ let rot = rotate_pyr(pitch, yaw, roll);
+ self.orient = self.orient.compose(&rot.to());
}
/// Sets the orientation of the camera to the given **world-space** angles.
diff --git a/core/src/render/clip.rs b/core/src/render/clip.rs
index 2b64f3ff..75853a95 100644
--- a/core/src/render/clip.rs
+++ b/core/src/render/clip.rs
@@ -76,6 +76,7 @@ pub struct ClipPlane(ClipVec, u8);
impl ClipPlane {
/// Creates a clip plane given a normal, offset, and outcode bit.
+ #[inline]
const fn new(x: f32, y: f32, z: f32, off: f32, bit: u8) -> Self {
Self(ClipVec::new([x, y, z, -off]), bit)
}
@@ -443,7 +444,7 @@ mod tests {
// 32 16 8 4 2 1
// Outside near == 1
- assert_eq!(outcode(&vec(0.0, 0.0, -1.5)), 0b00_0_01);
+ assert_eq!(outcode(&vec(0.0, 0.0, -1.5)), 0b00_00_01);
// Outside right == 8
assert_eq!(outcode(&vec(2.0, 0.0, 0.0)), 0b00_10_00);
// Outside bottom == 16
@@ -701,7 +702,7 @@ mod tests {
in_degen += is_degenerate(&tr) as u32;
out_tris[res.len()] += 1;
out_total += res.len();
- out_degen += res.iter().filter(|t| is_degenerate(t)).count()
+ out_degen += res.iter().filter(|t| is_degenerate(t)).count();
}
#[cfg(feature = "std")]
{
diff --git a/core/src/render/ctx.rs b/core/src/render/ctx.rs
index f3ed5fcb..49f3894b 100644
--- a/core/src/render/ctx.rs
+++ b/core/src/render/ctx.rs
@@ -94,8 +94,8 @@ impl Context {
#[inline]
pub fn face_cull(&self, is_backface: bool) -> bool {
match self.face_cull {
- Some(FaceCull::Back) if is_backface => true,
- Some(FaceCull::Front) if !is_backface => true,
+ Some(FaceCull::Back) => is_backface,
+ Some(FaceCull::Front) => !is_backface,
_ => false,
}
}
diff --git a/core/src/render/debug.rs b/core/src/render/debug.rs
index b1b6646a..fea2f437 100644
--- a/core/src/render/debug.rs
+++ b/core/src/render/debug.rs
@@ -60,7 +60,7 @@ pub fn dir_to_rgb(v: Vec3) -> Color4f {
}
/// Draws an illustration of a ray.
-pub fn ray<'a, B>(o: Point3, dir: Vec3) -> DbgBatch {
+pub fn ray(o: Point3, dir: Vec3) -> DbgBatch {
let mut b = dir.cross(&Vec3::Y);
if b.len_sqr() < 1e-6 {
b = dir.cross(&Vec3::X);
@@ -88,7 +88,7 @@ pub fn ray<'a, B>(o: Point3, dir: Vec3) -> DbgBatch {
///
/// The ray originates from the triangle's centroid.
pub fn face_normal(
- tri: Tri>,
+ tri: &Tri>,
) -> DbgBatch {
ray(tri.centroid(), tri.normal().to())
}
diff --git a/core/src/render/prim.rs b/core/src/render/prim.rs
index a510be9a..e25caf89 100644
--- a/core/src/render/prim.rs
+++ b/core/src/render/prim.rs
@@ -14,25 +14,26 @@ impl Render for Tri {
type Clips = [Tri>];
type Screen = Tri>;
+ #[inline]
fn inline(tri: Self, vs: &[ClipVert]) -> Tri> {
tri.map(|i| vs[i].clone())
}
-
+ #[inline]
fn depth(Tri([a, b, c]): &Self::Clip) -> f32 {
(a.pos.z() + b.pos.z() + c.pos.z()) / 3.0
}
-
+ #[inline]
fn is_backface(tri: &Self::Screen) -> bool {
tri.winding() == Winding::Cw
}
-
+ #[inline]
fn to_screen(
clip: Tri>,
tf: &Mat4,
) -> Self::Screen {
Tri(to_screen(clip.0, tf))
}
-
+ #[inline]
fn rasterize)>(scr: Self::Screen, scanline_fn: F) {
tri_fill(scr.0, scanline_fn);
}
@@ -45,26 +46,29 @@ impl Render for Edge {
type Screen = Edge>;
+ #[inline]
fn inline(Edge(i, j): Edge, vs: &[ClipVert]) -> Self::Clip {
Edge(vs[i].clone(), vs[j].clone())
}
-
+ #[inline]
fn to_screen(e: Self::Clip, tf: &Mat4) -> Self::Screen {
let [a, b] = to_screen([e.0, e.1], tf);
Edge(a, b)
}
-
+ #[inline]
fn rasterize)>(e: Self::Screen, scanline_fn: F) {
line([e.0, e.1], scanline_fn);
}
}
+/// A helper function to perform z-division and viewport transform for a list
+/// of clip-space vertices.
+#[inline]
pub fn to_screen(
vs: [ClipVert; N],
tf: &Mat4,
) -> [Vertex; N] {
vs.map(|v| {
- let [x, y, _, w] = v.pos.0;
// Perspective division (projection to the real plane)
//
// We use the screen-space z coordinate to store the reciprocal
@@ -77,12 +81,14 @@ pub fn to_screen(
// Vec3 was used here, which only worked because apply used to use
// w=1 for vectors. Fixing it made viewport transform incorrect.
// The z-div concept and trait likely need clarification.
- let pos = pt3(x, y, 1.0).z_div(w);
+ let [x, y, _, w] = v.pos.0;
+ let recip_w = w.recip();
+ let pos = pt3(x, y, 1.0).z_div_recip(recip_w);
Vertex {
// Viewport transform
pos: tf.apply(&pos),
// Perspective correction
- attrib: v.attrib.z_div(w),
+ attrib: v.attrib.z_div_recip(recip_w),
}
})
}
diff --git a/core/src/render/scene.rs b/core/src/render/scene.rs
index 85edb5da..261e2251 100644
--- a/core/src/render/scene.rs
+++ b/core/src/render/scene.rs
@@ -26,6 +26,8 @@ impl Obj {
pub fn new(geom: Mesh) -> Self {
Self::with_transform(geom, Mat4::identity())
}
+
+ #[must_use]
pub fn with_transform(geom: Mesh, tf: Mat4) -> Self {
let bbox = BBox::of(&geom);
Self { geom, bbox, tf }
diff --git a/core/src/render/shader.rs b/core/src/render/shader.rs
index e67e3f99..b2560654 100644
--- a/core/src/render/shader.rs
+++ b/core/src/render/shader.rs
@@ -60,6 +60,7 @@ where
{
type Output = Out;
+ #[inline]
fn shade_vertex(&self, vertex: In, uniform: Uni) -> Out {
self(vertex, uniform)
}
@@ -70,6 +71,7 @@ where
F: Fn(Frag) -> Out,
Out: Into