├── clippy.toml ├── swizzlegen ├── .gitignore └── Cargo.toml ├── test_no_std ├── src │ └── lib.rs └── Cargo.toml ├── src ├── core │ ├── sse2 │ │ ├── mod.rs │ │ ├── quaternion.rs │ │ └── float.rs │ ├── scalar │ │ ├── mod.rs │ │ ├── quaternion.rs │ │ └── matrix.rs │ ├── wasm32 │ │ ├── mod.rs │ │ ├── float.rs │ │ └── quaternion.rs │ ├── traits │ │ ├── mod.rs │ │ ├── quaternion.rs │ │ └── projection.rs │ ├── mod.rs │ └── storage.rs ├── features │ ├── mod.rs │ ├── impl_bytemuck.rs │ ├── impl_rkyv.rs │ ├── impl_rand.rs │ └── impl_approx.rs ├── spirv.rs ├── swizzles │ ├── mod.rs │ ├── vec2_impl_scalar.rs │ ├── dvec2_impl_scalar.rs │ ├── ivec2_impl_scalar.rs │ └── uvec2_impl_scalar.rs ├── mat.rs ├── cast.rs └── vec2.rs ├── .gitignore ├── .github └── workflows │ ├── cargo-deny.yml │ ├── coverage.yml │ └── ci.yml ├── deny.toml ├── .tarpaulin.toml ├── benches ├── vec4.rs ├── mat2.rs ├── vec2.rs ├── quat.rs ├── mat3.rs ├── mat3a.rs ├── vec3a.rs ├── affine2.rs ├── mat4.rs ├── affine3.rs ├── transform.rs ├── support │ ├── mod.rs │ └── macros.rs └── vec3.rs ├── LICENSE-MIT ├── CONTRIBUTING.md ├── Cargo.toml ├── ATTRIBUTION.md ├── tests ├── transform.rs ├── support │ ├── macros.rs │ └── mod.rs ├── affine2.rs ├── euler.rs └── mat2.rs └── README.md /clippy.toml: -------------------------------------------------------------------------------- 1 | msrv = "1.51" 2 | -------------------------------------------------------------------------------- /swizzlegen/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /test_no_std/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub use glam::*; 4 | -------------------------------------------------------------------------------- /src/core/sse2/mod.rs: -------------------------------------------------------------------------------- 1 | mod float; 2 | pub mod matrix; 3 | pub mod quaternion; 4 | pub mod vector; 5 | -------------------------------------------------------------------------------- /src/core/scalar/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mask; 2 | pub mod matrix; 3 | pub mod quaternion; 4 | pub mod vector; 5 | -------------------------------------------------------------------------------- /src/core/wasm32/mod.rs: -------------------------------------------------------------------------------- 1 | // mod float; 2 | pub mod matrix; 3 | pub mod quaternion; 4 | pub mod vector; 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | *.json 3 | *.swp 4 | **/*.rs.bk 5 | Cargo.lock 6 | Session.vim 7 | .cargo-ok 8 | cargo-timing*.html 9 | -------------------------------------------------------------------------------- /src/core/traits/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod matrix; 2 | pub mod projection; 3 | pub mod quaternion; 4 | pub mod scalar; 5 | pub mod vector; 6 | -------------------------------------------------------------------------------- /.github/workflows/cargo-deny.yml: -------------------------------------------------------------------------------- 1 | name: cargo-deny 2 | on: [push, pull_request] 3 | jobs: 4 | cargo-deny: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | - uses: EmbarkStudios/cargo-deny-action@v1 9 | -------------------------------------------------------------------------------- /swizzlegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swizzlegen" 3 | version = "0.1.0" 4 | authors = ["Cameron Hart "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /test_no_std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "glam-no_std" 3 | version = "0.1.0" 4 | authors = ["Cameron Hart "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | glam = { path = "..", default-features = false, features = ["libm"] } 11 | -------------------------------------------------------------------------------- /src/features/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "approx")] 2 | pub mod impl_approx; 3 | 4 | #[cfg(feature = "bytemuck")] 5 | pub mod impl_bytemuck; 6 | 7 | #[cfg(feature = "mint")] 8 | pub mod impl_mint; 9 | 10 | #[cfg(feature = "rand")] 11 | pub mod impl_rand; 12 | 13 | #[cfg(feature = "serde")] 14 | pub mod impl_serde; 15 | 16 | #[cfg(feature = "rkyv")] 17 | pub mod impl_rkyv; 18 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [bans] 2 | multiple-versions = "deny" 3 | deny = [] 4 | skip-tree = [ 5 | # ignore criterion dev-dependency that often have duplicate dependencies internally 6 | { name = "criterion" }, 7 | ] 8 | 9 | [licenses] 10 | unlicensed = "deny" 11 | allow = [ 12 | "Apache-2.0", 13 | "BSD-2-Clause", 14 | "BSD-3-Clause", 15 | "ISC", 16 | "MIT", 17 | "MPL-2.0", 18 | ] 19 | -------------------------------------------------------------------------------- /.tarpaulin.toml: -------------------------------------------------------------------------------- 1 | [sse2_math] 2 | features = "approx bytemuck mint rand rkyv serde debug-glam-assert" 3 | exclude-files = ["src/transform.rs", "src/core/wasm32/*", "src/swizzles/vec3a_impl_wasm32.rs", "src/swizzles/vec4_impl_wasm32.rs", "benches/*", "tests/support/mod.rs"] 4 | 5 | [scalar_math] 6 | features = "scalar-math approx bytemuck mint rand rkyv serde debug-glam-assert" 7 | exclude-files = ["src/transform.rs", "src/core/wasm32/*", "src/swizzles/vec3a_impl_wasm32.rs", "src/swizzles/vec4_impl_wasm32.rs", "benches/*", "tests/support/mod.rs"] 8 | -------------------------------------------------------------------------------- /benches/vec4.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::Vec4; 8 | use std::ops::Mul; 9 | use support::random_vec4; 10 | 11 | bench_binop!( 12 | vec4_mul_vec4, 13 | "vec4 mul vec4", 14 | op => mul, 15 | from1 => random_vec4, 16 | from2 => random_vec4 17 | ); 18 | 19 | bench_select!( 20 | vec4_select, 21 | "vec4 select", 22 | ty => Vec4, 23 | op => cmple, 24 | from => random_vec4 25 | ); 26 | 27 | criterion_group!(benches, vec4_mul_vec4, vec4_select); 28 | 29 | criterion_main!(benches); 30 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: coverage 2 | on: [push] 3 | jobs: 4 | coverage: 5 | container: 6 | image: xd009642/tarpaulin 7 | options: --security-opt seccomp=unconfined 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | # - name: Install tarpaulin 13 | # run: cargo install cargo-tarpaulin 14 | 15 | - name: Generate code coverage 16 | run: cargo tarpaulin -v --timeout 120 --out Lcov --output-dir ./coverage 17 | 18 | - name: Upload to coveralls.io 19 | uses: coverallsapp/github-action@master 20 | with: 21 | github-token: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /src/spirv.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "spirv")] 2 | macro_rules! unsupported_features { 3 | ($($feature:literal),+ $(,)?) => { 4 | $( 5 | #[cfg(feature = $feature)] 6 | compile_error!( 7 | concat!( 8 | "`", 9 | $feature, 10 | "`", 11 | " feature is not supported when building for SPIR-V.", 12 | ) 13 | ); 14 | )+ 15 | } 16 | } 17 | 18 | #[cfg(target_arch = "spirv")] 19 | unsupported_features! { 20 | "approx", 21 | "debug-glam-assert", 22 | "glam-assert", 23 | "rand", 24 | "serde", 25 | "std", 26 | } 27 | -------------------------------------------------------------------------------- /src/core/mod.rs: -------------------------------------------------------------------------------- 1 | // the core module provides traits for implementing vector, quaternion and matrix operations, 2 | // storage structs for scalar vector, quaternion and matrix data and implementations of the traits 3 | // for those structs and for supported SIMD types such as SSE2's `__m128`. 4 | // 5 | // The higher level glam library types have an inner type which either uses one of these storage 6 | // structs, or `__m128` and the actual implementation is provided by the core module. 7 | // 8 | // This architecture allows the public API to not require generics or traits, while still 9 | // supporting a number of Rust primitive types and SIMD architectures such as SSE2. 10 | // 11 | pub mod storage; 12 | pub mod traits; 13 | 14 | mod scalar; 15 | #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))] 16 | mod sse2; 17 | #[cfg(all(target_feature = "simd128", not(feature = "scalar-math")))] 18 | mod wasm32; 19 | -------------------------------------------------------------------------------- /benches/mat2.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use std::ops::Mul; 8 | use support::*; 9 | 10 | bench_binop!( 11 | mat2_mul_vec2, 12 | "mat2 mul vec2", 13 | op => mul, 14 | from1 => random_mat2, 15 | from2 => random_vec2 16 | ); 17 | 18 | bench_unop!( 19 | mat2_transpose, 20 | "mat2 transpose", 21 | op => transpose, 22 | from => random_mat2 23 | ); 24 | bench_unop!( 25 | mat2_determinant, 26 | "mat2 determinant", 27 | op => determinant, 28 | from => random_mat2 29 | ); 30 | bench_unop!(mat2_inverse, "mat2 inverse", op => inverse, from => random_mat2); 31 | bench_binop!(mat2_mul_mat2, "mat2 mul mat2", op => mul, from => random_mat2); 32 | 33 | criterion_group!( 34 | benches, 35 | mat2_transpose, 36 | mat2_determinant, 37 | mat2_inverse, 38 | mat2_mul_vec2, 39 | mat2_mul_mat2, 40 | ); 41 | 42 | criterion_main!(benches); 43 | -------------------------------------------------------------------------------- /benches/vec2.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::Vec2; 8 | use std::ops::Mul; 9 | use support::*; 10 | 11 | euler!( 12 | vec2_euler, 13 | "vec2 euler", 14 | ty => Vec2, 15 | storage => Vec2, 16 | zero => Vec2::ZERO, 17 | rand => random_vec2); 18 | 19 | bench_binop!( 20 | vec2_mul_vec2, 21 | "vec2 mul vec2", 22 | op => mul, 23 | from1 => random_vec2, 24 | from2 => random_vec2 25 | ); 26 | 27 | bench_binop!( 28 | vec2_angle_between, 29 | "vec2 angle_between", 30 | op => angle_between, 31 | from1 => random_vec2, 32 | from2 => random_vec2 33 | ); 34 | 35 | bench_select!( 36 | vec2_select, 37 | "vec2 select", 38 | ty => Vec2, 39 | op => cmple, 40 | from => random_vec2 41 | ); 42 | 43 | criterion_group!( 44 | benches, 45 | vec2_mul_vec2, 46 | vec2_euler, 47 | vec2_select, 48 | vec2_angle_between 49 | ); 50 | 51 | criterion_main!(benches); 52 | -------------------------------------------------------------------------------- /src/swizzles/mod.rs: -------------------------------------------------------------------------------- 1 | mod dvec2_impl_scalar; 2 | mod dvec3_impl_scalar; 3 | mod dvec4_impl_scalar; 4 | 5 | mod ivec2_impl_scalar; 6 | mod ivec3_impl_scalar; 7 | mod ivec4_impl_scalar; 8 | 9 | mod uvec2_impl_scalar; 10 | mod uvec3_impl_scalar; 11 | mod uvec4_impl_scalar; 12 | 13 | mod vec2_impl_scalar; 14 | mod vec3_impl_scalar; 15 | #[cfg(any( 16 | not(any(target_feature = "sse2", target_feature = "simd128")), 17 | feature = "scalar-math" 18 | ))] 19 | mod vec3a_impl_scalar; 20 | #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))] 21 | mod vec3a_impl_sse2; 22 | #[cfg(all(target_feature = "simd128", not(feature = "scalar-math")))] 23 | mod vec3a_impl_wasm32; 24 | #[cfg(any( 25 | not(any(target_feature = "sse2", target_feature = "simd128")), 26 | feature = "scalar-math" 27 | ))] 28 | mod vec4_impl_scalar; 29 | #[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))] 30 | mod vec4_impl_sse2; 31 | #[cfg(all(target_feature = "simd128", not(feature = "scalar-math")))] 32 | mod vec4_impl_wasm32; 33 | mod vec_traits; 34 | 35 | pub use vec_traits::*; 36 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | 5 | lint: 6 | name: Lint 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | toolchain: stable 14 | components: rustfmt, clippy 15 | 16 | # make sure all code has been formatted with rustfmt and linted with clippy 17 | - name: rustfmt 18 | run: cargo fmt -- --check --color always 19 | 20 | # run clippy to verify we have no warnings 21 | - run: cargo fetch 22 | - name: cargo clippy 23 | run: cargo clippy --all-features -- -D warnings 24 | 25 | test: 26 | name: Test 27 | strategy: 28 | matrix: 29 | os: [ubuntu-latest, macos-latest, windows-latest] 30 | toolchain: [1.52.1, stable, beta, nightly] 31 | runs-on: ${{ matrix.os }} 32 | steps: 33 | - uses: actions/checkout@v2 34 | - run: rustup update --no-self-update ${{ matrix.toolchain }} 35 | - run: rustup default ${{ matrix.toolchain }} 36 | - run: ./build_and_test_features.sh 37 | shell: bash 38 | 39 | test-wasm: 40 | strategy: 41 | matrix: 42 | toolchain: [stable] 43 | os: [ubuntu-latest] 44 | runs-on: ${{ matrix.os }} 45 | steps: 46 | - uses: actions/checkout@v2 47 | 48 | - name: Install 49 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 50 | 51 | - run: ./build_and_test_wasm32_firefox.sh 52 | - run: ./build_and_test_wasm32_chrome.sh 53 | -------------------------------------------------------------------------------- /benches/quat.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::Quat; 8 | use std::ops::Mul; 9 | use support::*; 10 | 11 | bench_unop!( 12 | quat_conjugate, 13 | "quat conjugate", 14 | op => conjugate, 15 | from => random_quat 16 | ); 17 | 18 | bench_binop!( 19 | quat_mul_vec3, 20 | "quat mul vec3", 21 | op => mul, 22 | from1 => random_quat, 23 | from2 => random_vec3 24 | ); 25 | 26 | bench_binop!( 27 | quat_mul_vec3a, 28 | "quat mul vec3a", 29 | op => mul, 30 | from1 => random_quat, 31 | from2 => random_vec3a 32 | ); 33 | 34 | bench_binop!( 35 | quat_mul_quat, 36 | "quat mul quat", 37 | op => mul, 38 | from => random_quat 39 | ); 40 | 41 | bench_binop!( 42 | quat_dot, 43 | "quat dot", 44 | op => dot, 45 | from => random_quat 46 | ); 47 | 48 | bench_trinop!( 49 | quat_lerp, 50 | "quat lerp", 51 | op => lerp, 52 | from1 => random_quat, 53 | from2 => random_quat, 54 | from3 => random_f32 55 | ); 56 | 57 | bench_trinop!( 58 | quat_slerp, 59 | "quat slerp", 60 | op => slerp, 61 | from1 => random_quat, 62 | from2 => random_quat, 63 | from3 => random_f32 64 | ); 65 | 66 | bench_from_ypr!(quat_from_ypr, "quat from ypr", ty => Quat); 67 | 68 | criterion_group!( 69 | benches, 70 | quat_conjugate, 71 | quat_dot, 72 | quat_lerp, 73 | quat_slerp, 74 | quat_mul_quat, 75 | quat_mul_vec3, 76 | quat_mul_vec3a, 77 | quat_from_ypr 78 | ); 79 | 80 | criterion_main!(benches); 81 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to glam 2 | 3 | Thanks for contributing to `glam`! These guidelines will try to make the process 4 | painless and efficient. 5 | 6 | The short guide to contributing is [open a github issue]. Pull requests are 7 | welcome for bug fixes, documentation improvements and optimizations. For 8 | anything else it would be best to discuss it first. 9 | 10 | ## Questions 11 | 12 | If you have a question about the usage of this library please 13 | [open a github issue]. That's the easiest way to get support right now. 14 | 15 | ## Bugs 16 | 17 | If you find a bug please [open a github issue] or submit a pull request. A unit 18 | test for any bug that slipped through existing coverage would also be greatly 19 | appreciated. 20 | 21 | ## New functions and methods 22 | 23 | If `glam` is missing functionality on existing types, [open a github issue] 24 | describing what feature you would like added and ideally what your use case is 25 | for it just so I have a better understanding of the feature. I'd like to keep 26 | `glam` reasonably light functionality wise initially but commonly used 27 | functionality that is missing is very welcome. If you do submit a pull request 28 | please ensure any new functionality also has a test. 29 | 30 | ## Optimizations 31 | 32 | If you feel some functionality could be optimized please [open a github issue] 33 | or submit a pull request. Any optimization pull request should include a 34 | benchmark if there isn't one already, so I can confirm the performance 35 | improvement. 36 | 37 | ## Documentation 38 | 39 | If you feel any documentation could be added or improved please 40 | [open a github issue] or submit a pull request. 41 | 42 | [open a github issue]: https://github.com/bitshifter/glam-rs/issues 43 | -------------------------------------------------------------------------------- /benches/mat3.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::Mat3; 8 | use std::ops::Mul; 9 | use support::*; 10 | 11 | bench_unop!( 12 | mat3_transpose, 13 | "mat3 transpose", 14 | op => transpose, 15 | from => random_mat3 16 | ); 17 | bench_unop!( 18 | mat3_determinant, 19 | "mat3 determinant", 20 | op => determinant, 21 | from => random_mat3 22 | ); 23 | bench_unop!(mat3_inverse, "mat3 inverse", op => inverse, from => random_mat3); 24 | bench_binop!(mat3_mul_mat3, "mat3 mul mat3", op => mul, from => random_mat3); 25 | bench_from_ypr!(mat3_from_ypr, "mat3 from ypr", ty => Mat3); 26 | 27 | bench_binop!( 28 | mat3_mul_vec3, 29 | "mat3 mul vec3", 30 | op => mul, 31 | from1 => random_mat3, 32 | from2 => random_vec3 33 | ); 34 | 35 | bench_binop!( 36 | mat3_mul_vec3a, 37 | "mat3 mul vec3a", 38 | op => mul, 39 | from1 => random_mat3, 40 | from2 => random_vec3a 41 | ); 42 | 43 | bench_binop!( 44 | mat3_transform_point2, 45 | "mat3 transform point2", 46 | op => transform_point2, 47 | from1 => random_srt_mat3, 48 | from2 => random_vec2 49 | ); 50 | 51 | bench_binop!( 52 | mat3_transform_vector2, 53 | "mat3 transform vector2", 54 | op => transform_vector2, 55 | from1 => random_srt_mat3, 56 | from2 => random_vec2 57 | ); 58 | 59 | criterion_group!( 60 | benches, 61 | mat3_transpose, 62 | mat3_determinant, 63 | mat3_inverse, 64 | mat3_mul_vec3, 65 | mat3_mul_vec3a, 66 | mat3_mul_mat3, 67 | mat3_from_ypr, 68 | mat3_transform_vector2, 69 | mat3_transform_point2, 70 | ); 71 | 72 | criterion_main!(benches); 73 | -------------------------------------------------------------------------------- /benches/mat3a.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::Mat3A; 8 | use std::ops::Mul; 9 | use support::*; 10 | 11 | bench_unop!( 12 | mat3a_transpose, 13 | "mat3a transpose", 14 | op => transpose, 15 | from => random_mat3a 16 | ); 17 | bench_unop!( 18 | mat3a_determinant, 19 | "mat3a determinant", 20 | op => determinant, 21 | from => random_mat3a 22 | ); 23 | bench_unop!(mat3a_inverse, "mat3a inverse", op => inverse, from => random_mat3a); 24 | bench_binop!(mat3a_mul_mat3a, "mat3a mul mat3a", op => mul, from => random_mat3a); 25 | bench_from_ypr!(mat3a_from_ypr, "mat3a from ypr", ty => Mat3A); 26 | 27 | bench_binop!( 28 | mat3a_mul_vec3, 29 | "mat3a mul vec3", 30 | op => mul, 31 | from1 => random_mat3a, 32 | from2 => random_vec3 33 | ); 34 | 35 | bench_binop!( 36 | mat3a_mul_vec3a, 37 | "mat3a mul vec3a", 38 | op => mul, 39 | from1 => random_mat3a, 40 | from2 => random_vec3a 41 | ); 42 | 43 | bench_binop!( 44 | mat3a_transform_point2, 45 | "mat3a transform point2", 46 | op => transform_point2, 47 | from1 => random_srt_mat3a, 48 | from2 => random_vec2 49 | ); 50 | 51 | bench_binop!( 52 | mat3a_transform_vector2, 53 | "mat3a transform vector2", 54 | op => transform_vector2, 55 | from1 => random_srt_mat3a, 56 | from2 => random_vec2 57 | ); 58 | 59 | criterion_group!( 60 | benches, 61 | mat3a_transpose, 62 | mat3a_determinant, 63 | mat3a_inverse, 64 | mat3a_mul_vec3, 65 | mat3a_mul_vec3a, 66 | mat3a_mul_mat3a, 67 | mat3a_from_ypr, 68 | mat3a_transform_vector2, 69 | mat3a_transform_point2, 70 | ); 71 | 72 | criterion_main!(benches); 73 | -------------------------------------------------------------------------------- /benches/vec3a.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::{Vec3, Vec3A}; 8 | use std::ops::Mul; 9 | use support::*; 10 | 11 | bench_binop!( 12 | vec3a_mul_vec3a, 13 | "vec3a mul vec3a", 14 | op => mul, 15 | from1 => random_vec3a, 16 | from2 => random_vec3a 17 | ); 18 | 19 | #[inline] 20 | fn vec3a_to_rgb_op(v: Vec3A) -> u32 { 21 | let (red, green, blue) = (v.min(Vec3A::ONE).max(Vec3A::ZERO) * 255.0).into(); 22 | (red as u32) << 16 | (green as u32) << 8 | (blue as u32) 23 | } 24 | 25 | #[inline] 26 | fn vec3a_deref(v: Vec3A) -> [f32; 3] { 27 | [v.x, v.y, v.z] 28 | } 29 | 30 | #[inline] 31 | fn vec3a_into_array(v: Vec3A) -> [f32; 3] { 32 | v.into() 33 | } 34 | 35 | #[inline] 36 | fn vec3a_into_tuple(v: Vec3A) -> (f32, f32, f32) { 37 | v.into() 38 | } 39 | 40 | #[inline] 41 | fn vec3a_into_vec3(v: Vec3A) -> Vec3 { 42 | v.into() 43 | } 44 | 45 | bench_func!( 46 | vec3a_to_vec3, 47 | "vec3a into vec3", 48 | op => vec3a_into_vec3, 49 | from => random_vec3a 50 | ); 51 | 52 | bench_func!( 53 | vec3a_to_rgb, 54 | "vec3a to rgb", 55 | op => vec3a_to_rgb_op, 56 | from => random_vec3a 57 | ); 58 | 59 | bench_func!( 60 | vec3a_to_array_deref, 61 | "vec3a into array deref", 62 | op => vec3a_deref, 63 | from => random_vec3a 64 | ); 65 | 66 | bench_func!( 67 | vec3a_to_array_into, 68 | "vec3a into array fast", 69 | op => vec3a_into_array, 70 | from => random_vec3a 71 | ); 72 | 73 | bench_func!( 74 | vec3a_to_tuple_into, 75 | "vec3a into tuple fast", 76 | op => vec3a_into_tuple, 77 | from => random_vec3a 78 | ); 79 | 80 | euler!(vec3a_euler, "vec3a euler", ty => Vec3A, storage => Vec3A, zero => Vec3A::ZERO, rand => random_vec3a); 81 | 82 | bench_binop!( 83 | vec3a_angle_between, 84 | "vec3a angle_between", 85 | op => angle_between, 86 | from1 => random_vec3a, 87 | from2 => random_vec3a 88 | ); 89 | 90 | bench_select!( 91 | vec3a_select, 92 | "vec3a select", 93 | ty => Vec3A, 94 | op => cmple, 95 | from => random_vec3a 96 | ); 97 | 98 | criterion_group!( 99 | benches, 100 | vec3a_mul_vec3a, 101 | vec3a_angle_between, 102 | vec3a_euler, 103 | vec3a_select, 104 | vec3a_to_array_deref, 105 | vec3a_to_array_into, 106 | vec3a_to_rgb, 107 | vec3a_to_tuple_into, 108 | vec3a_to_vec3, 109 | ); 110 | 111 | criterion_main!(benches); 112 | -------------------------------------------------------------------------------- /benches/affine2.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::Affine2; 8 | use std::ops::Mul; 9 | use support::*; 10 | 11 | pub fn random_srt_affine2(rng: &mut PCG32) -> Affine2 { 12 | Affine2::from_scale_angle_translation( 13 | random_nonzero_vec2(rng), 14 | random_radians(rng), 15 | random_vec2(rng), 16 | ) 17 | } 18 | 19 | bench_unop!(affine2_inverse, "affine2 inverse", op => inverse, from => random_srt_affine2); 20 | bench_binop!( 21 | affine2_transform_point2, 22 | "affine2 transform point2", 23 | op => transform_point2, 24 | from1 => random_srt_affine2, 25 | from2 => random_vec2 26 | ); 27 | 28 | bench_binop!( 29 | affine2_transform_vector2, 30 | "affine2 transform vector2", 31 | op => transform_vector2, 32 | from1 => random_srt_affine2, 33 | from2 => random_vec2 34 | ); 35 | bench_binop!(affine2_mul_affine2, "affine2 mul affine2", op => mul, from => random_srt_affine2); 36 | bench_binop!(affine2_mul_mat3, "affine2 mul mat3", op => mul, from1 => random_srt_affine2, from2 => random_srt_mat3); 37 | bench_binop!(mat3_mul_affine2, "mat3 mul affine2", op => mul, from1 => random_srt_mat3, from2 => random_srt_affine2); 38 | 39 | pub fn affine2_from_srt(c: &mut Criterion) { 40 | use glam::Vec2; 41 | const SIZE: usize = 1 << 13; 42 | let mut rng = support::PCG32::default(); 43 | let inputs = criterion::black_box( 44 | (0..SIZE) 45 | .map(|_| { 46 | ( 47 | random_nonzero_vec2(&mut rng), 48 | random_radians(&mut rng), 49 | random_vec2(&mut rng), 50 | ) 51 | }) 52 | .collect::>(), 53 | ); 54 | let mut outputs = vec![Affine2::default(); SIZE]; 55 | let mut i = 0; 56 | c.bench_function("affine2 from srt", |b| { 57 | b.iter(|| { 58 | i = (i + 1) & (SIZE - 1); 59 | unsafe { 60 | let data = inputs.get_unchecked(i); 61 | *outputs.get_unchecked_mut(i) = 62 | Affine2::from_scale_angle_translation(data.0, data.1, data.2) 63 | } 64 | }) 65 | }); 66 | } 67 | 68 | criterion_group!( 69 | benches, 70 | affine2_inverse, 71 | affine2_transform_point2, 72 | affine2_transform_vector2, 73 | affine2_mul_affine2, 74 | affine2_mul_mat3, 75 | mat3_mul_affine2, 76 | affine2_from_srt, 77 | ); 78 | 79 | criterion_main!(benches); 80 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "glam" 3 | version = "0.20.0" # remember to update html_root_url 4 | edition = "2018" 5 | authors = ["Cameron Hart "] 6 | description = "A simple and fast 3D math library for games and graphics" 7 | repository = "https://github.com/bitshifter/glam-rs" 8 | readme = "README.md" 9 | license = "MIT OR Apache-2.0" 10 | keywords = ["gamedev", "math", "matrix", "vector", "quaternion"] 11 | categories = ["game-engines", "no-std"] 12 | 13 | [badges] 14 | maintenance = { status = "actively-developed" } 15 | 16 | [features] 17 | default = ["std"] 18 | 19 | # enable support for the standard library 20 | std = [] 21 | 22 | # enable additional glam checks if debug assertions are enabled 23 | debug-glam-assert = [] 24 | # always enable additional glam checks 25 | glam-assert = [] 26 | 27 | # this is primarily for testing the fallback implementation 28 | scalar-math = [] 29 | 30 | # deprecated and will move to a separate crate 31 | transform-types = [] 32 | 33 | # libm is required when building no_std 34 | libm = ["num-traits/libm"] 35 | 36 | [dependencies] 37 | approx = { version = "0.5", optional = true, default-features = false } 38 | bytemuck = { version = "1.5", optional = true, default-features = false } 39 | mint = { version = "0.5.8", optional = true, default-features = false } 40 | num-traits = { version = "0.2.14", optional = true, default-features = false } 41 | rand = { version = "0.8", optional = true, default-features = false } 42 | serde = { version = "1.0", optional = true, features = ["derive"] } 43 | rkyv = { version = "0.7", optional = true } 44 | bytecheck = { version = "0.6", optional = true, default-features = false} 45 | 46 | [dev-dependencies] 47 | # rand_xoshiro is required for tests if rand is enabled 48 | rand_xoshiro = "0.6" 49 | serde_json = "1.0" 50 | 51 | [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] 52 | criterion = { version = "0.3", features = ["html_reports"] } 53 | 54 | [target.'cfg(target_arch = "wasm32")'.dev-dependencies] 55 | wasm-bindgen-test = "0.3.0" 56 | 57 | [lib] 58 | bench = false 59 | 60 | [[bench]] 61 | name = "mat2" 62 | harness = false 63 | 64 | [[bench]] 65 | name = "mat3" 66 | harness = false 67 | 68 | [[bench]] 69 | name = "mat3a" 70 | harness = false 71 | 72 | [[bench]] 73 | name = "affine2" 74 | harness = false 75 | 76 | [[bench]] 77 | name = "affine3" 78 | harness = false 79 | 80 | [[bench]] 81 | name = "mat4" 82 | harness = false 83 | 84 | [[bench]] 85 | name = "quat" 86 | harness = false 87 | 88 | [[bench]] 89 | name = "transform" 90 | harness = false 91 | required-features = ["transform-types"] 92 | 93 | [[bench]] 94 | name = "vec2" 95 | harness = false 96 | 97 | [[bench]] 98 | name = "vec3" 99 | harness = false 100 | 101 | [[bench]] 102 | name = "vec3a" 103 | harness = false 104 | 105 | [[bench]] 106 | name = "vec4" 107 | harness = false 108 | -------------------------------------------------------------------------------- /src/features/impl_bytemuck.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat, 3 | UVec2, UVec3, UVec4, Vec2, Vec3, Vec4, 4 | }; 5 | use bytemuck::{Pod, Zeroable}; 6 | 7 | unsafe impl Pod for Mat2 {} 8 | unsafe impl Zeroable for Mat2 {} 9 | unsafe impl Pod for Mat3 {} 10 | unsafe impl Zeroable for Mat3 {} 11 | unsafe impl Pod for Mat4 {} 12 | unsafe impl Zeroable for Mat4 {} 13 | 14 | unsafe impl Pod for Quat {} 15 | unsafe impl Zeroable for Quat {} 16 | 17 | unsafe impl Pod for Vec2 {} 18 | unsafe impl Zeroable for Vec2 {} 19 | unsafe impl Pod for Vec3 {} 20 | unsafe impl Zeroable for Vec3 {} 21 | unsafe impl Pod for Vec4 {} 22 | unsafe impl Zeroable for Vec4 {} 23 | 24 | unsafe impl Pod for DMat2 {} 25 | unsafe impl Zeroable for DMat2 {} 26 | unsafe impl Pod for DMat3 {} 27 | unsafe impl Zeroable for DMat3 {} 28 | unsafe impl Pod for DMat4 {} 29 | unsafe impl Zeroable for DMat4 {} 30 | 31 | unsafe impl Pod for DQuat {} 32 | unsafe impl Zeroable for DQuat {} 33 | 34 | unsafe impl Pod for DVec2 {} 35 | unsafe impl Zeroable for DVec2 {} 36 | unsafe impl Pod for DVec3 {} 37 | unsafe impl Zeroable for DVec3 {} 38 | unsafe impl Pod for DVec4 {} 39 | unsafe impl Zeroable for DVec4 {} 40 | 41 | unsafe impl Pod for IVec2 {} 42 | unsafe impl Zeroable for IVec2 {} 43 | unsafe impl Pod for IVec3 {} 44 | unsafe impl Zeroable for IVec3 {} 45 | unsafe impl Pod for IVec4 {} 46 | unsafe impl Zeroable for IVec4 {} 47 | 48 | unsafe impl Pod for UVec2 {} 49 | unsafe impl Zeroable for UVec2 {} 50 | unsafe impl Pod for UVec3 {} 51 | unsafe impl Zeroable for UVec3 {} 52 | unsafe impl Pod for UVec4 {} 53 | unsafe impl Zeroable for UVec4 {} 54 | 55 | #[cfg(test)] 56 | mod test { 57 | use crate::{ 58 | DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, 59 | Quat, UVec2, UVec3, UVec4, Vec2, Vec3, Vec4, 60 | }; 61 | use core::mem; 62 | 63 | macro_rules! test_t { 64 | ($name:ident, $t:ty) => { 65 | #[test] 66 | fn $name() { 67 | let t = <$t>::default(); 68 | let b = bytemuck::bytes_of(&t); 69 | assert_eq!(t.as_ref().as_ptr() as usize, b.as_ptr() as usize); 70 | assert_eq!(b.len(), mem::size_of_val(&t)); 71 | } 72 | }; 73 | } 74 | 75 | test_t!(mat2, Mat2); 76 | test_t!(mat3, Mat3); 77 | test_t!(mat4, Mat4); 78 | test_t!(quat, Quat); 79 | test_t!(vec2, Vec2); 80 | test_t!(vec3, Vec3); 81 | test_t!(vec4, Vec4); 82 | 83 | test_t!(dmat2, DMat2); 84 | test_t!(dmat3, DMat3); 85 | test_t!(dmat4, DMat4); 86 | test_t!(dquat, DQuat); 87 | test_t!(dvec2, DVec2); 88 | test_t!(dvec3, DVec3); 89 | test_t!(dvec4, DVec4); 90 | 91 | test_t!(ivec2, IVec2); 92 | test_t!(ivec3, IVec3); 93 | test_t!(ivec4, IVec4); 94 | 95 | test_t!(uvec2, UVec2); 96 | test_t!(uvec3, UVec3); 97 | test_t!(uvec4, UVec4); 98 | } 99 | -------------------------------------------------------------------------------- /src/core/scalar/quaternion.rs: -------------------------------------------------------------------------------- 1 | use crate::core::{ 2 | storage::{XYZ, XYZW}, 3 | traits::{quaternion::Quaternion, scalar::*, vector::*}, 4 | }; 5 | 6 | impl Quaternion for XYZW { 7 | // fallback 8 | type SIMDVector3 = XYZ; 9 | 10 | #[inline(always)] 11 | fn conjugate(self) -> Self { 12 | Self::new(-self.x, -self.y, -self.z, self.w) 13 | } 14 | 15 | #[inline] 16 | fn lerp(self, end: Self, s: T) -> Self { 17 | glam_assert!(FloatVector4::is_normalized(self)); 18 | glam_assert!(FloatVector4::is_normalized(end)); 19 | 20 | let start = self; 21 | let end = end; 22 | let dot = start.dot(end); 23 | let bias = if dot >= T::ZERO { T::ONE } else { T::NEG_ONE }; 24 | let interpolated = start.add(end.mul_scalar(bias).sub(start).mul_scalar(s)); 25 | interpolated.normalize() 26 | } 27 | 28 | #[inline] 29 | fn slerp(self, end: Self, s: T) -> Self { 30 | // http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/ 31 | 32 | glam_assert!(FloatVector4::is_normalized(self)); 33 | glam_assert!(FloatVector4::is_normalized(end)); 34 | 35 | let dot = self.dot(end); 36 | 37 | if dot > T::from_f32(0.9995) { 38 | // assumes lerp returns a normalized quaternion 39 | self.lerp(end, s) 40 | } else { 41 | // assumes scalar_acos clamps the input to [-1.0, 1.0] 42 | let theta = dot.acos_approx(); 43 | let scale1 = (theta * (T::ONE - s)).sin(); 44 | let scale2 = (theta * s).sin(); 45 | let theta_sin = theta.sin(); 46 | 47 | self.mul_scalar(scale1) 48 | .add(end.mul_scalar(scale2)) 49 | .mul_scalar(theta_sin.recip()) 50 | } 51 | } 52 | 53 | #[inline] 54 | fn mul_vector3(self, other: XYZ) -> XYZ { 55 | glam_assert!(FloatVector4::is_normalized(self)); 56 | let w = self.w; 57 | let b = XYZ { 58 | x: self.x, 59 | y: self.y, 60 | z: self.z, 61 | }; 62 | let b2 = b.dot(b); 63 | other 64 | .mul_scalar(w * w - b2) 65 | .add(b.mul_scalar(other.dot(b) * T::TWO)) 66 | .add(b.cross(other).mul_scalar(w * T::TWO)) 67 | } 68 | 69 | #[inline] 70 | fn mul_quaternion(self, other: Self) -> Self { 71 | glam_assert!(FloatVector4::is_normalized(self)); 72 | glam_assert!(FloatVector4::is_normalized(other)); 73 | let (x0, y0, z0, w0) = self.into_tuple(); 74 | let (x1, y1, z1, w1) = other.into_tuple(); 75 | Self::new( 76 | w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1, 77 | w0 * y1 - x0 * z1 + y0 * w1 + z0 * x1, 78 | w0 * z1 + x0 * y1 - y0 * x1 + z0 * w1, 79 | w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1, 80 | ) 81 | } 82 | 83 | #[inline(always)] 84 | fn mul_float4_as_vector3(self, other: XYZ) -> XYZ { 85 | self.mul_vector3(other) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /benches/mat4.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::Mat4; 8 | use std::ops::Mul; 9 | use support::*; 10 | 11 | bench_unop!( 12 | mat4_transpose, 13 | "mat4 transpose", 14 | op => transpose, 15 | from => random_srt_mat4 16 | ); 17 | 18 | bench_unop!( 19 | mat4_determinant, 20 | "mat4 determinant", 21 | op => determinant, 22 | from => random_srt_mat4 23 | ); 24 | 25 | bench_unop!( 26 | mat4_inverse, 27 | "mat4 inverse", 28 | op => inverse, 29 | from => random_srt_mat4 30 | ); 31 | 32 | bench_binop!( 33 | mat4_mul_vec4, 34 | "mat4 mul vec4", 35 | op => mul, 36 | from1 => random_srt_mat4, 37 | from2 => random_vec4 38 | ); 39 | 40 | bench_binop!( 41 | mat4_transform_point3, 42 | "mat4 transform point3", 43 | op => transform_point3, 44 | from1 => random_srt_mat4, 45 | from2 => random_vec3 46 | ); 47 | 48 | bench_binop!( 49 | mat4_transform_vector3, 50 | "mat4 transform vector3", 51 | op => transform_vector3, 52 | from1 => random_srt_mat4, 53 | from2 => random_vec3 54 | ); 55 | 56 | bench_binop!( 57 | mat4_transform_point3a, 58 | "mat4 transform point3a", 59 | op => transform_point3a, 60 | from1 => random_srt_mat4, 61 | from2 => random_vec3a 62 | ); 63 | 64 | bench_binop!( 65 | mat4_transform_vector3a, 66 | "mat4 transform vector3a", 67 | op => transform_vector3a, 68 | from1 => random_srt_mat4, 69 | from2 => random_vec3a 70 | ); 71 | 72 | bench_binop!( 73 | mat4_mul_mat4, 74 | "mat4 mul mat4", 75 | op => mul, 76 | from => random_srt_mat4 77 | ); 78 | 79 | bench_from_ypr!( 80 | mat4_from_ypr, 81 | "mat4 from ypr", 82 | ty => Mat4 83 | ); 84 | 85 | pub fn mat4_from_srt(c: &mut Criterion) { 86 | use glam::{Quat, Vec3}; 87 | const SIZE: usize = 1 << 13; 88 | let mut rng = support::PCG32::default(); 89 | let inputs = criterion::black_box( 90 | (0..SIZE) 91 | .map(|_| { 92 | ( 93 | random_nonzero_vec3(&mut rng), 94 | random_quat(&mut rng), 95 | random_vec3(&mut rng), 96 | ) 97 | }) 98 | .collect::>(), 99 | ); 100 | let mut outputs = vec![Mat4::default(); SIZE]; 101 | let mut i = 0; 102 | c.bench_function("mat4 from srt", |b| { 103 | b.iter(|| { 104 | i = (i + 1) & (SIZE - 1); 105 | unsafe { 106 | let data = inputs.get_unchecked(i); 107 | *outputs.get_unchecked_mut(i) = 108 | Mat4::from_scale_rotation_translation(data.0, data.1, data.2) 109 | } 110 | }) 111 | }); 112 | } 113 | 114 | criterion_group!( 115 | benches, 116 | mat4_determinant, 117 | mat4_from_srt, 118 | mat4_from_ypr, 119 | mat4_inverse, 120 | mat4_mul_mat4, 121 | mat4_mul_vec4, 122 | mat4_transform_point3, 123 | mat4_transform_point3a, 124 | mat4_transform_vector3, 125 | mat4_transform_vector3a, 126 | mat4_transpose, 127 | ); 128 | 129 | criterion_main!(benches); 130 | -------------------------------------------------------------------------------- /src/swizzles/vec2_impl_scalar.rs: -------------------------------------------------------------------------------- 1 | // Generated by swizzlegen. Do not edit. 2 | 3 | use super::Vec2Swizzles; 4 | use crate::{Vec2, Vec3, Vec4}; 5 | 6 | impl Vec2Swizzles for Vec2 { 7 | type Vec3 = Vec3; 8 | type Vec4 = Vec4; 9 | 10 | #[inline] 11 | fn xxxx(self) -> Vec4 { 12 | Vec4::new(self.x, self.x, self.x, self.x) 13 | } 14 | #[inline] 15 | fn xxxy(self) -> Vec4 { 16 | Vec4::new(self.x, self.x, self.x, self.y) 17 | } 18 | #[inline] 19 | fn xxyx(self) -> Vec4 { 20 | Vec4::new(self.x, self.x, self.y, self.x) 21 | } 22 | #[inline] 23 | fn xxyy(self) -> Vec4 { 24 | Vec4::new(self.x, self.x, self.y, self.y) 25 | } 26 | #[inline] 27 | fn xyxx(self) -> Vec4 { 28 | Vec4::new(self.x, self.y, self.x, self.x) 29 | } 30 | #[inline] 31 | fn xyxy(self) -> Vec4 { 32 | Vec4::new(self.x, self.y, self.x, self.y) 33 | } 34 | #[inline] 35 | fn xyyx(self) -> Vec4 { 36 | Vec4::new(self.x, self.y, self.y, self.x) 37 | } 38 | #[inline] 39 | fn xyyy(self) -> Vec4 { 40 | Vec4::new(self.x, self.y, self.y, self.y) 41 | } 42 | #[inline] 43 | fn yxxx(self) -> Vec4 { 44 | Vec4::new(self.y, self.x, self.x, self.x) 45 | } 46 | #[inline] 47 | fn yxxy(self) -> Vec4 { 48 | Vec4::new(self.y, self.x, self.x, self.y) 49 | } 50 | #[inline] 51 | fn yxyx(self) -> Vec4 { 52 | Vec4::new(self.y, self.x, self.y, self.x) 53 | } 54 | #[inline] 55 | fn yxyy(self) -> Vec4 { 56 | Vec4::new(self.y, self.x, self.y, self.y) 57 | } 58 | #[inline] 59 | fn yyxx(self) -> Vec4 { 60 | Vec4::new(self.y, self.y, self.x, self.x) 61 | } 62 | #[inline] 63 | fn yyxy(self) -> Vec4 { 64 | Vec4::new(self.y, self.y, self.x, self.y) 65 | } 66 | #[inline] 67 | fn yyyx(self) -> Vec4 { 68 | Vec4::new(self.y, self.y, self.y, self.x) 69 | } 70 | #[inline] 71 | fn yyyy(self) -> Vec4 { 72 | Vec4::new(self.y, self.y, self.y, self.y) 73 | } 74 | #[inline] 75 | fn xxx(self) -> Vec3 { 76 | Vec3::new(self.x, self.x, self.x) 77 | } 78 | #[inline] 79 | fn xxy(self) -> Vec3 { 80 | Vec3::new(self.x, self.x, self.y) 81 | } 82 | #[inline] 83 | fn xyx(self) -> Vec3 { 84 | Vec3::new(self.x, self.y, self.x) 85 | } 86 | #[inline] 87 | fn xyy(self) -> Vec3 { 88 | Vec3::new(self.x, self.y, self.y) 89 | } 90 | #[inline] 91 | fn yxx(self) -> Vec3 { 92 | Vec3::new(self.y, self.x, self.x) 93 | } 94 | #[inline] 95 | fn yxy(self) -> Vec3 { 96 | Vec3::new(self.y, self.x, self.y) 97 | } 98 | #[inline] 99 | fn yyx(self) -> Vec3 { 100 | Vec3::new(self.y, self.y, self.x) 101 | } 102 | #[inline] 103 | fn yyy(self) -> Vec3 { 104 | Vec3::new(self.y, self.y, self.y) 105 | } 106 | #[inline] 107 | fn xx(self) -> Self { 108 | Self::new(self.x, self.x) 109 | } 110 | #[inline] 111 | fn yx(self) -> Self { 112 | Self::new(self.y, self.x) 113 | } 114 | #[inline] 115 | fn yy(self) -> Self { 116 | Self::new(self.y, self.y) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/swizzles/dvec2_impl_scalar.rs: -------------------------------------------------------------------------------- 1 | // Generated by swizzlegen. Do not edit. 2 | 3 | use super::Vec2Swizzles; 4 | use crate::{DVec2, DVec3, DVec4}; 5 | 6 | impl Vec2Swizzles for DVec2 { 7 | type Vec3 = DVec3; 8 | type Vec4 = DVec4; 9 | 10 | #[inline] 11 | fn xxxx(self) -> DVec4 { 12 | DVec4::new(self.x, self.x, self.x, self.x) 13 | } 14 | #[inline] 15 | fn xxxy(self) -> DVec4 { 16 | DVec4::new(self.x, self.x, self.x, self.y) 17 | } 18 | #[inline] 19 | fn xxyx(self) -> DVec4 { 20 | DVec4::new(self.x, self.x, self.y, self.x) 21 | } 22 | #[inline] 23 | fn xxyy(self) -> DVec4 { 24 | DVec4::new(self.x, self.x, self.y, self.y) 25 | } 26 | #[inline] 27 | fn xyxx(self) -> DVec4 { 28 | DVec4::new(self.x, self.y, self.x, self.x) 29 | } 30 | #[inline] 31 | fn xyxy(self) -> DVec4 { 32 | DVec4::new(self.x, self.y, self.x, self.y) 33 | } 34 | #[inline] 35 | fn xyyx(self) -> DVec4 { 36 | DVec4::new(self.x, self.y, self.y, self.x) 37 | } 38 | #[inline] 39 | fn xyyy(self) -> DVec4 { 40 | DVec4::new(self.x, self.y, self.y, self.y) 41 | } 42 | #[inline] 43 | fn yxxx(self) -> DVec4 { 44 | DVec4::new(self.y, self.x, self.x, self.x) 45 | } 46 | #[inline] 47 | fn yxxy(self) -> DVec4 { 48 | DVec4::new(self.y, self.x, self.x, self.y) 49 | } 50 | #[inline] 51 | fn yxyx(self) -> DVec4 { 52 | DVec4::new(self.y, self.x, self.y, self.x) 53 | } 54 | #[inline] 55 | fn yxyy(self) -> DVec4 { 56 | DVec4::new(self.y, self.x, self.y, self.y) 57 | } 58 | #[inline] 59 | fn yyxx(self) -> DVec4 { 60 | DVec4::new(self.y, self.y, self.x, self.x) 61 | } 62 | #[inline] 63 | fn yyxy(self) -> DVec4 { 64 | DVec4::new(self.y, self.y, self.x, self.y) 65 | } 66 | #[inline] 67 | fn yyyx(self) -> DVec4 { 68 | DVec4::new(self.y, self.y, self.y, self.x) 69 | } 70 | #[inline] 71 | fn yyyy(self) -> DVec4 { 72 | DVec4::new(self.y, self.y, self.y, self.y) 73 | } 74 | #[inline] 75 | fn xxx(self) -> DVec3 { 76 | DVec3::new(self.x, self.x, self.x) 77 | } 78 | #[inline] 79 | fn xxy(self) -> DVec3 { 80 | DVec3::new(self.x, self.x, self.y) 81 | } 82 | #[inline] 83 | fn xyx(self) -> DVec3 { 84 | DVec3::new(self.x, self.y, self.x) 85 | } 86 | #[inline] 87 | fn xyy(self) -> DVec3 { 88 | DVec3::new(self.x, self.y, self.y) 89 | } 90 | #[inline] 91 | fn yxx(self) -> DVec3 { 92 | DVec3::new(self.y, self.x, self.x) 93 | } 94 | #[inline] 95 | fn yxy(self) -> DVec3 { 96 | DVec3::new(self.y, self.x, self.y) 97 | } 98 | #[inline] 99 | fn yyx(self) -> DVec3 { 100 | DVec3::new(self.y, self.y, self.x) 101 | } 102 | #[inline] 103 | fn yyy(self) -> DVec3 { 104 | DVec3::new(self.y, self.y, self.y) 105 | } 106 | #[inline] 107 | fn xx(self) -> Self { 108 | Self::new(self.x, self.x) 109 | } 110 | #[inline] 111 | fn yx(self) -> Self { 112 | Self::new(self.y, self.x) 113 | } 114 | #[inline] 115 | fn yy(self) -> Self { 116 | Self::new(self.y, self.y) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/swizzles/ivec2_impl_scalar.rs: -------------------------------------------------------------------------------- 1 | // Generated by swizzlegen. Do not edit. 2 | 3 | use super::Vec2Swizzles; 4 | use crate::{IVec2, IVec3, IVec4}; 5 | 6 | impl Vec2Swizzles for IVec2 { 7 | type Vec3 = IVec3; 8 | type Vec4 = IVec4; 9 | 10 | #[inline] 11 | fn xxxx(self) -> IVec4 { 12 | IVec4::new(self.x, self.x, self.x, self.x) 13 | } 14 | #[inline] 15 | fn xxxy(self) -> IVec4 { 16 | IVec4::new(self.x, self.x, self.x, self.y) 17 | } 18 | #[inline] 19 | fn xxyx(self) -> IVec4 { 20 | IVec4::new(self.x, self.x, self.y, self.x) 21 | } 22 | #[inline] 23 | fn xxyy(self) -> IVec4 { 24 | IVec4::new(self.x, self.x, self.y, self.y) 25 | } 26 | #[inline] 27 | fn xyxx(self) -> IVec4 { 28 | IVec4::new(self.x, self.y, self.x, self.x) 29 | } 30 | #[inline] 31 | fn xyxy(self) -> IVec4 { 32 | IVec4::new(self.x, self.y, self.x, self.y) 33 | } 34 | #[inline] 35 | fn xyyx(self) -> IVec4 { 36 | IVec4::new(self.x, self.y, self.y, self.x) 37 | } 38 | #[inline] 39 | fn xyyy(self) -> IVec4 { 40 | IVec4::new(self.x, self.y, self.y, self.y) 41 | } 42 | #[inline] 43 | fn yxxx(self) -> IVec4 { 44 | IVec4::new(self.y, self.x, self.x, self.x) 45 | } 46 | #[inline] 47 | fn yxxy(self) -> IVec4 { 48 | IVec4::new(self.y, self.x, self.x, self.y) 49 | } 50 | #[inline] 51 | fn yxyx(self) -> IVec4 { 52 | IVec4::new(self.y, self.x, self.y, self.x) 53 | } 54 | #[inline] 55 | fn yxyy(self) -> IVec4 { 56 | IVec4::new(self.y, self.x, self.y, self.y) 57 | } 58 | #[inline] 59 | fn yyxx(self) -> IVec4 { 60 | IVec4::new(self.y, self.y, self.x, self.x) 61 | } 62 | #[inline] 63 | fn yyxy(self) -> IVec4 { 64 | IVec4::new(self.y, self.y, self.x, self.y) 65 | } 66 | #[inline] 67 | fn yyyx(self) -> IVec4 { 68 | IVec4::new(self.y, self.y, self.y, self.x) 69 | } 70 | #[inline] 71 | fn yyyy(self) -> IVec4 { 72 | IVec4::new(self.y, self.y, self.y, self.y) 73 | } 74 | #[inline] 75 | fn xxx(self) -> IVec3 { 76 | IVec3::new(self.x, self.x, self.x) 77 | } 78 | #[inline] 79 | fn xxy(self) -> IVec3 { 80 | IVec3::new(self.x, self.x, self.y) 81 | } 82 | #[inline] 83 | fn xyx(self) -> IVec3 { 84 | IVec3::new(self.x, self.y, self.x) 85 | } 86 | #[inline] 87 | fn xyy(self) -> IVec3 { 88 | IVec3::new(self.x, self.y, self.y) 89 | } 90 | #[inline] 91 | fn yxx(self) -> IVec3 { 92 | IVec3::new(self.y, self.x, self.x) 93 | } 94 | #[inline] 95 | fn yxy(self) -> IVec3 { 96 | IVec3::new(self.y, self.x, self.y) 97 | } 98 | #[inline] 99 | fn yyx(self) -> IVec3 { 100 | IVec3::new(self.y, self.y, self.x) 101 | } 102 | #[inline] 103 | fn yyy(self) -> IVec3 { 104 | IVec3::new(self.y, self.y, self.y) 105 | } 106 | #[inline] 107 | fn xx(self) -> Self { 108 | Self::new(self.x, self.x) 109 | } 110 | #[inline] 111 | fn yx(self) -> Self { 112 | Self::new(self.y, self.x) 113 | } 114 | #[inline] 115 | fn yy(self) -> Self { 116 | Self::new(self.y, self.y) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/swizzles/uvec2_impl_scalar.rs: -------------------------------------------------------------------------------- 1 | // Generated by swizzlegen. Do not edit. 2 | 3 | use super::Vec2Swizzles; 4 | use crate::{UVec2, UVec3, UVec4}; 5 | 6 | impl Vec2Swizzles for UVec2 { 7 | type Vec3 = UVec3; 8 | type Vec4 = UVec4; 9 | 10 | #[inline] 11 | fn xxxx(self) -> UVec4 { 12 | UVec4::new(self.x, self.x, self.x, self.x) 13 | } 14 | #[inline] 15 | fn xxxy(self) -> UVec4 { 16 | UVec4::new(self.x, self.x, self.x, self.y) 17 | } 18 | #[inline] 19 | fn xxyx(self) -> UVec4 { 20 | UVec4::new(self.x, self.x, self.y, self.x) 21 | } 22 | #[inline] 23 | fn xxyy(self) -> UVec4 { 24 | UVec4::new(self.x, self.x, self.y, self.y) 25 | } 26 | #[inline] 27 | fn xyxx(self) -> UVec4 { 28 | UVec4::new(self.x, self.y, self.x, self.x) 29 | } 30 | #[inline] 31 | fn xyxy(self) -> UVec4 { 32 | UVec4::new(self.x, self.y, self.x, self.y) 33 | } 34 | #[inline] 35 | fn xyyx(self) -> UVec4 { 36 | UVec4::new(self.x, self.y, self.y, self.x) 37 | } 38 | #[inline] 39 | fn xyyy(self) -> UVec4 { 40 | UVec4::new(self.x, self.y, self.y, self.y) 41 | } 42 | #[inline] 43 | fn yxxx(self) -> UVec4 { 44 | UVec4::new(self.y, self.x, self.x, self.x) 45 | } 46 | #[inline] 47 | fn yxxy(self) -> UVec4 { 48 | UVec4::new(self.y, self.x, self.x, self.y) 49 | } 50 | #[inline] 51 | fn yxyx(self) -> UVec4 { 52 | UVec4::new(self.y, self.x, self.y, self.x) 53 | } 54 | #[inline] 55 | fn yxyy(self) -> UVec4 { 56 | UVec4::new(self.y, self.x, self.y, self.y) 57 | } 58 | #[inline] 59 | fn yyxx(self) -> UVec4 { 60 | UVec4::new(self.y, self.y, self.x, self.x) 61 | } 62 | #[inline] 63 | fn yyxy(self) -> UVec4 { 64 | UVec4::new(self.y, self.y, self.x, self.y) 65 | } 66 | #[inline] 67 | fn yyyx(self) -> UVec4 { 68 | UVec4::new(self.y, self.y, self.y, self.x) 69 | } 70 | #[inline] 71 | fn yyyy(self) -> UVec4 { 72 | UVec4::new(self.y, self.y, self.y, self.y) 73 | } 74 | #[inline] 75 | fn xxx(self) -> UVec3 { 76 | UVec3::new(self.x, self.x, self.x) 77 | } 78 | #[inline] 79 | fn xxy(self) -> UVec3 { 80 | UVec3::new(self.x, self.x, self.y) 81 | } 82 | #[inline] 83 | fn xyx(self) -> UVec3 { 84 | UVec3::new(self.x, self.y, self.x) 85 | } 86 | #[inline] 87 | fn xyy(self) -> UVec3 { 88 | UVec3::new(self.x, self.y, self.y) 89 | } 90 | #[inline] 91 | fn yxx(self) -> UVec3 { 92 | UVec3::new(self.y, self.x, self.x) 93 | } 94 | #[inline] 95 | fn yxy(self) -> UVec3 { 96 | UVec3::new(self.y, self.x, self.y) 97 | } 98 | #[inline] 99 | fn yyx(self) -> UVec3 { 100 | UVec3::new(self.y, self.y, self.x) 101 | } 102 | #[inline] 103 | fn yyy(self) -> UVec3 { 104 | UVec3::new(self.y, self.y, self.y) 105 | } 106 | #[inline] 107 | fn xx(self) -> Self { 108 | Self::new(self.x, self.x) 109 | } 110 | #[inline] 111 | fn yx(self) -> Self { 112 | Self::new(self.y, self.x) 113 | } 114 | #[inline] 115 | fn yy(self) -> Self { 116 | Self::new(self.y, self.y) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /benches/affine3.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::Affine3A; 8 | use std::ops::Mul; 9 | use support::*; 10 | 11 | pub fn random_srt_affine3a(rng: &mut PCG32) -> Affine3A { 12 | Affine3A::from_scale_rotation_translation( 13 | random_nonzero_vec3(rng), 14 | random_quat(rng), 15 | random_vec3(rng), 16 | ) 17 | } 18 | 19 | bench_unop!(affine3a_inverse, "affine3a inverse", op => inverse, from => random_srt_affine3a); 20 | 21 | bench_binop!( 22 | affine3a_transform_point3, 23 | "affine3a transform point3", 24 | op => transform_point3, 25 | from1 => random_srt_affine3a, 26 | from2 => random_vec3 27 | ); 28 | 29 | bench_binop!( 30 | affine3a_transform_vector3, 31 | "affine3a transform vector3", 32 | op => transform_vector3, 33 | from1 => random_srt_affine3a, 34 | from2 => random_vec3 35 | ); 36 | 37 | bench_binop!( 38 | affine3a_transform_point3a, 39 | "affine3a transform point3a", 40 | op => transform_point3a, 41 | from1 => random_srt_affine3a, 42 | from2 => random_vec3a 43 | ); 44 | 45 | bench_binop!( 46 | affine3a_transform_vector3a, 47 | "affine3a transform vector3a", 48 | op => transform_vector3a, 49 | from1 => random_srt_affine3a, 50 | from2 => random_vec3a 51 | ); 52 | 53 | bench_binop!( 54 | affine3a_mul_affine3a, 55 | "affine3a mul affine3a", 56 | op => mul, 57 | from => random_srt_affine3a 58 | ); 59 | 60 | bench_binop!(affine3a_mul_mat4, 61 | "affine3a mul mat4", 62 | op => mul, 63 | from1 => random_srt_affine3a, 64 | from2 => random_srt_mat4 65 | ); 66 | 67 | bench_binop!( 68 | mat4_mul_affine3a, 69 | "mat4 mul affine3a", 70 | op => mul, 71 | from1 => random_srt_mat4, 72 | from2 => random_srt_affine3a 73 | ); 74 | 75 | pub fn affine3a_from_srt(c: &mut Criterion) { 76 | use glam::{Quat, Vec3}; 77 | const SIZE: usize = 1 << 13; 78 | let mut rng = support::PCG32::default(); 79 | let inputs = criterion::black_box( 80 | (0..SIZE) 81 | .map(|_| { 82 | ( 83 | random_nonzero_vec3(&mut rng), 84 | random_quat(&mut rng), 85 | random_vec3(&mut rng), 86 | ) 87 | }) 88 | .collect::>(), 89 | ); 90 | let mut outputs = vec![Affine3A::IDENTITY; SIZE]; 91 | let mut i = 0; 92 | c.bench_function("affine3a from srt", |b| { 93 | b.iter(|| { 94 | i = (i + 1) & (SIZE - 1); 95 | unsafe { 96 | let data = inputs.get_unchecked(i); 97 | *outputs.get_unchecked_mut(i) = 98 | Affine3A::from_scale_rotation_translation(data.0, data.1, data.2) 99 | } 100 | }) 101 | }); 102 | } 103 | 104 | criterion_group!( 105 | benches, 106 | affine3a_from_srt, 107 | affine3a_inverse, 108 | affine3a_mul_affine3a, 109 | affine3a_mul_mat4, 110 | affine3a_transform_point3, 111 | affine3a_transform_point3a, 112 | affine3a_transform_vector3, 113 | affine3a_transform_vector3a, 114 | mat4_mul_affine3a, 115 | ); 116 | 117 | criterion_main!(benches); 118 | -------------------------------------------------------------------------------- /src/core/storage.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] 2 | #[cfg_attr(target_arch = "spirv", repr(simd))] 3 | #[cfg_attr(not(target_arch = "spirv"), repr(C))] 4 | pub struct XY { 5 | pub x: T, 6 | pub y: T, 7 | } 8 | 9 | #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] 10 | #[cfg_attr(target_arch = "spirv", repr(simd))] 11 | #[cfg_attr(not(target_arch = "spirv"), repr(C))] 12 | pub struct XYZ { 13 | pub x: T, 14 | pub y: T, 15 | pub z: T, 16 | } 17 | 18 | #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] 19 | #[cfg_attr(target_arch = "spirv", repr(simd))] 20 | #[cfg_attr(not(target_arch = "spirv"), repr(C))] 21 | pub struct XYZW { 22 | pub x: T, 23 | pub y: T, 24 | pub z: T, 25 | pub w: T, 26 | } 27 | 28 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd)] 29 | #[cfg_attr(not(target_arch = "spirv"), repr(C))] 30 | pub struct Columns2 { 31 | pub x_axis: V, 32 | pub y_axis: V, 33 | } 34 | 35 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd)] 36 | #[cfg_attr(not(target_arch = "spirv"), repr(C))] 37 | pub struct Columns3 { 38 | pub x_axis: V, 39 | pub y_axis: V, 40 | pub z_axis: V, 41 | } 42 | 43 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd)] 44 | #[cfg_attr(not(target_arch = "spirv"), repr(C))] 45 | pub struct Columns4 { 46 | pub x_axis: V, 47 | pub y_axis: V, 48 | pub z_axis: V, 49 | pub w_axis: V, 50 | } 51 | 52 | /// The `XYZF32A16` is used for the `Vec3A` type, that is a 16 btye aligned `XYZ` type. 53 | #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] 54 | #[cfg_attr(target_arch = "spirv", repr(simd))] 55 | #[cfg_attr(not(target_arch = "spirv"), repr(align(16), C))] 56 | pub struct XYZF32A16 { 57 | pub x: f32, 58 | pub y: f32, 59 | pub z: f32, 60 | } 61 | 62 | impl From> for XYZF32A16 { 63 | #[inline(always)] 64 | fn from(v: XYZW) -> Self { 65 | Self { 66 | x: v.x, 67 | y: v.y, 68 | z: v.z, 69 | } 70 | } 71 | } 72 | 73 | impl From> for XYZF32A16 { 74 | #[inline(always)] 75 | fn from(v: XYZ) -> Self { 76 | Self { 77 | x: v.x, 78 | y: v.y, 79 | z: v.z, 80 | } 81 | } 82 | } 83 | 84 | impl From for XYZ { 85 | #[inline(always)] 86 | fn from(v: XYZF32A16) -> Self { 87 | Self { 88 | x: v.x, 89 | y: v.y, 90 | z: v.z, 91 | } 92 | } 93 | } 94 | 95 | impl From for XY { 96 | #[inline(always)] 97 | fn from(v: XYZF32A16) -> Self { 98 | Self { x: v.x, y: v.y } 99 | } 100 | } 101 | 102 | #[derive(Clone, Copy, Default, PartialEq, PartialOrd)] 103 | #[repr(align(16))] 104 | pub(crate) struct Align16(pub T); 105 | 106 | impl Align16 { 107 | #[allow(dead_code)] 108 | pub fn as_ptr(&self) -> *const T { 109 | &self.0 110 | } 111 | 112 | #[allow(dead_code)] 113 | pub fn as_mut_ptr(&mut self) -> *mut T { 114 | &mut self.0 115 | } 116 | } 117 | 118 | #[test] 119 | fn test_align16() { 120 | use core::{mem, ptr}; 121 | let mut a = Align16::(1.0); 122 | assert_eq!(mem::align_of_val(&a), 16); 123 | unsafe { 124 | assert_eq!(ptr::read(a.as_ptr()).to_bits(), f32::to_bits(1.0)); 125 | ptr::write(a.as_mut_ptr(), -1.0); 126 | } 127 | assert_eq!(a.0.to_bits(), f32::to_bits(-1.0)); 128 | } 129 | -------------------------------------------------------------------------------- /src/mat.rs: -------------------------------------------------------------------------------- 1 | // Adds common vector trait implementations. 2 | // The traits here should be supported for all types of $t and all sizes of vector. 3 | macro_rules! impl_matn_common_traits { 4 | ($t:ty, $matn:ident, $vecn:ident) => { 5 | impl Default for $matn { 6 | #[inline(always)] 7 | fn default() -> Self { 8 | Self::IDENTITY 9 | } 10 | } 11 | 12 | impl Add<$matn> for $matn { 13 | type Output = Self; 14 | #[inline(always)] 15 | fn add(self, other: Self) -> Self::Output { 16 | Self(self.0.add_matrix(&other.0)) 17 | } 18 | } 19 | 20 | impl AddAssign<$matn> for $matn { 21 | #[inline(always)] 22 | fn add_assign(&mut self, other: Self) { 23 | self.0 = self.0.add_matrix(&other.0); 24 | } 25 | } 26 | 27 | impl Sub<$matn> for $matn { 28 | type Output = Self; 29 | #[inline(always)] 30 | fn sub(self, other: Self) -> Self::Output { 31 | Self(self.0.sub_matrix(&other.0)) 32 | } 33 | } 34 | 35 | impl SubAssign<$matn> for $matn { 36 | #[inline(always)] 37 | fn sub_assign(&mut self, other: Self) { 38 | self.0 = self.0.sub_matrix(&other.0); 39 | } 40 | } 41 | 42 | impl Mul<$matn> for $matn { 43 | type Output = Self; 44 | #[inline(always)] 45 | fn mul(self, other: Self) -> Self::Output { 46 | Self(self.0.mul_matrix(&other.0)) 47 | } 48 | } 49 | 50 | impl MulAssign<$matn> for $matn { 51 | #[inline(always)] 52 | fn mul_assign(&mut self, other: Self) { 53 | self.0 = self.0.mul_matrix(&other.0); 54 | } 55 | } 56 | 57 | impl Mul<$vecn> for $matn { 58 | type Output = $vecn; 59 | #[inline(always)] 60 | fn mul(self, other: $vecn) -> Self::Output { 61 | $vecn(self.0.mul_vector(other.0)) 62 | } 63 | } 64 | 65 | impl Mul<$matn> for $t { 66 | type Output = $matn; 67 | #[inline(always)] 68 | fn mul(self, other: $matn) -> Self::Output { 69 | $matn(other.0.mul_scalar(self)) 70 | } 71 | } 72 | 73 | impl Mul<$t> for $matn { 74 | type Output = Self; 75 | #[inline(always)] 76 | fn mul(self, other: $t) -> Self::Output { 77 | Self(self.0.mul_scalar(other)) 78 | } 79 | } 80 | 81 | impl MulAssign<$t> for $matn { 82 | #[inline(always)] 83 | fn mul_assign(&mut self, other: $t) { 84 | self.0 = self.0.mul_scalar(other); 85 | } 86 | } 87 | 88 | impl<'a> Sum<&'a Self> for $matn { 89 | fn sum(iter: I) -> Self 90 | where 91 | I: Iterator, 92 | { 93 | iter.fold(Self::ZERO, |a, &b| Self::add(a, b)) 94 | } 95 | } 96 | 97 | impl<'a> Product<&'a Self> for $matn { 98 | fn product(iter: I) -> Self 99 | where 100 | I: Iterator, 101 | { 102 | iter.fold(Self::IDENTITY, |a, &b| Self::mul(a, b)) 103 | } 104 | } 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /benches/transform.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | 3 | #[path = "support/macros.rs"] 4 | #[macro_use] 5 | mod macros; 6 | mod support; 7 | 8 | use criterion::{criterion_group, criterion_main, Criterion}; 9 | use glam::{TransformRT, TransformSRT}; 10 | use std::ops::Mul; 11 | use support::*; 12 | 13 | fn random_transformsrt(rng: &mut PCG32) -> TransformSRT { 14 | TransformSRT::from_scale_rotation_translation( 15 | random_nonzero_vec3(rng), 16 | random_quat(rng), 17 | random_vec3(rng), 18 | ) 19 | } 20 | 21 | fn random_transformrt(rng: &mut PCG32) -> TransformRT { 22 | TransformRT::from_rotation_translation(random_quat(rng), random_vec3(rng)) 23 | } 24 | 25 | bench_unop!( 26 | transformrt_inverse, 27 | "transform_rt inverse", 28 | op => inverse, 29 | from => random_transformrt 30 | ); 31 | 32 | bench_unop!( 33 | transformsrt_inverse, 34 | "transform_srt inverse", 35 | op => inverse, 36 | from => random_transformsrt 37 | ); 38 | 39 | bench_binop!( 40 | transformrt_transform_point3, 41 | "transform_rt transform point3", 42 | op => transform_point3, 43 | from1 => random_transformrt, 44 | from2 => random_vec3 45 | ); 46 | 47 | bench_binop!( 48 | transformrt_transform_point3a, 49 | "transform_rt transform point3a", 50 | op => transform_point3a, 51 | from1 => random_transformrt, 52 | from2 => random_vec3a 53 | ); 54 | 55 | bench_binop!( 56 | transformrt_transform_vector3, 57 | "transform_rt transform vector3", 58 | op => transform_vector3, 59 | from1 => random_transformrt, 60 | from2 => random_vec3 61 | ); 62 | 63 | bench_binop!( 64 | transformrt_transform_vector3a, 65 | "transform_rt transform vector3a", 66 | op => transform_vector3a, 67 | from1 => random_transformrt, 68 | from2 => random_vec3a 69 | ); 70 | 71 | bench_binop!( 72 | transformsrt_transform_point3, 73 | "transform_srt transform point3", 74 | op => transform_point3, 75 | from1 => random_transformsrt, 76 | from2 => random_vec3 77 | ); 78 | 79 | bench_binop!( 80 | transformsrt_transform_point3a, 81 | "transform_srt transform point3a", 82 | op => transform_point3a, 83 | from1 => random_transformsrt, 84 | from2 => random_vec3a 85 | ); 86 | 87 | bench_binop!( 88 | transformsrt_transform_vector3, 89 | "transform_srt transform vector3", 90 | op => transform_vector3, 91 | from1 => random_transformsrt, 92 | from2 => random_vec3 93 | ); 94 | 95 | bench_binop!( 96 | transformsrt_transform_vector3a, 97 | "transform_srt transform vector3a", 98 | op => transform_vector3a, 99 | from1 => random_transformsrt, 100 | from2 => random_vec3a 101 | ); 102 | bench_binop!( 103 | transformsrt_mul_transformsrt, 104 | "transform_srt mul transform_srt", 105 | op => mul, 106 | from => random_transformsrt 107 | ); 108 | 109 | bench_binop!( 110 | transformrt_mul_transformrt, 111 | "transform_rt mul transform_rt", 112 | op => mul, 113 | from => random_transformrt 114 | ); 115 | 116 | criterion_group!( 117 | benches, 118 | transformrt_inverse, 119 | transformrt_mul_transformrt, 120 | transformrt_transform_point3, 121 | transformrt_transform_point3a, 122 | transformrt_transform_vector3, 123 | transformrt_transform_vector3a, 124 | transformsrt_inverse, 125 | transformsrt_mul_transformsrt, 126 | transformsrt_transform_point3, 127 | transformsrt_transform_point3a, 128 | transformsrt_transform_vector3, 129 | transformsrt_transform_vector3a, 130 | ); 131 | 132 | criterion_main!(benches); 133 | -------------------------------------------------------------------------------- /src/cast.rs: -------------------------------------------------------------------------------- 1 | use crate::{DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4}; 2 | use crate::{IVec2, IVec3, IVec4}; 3 | use crate::{Mat2, Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4}; 4 | use crate::{UVec2, UVec3, UVec4}; 5 | #[cfg(target_feature = "simd128")] 6 | use core::arch::wasm32::v128; 7 | #[cfg(target_arch = "x86")] 8 | use core::arch::x86::*; 9 | #[cfg(target_arch = "x86_64")] 10 | use core::arch::x86_64::*; 11 | 12 | #[repr(C)] 13 | pub union Vec4Cast { 14 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 15 | pub m128: __m128, 16 | #[cfg(target_feature = "simd128")] 17 | pub v128: v128, 18 | pub fx4: [f32; 4], 19 | pub fx2x2: [[f32; 2]; 2], 20 | pub v4: Vec4, 21 | pub v3a: Vec3A, 22 | pub q: Quat, 23 | } 24 | 25 | #[repr(C)] 26 | pub union Vec3Cast { 27 | pub fx3: [f32; 3], 28 | pub v3: Vec3, 29 | } 30 | 31 | #[repr(C)] 32 | pub union Vec2Cast { 33 | pub fx2: [f32; 2], 34 | pub v2: Vec2, 35 | } 36 | 37 | #[repr(C)] 38 | pub union F32x9Cast { 39 | pub fx3x3: [[f32; 3]; 3], 40 | pub fx9: [f32; 9], 41 | } 42 | 43 | #[repr(C)] 44 | pub union F32x16Cast { 45 | pub fx4x4: [[f32; 4]; 4], 46 | pub fx16: [f32; 16], 47 | } 48 | 49 | #[repr(C)] 50 | pub union Mat4Cast { 51 | pub v4x4: [Vec4; 4], 52 | pub m4: Mat4, 53 | } 54 | 55 | #[repr(C)] 56 | pub union Mat3Cast { 57 | pub v3x3: [Vec3; 3], 58 | pub m3: Mat3, 59 | } 60 | 61 | #[repr(C)] 62 | pub union Mat3ACast { 63 | pub v3x3: [Vec3A; 3], 64 | pub m3: Mat3A, 65 | } 66 | 67 | #[repr(C)] 68 | pub union Mat2Cast { 69 | pub v2x2: [Vec2; 2], 70 | pub m2: Mat2, 71 | } 72 | 73 | #[repr(C)] 74 | pub union DVec4Cast { 75 | pub fx4: [f64; 4], 76 | pub fx2x2: [[f64; 2]; 2], 77 | pub v4: DVec4, 78 | pub q: DQuat, 79 | } 80 | 81 | #[repr(C)] 82 | pub union DVec3Cast { 83 | pub fx3: [f64; 3], 84 | pub v3: DVec3, 85 | } 86 | 87 | #[repr(C)] 88 | pub union DVec2Cast { 89 | pub fx2: [f64; 2], 90 | pub v2: DVec2, 91 | } 92 | 93 | #[repr(C)] 94 | pub union F64x9Cast { 95 | pub fx3x3: [[f64; 3]; 3], 96 | pub fx9: [f64; 9], 97 | } 98 | 99 | #[repr(C)] 100 | pub union F64x16Cast { 101 | pub fx4x4: [[f64; 4]; 4], 102 | pub fx16: [f64; 16], 103 | } 104 | 105 | #[repr(C)] 106 | pub union DMat4Cast { 107 | pub v4x4: [DVec4; 4], 108 | pub m4: DMat4, 109 | } 110 | 111 | #[repr(C)] 112 | pub union DMat3Cast { 113 | pub v3x3: [DVec3; 3], 114 | pub m3: DMat3, 115 | } 116 | 117 | #[repr(C)] 118 | pub union DMat2Cast { 119 | pub v2x2: [DVec2; 2], 120 | pub m2: DMat2, 121 | } 122 | 123 | #[repr(C)] 124 | pub union IVec4Cast { 125 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 126 | pub m128: __m128i, 127 | #[cfg(target_feature = "simd128")] 128 | pub v128: v128, 129 | pub ix4: [i32; 4], 130 | pub ix2x2: [[i32; 2]; 2], 131 | pub v4: IVec4, 132 | } 133 | 134 | #[repr(C)] 135 | pub union IVec3Cast { 136 | pub ix3: [i32; 3], 137 | pub v3: IVec3, 138 | } 139 | 140 | #[repr(C)] 141 | pub union IVec2Cast { 142 | pub ix2: [i32; 2], 143 | pub v2: IVec2, 144 | } 145 | 146 | #[repr(C)] 147 | pub union UVec4Cast { 148 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 149 | pub m128: __m128, 150 | #[cfg(target_feature = "simd128")] 151 | pub v128: v128, 152 | pub ux4: [u32; 4], 153 | pub ux2x2: [[u32; 2]; 2], 154 | pub v4: UVec4, 155 | } 156 | 157 | #[repr(C)] 158 | pub union UVec3Cast { 159 | pub ux3: [u32; 3], 160 | pub v3: UVec3, 161 | } 162 | 163 | #[repr(C)] 164 | pub union UVec2Cast { 165 | pub ux2: [u32; 2], 166 | pub v2: UVec2, 167 | } 168 | -------------------------------------------------------------------------------- /benches/support/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use core::f32; 3 | use glam::{Mat2, Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4}; 4 | 5 | pub struct PCG32 { 6 | state: u64, 7 | inc: u64, 8 | } 9 | 10 | impl PCG32 { 11 | pub fn seed(initstate: u64, initseq: u64) -> Self { 12 | let mut rng = PCG32 { 13 | state: 0, 14 | inc: (initseq << 1) | 1, 15 | }; 16 | rng.next_u32(); 17 | rng.state = rng.state.wrapping_add(initstate); 18 | rng.next_u32(); 19 | rng 20 | } 21 | 22 | pub fn default() -> Self { 23 | PCG32::seed(0x853c49e6748fea9b, 0xda3e39cb94b95bdb) 24 | } 25 | 26 | pub fn next_u32(&mut self) -> u32 { 27 | let oldstate = self.state; 28 | self.state = oldstate 29 | .wrapping_mul(6364136223846793005) 30 | .wrapping_add(self.inc | 1); 31 | let xorshifted = ((oldstate >> 18) ^ oldstate) >> 27; 32 | let rot = oldstate >> 59; 33 | ((xorshifted >> rot) | (xorshifted << (rot.wrapping_neg() & 31))) as u32 34 | } 35 | 36 | pub fn next_f32(&mut self) -> f32 { 37 | (self.next_u32() & 0xffffff) as f32 / 16777216.0 38 | } 39 | } 40 | 41 | pub fn random_vec2(rng: &mut PCG32) -> Vec2 { 42 | Vec2::new(rng.next_f32(), rng.next_f32()) 43 | } 44 | 45 | pub fn random_vec3(rng: &mut PCG32) -> Vec3 { 46 | Vec3::new(rng.next_f32(), rng.next_f32(), rng.next_f32()) 47 | } 48 | 49 | pub fn random_vec3a(rng: &mut PCG32) -> Vec3A { 50 | Vec3A::new(rng.next_f32(), rng.next_f32(), rng.next_f32()) 51 | } 52 | 53 | pub fn random_vec4(rng: &mut PCG32) -> Vec4 { 54 | Vec4::new( 55 | rng.next_f32(), 56 | rng.next_f32(), 57 | rng.next_f32(), 58 | rng.next_f32(), 59 | ) 60 | } 61 | 62 | pub fn random_nonzero_vec2(rng: &mut PCG32) -> Vec2 { 63 | loop { 64 | let v = random_vec2(rng); 65 | if v.length_squared() > 0.01 { 66 | return v; 67 | } 68 | } 69 | } 70 | 71 | pub fn random_nonzero_vec3(rng: &mut PCG32) -> Vec3 { 72 | loop { 73 | let v = random_vec3(rng); 74 | if v.length_squared() > 0.01 { 75 | return v; 76 | } 77 | } 78 | } 79 | 80 | pub fn random_f32(rng: &mut PCG32) -> f32 { 81 | rng.next_f32() 82 | } 83 | 84 | pub fn random_radians(rng: &mut PCG32) -> f32 { 85 | -f32::consts::PI + rng.next_f32() * 2.0 * f32::consts::PI 86 | } 87 | 88 | pub fn random_quat(rng: &mut PCG32) -> Quat { 89 | let yaw = random_radians(rng); 90 | let pitch = random_radians(rng); 91 | let roll = random_radians(rng); 92 | Quat::from_euler(glam::EulerRot::YXZ, yaw, pitch, roll) 93 | } 94 | 95 | pub fn random_mat2(rng: &mut PCG32) -> Mat2 { 96 | Mat2::from_cols(random_vec2(rng), random_vec2(rng)) 97 | } 98 | 99 | pub fn random_mat3(rng: &mut PCG32) -> Mat3 { 100 | Mat3::from_cols(random_vec3(rng), random_vec3(rng), random_vec3(rng)) 101 | } 102 | 103 | pub fn random_srt_mat3(rng: &mut PCG32) -> Mat3 { 104 | Mat3::from_scale_angle_translation( 105 | random_nonzero_vec2(rng), 106 | random_radians(rng), 107 | random_vec2(rng), 108 | ) 109 | } 110 | 111 | pub fn random_mat3a(rng: &mut PCG32) -> Mat3A { 112 | Mat3A::from_cols(random_vec3a(rng), random_vec3a(rng), random_vec3a(rng)) 113 | } 114 | 115 | pub fn random_srt_mat3a(rng: &mut PCG32) -> Mat3A { 116 | Mat3A::from_scale_angle_translation( 117 | random_nonzero_vec2(rng), 118 | random_radians(rng), 119 | random_vec2(rng), 120 | ) 121 | } 122 | 123 | pub fn random_srt_mat4(rng: &mut PCG32) -> Mat4 { 124 | Mat4::from_scale_rotation_translation( 125 | random_nonzero_vec3(rng), 126 | random_quat(rng), 127 | random_vec3(rng), 128 | ) 129 | } 130 | -------------------------------------------------------------------------------- /benches/vec3.rs: -------------------------------------------------------------------------------- 1 | #[path = "support/macros.rs"] 2 | #[macro_use] 3 | mod macros; 4 | mod support; 5 | 6 | use criterion::{criterion_group, criterion_main, Criterion}; 7 | use glam::Vec3; 8 | use std::ops::Mul; 9 | use support::*; 10 | 11 | bench_binop!( 12 | vec3_mul_vec3, 13 | "vec3 mul vec3", 14 | op => mul, 15 | from1 => random_vec3, 16 | from2 => random_vec3 17 | ); 18 | 19 | #[inline] 20 | fn vec3_to_rgb_op(v: Vec3) -> u32 { 21 | let (red, green, blue) = (v.min(Vec3::ONE).max(Vec3::ZERO) * 255.0).into(); 22 | ((red as u32) << 16 | (green as u32) << 8 | (blue as u32)).into() 23 | } 24 | 25 | #[inline] 26 | fn vec3_fields(v: Vec3) -> [f32; 3] { 27 | [v.x, v.y, v.z] 28 | } 29 | 30 | #[inline] 31 | fn vec3_into_array(v: Vec3) -> [f32; 3] { 32 | v.into() 33 | } 34 | 35 | #[inline] 36 | fn vec3_into_tuple(v: Vec3) -> (f32, f32, f32) { 37 | v.into() 38 | } 39 | 40 | bench_func!( 41 | vec3_to_rgb, 42 | "vec3 to rgb", 43 | op => vec3_to_rgb_op, 44 | from => random_vec3 45 | ); 46 | 47 | bench_func!( 48 | vec3_to_array_fields, 49 | "vec3 into array fields", 50 | op => vec3_fields, 51 | from => random_vec3 52 | ); 53 | 54 | bench_func!( 55 | vec3_to_array_into, 56 | "vec3 into array fast", 57 | op => vec3_into_array, 58 | from => random_vec3 59 | ); 60 | 61 | bench_func!( 62 | vec3_to_tuple_into, 63 | "vec3 into tuple fast", 64 | op => vec3_into_tuple, 65 | from => random_vec3 66 | ); 67 | 68 | // --- 69 | 70 | #[inline] 71 | fn vec3_normalize(v: Vec3) -> Vec3 { 72 | v.normalize() 73 | } 74 | 75 | bench_func!( 76 | vec3_normalize_bench, 77 | "vec3 normalize", 78 | op => vec3_normalize, 79 | from => random_vec3 80 | ); 81 | 82 | #[inline] 83 | fn vec3_normalize_or_zero(v: Vec3) -> Vec3 { 84 | v.normalize_or_zero() 85 | } 86 | 87 | bench_func!( 88 | vec3_normalize_or_zero_bench, 89 | "vec3 normalize_or_zero", 90 | op => vec3_normalize_or_zero, 91 | from => random_vec3 92 | ); 93 | 94 | // --- 95 | 96 | #[inline(always)] 97 | fn vec3_any_orthogonal_vector(v: Vec3) -> Vec3 { 98 | v.any_orthogonal_vector() 99 | } 100 | 101 | bench_func!( 102 | vec3_any_orthogonal_vector_bench, 103 | "vec3 any_orthogonal_vector", 104 | op => vec3_any_orthogonal_vector, 105 | from => random_vec3 106 | ); 107 | 108 | #[inline(always)] 109 | fn vec3_any_orthonormal_vector(v: Vec3) -> Vec3 { 110 | v.any_orthonormal_vector() 111 | } 112 | 113 | bench_func!( 114 | vec3_any_orthonormal_vector_bench, 115 | "vec3 any_orthonormal_vector", 116 | op => vec3_any_orthonormal_vector, 117 | from => random_vec3 118 | ); 119 | 120 | #[inline(always)] 121 | fn vec3_any_orthonormal_pair(v: Vec3) -> (Vec3, Vec3) { 122 | v.any_orthonormal_pair() 123 | } 124 | 125 | bench_func!( 126 | vec3_any_orthonormal_pair_bench, 127 | "vec3 any_orthonormal_pair", 128 | op => vec3_any_orthonormal_pair, 129 | from => random_vec3 130 | ); 131 | 132 | // --- 133 | 134 | euler!(vec3_euler, "vec3 euler", ty => Vec3, storage => Vec3, zero => Vec3::ZERO, rand => random_vec3); 135 | 136 | bench_binop!( 137 | vec3_angle_between, 138 | "vec3 angle_between", 139 | op => angle_between, 140 | from1 => random_vec3, 141 | from2 => random_vec3 142 | ); 143 | 144 | bench_select!( 145 | vec3_select, 146 | "vec3 select", 147 | ty => Vec3, 148 | op => cmple, 149 | from => random_vec3 150 | ); 151 | 152 | criterion_group!( 153 | benches, 154 | vec3_mul_vec3, 155 | vec3_angle_between, 156 | vec3_normalize_bench, 157 | vec3_normalize_or_zero_bench, 158 | vec3_any_orthogonal_vector_bench, 159 | vec3_any_orthonormal_vector_bench, 160 | vec3_any_orthonormal_pair_bench, 161 | vec3_euler, 162 | vec3_select, 163 | vec3_to_array_fields, 164 | vec3_to_array_into, 165 | vec3_to_rgb, 166 | vec3_to_tuple_into, 167 | ); 168 | 169 | criterion_main!(benches); 170 | -------------------------------------------------------------------------------- /ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | # Attribution 2 | 3 | `glam` contains code ported from the following C++ libraries. 4 | 5 | ## [DirectXMath] 6 | 7 | [DirectXMath]: https://docs.microsoft.com/en-us/windows/win32/dxmath/directxmath-portal 8 | 9 | [The MIT License (MIT)](https://github.com/microsoft/DirectXMath/blob/master/LICENSE) 10 | 11 | Copyright (c) 2011-2020 Microsoft Corp 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 14 | software and associated documentation files (the "Software"), to deal in the Software 15 | without restriction, including without limitation the rights to use, copy, modify, 16 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to the following 18 | conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all copies 21 | or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 24 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 25 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 26 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 28 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | ## [Realtime Math] 31 | 32 | [Realtime Math]: https://github.com/nfrechette/rtm 33 | 34 | [MIT License](https://github.com/nfrechette/rtm/blob/develop/LICENSE) 35 | 36 | Copyright (c) 2018 Nicholas Frechette 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in all 46 | copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. 55 | 56 | ## [GLM] 57 | 58 | [GLM]: https://glm.g-truc.net 59 | 60 | [The MIT License](https://github.com/g-truc/glm/blob/master/copying.txt) 61 | 62 | Copyright (c) 2005 - G-Truc Creation 63 | 64 | Permission is hereby granted, free of charge, to any person obtaining a copy 65 | of this software and associated documentation files (the "Software"), to deal 66 | in the Software without restriction, including without limitation the rights 67 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 68 | copies of the Software, and to permit persons to whom the Software is 69 | furnished to do so, subject to the following conditions: 70 | 71 | The above copyright notice and this permission notice shall be included in 72 | all copies or substantial portions of the Software. 73 | 74 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 75 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 76 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 77 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 78 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 79 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 80 | THE SOFTWARE. 81 | -------------------------------------------------------------------------------- /src/core/wasm32/float.rs: -------------------------------------------------------------------------------- 1 | use core::arch::wasm32::*; 2 | 3 | macro_rules! const_u32x4 { 4 | ($ux4:expr) => { 5 | unsafe { $crate::cast::UVec4Cast { ux4: $ux4 }.v128 } 6 | }; 7 | } 8 | 9 | const PS_NEGATIVE_ZERO: v128 = const_u32x4!([0x8000_0000; 4]); 10 | const PS_PI: v128 = const_f32x4!([core::f32::consts::PI; 4]); 11 | const PS_HALF_PI: v128 = const_f32x4!([core::f32::consts::FRAC_PI_2; 4]); 12 | const PS_SIN_COEFFICIENTS0: v128 = 13 | const_f32x4!([-0.16666667, 0.008_333_331, -0.00019840874, 2.752_556_2e-6]); 14 | const PS_SIN_COEFFICIENTS1: v128 = const_f32x4!([ 15 | -2.388_985_9e-8, 16 | -0.16665852, /*Est1*/ 17 | 0.008_313_95, /*Est2*/ 18 | -0.000_185_246_7 /*Est3*/ 19 | ]); 20 | const PS_ONE: v128 = const_f32x4!([1.0; 4]); 21 | const PS_TWO_PI: v128 = const_f32x4!([core::f32::consts::TAU; 4]); 22 | const PS_RECIPROCAL_TWO_PI: v128 = const_f32x4!([0.159_154_94; 4]); 23 | 24 | #[inline(always)] 25 | pub(crate) fn v128_mul_add(a: v128, b: v128, c: v128) -> v128 { 26 | f32x4_add(f32x4_mul(a, b), c) 27 | } 28 | 29 | #[inline(always)] 30 | pub(crate) fn v128_neg_mul_sub(a: v128, b: v128, c: v128) -> v128 { 31 | f32x4_sub(c, f32x4_mul(a, b)) 32 | } 33 | 34 | /// Returns a vector whose components are the corresponding components of Angles modulo 2PI. 35 | #[inline] 36 | pub(crate) fn v128_mod_angles(angles: v128) -> v128 { 37 | // Based on https://github.com/microsoft/DirectXMath `XMVectorModAngles` 38 | let v = f32x4_mul(angles, PS_RECIPROCAL_TWO_PI); 39 | let v = f32x4_nearest(v); 40 | v128_neg_mul_sub(PS_TWO_PI, v, angles) 41 | } 42 | 43 | /// Computes the sine of the angle in each lane of `v`. Values outside 44 | /// the bounds of PI may produce an increasing error as the input angle 45 | /// drifts from `[-PI, PI]`. 46 | #[inline] 47 | pub(crate) fn v128_sin(v: v128) -> v128 { 48 | // Based on https://github.com/microsoft/DirectXMath `XMVectorSin` 49 | 50 | // 11-degree minimax approximation 51 | 52 | // Force the value within the bounds of pi 53 | let mut x = v128_mod_angles(v); 54 | 55 | // Map in [-pi/2,pi/2] with sin(y) = sin(x). 56 | let sign = v128_and(x, PS_NEGATIVE_ZERO); 57 | // pi when x >= 0, -pi when x < 0 58 | let c = v128_or(PS_PI, sign); 59 | // |x| 60 | let absx = v128_andnot(sign, x); 61 | let rflx = f32x4_sub(c, x); 62 | let comp = f32x4_le(absx, PS_HALF_PI); 63 | let select0 = v128_and(comp, x); 64 | let select1 = v128_andnot(comp, rflx); 65 | x = v128_or(select0, select1); 66 | 67 | let x2 = f32x4_mul(x, x); 68 | 69 | // Compute polynomial approximation 70 | const SC1: v128 = PS_SIN_COEFFICIENTS1; 71 | let v_constants_b = i32x4_shuffle::<0, 0, 4, 4>(SC1, SC1); 72 | 73 | const SC0: v128 = PS_SIN_COEFFICIENTS0; 74 | let mut v_constants = i32x4_shuffle::<3, 3, 7, 7>(SC0, SC0); 75 | let mut result = v128_mul_add(v_constants_b, x2, v_constants); 76 | 77 | v_constants = i32x4_shuffle::<2, 2, 6, 6>(SC0, SC0); 78 | result = v128_mul_add(result, x2, v_constants); 79 | 80 | v_constants = i32x4_shuffle::<1, 1, 5, 5>(SC0, SC0); 81 | result = v128_mul_add(result, x2, v_constants); 82 | 83 | v_constants = i32x4_shuffle::<0, 0, 4, 4>(SC0, SC0); 84 | result = v128_mul_add(result, x2, v_constants); 85 | 86 | result = v128_mul_add(result, x2, PS_ONE); 87 | result = f32x4_mul(result, x); 88 | 89 | result 90 | } 91 | 92 | #[test] 93 | fn test_wasm32_v128_sin() { 94 | use crate::core::traits::vector::*; 95 | use core::f32::consts::PI; 96 | 97 | fn test_wasm32_v128_sin_angle(a: f32) { 98 | let v = v128_sin(f32x4_splat(a)); 99 | let v = v.as_ref_xyzw(); 100 | let a_sin = a.sin(); 101 | // dbg!((a, a_sin, v)); 102 | assert!(v.abs_diff_eq(Vector::splat(a_sin), 1e-6)); 103 | } 104 | 105 | let mut a = -PI; 106 | let end = PI; 107 | let step = PI / 8192.0; 108 | 109 | while a <= end { 110 | test_wasm32_v128_sin_angle(a); 111 | a += step; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /tests/transform.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | 3 | #[cfg(feature = "transform-types")] 4 | #[macro_use] 5 | mod support; 6 | 7 | #[cfg(feature = "transform-types")] 8 | mod transform { 9 | use crate::support::FloatCompare; 10 | use glam::*; 11 | 12 | impl FloatCompare for TransformSRT { 13 | #[inline] 14 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 15 | self.abs_diff_eq(*other, max_abs_diff) 16 | } 17 | 18 | #[inline] 19 | fn abs_diff(&self, other: &Self) -> Self { 20 | Self::from_scale_rotation_translation( 21 | self.scale.abs_diff(&other.scale), 22 | self.rotation.abs_diff(&other.rotation), 23 | self.translation.abs_diff(&other.translation), 24 | ) 25 | } 26 | } 27 | 28 | impl FloatCompare for TransformRT { 29 | #[inline] 30 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 31 | self.abs_diff_eq(*other, max_abs_diff) 32 | } 33 | 34 | #[inline] 35 | fn abs_diff(&self, other: &Self) -> Self { 36 | Self::from_rotation_translation( 37 | self.rotation.abs_diff(&other.rotation), 38 | self.translation.abs_diff(&other.translation), 39 | ) 40 | } 41 | } 42 | 43 | #[test] 44 | fn test_identity() { 45 | let tr = TransformRT::IDENTITY; 46 | assert_eq!(tr.rotation, Quat::IDENTITY); 47 | assert_eq!(tr.translation, Vec3::ZERO); 48 | 49 | let srt = TransformSRT::IDENTITY; 50 | assert_eq!(srt.scale, Vec3::ONE); 51 | assert_eq!(srt.rotation, Quat::IDENTITY); 52 | assert_eq!(srt.translation, Vec3::ZERO); 53 | 54 | assert_eq!(srt, tr.into()); 55 | 56 | assert_eq!(TransformRT::IDENTITY, TransformRT::default()); 57 | assert_eq!(TransformSRT::IDENTITY, TransformSRT::default()); 58 | } 59 | 60 | #[test] 61 | fn test_nan() { 62 | assert!(TransformRT::NAN.is_nan()); 63 | assert!(!TransformRT::NAN.is_finite()); 64 | 65 | assert!(TransformSRT::NAN.is_nan()); 66 | assert!(!TransformSRT::NAN.is_finite()); 67 | } 68 | 69 | #[test] 70 | fn test_new() { 71 | let t = Vec3::new(1.0, 2.0, 3.0); 72 | let r = Quat::from_rotation_y(90.0_f32.to_radians()); 73 | let s = Vec3::new(-1.0, -2.0, -3.0); 74 | 75 | let tr = TransformRT::from_rotation_translation(r, t); 76 | assert_eq!(tr.rotation, r); 77 | assert_eq!(tr.translation, t); 78 | 79 | let srt = TransformSRT::from_scale_rotation_translation(s, r, t); 80 | assert_eq!(srt.scale, s); 81 | assert_eq!(srt.rotation, r); 82 | assert_eq!(srt.translation, t); 83 | 84 | assert_eq!(tr, tr); 85 | assert_eq!(srt, srt); 86 | } 87 | 88 | #[test] 89 | fn test_mul() { 90 | let tr = TransformRT::from_rotation_translation( 91 | Quat::from_rotation_z(-90.0_f32.to_radians()), 92 | Vec3::X, 93 | ); 94 | let v0 = Vec3A::Y; 95 | let v1 = tr.transform_point3a(v0); 96 | assert_approx_eq!(v1, Vec3A::X * 2.0); 97 | assert_approx_eq!(v1, tr.transform_point3a(v0)); 98 | let inv_tr = tr.inverse(); 99 | let v2 = inv_tr.transform_point3a(v1); 100 | assert_approx_eq!(v0, v2); 101 | 102 | assert_eq!(tr * TransformRT::IDENTITY, tr); 103 | assert_approx_eq!(tr * inv_tr, TransformRT::IDENTITY); 104 | 105 | assert_eq!(tr * TransformSRT::IDENTITY, TransformSRT::from(tr)); 106 | assert_eq!(TransformSRT::IDENTITY * tr, TransformSRT::from(tr)); 107 | 108 | let s = Vec3::splat(2.0); 109 | let r = Quat::from_rotation_y(180.0_f32.to_radians()); 110 | let t = -Vec3::Y; 111 | let srt = TransformSRT::from_scale_rotation_translation(s, r, t); 112 | let v0 = Vec3A::X; 113 | let v1 = srt.transform_point3a(v0); 114 | assert_approx_eq!(v1, (r * (v0 * Vec3A::from(s))) + Vec3A::from(t)); 115 | assert_approx_eq!(v1, srt.transform_point3a(v0)); 116 | let inv_srt = srt.inverse(); 117 | let v2 = inv_srt.transform_point3a(v1); 118 | assert_approx_eq!(v0, v2); 119 | 120 | assert_eq!(srt * TransformSRT::IDENTITY, srt); 121 | assert_eq!(srt * inv_srt, TransformSRT::IDENTITY); 122 | 123 | // negative scale mul test 124 | let s = Vec3::splat(-2.0); 125 | let srt = TransformSRT::from_scale_rotation_translation(s, r, t); 126 | let inv_srt = srt.inverse(); 127 | assert_eq!(srt * inv_srt, TransformSRT::IDENTITY); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/core/wasm32/quaternion.rs: -------------------------------------------------------------------------------- 1 | use core::arch::wasm32::*; 2 | 3 | // use super::float::*; 4 | use crate::core::{ 5 | storage::XYZ, 6 | traits::{quaternion::Quaternion, scalar::*, vector::*}, 7 | }; 8 | 9 | impl Quaternion for v128 { 10 | type SIMDVector3 = v128; 11 | 12 | #[inline(always)] 13 | fn conjugate(self) -> Self { 14 | const SIGN: v128 = const_f32x4!([-1.0, -1.0, -1.0, 1.0]); 15 | f32x4_mul(self, SIGN) 16 | } 17 | 18 | #[inline] 19 | fn lerp(self, end: Self, s: f32) -> Self { 20 | glam_assert!(FloatVector4::is_normalized(self)); 21 | glam_assert!(FloatVector4::is_normalized(end)); 22 | 23 | const NEG_ZERO: v128 = const_f32x4!([-0.0; 4]); 24 | let start = self; 25 | let end = end; 26 | let dot = Vector4::dot_into_vec(start, end); 27 | // Calculate the bias, if the dot product is positive or zero, there is no bias 28 | // but if it is negative, we want to flip the 'end' rotation XYZW components 29 | let bias = v128_and(dot, NEG_ZERO); 30 | let interpolated = f32x4_add( 31 | f32x4_mul(f32x4_sub(v128_xor(end, bias), start), f32x4_splat(s)), 32 | start, 33 | ); 34 | FloatVector4::normalize(interpolated) 35 | } 36 | 37 | #[inline] 38 | fn slerp(self, end: Self, s: f32) -> Self { 39 | // http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/ 40 | glam_assert!(FloatVector4::is_normalized(self)); 41 | glam_assert!(FloatVector4::is_normalized(end)); 42 | 43 | const DOT_THRESHOLD: f32 = 0.9995; 44 | 45 | let dot = Vector4::dot(self, end); 46 | 47 | if dot > DOT_THRESHOLD { 48 | // assumes lerp returns a normalized quaternion 49 | self.lerp(end, s) 50 | } else { 51 | // assumes scalar_acos clamps the input to [-1.0, 1.0] 52 | let theta = dot.acos_approx(); 53 | 54 | // TODO: v128_sin is broken 55 | // let x = 1.0 - s; 56 | // let y = s; 57 | // let z = 1.0; 58 | // let w = 0.0; 59 | // let tmp = f32x4_mul(f32x4_splat(theta), f32x4(x, y, z, w)); 60 | // let tmp = v128_sin(tmp); 61 | let x = (theta * (1.0 - s)).sin(); 62 | let y = (theta * s).sin(); 63 | let z = theta.sin(); 64 | let w = 0.0; 65 | let tmp = f32x4(x, y, z, w); 66 | 67 | let scale1 = i32x4_shuffle::<0, 0, 4, 4>(tmp, tmp); 68 | let scale2 = i32x4_shuffle::<1, 1, 5, 5>(tmp, tmp); 69 | let theta_sin = i32x4_shuffle::<2, 2, 6, 6>(tmp, tmp); 70 | 71 | self.mul(scale1).add(end.mul(scale2)).div(theta_sin) 72 | } 73 | } 74 | 75 | #[inline] 76 | fn mul_quaternion(self, other: Self) -> Self { 77 | glam_assert!(FloatVector4::is_normalized(self)); 78 | glam_assert!(FloatVector4::is_normalized(other)); 79 | // Based on https://github.com/nfrechette/rtm `rtm::quat_mul` 80 | let lhs = self; 81 | let rhs = other; 82 | 83 | const CONTROL_WZYX: v128 = const_f32x4!([1.0, -1.0, 1.0, -1.0]); 84 | const CONTROL_ZWXY: v128 = const_f32x4!([1.0, 1.0, -1.0, -1.0]); 85 | const CONTROL_YXWZ: v128 = const_f32x4!([-1.0, 1.0, 1.0, -1.0]); 86 | 87 | let r_xxxx = i32x4_shuffle::<0, 0, 4, 4>(lhs, lhs); 88 | let r_yyyy = i32x4_shuffle::<1, 1, 5, 5>(lhs, lhs); 89 | let r_zzzz = i32x4_shuffle::<2, 2, 6, 6>(lhs, lhs); 90 | let r_wwww = i32x4_shuffle::<3, 3, 7, 7>(lhs, lhs); 91 | 92 | let lxrw_lyrw_lzrw_lwrw = f32x4_mul(r_wwww, rhs); 93 | let l_wzyx = i32x4_shuffle::<3, 2, 5, 4>(rhs, rhs); 94 | 95 | let lwrx_lzrx_lyrx_lxrx = f32x4_mul(r_xxxx, l_wzyx); 96 | let l_zwxy = i32x4_shuffle::<1, 0, 7, 6>(l_wzyx, l_wzyx); 97 | 98 | let lwrx_nlzrx_lyrx_nlxrx = f32x4_mul(lwrx_lzrx_lyrx_lxrx, CONTROL_WZYX); 99 | 100 | let lzry_lwry_lxry_lyry = f32x4_mul(r_yyyy, l_zwxy); 101 | let l_yxwz = i32x4_shuffle::<3, 2, 5, 4>(l_zwxy, l_zwxy); 102 | 103 | let lzry_lwry_nlxry_nlyry = f32x4_mul(lzry_lwry_lxry_lyry, CONTROL_ZWXY); 104 | 105 | let lyrz_lxrz_lwrz_lzrz = f32x4_mul(r_zzzz, l_yxwz); 106 | let result0 = f32x4_add(lxrw_lyrw_lzrw_lwrw, lwrx_nlzrx_lyrx_nlxrx); 107 | 108 | let nlyrz_lxrz_lwrz_wlzrz = f32x4_mul(lyrz_lxrz_lwrz_lzrz, CONTROL_YXWZ); 109 | let result1 = f32x4_add(lzry_lwry_nlxry_nlyry, nlyrz_lxrz_lwrz_wlzrz); 110 | f32x4_add(result0, result1) 111 | } 112 | 113 | #[inline] 114 | fn mul_vector3(self, other: XYZ) -> XYZ { 115 | self.mul_float4_as_vector3(other.into()).into() 116 | } 117 | 118 | #[inline] 119 | fn mul_float4_as_vector3(self, other: v128) -> v128 { 120 | glam_assert!(FloatVector4::is_normalized(self)); 121 | const TWO: v128 = const_f32x4!([2.0; 4]); 122 | let w = i32x4_shuffle::<3, 3, 7, 7>(self, self); 123 | let b = self; 124 | let b2 = Vector3::dot_into_vec(b, b); 125 | other 126 | .mul(w.mul(w).sub(b2)) 127 | .add(b.mul(Vector3::dot_into_vec(other, b).mul(TWO))) 128 | .add(b.cross(other).mul(w.mul(TWO))) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/core/sse2/quaternion.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "x86")] 2 | use core::arch::x86::*; 3 | #[cfg(target_arch = "x86_64")] 4 | use core::arch::x86_64::*; 5 | 6 | use super::float::*; 7 | use crate::core::{ 8 | storage::XYZ, 9 | traits::{quaternion::Quaternion, scalar::*, vector::*}, 10 | }; 11 | 12 | impl Quaternion for __m128 { 13 | type SIMDVector3 = __m128; 14 | 15 | #[inline(always)] 16 | fn conjugate(self) -> Self { 17 | const SIGN: __m128 = const_f32x4!([-0.0, -0.0, -0.0, 0.0]); 18 | unsafe { _mm_xor_ps(self, SIGN) } 19 | } 20 | 21 | #[inline] 22 | fn lerp(self, end: Self, s: f32) -> Self { 23 | glam_assert!(FloatVector4::is_normalized(self)); 24 | glam_assert!(FloatVector4::is_normalized(end)); 25 | 26 | unsafe { 27 | const NEG_ZERO: __m128 = const_f32x4!([-0.0; 4]); 28 | let start = self; 29 | let end = end; 30 | let dot = Vector4::dot_into_vec(start, end); 31 | // Calculate the bias, if the dot product is positive or zero, there is no bias 32 | // but if it is negative, we want to flip the 'end' rotation XYZW components 33 | let bias = _mm_and_ps(dot, NEG_ZERO); 34 | let interpolated = _mm_add_ps( 35 | _mm_mul_ps(_mm_sub_ps(_mm_xor_ps(end, bias), start), _mm_set_ps1(s)), 36 | start, 37 | ); 38 | FloatVector4::normalize(interpolated) 39 | } 40 | } 41 | 42 | #[inline] 43 | fn slerp(self, end: Self, s: f32) -> Self { 44 | // http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/ 45 | glam_assert!(FloatVector4::is_normalized(self)); 46 | glam_assert!(FloatVector4::is_normalized(end)); 47 | 48 | const DOT_THRESHOLD: f32 = 0.9995; 49 | 50 | let dot = Vector4::dot(self, end); 51 | 52 | if dot > DOT_THRESHOLD { 53 | // assumes lerp returns a normalized quaternion 54 | self.lerp(end, s) 55 | } else { 56 | // assumes scalar_acos clamps the input to [-1.0, 1.0] 57 | let theta = dot.acos_approx(); 58 | 59 | let x = 1.0 - s; 60 | let y = s; 61 | let z = 1.0; 62 | 63 | unsafe { 64 | let tmp = _mm_mul_ps(_mm_set_ps1(theta), _mm_set_ps(0.0, z, y, x)); 65 | let tmp = m128_sin(tmp); 66 | 67 | let scale1 = _mm_shuffle_ps(tmp, tmp, 0b00_00_00_00); 68 | let scale2 = _mm_shuffle_ps(tmp, tmp, 0b01_01_01_01); 69 | let theta_sin = _mm_shuffle_ps(tmp, tmp, 0b10_10_10_10); 70 | 71 | self.mul(scale1).add(end.mul(scale2)).div(theta_sin) 72 | } 73 | } 74 | } 75 | 76 | #[inline] 77 | fn mul_quaternion(self, other: Self) -> Self { 78 | glam_assert!(FloatVector4::is_normalized(self)); 79 | glam_assert!(FloatVector4::is_normalized(other)); 80 | unsafe { 81 | // Based on https://github.com/nfrechette/rtm `rtm::quat_mul` 82 | let lhs = self; 83 | let rhs = other; 84 | 85 | const CONTROL_WZYX: __m128 = const_f32x4!([1.0, -1.0, 1.0, -1.0]); 86 | const CONTROL_ZWXY: __m128 = const_f32x4!([1.0, 1.0, -1.0, -1.0]); 87 | const CONTROL_YXWZ: __m128 = const_f32x4!([-1.0, 1.0, 1.0, -1.0]); 88 | 89 | let r_xxxx = _mm_shuffle_ps(lhs, lhs, 0b00_00_00_00); 90 | let r_yyyy = _mm_shuffle_ps(lhs, lhs, 0b01_01_01_01); 91 | let r_zzzz = _mm_shuffle_ps(lhs, lhs, 0b10_10_10_10); 92 | let r_wwww = _mm_shuffle_ps(lhs, lhs, 0b11_11_11_11); 93 | 94 | let lxrw_lyrw_lzrw_lwrw = _mm_mul_ps(r_wwww, rhs); 95 | let l_wzyx = _mm_shuffle_ps(rhs, rhs, 0b00_01_10_11); 96 | 97 | let lwrx_lzrx_lyrx_lxrx = _mm_mul_ps(r_xxxx, l_wzyx); 98 | let l_zwxy = _mm_shuffle_ps(l_wzyx, l_wzyx, 0b10_11_00_01); 99 | 100 | let lwrx_nlzrx_lyrx_nlxrx = _mm_mul_ps(lwrx_lzrx_lyrx_lxrx, CONTROL_WZYX); 101 | 102 | let lzry_lwry_lxry_lyry = _mm_mul_ps(r_yyyy, l_zwxy); 103 | let l_yxwz = _mm_shuffle_ps(l_zwxy, l_zwxy, 0b00_01_10_11); 104 | 105 | let lzry_lwry_nlxry_nlyry = _mm_mul_ps(lzry_lwry_lxry_lyry, CONTROL_ZWXY); 106 | 107 | let lyrz_lxrz_lwrz_lzrz = _mm_mul_ps(r_zzzz, l_yxwz); 108 | let result0 = _mm_add_ps(lxrw_lyrw_lzrw_lwrw, lwrx_nlzrx_lyrx_nlxrx); 109 | 110 | let nlyrz_lxrz_lwrz_wlzrz = _mm_mul_ps(lyrz_lxrz_lwrz_lzrz, CONTROL_YXWZ); 111 | let result1 = _mm_add_ps(lzry_lwry_nlxry_nlyry, nlyrz_lxrz_lwrz_wlzrz); 112 | _mm_add_ps(result0, result1) 113 | } 114 | } 115 | 116 | #[inline] 117 | fn mul_vector3(self, other: XYZ) -> XYZ { 118 | self.mul_float4_as_vector3(other.into()).into() 119 | } 120 | 121 | #[inline] 122 | fn mul_float4_as_vector3(self, other: __m128) -> __m128 { 123 | glam_assert!(FloatVector4::is_normalized(self)); 124 | unsafe { 125 | const TWO: __m128 = const_f32x4!([2.0; 4]); 126 | let w = _mm_shuffle_ps(self, self, 0b11_11_11_11); 127 | let b = self; 128 | let b2 = Vector3::dot_into_vec(b, b); 129 | other 130 | .mul(w.mul(w).sub(b2)) 131 | .add(b.mul(Vector3::dot_into_vec(other, b).mul(TWO))) 132 | .add(b.cross(other).mul(w.mul(TWO))) 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/core/traits/quaternion.rs: -------------------------------------------------------------------------------- 1 | use crate::core::{ 2 | storage::XYZ, 3 | traits::{ 4 | scalar::{FloatEx, NumEx}, 5 | vector::*, 6 | }, 7 | }; 8 | 9 | pub trait Quaternion: FloatVector4 { 10 | type SIMDVector3; 11 | 12 | #[inline] 13 | fn from_axis_angle(axis: XYZ, angle: T) -> Self { 14 | glam_assert!(FloatVector3::is_normalized(axis)); 15 | let (s, c) = (angle * T::HALF).sin_cos(); 16 | let v = axis.mul_scalar(s); 17 | Self::new(v.x, v.y, v.z, c) 18 | } 19 | 20 | #[inline] 21 | fn from_rotation_x(angle: T) -> Self { 22 | let (s, c) = (angle * T::HALF).sin_cos(); 23 | Self::new(s, T::ZERO, T::ZERO, c) 24 | } 25 | 26 | #[inline] 27 | fn from_rotation_y(angle: T) -> Self { 28 | let (s, c) = (angle * T::HALF).sin_cos(); 29 | Self::new(T::ZERO, s, T::ZERO, c) 30 | } 31 | 32 | #[inline] 33 | fn from_rotation_z(angle: T) -> Self { 34 | let (s, c) = (angle * T::HALF).sin_cos(); 35 | Self::new(T::ZERO, T::ZERO, s, c) 36 | } 37 | 38 | /// From the columns of a 3x3 rotation matrix. 39 | #[inline] 40 | fn from_rotation_axes(x_axis: XYZ, y_axis: XYZ, z_axis: XYZ) -> Self { 41 | // Based on https://github.com/microsoft/DirectXMath `XM$quaternionRotationMatrix` 42 | // TODO: sse2 version 43 | let (m00, m01, m02) = x_axis.into_tuple(); 44 | let (m10, m11, m12) = y_axis.into_tuple(); 45 | let (m20, m21, m22) = z_axis.into_tuple(); 46 | if m22 <= T::ZERO { 47 | // x^2 + y^2 >= z^2 + w^2 48 | let dif10 = m11 - m00; 49 | let omm22 = T::ONE - m22; 50 | if dif10 <= T::ZERO { 51 | // x^2 >= y^2 52 | let four_xsq = omm22 - dif10; 53 | let inv4x = T::HALF / four_xsq.sqrt(); 54 | Self::new( 55 | four_xsq * inv4x, 56 | (m01 + m10) * inv4x, 57 | (m02 + m20) * inv4x, 58 | (m12 - m21) * inv4x, 59 | ) 60 | } else { 61 | // y^2 >= x^2 62 | let four_ysq = omm22 + dif10; 63 | let inv4y = T::HALF / four_ysq.sqrt(); 64 | Self::new( 65 | (m01 + m10) * inv4y, 66 | four_ysq * inv4y, 67 | (m12 + m21) * inv4y, 68 | (m20 - m02) * inv4y, 69 | ) 70 | } 71 | } else { 72 | // z^2 + w^2 >= x^2 + y^2 73 | let sum10 = m11 + m00; 74 | let opm22 = T::ONE + m22; 75 | if sum10 <= T::ZERO { 76 | // z^2 >= w^2 77 | let four_zsq = opm22 - sum10; 78 | let inv4z = T::HALF / four_zsq.sqrt(); 79 | Self::new( 80 | (m02 + m20) * inv4z, 81 | (m12 + m21) * inv4z, 82 | four_zsq * inv4z, 83 | (m01 - m10) * inv4z, 84 | ) 85 | } else { 86 | // w^2 >= z^2 87 | let four_wsq = opm22 + sum10; 88 | let inv4w = T::HALF / four_wsq.sqrt(); 89 | Self::new( 90 | (m12 - m21) * inv4w, 91 | (m20 - m02) * inv4w, 92 | (m01 - m10) * inv4w, 93 | four_wsq * inv4w, 94 | ) 95 | } 96 | } 97 | } 98 | 99 | fn to_axis_angle(self) -> (XYZ, T) { 100 | // const EPSILON: f32 = 1.0e-8; 101 | // const EPSILON_SQUARED: f32 = EPSILON * EPSILON; 102 | let (x, y, z, w) = Vector4::into_tuple(self); 103 | let angle = w.acos_approx() * T::TWO; 104 | let scale_sq = NumEx::max(T::ONE - w * w, T::ZERO); 105 | // TODO: constants for epslions? 106 | if scale_sq >= T::from_f32(1.0e-8 * 1.0e-8) { 107 | (XYZ { x, y, z }.mul_scalar(scale_sq.sqrt().recip()), angle) 108 | } else { 109 | (Vector3Const::X, angle) 110 | } 111 | } 112 | 113 | #[inline] 114 | fn is_near_identity(self) -> bool { 115 | // Based on https://github.com/nfrechette/rtm `rtm::quat_near_identity` 116 | let threshold_angle = T::from_f64(0.002_847_144_6); 117 | // Because of floating point precision, we cannot represent very small rotations. 118 | // The closest f32 to 1.0 that is not 1.0 itself yields: 119 | // 0.99999994.acos() * 2.0 = 0.000690533954 rad 120 | // 121 | // An error threshold of 1.e-6 is used by default. 122 | // (1.0 - 1.e-6).acos() * 2.0 = 0.00284714461 rad 123 | // (1.0 - 1.e-7).acos() * 2.0 = 0.00097656250 rad 124 | // 125 | // We don't really care about the angle value itself, only if it's close to 0. 126 | // This will happen whenever quat.w is close to 1.0. 127 | // If the quat.w is close to -1.0, the angle will be near 2*PI which is close to 128 | // a negative 0 rotation. By forcing quat.w to be positive, we'll end up with 129 | // the shortest path. 130 | let positive_w_angle = self.as_ref_xyzw().w.abs().acos_approx() * T::TWO; 131 | positive_w_angle < threshold_angle 132 | } 133 | 134 | fn conjugate(self) -> Self; 135 | fn lerp(self, end: Self, s: T) -> Self; 136 | fn slerp(self, end: Self, s: T) -> Self; 137 | fn mul_quaternion(self, other: Self) -> Self; 138 | fn mul_vector3(self, other: XYZ) -> XYZ; 139 | fn mul_float4_as_vector3(self, other: Self::SIMDVector3) -> Self::SIMDVector3; 140 | } 141 | -------------------------------------------------------------------------------- /src/features/impl_rkyv.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "bytecheck")] 2 | macro_rules! impl_rkyv { 3 | (@bytecheck $type:ty) => { 4 | impl bytecheck::CheckBytes for $type { 5 | type Error = core::convert::Infallible; 6 | 7 | #[inline] 8 | unsafe fn check_bytes<'a>( 9 | value: *const Self, 10 | _: &mut C, 11 | ) -> Result<&'a Self, Self::Error> { 12 | Ok(&*value) 13 | } 14 | } 15 | }; 16 | 17 | ($type:ty) => { 18 | impl_rkyv_derive!(@serialize $type); 19 | impl_rkyv_derive!(@archive_deserialize $type); 20 | impl_rkyv!(@bytecheck $type); 21 | }; 22 | } 23 | 24 | #[cfg(not(feature = "bytecheck"))] 25 | macro_rules! impl_rkyv { 26 | ($type:ty) => { 27 | impl_rkyv_derive!(@serialize $type); 28 | impl_rkyv_derive!(@archive_deserialize $type); 29 | }; 30 | } 31 | 32 | macro_rules! impl_rkyv_derive { 33 | (@serialize $type:ty) => { 34 | impl Serialize for $type { 35 | #[inline] 36 | fn serialize(&self, _: &mut S) -> Result { 37 | Ok(()) 38 | } 39 | } 40 | }; 41 | 42 | (@archive_deserialize $type:ty) => { 43 | impl Archive for $type { 44 | type Archived = $type; 45 | type Resolver = (); 46 | 47 | #[inline] 48 | unsafe fn resolve(&self, _: usize, _: Self::Resolver, out: *mut Self::Archived) { 49 | out.write(to_archived!(*self as Self)); 50 | } 51 | } 52 | 53 | impl Deserialize<$type, D> for $type { 54 | #[inline] 55 | fn deserialize(&self, _: &mut D) -> Result<$type, D::Error> { 56 | Ok(from_archived!(*self)) 57 | } 58 | } 59 | }; 60 | } 61 | 62 | mod f32 { 63 | use crate::{Affine2, Affine3A, Mat2, Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4}; 64 | use rkyv::{from_archived, to_archived, Archive, Deserialize, Fallible, Serialize}; 65 | impl_rkyv!(Affine2); 66 | impl_rkyv!(Affine3A); 67 | impl_rkyv!(Mat2); 68 | impl_rkyv!(Mat3); 69 | impl_rkyv!(Mat3A); 70 | impl_rkyv!(Mat4); 71 | impl_rkyv!(Quat); 72 | impl_rkyv!(Vec2); 73 | impl_rkyv!(Vec3); 74 | impl_rkyv!(Vec3A); 75 | impl_rkyv!(Vec4); 76 | } 77 | 78 | mod f64 { 79 | use crate::{DAffine2, DAffine3, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4}; 80 | use rkyv::{from_archived, to_archived, Archive, Deserialize, Fallible, Serialize}; 81 | 82 | impl_rkyv!(DAffine2); 83 | impl_rkyv!(DAffine3); 84 | impl_rkyv!(DMat2); 85 | impl_rkyv!(DMat3); 86 | impl_rkyv!(DMat4); 87 | impl_rkyv!(DQuat); 88 | impl_rkyv!(DVec2); 89 | impl_rkyv!(DVec3); 90 | impl_rkyv!(DVec4); 91 | } 92 | 93 | mod i32 { 94 | use crate::{IVec2, IVec3, IVec4}; 95 | use rkyv::{from_archived, to_archived, Archive, Deserialize, Fallible, Serialize}; 96 | 97 | impl_rkyv!(IVec2); 98 | impl_rkyv!(IVec3); 99 | impl_rkyv!(IVec4); 100 | } 101 | 102 | mod u32 { 103 | use crate::{UVec2, UVec3, UVec4}; 104 | use rkyv::{from_archived, to_archived, Archive, Deserialize, Fallible, Serialize}; 105 | 106 | impl_rkyv!(UVec2); 107 | impl_rkyv!(UVec3); 108 | impl_rkyv!(UVec4); 109 | } 110 | 111 | #[cfg(test)] 112 | mod test { 113 | pub type DefaultSerializer = rkyv::ser::serializers::CoreSerializer<256, 256>; 114 | pub type DefaultDeserializer = rkyv::Infallible; 115 | use rkyv::ser::Serializer; 116 | use rkyv::*; 117 | pub fn test_archive(value: &T) 118 | where 119 | T: core::fmt::Debug + PartialEq + rkyv::Serialize, 120 | T::Archived: core::fmt::Debug + PartialEq + rkyv::Deserialize, 121 | { 122 | let mut serializer = DefaultSerializer::default(); 123 | serializer 124 | .serialize_value(value) 125 | .expect("failed to archive value"); 126 | let len = serializer.pos(); 127 | let buffer = serializer.into_serializer().into_inner(); 128 | 129 | let archived_value = unsafe { rkyv::archived_root::(&buffer[0..len]) }; 130 | assert_eq!(archived_value, value); 131 | let mut deserializer = DefaultDeserializer::default(); 132 | assert_eq!( 133 | &archived_value.deserialize(&mut deserializer).unwrap(), 134 | value 135 | ); 136 | } 137 | 138 | #[test] 139 | fn test_rkyv() { 140 | use crate::{Affine2, Affine3A, Mat2, Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4}; 141 | test_archive(&Affine2::from_cols_array(&[1.0, 0.0, 2.0, 0.0, 3.0, 4.0])); 142 | test_archive(&Affine3A::from_cols_array(&[ 143 | 1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 3.0, 4.0, 5.0, 6.0, 144 | ])); 145 | test_archive(&Mat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0])); 146 | test_archive(&Mat3::from_cols_array(&[ 147 | 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 148 | ])); 149 | test_archive(&Mat3A::from_cols_array(&[ 150 | 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 151 | ])); 152 | test_archive(&Mat4::from_cols_array(&[ 153 | 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, 154 | ])); 155 | test_archive(&Quat::from_xyzw(1.0, 2.0, 3.0, 4.0)); 156 | test_archive(&Vec2::new(1.0, 2.0)); 157 | test_archive(&Vec3::new(1.0, 2.0, 3.0)); 158 | test_archive(&Vec3A::new(1.0, 2.0, 3.0)); 159 | test_archive(&Vec4::new(1.0, 2.0, 3.0, 4.0)); 160 | 161 | use crate::{DAffine2, DAffine3, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4}; 162 | test_archive(&DAffine2::from_cols_array(&[1.0, 0.0, 2.0, 0.0, 3.0, 4.0])); 163 | test_archive(&DAffine3::from_cols_array(&[ 164 | 1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 3.0, 4.0, 5.0, 6.0, 165 | ])); 166 | test_archive(&DMat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0])); 167 | test_archive(&DMat3::from_cols_array(&[ 168 | 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 169 | ])); 170 | test_archive(&DMat4::from_cols_array(&[ 171 | 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, 172 | ])); 173 | test_archive(&DQuat::from_xyzw(1.0, 2.0, 3.0, 4.0)); 174 | test_archive(&DVec2::new(1.0, 2.0)); 175 | test_archive(&DVec3::new(1.0, 2.0, 3.0)); 176 | test_archive(&DVec4::new(1.0, 2.0, 3.0, 4.0)); 177 | 178 | use crate::{IVec2, IVec3, IVec4}; 179 | test_archive(&IVec2::new(-1, 2)); 180 | test_archive(&IVec3::new(-1, 2, 3)); 181 | test_archive(&IVec4::new(-1, 2, 3, 4)); 182 | 183 | use crate::{UVec2, UVec3, UVec4}; 184 | test_archive(&UVec2::new(1, 2)); 185 | test_archive(&UVec3::new(1, 2, 3)); 186 | test_archive(&UVec4::new(1, 2, 3, 4)); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/features/impl_rand.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_vec_types { 2 | ($t:ty, $vec2:ident, $vec3:ident, $vec4:ident) => { 3 | impl Distribution<$vec2> for Standard { 4 | #[inline] 5 | fn sample(&self, rng: &mut R) -> $vec2 { 6 | rng.gen::<[$t; 2]>().into() 7 | } 8 | } 9 | 10 | impl Distribution<$vec3> for Standard { 11 | #[inline] 12 | fn sample(&self, rng: &mut R) -> $vec3 { 13 | rng.gen::<[$t; 3]>().into() 14 | } 15 | } 16 | 17 | impl Distribution<$vec4> for Standard { 18 | #[inline] 19 | fn sample(&self, rng: &mut R) -> $vec4 { 20 | rng.gen::<[$t; 4]>().into() 21 | } 22 | } 23 | 24 | #[test] 25 | fn test_vec2_rand() { 26 | use rand::{Rng, SeedableRng}; 27 | use rand_xoshiro::Xoshiro256Plus; 28 | let mut rng1 = Xoshiro256Plus::seed_from_u64(0); 29 | let a: ($t, $t) = rng1.gen(); 30 | let mut rng2 = Xoshiro256Plus::seed_from_u64(0); 31 | let b: $vec2 = rng2.gen(); 32 | assert_eq!(a, b.into()); 33 | } 34 | 35 | #[test] 36 | fn test_vec3_rand() { 37 | use rand::{Rng, SeedableRng}; 38 | use rand_xoshiro::Xoshiro256Plus; 39 | let mut rng1 = Xoshiro256Plus::seed_from_u64(0); 40 | let a: ($t, $t, $t) = rng1.gen(); 41 | let mut rng2 = Xoshiro256Plus::seed_from_u64(0); 42 | let b: $vec3 = rng2.gen(); 43 | assert_eq!(a, b.into()); 44 | } 45 | 46 | #[test] 47 | fn test_vec4_rand() { 48 | use rand::{Rng, SeedableRng}; 49 | use rand_xoshiro::Xoshiro256Plus; 50 | let mut rng1 = Xoshiro256Plus::seed_from_u64(0); 51 | let a: ($t, $t, $t, $t) = rng1.gen(); 52 | let mut rng2 = Xoshiro256Plus::seed_from_u64(0); 53 | let b: $vec4 = rng2.gen(); 54 | assert_eq!(a, b.into()); 55 | } 56 | }; 57 | } 58 | 59 | macro_rules! impl_float_types { 60 | ($t:ident, $mat2:ident, $mat3:ident, $mat4:ident, $quat:ident, $vec2:ident, $vec3:ident, $vec4:ident) => { 61 | impl_vec_types!($t, $vec2, $vec3, $vec4); 62 | 63 | impl Distribution<$mat2> for Standard { 64 | #[inline] 65 | fn sample(&self, rng: &mut R) -> $mat2 { 66 | $mat2::from_cols_array(&rng.gen()) 67 | } 68 | } 69 | 70 | impl Distribution<$mat3> for Standard { 71 | #[inline] 72 | fn sample(&self, rng: &mut R) -> $mat3 { 73 | $mat3::from_cols_array(&rng.gen()) 74 | } 75 | } 76 | 77 | impl Distribution<$mat4> for Standard { 78 | #[inline] 79 | fn sample(&self, rng: &mut R) -> $mat4 { 80 | $mat4::from_cols_array(&rng.gen()) 81 | } 82 | } 83 | 84 | impl Distribution<$quat> for Standard { 85 | #[inline] 86 | fn sample(&self, rng: &mut R) -> $quat { 87 | let yaw = -PI + rng.gen::<$t>() * 2.0 * PI; 88 | let pitch = -PI + rng.gen::<$t>() * 2.0 * PI; 89 | let roll = -PI + rng.gen::<$t>() * 2.0 * PI; 90 | $quat::from_euler(crate::EulerRot::YXZ, yaw, pitch, roll) 91 | } 92 | } 93 | 94 | #[test] 95 | fn test_mat2_rand() { 96 | use rand::{Rng, SeedableRng}; 97 | use rand_xoshiro::Xoshiro256Plus; 98 | let mut rng1 = Xoshiro256Plus::seed_from_u64(0); 99 | let a = $mat2::from_cols_array(&rng1.gen::<[$t; 4]>()); 100 | let mut rng2 = Xoshiro256Plus::seed_from_u64(0); 101 | let b = rng2.gen::<$mat2>(); 102 | assert_eq!(a, b); 103 | } 104 | 105 | #[test] 106 | fn test_mat3_rand() { 107 | use rand::{Rng, SeedableRng}; 108 | use rand_xoshiro::Xoshiro256Plus; 109 | let mut rng1 = Xoshiro256Plus::seed_from_u64(0); 110 | let a = $mat3::from_cols_array(&rng1.gen::<[$t; 9]>()); 111 | let mut rng2 = Xoshiro256Plus::seed_from_u64(0); 112 | let b = rng2.gen::<$mat3>(); 113 | assert_eq!(a, b); 114 | } 115 | 116 | #[test] 117 | fn test_mat4_rand() { 118 | use rand::{Rng, SeedableRng}; 119 | use rand_xoshiro::Xoshiro256Plus; 120 | let mut rng1 = Xoshiro256Plus::seed_from_u64(0); 121 | let a = $mat4::from_cols_array(&rng1.gen::<[$t; 16]>()); 122 | let mut rng2 = Xoshiro256Plus::seed_from_u64(0); 123 | let b = rng2.gen::<$mat4>(); 124 | assert_eq!(a, b); 125 | } 126 | 127 | #[test] 128 | fn test_quat_rand() { 129 | use rand::{Rng, SeedableRng}; 130 | use rand_xoshiro::Xoshiro256Plus; 131 | let mut rng1 = Xoshiro256Plus::seed_from_u64(0); 132 | let a: $quat = rng1.gen(); 133 | assert!(a.is_normalized()); 134 | let mut rng2 = Xoshiro256Plus::seed_from_u64(0); 135 | let b: $quat = rng2.gen(); 136 | assert_eq!(a, b); 137 | } 138 | }; 139 | } 140 | 141 | mod f32 { 142 | use crate::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4}; 143 | use core::f32::consts::PI; 144 | use rand::{ 145 | distributions::{Distribution, Standard}, 146 | Rng, 147 | }; 148 | 149 | impl_float_types!(f32, Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4); 150 | 151 | impl Distribution for Standard { 152 | #[inline] 153 | fn sample(&self, rng: &mut R) -> Vec3A { 154 | rng.gen::<[f32; 3]>().into() 155 | } 156 | } 157 | 158 | #[test] 159 | fn test_vec3a_rand() { 160 | use rand::{Rng, SeedableRng}; 161 | use rand_xoshiro::Xoshiro256Plus; 162 | let mut rng1 = Xoshiro256Plus::seed_from_u64(0); 163 | let a: (f32, f32, f32) = rng1.gen(); 164 | let mut rng2 = Xoshiro256Plus::seed_from_u64(0); 165 | let b: Vec3A = rng2.gen(); 166 | assert_eq!(a, b.into()); 167 | } 168 | } 169 | 170 | mod f64 { 171 | use crate::{DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4}; 172 | use core::f64::consts::PI; 173 | use rand::{ 174 | distributions::{Distribution, Standard}, 175 | Rng, 176 | }; 177 | 178 | impl_float_types!(f64, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4); 179 | } 180 | 181 | mod i32 { 182 | use crate::{IVec2, IVec3, IVec4}; 183 | use rand::{ 184 | distributions::{Distribution, Standard}, 185 | Rng, 186 | }; 187 | 188 | impl_vec_types!(i32, IVec2, IVec3, IVec4); 189 | } 190 | 191 | mod u32 { 192 | use crate::{UVec2, UVec3, UVec4}; 193 | use rand::{ 194 | distributions::{Distribution, Standard}, 195 | Rng, 196 | }; 197 | 198 | impl_vec_types!(u32, UVec2, UVec3, UVec4); 199 | } 200 | -------------------------------------------------------------------------------- /src/core/traits/projection.rs: -------------------------------------------------------------------------------- 1 | use crate::core::traits::{ 2 | matrix::FloatMatrix4x4, quaternion::Quaternion, scalar::FloatEx, vector::*, 3 | }; 4 | 5 | pub trait ProjectionMatrix + Quaternion>: 6 | FloatMatrix4x4 7 | { 8 | /// Creates a right-handed perspective projection matrix with [-1,1] depth range. 9 | /// This is the same as the OpenGL [`gluPerspective`] function. 10 | /// [`gluPerspective`]: 11 | fn perspective_rh_gl(fov_y_radians: T, aspect_ratio: T, z_near: T, z_far: T) -> Self { 12 | let inv_length = T::ONE / (z_near - z_far); 13 | let f = T::ONE / (T::HALF * fov_y_radians).tan(); 14 | let a = f / aspect_ratio; 15 | let b = (z_near + z_far) * inv_length; 16 | let c = (T::TWO * z_near * z_far) * inv_length; 17 | Self::from_cols( 18 | V4::new(a, T::ZERO, T::ZERO, T::ZERO), 19 | V4::new(T::ZERO, f, T::ZERO, T::ZERO), 20 | V4::new(T::ZERO, T::ZERO, b, -T::ONE), 21 | V4::new(T::ZERO, T::ZERO, c, T::ZERO), 22 | ) 23 | } 24 | 25 | /// Creates a left-handed perspective projection matrix with [0,1] depth range. 26 | fn perspective_lh(fov_y_radians: T, aspect_ratio: T, z_near: T, z_far: T) -> Self { 27 | glam_assert!(z_near > T::ZERO && z_far > T::ZERO); 28 | let (sin_fov, cos_fov) = (T::HALF * fov_y_radians).sin_cos(); 29 | let h = cos_fov / sin_fov; 30 | let w = h / aspect_ratio; 31 | let r = z_far / (z_far - z_near); 32 | Self::from_cols( 33 | V4::new(w, T::ZERO, T::ZERO, T::ZERO), 34 | V4::new(T::ZERO, h, T::ZERO, T::ZERO), 35 | V4::new(T::ZERO, T::ZERO, r, T::ONE), 36 | V4::new(T::ZERO, T::ZERO, -r * z_near, T::ZERO), 37 | ) 38 | } 39 | 40 | /// Creates a right-handed perspective projection matrix with [0,1] depth range. 41 | fn perspective_rh(fov_y_radians: T, aspect_ratio: T, z_near: T, z_far: T) -> Self { 42 | glam_assert!(z_near > T::ZERO && z_far > T::ZERO); 43 | let (sin_fov, cos_fov) = (T::HALF * fov_y_radians).sin_cos(); 44 | let h = cos_fov / sin_fov; 45 | let w = h / aspect_ratio; 46 | let r = z_far / (z_near - z_far); 47 | Self::from_cols( 48 | V4::new(w, T::ZERO, T::ZERO, T::ZERO), 49 | V4::new(T::ZERO, h, T::ZERO, T::ZERO), 50 | V4::new(T::ZERO, T::ZERO, r, -T::ONE), 51 | V4::new(T::ZERO, T::ZERO, r * z_near, T::ZERO), 52 | ) 53 | } 54 | 55 | /// Creates an infinite left-handed perspective projection matrix with [0,1] depth range. 56 | fn perspective_infinite_lh(fov_y_radians: T, aspect_ratio: T, z_near: T) -> Self { 57 | glam_assert!(z_near > T::ZERO); 58 | let (sin_fov, cos_fov) = (T::HALF * fov_y_radians).sin_cos(); 59 | let h = cos_fov / sin_fov; 60 | let w = h / aspect_ratio; 61 | Self::from_cols( 62 | V4::new(w, T::ZERO, T::ZERO, T::ZERO), 63 | V4::new(T::ZERO, h, T::ZERO, T::ZERO), 64 | V4::new(T::ZERO, T::ZERO, T::ONE, T::ONE), 65 | V4::new(T::ZERO, T::ZERO, -z_near, T::ZERO), 66 | ) 67 | } 68 | 69 | /// Creates an infinite left-handed perspective projection matrix with [0,1] depth range. 70 | fn perspective_infinite_reverse_lh(fov_y_radians: T, aspect_ratio: T, z_near: T) -> Self { 71 | glam_assert!(z_near > T::ZERO); 72 | let (sin_fov, cos_fov) = (T::HALF * fov_y_radians).sin_cos(); 73 | let h = cos_fov / sin_fov; 74 | let w = h / aspect_ratio; 75 | Self::from_cols( 76 | V4::new(w, T::ZERO, T::ZERO, T::ZERO), 77 | V4::new(T::ZERO, h, T::ZERO, T::ZERO), 78 | V4::new(T::ZERO, T::ZERO, T::ZERO, T::ONE), 79 | V4::new(T::ZERO, T::ZERO, z_near, T::ZERO), 80 | ) 81 | } 82 | 83 | /// Creates an infinite right-handed perspective projection matrix with 84 | /// [0,1] depth range. 85 | fn perspective_infinite_rh(fov_y_radians: T, aspect_ratio: T, z_near: T) -> Self { 86 | glam_assert!(z_near > T::ZERO); 87 | let f = T::ONE / (T::HALF * fov_y_radians).tan(); 88 | Self::from_cols( 89 | V4::new(f / aspect_ratio, T::ZERO, T::ZERO, T::ZERO), 90 | V4::new(T::ZERO, f, T::ZERO, T::ZERO), 91 | V4::new(T::ZERO, T::ZERO, -T::ONE, -T::ONE), 92 | V4::new(T::ZERO, T::ZERO, -z_near, T::ZERO), 93 | ) 94 | } 95 | 96 | /// Creates an infinite reverse right-handed perspective projection matrix 97 | /// with [0,1] depth range. 98 | fn perspective_infinite_reverse_rh(fov_y_radians: T, aspect_ratio: T, z_near: T) -> Self { 99 | glam_assert!(z_near > T::ZERO); 100 | let f = T::ONE / (T::HALF * fov_y_radians).tan(); 101 | Self::from_cols( 102 | V4::new(f / aspect_ratio, T::ZERO, T::ZERO, T::ZERO), 103 | V4::new(T::ZERO, f, T::ZERO, T::ZERO), 104 | V4::new(T::ZERO, T::ZERO, T::ZERO, -T::ONE), 105 | V4::new(T::ZERO, T::ZERO, z_near, T::ZERO), 106 | ) 107 | } 108 | 109 | /// Creates a right-handed orthographic projection matrix with [-1,1] depth 110 | /// range. This is the same as the OpenGL [`glOrtho`] function in OpenGL. 111 | /// See 112 | /// [`glOrtho`]: 113 | fn orthographic_rh_gl(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self { 114 | let a = T::TWO / (right - left); 115 | let b = T::TWO / (top - bottom); 116 | let c = -T::TWO / (far - near); 117 | let tx = -(right + left) / (right - left); 118 | let ty = -(top + bottom) / (top - bottom); 119 | let tz = -(far + near) / (far - near); 120 | 121 | Self::from_cols( 122 | V4::new(a, T::ZERO, T::ZERO, T::ZERO), 123 | V4::new(T::ZERO, b, T::ZERO, T::ZERO), 124 | V4::new(T::ZERO, T::ZERO, c, T::ZERO), 125 | V4::new(tx, ty, tz, T::ONE), 126 | ) 127 | } 128 | 129 | /// Creates a left-handed orthographic projection matrix with [0,1] depth range. 130 | fn orthographic_lh(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self { 131 | let rcp_width = T::ONE / (right - left); 132 | let rcp_height = T::ONE / (top - bottom); 133 | let r = T::ONE / (far - near); 134 | Self::from_cols( 135 | V4::new(rcp_width + rcp_width, T::ZERO, T::ZERO, T::ZERO), 136 | V4::new(T::ZERO, rcp_height + rcp_height, T::ZERO, T::ZERO), 137 | V4::new(T::ZERO, T::ZERO, r, T::ZERO), 138 | V4::new( 139 | -(left + right) * rcp_width, 140 | -(top + bottom) * rcp_height, 141 | -r * near, 142 | T::ONE, 143 | ), 144 | ) 145 | } 146 | 147 | /// Creates a right-handed orthographic projection matrix with [0,1] depth range. 148 | fn orthographic_rh(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self { 149 | let rcp_width = T::ONE / (right - left); 150 | let rcp_height = T::ONE / (top - bottom); 151 | let r = T::ONE / (near - far); 152 | Self::from_cols( 153 | V4::new(rcp_width + rcp_width, T::ZERO, T::ZERO, T::ZERO), 154 | V4::new(T::ZERO, rcp_height + rcp_height, T::ZERO, T::ZERO), 155 | V4::new(T::ZERO, T::ZERO, r, T::ZERO), 156 | V4::new( 157 | -(left + right) * rcp_width, 158 | -(top + bottom) * rcp_height, 159 | r * near, 160 | T::ONE, 161 | ), 162 | ) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /tests/support/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! glam_test { 3 | ($name:ident, $block:block) => { 4 | #[cfg_attr(not(target_arch = "wasm32"), test)] 5 | #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] 6 | fn $name() { 7 | $block 8 | } 9 | }; 10 | } 11 | 12 | #[macro_export] 13 | macro_rules! should_panic { 14 | ($block:block) => {{ 15 | #[cfg(all(feature = "std", not(target_arch = "wasm32")))] 16 | assert!(std::panic::catch_unwind(|| $block).is_err()); 17 | }}; 18 | } 19 | 20 | #[macro_export] 21 | macro_rules! should_glam_assert { 22 | ($block:block) => {{ 23 | #[cfg(any(feature = "glam-assert", feature = "debug-glam-assert"))] 24 | should_panic!($block); 25 | }}; 26 | } 27 | 28 | #[macro_export] 29 | macro_rules! assert_approx_eq { 30 | ($a:expr, $b:expr) => {{ 31 | #[allow(unused_imports)] 32 | use crate::support::FloatCompare; 33 | let eps = core::f32::EPSILON; 34 | let (a, b) = (&$a, &$b); 35 | assert!( 36 | a.approx_eq(b, eps), 37 | "assertion failed: `(left !== right)` \ 38 | (left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)", 39 | *a, 40 | *b, 41 | eps, 42 | a.abs_diff(b) 43 | ); 44 | }}; 45 | ($a:expr, $b:expr, $eps:expr) => {{ 46 | use crate::support::FloatCompare; 47 | let (a, b) = (&$a, &$b); 48 | let eps = $eps; 49 | assert!( 50 | a.approx_eq(b, $eps), 51 | "assertion failed: `(left !== right)` \ 52 | (left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)", 53 | *a, 54 | *b, 55 | eps, 56 | a.abs_diff(b) 57 | ); 58 | }}; 59 | } 60 | 61 | /// Test vector normalization for float vector 62 | #[macro_export] 63 | macro_rules! impl_vec_float_normalize_tests { 64 | ($t:ident, $vec:ident) => { 65 | use core::$t::MAX; 66 | use core::$t::MIN_POSITIVE; 67 | 68 | /// Works for vec2, vec3, vec4 69 | fn from_x_y(x: $t, y: $t) -> $vec { 70 | let mut v = $vec::ZERO; 71 | v.x = x; 72 | v.y = y; 73 | v 74 | } 75 | 76 | glam_test!(test_normalize, { 77 | assert_eq!(from_x_y(-42.0, 0.0).normalize(), from_x_y(-1.0, 0.0)); 78 | assert_eq!(from_x_y(MAX.sqrt(), 0.0).normalize(), from_x_y(1.0, 0.0)); 79 | // assert_eq!(from_x_y(MAX, 0.0).normalize(), from_x_y(1.0, 0.0)); // normalize fails for huge vectors and returns zero 80 | 81 | // We expect not to be able to normalize small numbers: 82 | should_glam_assert!({ from_x_y(0.0, 0.0).normalize() }); 83 | should_glam_assert!({ from_x_y(MIN_POSITIVE, 0.0).normalize() }); 84 | 85 | // We expect not to be able to normalize non-finite vectors: 86 | should_glam_assert!({ from_x_y(INFINITY, 0.0).normalize() }); 87 | should_glam_assert!({ from_x_y(NAN, 0.0).normalize() }); 88 | }); 89 | 90 | #[cfg(not(any(feature = "debug-glam-assert", feature = "glam-assert")))] 91 | glam_test!(test_normalize_no_glam_assert, { 92 | // We expect not to be able to normalize small numbers: 93 | assert!(!from_x_y(0.0, 0.0).normalize().is_finite()); 94 | assert!(!from_x_y(MIN_POSITIVE, 0.0).normalize().is_finite()); 95 | 96 | // We expect not to be able to normalize non-finite vectors: 97 | assert!(!from_x_y(INFINITY, 0.0).normalize().is_finite()); 98 | assert!(!from_x_y(NAN, 0.0).normalize().is_finite()); 99 | }); 100 | 101 | glam_test!(test_try_normalize, { 102 | assert_eq!( 103 | from_x_y(-42.0, 0.0).try_normalize(), 104 | Some(from_x_y(-1.0, 0.0)) 105 | ); 106 | assert_eq!( 107 | from_x_y(MAX.sqrt(), 0.0).try_normalize(), 108 | Some(from_x_y(1.0, 0.0)) 109 | ); 110 | 111 | // We expect `try_normalize` to return None when inputs are very small: 112 | assert_eq!(from_x_y(0.0, 0.0).try_normalize(), None); 113 | assert_eq!(from_x_y(MIN_POSITIVE, 0.0).try_normalize(), None); 114 | 115 | // We expect `try_normalize` to return None when inputs are non-finite: 116 | assert_eq!(from_x_y(INFINITY, 0.0).try_normalize(), None); 117 | assert_eq!(from_x_y(NAN, 0.0).try_normalize(), None); 118 | 119 | // We expect `try_normalize` to return None when inputs are very large: 120 | assert_eq!(from_x_y(MAX, 0.0).try_normalize(), None); 121 | assert_eq!(from_x_y(MAX, MAX).try_normalize(), None); 122 | }); 123 | 124 | glam_test!(test_normalize_or_zero, { 125 | assert_eq!( 126 | from_x_y(-42.0, 0.0).normalize_or_zero(), 127 | from_x_y(-1.0, 0.0) 128 | ); 129 | assert_eq!( 130 | from_x_y(MAX.sqrt(), 0.0).normalize_or_zero(), 131 | from_x_y(1.0, 0.0) 132 | ); 133 | 134 | // We expect `normalize_or_zero` to return zero when inputs are very small: 135 | assert_eq!(from_x_y(0.0, 0.0).normalize_or_zero(), $vec::ZERO); 136 | assert_eq!(from_x_y(MIN_POSITIVE, 0.0).normalize_or_zero(), $vec::ZERO); 137 | 138 | // We expect `normalize_or_zero` to return zero when inputs are non-finite: 139 | assert_eq!(from_x_y(INFINITY, 0.0).normalize_or_zero(), $vec::ZERO); 140 | assert_eq!(from_x_y(NAN, 0.0).normalize_or_zero(), $vec::ZERO); 141 | 142 | // We expect `normalize_or_zero` to return zero when inputs are very large: 143 | assert_eq!(from_x_y(MAX, 0.0).normalize_or_zero(), $vec::ZERO); 144 | assert_eq!(from_x_y(MAX, MAX).normalize_or_zero(), $vec::ZERO); 145 | }); 146 | }; 147 | } 148 | 149 | /// Useful test vectors 150 | #[macro_export] 151 | macro_rules! vec3_float_test_vectors { 152 | ($vec3:ident) => { 153 | [ 154 | $vec3::X, 155 | $vec3::Y, 156 | $vec3::Z, 157 | -$vec3::X, 158 | -$vec3::Y, 159 | -$vec3::Z, 160 | $vec3::new(1.0, 1e-3, 0.0), 161 | $vec3::new(1.0, 1e-4, 0.0), 162 | $vec3::new(1.0, 1e-5, 0.0), 163 | $vec3::new(1.0, 1e-6, 0.0), 164 | $vec3::new(1.0, 1e-7, 0.0), 165 | $vec3::new(1.0, 1e-14, 0.0), 166 | $vec3::new(1.0, 1e-15, 0.0), 167 | $vec3::new(1.0, 1e-16, 0.0), 168 | $vec3::new(0.1, 0.2, 0.3), 169 | $vec3::new(0.2, 0.3, 0.4), 170 | $vec3::new(4.0, -5.0, 6.0), 171 | $vec3::new(-2.0, 0.5, -1.0), 172 | // Pathalogical cases from : 173 | $vec3::new(0.00038527316, 0.00038460016, -0.99999988079), 174 | $vec3::new(-0.00019813581, -0.00008946839, -0.99999988079), 175 | ] 176 | }; 177 | } 178 | 179 | #[macro_export] 180 | macro_rules! vec2_float_test_vectors { 181 | ($vec2:ident) => { 182 | [ 183 | $vec2::X, 184 | $vec2::Y, 185 | -$vec2::X, 186 | -$vec2::Y, 187 | $vec2::new(1.0, 1e-3), 188 | $vec2::new(1.0, 1e-4), 189 | $vec2::new(1.0, 1e-5), 190 | $vec2::new(1.0, 1e-6), 191 | $vec2::new(1.0, 1e-7), 192 | $vec2::new(1.0, 1e-14), 193 | $vec2::new(1.0, 1e-15), 194 | $vec2::new(1.0, 1e-16), 195 | $vec2::new(0.1, 0.2), 196 | $vec2::new(0.2, 0.3), 197 | $vec2::new(4.0, -5.0), 198 | $vec2::new(-2.0, 0.5), 199 | // Pathalogical cases from : 200 | $vec2::new(0.00038527316, 0.00038460016), 201 | $vec2::new(-0.00019813581, -0.00008946839), 202 | ] 203 | }; 204 | } 205 | -------------------------------------------------------------------------------- /tests/affine2.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod support; 3 | 4 | macro_rules! impl_affine2_tests { 5 | ($t:ident, $affine2:ident, $vec2:ident) => { 6 | const MATRIX2D: [[$t; 2]; 3] = [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]; 7 | 8 | use core::$t::NAN; 9 | use core::$t::NEG_INFINITY; 10 | 11 | glam_test!(test_affine2_identity, { 12 | assert_eq!($affine2::IDENTITY, $affine2::IDENTITY * $affine2::IDENTITY); 13 | assert_eq!($affine2::IDENTITY, $affine2::default()); 14 | }); 15 | 16 | glam_test!(test_affine2_zero, { 17 | assert_eq!( 18 | $affine2::ZERO.transform_point2($vec2::new(1., 2.)), 19 | $vec2::ZERO 20 | ); 21 | }); 22 | 23 | glam_test!(test_affine2_nan, { 24 | assert!($affine2::NAN.is_nan()); 25 | assert!(!$affine2::NAN.is_finite()); 26 | }); 27 | 28 | glam_test!(test_affine2_translation, { 29 | let translate = $affine2::from_translation($vec2::new(1.0, 2.0)); 30 | assert_eq!(translate.translation, $vec2::new(1.0, 2.0).into()); 31 | assert_eq!( 32 | translate.transform_point2($vec2::new(2.0, 3.0)), 33 | $vec2::new(3.0, 5.0), 34 | ); 35 | }); 36 | 37 | glam_test!(test_affine2_mul, { 38 | let m = $affine2::from_angle(deg(90.0)); 39 | let result3 = m.transform_vector2($vec2::Y); 40 | assert_approx_eq!($vec2::new(-1.0, 0.0), result3); 41 | 42 | let m = $affine2::from_scale_angle_translation( 43 | $vec2::new(0.5, 1.5), 44 | deg(90.0), 45 | $vec2::new(1.0, 2.0), 46 | ); 47 | let result3 = m.transform_vector2($vec2::Y); 48 | assert_approx_eq!($vec2::new(-1.5, 0.0), result3, 1.0e-6); 49 | 50 | let result3 = m.transform_point2($vec2::Y); 51 | assert_approx_eq!($vec2::new(-0.5, 2.0), result3, 1.0e-6); 52 | }); 53 | 54 | glam_test!(test_from_scale, { 55 | let m = $affine2::from_scale($vec2::new(2.0, 4.0)); 56 | assert_approx_eq!( 57 | m.transform_point2($vec2::new(1.0, 1.0)), 58 | $vec2::new(2.0, 4.0) 59 | ); 60 | }); 61 | 62 | glam_test!(test_affine2_inverse, { 63 | let inv = $affine2::IDENTITY.inverse(); 64 | assert_approx_eq!($affine2::IDENTITY, inv); 65 | 66 | let rot = $affine2::from_angle(deg(90.0)); 67 | let rot_inv = rot.inverse(); 68 | assert_approx_eq!($affine2::IDENTITY, rot * rot_inv); 69 | assert_approx_eq!($affine2::IDENTITY, rot_inv * rot); 70 | 71 | let trans = $affine2::from_translation($vec2::new(1.0, 2.0)); 72 | let trans_inv = trans.inverse(); 73 | assert_approx_eq!($affine2::IDENTITY, trans * trans_inv); 74 | assert_approx_eq!($affine2::IDENTITY, trans_inv * trans); 75 | 76 | let scale = $affine2::from_scale($vec2::new(4.0, 5.0)); 77 | let scale_inv = scale.inverse(); 78 | assert_approx_eq!($affine2::IDENTITY, scale * scale_inv); 79 | assert_approx_eq!($affine2::IDENTITY, scale_inv * scale); 80 | 81 | let m = scale * rot * trans; 82 | let m_inv = m.inverse(); 83 | assert_approx_eq!($affine2::IDENTITY, m * m_inv, 1.0e-5); 84 | assert_approx_eq!($affine2::IDENTITY, m_inv * m, 1.0e-5); 85 | assert_approx_eq!(m_inv, trans_inv * rot_inv * scale_inv, 1.0e-6); 86 | 87 | // Make sure we can invert a shear matrix: 88 | let m = $affine2::from_angle(0.5) 89 | * $affine2::from_scale($vec2::new(1.0, 0.5)) 90 | * $affine2::from_angle(-0.5); 91 | let m_inv = m.inverse(); 92 | assert_approx_eq!($affine2::IDENTITY, m * m_inv, 1.0e-5); 93 | assert_approx_eq!($affine2::IDENTITY, m_inv * m, 1.0e-5); 94 | 95 | should_glam_assert!({ $affine2::ZERO.inverse() }); 96 | }); 97 | 98 | glam_test!(test_affine2_ops, { 99 | let m0 = $affine2::from_cols_array_2d(&MATRIX2D); 100 | let m0x2 = $affine2::from_cols_array_2d(&[[2.0, 4.0], [6.0, 8.0], [10.0, 12.0]]); 101 | assert_eq!(m0x2, m0 * 2.0); 102 | assert_eq!(m0x2, 2.0 * m0); 103 | assert_eq!(m0x2, m0 + m0); 104 | assert_eq!($affine2::ZERO, m0 - m0); 105 | assert_approx_eq!(m0, m0 * $affine2::IDENTITY); 106 | assert_approx_eq!(m0, $affine2::IDENTITY * m0); 107 | }); 108 | 109 | glam_test!(test_affine2_fmt, { 110 | let a = $affine2::from_cols_array_2d(&MATRIX2D); 111 | assert_eq!(format!("{}", a), "[[1, 2], [3, 4], [5, 6]]"); 112 | }); 113 | 114 | glam_test!(test_affine2_to_from_slice, { 115 | const MATRIX1D: [$t; 6] = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; 116 | let m = $affine2::from_cols_slice(&MATRIX1D); 117 | assert_eq!($affine2::from_cols_array(&MATRIX1D), m); 118 | assert_eq!(MATRIX1D, m.to_cols_array()); 119 | assert_eq!(MATRIX2D, m.to_cols_array_2d()); 120 | let mut out: [$t; 6] = Default::default(); 121 | m.write_cols_to_slice(&mut out); 122 | assert_eq!(MATRIX1D, out); 123 | assert_eq!( 124 | m, 125 | $affine2::from_cols(MATRIX2D[0].into(), MATRIX2D[1].into(), MATRIX2D[2].into()) 126 | ); 127 | 128 | should_panic!({ $affine2::from_cols_slice(&[0.0; 5]) }); 129 | should_panic!({ $affine2::IDENTITY.write_cols_to_slice(&mut [0.0; 5]) }); 130 | }); 131 | 132 | glam_test!(test_product, { 133 | let ident = $affine2::IDENTITY; 134 | assert_eq!( 135 | vec![ident, ident].iter().product::<$affine2>(), 136 | ident * ident 137 | ); 138 | }); 139 | 140 | glam_test!(test_affine2_is_finite, { 141 | assert!($affine2::from_scale($vec2::new(1.0, 1.0)).is_finite()); 142 | assert!($affine2::from_scale($vec2::new(0.0, 1.0)).is_finite()); 143 | assert!(!$affine2::from_scale($vec2::new(1.0, NAN)).is_finite()); 144 | assert!(!$affine2::from_scale($vec2::new(1.0, NEG_INFINITY)).is_finite()); 145 | }); 146 | }; 147 | } 148 | 149 | mod affine2 { 150 | use super::support::{deg, FloatCompare}; 151 | use glam::{Affine2, Vec2}; 152 | 153 | impl FloatCompare for Affine2 { 154 | #[inline] 155 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 156 | self.abs_diff_eq(*other, max_abs_diff) 157 | } 158 | #[inline] 159 | fn abs_diff(&self, other: &Self) -> Self { 160 | Self { 161 | matrix2: self.matrix2.abs_diff(&other.matrix2), 162 | translation: self.translation.abs_diff(&other.translation), 163 | } 164 | } 165 | } 166 | 167 | glam_test!(test_align, { 168 | use std::mem; 169 | if cfg!(not(feature = "scalar-math")) { 170 | assert_eq!(32, mem::size_of::()); 171 | assert_eq!(16, mem::align_of::()); 172 | } else { 173 | assert_eq!(24, mem::size_of::()); 174 | assert_eq!(4, mem::align_of::()); 175 | } 176 | }); 177 | 178 | impl_affine2_tests!(f32, Affine2, Vec2); 179 | } 180 | 181 | mod daffine2 { 182 | use super::support::{deg, FloatCompare}; 183 | use glam::{DAffine2, DVec2}; 184 | 185 | impl FloatCompare for DAffine2 { 186 | #[inline] 187 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 188 | self.abs_diff_eq(*other, max_abs_diff as f64) 189 | } 190 | #[inline] 191 | fn abs_diff(&self, other: &Self) -> Self { 192 | Self { 193 | matrix2: self.matrix2.abs_diff(&other.matrix2), 194 | translation: self.translation.abs_diff(&other.translation), 195 | } 196 | } 197 | } 198 | 199 | glam_test!(test_align, { 200 | use std::mem; 201 | assert_eq!(48, mem::size_of::()); 202 | assert_eq!(mem::align_of::(), mem::align_of::()); 203 | }); 204 | 205 | impl_affine2_tests!(f64, DAffine2, DVec2); 206 | } 207 | -------------------------------------------------------------------------------- /src/core/scalar/matrix.rs: -------------------------------------------------------------------------------- 1 | use crate::core::{ 2 | storage::{Columns2, Columns3, Columns4, XY, XYZ, XYZF32A16, XYZW}, 3 | traits::{ 4 | matrix::{ 5 | FloatMatrix2x2, FloatMatrix3x3, FloatMatrix4x4, Matrix, Matrix2x2, Matrix3x3, 6 | Matrix4x4, MatrixConst, 7 | }, 8 | projection::ProjectionMatrix, 9 | scalar::{FloatEx, NanConstEx, NumEx}, 10 | vector::*, 11 | }, 12 | }; 13 | 14 | impl MatrixConst for Columns2> { 15 | const ZERO: Self = Self { 16 | x_axis: XY::ZERO, 17 | y_axis: XY::ZERO, 18 | }; 19 | const IDENTITY: Self = Self { 20 | x_axis: XY::X, 21 | y_axis: XY::Y, 22 | }; 23 | } 24 | 25 | impl NanConstEx for Columns2> { 26 | const NAN: Self = Self { 27 | x_axis: XY::NAN, 28 | y_axis: XY::NAN, 29 | }; 30 | } 31 | 32 | impl Matrix for Columns2> {} 33 | 34 | impl Matrix2x2> for Columns2> { 35 | #[inline(always)] 36 | fn from_cols(x_axis: XY, y_axis: XY) -> Self { 37 | Self { x_axis, y_axis } 38 | } 39 | 40 | #[inline(always)] 41 | fn x_axis(&self) -> &XY { 42 | &self.x_axis 43 | } 44 | 45 | #[inline(always)] 46 | fn y_axis(&self) -> &XY { 47 | &self.y_axis 48 | } 49 | } 50 | 51 | impl FloatMatrix2x2> for Columns2> {} 52 | 53 | impl MatrixConst for Columns3> { 54 | const ZERO: Self = Self { 55 | x_axis: XYZ::ZERO, 56 | y_axis: XYZ::ZERO, 57 | z_axis: XYZ::ZERO, 58 | }; 59 | const IDENTITY: Self = Self { 60 | x_axis: XYZ::X, 61 | y_axis: XYZ::Y, 62 | z_axis: XYZ::Z, 63 | }; 64 | } 65 | 66 | impl NanConstEx for Columns3> { 67 | const NAN: Self = Self { 68 | x_axis: XYZ::NAN, 69 | y_axis: XYZ::NAN, 70 | z_axis: XYZ::NAN, 71 | }; 72 | } 73 | 74 | impl Matrix for Columns3> {} 75 | 76 | impl Matrix3x3> for Columns3> { 77 | #[inline(always)] 78 | fn from_cols(x_axis: XYZ, y_axis: XYZ, z_axis: XYZ) -> Self { 79 | Self { 80 | x_axis, 81 | y_axis, 82 | z_axis, 83 | } 84 | } 85 | 86 | #[inline(always)] 87 | fn x_axis(&self) -> &XYZ { 88 | &self.x_axis 89 | } 90 | 91 | #[inline(always)] 92 | fn y_axis(&self) -> &XYZ { 93 | &self.y_axis 94 | } 95 | 96 | #[inline(always)] 97 | fn z_axis(&self) -> &XYZ { 98 | &self.z_axis 99 | } 100 | 101 | #[inline] 102 | fn mul_vector(&self, other: XYZ) -> XYZ { 103 | // default implementation uses splat_x etc, which might not be optimal. Need to check. 104 | let mut res = self.x_axis.mul_scalar(other.x); 105 | res = self.y_axis.mul_scalar(other.y).add(res); 106 | res = self.z_axis.mul_scalar(other.z).add(res); 107 | res 108 | } 109 | } 110 | 111 | impl FloatMatrix3x3> for Columns3> { 112 | #[inline] 113 | fn transform_point2(&self, other: XY) -> XY { 114 | // TODO: This is untested, probably slower than the high level code that uses a SIMD mat2 115 | Columns2::from_cols(self.x_axis.into(), self.y_axis.into()) 116 | .mul_vector(other) 117 | .add(self.z_axis.into()) 118 | } 119 | 120 | #[inline] 121 | fn transform_vector2(&self, other: XY) -> XY { 122 | // TODO: This is untested, probably slower than the high level code that uses a SIMD mat2 123 | Columns2::from_cols(self.x_axis.into(), self.y_axis.into()).mul_vector(other) 124 | } 125 | } 126 | 127 | impl MatrixConst for Columns3 { 128 | const ZERO: Self = Self { 129 | x_axis: XYZF32A16::ZERO, 130 | y_axis: XYZF32A16::ZERO, 131 | z_axis: XYZF32A16::ZERO, 132 | }; 133 | const IDENTITY: Self = Self { 134 | x_axis: XYZF32A16::X, 135 | y_axis: XYZF32A16::Y, 136 | z_axis: XYZF32A16::Z, 137 | }; 138 | } 139 | 140 | impl NanConstEx for Columns3 { 141 | const NAN: Self = Self { 142 | x_axis: XYZF32A16::NAN, 143 | y_axis: XYZF32A16::NAN, 144 | z_axis: XYZF32A16::NAN, 145 | }; 146 | } 147 | 148 | impl Matrix for Columns3 {} 149 | 150 | impl Matrix3x3 for Columns3 { 151 | #[inline(always)] 152 | fn from_cols(x_axis: XYZF32A16, y_axis: XYZF32A16, z_axis: XYZF32A16) -> Self { 153 | Self { 154 | x_axis, 155 | y_axis, 156 | z_axis, 157 | } 158 | } 159 | 160 | #[inline(always)] 161 | fn x_axis(&self) -> &XYZF32A16 { 162 | &self.x_axis 163 | } 164 | 165 | #[inline(always)] 166 | fn y_axis(&self) -> &XYZF32A16 { 167 | &self.y_axis 168 | } 169 | 170 | #[inline(always)] 171 | fn z_axis(&self) -> &XYZF32A16 { 172 | &self.z_axis 173 | } 174 | } 175 | 176 | impl FloatMatrix3x3 for Columns3 { 177 | #[inline] 178 | fn transform_point2(&self, other: XY) -> XY { 179 | // TODO: This is untested, probably slower than the high level code that uses a SIMD mat2 180 | Columns2::from_cols(self.x_axis.into_xy(), self.y_axis.into_xy()) 181 | .mul_vector(other) 182 | .add(self.z_axis.into_xy()) 183 | } 184 | 185 | #[inline] 186 | fn transform_vector2(&self, other: XY) -> XY { 187 | // TODO: This is untested, probably slower than the high level code that uses a SIMD mat2 188 | Columns2::from_cols(self.x_axis.into_xy(), self.y_axis.into_xy()).mul_vector(other) 189 | } 190 | } 191 | 192 | impl MatrixConst for Columns4> { 193 | const ZERO: Self = Self { 194 | x_axis: XYZW::ZERO, 195 | y_axis: XYZW::ZERO, 196 | z_axis: XYZW::ZERO, 197 | w_axis: XYZW::ZERO, 198 | }; 199 | const IDENTITY: Self = Self { 200 | x_axis: XYZW::X, 201 | y_axis: XYZW::Y, 202 | z_axis: XYZW::Z, 203 | w_axis: XYZW::W, 204 | }; 205 | } 206 | 207 | impl NanConstEx for Columns4> { 208 | const NAN: Self = Self { 209 | x_axis: XYZW::NAN, 210 | y_axis: XYZW::NAN, 211 | z_axis: XYZW::NAN, 212 | w_axis: XYZW::NAN, 213 | }; 214 | } 215 | 216 | impl Matrix for Columns4> {} 217 | 218 | impl Matrix4x4> for Columns4> { 219 | #[rustfmt::skip] 220 | #[inline(always)] 221 | fn from_cols(x_axis: XYZW, y_axis: XYZW, z_axis: XYZW, w_axis: XYZW) -> Self { 222 | Self { x_axis, y_axis, z_axis, w_axis } 223 | } 224 | 225 | #[inline(always)] 226 | fn x_axis(&self) -> &XYZW { 227 | &self.x_axis 228 | } 229 | 230 | #[inline(always)] 231 | fn y_axis(&self) -> &XYZW { 232 | &self.y_axis 233 | } 234 | 235 | #[inline(always)] 236 | fn z_axis(&self) -> &XYZW { 237 | &self.z_axis 238 | } 239 | 240 | #[inline(always)] 241 | fn w_axis(&self) -> &XYZW { 242 | &self.w_axis 243 | } 244 | } 245 | 246 | impl FloatMatrix4x4> for Columns4> { 247 | type SIMDVector3 = XYZ; 248 | 249 | #[inline(always)] 250 | fn transform_float4_as_point3(&self, other: XYZ) -> XYZ { 251 | self.transform_point3(other) 252 | } 253 | 254 | #[inline(always)] 255 | fn transform_float4_as_vector3(&self, other: XYZ) -> XYZ { 256 | self.transform_vector3(other) 257 | } 258 | 259 | #[inline(always)] 260 | fn project_float4_as_point3(&self, other: XYZ) -> XYZ { 261 | self.project_point3(other) 262 | } 263 | } 264 | 265 | impl ProjectionMatrix> for Columns4> {} 266 | 267 | impl From>> for Columns3 { 268 | fn from(v: Columns3>) -> Columns3 { 269 | Self { 270 | x_axis: v.x_axis.into(), 271 | y_axis: v.y_axis.into(), 272 | z_axis: v.z_axis.into(), 273 | } 274 | } 275 | } 276 | 277 | impl From> for Columns3> { 278 | fn from(v: Columns3) -> Columns3> { 279 | Self { 280 | x_axis: v.x_axis.into(), 281 | y_axis: v.y_axis.into(), 282 | z_axis: v.z_axis.into(), 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /tests/support/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod macros; 3 | 4 | #[cfg(target_arch = "wasm32")] 5 | wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); 6 | 7 | use glam::{ 8 | DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, Mat2, Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, 9 | Vec3A, Vec4, 10 | }; 11 | 12 | pub trait Deg { 13 | fn to_radians(self) -> Self; 14 | } 15 | 16 | impl Deg for f32 { 17 | fn to_radians(self) -> f32 { 18 | f32::to_radians(self) 19 | } 20 | } 21 | 22 | impl Deg for f64 { 23 | fn to_radians(self) -> f64 { 24 | f64::to_radians(self) 25 | } 26 | } 27 | 28 | /// Helper function for migrating away from `glam::angle::deg`. 29 | #[allow(dead_code)] 30 | #[inline] 31 | pub fn deg(angle: T) -> T { 32 | angle.to_radians() 33 | } 34 | 35 | /// Helper function for migrating away from `glam::angle::rad`. 36 | #[allow(dead_code)] 37 | #[inline] 38 | pub fn rad(angle: T) -> T { 39 | angle 40 | } 41 | 42 | /// Trait used by the `assert_approx_eq` macro for floating point comparisons. 43 | pub trait FloatCompare { 44 | /// Return true if the absolute difference between `self` and `other` is 45 | /// less then or equal to `max_abs_diff`. 46 | fn approx_eq(&self, other: &Rhs, max_abs_diff: f32) -> bool; 47 | /// Returns the absolute difference of `self` and `other` which is printed 48 | /// if `assert_approx_eq` fails. 49 | fn abs_diff(&self, other: &Rhs) -> Rhs; 50 | } 51 | 52 | impl FloatCompare for f32 { 53 | #[inline] 54 | fn approx_eq(&self, other: &f32, max_abs_diff: f32) -> bool { 55 | (self - other).abs() <= max_abs_diff 56 | } 57 | #[inline] 58 | fn abs_diff(&self, other: &f32) -> f32 { 59 | (self - other).abs() 60 | } 61 | } 62 | 63 | impl FloatCompare for f64 { 64 | #[inline] 65 | fn approx_eq(&self, other: &f64, max_abs_diff: f32) -> bool { 66 | (self - other).abs() <= max_abs_diff as f64 67 | } 68 | #[inline] 69 | fn abs_diff(&self, other: &f64) -> f64 { 70 | (self - other).abs() 71 | } 72 | } 73 | 74 | impl FloatCompare for Mat2 { 75 | #[inline] 76 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 77 | self.abs_diff_eq(other, max_abs_diff) 78 | } 79 | #[inline] 80 | fn abs_diff(&self, other: &Self) -> Self { 81 | Self::from_cols( 82 | (self.x_axis - other.x_axis).abs(), 83 | (self.y_axis - other.y_axis).abs(), 84 | ) 85 | } 86 | } 87 | 88 | impl FloatCompare for DMat2 { 89 | #[inline] 90 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 91 | self.abs_diff_eq(other, max_abs_diff as f64) 92 | } 93 | #[inline] 94 | fn abs_diff(&self, other: &Self) -> Self { 95 | Self::from_cols( 96 | (self.x_axis - other.x_axis).abs(), 97 | (self.y_axis - other.y_axis).abs(), 98 | ) 99 | } 100 | } 101 | 102 | impl FloatCompare for Mat3 { 103 | #[inline] 104 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 105 | self.abs_diff_eq(*other, max_abs_diff) 106 | } 107 | #[inline] 108 | fn abs_diff(&self, other: &Self) -> Self { 109 | Self::from_cols( 110 | (self.x_axis - other.x_axis).abs(), 111 | (self.y_axis - other.y_axis).abs(), 112 | (self.z_axis - other.z_axis).abs(), 113 | ) 114 | } 115 | } 116 | 117 | impl FloatCompare for Mat3A { 118 | #[inline] 119 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 120 | self.abs_diff_eq(*other, max_abs_diff) 121 | } 122 | #[inline] 123 | fn abs_diff(&self, other: &Self) -> Self { 124 | Self::from_cols( 125 | (self.x_axis - other.x_axis).abs(), 126 | (self.y_axis - other.y_axis).abs(), 127 | (self.z_axis - other.z_axis).abs(), 128 | ) 129 | } 130 | } 131 | 132 | impl FloatCompare for DMat3 { 133 | #[inline] 134 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 135 | self.abs_diff_eq(*other, max_abs_diff as f64) 136 | } 137 | #[inline] 138 | fn abs_diff(&self, other: &Self) -> Self { 139 | Self::from_cols( 140 | (self.x_axis - other.x_axis).abs(), 141 | (self.y_axis - other.y_axis).abs(), 142 | (self.z_axis - other.z_axis).abs(), 143 | ) 144 | } 145 | } 146 | 147 | impl FloatCompare for DMat4 { 148 | #[inline] 149 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 150 | self.abs_diff_eq(*other, max_abs_diff as f64) 151 | } 152 | #[inline] 153 | fn abs_diff(&self, other: &Self) -> Self { 154 | Self::from_cols( 155 | (self.x_axis - other.x_axis).abs(), 156 | (self.y_axis - other.y_axis).abs(), 157 | (self.z_axis - other.z_axis).abs(), 158 | (self.w_axis - other.w_axis).abs(), 159 | ) 160 | } 161 | } 162 | 163 | impl FloatCompare for Mat4 { 164 | #[inline] 165 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 166 | self.abs_diff_eq(*other, max_abs_diff) 167 | } 168 | #[inline] 169 | fn abs_diff(&self, other: &Self) -> Self { 170 | Self::from_cols( 171 | (self.x_axis - other.x_axis).abs(), 172 | (self.y_axis - other.y_axis).abs(), 173 | (self.z_axis - other.z_axis).abs(), 174 | (self.w_axis - other.w_axis).abs(), 175 | ) 176 | } 177 | } 178 | 179 | impl FloatCompare for Quat { 180 | #[inline] 181 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 182 | self.abs_diff_eq(*other, max_abs_diff) 183 | } 184 | #[inline] 185 | fn abs_diff(&self, other: &Self) -> Self { 186 | let a: Vec4 = (*self).into(); 187 | let b: Vec4 = (*other).into(); 188 | Quat::from_vec4((a - b).abs()) 189 | } 190 | } 191 | 192 | impl FloatCompare for Vec2 { 193 | #[inline] 194 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 195 | self.abs_diff_eq(*other, max_abs_diff) 196 | } 197 | #[inline] 198 | fn abs_diff(&self, other: &Self) -> Self { 199 | (*self - *other).abs() 200 | } 201 | } 202 | 203 | impl FloatCompare for Vec3 { 204 | #[inline] 205 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 206 | self.abs_diff_eq(*other, max_abs_diff) 207 | } 208 | #[inline] 209 | fn abs_diff(&self, other: &Self) -> Self { 210 | (*self - *other).abs() 211 | } 212 | } 213 | 214 | impl FloatCompare for Vec3A { 215 | #[inline] 216 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 217 | self.abs_diff_eq(*other, max_abs_diff) 218 | } 219 | #[inline] 220 | fn abs_diff(&self, other: &Self) -> Self { 221 | (*self - *other).abs() 222 | } 223 | } 224 | 225 | impl FloatCompare for Vec4 { 226 | #[inline] 227 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 228 | self.abs_diff_eq(*other, max_abs_diff) 229 | } 230 | #[inline] 231 | fn abs_diff(&self, other: &Self) -> Self { 232 | (*self - *other).abs() 233 | } 234 | } 235 | 236 | impl FloatCompare for DQuat { 237 | #[inline] 238 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 239 | self.abs_diff_eq(*other, max_abs_diff as f64) 240 | } 241 | #[inline] 242 | fn abs_diff(&self, other: &Self) -> Self { 243 | let a: DVec4 = (*self).into(); 244 | let b: DVec4 = (*other).into(); 245 | DQuat::from_vec4((a - b).abs()) 246 | } 247 | } 248 | 249 | impl FloatCompare for DVec2 { 250 | #[inline] 251 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 252 | self.abs_diff_eq(*other, max_abs_diff as f64) 253 | } 254 | #[inline] 255 | fn abs_diff(&self, other: &Self) -> Self { 256 | (*self - *other).abs() 257 | } 258 | } 259 | 260 | impl FloatCompare for DVec3 { 261 | #[inline] 262 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 263 | self.abs_diff_eq(*other, max_abs_diff as f64) 264 | } 265 | #[inline] 266 | fn abs_diff(&self, other: &Self) -> Self { 267 | (*self - *other).abs() 268 | } 269 | } 270 | 271 | impl FloatCompare for DVec4 { 272 | #[inline] 273 | fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool { 274 | self.abs_diff_eq(*other, max_abs_diff as f64) 275 | } 276 | #[inline] 277 | fn abs_diff(&self, other: &Self) -> Self { 278 | (*self - *other).abs() 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /tests/euler.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod support; 3 | 4 | use glam::{DQuat, Quat}; 5 | 6 | /// Helper to calculate the inner angle in the range [0, 2*PI) 7 | trait AngleDiff { 8 | type Output; 9 | fn angle_diff(self, other: Self) -> Self::Output; 10 | } 11 | #[macro_export] 12 | macro_rules! impl_angle_diff { 13 | ($t:ty, $pi:expr) => { 14 | impl AngleDiff for $t { 15 | type Output = $t; 16 | fn angle_diff(self, other: $t) -> $t { 17 | const PI2: $t = $pi + $pi; 18 | let s = self.rem_euclid(PI2); 19 | let o = other.rem_euclid(PI2); 20 | if s > o { 21 | (s - o).min(PI2 + o - s) 22 | } else { 23 | (o - s).min(PI2 + s - o) 24 | } 25 | } 26 | } 27 | }; 28 | } 29 | impl_angle_diff!(f32, std::f32::consts::PI); 30 | impl_angle_diff!(f64, std::f64::consts::PI); 31 | 32 | #[macro_export] 33 | macro_rules! assert_approx_angle { 34 | ($a:expr, $b:expr, $eps:expr) => {{ 35 | let (a, b) = ($a, $b); 36 | let eps = $eps; 37 | let diff = a.angle_diff(b); 38 | assert!( 39 | diff < $eps, 40 | "assertion failed: `(left !== right)` \ 41 | (left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)", 42 | a, 43 | b, 44 | eps, 45 | diff 46 | ); 47 | }}; 48 | } 49 | 50 | trait EqApprox { 51 | type EPS; 52 | fn eq_approx(self, other: Self, axis_eps: Self::EPS, rot_axis: Self::EPS) -> bool; 53 | } 54 | 55 | macro_rules! impl_eq_approx { 56 | ($t:ty, $quat:ident, $pi:expr) => { 57 | impl EqApprox for $quat { 58 | type EPS = $t; 59 | fn eq_approx(self, other: Self, axis_eps: Self::EPS, rot_axis: Self::EPS) -> bool { 60 | let (s_axis, s_angle) = self.to_axis_angle(); 61 | let (o_axis, o_angle) = other.to_axis_angle(); 62 | if s_angle.abs() < rot_axis && o_angle.abs() < rot_axis { 63 | // No rotation 64 | true 65 | } else { 66 | let a = s_axis.angle_between(o_axis); 67 | if a < axis_eps { 68 | // Same axes 69 | (s_angle - o_angle).abs() < rot_axis 70 | } else if ($pi - a).abs() < axis_eps { 71 | // Inverted axes (180°) 72 | (s_angle + o_angle).abs() < rot_axis 73 | } else { 74 | // Other 75 | false 76 | } 77 | } 78 | } 79 | } 80 | }; 81 | } 82 | impl_eq_approx!(f32, Quat, std::f32::consts::PI); 83 | impl_eq_approx!(f64, DQuat, std::f64::consts::PI); 84 | 85 | #[macro_export] 86 | macro_rules! impl_3axis_test { 87 | ($name:ident, $t:ty, $quat:ident, $euler:path, $U:path, $V:path, $W:path, $vec:ident) => { 88 | glam_test!($name, { 89 | let euler = $euler; 90 | assert!($U != $W); // First and last axis must be different for three axis 91 | for u in (-176..=176).step_by(44) { 92 | for v in (-88..=88).step_by(44) { 93 | for w in (-176..=176).step_by(44) { 94 | let u1 = (u as $t).to_radians(); 95 | let v1 = (v as $t).to_radians(); 96 | let w1 = (w as $t).to_radians(); 97 | 98 | let q1: $quat = ($quat::from_axis_angle($U, u1) 99 | * $quat::from_axis_angle($V, v1) 100 | * $quat::from_axis_angle($W, w1)) 101 | .normalize(); 102 | 103 | // Test if the rotation is the expected 104 | let q2: $quat = $quat::from_euler(euler, u1, v1, w1).normalize(); 105 | assert_approx_eq!(q1, q2, 1e-5); 106 | 107 | // Test angle reconstruction 108 | let (u2, v2, w2) = q1.to_euler(euler); 109 | let q3 = $quat::from_euler(euler, u2, v2, w2).normalize(); 110 | 111 | assert_approx_angle!(u1, u2, 1e-4 as $t); 112 | assert_approx_angle!(v1, v2, 1e-4 as $t); 113 | assert_approx_angle!(w1, w2, 1e-4 as $t); 114 | 115 | assert_approx_eq!(q1 * $vec::X, q3 * $vec::X, 1e-4); 116 | assert_approx_eq!(q1 * $vec::Y, q3 * $vec::Y, 1e-4); 117 | assert_approx_eq!(q1 * $vec::Z, q3 * $vec::Z, 1e-4); 118 | } 119 | } 120 | } 121 | }); 122 | }; 123 | } 124 | 125 | #[macro_export] 126 | macro_rules! impl_2axis_test { 127 | ($name:ident, $t:ty, $quat:ident, $euler:path, $U:path, $V:path, $W:path, $vec:ident) => { 128 | glam_test!($name, { 129 | #[allow(deprecated)] 130 | let euler = $euler; 131 | assert!($U == $W); // First and last axis must be different for three axis 132 | for u in (-176..=176).step_by(44) { 133 | for v in (-176..=176).step_by(44) { 134 | for w in (-176..=176).step_by(44) { 135 | let u1 = (u as $t).to_radians(); 136 | let v1 = (v as $t).to_radians(); 137 | let w1 = (w as $t).to_radians(); 138 | 139 | let q1: $quat = ($quat::from_axis_angle($U, u1) 140 | * $quat::from_axis_angle($V, v1) 141 | * $quat::from_axis_angle($W, w1)) 142 | .normalize(); 143 | 144 | // Test if the rotation is the expected 145 | let q2 = $quat::from_euler(euler, u1, v1, w1).normalize(); 146 | assert_approx_eq!(q1, q2, 1e-5); 147 | 148 | // Test angle reconstruction 149 | let (u2, v2, w2) = q1.to_euler(euler); 150 | let _q3 = $quat::from_euler(euler, u2, v2, w2).normalize(); 151 | 152 | // Disabled tests, since no generic tests for ambiguous results in the two-axis results... 153 | // assert_approx_angle!(u1, u2, 1e-4 as $t); 154 | // assert_approx_angle!(v1, v2, 1e-4 as $t); 155 | // assert_approx_angle!(w1, w2, 1e-4 as $t); 156 | 157 | // assert_approx_eq!(q1 * $vec::X, q3 * $vec::X, 1e-4); 158 | // assert_approx_eq!(q1 * $vec::Y, q3 * $vec::Y, 1e-4); 159 | // assert_approx_eq!(q1 * $vec::Z, q3 * $vec::Z, 1e-4); 160 | } 161 | } 162 | } 163 | }); 164 | }; 165 | } 166 | 167 | macro_rules! impl_all_quat_tests_three_axis { 168 | ($t:ty, $q:ident, $v:ident) => { 169 | impl_3axis_test!(test_euler_zyx, $t, $q, ER::ZYX, $v::Z, $v::Y, $v::X, $v); 170 | impl_3axis_test!(test_euler_zxy, $t, $q, ER::ZXY, $v::Z, $v::X, $v::Y, $v); 171 | impl_3axis_test!(test_euler_yxz, $t, $q, ER::YXZ, $v::Y, $v::X, $v::Z, $v); 172 | impl_3axis_test!(test_euler_yzx, $t, $q, ER::YZX, $v::Y, $v::Z, $v::X, $v); 173 | impl_3axis_test!(test_euler_xyz, $t, $q, ER::XYZ, $v::X, $v::Y, $v::Z, $v); 174 | impl_3axis_test!(test_euler_xzy, $t, $q, ER::XZY, $v::X, $v::Z, $v::Y, $v); 175 | }; 176 | } 177 | 178 | macro_rules! impl_all_quat_tests_two_axis { 179 | ($t:ty, $q:ident, $v:ident) => { 180 | impl_2axis_test!(test_euler_zyz, $t, $q, ER::ZYZ, $v::Z, $v::Y, $v::Z, $v); 181 | impl_2axis_test!(test_euler_zxz, $t, $q, ER::ZXZ, $v::Z, $v::X, $v::Z, $v); 182 | impl_2axis_test!(test_euler_yxy, $t, $q, ER::YXY, $v::Y, $v::X, $v::Y, $v); 183 | impl_2axis_test!(test_euler_yzy, $t, $q, ER::YZY, $v::Y, $v::Z, $v::Y, $v); 184 | impl_2axis_test!(test_euler_xyx, $t, $q, ER::XYX, $v::X, $v::Y, $v::X, $v); 185 | impl_2axis_test!(test_euler_xzx, $t, $q, ER::XZX, $v::X, $v::Z, $v::X, $v); 186 | }; 187 | } 188 | 189 | mod euler { 190 | use super::AngleDiff; 191 | use glam::*; 192 | type ER = EulerRot; 193 | 194 | mod quat { 195 | use super::*; 196 | 197 | impl_all_quat_tests_three_axis!(f32, Quat, Vec3); 198 | 199 | impl_all_quat_tests_two_axis!(f32, Quat, Vec3); 200 | } 201 | 202 | mod dquat { 203 | use super::*; 204 | 205 | impl_all_quat_tests_three_axis!(f64, DQuat, DVec3); 206 | 207 | impl_all_quat_tests_two_axis!(f64, DQuat, DVec3); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/features/impl_approx.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Affine2, Affine3A, DAffine2, DAffine3, DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, Mat2, 3 | Mat3, Mat3A, Mat4, Quat, Vec2, Vec3, Vec3A, Vec4, 4 | }; 5 | use approx::{AbsDiffEq, RelativeEq, UlpsEq}; 6 | 7 | macro_rules! impl_approx_as_ref { 8 | ($prim:ident, $type:ty) => { 9 | impl AbsDiffEq for $type { 10 | type Epsilon = <$prim as AbsDiffEq>::Epsilon; 11 | fn default_epsilon() -> Self::Epsilon { 12 | $prim::default_epsilon() 13 | } 14 | fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { 15 | self.as_ref().abs_diff_eq(other.as_ref(), epsilon) 16 | } 17 | } 18 | 19 | impl RelativeEq for $type { 20 | fn default_max_relative() -> Self::Epsilon { 21 | $prim::default_max_relative() 22 | } 23 | fn relative_eq( 24 | &self, 25 | other: &Self, 26 | epsilon: Self::Epsilon, 27 | max_relative: Self::Epsilon, 28 | ) -> bool { 29 | self.as_ref() 30 | .relative_eq(other.as_ref(), epsilon, max_relative) 31 | } 32 | } 33 | 34 | impl UlpsEq for $type { 35 | fn default_max_ulps() -> u32 { 36 | $prim::default_max_ulps() 37 | } 38 | fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { 39 | self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps) 40 | } 41 | } 42 | }; 43 | } 44 | 45 | macro_rules! impl_approx_xzy_axes { 46 | ($prim:ident, $type:ty) => { 47 | impl AbsDiffEq for $type { 48 | type Epsilon = <$prim as AbsDiffEq>::Epsilon; 49 | fn default_epsilon() -> Self::Epsilon { 50 | $prim::default_epsilon() 51 | } 52 | fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { 53 | AbsDiffEq::abs_diff_eq(&self.x_axis, &other.x_axis, epsilon) 54 | && AbsDiffEq::abs_diff_eq(&self.y_axis, &other.y_axis, epsilon) 55 | && AbsDiffEq::abs_diff_eq(&self.z_axis, &other.z_axis, epsilon) 56 | } 57 | } 58 | 59 | impl RelativeEq for $type { 60 | fn default_max_relative() -> Self::Epsilon { 61 | $prim::default_max_relative() 62 | } 63 | fn relative_eq( 64 | &self, 65 | other: &Self, 66 | epsilon: Self::Epsilon, 67 | max_relative: Self::Epsilon, 68 | ) -> bool { 69 | RelativeEq::relative_eq(&self.x_axis, &other.x_axis, epsilon, max_relative) 70 | && RelativeEq::relative_eq(&self.y_axis, &other.y_axis, epsilon, max_relative) 71 | && RelativeEq::relative_eq(&self.z_axis, &other.z_axis, epsilon, max_relative) 72 | } 73 | } 74 | 75 | impl UlpsEq for $type { 76 | fn default_max_ulps() -> u32 { 77 | $prim::default_max_ulps() 78 | } 79 | fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { 80 | UlpsEq::ulps_eq(&self.x_axis, &other.x_axis, epsilon, max_ulps) 81 | && UlpsEq::ulps_eq(&self.y_axis, &other.y_axis, epsilon, max_ulps) 82 | && UlpsEq::ulps_eq(&self.z_axis, &other.z_axis, epsilon, max_ulps) 83 | } 84 | } 85 | }; 86 | } 87 | 88 | macro_rules! impl_approx_xzyw_axes { 89 | ($prim:ident, $type:ty) => { 90 | impl AbsDiffEq for $type { 91 | type Epsilon = <$prim as AbsDiffEq>::Epsilon; 92 | fn default_epsilon() -> Self::Epsilon { 93 | $prim::default_epsilon() 94 | } 95 | fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { 96 | AbsDiffEq::abs_diff_eq(&self.x_axis, &other.x_axis, epsilon) 97 | && AbsDiffEq::abs_diff_eq(&self.y_axis, &other.y_axis, epsilon) 98 | && AbsDiffEq::abs_diff_eq(&self.z_axis, &other.z_axis, epsilon) 99 | && AbsDiffEq::abs_diff_eq(&self.w_axis, &other.w_axis, epsilon) 100 | } 101 | } 102 | 103 | impl RelativeEq for $type { 104 | fn default_max_relative() -> Self::Epsilon { 105 | $prim::default_max_relative() 106 | } 107 | fn relative_eq( 108 | &self, 109 | other: &Self, 110 | epsilon: Self::Epsilon, 111 | max_relative: Self::Epsilon, 112 | ) -> bool { 113 | RelativeEq::relative_eq(&self.x_axis, &other.x_axis, epsilon, max_relative) 114 | && RelativeEq::relative_eq(&self.y_axis, &other.y_axis, epsilon, max_relative) 115 | && RelativeEq::relative_eq(&self.z_axis, &other.z_axis, epsilon, max_relative) 116 | && RelativeEq::relative_eq(&self.w_axis, &other.w_axis, epsilon, max_relative) 117 | } 118 | } 119 | 120 | impl UlpsEq for $type { 121 | fn default_max_ulps() -> u32 { 122 | $prim::default_max_ulps() 123 | } 124 | fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { 125 | UlpsEq::ulps_eq(&self.x_axis, &other.x_axis, epsilon, max_ulps) 126 | && UlpsEq::ulps_eq(&self.y_axis, &other.y_axis, epsilon, max_ulps) 127 | && UlpsEq::ulps_eq(&self.z_axis, &other.z_axis, epsilon, max_ulps) 128 | && UlpsEq::ulps_eq(&self.w_axis, &other.w_axis, epsilon, max_ulps) 129 | } 130 | } 131 | }; 132 | } 133 | 134 | impl_approx_as_ref!(f32, Mat2); 135 | impl_approx_as_ref!(f32, Mat3); 136 | impl_approx_as_ref!(f32, Mat4); 137 | impl_approx_as_ref!(f32, Quat); 138 | impl_approx_as_ref!(f32, Vec2); 139 | impl_approx_as_ref!(f32, Vec3); 140 | impl_approx_as_ref!(f32, Vec4); 141 | impl_approx_as_ref!(f32, Vec3A); 142 | 143 | impl_approx_xzy_axes!(f32, Affine2); 144 | impl_approx_xzyw_axes!(f32, Affine3A); 145 | impl_approx_xzy_axes!(f32, Mat3A); 146 | 147 | impl_approx_xzy_axes!(f64, DAffine2); 148 | impl_approx_xzyw_axes!(f64, DAffine3); 149 | impl_approx_as_ref!(f64, DMat2); 150 | impl_approx_as_ref!(f64, DMat3); 151 | impl_approx_as_ref!(f64, DMat4); 152 | impl_approx_as_ref!(f64, DQuat); 153 | impl_approx_as_ref!(f64, DVec2); 154 | impl_approx_as_ref!(f64, DVec3); 155 | impl_approx_as_ref!(f64, DVec4); 156 | 157 | #[cfg(test)] 158 | mod test { 159 | use crate::*; 160 | use approx::*; 161 | 162 | macro_rules! impl_approx_test { 163 | ($prim:ident, $type:ident, $ones:expr) => { 164 | let one_eps = $ones * $type::default_epsilon(); 165 | let two_eps = one_eps + one_eps; 166 | 167 | let one_ulp = $ones * $prim::from_bits($prim::to_bits(1.0) + 1); 168 | let four_ulp = $ones * $prim::from_bits($prim::to_bits(1.0) + 16); 169 | 170 | approx::assert_abs_diff_eq!($ones, $ones); 171 | approx::assert_abs_diff_eq!($ones, $ones + one_eps); 172 | approx::assert_abs_diff_eq!($ones, $ones - one_eps); 173 | 174 | approx::assert_abs_diff_ne!($ones, $ones + two_eps); 175 | approx::assert_abs_diff_ne!($ones, $ones - two_eps); 176 | 177 | approx::assert_relative_eq!($ones, $ones); 178 | approx::assert_relative_ne!($ones, $ones - $ones); 179 | 180 | // defaults to 4 ulps and I have no idea how to pass other parameters to this macro :) 181 | approx::assert_ulps_eq!($ones, one_ulp); 182 | approx::assert_ulps_ne!($ones, four_ulp); 183 | }; 184 | ($prim:ident, $type:ident) => { 185 | impl_approx_test!($prim, $type, $type::ONE) 186 | }; 187 | } 188 | 189 | #[test] 190 | fn test_approx() { 191 | const ONESF32: [f32; 16] = [1.0; 16]; 192 | 193 | impl_approx_test!(f32, Vec2); 194 | impl_approx_test!(f32, Vec3); 195 | impl_approx_test!(f32, Vec3A); 196 | impl_approx_test!(f32, Vec4); 197 | impl_approx_test!(f32, Quat, Quat::from_slice(&ONESF32)); 198 | impl_approx_test!(f32, Mat2, Mat2::from_cols_slice(&ONESF32)); 199 | impl_approx_test!(f32, Mat3, Mat3::from_cols_slice(&ONESF32)); 200 | impl_approx_test!(f32, Mat3A, Mat3A::from_cols_slice(&ONESF32)); 201 | impl_approx_test!(f32, Mat4, Mat4::from_cols_slice(&ONESF32)); 202 | 203 | const ONESF64: [f64; 16] = [1.0; 16]; 204 | impl_approx_test!(f64, DVec2); 205 | impl_approx_test!(f64, DVec3); 206 | impl_approx_test!(f64, DVec4); 207 | impl_approx_test!(f64, DQuat, DQuat::from_slice(&ONESF64)); 208 | impl_approx_test!(f64, DMat2, DMat2::from_cols_slice(&ONESF64)); 209 | impl_approx_test!(f64, DMat3, DMat3::from_cols_slice(&ONESF64)); 210 | impl_approx_test!(f64, DMat4, DMat4::from_cols_slice(&ONESF64)); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /benches/support/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! bench_func { 3 | ($name: ident, $desc: expr, op => $func: ident, from => $from: expr) => { 4 | pub(crate) fn $name(c: &mut Criterion) { 5 | const SIZE: usize = 1 << 13; 6 | let mut rng = support::PCG32::default(); 7 | let inputs = 8 | criterion::black_box((0..SIZE).map(|_| $from(&mut rng)).collect::>()); 9 | // pre-fill output vector with some random value 10 | let mut outputs = vec![$func($from(&mut rng)); SIZE]; 11 | let mut i = 0; 12 | c.bench_function($desc, |b| { 13 | b.iter(|| { 14 | i = (i + 1) & (SIZE - 1); 15 | unsafe { 16 | *outputs.get_unchecked_mut(i) = $func(*inputs.get_unchecked(i)); 17 | } 18 | }) 19 | }); 20 | criterion::black_box(outputs); 21 | } 22 | }; 23 | } 24 | 25 | #[macro_export] 26 | macro_rules! bench_unop { 27 | ($name: ident, $desc: expr, op => $unop: ident, from => $from: expr) => { 28 | pub(crate) fn $name(c: &mut Criterion) { 29 | const SIZE: usize = 1 << 13; 30 | let mut rng = support::PCG32::default(); 31 | let inputs = 32 | criterion::black_box((0..SIZE).map(|_| $from(&mut rng)).collect::>()); 33 | // pre-fill output vector with some random value 34 | let mut outputs = vec![$from(&mut rng).$unop(); SIZE]; 35 | let mut i = 0; 36 | c.bench_function($desc, |b| { 37 | b.iter(|| { 38 | i = (i + 1) & (SIZE - 1); 39 | unsafe { 40 | *outputs.get_unchecked_mut(i) = inputs.get_unchecked(i).$unop(); 41 | } 42 | }) 43 | }); 44 | criterion::black_box(outputs); 45 | } 46 | }; 47 | } 48 | 49 | #[macro_export] 50 | macro_rules! bench_binop { 51 | ($name: ident, $desc: expr, op => $binop: ident, from1 => $from1:expr, from2 => $from2:expr) => { 52 | pub(crate) fn $name(c: &mut Criterion) { 53 | const SIZE: usize = 1 << 13; 54 | let mut rng = support::PCG32::default(); 55 | let inputs1 = 56 | criterion::black_box((0..SIZE).map(|_| $from1(&mut rng)).collect::>()); 57 | let inputs2 = 58 | criterion::black_box((0..SIZE).map(|_| $from2(&mut rng)).collect::>()); 59 | // pre-fill output vector with some random value 60 | let mut outputs = vec![$from1(&mut rng).$binop($from2(&mut rng)); SIZE]; 61 | let mut i = 0; 62 | c.bench_function($desc, |b| { 63 | b.iter(|| { 64 | i = (i + 1) & (SIZE - 1); 65 | unsafe { 66 | *outputs.get_unchecked_mut(i) = inputs1.get_unchecked(i).$binop(*inputs2.get_unchecked(i)); 67 | } 68 | }) 69 | }); 70 | criterion::black_box(outputs); 71 | } 72 | }; 73 | ($name: ident, $desc: expr, op => $binop: ident, from => $from: expr) => { 74 | bench_binop!($name, $desc, op => $binop, from1 => $from, from2 => $from); 75 | }; 76 | } 77 | 78 | #[macro_export] 79 | macro_rules! bench_trinop { 80 | ($name: ident, $desc: expr, op => $trinop: ident, from1 => $from1:expr, from2 => $from2:expr, from3 => $from3:expr) => { 81 | pub(crate) fn $name(c: &mut Criterion) { 82 | const SIZE: usize = 1 << 13; 83 | let mut rng = support::PCG32::default(); 84 | let inputs1 = 85 | criterion::black_box((0..SIZE).map(|_| $from1(&mut rng)).collect::>()); 86 | let inputs2 = 87 | criterion::black_box((0..SIZE).map(|_| $from2(&mut rng)).collect::>()); 88 | let inputs3 = 89 | criterion::black_box((0..SIZE).map(|_| $from3(&mut rng)).collect::>()); 90 | // pre-fill output vector with some random value 91 | let mut outputs = 92 | vec![$from1(&mut rng).$trinop($from2(&mut rng), $from3(&mut rng)); SIZE]; 93 | let mut i = 0; 94 | c.bench_function($desc, |b| { 95 | b.iter(|| { 96 | i = (i + 1) & (SIZE - 1); 97 | unsafe { 98 | *outputs.get_unchecked_mut(i) = inputs1 99 | .get_unchecked(i) 100 | .$trinop(*inputs2.get_unchecked(i), *inputs3.get_unchecked(i)); 101 | } 102 | }) 103 | }); 104 | criterion::black_box(outputs); 105 | } 106 | }; 107 | } 108 | 109 | #[macro_export] 110 | macro_rules! bench_select { 111 | ($name:ident, $desc:expr, ty => $ty: ident, op => $op: ident, from => $from:expr) => { 112 | pub(crate) fn $name(c: &mut Criterion) { 113 | const SIZE: usize = 1 << 13; 114 | let mut rng = support::PCG32::default(); 115 | let inputs1 = 116 | criterion::black_box((0..SIZE).map(|_| $from(&mut rng)).collect::>()); 117 | let inputs2 = 118 | criterion::black_box((0..SIZE).map(|_| $from(&mut rng)).collect::>()); 119 | let masks = vec![$from(&mut rng).$op($from(&mut rng)); SIZE]; 120 | // pre-fill output vector with some random value 121 | let mut outputs = vec![$from(&mut rng); SIZE]; 122 | let mut i = 0; 123 | c.bench_function($desc, |b| { 124 | b.iter(|| { 125 | i = (i + 1) & (SIZE - 1); 126 | unsafe { 127 | *outputs.get_unchecked_mut(i) = $ty::select( 128 | *masks.get_unchecked(i), 129 | *inputs1.get_unchecked(i), 130 | *inputs2.get_unchecked(i), 131 | ); 132 | } 133 | }) 134 | }); 135 | criterion::black_box(outputs); 136 | } 137 | }; 138 | } 139 | #[macro_export] 140 | macro_rules! bench_from_ypr { 141 | ($name: ident, $desc: expr, ty => $ty:ty) => { 142 | pub(crate) fn $name(c: &mut Criterion) { 143 | const SIZE: usize = 1 << 13; 144 | let mut rng = support::PCG32::default(); 145 | let inputs = criterion::black_box( 146 | (0..SIZE) 147 | .map(|_| { 148 | ( 149 | random_radians(&mut rng), 150 | random_radians(&mut rng), 151 | random_radians(&mut rng), 152 | ) 153 | }) 154 | .collect::>(), 155 | ); 156 | let mut outputs = vec![<$ty>::default(); SIZE]; 157 | let mut i = 0; 158 | c.bench_function($desc, |b| { 159 | b.iter(|| { 160 | i = (i + 1) & (SIZE - 1); 161 | unsafe { 162 | let data = inputs.get_unchecked(i); 163 | *outputs.get_unchecked_mut(i) = 164 | <$ty>::from_euler(glam::EulerRot::YXZ, data.0, data.1, data.2) 165 | } 166 | }) 167 | }); 168 | } 169 | }; 170 | } 171 | 172 | #[macro_export] 173 | macro_rules! euler { 174 | ($name: ident, $desc: expr, ty => $t: ty, storage => $storage: ty, zero => $zero: expr, rand => $rand: ident) => { 175 | pub(crate) fn $name(c: &mut Criterion) { 176 | const UPDATE_RATE: f32 = 1.0 / 60.0; 177 | const NUM_OBJECTS: usize = 10000; 178 | 179 | struct TestData { 180 | acc: Vec<$storage>, 181 | vel: Vec<$storage>, 182 | pos: Vec<$storage>, 183 | } 184 | 185 | let mut rng = support::PCG32::default(); 186 | let mut data = TestData { 187 | acc: vec![$rand(&mut rng); NUM_OBJECTS], 188 | vel: vec![$zero; NUM_OBJECTS], 189 | pos: vec![$zero; NUM_OBJECTS], 190 | }; 191 | let dt = <$t>::splat(UPDATE_RATE); 192 | 193 | c.bench_function($desc, |b| { 194 | b.iter(|| { 195 | for ((position, acceleration), velocity) in 196 | data.pos.iter_mut().zip(&data.acc).zip(&mut data.vel) 197 | { 198 | let local_acc: $t = (*acceleration).into(); 199 | let mut local_pos: $t = (*position).into(); 200 | let mut local_vel: $t = (*velocity).into(); 201 | local_vel += local_acc * dt; 202 | local_pos += local_vel * dt; 203 | *velocity = local_vel.into(); 204 | *position = local_pos.into(); 205 | } 206 | }) 207 | }); 208 | } 209 | }; 210 | } 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # glam 2 | 3 | [![Build Status]][github-ci] [![Coverage Status]][coveralls.io] 4 | [![Latest Version]][crates.io] [![docs]][docs.rs] 5 | [![Minimum Supported Rust Version]][Rust 1.52] 6 | 7 | A simple and fast 3D math library for games and graphics. 8 | 9 | ## Development status 10 | 11 | `glam` is in beta stage. Base functionality has been implemented and the look 12 | and feel of the API has solidified. 13 | 14 | ## Features 15 | 16 | * `f32` types 17 | * vectors: `Vec2`, `Vec3`, `Vec3A` and `Vec4` 18 | * square matrices: `Mat2`, `Mat3`, `Mat3A` and `Mat4` 19 | * a quaternion type: `Quat` 20 | * affine transformation types: `Affine2` and `Affine3A` 21 | * `f64` types 22 | * vectors: `DVec2`, `DVec3` and `DVec4` 23 | * square matrices: `DMat2`, `DMat3` and `DMat4` 24 | * a quaternion type: `DQuat` 25 | * affine transformation types: `DAffine2` and `DAffine3` 26 | * `i32` types 27 | * vectors: `IVec2`, `IVec3` and `IVec4` 28 | * `u32` types 29 | * vectors: `UVec2`, `UVec3` and `UVec4` 30 | * `bool` types 31 | * vectors: `BVec2`, `BVec3` and `BVec4` 32 | 33 | ### SIMD 34 | 35 | The `Vec3A`, `Vec4`, `Quat`, `Mat2`, `Mat3A`, `Mat4`, `Affine2` and `Affine3A` 36 | types use 128-bit wide SIMD vector types for storage on `x86`, `x86_64` and 37 | `wasm32` architectures. As a result, these types are all 16 byte aligned and 38 | depending on the size of the type or the type's members, they may contain 39 | internal padding. This results in some wasted space in the cases of `Vec3A`, 40 | `Mat3A`, `Affine2` and `Affine3A`. However, the use of SIMD generally results 41 | in better performance than scalar math. 42 | 43 | `glam` outperforms similar Rust libraries for common operations as tested by the 44 | [`mathbench`][mathbench] project. 45 | 46 | [mathbench]: https://github.com/bitshifter/mathbench-rs 47 | 48 | ### Enabling SIMD 49 | 50 | SIMD is supported on `x86`, `x86_64` and `wasm32` targets. 51 | 52 | * `SSE2` is enabled by default on `x86_64` targets. 53 | * To enable `SSE2` on `x86` targets add `-C target-feature=+sse2` to 54 | `RUSTCFLAGS`. 55 | * To enable `simd128` on `wasm32` targets add `-C target-feature=+simd128` to 56 | `RUSTFLAGS`. 57 | 58 | Note that SIMD on `wasm32` passes tests but has not been benchmarked, 59 | performance may or may not be better than scalar math. 60 | 61 | ### `no_std` support 62 | 63 | `no_std` support can be enabled by compiling with `--no-default-features` to 64 | disable `std` support and `--features libm` for math functions that are only 65 | defined in `std`. For example: 66 | 67 | ```toml 68 | [dependencies] 69 | glam = { version = "0.20.0", default-features = false, features = ["libm"] } 70 | ``` 71 | 72 | To support both `std` and `no_std` builds in project, you can use the following 73 | in your `Cargo.toml`: 74 | 75 | ```toml 76 | [features] 77 | default = ["std"] 78 | 79 | std = ["glam/std"] 80 | libm = ["glam/libm"] 81 | 82 | [dependencies] 83 | glam = { version = "0.20.0", default-features = false } 84 | ``` 85 | 86 | ### Optional features 87 | 88 | * [`approx`] - traits and macros for approximate float comparisons 89 | * [`bytemuck`] - for casting into slices of bytes 90 | * [`libm`] - required to compile with `no_std` 91 | * [`mint`] - for interoperating with other 3D math libraries 92 | * [`num-traits`] - required to compile `no_std`, will be included when enabling 93 | the `libm` feature 94 | * [`rand`] - implementations of `Distribution` trait for all `glam` types. 95 | * [`serde`] - implementations of `Serialize` and `Deserialize` for all `glam` 96 | types. Note that serialization should work between builds of `glam` with and 97 | without SIMD enabled 98 | * [`rkyv`] - implementations of `Archive`, `Serialize` and `Deserialize` for all 99 | `glam` types. Note that serialization is not interoperable with and without the 100 | `scalar-math` feature. It should work between all other builds of `glam`. 101 | Endian conversion is currently not supported 102 | * [`bytecheck`] - to perform archive validation when using the `rkyv` feature 103 | 104 | [`approx`]: https://docs.rs/approx 105 | [`bytemuck`]: https://docs.rs/bytemuck 106 | [`libm`]: https://github.com/rust-lang/libm 107 | [`mint`]: https://github.com/kvark/mint 108 | [`num-traits`]: https://github.com/rust-num/num-traits 109 | [`rand`]: https://github.com/rust-random/rand 110 | [`serde`]: https://serde.rs 111 | [`rkyv`]: https://github.com/rkyv/rkyv 112 | [`bytecheck`]: https://github.com/rkyv/bytecheck 113 | 114 | ### Feature gates 115 | 116 | * `scalar-math` - compiles with SIMD support disabled 117 | * `debug-glam-assert` - adds assertions in debug builds which check the validity 118 | of parameters passed to `glam` to help catch runtime errors 119 | * `glam-assert` - adds validation assertions to all builds 120 | 121 | ### Minimum Supported Rust Version (MSRV) 122 | 123 | The minimum supported version of Rust for `glam` is `1.52.1`. 124 | 125 | `wasm32` SIMD intrinsics require Rust `1.54.0`. 126 | 127 | ## Conventions 128 | 129 | ### Column vectors 130 | 131 | `glam` interprets vectors as column matrices (also known as "column vectors") 132 | meaning when transforming a vector with a matrix the matrix goes on the left, 133 | e.g. `v' = Mv`. DirectX uses row vectors, OpenGL uses column vectors. There 134 | are pros and cons to both. 135 | 136 | ### Column-major order 137 | 138 | Matrices are stored in column major format. Each column vector is stored in 139 | contiguous memory. 140 | 141 | ### Co-ordinate system 142 | 143 | `glam` is co-ordinate system agnostic and intends to support both right-handed 144 | and left-handed conventions. 145 | 146 | ## Design Philosophy 147 | 148 | The design of this library is guided by a desire for simplicity and good 149 | performance. 150 | 151 | * No generics and minimal traits in the public API for simplicity of usage 152 | * All dependencies are optional (e.g. `mint`, `rand` and `serde`) 153 | * Follows the [Rust API Guidelines] where possible 154 | * Aiming for 100% test [coverage] 155 | * Common functionality is benchmarked using [Criterion.rs] 156 | 157 | [Rust API Guidelines]: https://rust-lang-nursery.github.io/api-guidelines/ 158 | [coverage]: coveralls.io 159 | [Criterion.rs]: https://bheisler.github.io/criterion.rs/book/index.html 160 | 161 | ## Architecture 162 | 163 | See [ARCHITECTURE.md] for details on `glam`'s internals. 164 | 165 | [ARCHITECTURE.md]: ARCHITECTURE.md 166 | 167 | ## Inspirations 168 | 169 | There were many inspirations for the interface and internals of glam from the 170 | Rust and C++ worlds. In particular: 171 | 172 | * [How to write a maths library in 2016] inspired the initial `Vec3A` 173 | implementation 174 | * [Realtime Math] - header only C++11 with SSE and NEON SIMD intrinsic support 175 | * [DirectXMath] - header only SIMD C++ linear algebra library for use in games 176 | and graphics apps 177 | * `glam` is a play on the name of the popular C++ library [GLM] 178 | 179 | [How to write a maths library in 2016]: http://www.codersnotes.com/notes/maths-lib-2016/ 180 | [Realtime Math]: https://github.com/nfrechette/rtm 181 | [DirectXMath]: https://docs.microsoft.com/en-us/windows/desktop/dxmath/directxmath-portal 182 | [GLM]: https://glm.g-truc.net 183 | 184 | ## License 185 | 186 | Licensed under either of 187 | 188 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) 189 | or http://www.apache.org/licenses/LICENSE-2.0) 190 | * MIT license ([LICENSE-MIT](LICENSE-MIT) 191 | or http://opensource.org/licenses/MIT) 192 | 193 | at your option. 194 | 195 | ## Contribution 196 | 197 | Contributions in any form (issues, pull requests, etc.) to this project must 198 | adhere to Rust's [Code of Conduct]. 199 | 200 | Unless you explicitly state otherwise, any contribution intentionally submitted 201 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 202 | dual licensed as above, without any additional terms or conditions. 203 | 204 | Thank you to all of the `glam` [contributors]! 205 | 206 | [Code of Conduct]: https://www.rust-lang.org/en-US/conduct.html 207 | [contributors]: https://github.com/bitshifter/glam-rs/graphs/contributors 208 | 209 | ## Support 210 | 211 | If you are interested in contributing or have a request or suggestion 212 | [start a discussion] on GitHub. See [CONTRIBUTING.md] for more information for 213 | contributors. 214 | 215 | The [Game Development in Rust Discord] and [Bevy Engine Discord] servers can are 216 | also good places to ask for help with `glam`. 217 | 218 | [start a discussion]: https://github.com/bitshifter/glam-rs/discussions 219 | [CONTRIBUTING.md]: CONTRIBUTING.md 220 | [Game Development in Rust Discord]: https://discord.gg/yNtPTb2 221 | [Bevy Engine Discord]: https://discord.gg/gMUk5Ph 222 | 223 | ## Attribution 224 | 225 | `glam` contains code ported from the following C++ libraries: 226 | 227 | * [DirectXMath] - MIT License - Copyright (c) 2011-2020 Microsoft Corp 228 | * [Realtime Math] - MIT License - Copyright (c) 2018 Nicholas Frechette 229 | * [GLM] - MIT License - Copyright (c) 2005 - G-Truc Creation 230 | 231 | See [ATTRIBUTION.md] for details. 232 | 233 | [ATTRIBUTION.md]: ATTRIBUTION.md 234 | 235 | [Build Status]: https://github.com/bitshifter/glam-rs/actions/workflows/ci.yml/badge.svg 236 | [github-ci]: https://github.com/bitshifter/glam-rs/actions/workflows/ci.yml 237 | [Coverage Status]: https://coveralls.io/repos/github/bitshifter/glam-rs/badge.svg?branch=main 238 | [coveralls.io]: https://coveralls.io/github/bitshifter/glam-rs?branch=main 239 | [Latest Version]: https://img.shields.io/crates/v/glam.svg 240 | [crates.io]: https://crates.io/crates/glam/ 241 | [docs]: https://docs.rs/glam/badge.svg 242 | [docs.rs]: https://docs.rs/glam/ 243 | [Minimum Supported Rust Version]: https://img.shields.io/badge/Rust-1.52.1-blue?color=fc8d62&logo=rust 244 | [Rust 1.52]: https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1521-2021-05-10 245 | -------------------------------------------------------------------------------- /src/vec2.rs: -------------------------------------------------------------------------------- 1 | use crate::core::traits::vector::*; 2 | use crate::{BVec2, DVec3, IVec3, UVec3, Vec3, XY}; 3 | #[cfg(not(target_arch = "spirv"))] 4 | use core::fmt; 5 | use core::iter::{Product, Sum}; 6 | use core::{f32, ops::*}; 7 | 8 | #[cfg(not(feature = "std"))] 9 | use num_traits::Float; 10 | 11 | macro_rules! impl_vec2_common_methods { 12 | ($t:ty, $vec2:ident, $vec3:ident, $mask:ident, $inner:ident) => { 13 | /// All zeroes. 14 | pub const ZERO: Self = Self($inner::ZERO); 15 | 16 | /// All ones. 17 | pub const ONE: Self = Self($inner::ONE); 18 | 19 | /// `[1, 0]`: a unit-length vector pointing along the positive X axis. 20 | pub const X: Self = Self($inner::X); 21 | 22 | /// `[0, 1]`: a unit-length vector pointing along the positive Y axis. 23 | pub const Y: Self = Self($inner::Y); 24 | 25 | /// The unit axes. 26 | pub const AXES: [Self; 2] = [Self::X, Self::Y]; 27 | 28 | /// Creates a new vector. 29 | #[inline(always)] 30 | pub fn new(x: $t, y: $t) -> $vec2 { 31 | Self(Vector2::new(x, y)) 32 | } 33 | 34 | /// Creates a 3D vector from `self` and the given `z` value. 35 | #[inline(always)] 36 | pub fn extend(self, z: $t) -> $vec3 { 37 | $vec3::new(self.x, self.y, z) 38 | } 39 | 40 | /// `[x, y]` 41 | #[inline(always)] 42 | pub fn to_array(&self) -> [$t; 2] { 43 | [self.x, self.y] 44 | } 45 | 46 | impl_vecn_common_methods!($t, $vec2, $mask, $inner, Vector2); 47 | }; 48 | } 49 | 50 | macro_rules! impl_vec2_signed_methods { 51 | ($t:ty, $vec2:ident, $vec3:ident, $mask:ident, $inner:ident) => { 52 | impl_vec2_common_methods!($t, $vec2, $vec3, $mask, $inner); 53 | impl_vecn_signed_methods!($t, $vec2, $mask, $inner, SignedVector2); 54 | 55 | /// Returns a vector that is equal to `self` rotated by 90 degrees. 56 | #[inline(always)] 57 | pub fn perp(self) -> Self { 58 | Self(self.0.perp()) 59 | } 60 | 61 | /// The perpendicular dot product of `self` and `other`. 62 | /// Also known as the wedge product, 2d cross product, and determinant. 63 | #[doc(alias = "wedge")] 64 | #[doc(alias = "cross")] 65 | #[doc(alias = "determinant")] 66 | #[inline(always)] 67 | pub fn perp_dot(self, other: $vec2) -> $t { 68 | self.0.perp_dot(other.0) 69 | } 70 | }; 71 | } 72 | 73 | macro_rules! impl_vec2_float_methods { 74 | ($t:ty, $vec2:ident, $vec3:ident, $mask:ident, $inner:ident) => { 75 | impl_vec2_signed_methods!($t, $vec2, $vec3, $mask, $inner); 76 | impl_vecn_float_methods!($t, $vec2, $mask, $inner, FloatVector2); 77 | 78 | /// Returns the angle (in radians) between `self` and `other`. 79 | /// 80 | /// The input vectors do not need to be unit length however they must be non-zero. 81 | #[inline(always)] 82 | pub fn angle_between(self, other: Self) -> $t { 83 | self.0.angle_between(other.0) 84 | } 85 | }; 86 | } 87 | 88 | macro_rules! impl_vec2_common_traits { 89 | ($t:ty, $new:ident, $vec2:ident, $vec3:ident, $mask:ident, $inner:ident) => { 90 | /// Creates a 2-dimensional vector. 91 | #[inline(always)] 92 | pub fn $new(x: $t, y: $t) -> $vec2 { 93 | $vec2::new(x, y) 94 | } 95 | 96 | impl Index for $vec2 { 97 | type Output = $t; 98 | #[inline(always)] 99 | fn index(&self, index: usize) -> &Self::Output { 100 | match index { 101 | 0 => &self.x, 102 | 1 => &self.y, 103 | _ => panic!("index out of bounds"), 104 | } 105 | } 106 | } 107 | 108 | impl IndexMut for $vec2 { 109 | #[inline(always)] 110 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 111 | match index { 112 | 0 => &mut self.x, 113 | 1 => &mut self.y, 114 | _ => panic!("index out of bounds"), 115 | } 116 | } 117 | } 118 | #[cfg(not(target_arch = "spirv"))] 119 | impl fmt::Display for $vec2 { 120 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 121 | write!(f, "[{}, {}]", self.x, self.y) 122 | } 123 | } 124 | 125 | #[cfg(not(target_arch = "spirv"))] 126 | impl fmt::Debug for $vec2 { 127 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 128 | fmt.debug_tuple(stringify!($vec2)) 129 | .field(&self.x) 130 | .field(&self.y) 131 | .finish() 132 | } 133 | } 134 | 135 | impl From<($t, $t)> for $vec2 { 136 | #[inline(always)] 137 | fn from(t: ($t, $t)) -> Self { 138 | Self($inner::from_tuple(t)) 139 | } 140 | } 141 | 142 | impl From<$vec2> for ($t, $t) { 143 | #[inline(always)] 144 | fn from(v: $vec2) -> Self { 145 | v.0.into_tuple() 146 | } 147 | } 148 | 149 | impl Deref for $vec2 { 150 | type Target = XY<$t>; 151 | #[inline(always)] 152 | fn deref(&self) -> &Self::Target { 153 | self.0.as_ref_xy() 154 | } 155 | } 156 | 157 | impl DerefMut for $vec2 { 158 | #[inline(always)] 159 | fn deref_mut(&mut self) -> &mut Self::Target { 160 | self.0.as_mut_xy() 161 | } 162 | } 163 | 164 | impl_vecn_common_traits!($t, 2, $vec2, $inner, Vector2); 165 | }; 166 | } 167 | 168 | macro_rules! impl_vec2_unsigned_traits { 169 | ($t:ty, $new:ident, $vec2:ident, $vec3:ident, $mask:ident, $inner:ident) => { 170 | impl_vec2_common_traits!($t, $new, $vec2, $vec3, $mask, $inner); 171 | }; 172 | } 173 | 174 | macro_rules! impl_vec2_signed_traits { 175 | ($t:ty, $new:ident, $vec2:ident, $vec3:ident, $mask:ident, $inner:ident) => { 176 | impl_vec2_common_traits!($t, $new, $vec2, $vec3, $mask, $inner); 177 | impl_vecn_signed_traits!($t, 2, $vec2, $inner, SignedVector2); 178 | }; 179 | } 180 | 181 | type XYF32 = XY; 182 | 183 | /// A 2-dimensional vector. 184 | #[derive(Clone, Copy)] 185 | #[repr(transparent)] 186 | pub struct Vec2(pub(crate) XYF32); 187 | 188 | impl Vec2 { 189 | impl_vec2_float_methods!(f32, Vec2, Vec3, BVec2, XYF32); 190 | impl_as_dvec2!(); 191 | impl_as_ivec2!(); 192 | impl_as_uvec2!(); 193 | } 194 | impl_vec2_signed_traits!(f32, vec2, Vec2, Vec3, BVec2, XYF32); 195 | 196 | type XYF64 = XY; 197 | 198 | /// A 2-dimensional vector. 199 | #[derive(Clone, Copy)] 200 | #[repr(transparent)] 201 | pub struct DVec2(pub(crate) XYF64); 202 | 203 | impl DVec2 { 204 | impl_vec2_float_methods!(f64, DVec2, DVec3, BVec2, XYF64); 205 | impl_as_vec2!(); 206 | impl_as_ivec2!(); 207 | impl_as_uvec2!(); 208 | } 209 | impl_vec2_signed_traits!(f64, dvec2, DVec2, DVec3, BVec2, XYF64); 210 | 211 | type XYI32 = XY; 212 | 213 | /// A 2-dimensional vector. 214 | #[derive(Clone, Copy)] 215 | #[repr(transparent)] 216 | pub struct IVec2(pub(crate) XYI32); 217 | 218 | impl IVec2 { 219 | impl_vec2_signed_methods!(i32, IVec2, IVec3, BVec2, XYI32); 220 | impl_as_vec2!(); 221 | impl_as_dvec2!(); 222 | impl_as_uvec2!(); 223 | } 224 | impl_vec2_signed_traits!(i32, ivec2, IVec2, IVec3, BVec2, XYI32); 225 | impl_vecn_eq_hash_traits!(i32, 2, IVec2); 226 | 227 | impl_vecn_scalar_shift_op_traits!(IVec2, i8, XYI32); 228 | impl_vecn_scalar_shift_op_traits!(IVec2, i16, XYI32); 229 | impl_vecn_scalar_shift_op_traits!(IVec2, i32, XYI32); 230 | impl_vecn_scalar_shift_op_traits!(IVec2, u8, XYI32); 231 | impl_vecn_scalar_shift_op_traits!(IVec2, u16, XYI32); 232 | impl_vecn_scalar_shift_op_traits!(IVec2, u32, XYI32); 233 | 234 | impl_vecn_shift_op_traits!(IVec2, IVec2, XYI32); 235 | impl_vecn_shift_op_traits!(IVec2, UVec2, XYI32); 236 | 237 | impl_vecn_scalar_bit_op_traits!(IVec2, i32, XYI32); 238 | 239 | impl_vecn_bit_op_traits!(IVec2, XYI32); 240 | 241 | type XYU32 = XY; 242 | 243 | /// A 2-dimensional vector. 244 | #[derive(Clone, Copy)] 245 | #[repr(transparent)] 246 | pub struct UVec2(pub(crate) XYU32); 247 | 248 | impl UVec2 { 249 | impl_vec2_common_methods!(u32, UVec2, UVec3, BVec2, XYU32); 250 | impl_as_vec2!(); 251 | impl_as_dvec2!(); 252 | impl_as_ivec2!(); 253 | } 254 | impl_vec2_unsigned_traits!(u32, uvec2, UVec2, UVec3, BVec2, XYU32); 255 | impl_vecn_eq_hash_traits!(u32, 2, UVec2); 256 | 257 | impl_vecn_scalar_shift_op_traits!(UVec2, i8, XYU32); 258 | impl_vecn_scalar_shift_op_traits!(UVec2, i16, XYU32); 259 | impl_vecn_scalar_shift_op_traits!(UVec2, i32, XYU32); 260 | impl_vecn_scalar_shift_op_traits!(UVec2, u8, XYU32); 261 | impl_vecn_scalar_shift_op_traits!(UVec2, u16, XYU32); 262 | impl_vecn_scalar_shift_op_traits!(UVec2, u32, XYU32); 263 | 264 | impl_vecn_shift_op_traits!(UVec2, IVec2, XYU32); 265 | impl_vecn_shift_op_traits!(UVec2, UVec2, XYU32); 266 | 267 | impl_vecn_scalar_bit_op_traits!(UVec2, u32, XYU32); 268 | 269 | impl_vecn_bit_op_traits!(UVec2, XYU32); 270 | 271 | mod const_test_vec2 { 272 | const_assert_eq!( 273 | core::mem::align_of::(), 274 | core::mem::align_of::() 275 | ); 276 | const_assert_eq!(8, core::mem::size_of::()); 277 | } 278 | 279 | mod const_test_dvec2 { 280 | const_assert_eq!( 281 | core::mem::align_of::(), 282 | core::mem::align_of::() 283 | ); 284 | const_assert_eq!(16, core::mem::size_of::()); 285 | } 286 | 287 | mod const_test_ivec2 { 288 | const_assert_eq!( 289 | core::mem::align_of::(), 290 | core::mem::align_of::() 291 | ); 292 | const_assert_eq!(8, core::mem::size_of::()); 293 | } 294 | 295 | mod const_test_uvec2 { 296 | const_assert_eq!( 297 | core::mem::align_of::(), 298 | core::mem::align_of::() 299 | ); 300 | const_assert_eq!(8, core::mem::size_of::()); 301 | } 302 | -------------------------------------------------------------------------------- /tests/mat2.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod support; 3 | 4 | macro_rules! impl_mat2_tests { 5 | ($t:ident, $const_new:ident, $newmat2:ident, $mat2:ident, $mat3:ident, $newvec2:ident, $vec2:ident) => { 6 | const IDENTITY: [[$t; 2]; 2] = [[1.0, 0.0], [0.0, 1.0]]; 7 | 8 | const MATRIX: [[$t; 2]; 2] = [[1.0, 2.0], [3.0, 4.0]]; 9 | 10 | glam_test!(test_const, { 11 | const M0: $mat2 = $const_new!([0.0; 4]); 12 | const M1: $mat2 = $const_new!([1.0, 2.0, 3.0, 4.0]); 13 | const M2: $mat2 = $const_new!([1.0, 2.0], [3.0, 4.0]); 14 | assert_eq!($mat2::ZERO, M0); 15 | assert_eq!($mat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0]), M1); 16 | assert_eq!($mat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0]), M2); 17 | }); 18 | 19 | glam_test!(test_mat2_identity, { 20 | assert_eq!($mat2::IDENTITY, $mat2::from_cols_array(&[1., 0., 0., 1.])); 21 | let identity = $mat2::IDENTITY; 22 | assert_eq!(IDENTITY, identity.to_cols_array_2d()); 23 | assert_eq!($mat2::from_cols_array_2d(&IDENTITY), identity); 24 | assert_eq!(identity, identity * identity); 25 | assert_eq!(identity, $mat2::default()); 26 | }); 27 | 28 | glam_test!(test_mat2_zero, { 29 | assert_eq!($mat2::ZERO, $mat2::from_cols_array(&[0., 0., 0., 0.])); 30 | }); 31 | 32 | glam_test!(test_mat2_nan, { 33 | assert!($mat2::NAN.is_nan()); 34 | assert!(!$mat2::NAN.is_finite()); 35 | }); 36 | 37 | glam_test!(test_mat2_accessors, { 38 | let mut m = $mat2::ZERO; 39 | m.x_axis = $vec2::new(1.0, 2.0); 40 | m.y_axis = $vec2::new(3.0, 4.0); 41 | assert_eq!($mat2::from_cols_array_2d(&MATRIX), m); 42 | assert_eq!($vec2::new(1.0, 2.0), m.x_axis); 43 | assert_eq!($vec2::new(3.0, 4.0), m.y_axis); 44 | 45 | assert_eq!($vec2::new(1.0, 2.0), m.col(0)); 46 | assert_eq!($vec2::new(3.0, 4.0), m.col(1)); 47 | 48 | assert_eq!($newvec2(1.0, 3.0), m.row(0)); 49 | assert_eq!($newvec2(2.0, 4.0), m.row(1)); 50 | 51 | *m.col_mut(0) = m.col(0).yx(); 52 | *m.col_mut(1) = m.col(1).yx(); 53 | assert_eq!($vec2::new(2.0, 1.0), m.col(0)); 54 | assert_eq!($vec2::new(4.0, 3.0), m.col(1)); 55 | 56 | should_panic!({ $mat2::ZERO.col(2) }); 57 | should_panic!({ 58 | let mut m = $mat2::ZERO; 59 | m.col_mut(2); 60 | }); 61 | should_panic!({ $mat2::ZERO.row(2) }); 62 | }); 63 | 64 | glam_test!(test_mat2_from_axes, { 65 | let a = $mat2::from_cols_array_2d(&[[1.0, 2.0], [3.0, 4.0]]); 66 | assert_eq!(MATRIX, a.to_cols_array_2d()); 67 | let b = $mat2::from_cols($newvec2(1.0, 2.0), $newvec2(3.0, 4.0)); 68 | assert_eq!(a, b); 69 | let c = $newmat2($newvec2(1.0, 2.0), $newvec2(3.0, 4.0)); 70 | assert_eq!(a, c); 71 | let d = b.to_cols_array(); 72 | let f = $mat2::from_cols_array(&d); 73 | assert_eq!(b, f); 74 | }); 75 | 76 | glam_test!(test_mat2_mul, { 77 | let mat_a = $mat2::from_angle(deg(90.0)); 78 | let res_a = mat_a * $vec2::Y; 79 | assert_approx_eq!($newvec2(-1.0, 0.0), res_a); 80 | let res_b = mat_a * $vec2::X; 81 | assert_approx_eq!($newvec2(0.0, 1.0), res_b); 82 | }); 83 | 84 | glam_test!(test_from_scale_angle, { 85 | let rot = $mat2::from_scale_angle($vec2::new(4.0, 2.0), deg(180.0)); 86 | assert_approx_eq!($vec2::X * -4.0, rot * $vec2::X, 1.0e-6); 87 | assert_approx_eq!($vec2::Y * -2.0, rot * $vec2::Y, 1.0e-6); 88 | }); 89 | 90 | glam_test!(test_from_diagonal, { 91 | let m = $mat2::from_diagonal($vec2::new(2 as $t, 4 as $t)); 92 | assert_eq!( 93 | $mat2::from_cols_array_2d(&[[2 as $t, 0 as $t], [0 as $t, 4 as $t]]), 94 | m 95 | ); 96 | assert_approx_eq!(m * $vec2::new(1.0, 1.0), $vec2::new(2.0, 4.0)); 97 | assert_approx_eq!($vec2::X * 2.0, m.x_axis); 98 | assert_approx_eq!($vec2::Y * 4.0, m.y_axis); 99 | }); 100 | 101 | glam_test!(test_from_mat3, { 102 | let m3 = 103 | $mat3::from_cols_array_2d(&[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]); 104 | let m2 = $mat2::from_mat3(m3); 105 | assert_eq!($mat2::from_cols_array_2d(&[[1.0, 2.0], [4.0, 5.0]]), m2); 106 | }); 107 | 108 | glam_test!(test_mat2_transpose, { 109 | let m = $newmat2($newvec2(1.0, 2.0), $newvec2(3.0, 4.0)); 110 | let mt = m.transpose(); 111 | assert_eq!($newvec2(1.0, 3.0), mt.x_axis); 112 | assert_eq!($newvec2(2.0, 4.0), mt.y_axis); 113 | }); 114 | 115 | glam_test!(test_mat2_det, { 116 | assert_eq!(0.0, $mat2::ZERO.determinant()); 117 | assert_eq!(1.0, $mat2::IDENTITY.determinant()); 118 | assert_eq!(1.0, $mat2::from_angle(deg(90.0)).determinant()); 119 | assert_eq!(1.0, $mat2::from_angle(deg(180.0)).determinant()); 120 | assert_eq!(1.0, $mat2::from_angle(deg(270.0)).determinant()); 121 | assert_eq!( 122 | 2.0 * 2.0, 123 | $mat2::from_diagonal($newvec2(2.0, 2.0)).determinant() 124 | ); 125 | assert_eq!( 126 | 1.0 * 4.0 - 2.0 * 3.0, 127 | $mat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0]).determinant() 128 | ); 129 | }); 130 | 131 | glam_test!(test_mat2_inverse, { 132 | let inv = $mat2::IDENTITY.inverse(); 133 | assert_approx_eq!($mat2::IDENTITY, inv); 134 | 135 | let rot = $mat2::from_angle(deg(90.0)); 136 | let rot_inv = rot.inverse(); 137 | assert_approx_eq!($mat2::IDENTITY, rot * rot_inv); 138 | assert_approx_eq!($mat2::IDENTITY, rot_inv * rot); 139 | 140 | let scale = $mat2::from_diagonal($newvec2(4.0, 5.0)); 141 | let scale_inv = scale.inverse(); 142 | assert_approx_eq!($mat2::IDENTITY, scale * scale_inv); 143 | assert_approx_eq!($mat2::IDENTITY, scale_inv * scale); 144 | 145 | let m = scale * rot; 146 | let m_inv = m.inverse(); 147 | assert_approx_eq!($mat2::IDENTITY, m * m_inv); 148 | assert_approx_eq!($mat2::IDENTITY, m_inv * m); 149 | assert_approx_eq!(m_inv, rot_inv * scale_inv); 150 | 151 | should_glam_assert!({ $mat2::ZERO.inverse() }); 152 | }); 153 | 154 | glam_test!(test_mat2_ops, { 155 | let m0 = $mat2::from_cols_array_2d(&MATRIX); 156 | let m0x2 = $mat2::from_cols_array_2d(&[[2.0, 4.0], [6.0, 8.0]]); 157 | assert_eq!(m0x2, m0 * 2.0); 158 | assert_eq!(m0x2, 2.0 * m0); 159 | assert_eq!(m0x2, m0 + m0); 160 | assert_eq!($mat2::ZERO, m0 - m0); 161 | assert_approx_eq!(m0, m0 * $mat2::IDENTITY); 162 | assert_approx_eq!(m0, $mat2::IDENTITY * m0); 163 | 164 | let mut m1 = m0; 165 | m1 *= 2.0; 166 | assert_eq!(m0x2, m1); 167 | 168 | let mut m1 = m0; 169 | m1 += m0; 170 | assert_eq!(m0x2, m1); 171 | 172 | let mut m1 = m0; 173 | m1 -= m0; 174 | assert_eq!($mat2::ZERO, m1); 175 | 176 | let mut m1 = $mat2::IDENTITY; 177 | m1 *= m0; 178 | assert_approx_eq!(m0, m1); 179 | }); 180 | 181 | glam_test!(test_mat2_fmt, { 182 | let a = $mat2::from_cols_array_2d(&MATRIX); 183 | assert_eq!(format!("{}", a), "[[1, 2], [3, 4]]"); 184 | }); 185 | 186 | glam_test!(test_mat2_to_from_slice, { 187 | const MATRIX1D: [$t; 4] = [1.0, 2.0, 3.0, 4.0]; 188 | let m = $mat2::from_cols_slice(&MATRIX1D); 189 | assert_eq!($mat2::from_cols_array(&MATRIX1D), m); 190 | let mut out: [$t; 4] = Default::default(); 191 | m.write_cols_to_slice(&mut out); 192 | assert_eq!(MATRIX1D, out); 193 | 194 | should_panic!({ $mat2::from_cols_slice(&[0.0; 3]) }); 195 | should_panic!({ $mat2::IDENTITY.write_cols_to_slice(&mut [0.0; 3]) }); 196 | }); 197 | 198 | glam_test!(test_sum, { 199 | let id = $mat2::IDENTITY; 200 | assert_eq!(vec![id, id].iter().sum::<$mat2>(), id + id); 201 | }); 202 | 203 | glam_test!(test_product, { 204 | let two = $mat2::IDENTITY + $mat2::IDENTITY; 205 | assert_eq!(vec![two, two].iter().product::<$mat2>(), two * two); 206 | }); 207 | 208 | glam_test!(test_mat2_is_finite, { 209 | use std::$t::INFINITY; 210 | use std::$t::NAN; 211 | use std::$t::NEG_INFINITY; 212 | assert!($mat2::IDENTITY.is_finite()); 213 | assert!(!($mat2::IDENTITY * INFINITY).is_finite()); 214 | assert!(!($mat2::IDENTITY * NEG_INFINITY).is_finite()); 215 | assert!(!($mat2::IDENTITY * NAN).is_finite()); 216 | }); 217 | }; 218 | } 219 | 220 | mod mat2 { 221 | use super::support::deg; 222 | use glam::{const_mat2, mat2, swizzles::*, vec2, Mat2, Mat3, Vec2}; 223 | 224 | glam_test!(test_align, { 225 | use std::mem; 226 | assert_eq!(16, mem::size_of::()); 227 | if cfg!(feature = "scalar-math") { 228 | assert_eq!(4, mem::align_of::()); 229 | } else { 230 | assert_eq!(16, mem::align_of::()); 231 | } 232 | }); 233 | 234 | glam_test!(test_as, { 235 | use glam::DMat2; 236 | assert_eq!( 237 | DMat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0]), 238 | Mat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0]).as_dmat2() 239 | ); 240 | assert_eq!( 241 | Mat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0]), 242 | DMat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0]).as_mat2() 243 | ); 244 | }); 245 | 246 | impl_mat2_tests!(f32, const_mat2, mat2, Mat2, Mat3, vec2, Vec2); 247 | } 248 | 249 | mod dmat2 { 250 | use super::support::deg; 251 | use glam::{const_dmat2, dmat2, dvec2, swizzles::*, DMat2, DMat3, DVec2}; 252 | 253 | glam_test!(test_align, { 254 | use std::mem; 255 | assert_eq!(32, mem::size_of::()); 256 | assert_eq!(mem::align_of::(), mem::align_of::()); 257 | }); 258 | 259 | impl_mat2_tests!(f64, const_dmat2, dmat2, DMat2, DMat3, dvec2, DVec2); 260 | } 261 | -------------------------------------------------------------------------------- /src/core/sse2/float.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "x86")] 2 | use core::arch::x86::*; 3 | #[cfg(target_arch = "x86_64")] 4 | use core::arch::x86_64::*; 5 | 6 | macro_rules! const_u32x4 { 7 | ($ux4:expr) => { 8 | unsafe { $crate::cast::UVec4Cast { ux4: $ux4 }.m128 } 9 | }; 10 | } 11 | 12 | const PS_INV_SIGN_MASK: __m128 = const_u32x4!([!0x8000_0000; 4]); 13 | const PS_SIGN_MASK: __m128 = const_u32x4!([0x8000_0000; 4]); 14 | const PS_NO_FRACTION: __m128 = const_f32x4!([8388608.0; 4]); 15 | const PS_NEGATIVE_ZERO: __m128 = const_u32x4!([0x8000_0000; 4]); 16 | const PS_PI: __m128 = const_f32x4!([core::f32::consts::PI; 4]); 17 | const PS_HALF_PI: __m128 = const_f32x4!([core::f32::consts::FRAC_PI_2; 4]); 18 | const PS_SIN_COEFFICIENTS0: __m128 = 19 | const_f32x4!([-0.16666667, 0.008_333_331, -0.00019840874, 2.752_556_2e-6]); 20 | const PS_SIN_COEFFICIENTS1: __m128 = const_f32x4!([ 21 | -2.388_985_9e-8, 22 | -0.16665852, /*Est1*/ 23 | 0.008_313_95, /*Est2*/ 24 | -0.000_185_246_7 /*Est3*/ 25 | ]); 26 | const PS_ONE: __m128 = const_f32x4!([1.0; 4]); 27 | const PS_TWO_PI: __m128 = const_f32x4!([core::f32::consts::TAU; 4]); 28 | const PS_RECIPROCAL_TWO_PI: __m128 = const_f32x4!([0.159_154_94; 4]); 29 | 30 | #[inline] 31 | pub(crate) unsafe fn m128_abs(v: __m128) -> __m128 { 32 | _mm_and_ps(v, _mm_castsi128_ps(_mm_set1_epi32(0x7f_ff_ff_ff))) 33 | } 34 | 35 | #[inline] 36 | pub(crate) unsafe fn m128_round(v: __m128) -> __m128 { 37 | // Based on https://github.com/microsoft/DirectXMath `XMVectorRound` 38 | let sign = _mm_and_ps(v, PS_SIGN_MASK); 39 | let s_magic = _mm_or_ps(PS_NO_FRACTION, sign); 40 | let r1 = _mm_add_ps(v, s_magic); 41 | let r1 = _mm_sub_ps(r1, s_magic); 42 | let r2 = _mm_and_ps(v, PS_INV_SIGN_MASK); 43 | let mask = _mm_cmple_ps(r2, PS_NO_FRACTION); 44 | let r2 = _mm_andnot_ps(mask, v); 45 | let r1 = _mm_and_ps(r1, mask); 46 | _mm_xor_ps(r1, r2) 47 | } 48 | 49 | #[inline] 50 | pub(crate) unsafe fn m128_floor(v: __m128) -> __m128 { 51 | // Based on https://github.com/microsoft/DirectXMath `XMVectorFloor` 52 | // To handle NAN, INF and numbers greater than 8388608, use masking 53 | let test = _mm_and_si128(_mm_castps_si128(v), _mm_castps_si128(PS_INV_SIGN_MASK)); 54 | let test = _mm_cmplt_epi32(test, _mm_castps_si128(PS_NO_FRACTION)); 55 | // Truncate 56 | let vint = _mm_cvttps_epi32(v); 57 | let result = _mm_cvtepi32_ps(vint); 58 | let larger = _mm_cmpgt_ps(result, v); 59 | // 0 -> 0, 0xffffffff -> -1.0f 60 | let larger = _mm_cvtepi32_ps(_mm_castps_si128(larger)); 61 | let result = _mm_add_ps(result, larger); 62 | // All numbers less than 8388608 will use the round to int 63 | let result = _mm_and_ps(result, _mm_castsi128_ps(test)); 64 | // All others, use the ORIGINAL value 65 | let test = _mm_andnot_si128(test, _mm_castps_si128(v)); 66 | _mm_or_ps(result, _mm_castsi128_ps(test)) 67 | } 68 | 69 | #[inline] 70 | pub(crate) unsafe fn m128_ceil(v: __m128) -> __m128 { 71 | // Based on https://github.com/microsoft/DirectXMath `XMVectorCeil` 72 | // To handle NAN, INF and numbers greater than 8388608, use masking 73 | let test = _mm_and_si128(_mm_castps_si128(v), _mm_castps_si128(PS_INV_SIGN_MASK)); 74 | let test = _mm_cmplt_epi32(test, _mm_castps_si128(PS_NO_FRACTION)); 75 | // Truncate 76 | let vint = _mm_cvttps_epi32(v); 77 | let result = _mm_cvtepi32_ps(vint); 78 | let smaller = _mm_cmplt_ps(result, v); 79 | // 0 -> 0, 0xffffffff -> -1.0f 80 | let smaller = _mm_cvtepi32_ps(_mm_castps_si128(smaller)); 81 | let result = _mm_sub_ps(result, smaller); 82 | // All numbers less than 8388608 will use the round to int 83 | let result = _mm_and_ps(result, _mm_castsi128_ps(test)); 84 | // All others, use the ORIGINAL value 85 | let test = _mm_andnot_si128(test, _mm_castps_si128(v)); 86 | _mm_or_ps(result, _mm_castsi128_ps(test)) 87 | } 88 | 89 | #[inline(always)] 90 | pub(crate) unsafe fn m128_mul_add(a: __m128, b: __m128, c: __m128) -> __m128 { 91 | #[cfg(target_feature = "fma")] 92 | { 93 | _mm_fmadd_ps(a, b, c) 94 | } 95 | 96 | #[cfg(not(target_feature = "fma"))] 97 | { 98 | _mm_add_ps(_mm_mul_ps(a, b), c) 99 | } 100 | } 101 | 102 | #[inline(always)] 103 | pub(crate) unsafe fn m128_neg_mul_sub(a: __m128, b: __m128, c: __m128) -> __m128 { 104 | _mm_sub_ps(c, _mm_mul_ps(a, b)) 105 | } 106 | 107 | /// Returns a vector whose components are the corresponding components of Angles modulo 2PI. 108 | #[inline] 109 | pub(crate) unsafe fn m128_mod_angles(angles: __m128) -> __m128 { 110 | // Based on https://github.com/microsoft/DirectXMath `XMVectorModAngles` 111 | let v = _mm_mul_ps(angles, PS_RECIPROCAL_TWO_PI); 112 | let v = m128_round(v); 113 | m128_neg_mul_sub(PS_TWO_PI, v, angles) 114 | } 115 | 116 | /// Computes the sine of the angle in each lane of `v`. Values outside 117 | /// the bounds of PI may produce an increasing error as the input angle 118 | /// drifts from `[-PI, PI]`. 119 | #[inline] 120 | pub(crate) unsafe fn m128_sin(v: __m128) -> __m128 { 121 | // Based on https://github.com/microsoft/DirectXMath `XMVectorSin` 122 | 123 | // 11-degree minimax approximation 124 | 125 | // Force the value within the bounds of pi 126 | let mut x = m128_mod_angles(v); 127 | 128 | // Map in [-pi/2,pi/2] with sin(y) = sin(x). 129 | let sign = _mm_and_ps(x, PS_NEGATIVE_ZERO); 130 | // pi when x >= 0, -pi when x < 0 131 | let c = _mm_or_ps(PS_PI, sign); 132 | // |x| 133 | let absx = _mm_andnot_ps(sign, x); 134 | let rflx = _mm_sub_ps(c, x); 135 | let comp = _mm_cmple_ps(absx, PS_HALF_PI); 136 | let select0 = _mm_and_ps(comp, x); 137 | let select1 = _mm_andnot_ps(comp, rflx); 138 | x = _mm_or_ps(select0, select1); 139 | 140 | let x2 = _mm_mul_ps(x, x); 141 | 142 | // Compute polynomial approximation 143 | const SC1: __m128 = PS_SIN_COEFFICIENTS1; 144 | let v_constants_b = _mm_shuffle_ps(SC1, SC1, 0b00_00_00_00); 145 | 146 | const SC0: __m128 = PS_SIN_COEFFICIENTS0; 147 | let mut v_constants = _mm_shuffle_ps(SC0, SC0, 0b11_11_11_11); 148 | let mut result = m128_mul_add(v_constants_b, x2, v_constants); 149 | 150 | v_constants = _mm_shuffle_ps(SC0, SC0, 0b10_10_10_10); 151 | result = m128_mul_add(result, x2, v_constants); 152 | 153 | v_constants = _mm_shuffle_ps(SC0, SC0, 0b01_01_01_01); 154 | result = m128_mul_add(result, x2, v_constants); 155 | 156 | v_constants = _mm_shuffle_ps(SC0, SC0, 0b00_00_00_00); 157 | result = m128_mul_add(result, x2, v_constants); 158 | 159 | result = m128_mul_add(result, x2, PS_ONE); 160 | result = _mm_mul_ps(result, x); 161 | 162 | result 163 | } 164 | 165 | // Based on http://gruntthepeon.free.fr/ssemath/sse_mathfun.h 166 | // #[cfg(target_feature = "sse2")] 167 | // unsafe fn sin_cos_sse2(x: __m128) -> (__m128, __m128) { 168 | // let mut sign_bit_sin = x; 169 | // // take the absolute value 170 | // let mut x = _mm_and_ps(x, PS_INV_SIGN_MASK.m128); 171 | // // extract the sign bit (upper one) 172 | // sign_bit_sin = _mm_and_ps(sign_bit_sin, PS_SIGN_MASK.m128); 173 | 174 | // // scale by 4/Pi 175 | // let mut y = _mm_mul_ps(x, PS_CEPHES_FOPI.m128); 176 | 177 | // // store the integer part of y in emm2 178 | // let mut emm2 = _mm_cvttps_epi32(y); 179 | 180 | // // j=(j+1) & (~1) (see the cephes sources) 181 | // emm2 = _mm_add_epi32(emm2, PI32_1.m128i); 182 | // emm2 = _mm_and_si128(emm2, PI32_INV_1.m128i); 183 | // y = _mm_cvtepi32_ps(emm2); 184 | 185 | // let mut emm4 = emm2; 186 | 187 | // /* get the swap sign flag for the sine */ 188 | // let mut emm0 = _mm_and_si128(emm2, PI32_4.m128i); 189 | // emm0 = _mm_slli_epi32(emm0, 29); 190 | // let swap_sign_bit_sin = _mm_castsi128_ps(emm0); 191 | 192 | // /* get the polynom selection mask for the sine*/ 193 | // emm2 = _mm_and_si128(emm2, PI32_2.m128i); 194 | // emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128()); 195 | // let poly_mask = _mm_castsi128_ps(emm2); 196 | 197 | // /* The magic pass: "Extended precision modular arithmetic" 198 | // x = ((x - y * DP1) - y * DP2) - y * DP3; */ 199 | // let mut xmm1 = PS_MINUS_CEPHES_DP1.m128; 200 | // let mut xmm2 = PS_MINUS_CEPHES_DP2.m128; 201 | // let mut xmm3 = PS_MINUS_CEPHES_DP3.m128; 202 | // xmm1 = _mm_mul_ps(y, xmm1); 203 | // xmm2 = _mm_mul_ps(y, xmm2); 204 | // xmm3 = _mm_mul_ps(y, xmm3); 205 | // x = _mm_add_ps(x, xmm1); 206 | // x = _mm_add_ps(x, xmm2); 207 | // x = _mm_add_ps(x, xmm3); 208 | 209 | // emm4 = _mm_sub_epi32(emm4, PI32_2.m128i); 210 | // emm4 = _mm_andnot_si128(emm4, PI32_4.m128i); 211 | // emm4 = _mm_slli_epi32(emm4, 29); 212 | // let sign_bit_cos = _mm_castsi128_ps(emm4); 213 | 214 | // sign_bit_sin = _mm_xor_ps(sign_bit_sin, swap_sign_bit_sin); 215 | 216 | // // Evaluate the first polynom (0 <= x <= Pi/4) 217 | // let z = _mm_mul_ps(x, x); 218 | // y = PS_COSCOF_P0.m128; 219 | 220 | // y = _mm_mul_ps(y, z); 221 | // y = _mm_add_ps(y, PS_COSCOF_P1.m128); 222 | // y = _mm_mul_ps(y, z); 223 | // y = _mm_add_ps(y, PS_COSCOF_P2.m128); 224 | // y = _mm_mul_ps(y, z); 225 | // y = _mm_mul_ps(y, z); 226 | // let tmp = _mm_mul_ps(z, PS_0_5.m128); 227 | // y = _mm_sub_ps(y, tmp); 228 | // y = _mm_add_ps(y, PS_1_0.m128); 229 | 230 | // // Evaluate the second polynom (Pi/4 <= x <= 0) 231 | // let mut y2 = PS_SINCOF_P0.m128; 232 | // y2 = _mm_mul_ps(y2, z); 233 | // y2 = _mm_add_ps(y2, PS_SINCOF_P1.m128); 234 | // y2 = _mm_mul_ps(y2, z); 235 | // y2 = _mm_add_ps(y2, PS_SINCOF_P2.m128); 236 | // y2 = _mm_mul_ps(y2, z); 237 | // y2 = _mm_mul_ps(y2, x); 238 | // y2 = _mm_add_ps(y2, x); 239 | 240 | // // select the correct result from the two polynoms 241 | // xmm3 = poly_mask; 242 | // let ysin2 = _mm_and_ps(xmm3, y2); 243 | // let ysin1 = _mm_andnot_ps(xmm3, y); 244 | // y2 = _mm_sub_ps(y2, ysin2); 245 | // y = _mm_sub_ps(y, ysin1); 246 | 247 | // xmm1 = _mm_add_ps(ysin1, ysin2); 248 | // xmm2 = _mm_add_ps(y, y2); 249 | 250 | // // update the sign 251 | // ( 252 | // _mm_xor_ps(xmm1, sign_bit_sin), 253 | // _mm_xor_ps(xmm2, sign_bit_cos), 254 | // ) 255 | // } 256 | 257 | #[test] 258 | fn test_sse2_m128_sin() { 259 | use crate::core::traits::vector::*; 260 | use core::f32::consts::PI; 261 | 262 | fn test_sse2_m128_sin_angle(a: f32) { 263 | let v = unsafe { m128_sin(_mm_set_ps1(a)) }; 264 | let v = v.as_ref_xyzw(); 265 | let a_sin = a.sin(); 266 | // dbg!((a, a_sin, v)); 267 | assert!(v.abs_diff_eq(Vector::splat(a_sin), 1e-6)); 268 | } 269 | 270 | let mut a = -PI; 271 | let end = PI; 272 | let step = PI / 8192.0; 273 | 274 | while a <= end { 275 | test_sse2_m128_sin_angle(a); 276 | a += step; 277 | } 278 | } 279 | --------------------------------------------------------------------------------