mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 14:10:19 +00:00
bytes: AsBytes trait, remove zerocopy, remove glam fork
This commit is contained in:
parent
d5d0107ada
commit
5bcd594cb4
78 changed files with 407 additions and 13071 deletions
|
@ -59,7 +59,7 @@ bevy_window = { path = "crates/bevy_window", optional = true }
|
||||||
bevy_wgpu = { path = "crates/bevy_wgpu", optional = true }
|
bevy_wgpu = { path = "crates/bevy_wgpu", optional = true }
|
||||||
bevy_winit = { path = "crates/bevy_winit", optional = true }
|
bevy_winit = { path = "crates/bevy_winit", optional = true }
|
||||||
legion = { path = "crates/bevy_legion" }
|
legion = { path = "crates/bevy_legion" }
|
||||||
glam = { path = "crates/bevy_glam" }
|
glam = "0.8.7"
|
||||||
# other
|
# other
|
||||||
log = { version = "0.4", features = ["release_max_level_info"] }
|
log = { version = "0.4", features = ["release_max_level_info"] }
|
||||||
|
|
||||||
|
@ -79,6 +79,10 @@ path = "examples/hello_world.rs"
|
||||||
name = "sprite"
|
name = "sprite"
|
||||||
path = "examples/2d/sprite.rs"
|
path = "examples/2d/sprite.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "sprite_sheet"
|
||||||
|
path = "examples/2d/sprite_sheet.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "load_model"
|
name = "load_model"
|
||||||
path = "examples/3d/load_model.rs"
|
path = "examples/3d/load_model.rs"
|
||||||
|
|
|
@ -9,5 +9,4 @@ bevy_app = { path = "../bevy_app" }
|
||||||
bevy_type_registry = { path = "../bevy_type_registry" }
|
bevy_type_registry = { path = "../bevy_type_registry" }
|
||||||
bevy_transform = { path = "../bevy_transform" }
|
bevy_transform = { path = "../bevy_transform" }
|
||||||
legion = { path = "../bevy_legion" }
|
legion = { path = "../bevy_legion" }
|
||||||
glam = { path = "../bevy_glam" }
|
glam = "0.8.7"
|
||||||
zerocopy = "0.3"
|
|
|
@ -1,82 +1,71 @@
|
||||||
use glam::{Mat4, Vec2, Vec3, Vec4};
|
use glam::{Mat4, Vec2, Vec3, Vec4};
|
||||||
use zerocopy::AsBytes;
|
|
||||||
|
|
||||||
macro_rules! impl_bytes_zerocopy {
|
|
||||||
($ty:tt) => {
|
|
||||||
impl Bytes for $ty {
|
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
|
||||||
buffer[0..self.byte_len()].copy_from_slice(self.as_bytes())
|
|
||||||
}
|
|
||||||
fn byte_len(&self) -> usize {
|
|
||||||
std::mem::size_of::<Self>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Bytes {
|
pub trait Bytes {
|
||||||
fn write_bytes(&self, buffer: &mut [u8]);
|
fn write_bytes(&self, buffer: &mut [u8]);
|
||||||
fn byte_len(&self) -> usize;
|
fn byte_len(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_bytes_zerocopy!(u8);
|
pub unsafe trait Byteable where Self: Sized {}
|
||||||
impl_bytes_zerocopy!(u16);
|
|
||||||
impl_bytes_zerocopy!(u32);
|
|
||||||
impl_bytes_zerocopy!(u64);
|
|
||||||
impl_bytes_zerocopy!(usize);
|
|
||||||
impl_bytes_zerocopy!(i8);
|
|
||||||
impl_bytes_zerocopy!(i16);
|
|
||||||
impl_bytes_zerocopy!(i32);
|
|
||||||
impl_bytes_zerocopy!(i64);
|
|
||||||
impl_bytes_zerocopy!(isize);
|
|
||||||
impl_bytes_zerocopy!(f32);
|
|
||||||
impl_bytes_zerocopy!(f64);
|
|
||||||
|
|
||||||
impl_bytes_zerocopy!([u8; 2]);
|
impl<T> Bytes for T
|
||||||
impl_bytes_zerocopy!([u16; 2]);
|
where
|
||||||
impl_bytes_zerocopy!([u32; 2]);
|
T: Byteable,
|
||||||
impl_bytes_zerocopy!([u64; 2]);
|
{
|
||||||
impl_bytes_zerocopy!([usize; 2]);
|
fn write_bytes(&self, buffer: &mut [u8]) {
|
||||||
impl_bytes_zerocopy!([i8; 2]);
|
let bytes = self.as_bytes();
|
||||||
impl_bytes_zerocopy!([i16; 2]);
|
buffer[0..self.byte_len()].copy_from_slice(bytes)
|
||||||
impl_bytes_zerocopy!([i32; 2]);
|
}
|
||||||
impl_bytes_zerocopy!([i64; 2]);
|
fn byte_len(&self) -> usize {
|
||||||
impl_bytes_zerocopy!([isize; 2]);
|
std::mem::size_of::<Self>()
|
||||||
impl_bytes_zerocopy!([f32; 2]);
|
}
|
||||||
impl_bytes_zerocopy!([f64; 2]);
|
}
|
||||||
|
|
||||||
impl_bytes_zerocopy!([u8; 3]);
|
pub trait AsBytes {
|
||||||
impl_bytes_zerocopy!([u16; 3]);
|
fn as_bytes(&self) -> &[u8];
|
||||||
impl_bytes_zerocopy!([u32; 3]);
|
}
|
||||||
impl_bytes_zerocopy!([u64; 3]);
|
|
||||||
impl_bytes_zerocopy!([usize; 3]);
|
|
||||||
impl_bytes_zerocopy!([i8; 3]);
|
|
||||||
impl_bytes_zerocopy!([i16; 3]);
|
|
||||||
impl_bytes_zerocopy!([i32; 3]);
|
|
||||||
impl_bytes_zerocopy!([i64; 3]);
|
|
||||||
impl_bytes_zerocopy!([isize; 3]);
|
|
||||||
impl_bytes_zerocopy!([f32; 3]);
|
|
||||||
impl_bytes_zerocopy!([f64; 3]);
|
|
||||||
|
|
||||||
impl_bytes_zerocopy!([u8; 4]);
|
impl<T> AsBytes for T
|
||||||
impl_bytes_zerocopy!([u16; 4]);
|
where
|
||||||
impl_bytes_zerocopy!([u32; 4]);
|
T: Byteable,
|
||||||
impl_bytes_zerocopy!([u64; 4]);
|
{
|
||||||
impl_bytes_zerocopy!([usize; 4]);
|
fn as_bytes(&self) -> &[u8] {
|
||||||
impl_bytes_zerocopy!([i8; 4]);
|
let len = std::mem::size_of_val(self);
|
||||||
impl_bytes_zerocopy!([i16; 4]);
|
unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, len) }
|
||||||
impl_bytes_zerocopy!([i32; 4]);
|
}
|
||||||
impl_bytes_zerocopy!([i64; 4]);
|
}
|
||||||
impl_bytes_zerocopy!([isize; 4]);
|
|
||||||
impl_bytes_zerocopy!([f32; 4]);
|
|
||||||
impl_bytes_zerocopy!([f64; 4]);
|
|
||||||
|
|
||||||
|
impl<'a, T> AsBytes for [T]
|
||||||
|
where
|
||||||
|
T: Byteable,
|
||||||
|
{
|
||||||
|
fn as_bytes(&self) -> &[u8] {
|
||||||
|
let len = std::mem::size_of_val(self);
|
||||||
|
unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, len) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T> Byteable for [T] where Self: Sized {}
|
||||||
|
unsafe impl<T> Byteable for [T; 2] where T: Byteable {}
|
||||||
|
unsafe impl<T> Byteable for [T; 3] where T: Byteable {}
|
||||||
|
unsafe impl<T> Byteable for [T; 4] where T: Byteable {}
|
||||||
|
unsafe impl<T> Byteable for [T; 16] where T: Byteable {}
|
||||||
|
|
||||||
|
unsafe impl Byteable for u8 {}
|
||||||
|
unsafe impl Byteable for u16 {}
|
||||||
|
unsafe impl Byteable for u32 {}
|
||||||
|
unsafe impl Byteable for u64 {}
|
||||||
|
unsafe impl Byteable for usize {}
|
||||||
|
unsafe impl Byteable for i8 {}
|
||||||
|
unsafe impl Byteable for i16 {}
|
||||||
|
unsafe impl Byteable for i32 {}
|
||||||
|
unsafe impl Byteable for i64 {}
|
||||||
|
unsafe impl Byteable for isize {}
|
||||||
|
unsafe impl Byteable for f32 {}
|
||||||
|
unsafe impl Byteable for f64 {}
|
||||||
|
|
||||||
impl Bytes for Vec2 {
|
impl Bytes for Vec2 {
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
fn write_bytes(&self, buffer: &mut [u8]) {
|
||||||
let array: [f32; 2] = (*self).into();
|
let array: [f32; 2] = (*self).into();
|
||||||
buffer[0..self.byte_len()].copy_from_slice(array.as_bytes())
|
array.write_bytes(buffer);
|
||||||
}
|
}
|
||||||
fn byte_len(&self) -> usize {
|
fn byte_len(&self) -> usize {
|
||||||
std::mem::size_of::<Self>()
|
std::mem::size_of::<Self>()
|
||||||
|
@ -86,7 +75,7 @@ impl Bytes for Vec2 {
|
||||||
impl Bytes for Vec3 {
|
impl Bytes for Vec3 {
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
fn write_bytes(&self, buffer: &mut [u8]) {
|
||||||
let array: [f32; 3] = (*self).into();
|
let array: [f32; 3] = (*self).into();
|
||||||
buffer[0..self.byte_len()].copy_from_slice(array.as_bytes())
|
array.write_bytes(buffer);
|
||||||
}
|
}
|
||||||
fn byte_len(&self) -> usize {
|
fn byte_len(&self) -> usize {
|
||||||
std::mem::size_of::<Self>()
|
std::mem::size_of::<Self>()
|
||||||
|
@ -96,7 +85,7 @@ impl Bytes for Vec3 {
|
||||||
impl Bytes for Vec4 {
|
impl Bytes for Vec4 {
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
fn write_bytes(&self, buffer: &mut [u8]) {
|
||||||
let array: [f32; 4] = (*self).into();
|
let array: [f32; 4] = (*self).into();
|
||||||
buffer[0..self.byte_len()].copy_from_slice(array.as_bytes())
|
array.write_bytes(buffer);
|
||||||
}
|
}
|
||||||
fn byte_len(&self) -> usize {
|
fn byte_len(&self) -> usize {
|
||||||
std::mem::size_of::<Self>()
|
std::mem::size_of::<Self>()
|
||||||
|
@ -105,7 +94,8 @@ impl Bytes for Vec4 {
|
||||||
|
|
||||||
impl Bytes for Mat4 {
|
impl Bytes for Mat4 {
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
fn write_bytes(&self, buffer: &mut [u8]) {
|
||||||
buffer[0..self.byte_len()].copy_from_slice(self.to_cols_array().as_bytes())
|
let array = self.to_cols_array();
|
||||||
|
array.write_bytes(buffer);
|
||||||
}
|
}
|
||||||
fn byte_len(&self) -> usize {
|
fn byte_len(&self) -> usize {
|
||||||
std::mem::size_of::<Self>()
|
std::mem::size_of::<Self>()
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"git": {
|
|
||||||
"sha1": "9690d383cfa7d720706d66b25ec862fa6100adf5"
|
|
||||||
}
|
|
||||||
}
|
|
8
crates/bevy_glam/.github/workflows/CI.yml
vendored
8
crates/bevy_glam/.github/workflows/CI.yml
vendored
|
@ -1,8 +0,0 @@
|
||||||
name: CI
|
|
||||||
on: [push, pull_request]
|
|
||||||
jobs:
|
|
||||||
cargo-deny:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v0
|
|
6
crates/bevy_glam/.gitignore
vendored
6
crates/bevy_glam/.gitignore
vendored
|
@ -1,6 +0,0 @@
|
||||||
/target
|
|
||||||
*.swp
|
|
||||||
**/*.rs.bk
|
|
||||||
Cargo.lock
|
|
||||||
Session.vim
|
|
||||||
.cargo-ok
|
|
|
@ -1,40 +0,0 @@
|
||||||
language: rust
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- libssl-dev
|
|
||||||
|
|
||||||
cache: cargo
|
|
||||||
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
- nightly
|
|
||||||
|
|
||||||
env:
|
|
||||||
- CARGO_FEATURES="mint rand serde debug-glam-assert transform-types"
|
|
||||||
- CARGO_FEATURES="mint rand serde packed-vec3 debug-glam-assert transform-types"
|
|
||||||
- CARGO_FEATURES="mint rand serde scalar-math debug-glam-assert transform-types"
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- rust: nightly
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
before_script: |
|
|
||||||
if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then
|
|
||||||
cargo install cargo-tarpaulin
|
|
||||||
fi
|
|
||||||
|
|
||||||
script:
|
|
||||||
- cargo clean
|
|
||||||
- cargo build --features "$CARGO_FEATURES"
|
|
||||||
- cargo test --features "$CARGO_FEATURES"
|
|
||||||
- cargo bench --features "$CARGO_FEATURES" --no-run
|
|
||||||
|
|
||||||
after_success: |
|
|
||||||
if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then
|
|
||||||
cargo tarpaulin --features "$CARGO_FEATURES" --exclude-files src/f32/transform.rs --exclude-files benches/* --exclude-files tests/support/mod.rs --ciserver travis-ci --coveralls $TRAVIS_JOB_ID
|
|
||||||
fi
|
|
|
@ -1,188 +0,0 @@
|
||||||
# Changelog
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog], and this project adheres to
|
|
||||||
[Semantic Versioning].
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
## [0.8.7] - 2020-04-28
|
|
||||||
|
|
||||||
### Added
|
|
||||||
* Added `Quat::slerp` - note that this uses a `sin` approximation.
|
|
||||||
* Added `angle_between` method for `Vec2` and `Vec3`.
|
|
||||||
* Implemented `Debug`, `Display`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`,
|
|
||||||
`Hash`, and `AsRef` traits for `Vec2Mask`, `Vec3Mask` and `Vec4Mask`.
|
|
||||||
* Added conversion functions from `Vec2Mask`, `Vec3Mask` and `Vec4Mask` to an
|
|
||||||
array of `[u32]`.
|
|
||||||
* Added `build.rs` to simplify conditional feature compilation.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* Removed `cfg-if` dependency.
|
|
||||||
* Increased test coverage.
|
|
||||||
|
|
||||||
## [0.8.6] - 2020-02-18
|
|
||||||
|
|
||||||
### Added
|
|
||||||
* Added the `packed-vec3` feature flag to disable using SIMD types for `Vec3`
|
|
||||||
and `Mat3` types. This avoids wasting some space due to 16 byte alignment at
|
|
||||||
the cost of some performance.
|
|
||||||
* Added `x_mut`, `y_mut`, `z_mut`, `w_mut` where appropriate to `Vec2`, `Vec3`
|
|
||||||
and `Vec4`.
|
|
||||||
* Added implementation of `core::ops::Index` and `core::ops::IndexMut` for
|
|
||||||
`Vec2`, `Vec3` and `Vec4`.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* Merged SSE2 and scalar `Vec3` and `Vec4` implementations into single files
|
|
||||||
using the `cfg-if` crate.
|
|
||||||
|
|
||||||
## [0.8.5] - 2020-01-02
|
|
||||||
|
|
||||||
### Added
|
|
||||||
* Added projection functions `Mat4::perspective_lh`,
|
|
||||||
`Mat4::perspective_infinite_lh`, `Mat4::perspective_infinite_reverse_lh`,
|
|
||||||
`Mat4::orthgraphic_lh` and `Mat4::orthographic_rh`.
|
|
||||||
* Added `round`, `ceil` and `floor` methods to `Vec2`, `Vec3` and `Vec4`.
|
|
||||||
|
|
||||||
## [0.8.4] - 2019-12-17
|
|
||||||
|
|
||||||
### Added
|
|
||||||
* Added `Mat4::to_scale_rotation_translation` for extracting scale, rotation and
|
|
||||||
translation from a 4x4 homogeneous transformation matrix.
|
|
||||||
* Added `cargo-deny` GitHub Action.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* Renamed `Quat::new` to `Quat::from_xyzw`.
|
|
||||||
|
|
||||||
## [0.8.3] - 2019-11-27
|
|
||||||
|
|
||||||
### Added
|
|
||||||
* Added `Mat4::orthographic_rh_gl`.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* Renamed `Mat4::perspective_glu_rh` to `Mat4::perspective_rh_gl`.
|
|
||||||
* SSE2 optimizations for `Mat2::determinant`, `Mat2::inverse`,
|
|
||||||
`Mat2::transpose`, `Mat3::transpose`, `Quat::conjugate`, `Quat::lerp`,
|
|
||||||
`Quat::mul_vec3`, `Quat::mul_quat` and `Quat::from_rotation_ypr`.
|
|
||||||
* Disabled optimizations to `Mat4::transform_point3` and
|
|
||||||
`Mat4::transform_vector3` as they are probably incorrect and need
|
|
||||||
investigating.
|
|
||||||
* Added missing `#[repr(C)]` to `Mat2`, `Mat3` and `Mat4`.
|
|
||||||
* Benchmarks now store output of functions to better estimate the cost of a
|
|
||||||
function call.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
* Removed deprecated functions `Mat2::new`, `Mat3::new` and `Mat4::new`.
|
|
||||||
|
|
||||||
## [0.8.2] - 2019-11-06
|
|
||||||
### Changed
|
|
||||||
* `glam_assert!` is no longer enabled by default in debug builds, it can be
|
|
||||||
enabled in any configuration using the `glam-assert` feature or in debug
|
|
||||||
builds only using the `debug-glam-assert` feature.
|
|
||||||
### Removed
|
|
||||||
* `glam_assert!`'s checking `lerp` is bounded between 0.0 and 1.0 and that
|
|
||||||
matrix scales are non-zero have been removed.
|
|
||||||
|
|
||||||
## [0.8.1] - 2019-11-03
|
|
||||||
### Added
|
|
||||||
* Added `Display` trait implementations for `Mat2`, `Mat3` and `Mat4`.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* Disabled `glam`'s SSE2 `sin_cos` implementation - it became less precise for
|
|
||||||
large angle values.
|
|
||||||
* Reduced the default epsilon used by the `is_normalized!` macro from
|
|
||||||
`std::f32::EPSILON` to `1e-6`.
|
|
||||||
|
|
||||||
## [0.8.0] - 2019-10-14
|
|
||||||
### Removed
|
|
||||||
* Removed the `approx` crate dependency. Each `glam` type has an `abs_diff_eq`
|
|
||||||
method added which is used by unit tests for approximate floating point
|
|
||||||
comparisons.
|
|
||||||
* Removed the `Angle` type. All angles are now `f32` and are expected to
|
|
||||||
be in radians.
|
|
||||||
* Removed the deprecated `Vec2b`, `Vec3b` and `Vec4b` types and the `mask`
|
|
||||||
methods on `Vec2Mask`, `Vec3Mask` and `Vec4Mask`.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
* The `rand` crate dependency has been removed from default features. This was
|
|
||||||
required for benchmarking but a simple random number generator has been added
|
|
||||||
to the benches `support` module instead.
|
|
||||||
* The `From` trait implementation converting between 1D and 2D `f32` arrays and
|
|
||||||
matrix types have been removed. It was ambiguous how array data would map to
|
|
||||||
matrix columns so these have been replaced with explicit methods
|
|
||||||
`from_cols_array` and `from_cols_array_2d`.
|
|
||||||
* Matrix `new` methods have been renamed to `from_cols` to be consistent with
|
|
||||||
the other methods that create matrices from data.
|
|
||||||
* Renamed `Mat4::perspective_glu` to `Mat4::perspective_glu_rh`.
|
|
||||||
|
|
||||||
## [0.7.2] - 2019-09-22
|
|
||||||
### Fixed
|
|
||||||
* Fixed incorrect projection matrix methods `Mat4::look_at_lh`
|
|
||||||
and `Mat4::look_at_rh`.
|
|
||||||
### Added
|
|
||||||
* Added support for building infinite projection matrices, including both
|
|
||||||
standard and reverse depth `Mat4::perspective_infinite_rh` and
|
|
||||||
`Mat4::perspective_infinite_rh`.
|
|
||||||
* Added `Vec2Mask::new`, `Vec3Mask::new` and `Vec4Mask::new` methods.
|
|
||||||
* Implemented `std::ops` `BitAnd`, `BitAndAssign`, `BitOr`, `BitOrAssign`
|
|
||||||
and `Not` traits for `Vec2Mask`, `Vec3Mask` and `Vec4Mask`.
|
|
||||||
* Added method documentation for `Vec4` and `Vec4Mask` types.
|
|
||||||
* Added missing `serde` implementations for `Mat2`, `Mat3` and `Mat4`.
|
|
||||||
* Updated `rand` and `criterion` versions.
|
|
||||||
|
|
||||||
## [0.7.1] - 2019-07-08
|
|
||||||
### Fixed
|
|
||||||
* The SSE2 implementation of `Vec4` `dot` was missing a shuffle, meaning the
|
|
||||||
`dot`, `length`, `length_squared`, `length_reciprocal` and `normalize`
|
|
||||||
methods were sometimes incorrect.
|
|
||||||
### Added
|
|
||||||
* Added the `glam_assert` macro which behaves like Rust's `debug_assert` but
|
|
||||||
can be enabled separately to `debug_assert`. This is used to perform
|
|
||||||
asserts on correctness.
|
|
||||||
* Added `is_normalized` method to `Vec2`, `Vec3` and `Vec4`.
|
|
||||||
### Changed
|
|
||||||
* Replaced usage of `std::mem::uninitialized` with `std::mem::MaybeUninit`. This
|
|
||||||
change requires stable Rust 1.36.
|
|
||||||
* Renamed `Vec2b` to `Vec2Mask`, `Vec3b` to `Vec3Mask` and `Vec4b` to
|
|
||||||
`Vec4Mask`. Old names are aliased to the new name and deprecated.
|
|
||||||
* Deprecate `VecNMask` `mask` method, use new `bitmask` method instead
|
|
||||||
* Made fallback version of `VecNMask` types the same size and alignment as the
|
|
||||||
SIMD versions.
|
|
||||||
* Added `Default` support to `VecNMask` types, will add more common traits in
|
|
||||||
the future.
|
|
||||||
* Added `#[inline]` to `mat2`, `mat3` and `mat4` functions.
|
|
||||||
|
|
||||||
## [0.7.0] - 2019-06-28
|
|
||||||
### Added
|
|
||||||
* Added `Mat2` into `[f32; 4]`, `Mat3` into `[f32; 9]` and `Mat4` into
|
|
||||||
`[f32; 16]`.
|
|
||||||
### Changed
|
|
||||||
* Removed `impl Mul<&Vec2> for Mat2` and `impl Mul<&Vec3> for Vec3` as these
|
|
||||||
don't exist for any other types.
|
|
||||||
|
|
||||||
## [0.6.1] - 2019-06-22
|
|
||||||
### Changed
|
|
||||||
* `Mat2` now uses a `Vec4` internally which gives it some performance
|
|
||||||
improvements when SSE2 is available.
|
|
||||||
|
|
||||||
## 0.6.0 - 2019-06-13
|
|
||||||
### Changed
|
|
||||||
* Switched from row vectors to column vectors
|
|
||||||
* Vectors are now on the right of multiplications with matrices and quaternions.
|
|
||||||
|
|
||||||
|
|
||||||
[Keep a Changelog]: https://keepachangelog.com/
|
|
||||||
[Semantic Versioning]: https://semver.org/spec/v2.0.0.html
|
|
||||||
[Unreleased]: https://github.com/bitshifter/glam-rs/compare/0.8.7...HEAD
|
|
||||||
[0.8.7]: https://github.com/bitshifter/glam-rs/compare/0.8.6...0.8.7
|
|
||||||
[0.8.6]: https://github.com/bitshifter/glam-rs/compare/0.8.5...0.8.6
|
|
||||||
[0.8.5]: https://github.com/bitshifter/glam-rs/compare/0.8.4...0.8.5
|
|
||||||
[0.8.4]: https://github.com/bitshifter/glam-rs/compare/0.8.3...0.8.4
|
|
||||||
[0.8.3]: https://github.com/bitshifter/glam-rs/compare/0.8.2...0.8.3
|
|
||||||
[0.8.2]: https://github.com/bitshifter/glam-rs/compare/0.8.1...0.8.2
|
|
||||||
[0.8.1]: https://github.com/bitshifter/glam-rs/compare/0.8.0...0.8.1
|
|
||||||
[0.8.0]: https://github.com/bitshifter/glam-rs/compare/0.7.2...0.8.0
|
|
||||||
[0.7.2]: https://github.com/bitshifter/glam-rs/compare/0.7.1...0.7.2
|
|
||||||
[0.7.1]: https://github.com/bitshifter/glam-rs/compare/0.7.0...0.7.1
|
|
||||||
[0.7.0]: https://github.com/bitshifter/glam-rs/compare/0.6.1...0.7.0
|
|
||||||
[0.6.1]: https://github.com/bitshifter/glam-rs/compare/0.6.0...0.6.1
|
|
|
@ -1,50 +0,0 @@
|
||||||
Thanks for contributing to `glam`! These guidelines will try make the process
|
|
||||||
painless and efficient.
|
|
||||||
|
|
||||||
The short guide to contributing is [open a github issue]. Pull requests are
|
|
||||||
welcome for bug fixes, documentation improvements and optimizations. For
|
|
||||||
anything else it would be best to discuss it first.
|
|
||||||
|
|
||||||
# Questions
|
|
||||||
|
|
||||||
If you have a question about the usage of this library please
|
|
||||||
[open a github issue]. That's the easiest way to get support right now.
|
|
||||||
|
|
||||||
# Bugs
|
|
||||||
|
|
||||||
If you find a bug please [open a github issue] or submit a pull request. A unit
|
|
||||||
test for any bug that slipped through existing coverage would also be greatly
|
|
||||||
appreciated.
|
|
||||||
|
|
||||||
# New functions and methods
|
|
||||||
|
|
||||||
If `glam` is missing functionality on existing types, [open a github issue]
|
|
||||||
describing what feature you would like added and ideally what your use case is
|
|
||||||
for it just so I have a better understanding of the feature. I'd like to keep
|
|
||||||
`glam` reasonably light functionality wise initially but commonly used
|
|
||||||
functionality that is missing is very welcome. If you do submit a pull request
|
|
||||||
please ensure any new functionality also has a test.
|
|
||||||
|
|
||||||
# New types
|
|
||||||
|
|
||||||
If you would like to add a new type to `glam` please [open a github issue]. Only
|
|
||||||
`f32` has been supported for now but long term I'd like to support people with
|
|
||||||
other needs. I am interested in what types you feel you need and what you would
|
|
||||||
use them for. Examples might be `i32` or `f64` vectors or generic types. I don't
|
|
||||||
want to rush to support new types until the API has stabilised a bit more but it
|
|
||||||
would be good to collect information on functionality people would like to see
|
|
||||||
in `glam`.
|
|
||||||
|
|
||||||
# Optimizations
|
|
||||||
|
|
||||||
If you feel some functionality could be optimized please [open a github issue]
|
|
||||||
or submit a pull request. Any optimization pull request should include a
|
|
||||||
benchmark if there isn't one already so I can confirm the performance
|
|
||||||
improvement.
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
|
|
||||||
If you feel any documentation could be added or improved please
|
|
||||||
[open a github issue] or submit a pull request.
|
|
||||||
|
|
||||||
[open a github issue]: https://github.com/bitshifter/glam-rs/issues
|
|
|
@ -1,83 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "glam"
|
|
||||||
version = "0.8.7" # remember to update html_root_url
|
|
||||||
edition = "2018"
|
|
||||||
authors = ["Cameron Hart <cameron.hart@gmail.com>"]
|
|
||||||
description = "A simple and fast 3D math library for games and graphics"
|
|
||||||
repository = "https://github.com/bitshifter/glam-rs"
|
|
||||||
readme = "README.md"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
keywords = ["gamedev", "math", "matrix", "vector", "quaternion"]
|
|
||||||
categories = ["game-engines"]
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[badges]
|
|
||||||
travis-ci = { repository = "bitshifter/glam-rs" }
|
|
||||||
coveralls = { repository = "bitshifter/glam-rs" }
|
|
||||||
maintenance = { status = "experimental" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
|
|
||||||
# enable support for the standard library
|
|
||||||
std = []
|
|
||||||
|
|
||||||
# enable additional glam checks if debug assertions are enabled
|
|
||||||
debug-glam-assert = []
|
|
||||||
# always enable additional glam checks
|
|
||||||
glam-assert = []
|
|
||||||
|
|
||||||
# opt out of Vec3 and Mat3 using SIMD (and thus being 16 byte aligned)
|
|
||||||
packed-vec3 = []
|
|
||||||
|
|
||||||
# this is primarily for testing the fallback implementation
|
|
||||||
scalar-math = []
|
|
||||||
|
|
||||||
# trying out transform types instead of matrices but they are generally slower
|
|
||||||
# at everything except for inverse. They are 16 bytes smaller than a Mat4.
|
|
||||||
transform-types = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
mint = { version = "0.5", optional = true, default-features = false }
|
|
||||||
rand = { version = "0.7", optional = true, default-features = false }
|
|
||||||
serde = { version = "1.0", optional = true, features = ["derive"] }
|
|
||||||
zerocopy = { version = "0.3.0" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
criterion = "0.3"
|
|
||||||
# rand_xoshiro is required for tests if rand is enabled
|
|
||||||
rand_xoshiro = "0.4"
|
|
||||||
serde_json = "1.0"
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "mat2"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "mat3"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "mat4"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "quat"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "transform"
|
|
||||||
harness = false
|
|
||||||
required-features = ["transform-types"]
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "vec2"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "vec3"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "vec4"
|
|
||||||
harness = false
|
|
|
@ -1,201 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2020 Cameron Hart
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
|
@ -1,23 +0,0 @@
|
||||||
Permission is hereby granted, free of charge, to any
|
|
||||||
person obtaining a copy of this software and associated
|
|
||||||
documentation files (the "Software"), to deal in the
|
|
||||||
Software without restriction, including without
|
|
||||||
limitation the rights to use, copy, modify, merge,
|
|
||||||
publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software
|
|
||||||
is furnished to do so, subject to the following
|
|
||||||
conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice
|
|
||||||
shall be included in all copies or substantial portions
|
|
||||||
of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
||||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
||||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
||||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
||||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
|
@ -1,163 +0,0 @@
|
||||||
# glam
|
|
||||||
|
|
||||||
[![Build Status]][travis-ci] [![Coverage Status]][coveralls.io]
|
|
||||||
[![Latest Version]][crates.io] [![docs]][docs.rs]
|
|
||||||
|
|
||||||
A simple and fast 3D math library for games and graphics.
|
|
||||||
|
|
||||||
## Development status
|
|
||||||
|
|
||||||
`glam` is in alpha stage. Minimal base functionality has been implemented
|
|
||||||
and the look and feel of the API has solidified.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
* Only single precision floating point (`f32`) arithmetic is supported
|
|
||||||
* vectors: `Vec2`, `Vec3`, `Vec4`
|
|
||||||
* square matrices: `Mat2`, `Mat3`, `Mat4`
|
|
||||||
* a quaternion type: `Quat`
|
|
||||||
|
|
||||||
### SIMD
|
|
||||||
|
|
||||||
The `Vec3`, `Vec4` and `Quat` types use SSE2 on x86/x86_64 architectures.
|
|
||||||
`Mat2`, `Mat3` and `Mat4` also use SSE2 for some functionality. Not everything
|
|
||||||
has a SIMD implementation yet.
|
|
||||||
|
|
||||||
Note that this does result in some wasted space in the case of `Vec3` and `Mat3`
|
|
||||||
as the SIMD vector type is 16 bytes large and 16 byte aligned.
|
|
||||||
|
|
||||||
It is possible to opt out of using SIMD types for Vec3 and Mat3 storage with the
|
|
||||||
`packed-vec3` feature.
|
|
||||||
|
|
||||||
`glam` outperforms similar Rust libraries such as [`cgmath`][cgmath],
|
|
||||||
[`nalgebra-glm`][nalgebra-glm] and others for common operations as tested by the
|
|
||||||
[`mathbench`][mathbench] project.
|
|
||||||
|
|
||||||
If you are more concerned with size than speed you can build glam with the
|
|
||||||
feature `scalar-math` enabled to disable SIMD usage.
|
|
||||||
|
|
||||||
Due to the use of SIMD, vector elements may only be get and set via accessor
|
|
||||||
methods, e.g. `Vec3::x()` and `Vec3::x_mut()` or `Vec3::set_x()`. If getting or
|
|
||||||
setting more than one element it is more efficient to convert from tuples or
|
|
||||||
arrays:
|
|
||||||
|
|
||||||
```
|
|
||||||
let (x, y, z) = v.into();
|
|
||||||
let [x, y, z]: [f32; 3] = v.into();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Optional features
|
|
||||||
|
|
||||||
* `mint` - for interoperating with other 3D math libraries
|
|
||||||
* `rand` - implementations of `Distribution` trait for all `glam` types. This
|
|
||||||
is primarily used for unit testing
|
|
||||||
* `serde` - implementations of `Serialize` and `Deserialize` for all `glam`
|
|
||||||
types. Note that serialization should work between builds of `glam` with and
|
|
||||||
without SIMD enabled
|
|
||||||
|
|
||||||
### Feature gates
|
|
||||||
|
|
||||||
* `packed-vec3` - disable using SIMD types for `Vec3` and `Mat3` storage. This
|
|
||||||
avoids wasting space due to 16 byte alignment at the cost of some performance.
|
|
||||||
* `scalar-math` - compiles with SIMD support disabled
|
|
||||||
* `glam-assert` - adds assertions which check the validity of parameters passed to
|
|
||||||
`glam` to help catch runtime errors
|
|
||||||
|
|
||||||
## Conventions
|
|
||||||
|
|
||||||
### Column vectors
|
|
||||||
|
|
||||||
`glam` interprets vectors as column matrices (also known as "column vectors")
|
|
||||||
meaning when transforming a vector with a matrix the matrix goes on the left,
|
|
||||||
e.g. `v' = Mv`. DirectX uses row vectors, OpenGL uses column vectors. There
|
|
||||||
are pros and cons to both.
|
|
||||||
|
|
||||||
### Column-major order
|
|
||||||
|
|
||||||
Matrices are stored in column major format. Each column vector is stored in
|
|
||||||
contiguous memory.
|
|
||||||
|
|
||||||
### Co-ordinate system
|
|
||||||
|
|
||||||
`glam` is co-ordinate system agnostic and intends to support both right handed
|
|
||||||
and left handed conventions.
|
|
||||||
|
|
||||||
Rotations follow the left-hand rule.
|
|
||||||
|
|
||||||
## Design Philosophy
|
|
||||||
|
|
||||||
The design of this library is guided by a desire for simplicity and good
|
|
||||||
performance.
|
|
||||||
|
|
||||||
* No traits or generics for simplicity of implementation and usage
|
|
||||||
* Only single precision floating point (`f32`) arithmetic is supported
|
|
||||||
* All dependencies are optional (e.g. `mint`, `rand` and `serde`)
|
|
||||||
* Follows the [Rust API Guidelines] where possible
|
|
||||||
* Aiming for 100% test [coverage][coveralls.io]
|
|
||||||
* Common functionality is benchmarked using [Criterion.rs]
|
|
||||||
|
|
||||||
## Future work
|
|
||||||
|
|
||||||
* Experiment with a using a 4x3 matrix as a 3D transform type that can be more
|
|
||||||
efficient than `Mat4` for certain operations like inverse and multiplies
|
|
||||||
* `no-std` support
|
|
||||||
* `wasm` support
|
|
||||||
|
|
||||||
## Inspirations
|
|
||||||
|
|
||||||
There were many inspirations for the interface and internals of glam from the
|
|
||||||
Rust and C++ worlds. In particular:
|
|
||||||
|
|
||||||
* [How to write a maths library in 2016] inspired the initial `Vec3`
|
|
||||||
implementation
|
|
||||||
* [Realtime Math] - header only C++11 with SSE and NEON SIMD intrinsic support
|
|
||||||
* [DirectXMath] - header only SIMD C++ linear algebra library for use in games
|
|
||||||
and graphics apps
|
|
||||||
* `glam` is a play on the name of the popular C++ library `glm`
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Licensed under either of
|
|
||||||
|
|
||||||
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE)
|
|
||||||
or http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
* MIT license ([LICENSE-MIT](LICENSE-MIT)
|
|
||||||
or http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
at your option.
|
|
||||||
|
|
||||||
## Contribution
|
|
||||||
|
|
||||||
Contributions in any form (issues, pull requests, etc.) to this project must
|
|
||||||
adhere to Rust's [Code of Conduct].
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
|
||||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
|
||||||
dual licensed as above, without any additional terms or conditions.
|
|
||||||
|
|
||||||
Thank you to all of the `glam` [contributors]!
|
|
||||||
|
|
||||||
## Support
|
|
||||||
If you are interested in contributing or have a request or suggestion
|
|
||||||
[create an issue] on github.
|
|
||||||
|
|
||||||
|
|
||||||
[Build Status]: https://travis-ci.org/bitshifter/glam-rs.svg?branch=master
|
|
||||||
[travis-ci]: https://travis-ci.org/bitshifter/glam-rs
|
|
||||||
[Coverage Status]: https://coveralls.io/repos/github/bitshifter/glam-rs/badge.svg?branch=master
|
|
||||||
[coveralls.io]: https://coveralls.io/github/bitshifter/glam-rs?branch=master
|
|
||||||
[Code of Conduct]: https://www.rust-lang.org/en-US/conduct.html
|
|
||||||
[Latest Version]: https://img.shields.io/crates/v/glam.svg
|
|
||||||
[crates.io]: https://crates.io/crates/glam/
|
|
||||||
[docs]: https://docs.rs/glam/badge.svg
|
|
||||||
[docs.rs]: https://docs.rs/glam/
|
|
||||||
[Rust API Guidelines]: https://rust-lang-nursery.github.io/api-guidelines/
|
|
||||||
[Criterion.rs]: https://bheisler.github.io/criterion.rs/book/index.html
|
|
||||||
[cgmath]: https://github.com/rustgd/cgmath
|
|
||||||
[nalgebra-glm]: https://github.com/rustsim/nalgebra
|
|
||||||
[mathbench]: https://github.com/bitshifter/mathbench-rs
|
|
||||||
[create an issue]: https://github.com/bitshifter/glam-rs/issues
|
|
||||||
[contributors]: https://github.com/bitshifter/glam-rs/graphs/contributors
|
|
||||||
[How to write a maths library in 2016]: http://www.codersnotes.com/notes/maths-lib-2016/
|
|
||||||
[Realtime Math]: https://github.com/nfrechette/rtm
|
|
||||||
[DirectXMath]: https://docs.microsoft.com/en-us/windows/desktop/dxmath/directxmath-portal
|
|
|
@ -1,34 +0,0 @@
|
||||||
#[path = "support/macros.rs"]
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use std::ops::Mul;
|
|
||||||
use support::*;
|
|
||||||
|
|
||||||
bench_unop!(
|
|
||||||
mat2_transpose,
|
|
||||||
"mat2 transpose",
|
|
||||||
op => transpose,
|
|
||||||
from => random_mat2
|
|
||||||
);
|
|
||||||
bench_unop!(
|
|
||||||
mat2_determinant,
|
|
||||||
"mat2 determinant",
|
|
||||||
op => determinant,
|
|
||||||
from => random_mat2
|
|
||||||
);
|
|
||||||
bench_unop!(mat2_inverse, "mat2 inverse", op => inverse, from => random_mat2);
|
|
||||||
bench_binop!(mat2_mul_mat2, "mat2 * mat2", op => mul, from => random_mat2);
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
mat2_transpose,
|
|
||||||
mat2_determinant,
|
|
||||||
mat2_inverse,
|
|
||||||
// mat2_mul_op_mat2,
|
|
||||||
mat2_mul_mat2,
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,36 +0,0 @@
|
||||||
#[path = "support/macros.rs"]
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use glam::Mat3;
|
|
||||||
use std::ops::Mul;
|
|
||||||
use support::*;
|
|
||||||
|
|
||||||
bench_unop!(
|
|
||||||
mat3_transpose,
|
|
||||||
"mat3 transpose",
|
|
||||||
op => transpose,
|
|
||||||
from => random_mat3
|
|
||||||
);
|
|
||||||
bench_unop!(
|
|
||||||
mat3_determinant,
|
|
||||||
"mat3 determinant",
|
|
||||||
op => determinant,
|
|
||||||
from => random_mat3
|
|
||||||
);
|
|
||||||
bench_unop!(mat3_inverse, "mat3 inverse", op => inverse, from => random_mat3);
|
|
||||||
bench_binop!(mat3_mul_mat3, "mat3 * mat3", op => mul, from => random_mat3);
|
|
||||||
bench_from_ypr!(mat3_from_ypr, "mat3 from ypr", ty => Mat3);
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
mat3_transpose,
|
|
||||||
mat3_determinant,
|
|
||||||
mat3_inverse,
|
|
||||||
mat3_mul_mat3,
|
|
||||||
mat3_from_ypr,
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,36 +0,0 @@
|
||||||
#[path = "support/macros.rs"]
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use glam::Mat4;
|
|
||||||
use std::ops::Mul;
|
|
||||||
use support::*;
|
|
||||||
|
|
||||||
bench_unop!(
|
|
||||||
mat4_transpose,
|
|
||||||
"mat4 transpose",
|
|
||||||
op => transpose,
|
|
||||||
from => random_srt_mat4
|
|
||||||
);
|
|
||||||
bench_unop!(
|
|
||||||
mat4_determinant,
|
|
||||||
"mat4 determinant",
|
|
||||||
op => determinant,
|
|
||||||
from => random_srt_mat4
|
|
||||||
);
|
|
||||||
bench_unop!(mat4_inverse, "mat4 inverse", op => inverse, from => random_srt_mat4);
|
|
||||||
bench_binop!(mat4_mul_mat4, "mat4 * mat4", op => mul, from => random_srt_mat4);
|
|
||||||
bench_from_ypr!(mat4_from_ypr, "mat4 from ypr", ty => Mat4);
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
mat4_transpose,
|
|
||||||
mat4_determinant,
|
|
||||||
mat4_inverse,
|
|
||||||
mat4_mul_mat4,
|
|
||||||
mat4_from_ypr,
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,62 +0,0 @@
|
||||||
#[path = "support/macros.rs"]
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use glam::Quat;
|
|
||||||
use std::ops::Mul;
|
|
||||||
use support::{random_f32, random_quat, random_radians};
|
|
||||||
|
|
||||||
bench_unop!(
|
|
||||||
quat_conjugate,
|
|
||||||
"quat conjugate",
|
|
||||||
op => conjugate,
|
|
||||||
from => random_quat
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
quat_mul_quat,
|
|
||||||
"quat * quat",
|
|
||||||
op => mul,
|
|
||||||
from => random_quat
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
quat_dot,
|
|
||||||
"quat dot",
|
|
||||||
op => dot,
|
|
||||||
from => random_quat
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_trinop!(
|
|
||||||
quat_lerp,
|
|
||||||
"quat lerp",
|
|
||||||
op => lerp,
|
|
||||||
from1 => random_quat,
|
|
||||||
from2 => random_quat,
|
|
||||||
from3 => random_f32
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_trinop!(
|
|
||||||
quat_slerp,
|
|
||||||
"quat slerp",
|
|
||||||
op => slerp,
|
|
||||||
from1 => random_quat,
|
|
||||||
from2 => random_quat,
|
|
||||||
from3 => random_f32
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_from_ypr!(quat_from_ypr, "quat from ypr", ty => Quat);
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
quat_conjugate,
|
|
||||||
quat_dot,
|
|
||||||
quat_lerp,
|
|
||||||
quat_slerp,
|
|
||||||
quat_mul_quat,
|
|
||||||
quat_from_ypr
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,180 +0,0 @@
|
||||||
#[macro_export]
|
|
||||||
macro_rules! bench_func {
|
|
||||||
($name: ident, $desc: expr, op => $func: ident, from => $from: expr) => {
|
|
||||||
pub(crate) fn $name(c: &mut Criterion) {
|
|
||||||
const SIZE: usize = 1 << 13;
|
|
||||||
let mut rng = support::PCG32::default();
|
|
||||||
let inputs =
|
|
||||||
criterion::black_box((0..SIZE).map(|_| $from(&mut rng)).collect::<Vec<_>>());
|
|
||||||
// pre-fill output vector with some random value
|
|
||||||
let mut outputs = vec![$func($from(&mut rng)); SIZE];
|
|
||||||
let mut i = 0;
|
|
||||||
c.bench_function($desc, |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
i = (i + 1) & (SIZE - 1);
|
|
||||||
unsafe {
|
|
||||||
*outputs.get_unchecked_mut(i) = $func(*inputs.get_unchecked(i));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
criterion::black_box(outputs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! bench_unop {
|
|
||||||
($name: ident, $desc: expr, op => $unop: ident, from => $from: expr) => {
|
|
||||||
pub(crate) fn $name(c: &mut Criterion) {
|
|
||||||
const SIZE: usize = 1 << 13;
|
|
||||||
let mut rng = support::PCG32::default();
|
|
||||||
let inputs =
|
|
||||||
criterion::black_box((0..SIZE).map(|_| $from(&mut rng)).collect::<Vec<_>>());
|
|
||||||
// pre-fill output vector with some random value
|
|
||||||
let mut outputs = vec![$from(&mut rng).$unop(); SIZE];
|
|
||||||
let mut i = 0;
|
|
||||||
c.bench_function($desc, |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
i = (i + 1) & (SIZE - 1);
|
|
||||||
unsafe {
|
|
||||||
*outputs.get_unchecked_mut(i) = inputs.get_unchecked(i).$unop();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
criterion::black_box(outputs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! bench_binop {
|
|
||||||
($name: ident, $desc: expr, op => $binop: ident, from1 => $from1:expr, from2 => $from2:expr) => {
|
|
||||||
pub(crate) fn $name(c: &mut Criterion) {
|
|
||||||
const SIZE: usize = 1 << 13;
|
|
||||||
let mut rng = support::PCG32::default();
|
|
||||||
let inputs1 =
|
|
||||||
criterion::black_box((0..SIZE).map(|_| $from1(&mut rng)).collect::<Vec<_>>());
|
|
||||||
let inputs2 =
|
|
||||||
criterion::black_box((0..SIZE).map(|_| $from2(&mut rng)).collect::<Vec<_>>());
|
|
||||||
// pre-fill output vector with some random value
|
|
||||||
let mut outputs = vec![$from1(&mut rng).$binop($from2(&mut rng)); SIZE];
|
|
||||||
let mut i = 0;
|
|
||||||
c.bench_function($desc, |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
i = (i + 1) & (SIZE - 1);
|
|
||||||
unsafe {
|
|
||||||
*outputs.get_unchecked_mut(i) = inputs1.get_unchecked(i).$binop(*inputs2.get_unchecked(i));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
criterion::black_box(outputs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($name: ident, $desc: expr, op => $binop: ident, from => $from: expr) => {
|
|
||||||
bench_binop!($name, $desc, op => $binop, from1 => $from, from2 => $from);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! bench_trinop {
|
|
||||||
($name: ident, $desc: expr, op => $trinop: ident, from1 => $from1:expr, from2 => $from2:expr, from3 => $from3:expr) => {
|
|
||||||
pub(crate) fn $name(c: &mut Criterion) {
|
|
||||||
const SIZE: usize = 1 << 13;
|
|
||||||
let mut rng = support::PCG32::default();
|
|
||||||
let inputs1 =
|
|
||||||
criterion::black_box((0..SIZE).map(|_| $from1(&mut rng)).collect::<Vec<_>>());
|
|
||||||
let inputs2 =
|
|
||||||
criterion::black_box((0..SIZE).map(|_| $from2(&mut rng)).collect::<Vec<_>>());
|
|
||||||
let inputs3 =
|
|
||||||
criterion::black_box((0..SIZE).map(|_| $from3(&mut rng)).collect::<Vec<_>>());
|
|
||||||
// pre-fill output vector with some random value
|
|
||||||
let mut outputs =
|
|
||||||
vec![$from1(&mut rng).$trinop($from2(&mut rng), $from3(&mut rng)); SIZE];
|
|
||||||
let mut i = 0;
|
|
||||||
c.bench_function($desc, |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
i = (i + 1) & (SIZE - 1);
|
|
||||||
unsafe {
|
|
||||||
*outputs.get_unchecked_mut(i) = inputs1
|
|
||||||
.get_unchecked(i)
|
|
||||||
.$trinop(*inputs2.get_unchecked(i), *inputs3.get_unchecked(i));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
criterion::black_box(outputs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! bench_from_ypr {
|
|
||||||
($name: ident, $desc: expr, ty => $ty:ty) => {
|
|
||||||
pub(crate) fn $name(c: &mut Criterion) {
|
|
||||||
const SIZE: usize = 1 << 13;
|
|
||||||
let mut rng = support::PCG32::default();
|
|
||||||
let inputs = criterion::black_box(
|
|
||||||
(0..SIZE)
|
|
||||||
.map(|_| {
|
|
||||||
(
|
|
||||||
random_radians(&mut rng),
|
|
||||||
random_radians(&mut rng),
|
|
||||||
random_radians(&mut rng),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
);
|
|
||||||
let mut outputs = vec![<$ty>::default(); SIZE];
|
|
||||||
let mut i = 0;
|
|
||||||
c.bench_function($desc, |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
i = (i + 1) & (SIZE - 1);
|
|
||||||
unsafe {
|
|
||||||
let data = inputs.get_unchecked(i);
|
|
||||||
*outputs.get_unchecked_mut(i) =
|
|
||||||
<$ty>::from_rotation_ypr(data.0, data.1, data.2)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! euler {
|
|
||||||
($name: ident, $desc: expr, ty => $t: ty, storage => $storage: ty, zero => $zero: expr, rand => $rand: ident) => {
|
|
||||||
pub(crate) fn $name(c: &mut Criterion) {
|
|
||||||
const UPDATE_RATE: f32 = 1.0 / 60.0;
|
|
||||||
const NUM_OBJECTS: usize = 10000;
|
|
||||||
|
|
||||||
struct TestData {
|
|
||||||
acc: Vec<$storage>,
|
|
||||||
vel: Vec<$storage>,
|
|
||||||
pos: Vec<$storage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rng = support::PCG32::default();
|
|
||||||
let mut data = TestData {
|
|
||||||
acc: vec![$rand(&mut rng); NUM_OBJECTS],
|
|
||||||
vel: vec![$zero; NUM_OBJECTS],
|
|
||||||
pos: vec![$zero; NUM_OBJECTS],
|
|
||||||
};
|
|
||||||
let dt = <$t>::splat(UPDATE_RATE);
|
|
||||||
|
|
||||||
c.bench_function($desc, |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
for ((position, acceleration), velocity) in
|
|
||||||
data.pos.iter_mut().zip(&data.acc).zip(&mut data.vel)
|
|
||||||
{
|
|
||||||
let local_acc: $t = (*acceleration).into();
|
|
||||||
let mut local_pos: $t = (*position).into();
|
|
||||||
let mut local_vel: $t = (*velocity).into();
|
|
||||||
local_vel += local_acc * dt;
|
|
||||||
local_pos += local_vel * dt;
|
|
||||||
*velocity = local_vel.into();
|
|
||||||
*position = local_pos.into();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
use core::f32;
|
|
||||||
use glam::f32::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
|
|
||||||
|
|
||||||
pub struct PCG32 {
|
|
||||||
state: u64,
|
|
||||||
inc: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PCG32 {
|
|
||||||
pub fn seed(initstate: u64, initseq: u64) -> Self {
|
|
||||||
let mut rng = PCG32 {
|
|
||||||
state: 0,
|
|
||||||
inc: (initseq << 1) | 1,
|
|
||||||
};
|
|
||||||
rng.next_u32();
|
|
||||||
rng.state = rng.state.wrapping_add(initstate);
|
|
||||||
rng.next_u32();
|
|
||||||
rng
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default() -> Self {
|
|
||||||
PCG32::seed(0x853c49e6748fea9b, 0xda3e39cb94b95bdb)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_u32(&mut self) -> u32 {
|
|
||||||
let oldstate = self.state;
|
|
||||||
self.state = oldstate
|
|
||||||
.wrapping_mul(6364136223846793005)
|
|
||||||
.wrapping_add(self.inc | 1);
|
|
||||||
let xorshifted = ((oldstate >> 18) ^ oldstate) >> 27;
|
|
||||||
let rot = oldstate >> 59;
|
|
||||||
((xorshifted >> rot) | (xorshifted << (rot.wrapping_neg() & 31))) as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_f32(&mut self) -> f32 {
|
|
||||||
(self.next_u32() & 0xffffff) as f32 / 16777216.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_vec2(rng: &mut PCG32) -> Vec2 {
|
|
||||||
Vec2::new(rng.next_f32(), rng.next_f32())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_vec3(rng: &mut PCG32) -> Vec3 {
|
|
||||||
Vec3::new(rng.next_f32(), rng.next_f32(), rng.next_f32())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_vec4(rng: &mut PCG32) -> Vec4 {
|
|
||||||
Vec4::new(
|
|
||||||
rng.next_f32(),
|
|
||||||
rng.next_f32(),
|
|
||||||
rng.next_f32(),
|
|
||||||
rng.next_f32(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_nonzero_vec3(rng: &mut PCG32) -> Vec3 {
|
|
||||||
loop {
|
|
||||||
let v = random_vec3(rng);
|
|
||||||
if v.length_squared() > 0.01 {
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_f32(rng: &mut PCG32) -> f32 {
|
|
||||||
rng.next_f32()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_radians(rng: &mut PCG32) -> f32 {
|
|
||||||
-f32::consts::PI + rng.next_f32() * 2.0 * f32::consts::PI
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_quat(rng: &mut PCG32) -> Quat {
|
|
||||||
let yaw = random_radians(rng);
|
|
||||||
let pitch = random_radians(rng);
|
|
||||||
let roll = random_radians(rng);
|
|
||||||
Quat::from_rotation_ypr(yaw, pitch, roll)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_mat2(rng: &mut PCG32) -> Mat2 {
|
|
||||||
Mat2::from_cols(random_vec2(rng), random_vec2(rng))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_mat3(rng: &mut PCG32) -> Mat3 {
|
|
||||||
Mat3::from_cols(random_vec3(rng), random_vec3(rng), random_vec3(rng))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_srt_mat4(rng: &mut PCG32) -> Mat4 {
|
|
||||||
Mat4::from_scale_rotation_translation(
|
|
||||||
random_nonzero_vec3(rng),
|
|
||||||
random_quat(rng),
|
|
||||||
random_vec3(rng),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
#[path = "support/macros.rs"]
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use glam::f32::{TransformRT, TransformSRT};
|
|
||||||
use std::ops::Mul;
|
|
||||||
use support::*;
|
|
||||||
|
|
||||||
fn random_transform_srt(rng: &mut PCG32) -> TransformSRT {
|
|
||||||
TransformSRT::from_scale_rotation_translation(
|
|
||||||
random_nonzero_vec3(rng),
|
|
||||||
random_quat(rng),
|
|
||||||
random_vec3(rng),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_transform_rt(rng: &mut PCG32) -> TransformRT {
|
|
||||||
TransformRT::from_rotation_translation(random_quat(rng), random_vec3(rng))
|
|
||||||
}
|
|
||||||
|
|
||||||
bench_unop!(
|
|
||||||
transform_srt_inverse,
|
|
||||||
"transform_srt inverse",
|
|
||||||
op => inverse,
|
|
||||||
from => random_transform_srt
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
vec3_mul_transform_srt,
|
|
||||||
"transform_srt * vec3",
|
|
||||||
op => mul,
|
|
||||||
from1 => random_transform_srt,
|
|
||||||
from2 => random_vec3
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
vec3_mul_transform_rt,
|
|
||||||
"transform_rt * vec3",
|
|
||||||
op => mul,
|
|
||||||
from1 => random_transform_rt,
|
|
||||||
from2 => random_vec3
|
|
||||||
);
|
|
||||||
|
|
||||||
// bench_unop!(
|
|
||||||
// transform_srt_inverse_ptv_scale,
|
|
||||||
// "transform_srt inverse (+ve scale)",
|
|
||||||
// op => inverse,
|
|
||||||
// ty => TransformSRT,
|
|
||||||
// from => TransformRT
|
|
||||||
// );
|
|
||||||
// bench_unop!(
|
|
||||||
// transform_rt_inverse,
|
|
||||||
// "transform_rt inverse",
|
|
||||||
// op => inverse,
|
|
||||||
// ty => TransformRT
|
|
||||||
// );
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
transform_srt_mul_srt,
|
|
||||||
"transform_srt * transform_srt",
|
|
||||||
op => mul,
|
|
||||||
from => random_transform_srt
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
transform_rt_mul_rt,
|
|
||||||
"transform_rt * transform_rt",
|
|
||||||
op => mul,
|
|
||||||
from => random_transform_rt
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
// transform_rt_inverse,
|
|
||||||
transform_srt_inverse,
|
|
||||||
// transform_srt_inverse_ptv_scale,
|
|
||||||
transform_rt_mul_rt,
|
|
||||||
transform_srt_mul_srt,
|
|
||||||
vec3_mul_transform_rt,
|
|
||||||
vec3_mul_transform_srt,
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,31 +0,0 @@
|
||||||
#[path = "support/macros.rs"]
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use glam::f32::Vec2;
|
|
||||||
use std::ops::Mul;
|
|
||||||
use support::{random_mat2, random_vec2};
|
|
||||||
|
|
||||||
euler!(vec2_euler, "vec2 euler", ty => Vec2, storage => Vec2, zero => Vec2::zero(), rand => random_vec2);
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
mat2_mul_vec2,
|
|
||||||
"mat2 * vec2",
|
|
||||||
op => mul,
|
|
||||||
from1 => random_mat2,
|
|
||||||
from2 => random_vec2
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
vec2_angle_between,
|
|
||||||
"vec2 angle_between",
|
|
||||||
op => angle_between,
|
|
||||||
from1 => random_vec2,
|
|
||||||
from2 => random_vec2
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_group!(benches, vec2_euler, mat2_mul_vec2, vec2_angle_between);
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,98 +0,0 @@
|
||||||
#[path = "support/macros.rs"]
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use glam::f32::Vec3;
|
|
||||||
use std::ops::Mul;
|
|
||||||
use support::{random_mat3, random_quat, random_vec3};
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
quat_mul_vec3,
|
|
||||||
"quat * vec3",
|
|
||||||
op => mul,
|
|
||||||
from1 => random_quat,
|
|
||||||
from2 => random_vec3
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
mat3_mul_vec3,
|
|
||||||
"mat3 * vec3",
|
|
||||||
op => mul,
|
|
||||||
from1 => random_mat3,
|
|
||||||
from2 => random_vec3
|
|
||||||
);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn vec3_to_rgb_op(v: Vec3) -> u32 {
|
|
||||||
let (red, green, blue) = (v.min(Vec3::one()).max(Vec3::zero()) * 255.0).into();
|
|
||||||
((red as u32) << 16 | (green as u32) << 8 | (blue as u32)).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn vec3_accessors(v: Vec3) -> [f32; 3] {
|
|
||||||
[v.x(), v.y(), v.z()]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn vec3_into_array(v: Vec3) -> [f32; 3] {
|
|
||||||
v.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn vec3_into_tuple(v: Vec3) -> (f32, f32, f32) {
|
|
||||||
v.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
bench_func!(
|
|
||||||
vec3_to_rgb,
|
|
||||||
"vec3 to rgb",
|
|
||||||
op => vec3_to_rgb_op,
|
|
||||||
from => random_vec3
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_func!(
|
|
||||||
vec3_to_array_accessors,
|
|
||||||
"vec3 into array slow",
|
|
||||||
op => vec3_accessors,
|
|
||||||
from => random_vec3
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_func!(
|
|
||||||
vec3_to_array_into,
|
|
||||||
"vec3 into array fast",
|
|
||||||
op => vec3_into_array,
|
|
||||||
from => random_vec3
|
|
||||||
);
|
|
||||||
|
|
||||||
bench_func!(
|
|
||||||
vec3_to_tuple_into,
|
|
||||||
"vec3 into tuple fast",
|
|
||||||
op => vec3_into_tuple,
|
|
||||||
from => random_vec3
|
|
||||||
);
|
|
||||||
|
|
||||||
euler!(vec3_euler, "vec3 euler", ty => Vec3, storage => Vec3, zero => Vec3::zero(), rand => random_vec3);
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
vec3_angle_between,
|
|
||||||
"vec3 angle_between",
|
|
||||||
op => angle_between,
|
|
||||||
from1 => random_vec3,
|
|
||||||
from2 => random_vec3
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
quat_mul_vec3,
|
|
||||||
mat3_mul_vec3,
|
|
||||||
vec3_angle_between,
|
|
||||||
vec3_euler,
|
|
||||||
vec3_to_rgb,
|
|
||||||
vec3_to_array_accessors,
|
|
||||||
vec3_to_array_into,
|
|
||||||
vec3_to_tuple_into,
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,20 +0,0 @@
|
||||||
#[path = "support/macros.rs"]
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use std::ops::Mul;
|
|
||||||
use support::{random_srt_mat4, random_vec4};
|
|
||||||
|
|
||||||
bench_binop!(
|
|
||||||
vec4_mul_mat4,
|
|
||||||
"vec4 * mat4",
|
|
||||||
op => mul,
|
|
||||||
from1 => random_srt_mat4,
|
|
||||||
from2 => random_vec4
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_group!(benches, vec4_mul_mat4,);
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,29 +0,0 @@
|
||||||
use std::env;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let force_scalar_math = env::var("CARGO_FEATURE_SCALAR_MATH").is_ok();
|
|
||||||
let force_packed_vec3 = env::var("CARGO_FEATURE_PACKED_VEC3").is_ok();
|
|
||||||
|
|
||||||
let target_feature_sse2 = env::var("CARGO_CFG_TARGET_FEATURE")
|
|
||||||
.map_or(false, |cfg| cfg.split(',').find(|&f| f == "sse2").is_some());
|
|
||||||
|
|
||||||
if target_feature_sse2 && !force_scalar_math && !force_packed_vec3 {
|
|
||||||
println!("cargo:rustc-cfg=vec3sse2");
|
|
||||||
} else {
|
|
||||||
if !force_scalar_math && !force_packed_vec3 {
|
|
||||||
// simd not available but not explicitly disabled so maintain 16 byte alignment
|
|
||||||
println!("cargo:rustc-cfg=vec3f32_align16");
|
|
||||||
}
|
|
||||||
println!("cargo:rustc-cfg=vec3f32");
|
|
||||||
}
|
|
||||||
|
|
||||||
if target_feature_sse2 && !force_scalar_math {
|
|
||||||
println!("cargo:rustc-cfg=vec4sse2");
|
|
||||||
} else {
|
|
||||||
if !force_scalar_math {
|
|
||||||
// simd not available but not explicitly disabled so maintain 16 byte alignment
|
|
||||||
println!("cargo:rustc-cfg=vec4f32_align16");
|
|
||||||
}
|
|
||||||
println!("cargo:rustc-cfg=vec4f32");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
[bans]
|
|
||||||
multiple-versions = "deny"
|
|
||||||
deny = []
|
|
||||||
skip-tree = [
|
|
||||||
# ignore criterion dev-dependency that often have duplicate dependencies internally
|
|
||||||
{ name = "criterion" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[licenses]
|
|
||||||
unlicensed = "deny"
|
|
||||||
allow = [
|
|
||||||
"Apache-2.0",
|
|
||||||
"BSD-2-Clause",
|
|
||||||
"BSD-3-Clause",
|
|
||||||
"ISC",
|
|
||||||
"MIT",
|
|
||||||
"MPL-2.0",
|
|
||||||
]
|
|
|
@ -1,550 +0,0 @@
|
||||||
#[inline]
|
|
||||||
pub(crate) fn scalar_sin_cos(x: f32) -> (f32, f32) {
|
|
||||||
// // expect sse2 to be available on all x86 builds
|
|
||||||
// #[cfg(target_feature = "sse2")]
|
|
||||||
// unsafe {
|
|
||||||
// let (sinx, cosx) = sin_cos_sse2(_mm_set1_ps(x));
|
|
||||||
// (_mm_cvtss_f32(sinx), _mm_cvtss_f32(cosx))
|
|
||||||
// }
|
|
||||||
// #[cfg(not(target_feature = "sse2"))]
|
|
||||||
x.sin_cos()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn scalar_acos(value: f32) -> f32 {
|
|
||||||
// from DirectXMath XMScalarAcos
|
|
||||||
// Clamp input to [-1,1].
|
|
||||||
let nonnegative = value >= 0.0;
|
|
||||||
let x = value.abs();
|
|
||||||
let mut omx = 1.0 - x;
|
|
||||||
if omx < 0.0 {
|
|
||||||
omx = 0.0;
|
|
||||||
}
|
|
||||||
let root = omx.sqrt();
|
|
||||||
|
|
||||||
// 7-degree minimax approximation
|
|
||||||
#[allow(clippy::approx_constant)]
|
|
||||||
let mut result =
|
|
||||||
((((((-0.001_262_491_1 * x + 0.006_670_09) * x - 0.017_088_126) * x + 0.030_891_88) * x
|
|
||||||
- 0.050_174_303)
|
|
||||||
* x
|
|
||||||
+ 0.088_978_99)
|
|
||||||
* x
|
|
||||||
- 0.214_598_8)
|
|
||||||
* x
|
|
||||||
+ 1.570_796_3;
|
|
||||||
result *= root;
|
|
||||||
|
|
||||||
// acos(x) = pi - acos(-x) when x < 0
|
|
||||||
if nonnegative {
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
core::f32::consts::PI - result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
pub(crate) mod sse2 {
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
use crate::f32::x86_utils::UnionCast;
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
use core::arch::x86::*;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use core::arch::x86_64::*;
|
|
||||||
|
|
||||||
macro_rules! _ps_const_ty {
|
|
||||||
($name:ident, $field:ident, $x:expr) => {
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
const $name: UnionCast = UnionCast {
|
|
||||||
$field: [$x, $x, $x, $x],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
($name:ident, $field:ident, $x:expr, $y:expr, $z:expr, $w:expr) => {
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
const $name: UnionCast = UnionCast {
|
|
||||||
$field: [$x, $y, $z, $w],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_ps_const_ty!(PS_INV_SIGN_MASK, u32x4, !0x8000_0000);
|
|
||||||
_ps_const_ty!(PS_SIGN_MASK, u32x4, 0x8000_0000);
|
|
||||||
_ps_const_ty!(PS_NO_FRACTION, f32x4, 8388608.0);
|
|
||||||
|
|
||||||
// _ps_const_ty!(PS_1_0, f32x4, 1.0);
|
|
||||||
// _ps_const_ty!(PS_0_5, f32x4, 0.5);
|
|
||||||
|
|
||||||
// _ps_const_ty!(PI32_1, i32x4, 1);
|
|
||||||
// _ps_const_ty!(PI32_INV_1, i32x4, !1);
|
|
||||||
// _ps_const_ty!(PI32_2, i32x4, 2);
|
|
||||||
// _ps_const_ty!(PI32_4, i32x4, 4);
|
|
||||||
|
|
||||||
// _ps_const_ty!(PS_MINUS_CEPHES_DP1, f32x4, -0.785_156_25);
|
|
||||||
// _ps_const_ty!(PS_MINUS_CEPHES_DP2, f32x4, -2.418_756_5e-4);
|
|
||||||
// _ps_const_ty!(PS_MINUS_CEPHES_DP3, f32x4, -3.774_895e-8);
|
|
||||||
// _ps_const_ty!(PS_SINCOF_P0, f32x4, -1.951_529_6e-4);
|
|
||||||
// _ps_const_ty!(PS_SINCOF_P1, f32x4, 8.332_161e-3);
|
|
||||||
// _ps_const_ty!(PS_SINCOF_P2, f32x4, -1.666_665_5e-1);
|
|
||||||
// _ps_const_ty!(PS_COSCOF_P0, f32x4, 2.443_315_7e-5);
|
|
||||||
// _ps_const_ty!(PS_COSCOF_P1, f32x4, -1.388_731_6E-3);
|
|
||||||
// _ps_const_ty!(PS_COSCOF_P2, f32x4, 4.166_664_6e-2);
|
|
||||||
// _ps_const_ty!(PS_CEPHES_FOPI, f32x4, 1.273_239_5); // 4 / M_PI
|
|
||||||
|
|
||||||
_ps_const_ty!(PS_NEGATIVE_ZERO, u32x4, 0x80000000);
|
|
||||||
_ps_const_ty!(PS_PI, f32x4, core::f32::consts::PI);
|
|
||||||
_ps_const_ty!(PS_HALF_PI, f32x4, core::f32::consts::FRAC_PI_2);
|
|
||||||
_ps_const_ty!(
|
|
||||||
PS_SIN_COEFFICIENTS0,
|
|
||||||
f32x4,
|
|
||||||
-0.16666667,
|
|
||||||
0.0083333310,
|
|
||||||
-0.00019840874,
|
|
||||||
2.7525562e-06
|
|
||||||
);
|
|
||||||
_ps_const_ty!(
|
|
||||||
PS_SIN_COEFFICIENTS1,
|
|
||||||
f32x4,
|
|
||||||
-2.3889859e-08,
|
|
||||||
-0.16665852, /*Est1*/
|
|
||||||
0.0083139502, /*Est2*/
|
|
||||||
-0.00018524670 /*Est3*/
|
|
||||||
);
|
|
||||||
_ps_const_ty!(PS_ONE, f32x4, 1.0);
|
|
||||||
_ps_const_ty!(PS_TWO_PI, f32x4, core::f32::consts::PI * 2.0);
|
|
||||||
_ps_const_ty!(PS_RECIPROCAL_TWO_PI, f32x4, 0.159154943);
|
|
||||||
|
|
||||||
#[cfg(target_feature = "fma")]
|
|
||||||
macro_rules! m128_mul_add {
|
|
||||||
($a:expr, $b:expr, $c:expr) => {
|
|
||||||
_mm_fmadd_ps($a, $b, $c)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_feature = "fma"))]
|
|
||||||
macro_rules! m128_mul_add {
|
|
||||||
($a:expr, $b:expr, $c:expr) => {
|
|
||||||
_mm_add_ps(_mm_mul_ps($a, $b), $c)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_feature = "fma")]
|
|
||||||
macro_rules! m128_neg_mul_sub {
|
|
||||||
($a:expr, $b:expr, $c:expr) => {
|
|
||||||
_mm_fnmadd_ps($a, $b, $c)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_feature = "fma"))]
|
|
||||||
macro_rules! m128_neg_mul_sub {
|
|
||||||
($a:expr, $b:expr, $c:expr) => {
|
|
||||||
_mm_sub_ps($c, _mm_mul_ps($a, $b))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) unsafe fn m128_round(v: __m128) -> __m128 {
|
|
||||||
// From DirectXMath XMVectorRound.
|
|
||||||
let sign = _mm_and_ps(v, PS_SIGN_MASK.m128);
|
|
||||||
let s_magic = _mm_or_ps(PS_NO_FRACTION.m128, sign);
|
|
||||||
let r1 = _mm_add_ps(v, s_magic);
|
|
||||||
let r1 = _mm_sub_ps(r1, s_magic);
|
|
||||||
let r2 = _mm_and_ps(v, PS_INV_SIGN_MASK.m128);
|
|
||||||
let mask = _mm_cmple_ps(r2, PS_NO_FRACTION.m128);
|
|
||||||
let r2 = _mm_andnot_ps(mask, v);
|
|
||||||
let r1 = _mm_and_ps(r1, mask);
|
|
||||||
_mm_xor_ps(r1, r2)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) unsafe fn m128_floor(v: __m128) -> __m128 {
|
|
||||||
// From DirectXMath XMVectorFloor
|
|
||||||
// To handle NAN, INF and numbers greater than 8388608, use masking
|
|
||||||
let test = _mm_and_si128(_mm_castps_si128(v), PS_INV_SIGN_MASK.m128i);
|
|
||||||
let test = _mm_cmplt_epi32(test, PS_NO_FRACTION.m128i);
|
|
||||||
// Truncate
|
|
||||||
let vint = _mm_cvttps_epi32(v);
|
|
||||||
let result = _mm_cvtepi32_ps(vint);
|
|
||||||
let larger = _mm_cmpgt_ps(result, v);
|
|
||||||
// 0 -> 0, 0xffffffff -> -1.0f
|
|
||||||
let larger = _mm_cvtepi32_ps(_mm_castps_si128(larger));
|
|
||||||
let result = _mm_add_ps(result, larger);
|
|
||||||
// All numbers less than 8388608 will use the round to int
|
|
||||||
let result = _mm_and_ps(result, _mm_castsi128_ps(test));
|
|
||||||
// All others, use the ORIGINAL value
|
|
||||||
let test = _mm_andnot_si128(test, _mm_castps_si128(v));
|
|
||||||
_mm_or_ps(result, _mm_castsi128_ps(test))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) unsafe fn m128_ceil(v: __m128) -> __m128 {
|
|
||||||
// From DirectXMath XMVectorCeil
|
|
||||||
// To handle NAN, INF and numbers greater than 8388608, use masking
|
|
||||||
let test = _mm_and_si128(_mm_castps_si128(v), PS_INV_SIGN_MASK.m128i);
|
|
||||||
let test = _mm_cmplt_epi32(test, PS_NO_FRACTION.m128i);
|
|
||||||
// Truncate
|
|
||||||
let vint = _mm_cvttps_epi32(v);
|
|
||||||
let result = _mm_cvtepi32_ps(vint);
|
|
||||||
let smaller = _mm_cmplt_ps(result, v);
|
|
||||||
// 0 -> 0, 0xffffffff -> -1.0f
|
|
||||||
let smaller = _mm_cvtepi32_ps(_mm_castps_si128(smaller));
|
|
||||||
let result = _mm_sub_ps(result, smaller);
|
|
||||||
// All numbers less than 8388608 will use the round to int
|
|
||||||
let result = _mm_and_ps(result, _mm_castsi128_ps(test));
|
|
||||||
// All others, use the ORIGINAL value
|
|
||||||
let test = _mm_andnot_si128(test, _mm_castps_si128(v));
|
|
||||||
_mm_or_ps(result, _mm_castsi128_ps(test))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a vector whose components are the corresponding components of Angles modulo 2PI.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) unsafe fn m128_mod_angles(angles: __m128) -> __m128 {
|
|
||||||
// From DirectXMath: XMVectorModAngles
|
|
||||||
let v = _mm_mul_ps(angles, PS_RECIPROCAL_TWO_PI.m128);
|
|
||||||
let v = m128_round(v);
|
|
||||||
m128_neg_mul_sub!(PS_TWO_PI.m128, v, angles)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the sine of the angle in each lane of `v`. Values outside
|
|
||||||
/// the bounds of PI may produce an increasing error as the input angle
|
|
||||||
/// drifts from `[-PI, PI]`.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) unsafe fn m128_sin(v: __m128) -> __m128 {
|
|
||||||
// From DirectXMath: XMVectorSin
|
|
||||||
//
|
|
||||||
// 11-degree minimax approximation
|
|
||||||
//
|
|
||||||
// Force the value within the bounds of pi
|
|
||||||
let mut x = m128_mod_angles(v);
|
|
||||||
|
|
||||||
// Map in [-pi/2,pi/2] with sin(y) = sin(x).
|
|
||||||
let sign = _mm_and_ps(x, PS_NEGATIVE_ZERO.m128);
|
|
||||||
// pi when x >= 0, -pi when x < 0
|
|
||||||
let c = _mm_or_ps(PS_PI.m128, sign);
|
|
||||||
// |x|
|
|
||||||
let absx = _mm_andnot_ps(sign, x);
|
|
||||||
let rflx = _mm_sub_ps(c, x);
|
|
||||||
let comp = _mm_cmple_ps(absx, PS_HALF_PI.m128);
|
|
||||||
let select0 = _mm_and_ps(comp, x);
|
|
||||||
let select1 = _mm_andnot_ps(comp, rflx);
|
|
||||||
x = _mm_or_ps(select0, select1);
|
|
||||||
|
|
||||||
let x2 = _mm_mul_ps(x, x);
|
|
||||||
|
|
||||||
// Compute polynomial approximation
|
|
||||||
const SC1: __m128 = unsafe { PS_SIN_COEFFICIENTS1.m128 };
|
|
||||||
let v_constants_b = _mm_shuffle_ps(SC1, SC1, 0b00_00_00_00);
|
|
||||||
|
|
||||||
const SC0: __m128 = unsafe { PS_SIN_COEFFICIENTS0.m128 };
|
|
||||||
let mut v_constants = _mm_shuffle_ps(SC0, SC0, 0b11_11_11_11);
|
|
||||||
let mut result = m128_mul_add!(v_constants_b, x2, v_constants);
|
|
||||||
|
|
||||||
v_constants = _mm_shuffle_ps(SC0, SC0, 0b10_10_10_10);
|
|
||||||
result = m128_mul_add!(result, x2, v_constants);
|
|
||||||
|
|
||||||
v_constants = _mm_shuffle_ps(SC0, SC0, 0b01_01_01_01);
|
|
||||||
result = m128_mul_add!(result, x2, v_constants);
|
|
||||||
|
|
||||||
v_constants = _mm_shuffle_ps(SC0, SC0, 0b00_00_00_00);
|
|
||||||
result = m128_mul_add!(result, x2, v_constants);
|
|
||||||
|
|
||||||
result = m128_mul_add!(result, x2, PS_ONE.m128);
|
|
||||||
result = _mm_mul_ps(result, x);
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Based on http://gruntthepeon.free.fr/ssemath/sse_mathfun.h
|
|
||||||
// #[cfg(target_feature = "sse2")]
|
|
||||||
// unsafe fn sin_cos_sse2(x: __m128) -> (__m128, __m128) {
|
|
||||||
// let mut sign_bit_sin = x;
|
|
||||||
// // take the absolute value
|
|
||||||
// let mut x = _mm_and_ps(x, PS_INV_SIGN_MASK.m128);
|
|
||||||
// // extract the sign bit (upper one)
|
|
||||||
// sign_bit_sin = _mm_and_ps(sign_bit_sin, PS_SIGN_MASK.m128);
|
|
||||||
|
|
||||||
// // scale by 4/Pi
|
|
||||||
// let mut y = _mm_mul_ps(x, PS_CEPHES_FOPI.m128);
|
|
||||||
|
|
||||||
// // store the integer part of y in emm2
|
|
||||||
// let mut emm2 = _mm_cvttps_epi32(y);
|
|
||||||
|
|
||||||
// // j=(j+1) & (~1) (see the cephes sources)
|
|
||||||
// emm2 = _mm_add_epi32(emm2, PI32_1.m128i);
|
|
||||||
// emm2 = _mm_and_si128(emm2, PI32_INV_1.m128i);
|
|
||||||
// y = _mm_cvtepi32_ps(emm2);
|
|
||||||
|
|
||||||
// let mut emm4 = emm2;
|
|
||||||
|
|
||||||
// /* get the swap sign flag for the sine */
|
|
||||||
// let mut emm0 = _mm_and_si128(emm2, PI32_4.m128i);
|
|
||||||
// emm0 = _mm_slli_epi32(emm0, 29);
|
|
||||||
// let swap_sign_bit_sin = _mm_castsi128_ps(emm0);
|
|
||||||
|
|
||||||
// /* get the polynom selection mask for the sine*/
|
|
||||||
// emm2 = _mm_and_si128(emm2, PI32_2.m128i);
|
|
||||||
// emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
|
|
||||||
// let poly_mask = _mm_castsi128_ps(emm2);
|
|
||||||
|
|
||||||
// /* The magic pass: "Extended precision modular arithmetic"
|
|
||||||
// x = ((x - y * DP1) - y * DP2) - y * DP3; */
|
|
||||||
// let mut xmm1 = PS_MINUS_CEPHES_DP1.m128;
|
|
||||||
// let mut xmm2 = PS_MINUS_CEPHES_DP2.m128;
|
|
||||||
// let mut xmm3 = PS_MINUS_CEPHES_DP3.m128;
|
|
||||||
// xmm1 = _mm_mul_ps(y, xmm1);
|
|
||||||
// xmm2 = _mm_mul_ps(y, xmm2);
|
|
||||||
// xmm3 = _mm_mul_ps(y, xmm3);
|
|
||||||
// x = _mm_add_ps(x, xmm1);
|
|
||||||
// x = _mm_add_ps(x, xmm2);
|
|
||||||
// x = _mm_add_ps(x, xmm3);
|
|
||||||
|
|
||||||
// emm4 = _mm_sub_epi32(emm4, PI32_2.m128i);
|
|
||||||
// emm4 = _mm_andnot_si128(emm4, PI32_4.m128i);
|
|
||||||
// emm4 = _mm_slli_epi32(emm4, 29);
|
|
||||||
// let sign_bit_cos = _mm_castsi128_ps(emm4);
|
|
||||||
|
|
||||||
// sign_bit_sin = _mm_xor_ps(sign_bit_sin, swap_sign_bit_sin);
|
|
||||||
|
|
||||||
// // Evaluate the first polynom (0 <= x <= Pi/4)
|
|
||||||
// let z = _mm_mul_ps(x, x);
|
|
||||||
// y = PS_COSCOF_P0.m128;
|
|
||||||
|
|
||||||
// y = _mm_mul_ps(y, z);
|
|
||||||
// y = _mm_add_ps(y, PS_COSCOF_P1.m128);
|
|
||||||
// y = _mm_mul_ps(y, z);
|
|
||||||
// y = _mm_add_ps(y, PS_COSCOF_P2.m128);
|
|
||||||
// y = _mm_mul_ps(y, z);
|
|
||||||
// y = _mm_mul_ps(y, z);
|
|
||||||
// let tmp = _mm_mul_ps(z, PS_0_5.m128);
|
|
||||||
// y = _mm_sub_ps(y, tmp);
|
|
||||||
// y = _mm_add_ps(y, PS_1_0.m128);
|
|
||||||
|
|
||||||
// // Evaluate the second polynom (Pi/4 <= x <= 0)
|
|
||||||
// let mut y2 = PS_SINCOF_P0.m128;
|
|
||||||
// y2 = _mm_mul_ps(y2, z);
|
|
||||||
// y2 = _mm_add_ps(y2, PS_SINCOF_P1.m128);
|
|
||||||
// y2 = _mm_mul_ps(y2, z);
|
|
||||||
// y2 = _mm_add_ps(y2, PS_SINCOF_P2.m128);
|
|
||||||
// y2 = _mm_mul_ps(y2, z);
|
|
||||||
// y2 = _mm_mul_ps(y2, x);
|
|
||||||
// y2 = _mm_add_ps(y2, x);
|
|
||||||
|
|
||||||
// // select the correct result from the two polynoms
|
|
||||||
// xmm3 = poly_mask;
|
|
||||||
// let ysin2 = _mm_and_ps(xmm3, y2);
|
|
||||||
// let ysin1 = _mm_andnot_ps(xmm3, y);
|
|
||||||
// y2 = _mm_sub_ps(y2, ysin2);
|
|
||||||
// y = _mm_sub_ps(y, ysin1);
|
|
||||||
|
|
||||||
// xmm1 = _mm_add_ps(ysin1, ysin2);
|
|
||||||
// xmm2 = _mm_add_ps(y, y2);
|
|
||||||
|
|
||||||
// // update the sign
|
|
||||||
// (
|
|
||||||
// _mm_xor_ps(xmm1, sign_bit_sin),
|
|
||||||
// _mm_xor_ps(xmm2, sign_bit_cos),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
macro_rules! assert_approx_eq {
|
|
||||||
($a:expr, $b:expr) => {{
|
|
||||||
assert_approx_eq!($a, $b, core::f32::EPSILON);
|
|
||||||
}};
|
|
||||||
($a:expr, $b:expr, $eps:expr) => {{
|
|
||||||
let (a, b) = (&$a, &$b);
|
|
||||||
let eps = $eps;
|
|
||||||
assert!(
|
|
||||||
(a - b).abs() <= eps,
|
|
||||||
"assertion failed: `(left !== right)` \
|
|
||||||
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
|
|
||||||
*a,
|
|
||||||
*b,
|
|
||||||
eps,
|
|
||||||
(a - b).abs()
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
macro_rules! assert_relative_eq {
|
|
||||||
($a:expr, $b:expr) => {{
|
|
||||||
assert_relative_eq!($a, $b, core::f32::EPSILON);
|
|
||||||
}};
|
|
||||||
($a:expr, $b:expr, $eps:expr) => {{
|
|
||||||
let (a, b) = (&$a, &$b);
|
|
||||||
let eps = $eps;
|
|
||||||
let diff = (a - b).abs();
|
|
||||||
let largest = a.abs().max(b.abs());
|
|
||||||
assert!(
|
|
||||||
diff <= largest * eps,
|
|
||||||
"assertion failed: `(left !== right)` \
|
|
||||||
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
|
|
||||||
*a,
|
|
||||||
*b,
|
|
||||||
largest * eps,
|
|
||||||
diff
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_scalar_acos() {
|
|
||||||
fn test_scalar_acos_angle(a: f32) {
|
|
||||||
// 1e-6 is the lowest epsilon that will pass
|
|
||||||
assert_relative_eq!(scalar_acos(a), a.acos(), 1e-6);
|
|
||||||
// assert_approx_eq!(scalar_acos(a), a.acos(), 1e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
// test 1024 floats between -1.0 and 1.0 inclusive
|
|
||||||
const MAX_TESTS: u32 = 1024 / 2;
|
|
||||||
const SIGN: u32 = 0x80_00_00_00;
|
|
||||||
const PTVE_ONE: u32 = 0x3f_80_00_00; // 1.0_f32.to_bits();
|
|
||||||
const NGVE_ONE: u32 = SIGN | PTVE_ONE;
|
|
||||||
const STEP_SIZE: usize = (PTVE_ONE / MAX_TESTS) as usize;
|
|
||||||
for f in (SIGN..=NGVE_ONE)
|
|
||||||
.step_by(STEP_SIZE)
|
|
||||||
.map(|i| f32::from_bits(i))
|
|
||||||
{
|
|
||||||
test_scalar_acos_angle(f);
|
|
||||||
}
|
|
||||||
for f in (0..=PTVE_ONE).step_by(STEP_SIZE).map(|i| f32::from_bits(i)) {
|
|
||||||
test_scalar_acos_angle(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// input is clamped to -1.0..1.0
|
|
||||||
assert_approx_eq!(scalar_acos(2.0), 0.0);
|
|
||||||
assert_approx_eq!(scalar_acos(-2.0), core::f32::consts::PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_scalar_sin_cos() {
|
|
||||||
fn test_scalar_sin_cos_angle(a: f32) {
|
|
||||||
let (s1, c1) = scalar_sin_cos(a);
|
|
||||||
let (s2, c2) = a.sin_cos();
|
|
||||||
dbg!(a);
|
|
||||||
assert_approx_eq!(s1, s2);
|
|
||||||
assert_approx_eq!(c1, c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// test 1024 floats between -PI and PI inclusive
|
|
||||||
const MAX_TESTS: u32 = 1024 / 2;
|
|
||||||
const SIGN: u32 = 0x80_00_00_00;
|
|
||||||
let ptve_pi = core::f32::consts::PI.to_bits();
|
|
||||||
let ngve_pi = SIGN | ptve_pi;
|
|
||||||
let step_pi = (ptve_pi / MAX_TESTS) as usize;
|
|
||||||
for f in (SIGN..=ngve_pi).step_by(step_pi).map(|i| f32::from_bits(i)) {
|
|
||||||
test_scalar_sin_cos_angle(f);
|
|
||||||
}
|
|
||||||
for f in (0..=ptve_pi).step_by(step_pi).map(|i| f32::from_bits(i)) {
|
|
||||||
test_scalar_sin_cos_angle(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// test 1024 floats between -INF and +INF exclusive
|
|
||||||
let ptve_inf = core::f32::INFINITY.to_bits();
|
|
||||||
let ngve_inf = core::f32::NEG_INFINITY.to_bits();
|
|
||||||
let step_inf = (ptve_inf / MAX_TESTS) as usize;
|
|
||||||
for f in (SIGN..ngve_inf)
|
|
||||||
.step_by(step_inf)
|
|
||||||
.map(|i| f32::from_bits(i))
|
|
||||||
{
|
|
||||||
test_scalar_sin_cos_angle(f);
|
|
||||||
}
|
|
||||||
for f in (0..ptve_inf).step_by(step_inf).map(|i| f32::from_bits(i)) {
|
|
||||||
test_scalar_sin_cos_angle(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// +inf and -inf should return NaN
|
|
||||||
let (s, c) = scalar_sin_cos(core::f32::INFINITY);
|
|
||||||
assert!(s.is_nan());
|
|
||||||
assert!(c.is_nan());
|
|
||||||
|
|
||||||
let (s, c) = scalar_sin_cos(core::f32::NEG_INFINITY);
|
|
||||||
assert!(s.is_nan());
|
|
||||||
assert!(c.is_nan());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
fn test_sse2_m128_sin() {
|
|
||||||
use crate::Vec4;
|
|
||||||
use core::f32::consts::PI;
|
|
||||||
|
|
||||||
fn test_sse2_m128_sin_angle(a: f32) {
|
|
||||||
let v = Vec4::splat(a);
|
|
||||||
let v = unsafe { Vec4(sse2::m128_sin(v.0)) };
|
|
||||||
let a_sin = a.sin();
|
|
||||||
dbg!((a, a_sin, v));
|
|
||||||
assert_approx_eq!(v.x(), a_sin, 1e-6);
|
|
||||||
assert_approx_eq!(v.z(), a_sin, 1e-6);
|
|
||||||
assert_approx_eq!(v.y(), a_sin, 1e-6);
|
|
||||||
assert_approx_eq!(v.w(), a_sin, 1e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut a = -PI;
|
|
||||||
let end = PI;
|
|
||||||
let step = PI / 8192.0;
|
|
||||||
|
|
||||||
while a <= end {
|
|
||||||
test_sse2_m128_sin_angle(a);
|
|
||||||
a += step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sse2::m128_sin is derived from the XMVectorSin in DirectXMath. It's been
|
|
||||||
// observed both here and in the C++ version that the error rate increases
|
|
||||||
// as the input angle drifts further from the bounds of PI.
|
|
||||||
//
|
|
||||||
// #[test]
|
|
||||||
// #[cfg(vec4sse2)]
|
|
||||||
// fn test_sse2_m128_sin2() {
|
|
||||||
// use crate::Vec4;
|
|
||||||
|
|
||||||
// fn test_sse2_m128_sin_angle(a: f32) -> f32 {
|
|
||||||
// let v = Vec4::splat(a);
|
|
||||||
// let v = unsafe { Vec4(sse2::m128_sin(v.0)) };
|
|
||||||
// let a_sin = a.sin();
|
|
||||||
// let v_sin = v.x();
|
|
||||||
// // println!("{:?}", (a, a_sin, v_sin));
|
|
||||||
// assert_approx_eq!(a_sin, v.x(), 1e-4);
|
|
||||||
// assert_approx_eq!(a_sin, v.z(), 1e-4);
|
|
||||||
// assert_approx_eq!(a_sin, v.y(), 1e-4);
|
|
||||||
// assert_approx_eq!(a_sin, v.w(), 1e-4);
|
|
||||||
// v.x()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // test 1024 floats between -PI and PI inclusive
|
|
||||||
// const MAX_TESTS: u32 = 1024 / 2;
|
|
||||||
// const SIGN: u32 = 0x80_00_00_00;
|
|
||||||
// let ptve_pi = std::f32::consts::PI.to_bits();
|
|
||||||
// let ngve_pi = SIGN | ptve_pi;
|
|
||||||
// let step_pi = (ptve_pi / MAX_TESTS) as usize;
|
|
||||||
// for f in (SIGN..=ngve_pi).step_by(step_pi).map(|i| f32::from_bits(i)) {
|
|
||||||
// test_sse2_m128_sin_angle(f);
|
|
||||||
// }
|
|
||||||
// for f in (0..=ptve_pi).step_by(step_pi).map(|i| f32::from_bits(i)) {
|
|
||||||
// test_sse2_m128_sin_angle(f);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // test 1024 floats between -INF and +INF exclusive
|
|
||||||
// let ptve_inf = std::f32::INFINITY.to_bits();
|
|
||||||
// let ngve_inf = std::f32::NEG_INFINITY.to_bits();
|
|
||||||
// let step_inf = (ptve_inf / MAX_TESTS) as usize;
|
|
||||||
// for f in (SIGN..ngve_inf)
|
|
||||||
// .step_by(step_inf)
|
|
||||||
// .map(|i| f32::from_bits(i))
|
|
||||||
// {
|
|
||||||
// test_sse2_m128_sin_angle(f);
|
|
||||||
// }
|
|
||||||
// for f in (0..ptve_inf).step_by(step_inf).map(|i| f32::from_bits(i)) {
|
|
||||||
// test_sse2_m128_sin_angle(f);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // +inf and -inf should return NaN
|
|
||||||
// let s = test_sse2_m128_sin_angle(std::f32::INFINITY);
|
|
||||||
// assert!(s.is_nan());
|
|
||||||
|
|
||||||
// let s = test_sse2_m128_sin_angle(std::f32::NEG_INFINITY);
|
|
||||||
// assert!(s.is_nan());
|
|
||||||
// }
|
|
|
@ -1,309 +0,0 @@
|
||||||
use super::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
|
|
||||||
use mint;
|
|
||||||
|
|
||||||
impl From<mint::Point2<f32>> for Vec2 {
|
|
||||||
fn from(v: mint::Point2<f32>) -> Self {
|
|
||||||
Self::new(v.x, v.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec2> for mint::Point2<f32> {
|
|
||||||
fn from(v: Vec2) -> Self {
|
|
||||||
let (x, y) = v.into();
|
|
||||||
Self { x, y }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::Point3<f32>> for Vec3 {
|
|
||||||
fn from(v: mint::Point3<f32>) -> Self {
|
|
||||||
Self::new(v.x, v.y, v.z)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec3> for mint::Point3<f32> {
|
|
||||||
fn from(v: Vec3) -> Self {
|
|
||||||
let (x, y, z) = v.into();
|
|
||||||
Self { x, y, z }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::Vector2<f32>> for Vec2 {
|
|
||||||
fn from(v: mint::Vector2<f32>) -> Self {
|
|
||||||
Self::new(v.x, v.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec2> for mint::Vector2<f32> {
|
|
||||||
fn from(v: Vec2) -> Self {
|
|
||||||
let (x, y) = v.into();
|
|
||||||
Self { x, y }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::Vector3<f32>> for Vec3 {
|
|
||||||
fn from(v: mint::Vector3<f32>) -> Self {
|
|
||||||
Self::new(v.x, v.y, v.z)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec3> for mint::Vector3<f32> {
|
|
||||||
fn from(v: Vec3) -> Self {
|
|
||||||
let (x, y, z) = v.into();
|
|
||||||
Self { x, y, z }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::Vector4<f32>> for Vec4 {
|
|
||||||
fn from(v: mint::Vector4<f32>) -> Self {
|
|
||||||
Self::new(v.x, v.y, v.z, v.w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec4> for mint::Vector4<f32> {
|
|
||||||
fn from(v: Vec4) -> Self {
|
|
||||||
let (x, y, z, w) = v.into();
|
|
||||||
Self { x, y, z, w }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::Quaternion<f32>> for Quat {
|
|
||||||
fn from(q: mint::Quaternion<f32>) -> Self {
|
|
||||||
Self::from_xyzw(q.v.x, q.v.y, q.v.z, q.s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Quat> for mint::Quaternion<f32> {
|
|
||||||
fn from(q: Quat) -> Self {
|
|
||||||
let (x, y, z, s) = q.into();
|
|
||||||
Self {
|
|
||||||
s,
|
|
||||||
v: mint::Vector3 { x, y, z },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::RowMatrix2<f32>> for Mat2 {
|
|
||||||
fn from(m: mint::RowMatrix2<f32>) -> Self {
|
|
||||||
Self::from_cols(m.x.into(), m.y.into()).transpose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Mat2> for mint::RowMatrix2<f32> {
|
|
||||||
fn from(m: Mat2) -> Self {
|
|
||||||
let mt = m.transpose();
|
|
||||||
Self {
|
|
||||||
x: mt.x_axis().into(),
|
|
||||||
y: mt.y_axis().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::ColumnMatrix2<f32>> for Mat2 {
|
|
||||||
fn from(m: mint::ColumnMatrix2<f32>) -> Self {
|
|
||||||
Self::from_cols(m.x.into(), m.y.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Mat2> for mint::ColumnMatrix2<f32> {
|
|
||||||
fn from(m: Mat2) -> Self {
|
|
||||||
Self {
|
|
||||||
x: m.x_axis().into(),
|
|
||||||
y: m.y_axis().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::RowMatrix3<f32>> for Mat3 {
|
|
||||||
fn from(m: mint::RowMatrix3<f32>) -> Self {
|
|
||||||
Self::from_cols(m.x.into(), m.y.into(), m.z.into()).transpose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Mat3> for mint::RowMatrix3<f32> {
|
|
||||||
fn from(m: Mat3) -> Self {
|
|
||||||
let mt = m.transpose();
|
|
||||||
Self {
|
|
||||||
x: mt.x_axis().into(),
|
|
||||||
y: mt.y_axis().into(),
|
|
||||||
z: mt.z_axis().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::ColumnMatrix3<f32>> for Mat3 {
|
|
||||||
fn from(m: mint::ColumnMatrix3<f32>) -> Self {
|
|
||||||
Self::from_cols(m.x.into(), m.y.into(), m.z.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Mat3> for mint::ColumnMatrix3<f32> {
|
|
||||||
fn from(m: Mat3) -> Self {
|
|
||||||
Self {
|
|
||||||
x: m.x_axis().into(),
|
|
||||||
y: m.y_axis().into(),
|
|
||||||
z: m.z_axis().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::RowMatrix4<f32>> for Mat4 {
|
|
||||||
fn from(m: mint::RowMatrix4<f32>) -> Self {
|
|
||||||
Self::from_cols(m.x.into(), m.y.into(), m.z.into(), m.w.into()).transpose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Mat4> for mint::RowMatrix4<f32> {
|
|
||||||
fn from(m: Mat4) -> Self {
|
|
||||||
let mt = m.transpose();
|
|
||||||
Self {
|
|
||||||
x: mt.x_axis().into(),
|
|
||||||
y: mt.y_axis().into(),
|
|
||||||
z: mt.z_axis().into(),
|
|
||||||
w: mt.w_axis().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<mint::ColumnMatrix4<f32>> for Mat4 {
|
|
||||||
fn from(m: mint::ColumnMatrix4<f32>) -> Self {
|
|
||||||
Self::from_cols(m.x.into(), m.y.into(), m.z.into(), m.w.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Mat4> for mint::ColumnMatrix4<f32> {
|
|
||||||
fn from(m: Mat4) -> Self {
|
|
||||||
Self {
|
|
||||||
x: m.x_axis().into(),
|
|
||||||
y: m.y_axis().into(),
|
|
||||||
z: m.z_axis().into(),
|
|
||||||
w: m.w_axis().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use mint;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_point2() {
|
|
||||||
use crate::Vec2;
|
|
||||||
let m = mint::Point2 { x: 1.0, y: 2.0 };
|
|
||||||
let g = Vec2::from(m);
|
|
||||||
assert_eq!(g, Vec2::new(1.0, 2.0));
|
|
||||||
assert_eq!(m, g.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_point3() {
|
|
||||||
use crate::Vec3;
|
|
||||||
let m = mint::Point3 {
|
|
||||||
x: 1.0,
|
|
||||||
y: 2.0,
|
|
||||||
z: 3.0,
|
|
||||||
};
|
|
||||||
let g = Vec3::from(m);
|
|
||||||
assert_eq!(g, Vec3::new(1.0, 2.0, 3.0));
|
|
||||||
assert_eq!(m, g.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vector2() {
|
|
||||||
use crate::Vec2;
|
|
||||||
let m = mint::Vector2 { x: 1.0, y: 2.0 };
|
|
||||||
let g = Vec2::from(m);
|
|
||||||
assert_eq!(g, Vec2::new(1.0, 2.0));
|
|
||||||
assert_eq!(m, g.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vector3() {
|
|
||||||
use crate::Vec3;
|
|
||||||
let m = mint::Vector3 {
|
|
||||||
x: 1.0,
|
|
||||||
y: 2.0,
|
|
||||||
z: 3.0,
|
|
||||||
};
|
|
||||||
let g = Vec3::from(m);
|
|
||||||
assert_eq!(g, Vec3::new(1.0, 2.0, 3.0));
|
|
||||||
assert_eq!(m, g.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vector4() {
|
|
||||||
use crate::Vec4;
|
|
||||||
let m = mint::Vector4 {
|
|
||||||
x: 1.0,
|
|
||||||
y: 2.0,
|
|
||||||
z: 3.0,
|
|
||||||
w: 4.0,
|
|
||||||
};
|
|
||||||
let g = Vec4::from(m);
|
|
||||||
assert_eq!(g, Vec4::new(1.0, 2.0, 3.0, 4.0));
|
|
||||||
assert_eq!(m, g.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quaternion() {
|
|
||||||
use crate::Quat;
|
|
||||||
let m = mint::Quaternion {
|
|
||||||
v: mint::Vector3 {
|
|
||||||
x: 1.0,
|
|
||||||
y: 2.0,
|
|
||||||
z: 3.0,
|
|
||||||
},
|
|
||||||
s: 4.0,
|
|
||||||
};
|
|
||||||
let g = Quat::from(m);
|
|
||||||
assert_eq!(g, Quat::from((1.0, 2.0, 3.0, 4.0)));
|
|
||||||
assert_eq!(m, g.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_matrix2() {
|
|
||||||
use crate::Mat2;
|
|
||||||
let g = Mat2::from_cols_array_2d(&[[1.0, 2.0], [3.0, 4.0]]);
|
|
||||||
let m = mint::ColumnMatrix2::from(g);
|
|
||||||
assert_eq!(g, Mat2::from(m));
|
|
||||||
let mt = mint::RowMatrix2::from(g);
|
|
||||||
assert_eq!(mt, mint::RowMatrix2::from([[1.0, 3.0], [2.0, 4.0]]));
|
|
||||||
assert_eq!(g, Mat2::from(mt));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_matrix3() {
|
|
||||||
use crate::Mat3;
|
|
||||||
let g = Mat3::from_cols_array_2d(&[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
|
|
||||||
let m = mint::ColumnMatrix3::from(g);
|
|
||||||
assert_eq!(g, Mat3::from(m));
|
|
||||||
let mt = mint::RowMatrix3::from(g);
|
|
||||||
assert_eq!(
|
|
||||||
mt,
|
|
||||||
mint::RowMatrix3::from([[1.0, 4.0, 7.0], [2.0, 5.0, 8.0], [3.0, 6.0, 9.0]])
|
|
||||||
);
|
|
||||||
assert_eq!(g, Mat3::from(mt));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_matrix4() {
|
|
||||||
use crate::Mat4;
|
|
||||||
let g = Mat4::from_cols_array_2d(&[
|
|
||||||
[1.0, 2.0, 3.0, 4.0],
|
|
||||||
[5.0, 6.0, 7.0, 8.0],
|
|
||||||
[9.0, 10.0, 11.0, 12.0],
|
|
||||||
[13.0, 14.0, 15.0, 16.0],
|
|
||||||
]);
|
|
||||||
let m = mint::ColumnMatrix4::from(g);
|
|
||||||
assert_eq!(g, Mat4::from(m));
|
|
||||||
let mt = mint::RowMatrix4::from(g);
|
|
||||||
assert_eq!(
|
|
||||||
mt,
|
|
||||||
mint::RowMatrix4::from([
|
|
||||||
[1.0, 5.0, 9.0, 13.0],
|
|
||||||
[2.0, 6.0, 10.0, 14.0],
|
|
||||||
[3.0, 7.0, 11.0, 15.0],
|
|
||||||
[4.0, 8.0, 12.0, 16.0]
|
|
||||||
])
|
|
||||||
);
|
|
||||||
assert_eq!(g, Mat4::from(mt));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
use super::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
|
|
||||||
|
|
||||||
use rand::{
|
|
||||||
distributions::{Distribution, Standard},
|
|
||||||
Rng,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Distribution<Mat2> for Standard {
|
|
||||||
#[inline]
|
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Mat2 {
|
|
||||||
Mat2::from_cols_array(&rng.gen())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Distribution<Mat3> for Standard {
|
|
||||||
#[inline]
|
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Mat3 {
|
|
||||||
Mat3::from_cols_array(&rng.gen())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Distribution<Mat4> for Standard {
|
|
||||||
#[inline]
|
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Mat4 {
|
|
||||||
Mat4::from_cols_array(&rng.gen())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Distribution<Quat> for Standard {
|
|
||||||
#[inline]
|
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Quat {
|
|
||||||
use core::f32::consts::PI;
|
|
||||||
let yaw = -PI + rng.gen::<f32>() * 2.0 * PI;
|
|
||||||
let pitch = -PI + rng.gen::<f32>() * 2.0 * PI;
|
|
||||||
let roll = -PI + rng.gen::<f32>() * 2.0 * PI;
|
|
||||||
Quat::from_rotation_ypr(yaw, pitch, roll)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Distribution<Vec2> for Standard {
|
|
||||||
#[inline]
|
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec2 {
|
|
||||||
rng.gen::<(f32, f32)>().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Distribution<Vec3> for Standard {
|
|
||||||
#[inline]
|
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec3 {
|
|
||||||
rng.gen::<(f32, f32, f32)>().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Distribution<Vec4> for Standard {
|
|
||||||
#[inline]
|
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec4 {
|
|
||||||
rng.gen::<[f32; 4]>().into()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,385 +0,0 @@
|
||||||
use crate::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
|
|
||||||
use core::fmt;
|
|
||||||
use serde::{
|
|
||||||
de::{self, Deserialize, Deserializer, SeqAccess, Visitor},
|
|
||||||
ser::{Serialize, SerializeTupleStruct, Serializer},
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Serialize for Vec2 {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let (x, y) = (*self).into();
|
|
||||||
let mut state = serializer.serialize_tuple_struct("Vec2", 2)?;
|
|
||||||
state.serialize_field(&x)?;
|
|
||||||
state.serialize_field(&y)?;
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl Serialize for Vec3 {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let (x, y, z) = (*self).into();
|
|
||||||
// 3 is the number of fields in the struct.
|
|
||||||
let mut state = serializer.serialize_tuple_struct("Vec3", 3)?;
|
|
||||||
state.serialize_field(&x)?;
|
|
||||||
state.serialize_field(&y)?;
|
|
||||||
state.serialize_field(&z)?;
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl Serialize for Vec4 {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let (x, y, z, w) = (*self).into();
|
|
||||||
// 4 is the number of fields in the struct.
|
|
||||||
let mut state = serializer.serialize_tuple_struct("Vec4", 4)?;
|
|
||||||
state.serialize_field(&x)?;
|
|
||||||
state.serialize_field(&y)?;
|
|
||||||
state.serialize_field(&z)?;
|
|
||||||
state.serialize_field(&w)?;
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl Serialize for Quat {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let (x, y, z, w) = (*self).into();
|
|
||||||
// 4 is the number of fields in the struct.
|
|
||||||
let mut state = serializer.serialize_tuple_struct("Quat", 4)?;
|
|
||||||
state.serialize_field(&x)?;
|
|
||||||
state.serialize_field(&y)?;
|
|
||||||
state.serialize_field(&z)?;
|
|
||||||
state.serialize_field(&w)?;
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl Serialize for Mat2 {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let f: &[f32; 4] = self.as_ref();
|
|
||||||
let mut state = serializer.serialize_tuple_struct("Mat2", 4)?;
|
|
||||||
state.serialize_field(&f[0])?;
|
|
||||||
state.serialize_field(&f[1])?;
|
|
||||||
state.serialize_field(&f[2])?;
|
|
||||||
state.serialize_field(&f[3])?;
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl Serialize for Mat3 {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let (m00, m01, m02) = self.x_axis.into();
|
|
||||||
let (m10, m11, m12) = self.y_axis.into();
|
|
||||||
let (m20, m21, m22) = self.z_axis.into();
|
|
||||||
|
|
||||||
let mut state = serializer.serialize_tuple_struct("Mat3", 9)?;
|
|
||||||
state.serialize_field(&m00)?;
|
|
||||||
state.serialize_field(&m01)?;
|
|
||||||
state.serialize_field(&m02)?;
|
|
||||||
state.serialize_field(&m10)?;
|
|
||||||
state.serialize_field(&m11)?;
|
|
||||||
state.serialize_field(&m12)?;
|
|
||||||
state.serialize_field(&m20)?;
|
|
||||||
state.serialize_field(&m21)?;
|
|
||||||
state.serialize_field(&m22)?;
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl Serialize for Mat4 {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let mut state = serializer.serialize_tuple_struct("Mat4", 16)?;
|
|
||||||
for f in self.as_ref() {
|
|
||||||
state.serialize_field(f)?;
|
|
||||||
}
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Vec2 {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct Vec2Visitor;
|
|
||||||
|
|
||||||
// TODO: Not sure why this line is reported as uncovered
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
impl<'de> Visitor<'de> for Vec2Visitor {
|
|
||||||
type Value = Vec2;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("struct Vec2")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<V>(self, mut seq: V) -> Result<Vec2, V::Error>
|
|
||||||
where
|
|
||||||
V: SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let x = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
|
|
||||||
let y = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
|
|
||||||
Ok(Vec2::new(x, y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_tuple_struct("Vec2", 2, Vec2Visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Vec3 {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct Vec3Visitor;
|
|
||||||
|
|
||||||
// TODO: Not sure why this line is reported as uncovered
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
impl<'de> Visitor<'de> for Vec3Visitor {
|
|
||||||
type Value = Vec3;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("struct Vec2")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<V>(self, mut seq: V) -> Result<Vec3, V::Error>
|
|
||||||
where
|
|
||||||
V: SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let x = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
|
|
||||||
let y = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
|
|
||||||
let z = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
|
|
||||||
Ok(Vec3::new(x, y, z))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_tuple_struct("Vec3", 3, Vec3Visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Vec4 {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct Vec4Visitor;
|
|
||||||
|
|
||||||
// TODO: Not sure why this line is reported as uncovered
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
impl<'de> Visitor<'de> for Vec4Visitor {
|
|
||||||
type Value = Vec4;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("struct Vec2")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<V>(self, mut seq: V) -> Result<Vec4, V::Error>
|
|
||||||
where
|
|
||||||
V: SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let x = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
|
|
||||||
let y = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
|
|
||||||
let z = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
|
|
||||||
let w = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(3, &self))?;
|
|
||||||
Ok(Vec4::new(x, y, z, w))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_tuple_struct("Vec4", 4, Vec4Visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Quat {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct QuatVisitor;
|
|
||||||
|
|
||||||
// TODO: Not sure why this line is reported as uncovered
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
impl<'de> Visitor<'de> for QuatVisitor {
|
|
||||||
type Value = Quat;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("struct Vec2")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<V>(self, mut seq: V) -> Result<Quat, V::Error>
|
|
||||||
where
|
|
||||||
V: SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let x = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
|
|
||||||
let y = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
|
|
||||||
let z = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
|
|
||||||
let w = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(3, &self))?;
|
|
||||||
Ok(Quat::from_xyzw(x, y, z, w))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_tuple_struct("Quat", 4, QuatVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Mat2 {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct Mat2Visitor;
|
|
||||||
|
|
||||||
// TODO: Not sure why this line is reported as uncovered
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
impl<'de> Visitor<'de> for Mat2Visitor {
|
|
||||||
type Value = Mat2;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("struct Mat2")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<V>(self, mut seq: V) -> Result<Mat2, V::Error>
|
|
||||||
where
|
|
||||||
V: SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut f = { [0.0; 4] };
|
|
||||||
for i in 0..4 {
|
|
||||||
f[i] = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(i, &self))?;
|
|
||||||
}
|
|
||||||
let x = Vec2::new(f[0], f[1]);
|
|
||||||
let y = Vec2::new(f[2], f[3]);
|
|
||||||
Ok(Mat2::from_cols(x, y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_tuple_struct("Mat2", 4, Mat2Visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Mat3 {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct Mat3Visitor;
|
|
||||||
|
|
||||||
// TODO: Not sure why this line is reported as uncovered
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
impl<'de> Visitor<'de> for Mat3Visitor {
|
|
||||||
type Value = Mat3;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("struct Mat3")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<V>(self, mut seq: V) -> Result<Mat3, V::Error>
|
|
||||||
where
|
|
||||||
V: SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut f = { [0.0; 9] };
|
|
||||||
for i in 0..9 {
|
|
||||||
f[i] = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(i, &self))?;
|
|
||||||
}
|
|
||||||
let x = Vec3::new(f[0], f[1], f[2]);
|
|
||||||
let y = Vec3::new(f[3], f[4], f[5]);
|
|
||||||
let z = Vec3::new(f[6], f[7], f[8]);
|
|
||||||
Ok(Mat3::from_cols(x, y, z))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_tuple_struct("Mat3", 9, Mat3Visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Mat4 {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct Mat4Visitor;
|
|
||||||
|
|
||||||
// TODO: Not sure why this line is reported as uncovered
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
impl<'de> Visitor<'de> for Mat4Visitor {
|
|
||||||
type Value = Mat4;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("struct Mat4")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_seq<V>(self, mut seq: V) -> Result<Mat4, V::Error>
|
|
||||||
where
|
|
||||||
V: SeqAccess<'de>,
|
|
||||||
{
|
|
||||||
let mut f = { [0.0; 16] };
|
|
||||||
for i in 0..16 {
|
|
||||||
f[i] = seq
|
|
||||||
.next_element()?
|
|
||||||
.ok_or_else(|| de::Error::invalid_length(i, &self))?;
|
|
||||||
}
|
|
||||||
let x = Vec4::new(f[0], f[1], f[2], f[3]);
|
|
||||||
let y = Vec4::new(f[4], f[5], f[6], f[7]);
|
|
||||||
let z = Vec4::new(f[8], f[9], f[10], f[11]);
|
|
||||||
let w = Vec4::new(f[12], f[13], f[14], f[15]);
|
|
||||||
Ok(Mat4::from_cols(x, y, z, w))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_tuple_struct("Mat4", 16, Mat4Visitor)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
use crate::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
|
|
||||||
use zerocopy::{AsBytes, FromBytes};
|
|
||||||
|
|
||||||
unsafe impl AsBytes for Vec2 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl FromBytes for Vec2 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl AsBytes for Vec3 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl FromBytes for Vec3 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl AsBytes for Vec4 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl FromBytes for Vec4 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl AsBytes for Mat2 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl FromBytes for Mat2 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl AsBytes for Mat3 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl FromBytes for Mat3 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl AsBytes for Mat4 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl FromBytes for Mat4 {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl AsBytes for Quat {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl FromBytes for Quat {
|
|
||||||
fn only_derive_is_allowed_to_implement_this_trait()
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,335 +0,0 @@
|
||||||
use super::{scalar_sin_cos, Vec2, Vec4};
|
|
||||||
#[cfg(all(vec4sse2, target_arch = "x86",))]
|
|
||||||
use core::arch::x86::*;
|
|
||||||
#[cfg(all(vec4sse2, target_arch = "x86_64",))]
|
|
||||||
use core::arch::x86_64::*;
|
|
||||||
use core::{
|
|
||||||
fmt,
|
|
||||||
ops::{Add, Mul, Sub},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mat2(x_axis: Vec2, y_axis: Vec2) -> Mat2 {
|
|
||||||
Mat2::from_cols(x_axis, y_axis)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A 2x2 column major matrix.
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Mat2(pub(crate) Vec4);
|
|
||||||
|
|
||||||
impl Default for Mat2 {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::identity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Mat2 {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "[{}, {}]", self.x_axis(), self.y_axis())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mat2 {
|
|
||||||
/// Creates a 2x2 matrix with all elements set to `0.0`.
|
|
||||||
#[inline]
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
Mat2(Vec4::zero())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 2x2 identity matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn identity() -> Self {
|
|
||||||
Self(Vec4::new(1.0, 0.0, 0.0, 1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 2x2 matrix from four column vectors.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_cols(x_axis: Vec2, y_axis: Vec2) -> Self {
|
|
||||||
Self(Vec4::new(x_axis.x(), x_axis.y(), y_axis.x(), y_axis.y()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 2x2 matrix from a `[f32; 4]` stored in column major order. If
|
|
||||||
/// your data is stored in row major you will need to `transpose` the
|
|
||||||
/// returned matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_cols_array(m: &[f32; 4]) -> Self {
|
|
||||||
Mat2(Vec4::new(m[0], m[1], m[2], m[3]))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `[f32; 4]` storing data in column major order.
|
|
||||||
/// If you require data in row major order `transpose` the matrix first.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_cols_array(&self) -> [f32; 4] {
|
|
||||||
self.0.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 2x2 matrix from a `[[f32; 2]; 2]` stored in column major
|
|
||||||
/// order. If your data is in row major order you will need to `transpose`
|
|
||||||
/// the returned matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_cols_array_2d(m: &[[f32; 2]; 2]) -> Self {
|
|
||||||
Mat2(Vec4::new(m[0][0], m[0][1], m[1][0], m[1][1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `[[f32; 2]; 2]` storing data in column major order.
|
|
||||||
/// If you require data in row major order `transpose` the matrix first.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
|
|
||||||
let (x0, y0, x1, y1) = self.0.into();
|
|
||||||
[[x0, y0], [x1, y1]]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 2x2 matrix containing the given `scale` and rotation of
|
|
||||||
/// `angle` (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_scale_angle(scale: Vec2, angle: f32) -> Self {
|
|
||||||
let (sin, cos) = scalar_sin_cos(angle);
|
|
||||||
let (scale_x, scale_y) = scale.into();
|
|
||||||
Self(Vec4::new(
|
|
||||||
cos * scale_x,
|
|
||||||
sin * scale_x,
|
|
||||||
-sin * scale_y,
|
|
||||||
cos * scale_y,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 2x2 matrix containing a rotation of `angle` (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_angle(angle: f32) -> Self {
|
|
||||||
let (sin, cos) = scalar_sin_cos(angle);
|
|
||||||
Self(Vec4::new(cos, sin, -sin, cos))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 2x2 matrix containing the given non-uniform `scale`.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_scale(scale: Vec2) -> Self {
|
|
||||||
let (x, y) = scale.into();
|
|
||||||
Self(Vec4::new(x, 0.0, 0.0, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_x_axis(&mut self, x: Vec2) {
|
|
||||||
let m = self.0.as_mut();
|
|
||||||
m[0] = x.x();
|
|
||||||
m[1] = x.y();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_y_axis(&mut self, y: Vec2) {
|
|
||||||
let m = self.0.as_mut();
|
|
||||||
m[2] = y.x();
|
|
||||||
m[3] = y.y();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn x_axis(&self) -> Vec2 {
|
|
||||||
let (x, y, _, _) = self.0.into();
|
|
||||||
Vec2::new(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn y_axis(&self) -> Vec2 {
|
|
||||||
let (_, _, x, y) = self.0.into();
|
|
||||||
Vec2::new(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[inline]
|
|
||||||
// pub(crate) fn col(&self, index: usize) -> Vec2 {
|
|
||||||
// match index {
|
|
||||||
// 0 => self.x_axis(),
|
|
||||||
// 1 => self.y_axis(),
|
|
||||||
// _ => panic!(
|
|
||||||
// "index out of bounds: the len is 2 but the index is {}",
|
|
||||||
// index
|
|
||||||
// ),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[inline]
|
|
||||||
// pub(crate) fn col_mut(&mut self, index: usize) -> &mut Vec2 {
|
|
||||||
// match index {
|
|
||||||
// 0 => unsafe { &mut *(self.0.as_mut().as_mut_ptr() as *mut Vec2) },
|
|
||||||
// 1 => unsafe { &mut *(self.0.as_mut()[2..].as_mut_ptr() as *mut Vec2) },
|
|
||||||
// _ => panic!(
|
|
||||||
// "index out of bounds: the len is 2 but the index is {}",
|
|
||||||
// index
|
|
||||||
// ),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Returns the transpose of `self`.
|
|
||||||
#[inline]
|
|
||||||
pub fn transpose(&self) -> Self {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
let abcd = self.0.into();
|
|
||||||
let acbd = _mm_shuffle_ps(abcd, abcd, 0b11_01_10_00);
|
|
||||||
Self(acbd.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
let (m00, m01, m10, m11) = self.0.into();
|
|
||||||
Self(Vec4::new(m00, m10, m01, m11))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the determinant of `self`.
|
|
||||||
#[inline]
|
|
||||||
pub fn determinant(&self) -> f32 {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
let abcd = self.0.into();
|
|
||||||
let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11);
|
|
||||||
let prod = _mm_mul_ps(abcd, dcba);
|
|
||||||
let det = _mm_sub_ps(prod, _mm_shuffle_ps(prod, prod, 0b01_01_01_01));
|
|
||||||
_mm_cvtss_f32(det)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
let (a, b, c, d) = self.0.into();
|
|
||||||
a * d - b * c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inverse of `self`.
|
|
||||||
///
|
|
||||||
/// If the matrix is not invertible the returned matrix will be invalid.
|
|
||||||
#[inline]
|
|
||||||
pub fn inverse(&self) -> Self {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
let abcd = self.0.into();
|
|
||||||
let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11);
|
|
||||||
let prod = _mm_mul_ps(abcd, dcba);
|
|
||||||
let sub = _mm_sub_ps(prod, _mm_shuffle_ps(prod, prod, 0b01_01_01_01));
|
|
||||||
let det = _mm_shuffle_ps(sub, sub, 0b00_00_00_00);
|
|
||||||
let tmp = _mm_div_ps(_mm_set_ps(1.0, -1.0, -1.0, 1.0), det);
|
|
||||||
let dbca = _mm_shuffle_ps(abcd, abcd, 0b00_10_01_11);
|
|
||||||
Self(_mm_mul_ps(dbca, tmp).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
let (a, b, c, d) = self.0.into();
|
|
||||||
let det = a * d - b * c;
|
|
||||||
glam_assert!(det != 0.0);
|
|
||||||
let tmp = Vec4::new(1.0, -1.0, -1.0, 1.0) / det;
|
|
||||||
Self(Vec4::new(d, b, c, a) * tmp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_vec2(&self, other: Vec2) -> Vec2 {
|
|
||||||
// TODO: SSE2
|
|
||||||
let other = Vec4::new(other.x(), other.x(), other.y(), other.y());
|
|
||||||
let tmp = self.0 * other;
|
|
||||||
let (x0, y0, x1, y1) = tmp.into();
|
|
||||||
Vec2::new(x0 + x1, y0 + y1)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_mat2(&self, other: &Self) -> Self {
|
|
||||||
// TODO: SSE2
|
|
||||||
let (x0, y0, x1, y1) = other.0.into();
|
|
||||||
Mat2::from_cols(
|
|
||||||
self.mul_vec2(Vec2::new(x0, y0)),
|
|
||||||
self.mul_vec2(Vec2::new(x1, y1)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn add_mat2(&self, other: &Self) -> Self {
|
|
||||||
Mat2(self.0 + other.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn sub_mat2(&self, other: &Self) -> Self {
|
|
||||||
Mat2(self.0 - other.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_scalar(&self, other: f32) -> Self {
|
|
||||||
let s = Vec4::splat(other);
|
|
||||||
Mat2(self.0 * s)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the absolute difference of all elements between `self`
|
|
||||||
/// and `other` is less than or equal to `max_abs_diff`.
|
|
||||||
///
|
|
||||||
/// This can be used to compare if two `Mat2`'s contain similar elements. It
|
|
||||||
/// works best when comparing with a known value. The `max_abs_diff` that
|
|
||||||
/// should be used used depends on the values being compared against.
|
|
||||||
///
|
|
||||||
/// For more on floating point comparisons see
|
|
||||||
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
|
||||||
#[inline]
|
|
||||||
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
|
|
||||||
self.0.abs_diff_eq(other.0, max_abs_diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[f32; 4]> for Mat2 {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &[f32; 4] {
|
|
||||||
unsafe { &*(self as *const Self as *const [f32; 4]) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<[f32; 4]> for Mat2 {
|
|
||||||
#[inline]
|
|
||||||
fn as_mut(&mut self) -> &mut [f32; 4] {
|
|
||||||
unsafe { &mut *(self as *mut Self as *mut [f32; 4]) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<Mat2> for Mat2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn add(self, other: Self) -> Self {
|
|
||||||
self.add_mat2(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<Mat2> for Mat2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn sub(self, other: Self) -> Self {
|
|
||||||
self.sub_mat2(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Mat2> for Mat2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Self) -> Self {
|
|
||||||
self.mul_mat2(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec2> for Mat2 {
|
|
||||||
type Output = Vec2;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Vec2) -> Vec2 {
|
|
||||||
self.mul_vec2(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Mat2> for f32 {
|
|
||||||
type Output = Mat2;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Mat2) -> Mat2 {
|
|
||||||
other.mul_scalar(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<f32> for Mat2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: f32) -> Self {
|
|
||||||
self.mul_scalar(other)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,482 +0,0 @@
|
||||||
use super::{scalar_sin_cos, Quat, Vec2, Vec3};
|
|
||||||
use core::{
|
|
||||||
fmt,
|
|
||||||
ops::{Add, Mul, Sub},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mat3(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3) -> Mat3 {
|
|
||||||
Mat3 {
|
|
||||||
x_axis,
|
|
||||||
y_axis,
|
|
||||||
z_axis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn quat_to_axes(rotation: Quat) -> (Vec3, Vec3, Vec3) {
|
|
||||||
glam_assert!(rotation.is_normalized());
|
|
||||||
let (x, y, z, w) = rotation.into();
|
|
||||||
let x2 = x + x;
|
|
||||||
let y2 = y + y;
|
|
||||||
let z2 = z + z;
|
|
||||||
let xx = x * x2;
|
|
||||||
let xy = x * y2;
|
|
||||||
let xz = x * z2;
|
|
||||||
let yy = y * y2;
|
|
||||||
let yz = y * z2;
|
|
||||||
let zz = z * z2;
|
|
||||||
let wx = w * x2;
|
|
||||||
let wy = w * y2;
|
|
||||||
let wz = w * z2;
|
|
||||||
|
|
||||||
let x_axis = Vec3::new(1.0 - (yy + zz), xy + wz, xz - wy);
|
|
||||||
let y_axis = Vec3::new(xy - wz, 1.0 - (xx + zz), yz + wx);
|
|
||||||
let z_axis = Vec3::new(xz + wy, yz - wx, 1.0 - (xx + yy));
|
|
||||||
(x_axis, y_axis, z_axis)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A 3x3 column major matrix.
|
|
||||||
///
|
|
||||||
/// This type is 16 byte aligned.
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Mat3 {
|
|
||||||
pub(crate) x_axis: Vec3,
|
|
||||||
pub(crate) y_axis: Vec3,
|
|
||||||
pub(crate) z_axis: Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Mat3 {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::identity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Mat3 {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "[{}, {}, {}]", self.x_axis, self.y_axis, self.z_axis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mat3 {
|
|
||||||
/// Creates a 3x3 matrix with all elements set to `0.0`.
|
|
||||||
#[inline]
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: Vec3::zero(),
|
|
||||||
y_axis: Vec3::zero(),
|
|
||||||
z_axis: Vec3::zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 identity matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn identity() -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: Vec3::unit_x(),
|
|
||||||
y_axis: Vec3::unit_y(),
|
|
||||||
z_axis: Vec3::unit_z(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 matrix from three column vectors.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_cols(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3) -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis,
|
|
||||||
y_axis,
|
|
||||||
z_axis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 matrix from a `[f32; 9]` stored in column major order.
|
|
||||||
/// If your data is stored in row major you will need to `transpose` the
|
|
||||||
/// returned matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_cols_array(m: &[f32; 9]) -> Self {
|
|
||||||
Mat3 {
|
|
||||||
x_axis: Vec3::new(m[0], m[1], m[2]),
|
|
||||||
y_axis: Vec3::new(m[3], m[4], m[5]),
|
|
||||||
z_axis: Vec3::new(m[6], m[7], m[8]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `[f32; 9]` storing data in column major order.
|
|
||||||
/// If you require data in row major order `transpose` the matrix first.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_cols_array(&self) -> [f32; 9] {
|
|
||||||
let (m00, m01, m02) = self.x_axis.into();
|
|
||||||
let (m10, m11, m12) = self.y_axis.into();
|
|
||||||
let (m20, m21, m22) = self.z_axis.into();
|
|
||||||
[m00, m01, m02, m10, m11, m12, m20, m21, m22]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 matrix from a `[[f32; 3]; 3]` stored in column major order.
|
|
||||||
/// If your data is in row major order you will need to `transpose` the
|
|
||||||
/// returned matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_cols_array_2d(m: &[[f32; 3]; 3]) -> Self {
|
|
||||||
Mat3 {
|
|
||||||
x_axis: m[0].into(),
|
|
||||||
y_axis: m[1].into(),
|
|
||||||
z_axis: m[2].into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `[[f32; 3]; 3]` storing data in column major order.
|
|
||||||
/// If you require data in row major order `transpose` the matrix first.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
|
|
||||||
[self.x_axis.into(), self.y_axis.into(), self.z_axis.into()]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 homogeneous transformation matrix from the given `scale`,
|
|
||||||
/// rotation `angle` (in radians) and `translation`.
|
|
||||||
///
|
|
||||||
/// The resulting matrix can be used to transform 2D points and vectors.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_scale_angle_translation(scale: Vec2, angle: f32, translation: Vec2) -> Self {
|
|
||||||
let (sin, cos) = scalar_sin_cos(angle);
|
|
||||||
let (scale_x, scale_y) = scale.into();
|
|
||||||
Self {
|
|
||||||
x_axis: Vec3::new(cos * scale_x, sin * scale_x, 0.0),
|
|
||||||
y_axis: Vec3::new(-sin * scale_y, cos * scale_y, 0.0),
|
|
||||||
z_axis: translation.extend(1.0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Creates a 3x3 rotation matrix from the given quaternion.
|
|
||||||
pub fn from_quat(rotation: Quat) -> Self {
|
|
||||||
let (x_axis, y_axis, z_axis) = quat_to_axes(rotation);
|
|
||||||
Self {
|
|
||||||
x_axis,
|
|
||||||
y_axis,
|
|
||||||
z_axis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 rotation matrix from a normalized rotation `axis` and
|
|
||||||
/// `angle` (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
|
|
||||||
glam_assert!(axis.is_normalized());
|
|
||||||
let (sin, cos) = scalar_sin_cos(angle);
|
|
||||||
let (x, y, z) = axis.into();
|
|
||||||
let (xsin, ysin, zsin) = (axis * sin).into();
|
|
||||||
let (x2, y2, z2) = (axis * axis).into();
|
|
||||||
let omc = 1.0 - cos;
|
|
||||||
let xyomc = x * y * omc;
|
|
||||||
let xzomc = x * z * omc;
|
|
||||||
let yzomc = y * z * omc;
|
|
||||||
Self {
|
|
||||||
x_axis: Vec3::new(x2 * omc + cos, xyomc + zsin, xzomc - ysin),
|
|
||||||
y_axis: Vec3::new(xyomc - zsin, y2 * omc + cos, yzomc + xsin),
|
|
||||||
z_axis: Vec3::new(xzomc + ysin, yzomc - xsin, z2 * omc + cos),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 rotation matrix from the given Euler angles (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_ypr(yaw: f32, pitch: f32, roll: f32) -> Self {
|
|
||||||
let quat = Quat::from_rotation_ypr(yaw, pitch, roll);
|
|
||||||
Self::from_quat(quat)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 rotation matrix from `angle` (in radians) around the x axis.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_x(angle: f32) -> Self {
|
|
||||||
let (sina, cosa) = scalar_sin_cos(angle);
|
|
||||||
Self {
|
|
||||||
x_axis: Vec3::unit_x(),
|
|
||||||
y_axis: Vec3::new(0.0, cosa, sina),
|
|
||||||
z_axis: Vec3::new(0.0, -sina, cosa),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 rotation matrix from `angle` (in radians) around the y axis.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_y(angle: f32) -> Self {
|
|
||||||
let (sina, cosa) = scalar_sin_cos(angle);
|
|
||||||
Self {
|
|
||||||
x_axis: Vec3::new(cosa, 0.0, -sina),
|
|
||||||
y_axis: Vec3::unit_y(),
|
|
||||||
z_axis: Vec3::new(sina, 0.0, cosa),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 rotation matrix from `angle` (in radians) around the z axis.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_z(angle: f32) -> Self {
|
|
||||||
let (sina, cosa) = scalar_sin_cos(angle);
|
|
||||||
Self {
|
|
||||||
x_axis: Vec3::new(cosa, sina, 0.0),
|
|
||||||
y_axis: Vec3::new(-sina, cosa, 0.0),
|
|
||||||
z_axis: Vec3::unit_z(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 3x3 non-uniform scale matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_scale(scale: Vec3) -> Self {
|
|
||||||
// TODO: should have a affine 2D scale and a 3d scale?
|
|
||||||
// Do not panic as long as any component is non-zero
|
|
||||||
glam_assert!(scale.cmpne(Vec3::zero()).any());
|
|
||||||
let (x, y, z) = scale.into();
|
|
||||||
Self {
|
|
||||||
x_axis: Vec3::new(x, 0.0, 0.0),
|
|
||||||
y_axis: Vec3::new(0.0, y, 0.0),
|
|
||||||
z_axis: Vec3::new(0.0, 0.0, z),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_x_axis(&mut self, x: Vec3) {
|
|
||||||
self.x_axis = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_y_axis(&mut self, y: Vec3) {
|
|
||||||
self.y_axis = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_z_axis(&mut self, z: Vec3) {
|
|
||||||
self.z_axis = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn x_axis(&self) -> Vec3 {
|
|
||||||
self.x_axis
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn y_axis(&self) -> Vec3 {
|
|
||||||
self.y_axis
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn z_axis(&self) -> Vec3 {
|
|
||||||
self.z_axis
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[inline]
|
|
||||||
// pub(crate) fn col(&self, index: usize) -> Vec3 {
|
|
||||||
// match index {
|
|
||||||
// 0 => self.x_axis,
|
|
||||||
// 1 => self.y_axis,
|
|
||||||
// 2 => self.z_axis,
|
|
||||||
// _ => panic!(
|
|
||||||
// "index out of bounds: the len is 3 but the index is {}",
|
|
||||||
// index
|
|
||||||
// ),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[inline]
|
|
||||||
// pub(crate) fn col_mut(&mut self, index: usize) -> &mut Vec3 {
|
|
||||||
// match index {
|
|
||||||
// 0 => &mut self.x_axis,
|
|
||||||
// 1 => &mut self.y_axis,
|
|
||||||
// 2 => &mut self.z_axis,
|
|
||||||
// _ => panic!(
|
|
||||||
// "index out of bounds: the len is 3 but the index is {}",
|
|
||||||
// index
|
|
||||||
// ),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Returns the transpose of `self`.
|
|
||||||
#[inline]
|
|
||||||
pub fn transpose(&self) -> Self {
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
{
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
use core::arch::x86::*;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use core::arch::x86_64::*;
|
|
||||||
unsafe {
|
|
||||||
let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
|
|
||||||
let tmp1 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b11_10_11_10);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
x_axis: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b00_00_10_00).into(),
|
|
||||||
y_axis: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b01_01_11_01).into(),
|
|
||||||
z_axis: _mm_shuffle_ps(tmp1, self.z_axis.0, 0b10_10_10_00).into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
let (m00, m01, m02) = self.x_axis.into();
|
|
||||||
let (m10, m11, m12) = self.y_axis.into();
|
|
||||||
let (m20, m21, m22) = self.z_axis.into();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
x_axis: Vec3::new(m00, m10, m20),
|
|
||||||
y_axis: Vec3::new(m01, m11, m21),
|
|
||||||
z_axis: Vec3::new(m02, m12, m22),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the determinant of `self`.
|
|
||||||
#[inline]
|
|
||||||
pub fn determinant(&self) -> f32 {
|
|
||||||
self.z_axis.dot(self.x_axis.cross(self.y_axis))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inverse of `self`.
|
|
||||||
///
|
|
||||||
/// If the matrix is not invertible the returned matrix will be invalid.
|
|
||||||
pub fn inverse(&self) -> Self {
|
|
||||||
let tmp0 = self.y_axis.cross(self.z_axis);
|
|
||||||
let tmp1 = self.z_axis.cross(self.x_axis);
|
|
||||||
let tmp2 = self.x_axis.cross(self.y_axis);
|
|
||||||
let det = self.z_axis.dot_as_vec3(tmp2);
|
|
||||||
glam_assert!(det.cmpne(Vec3::zero()).all());
|
|
||||||
let inv_det = det.reciprocal();
|
|
||||||
// TODO: Work out if it's possible to get rid of the transpose
|
|
||||||
Mat3::from_cols(tmp0 * inv_det, tmp1 * inv_det, tmp2 * inv_det).transpose()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transforms a 3D vector.
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_vec3(&self, other: Vec3) -> Vec3 {
|
|
||||||
let mut res = self.x_axis * other.dup_x();
|
|
||||||
res = self.y_axis.mul_add(other.dup_y(), res);
|
|
||||||
res = self.z_axis.mul_add(other.dup_z(), res);
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multiplies two 3x3 matrices.
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_mat3(&self, other: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: self.mul_vec3(other.x_axis),
|
|
||||||
y_axis: self.mul_vec3(other.y_axis),
|
|
||||||
z_axis: self.mul_vec3(other.z_axis),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds two 3x3 matrices.
|
|
||||||
#[inline]
|
|
||||||
pub fn add_mat3(&self, other: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: self.x_axis + other.x_axis,
|
|
||||||
y_axis: self.y_axis + other.y_axis,
|
|
||||||
z_axis: self.z_axis + other.z_axis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Subtracts two 3x3 matrices.
|
|
||||||
#[inline]
|
|
||||||
pub fn sub_mat3(&self, other: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: self.x_axis - other.x_axis,
|
|
||||||
y_axis: self.y_axis - other.y_axis,
|
|
||||||
z_axis: self.z_axis - other.z_axis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Multiplies a 3x3 matrix by a scalar.
|
|
||||||
pub fn mul_scalar(&self, other: f32) -> Self {
|
|
||||||
let s = Vec3::splat(other);
|
|
||||||
Self {
|
|
||||||
x_axis: self.x_axis * s,
|
|
||||||
y_axis: self.y_axis * s,
|
|
||||||
z_axis: self.z_axis * s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transforms the given `Vec2` as 2D point.
|
|
||||||
/// This is the equivalent of multiplying the `Vec2` as a `Vec3` where `w`
|
|
||||||
/// is `1.0`.
|
|
||||||
#[inline]
|
|
||||||
pub fn transform_point2(&self, other: Vec2) -> Vec2 {
|
|
||||||
// let mut res = self.x_axis * Vec3::splat(other.x());
|
|
||||||
// res = self.y_axis.mul_add(Vec3::splat(other.y()), res);
|
|
||||||
// res = self.z_axis + res;
|
|
||||||
// res.truncate()
|
|
||||||
self.mul_vec3(other.extend(1.0)).truncate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transforms the given `Vec2` as 2D vector.
|
|
||||||
/// This is the equivalent of multiplying the `Vec2` as a `Vec3` where `w`
|
|
||||||
/// is `0.0`.
|
|
||||||
#[inline]
|
|
||||||
pub fn transform_vector2(&self, other: Vec2) -> Vec2 {
|
|
||||||
// TODO: can optimize for w=0.
|
|
||||||
// let mut res = self.x_axis * Vec3::splat(other.x());
|
|
||||||
// res = self.y_axis.mul_add(Vec3::splat(other.y()), res);
|
|
||||||
// res.truncate()
|
|
||||||
self.mul_vec3(other.extend(0.0)).truncate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the absolute difference of all elements between `self`
|
|
||||||
/// and `other` is less than or equal to `max_abs_diff`.
|
|
||||||
///
|
|
||||||
/// This can be used to compare if two `Mat3`'s contain similar elements. It
|
|
||||||
/// works best when comparing with a known value. The `max_abs_diff` that
|
|
||||||
/// should be used used depends on the values being compared against.
|
|
||||||
///
|
|
||||||
/// For more on floating point comparisons see
|
|
||||||
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
|
||||||
#[inline]
|
|
||||||
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
|
|
||||||
self.x_axis.abs_diff_eq(other.x_axis, max_abs_diff)
|
|
||||||
&& self.y_axis.abs_diff_eq(other.y_axis, max_abs_diff)
|
|
||||||
&& self.z_axis.abs_diff_eq(other.z_axis, max_abs_diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<Mat3> for Mat3 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn add(self, other: Self) -> Self {
|
|
||||||
self.add_mat3(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<Mat3> for Mat3 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn sub(self, other: Self) -> Self {
|
|
||||||
self.sub_mat3(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Mat3> for Mat3 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Self) -> Self {
|
|
||||||
self.mul_mat3(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec3> for Mat3 {
|
|
||||||
type Output = Vec3;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Vec3) -> Vec3 {
|
|
||||||
self.mul_vec3(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Mat3> for f32 {
|
|
||||||
type Output = Mat3;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Mat3) -> Mat3 {
|
|
||||||
other.mul_scalar(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<f32> for Mat3 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: f32) -> Self {
|
|
||||||
self.mul_scalar(other)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,886 +0,0 @@
|
||||||
use super::{scalar_sin_cos, Mat3, Quat, Vec3, Vec4};
|
|
||||||
#[cfg(all(vec4sse2, target_arch = "x86"))]
|
|
||||||
use core::arch::x86::*;
|
|
||||||
#[cfg(all(vec4sse2, target_arch = "x86_64"))]
|
|
||||||
use core::arch::x86_64::*;
|
|
||||||
use core::{
|
|
||||||
fmt,
|
|
||||||
ops::{Add, Mul, Sub},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mat4(x_axis: Vec4, y_axis: Vec4, z_axis: Vec4, w_axis: Vec4) -> Mat4 {
|
|
||||||
Mat4 {
|
|
||||||
x_axis,
|
|
||||||
y_axis,
|
|
||||||
z_axis,
|
|
||||||
w_axis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn quat_to_axes(rotation: Quat) -> (Vec4, Vec4, Vec4) {
|
|
||||||
glam_assert!(rotation.is_normalized());
|
|
||||||
let (x, y, z, w) = rotation.into();
|
|
||||||
let x2 = x + x;
|
|
||||||
let y2 = y + y;
|
|
||||||
let z2 = z + z;
|
|
||||||
let xx = x * x2;
|
|
||||||
let xy = x * y2;
|
|
||||||
let xz = x * z2;
|
|
||||||
let yy = y * y2;
|
|
||||||
let yz = y * z2;
|
|
||||||
let zz = z * z2;
|
|
||||||
let wx = w * x2;
|
|
||||||
let wy = w * y2;
|
|
||||||
let wz = w * z2;
|
|
||||||
|
|
||||||
let x_axis = Vec4::new(1.0 - (yy + zz), xy + wz, xz - wy, 0.0);
|
|
||||||
let y_axis = Vec4::new(xy - wz, 1.0 - (xx + zz), yz + wx, 0.0);
|
|
||||||
let z_axis = Vec4::new(xz + wy, yz - wx, 1.0 - (xx + yy), 0.0);
|
|
||||||
(x_axis, y_axis, z_axis)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A 4x4 column major matrix.
|
|
||||||
///
|
|
||||||
/// This type is 16 byte aligned.
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Mat4 {
|
|
||||||
pub(crate) x_axis: Vec4,
|
|
||||||
pub(crate) y_axis: Vec4,
|
|
||||||
pub(crate) z_axis: Vec4,
|
|
||||||
pub(crate) w_axis: Vec4,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Mat4 {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::identity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Mat4 {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"[{}, {}, {}, {}]",
|
|
||||||
self.x_axis, self.y_axis, self.z_axis, self.w_axis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mat4 {
|
|
||||||
/// Creates a 4x4 matrix with all elements set to `0.0`.
|
|
||||||
#[inline]
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: Vec4::zero(),
|
|
||||||
y_axis: Vec4::zero(),
|
|
||||||
z_axis: Vec4::zero(),
|
|
||||||
w_axis: Vec4::zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 identity matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn identity() -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: Vec4::unit_x(),
|
|
||||||
y_axis: Vec4::unit_y(),
|
|
||||||
z_axis: Vec4::unit_z(),
|
|
||||||
w_axis: Vec4::unit_w(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 matrix from four column vectors.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_cols(x_axis: Vec4, y_axis: Vec4, z_axis: Vec4, w_axis: Vec4) -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis,
|
|
||||||
y_axis,
|
|
||||||
z_axis,
|
|
||||||
w_axis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 matrix from a `[f32; 16]` stored in column major order.
|
|
||||||
/// If your data is stored in row major you will need to `transpose` the
|
|
||||||
/// returned matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_cols_array(m: &[f32; 16]) -> Self {
|
|
||||||
Mat4 {
|
|
||||||
x_axis: Vec4::new(m[0], m[1], m[2], m[3]),
|
|
||||||
y_axis: Vec4::new(m[4], m[5], m[6], m[7]),
|
|
||||||
z_axis: Vec4::new(m[8], m[9], m[10], m[11]),
|
|
||||||
w_axis: Vec4::new(m[12], m[13], m[14], m[15]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `[f32; 16]` storing data in column major order.
|
|
||||||
/// If you require data in row major order `transpose` the matrix first.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_cols_array(&self) -> [f32; 16] {
|
|
||||||
*self.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 matrix from a `[[f32; 4]; 4]` stored in column major
|
|
||||||
/// order. If your data is in row major order you will need to `transpose`
|
|
||||||
/// the returned matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_cols_array_2d(m: &[[f32; 4]; 4]) -> Self {
|
|
||||||
Mat4 {
|
|
||||||
x_axis: m[0].into(),
|
|
||||||
y_axis: m[1].into(),
|
|
||||||
z_axis: m[2].into(),
|
|
||||||
w_axis: m[3].into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `[[f32; 4]; 4]` storing data in column major order.
|
|
||||||
/// If you require data in row major order `transpose` the matrix first.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
|
|
||||||
[
|
|
||||||
self.x_axis.into(),
|
|
||||||
self.y_axis.into(),
|
|
||||||
self.z_axis.into(),
|
|
||||||
self.w_axis.into(),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix from the given `scale`,
|
|
||||||
/// `rotation` and `translation`.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
|
|
||||||
glam_assert!(rotation.is_normalized());
|
|
||||||
let (x_axis, y_axis, z_axis) = quat_to_axes(rotation);
|
|
||||||
let (scale_x, scale_y, scale_z) = scale.into();
|
|
||||||
Self {
|
|
||||||
x_axis: x_axis * scale_x,
|
|
||||||
y_axis: y_axis * scale_y,
|
|
||||||
z_axis: z_axis * scale_z,
|
|
||||||
w_axis: translation.extend(1.0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix from the given `translation`.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
|
|
||||||
glam_assert!(rotation.is_normalized());
|
|
||||||
let (x_axis, y_axis, z_axis) = quat_to_axes(rotation);
|
|
||||||
Self {
|
|
||||||
x_axis,
|
|
||||||
y_axis,
|
|
||||||
z_axis,
|
|
||||||
w_axis: translation.extend(1.0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts `scale`, `rotation` and `translation` from `self`. The input matrix is expected to
|
|
||||||
/// be a 4x4 homogeneous transformation matrix otherwise the output will be invalid.
|
|
||||||
pub fn to_scale_rotation_translation(&self) -> (Vec3, Quat, Vec3) {
|
|
||||||
let det = self.determinant();
|
|
||||||
glam_assert!(det != 0.0);
|
|
||||||
|
|
||||||
let scale = Vec3::new(
|
|
||||||
self.x_axis.length() * det.signum(),
|
|
||||||
self.y_axis.length(),
|
|
||||||
self.z_axis.length(),
|
|
||||||
);
|
|
||||||
glam_assert!(scale.cmpne(Vec3::zero()).all());
|
|
||||||
|
|
||||||
let inv_scale = scale.reciprocal();
|
|
||||||
|
|
||||||
let rotation = Quat::from_rotation_mat3(&Mat3::from_cols(
|
|
||||||
self.x_axis().truncate() * inv_scale.dup_x(),
|
|
||||||
self.y_axis().truncate() * inv_scale.dup_y(),
|
|
||||||
self.z_axis().truncate() * inv_scale.dup_z(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let translation = self.w_axis.truncate();
|
|
||||||
|
|
||||||
(scale, rotation, translation)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix from the given `rotation`.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_quat(rotation: Quat) -> Self {
|
|
||||||
glam_assert!(rotation.is_normalized());
|
|
||||||
let (x_axis, y_axis, z_axis) = quat_to_axes(rotation);
|
|
||||||
Self {
|
|
||||||
x_axis,
|
|
||||||
y_axis,
|
|
||||||
z_axis,
|
|
||||||
w_axis: Vec4::unit_w(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix from the given `translation`.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_translation(translation: Vec3) -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: Vec4::unit_x(),
|
|
||||||
y_axis: Vec4::unit_y(),
|
|
||||||
z_axis: Vec4::unit_z(),
|
|
||||||
w_axis: translation.extend(1.0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
|
|
||||||
/// around a normalized rotation `axis` of `angle` (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
|
|
||||||
glam_assert!(axis.is_normalized());
|
|
||||||
let (sin, cos) = scalar_sin_cos(angle);
|
|
||||||
let (x, y, z) = axis.into();
|
|
||||||
let (xsin, ysin, zsin) = (axis * sin).into();
|
|
||||||
let (x2, y2, z2) = (axis * axis).into();
|
|
||||||
let omc = 1.0 - cos;
|
|
||||||
let xyomc = x * y * omc;
|
|
||||||
let xzomc = x * z * omc;
|
|
||||||
let yzomc = y * z * omc;
|
|
||||||
Self {
|
|
||||||
x_axis: Vec4::new(x2 * omc + cos, xyomc + zsin, xzomc - ysin, 0.0),
|
|
||||||
y_axis: Vec4::new(xyomc - zsin, y2 * omc + cos, yzomc + xsin, 0.0),
|
|
||||||
z_axis: Vec4::new(xzomc + ysin, yzomc - xsin, z2 * omc + cos, 0.0),
|
|
||||||
w_axis: Vec4::unit_w(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
|
|
||||||
/// around the given Euler angles (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_ypr(yaw: f32, pitch: f32, roll: f32) -> Self {
|
|
||||||
let quat = Quat::from_rotation_ypr(yaw, pitch, roll);
|
|
||||||
Self::from_quat(quat)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
|
|
||||||
/// around the x axis of `angle` (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_x(angle: f32) -> Self {
|
|
||||||
let (sina, cosa) = scalar_sin_cos(angle);
|
|
||||||
Self {
|
|
||||||
x_axis: Vec4::unit_x(),
|
|
||||||
y_axis: Vec4::new(0.0, cosa, sina, 0.0),
|
|
||||||
z_axis: Vec4::new(0.0, -sina, cosa, 0.0),
|
|
||||||
w_axis: Vec4::unit_w(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
|
|
||||||
/// around the y axis of `angle` (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_y(angle: f32) -> Self {
|
|
||||||
let (sina, cosa) = scalar_sin_cos(angle);
|
|
||||||
Self {
|
|
||||||
x_axis: Vec4::new(cosa, 0.0, -sina, 0.0),
|
|
||||||
y_axis: Vec4::unit_y(),
|
|
||||||
z_axis: Vec4::new(sina, 0.0, cosa, 0.0),
|
|
||||||
w_axis: Vec4::unit_w(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
|
|
||||||
/// around the z axis of `angle` (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_z(angle: f32) -> Self {
|
|
||||||
let (sina, cosa) = scalar_sin_cos(angle);
|
|
||||||
Self {
|
|
||||||
x_axis: Vec4::new(cosa, sina, 0.0, 0.0),
|
|
||||||
y_axis: Vec4::new(-sina, cosa, 0.0, 0.0),
|
|
||||||
z_axis: Vec4::unit_z(),
|
|
||||||
w_axis: Vec4::unit_w(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 4x4 homogeneous transformation matrix containing the given
|
|
||||||
/// non-uniform `scale`.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_scale(scale: Vec3) -> Self {
|
|
||||||
// Do not panic as long as any component is non-zero
|
|
||||||
glam_assert!(scale.cmpne(Vec3::zero()).any());
|
|
||||||
let (x, y, z) = scale.into();
|
|
||||||
Self {
|
|
||||||
x_axis: Vec4::new(x, 0.0, 0.0, 0.0),
|
|
||||||
y_axis: Vec4::new(0.0, y, 0.0, 0.0),
|
|
||||||
z_axis: Vec4::new(0.0, 0.0, z, 0.0),
|
|
||||||
w_axis: Vec4::unit_w(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_x_axis(&mut self, x: Vec4) {
|
|
||||||
self.x_axis = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_y_axis(&mut self, y: Vec4) {
|
|
||||||
self.y_axis = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_z_axis(&mut self, z: Vec4) {
|
|
||||||
self.z_axis = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_w_axis(&mut self, w: Vec4) {
|
|
||||||
self.w_axis = w;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn x_axis(&self) -> Vec4 {
|
|
||||||
self.x_axis
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn y_axis(&self) -> Vec4 {
|
|
||||||
self.y_axis
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn z_axis(&self) -> Vec4 {
|
|
||||||
self.z_axis
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn w_axis(&self) -> Vec4 {
|
|
||||||
self.w_axis
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[inline]
|
|
||||||
// pub(crate) fn col(&self, index: usize) -> Vec4 {
|
|
||||||
// match index {
|
|
||||||
// 0 => self.x_axis,
|
|
||||||
// 1 => self.y_axis,
|
|
||||||
// 2 => self.z_axis,
|
|
||||||
// 3 => self.w_axis,
|
|
||||||
// _ => panic!(
|
|
||||||
// "index out of bounds: the len is 4 but the index is {}",
|
|
||||||
// index
|
|
||||||
// ),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[inline]
|
|
||||||
// pub(crate) fn col_mut(&mut self, index: usize) -> &mut Vec4 {
|
|
||||||
// match index {
|
|
||||||
// 0 => &mut self.x_axis,
|
|
||||||
// 1 => &mut self.y_axis,
|
|
||||||
// 2 => &mut self.z_axis,
|
|
||||||
// 3 => &mut self.w_axis,
|
|
||||||
// _ => panic!(
|
|
||||||
// "index out of bounds: the len is 4 but the index is {}",
|
|
||||||
// index
|
|
||||||
// ),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Returns the transpose of `self`.
|
|
||||||
#[inline]
|
|
||||||
pub fn transpose(&self) -> Self {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
// sse2 implementation based off DirectXMath XMMatrixInverse (MIT License)
|
|
||||||
let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
|
|
||||||
let tmp1 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b11_10_11_10);
|
|
||||||
let tmp2 = _mm_shuffle_ps(self.z_axis.0, self.w_axis.0, 0b01_00_01_00);
|
|
||||||
let tmp3 = _mm_shuffle_ps(self.z_axis.0, self.w_axis.0, 0b11_10_11_10);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
x_axis: _mm_shuffle_ps(tmp0, tmp2, 0b10_00_10_00).into(),
|
|
||||||
y_axis: _mm_shuffle_ps(tmp0, tmp2, 0b11_01_11_01).into(),
|
|
||||||
z_axis: _mm_shuffle_ps(tmp1, tmp3, 0b10_00_10_00).into(),
|
|
||||||
w_axis: _mm_shuffle_ps(tmp1, tmp3, 0b11_01_11_01).into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
let (m00, m01, m02, m03) = self.x_axis.into();
|
|
||||||
let (m10, m11, m12, m13) = self.y_axis.into();
|
|
||||||
let (m20, m21, m22, m23) = self.z_axis.into();
|
|
||||||
let (m30, m31, m32, m33) = self.w_axis.into();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
x_axis: Vec4::new(m00, m10, m20, m30),
|
|
||||||
y_axis: Vec4::new(m01, m11, m21, m31),
|
|
||||||
z_axis: Vec4::new(m02, m12, m22, m32),
|
|
||||||
w_axis: Vec4::new(m03, m13, m23, m33),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the determinant of `self`.
|
|
||||||
#[inline]
|
|
||||||
pub fn determinant(&self) -> f32 {
|
|
||||||
let (m00, m01, m02, m03) = self.x_axis.into();
|
|
||||||
let (m10, m11, m12, m13) = self.y_axis.into();
|
|
||||||
let (m20, m21, m22, m23) = self.z_axis.into();
|
|
||||||
let (m30, m31, m32, m33) = self.w_axis.into();
|
|
||||||
|
|
||||||
let a2323 = m22 * m33 - m23 * m32;
|
|
||||||
let a1323 = m21 * m33 - m23 * m31;
|
|
||||||
let a1223 = m21 * m32 - m22 * m31;
|
|
||||||
let a0323 = m20 * m33 - m23 * m30;
|
|
||||||
let a0223 = m20 * m32 - m22 * m30;
|
|
||||||
let a0123 = m20 * m31 - m21 * m30;
|
|
||||||
|
|
||||||
m00 * (m11 * a2323 - m12 * a1323 + m13 * a1223)
|
|
||||||
- m01 * (m10 * a2323 - m12 * a0323 + m13 * a0223)
|
|
||||||
+ m02 * (m10 * a1323 - m11 * a0323 + m13 * a0123)
|
|
||||||
- m03 * (m10 * a1223 - m11 * a0223 + m12 * a0123)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inverse of `self`.
|
|
||||||
///
|
|
||||||
/// If the matrix is not invertible the returned matrix will be invalid.
|
|
||||||
pub fn inverse(&self) -> Self {
|
|
||||||
let (m00, m01, m02, m03) = self.x_axis.into();
|
|
||||||
let (m10, m11, m12, m13) = self.y_axis.into();
|
|
||||||
let (m20, m21, m22, m23) = self.z_axis.into();
|
|
||||||
let (m30, m31, m32, m33) = self.w_axis.into();
|
|
||||||
|
|
||||||
let coef00 = m22 * m33 - m32 * m23;
|
|
||||||
let coef02 = m12 * m33 - m32 * m13;
|
|
||||||
let coef03 = m12 * m23 - m22 * m13;
|
|
||||||
|
|
||||||
let coef04 = m21 * m33 - m31 * m23;
|
|
||||||
let coef06 = m11 * m33 - m31 * m13;
|
|
||||||
let coef07 = m11 * m23 - m21 * m13;
|
|
||||||
|
|
||||||
let coef08 = m21 * m32 - m31 * m22;
|
|
||||||
let coef10 = m11 * m32 - m31 * m12;
|
|
||||||
let coef11 = m11 * m22 - m21 * m12;
|
|
||||||
|
|
||||||
let coef12 = m20 * m33 - m30 * m23;
|
|
||||||
let coef14 = m10 * m33 - m30 * m13;
|
|
||||||
let coef15 = m10 * m23 - m20 * m13;
|
|
||||||
|
|
||||||
let coef16 = m20 * m32 - m30 * m22;
|
|
||||||
let coef18 = m10 * m32 - m30 * m12;
|
|
||||||
let coef19 = m10 * m22 - m20 * m12;
|
|
||||||
|
|
||||||
let coef20 = m20 * m31 - m30 * m21;
|
|
||||||
let coef22 = m10 * m31 - m30 * m11;
|
|
||||||
let coef23 = m10 * m21 - m20 * m11;
|
|
||||||
|
|
||||||
let fac0 = Vec4::new(coef00, coef00, coef02, coef03);
|
|
||||||
let fac1 = Vec4::new(coef04, coef04, coef06, coef07);
|
|
||||||
let fac2 = Vec4::new(coef08, coef08, coef10, coef11);
|
|
||||||
let fac3 = Vec4::new(coef12, coef12, coef14, coef15);
|
|
||||||
let fac4 = Vec4::new(coef16, coef16, coef18, coef19);
|
|
||||||
let fac5 = Vec4::new(coef20, coef20, coef22, coef23);
|
|
||||||
|
|
||||||
let vec0 = Vec4::new(m10, m00, m00, m00);
|
|
||||||
let vec1 = Vec4::new(m11, m01, m01, m01);
|
|
||||||
let vec2 = Vec4::new(m12, m02, m02, m02);
|
|
||||||
let vec3 = Vec4::new(m13, m03, m03, m03);
|
|
||||||
|
|
||||||
let inv0 = vec1 * fac0 - vec2 * fac1 + vec3 * fac2;
|
|
||||||
let inv1 = vec0 * fac0 - vec2 * fac3 + vec3 * fac4;
|
|
||||||
let inv2 = vec0 * fac1 - vec1 * fac3 + vec3 * fac5;
|
|
||||||
let inv3 = vec0 * fac2 - vec1 * fac4 + vec2 * fac5;
|
|
||||||
|
|
||||||
let sign_a = Vec4::new(1.0, -1.0, 1.0, -1.0);
|
|
||||||
let sign_b = Vec4::new(-1.0, 1.0, -1.0, 1.0);
|
|
||||||
|
|
||||||
let inverse = Self {
|
|
||||||
x_axis: inv0 * sign_a,
|
|
||||||
y_axis: inv1 * sign_b,
|
|
||||||
z_axis: inv2 * sign_a,
|
|
||||||
w_axis: inv3 * sign_b,
|
|
||||||
};
|
|
||||||
|
|
||||||
let col0 = Vec4::new(
|
|
||||||
inverse.x_axis.x(),
|
|
||||||
inverse.y_axis.x(),
|
|
||||||
inverse.z_axis.x(),
|
|
||||||
inverse.w_axis.x(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let dot0 = self.x_axis * col0;
|
|
||||||
let dot1 = dot0.x() + dot0.y() + dot0.z() + dot0.w();
|
|
||||||
|
|
||||||
glam_assert!(dot1 != 0.0);
|
|
||||||
|
|
||||||
let rcp_det = 1.0 / dot1;
|
|
||||||
inverse * rcp_det
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
// TODO: make public at some point
|
|
||||||
fn look_to_lh(eye: Vec3, dir: Vec3, up: Vec3) -> Self {
|
|
||||||
let f = dir.normalize();
|
|
||||||
let s = up.cross(f).normalize();
|
|
||||||
let u = f.cross(s);
|
|
||||||
let (fx, fy, fz) = f.into();
|
|
||||||
let (sx, sy, sz) = s.into();
|
|
||||||
let (ux, uy, uz) = u.into();
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(sx, ux, fx, 0.0),
|
|
||||||
Vec4::new(sy, uy, fy, 0.0),
|
|
||||||
Vec4::new(sz, uz, fz, 0.0),
|
|
||||||
Vec4::new(-s.dot(eye), -u.dot(eye), -f.dot(eye), 1.0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn look_at_lh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
|
|
||||||
glam_assert!(up.is_normalized());
|
|
||||||
Mat4::look_to_lh(eye, center - eye, up)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn look_at_rh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
|
|
||||||
glam_assert!(up.is_normalized());
|
|
||||||
Mat4::look_to_lh(eye, eye - center, up)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a right-handed perspective projection matrix with [-1,1] depth range.
|
|
||||||
/// This is the same as the OpenGL `gluPerspective` function.
|
|
||||||
/// See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
|
|
||||||
pub fn perspective_rh_gl(
|
|
||||||
fov_y_radians: f32,
|
|
||||||
aspect_ratio: f32,
|
|
||||||
z_near: f32,
|
|
||||||
z_far: f32,
|
|
||||||
) -> Self {
|
|
||||||
let inv_length = 1.0 / (z_near - z_far);
|
|
||||||
let f = 1.0 / (0.5 * fov_y_radians).tan();
|
|
||||||
let a = f / aspect_ratio;
|
|
||||||
let b = (z_near + z_far) * inv_length;
|
|
||||||
let c = (2.0 * z_near * z_far) * inv_length;
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(a, 0.0, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, f, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, 0.0, b, -1.0),
|
|
||||||
Vec4::new(0.0, 0.0, c, 0.0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a left-handed perspective projection matrix with [0,1] depth range.
|
|
||||||
pub fn perspective_lh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32, z_far: f32) -> Self {
|
|
||||||
glam_assert!(z_near > 0.0 && z_far > 0.0);
|
|
||||||
let (sin_fov, cos_fov) = scalar_sin_cos(0.5 * fov_y_radians);
|
|
||||||
let h = cos_fov / sin_fov;
|
|
||||||
let w = h / aspect_ratio;
|
|
||||||
let r = z_far / (z_far - z_near);
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(w, 0.0, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, h, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, 0.0, r, 1.0),
|
|
||||||
Vec4::new(0.0, 0.0, -r * z_near, 0.0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an infinite left-handed perspective projection matrix with [0,1] depth range.
|
|
||||||
pub fn perspective_infinite_lh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32) -> Self {
|
|
||||||
glam_assert!(z_near > 0.0);
|
|
||||||
let (sin_fov, cos_fov) = scalar_sin_cos(0.5 * fov_y_radians);
|
|
||||||
let h = cos_fov / sin_fov;
|
|
||||||
let w = h / aspect_ratio;
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(w, 0.0, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, h, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, 0.0, 1.0, 1.0),
|
|
||||||
Vec4::new(0.0, 0.0, -z_near, 0.0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an infinite left-handed perspective projection matrix with [0,1] depth range.
|
|
||||||
pub fn perspective_infinite_reverse_lh(
|
|
||||||
fov_y_radians: f32,
|
|
||||||
aspect_ratio: f32,
|
|
||||||
z_near: f32,
|
|
||||||
) -> Self {
|
|
||||||
glam_assert!(z_near > 0.0);
|
|
||||||
let (sin_fov, cos_fov) = scalar_sin_cos(0.5 * fov_y_radians);
|
|
||||||
let h = cos_fov / sin_fov;
|
|
||||||
let w = h / aspect_ratio;
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(w, 0.0, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, h, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, 0.0, 0.0, 1.0),
|
|
||||||
Vec4::new(0.0, 0.0, z_near, 0.0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[deprecated(since = "0.8.2", note = "please use `Mat4::perspective_rh_gl` instead")]
|
|
||||||
pub fn perspective_glu_rh(
|
|
||||||
fov_y_radians: f32,
|
|
||||||
aspect_ratio: f32,
|
|
||||||
z_near: f32,
|
|
||||||
z_far: f32,
|
|
||||||
) -> Self {
|
|
||||||
Mat4::perspective_rh_gl(fov_y_radians, aspect_ratio, z_near, z_far)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an infinite right-handed perspective projection matrix with
|
|
||||||
/// [0,1] depth range.
|
|
||||||
pub fn perspective_infinite_rh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32) -> Self {
|
|
||||||
let f = 1.0 / (0.5 * fov_y_radians).tan();
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(f / aspect_ratio, 0.0, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, f, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, 0.0, -1.0, -1.0),
|
|
||||||
Vec4::new(0.0, 0.0, -z_near, 0.0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an infinite reverse right-handed perspective projection matrix
|
|
||||||
/// with [0,1] depth range.
|
|
||||||
pub fn perspective_infinite_reverse_rh(
|
|
||||||
fov_y_radians: f32,
|
|
||||||
aspect_ratio: f32,
|
|
||||||
z_near: f32,
|
|
||||||
) -> Self {
|
|
||||||
let f = 1.0 / (0.5 * fov_y_radians).tan();
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(f / aspect_ratio, 0.0, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, f, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, 0.0, 0.0, -1.0),
|
|
||||||
Vec4::new(0.0, 0.0, z_near, 0.0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a right-handed orthographic projection matrix with [-1,1] depth
|
|
||||||
/// range. This is the same as the OpenGL `glOrtho` function in OpenGL.
|
|
||||||
/// See
|
|
||||||
/// https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml
|
|
||||||
pub fn orthographic_rh_gl(
|
|
||||||
left: f32,
|
|
||||||
right: f32,
|
|
||||||
bottom: f32,
|
|
||||||
top: f32,
|
|
||||||
near: f32,
|
|
||||||
far: f32,
|
|
||||||
) -> Self {
|
|
||||||
let a = 2.0 / (right - left);
|
|
||||||
let b = 2.0 / (top - bottom);
|
|
||||||
let c = -2.0 / (far - near);
|
|
||||||
let tx = -(right + left) / (right - left);
|
|
||||||
let ty = -(top + bottom) / (top - bottom);
|
|
||||||
let tz = -(far + near) / (far - near);
|
|
||||||
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(a, 0.0, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, b, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, 0.0, c, 0.0),
|
|
||||||
Vec4::new(tx, ty, tz, 1.0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a left-handed orthographic projection matrix with [0,1] depth range.
|
|
||||||
pub fn orthographic_lh(
|
|
||||||
left: f32,
|
|
||||||
right: f32,
|
|
||||||
bottom: f32,
|
|
||||||
top: f32,
|
|
||||||
near: f32,
|
|
||||||
far: f32,
|
|
||||||
) -> Self {
|
|
||||||
let rcp_width = 1.0 / (right - left);
|
|
||||||
let rcp_height = 1.0 / (top - bottom);
|
|
||||||
let r = 1.0 / (far - near);
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(rcp_width + rcp_width, 0.0, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, rcp_height + rcp_height, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, 0.0, r, 0.0),
|
|
||||||
Vec4::new(
|
|
||||||
-(left + right) * rcp_width,
|
|
||||||
-(top + bottom) * rcp_height,
|
|
||||||
-r * near,
|
|
||||||
1.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a right-handed orthographic projection matrix with [0,1] depth range.
|
|
||||||
pub fn orthographic_rh(
|
|
||||||
left: f32,
|
|
||||||
right: f32,
|
|
||||||
bottom: f32,
|
|
||||||
top: f32,
|
|
||||||
near: f32,
|
|
||||||
far: f32,
|
|
||||||
) -> Self {
|
|
||||||
let rcp_width = 1.0 / (right - left);
|
|
||||||
let rcp_height = 1.0 / (top - bottom);
|
|
||||||
let r = 1.0 / (near - far);
|
|
||||||
Mat4::from_cols(
|
|
||||||
Vec4::new(rcp_width + rcp_width, 0.0, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, rcp_height + rcp_height, 0.0, 0.0),
|
|
||||||
Vec4::new(0.0, 0.0, r, 0.0),
|
|
||||||
Vec4::new(
|
|
||||||
-(left + right) * rcp_width,
|
|
||||||
-(top + bottom) * rcp_height,
|
|
||||||
r * near,
|
|
||||||
1.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_vec4(&self, other: Vec4) -> Vec4 {
|
|
||||||
let mut res = self.x_axis * other.dup_x();
|
|
||||||
res = self.y_axis.mul_add(other.dup_y(), res);
|
|
||||||
res = self.z_axis.mul_add(other.dup_z(), res);
|
|
||||||
res = self.w_axis.mul_add(other.dup_w(), res);
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multiplies two 4x4 matrices.
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_mat4(&self, other: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: self.mul_vec4(other.x_axis),
|
|
||||||
y_axis: self.mul_vec4(other.y_axis),
|
|
||||||
z_axis: self.mul_vec4(other.z_axis),
|
|
||||||
w_axis: self.mul_vec4(other.w_axis),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn add_mat4(&self, other: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: self.x_axis + other.x_axis,
|
|
||||||
y_axis: self.y_axis + other.y_axis,
|
|
||||||
z_axis: self.z_axis + other.z_axis,
|
|
||||||
w_axis: self.w_axis + other.w_axis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn sub_mat4(&self, other: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
x_axis: self.x_axis - other.x_axis,
|
|
||||||
y_axis: self.y_axis - other.y_axis,
|
|
||||||
z_axis: self.z_axis - other.z_axis,
|
|
||||||
w_axis: self.w_axis - other.w_axis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_scalar(&self, other: f32) -> Self {
|
|
||||||
let s = Vec4::splat(other);
|
|
||||||
Self {
|
|
||||||
x_axis: self.x_axis * s,
|
|
||||||
y_axis: self.y_axis * s,
|
|
||||||
z_axis: self.z_axis * s,
|
|
||||||
w_axis: self.w_axis * s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transforms the given `Vec3` as 3D point.
|
|
||||||
/// This is the equivalent of multiplying the `Vec3` as a `Vec4` where `w`
|
|
||||||
/// is `1.0`.
|
|
||||||
#[inline]
|
|
||||||
pub fn transform_point3(&self, other: Vec3) -> Vec3 {
|
|
||||||
// TODO: optimized version below probably won't work for perspective projections
|
|
||||||
// let mut res = self.x_axis.truncate() * other.dup_x();
|
|
||||||
// res = self.y_axis.truncate().mul_add(other.dup_y(), res);
|
|
||||||
// res = self.z_axis.truncate().mul_add(other.dup_z(), res);
|
|
||||||
// // other w = 1
|
|
||||||
// res = self.w_axis.truncate() + res;
|
|
||||||
// res
|
|
||||||
self.mul_vec4(other.extend(1.0)).truncate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transforms the give `Vec3` as 3D vector.
|
|
||||||
/// This is the equivalent of multiplying the `Vec3` as a `Vec4` where `w`
|
|
||||||
/// is `0.0`.
|
|
||||||
#[inline]
|
|
||||||
pub fn transform_vector3(&self, other: Vec3) -> Vec3 {
|
|
||||||
// TODO: can optimize for w=0.
|
|
||||||
// TODO: optimized version below probably won't work for perspective projections
|
|
||||||
// let mut res = self.x_axis.truncate() * other.dup_x();
|
|
||||||
// res = self.y_axis.truncate().mul_add(other.dup_y(), res);
|
|
||||||
// res = self.z_axis.truncate().mul_add(other.dup_z(), res);
|
|
||||||
// // other w = 0
|
|
||||||
// res
|
|
||||||
self.mul_vec4(other.extend(0.0)).truncate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the absolute difference of all elements between `self`
|
|
||||||
/// and `other` is less than or equal to `max_abs_diff`.
|
|
||||||
///
|
|
||||||
/// This can be used to compare if two `Mat4`'s contain similar elements. It
|
|
||||||
/// works best when comparing with a known value. The `max_abs_diff` that
|
|
||||||
/// should be used used depends on the values being compared against.
|
|
||||||
///
|
|
||||||
/// For more on floating point comparisons see
|
|
||||||
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
|
||||||
#[inline]
|
|
||||||
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
|
|
||||||
self.x_axis.abs_diff_eq(other.x_axis, max_abs_diff)
|
|
||||||
&& self.y_axis.abs_diff_eq(other.y_axis, max_abs_diff)
|
|
||||||
&& self.z_axis.abs_diff_eq(other.z_axis, max_abs_diff)
|
|
||||||
&& self.w_axis.abs_diff_eq(other.w_axis, max_abs_diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[f32; 16]> for Mat4 {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &[f32; 16] {
|
|
||||||
unsafe { &*(self as *const Self as *const [f32; 16]) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<[f32; 16]> for Mat4 {
|
|
||||||
#[inline]
|
|
||||||
fn as_mut(&mut self) -> &mut [f32; 16] {
|
|
||||||
unsafe { &mut *(self as *mut Self as *mut [f32; 16]) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<Mat4> for Mat4 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn add(self, other: Self) -> Self {
|
|
||||||
self.add_mat4(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub<Mat4> for Mat4 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn sub(self, other: Self) -> Self {
|
|
||||||
self.sub_mat4(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Mat4> for Mat4 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Self) -> Self {
|
|
||||||
self.mul_mat4(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec4> for Mat4 {
|
|
||||||
type Output = Vec4;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Vec4) -> Vec4 {
|
|
||||||
self.mul_vec4(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Mat4> for f32 {
|
|
||||||
type Output = Mat4;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Mat4) -> Mat4 {
|
|
||||||
other.mul_scalar(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<f32> for Mat4 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: f32) -> Self {
|
|
||||||
self.mul_scalar(other)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
mod funcs;
|
|
||||||
mod mat2;
|
|
||||||
mod mat3;
|
|
||||||
mod mat4;
|
|
||||||
mod quat;
|
|
||||||
#[cfg(feature = "transform-types")]
|
|
||||||
mod transform;
|
|
||||||
mod vec2;
|
|
||||||
mod vec2_mask;
|
|
||||||
mod vec3;
|
|
||||||
mod vec3_mask;
|
|
||||||
mod vec4;
|
|
||||||
mod vec4_mask;
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
mod x86_utils;
|
|
||||||
|
|
||||||
pub(crate) use funcs::{scalar_acos, scalar_sin_cos};
|
|
||||||
pub use mat2::*;
|
|
||||||
pub use mat3::*;
|
|
||||||
pub use mat4::*;
|
|
||||||
pub use quat::*;
|
|
||||||
#[cfg(feature = "transform-types")]
|
|
||||||
pub use transform::*;
|
|
||||||
pub use vec2::*;
|
|
||||||
pub use vec2_mask::*;
|
|
||||||
pub use vec3::*;
|
|
||||||
pub use vec3_mask::*;
|
|
||||||
pub use vec4::*;
|
|
||||||
pub use vec4_mask::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "mint")]
|
|
||||||
mod glam_mint;
|
|
||||||
#[cfg(feature = "mint")]
|
|
||||||
pub use glam_mint::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
mod glam_rand;
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
pub use glam_rand::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
mod glam_serde;
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
pub use glam_serde::*;
|
|
||||||
|
|
||||||
mod glam_zerocopy;
|
|
||||||
pub use glam_zerocopy::*;
|
|
|
@ -1,670 +0,0 @@
|
||||||
use super::{scalar_acos, scalar_sin_cos, Mat3, Mat4, Vec3, Vec4};
|
|
||||||
#[cfg(all(vec4sse2, target_arch = "x86",))]
|
|
||||||
use core::arch::x86::*;
|
|
||||||
#[cfg(all(vec4sse2, target_arch = "x86_64",))]
|
|
||||||
use core::arch::x86_64::*;
|
|
||||||
use core::{
|
|
||||||
cmp::Ordering,
|
|
||||||
fmt,
|
|
||||||
ops::{Mul, MulAssign, Neg},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A quaternion representing an orientation.
|
|
||||||
///
|
|
||||||
/// This quaternion is intended to be of unit length but may denormalize due to
|
|
||||||
/// floating point "error creep" which can occur when successive quaternion
|
|
||||||
/// operations are applied.
|
|
||||||
///
|
|
||||||
/// This type is 16 byte aligned.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Quat(pub(crate) Vec4);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn quat(x: f32, y: f32, z: f32, w: f32) -> Quat {
|
|
||||||
Quat::from_xyzw(x, y, z, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Quat {
|
|
||||||
/// Creates a new rotation quaternion.
|
|
||||||
///
|
|
||||||
/// This should generally not be called manually unless you know what you are doing. Use one of
|
|
||||||
/// the other constructors instead such as `identity` or `from_axis_angle`.
|
|
||||||
///
|
|
||||||
/// `from_xyzw` is mostly used by unit tests and `serde` deserialization.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Self {
|
|
||||||
Self(Vec4::new(x, y, z, w))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn identity() -> Self {
|
|
||||||
Self(Vec4::new(0.0, 0.0, 0.0, 1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new rotation quaternion from an unaligned `&[f32]`.
|
|
||||||
///
|
|
||||||
/// # Preconditions
|
|
||||||
///
|
|
||||||
/// The resulting quaternion is expected to be of unit length.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `slice` length is less than 4.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_slice_unaligned(slice: &[f32]) -> Self {
|
|
||||||
let q = Self(Vec4::from_slice_unaligned(slice));
|
|
||||||
glam_assert!(q.is_normalized());
|
|
||||||
q
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the quaternion to an unaligned `&mut [f32]`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `slice` length is less than 4.
|
|
||||||
#[inline]
|
|
||||||
pub fn write_to_slice_unaligned(self, slice: &mut [f32]) {
|
|
||||||
self.0.write_to_slice_unaligned(slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new quaterion for a normalized rotation axis and angle
|
|
||||||
/// (in radians).
|
|
||||||
#[inline]
|
|
||||||
pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
|
|
||||||
glam_assert!(axis.is_normalized());
|
|
||||||
let (s, c) = scalar_sin_cos(angle * 0.5);
|
|
||||||
Self((axis * s).extend(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new quaternion from the angle (in radians) around the x axis.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_x(angle: f32) -> Self {
|
|
||||||
let (s, c) = scalar_sin_cos(angle * 0.5);
|
|
||||||
Self::from_xyzw(s, 0.0, 0.0, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new quaternion from the angle (in radians) around the y axis.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_y(angle: f32) -> Self {
|
|
||||||
let (s, c) = scalar_sin_cos(angle * 0.5);
|
|
||||||
Self::from_xyzw(0.0, s, 0.0, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new quaternion from the angle (in radians) around the z axis.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_z(angle: f32) -> Self {
|
|
||||||
let (s, c) = scalar_sin_cos(angle * 0.5);
|
|
||||||
Self::from_xyzw(0.0, 0.0, s, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Create a quaternion from the given yaw (around y), pitch (around x) and roll (around z)
|
|
||||||
/// in radians.
|
|
||||||
pub fn from_rotation_ypr(yaw: f32, pitch: f32, roll: f32) -> Self {
|
|
||||||
// Self::from_rotation_y(yaw) * Self::from_rotation_x(pitch) * Self::from_rotation_z(roll)
|
|
||||||
let (y0, w0) = scalar_sin_cos(yaw * 0.5);
|
|
||||||
let (x1, w1) = scalar_sin_cos(pitch * 0.5);
|
|
||||||
let (z2, w2) = scalar_sin_cos(roll * 0.5);
|
|
||||||
|
|
||||||
let x3 = w0 * x1;
|
|
||||||
let y3 = y0 * w1;
|
|
||||||
let z3 = -y0 * x1;
|
|
||||||
let w3 = w0 * w1;
|
|
||||||
|
|
||||||
let x4 = x3 * w2 + y3 * z2;
|
|
||||||
let y4 = -x3 * z2 + y3 * w2;
|
|
||||||
let z4 = w3 * z2 + z3 * w2;
|
|
||||||
let w4 = w3 * w2 - z3 * z2;
|
|
||||||
|
|
||||||
Self(Vec4::new(x4, y4, z4, w4))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_rotation_axes(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3) -> Self {
|
|
||||||
// from DirectXMath XMQuaternionRotationMatrix
|
|
||||||
// TODO: sse2 version
|
|
||||||
let (m00, m01, m02) = x_axis.into();
|
|
||||||
let (m10, m11, m12) = y_axis.into();
|
|
||||||
let (m20, m21, m22) = z_axis.into();
|
|
||||||
if m22 <= 0.0 {
|
|
||||||
// x^2 + y^2 >= z^2 + w^2
|
|
||||||
let dif10 = m11 - m00;
|
|
||||||
let omm22 = 1.0 - m22;
|
|
||||||
if dif10 <= 0.0 {
|
|
||||||
// x^2 >= y^2
|
|
||||||
let four_xsq = omm22 - dif10;
|
|
||||||
let inv4x = 0.5 / four_xsq.sqrt();
|
|
||||||
Self::from_xyzw(
|
|
||||||
four_xsq * inv4x,
|
|
||||||
(m01 + m10) * inv4x,
|
|
||||||
(m02 + m20) * inv4x,
|
|
||||||
(m12 - m21) * inv4x,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// y^2 >= x^2
|
|
||||||
let four_ysq = omm22 + dif10;
|
|
||||||
let inv4y = 0.5 / four_ysq.sqrt();
|
|
||||||
Self::from_xyzw(
|
|
||||||
(m01 + m10) * inv4y,
|
|
||||||
four_ysq * inv4y,
|
|
||||||
(m12 + m21) * inv4y,
|
|
||||||
(m20 - m02) * inv4y,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// z^2 + w^2 >= x^2 + y^2
|
|
||||||
let sum10 = m11 + m00;
|
|
||||||
let opm22 = 1.0 + m22;
|
|
||||||
if sum10 <= 0.0 {
|
|
||||||
// z^2 >= w^2
|
|
||||||
let four_zsq = opm22 - sum10;
|
|
||||||
let inv4z = 0.5 / four_zsq.sqrt();
|
|
||||||
Self::from_xyzw(
|
|
||||||
(m02 + m20) * inv4z,
|
|
||||||
(m12 + m21) * inv4z,
|
|
||||||
four_zsq * inv4z,
|
|
||||||
(m01 - m10) * inv4z,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// w^2 >= z^2
|
|
||||||
let four_wsq = opm22 + sum10;
|
|
||||||
let inv4w = 0.5 / four_wsq.sqrt();
|
|
||||||
Self::from_xyzw(
|
|
||||||
(m12 - m21) * inv4w,
|
|
||||||
(m20 - m02) * inv4w,
|
|
||||||
(m01 - m10) * inv4w,
|
|
||||||
four_wsq * inv4w,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new quaternion from a 3x3 rotation matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_mat3(mat: &Mat3) -> Self {
|
|
||||||
Self::from_rotation_axes(mat.x_axis(), mat.y_axis(), mat.z_axis())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new quaternion from a 3x3 rotation matrix inside a homogeneous
|
|
||||||
/// 4x4 matrix.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_mat4(mat: &Mat4) -> Self {
|
|
||||||
Self::from_rotation_axes(
|
|
||||||
mat.x_axis().truncate(),
|
|
||||||
mat.y_axis().truncate(),
|
|
||||||
mat.z_axis().truncate(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the rotation axis and angle of `self`.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_axis_angle(self) -> (Vec3, f32) {
|
|
||||||
const EPSILON: f32 = 1.0e-8;
|
|
||||||
const EPSILON_SQUARED: f32 = EPSILON * EPSILON;
|
|
||||||
let (x, y, z, w) = self.0.into();
|
|
||||||
let angle = scalar_acos(w) * 2.0;
|
|
||||||
let scale_sq = (1.0 - w * w).max(0.0);
|
|
||||||
if scale_sq >= EPSILON_SQUARED {
|
|
||||||
(Vec3::new(x, y, z) / scale_sq.sqrt(), angle)
|
|
||||||
} else {
|
|
||||||
(Vec3::unit_x(), angle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the quaternion conjugate of `self`. For a unit quaternion the
|
|
||||||
/// conjugate is also the inverse.
|
|
||||||
#[inline]
|
|
||||||
pub fn conjugate(self) -> Self {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
Self(Vec4(_mm_xor_ps(
|
|
||||||
(self.0).0,
|
|
||||||
_mm_set_ps(0.0, -0.0, -0.0, -0.0),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
Self::from_xyzw(-(self.0).0, -(self.0).1, -(self.0).2, (self.0).3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the dot product of `self` and `other`. The dot product is
|
|
||||||
/// equal to the the cosine of the angle between two quaterion rotations.
|
|
||||||
#[inline]
|
|
||||||
pub fn dot(self, other: Self) -> f32 {
|
|
||||||
self.0.dot(other.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the length of `self`.
|
|
||||||
#[inline]
|
|
||||||
pub fn length(self) -> f32 {
|
|
||||||
self.0.length()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the squared length of `self`.
|
|
||||||
///
|
|
||||||
/// This is generally faster than `Quat::length()` as it avoids a square
|
|
||||||
/// root operation.
|
|
||||||
#[inline]
|
|
||||||
pub fn length_squared(self) -> f32 {
|
|
||||||
self.0.length_squared()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes `1.0 / Quat::length()`.
|
|
||||||
///
|
|
||||||
/// For valid results, `self` must _not_ be of length zero.
|
|
||||||
#[inline]
|
|
||||||
pub fn length_reciprocal(self) -> f32 {
|
|
||||||
1.0 / self.0.length()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `self` normalized to length 1.0.
|
|
||||||
///
|
|
||||||
/// For valid results, `self` must _not_ be of length zero.
|
|
||||||
#[inline]
|
|
||||||
pub fn normalize(self) -> Self {
|
|
||||||
let inv_len = self.0.length_reciprocal();
|
|
||||||
Self(self.0.mul(inv_len))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether `self` of length `1.0` or not.
|
|
||||||
///
|
|
||||||
/// Uses a precision threshold of `1e-6`.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_normalized(self) -> bool {
|
|
||||||
is_normalized!(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_near_identity(self) -> bool {
|
|
||||||
// from rtm quat_near_identity
|
|
||||||
const THRESHOLD_ANGLE: f32 = 0.002_847_144_6;
|
|
||||||
// Because of floating point precision, we cannot represent very small rotations.
|
|
||||||
// The closest f32 to 1.0 that is not 1.0 itself yields:
|
|
||||||
// 0.99999994.acos() * 2.0 = 0.000690533954 rad
|
|
||||||
//
|
|
||||||
// An error threshold of 1.e-6 is used by default.
|
|
||||||
// (1.0 - 1.e-6).acos() * 2.0 = 0.00284714461 rad
|
|
||||||
// (1.0 - 1.e-7).acos() * 2.0 = 0.00097656250 rad
|
|
||||||
//
|
|
||||||
// We don't really care about the angle value itself, only if it's close to 0.
|
|
||||||
// This will happen whenever quat.w is close to 1.0.
|
|
||||||
// If the quat.w is close to -1.0, the angle will be near 2*PI which is close to
|
|
||||||
// a negative 0 rotation. By forcing quat.w to be positive, we'll end up with
|
|
||||||
// the shortest path.
|
|
||||||
let positive_w_angle = scalar_acos(self.0.w().abs()) * 2.0;
|
|
||||||
positive_w_angle < THRESHOLD_ANGLE
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the absolute difference of all elements between `self`
|
|
||||||
/// and `other` is less than or equal to `max_abs_diff`.
|
|
||||||
///
|
|
||||||
/// This can be used to compare if two `Quat`'s contain similar elements. It
|
|
||||||
/// works best when comparing with a known value. The `max_abs_diff` that
|
|
||||||
/// should be used used depends on the values being compared against.
|
|
||||||
///
|
|
||||||
/// For more on floating point comparisons see
|
|
||||||
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
|
||||||
#[inline]
|
|
||||||
pub fn abs_diff_eq(self, other: Self, max_abs_diff: f32) -> bool {
|
|
||||||
self.0.abs_diff_eq(other.0, max_abs_diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a linear interpolation between `self` and `other` based on
|
|
||||||
/// the value `s`.
|
|
||||||
///
|
|
||||||
/// When `s` is `0.0`, the result will be equal to `self`. When `s`
|
|
||||||
/// is `1.0`, the result will be equal to `other`.
|
|
||||||
#[inline]
|
|
||||||
pub fn lerp(self, end: Self, s: f32) -> Self {
|
|
||||||
glam_assert!(self.is_normalized());
|
|
||||||
glam_assert!(end.is_normalized());
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
let start = self.0;
|
|
||||||
let end = end.0;
|
|
||||||
let dot = start.dot_as_vec4(end);
|
|
||||||
// Calculate the bias, if the dot product is positive or zero, there is no bias
|
|
||||||
// but if it is negative, we want to flip the 'end' rotation XYZW components
|
|
||||||
let bias = _mm_and_ps(dot.into(), _mm_set_ps1(-0.0));
|
|
||||||
let interpolated = Vec4(_mm_add_ps(
|
|
||||||
_mm_mul_ps(
|
|
||||||
_mm_sub_ps(_mm_xor_ps(end.into(), bias), start.0),
|
|
||||||
_mm_set_ps1(s),
|
|
||||||
),
|
|
||||||
start.0,
|
|
||||||
));
|
|
||||||
Self(interpolated.normalize())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
let start = self.0;
|
|
||||||
let end = end.0;
|
|
||||||
let dot = start.dot(end);
|
|
||||||
let bias = if dot >= 0.0 { 1.0 } else { -1.0 };
|
|
||||||
let interpolated = start + (s * ((end * bias) - start));
|
|
||||||
Self(interpolated.normalize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a spherical linear interpolation between `self` and `end`
|
|
||||||
/// based on the value `s`.
|
|
||||||
///
|
|
||||||
/// When `s` is `0.0`, the result will be equal to `self`. When `s`
|
|
||||||
/// is `1.0`, the result will be equal to `end`.
|
|
||||||
///
|
|
||||||
/// Note that a rotation can be represented by two quaternions: `q` and
|
|
||||||
/// `-q`. The slerp path between `q` and `end` will be different from the
|
|
||||||
/// path between `-q` and `end`. One path will take the long way around and
|
|
||||||
/// one will take the short way. In order to correct for this, the `dot`
|
|
||||||
/// product between `self` and `end` should be positive. If the `dot`
|
|
||||||
/// product is negative, slerp between `-self` and `end`.
|
|
||||||
#[inline]
|
|
||||||
pub fn slerp(self, end: Self, s: f32) -> Self {
|
|
||||||
// http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
|
|
||||||
|
|
||||||
glam_assert!(self.is_normalized());
|
|
||||||
glam_assert!(end.is_normalized());
|
|
||||||
|
|
||||||
const DOT_THRESHOLD: f32 = 0.9995;
|
|
||||||
|
|
||||||
let dot = self.dot(end);
|
|
||||||
|
|
||||||
if dot > DOT_THRESHOLD {
|
|
||||||
// assumes lerp returns a normalized quaternion
|
|
||||||
self.lerp(end, s)
|
|
||||||
} else {
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
// assumes scalar_acos clamps the input to [-1.0, 1.0]
|
|
||||||
let theta = crate::f32::funcs::scalar_acos(dot);
|
|
||||||
let scale1 = f32::sin(theta * (1.0 - s));
|
|
||||||
let scale2 = f32::sin(theta * s);
|
|
||||||
let theta_sin = f32::sin(theta);
|
|
||||||
|
|
||||||
Quat((self.0 * scale1 + end.0 * scale2) * theta_sin.recip())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
{
|
|
||||||
// assumes scalar_acos clamps the input to [-1.0, 1.0]
|
|
||||||
let theta = crate::f32::funcs::scalar_acos(dot);
|
|
||||||
|
|
||||||
let x = 1.0 - s;
|
|
||||||
let y = s;
|
|
||||||
let z = 1.0;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let tmp = Vec4::splat(theta) * Vec4::new(x, y, z, 0.0);
|
|
||||||
let tmp = crate::f32::funcs::sse2::m128_sin(tmp.0);
|
|
||||||
|
|
||||||
let scale1 = _mm_shuffle_ps(tmp, tmp, 0b00_00_00_00);
|
|
||||||
let scale2 = _mm_shuffle_ps(tmp, tmp, 0b01_01_01_01);
|
|
||||||
let theta_sin = _mm_shuffle_ps(tmp, tmp, 0b10_10_10_10);
|
|
||||||
|
|
||||||
let theta_sin_recip = Vec4(_mm_rcp_ps(theta_sin));
|
|
||||||
|
|
||||||
Quat((self.0 * Vec4(scale1) + end.0 * Vec4(scale2)) * theta_sin_recip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Multiplies a quaternion and a 3D vector, rotating it.
|
|
||||||
pub fn mul_vec3(self, other: Vec3) -> Vec3 {
|
|
||||||
glam_assert!(self.is_normalized());
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
{
|
|
||||||
let w = self.0.dup_w().truncate();
|
|
||||||
let two = Vec3::splat(2.0);
|
|
||||||
let b = self.0.truncate();
|
|
||||||
let b2 = b.dot_as_vec3(b);
|
|
||||||
other * (w * w - b2) + b * (other.dot_as_vec3(b) * two) + b.cross(other) * (w * two)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
let w = self.0.w();
|
|
||||||
let b = self.0.truncate();
|
|
||||||
let b2 = b.dot(b);
|
|
||||||
other * (w * w - b2) + b * (other.dot(b) * 2.0) + b.cross(other) * (w * 2.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Multiplies two quaternions.
|
|
||||||
/// Note that due to floating point rounding the result may not be perfectly normalized.
|
|
||||||
pub fn mul_quat(self, other: Self) -> Self {
|
|
||||||
glam_assert!(self.is_normalized());
|
|
||||||
glam_assert!(other.is_normalized());
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
// from rtm quat_mul
|
|
||||||
let lhs = self.0.into();
|
|
||||||
let rhs = other.0.into();
|
|
||||||
|
|
||||||
let control_wzyx = _mm_set_ps(-1.0, 1.0, -1.0, 1.0);
|
|
||||||
let control_zwxy = _mm_set_ps(-1.0, -1.0, 1.0, 1.0);
|
|
||||||
let control_yxwz = _mm_set_ps(-1.0, 1.0, 1.0, -1.0);
|
|
||||||
|
|
||||||
let r_xxxx = _mm_shuffle_ps(lhs, lhs, 0b00_00_00_00);
|
|
||||||
let r_yyyy = _mm_shuffle_ps(lhs, lhs, 0b01_01_01_01);
|
|
||||||
let r_zzzz = _mm_shuffle_ps(lhs, lhs, 0b10_10_10_10);
|
|
||||||
let r_wwww = _mm_shuffle_ps(lhs, lhs, 0b11_11_11_11);
|
|
||||||
|
|
||||||
let lxrw_lyrw_lzrw_lwrw = _mm_mul_ps(r_wwww, rhs);
|
|
||||||
let l_wzyx = _mm_shuffle_ps(rhs, rhs, 0b00_01_10_11);
|
|
||||||
|
|
||||||
let lwrx_lzrx_lyrx_lxrx = _mm_mul_ps(r_xxxx, l_wzyx);
|
|
||||||
let l_zwxy = _mm_shuffle_ps(l_wzyx, l_wzyx, 0b10_11_00_01);
|
|
||||||
|
|
||||||
let lwrx_nlzrx_lyrx_nlxrx = _mm_mul_ps(lwrx_lzrx_lyrx_lxrx, control_wzyx);
|
|
||||||
|
|
||||||
let lzry_lwry_lxry_lyry = _mm_mul_ps(r_yyyy, l_zwxy);
|
|
||||||
let l_yxwz = _mm_shuffle_ps(l_zwxy, l_zwxy, 0b00_01_10_11);
|
|
||||||
|
|
||||||
let lzry_lwry_nlxry_nlyry = _mm_mul_ps(lzry_lwry_lxry_lyry, control_zwxy);
|
|
||||||
|
|
||||||
let lyrz_lxrz_lwrz_lzrz = _mm_mul_ps(r_zzzz, l_yxwz);
|
|
||||||
let result0 = _mm_add_ps(lxrw_lyrw_lzrw_lwrw, lwrx_nlzrx_lyrx_nlxrx);
|
|
||||||
|
|
||||||
let nlyrz_lxrz_lwrz_wlzrz = _mm_mul_ps(lyrz_lxrz_lwrz_lzrz, control_yxwz);
|
|
||||||
let result1 = _mm_add_ps(lzry_lwry_nlxry_nlyry, nlyrz_lxrz_lwrz_wlzrz);
|
|
||||||
Self(Vec4(_mm_add_ps(result0, result1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
let (x0, y0, z0, w0) = self.0.into();
|
|
||||||
let (x1, y1, z1, w1) = other.0.into();
|
|
||||||
Self::from_xyzw(
|
|
||||||
w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1,
|
|
||||||
w0 * y1 - x0 * z1 + y0 * w1 + z0 * x1,
|
|
||||||
w0 * z1 + x0 * y1 - y0 * x1 + z0 * w1,
|
|
||||||
w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Returns element `x`.
|
|
||||||
#[inline]
|
|
||||||
pub fn x(self) -> f32 {
|
|
||||||
self.0.x()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns element `y`.
|
|
||||||
#[inline]
|
|
||||||
pub fn y(self) -> f32 {
|
|
||||||
self.0.y()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns element `z`.
|
|
||||||
#[inline]
|
|
||||||
pub fn z(self) -> f32 {
|
|
||||||
self.0.z()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns element `w`.
|
|
||||||
#[inline]
|
|
||||||
pub fn w(self) -> f32 {
|
|
||||||
self.0.w()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Quat {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
{
|
|
||||||
fmt.debug_tuple("Quat").field(&(self.0).0).finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
fmt.debug_tuple("Quat")
|
|
||||||
.field(&self.0.x())
|
|
||||||
.field(&self.0.y())
|
|
||||||
.field(&self.0.z())
|
|
||||||
.field(&self.0.w())
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Quat {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let (x, y, z, w) = self.0.into();
|
|
||||||
write!(fmt, "[{}, {}, {}, {}]", x, y, z, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Quat> for Quat {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Self) -> Self {
|
|
||||||
self.mul_quat(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MulAssign<Quat> for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn mul_assign(&mut self, other: Self) {
|
|
||||||
*self = self.mul_quat(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec3> for Quat {
|
|
||||||
type Output = Vec3;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Vec3) -> Vec3 {
|
|
||||||
self.mul_vec3(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Neg for Quat {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn neg(self) -> Self {
|
|
||||||
Self(-1.0 * self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::identity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.0.cmpeq(other.0).all()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
self.as_ref().partial_cmp(other.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[f32; 4]> for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &[f32; 4] {
|
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<[f32; 4]> for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn as_mut(&mut self) -> &mut [f32; 4] {
|
|
||||||
self.0.as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec4> for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn from(v: Vec4) -> Self {
|
|
||||||
Self(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Quat> for Vec4 {
|
|
||||||
#[inline]
|
|
||||||
fn from(q: Quat) -> Self {
|
|
||||||
q.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(f32, f32, f32, f32)> for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn from(t: (f32, f32, f32, f32)) -> Self {
|
|
||||||
Quat::from_xyzw(t.0, t.1, t.2, t.3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Quat> for (f32, f32, f32, f32) {
|
|
||||||
#[inline]
|
|
||||||
fn from(q: Quat) -> Self {
|
|
||||||
q.0.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<[f32; 4]> for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn from(a: [f32; 4]) -> Self {
|
|
||||||
Self(a.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Quat> for [f32; 4] {
|
|
||||||
#[inline]
|
|
||||||
fn from(q: Quat) -> Self {
|
|
||||||
q.0.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
impl From<Quat> for __m128 {
|
|
||||||
// TODO: write test
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
#[inline]
|
|
||||||
fn from(q: Quat) -> Self {
|
|
||||||
(q.0).0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
impl From<__m128> for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn from(t: __m128) -> Self {
|
|
||||||
Self(Vec4(t))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,361 +0,0 @@
|
||||||
use super::{Mat4, Quat, Vec3};
|
|
||||||
use core::ops::Mul;
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
use rand::{
|
|
||||||
distributions::{Distribution, Standard},
|
|
||||||
Rng,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct TransformSRT {
|
|
||||||
pub scale: Vec3,
|
|
||||||
pub rotation: Quat,
|
|
||||||
pub translation: Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TransformSRT {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
scale: Vec3::one(),
|
|
||||||
rotation: Quat::identity(),
|
|
||||||
translation: Vec3::zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct TransformRT {
|
|
||||||
pub rotation: Quat,
|
|
||||||
pub translation: Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TransformRT {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
rotation: Quat::identity(),
|
|
||||||
translation: Vec3::zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransformSRT {
|
|
||||||
#[inline]
|
|
||||||
pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
|
|
||||||
Self {
|
|
||||||
scale,
|
|
||||||
rotation,
|
|
||||||
translation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn from_transform_rt(scale: Vec3, rt: &TransformRT) -> Self {
|
|
||||||
Self {
|
|
||||||
scale,
|
|
||||||
rotation: rt.rotation,
|
|
||||||
translation: rt.translation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn identity() -> Self {
|
|
||||||
Self {
|
|
||||||
scale: Vec3::one(),
|
|
||||||
rotation: Quat::identity(),
|
|
||||||
translation: Vec3::zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn inverse(&self) -> Self {
|
|
||||||
let scale = self.scale.reciprocal();
|
|
||||||
let rotation = self.rotation.conjugate();
|
|
||||||
let translation = -(rotation * (self.translation * scale));
|
|
||||||
Self {
|
|
||||||
scale,
|
|
||||||
rotation,
|
|
||||||
translation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn normalize(&self) -> Self {
|
|
||||||
let rotation = self.rotation.normalize();
|
|
||||||
Self {
|
|
||||||
scale: self.scale,
|
|
||||||
rotation,
|
|
||||||
translation: self.translation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_transform(&self, other: &Self) -> Self {
|
|
||||||
mul_srt_srt(self, other)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn transform_vec3(self, other: Vec3) -> Vec3 {
|
|
||||||
(self.rotation * (other * self.scale)) + self.translation
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the absolute difference of all elements between `self`
|
|
||||||
/// and `other` is less than or equal to `max_abs_diff`.
|
|
||||||
///
|
|
||||||
/// This can be used to compare if two `Mat4`'s contain similar elements. It
|
|
||||||
/// works best when comparing with a known value. The `max_abs_diff` that
|
|
||||||
/// should be used used depends on the values being compared against.
|
|
||||||
///
|
|
||||||
/// For more on floating point comparisons see
|
|
||||||
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
|
||||||
#[inline]
|
|
||||||
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
|
|
||||||
self.scale.abs_diff_eq(other.scale, max_abs_diff)
|
|
||||||
&& self.rotation.abs_diff_eq(other.rotation, max_abs_diff)
|
|
||||||
&& self
|
|
||||||
.translation
|
|
||||||
.abs_diff_eq(other.translation, max_abs_diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn mul_srt_srt(lhs: &TransformSRT, rhs: &TransformSRT) -> TransformSRT {
|
|
||||||
// from rtm qvv_mul
|
|
||||||
let min_scale = lhs.scale.min(rhs.scale);
|
|
||||||
let scale = lhs.scale * rhs.scale;
|
|
||||||
|
|
||||||
if min_scale.cmplt(Vec3::zero()).any() {
|
|
||||||
// If negative scale, we go through a matrix
|
|
||||||
let lhs_mtx =
|
|
||||||
Mat4::from_scale_rotation_translation(lhs.scale, lhs.rotation, lhs.translation);
|
|
||||||
let rhs_mtx =
|
|
||||||
Mat4::from_scale_rotation_translation(rhs.scale, rhs.rotation, rhs.translation);
|
|
||||||
let mut result_mtx = lhs_mtx * rhs_mtx;
|
|
||||||
|
|
||||||
let sign = scale.sign();
|
|
||||||
result_mtx
|
|
||||||
.set_x_axis((result_mtx.x_axis().truncate().normalize() * sign.dup_x()).extend(0.0));
|
|
||||||
result_mtx
|
|
||||||
.set_y_axis((result_mtx.y_axis().truncate().normalize() * sign.dup_y()).extend(0.0));
|
|
||||||
result_mtx
|
|
||||||
.set_z_axis((result_mtx.z_axis().truncate().normalize() * sign.dup_z()).extend(0.0));
|
|
||||||
|
|
||||||
let rotation = Quat::from_rotation_mat4(&result_mtx);
|
|
||||||
let translation = result_mtx.w_axis().truncate();
|
|
||||||
TransformSRT {
|
|
||||||
scale,
|
|
||||||
rotation,
|
|
||||||
translation,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let rotation = lhs.rotation * rhs.rotation;
|
|
||||||
let translation = (rhs.rotation * (lhs.translation * rhs.scale)) + rhs.translation;
|
|
||||||
TransformSRT {
|
|
||||||
scale,
|
|
||||||
rotation,
|
|
||||||
translation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn mul_rt_rt(lhs: &TransformRT, rhs: &TransformRT) -> TransformRT {
|
|
||||||
let rotation = lhs.rotation * rhs.rotation;
|
|
||||||
let translation = (rhs.rotation * lhs.translation) + rhs.translation;
|
|
||||||
TransformRT {
|
|
||||||
rotation,
|
|
||||||
translation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TransformRT {
|
|
||||||
#[inline]
|
|
||||||
pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
|
|
||||||
Self {
|
|
||||||
rotation,
|
|
||||||
translation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn identity() -> Self {
|
|
||||||
Self {
|
|
||||||
rotation: Quat::identity(),
|
|
||||||
translation: Vec3::zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn inverse(&self) -> Self {
|
|
||||||
let rotation = self.rotation.conjugate();
|
|
||||||
let translation = -(rotation * self.translation);
|
|
||||||
Self {
|
|
||||||
rotation,
|
|
||||||
translation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn normalize(&self) -> Self {
|
|
||||||
let rotation = self.rotation.normalize();
|
|
||||||
Self {
|
|
||||||
rotation,
|
|
||||||
translation: self.translation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_transform(&self, other: &Self) -> Self {
|
|
||||||
mul_rt_rt(self, other)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn transform_vec3(self, other: Vec3) -> Vec3 {
|
|
||||||
(self.rotation * other) + self.translation
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the absolute difference of all elements between `self`
|
|
||||||
/// and `other` is less than or equal to `max_abs_diff`.
|
|
||||||
///
|
|
||||||
/// This can be used to compare if two `Mat4`'s contain similar elements. It
|
|
||||||
/// works best when comparing with a known value. The `max_abs_diff` that
|
|
||||||
/// should be used used depends on the values being compared against.
|
|
||||||
///
|
|
||||||
/// For more on floating point comparisons see
|
|
||||||
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
|
||||||
#[inline]
|
|
||||||
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
|
|
||||||
self.rotation.abs_diff_eq(other.rotation, max_abs_diff)
|
|
||||||
&& self
|
|
||||||
.translation
|
|
||||||
.abs_diff_eq(other.translation, max_abs_diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<TransformRT> for TransformSRT {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &TransformRT {
|
|
||||||
unsafe { &*(self as *const Self as *const TransformRT) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<TransformRT> for TransformSRT {
|
|
||||||
#[inline]
|
|
||||||
fn as_mut(&mut self) -> &mut TransformRT {
|
|
||||||
unsafe { &mut *(self as *mut Self as *mut TransformRT) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec3> for TransformRT {
|
|
||||||
type Output = Vec3;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Vec3) -> Vec3 {
|
|
||||||
self.transform_vec3(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec3> for TransformSRT {
|
|
||||||
type Output = Vec3;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Vec3) -> Vec3 {
|
|
||||||
self.transform_vec3(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<TransformRT> for TransformRT {
|
|
||||||
type Output = TransformRT;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: TransformRT) -> TransformRT {
|
|
||||||
mul_rt_rt(&self, &other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<TransformSRT> for TransformSRT {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Self) -> Self::Output {
|
|
||||||
mul_srt_srt(&self, &other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<TransformRT> for TransformSRT {
|
|
||||||
type Output = TransformSRT;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: TransformRT) -> Self::Output {
|
|
||||||
mul_srt_srt(&self, &other.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<TransformSRT> for TransformRT {
|
|
||||||
type Output = TransformSRT;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: TransformSRT) -> Self::Output {
|
|
||||||
mul_srt_srt(&self.into(), &other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TransformRT> for TransformSRT {
|
|
||||||
#[inline]
|
|
||||||
fn from(tr: TransformRT) -> Self {
|
|
||||||
Self {
|
|
||||||
translation: tr.translation,
|
|
||||||
rotation: tr.rotation,
|
|
||||||
scale: Vec3::one(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
impl Distribution<TransformRT> for Standard {
|
|
||||||
#[inline]
|
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> TransformRT {
|
|
||||||
TransformRT::from_rotation_translation(
|
|
||||||
rng.gen::<Quat>(),
|
|
||||||
Vec3::new(
|
|
||||||
rng.gen_range(core::f32::MIN, core::f32::MAX),
|
|
||||||
rng.gen_range(core::f32::MIN, core::f32::MAX),
|
|
||||||
rng.gen_range(core::f32::MIN, core::f32::MAX),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
impl Distribution<TransformSRT> for Standard {
|
|
||||||
#[inline]
|
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> TransformSRT {
|
|
||||||
let mut gen_non_zero = || loop {
|
|
||||||
let f: f32 = rng.gen_range(core::f32::MIN, core::f32::MAX);
|
|
||||||
if f.abs() > core::f32::MIN_POSITIVE {
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
TransformSRT::from_scale_rotation_translation(
|
|
||||||
Vec3::new(gen_non_zero(), gen_non_zero(), gen_non_zero()),
|
|
||||||
rng.gen::<Quat>(),
|
|
||||||
Vec3::new(
|
|
||||||
rng.gen_range(core::f32::MIN, core::f32::MAX),
|
|
||||||
rng.gen_range(core::f32::MIN, core::f32::MAX),
|
|
||||||
rng.gen_range(core::f32::MIN, core::f32::MAX),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TransformSRT> for Mat4 {
|
|
||||||
#[inline]
|
|
||||||
fn from(srt: TransformSRT) -> Self {
|
|
||||||
Mat4::from_scale_rotation_translation(srt.scale, srt.rotation, srt.translation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TransformRT> for Mat4 {
|
|
||||||
#[inline]
|
|
||||||
fn from(rt: TransformRT) -> Self {
|
|
||||||
Mat4::from_rotation_translation(rt.rotation, rt.translation)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,509 +0,0 @@
|
||||||
use crate::f32::{Vec2Mask, Vec3};
|
|
||||||
use core::{f32, fmt, ops::*};
|
|
||||||
|
|
||||||
/// A 2-dimensional vector.
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vec2(pub(crate) f32, pub(crate) f32);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn vec2(x: f32, y: f32) -> Vec2 {
|
|
||||||
Vec2(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Vec2 {
|
|
||||||
/// Returns a new `Vec4` with elements representing the sign of `self`.
|
|
||||||
///
|
|
||||||
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`
|
|
||||||
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
|
|
||||||
#[inline]
|
|
||||||
pub fn sign(self) -> Self {
|
|
||||||
let mask = self.cmpge(Self::zero());
|
|
||||||
mask.select(Self::splat(1.0), Self::splat(-1.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the reciprocal `1.0/n` of each element, returning the
|
|
||||||
/// results in a new `Vec2`.
|
|
||||||
#[inline]
|
|
||||||
pub fn reciprocal(self) -> Self {
|
|
||||||
Self::one() / self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a linear interpolation between `self` and `other` based on
|
|
||||||
/// the value `s`.
|
|
||||||
///
|
|
||||||
/// When `s` is `0.0`, the result will be equal to `self`. When `s`
|
|
||||||
/// is `1.0`, the result will be equal to `other`.
|
|
||||||
#[inline]
|
|
||||||
pub fn lerp(self, other: Self, s: f32) -> Self {
|
|
||||||
self + ((other - self) * s)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether `self` is length `1.0` or not.
|
|
||||||
///
|
|
||||||
/// Uses a precision threshold of `1e-6`.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_normalized(self) -> bool {
|
|
||||||
is_normalized!(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the absolute difference of all elements between `self`
|
|
||||||
/// and `other` is less than or equal to `max_abs_diff`.
|
|
||||||
///
|
|
||||||
/// This can be used to compare if two `Vec2`'s contain similar elements. It
|
|
||||||
/// works best when comparing with a known value. The `max_abs_diff` that
|
|
||||||
/// should be used used depends on the values being compared against.
|
|
||||||
///
|
|
||||||
/// For more on floating point comparisons see
|
|
||||||
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
|
||||||
#[inline]
|
|
||||||
pub fn abs_diff_eq(self, other: Self, max_abs_diff: f32) -> bool {
|
|
||||||
abs_diff_eq!(self, other, max_abs_diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec2`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(x: f32, y: f32) -> Vec2 {
|
|
||||||
Vec2(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec2` with all elements set to `0.0`.
|
|
||||||
#[inline]
|
|
||||||
pub fn zero() -> Vec2 {
|
|
||||||
Vec2(0.0, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec2` with all elements set to `1.0`.
|
|
||||||
#[inline]
|
|
||||||
pub fn one() -> Vec2 {
|
|
||||||
Vec2(1.0, 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec2` with values `[x: 1.0, y: 0.0]`.
|
|
||||||
#[inline]
|
|
||||||
pub fn unit_x() -> Vec2 {
|
|
||||||
Vec2(1.0, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec2` with values `[x: 0.0, y: 1.0]`.
|
|
||||||
#[inline]
|
|
||||||
pub fn unit_y() -> Vec2 {
|
|
||||||
Vec2(0.0, 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec2` with all elements set to `v`.
|
|
||||||
#[inline]
|
|
||||||
pub fn splat(v: f32) -> Vec2 {
|
|
||||||
Vec2(v, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec3` from `self` and the given `z` value.
|
|
||||||
#[inline]
|
|
||||||
pub fn extend(self, z: f32) -> Vec3 {
|
|
||||||
Vec3::new(self.0, self.1, z)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns element `x`.
|
|
||||||
#[inline]
|
|
||||||
pub fn x(self) -> f32 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns element `y`.
|
|
||||||
#[inline]
|
|
||||||
pub fn y(self) -> f32 {
|
|
||||||
self.1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to element `x`.
|
|
||||||
#[inline]
|
|
||||||
pub fn x_mut(&mut self) -> &mut f32 {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to element `y`.
|
|
||||||
#[inline]
|
|
||||||
pub fn y_mut(&mut self) -> &mut f32 {
|
|
||||||
&mut self.1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets element `x`.
|
|
||||||
#[inline]
|
|
||||||
pub fn set_x(&mut self, x: f32) {
|
|
||||||
self.0 = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets element `y`.
|
|
||||||
#[inline]
|
|
||||||
pub fn set_y(&mut self, y: f32) {
|
|
||||||
self.1 = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the dot product of `self` and `other`.
|
|
||||||
#[inline]
|
|
||||||
pub fn dot(self, other: Vec2) -> f32 {
|
|
||||||
(self.0 * other.0) + (self.1 * other.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the length of `self`.
|
|
||||||
#[inline]
|
|
||||||
pub fn length(self) -> f32 {
|
|
||||||
self.dot(self).sqrt()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the squared length of `self`.
|
|
||||||
///
|
|
||||||
/// This is generally faster than `Vec2::length()` as it avoids a square
|
|
||||||
/// root operation.
|
|
||||||
#[inline]
|
|
||||||
pub fn length_squared(self) -> f32 {
|
|
||||||
self.dot(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes `1.0 / Vec2::length()`.
|
|
||||||
///
|
|
||||||
/// For valid results, `self` must _not_ be of length zero.
|
|
||||||
#[inline]
|
|
||||||
pub fn length_reciprocal(self) -> f32 {
|
|
||||||
1.0 / self.length()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `self` normalized to length 1.0.
|
|
||||||
///
|
|
||||||
/// For valid results, `self` must _not_ be of length zero.
|
|
||||||
#[inline]
|
|
||||||
pub fn normalize(self) -> Vec2 {
|
|
||||||
self * self.length_reciprocal()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the vertical minimum of `self` and `other`.
|
|
||||||
///
|
|
||||||
/// In other words, this computes
|
|
||||||
/// `[x: min(x1, x2), y: min(y1, y2)]`,
|
|
||||||
/// taking the minimum of each element individually.
|
|
||||||
#[inline]
|
|
||||||
pub fn min(self, other: Vec2) -> Vec2 {
|
|
||||||
Vec2(self.0.min(other.0), self.1.min(other.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the vertical maximum of `self` and `other`.
|
|
||||||
///
|
|
||||||
/// In other words, this computes
|
|
||||||
/// `[x: max(x1, x2), y: max(y1, y2)]`,
|
|
||||||
/// taking the maximum of each element individually.
|
|
||||||
#[inline]
|
|
||||||
pub fn max(self, other: Vec2) -> Vec2 {
|
|
||||||
Vec2(self.0.max(other.0), self.1.max(other.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the horizontal minimum of `self`'s elements.
|
|
||||||
///
|
|
||||||
/// In other words, this computes `min(x, y)`.
|
|
||||||
#[inline]
|
|
||||||
pub fn min_element(self) -> f32 {
|
|
||||||
self.0.min(self.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the horizontal maximum of `self`'s elements.
|
|
||||||
///
|
|
||||||
/// In other words, this computes `max(x, y)`.
|
|
||||||
#[inline]
|
|
||||||
pub fn max_element(self) -> f32 {
|
|
||||||
self.0.max(self.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a vertical `==` comparison between `self` and `other`,
|
|
||||||
/// returning a `Vec2Mask` of the results.
|
|
||||||
///
|
|
||||||
/// In other words, this computes `[x1 == x2, y1 == y2, z1 == z2, w1 == w2]`.
|
|
||||||
#[inline]
|
|
||||||
pub fn cmpeq(self, other: Vec2) -> Vec2Mask {
|
|
||||||
Vec2Mask::new(self.0.eq(&other.0), self.1.eq(&other.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a vertical `!=` comparison between `self` and `other`,
|
|
||||||
/// returning a `Vec2Mask` of the results.
|
|
||||||
///
|
|
||||||
/// In other words, this computes `[x1 != x2, y1 != y2, z1 != z2, w1 != w2]`.
|
|
||||||
#[inline]
|
|
||||||
pub fn cmpne(self, other: Vec2) -> Vec2Mask {
|
|
||||||
Vec2Mask::new(self.0.ne(&other.0), self.1.ne(&other.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a vertical `>=` comparison between `self` and `other`,
|
|
||||||
/// returning a `Vec2Mask` of the results.
|
|
||||||
///
|
|
||||||
/// In other words, this computes `[x1 >= x2, y1 >= y2, z1 >= z2, w1 >= w2]`.
|
|
||||||
#[inline]
|
|
||||||
pub fn cmpge(self, other: Vec2) -> Vec2Mask {
|
|
||||||
Vec2Mask::new(self.0.ge(&other.0), self.1.ge(&other.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a vertical `>` comparison between `self` and `other`,
|
|
||||||
/// returning a `Vec2Mask` of the results.
|
|
||||||
///
|
|
||||||
/// In other words, this computes `[x1 > x2, y1 > y2, z1 > z2, w1 > w2]`.
|
|
||||||
#[inline]
|
|
||||||
pub fn cmpgt(self, other: Vec2) -> Vec2Mask {
|
|
||||||
Vec2Mask::new(self.0.gt(&other.0), self.1.gt(&other.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a vertical `<=` comparison between `self` and `other`,
|
|
||||||
/// returning a `Vec2Mask` of the results.
|
|
||||||
///
|
|
||||||
/// In other words, this computes `[x1 <= x2, y1 <= y2, z1 <= z2, w1 <= w2]`.
|
|
||||||
#[inline]
|
|
||||||
pub fn cmple(self, other: Vec2) -> Vec2Mask {
|
|
||||||
Vec2Mask::new(self.0.le(&other.0), self.1.le(&other.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a vertical `<` comparison between `self` and `other`,
|
|
||||||
/// returning a `Vec2Mask` of the results.
|
|
||||||
///
|
|
||||||
/// In other words, this computes `[x1 < x2, y1 < y2, z1 < z2, w1 < w2]`.
|
|
||||||
#[inline]
|
|
||||||
pub fn cmplt(self, other: Vec2) -> Vec2Mask {
|
|
||||||
Vec2Mask::new(self.0.lt(&other.0), self.1.lt(&other.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec2` from the first two values in `slice`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `slice` is less than two elements long.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_slice_unaligned(slice: &[f32]) -> Self {
|
|
||||||
Self(slice[0], slice[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the elements of `self` to the first two elements in `slice`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `slice` is less than two elements long.
|
|
||||||
#[inline]
|
|
||||||
pub fn write_to_slice_unaligned(self, slice: &mut [f32]) {
|
|
||||||
slice[0] = self.0;
|
|
||||||
slice[1] = self.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new `Vec2` containing the absolute value of each element of the original
|
|
||||||
/// `Vec2`.
|
|
||||||
#[inline]
|
|
||||||
pub fn abs(self) -> Self {
|
|
||||||
Self(self.0.abs(), self.1.abs())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn round(self) -> Self {
|
|
||||||
Self(self.0.round(), self.1.round())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn floor(self) -> Self {
|
|
||||||
Self(self.0.floor(), self.1.floor())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn ceil(self) -> Self {
|
|
||||||
Self(self.0.ceil(), self.1.ceil())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The perpendicular dot product of the vector and `other`.
|
|
||||||
#[inline]
|
|
||||||
pub fn perp_dot(self, other: Vec2) -> f32 {
|
|
||||||
(self.0 * other.1) - (self.1 * other.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the angle between two vectors, in radians.
|
|
||||||
///
|
|
||||||
/// The vectors do not need to be unit length, but this function does
|
|
||||||
/// perform a `sqrt`.
|
|
||||||
#[inline]
|
|
||||||
pub fn angle_between(self, other: Self) -> f32 {
|
|
||||||
let angle = crate::f32::funcs::scalar_acos(
|
|
||||||
self.dot(other) / (self.dot(self) * other.dot(other)).sqrt(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.perp_dot(other) < 0.0 {
|
|
||||||
-angle
|
|
||||||
} else {
|
|
||||||
angle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Vec2 {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "[{}, {}]", self.0, self.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<Vec2> for Vec2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn div(self, other: Vec2) -> Self {
|
|
||||||
Self(self.0 / other.0, self.1 / other.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DivAssign<Vec2> for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn div_assign(&mut self, other: Vec2) {
|
|
||||||
self.0 /= other.0;
|
|
||||||
self.1 /= other.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<f32> for Vec2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn div(self, other: f32) -> Self {
|
|
||||||
Self(self.0 / other, self.1 / other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DivAssign<f32> for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn div_assign(&mut self, other: f32) {
|
|
||||||
self.0 /= other;
|
|
||||||
self.1 /= other;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec2> for Vec2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Vec2) -> Self {
|
|
||||||
Self(self.0 * other.0, self.1 * other.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MulAssign<Vec2> for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn mul_assign(&mut self, other: Vec2) {
|
|
||||||
self.0 *= other.0;
|
|
||||||
self.1 *= other.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<f32> for Vec2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: f32) -> Self {
|
|
||||||
Self(self.0 * other, self.1 * other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MulAssign<f32> for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn mul_assign(&mut self, other: f32) {
|
|
||||||
self.0 *= other;
|
|
||||||
self.1 *= other;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul<Vec2> for f32 {
|
|
||||||
type Output = Vec2;
|
|
||||||
#[inline]
|
|
||||||
fn mul(self, other: Vec2) -> Vec2 {
|
|
||||||
Vec2(self * other.0, self * other.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for Vec2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn add(self, other: Self) -> Self {
|
|
||||||
Self(self.0 + other.0, self.1 + other.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn add_assign(&mut self, other: Self) {
|
|
||||||
self.0 += other.0;
|
|
||||||
self.1 += other.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub for Vec2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn sub(self, other: Vec2) -> Self {
|
|
||||||
Self(self.0 - other.0, self.1 - other.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubAssign for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn sub_assign(&mut self, other: Vec2) {
|
|
||||||
self.0 -= other.0;
|
|
||||||
self.1 -= other.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Neg for Vec2 {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn neg(self) -> Self {
|
|
||||||
Self(-self.0, -self.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[f32; 2]> for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &[f32; 2] {
|
|
||||||
unsafe { &*(self as *const Vec2 as *const [f32; 2]) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<[f32; 2]> for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn as_mut(&mut self) -> &mut [f32; 2] {
|
|
||||||
unsafe { &mut *(self as *mut Vec2 as *mut [f32; 2]) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index<usize> for Vec2 {
|
|
||||||
type Output = f32;
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: usize) -> &Self::Output {
|
|
||||||
&self.as_ref()[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexMut<usize> for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
|
||||||
&mut self.as_mut()[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(f32, f32)> for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn from(t: (f32, f32)) -> Self {
|
|
||||||
Self(t.0, t.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec2> for (f32, f32) {
|
|
||||||
#[inline]
|
|
||||||
fn from(v: Vec2) -> Self {
|
|
||||||
(v.0, v.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<[f32; 2]> for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn from(a: [f32; 2]) -> Self {
|
|
||||||
Self(a[0], a[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec2> for [f32; 2] {
|
|
||||||
#[inline]
|
|
||||||
fn from(v: Vec2) -> Self {
|
|
||||||
[v.0, v.1]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
use super::Vec2;
|
|
||||||
use core::{fmt, ops::*};
|
|
||||||
|
|
||||||
/// A 2-dimensional vector mask.
|
|
||||||
///
|
|
||||||
/// This type is typically created by comparison methods on `Vec2`.
|
|
||||||
#[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vec2Mask(u32, u32);
|
|
||||||
|
|
||||||
impl Vec2Mask {
|
|
||||||
/// Creates a new `Vec2Mask`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(x: bool, y: bool) -> Self {
|
|
||||||
const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
|
|
||||||
Self(MASK[x as usize], MASK[y as usize])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a bitmask with the lowest two bits set from the elements of
|
|
||||||
/// the `Vec2Mask`.
|
|
||||||
///
|
|
||||||
/// A true element results in a `1` bit and a false element in a `0` bit.
|
|
||||||
/// Element `x` goes into the first lowest bit, element `y` into the
|
|
||||||
/// second, etc.
|
|
||||||
#[inline]
|
|
||||||
pub fn bitmask(self) -> u32 {
|
|
||||||
(self.0 & 0x1) | (self.1 & 0x1) << 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if any of the elements are true, false otherwise.
|
|
||||||
///
|
|
||||||
/// In other words: `x || y`.
|
|
||||||
#[inline]
|
|
||||||
pub fn any(self) -> bool {
|
|
||||||
// implementaton matches SSE2 `Vec4Mask` version
|
|
||||||
((self.0 | self.1) & 0x1) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if all the elements are true, false otherwise.
|
|
||||||
///
|
|
||||||
/// In other words: `x && y`.
|
|
||||||
#[inline]
|
|
||||||
pub fn all(self) -> bool {
|
|
||||||
// implementaton matches SSE2 `Vec4Mask` version
|
|
||||||
((self.0 & self.1) & 0x1) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec2` from the elements in `if_true` and `if_false`,
|
|
||||||
/// selecting which to use for each element based on the `Vec2Mask`.
|
|
||||||
///
|
|
||||||
/// A true element in the mask uses the corresponding element from
|
|
||||||
/// `if_true`, and false uses the element from `if_false`.
|
|
||||||
#[inline]
|
|
||||||
pub fn select(self, if_true: Vec2, if_false: Vec2) -> Vec2 {
|
|
||||||
Vec2(
|
|
||||||
if self.0 != 0 { if_true.0 } else { if_false.0 },
|
|
||||||
if self.1 != 0 { if_true.1 } else { if_false.1 },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitAnd for Vec2Mask {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitand(self, other: Self) -> Self {
|
|
||||||
Self(self.0 & other.0, self.1 & other.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitAndAssign for Vec2Mask {
|
|
||||||
#[inline]
|
|
||||||
fn bitand_assign(&mut self, other: Self) {
|
|
||||||
self.0 &= other.0;
|
|
||||||
self.1 &= other.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOr for Vec2Mask {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitor(self, other: Self) -> Self {
|
|
||||||
Self(self.0 | other.0, self.1 | other.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOrAssign for Vec2Mask {
|
|
||||||
#[inline]
|
|
||||||
fn bitor_assign(&mut self, other: Self) {
|
|
||||||
self.0 |= other.0;
|
|
||||||
self.1 |= other.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Not for Vec2Mask {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn not(self) -> Self {
|
|
||||||
Self(!self.0, !self.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Vec2Mask {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "Vec2Mask({:#x}, {:#x})", self.0, self.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Vec2Mask {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "[{}, {}]", self.0 != 0, self.1 != 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec2Mask> for [u32; 2] {
|
|
||||||
#[inline]
|
|
||||||
fn from(mask: Vec2Mask) -> Self {
|
|
||||||
[mask.0, mask.1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u32; 2]> for Vec2Mask {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &[u32; 2] {
|
|
||||||
unsafe { &*(self as *const Self as *const [u32; 2]) }
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,298 +0,0 @@
|
||||||
use super::Vec3;
|
|
||||||
use core::{fmt, ops::*};
|
|
||||||
|
|
||||||
#[cfg(all(vec3sse2, target_arch = "x86"))]
|
|
||||||
use core::arch::x86::*;
|
|
||||||
#[cfg(all(vec3sse2, target_arch = "x86_64"))]
|
|
||||||
use core::arch::x86_64::*;
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
use core::{cmp::Ordering, hash};
|
|
||||||
|
|
||||||
/// A 3-dimensional vector mask.
|
|
||||||
///
|
|
||||||
/// This type is typically created by comparison methods on `Vec3`. It is
|
|
||||||
/// essentially a vector of three boolean values.
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vec3Mask(pub(crate) __m128);
|
|
||||||
|
|
||||||
/// A 3-dimensional vector mask.
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
#[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
|
||||||
// if compiling with simd enabled assume alignment needs to match the simd type
|
|
||||||
#[cfg_attr(vec3f32_align16, repr(align(16)))]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vec3Mask(pub(crate) u32, pub(crate) u32, pub(crate) u32);
|
|
||||||
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
impl Default for Vec3Mask {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
unsafe { Self(_mm_setzero_ps()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
impl PartialEq for Vec3Mask {
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.as_ref().eq(other.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
impl Eq for Vec3Mask {}
|
|
||||||
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
impl Ord for Vec3Mask {
|
|
||||||
#[inline]
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
self.as_ref().cmp(other.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
impl PartialOrd for Vec3Mask {
|
|
||||||
#[inline]
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
impl hash::Hash for Vec3Mask {
|
|
||||||
#[inline]
|
|
||||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.as_ref().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Vec3Mask {
|
|
||||||
/// Creates a new `Vec3Mask`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(x: bool, y: bool, z: bool) -> Self {
|
|
||||||
// A SSE2 mask can be any bit pattern but for the `Vec3Mask` implementation of select we
|
|
||||||
// expect either 0 or 0xff_ff_ff_ff. This should be a safe assumption as this type can only
|
|
||||||
// be created via this function or by `Vec3` methods.
|
|
||||||
|
|
||||||
const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
unsafe {
|
|
||||||
Self(_mm_set_ps(
|
|
||||||
f32::from_bits(MASK[z as usize]),
|
|
||||||
f32::from_bits(MASK[z as usize]),
|
|
||||||
f32::from_bits(MASK[y as usize]),
|
|
||||||
f32::from_bits(MASK[x as usize]),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
Self(MASK[x as usize], MASK[y as usize], MASK[z as usize])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a bitmask with the lowest three bits set from the elements of
|
|
||||||
/// the `Vec3Mask`.
|
|
||||||
///
|
|
||||||
/// A true element results in a `1` bit and a false element in a `0` bit.
|
|
||||||
/// Element `x` goes into the first lowest bit, element `y` into the
|
|
||||||
/// second, etc.
|
|
||||||
#[inline]
|
|
||||||
pub fn bitmask(&self) -> u32 {
|
|
||||||
// _mm_movemask_ps only checks the most significant bit of the u32 is
|
|
||||||
// true, so we replicate that here with the non-SSE2 version.
|
|
||||||
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
unsafe {
|
|
||||||
(_mm_movemask_ps(self.0) as u32) & 0x7
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
(self.0 & 0x1) | (self.1 & 0x1) << 1 | (self.2 & 0x1) << 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if any of the elements are true, false otherwise.
|
|
||||||
///
|
|
||||||
/// In other words: `x || y || z`.
|
|
||||||
#[inline]
|
|
||||||
pub fn any(&self) -> bool {
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
unsafe {
|
|
||||||
(_mm_movemask_ps(self.0) & 0x7) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
((self.0 | self.1 | self.2) & 0x1) != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if all the elements are true, false otherwise.
|
|
||||||
///
|
|
||||||
/// In other words: `x && y && z`.
|
|
||||||
#[inline]
|
|
||||||
pub fn all(&self) -> bool {
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
unsafe {
|
|
||||||
(_mm_movemask_ps(self.0) & 0x7) == 0x7
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
((self.0 & self.1 & self.2) & 0x1) != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec3` from the elements in `if_true` and `if_false`,
|
|
||||||
/// selecting which to use for each element based on the `Vec3Mask`.
|
|
||||||
///
|
|
||||||
/// A true element in the mask uses the corresponding element from
|
|
||||||
/// `if_true`, and false uses the element from `if_false`.
|
|
||||||
#[inline]
|
|
||||||
pub fn select(self, if_true: Vec3, if_false: Vec3) -> Vec3 {
|
|
||||||
// We are assuming that the mask values are either 0 or 0xff_ff_ff_ff for the SSE2 and f32
|
|
||||||
// to behave the same here.
|
|
||||||
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
unsafe {
|
|
||||||
Vec3(_mm_or_ps(
|
|
||||||
_mm_andnot_ps(self.0, if_false.0),
|
|
||||||
_mm_and_ps(if_true.0, self.0),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
Vec3(
|
|
||||||
if self.0 != 0 { if_true.0 } else { if_false.0 },
|
|
||||||
if self.1 != 0 { if_true.1 } else { if_false.1 },
|
|
||||||
if self.2 != 0 { if_true.2 } else { if_false.2 },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitAnd for Vec3Mask {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitand(self, other: Self) -> Self {
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
unsafe {
|
|
||||||
Self(_mm_and_ps(self.0, other.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
Self(self.0 & other.0, self.1 & other.1, self.2 & other.2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitAndAssign for Vec3Mask {
|
|
||||||
#[inline]
|
|
||||||
fn bitand_assign(&mut self, other: Self) {
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
{
|
|
||||||
self.0 = unsafe { _mm_and_ps(self.0, other.0) };
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
self.0 &= other.0;
|
|
||||||
self.1 &= other.1;
|
|
||||||
self.2 &= other.2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOr for Vec3Mask {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitor(self, other: Self) -> Self {
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
unsafe {
|
|
||||||
Self(_mm_or_ps(self.0, other.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
Self(self.0 | other.0, self.1 | other.1, self.2 | other.2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOrAssign for Vec3Mask {
|
|
||||||
#[inline]
|
|
||||||
fn bitor_assign(&mut self, other: Self) {
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
{
|
|
||||||
self.0 = unsafe { _mm_or_ps(self.0, other.0) };
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
self.0 |= other.0;
|
|
||||||
self.1 |= other.1;
|
|
||||||
self.2 |= other.2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Not for Vec3Mask {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn not(self) -> Self {
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
unsafe {
|
|
||||||
Self(_mm_andnot_ps(
|
|
||||||
self.0,
|
|
||||||
_mm_set_ps1(f32::from_bits(0xff_ff_ff_ff)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
Self(!self.0, !self.1, !self.2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Vec3Mask {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
#[cfg(vec3sse2)]
|
|
||||||
{
|
|
||||||
let arr = self.as_ref();
|
|
||||||
write!(f, "Vec3Mask({:#x}, {:#x}, {:#x})", arr[0], arr[1], arr[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec3f32)]
|
|
||||||
{
|
|
||||||
write!(f, "Vec3Mask({:#x}, {:#x}, {:#x})", self.0, self.1, self.2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Vec3Mask {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let arr = self.as_ref();
|
|
||||||
write!(f, "[{}, {}, {}]", arr[0] != 0, arr[1] != 0, arr[2] != 0,)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec3Mask> for [u32; 3] {
|
|
||||||
#[inline]
|
|
||||||
fn from(mask: Vec3Mask) -> Self {
|
|
||||||
*mask.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u32; 3]> for Vec3Mask {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &[u32; 3] {
|
|
||||||
unsafe { &*(self as *const Self as *const [u32; 3]) }
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,329 +0,0 @@
|
||||||
use crate::Vec4;
|
|
||||||
use core::{fmt, ops::*};
|
|
||||||
|
|
||||||
#[cfg(all(vec4sse2, target_arch = "x86"))]
|
|
||||||
use core::arch::x86::*;
|
|
||||||
#[cfg(all(vec4sse2, target_arch = "x86_64"))]
|
|
||||||
use core::arch::x86_64::*;
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
use core::{cmp::Ordering, hash};
|
|
||||||
|
|
||||||
/// A 4-dimensional vector mask.
|
|
||||||
///
|
|
||||||
/// This type is typically created by comparison methods on `Vec4`. It is
|
|
||||||
/// essentially a vector of four boolean values.
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vec4Mask(pub(crate) __m128);
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
#[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
|
||||||
#[cfg_attr(vec4f32_align16, repr(align(16)))]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vec4Mask(u32, u32, u32, u32);
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
impl Default for Vec4Mask {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
unsafe { Self(_mm_setzero_ps()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
impl PartialEq for Vec4Mask {
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.as_ref().eq(other.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
impl Eq for Vec4Mask {}
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
impl Ord for Vec4Mask {
|
|
||||||
#[inline]
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
self.as_ref().cmp(other.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
impl PartialOrd for Vec4Mask {
|
|
||||||
#[inline]
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
impl hash::Hash for Vec4Mask {
|
|
||||||
#[inline]
|
|
||||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.as_ref().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Vec4Mask {
|
|
||||||
/// Creates a new `Vec4Mask`.
|
|
||||||
#[inline]
|
|
||||||
pub fn new(x: bool, y: bool, z: bool, w: bool) -> Self {
|
|
||||||
// A SSE2 mask can be any bit pattern but for the `Vec4Mask` implementation of select we
|
|
||||||
// expect either 0 or 0xff_ff_ff_ff. This should be a safe assumption as this type can only
|
|
||||||
// be created via this function or by `Vec4` methods.
|
|
||||||
|
|
||||||
const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
Self(_mm_set_ps(
|
|
||||||
f32::from_bits(MASK[w as usize]),
|
|
||||||
f32::from_bits(MASK[z as usize]),
|
|
||||||
f32::from_bits(MASK[y as usize]),
|
|
||||||
f32::from_bits(MASK[x as usize]),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
Self(
|
|
||||||
MASK[x as usize],
|
|
||||||
MASK[y as usize],
|
|
||||||
MASK[z as usize],
|
|
||||||
MASK[w as usize],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a bitmask with the lowest four bits set from the elements of
|
|
||||||
/// the `Vec4Mask`.
|
|
||||||
///
|
|
||||||
/// A true element results in a `1` bit and a false element in a `0` bit.
|
|
||||||
/// Element `x` goes into the first lowest bit, element `y` into the
|
|
||||||
/// second, etc.
|
|
||||||
#[inline]
|
|
||||||
pub fn bitmask(self) -> u32 {
|
|
||||||
// _mm_movemask_ps only checks the most significant bit of the u32 is true, so we replicate
|
|
||||||
// that here with the non-SSE2 version.
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
_mm_movemask_ps(self.0) as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
(self.0 & 0x1) | (self.1 & 0x1) << 1 | (self.2 & 0x1) << 2 | (self.3 & 0x1) << 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if any of the elements are true, false otherwise.
|
|
||||||
///
|
|
||||||
/// In other words: `x || y || z || w`.
|
|
||||||
#[inline]
|
|
||||||
pub fn any(self) -> bool {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
_mm_movemask_ps(self.0) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
((self.0 | self.1 | self.2 | self.3) & 0x1) != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if all the elements are true, false otherwise.
|
|
||||||
///
|
|
||||||
/// In other words: `x && y && z && w`.
|
|
||||||
#[inline]
|
|
||||||
pub fn all(self) -> bool {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
_mm_movemask_ps(self.0) == 0xf
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
((self.0 & self.1 & self.2 & self.3) & 0x1) != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec4` from the elements in `if_true` and `if_false`,
|
|
||||||
/// selecting which to use for each element based on the `Vec4Mask`.
|
|
||||||
///
|
|
||||||
/// A true element in the mask uses the corresponding element from
|
|
||||||
/// `if_true`, and false uses the element from `if_false`.
|
|
||||||
#[inline]
|
|
||||||
pub fn select(self, if_true: Vec4, if_false: Vec4) -> Vec4 {
|
|
||||||
// We are assuming that the mask values are either 0 or 0xff_ff_ff_ff for the SSE2 and f32
|
|
||||||
// to behave the same here.
|
|
||||||
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
Vec4(_mm_or_ps(
|
|
||||||
_mm_andnot_ps(self.0, if_false.0),
|
|
||||||
_mm_and_ps(if_true.0, self.0),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
Vec4(
|
|
||||||
if self.0 != 0 { if_true.0 } else { if_false.0 },
|
|
||||||
if self.1 != 0 { if_true.1 } else { if_false.1 },
|
|
||||||
if self.2 != 0 { if_true.2 } else { if_false.2 },
|
|
||||||
if self.3 != 0 { if_true.3 } else { if_false.3 },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitAnd for Vec4Mask {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitand(self, other: Self) -> Self {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
Self(_mm_and_ps(self.0, other.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
Self(
|
|
||||||
self.0 & other.0,
|
|
||||||
self.1 & other.1,
|
|
||||||
self.2 & other.2,
|
|
||||||
self.3 & other.3,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitAndAssign for Vec4Mask {
|
|
||||||
#[inline]
|
|
||||||
fn bitand_assign(&mut self, other: Self) {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
{
|
|
||||||
self.0 = unsafe { _mm_and_ps(self.0, other.0) };
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
self.0 &= other.0;
|
|
||||||
self.1 &= other.1;
|
|
||||||
self.2 &= other.2;
|
|
||||||
self.3 &= other.3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOr for Vec4Mask {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn bitor(self, other: Self) -> Self {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
Self(_mm_or_ps(self.0, other.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
Self(
|
|
||||||
self.0 | other.0,
|
|
||||||
self.1 | other.1,
|
|
||||||
self.2 | other.2,
|
|
||||||
self.3 | other.3,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOrAssign for Vec4Mask {
|
|
||||||
#[inline]
|
|
||||||
fn bitor_assign(&mut self, other: Self) {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
{
|
|
||||||
self.0 = unsafe { _mm_or_ps(self.0, other.0) };
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
self.0 |= other.0;
|
|
||||||
self.1 |= other.1;
|
|
||||||
self.2 |= other.2;
|
|
||||||
self.3 |= other.3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Not for Vec4Mask {
|
|
||||||
type Output = Self;
|
|
||||||
#[inline]
|
|
||||||
fn not(self) -> Self {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
unsafe {
|
|
||||||
Self(_mm_andnot_ps(
|
|
||||||
self.0,
|
|
||||||
_mm_set_ps1(f32::from_bits(0xff_ff_ff_ff)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
Self(!self.0, !self.1, !self.2, !self.3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Vec4Mask {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
#[cfg(vec4sse2)]
|
|
||||||
{
|
|
||||||
let arr = self.as_ref();
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Vec4Mask({:#x}, {:#x}, {:#x}, {:#x})",
|
|
||||||
arr[0], arr[1], arr[2], arr[3]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(vec4f32)]
|
|
||||||
{
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Vec4Mask({:#x}, {:#x}, {:#x}, {:#x})",
|
|
||||||
self.0, self.1, self.2, self.3
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Vec4Mask {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let arr = self.as_ref();
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"[{}, {}, {}, {}]",
|
|
||||||
arr[0] != 0,
|
|
||||||
arr[1] != 0,
|
|
||||||
arr[2] != 0,
|
|
||||||
arr[3] != 0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec4Mask> for [u32; 4] {
|
|
||||||
#[inline]
|
|
||||||
fn from(mask: Vec4Mask) -> Self {
|
|
||||||
*mask.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u32; 4]> for Vec4Mask {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &[u32; 4] {
|
|
||||||
unsafe { &*(self as *const Self as *const [u32; 4]) }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
use core::arch::x86::*;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
use core::arch::x86_64::*;
|
|
||||||
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub(crate) union UnionCast {
|
|
||||||
pub m128: __m128,
|
|
||||||
pub m128i: __m128i,
|
|
||||||
pub f32x4: [f32; 4],
|
|
||||||
pub i32x4: [i32; 4],
|
|
||||||
pub u32x4: [u32; 4],
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
/*!
|
|
||||||
# glam
|
|
||||||
|
|
||||||
`glam` is a simple and fast linear algebra library for games and graphics.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
`glam` is built with SIMD in mind. Currently only SSE2 on x86/x86_64 is
|
|
||||||
supported as this is what stable Rust supports.
|
|
||||||
|
|
||||||
* Single precision float (`f32`) support only
|
|
||||||
* SSE2 implementation for most types, including `Mat2`, `Mat3`, `Mat4`, `Quat`,
|
|
||||||
`Vec3` and `Vec4`
|
|
||||||
* Scalar fallback implementations exist when SSE2 is not available
|
|
||||||
* Most functionality includes unit tests and benchmarks
|
|
||||||
|
|
||||||
## Linear algebra conventions
|
|
||||||
|
|
||||||
`glam` interprets vectors as column matrices (also known as "column vectors")
|
|
||||||
meaning when transforming a vector with a matrix the matrix goes on the left.
|
|
||||||
|
|
||||||
```
|
|
||||||
use glam::{Mat3, Vec3};
|
|
||||||
let m = Mat3::identity();
|
|
||||||
let x = Vec3::unit_x();
|
|
||||||
let v = m * x;
|
|
||||||
assert_eq!(v, x);
|
|
||||||
```
|
|
||||||
|
|
||||||
Matrices are stored in memory in column-major order.
|
|
||||||
|
|
||||||
Rotations follow left-hand rule. The direction of the axis gives the direction
|
|
||||||
of rotation: with the left thumb pointing in the positive direction of the axis
|
|
||||||
the left fingers curl around the axis in the direction of the rotation.
|
|
||||||
|
|
||||||
```
|
|
||||||
use glam::{Mat3, Vec3};
|
|
||||||
// rotate +x 90 degrees clockwise around y giving -z
|
|
||||||
let m = Mat3::from_rotation_y(90.0_f32.to_radians());
|
|
||||||
let v = m * Vec3::unit_x();
|
|
||||||
assert!(v.abs_diff_eq(-Vec3::unit_z(), core::f32::EPSILON));
|
|
||||||
```
|
|
||||||
|
|
||||||
## Size and alignment of types
|
|
||||||
|
|
||||||
Most `glam` types use SIMD for storage meaning most types are 16 byte aligned.
|
|
||||||
The only exception is Vec2`. When SSE2 is not available on the target
|
|
||||||
architecture the types will still be 16 byte aligned, so object sizes and
|
|
||||||
layouts will not change between architectures.
|
|
||||||
|
|
||||||
16 byte alignment means that some types will have a stride larger than their
|
|
||||||
size resulting in some wasted space.
|
|
||||||
|
|
||||||
| Type | f32 bytes | SIMD bytes | Wasted bytes |
|
|
||||||
|:-----|----------:|-----------:|-------------:|
|
|
||||||
|`Vec3`| 12| 16| 4|
|
|
||||||
|`Mat3`| 36| 48| 12|
|
|
||||||
|
|
||||||
Despite this wasted space the SIMD version tends to outperform the `f32`
|
|
||||||
implementation in [**mathbench**](https://github.com/bitshifter/mathbench-rs)
|
|
||||||
benchmarks.
|
|
||||||
|
|
||||||
SIMD support can be disabled entirely using the `scalar-math` feature. This
|
|
||||||
feature will also disable SIMD alignment meaning most types will use native
|
|
||||||
`f32` alignment of 4 bytes.
|
|
||||||
|
|
||||||
All the main `glam` types are tagged with #[repr(C)], so they are possible
|
|
||||||
to expose as struct members to C interfaces if desired. Be mindful of Vec3's
|
|
||||||
extra float though.
|
|
||||||
|
|
||||||
## Accessing internal data
|
|
||||||
|
|
||||||
The SIMD types that `glam` builds on are opaque and their contents are not
|
|
||||||
directly accessible. Because of this `glam` types uses getter and setter
|
|
||||||
methods instead of providing direct access.
|
|
||||||
|
|
||||||
```
|
|
||||||
use glam::Vec3;
|
|
||||||
let mut v = Vec3::new(1.0, 2.0, 3.0);
|
|
||||||
assert_eq!(v.y(), 2.0);
|
|
||||||
v.set_z(1.0);
|
|
||||||
assert_eq!(v.z(), 1.0);
|
|
||||||
```
|
|
||||||
|
|
||||||
If you need to access multiple elements it is easier to convert the type to a
|
|
||||||
tuple or array:
|
|
||||||
|
|
||||||
```
|
|
||||||
use glam::Vec3;
|
|
||||||
let v = Vec3::new(1.0, 2.0, 3.0);
|
|
||||||
let (x, y, z) = v.into();
|
|
||||||
assert_eq!((x, y, z), (1.0, 2.0, 3.0));
|
|
||||||
```
|
|
||||||
|
|
||||||
## SIMD and scalar consistency
|
|
||||||
|
|
||||||
`glam` types implement `serde` `Serialize` and `Deserialize` traits to ensure
|
|
||||||
that they will serialize and deserialize exactly the same whether or not
|
|
||||||
SIMD support is being used.
|
|
||||||
|
|
||||||
The SIMD versions implement `core::fmt::Display` traits so they print the same as
|
|
||||||
the scalar version.
|
|
||||||
|
|
||||||
```
|
|
||||||
use glam::Vec3;
|
|
||||||
let a = Vec3::new(1.0, 2.0, 3.0);
|
|
||||||
assert_eq!(format!("{}", a), "[1, 2, 3]");
|
|
||||||
```
|
|
||||||
|
|
||||||
## Feature gates
|
|
||||||
|
|
||||||
All `glam` dependencies are optional, however some are required for tests
|
|
||||||
and benchmarks.
|
|
||||||
|
|
||||||
* `std` - the default feature, has no dependencies.
|
|
||||||
* `rand` - used to generate random values. Used in benchmarks.
|
|
||||||
* `serde` - used for serialization and deserialization of types.
|
|
||||||
* `mint` - used for interoperating with other linear algebra libraries.
|
|
||||||
* `scalar-math` - disables SIMD support and uses native alignment for all
|
|
||||||
types.
|
|
||||||
* `debug-glam-assert` - adds assertions in debug builds which check the validity
|
|
||||||
of parameters passed to `glam` to help catch runtime errors.
|
|
||||||
* `glam-assert` - adds assertions to all builds which check the validity of
|
|
||||||
parameters passed to `glam` to help catch runtime errors.
|
|
||||||
|
|
||||||
*/
|
|
||||||
#![doc(html_root_url = "https://docs.rs/glam/0.8.7")]
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
|
|
||||||
pub mod f32;
|
|
||||||
|
|
||||||
pub use self::f32::{
|
|
||||||
mat2, mat3, mat4, quat, vec2, vec3, vec4, Mat2, Mat3, Mat4, Quat, Vec2, Vec2Mask, Vec3,
|
|
||||||
Vec3Mask, Vec4, Vec4Mask,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "transform-types")]
|
|
||||||
pub use self::f32::{TransformRT, TransformSRT};
|
|
||||||
|
|
||||||
#[repr(align(16))]
|
|
||||||
pub(crate) struct Align16<T>(T);
|
|
||||||
|
|
||||||
impl<T> Align16<T> {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn as_ptr(&self) -> *const T {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut T {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_align16() {
|
|
||||||
use core::{mem, ptr};
|
|
||||||
let mut a = Align16::<f32>(1.0);
|
|
||||||
assert_eq!(mem::align_of_val(&a), 16);
|
|
||||||
unsafe {
|
|
||||||
assert_eq!(ptr::read(a.as_ptr()), 1.0);
|
|
||||||
ptr::write(a.as_mut_ptr(), -1.0);
|
|
||||||
}
|
|
||||||
assert_eq!(a.0, -1.0);
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
#[cfg(any(
|
|
||||||
all(debug_assertions, feature = "debug-glam-assert"),
|
|
||||||
feature = "glam-assert"
|
|
||||||
))]
|
|
||||||
macro_rules! glam_assert {
|
|
||||||
($($arg:tt)*) => ( assert!($($arg)*); )
|
|
||||||
}
|
|
||||||
#[cfg(not(any(
|
|
||||||
all(debug_assertions, feature = "debug-glam-assert"),
|
|
||||||
feature = "glam-assert"
|
|
||||||
)))]
|
|
||||||
macro_rules! glam_assert {
|
|
||||||
($($arg:tt)*) => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! is_normalized {
|
|
||||||
($self:expr, $max_diff:expr) => {
|
|
||||||
($self.length_squared() - 1.0).abs() <= $max_diff
|
|
||||||
};
|
|
||||||
($self:expr) => {
|
|
||||||
is_normalized!($self, 1e-6)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! abs_diff_eq {
|
|
||||||
($self:expr, $other:expr, $max_abs_diff:expr) => {
|
|
||||||
($self - $other)
|
|
||||||
.abs()
|
|
||||||
.cmple(Self::splat($max_abs_diff))
|
|
||||||
.all()
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,186 +0,0 @@
|
||||||
mod support;
|
|
||||||
|
|
||||||
use glam::f32::*;
|
|
||||||
use support::deg;
|
|
||||||
|
|
||||||
const IDENTITY: [[f32; 2]; 2] = [[1.0, 0.0], [0.0, 1.0]];
|
|
||||||
|
|
||||||
const MATRIX: [[f32; 2]; 2] = [[1.0, 2.0], [3.0, 4.0]];
|
|
||||||
|
|
||||||
const ZERO: [[f32; 2]; 2] = [[0.0; 2]; 2];
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_align() {
|
|
||||||
use std::mem;
|
|
||||||
assert_eq!(16, mem::size_of::<Mat2>());
|
|
||||||
if cfg!(feature = "scalar-math") {
|
|
||||||
assert_eq!(4, mem::align_of::<Mat2>());
|
|
||||||
} else {
|
|
||||||
assert_eq!(16, mem::align_of::<Mat2>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_identity() {
|
|
||||||
let identity = Mat2::identity();
|
|
||||||
assert_eq!(IDENTITY, identity.to_cols_array_2d());
|
|
||||||
assert_eq!(Mat2::from_cols_array_2d(&IDENTITY), identity);
|
|
||||||
assert_eq!(identity, identity * identity);
|
|
||||||
assert_eq!(identity, Mat2::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_zero() {
|
|
||||||
assert_eq!(Mat2::from_cols_array_2d(&ZERO), Mat2::zero());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_accessors() {
|
|
||||||
let mut m = Mat2::zero();
|
|
||||||
m.set_x_axis(Vec2::new(1.0, 2.0));
|
|
||||||
m.set_y_axis(Vec2::new(3.0, 4.0));
|
|
||||||
assert_eq!(Mat2::from_cols_array_2d(&MATRIX), m);
|
|
||||||
assert_eq!(Vec2::new(1.0, 2.0), m.x_axis());
|
|
||||||
assert_eq!(Vec2::new(3.0, 4.0), m.y_axis());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_from_axes() {
|
|
||||||
let a = Mat2::from_cols_array_2d(&[[1.0, 2.0], [3.0, 4.0]]);
|
|
||||||
assert_eq!(MATRIX, a.to_cols_array_2d());
|
|
||||||
let b = Mat2::from_cols(vec2(1.0, 2.0), vec2(3.0, 4.0));
|
|
||||||
assert_eq!(a, b);
|
|
||||||
let c = mat2(vec2(1.0, 2.0), vec2(3.0, 4.0));
|
|
||||||
assert_eq!(a, c);
|
|
||||||
let d = b.to_cols_array();
|
|
||||||
let f = Mat2::from_cols_array(&d);
|
|
||||||
assert_eq!(b, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_mul() {
|
|
||||||
let mat_a = Mat2::from_angle(deg(90.0));
|
|
||||||
let res_a = mat_a * Vec2::unit_y();
|
|
||||||
assert_approx_eq!(vec2(-1.0, 0.0), res_a);
|
|
||||||
let res_b = mat_a * Vec2::unit_x();
|
|
||||||
assert_approx_eq!(vec2(0.0, 1.0), res_b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_scale() {
|
|
||||||
let m = Mat2::from_scale(Vec2::new(2.0, 4.0));
|
|
||||||
assert_approx_eq!(m * Vec2::new(1.0, 1.0), Vec2::new(2.0, 4.0));
|
|
||||||
assert_approx_eq!(Vec2::unit_x() * 2.0, m.x_axis());
|
|
||||||
assert_approx_eq!(Vec2::unit_y() * 4.0, m.y_axis());
|
|
||||||
|
|
||||||
let rot = Mat2::from_scale_angle(Vec2::new(4.0, 2.0), deg(180.0));
|
|
||||||
assert_approx_eq!(Vec2::unit_x() * -4.0, rot * Vec2::unit_x(), 1.0e-6);
|
|
||||||
assert_approx_eq!(Vec2::unit_y() * -2.0, rot * Vec2::unit_y(), 1.0e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_transpose() {
|
|
||||||
let m = mat2(vec2(1.0, 2.0), vec2(3.0, 4.0));
|
|
||||||
let mt = m.transpose();
|
|
||||||
assert_eq!(mt.x_axis(), vec2(1.0, 3.0));
|
|
||||||
assert_eq!(mt.y_axis(), vec2(2.0, 4.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_det() {
|
|
||||||
assert_eq!(0.0, Mat2::zero().determinant());
|
|
||||||
assert_eq!(1.0, Mat2::identity().determinant());
|
|
||||||
assert_eq!(1.0, Mat2::from_angle(deg(90.0)).determinant());
|
|
||||||
assert_eq!(1.0, Mat2::from_angle(deg(180.0)).determinant());
|
|
||||||
assert_eq!(1.0, Mat2::from_angle(deg(270.0)).determinant());
|
|
||||||
assert_eq!(2.0 * 2.0, Mat2::from_scale(vec2(2.0, 2.0)).determinant());
|
|
||||||
assert_eq!(
|
|
||||||
1.0 * 4.0 - 2.0 * 3.0,
|
|
||||||
Mat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0]).determinant()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_inverse() {
|
|
||||||
let inv = Mat2::identity().inverse();
|
|
||||||
assert_approx_eq!(Mat2::identity(), inv);
|
|
||||||
|
|
||||||
let rot = Mat2::from_angle(deg(90.0));
|
|
||||||
let rot_inv = rot.inverse();
|
|
||||||
assert_approx_eq!(Mat2::identity(), rot * rot_inv);
|
|
||||||
assert_approx_eq!(Mat2::identity(), rot_inv * rot);
|
|
||||||
|
|
||||||
let scale = Mat2::from_scale(vec2(4.0, 5.0));
|
|
||||||
let scale_inv = scale.inverse();
|
|
||||||
assert_approx_eq!(Mat2::identity(), scale * scale_inv);
|
|
||||||
assert_approx_eq!(Mat2::identity(), scale_inv * scale);
|
|
||||||
|
|
||||||
let m = scale * rot;
|
|
||||||
let m_inv = m.inverse();
|
|
||||||
assert_approx_eq!(Mat2::identity(), m * m_inv);
|
|
||||||
assert_approx_eq!(Mat2::identity(), m_inv * m);
|
|
||||||
assert_approx_eq!(m_inv, rot_inv * scale_inv);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_ops() {
|
|
||||||
let m0 = Mat2::from_cols_array_2d(&MATRIX);
|
|
||||||
assert_eq!(
|
|
||||||
Mat2::from_cols_array_2d(&[[2.0, 4.0], [6.0, 8.0]]),
|
|
||||||
m0 * 2.0
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Mat2::from_cols_array_2d(&[[2.0, 4.0], [6.0, 8.0]]),
|
|
||||||
2.0 * m0
|
|
||||||
);
|
|
||||||
assert_eq!(Mat2::from_cols_array_2d(&[[2.0, 4.0], [6.0, 8.0]]), m0 + m0);
|
|
||||||
assert_eq!(Mat2::zero(), m0 - m0);
|
|
||||||
assert_approx_eq!(
|
|
||||||
Mat2::from_cols_array_2d(&[[1.0, 2.0], [3.0, 4.0]]),
|
|
||||||
m0 * Mat2::identity()
|
|
||||||
);
|
|
||||||
assert_approx_eq!(
|
|
||||||
Mat2::from_cols_array_2d(&[[1.0, 2.0], [3.0, 4.0]]),
|
|
||||||
Mat2::identity() * m0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_fmt() {
|
|
||||||
let a = Mat2::from_cols_array_2d(&MATRIX);
|
|
||||||
assert_eq!(format!("{}", a), "[[1, 2], [3, 4]]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_serde() {
|
|
||||||
let a = Mat2::from_cols(vec2(1.0, 2.0), vec2(3.0, 4.0));
|
|
||||||
let serialized = serde_json::to_string(&a).unwrap();
|
|
||||||
assert_eq!(serialized, "[1.0,2.0,3.0,4.0]");
|
|
||||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
|
||||||
assert_eq!(a, deserialized);
|
|
||||||
let deserialized = serde_json::from_str::<Mat2>("[]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat2>("[1.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat2>("[1.0,2.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat2>("[1.0,2.0,3.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat2>("[1.0,2.0,3.0,4.0,5.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat2>("[[1.0,2.0],[3.0,4.0]]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
#[test]
|
|
||||||
fn test_mat2_rand() {
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use rand_xoshiro::Xoshiro256Plus;
|
|
||||||
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let a = Mat2::from_cols_array(&rng1.gen::<[f32; 4]>());
|
|
||||||
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let b = rng2.gen::<Mat2>();
|
|
||||||
assert_eq!(a, b);
|
|
||||||
}
|
|
|
@ -1,253 +0,0 @@
|
||||||
mod support;
|
|
||||||
|
|
||||||
use glam::f32::*;
|
|
||||||
use support::deg;
|
|
||||||
|
|
||||||
const IDENTITY: [[f32; 3]; 3] = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
|
|
||||||
|
|
||||||
const MATRIX: [[f32; 3]; 3] = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
|
|
||||||
|
|
||||||
const ZERO: [[f32; 3]; 3] = [[0.0; 3]; 3];
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_align() {
|
|
||||||
use std::mem;
|
|
||||||
if cfg!(any(feature = "packed-vec3", feature = "scalar-math")) {
|
|
||||||
assert_eq!(36, mem::size_of::<Mat3>());
|
|
||||||
assert_eq!(4, mem::align_of::<Mat3>());
|
|
||||||
} else {
|
|
||||||
assert_eq!(48, mem::size_of::<Mat3>());
|
|
||||||
assert_eq!(16, mem::align_of::<Mat3>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_identity() {
|
|
||||||
let identity = Mat3::identity();
|
|
||||||
assert_eq!(IDENTITY, identity.to_cols_array_2d());
|
|
||||||
assert_eq!(Mat3::from_cols_array_2d(&IDENTITY), identity);
|
|
||||||
assert_eq!(identity, identity * identity);
|
|
||||||
assert_eq!(identity, Mat3::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_zero() {
|
|
||||||
assert_eq!(Mat3::from_cols_array_2d(&ZERO), Mat3::zero());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_accessors() {
|
|
||||||
let mut m = Mat3::zero();
|
|
||||||
m.set_x_axis(Vec3::new(1.0, 2.0, 3.0));
|
|
||||||
m.set_y_axis(Vec3::new(4.0, 5.0, 6.0));
|
|
||||||
m.set_z_axis(Vec3::new(7.0, 8.0, 9.0));
|
|
||||||
assert_eq!(Mat3::from_cols_array_2d(&MATRIX), m);
|
|
||||||
assert_eq!(Vec3::new(1.0, 2.0, 3.0), m.x_axis());
|
|
||||||
assert_eq!(Vec3::new(4.0, 5.0, 6.0), m.y_axis());
|
|
||||||
assert_eq!(Vec3::new(7.0, 8.0, 9.0), m.z_axis());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_from_axes() {
|
|
||||||
let a = Mat3::from_cols_array_2d(&[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
|
|
||||||
assert_eq!(MATRIX, a.to_cols_array_2d());
|
|
||||||
let b = Mat3::from_cols(
|
|
||||||
vec3(1.0, 2.0, 3.0),
|
|
||||||
vec3(4.0, 5.0, 6.0),
|
|
||||||
vec3(7.0, 8.0, 9.0),
|
|
||||||
);
|
|
||||||
assert_eq!(a, b);
|
|
||||||
let c = mat3(
|
|
||||||
vec3(1.0, 2.0, 3.0),
|
|
||||||
vec3(4.0, 5.0, 6.0),
|
|
||||||
vec3(7.0, 8.0, 9.0),
|
|
||||||
);
|
|
||||||
assert_eq!(a, c);
|
|
||||||
let d = b.to_cols_array();
|
|
||||||
let f = Mat3::from_cols_array(&d);
|
|
||||||
assert_eq!(b, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_rotation() {
|
|
||||||
let rot_x1 = Mat3::from_rotation_x(deg(180.0));
|
|
||||||
let rot_x2 = Mat3::from_axis_angle(Vec3::unit_x(), deg(180.0));
|
|
||||||
assert_approx_eq!(rot_x1, rot_x2);
|
|
||||||
let rot_y1 = Mat3::from_rotation_y(deg(180.0));
|
|
||||||
let rot_y2 = Mat3::from_axis_angle(Vec3::unit_y(), deg(180.0));
|
|
||||||
assert_approx_eq!(rot_y1, rot_y2);
|
|
||||||
let rot_z1 = Mat3::from_rotation_z(deg(180.0));
|
|
||||||
let rot_z2 = Mat3::from_axis_angle(Vec3::unit_z(), deg(180.0));
|
|
||||||
assert_approx_eq!(rot_z1, rot_z2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_mul() {
|
|
||||||
let mat_a = Mat3::from_axis_angle(Vec3::unit_z(), deg(90.0));
|
|
||||||
let result3 = mat_a * Vec3::unit_y();
|
|
||||||
assert_approx_eq!(vec3(-1.0, 0.0, 0.0), result3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_transform2d() {
|
|
||||||
let mat_b = Mat3::from_scale_angle_translation(
|
|
||||||
Vec2::new(0.5, 1.5),
|
|
||||||
f32::to_radians(90.0),
|
|
||||||
Vec2::new(1.0, 2.0),
|
|
||||||
);
|
|
||||||
let result2 = mat_b.transform_vector2(Vec2::unit_y());
|
|
||||||
assert_approx_eq!(vec2(-1.5, 0.0), result2, 1.0e-6);
|
|
||||||
assert_approx_eq!(result2, (mat_b * Vec2::unit_y().extend(0.0)).truncate());
|
|
||||||
|
|
||||||
let result2 = mat_b.transform_point2(Vec2::unit_y());
|
|
||||||
assert_approx_eq!(vec2(-0.5, 2.0), result2, 1.0e-6);
|
|
||||||
assert_approx_eq!(result2, (mat_b * Vec2::unit_y().extend(1.0)).truncate());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_ypr() {
|
|
||||||
let zero = deg(0.0);
|
|
||||||
let yaw = deg(30.0);
|
|
||||||
let pitch = deg(60.0);
|
|
||||||
let roll = deg(90.0);
|
|
||||||
let y0 = Mat3::from_rotation_y(yaw);
|
|
||||||
let y1 = Mat3::from_rotation_ypr(yaw, zero, zero);
|
|
||||||
assert_approx_eq!(y0, y1);
|
|
||||||
|
|
||||||
let x0 = Mat3::from_rotation_x(pitch);
|
|
||||||
let x1 = Mat3::from_rotation_ypr(zero, pitch, zero);
|
|
||||||
assert_approx_eq!(x0, x1);
|
|
||||||
|
|
||||||
let z0 = Mat3::from_rotation_z(roll);
|
|
||||||
let z1 = Mat3::from_rotation_ypr(zero, zero, roll);
|
|
||||||
assert_approx_eq!(z0, z1);
|
|
||||||
|
|
||||||
let yx0 = y0 * x0;
|
|
||||||
let yx1 = Mat3::from_rotation_ypr(yaw, pitch, zero);
|
|
||||||
assert_approx_eq!(yx0, yx1);
|
|
||||||
|
|
||||||
let yxz0 = y0 * x0 * z0;
|
|
||||||
let yxz1 = Mat3::from_rotation_ypr(yaw, pitch, roll);
|
|
||||||
assert_approx_eq!(yxz0, yxz1, 1e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_scale() {
|
|
||||||
let m = Mat3::from_scale(Vec3::new(2.0, 4.0, 8.0));
|
|
||||||
assert_approx_eq!(m * Vec3::new(1.0, 1.0, 1.0), Vec3::new(2.0, 4.0, 8.0));
|
|
||||||
assert_approx_eq!(Vec3::unit_x() * 2.0, m.x_axis());
|
|
||||||
assert_approx_eq!(Vec3::unit_y() * 4.0, m.y_axis());
|
|
||||||
assert_approx_eq!(Vec3::unit_z() * 8.0, m.z_axis());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_transpose() {
|
|
||||||
let m = mat3(
|
|
||||||
vec3(1.0, 2.0, 3.0),
|
|
||||||
vec3(4.0, 5.0, 6.0),
|
|
||||||
vec3(7.0, 8.0, 9.0),
|
|
||||||
);
|
|
||||||
let mt = m.transpose();
|
|
||||||
assert_eq!(mt.x_axis(), vec3(1.0, 4.0, 7.0));
|
|
||||||
assert_eq!(mt.y_axis(), vec3(2.0, 5.0, 8.0));
|
|
||||||
assert_eq!(mt.z_axis(), vec3(3.0, 6.0, 9.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_det() {
|
|
||||||
assert_eq!(0.0, Mat3::zero().determinant());
|
|
||||||
assert_eq!(1.0, Mat3::identity().determinant());
|
|
||||||
assert_eq!(1.0, Mat3::from_rotation_x(deg(90.0)).determinant());
|
|
||||||
assert_eq!(1.0, Mat3::from_rotation_y(deg(180.0)).determinant());
|
|
||||||
assert_eq!(1.0, Mat3::from_rotation_z(deg(270.0)).determinant());
|
|
||||||
assert_eq!(
|
|
||||||
2.0 * 2.0 * 2.0,
|
|
||||||
Mat3::from_scale(vec3(2.0, 2.0, 2.0)).determinant()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_inverse() {
|
|
||||||
// assert_eq!(None, Mat3::zero().inverse());
|
|
||||||
let inv = Mat3::identity().inverse();
|
|
||||||
// assert_ne!(None, inv);
|
|
||||||
assert_approx_eq!(Mat3::identity(), inv);
|
|
||||||
|
|
||||||
let rotz = Mat3::from_rotation_z(deg(90.0));
|
|
||||||
let rotz_inv = rotz.inverse();
|
|
||||||
// assert_ne!(None, rotz_inv);
|
|
||||||
// let rotz_inv = rotz_inv.unwrap();
|
|
||||||
assert_approx_eq!(Mat3::identity(), rotz * rotz_inv);
|
|
||||||
assert_approx_eq!(Mat3::identity(), rotz_inv * rotz);
|
|
||||||
|
|
||||||
let scale = Mat3::from_scale(vec3(4.0, 5.0, 6.0));
|
|
||||||
let scale_inv = scale.inverse();
|
|
||||||
// assert_ne!(None, scale_inv);
|
|
||||||
// let scale_inv = scale_inv.unwrap();
|
|
||||||
assert_approx_eq!(Mat3::identity(), scale * scale_inv);
|
|
||||||
assert_approx_eq!(Mat3::identity(), scale_inv * scale);
|
|
||||||
|
|
||||||
let m = scale * rotz;
|
|
||||||
let m_inv = m.inverse();
|
|
||||||
// assert_ne!(None, m_inv);
|
|
||||||
// let m_inv = m_inv.unwrap();
|
|
||||||
assert_approx_eq!(Mat3::identity(), m * m_inv);
|
|
||||||
assert_approx_eq!(Mat3::identity(), m_inv * m);
|
|
||||||
assert_approx_eq!(m_inv, rotz_inv * scale_inv);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_ops() {
|
|
||||||
let m0 = Mat3::from_cols_array_2d(&MATRIX);
|
|
||||||
let m0x2 = Mat3::from_cols_array_2d(&[[2.0, 4.0, 6.0], [8.0, 10.0, 12.0], [14.0, 16.0, 18.0]]);
|
|
||||||
assert_eq!(m0x2, m0 * 2.0);
|
|
||||||
assert_eq!(m0x2, 2.0 * m0);
|
|
||||||
assert_eq!(m0x2, m0 + m0);
|
|
||||||
assert_eq!(Mat3::zero(), m0 - m0);
|
|
||||||
assert_approx_eq!(m0, m0 * Mat3::identity());
|
|
||||||
assert_approx_eq!(m0, Mat3::identity() * m0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_fmt() {
|
|
||||||
let a = Mat3::from_cols_array_2d(&MATRIX);
|
|
||||||
assert_eq!(format!("{}", a), "[[1, 2, 3], [4, 5, 6], [7, 8, 9]]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_serde() {
|
|
||||||
let a = Mat3::from_cols(
|
|
||||||
vec3(1.0, 2.0, 3.0),
|
|
||||||
vec3(4.0, 5.0, 6.0),
|
|
||||||
vec3(7.0, 8.0, 9.0),
|
|
||||||
);
|
|
||||||
let serialized = serde_json::to_string(&a).unwrap();
|
|
||||||
assert_eq!(serialized, "[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0]");
|
|
||||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
|
||||||
assert_eq!(a, deserialized);
|
|
||||||
let deserialized = serde_json::from_str::<Mat3>("[]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat3>("[1.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat3>("[1.0,2.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat3>("[1.0,2.0,3.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat3>("[1.0,2.0,3.0,4.0,5.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat3>("[[1.0,2.0,3.0],[4.0,5.0,6.0],[7.0,8.0,9.0]]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
#[test]
|
|
||||||
fn test_mat3_rand() {
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use rand_xoshiro::Xoshiro256Plus;
|
|
||||||
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let a = Mat3::from_cols_array(&rng1.gen::<[f32; 9]>());
|
|
||||||
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let b = rng2.gen::<Mat3>();
|
|
||||||
assert_eq!(a, b);
|
|
||||||
}
|
|
|
@ -1,483 +0,0 @@
|
||||||
mod support;
|
|
||||||
|
|
||||||
use glam::f32::*;
|
|
||||||
use support::deg;
|
|
||||||
|
|
||||||
const IDENTITY: [[f32; 4]; 4] = [
|
|
||||||
[1.0, 0.0, 0.0, 0.0],
|
|
||||||
[0.0, 1.0, 0.0, 0.0],
|
|
||||||
[0.0, 0.0, 1.0, 0.0],
|
|
||||||
[0.0, 0.0, 0.0, 1.0],
|
|
||||||
];
|
|
||||||
|
|
||||||
const MATRIX: [[f32; 4]; 4] = [
|
|
||||||
[1.0, 2.0, 3.0, 4.0],
|
|
||||||
[5.0, 6.0, 7.0, 8.0],
|
|
||||||
[9.0, 10.0, 11.0, 12.0],
|
|
||||||
[13.0, 14.0, 15.0, 16.0],
|
|
||||||
];
|
|
||||||
|
|
||||||
const ZERO: [[f32; 4]; 4] = [[0.0; 4]; 4];
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_align() {
|
|
||||||
use std::mem;
|
|
||||||
assert_eq!(64, mem::size_of::<Mat4>());
|
|
||||||
if cfg!(feature = "scalar-math") {
|
|
||||||
assert_eq!(4, mem::align_of::<Mat4>());
|
|
||||||
} else {
|
|
||||||
assert_eq!(16, mem::align_of::<Mat4>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_identity() {
|
|
||||||
let identity = Mat4::identity();
|
|
||||||
assert_eq!(IDENTITY, identity.to_cols_array_2d());
|
|
||||||
assert_eq!(Mat4::from_cols_array_2d(&IDENTITY), identity);
|
|
||||||
assert_eq!(identity, identity * identity);
|
|
||||||
assert_eq!(identity, Mat4::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_zero() {
|
|
||||||
assert_eq!(Mat4::from_cols_array_2d(&ZERO), Mat4::zero());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_accessors() {
|
|
||||||
let mut m = Mat4::zero();
|
|
||||||
m.set_x_axis(Vec4::new(1.0, 2.0, 3.0, 4.0));
|
|
||||||
m.set_y_axis(Vec4::new(5.0, 6.0, 7.0, 8.0));
|
|
||||||
m.set_z_axis(Vec4::new(9.0, 10.0, 11.0, 12.0));
|
|
||||||
m.set_w_axis(Vec4::new(13.0, 14.0, 15.0, 16.0));
|
|
||||||
assert_eq!(Mat4::from_cols_array_2d(&MATRIX), m);
|
|
||||||
assert_eq!(Vec4::new(1.0, 2.0, 3.0, 4.0), m.x_axis());
|
|
||||||
assert_eq!(Vec4::new(5.0, 6.0, 7.0, 8.0), m.y_axis());
|
|
||||||
assert_eq!(Vec4::new(9.0, 10.0, 11.0, 12.0), m.z_axis());
|
|
||||||
assert_eq!(Vec4::new(13.0, 14.0, 15.0, 16.0), m.w_axis());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_from_axes() {
|
|
||||||
let a = Mat4::from_cols_array_2d(&[
|
|
||||||
[1.0, 2.0, 3.0, 4.0],
|
|
||||||
[5.0, 6.0, 7.0, 8.0],
|
|
||||||
[9.0, 10.0, 11.0, 12.0],
|
|
||||||
[13.0, 14.0, 15.0, 16.0],
|
|
||||||
]);
|
|
||||||
assert_eq!(MATRIX, a.to_cols_array_2d());
|
|
||||||
let b = Mat4::from_cols(
|
|
||||||
vec4(1.0, 2.0, 3.0, 4.0),
|
|
||||||
vec4(5.0, 6.0, 7.0, 8.0),
|
|
||||||
vec4(9.0, 10.0, 11.0, 12.0),
|
|
||||||
vec4(13.0, 14.0, 15.0, 16.0),
|
|
||||||
);
|
|
||||||
assert_eq!(a, b);
|
|
||||||
let c = mat4(
|
|
||||||
vec4(1.0, 2.0, 3.0, 4.0),
|
|
||||||
vec4(5.0, 6.0, 7.0, 8.0),
|
|
||||||
vec4(9.0, 10.0, 11.0, 12.0),
|
|
||||||
vec4(13.0, 14.0, 15.0, 16.0),
|
|
||||||
);
|
|
||||||
assert_eq!(a, c);
|
|
||||||
let d = b.to_cols_array();
|
|
||||||
let f = Mat4::from_cols_array(&d);
|
|
||||||
assert_eq!(b, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_translation() {
|
|
||||||
let translate = Mat4::from_translation(vec3(1.0, 2.0, 3.0));
|
|
||||||
assert_eq!(
|
|
||||||
Mat4::from_cols(
|
|
||||||
vec4(1.0, 0.0, 0.0, 0.0),
|
|
||||||
vec4(0.0, 1.0, 0.0, 0.0),
|
|
||||||
vec4(0.0, 0.0, 1.0, 0.0),
|
|
||||||
vec4(1.0, 2.0, 3.0, 1.0)
|
|
||||||
),
|
|
||||||
translate
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_rotation() {
|
|
||||||
let rot_x1 = Mat4::from_rotation_x(deg(180.0));
|
|
||||||
let rot_x2 = Mat4::from_axis_angle(Vec3::unit_x(), deg(180.0));
|
|
||||||
assert_approx_eq!(rot_x1, rot_x2);
|
|
||||||
let rot_y1 = Mat4::from_rotation_y(deg(180.0));
|
|
||||||
let rot_y2 = Mat4::from_axis_angle(Vec3::unit_y(), deg(180.0));
|
|
||||||
assert_approx_eq!(rot_y1, rot_y2);
|
|
||||||
let rot_z1 = Mat4::from_rotation_z(deg(180.0));
|
|
||||||
let rot_z2 = Mat4::from_axis_angle(Vec3::unit_z(), deg(180.0));
|
|
||||||
assert_approx_eq!(rot_z1, rot_z2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_mul() {
|
|
||||||
let mat_a = Mat4::from_axis_angle(Vec3::unit_z(), deg(90.0));
|
|
||||||
let result3 = mat_a.transform_vector3(Vec3::unit_y());
|
|
||||||
assert_approx_eq!(vec3(-1.0, 0.0, 0.0), result3);
|
|
||||||
assert_approx_eq!(result3, (mat_a * Vec3::unit_y().extend(0.0)).truncate());
|
|
||||||
let result4 = mat_a * Vec4::unit_y();
|
|
||||||
assert_approx_eq!(vec4(-1.0, 0.0, 0.0, 0.0), result4);
|
|
||||||
assert_approx_eq!(result4, mat_a * Vec4::unit_y());
|
|
||||||
|
|
||||||
let mat_b = Mat4::from_scale_rotation_translation(
|
|
||||||
Vec3::new(0.5, 1.5, 2.0),
|
|
||||||
Quat::from_rotation_x(deg(90.0)),
|
|
||||||
Vec3::new(1.0, 2.0, 3.0),
|
|
||||||
);
|
|
||||||
let result3 = mat_b.transform_vector3(Vec3::unit_y());
|
|
||||||
assert_approx_eq!(vec3(0.0, 0.0, 1.5), result3, 1.0e-6);
|
|
||||||
assert_approx_eq!(result3, (mat_b * Vec3::unit_y().extend(0.0)).truncate());
|
|
||||||
|
|
||||||
let result3 = mat_b.transform_point3(Vec3::unit_y());
|
|
||||||
assert_approx_eq!(vec3(1.0, 2.0, 4.5), result3, 1.0e-6);
|
|
||||||
assert_approx_eq!(result3, (mat_b * Vec3::unit_y().extend(1.0)).truncate());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_ypr() {
|
|
||||||
let zero = deg(0.0);
|
|
||||||
let yaw = deg(30.0);
|
|
||||||
let pitch = deg(60.0);
|
|
||||||
let roll = deg(90.0);
|
|
||||||
let y0 = Mat4::from_rotation_y(yaw);
|
|
||||||
let y1 = Mat4::from_rotation_ypr(yaw, zero, zero);
|
|
||||||
assert_approx_eq!(y0, y1);
|
|
||||||
|
|
||||||
let x0 = Mat4::from_rotation_x(pitch);
|
|
||||||
let x1 = Mat4::from_rotation_ypr(zero, pitch, zero);
|
|
||||||
assert_approx_eq!(x0, x1);
|
|
||||||
|
|
||||||
let z0 = Mat4::from_rotation_z(roll);
|
|
||||||
let z1 = Mat4::from_rotation_ypr(zero, zero, roll);
|
|
||||||
assert_approx_eq!(z0, z1);
|
|
||||||
|
|
||||||
let yx0 = y0 * x0;
|
|
||||||
let yx1 = Mat4::from_rotation_ypr(yaw, pitch, zero);
|
|
||||||
assert_approx_eq!(yx0, yx1);
|
|
||||||
|
|
||||||
let yxz0 = y0 * x0 * z0;
|
|
||||||
let yxz1 = Mat4::from_rotation_ypr(yaw, pitch, roll);
|
|
||||||
assert_approx_eq!(yxz0, yxz1, 1e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_scale() {
|
|
||||||
let m = Mat4::from_scale(Vec3::new(2.0, 4.0, 8.0));
|
|
||||||
assert_approx_eq!(
|
|
||||||
m.transform_point3(Vec3::new(1.0, 1.0, 1.0)),
|
|
||||||
Vec3::new(2.0, 4.0, 8.0)
|
|
||||||
);
|
|
||||||
assert_approx_eq!(Vec4::unit_x() * 2.0, m.x_axis());
|
|
||||||
assert_approx_eq!(Vec4::unit_y() * 4.0, m.y_axis());
|
|
||||||
assert_approx_eq!(Vec4::unit_z() * 8.0, m.z_axis());
|
|
||||||
assert_approx_eq!(Vec4::unit_w(), m.w_axis());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_transpose() {
|
|
||||||
let m = mat4(
|
|
||||||
vec4(1.0, 2.0, 3.0, 4.0),
|
|
||||||
vec4(5.0, 6.0, 7.0, 8.0),
|
|
||||||
vec4(9.0, 10.0, 11.0, 12.0),
|
|
||||||
vec4(13.0, 14.0, 15.0, 16.0),
|
|
||||||
);
|
|
||||||
let mt = m.transpose();
|
|
||||||
assert_eq!(mt.x_axis(), vec4(1.0, 5.0, 9.0, 13.0));
|
|
||||||
assert_eq!(mt.y_axis(), vec4(2.0, 6.0, 10.0, 14.0));
|
|
||||||
assert_eq!(mt.z_axis(), vec4(3.0, 7.0, 11.0, 15.0));
|
|
||||||
assert_eq!(mt.w_axis(), vec4(4.0, 8.0, 12.0, 16.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_det() {
|
|
||||||
assert_eq!(0.0, Mat4::zero().determinant());
|
|
||||||
assert_eq!(1.0, Mat4::identity().determinant());
|
|
||||||
assert_eq!(1.0, Mat4::from_rotation_x(deg(90.0)).determinant());
|
|
||||||
assert_eq!(1.0, Mat4::from_rotation_y(deg(180.0)).determinant());
|
|
||||||
assert_eq!(1.0, Mat4::from_rotation_z(deg(270.0)).determinant());
|
|
||||||
assert_eq!(
|
|
||||||
2.0 * 2.0 * 2.0,
|
|
||||||
Mat4::from_scale(vec3(2.0, 2.0, 2.0)).determinant()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_inverse() {
|
|
||||||
// assert_eq!(None, Mat4::zero().inverse());
|
|
||||||
let inv = Mat4::identity().inverse();
|
|
||||||
// assert_ne!(None, inv);
|
|
||||||
assert_approx_eq!(Mat4::identity(), inv);
|
|
||||||
|
|
||||||
let rotz = Mat4::from_rotation_z(deg(90.0));
|
|
||||||
let rotz_inv = rotz.inverse();
|
|
||||||
// assert_ne!(None, rotz_inv);
|
|
||||||
// let rotz_inv = rotz_inv.unwrap();
|
|
||||||
assert_approx_eq!(Mat4::identity(), rotz * rotz_inv);
|
|
||||||
assert_approx_eq!(Mat4::identity(), rotz_inv * rotz);
|
|
||||||
|
|
||||||
let trans = Mat4::from_translation(vec3(1.0, 2.0, 3.0));
|
|
||||||
let trans_inv = trans.inverse();
|
|
||||||
// assert_ne!(None, trans_inv);
|
|
||||||
// let trans_inv = trans_inv.unwrap();
|
|
||||||
assert_approx_eq!(Mat4::identity(), trans * trans_inv);
|
|
||||||
assert_approx_eq!(Mat4::identity(), trans_inv * trans);
|
|
||||||
|
|
||||||
let scale = Mat4::from_scale(vec3(4.0, 5.0, 6.0));
|
|
||||||
let scale_inv = scale.inverse();
|
|
||||||
// assert_ne!(None, scale_inv);
|
|
||||||
// let scale_inv = scale_inv.unwrap();
|
|
||||||
assert_approx_eq!(Mat4::identity(), scale * scale_inv);
|
|
||||||
assert_approx_eq!(Mat4::identity(), scale_inv * scale);
|
|
||||||
|
|
||||||
let m = scale * rotz * trans;
|
|
||||||
let m_inv = m.inverse();
|
|
||||||
// assert_ne!(None, m_inv);
|
|
||||||
// let m_inv = m_inv.unwrap();
|
|
||||||
assert_approx_eq!(Mat4::identity(), m * m_inv, 1.0e-5);
|
|
||||||
assert_approx_eq!(Mat4::identity(), m_inv * m, 1.0e-5);
|
|
||||||
assert_approx_eq!(m_inv, trans_inv * rotz_inv * scale_inv, 1.0e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_decompose() {
|
|
||||||
// identity
|
|
||||||
let (out_scale, out_rotation, out_translation) =
|
|
||||||
Mat4::identity().to_scale_rotation_translation();
|
|
||||||
assert_approx_eq!(Vec3::one(), out_scale);
|
|
||||||
assert!(out_rotation.is_near_identity());
|
|
||||||
assert_approx_eq!(Vec3::zero(), out_translation);
|
|
||||||
|
|
||||||
// no scale
|
|
||||||
let in_scale = Vec3::one();
|
|
||||||
let in_translation = Vec3::new(-2.0, 4.0, -0.125);
|
|
||||||
let in_rotation = Quat::from_rotation_ypr(
|
|
||||||
f32::to_radians(-45.0),
|
|
||||||
f32::to_radians(180.0),
|
|
||||||
f32::to_radians(270.0),
|
|
||||||
);
|
|
||||||
let in_mat = Mat4::from_scale_rotation_translation(in_scale, in_rotation, in_translation);
|
|
||||||
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_rotation_translation();
|
|
||||||
assert_approx_eq!(in_scale, out_scale, 1e-6);
|
|
||||||
// out_rotation is different but produces the same matrix
|
|
||||||
// assert_approx_eq!(in_rotation, out_rotation);
|
|
||||||
assert_approx_eq!(in_translation, out_translation);
|
|
||||||
assert_approx_eq!(
|
|
||||||
in_mat,
|
|
||||||
Mat4::from_scale_rotation_translation(out_scale, out_rotation, out_translation),
|
|
||||||
1e-6
|
|
||||||
);
|
|
||||||
|
|
||||||
// positive scale
|
|
||||||
let in_scale = Vec3::new(1.0, 2.0, 4.0);
|
|
||||||
let in_mat = Mat4::from_scale_rotation_translation(in_scale, in_rotation, in_translation);
|
|
||||||
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_rotation_translation();
|
|
||||||
assert_approx_eq!(in_scale, out_scale, 1e-6);
|
|
||||||
// out_rotation is different but produces the same matrix
|
|
||||||
// assert_approx_eq!(in_rotation, out_rotation);
|
|
||||||
assert_approx_eq!(in_translation, out_translation);
|
|
||||||
assert_approx_eq!(
|
|
||||||
in_mat,
|
|
||||||
Mat4::from_scale_rotation_translation(out_scale, out_rotation, out_translation),
|
|
||||||
1e-6
|
|
||||||
);
|
|
||||||
|
|
||||||
// negative scale
|
|
||||||
let in_scale = Vec3::new(-4.0, 1.0, 2.0);
|
|
||||||
let in_mat = Mat4::from_scale_rotation_translation(in_scale, in_rotation, in_translation);
|
|
||||||
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_rotation_translation();
|
|
||||||
assert_approx_eq!(in_scale, out_scale, 1e-6);
|
|
||||||
// out_rotation is different but produces the same matrix
|
|
||||||
// assert_approx_eq!(in_rotation, out_rotation);
|
|
||||||
assert_approx_eq!(in_translation, out_translation);
|
|
||||||
assert_approx_eq!(
|
|
||||||
in_mat,
|
|
||||||
Mat4::from_scale_rotation_translation(out_scale, out_rotation, out_translation),
|
|
||||||
1e-5
|
|
||||||
);
|
|
||||||
|
|
||||||
// negative scale
|
|
||||||
let in_scale = Vec3::new(4.0, -1.0, -2.0);
|
|
||||||
let in_mat = Mat4::from_scale_rotation_translation(in_scale, in_rotation, in_translation);
|
|
||||||
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_rotation_translation();
|
|
||||||
// out_scale and out_rotation are different but they produce the same matrix
|
|
||||||
// assert_approx_eq!(in_scale, out_scale, 1e-6);
|
|
||||||
// assert_approx_eq!(in_rotation, out_rotation);
|
|
||||||
assert_approx_eq!(in_translation, out_translation);
|
|
||||||
assert_approx_eq!(
|
|
||||||
in_mat,
|
|
||||||
Mat4::from_scale_rotation_translation(out_scale, out_rotation, out_translation),
|
|
||||||
1e-6
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_look_at() {
|
|
||||||
let eye = Vec3::new(0.0, 0.0, -5.0);
|
|
||||||
let center = Vec3::new(0.0, 0.0, 0.0);
|
|
||||||
let up = Vec3::new(1.0, 0.0, 0.0);
|
|
||||||
let lh = Mat4::look_at_lh(eye, center, up);
|
|
||||||
let rh = Mat4::look_at_rh(eye, center, up);
|
|
||||||
let point = Vec3::new(1.0, 0.0, 0.0);
|
|
||||||
assert_approx_eq!(lh.transform_point3(point), Vec3::new(0.0, 1.0, 5.0));
|
|
||||||
assert_approx_eq!(rh.transform_point3(point), Vec3::new(0.0, 1.0, -5.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_perspective_gl_rh() {
|
|
||||||
let projection = Mat4::perspective_rh_gl(f32::to_radians(90.0), 2.0, 5.0, 15.0);
|
|
||||||
|
|
||||||
let original = Vec3::new(5.0, 5.0, -15.0);
|
|
||||||
let projected = projection * original.extend(1.0);
|
|
||||||
assert_approx_eq!(Vec4::new(2.5, 5.0, 15.0, 15.0), projected);
|
|
||||||
|
|
||||||
let original = Vec3::new(5.0, 5.0, -5.0);
|
|
||||||
let projected = projection * original.extend(1.0);
|
|
||||||
assert_approx_eq!(Vec4::new(2.5, 5.0, -5.0, 5.0), projected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_perspective_lh() {
|
|
||||||
let projection = Mat4::perspective_lh(f32::to_radians(90.0), 2.0, 5.0, 15.0);
|
|
||||||
|
|
||||||
let original = Vec3::new(5.0, 5.0, 15.0);
|
|
||||||
let projected = projection * original.extend(1.0);
|
|
||||||
assert_approx_eq!(Vec4::new(2.5, 5.0, 15.0, 15.0), projected);
|
|
||||||
|
|
||||||
let original = Vec3::new(5.0, 5.0, 5.0);
|
|
||||||
let projected = projection * original.extend(1.0);
|
|
||||||
assert_approx_eq!(Vec4::new(2.5, 5.0, 0.0, 5.0), projected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_perspective_infinite_lh() {
|
|
||||||
let projection = Mat4::perspective_infinite_lh(f32::to_radians(90.0), 2.0, 5.0);
|
|
||||||
|
|
||||||
let original = Vec3::new(5.0, 5.0, 15.0);
|
|
||||||
let projected = projection * original.extend(1.0);
|
|
||||||
assert_approx_eq!(Vec4::new(2.5, 5.0, 10.0, 15.0), projected);
|
|
||||||
|
|
||||||
let original = Vec3::new(5.0, 5.0, 5.0);
|
|
||||||
let projected = projection * original.extend(1.0);
|
|
||||||
assert_approx_eq!(Vec4::new(2.5, 5.0, 0.0, 5.0), projected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_perspective_infinite_reverse_lh() {
|
|
||||||
let projection = Mat4::perspective_infinite_reverse_lh(f32::to_radians(90.0), 2.0, 5.0);
|
|
||||||
|
|
||||||
let original = Vec3::new(5.0, 5.0, 15.0);
|
|
||||||
let projected = projection * original.extend(1.0);
|
|
||||||
assert_approx_eq!(Vec4::new(2.5, 5.0, 5.0, 15.0), projected);
|
|
||||||
|
|
||||||
let original = Vec3::new(5.0, 5.0, 5.0);
|
|
||||||
let projected = projection * original.extend(1.0);
|
|
||||||
assert_approx_eq!(Vec4::new(2.5, 5.0, 5.0, 5.0), projected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_orthographic_gl_rh() {
|
|
||||||
let projection = Mat4::orthographic_rh_gl(-10.0, 10.0, -5.0, 5.0, 0.0, -10.0);
|
|
||||||
let original = Vec4::new(5.0, 5.0, -5.0, 1.0);
|
|
||||||
let projected = projection.mul_vec4(original);
|
|
||||||
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, -2.0, 1.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_orthographic_rh() {
|
|
||||||
let projection = Mat4::orthographic_rh(-10.0, 10.0, -5.0, 5.0, -10.0, 10.0);
|
|
||||||
let original = Vec4::new(5.0, 5.0, -5.0, 1.0);
|
|
||||||
let projected = projection.mul_vec4(original);
|
|
||||||
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, 0.75, 1.0));
|
|
||||||
|
|
||||||
let original = Vec4::new(5.0, 5.0, 5.0, 1.0);
|
|
||||||
let projected = projection.mul_vec4(original);
|
|
||||||
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, 0.25, 1.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_orthographic_lh() {
|
|
||||||
let projection = Mat4::orthographic_lh(-10.0, 10.0, -5.0, 5.0, -10.0, 10.0);
|
|
||||||
let original = Vec4::new(5.0, 5.0, -5.0, 1.0);
|
|
||||||
let projected = projection.mul_vec4(original);
|
|
||||||
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, 0.25, 1.0));
|
|
||||||
|
|
||||||
let original = Vec4::new(5.0, 5.0, 5.0, 1.0);
|
|
||||||
let projected = projection.mul_vec4(original);
|
|
||||||
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, 0.75, 1.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_ops() {
|
|
||||||
let m0 = Mat4::from_cols_array_2d(&MATRIX);
|
|
||||||
let m0x2 = Mat4::from_cols_array_2d(&[
|
|
||||||
[2.0, 4.0, 6.0, 8.0],
|
|
||||||
[10.0, 12.0, 14.0, 16.0],
|
|
||||||
[18.0, 20.0, 22.0, 24.0],
|
|
||||||
[26.0, 28.0, 30.0, 32.0],
|
|
||||||
]);
|
|
||||||
assert_eq!(m0x2, m0 * 2.0);
|
|
||||||
assert_eq!(m0x2, 2.0 * m0);
|
|
||||||
assert_eq!(m0x2, m0 + m0);
|
|
||||||
assert_eq!(Mat4::zero(), m0 - m0);
|
|
||||||
assert_approx_eq!(m0, m0 * Mat4::identity());
|
|
||||||
assert_approx_eq!(m0, Mat4::identity() * m0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_fmt() {
|
|
||||||
let a = Mat4::from_cols_array_2d(&MATRIX);
|
|
||||||
assert_eq!(
|
|
||||||
format!("{}", a),
|
|
||||||
"[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_serde() {
|
|
||||||
let a = Mat4::from_cols(
|
|
||||||
vec4(1.0, 2.0, 3.0, 4.0),
|
|
||||||
vec4(5.0, 6.0, 7.0, 8.0),
|
|
||||||
vec4(9.0, 10.0, 11.0, 12.0),
|
|
||||||
vec4(13.0, 14.0, 15.0, 16.0),
|
|
||||||
);
|
|
||||||
let serialized = serde_json::to_string(&a).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
serialized,
|
|
||||||
"[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0]"
|
|
||||||
);
|
|
||||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
|
||||||
assert_eq!(a, deserialized);
|
|
||||||
let deserialized = serde_json::from_str::<Mat4>("[]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat4>("[1.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat4>("[1.0,2.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat4>("[1.0,2.0,3.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat4>("[1.0,2.0,3.0,4.0,5.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat4>("[[1.0,2.0,3.0],[4.0,5.0,6.0],[7.0,8.0,9.0]]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Mat4>(
|
|
||||||
"[[1.0,2.0,3.0,4.0],[5.0,6.0,7.0,8.0],[9.0,10.0,11.0,12.0][13.0,14.0,15.0,16.0]]",
|
|
||||||
);
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
#[test]
|
|
||||||
fn test_mat4_rand() {
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use rand_xoshiro::Xoshiro256Plus;
|
|
||||||
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let a = Mat4::from_cols_array(&rng1.gen::<[f32; 16]>());
|
|
||||||
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let b = rng2.gen::<Mat4>();
|
|
||||||
assert_eq!(a, b);
|
|
||||||
}
|
|
|
@ -1,284 +0,0 @@
|
||||||
mod support;
|
|
||||||
|
|
||||||
use glam::f32::{quat, Mat3, Mat4, Quat, Vec3, Vec4};
|
|
||||||
use support::{deg, rad};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_align() {
|
|
||||||
use std::mem;
|
|
||||||
assert_eq!(16, mem::size_of::<Quat>());
|
|
||||||
if cfg!(feature = "scalar-math") {
|
|
||||||
assert_eq!(4, mem::align_of::<Quat>());
|
|
||||||
} else {
|
|
||||||
assert_eq!(16, mem::align_of::<Quat>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_rotation() {
|
|
||||||
let zero = deg(0.0);
|
|
||||||
let yaw = deg(30.0);
|
|
||||||
let pitch = deg(60.0);
|
|
||||||
let roll = deg(90.0);
|
|
||||||
let y0 = Quat::from_rotation_y(yaw);
|
|
||||||
assert!(y0.is_normalized());
|
|
||||||
let (axis, angle) = y0.to_axis_angle();
|
|
||||||
assert_approx_eq!(axis, Vec3::unit_y(), 1.0e-6);
|
|
||||||
assert_approx_eq!(angle, yaw);
|
|
||||||
let y1 = Quat::from_rotation_ypr(yaw, zero, zero);
|
|
||||||
assert_approx_eq!(y0, y1);
|
|
||||||
let y2 = Quat::from_axis_angle(Vec3::unit_y(), yaw);
|
|
||||||
assert_approx_eq!(y0, y2);
|
|
||||||
let y3 = Quat::from_rotation_mat3(&Mat3::from_rotation_y(yaw));
|
|
||||||
assert_approx_eq!(y0, y3);
|
|
||||||
let y4 = Quat::from_rotation_mat3(&Mat3::from_quat(y0));
|
|
||||||
assert_approx_eq!(y0, y4);
|
|
||||||
|
|
||||||
let x0 = Quat::from_rotation_x(pitch);
|
|
||||||
assert!(x0.is_normalized());
|
|
||||||
let (axis, angle) = x0.to_axis_angle();
|
|
||||||
assert_approx_eq!(axis, Vec3::unit_x());
|
|
||||||
assert_approx_eq!(angle, pitch);
|
|
||||||
let x1 = Quat::from_rotation_ypr(zero, pitch, zero);
|
|
||||||
assert_approx_eq!(x0, x1);
|
|
||||||
let x2 = Quat::from_axis_angle(Vec3::unit_x(), pitch);
|
|
||||||
assert_approx_eq!(x0, x2);
|
|
||||||
let x3 = Quat::from_rotation_mat4(&Mat4::from_rotation_x(deg(180.0)));
|
|
||||||
assert!(x3.is_normalized());
|
|
||||||
assert_approx_eq!(Quat::from_rotation_x(deg(180.0)), x3);
|
|
||||||
|
|
||||||
let z0 = Quat::from_rotation_z(roll);
|
|
||||||
assert!(z0.is_normalized());
|
|
||||||
let (axis, angle) = z0.to_axis_angle();
|
|
||||||
assert_approx_eq!(axis, Vec3::unit_z());
|
|
||||||
assert_approx_eq!(angle, roll);
|
|
||||||
let z1 = Quat::from_rotation_ypr(zero, zero, roll);
|
|
||||||
assert_approx_eq!(z0, z1);
|
|
||||||
let z2 = Quat::from_axis_angle(Vec3::unit_z(), roll);
|
|
||||||
assert_approx_eq!(z0, z2);
|
|
||||||
let z3 = Quat::from_rotation_mat4(&Mat4::from_rotation_z(roll));
|
|
||||||
assert_approx_eq!(z0, z3);
|
|
||||||
|
|
||||||
let yx0 = y0 * x0;
|
|
||||||
assert!(yx0.is_normalized());
|
|
||||||
let yx1 = Quat::from_rotation_ypr(yaw, pitch, zero);
|
|
||||||
assert_approx_eq!(yx0, yx1);
|
|
||||||
|
|
||||||
let yxz0 = y0 * x0 * z0;
|
|
||||||
assert!(yxz0.is_normalized());
|
|
||||||
let yxz1 = Quat::from_rotation_ypr(yaw, pitch, roll);
|
|
||||||
assert_approx_eq!(yxz0, yxz1);
|
|
||||||
|
|
||||||
// use the conjugate of z0 to remove the rotation from yxz0
|
|
||||||
let yx2 = yxz0 * z0.conjugate();
|
|
||||||
assert_approx_eq!(yx0, yx2);
|
|
||||||
|
|
||||||
let yxz2 = Quat::from_rotation_mat4(&Mat4::from_quat(yxz0));
|
|
||||||
assert_approx_eq!(yxz0, yxz2);
|
|
||||||
|
|
||||||
// if near identity, just returns x axis and 0 rotation
|
|
||||||
let (axis, angle) = Quat::identity().to_axis_angle();
|
|
||||||
assert_eq!(axis, Vec3::unit_x());
|
|
||||||
assert_eq!(angle, rad(0.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_new() {
|
|
||||||
let ytheta = deg(45.0);
|
|
||||||
let q0 = Quat::from_rotation_y(ytheta);
|
|
||||||
|
|
||||||
let t1 = (0.0, (ytheta * 0.5).sin(), 0.0, (ytheta * 0.5).cos());
|
|
||||||
assert_eq!(q0, t1.into());
|
|
||||||
let q1 = Quat::from(t1);
|
|
||||||
assert_eq!(t1, q1.into());
|
|
||||||
|
|
||||||
assert_eq!(q0, quat(t1.0, t1.1, t1.2, t1.3));
|
|
||||||
|
|
||||||
let a1 = [0.0, (ytheta * 0.5).sin(), 0.0, (ytheta * 0.5).cos()];
|
|
||||||
assert_eq!(q0, a1.into());
|
|
||||||
let q1 = Quat::from(a1);
|
|
||||||
let a2: [f32; 4] = q1.into();
|
|
||||||
assert_eq!(a1, a2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_mul_vec() {
|
|
||||||
let qrz = Quat::from_rotation_z(deg(90.0));
|
|
||||||
assert_approx_eq!(Vec3::unit_y(), qrz * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(Vec3::unit_y(), -qrz * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(-Vec3::unit_x(), qrz * Vec3::unit_y());
|
|
||||||
assert_approx_eq!(-Vec3::unit_x(), -qrz * Vec3::unit_y());
|
|
||||||
|
|
||||||
// check vec3 * mat3 is the same
|
|
||||||
let mrz = Mat3::from_quat(qrz);
|
|
||||||
assert_approx_eq!(Vec3::unit_y(), mrz * Vec3::unit_x());
|
|
||||||
// assert_approx_eq!(Vec3::unit_y(), -mrz * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(-Vec3::unit_x(), mrz * Vec3::unit_y());
|
|
||||||
|
|
||||||
let qrx = Quat::from_rotation_x(deg(90.0));
|
|
||||||
assert_approx_eq!(Vec3::unit_x(), qrx * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(Vec3::unit_x(), -qrx * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(Vec3::unit_z(), qrx * Vec3::unit_y());
|
|
||||||
assert_approx_eq!(Vec3::unit_z(), -qrx * Vec3::unit_y());
|
|
||||||
|
|
||||||
// check vec3 * mat3 is the same
|
|
||||||
let mrx = Mat3::from_quat(qrx);
|
|
||||||
assert_approx_eq!(Vec3::unit_x(), mrx * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(Vec3::unit_z(), mrx * Vec3::unit_y());
|
|
||||||
|
|
||||||
let qrxz = qrz * qrx;
|
|
||||||
assert_approx_eq!(Vec3::unit_y(), qrxz * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(Vec3::unit_z(), qrxz * Vec3::unit_y());
|
|
||||||
|
|
||||||
let mrxz = mrz * mrx;
|
|
||||||
assert_approx_eq!(Vec3::unit_y(), mrxz * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(Vec3::unit_z(), mrxz * Vec3::unit_y());
|
|
||||||
|
|
||||||
let qrzx = qrx * qrz;
|
|
||||||
assert_approx_eq!(Vec3::unit_z(), qrzx * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(-Vec3::unit_x(), qrzx * Vec3::unit_y());
|
|
||||||
|
|
||||||
let mrzx = qrx * qrz;
|
|
||||||
assert_approx_eq!(Vec3::unit_z(), mrzx * Vec3::unit_x());
|
|
||||||
assert_approx_eq!(-Vec3::unit_x(), mrzx * Vec3::unit_y());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_funcs() {
|
|
||||||
let q0 = Quat::from_rotation_ypr(deg(45.0), deg(180.0), deg(90.0));
|
|
||||||
assert!(q0.is_normalized());
|
|
||||||
assert_approx_eq!(q0.length_squared(), 1.0);
|
|
||||||
assert_approx_eq!(q0.length(), 1.0);
|
|
||||||
assert_approx_eq!(q0.length_reciprocal(), 1.0);
|
|
||||||
assert_approx_eq!(q0, q0.normalize());
|
|
||||||
|
|
||||||
assert_approx_eq!(q0.dot(q0), 1.0);
|
|
||||||
assert_approx_eq!(q0.dot(q0), 1.0);
|
|
||||||
|
|
||||||
let q1 = Quat::from(Vec4::from(q0) * 2.0);
|
|
||||||
assert!(!q1.is_normalized());
|
|
||||||
assert_approx_eq!(q1.length_squared(), 4.0, 1.0e-6);
|
|
||||||
assert_approx_eq!(q1.length(), 2.0);
|
|
||||||
assert_approx_eq!(q1.length_reciprocal(), 0.5);
|
|
||||||
assert_approx_eq!(q0, q1.normalize());
|
|
||||||
assert_approx_eq!(q0.dot(q1), 2.0, 1.0e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_lerp() {
|
|
||||||
let q0 = Quat::from_rotation_y(deg(0.0));
|
|
||||||
let q1 = Quat::from_rotation_y(deg(90.0));
|
|
||||||
assert_approx_eq!(q0, q0.lerp(q1, 0.0));
|
|
||||||
assert_approx_eq!(q1, q0.lerp(q1, 1.0));
|
|
||||||
assert_approx_eq!(Quat::from_rotation_y(deg(45.0)), q0.lerp(q1, 0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_slerp() {
|
|
||||||
let q0 = Quat::from_rotation_y(deg(0.0));
|
|
||||||
let q1 = Quat::from_rotation_y(deg(90.0));
|
|
||||||
assert_approx_eq!(q0, q0.slerp(q1, 0.0), 1.0e-3);
|
|
||||||
assert_approx_eq!(q1, q0.slerp(q1, 1.0), 1.0e-3);
|
|
||||||
assert_approx_eq!(Quat::from_rotation_y(deg(45.0)), q0.slerp(q1, 0.5), 1.0e-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_slerp_constant_speed() {
|
|
||||||
let step = 0.01;
|
|
||||||
let mut s = 0.0;
|
|
||||||
while s <= 1.0 {
|
|
||||||
let q0 = Quat::from_rotation_y(deg(0.0));
|
|
||||||
let q1 = Quat::from_rotation_y(deg(90.0));
|
|
||||||
assert_approx_eq!(
|
|
||||||
Quat::from_rotation_y(deg(s * 90.0)),
|
|
||||||
q0.slerp(q1, s),
|
|
||||||
1.0e-3
|
|
||||||
);
|
|
||||||
s += step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_fmt() {
|
|
||||||
let a = Quat::identity();
|
|
||||||
#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
|
|
||||||
assert_eq!(format!("{:?}", a), "Quat(__m128(0.0, 0.0, 0.0, 1.0))");
|
|
||||||
#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
|
|
||||||
assert_eq!(format!("{:?}", a), "Quat(0.0, 0.0, 0.0, 1.0)");
|
|
||||||
// assert_eq!(
|
|
||||||
// format!("{:#?}", a),
|
|
||||||
// "Quat(\n 1.0,\n 2.0,\n 3.0,\n 4.0\n)"
|
|
||||||
// );
|
|
||||||
assert_eq!(format!("{}", a), "[0, 0, 0, 1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_identity() {
|
|
||||||
let identity = Quat::identity();
|
|
||||||
assert!(identity.is_near_identity());
|
|
||||||
assert!(identity.is_normalized());
|
|
||||||
assert_eq!(identity, Quat::from_xyzw(0.0, 0.0, 0.0, 1.0));
|
|
||||||
assert_eq!(identity, identity * identity);
|
|
||||||
let q = Quat::from_rotation_ypr(deg(10.0), deg(-10.0), deg(45.0));
|
|
||||||
assert_eq!(q, q * identity);
|
|
||||||
assert_eq!(q, identity * q);
|
|
||||||
assert_eq!(identity, Quat::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_slice() {
|
|
||||||
let a: [f32; 4] = Quat::from_rotation_ypr(deg(30.0), deg(60.0), deg(90.0)).into();
|
|
||||||
let b = Quat::from_slice_unaligned(&a);
|
|
||||||
let c: [f32; 4] = b.into();
|
|
||||||
assert_eq!(a, c);
|
|
||||||
let mut d = [0.0, 0.0, 0.0, 0.0];
|
|
||||||
b.write_to_slice_unaligned(&mut d[..]);
|
|
||||||
assert_eq!(a, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_quat_elements() {
|
|
||||||
let x = 1.0;
|
|
||||||
let y = 2.0;
|
|
||||||
let z = 3.0;
|
|
||||||
let w = 4.0;
|
|
||||||
|
|
||||||
let a = Quat::from_xyzw(x, y, z, w);
|
|
||||||
assert!(a.x() == x);
|
|
||||||
assert!(a.y() == y);
|
|
||||||
assert!(a.z() == z);
|
|
||||||
assert!(a.w() == w);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[test]
|
|
||||||
fn test_quat_serde() {
|
|
||||||
let a = Quat::from_xyzw(1.0, 2.0, 3.0, 4.0);
|
|
||||||
let serialized = serde_json::to_string(&a).unwrap();
|
|
||||||
assert_eq!(serialized, "[1.0,2.0,3.0,4.0]");
|
|
||||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
|
||||||
assert_eq!(a, deserialized);
|
|
||||||
let deserialized = serde_json::from_str::<Quat>("[]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Quat>("[1.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Quat>("[1.0,2.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Quat>("[1.0,2.0,3.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Quat>("[1.0,2.0,3.0,4.0,5.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
#[test]
|
|
||||||
fn test_quat_rand() {
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use rand_xoshiro::Xoshiro256Plus;
|
|
||||||
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let a: Quat = rng1.gen();
|
|
||||||
assert!(a.is_normalized());
|
|
||||||
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let b: Quat = rng2.gen();
|
|
||||||
assert_eq!(a, b);
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
#[macro_export]
|
|
||||||
macro_rules! assert_approx_eq {
|
|
||||||
($a:expr, $b:expr) => {{
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use support::FloatCompare;
|
|
||||||
let eps = core::f32::EPSILON;
|
|
||||||
let (a, b) = (&$a, &$b);
|
|
||||||
assert!(
|
|
||||||
a.approx_eq(b, eps),
|
|
||||||
"assertion failed: `(left !== right)` \
|
|
||||||
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
|
|
||||||
*a,
|
|
||||||
*b,
|
|
||||||
eps,
|
|
||||||
a.abs_diff(b)
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
($a:expr, $b:expr, $eps:expr) => {{
|
|
||||||
use support::FloatCompare;
|
|
||||||
let (a, b) = (&$a, &$b);
|
|
||||||
let eps = $eps;
|
|
||||||
assert!(
|
|
||||||
a.approx_eq(b, $eps),
|
|
||||||
"assertion failed: `(left !== right)` \
|
|
||||||
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
|
|
||||||
*a,
|
|
||||||
*b,
|
|
||||||
eps,
|
|
||||||
a.abs_diff(b)
|
|
||||||
);
|
|
||||||
}};
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
|
|
||||||
use glam::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
|
|
||||||
|
|
||||||
#[cfg(feature = "transform-types")]
|
|
||||||
use glam::{TransformRT, TransformSRT};
|
|
||||||
|
|
||||||
/// Helper function for migrating away from `glam::angle::deg`.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[inline]
|
|
||||||
pub fn deg(angle: f32) -> f32 {
|
|
||||||
angle.to_radians()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper function for migrating away from `glam::angle::rad`.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[inline]
|
|
||||||
pub fn rad(angle: f32) -> f32 {
|
|
||||||
angle
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait used by the `assert_approx_eq` macro for floating point comparisons.
|
|
||||||
pub trait FloatCompare<Rhs: ?Sized = Self> {
|
|
||||||
/// Return true if the absolute difference between `self` and `other` is
|
|
||||||
/// less then or equal to `max_abs_diff`.
|
|
||||||
fn approx_eq(&self, other: &Rhs, max_abs_diff: f32) -> bool;
|
|
||||||
/// Returns the absolute difference of `self` and `other` which is printed
|
|
||||||
/// if `assert_approx_eq` fails.
|
|
||||||
fn abs_diff(&self, other: &Rhs) -> Rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatCompare for f32 {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &f32, max_abs_diff: f32) -> bool {
|
|
||||||
(self - other).abs() <= max_abs_diff
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &f32) -> f32 {
|
|
||||||
(self - other).abs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatCompare for Mat2 {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &Mat2, max_abs_diff: f32) -> bool {
|
|
||||||
self.abs_diff_eq(*other, max_abs_diff)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &Mat2) -> Mat2 {
|
|
||||||
Mat2::from_cols(
|
|
||||||
(self.x_axis() - other.x_axis()).abs(),
|
|
||||||
(self.y_axis() - other.y_axis()).abs(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatCompare for Mat3 {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &Mat3, max_abs_diff: f32) -> bool {
|
|
||||||
self.abs_diff_eq(*other, max_abs_diff)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &Mat3) -> Mat3 {
|
|
||||||
Mat3::from_cols(
|
|
||||||
(self.x_axis() - other.x_axis()).abs(),
|
|
||||||
(self.y_axis() - other.y_axis()).abs(),
|
|
||||||
(self.z_axis() - other.z_axis()).abs(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatCompare for Mat4 {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &Mat4, max_abs_diff: f32) -> bool {
|
|
||||||
self.abs_diff_eq(*other, max_abs_diff)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &Mat4) -> Mat4 {
|
|
||||||
Mat4::from_cols(
|
|
||||||
(self.x_axis() - other.x_axis()).abs(),
|
|
||||||
(self.y_axis() - other.y_axis()).abs(),
|
|
||||||
(self.z_axis() - other.z_axis()).abs(),
|
|
||||||
(self.w_axis() - other.w_axis()).abs(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatCompare for Quat {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &Quat, max_abs_diff: f32) -> bool {
|
|
||||||
self.abs_diff_eq(*other, max_abs_diff)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &Quat) -> Quat {
|
|
||||||
let a: Vec4 = (*self).into();
|
|
||||||
let b: Vec4 = (*other).into();
|
|
||||||
(a - b).abs().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatCompare for Vec2 {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &Vec2, max_abs_diff: f32) -> bool {
|
|
||||||
self.abs_diff_eq(*other, max_abs_diff)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &Vec2) -> Vec2 {
|
|
||||||
(*self - *other).abs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatCompare for Vec3 {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &Vec3, max_abs_diff: f32) -> bool {
|
|
||||||
self.abs_diff_eq(*other, max_abs_diff)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &Vec3) -> Vec3 {
|
|
||||||
(*self - *other).abs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FloatCompare for Vec4 {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &Vec4, max_abs_diff: f32) -> bool {
|
|
||||||
self.abs_diff_eq(*other, max_abs_diff)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &Vec4) -> Vec4 {
|
|
||||||
(*self - *other).abs()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "transform-types")]
|
|
||||||
impl FloatCompare for TransformSRT {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
|
|
||||||
self.abs_diff_eq(*other, max_abs_diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &Self) -> Self {
|
|
||||||
Self::from_scale_rotation_translation(
|
|
||||||
self.scale.abs_diff(&other.scale),
|
|
||||||
self.rotation.abs_diff(&other.rotation),
|
|
||||||
self.translation.abs_diff(&other.translation),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "transform-types")]
|
|
||||||
impl FloatCompare for TransformRT {
|
|
||||||
#[inline]
|
|
||||||
fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
|
|
||||||
self.abs_diff_eq(*other, max_abs_diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn abs_diff(&self, other: &Self) -> Self {
|
|
||||||
Self::from_rotation_translation(
|
|
||||||
self.rotation.abs_diff(&other.rotation),
|
|
||||||
self.translation.abs_diff(&other.translation),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
#[cfg(feature = "transform-types")]
|
|
||||||
#[macro_use]
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
#[cfg(feature = "transform-types")]
|
|
||||||
mod transform {
|
|
||||||
use super::support;
|
|
||||||
use glam::f32::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_identity() {
|
|
||||||
let tr = TransformRT::identity();
|
|
||||||
assert_eq!(tr.rotation, Quat::identity());
|
|
||||||
assert_eq!(tr.translation, Vec3::zero());
|
|
||||||
|
|
||||||
let srt = TransformSRT::identity();
|
|
||||||
assert_eq!(srt.scale, Vec3::one());
|
|
||||||
assert_eq!(srt.rotation, Quat::identity());
|
|
||||||
assert_eq!(srt.translation, Vec3::zero());
|
|
||||||
|
|
||||||
assert_eq!(srt, tr.into());
|
|
||||||
|
|
||||||
assert_eq!(TransformRT::identity(), TransformRT::default());
|
|
||||||
assert_eq!(TransformSRT::identity(), TransformSRT::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_new() {
|
|
||||||
let t = Vec3::new(1.0, 2.0, 3.0);
|
|
||||||
let r = Quat::from_rotation_y(90.0_f32.to_radians());
|
|
||||||
let s = Vec3::new(-1.0, -2.0, -3.0);
|
|
||||||
|
|
||||||
let tr = TransformRT::from_rotation_translation(r, t);
|
|
||||||
assert_eq!(tr.rotation, r);
|
|
||||||
assert_eq!(tr.translation, t);
|
|
||||||
|
|
||||||
let srt = TransformSRT::from_scale_rotation_translation(s, r, t);
|
|
||||||
assert_eq!(srt.scale, s);
|
|
||||||
assert_eq!(srt.rotation, r);
|
|
||||||
assert_eq!(srt.translation, t);
|
|
||||||
|
|
||||||
assert_eq!(tr, tr);
|
|
||||||
assert_eq!(srt, srt);
|
|
||||||
assert_eq!(srt, TransformSRT::from_transform_rt(s, &tr));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mul() {
|
|
||||||
let tr = TransformRT::from_rotation_translation(
|
|
||||||
Quat::from_rotation_z(-90.0_f32.to_radians()),
|
|
||||||
Vec3::unit_x(),
|
|
||||||
);
|
|
||||||
let v0 = Vec3::unit_y();
|
|
||||||
let v1 = tr * v0;
|
|
||||||
assert_approx_eq!(v1, Vec3::unit_x() * 2.0);
|
|
||||||
assert_approx_eq!(v1, tr * v0);
|
|
||||||
let inv_tr = tr.inverse();
|
|
||||||
let v2 = inv_tr * v1;
|
|
||||||
assert_approx_eq!(v0, v2);
|
|
||||||
|
|
||||||
assert_eq!(tr * TransformRT::identity(), tr);
|
|
||||||
assert_approx_eq!(tr * inv_tr, TransformRT::identity());
|
|
||||||
|
|
||||||
assert_eq!(tr * TransformSRT::identity(), TransformSRT::from(tr));
|
|
||||||
assert_eq!(TransformSRT::identity() * tr, TransformSRT::from(tr));
|
|
||||||
|
|
||||||
let s = Vec3::splat(2.0);
|
|
||||||
let r = Quat::from_rotation_y(180.0_f32.to_radians());
|
|
||||||
let t = -Vec3::unit_y();
|
|
||||||
let srt = TransformSRT::from_scale_rotation_translation(s, r, t);
|
|
||||||
let v0 = Vec3::unit_x();
|
|
||||||
let v1 = srt * v0;
|
|
||||||
assert_approx_eq!(v1, (r * (v0 * s)) + t);
|
|
||||||
assert_approx_eq!(v1, srt * v0);
|
|
||||||
let inv_srt = srt.inverse();
|
|
||||||
let v2 = inv_srt * v1;
|
|
||||||
assert_approx_eq!(v0, v2);
|
|
||||||
|
|
||||||
assert_eq!(srt * TransformSRT::identity(), srt);
|
|
||||||
assert_eq!(srt * inv_srt, TransformSRT::identity());
|
|
||||||
|
|
||||||
// negative scale mul test
|
|
||||||
let s = Vec3::splat(-2.0);
|
|
||||||
let srt = TransformSRT::from_scale_rotation_translation(s, r, t);
|
|
||||||
let inv_srt = srt.inverse();
|
|
||||||
assert_eq!(srt * inv_srt, TransformSRT::identity());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,490 +0,0 @@
|
||||||
mod support;
|
|
||||||
|
|
||||||
use glam::*;
|
|
||||||
use std::f32;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_align() {
|
|
||||||
use core::mem;
|
|
||||||
assert_eq!(8, mem::size_of::<Vec2>());
|
|
||||||
assert_eq!(4, mem::align_of::<Vec2>());
|
|
||||||
assert_eq!(8, mem::size_of::<Vec2Mask>());
|
|
||||||
assert_eq!(4, mem::align_of::<Vec2Mask>());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_new() {
|
|
||||||
let v = vec2(1.0, 2.0);
|
|
||||||
|
|
||||||
assert_eq!(v.x(), 1.0);
|
|
||||||
assert_eq!(v.y(), 2.0);
|
|
||||||
|
|
||||||
let t = (1.0, 2.0);
|
|
||||||
let v = Vec2::from(t);
|
|
||||||
assert_eq!(t, v.into());
|
|
||||||
|
|
||||||
let a = [1.0, 2.0];
|
|
||||||
let v = Vec2::from(a);
|
|
||||||
let a1: [f32; 2] = v.into();
|
|
||||||
assert_eq!(a, a1);
|
|
||||||
|
|
||||||
let v = Vec2::new(t.0, t.1);
|
|
||||||
assert_eq!(t, v.into());
|
|
||||||
|
|
||||||
assert_eq!(Vec2::new(1.0, 0.0), Vec2::unit_x());
|
|
||||||
assert_eq!(Vec2::new(0.0, 1.0), Vec2::unit_y());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_fmt() {
|
|
||||||
let a = Vec2::new(1.0, 2.0);
|
|
||||||
assert_eq!(format!("{:?}", a), "Vec2(1.0, 2.0)");
|
|
||||||
// assert_eq!(format!("{:#?}", a), "Vec2(\n 1.0,\n 2.0\n)");
|
|
||||||
assert_eq!(format!("{}", a), "[1, 2]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_zero() {
|
|
||||||
let v = Vec2::zero();
|
|
||||||
assert_eq!(vec2(0.0, 0.0), v);
|
|
||||||
assert_eq!(v, Vec2::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_splat() {
|
|
||||||
let v = Vec2::splat(1.0);
|
|
||||||
assert_eq!(vec2(1.0, 1.0), v);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_accessors() {
|
|
||||||
let mut a = Vec2::zero();
|
|
||||||
a.set_x(1.0);
|
|
||||||
a.set_y(2.0);
|
|
||||||
assert_eq!(1.0, a.x());
|
|
||||||
assert_eq!(2.0, a.y());
|
|
||||||
assert_eq!(Vec2::new(1.0, 2.0), a);
|
|
||||||
|
|
||||||
let mut a = Vec2::zero();
|
|
||||||
*a.x_mut() = 1.0;
|
|
||||||
*a.y_mut() = 2.0;
|
|
||||||
assert_eq!(1.0, a.x());
|
|
||||||
assert_eq!(2.0, a.y());
|
|
||||||
assert_eq!(Vec2::new(1.0, 2.0), a);
|
|
||||||
|
|
||||||
let mut a = Vec2::zero();
|
|
||||||
a[0] = 1.0;
|
|
||||||
a[1] = 2.0;
|
|
||||||
assert_eq!(1.0, a[0]);
|
|
||||||
assert_eq!(2.0, a[1]);
|
|
||||||
assert_eq!(Vec2::new(1.0, 2.0), a);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_funcs() {
|
|
||||||
let x = vec2(1.0, 0.0);
|
|
||||||
let y = vec2(0.0, 1.0);
|
|
||||||
assert_eq!(1.0, x.dot(x));
|
|
||||||
assert_eq!(0.0, x.dot(y));
|
|
||||||
assert_eq!(-1.0, x.dot(-x));
|
|
||||||
assert_eq!(4.0, (2.0 * x).length_squared());
|
|
||||||
assert_eq!(9.0, (-3.0 * y).length_squared());
|
|
||||||
assert_eq!(2.0, (-2.0 * x).length());
|
|
||||||
assert_eq!(3.0, (3.0 * y).length());
|
|
||||||
assert_eq!(x, (2.0 * x).normalize());
|
|
||||||
assert_eq!(1.0 * 3.0 + 2.0 * 4.0, vec2(1.0, 2.0).dot(vec2(3.0, 4.0)));
|
|
||||||
assert_eq!(2.0 * 2.0 + 3.0 * 3.0, vec2(2.0, 3.0).length_squared());
|
|
||||||
assert_eq!((2.0_f32 * 2.0 + 3.0 * 3.0).sqrt(), vec2(2.0, 3.0).length());
|
|
||||||
assert_eq!(
|
|
||||||
1.0 / (2.0_f32 * 2.0 + 3.0 * 3.0).sqrt(),
|
|
||||||
vec2(2.0, 3.0).length_reciprocal()
|
|
||||||
);
|
|
||||||
assert!(vec2(2.0, 3.0).normalize().is_normalized());
|
|
||||||
assert_eq!(
|
|
||||||
vec2(2.0, 3.0) / (2.0_f32 * 2.0 + 3.0 * 3.0).sqrt(),
|
|
||||||
vec2(2.0, 3.0).normalize()
|
|
||||||
);
|
|
||||||
assert_eq!(vec2(0.5, 0.25), vec2(2.0, 4.0).reciprocal());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_ops() {
|
|
||||||
let a = vec2(1.0, 2.0);
|
|
||||||
assert_eq!(vec2(2.0, 4.0), (a + a));
|
|
||||||
assert_eq!(vec2(0.0, 0.0), (a - a));
|
|
||||||
assert_eq!(vec2(1.0, 4.0), (a * a));
|
|
||||||
assert_eq!(vec2(2.0, 4.0), (a * 2.0));
|
|
||||||
assert_eq!(vec2(1.0, 1.0), (a / a));
|
|
||||||
assert_eq!(vec2(0.5, 1.0), (a / 2.0));
|
|
||||||
assert_eq!(vec2(-1.0, -2.0), (-a));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_assign_ops() {
|
|
||||||
let a = vec2(1.0, 2.0);
|
|
||||||
let mut b = a;
|
|
||||||
b += a;
|
|
||||||
assert_eq!(vec2(2.0, 4.0), b);
|
|
||||||
b -= a;
|
|
||||||
assert_eq!(vec2(1.0, 2.0), b);
|
|
||||||
b *= a;
|
|
||||||
assert_eq!(vec2(1.0, 4.0), b);
|
|
||||||
b /= a;
|
|
||||||
assert_eq!(vec2(1.0, 2.0), b);
|
|
||||||
b *= 2.0;
|
|
||||||
assert_eq!(vec2(2.0, 4.0), b);
|
|
||||||
b /= 2.0;
|
|
||||||
assert_eq!(vec2(1.0, 2.0), b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_min_max() {
|
|
||||||
let a = vec2(-1.0, 2.0);
|
|
||||||
let b = vec2(1.0, -2.0);
|
|
||||||
assert_eq!(vec2(-1.0, -2.0), a.min(b));
|
|
||||||
assert_eq!(vec2(-1.0, -2.0), b.min(a));
|
|
||||||
assert_eq!(vec2(1.0, 2.0), a.max(b));
|
|
||||||
assert_eq!(vec2(1.0, 2.0), b.max(a));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_hmin_hmax() {
|
|
||||||
let a = vec2(-1.0, 2.0);
|
|
||||||
assert_eq!(-1.0, a.min_element());
|
|
||||||
assert_eq!(2.0, a.max_element());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_eq() {
|
|
||||||
let a = vec2(1.0, 1.0);
|
|
||||||
let b = vec2(1.0, 2.0);
|
|
||||||
assert!(a.cmpeq(a).all());
|
|
||||||
assert!(b.cmpeq(b).all());
|
|
||||||
assert!(a.cmpne(b).any());
|
|
||||||
assert!(b.cmpne(a).any());
|
|
||||||
assert!(b.cmpeq(a).any());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_cmp() {
|
|
||||||
assert!(!Vec2Mask::default().any());
|
|
||||||
assert!(!Vec2Mask::default().all());
|
|
||||||
assert_eq!(Vec2Mask::default().bitmask(), 0x0);
|
|
||||||
let a = vec2(-1.0, -1.0);
|
|
||||||
let b = vec2(1.0, 1.0);
|
|
||||||
let c = vec2(-1.0, -1.0);
|
|
||||||
let d = vec2(1.0, -1.0);
|
|
||||||
assert_eq!(a.cmplt(a).bitmask(), 0x0);
|
|
||||||
assert_eq!(a.cmplt(b).bitmask(), 0x3);
|
|
||||||
assert_eq!(a.cmplt(d).bitmask(), 0x1);
|
|
||||||
assert_eq!(c.cmple(a).bitmask(), 0x3);
|
|
||||||
assert!(a.cmplt(b).all());
|
|
||||||
assert!(a.cmplt(d).any());
|
|
||||||
assert!(a.cmple(b).all());
|
|
||||||
assert!(a.cmple(a).all());
|
|
||||||
assert!(b.cmpgt(a).all());
|
|
||||||
assert!(b.cmpge(a).all());
|
|
||||||
assert!(b.cmpge(b).all());
|
|
||||||
assert!(!(a.cmpge(d).all()));
|
|
||||||
assert!(c.cmple(c).all());
|
|
||||||
assert!(c.cmpge(c).all());
|
|
||||||
assert!(a == a);
|
|
||||||
assert!(a < b);
|
|
||||||
assert!(b > a);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_extend_truncate() {
|
|
||||||
let a = vec2(1.0, 2.0);
|
|
||||||
let b = a.extend(3.0);
|
|
||||||
assert_eq!(vec3(1.0, 2.0, 3.0), b);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2b() {
|
|
||||||
// make sure the unused 'w' value doesn't break Vec2b behaviour
|
|
||||||
let a = Vec3::zero();
|
|
||||||
let mut b = a.truncate();
|
|
||||||
b.set_x(1.0);
|
|
||||||
b.set_y(1.0);
|
|
||||||
assert!(!b.cmpeq(Vec2::zero()).any());
|
|
||||||
assert!(b.cmpeq(Vec2::splat(1.0)).all());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_as_ref() {
|
|
||||||
assert_eq!(Vec2Mask::new(false, false).as_ref(), &[0, 0]);
|
|
||||||
assert_eq!(Vec2Mask::new(true, false).as_ref(), &[!0, 0]);
|
|
||||||
assert_eq!(Vec2Mask::new(false, true).as_ref(), &[0, !0]);
|
|
||||||
assert_eq!(Vec2Mask::new(true, true).as_ref(), &[!0, !0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_from() {
|
|
||||||
assert_eq!(Into::<[u32; 2]>::into(Vec2Mask::new(false, false)), [0, 0]);
|
|
||||||
assert_eq!(Into::<[u32; 2]>::into(Vec2Mask::new(true, false)), [!0, 0]);
|
|
||||||
assert_eq!(Into::<[u32; 2]>::into(Vec2Mask::new(false, true)), [0, !0]);
|
|
||||||
assert_eq!(Into::<[u32; 2]>::into(Vec2Mask::new(true, true)), [!0, !0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_bitmask() {
|
|
||||||
assert_eq!(Vec2Mask::new(false, false).bitmask(), 0b00);
|
|
||||||
assert_eq!(Vec2Mask::new(true, false).bitmask(), 0b01);
|
|
||||||
assert_eq!(Vec2Mask::new(false, true).bitmask(), 0b10);
|
|
||||||
assert_eq!(Vec2Mask::new(true, true).bitmask(), 0b11);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_any() {
|
|
||||||
assert_eq!(Vec2Mask::new(false, false).any(), false);
|
|
||||||
assert_eq!(Vec2Mask::new(true, false).any(), true);
|
|
||||||
assert_eq!(Vec2Mask::new(false, true).any(), true);
|
|
||||||
assert_eq!(Vec2Mask::new(true, true).any(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_all() {
|
|
||||||
assert_eq!(Vec2Mask::new(false, false).all(), false);
|
|
||||||
assert_eq!(Vec2Mask::new(true, false).all(), false);
|
|
||||||
assert_eq!(Vec2Mask::new(false, true).all(), false);
|
|
||||||
assert_eq!(Vec2Mask::new(true, true).all(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_select() {
|
|
||||||
let a = Vec2::new(1.0, 2.0);
|
|
||||||
let b = Vec2::new(3.0, 4.0);
|
|
||||||
assert_eq!(Vec2Mask::new(true, true).select(a, b), Vec2::new(1.0, 2.0),);
|
|
||||||
assert_eq!(Vec2Mask::new(true, false).select(a, b), Vec2::new(1.0, 4.0),);
|
|
||||||
assert_eq!(Vec2Mask::new(false, true).select(a, b), Vec2::new(3.0, 2.0),);
|
|
||||||
assert_eq!(
|
|
||||||
Vec2Mask::new(false, false).select(a, b),
|
|
||||||
Vec2::new(3.0, 4.0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_and() {
|
|
||||||
assert_eq!(
|
|
||||||
(Vec2Mask::new(false, false) & Vec2Mask::new(false, false)).bitmask(),
|
|
||||||
0b00,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec2Mask::new(true, true) & Vec2Mask::new(true, false)).bitmask(),
|
|
||||||
0b01,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec2Mask::new(true, false) & Vec2Mask::new(false, true)).bitmask(),
|
|
||||||
0b00,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec2Mask::new(true, true) & Vec2Mask::new(true, true)).bitmask(),
|
|
||||||
0b11,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut mask = Vec2Mask::new(true, true);
|
|
||||||
mask &= Vec2Mask::new(true, false);
|
|
||||||
assert_eq!(mask.bitmask(), 0b01);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_or() {
|
|
||||||
assert_eq!(
|
|
||||||
(Vec2Mask::new(false, false) | Vec2Mask::new(false, false)).bitmask(),
|
|
||||||
0b00,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec2Mask::new(false, false) | Vec2Mask::new(false, true)).bitmask(),
|
|
||||||
0b10,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec2Mask::new(true, false) | Vec2Mask::new(false, true)).bitmask(),
|
|
||||||
0b11,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec2Mask::new(true, true) | Vec2Mask::new(true, true)).bitmask(),
|
|
||||||
0b11,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut mask = Vec2Mask::new(true, true);
|
|
||||||
mask |= Vec2Mask::new(true, false);
|
|
||||||
assert_eq!(mask.bitmask(), 0b11);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_not() {
|
|
||||||
assert_eq!((!Vec2Mask::new(false, false)).bitmask(), 0b11);
|
|
||||||
assert_eq!((!Vec2Mask::new(true, false)).bitmask(), 0b10);
|
|
||||||
assert_eq!((!Vec2Mask::new(false, true)).bitmask(), 0b01);
|
|
||||||
assert_eq!((!Vec2Mask::new(true, true)).bitmask(), 0b00);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_fmt() {
|
|
||||||
let a = Vec2Mask::new(true, false);
|
|
||||||
|
|
||||||
assert_eq!(format!("{:?}", a), "Vec2Mask(0xffffffff, 0x0)");
|
|
||||||
assert_eq!(format!("{}", a), "[true, false]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_eq() {
|
|
||||||
let a = Vec2Mask::new(true, false);
|
|
||||||
let b = Vec2Mask::new(true, false);
|
|
||||||
let c = Vec2Mask::new(false, true);
|
|
||||||
|
|
||||||
assert_eq!(a, b);
|
|
||||||
assert_eq!(b, a);
|
|
||||||
assert_ne!(a, c);
|
|
||||||
assert_ne!(b, c);
|
|
||||||
|
|
||||||
assert!(a > c);
|
|
||||||
assert!(c < a);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2mask_hash() {
|
|
||||||
use std::{
|
|
||||||
collections::hash_map::DefaultHasher,
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
};
|
|
||||||
|
|
||||||
let a = Vec2Mask::new(true, false);
|
|
||||||
let b = Vec2Mask::new(true, false);
|
|
||||||
let c = Vec2Mask::new(false, true);
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
a.hash(&mut hasher);
|
|
||||||
let a_hashed = hasher.finish();
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
b.hash(&mut hasher);
|
|
||||||
let b_hashed = hasher.finish();
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
c.hash(&mut hasher);
|
|
||||||
let c_hashed = hasher.finish();
|
|
||||||
|
|
||||||
assert_eq!(a, b);
|
|
||||||
assert_eq!(a_hashed, b_hashed);
|
|
||||||
assert_ne!(a, c);
|
|
||||||
assert_ne!(a_hashed, c_hashed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_sign() {
|
|
||||||
assert_eq!(Vec2::zero().sign(), Vec2::one());
|
|
||||||
assert_eq!(-Vec2::zero().sign(), -Vec2::one());
|
|
||||||
assert_eq!(Vec2::one().sign(), Vec2::one());
|
|
||||||
assert_eq!((-Vec2::one()).sign(), -Vec2::one());
|
|
||||||
assert_eq!(Vec2::splat(core::f32::NEG_INFINITY).sign(), -Vec2::one());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_abs() {
|
|
||||||
assert_eq!(Vec2::zero().abs(), Vec2::zero());
|
|
||||||
assert_eq!(Vec2::one().abs(), Vec2::one());
|
|
||||||
assert_eq!((-Vec2::one()).abs(), Vec2::one());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_round() {
|
|
||||||
assert_eq!(Vec2::new(1.35, 0.0).round().x(), 1.0);
|
|
||||||
assert_eq!(Vec2::new(0.0, 1.5).round().y(), 2.0);
|
|
||||||
assert_eq!(Vec2::new(0.0, -15.5).round().y(), -16.0);
|
|
||||||
assert_eq!(Vec2::new(0.0, 0.0).round().y(), 0.0);
|
|
||||||
assert_eq!(Vec2::new(0.0, 21.1).round().y(), 21.0);
|
|
||||||
assert_eq!(Vec2::new(0.0, 11.123).round().y(), 11.0);
|
|
||||||
assert_eq!(Vec2::new(0.0, 11.499).round().y(), 11.0);
|
|
||||||
assert_eq!(
|
|
||||||
Vec2::new(f32::NEG_INFINITY, f32::INFINITY).round(),
|
|
||||||
Vec2::new(f32::NEG_INFINITY, f32::INFINITY)
|
|
||||||
);
|
|
||||||
assert!(Vec2::new(f32::NAN, 0.0).round().x().is_nan());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_floor() {
|
|
||||||
assert_eq!(Vec2::new(1.35, -1.5).floor(), Vec2::new(1.0, -2.0));
|
|
||||||
assert_eq!(
|
|
||||||
Vec2::new(f32::INFINITY, f32::NEG_INFINITY).floor(),
|
|
||||||
Vec2::new(f32::INFINITY, f32::NEG_INFINITY)
|
|
||||||
);
|
|
||||||
assert!(Vec2::new(f32::NAN, 0.0).floor().x().is_nan());
|
|
||||||
assert_eq!(
|
|
||||||
Vec2::new(-2000000.123, 10000000.123).floor(),
|
|
||||||
Vec2::new(-2000001.0, 10000000.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_ceil() {
|
|
||||||
assert_eq!(Vec2::new(1.35, -1.5).ceil(), Vec2::new(2.0, -1.0));
|
|
||||||
assert_eq!(
|
|
||||||
Vec2::new(f32::INFINITY, f32::NEG_INFINITY).ceil(),
|
|
||||||
Vec2::new(f32::INFINITY, f32::NEG_INFINITY)
|
|
||||||
);
|
|
||||||
assert!(Vec2::new(f32::NAN, 0.0).ceil().x().is_nan());
|
|
||||||
assert_eq!(
|
|
||||||
Vec2::new(-2000000.123, 1000000.123).ceil(),
|
|
||||||
Vec2::new(-2000000.0, 1000001.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_lerp() {
|
|
||||||
let v0 = Vec2::new(-1.0, -1.0);
|
|
||||||
let v1 = Vec2::new(1.0, 1.0);
|
|
||||||
assert_approx_eq!(v0, v0.lerp(v1, 0.0));
|
|
||||||
assert_approx_eq!(v1, v0.lerp(v1, 1.0));
|
|
||||||
assert_approx_eq!(Vec2::zero(), v0.lerp(v1, 0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_to_from_slice() {
|
|
||||||
let v = Vec2::new(1.0, 2.0);
|
|
||||||
let mut a = [0.0, 0.0];
|
|
||||||
v.write_to_slice_unaligned(&mut a);
|
|
||||||
assert_eq!(v, Vec2::from_slice_unaligned(&a));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_angle_between() {
|
|
||||||
let angle = Vec2::new(1.0, 0.0).angle_between(Vec2::new(0.0, 1.0));
|
|
||||||
assert_approx_eq!(f32::consts::FRAC_PI_2, angle, 1e-6);
|
|
||||||
|
|
||||||
let angle = Vec2::new(10.0, 0.0).angle_between(Vec2::new(0.0, 5.0));
|
|
||||||
assert_approx_eq!(f32::consts::FRAC_PI_2, angle, 1e-6);
|
|
||||||
|
|
||||||
let angle = Vec2::new(-1.0, 0.0).angle_between(Vec2::new(0.0, 1.0));
|
|
||||||
assert_approx_eq!(-f32::consts::FRAC_PI_2, angle, 1e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_serde() {
|
|
||||||
let a = Vec2::new(1.0, 2.0);
|
|
||||||
let serialized = serde_json::to_string(&a).unwrap();
|
|
||||||
assert_eq!(serialized, "[1.0,2.0]");
|
|
||||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
|
||||||
assert_eq!(a, deserialized);
|
|
||||||
let deserialized = serde_json::from_str::<Vec2>("[]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Vec2>("[1.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Vec2>("[1.0,2.0,3.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
#[test]
|
|
||||||
fn test_vec2_rand() {
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use rand_xoshiro::Xoshiro256Plus;
|
|
||||||
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let a: (f32, f32) = rng1.gen();
|
|
||||||
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let b: Vec2 = rng2.gen();
|
|
||||||
assert_eq!(a, b.into());
|
|
||||||
}
|
|
|
@ -1,599 +0,0 @@
|
||||||
mod support;
|
|
||||||
|
|
||||||
use glam::*;
|
|
||||||
use std::f32;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_align() {
|
|
||||||
use std::mem;
|
|
||||||
if cfg!(any(feature = "packed-vec3", feature = "scalar-math")) {
|
|
||||||
assert_eq!(12, mem::size_of::<Vec3>());
|
|
||||||
assert_eq!(4, mem::align_of::<Vec3>());
|
|
||||||
assert_eq!(12, mem::size_of::<Vec3Mask>());
|
|
||||||
assert_eq!(4, mem::align_of::<Vec3Mask>());
|
|
||||||
} else {
|
|
||||||
assert_eq!(16, mem::size_of::<Vec3>());
|
|
||||||
assert_eq!(16, mem::align_of::<Vec3>());
|
|
||||||
assert_eq!(16, mem::size_of::<Vec3Mask>());
|
|
||||||
assert_eq!(16, mem::align_of::<Vec3Mask>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_new() {
|
|
||||||
let v = vec3(1.0, 2.0, 3.0);
|
|
||||||
|
|
||||||
assert_eq!(v.x(), 1.0);
|
|
||||||
assert_eq!(v.y(), 2.0);
|
|
||||||
assert_eq!(v.z(), 3.0);
|
|
||||||
|
|
||||||
let t = (1.0, 2.0, 3.0);
|
|
||||||
let v = Vec3::from(t);
|
|
||||||
assert_eq!(t, v.into());
|
|
||||||
|
|
||||||
let a = [1.0, 2.0, 3.0];
|
|
||||||
let v = Vec3::from(a);
|
|
||||||
let a1: [f32; 3] = v.into();
|
|
||||||
assert_eq!(a, a1);
|
|
||||||
|
|
||||||
let v = Vec3::new(t.0, t.1, t.2);
|
|
||||||
assert_eq!(t, v.into());
|
|
||||||
|
|
||||||
assert_eq!(Vec3::new(1.0, 0.0, 0.0), Vec3::unit_x());
|
|
||||||
assert_eq!(Vec3::new(0.0, 1.0, 0.0), Vec3::unit_y());
|
|
||||||
assert_eq!(Vec3::new(0.0, 0.0, 1.0), Vec3::unit_z());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_fmt() {
|
|
||||||
let a = Vec3::new(1.0, 2.0, 3.0);
|
|
||||||
#[cfg(all(
|
|
||||||
target_feature = "sse2",
|
|
||||||
not(feature = "packed-vec3"),
|
|
||||||
not(feature = "scalar-math")
|
|
||||||
))]
|
|
||||||
assert_eq!(format!("{:?}", a), "Vec3(__m128(1.0, 2.0, 3.0, 3.0))");
|
|
||||||
#[cfg(any(
|
|
||||||
not(target_feature = "sse2"),
|
|
||||||
feature = "packed-vec3",
|
|
||||||
feature = "scalar-math"
|
|
||||||
))]
|
|
||||||
assert_eq!(format!("{:?}", a), "Vec3(1.0, 2.0, 3.0)");
|
|
||||||
// assert_eq!(format!("{:#?}", a), "Vec3(\n 1.0,\n 2.0,\n 3.0\n)");
|
|
||||||
assert_eq!(format!("{}", a), "[1, 2, 3]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_zero() {
|
|
||||||
let v = Vec3::zero();
|
|
||||||
assert_eq!((0.0, 0.0, 0.0), v.into());
|
|
||||||
assert_eq!(v, Vec3::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_splat() {
|
|
||||||
let v = Vec3::splat(1.0);
|
|
||||||
assert_eq!((1.0, 1.0, 1.0), v.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_accessors() {
|
|
||||||
let mut a = Vec3::zero();
|
|
||||||
a.set_x(1.0);
|
|
||||||
a.set_y(2.0);
|
|
||||||
a.set_z(3.0);
|
|
||||||
assert_eq!(1.0, a.x());
|
|
||||||
assert_eq!(2.0, a.y());
|
|
||||||
assert_eq!(3.0, a.z());
|
|
||||||
assert_eq!((1.0, 2.0, 3.0), a.into());
|
|
||||||
|
|
||||||
let mut a = Vec3::zero();
|
|
||||||
*a.x_mut() = 1.0;
|
|
||||||
*a.y_mut() = 2.0;
|
|
||||||
*a.z_mut() = 3.0;
|
|
||||||
assert_eq!(1.0, a.x());
|
|
||||||
assert_eq!(2.0, a.y());
|
|
||||||
assert_eq!(3.0, a.z());
|
|
||||||
assert_eq!((1.0, 2.0, 3.0), a.into());
|
|
||||||
|
|
||||||
let mut a = Vec3::zero();
|
|
||||||
a[0] = 1.0;
|
|
||||||
a[1] = 2.0;
|
|
||||||
a[2] = 3.0;
|
|
||||||
assert_eq!(1.0, a[0]);
|
|
||||||
assert_eq!(2.0, a[1]);
|
|
||||||
assert_eq!(3.0, a[2]);
|
|
||||||
assert_eq!((1.0, 2.0, 3.0), a.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_funcs() {
|
|
||||||
let x = vec3(1.0, 0.0, 0.0);
|
|
||||||
let y = vec3(0.0, 1.0, 0.0);
|
|
||||||
let z = vec3(0.0, 0.0, 1.0);
|
|
||||||
assert_eq!(1.0, x.dot(x));
|
|
||||||
assert_eq!(0.0, x.dot(y));
|
|
||||||
assert_eq!(-1.0, z.dot(-z));
|
|
||||||
assert_eq!(y, z.cross(x));
|
|
||||||
assert_eq!(z, x.cross(y));
|
|
||||||
assert_eq!(4.0, (2.0 * x).length_squared());
|
|
||||||
assert_eq!(9.0, (-3.0 * y).length_squared());
|
|
||||||
assert_eq!(16.0, (4.0 * z).length_squared());
|
|
||||||
assert_eq!(2.0, (-2.0 * x).length());
|
|
||||||
assert_eq!(3.0, (3.0 * y).length());
|
|
||||||
assert_eq!(4.0, (-4.0 * z).length());
|
|
||||||
assert_eq!(x, (2.0 * x).normalize());
|
|
||||||
assert_eq!(
|
|
||||||
1.0 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0,
|
|
||||||
vec3(1.0, 2.0, 3.0).dot(vec3(4.0, 5.0, 6.0))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
2.0 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0,
|
|
||||||
vec3(2.0, 3.0, 4.0).length_squared()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0).sqrt(),
|
|
||||||
vec3(2.0, 3.0, 4.0).length()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
1.0 / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0).sqrt(),
|
|
||||||
vec3(2.0, 3.0, 4.0).length_reciprocal()
|
|
||||||
);
|
|
||||||
assert!(vec3(2.0, 3.0, 4.0).normalize().is_normalized());
|
|
||||||
assert_approx_eq!(
|
|
||||||
vec3(2.0, 3.0, 4.0) / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0).sqrt(),
|
|
||||||
vec3(2.0, 3.0, 4.0).normalize()
|
|
||||||
);
|
|
||||||
assert_eq!(vec3(0.5, 0.25, 0.125), vec3(2.0, 4.0, 8.0).reciprocal());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_ops() {
|
|
||||||
let a = vec3(1.0, 2.0, 3.0);
|
|
||||||
assert_eq!((2.0, 4.0, 6.0), (a + a).into());
|
|
||||||
assert_eq!((0.0, 0.0, 0.0), (a - a).into());
|
|
||||||
assert_eq!((1.0, 4.0, 9.0), (a * a).into());
|
|
||||||
assert_eq!((2.0, 4.0, 6.0), (a * 2.0).into());
|
|
||||||
assert_eq!((1.0, 1.0, 1.0), (a / a).into());
|
|
||||||
assert_eq!((0.5, 1.0, 1.5), (a / 2.0).into());
|
|
||||||
assert_eq!((-1.0, -2.0, -3.0), (-a).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_assign_ops() {
|
|
||||||
let a = vec3(1.0, 2.0, 3.0);
|
|
||||||
let mut b = a;
|
|
||||||
b += a;
|
|
||||||
assert_eq!((2.0, 4.0, 6.0), b.into());
|
|
||||||
b -= a;
|
|
||||||
assert_eq!((1.0, 2.0, 3.0), b.into());
|
|
||||||
b *= a;
|
|
||||||
assert_eq!((1.0, 4.0, 9.0), b.into());
|
|
||||||
b /= a;
|
|
||||||
assert_eq!((1.0, 2.0, 3.0), b.into());
|
|
||||||
b *= 2.0;
|
|
||||||
assert_eq!((2.0, 4.0, 6.0), b.into());
|
|
||||||
b /= 2.0;
|
|
||||||
assert_eq!((1.0, 2.0, 3.0), b.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_min_max() {
|
|
||||||
let a = vec3(-1.0, 2.0, -3.0);
|
|
||||||
let b = vec3(1.0, -2.0, 3.0);
|
|
||||||
assert_eq!((-1.0, -2.0, -3.0), a.min(b).into());
|
|
||||||
assert_eq!((-1.0, -2.0, -3.0), b.min(a).into());
|
|
||||||
assert_eq!((1.0, 2.0, 3.0), a.max(b).into());
|
|
||||||
assert_eq!((1.0, 2.0, 3.0), b.max(a).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_hmin_hmax() {
|
|
||||||
let a = vec3(-1.0, 2.0, -3.0);
|
|
||||||
assert_eq!(-3.0, a.min_element());
|
|
||||||
assert_eq!(2.0, a.max_element());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_eq() {
|
|
||||||
let a = vec3(1.0, 1.0, 1.0);
|
|
||||||
let b = vec3(1.0, 2.0, 3.0);
|
|
||||||
assert!(a.cmpeq(a).all());
|
|
||||||
assert!(b.cmpeq(b).all());
|
|
||||||
assert!(a.cmpne(b).any());
|
|
||||||
assert!(b.cmpne(a).any());
|
|
||||||
assert!(b.cmpeq(a).any());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_cmp() {
|
|
||||||
assert!(!Vec3Mask::default().any());
|
|
||||||
assert!(!Vec3Mask::default().all());
|
|
||||||
assert_eq!(Vec3Mask::default().bitmask(), 0x0);
|
|
||||||
let a = vec3(-1.0, -1.0, -1.0);
|
|
||||||
let b = vec3(1.0, 1.0, 1.0);
|
|
||||||
let c = vec3(-1.0, -1.0, 1.0);
|
|
||||||
let d = vec3(1.0, -1.0, -1.0);
|
|
||||||
assert_eq!(a.cmplt(a).bitmask(), 0x0);
|
|
||||||
assert_eq!(a.cmplt(b).bitmask(), 0x7);
|
|
||||||
assert_eq!(a.cmplt(c).bitmask(), 0x4);
|
|
||||||
assert_eq!(c.cmple(a).bitmask(), 0x3);
|
|
||||||
assert_eq!(a.cmplt(d).bitmask(), 0x1);
|
|
||||||
assert!(a.cmplt(b).all());
|
|
||||||
assert!(a.cmplt(c).any());
|
|
||||||
assert!(a.cmple(b).all());
|
|
||||||
assert!(a.cmple(a).all());
|
|
||||||
assert!(b.cmpgt(a).all());
|
|
||||||
assert!(b.cmpge(a).all());
|
|
||||||
assert!(b.cmpge(b).all());
|
|
||||||
assert!(!(a.cmpge(c).all()));
|
|
||||||
assert!(c.cmple(c).all());
|
|
||||||
assert!(c.cmpge(c).all());
|
|
||||||
assert!(a == a);
|
|
||||||
assert!(a < b);
|
|
||||||
assert!(b > a);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_extend_truncate() {
|
|
||||||
let a = vec3(1.0, 2.0, 3.0);
|
|
||||||
let b = a.extend(4.0);
|
|
||||||
assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
|
|
||||||
let c = b.truncate();
|
|
||||||
assert_eq!(a, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3b() {
|
|
||||||
// make sure the unused 'w' value doesn't break Vec3b behaviour
|
|
||||||
let a = Vec4::zero();
|
|
||||||
let mut b = a.truncate();
|
|
||||||
b.set_x(1.0);
|
|
||||||
b.set_y(1.0);
|
|
||||||
b.set_z(1.0);
|
|
||||||
assert!(!b.cmpeq(Vec3::zero()).any());
|
|
||||||
assert!(b.cmpeq(Vec3::splat(1.0)).all());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_as_ref() {
|
|
||||||
assert_eq!(Vec3Mask::new(false, false, false).as_ref(), &[0, 0, 0]);
|
|
||||||
assert_eq!(Vec3Mask::new(true, false, false).as_ref(), &[!0, 0, 0]);
|
|
||||||
assert_eq!(Vec3Mask::new(false, true, true).as_ref(), &[0, !0, !0]);
|
|
||||||
assert_eq!(Vec3Mask::new(false, true, false).as_ref(), &[0, !0, 0]);
|
|
||||||
assert_eq!(Vec3Mask::new(true, false, true).as_ref(), &[!0, 0, !0]);
|
|
||||||
assert_eq!(Vec3Mask::new(true, true, true).as_ref(), &[!0, !0, !0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_from() {
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 3]>::into(Vec3Mask::new(false, false, false)),
|
|
||||||
[0, 0, 0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 3]>::into(Vec3Mask::new(true, false, false)),
|
|
||||||
[!0, 0, 0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 3]>::into(Vec3Mask::new(false, true, true)),
|
|
||||||
[0, !0, !0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 3]>::into(Vec3Mask::new(false, true, false)),
|
|
||||||
[0, !0, 0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 3]>::into(Vec3Mask::new(true, false, true)),
|
|
||||||
[!0, 0, !0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 3]>::into(Vec3Mask::new(true, true, true)),
|
|
||||||
[!0, !0, !0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_bitmask() {
|
|
||||||
assert_eq!(Vec3Mask::new(false, false, false).bitmask(), 0b000);
|
|
||||||
assert_eq!(Vec3Mask::new(true, false, false).bitmask(), 0b001);
|
|
||||||
assert_eq!(Vec3Mask::new(false, true, true).bitmask(), 0b110);
|
|
||||||
assert_eq!(Vec3Mask::new(false, true, false).bitmask(), 0b010);
|
|
||||||
assert_eq!(Vec3Mask::new(true, false, true).bitmask(), 0b101);
|
|
||||||
assert_eq!(Vec3Mask::new(true, true, true).bitmask(), 0b111);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_any() {
|
|
||||||
assert_eq!(Vec3Mask::new(false, false, false).any(), false);
|
|
||||||
assert_eq!(Vec3Mask::new(true, false, false).any(), true);
|
|
||||||
assert_eq!(Vec3Mask::new(false, true, false).any(), true);
|
|
||||||
assert_eq!(Vec3Mask::new(false, false, true).any(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_all() {
|
|
||||||
assert_eq!(Vec3Mask::new(true, true, true).all(), true);
|
|
||||||
assert_eq!(Vec3Mask::new(false, true, true).all(), false);
|
|
||||||
assert_eq!(Vec3Mask::new(true, false, true).all(), false);
|
|
||||||
assert_eq!(Vec3Mask::new(true, true, false).all(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_select() {
|
|
||||||
let a = Vec3::new(1.0, 2.0, 3.0);
|
|
||||||
let b = Vec3::new(4.0, 5.0, 6.0);
|
|
||||||
assert_eq!(
|
|
||||||
Vec3Mask::new(true, true, true).select(a, b),
|
|
||||||
Vec3::new(1.0, 2.0, 3.0),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec3Mask::new(true, false, true).select(a, b),
|
|
||||||
Vec3::new(1.0, 5.0, 3.0),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec3Mask::new(false, true, false).select(a, b),
|
|
||||||
Vec3::new(4.0, 2.0, 6.0),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec3Mask::new(false, false, false).select(a, b),
|
|
||||||
Vec3::new(4.0, 5.0, 6.0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_and() {
|
|
||||||
assert_eq!(
|
|
||||||
(Vec3Mask::new(false, false, false) & Vec3Mask::new(false, false, false)).bitmask(),
|
|
||||||
0b000,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec3Mask::new(true, true, true) & Vec3Mask::new(true, true, true)).bitmask(),
|
|
||||||
0b111,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec3Mask::new(true, false, true) & Vec3Mask::new(false, true, false)).bitmask(),
|
|
||||||
0b000,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec3Mask::new(true, false, true) & Vec3Mask::new(true, true, true)).bitmask(),
|
|
||||||
0b101,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut mask = Vec3Mask::new(true, true, false);
|
|
||||||
mask &= Vec3Mask::new(true, false, false);
|
|
||||||
assert_eq!(mask.bitmask(), 0b001);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_or() {
|
|
||||||
assert_eq!(
|
|
||||||
(Vec3Mask::new(false, false, false) | Vec3Mask::new(false, false, false)).bitmask(),
|
|
||||||
0b000,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec3Mask::new(true, true, true) | Vec3Mask::new(true, true, true)).bitmask(),
|
|
||||||
0b111,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec3Mask::new(true, false, true) | Vec3Mask::new(false, true, false)).bitmask(),
|
|
||||||
0b111,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec3Mask::new(true, false, true) | Vec3Mask::new(true, false, true)).bitmask(),
|
|
||||||
0b101,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut mask = Vec3Mask::new(true, true, false);
|
|
||||||
mask |= Vec3Mask::new(true, false, false);
|
|
||||||
assert_eq!(mask.bitmask(), 0b011);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_not() {
|
|
||||||
assert_eq!((!Vec3Mask::new(false, false, false)).bitmask(), 0b111);
|
|
||||||
assert_eq!((!Vec3Mask::new(true, true, true)).bitmask(), 0b000);
|
|
||||||
assert_eq!((!Vec3Mask::new(true, false, true)).bitmask(), 0b010);
|
|
||||||
assert_eq!((!Vec3Mask::new(false, true, false)).bitmask(), 0b101);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_fmt() {
|
|
||||||
let a = Vec3Mask::new(true, false, false);
|
|
||||||
|
|
||||||
// debug fmt
|
|
||||||
#[cfg(all(
|
|
||||||
target_feature = "sse2",
|
|
||||||
not(feature = "packed-vec3"),
|
|
||||||
not(feature = "scalar-math")
|
|
||||||
))]
|
|
||||||
assert_eq!(format!("{:?}", a), "Vec3Mask(0xffffffff, 0x0, 0x0)");
|
|
||||||
|
|
||||||
#[cfg(any(
|
|
||||||
not(target_feature = "sse2"),
|
|
||||||
feature = "packed-vec3",
|
|
||||||
feature = "scalar-math"
|
|
||||||
))]
|
|
||||||
assert_eq!(format!("{:?}", a), "Vec3Mask(0xffffffff, 0x0, 0x0)");
|
|
||||||
|
|
||||||
// display fmt
|
|
||||||
#[cfg(all(
|
|
||||||
target_feature = "sse2",
|
|
||||||
not(feature = "packed-vec3"),
|
|
||||||
not(feature = "scalar-math")
|
|
||||||
))]
|
|
||||||
assert_eq!(format!("{}", a), "[true, false, false]");
|
|
||||||
|
|
||||||
#[cfg(any(
|
|
||||||
not(target_feature = "sse2"),
|
|
||||||
feature = "packed-vec3",
|
|
||||||
feature = "scalar-math"
|
|
||||||
))]
|
|
||||||
assert_eq!(format!("{}", a), "[true, false, false]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_eq() {
|
|
||||||
let a = Vec3Mask::new(true, false, true);
|
|
||||||
let b = Vec3Mask::new(true, false, true);
|
|
||||||
let c = Vec3Mask::new(false, true, true);
|
|
||||||
|
|
||||||
assert_eq!(a, b);
|
|
||||||
assert_eq!(b, a);
|
|
||||||
assert_ne!(a, c);
|
|
||||||
assert_ne!(b, c);
|
|
||||||
|
|
||||||
assert!(a > c);
|
|
||||||
assert!(c < a);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3mask_hash() {
|
|
||||||
use std::{
|
|
||||||
collections::hash_map::DefaultHasher,
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
};
|
|
||||||
|
|
||||||
let a = Vec3Mask::new(true, false, true);
|
|
||||||
let b = Vec3Mask::new(true, false, true);
|
|
||||||
let c = Vec3Mask::new(false, true, true);
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
a.hash(&mut hasher);
|
|
||||||
let a_hashed = hasher.finish();
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
b.hash(&mut hasher);
|
|
||||||
let b_hashed = hasher.finish();
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
c.hash(&mut hasher);
|
|
||||||
let c_hashed = hasher.finish();
|
|
||||||
|
|
||||||
assert_eq!(a, b);
|
|
||||||
assert_eq!(a_hashed, b_hashed);
|
|
||||||
assert_ne!(a, c);
|
|
||||||
assert_ne!(a_hashed, c_hashed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_sign() {
|
|
||||||
assert_eq!(Vec3::zero().sign(), Vec3::one());
|
|
||||||
assert_eq!(-Vec3::zero().sign(), -Vec3::one());
|
|
||||||
assert_eq!(Vec3::one().sign(), Vec3::one());
|
|
||||||
assert_eq!((-Vec3::one()).sign(), -Vec3::one());
|
|
||||||
assert_eq!(Vec3::splat(core::f32::NEG_INFINITY).sign(), -Vec3::one());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_abs() {
|
|
||||||
assert_eq!(Vec3::zero().abs(), Vec3::zero());
|
|
||||||
assert_eq!(Vec3::one().abs(), Vec3::one());
|
|
||||||
assert_eq!((-Vec3::one()).abs(), Vec3::one());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_round() {
|
|
||||||
assert_eq!(Vec3::new(1.35, 0.0, 0.0).round().x(), 1.0);
|
|
||||||
assert_eq!(Vec3::new(0.0, 1.5, 0.0).round().y(), 2.0);
|
|
||||||
assert_eq!(Vec3::new(0.0, 0.0, -15.5).round().z(), -16.0);
|
|
||||||
assert_eq!(Vec3::new(0.0, 0.0, 0.0).round().z(), 0.0);
|
|
||||||
assert_eq!(Vec3::new(0.0, 21.1, 0.0).round().y(), 21.0);
|
|
||||||
assert_eq!(Vec3::new(0.0, 11.123, 0.0).round().y(), 11.0);
|
|
||||||
assert_eq!(Vec3::new(0.0, 11.499, 0.0).round().y(), 11.0);
|
|
||||||
assert_eq!(
|
|
||||||
Vec3::new(f32::NEG_INFINITY, f32::INFINITY, 0.0).round(),
|
|
||||||
Vec3::new(f32::NEG_INFINITY, f32::INFINITY, 0.0)
|
|
||||||
);
|
|
||||||
assert!(Vec3::new(f32::NAN, 0.0, 0.0).round().x().is_nan());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_floor() {
|
|
||||||
assert_eq!(
|
|
||||||
Vec3::new(1.35, 1.5, -1.5).floor(),
|
|
||||||
Vec3::new(1.0, 1.0, -2.0)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec3::new(f32::INFINITY, f32::NEG_INFINITY, 0.0).floor(),
|
|
||||||
Vec3::new(f32::INFINITY, f32::NEG_INFINITY, 0.0)
|
|
||||||
);
|
|
||||||
assert!(Vec3::new(f32::NAN, 0.0, 0.0).floor().x().is_nan());
|
|
||||||
assert_eq!(
|
|
||||||
Vec3::new(-2000000.123, 10000000.123, 1000.9).floor(),
|
|
||||||
Vec3::new(-2000001.0, 10000000.0, 1000.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_ceil() {
|
|
||||||
assert_eq!(Vec3::new(1.35, 1.5, -1.5).ceil(), Vec3::new(2.0, 2.0, -1.0));
|
|
||||||
assert_eq!(
|
|
||||||
Vec3::new(f32::INFINITY, f32::NEG_INFINITY, 0.0).ceil(),
|
|
||||||
Vec3::new(f32::INFINITY, f32::NEG_INFINITY, 0.0)
|
|
||||||
);
|
|
||||||
assert!(Vec3::new(f32::NAN, 0.0, 0.0).ceil().x().is_nan());
|
|
||||||
assert_eq!(
|
|
||||||
Vec3::new(-2000000.123, 1000000.123, 1000.9).ceil(),
|
|
||||||
Vec3::new(-2000000.0, 1000001.0, 1001.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_lerp() {
|
|
||||||
let v0 = Vec3::new(-1.0, -1.0, -1.0);
|
|
||||||
let v1 = Vec3::new(1.0, 1.0, 1.0);
|
|
||||||
assert_approx_eq!(v0, v0.lerp(v1, 0.0));
|
|
||||||
assert_approx_eq!(v1, v0.lerp(v1, 1.0));
|
|
||||||
assert_approx_eq!(Vec3::zero(), v0.lerp(v1, 0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_to_from_slice() {
|
|
||||||
let v = Vec3::new(1.0, 2.0, 3.0);
|
|
||||||
let mut a = [0.0, 0.0, 0.0];
|
|
||||||
v.write_to_slice_unaligned(&mut a);
|
|
||||||
assert_eq!(v, Vec3::from_slice_unaligned(&a));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_angle_between() {
|
|
||||||
let angle = Vec3::new(1.0, 0.0, 1.0).angle_between(Vec3::new(1.0, 1.0, 0.0));
|
|
||||||
assert_approx_eq!(f32::consts::FRAC_PI_3, angle, 1e-6);
|
|
||||||
|
|
||||||
let angle = Vec3::new(10.0, 0.0, 10.0).angle_between(Vec3::new(5.0, 5.0, 0.0));
|
|
||||||
assert_approx_eq!(f32::consts::FRAC_PI_3, angle, 1e-6);
|
|
||||||
|
|
||||||
let angle = Vec3::new(-1.0, 0.0, -1.0).angle_between(Vec3::new(1.0, -1.0, 0.0));
|
|
||||||
assert_approx_eq!(2.0 * f32::consts::FRAC_PI_3, angle, 1e-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_serde() {
|
|
||||||
let a = Vec3::new(1.0, 2.0, 3.0);
|
|
||||||
let serialized = serde_json::to_string(&a).unwrap();
|
|
||||||
assert_eq!(serialized, "[1.0,2.0,3.0]");
|
|
||||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
|
||||||
assert_eq!(a, deserialized);
|
|
||||||
let deserialized = serde_json::from_str::<Vec3>("[]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Vec3>("[1.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Vec3>("[1.0,2.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Vec3>("[1.0,2.0,3.0,4.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
#[test]
|
|
||||||
fn test_vec3_rand() {
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use rand_xoshiro::Xoshiro256Plus;
|
|
||||||
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let a: (f32, f32, f32) = rng1.gen();
|
|
||||||
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let b: Vec3 = rng2.gen();
|
|
||||||
assert_eq!(a, b.into());
|
|
||||||
}
|
|
|
@ -1,621 +0,0 @@
|
||||||
mod support;
|
|
||||||
|
|
||||||
use glam::*;
|
|
||||||
use std::f32;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_align() {
|
|
||||||
use std::mem;
|
|
||||||
assert_eq!(16, mem::size_of::<Vec4>());
|
|
||||||
assert_eq!(16, mem::size_of::<Vec4Mask>());
|
|
||||||
if cfg!(feature = "scalar-math") {
|
|
||||||
assert_eq!(4, mem::align_of::<Vec4>());
|
|
||||||
assert_eq!(4, mem::align_of::<Vec4Mask>());
|
|
||||||
} else {
|
|
||||||
assert_eq!(16, mem::align_of::<Vec4>());
|
|
||||||
assert_eq!(16, mem::align_of::<Vec4Mask>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_new() {
|
|
||||||
let v = vec4(1.0, 2.0, 3.0, 4.0);
|
|
||||||
|
|
||||||
assert_eq!(v.x(), 1.0);
|
|
||||||
assert_eq!(v.y(), 2.0);
|
|
||||||
assert_eq!(v.z(), 3.0);
|
|
||||||
assert_eq!(v.w(), 4.0);
|
|
||||||
|
|
||||||
let t = (1.0, 2.0, 3.0, 4.0);
|
|
||||||
let v = Vec4::from(t);
|
|
||||||
assert_eq!(t, v.into());
|
|
||||||
|
|
||||||
let a = [1.0, 2.0, 3.0, 4.0];
|
|
||||||
let v = Vec4::from(a);
|
|
||||||
let a1: [f32; 4] = v.into();
|
|
||||||
assert_eq!(a, a1);
|
|
||||||
|
|
||||||
let v = Vec4::new(t.0, t.1, t.2, t.3);
|
|
||||||
assert_eq!(t, v.into());
|
|
||||||
|
|
||||||
assert_eq!(Vec4::new(1.0, 0.0, 0.0, 0.0), Vec4::unit_x());
|
|
||||||
assert_eq!(Vec4::new(0.0, 1.0, 0.0, 0.0), Vec4::unit_y());
|
|
||||||
assert_eq!(Vec4::new(0.0, 0.0, 1.0, 0.0), Vec4::unit_z());
|
|
||||||
assert_eq!(Vec4::new(0.0, 0.0, 0.0, 1.0), Vec4::unit_w());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_fmt() {
|
|
||||||
let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
|
|
||||||
#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
|
|
||||||
assert_eq!(format!("{:?}", a), "Vec4(__m128(1.0, 2.0, 3.0, 4.0))");
|
|
||||||
#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
|
|
||||||
assert_eq!(format!("{:?}", a), "Vec4(1.0, 2.0, 3.0, 4.0)");
|
|
||||||
// assert_eq!(
|
|
||||||
// format!("{:#?}", a),
|
|
||||||
// "Vec4(\n 1.0,\n 2.0,\n 3.0,\n 4.0\n)"
|
|
||||||
// );
|
|
||||||
assert_eq!(format!("{}", a), "[1, 2, 3, 4]");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_zero() {
|
|
||||||
let v = Vec4::zero();
|
|
||||||
assert_eq!((0.0, 0.0, 0.0, 0.0), v.into());
|
|
||||||
assert_eq!(v, Vec4::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_splat() {
|
|
||||||
let v = Vec4::splat(1.0);
|
|
||||||
assert_eq!((1.0, 1.0, 1.0, 1.0), v.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_accessors() {
|
|
||||||
let mut a = Vec4::zero();
|
|
||||||
a.set_x(1.0);
|
|
||||||
a.set_y(2.0);
|
|
||||||
a.set_z(3.0);
|
|
||||||
a.set_w(4.0);
|
|
||||||
assert_eq!(1.0, a.x());
|
|
||||||
assert_eq!(2.0, a.y());
|
|
||||||
assert_eq!(3.0, a.z());
|
|
||||||
assert_eq!(4.0, a.w());
|
|
||||||
assert_eq!((1.0, 2.0, 3.0, 4.0), a.into());
|
|
||||||
|
|
||||||
let mut a = Vec4::zero();
|
|
||||||
*a.x_mut() = 1.0;
|
|
||||||
*a.y_mut() = 2.0;
|
|
||||||
*a.z_mut() = 3.0;
|
|
||||||
*a.w_mut() = 4.0;
|
|
||||||
assert_eq!(1.0, a.x());
|
|
||||||
assert_eq!(2.0, a.y());
|
|
||||||
assert_eq!(3.0, a.z());
|
|
||||||
assert_eq!(4.0, a.w());
|
|
||||||
assert_eq!((1.0, 2.0, 3.0, 4.0), a.into());
|
|
||||||
|
|
||||||
let mut a = Vec4::zero();
|
|
||||||
a[0] = 1.0;
|
|
||||||
a[1] = 2.0;
|
|
||||||
a[2] = 3.0;
|
|
||||||
a[3] = 4.0;
|
|
||||||
assert_eq!(1.0, a[0]);
|
|
||||||
assert_eq!(2.0, a[1]);
|
|
||||||
assert_eq!(3.0, a[2]);
|
|
||||||
assert_eq!(4.0, a[3]);
|
|
||||||
assert_eq!((1.0, 2.0, 3.0, 4.0), a.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_funcs() {
|
|
||||||
let x = vec4(1.0, 0.0, 0.0, 0.0);
|
|
||||||
let y = vec4(0.0, 1.0, 0.0, 0.0);
|
|
||||||
let z = vec4(0.0, 0.0, 1.0, 0.0);
|
|
||||||
let w = vec4(0.0, 0.0, 0.0, 1.0);
|
|
||||||
assert_eq!(1.0, x.dot(x));
|
|
||||||
assert_eq!(0.0, x.dot(y));
|
|
||||||
assert_eq!(-1.0, z.dot(-z));
|
|
||||||
assert_eq!(4.0, (2.0 * x).length_squared());
|
|
||||||
assert_eq!(9.0, (-3.0 * y).length_squared());
|
|
||||||
assert_eq!(16.0, (4.0 * z).length_squared());
|
|
||||||
assert_eq!(64.0, (8.0 * w).length_squared());
|
|
||||||
assert_eq!(2.0, (-2.0 * x).length());
|
|
||||||
assert_eq!(3.0, (3.0 * y).length());
|
|
||||||
assert_eq!(4.0, (-4.0 * z).length());
|
|
||||||
assert_eq!(5.0, (-5.0 * w).length());
|
|
||||||
assert_eq!(x, (2.0 * x).normalize());
|
|
||||||
assert_eq!(
|
|
||||||
1.0 * 5.0 + 2.0 * 6.0 + 3.0 * 7.0 + 4.0 * 8.0,
|
|
||||||
vec4(1.0, 2.0, 3.0, 4.0).dot(vec4(5.0, 6.0, 7.0, 8.0))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
2.0 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0,
|
|
||||||
vec4(2.0, 3.0, 4.0, 5.0).length_squared()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0).sqrt(),
|
|
||||||
vec4(2.0, 3.0, 4.0, 5.0).length()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
1.0 / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0).sqrt(),
|
|
||||||
vec4(2.0, 3.0, 4.0, 5.0).length_reciprocal()
|
|
||||||
);
|
|
||||||
assert!(vec4(2.0, 3.0, 4.0, 5.0).normalize().is_normalized());
|
|
||||||
assert_approx_eq!(
|
|
||||||
vec4(2.0, 3.0, 4.0, 5.0) / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0).sqrt(),
|
|
||||||
vec4(2.0, 3.0, 4.0, 5.0).normalize()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
vec4(0.5, 0.25, 0.125, 0.0625),
|
|
||||||
vec4(2.0, 4.0, 8.0, 16.0).reciprocal()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_ops() {
|
|
||||||
let a = vec4(1.0, 2.0, 3.0, 4.0);
|
|
||||||
assert_eq!((2.0, 4.0, 6.0, 8.0), (a + a).into());
|
|
||||||
assert_eq!((0.0, 0.0, 0.0, 0.0), (a - a).into());
|
|
||||||
assert_eq!((1.0, 4.0, 9.0, 16.0), (a * a).into());
|
|
||||||
assert_eq!((2.0, 4.0, 6.0, 8.0), (a * 2.0).into());
|
|
||||||
assert_eq!((2.0, 4.0, 6.0, 8.0), (2.0 * a).into());
|
|
||||||
assert_eq!((1.0, 1.0, 1.0, 1.0), (a / a).into());
|
|
||||||
assert_eq!((0.5, 1.0, 1.5, 2.0), (a / 2.0).into());
|
|
||||||
// is this a sensible operator?
|
|
||||||
// assert_eq!((1.0, 0.5, 1.0/3.0, 0.25), (1.0 / a).into());
|
|
||||||
assert_eq!((-1.0, -2.0, -3.0, -4.0), (-a).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_assign_ops() {
|
|
||||||
let a = vec4(1.0, 2.0, 3.0, 4.0);
|
|
||||||
let mut b = a;
|
|
||||||
b += a;
|
|
||||||
assert_eq!((2.0, 4.0, 6.0, 8.0), b.into());
|
|
||||||
b -= a;
|
|
||||||
assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
|
|
||||||
b *= a;
|
|
||||||
assert_eq!((1.0, 4.0, 9.0, 16.0), b.into());
|
|
||||||
b /= a;
|
|
||||||
assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
|
|
||||||
b *= 2.0;
|
|
||||||
assert_eq!((2.0, 4.0, 6.0, 8.0), b.into());
|
|
||||||
b /= 2.0;
|
|
||||||
assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_min_max() {
|
|
||||||
let a = vec4(-1.0, 2.0, -3.0, 4.0);
|
|
||||||
let b = vec4(1.0, -2.0, 3.0, -4.0);
|
|
||||||
assert_eq!((-1.0, -2.0, -3.0, -4.0), a.min(b).into());
|
|
||||||
assert_eq!((-1.0, -2.0, -3.0, -4.0), b.min(a).into());
|
|
||||||
assert_eq!((1.0, 2.0, 3.0, 4.0), a.max(b).into());
|
|
||||||
assert_eq!((1.0, 2.0, 3.0, 4.0), b.max(a).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_hmin_hmax() {
|
|
||||||
let a = vec4(-1.0, 4.0, -3.0, 2.0);
|
|
||||||
assert_eq!(-3.0, a.min_element());
|
|
||||||
assert_eq!(4.0, a.max_element());
|
|
||||||
assert_eq!(3.0, vec4(1.0, 2.0, 3.0, 4.0).truncate().max_element());
|
|
||||||
assert_eq!(-3.0, vec4(-1.0, -2.0, -3.0, -4.0).truncate().min_element());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_eq() {
|
|
||||||
let a = vec4(1.0, 1.0, 1.0, 1.0);
|
|
||||||
let b = vec4(1.0, 2.0, 3.0, 4.0);
|
|
||||||
assert!(a.cmpeq(a).all());
|
|
||||||
assert!(b.cmpeq(b).all());
|
|
||||||
assert!(a.cmpne(b).any());
|
|
||||||
assert!(b.cmpne(a).any());
|
|
||||||
assert!(b.cmpeq(a).any());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_cmp() {
|
|
||||||
assert!(!Vec4Mask::default().any());
|
|
||||||
assert!(!Vec4Mask::default().all());
|
|
||||||
assert_eq!(Vec4Mask::default().bitmask(), 0x0);
|
|
||||||
let a = vec4(-1.0, -1.0, -1.0, -1.0);
|
|
||||||
let b = vec4(1.0, 1.0, 1.0, 1.0);
|
|
||||||
let c = vec4(-1.0, -1.0, 1.0, 1.0);
|
|
||||||
let d = vec4(1.0, -1.0, -1.0, 1.0);
|
|
||||||
assert_eq!(a.cmplt(a).bitmask(), 0x0);
|
|
||||||
assert_eq!(a.cmplt(b).bitmask(), 0xf);
|
|
||||||
assert_eq!(a.cmplt(c).bitmask(), 0xc);
|
|
||||||
assert_eq!(c.cmple(a).bitmask(), 0x3);
|
|
||||||
assert_eq!(a.cmplt(d).bitmask(), 0x9);
|
|
||||||
assert!(a.cmplt(b).all());
|
|
||||||
assert!(a.cmplt(c).any());
|
|
||||||
assert!(a.cmple(b).all());
|
|
||||||
assert!(a.cmple(a).all());
|
|
||||||
assert!(b.cmpgt(a).all());
|
|
||||||
assert!(b.cmpge(a).all());
|
|
||||||
assert!(b.cmpge(b).all());
|
|
||||||
assert!(!(a.cmpge(c).all()));
|
|
||||||
assert!(c.cmple(c).all());
|
|
||||||
assert!(c.cmpge(c).all());
|
|
||||||
assert!(a.cmpeq(a).all());
|
|
||||||
assert!(!a.cmpeq(b).all());
|
|
||||||
assert!(a.cmpeq(c).any());
|
|
||||||
assert!(!a.cmpne(a).all());
|
|
||||||
assert!(a.cmpne(b).all());
|
|
||||||
assert!(a.cmpne(c).any());
|
|
||||||
assert!(a == a);
|
|
||||||
assert!(a < b);
|
|
||||||
assert!(b > a);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_slice() {
|
|
||||||
let a = [1.0, 2.0, 3.0, 4.0];
|
|
||||||
let b = Vec4::from_slice_unaligned(&a);
|
|
||||||
let c: [f32; 4] = b.into();
|
|
||||||
assert_eq!(a, c);
|
|
||||||
let mut d = [0.0, 0.0, 0.0, 0.0];
|
|
||||||
b.write_to_slice_unaligned(&mut d[..]);
|
|
||||||
assert_eq!(a, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_sign() {
|
|
||||||
assert_eq!(Vec4::zero().sign(), Vec4::one());
|
|
||||||
assert_eq!(-Vec4::zero().sign(), -Vec4::one());
|
|
||||||
assert_eq!(Vec4::one().sign(), Vec4::one());
|
|
||||||
assert_eq!((-Vec4::one()).sign(), -Vec4::one());
|
|
||||||
assert_eq!(Vec4::splat(core::f32::NEG_INFINITY).sign(), -Vec4::one());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_abs() {
|
|
||||||
assert_eq!(Vec4::zero().abs(), Vec4::zero());
|
|
||||||
assert_eq!(Vec4::one().abs(), Vec4::one());
|
|
||||||
assert_eq!((-Vec4::one()).abs(), Vec4::one());
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn dup_element() {
|
|
||||||
// let a = vec4(1.0, 2.0, 3.0, 4.0);
|
|
||||||
// assert_eq!(vec4(1.0, 1.0, 1.0, 1.0), a.dup_x());
|
|
||||||
// assert_eq!(vec4(2.0, 2.0, 2.0, 2.0), a.dup_y());
|
|
||||||
// assert_eq!(vec4(3.0, 3.0, 3.0, 3.0), a.dup_z());
|
|
||||||
// assert_eq!(vec4(4.0, 4.0, 4.0, 4.0), a.dup_w());
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_as_ref() {
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(false, false, false, false).as_ref(),
|
|
||||||
&[0, 0, 0, 0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(false, false, true, true).as_ref(),
|
|
||||||
&[0, 0, !0, !0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(true, true, false, false).as_ref(),
|
|
||||||
&[!0, !0, 0, 0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(false, true, false, true).as_ref(),
|
|
||||||
&[0, !0, 0, !0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(true, false, true, false).as_ref(),
|
|
||||||
&[!0, 0, !0, 0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(true, true, true, true).as_ref(),
|
|
||||||
&[!0, !0, !0, !0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_from() {
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 4]>::into(Vec4Mask::new(false, false, false, false)),
|
|
||||||
[0, 0, 0, 0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 4]>::into(Vec4Mask::new(false, false, true, true)),
|
|
||||||
[0, 0, !0, !0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 4]>::into(Vec4Mask::new(true, true, false, false)),
|
|
||||||
[!0, !0, 0, 0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 4]>::into(Vec4Mask::new(false, true, false, true)),
|
|
||||||
[0, !0, 0, !0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 4]>::into(Vec4Mask::new(true, false, true, false)),
|
|
||||||
[!0, 0, !0, 0]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Into::<[u32; 4]>::into(Vec4Mask::new(true, true, true, true)),
|
|
||||||
[!0, !0, !0, !0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_bitmask() {
|
|
||||||
assert_eq!(Vec4Mask::new(false, false, false, false).bitmask(), 0b0000);
|
|
||||||
assert_eq!(Vec4Mask::new(false, false, true, true).bitmask(), 0b1100);
|
|
||||||
assert_eq!(Vec4Mask::new(true, true, false, false).bitmask(), 0b0011);
|
|
||||||
assert_eq!(Vec4Mask::new(false, true, false, true).bitmask(), 0b1010);
|
|
||||||
assert_eq!(Vec4Mask::new(true, false, true, false).bitmask(), 0b0101);
|
|
||||||
assert_eq!(Vec4Mask::new(true, true, true, true).bitmask(), 0b1111);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_any() {
|
|
||||||
assert_eq!(Vec4Mask::new(false, false, false, false).any(), false);
|
|
||||||
assert_eq!(Vec4Mask::new(true, false, false, false).any(), true);
|
|
||||||
assert_eq!(Vec4Mask::new(false, true, false, false).any(), true);
|
|
||||||
assert_eq!(Vec4Mask::new(false, false, true, false).any(), true);
|
|
||||||
assert_eq!(Vec4Mask::new(false, false, false, true).any(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_all() {
|
|
||||||
assert_eq!(Vec4Mask::new(true, true, true, true).all(), true);
|
|
||||||
assert_eq!(Vec4Mask::new(false, true, true, true).all(), false);
|
|
||||||
assert_eq!(Vec4Mask::new(true, false, true, true).all(), false);
|
|
||||||
assert_eq!(Vec4Mask::new(true, true, false, true).all(), false);
|
|
||||||
assert_eq!(Vec4Mask::new(true, true, true, false).all(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_select() {
|
|
||||||
let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
|
|
||||||
let b = Vec4::new(5.0, 6.0, 7.0, 8.0);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(true, true, true, true).select(a, b),
|
|
||||||
Vec4::new(1.0, 2.0, 3.0, 4.0),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(true, false, true, false).select(a, b),
|
|
||||||
Vec4::new(1.0, 6.0, 3.0, 8.0),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(false, true, false, true).select(a, b),
|
|
||||||
Vec4::new(5.0, 2.0, 7.0, 4.0),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4Mask::new(false, false, false, false).select(a, b),
|
|
||||||
Vec4::new(5.0, 6.0, 7.0, 8.0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_and() {
|
|
||||||
assert_eq!(
|
|
||||||
(Vec4Mask::new(false, false, false, false) & Vec4Mask::new(false, false, false, false))
|
|
||||||
.bitmask(),
|
|
||||||
0b0000,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec4Mask::new(true, true, true, true) & Vec4Mask::new(true, true, true, true)).bitmask(),
|
|
||||||
0b1111,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec4Mask::new(true, false, true, false) & Vec4Mask::new(false, true, false, true))
|
|
||||||
.bitmask(),
|
|
||||||
0b0000,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec4Mask::new(true, false, true, true) & Vec4Mask::new(true, true, true, false)).bitmask(),
|
|
||||||
0b0101,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut mask = Vec4Mask::new(true, true, false, false);
|
|
||||||
mask &= Vec4Mask::new(true, false, true, false);
|
|
||||||
assert_eq!(mask.bitmask(), 0b0001);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_or() {
|
|
||||||
assert_eq!(
|
|
||||||
(Vec4Mask::new(false, false, false, false) | Vec4Mask::new(false, false, false, false))
|
|
||||||
.bitmask(),
|
|
||||||
0b0000,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec4Mask::new(true, true, true, true) | Vec4Mask::new(true, true, true, true)).bitmask(),
|
|
||||||
0b1111,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec4Mask::new(true, false, true, false) | Vec4Mask::new(false, true, false, true))
|
|
||||||
.bitmask(),
|
|
||||||
0b1111,
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
(Vec4Mask::new(true, false, true, false) | Vec4Mask::new(true, false, true, false))
|
|
||||||
.bitmask(),
|
|
||||||
0b0101,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut mask = Vec4Mask::new(true, true, false, false);
|
|
||||||
mask |= Vec4Mask::new(true, false, true, false);
|
|
||||||
assert_eq!(mask.bitmask(), 0b0111);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_not() {
|
|
||||||
assert_eq!(
|
|
||||||
(!Vec4Mask::new(false, false, false, false)).bitmask(),
|
|
||||||
0b1111
|
|
||||||
);
|
|
||||||
assert_eq!((!Vec4Mask::new(true, true, true, true)).bitmask(), 0b0000);
|
|
||||||
assert_eq!((!Vec4Mask::new(true, false, true, false)).bitmask(), 0b1010);
|
|
||||||
assert_eq!((!Vec4Mask::new(false, true, false, true)).bitmask(), 0b0101);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_fmt() {
|
|
||||||
let a = Vec4Mask::new(true, false, true, false);
|
|
||||||
|
|
||||||
#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
|
|
||||||
assert_eq!(format!("{}", a), "[true, false, true, false]");
|
|
||||||
#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", a),
|
|
||||||
"Vec4Mask(0xffffffff, 0x0, 0xffffffff, 0x0)"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
|
|
||||||
assert_eq!(format!("{}", a), "[true, false, true, false]");
|
|
||||||
#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", a),
|
|
||||||
"Vec4Mask(0xffffffff, 0x0, 0xffffffff, 0x0)"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_eq() {
|
|
||||||
let a = Vec4Mask::new(true, false, true, false);
|
|
||||||
let b = Vec4Mask::new(true, false, true, false);
|
|
||||||
let c = Vec4Mask::new(false, true, true, false);
|
|
||||||
|
|
||||||
assert_eq!(a, b);
|
|
||||||
assert_eq!(b, a);
|
|
||||||
assert_ne!(a, c);
|
|
||||||
assert_ne!(b, c);
|
|
||||||
|
|
||||||
assert!(a > c);
|
|
||||||
assert!(c < a);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4mask_hash() {
|
|
||||||
use std::{
|
|
||||||
collections::hash_map::DefaultHasher,
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
};
|
|
||||||
|
|
||||||
let a = Vec4Mask::new(true, false, true, false);
|
|
||||||
let b = Vec4Mask::new(true, false, true, false);
|
|
||||||
let c = Vec4Mask::new(false, true, true, false);
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
a.hash(&mut hasher);
|
|
||||||
let a_hashed = hasher.finish();
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
b.hash(&mut hasher);
|
|
||||||
let b_hashed = hasher.finish();
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
c.hash(&mut hasher);
|
|
||||||
let c_hashed = hasher.finish();
|
|
||||||
|
|
||||||
assert_eq!(a, b);
|
|
||||||
assert_eq!(a_hashed, b_hashed);
|
|
||||||
assert_ne!(a, c);
|
|
||||||
assert_ne!(a_hashed, c_hashed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_round() {
|
|
||||||
assert_eq!(Vec4::new(1.35, 0.0, 0.0, 0.0).round().x(), 1.0);
|
|
||||||
assert_eq!(Vec4::new(0.0, 1.5, 0.0, 0.0).round().y(), 2.0);
|
|
||||||
assert_eq!(Vec4::new(0.0, 0.0, -15.5, 0.0).round().z(), -16.0);
|
|
||||||
assert_eq!(Vec4::new(0.0, 0.0, 0.0, 0.0).round().z(), 0.0);
|
|
||||||
assert_eq!(Vec4::new(0.0, 21.1, 0.0, 0.0).round().y(), 21.0);
|
|
||||||
assert_eq!(Vec4::new(0.0, 0.0, 0.0, 11.123).round().w(), 11.0);
|
|
||||||
assert_eq!(Vec4::new(0.0, 0.0, 11.501, 0.0).round().z(), 12.0);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4::new(f32::NEG_INFINITY, f32::INFINITY, 1.0, -1.0).round(),
|
|
||||||
Vec4::new(f32::NEG_INFINITY, f32::INFINITY, 1.0, -1.0)
|
|
||||||
);
|
|
||||||
assert!(Vec4::new(f32::NAN, 0.0, 0.0, 1.0).round().x().is_nan());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_floor() {
|
|
||||||
assert_eq!(
|
|
||||||
Vec4::new(1.35, 1.5, -1.5, 1.999).floor(),
|
|
||||||
Vec4::new(1.0, 1.0, -2.0, 1.0)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4::new(f32::INFINITY, f32::NEG_INFINITY, 0.0, 0.0).floor(),
|
|
||||||
Vec4::new(f32::INFINITY, f32::NEG_INFINITY, 0.0, 0.0)
|
|
||||||
);
|
|
||||||
assert!(Vec4::new(0.0, f32::NAN, 0.0, 0.0).floor().y().is_nan());
|
|
||||||
assert_eq!(
|
|
||||||
Vec4::new(-0.0, -2000000.123, 10000000.123, 1000.9).floor(),
|
|
||||||
Vec4::new(-0.0, -2000001.0, 10000000.0, 1000.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_ceil() {
|
|
||||||
assert_eq!(
|
|
||||||
Vec4::new(1.35, 1.5, -1.5, 1234.1234).ceil(),
|
|
||||||
Vec4::new(2.0, 2.0, -1.0, 1235.0)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
Vec4::new(f32::INFINITY, f32::NEG_INFINITY, 0.0, 0.0).ceil(),
|
|
||||||
Vec4::new(f32::INFINITY, f32::NEG_INFINITY, 0.0, 0.0)
|
|
||||||
);
|
|
||||||
assert!(Vec4::new(0.0, 0.0, f32::NAN, 0.0).ceil().z().is_nan());
|
|
||||||
assert_eq!(
|
|
||||||
Vec4::new(-1234.1234, -2000000.123, 1000000.123, 1000.9).ceil(),
|
|
||||||
Vec4::new(-1234.0, -2000000.0, 1000001.0, 1001.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_lerp() {
|
|
||||||
let v0 = Vec4::new(-1.0, -1.0, -1.0, -1.0);
|
|
||||||
let v1 = Vec4::new(1.0, 1.0, 1.0, 1.0);
|
|
||||||
assert_approx_eq!(v0, v0.lerp(v1, 0.0));
|
|
||||||
assert_approx_eq!(v1, v0.lerp(v1, 1.0));
|
|
||||||
assert_approx_eq!(Vec4::zero(), v0.lerp(v1, 0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_to_from_slice() {
|
|
||||||
let v = Vec4::new(1.0, 2.0, 3.0, 4.0);
|
|
||||||
let mut a = [0.0, 0.0, 0.0, 0.0];
|
|
||||||
v.write_to_slice_unaligned(&mut a);
|
|
||||||
assert_eq!(v, Vec4::from_slice_unaligned(&a));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_serde() {
|
|
||||||
let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
|
|
||||||
let serialized = serde_json::to_string(&a).unwrap();
|
|
||||||
assert_eq!(serialized, "[1.0,2.0,3.0,4.0]");
|
|
||||||
let deserialized = serde_json::from_str(&serialized).unwrap();
|
|
||||||
assert_eq!(a, deserialized);
|
|
||||||
let deserialized = serde_json::from_str::<Vec4>("[]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Vec4>("[1.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Vec4>("[1.0,2.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Vec4>("[1.0,2.0,3.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
let deserialized = serde_json::from_str::<Vec4>("[1.0,2.0,3.0,4.0,5.0]");
|
|
||||||
assert!(deserialized.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
|
||||||
#[test]
|
|
||||||
fn test_vec4_rand() {
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use rand_xoshiro::Xoshiro256Plus;
|
|
||||||
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let a: (f32, f32, f32, f32) = rng1.gen();
|
|
||||||
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
|
|
||||||
let b: Vec4 = rng2.gen();
|
|
||||||
assert_eq!(a, b.into());
|
|
||||||
}
|
|
|
@ -16,5 +16,4 @@ bevy_transform = { path = "../bevy_transform" }
|
||||||
bevy_window = { path = "../bevy_window" }
|
bevy_window = { path = "../bevy_window" }
|
||||||
legion = { path = "../bevy_legion"}
|
legion = { path = "../bevy_legion"}
|
||||||
|
|
||||||
zerocopy = "0.3"
|
glam = "0.8.7"
|
||||||
glam = { path = "../bevy_glam" }
|
|
|
@ -3,7 +3,7 @@ use bevy_transform::components::Translation;
|
||||||
use bevy_property::Properties;
|
use bevy_property::Properties;
|
||||||
use glam::Mat4;
|
use glam::Mat4;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use zerocopy::{AsBytes, FromBytes};
|
use bevy_core::bytes::Byteable;
|
||||||
|
|
||||||
#[derive(Properties)]
|
#[derive(Properties)]
|
||||||
pub struct Light {
|
pub struct Light {
|
||||||
|
@ -23,13 +23,15 @@ impl Default for Light {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, AsBytes, FromBytes)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct LightRaw {
|
pub struct LightRaw {
|
||||||
pub proj: [[f32; 4]; 4],
|
pub proj: [[f32; 4]; 4],
|
||||||
pub pos: [f32; 4],
|
pub pos: [f32; 4],
|
||||||
pub color: [f32; 4],
|
pub color: [f32; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl Byteable for LightRaw {}
|
||||||
|
|
||||||
impl LightRaw {
|
impl LightRaw {
|
||||||
pub fn from(light: &Light, transform: &Mat4, translation: &Translation) -> LightRaw {
|
pub fn from(light: &Light, transform: &Mat4, translation: &Translation) -> LightRaw {
|
||||||
let perspective = PerspectiveProjection {
|
let perspective = PerspectiveProjection {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
use legion::prelude::*;
|
use legion::prelude::*;
|
||||||
use zerocopy::AsBytes;
|
use bevy_core::bytes::{Byteable, AsBytes};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct LightsNode {
|
pub struct LightsNode {
|
||||||
|
@ -43,11 +43,13 @@ impl Node for LightsNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, AsBytes)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct LightCount {
|
pub struct LightCount {
|
||||||
pub num_lights: [u32; 4],
|
pub num_lights: [u32; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl Byteable for LightCount {}
|
||||||
|
|
||||||
impl SystemNode for LightsNode {
|
impl SystemNode for LightsNode {
|
||||||
fn get_system(&self) -> Box<dyn Schedulable> {
|
fn get_system(&self) -> Box<dyn Schedulable> {
|
||||||
let mut light_buffer = None;
|
let mut light_buffer = None;
|
||||||
|
|
|
@ -9,6 +9,6 @@ serde = "1"
|
||||||
erased-serde = "0.3"
|
erased-serde = "0.3"
|
||||||
bevy_property_derive = { path = "bevy_property_derive" }
|
bevy_property_derive = { path = "bevy_property_derive" }
|
||||||
ron = { git = "https://github.com/ron-rs/ron", rev = "35355ba7eb495f07282162826c29873154c2fa14" }
|
ron = { git = "https://github.com/ron-rs/ron", rev = "35355ba7eb495f07282162826c29873154c2fa14" }
|
||||||
glam = { path = "../bevy_glam", features = ["serde"] }
|
glam = { version = "0.8.7", features = ["serde"] }
|
||||||
legion = { path = "../bevy_legion" }
|
legion = { path = "../bevy_legion" }
|
||||||
smallvec = { version = "1.4", features = ["serde"] }
|
smallvec = { version = "1.4", features = ["serde"] }
|
|
@ -26,9 +26,8 @@ png = "0.16.0"
|
||||||
# misc
|
# misc
|
||||||
log = { version = "0.4", features = ["release_max_level_info"] }
|
log = { version = "0.4", features = ["release_max_level_info"] }
|
||||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
uuid = { version = "0.8", features = ["v4", "serde"] }
|
||||||
glam = { path = "../bevy_glam" }
|
glam = "0.8.7"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
zerocopy = "0.3"
|
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
smallvec = "1.4.0"
|
smallvec = "1.4.0"
|
||||||
# TODO: replace once_cell with std equivalent if/when this lands: https://github.com/rust-lang/rfcs/pull/2788
|
# TODO: replace once_cell with std equivalent if/when this lands: https://github.com/rust-lang/rfcs/pull/2788
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
use super::texture::Texture;
|
use super::texture::Texture;
|
||||||
use crate::shader::ShaderDefSuffixProvider;
|
use crate::shader::ShaderDefSuffixProvider;
|
||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
use bevy_core::bytes::Bytes;
|
use bevy_core::bytes::{Byteable, Bytes};
|
||||||
use bevy_property::Property;
|
use bevy_property::Property;
|
||||||
use glam::Vec4;
|
use glam::Vec4;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
use zerocopy::AsBytes;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, AsBytes, Serialize, Deserialize, Property)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Property)]
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
pub r: f32,
|
pub r: f32,
|
||||||
pub g: f32,
|
pub g: f32,
|
||||||
|
@ -17,6 +16,8 @@ pub struct Color {
|
||||||
pub a: f32,
|
pub a: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl Byteable for Color {}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
pub const WHITE: Color = Color::rgb(1.0, 1.0, 1.0);
|
pub const WHITE: Color = Color::rgb(1.0, 1.0, 1.0);
|
||||||
pub const BLACK: Color = Color::rgb(0.0, 1.0, 0.0);
|
pub const BLACK: Color = Color::rgb(0.0, 1.0, 0.0);
|
||||||
|
@ -91,15 +92,6 @@ impl Into<[f32; 4]> for Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bytes for Color {
|
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
|
||||||
buffer[0..self.byte_len()].copy_from_slice(self.as_bytes())
|
|
||||||
}
|
|
||||||
fn byte_len(&self) -> usize {
|
|
||||||
std::mem::size_of::<Self>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Bytes for ColorSource {
|
impl Bytes for ColorSource {
|
||||||
fn write_bytes(&self, buffer: &mut [u8]) {
|
fn write_bytes(&self, buffer: &mut [u8]) {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
|
@ -12,9 +12,9 @@ use bevy_app::{GetEventReader, Events};
|
||||||
use bevy_asset::{AssetEvent, Assets, Handle};
|
use bevy_asset::{AssetEvent, Assets, Handle};
|
||||||
use glam::*;
|
use glam::*;
|
||||||
use legion::prelude::*;
|
use legion::prelude::*;
|
||||||
|
use bevy_core::bytes::{Byteable, AsBytes};
|
||||||
use std::{borrow::Cow, collections::HashSet};
|
use std::{borrow::Cow, collections::HashSet};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use zerocopy::AsBytes;
|
|
||||||
|
|
||||||
pub const VERTEX_BUFFER_ASSET_INDEX: usize = 0;
|
pub const VERTEX_BUFFER_ASSET_INDEX: usize = 0;
|
||||||
pub const INDEX_BUFFER_ASSET_INDEX: usize = 1;
|
pub const INDEX_BUFFER_ASSET_INDEX: usize = 1;
|
||||||
|
@ -37,12 +37,13 @@ impl VertexAttributeValues {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add vertex format as parameter here and perform type conversions
|
// TODO: add vertex format as parameter here and perform type conversions
|
||||||
pub fn get_bytes(&self) -> &[u8] {
|
// TODO: make this zero-copy if possible. how did zerocopy implement AsBytes for Vec<T>?
|
||||||
match *self {
|
pub fn get_bytes(&self) -> Vec<u8> {
|
||||||
VertexAttributeValues::Float(ref values) => values.as_bytes(),
|
match self {
|
||||||
VertexAttributeValues::Float2(ref values) => values.as_bytes(),
|
VertexAttributeValues::Float(values) => values.as_slice().as_bytes().to_vec(),
|
||||||
VertexAttributeValues::Float3(ref values) => values.as_bytes(),
|
VertexAttributeValues::Float2(values) => values.as_slice().as_bytes().to_vec(),
|
||||||
VertexAttributeValues::Float4(ref values) => values.as_bytes(),
|
VertexAttributeValues::Float3(values) => values.as_slice().as_bytes().to_vec(),
|
||||||
|
VertexAttributeValues::Float4(values) => values.as_slice().as_bytes().to_vec(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,9 +160,10 @@ impl Mesh {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| *i as u16)
|
.map(|i| *i as u16)
|
||||||
.collect::<Vec<u16>>()
|
.collect::<Vec<u16>>()
|
||||||
|
.as_slice()
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.to_vec(),
|
.to_vec(),
|
||||||
IndexFormat::Uint32 => indices.as_bytes().to_vec(),
|
IndexFormat::Uint32 => indices.as_slice().as_bytes().to_vec(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,7 +433,7 @@ pub fn mesh_resource_provider_system(resources: &mut Resources) -> Box<dyn Sched
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Mesh, VertexAttribute};
|
use super::{Mesh, VertexAttribute};
|
||||||
use crate::{pipeline::state_descriptors::PrimitiveTopology, shader::AsUniforms, Vertex};
|
use crate::{pipeline::state_descriptors::PrimitiveTopology, shader::AsUniforms, Vertex};
|
||||||
use zerocopy::AsBytes;
|
use bevy_core::bytes::AsBytes;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_vertex_bytes() {
|
fn test_get_vertex_bytes() {
|
||||||
|
|
|
@ -6,11 +6,11 @@ use crate::{
|
||||||
renderer::{RenderContext, RenderResources},
|
renderer::{RenderContext, RenderResources},
|
||||||
Camera,
|
Camera,
|
||||||
};
|
};
|
||||||
|
use bevy_core::bytes::AsBytes;
|
||||||
|
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
use legion::prelude::*;
|
use legion::prelude::*;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use zerocopy::AsBytes;
|
|
||||||
|
|
||||||
pub struct CameraNode {
|
pub struct CameraNode {
|
||||||
command_queue: CommandQueue,
|
command_queue: CommandQueue,
|
||||||
|
@ -76,8 +76,8 @@ impl SystemNode for CameraNode {
|
||||||
.iter(world)
|
.iter(world)
|
||||||
.find(|(camera, _)| camera.name.as_ref().map(|n| n.as_str()) == Some(&uniform_name))
|
.find(|(camera, _)| camera.name.as_ref().map(|n| n.as_str()) == Some(&uniform_name))
|
||||||
{
|
{
|
||||||
let camera_matrix: [[f32; 4]; 4] =
|
let camera_matrix: [f32; 16] =
|
||||||
(camera.view_matrix * local_to_world.value).to_cols_array_2d();
|
(camera.view_matrix * local_to_world.value).to_cols_array();
|
||||||
|
|
||||||
let tmp_buffer = render_resources.create_buffer_mapped(
|
let tmp_buffer = render_resources.create_buffer_mapped(
|
||||||
BufferInfo {
|
BufferInfo {
|
||||||
|
|
|
@ -12,8 +12,8 @@ use spirv_reflect::{
|
||||||
},
|
},
|
||||||
ShaderModule,
|
ShaderModule,
|
||||||
};
|
};
|
||||||
|
use bevy_core::bytes::AsBytes;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use zerocopy::AsBytes;
|
|
||||||
// use rspirv::{binary::Parser, dr::Loader, lift::LiftContext};
|
// use rspirv::{binary::Parser, dr::Loader, lift::LiftContext};
|
||||||
|
|
||||||
// TODO: use rspirv when structured representation is ready. this way we can remove spirv_reflect, which is a non-rust dependency
|
// TODO: use rspirv when structured representation is ready. this way we can remove spirv_reflect, which is a non-rust dependency
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use zerocopy::AsBytes;
|
|
||||||
use bevy_derive::Uniforms;
|
use bevy_derive::Uniforms;
|
||||||
|
use bevy_core::bytes::Byteable;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, AsBytes, Uniforms)]
|
#[derive(Clone, Copy, Uniforms)]
|
||||||
#[module(bevy_render = "crate")]
|
#[module(bevy_render = "crate")]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
#[uniform(vertex)]
|
#[uniform(vertex)]
|
||||||
|
@ -12,3 +12,7 @@ pub struct Vertex {
|
||||||
#[uniform(vertex)]
|
#[uniform(vertex)]
|
||||||
pub uv: [f32; 2],
|
pub uv: [f32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// SAFE: Vertex is repr(C) containing primitives
|
||||||
|
unsafe impl Byteable for Vertex {}
|
|
@ -14,5 +14,5 @@ bevy_type_registry = { path = "../bevy_type_registry" }
|
||||||
bevy_derive = { path = "../bevy_derive" }
|
bevy_derive = { path = "../bevy_derive" }
|
||||||
bevy_render = { path = "../bevy_render" }
|
bevy_render = { path = "../bevy_render" }
|
||||||
bevy_transform = { path = "../bevy_transform" }
|
bevy_transform = { path = "../bevy_transform" }
|
||||||
glam = { path = "../bevy_glam" }
|
glam = "0.8.7"
|
||||||
legion = { path = "../bevy_legion", features = ["serialize"] }
|
legion = { path = "../bevy_legion", features = ["serialize"] }
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
render::SPRITE_PIPELINE_HANDLE, sprite::Sprite, ColorMaterial, Quad, QUAD_HANDLE,
|
render::SPRITE_PIPELINE_HANDLE, sprite::Sprite, ColorMaterial, Quad, QUAD_HANDLE, SpriteSheet, SPRITE_SHEET_PIPELINE_HANDLE,
|
||||||
};
|
};
|
||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
use bevy_derive::EntityArchetype;
|
use bevy_derive::EntityArchetype;
|
||||||
use bevy_render::{mesh::Mesh, Renderable};
|
use bevy_render::{mesh::Mesh, Renderable};
|
||||||
|
use bevy_transform::prelude::*;
|
||||||
|
|
||||||
#[derive(EntityArchetype)]
|
#[derive(EntityArchetype)]
|
||||||
pub struct SpriteEntity {
|
pub struct SpriteEntity {
|
||||||
|
@ -16,7 +17,7 @@ pub struct SpriteEntity {
|
||||||
|
|
||||||
impl Default for SpriteEntity {
|
impl Default for SpriteEntity {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SpriteEntity {
|
Self {
|
||||||
sprite: Default::default(),
|
sprite: Default::default(),
|
||||||
quad: Default::default(),
|
quad: Default::default(),
|
||||||
mesh: QUAD_HANDLE,
|
mesh: QUAD_HANDLE,
|
||||||
|
@ -28,3 +29,31 @@ impl Default for SpriteEntity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(EntityArchetype)]
|
||||||
|
pub struct SpriteSheetEntity {
|
||||||
|
pub sprite: Sprite,
|
||||||
|
pub sprite_sheet: Handle<SpriteSheet>,
|
||||||
|
pub renderable: Renderable,
|
||||||
|
pub local_to_world: LocalToWorld,
|
||||||
|
pub translation: Translation,
|
||||||
|
pub rotation: Rotation,
|
||||||
|
pub scale: Scale,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SpriteSheetEntity {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
sprite: Default::default(),
|
||||||
|
sprite_sheet: Default::default(),
|
||||||
|
renderable: Renderable {
|
||||||
|
pipelines: vec![SPRITE_SHEET_PIPELINE_HANDLE],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
local_to_world: Default::default(),
|
||||||
|
translation: Default::default(),
|
||||||
|
rotation: Default::default(),
|
||||||
|
scale: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
mod color_material;
|
mod color_material;
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
mod quad;
|
mod quad;
|
||||||
|
mod rect;
|
||||||
mod render;
|
mod render;
|
||||||
mod sprite;
|
mod sprite;
|
||||||
mod sprite_sheet;
|
mod sprite_sheet;
|
||||||
|
|
||||||
pub use color_material::*;
|
pub use color_material::*;
|
||||||
pub use quad::*;
|
pub use quad::*;
|
||||||
|
pub use rect::*;
|
||||||
pub use render::*;
|
pub use render::*;
|
||||||
pub use sprite::*;
|
pub use sprite::*;
|
||||||
pub use sprite_sheet::*;
|
pub use sprite_sheet::*;
|
||||||
|
@ -30,10 +32,15 @@ pub const QUAD_HANDLE: Handle<Mesh> = Handle::from_u128(142404619811301375266013
|
||||||
impl AppPlugin for SpritePlugin {
|
impl AppPlugin for SpritePlugin {
|
||||||
fn build(&self, app: &mut AppBuilder) {
|
fn build(&self, app: &mut AppBuilder) {
|
||||||
app.add_asset::<ColorMaterial>()
|
app.add_asset::<ColorMaterial>()
|
||||||
|
.add_asset::<SpriteSheet>()
|
||||||
.add_system_to_stage(stage::POST_UPDATE, sprite_system())
|
.add_system_to_stage(stage::POST_UPDATE, sprite_system())
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
stage::POST_UPDATE,
|
stage::POST_UPDATE,
|
||||||
asset_shader_def_system::<ColorMaterial>.system(),
|
asset_shader_def_system::<ColorMaterial>.system(),
|
||||||
|
)
|
||||||
|
.init_system_to_stage(
|
||||||
|
bevy_render::stage::RENDER_RESOURCE,
|
||||||
|
sprite_sheet_resource_provider_system,
|
||||||
);
|
);
|
||||||
|
|
||||||
let resources = app.resources();
|
let resources = app.resources();
|
||||||
|
|
|
@ -1,20 +1,9 @@
|
||||||
use bevy_core::bytes::GetBytes;
|
use bevy_derive::{Uniform, Bytes};
|
||||||
use bevy_derive::Uniform;
|
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use zerocopy::AsBytes;
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Default, Clone, Copy, Debug, Uniform, AsBytes)]
|
#[derive(Default, Clone, Copy, Debug, Uniform, Bytes)]
|
||||||
pub struct Quad {
|
pub struct Quad {
|
||||||
pub position: Vec2,
|
pub position: Vec2,
|
||||||
pub size: Vec2,
|
pub size: Vec2,
|
||||||
pub z_index: f32,
|
pub z_index: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetBytes for Quad {
|
|
||||||
fn get_bytes(&self) -> Vec<u8> {
|
|
||||||
self.as_bytes().to_vec()
|
|
||||||
}
|
|
||||||
fn get_bytes_ref(&self) -> Option<&[u8]> {
|
|
||||||
Some(self.as_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,21 @@
|
||||||
|
use glam::Vec2;
|
||||||
|
|
||||||
|
/// A rectangle defined by two points. There is no defined origin, so 0,0 could be anywhere (top-left, bottom-left, etc)
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy, Debug)]
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
|
/// The beginning point of the rect
|
||||||
pub min: Vec2,
|
pub min: Vec2,
|
||||||
|
/// The ending point of the rect
|
||||||
pub max: Vec2,
|
pub max: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rect {
|
impl Rect {
|
||||||
pub fn width(&self) -> f32 {
|
pub fn width(&self) -> f32 {
|
||||||
self.max.x - self.min.x
|
self.max.x() - self.min.x()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> f32 {
|
pub fn height(&self) -> f32 {
|
||||||
self.max.y - self.min.y
|
self.max.y() - self.min.y()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,6 +16,54 @@ use legion::prelude::Resources;
|
||||||
pub const SPRITE_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
|
pub const SPRITE_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
|
||||||
Handle::from_u128(278534784033876544639935131272264723170);
|
Handle::from_u128(278534784033876544639935131272264723170);
|
||||||
|
|
||||||
|
pub const SPRITE_SHEET_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
|
||||||
|
Handle::from_u128(90168858051802816124217444474933884151);
|
||||||
|
|
||||||
|
pub fn build_sprite_sheet_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
|
||||||
|
PipelineDescriptor {
|
||||||
|
rasterization_state: Some(RasterizationStateDescriptor {
|
||||||
|
front_face: FrontFace::Ccw,
|
||||||
|
cull_mode: CullMode::None,
|
||||||
|
depth_bias: 0,
|
||||||
|
depth_bias_slope_scale: 0.0,
|
||||||
|
depth_bias_clamp: 0.0,
|
||||||
|
}),
|
||||||
|
depth_stencil_state: Some(DepthStencilStateDescriptor {
|
||||||
|
format: TextureFormat::Depth32Float,
|
||||||
|
depth_write_enabled: true,
|
||||||
|
depth_compare: CompareFunction::Less,
|
||||||
|
stencil_front: StencilStateFaceDescriptor::IGNORE,
|
||||||
|
stencil_back: StencilStateFaceDescriptor::IGNORE,
|
||||||
|
stencil_read_mask: 0,
|
||||||
|
stencil_write_mask: 0,
|
||||||
|
}),
|
||||||
|
color_states: vec![ColorStateDescriptor {
|
||||||
|
format: TextureFormat::Bgra8UnormSrgb,
|
||||||
|
color_blend: BlendDescriptor {
|
||||||
|
src_factor: BlendFactor::SrcAlpha,
|
||||||
|
dst_factor: BlendFactor::OneMinusSrcAlpha,
|
||||||
|
operation: BlendOperation::Add,
|
||||||
|
},
|
||||||
|
alpha_blend: BlendDescriptor {
|
||||||
|
src_factor: BlendFactor::One,
|
||||||
|
dst_factor: BlendFactor::One,
|
||||||
|
operation: BlendOperation::Add,
|
||||||
|
},
|
||||||
|
write_mask: ColorWrite::ALL,
|
||||||
|
}],
|
||||||
|
..PipelineDescriptor::new(ShaderStages {
|
||||||
|
vertex: shaders.add(Shader::from_glsl(
|
||||||
|
ShaderStage::Vertex,
|
||||||
|
include_str!("sprite_sheet.vert"),
|
||||||
|
)),
|
||||||
|
fragment: Some(shaders.add(Shader::from_glsl(
|
||||||
|
ShaderStage::Fragment,
|
||||||
|
include_str!("sprite_sheet.frag"),
|
||||||
|
))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_sprite_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
|
pub fn build_sprite_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
|
||||||
PipelineDescriptor {
|
PipelineDescriptor {
|
||||||
rasterization_state: Some(RasterizationStateDescriptor {
|
rasterization_state: Some(RasterizationStateDescriptor {
|
||||||
|
@ -86,6 +134,7 @@ impl SpriteRenderGraphBuilder for RenderGraph {
|
||||||
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
|
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
|
||||||
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
|
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
|
||||||
pipelines.set(SPRITE_PIPELINE_HANDLE, build_sprite_pipeline(&mut shaders));
|
pipelines.set(SPRITE_PIPELINE_HANDLE, build_sprite_pipeline(&mut shaders));
|
||||||
|
pipelines.set(SPRITE_SHEET_PIPELINE_HANDLE, build_sprite_sheet_pipeline(&mut shaders));
|
||||||
let main_pass: &mut PassNode = self
|
let main_pass: &mut PassNode = self
|
||||||
.get_node_mut(base_render_graph::node::MAIN_PASS)
|
.get_node_mut(base_render_graph::node::MAIN_PASS)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -93,6 +142,10 @@ impl SpriteRenderGraphBuilder for RenderGraph {
|
||||||
SPRITE_PIPELINE_HANDLE,
|
SPRITE_PIPELINE_HANDLE,
|
||||||
vec![Box::new(AssignedMeshesDrawTarget)],
|
vec![Box::new(AssignedMeshesDrawTarget)],
|
||||||
);
|
);
|
||||||
|
main_pass.add_pipeline(
|
||||||
|
SPRITE_SHEET_PIPELINE_HANDLE,
|
||||||
|
vec![Box::new(AssignedMeshesDrawTarget)],
|
||||||
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
crates/bevy_sprite/src/render/sprite_sheet.frag
Normal file
14
crates/bevy_sprite/src/render/sprite_sheet.frag
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 v_Uv;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 o_Target;
|
||||||
|
|
||||||
|
layout(set = 1, binding = 1) uniform texture2D SpriteSheet_texture;
|
||||||
|
layout(set = 1, binding = 2) uniform sampler SpriteSheet_texture_sampler;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
o_Target = texture(
|
||||||
|
sampler2D(SpriteSheet_texture, SpriteSheet_texture_sampler),
|
||||||
|
v_Uv);;
|
||||||
|
}
|
42
crates/bevy_sprite/src/render/sprite_sheet.vert
Normal file
42
crates/bevy_sprite/src/render/sprite_sheet.vert
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
// sprite
|
||||||
|
layout(location = 0) in vec3 Sprite_Position;
|
||||||
|
// this is a vec2 instead of an int due to WebGPU limitations
|
||||||
|
layout(location = 1) in ivec2 Sprite_Index;
|
||||||
|
|
||||||
|
layout(location = 0) out vec2 v_Uv;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform Camera2d {
|
||||||
|
mat4 ViewProj;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rect {
|
||||||
|
vec2 begin;
|
||||||
|
vec2 end;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(set = 1, binding = 0) buffer SpriteSheet {
|
||||||
|
Rect[] SpriteSheet_sprites;
|
||||||
|
};
|
||||||
|
|
||||||
|
const vec2 positions[4] = vec2[](
|
||||||
|
vec2(0.5, -0.5),
|
||||||
|
vec2(-0.5, -0.5),
|
||||||
|
vec2(0.5, 0.5),
|
||||||
|
vec2(-0.5, 0.5)
|
||||||
|
);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Rect sprite_rect = SpriteSheet_sprites[Sprite_Index.x];
|
||||||
|
vec2 dimensions = sprite_rect.end - sprite_rect.begin;
|
||||||
|
vec2 vertex_position = positions[gl_VertexIndex] * dimensions;
|
||||||
|
vec2 uvs[4] = vec2[](
|
||||||
|
vec2(sprite_rect.end.x, sprite_rect.begin.y),
|
||||||
|
sprite_rect.begin,
|
||||||
|
sprite_rect.end,
|
||||||
|
vec2(sprite_rect.begin.x, sprite_rect.end.y)
|
||||||
|
);
|
||||||
|
v_Uv = uvs[gl_VertexIndex];
|
||||||
|
gl_Position = ViewProj * vec4(vec3(vertex_position, 0.0) + Sprite_Position, 1.0);
|
||||||
|
}
|
|
@ -1,8 +1,112 @@
|
||||||
use crate::Quad;
|
use crate::Rect;
|
||||||
use bevy_asset::Handle;
|
use bevy_app::{Events, GetEventReader};
|
||||||
use bevy_render::texture::Texture;
|
use bevy_asset::{AssetEvent, Assets, Handle};
|
||||||
|
use bevy_derive::{Uniform, Bytes};
|
||||||
|
use bevy_render::{
|
||||||
|
render_resource::{BufferInfo, BufferUsage, RenderResourceAssignment, ResourceInfo},
|
||||||
|
renderer::{RenderResourceContext, RenderResources},
|
||||||
|
texture::Texture,
|
||||||
|
Renderable,
|
||||||
|
};
|
||||||
|
use legion::prelude::*;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use glam::{Vec4, Vec3};
|
||||||
|
|
||||||
pub struct SpriteSheet {
|
pub struct SpriteSheet {
|
||||||
pub texture: Handle<Texture>,
|
pub texture: Handle<Texture>,
|
||||||
pub sprites: Vec<Quad>,
|
pub sprites: Vec<Rect>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Uniform, Bytes)]
|
||||||
|
pub struct SpriteSheetSprite {
|
||||||
|
#[uniform(vertex)]
|
||||||
|
pub postition: Vec4,
|
||||||
|
#[uniform(vertex)]
|
||||||
|
pub index: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SPRITE_SHEET_BUFFER_ASSET_INDEX: usize = 0;
|
||||||
|
|
||||||
|
fn remove_sprite_sheet_resource(
|
||||||
|
render_resources: &dyn RenderResourceContext,
|
||||||
|
handle: Handle<SpriteSheet>,
|
||||||
|
) {
|
||||||
|
if let Some(resource) =
|
||||||
|
render_resources.get_asset_resource(handle, SPRITE_SHEET_BUFFER_ASSET_INDEX)
|
||||||
|
{
|
||||||
|
render_resources.remove_buffer(resource);
|
||||||
|
render_resources.remove_asset_resource(handle, SPRITE_SHEET_BUFFER_ASSET_INDEX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sprite_sheet_resource_provider_system(resources: &mut Resources) -> Box<dyn Schedulable> {
|
||||||
|
let mut sprite_sheet_event_reader = resources.get_event_reader::<AssetEvent<SpriteSheet>>();
|
||||||
|
(move |world: &mut SubWorld,
|
||||||
|
render_resources: Res<RenderResources>,
|
||||||
|
sprite_sheets: Res<Assets<SpriteSheet>>,
|
||||||
|
sprite_sheet_events: Res<Events<AssetEvent<SpriteSheet>>>,
|
||||||
|
query: &mut Query<(Read<Handle<SpriteSheet>>, Write<Renderable>)>| {
|
||||||
|
let render_resources = &*render_resources.context;
|
||||||
|
let mut changed_sprite_sheets = HashSet::new();
|
||||||
|
for event in sprite_sheet_event_reader.iter(&sprite_sheet_events) {
|
||||||
|
match event {
|
||||||
|
AssetEvent::Created { handle } => {
|
||||||
|
changed_sprite_sheets.insert(*handle);
|
||||||
|
}
|
||||||
|
AssetEvent::Modified { handle } => {
|
||||||
|
changed_sprite_sheets.insert(*handle);
|
||||||
|
remove_sprite_sheet_resource(render_resources, *handle);
|
||||||
|
}
|
||||||
|
AssetEvent::Removed { handle } => {
|
||||||
|
remove_sprite_sheet_resource(render_resources, *handle);
|
||||||
|
// if sprite sheet was modified and removed in the same update, ignore the modification
|
||||||
|
// events are ordered so future modification events are ok
|
||||||
|
changed_sprite_sheets.remove(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for changed_sprite_sheet_handle in changed_sprite_sheets.iter() {
|
||||||
|
if let Some(sprite_sheet) = sprite_sheets.get(changed_sprite_sheet_handle) {
|
||||||
|
// let sprite_sheet_bytes = sprite_sheet.sprites.as_bytes();
|
||||||
|
// let sprite_sheet_buffer = render_resources.create_buffer_with_data(
|
||||||
|
// BufferInfo {
|
||||||
|
// buffer_usage: BufferUsage::STORAGE,
|
||||||
|
// ..Default::default()
|
||||||
|
// },
|
||||||
|
// &sprite_sheet_bytes,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// render_resources.set_asset_resource(
|
||||||
|
// *changed_sprite_sheet_handle,
|
||||||
|
// sprite_sheet_buffer,
|
||||||
|
// SPRITE_SHEET_BUFFER_ASSET_INDEX,
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove this when batching is implemented
|
||||||
|
for (handle, mut renderable) in query.iter_mut(world) {
|
||||||
|
if let Some(sprite_sheet_buffer) =
|
||||||
|
render_resources.get_asset_resource(*handle, SPRITE_SHEET_BUFFER_ASSET_INDEX)
|
||||||
|
{
|
||||||
|
let mut buffer_size = None;
|
||||||
|
render_resources.get_resource_info(sprite_sheet_buffer, &mut |info| {
|
||||||
|
if let Some(ResourceInfo::Buffer(BufferInfo { size, .. })) = info {
|
||||||
|
buffer_size = Some(*size as u64)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
renderable.render_resource_assignments.set(
|
||||||
|
"SpriteSheet",
|
||||||
|
RenderResourceAssignment::Buffer {
|
||||||
|
dynamic_index: None,
|
||||||
|
range: 0..buffer_size.unwrap(),
|
||||||
|
resource: sprite_sheet_buffer,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.system()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
legion = { path = "../bevy_legion", features = ["serialize"]}
|
legion = { path = "../bevy_legion", features = ["serialize"]}
|
||||||
glam = { path = "../bevy_glam" }
|
glam = "0.8.7"
|
||||||
bevy_property = { path = "../bevy_property" }
|
bevy_property = { path = "../bevy_property" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
rayon = "1.2"
|
rayon = "1.2"
|
||||||
|
|
|
@ -17,6 +17,4 @@ bevy_render = { path = "../bevy_render" }
|
||||||
bevy_window = { path = "../bevy_window" }
|
bevy_window = { path = "../bevy_window" }
|
||||||
|
|
||||||
legion = { path = "../bevy_legion", features = ["serialize"] }
|
legion = { path = "../bevy_legion", features = ["serialize"] }
|
||||||
glam = { path = "../bevy_glam" }
|
glam = "0.8.7"
|
||||||
|
|
||||||
zerocopy = "0.3.0"
|
|
36
examples/2d/sprite_sheet.rs
Normal file
36
examples/2d/sprite_sheet.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::input::system::exit_on_esc_system;
|
||||||
|
use bevy_sprite::{Rect, SpriteSheet};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::build()
|
||||||
|
.add_default_plugins()
|
||||||
|
.add_startup_system(setup.system())
|
||||||
|
.init_system(exit_on_esc_system)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
command_buffer: &mut CommandBuffer,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut sprite_sheets: ResMut<Assets<SpriteSheet>>,
|
||||||
|
) {
|
||||||
|
let texture_handle = asset_server.load("assets/branding/icon.png").unwrap();
|
||||||
|
let sprite_sheet = SpriteSheet {
|
||||||
|
texture: texture_handle,
|
||||||
|
sprites: vec![
|
||||||
|
Rect {
|
||||||
|
min: Vec2::new(0.0, 0.0),
|
||||||
|
max: Vec2::new(1.0, 1.0),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
let sprite_sheet_handle = sprite_sheets.add(sprite_sheet);
|
||||||
|
command_buffer
|
||||||
|
.build()
|
||||||
|
.add_entity(OrthographicCameraEntity::default())
|
||||||
|
.add_entity(SpriteSheetEntity {
|
||||||
|
sprite_sheet: sprite_sheet_handle,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
|
@ -33,7 +33,7 @@ pub use crate::render::{
|
||||||
#[cfg(feature = "scene")]
|
#[cfg(feature = "scene")]
|
||||||
pub use crate::scene::{Scene, SceneSpawner};
|
pub use crate::scene::{Scene, SceneSpawner};
|
||||||
#[cfg(feature = "sprite")]
|
#[cfg(feature = "sprite")]
|
||||||
pub use crate::sprite::{ColorMaterial, Quad, Sprite, entity::SpriteEntity};
|
pub use crate::sprite::{ColorMaterial, Quad, Sprite, entity::{SpriteEntity, SpriteSheetEntity}};
|
||||||
#[cfg(feature = "text")]
|
#[cfg(feature = "text")]
|
||||||
pub use crate::text::Font;
|
pub use crate::text::Font;
|
||||||
#[cfg(feature = "transform")]
|
#[cfg(feature = "transform")]
|
||||||
|
|
Loading…
Reference in a new issue