Skip to content

Commit

Permalink
Merge pull request #28 from rust3ds/feature/more-math
Browse files Browse the repository at this point in the history
More math types and operator overloads
  • Loading branch information
ian-h-chamberlain authored Nov 21, 2023
2 parents 6ce6c96 + e1bba06 commit c4ae496
Show file tree
Hide file tree
Showing 12 changed files with 977 additions and 197 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ on:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:

jobs:
lint:
strategy:
fail-fast: false
matrix:
toolchain:
# Run against a "known good" nightly. Rustc version is 1 day behind the toolchain date
Expand Down Expand Up @@ -48,6 +47,7 @@ jobs:

test:
strategy:
fail-fast: false
matrix:
toolchain:
- nightly-2023-06-01
Expand Down
3 changes: 3 additions & 0 deletions citro3d-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ ctru-sys = { git = "https://github.com/rust3ds/ctru-rs.git" }
bindgen = { version = "0.68.1", features = ["experimental"] }
cc = "1.0.83"
doxygen-rs = "0.4.2"

[dev-dependencies]
shim-3ds = { git = "https://github.com/rust3ds/shim-3ds.git" }
4 changes: 4 additions & 0 deletions citro3d-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

pub mod gx;
pub use gx::*;

// Prevent linking errors from the standard `test` library when running `cargo 3ds test --lib`.
#[cfg(test)]
extern crate shim_3ds;
21 changes: 20 additions & 1 deletion citro3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,32 @@ version = "0.1.0"
edition = "2021"

[dependencies]
citro3d-macros = { version = "0.1.0", path = "../citro3d-macros" }
approx = { version = "0.5.1", optional = true }
bitflags = "1.3.2"
bytemuck = { version = "1.10.0", features = ["extern_crate_std"] }
citro3d-macros = { version = "0.1.0", path = "../citro3d-macros" }
citro3d-sys = { git = "https://github.com/rust3ds/citro3d-rs.git" }
ctru-rs = { git = "https://github.com/rust3ds/ctru-rs.git" }
ctru-sys = { git = "https://github.com/rust3ds/ctru-rs.git" }
document-features = "0.2.7"
libc = "0.2.125"

[features]
default = []
## Enable this feature to use the `approx` crate for comparing vectors and matrices.
approx = ["dep:approx"]

[dev-dependencies]
test-runner = { git = "https://github.com/rust3ds/test-runner.git" }

[dev-dependencies.citro3d]
# Basically, this works like `cargo 3ds test --features ...` for building tests
# https://github.com/rust-lang/cargo/issues/2911#issuecomment-749580481
path = "."
features = ["approx"]

[package.metadata.docs.rs]
all-features = true
default-target = "armv6k-nintendo-3ds"
targs = []
cargo-args = ["-Z", "build-std"]
8 changes: 4 additions & 4 deletions citro3d/examples/triangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#![feature(allocator_api)]

use citro3d::macros::include_shader;
use citro3d::math::{AspectRatio, ClipPlanes, Matrix, Projection, StereoDisplacement};
use citro3d::math::{AspectRatio, ClipPlanes, Matrix4, Projection, StereoDisplacement};
use citro3d::render::ClearFlags;
use citro3d::{attrib, buffer, render, shader};
use ctru::prelude::*;
Expand Down Expand Up @@ -158,9 +158,9 @@ where
}

struct Projections {
left_eye: Matrix,
right_eye: Matrix,
center: Matrix,
left_eye: Matrix4,
right_eye: Matrix4,
center: Matrix4,
}

fn calculate_projections() -> Projections {
Expand Down
9 changes: 8 additions & 1 deletion citro3d/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
//! Safe Rust bindings to `citro3d`.
#![feature(custom_test_frameworks)]
#![test_runner(test_runner::run_gdb)]
#![feature(doc_cfg)]
#![feature(doc_auto_cfg)]

//! Safe Rust bindings to `citro3d`. This crate wraps `citro3d-sys` to provide
//! safer APIs for graphics programs targeting the 3DS.
//!
//! ## Feature flags
#![doc = document_features::document_features!()]

pub mod attrib;
pub mod buffer;
Expand Down
183 changes: 11 additions & 172 deletions citro3d/src/math.rs
Original file line number Diff line number Diff line change
@@ -1,185 +1,24 @@
//! Safe wrappers for working with matrix and vector types provided by `citro3d`.
use std::mem::MaybeUninit;
// TODO: bench FFI calls into `inline statics` generated by bindgen, vs
// reimplementing some of those calls. Many of them are pretty trivial impls

mod fvec;
mod matrix;
mod ops;
mod projection;

pub use projection::{Orthographic, Perspective, Projection};
pub use fvec::{FVec, FVec3, FVec4};
pub use matrix::{Matrix, Matrix3, Matrix4};
pub use projection::{
AspectRatio, ClipPlanes, CoordinateOrientation, Orthographic, Perspective, Projection,
ScreenOrientation, StereoDisplacement,
};

/// A 4-vector of `u8`s.
#[doc(alias = "C3D_IVec")]
pub struct IVec(citro3d_sys::C3D_IVec);

/// A 4-vector of `f32`s.
#[doc(alias = "C3D_FVec")]
pub struct FVec(citro3d_sys::C3D_FVec);

/// A quaternion, internally represented the same way as [`FVec`].
#[doc(alias = "C3D_FQuat")]
pub struct FQuat(citro3d_sys::C3D_FQuat);

/// A 4x4 row-major matrix of `f32`s.
#[doc(alias = "C3D_Mtx")]
pub struct Matrix(citro3d_sys::C3D_Mtx);

impl Matrix {
/// Construct the zero matrix.
#[doc(alias = "Mtx_Zeros")]
pub fn zero() -> Self {
// TODO: should this also be Default::default()?
let mut out = MaybeUninit::uninit();
unsafe {
citro3d_sys::Mtx_Zeros(out.as_mut_ptr());
Self(out.assume_init())
}
}

/// Construct the identity matrix.
#[doc(alias = "Mtx_Identity")]
pub fn identity() -> Self {
let mut out = MaybeUninit::uninit();
unsafe {
citro3d_sys::Mtx_Identity(out.as_mut_ptr());
Self(out.assume_init())
}
}

pub(crate) fn as_raw(&self) -> *const citro3d_sys::C3D_Mtx {
&self.0
}
}

// region: Projection configuration
//
// TODO: maybe move into `mod projection`, or hoist `projection::*` into here.
// it will probably mostly depend on how big all the matrices/vec impls get.
// Also worth considering is whether `mod projection` should be pub.

/// The [orientation](https://en.wikipedia.org/wiki/Orientation_(geometry))
/// (or "handedness") of the coordinate system. Coordinates are always +Y-up,
/// +X-right.
#[derive(Clone, Copy, Debug)]
pub enum CoordinateOrientation {
/// A left-handed coordinate system. +Z points into the screen.
LeftHanded,
/// A right-handed coordinate system. +Z points out of the screen.
RightHanded,
}

impl CoordinateOrientation {
pub(crate) fn is_left_handed(self) -> bool {
matches!(self, Self::LeftHanded)
}
}

impl Default for CoordinateOrientation {
/// This is an opinionated default, but [`RightHanded`](Self::RightHanded)
/// seems to be the preferred coordinate system for most
/// [examples](https://github.com/devkitPro/3ds-examples)
/// from upstream, and is also fairly common in other applications.
fn default() -> Self {
Self::RightHanded
}
}

/// Whether to rotate a projection to account for the 3DS screen orientation.
/// Both screens on the 3DS are oriented such that the "top-left" of the screen
/// in framebuffer coordinates is the physical bottom-left of the screen
/// (i.e. the "width" is smaller than the "height").
#[derive(Clone, Copy, Debug)]
pub enum ScreenOrientation {
/// Rotate 90° clockwise to account for the 3DS screen rotation. Most
/// applications will use this variant.
Rotated,
/// Do not apply any extra rotation to the projection.
None,
}

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

/// Configuration for calculating stereoscopic projections.
// TODO: not totally happy with this name + API yet, but it works for now.
#[derive(Clone, Copy, Debug)]
pub struct StereoDisplacement {
/// The horizontal offset of the eye from center. Negative values
/// correspond to the left eye, and positive values to the right eye.
pub displacement: f32,
/// The position of the screen, which determines the focal length. Objects
/// closer than this depth will appear to pop out of the screen, and objects
/// further than this will appear inside the screen.
pub screen_depth: f32,
}

impl StereoDisplacement {
/// Construct displacement for the left and right eyes simulataneously.
/// The given `interocular_distance` describes the distance between the two
/// rendered "eyes". A negative value will be treated the same as a positive
/// value of the same magnitude.
///
/// See struct documentation for details about the
/// [`screen_depth`](Self::screen_depth) parameter.
pub fn new(interocular_distance: f32, screen_depth: f32) -> (Self, Self) {
let displacement = interocular_distance.abs() / 2.0;

let left_eye = Self {
displacement: -displacement,
screen_depth,
};
let right_eye = Self {
displacement,
screen_depth,
};

(left_eye, right_eye)
}
}

/// Configuration for the clipping planes of a projection.
///
/// For [`Perspective`] projections, this is used for the near and far clip planes
/// of the [view frustum](https://en.wikipedia.org/wiki/Viewing_frustum).
///
/// For [`Orthographic`] projections, this is used for the Z clipping planes of
/// the projection.
///
/// Note that the `near` value should always be less than `far`, regardless of
/// [`CoordinateOrientation`]. In other words, these values will be negated
/// when used with a [`RightHanded`](CoordinateOrientation::RightHanded)
/// orientation.
#[derive(Clone, Copy, Debug)]
pub struct ClipPlanes {
/// The Z-depth of the near clip plane, usually close or equal to zero.
pub near: f32,
/// The Z-depth of the far clip plane, usually greater than zero.
pub far: f32,
}

/// The aspect ratio of a projection plane.
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum AspectRatio {
/// The aspect ratio of the 3DS' top screen (per-eye).
#[doc(alias = "C3D_AspectRatioTop")]
TopScreen,
/// The aspect ratio of the 3DS' bottom screen.
#[doc(alias = "C3D_AspectRatioBot")]
BottomScreen,
/// A custom aspect ratio (should be calcualted as `width / height`).
Other(f32),
}

impl From<AspectRatio> for f32 {
fn from(ratio: AspectRatio) -> Self {
match ratio {
AspectRatio::TopScreen => citro3d_sys::C3D_AspectRatioTop as f32,
AspectRatio::BottomScreen => citro3d_sys::C3D_AspectRatioBot as f32,
AspectRatio::Other(ratio) => ratio,
}
}
}

// endregion
Loading

0 comments on commit c4ae496

Please sign in to comment.