custom glam with zerocopy impls

This commit is contained in:
Carter Anderson 2020-05-03 16:55:17 -07:00
parent dac9cac776
commit 41be55bf8d
59 changed files with 12880 additions and 7 deletions

View file

@ -41,9 +41,9 @@ bevy_window = { path = "crates/bevy_window", optional = true }
bevy_wgpu = { path = "crates/bevy_wgpu", optional = true }
bevy_winit = { path = "crates/bevy_winit", optional = true }
legion = { path = "crates/bevy_legion" }
glam = { path = "crates/bevy_glam" }
# other
log = { version = "0.4", features = ["release_max_level_info"] }
glam = "0.8.6"
[workspace]
members = [

View file

@ -8,5 +8,5 @@ edition = "2018"
bevy_app = { path = "../bevy_app" }
bevy_transform = { path = "../bevy_transform" }
legion = { path = "../bevy_legion" }
glam = "0.8.6"
glam = { path = "../bevy_glam" }
zerocopy = "0.3"

View file

@ -0,0 +1,5 @@
{
"git": {
"sha1": "9690d383cfa7d720706d66b25ec862fa6100adf5"
}
}

View file

@ -0,0 +1,8 @@
name: CI
on: [push, pull_request]
jobs:
cargo-deny:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: EmbarkStudios/cargo-deny-action@v0

6
crates/bevy_glam/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
/target
*.swp
**/*.rs.bk
Cargo.lock
Session.vim
.cargo-ok

View file

@ -0,0 +1,40 @@
language: rust
sudo: required
dist: trusty
addons:
apt:
packages:
- libssl-dev
cache: cargo
rust:
- stable
- beta
- nightly
env:
- CARGO_FEATURES="mint rand serde debug-glam-assert transform-types"
- CARGO_FEATURES="mint rand serde packed-vec3 debug-glam-assert transform-types"
- CARGO_FEATURES="mint rand serde scalar-math debug-glam-assert transform-types"
matrix:
allow_failures:
- rust: nightly
fast_finish: true
before_script: |
if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then
cargo install cargo-tarpaulin
fi
script:
- cargo clean
- cargo build --features "$CARGO_FEATURES"
- cargo test --features "$CARGO_FEATURES"
- cargo bench --features "$CARGO_FEATURES" --no-run
after_success: |
if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then
cargo tarpaulin --features "$CARGO_FEATURES" --exclude-files src/f32/transform.rs --exclude-files benches/* --exclude-files tests/support/mod.rs --ciserver travis-ci --coveralls $TRAVIS_JOB_ID
fi

View file

@ -0,0 +1,188 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog], and this project adheres to
[Semantic Versioning].
## [Unreleased]
## [0.8.7] - 2020-04-28
### Added
* Added `Quat::slerp` - note that this uses a `sin` approximation.
* Added `angle_between` method for `Vec2` and `Vec3`.
* Implemented `Debug`, `Display`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`,
`Hash`, and `AsRef` traits for `Vec2Mask`, `Vec3Mask` and `Vec4Mask`.
* Added conversion functions from `Vec2Mask`, `Vec3Mask` and `Vec4Mask` to an
array of `[u32]`.
* Added `build.rs` to simplify conditional feature compilation.
### Changed
* Removed `cfg-if` dependency.
* Increased test coverage.
## [0.8.6] - 2020-02-18
### Added
* Added the `packed-vec3` feature flag to disable using SIMD types for `Vec3`
and `Mat3` types. This avoids wasting some space due to 16 byte alignment at
the cost of some performance.
* Added `x_mut`, `y_mut`, `z_mut`, `w_mut` where appropriate to `Vec2`, `Vec3`
and `Vec4`.
* Added implementation of `core::ops::Index` and `core::ops::IndexMut` for
`Vec2`, `Vec3` and `Vec4`.
### Changed
* Merged SSE2 and scalar `Vec3` and `Vec4` implementations into single files
using the `cfg-if` crate.
## [0.8.5] - 2020-01-02
### Added
* Added projection functions `Mat4::perspective_lh`,
`Mat4::perspective_infinite_lh`, `Mat4::perspective_infinite_reverse_lh`,
`Mat4::orthgraphic_lh` and `Mat4::orthographic_rh`.
* Added `round`, `ceil` and `floor` methods to `Vec2`, `Vec3` and `Vec4`.
## [0.8.4] - 2019-12-17
### Added
* Added `Mat4::to_scale_rotation_translation` for extracting scale, rotation and
translation from a 4x4 homogeneous transformation matrix.
* Added `cargo-deny` GitHub Action.
### Changed
* Renamed `Quat::new` to `Quat::from_xyzw`.
## [0.8.3] - 2019-11-27
### Added
* Added `Mat4::orthographic_rh_gl`.
### Changed
* Renamed `Mat4::perspective_glu_rh` to `Mat4::perspective_rh_gl`.
* SSE2 optimizations for `Mat2::determinant`, `Mat2::inverse`,
`Mat2::transpose`, `Mat3::transpose`, `Quat::conjugate`, `Quat::lerp`,
`Quat::mul_vec3`, `Quat::mul_quat` and `Quat::from_rotation_ypr`.
* Disabled optimizations to `Mat4::transform_point3` and
`Mat4::transform_vector3` as they are probably incorrect and need
investigating.
* Added missing `#[repr(C)]` to `Mat2`, `Mat3` and `Mat4`.
* Benchmarks now store output of functions to better estimate the cost of a
function call.
### Removed
* Removed deprecated functions `Mat2::new`, `Mat3::new` and `Mat4::new`.
## [0.8.2] - 2019-11-06
### Changed
* `glam_assert!` is no longer enabled by default in debug builds, it can be
enabled in any configuration using the `glam-assert` feature or in debug
builds only using the `debug-glam-assert` feature.
### Removed
* `glam_assert!`'s checking `lerp` is bounded between 0.0 and 1.0 and that
matrix scales are non-zero have been removed.
## [0.8.1] - 2019-11-03
### Added
* Added `Display` trait implementations for `Mat2`, `Mat3` and `Mat4`.
### Changed
* Disabled `glam`'s SSE2 `sin_cos` implementation - it became less precise for
large angle values.
* Reduced the default epsilon used by the `is_normalized!` macro from
`std::f32::EPSILON` to `1e-6`.
## [0.8.0] - 2019-10-14
### Removed
* Removed the `approx` crate dependency. Each `glam` type has an `abs_diff_eq`
method added which is used by unit tests for approximate floating point
comparisons.
* Removed the `Angle` type. All angles are now `f32` and are expected to
be in radians.
* Removed the deprecated `Vec2b`, `Vec3b` and `Vec4b` types and the `mask`
methods on `Vec2Mask`, `Vec3Mask` and `Vec4Mask`.
### Changed
* The `rand` crate dependency has been removed from default features. This was
required for benchmarking but a simple random number generator has been added
to the benches `support` module instead.
* The `From` trait implementation converting between 1D and 2D `f32` arrays and
matrix types have been removed. It was ambiguous how array data would map to
matrix columns so these have been replaced with explicit methods
`from_cols_array` and `from_cols_array_2d`.
* Matrix `new` methods have been renamed to `from_cols` to be consistent with
the other methods that create matrices from data.
* Renamed `Mat4::perspective_glu` to `Mat4::perspective_glu_rh`.
## [0.7.2] - 2019-09-22
### Fixed
* Fixed incorrect projection matrix methods `Mat4::look_at_lh`
and `Mat4::look_at_rh`.
### Added
* Added support for building infinite projection matrices, including both
standard and reverse depth `Mat4::perspective_infinite_rh` and
`Mat4::perspective_infinite_rh`.
* Added `Vec2Mask::new`, `Vec3Mask::new` and `Vec4Mask::new` methods.
* Implemented `std::ops` `BitAnd`, `BitAndAssign`, `BitOr`, `BitOrAssign`
and `Not` traits for `Vec2Mask`, `Vec3Mask` and `Vec4Mask`.
* Added method documentation for `Vec4` and `Vec4Mask` types.
* Added missing `serde` implementations for `Mat2`, `Mat3` and `Mat4`.
* Updated `rand` and `criterion` versions.
## [0.7.1] - 2019-07-08
### Fixed
* The SSE2 implementation of `Vec4` `dot` was missing a shuffle, meaning the
`dot`, `length`, `length_squared`, `length_reciprocal` and `normalize`
methods were sometimes incorrect.
### Added
* Added the `glam_assert` macro which behaves like Rust's `debug_assert` but
can be enabled separately to `debug_assert`. This is used to perform
asserts on correctness.
* Added `is_normalized` method to `Vec2`, `Vec3` and `Vec4`.
### Changed
* Replaced usage of `std::mem::uninitialized` with `std::mem::MaybeUninit`. This
change requires stable Rust 1.36.
* Renamed `Vec2b` to `Vec2Mask`, `Vec3b` to `Vec3Mask` and `Vec4b` to
`Vec4Mask`. Old names are aliased to the new name and deprecated.
* Deprecate `VecNMask` `mask` method, use new `bitmask` method instead
* Made fallback version of `VecNMask` types the same size and alignment as the
SIMD versions.
* Added `Default` support to `VecNMask` types, will add more common traits in
the future.
* Added `#[inline]` to `mat2`, `mat3` and `mat4` functions.
## [0.7.0] - 2019-06-28
### Added
* Added `Mat2` into `[f32; 4]`, `Mat3` into `[f32; 9]` and `Mat4` into
`[f32; 16]`.
### Changed
* Removed `impl Mul<&Vec2> for Mat2` and `impl Mul<&Vec3> for Vec3` as these
don't exist for any other types.
## [0.6.1] - 2019-06-22
### Changed
* `Mat2` now uses a `Vec4` internally which gives it some performance
improvements when SSE2 is available.
## 0.6.0 - 2019-06-13
### Changed
* Switched from row vectors to column vectors
* Vectors are now on the right of multiplications with matrices and quaternions.
[Keep a Changelog]: https://keepachangelog.com/
[Semantic Versioning]: https://semver.org/spec/v2.0.0.html
[Unreleased]: https://github.com/bitshifter/glam-rs/compare/0.8.7...HEAD
[0.8.7]: https://github.com/bitshifter/glam-rs/compare/0.8.6...0.8.7
[0.8.6]: https://github.com/bitshifter/glam-rs/compare/0.8.5...0.8.6
[0.8.5]: https://github.com/bitshifter/glam-rs/compare/0.8.4...0.8.5
[0.8.4]: https://github.com/bitshifter/glam-rs/compare/0.8.3...0.8.4
[0.8.3]: https://github.com/bitshifter/glam-rs/compare/0.8.2...0.8.3
[0.8.2]: https://github.com/bitshifter/glam-rs/compare/0.8.1...0.8.2
[0.8.1]: https://github.com/bitshifter/glam-rs/compare/0.8.0...0.8.1
[0.8.0]: https://github.com/bitshifter/glam-rs/compare/0.7.2...0.8.0
[0.7.2]: https://github.com/bitshifter/glam-rs/compare/0.7.1...0.7.2
[0.7.1]: https://github.com/bitshifter/glam-rs/compare/0.7.0...0.7.1
[0.7.0]: https://github.com/bitshifter/glam-rs/compare/0.6.1...0.7.0
[0.6.1]: https://github.com/bitshifter/glam-rs/compare/0.6.0...0.6.1

View file

@ -0,0 +1,50 @@
Thanks for contributing to `glam`! These guidelines will try make the process
painless and efficient.
The short guide to contributing is [open a github issue]. Pull requests are
welcome for bug fixes, documentation improvements and optimizations. For
anything else it would be best to discuss it first.
# Questions
If you have a question about the usage of this library please
[open a github issue]. That's the easiest way to get support right now.
# Bugs
If you find a bug please [open a github issue] or submit a pull request. A unit
test for any bug that slipped through existing coverage would also be greatly
appreciated.
# New functions and methods
If `glam` is missing functionality on existing types, [open a github issue]
describing what feature you would like added and ideally what your use case is
for it just so I have a better understanding of the feature. I'd like to keep
`glam` reasonably light functionality wise initially but commonly used
functionality that is missing is very welcome. If you do submit a pull request
please ensure any new functionality also has a test.
# New types
If you would like to add a new type to `glam` please [open a github issue]. Only
`f32` has been supported for now but long term I'd like to support people with
other needs. I am interested in what types you feel you need and what you would
use them for. Examples might be `i32` or `f64` vectors or generic types. I don't
want to rush to support new types until the API has stabilised a bit more but it
would be good to collect information on functionality people would like to see
in `glam`.
# Optimizations
If you feel some functionality could be optimized please [open a github issue]
or submit a pull request. Any optimization pull request should include a
benchmark if there isn't one already so I can confirm the performance
improvement.
# Documentation
If you feel any documentation could be added or improved please
[open a github issue] or submit a pull request.
[open a github issue]: https://github.com/bitshifter/glam-rs/issues

View file

@ -0,0 +1,83 @@
[package]
name = "glam"
version = "0.8.7" # remember to update html_root_url
edition = "2018"
authors = ["Cameron Hart <cameron.hart@gmail.com>"]
description = "A simple and fast 3D math library for games and graphics"
repository = "https://github.com/bitshifter/glam-rs"
readme = "README.md"
license = "MIT OR Apache-2.0"
keywords = ["gamedev", "math", "matrix", "vector", "quaternion"]
categories = ["game-engines"]
build = "build.rs"
[badges]
travis-ci = { repository = "bitshifter/glam-rs" }
coveralls = { repository = "bitshifter/glam-rs" }
maintenance = { status = "experimental" }
[features]
default = ["std"]
# enable support for the standard library
std = []
# enable additional glam checks if debug assertions are enabled
debug-glam-assert = []
# always enable additional glam checks
glam-assert = []
# opt out of Vec3 and Mat3 using SIMD (and thus being 16 byte aligned)
packed-vec3 = []
# this is primarily for testing the fallback implementation
scalar-math = []
# trying out transform types instead of matrices but they are generally slower
# at everything except for inverse. They are 16 bytes smaller than a Mat4.
transform-types = []
[dependencies]
mint = { version = "0.5", optional = true, default-features = false }
rand = { version = "0.7", optional = true, default-features = false }
serde = { version = "1.0", optional = true, features = ["derive"] }
zerocopy = { version = "0.3.0" }
[dev-dependencies]
criterion = "0.3"
# rand_xoshiro is required for tests if rand is enabled
rand_xoshiro = "0.4"
serde_json = "1.0"
[[bench]]
name = "mat2"
harness = false
[[bench]]
name = "mat3"
harness = false
[[bench]]
name = "mat4"
harness = false
[[bench]]
name = "quat"
harness = false
[[bench]]
name = "transform"
harness = false
required-features = ["transform-types"]
[[bench]]
name = "vec2"
harness = false
[[bench]]
name = "vec3"
harness = false
[[bench]]
name = "vec4"
harness = false

View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020 Cameron Hart
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

163
crates/bevy_glam/README.md Normal file
View file

@ -0,0 +1,163 @@
# glam
[![Build Status]][travis-ci] [![Coverage Status]][coveralls.io]
[![Latest Version]][crates.io] [![docs]][docs.rs]
A simple and fast 3D math library for games and graphics.
## Development status
`glam` is in alpha stage. Minimal base functionality has been implemented
and the look and feel of the API has solidified.
## Features
* Only single precision floating point (`f32`) arithmetic is supported
* vectors: `Vec2`, `Vec3`, `Vec4`
* square matrices: `Mat2`, `Mat3`, `Mat4`
* a quaternion type: `Quat`
### SIMD
The `Vec3`, `Vec4` and `Quat` types use SSE2 on x86/x86_64 architectures.
`Mat2`, `Mat3` and `Mat4` also use SSE2 for some functionality. Not everything
has a SIMD implementation yet.
Note that this does result in some wasted space in the case of `Vec3` and `Mat3`
as the SIMD vector type is 16 bytes large and 16 byte aligned.
It is possible to opt out of using SIMD types for Vec3 and Mat3 storage with the
`packed-vec3` feature.
`glam` outperforms similar Rust libraries such as [`cgmath`][cgmath],
[`nalgebra-glm`][nalgebra-glm] and others for common operations as tested by the
[`mathbench`][mathbench] project.
If you are more concerned with size than speed you can build glam with the
feature `scalar-math` enabled to disable SIMD usage.
Due to the use of SIMD, vector elements may only be get and set via accessor
methods, e.g. `Vec3::x()` and `Vec3::x_mut()` or `Vec3::set_x()`. If getting or
setting more than one element it is more efficient to convert from tuples or
arrays:
```
let (x, y, z) = v.into();
let [x, y, z]: [f32; 3] = v.into();
```
### Optional features
* `mint` - for interoperating with other 3D math libraries
* `rand` - implementations of `Distribution` trait for all `glam` types. This
is primarily used for unit testing
* `serde` - implementations of `Serialize` and `Deserialize` for all `glam`
types. Note that serialization should work between builds of `glam` with and
without SIMD enabled
### Feature gates
* `packed-vec3` - disable using SIMD types for `Vec3` and `Mat3` storage. This
avoids wasting space due to 16 byte alignment at the cost of some performance.
* `scalar-math` - compiles with SIMD support disabled
* `glam-assert` - adds assertions which check the validity of parameters passed to
`glam` to help catch runtime errors
## Conventions
### Column vectors
`glam` interprets vectors as column matrices (also known as "column vectors")
meaning when transforming a vector with a matrix the matrix goes on the left,
e.g. `v' = Mv`. DirectX uses row vectors, OpenGL uses column vectors. There
are pros and cons to both.
### Column-major order
Matrices are stored in column major format. Each column vector is stored in
contiguous memory.
### Co-ordinate system
`glam` is co-ordinate system agnostic and intends to support both right handed
and left handed conventions.
Rotations follow the left-hand rule.
## Design Philosophy
The design of this library is guided by a desire for simplicity and good
performance.
* No traits or generics for simplicity of implementation and usage
* Only single precision floating point (`f32`) arithmetic is supported
* All dependencies are optional (e.g. `mint`, `rand` and `serde`)
* Follows the [Rust API Guidelines] where possible
* Aiming for 100% test [coverage][coveralls.io]
* Common functionality is benchmarked using [Criterion.rs]
## Future work
* Experiment with a using a 4x3 matrix as a 3D transform type that can be more
efficient than `Mat4` for certain operations like inverse and multiplies
* `no-std` support
* `wasm` support
## Inspirations
There were many inspirations for the interface and internals of glam from the
Rust and C++ worlds. In particular:
* [How to write a maths library in 2016] inspired the initial `Vec3`
implementation
* [Realtime Math] - header only C++11 with SSE and NEON SIMD intrinsic support
* [DirectXMath] - header only SIMD C++ linear algebra library for use in games
and graphics apps
* `glam` is a play on the name of the popular C++ library `glm`
## License
Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE)
or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT)
or http://opensource.org/licenses/MIT)
at your option.
## Contribution
Contributions in any form (issues, pull requests, etc.) to this project must
adhere to Rust's [Code of Conduct].
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
Thank you to all of the `glam` [contributors]!
## Support
If you are interested in contributing or have a request or suggestion
[create an issue] on github.
[Build Status]: https://travis-ci.org/bitshifter/glam-rs.svg?branch=master
[travis-ci]: https://travis-ci.org/bitshifter/glam-rs
[Coverage Status]: https://coveralls.io/repos/github/bitshifter/glam-rs/badge.svg?branch=master
[coveralls.io]: https://coveralls.io/github/bitshifter/glam-rs?branch=master
[Code of Conduct]: https://www.rust-lang.org/en-US/conduct.html
[Latest Version]: https://img.shields.io/crates/v/glam.svg
[crates.io]: https://crates.io/crates/glam/
[docs]: https://docs.rs/glam/badge.svg
[docs.rs]: https://docs.rs/glam/
[Rust API Guidelines]: https://rust-lang-nursery.github.io/api-guidelines/
[Criterion.rs]: https://bheisler.github.io/criterion.rs/book/index.html
[cgmath]: https://github.com/rustgd/cgmath
[nalgebra-glm]: https://github.com/rustsim/nalgebra
[mathbench]: https://github.com/bitshifter/mathbench-rs
[create an issue]: https://github.com/bitshifter/glam-rs/issues
[contributors]: https://github.com/bitshifter/glam-rs/graphs/contributors
[How to write a maths library in 2016]: http://www.codersnotes.com/notes/maths-lib-2016/
[Realtime Math]: https://github.com/nfrechette/rtm
[DirectXMath]: https://docs.microsoft.com/en-us/windows/desktop/dxmath/directxmath-portal

View file

@ -0,0 +1,34 @@
#[path = "support/macros.rs"]
#[macro_use]
mod macros;
mod support;
use criterion::{criterion_group, criterion_main, Criterion};
use std::ops::Mul;
use support::*;
bench_unop!(
mat2_transpose,
"mat2 transpose",
op => transpose,
from => random_mat2
);
bench_unop!(
mat2_determinant,
"mat2 determinant",
op => determinant,
from => random_mat2
);
bench_unop!(mat2_inverse, "mat2 inverse", op => inverse, from => random_mat2);
bench_binop!(mat2_mul_mat2, "mat2 * mat2", op => mul, from => random_mat2);
criterion_group!(
benches,
mat2_transpose,
mat2_determinant,
mat2_inverse,
// mat2_mul_op_mat2,
mat2_mul_mat2,
);
criterion_main!(benches);

View file

@ -0,0 +1,36 @@
#[path = "support/macros.rs"]
#[macro_use]
mod macros;
mod support;
use criterion::{criterion_group, criterion_main, Criterion};
use glam::Mat3;
use std::ops::Mul;
use support::*;
bench_unop!(
mat3_transpose,
"mat3 transpose",
op => transpose,
from => random_mat3
);
bench_unop!(
mat3_determinant,
"mat3 determinant",
op => determinant,
from => random_mat3
);
bench_unop!(mat3_inverse, "mat3 inverse", op => inverse, from => random_mat3);
bench_binop!(mat3_mul_mat3, "mat3 * mat3", op => mul, from => random_mat3);
bench_from_ypr!(mat3_from_ypr, "mat3 from ypr", ty => Mat3);
criterion_group!(
benches,
mat3_transpose,
mat3_determinant,
mat3_inverse,
mat3_mul_mat3,
mat3_from_ypr,
);
criterion_main!(benches);

View file

@ -0,0 +1,36 @@
#[path = "support/macros.rs"]
#[macro_use]
mod macros;
mod support;
use criterion::{criterion_group, criterion_main, Criterion};
use glam::Mat4;
use std::ops::Mul;
use support::*;
bench_unop!(
mat4_transpose,
"mat4 transpose",
op => transpose,
from => random_srt_mat4
);
bench_unop!(
mat4_determinant,
"mat4 determinant",
op => determinant,
from => random_srt_mat4
);
bench_unop!(mat4_inverse, "mat4 inverse", op => inverse, from => random_srt_mat4);
bench_binop!(mat4_mul_mat4, "mat4 * mat4", op => mul, from => random_srt_mat4);
bench_from_ypr!(mat4_from_ypr, "mat4 from ypr", ty => Mat4);
criterion_group!(
benches,
mat4_transpose,
mat4_determinant,
mat4_inverse,
mat4_mul_mat4,
mat4_from_ypr,
);
criterion_main!(benches);

View file

@ -0,0 +1,62 @@
#[path = "support/macros.rs"]
#[macro_use]
mod macros;
mod support;
use criterion::{criterion_group, criterion_main, Criterion};
use glam::Quat;
use std::ops::Mul;
use support::{random_f32, random_quat, random_radians};
bench_unop!(
quat_conjugate,
"quat conjugate",
op => conjugate,
from => random_quat
);
bench_binop!(
quat_mul_quat,
"quat * quat",
op => mul,
from => random_quat
);
bench_binop!(
quat_dot,
"quat dot",
op => dot,
from => random_quat
);
bench_trinop!(
quat_lerp,
"quat lerp",
op => lerp,
from1 => random_quat,
from2 => random_quat,
from3 => random_f32
);
bench_trinop!(
quat_slerp,
"quat slerp",
op => slerp,
from1 => random_quat,
from2 => random_quat,
from3 => random_f32
);
bench_from_ypr!(quat_from_ypr, "quat from ypr", ty => Quat);
criterion_group!(
benches,
quat_conjugate,
quat_dot,
quat_lerp,
quat_slerp,
quat_mul_quat,
quat_from_ypr
);
criterion_main!(benches);

View file

@ -0,0 +1,180 @@
#[macro_export]
macro_rules! bench_func {
($name: ident, $desc: expr, op => $func: ident, from => $from: expr) => {
pub(crate) fn $name(c: &mut Criterion) {
const SIZE: usize = 1 << 13;
let mut rng = support::PCG32::default();
let inputs =
criterion::black_box((0..SIZE).map(|_| $from(&mut rng)).collect::<Vec<_>>());
// pre-fill output vector with some random value
let mut outputs = vec![$func($from(&mut rng)); SIZE];
let mut i = 0;
c.bench_function($desc, |b| {
b.iter(|| {
i = (i + 1) & (SIZE - 1);
unsafe {
*outputs.get_unchecked_mut(i) = $func(*inputs.get_unchecked(i));
}
})
});
criterion::black_box(outputs);
}
};
}
#[macro_export]
macro_rules! bench_unop {
($name: ident, $desc: expr, op => $unop: ident, from => $from: expr) => {
pub(crate) fn $name(c: &mut Criterion) {
const SIZE: usize = 1 << 13;
let mut rng = support::PCG32::default();
let inputs =
criterion::black_box((0..SIZE).map(|_| $from(&mut rng)).collect::<Vec<_>>());
// pre-fill output vector with some random value
let mut outputs = vec![$from(&mut rng).$unop(); SIZE];
let mut i = 0;
c.bench_function($desc, |b| {
b.iter(|| {
i = (i + 1) & (SIZE - 1);
unsafe {
*outputs.get_unchecked_mut(i) = inputs.get_unchecked(i).$unop();
}
})
});
criterion::black_box(outputs);
}
};
}
#[macro_export]
macro_rules! bench_binop {
($name: ident, $desc: expr, op => $binop: ident, from1 => $from1:expr, from2 => $from2:expr) => {
pub(crate) fn $name(c: &mut Criterion) {
const SIZE: usize = 1 << 13;
let mut rng = support::PCG32::default();
let inputs1 =
criterion::black_box((0..SIZE).map(|_| $from1(&mut rng)).collect::<Vec<_>>());
let inputs2 =
criterion::black_box((0..SIZE).map(|_| $from2(&mut rng)).collect::<Vec<_>>());
// pre-fill output vector with some random value
let mut outputs = vec![$from1(&mut rng).$binop($from2(&mut rng)); SIZE];
let mut i = 0;
c.bench_function($desc, |b| {
b.iter(|| {
i = (i + 1) & (SIZE - 1);
unsafe {
*outputs.get_unchecked_mut(i) = inputs1.get_unchecked(i).$binop(*inputs2.get_unchecked(i));
}
})
});
criterion::black_box(outputs);
}
};
($name: ident, $desc: expr, op => $binop: ident, from => $from: expr) => {
bench_binop!($name, $desc, op => $binop, from1 => $from, from2 => $from);
};
}
#[macro_export]
macro_rules! bench_trinop {
($name: ident, $desc: expr, op => $trinop: ident, from1 => $from1:expr, from2 => $from2:expr, from3 => $from3:expr) => {
pub(crate) fn $name(c: &mut Criterion) {
const SIZE: usize = 1 << 13;
let mut rng = support::PCG32::default();
let inputs1 =
criterion::black_box((0..SIZE).map(|_| $from1(&mut rng)).collect::<Vec<_>>());
let inputs2 =
criterion::black_box((0..SIZE).map(|_| $from2(&mut rng)).collect::<Vec<_>>());
let inputs3 =
criterion::black_box((0..SIZE).map(|_| $from3(&mut rng)).collect::<Vec<_>>());
// pre-fill output vector with some random value
let mut outputs =
vec![$from1(&mut rng).$trinop($from2(&mut rng), $from3(&mut rng)); SIZE];
let mut i = 0;
c.bench_function($desc, |b| {
b.iter(|| {
i = (i + 1) & (SIZE - 1);
unsafe {
*outputs.get_unchecked_mut(i) = inputs1
.get_unchecked(i)
.$trinop(*inputs2.get_unchecked(i), *inputs3.get_unchecked(i));
}
})
});
criterion::black_box(outputs);
}
};
}
#[macro_export]
macro_rules! bench_from_ypr {
($name: ident, $desc: expr, ty => $ty:ty) => {
pub(crate) fn $name(c: &mut Criterion) {
const SIZE: usize = 1 << 13;
let mut rng = support::PCG32::default();
let inputs = criterion::black_box(
(0..SIZE)
.map(|_| {
(
random_radians(&mut rng),
random_radians(&mut rng),
random_radians(&mut rng),
)
})
.collect::<Vec<_>>(),
);
let mut outputs = vec![<$ty>::default(); SIZE];
let mut i = 0;
c.bench_function($desc, |b| {
b.iter(|| {
i = (i + 1) & (SIZE - 1);
unsafe {
let data = inputs.get_unchecked(i);
*outputs.get_unchecked_mut(i) =
<$ty>::from_rotation_ypr(data.0, data.1, data.2)
}
})
});
}
};
}
#[macro_export]
macro_rules! euler {
($name: ident, $desc: expr, ty => $t: ty, storage => $storage: ty, zero => $zero: expr, rand => $rand: ident) => {
pub(crate) fn $name(c: &mut Criterion) {
const UPDATE_RATE: f32 = 1.0 / 60.0;
const NUM_OBJECTS: usize = 10000;
struct TestData {
acc: Vec<$storage>,
vel: Vec<$storage>,
pos: Vec<$storage>,
}
let mut rng = support::PCG32::default();
let mut data = TestData {
acc: vec![$rand(&mut rng); NUM_OBJECTS],
vel: vec![$zero; NUM_OBJECTS],
pos: vec![$zero; NUM_OBJECTS],
};
let dt = <$t>::splat(UPDATE_RATE);
c.bench_function($desc, |b| {
b.iter(|| {
for ((position, acceleration), velocity) in
data.pos.iter_mut().zip(&data.acc).zip(&mut data.vel)
{
let local_acc: $t = (*acceleration).into();
let mut local_pos: $t = (*position).into();
let mut local_vel: $t = (*velocity).into();
local_vel += local_acc * dt;
local_pos += local_vel * dt;
*velocity = local_vel.into();
*position = local_pos.into();
}
})
});
}
};
}

View file

@ -0,0 +1,96 @@
#![allow(dead_code)]
use core::f32;
use glam::f32::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
pub struct PCG32 {
state: u64,
inc: u64,
}
impl PCG32 {
pub fn seed(initstate: u64, initseq: u64) -> Self {
let mut rng = PCG32 {
state: 0,
inc: (initseq << 1) | 1,
};
rng.next_u32();
rng.state = rng.state.wrapping_add(initstate);
rng.next_u32();
rng
}
pub fn default() -> Self {
PCG32::seed(0x853c49e6748fea9b, 0xda3e39cb94b95bdb)
}
pub fn next_u32(&mut self) -> u32 {
let oldstate = self.state;
self.state = oldstate
.wrapping_mul(6364136223846793005)
.wrapping_add(self.inc | 1);
let xorshifted = ((oldstate >> 18) ^ oldstate) >> 27;
let rot = oldstate >> 59;
((xorshifted >> rot) | (xorshifted << (rot.wrapping_neg() & 31))) as u32
}
pub fn next_f32(&mut self) -> f32 {
(self.next_u32() & 0xffffff) as f32 / 16777216.0
}
}
pub fn random_vec2(rng: &mut PCG32) -> Vec2 {
Vec2::new(rng.next_f32(), rng.next_f32())
}
pub fn random_vec3(rng: &mut PCG32) -> Vec3 {
Vec3::new(rng.next_f32(), rng.next_f32(), rng.next_f32())
}
pub fn random_vec4(rng: &mut PCG32) -> Vec4 {
Vec4::new(
rng.next_f32(),
rng.next_f32(),
rng.next_f32(),
rng.next_f32(),
)
}
pub fn random_nonzero_vec3(rng: &mut PCG32) -> Vec3 {
loop {
let v = random_vec3(rng);
if v.length_squared() > 0.01 {
return v;
}
}
}
pub fn random_f32(rng: &mut PCG32) -> f32 {
rng.next_f32()
}
pub fn random_radians(rng: &mut PCG32) -> f32 {
-f32::consts::PI + rng.next_f32() * 2.0 * f32::consts::PI
}
pub fn random_quat(rng: &mut PCG32) -> Quat {
let yaw = random_radians(rng);
let pitch = random_radians(rng);
let roll = random_radians(rng);
Quat::from_rotation_ypr(yaw, pitch, roll)
}
pub fn random_mat2(rng: &mut PCG32) -> Mat2 {
Mat2::from_cols(random_vec2(rng), random_vec2(rng))
}
pub fn random_mat3(rng: &mut PCG32) -> Mat3 {
Mat3::from_cols(random_vec3(rng), random_vec3(rng), random_vec3(rng))
}
pub fn random_srt_mat4(rng: &mut PCG32) -> Mat4 {
Mat4::from_scale_rotation_translation(
random_nonzero_vec3(rng),
random_quat(rng),
random_vec3(rng),
)
}

View file

@ -0,0 +1,85 @@
#[path = "support/macros.rs"]
#[macro_use]
mod macros;
mod support;
use criterion::{criterion_group, criterion_main, Criterion};
use glam::f32::{TransformRT, TransformSRT};
use std::ops::Mul;
use support::*;
fn random_transform_srt(rng: &mut PCG32) -> TransformSRT {
TransformSRT::from_scale_rotation_translation(
random_nonzero_vec3(rng),
random_quat(rng),
random_vec3(rng),
)
}
fn random_transform_rt(rng: &mut PCG32) -> TransformRT {
TransformRT::from_rotation_translation(random_quat(rng), random_vec3(rng))
}
bench_unop!(
transform_srt_inverse,
"transform_srt inverse",
op => inverse,
from => random_transform_srt
);
bench_binop!(
vec3_mul_transform_srt,
"transform_srt * vec3",
op => mul,
from1 => random_transform_srt,
from2 => random_vec3
);
bench_binop!(
vec3_mul_transform_rt,
"transform_rt * vec3",
op => mul,
from1 => random_transform_rt,
from2 => random_vec3
);
// bench_unop!(
// transform_srt_inverse_ptv_scale,
// "transform_srt inverse (+ve scale)",
// op => inverse,
// ty => TransformSRT,
// from => TransformRT
// );
// bench_unop!(
// transform_rt_inverse,
// "transform_rt inverse",
// op => inverse,
// ty => TransformRT
// );
bench_binop!(
transform_srt_mul_srt,
"transform_srt * transform_srt",
op => mul,
from => random_transform_srt
);
bench_binop!(
transform_rt_mul_rt,
"transform_rt * transform_rt",
op => mul,
from => random_transform_rt
);
criterion_group!(
benches,
// transform_rt_inverse,
transform_srt_inverse,
// transform_srt_inverse_ptv_scale,
transform_rt_mul_rt,
transform_srt_mul_srt,
vec3_mul_transform_rt,
vec3_mul_transform_srt,
);
criterion_main!(benches);

View file

@ -0,0 +1,31 @@
#[path = "support/macros.rs"]
#[macro_use]
mod macros;
mod support;
use criterion::{criterion_group, criterion_main, Criterion};
use glam::f32::Vec2;
use std::ops::Mul;
use support::{random_mat2, random_vec2};
euler!(vec2_euler, "vec2 euler", ty => Vec2, storage => Vec2, zero => Vec2::zero(), rand => random_vec2);
bench_binop!(
mat2_mul_vec2,
"mat2 * vec2",
op => mul,
from1 => random_mat2,
from2 => random_vec2
);
bench_binop!(
vec2_angle_between,
"vec2 angle_between",
op => angle_between,
from1 => random_vec2,
from2 => random_vec2
);
criterion_group!(benches, vec2_euler, mat2_mul_vec2, vec2_angle_between);
criterion_main!(benches);

View file

@ -0,0 +1,98 @@
#[path = "support/macros.rs"]
#[macro_use]
mod macros;
mod support;
use criterion::{criterion_group, criterion_main, Criterion};
use glam::f32::Vec3;
use std::ops::Mul;
use support::{random_mat3, random_quat, random_vec3};
bench_binop!(
quat_mul_vec3,
"quat * vec3",
op => mul,
from1 => random_quat,
from2 => random_vec3
);
bench_binop!(
mat3_mul_vec3,
"mat3 * vec3",
op => mul,
from1 => random_mat3,
from2 => random_vec3
);
#[inline]
fn vec3_to_rgb_op(v: Vec3) -> u32 {
let (red, green, blue) = (v.min(Vec3::one()).max(Vec3::zero()) * 255.0).into();
((red as u32) << 16 | (green as u32) << 8 | (blue as u32)).into()
}
#[inline]
fn vec3_accessors(v: Vec3) -> [f32; 3] {
[v.x(), v.y(), v.z()]
}
#[inline]
fn vec3_into_array(v: Vec3) -> [f32; 3] {
v.into()
}
#[inline]
fn vec3_into_tuple(v: Vec3) -> (f32, f32, f32) {
v.into()
}
bench_func!(
vec3_to_rgb,
"vec3 to rgb",
op => vec3_to_rgb_op,
from => random_vec3
);
bench_func!(
vec3_to_array_accessors,
"vec3 into array slow",
op => vec3_accessors,
from => random_vec3
);
bench_func!(
vec3_to_array_into,
"vec3 into array fast",
op => vec3_into_array,
from => random_vec3
);
bench_func!(
vec3_to_tuple_into,
"vec3 into tuple fast",
op => vec3_into_tuple,
from => random_vec3
);
euler!(vec3_euler, "vec3 euler", ty => Vec3, storage => Vec3, zero => Vec3::zero(), rand => random_vec3);
bench_binop!(
vec3_angle_between,
"vec3 angle_between",
op => angle_between,
from1 => random_vec3,
from2 => random_vec3
);
criterion_group!(
benches,
quat_mul_vec3,
mat3_mul_vec3,
vec3_angle_between,
vec3_euler,
vec3_to_rgb,
vec3_to_array_accessors,
vec3_to_array_into,
vec3_to_tuple_into,
);
criterion_main!(benches);

View file

@ -0,0 +1,20 @@
#[path = "support/macros.rs"]
#[macro_use]
mod macros;
mod support;
use criterion::{criterion_group, criterion_main, Criterion};
use std::ops::Mul;
use support::{random_srt_mat4, random_vec4};
bench_binop!(
vec4_mul_mat4,
"vec4 * mat4",
op => mul,
from1 => random_srt_mat4,
from2 => random_vec4
);
criterion_group!(benches, vec4_mul_mat4,);
criterion_main!(benches);

29
crates/bevy_glam/build.rs Normal file
View file

@ -0,0 +1,29 @@
use std::env;
fn main() {
let force_scalar_math = env::var("CARGO_FEATURE_SCALAR_MATH").is_ok();
let force_packed_vec3 = env::var("CARGO_FEATURE_PACKED_VEC3").is_ok();
let target_feature_sse2 = env::var("CARGO_CFG_TARGET_FEATURE")
.map_or(false, |cfg| cfg.split(',').find(|&f| f == "sse2").is_some());
if target_feature_sse2 && !force_scalar_math && !force_packed_vec3 {
println!("cargo:rustc-cfg=vec3sse2");
} else {
if !force_scalar_math && !force_packed_vec3 {
// simd not available but not explicitly disabled so maintain 16 byte alignment
println!("cargo:rustc-cfg=vec3f32_align16");
}
println!("cargo:rustc-cfg=vec3f32");
}
if target_feature_sse2 && !force_scalar_math {
println!("cargo:rustc-cfg=vec4sse2");
} else {
if !force_scalar_math {
// simd not available but not explicitly disabled so maintain 16 byte alignment
println!("cargo:rustc-cfg=vec4f32_align16");
}
println!("cargo:rustc-cfg=vec4f32");
}
}

View file

@ -0,0 +1,18 @@
[bans]
multiple-versions = "deny"
deny = []
skip-tree = [
# ignore criterion dev-dependency that often have duplicate dependencies internally
{ name = "criterion" },
]
[licenses]
unlicensed = "deny"
allow = [
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"MIT",
"MPL-2.0",
]

View file

@ -0,0 +1,550 @@
#[inline]
pub(crate) fn scalar_sin_cos(x: f32) -> (f32, f32) {
// // expect sse2 to be available on all x86 builds
// #[cfg(target_feature = "sse2")]
// unsafe {
// let (sinx, cosx) = sin_cos_sse2(_mm_set1_ps(x));
// (_mm_cvtss_f32(sinx), _mm_cvtss_f32(cosx))
// }
// #[cfg(not(target_feature = "sse2"))]
x.sin_cos()
}
#[inline]
pub fn scalar_acos(value: f32) -> f32 {
// from DirectXMath XMScalarAcos
// Clamp input to [-1,1].
let nonnegative = value >= 0.0;
let x = value.abs();
let mut omx = 1.0 - x;
if omx < 0.0 {
omx = 0.0;
}
let root = omx.sqrt();
// 7-degree minimax approximation
#[allow(clippy::approx_constant)]
let mut result =
((((((-0.001_262_491_1 * x + 0.006_670_09) * x - 0.017_088_126) * x + 0.030_891_88) * x
- 0.050_174_303)
* x
+ 0.088_978_99)
* x
- 0.214_598_8)
* x
+ 1.570_796_3;
result *= root;
// acos(x) = pi - acos(-x) when x < 0
if nonnegative {
result
} else {
core::f32::consts::PI - result
}
}
#[cfg(vec4sse2)]
pub(crate) mod sse2 {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use crate::f32::x86_utils::UnionCast;
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
macro_rules! _ps_const_ty {
($name:ident, $field:ident, $x:expr) => {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
const $name: UnionCast = UnionCast {
$field: [$x, $x, $x, $x],
};
};
($name:ident, $field:ident, $x:expr, $y:expr, $z:expr, $w:expr) => {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
const $name: UnionCast = UnionCast {
$field: [$x, $y, $z, $w],
};
};
}
_ps_const_ty!(PS_INV_SIGN_MASK, u32x4, !0x8000_0000);
_ps_const_ty!(PS_SIGN_MASK, u32x4, 0x8000_0000);
_ps_const_ty!(PS_NO_FRACTION, f32x4, 8388608.0);
// _ps_const_ty!(PS_1_0, f32x4, 1.0);
// _ps_const_ty!(PS_0_5, f32x4, 0.5);
// _ps_const_ty!(PI32_1, i32x4, 1);
// _ps_const_ty!(PI32_INV_1, i32x4, !1);
// _ps_const_ty!(PI32_2, i32x4, 2);
// _ps_const_ty!(PI32_4, i32x4, 4);
// _ps_const_ty!(PS_MINUS_CEPHES_DP1, f32x4, -0.785_156_25);
// _ps_const_ty!(PS_MINUS_CEPHES_DP2, f32x4, -2.418_756_5e-4);
// _ps_const_ty!(PS_MINUS_CEPHES_DP3, f32x4, -3.774_895e-8);
// _ps_const_ty!(PS_SINCOF_P0, f32x4, -1.951_529_6e-4);
// _ps_const_ty!(PS_SINCOF_P1, f32x4, 8.332_161e-3);
// _ps_const_ty!(PS_SINCOF_P2, f32x4, -1.666_665_5e-1);
// _ps_const_ty!(PS_COSCOF_P0, f32x4, 2.443_315_7e-5);
// _ps_const_ty!(PS_COSCOF_P1, f32x4, -1.388_731_6E-3);
// _ps_const_ty!(PS_COSCOF_P2, f32x4, 4.166_664_6e-2);
// _ps_const_ty!(PS_CEPHES_FOPI, f32x4, 1.273_239_5); // 4 / M_PI
_ps_const_ty!(PS_NEGATIVE_ZERO, u32x4, 0x80000000);
_ps_const_ty!(PS_PI, f32x4, core::f32::consts::PI);
_ps_const_ty!(PS_HALF_PI, f32x4, core::f32::consts::FRAC_PI_2);
_ps_const_ty!(
PS_SIN_COEFFICIENTS0,
f32x4,
-0.16666667,
0.0083333310,
-0.00019840874,
2.7525562e-06
);
_ps_const_ty!(
PS_SIN_COEFFICIENTS1,
f32x4,
-2.3889859e-08,
-0.16665852, /*Est1*/
0.0083139502, /*Est2*/
-0.00018524670 /*Est3*/
);
_ps_const_ty!(PS_ONE, f32x4, 1.0);
_ps_const_ty!(PS_TWO_PI, f32x4, core::f32::consts::PI * 2.0);
_ps_const_ty!(PS_RECIPROCAL_TWO_PI, f32x4, 0.159154943);
#[cfg(target_feature = "fma")]
macro_rules! m128_mul_add {
($a:expr, $b:expr, $c:expr) => {
_mm_fmadd_ps($a, $b, $c)
};
}
#[cfg(not(target_feature = "fma"))]
macro_rules! m128_mul_add {
($a:expr, $b:expr, $c:expr) => {
_mm_add_ps(_mm_mul_ps($a, $b), $c)
};
}
#[cfg(target_feature = "fma")]
macro_rules! m128_neg_mul_sub {
($a:expr, $b:expr, $c:expr) => {
_mm_fnmadd_ps($a, $b, $c)
};
}
#[cfg(not(target_feature = "fma"))]
macro_rules! m128_neg_mul_sub {
($a:expr, $b:expr, $c:expr) => {
_mm_sub_ps($c, _mm_mul_ps($a, $b))
};
}
#[inline]
pub(crate) unsafe fn m128_round(v: __m128) -> __m128 {
// From DirectXMath XMVectorRound.
let sign = _mm_and_ps(v, PS_SIGN_MASK.m128);
let s_magic = _mm_or_ps(PS_NO_FRACTION.m128, sign);
let r1 = _mm_add_ps(v, s_magic);
let r1 = _mm_sub_ps(r1, s_magic);
let r2 = _mm_and_ps(v, PS_INV_SIGN_MASK.m128);
let mask = _mm_cmple_ps(r2, PS_NO_FRACTION.m128);
let r2 = _mm_andnot_ps(mask, v);
let r1 = _mm_and_ps(r1, mask);
_mm_xor_ps(r1, r2)
}
#[inline]
pub(crate) unsafe fn m128_floor(v: __m128) -> __m128 {
// From DirectXMath XMVectorFloor
// To handle NAN, INF and numbers greater than 8388608, use masking
let test = _mm_and_si128(_mm_castps_si128(v), PS_INV_SIGN_MASK.m128i);
let test = _mm_cmplt_epi32(test, PS_NO_FRACTION.m128i);
// Truncate
let vint = _mm_cvttps_epi32(v);
let result = _mm_cvtepi32_ps(vint);
let larger = _mm_cmpgt_ps(result, v);
// 0 -> 0, 0xffffffff -> -1.0f
let larger = _mm_cvtepi32_ps(_mm_castps_si128(larger));
let result = _mm_add_ps(result, larger);
// All numbers less than 8388608 will use the round to int
let result = _mm_and_ps(result, _mm_castsi128_ps(test));
// All others, use the ORIGINAL value
let test = _mm_andnot_si128(test, _mm_castps_si128(v));
_mm_or_ps(result, _mm_castsi128_ps(test))
}
#[inline]
pub(crate) unsafe fn m128_ceil(v: __m128) -> __m128 {
// From DirectXMath XMVectorCeil
// To handle NAN, INF and numbers greater than 8388608, use masking
let test = _mm_and_si128(_mm_castps_si128(v), PS_INV_SIGN_MASK.m128i);
let test = _mm_cmplt_epi32(test, PS_NO_FRACTION.m128i);
// Truncate
let vint = _mm_cvttps_epi32(v);
let result = _mm_cvtepi32_ps(vint);
let smaller = _mm_cmplt_ps(result, v);
// 0 -> 0, 0xffffffff -> -1.0f
let smaller = _mm_cvtepi32_ps(_mm_castps_si128(smaller));
let result = _mm_sub_ps(result, smaller);
// All numbers less than 8388608 will use the round to int
let result = _mm_and_ps(result, _mm_castsi128_ps(test));
// All others, use the ORIGINAL value
let test = _mm_andnot_si128(test, _mm_castps_si128(v));
_mm_or_ps(result, _mm_castsi128_ps(test))
}
/// Returns a vector whose components are the corresponding components of Angles modulo 2PI.
#[inline]
pub(crate) unsafe fn m128_mod_angles(angles: __m128) -> __m128 {
// From DirectXMath: XMVectorModAngles
let v = _mm_mul_ps(angles, PS_RECIPROCAL_TWO_PI.m128);
let v = m128_round(v);
m128_neg_mul_sub!(PS_TWO_PI.m128, v, angles)
}
/// Computes the sine of the angle in each lane of `v`. Values outside
/// the bounds of PI may produce an increasing error as the input angle
/// drifts from `[-PI, PI]`.
#[inline]
pub(crate) unsafe fn m128_sin(v: __m128) -> __m128 {
// From DirectXMath: XMVectorSin
//
// 11-degree minimax approximation
//
// Force the value within the bounds of pi
let mut x = m128_mod_angles(v);
// Map in [-pi/2,pi/2] with sin(y) = sin(x).
let sign = _mm_and_ps(x, PS_NEGATIVE_ZERO.m128);
// pi when x >= 0, -pi when x < 0
let c = _mm_or_ps(PS_PI.m128, sign);
// |x|
let absx = _mm_andnot_ps(sign, x);
let rflx = _mm_sub_ps(c, x);
let comp = _mm_cmple_ps(absx, PS_HALF_PI.m128);
let select0 = _mm_and_ps(comp, x);
let select1 = _mm_andnot_ps(comp, rflx);
x = _mm_or_ps(select0, select1);
let x2 = _mm_mul_ps(x, x);
// Compute polynomial approximation
const SC1: __m128 = unsafe { PS_SIN_COEFFICIENTS1.m128 };
let v_constants_b = _mm_shuffle_ps(SC1, SC1, 0b00_00_00_00);
const SC0: __m128 = unsafe { PS_SIN_COEFFICIENTS0.m128 };
let mut v_constants = _mm_shuffle_ps(SC0, SC0, 0b11_11_11_11);
let mut result = m128_mul_add!(v_constants_b, x2, v_constants);
v_constants = _mm_shuffle_ps(SC0, SC0, 0b10_10_10_10);
result = m128_mul_add!(result, x2, v_constants);
v_constants = _mm_shuffle_ps(SC0, SC0, 0b01_01_01_01);
result = m128_mul_add!(result, x2, v_constants);
v_constants = _mm_shuffle_ps(SC0, SC0, 0b00_00_00_00);
result = m128_mul_add!(result, x2, v_constants);
result = m128_mul_add!(result, x2, PS_ONE.m128);
result = _mm_mul_ps(result, x);
result
}
// Based on http://gruntthepeon.free.fr/ssemath/sse_mathfun.h
// #[cfg(target_feature = "sse2")]
// unsafe fn sin_cos_sse2(x: __m128) -> (__m128, __m128) {
// let mut sign_bit_sin = x;
// // take the absolute value
// let mut x = _mm_and_ps(x, PS_INV_SIGN_MASK.m128);
// // extract the sign bit (upper one)
// sign_bit_sin = _mm_and_ps(sign_bit_sin, PS_SIGN_MASK.m128);
// // scale by 4/Pi
// let mut y = _mm_mul_ps(x, PS_CEPHES_FOPI.m128);
// // store the integer part of y in emm2
// let mut emm2 = _mm_cvttps_epi32(y);
// // j=(j+1) & (~1) (see the cephes sources)
// emm2 = _mm_add_epi32(emm2, PI32_1.m128i);
// emm2 = _mm_and_si128(emm2, PI32_INV_1.m128i);
// y = _mm_cvtepi32_ps(emm2);
// let mut emm4 = emm2;
// /* get the swap sign flag for the sine */
// let mut emm0 = _mm_and_si128(emm2, PI32_4.m128i);
// emm0 = _mm_slli_epi32(emm0, 29);
// let swap_sign_bit_sin = _mm_castsi128_ps(emm0);
// /* get the polynom selection mask for the sine*/
// emm2 = _mm_and_si128(emm2, PI32_2.m128i);
// emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
// let poly_mask = _mm_castsi128_ps(emm2);
// /* The magic pass: "Extended precision modular arithmetic"
// x = ((x - y * DP1) - y * DP2) - y * DP3; */
// let mut xmm1 = PS_MINUS_CEPHES_DP1.m128;
// let mut xmm2 = PS_MINUS_CEPHES_DP2.m128;
// let mut xmm3 = PS_MINUS_CEPHES_DP3.m128;
// xmm1 = _mm_mul_ps(y, xmm1);
// xmm2 = _mm_mul_ps(y, xmm2);
// xmm3 = _mm_mul_ps(y, xmm3);
// x = _mm_add_ps(x, xmm1);
// x = _mm_add_ps(x, xmm2);
// x = _mm_add_ps(x, xmm3);
// emm4 = _mm_sub_epi32(emm4, PI32_2.m128i);
// emm4 = _mm_andnot_si128(emm4, PI32_4.m128i);
// emm4 = _mm_slli_epi32(emm4, 29);
// let sign_bit_cos = _mm_castsi128_ps(emm4);
// sign_bit_sin = _mm_xor_ps(sign_bit_sin, swap_sign_bit_sin);
// // Evaluate the first polynom (0 <= x <= Pi/4)
// let z = _mm_mul_ps(x, x);
// y = PS_COSCOF_P0.m128;
// y = _mm_mul_ps(y, z);
// y = _mm_add_ps(y, PS_COSCOF_P1.m128);
// y = _mm_mul_ps(y, z);
// y = _mm_add_ps(y, PS_COSCOF_P2.m128);
// y = _mm_mul_ps(y, z);
// y = _mm_mul_ps(y, z);
// let tmp = _mm_mul_ps(z, PS_0_5.m128);
// y = _mm_sub_ps(y, tmp);
// y = _mm_add_ps(y, PS_1_0.m128);
// // Evaluate the second polynom (Pi/4 <= x <= 0)
// let mut y2 = PS_SINCOF_P0.m128;
// y2 = _mm_mul_ps(y2, z);
// y2 = _mm_add_ps(y2, PS_SINCOF_P1.m128);
// y2 = _mm_mul_ps(y2, z);
// y2 = _mm_add_ps(y2, PS_SINCOF_P2.m128);
// y2 = _mm_mul_ps(y2, z);
// y2 = _mm_mul_ps(y2, x);
// y2 = _mm_add_ps(y2, x);
// // select the correct result from the two polynoms
// xmm3 = poly_mask;
// let ysin2 = _mm_and_ps(xmm3, y2);
// let ysin1 = _mm_andnot_ps(xmm3, y);
// y2 = _mm_sub_ps(y2, ysin2);
// y = _mm_sub_ps(y, ysin1);
// xmm1 = _mm_add_ps(ysin1, ysin2);
// xmm2 = _mm_add_ps(y, y2);
// // update the sign
// (
// _mm_xor_ps(xmm1, sign_bit_sin),
// _mm_xor_ps(xmm2, sign_bit_cos),
// )
// }
}
#[cfg(test)]
macro_rules! assert_approx_eq {
($a:expr, $b:expr) => {{
assert_approx_eq!($a, $b, core::f32::EPSILON);
}};
($a:expr, $b:expr, $eps:expr) => {{
let (a, b) = (&$a, &$b);
let eps = $eps;
assert!(
(a - b).abs() <= eps,
"assertion failed: `(left !== right)` \
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
*a,
*b,
eps,
(a - b).abs()
);
}};
}
#[cfg(test)]
macro_rules! assert_relative_eq {
($a:expr, $b:expr) => {{
assert_relative_eq!($a, $b, core::f32::EPSILON);
}};
($a:expr, $b:expr, $eps:expr) => {{
let (a, b) = (&$a, &$b);
let eps = $eps;
let diff = (a - b).abs();
let largest = a.abs().max(b.abs());
assert!(
diff <= largest * eps,
"assertion failed: `(left !== right)` \
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
*a,
*b,
largest * eps,
diff
);
}};
}
#[test]
fn test_scalar_acos() {
fn test_scalar_acos_angle(a: f32) {
// 1e-6 is the lowest epsilon that will pass
assert_relative_eq!(scalar_acos(a), a.acos(), 1e-6);
// assert_approx_eq!(scalar_acos(a), a.acos(), 1e-6);
}
// test 1024 floats between -1.0 and 1.0 inclusive
const MAX_TESTS: u32 = 1024 / 2;
const SIGN: u32 = 0x80_00_00_00;
const PTVE_ONE: u32 = 0x3f_80_00_00; // 1.0_f32.to_bits();
const NGVE_ONE: u32 = SIGN | PTVE_ONE;
const STEP_SIZE: usize = (PTVE_ONE / MAX_TESTS) as usize;
for f in (SIGN..=NGVE_ONE)
.step_by(STEP_SIZE)
.map(|i| f32::from_bits(i))
{
test_scalar_acos_angle(f);
}
for f in (0..=PTVE_ONE).step_by(STEP_SIZE).map(|i| f32::from_bits(i)) {
test_scalar_acos_angle(f);
}
// input is clamped to -1.0..1.0
assert_approx_eq!(scalar_acos(2.0), 0.0);
assert_approx_eq!(scalar_acos(-2.0), core::f32::consts::PI);
}
#[test]
fn test_scalar_sin_cos() {
fn test_scalar_sin_cos_angle(a: f32) {
let (s1, c1) = scalar_sin_cos(a);
let (s2, c2) = a.sin_cos();
dbg!(a);
assert_approx_eq!(s1, s2);
assert_approx_eq!(c1, c2);
}
// test 1024 floats between -PI and PI inclusive
const MAX_TESTS: u32 = 1024 / 2;
const SIGN: u32 = 0x80_00_00_00;
let ptve_pi = core::f32::consts::PI.to_bits();
let ngve_pi = SIGN | ptve_pi;
let step_pi = (ptve_pi / MAX_TESTS) as usize;
for f in (SIGN..=ngve_pi).step_by(step_pi).map(|i| f32::from_bits(i)) {
test_scalar_sin_cos_angle(f);
}
for f in (0..=ptve_pi).step_by(step_pi).map(|i| f32::from_bits(i)) {
test_scalar_sin_cos_angle(f);
}
// test 1024 floats between -INF and +INF exclusive
let ptve_inf = core::f32::INFINITY.to_bits();
let ngve_inf = core::f32::NEG_INFINITY.to_bits();
let step_inf = (ptve_inf / MAX_TESTS) as usize;
for f in (SIGN..ngve_inf)
.step_by(step_inf)
.map(|i| f32::from_bits(i))
{
test_scalar_sin_cos_angle(f);
}
for f in (0..ptve_inf).step_by(step_inf).map(|i| f32::from_bits(i)) {
test_scalar_sin_cos_angle(f);
}
// +inf and -inf should return NaN
let (s, c) = scalar_sin_cos(core::f32::INFINITY);
assert!(s.is_nan());
assert!(c.is_nan());
let (s, c) = scalar_sin_cos(core::f32::NEG_INFINITY);
assert!(s.is_nan());
assert!(c.is_nan());
}
#[test]
#[cfg(vec4sse2)]
fn test_sse2_m128_sin() {
use crate::Vec4;
use core::f32::consts::PI;
fn test_sse2_m128_sin_angle(a: f32) {
let v = Vec4::splat(a);
let v = unsafe { Vec4(sse2::m128_sin(v.0)) };
let a_sin = a.sin();
dbg!((a, a_sin, v));
assert_approx_eq!(v.x(), a_sin, 1e-6);
assert_approx_eq!(v.z(), a_sin, 1e-6);
assert_approx_eq!(v.y(), a_sin, 1e-6);
assert_approx_eq!(v.w(), a_sin, 1e-6);
}
let mut a = -PI;
let end = PI;
let step = PI / 8192.0;
while a <= end {
test_sse2_m128_sin_angle(a);
a += step;
}
}
// sse2::m128_sin is derived from the XMVectorSin in DirectXMath. It's been
// observed both here and in the C++ version that the error rate increases
// as the input angle drifts further from the bounds of PI.
//
// #[test]
// #[cfg(vec4sse2)]
// fn test_sse2_m128_sin2() {
// use crate::Vec4;
// fn test_sse2_m128_sin_angle(a: f32) -> f32 {
// let v = Vec4::splat(a);
// let v = unsafe { Vec4(sse2::m128_sin(v.0)) };
// let a_sin = a.sin();
// let v_sin = v.x();
// // println!("{:?}", (a, a_sin, v_sin));
// assert_approx_eq!(a_sin, v.x(), 1e-4);
// assert_approx_eq!(a_sin, v.z(), 1e-4);
// assert_approx_eq!(a_sin, v.y(), 1e-4);
// assert_approx_eq!(a_sin, v.w(), 1e-4);
// v.x()
// }
// // test 1024 floats between -PI and PI inclusive
// const MAX_TESTS: u32 = 1024 / 2;
// const SIGN: u32 = 0x80_00_00_00;
// let ptve_pi = std::f32::consts::PI.to_bits();
// let ngve_pi = SIGN | ptve_pi;
// let step_pi = (ptve_pi / MAX_TESTS) as usize;
// for f in (SIGN..=ngve_pi).step_by(step_pi).map(|i| f32::from_bits(i)) {
// test_sse2_m128_sin_angle(f);
// }
// for f in (0..=ptve_pi).step_by(step_pi).map(|i| f32::from_bits(i)) {
// test_sse2_m128_sin_angle(f);
// }
// // test 1024 floats between -INF and +INF exclusive
// let ptve_inf = std::f32::INFINITY.to_bits();
// let ngve_inf = std::f32::NEG_INFINITY.to_bits();
// let step_inf = (ptve_inf / MAX_TESTS) as usize;
// for f in (SIGN..ngve_inf)
// .step_by(step_inf)
// .map(|i| f32::from_bits(i))
// {
// test_sse2_m128_sin_angle(f);
// }
// for f in (0..ptve_inf).step_by(step_inf).map(|i| f32::from_bits(i)) {
// test_sse2_m128_sin_angle(f);
// }
// // +inf and -inf should return NaN
// let s = test_sse2_m128_sin_angle(std::f32::INFINITY);
// assert!(s.is_nan());
// let s = test_sse2_m128_sin_angle(std::f32::NEG_INFINITY);
// assert!(s.is_nan());
// }

View file

@ -0,0 +1,309 @@
use super::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
use mint;
impl From<mint::Point2<f32>> for Vec2 {
fn from(v: mint::Point2<f32>) -> Self {
Self::new(v.x, v.y)
}
}
impl From<Vec2> for mint::Point2<f32> {
fn from(v: Vec2) -> Self {
let (x, y) = v.into();
Self { x, y }
}
}
impl From<mint::Point3<f32>> for Vec3 {
fn from(v: mint::Point3<f32>) -> Self {
Self::new(v.x, v.y, v.z)
}
}
impl From<Vec3> for mint::Point3<f32> {
fn from(v: Vec3) -> Self {
let (x, y, z) = v.into();
Self { x, y, z }
}
}
impl From<mint::Vector2<f32>> for Vec2 {
fn from(v: mint::Vector2<f32>) -> Self {
Self::new(v.x, v.y)
}
}
impl From<Vec2> for mint::Vector2<f32> {
fn from(v: Vec2) -> Self {
let (x, y) = v.into();
Self { x, y }
}
}
impl From<mint::Vector3<f32>> for Vec3 {
fn from(v: mint::Vector3<f32>) -> Self {
Self::new(v.x, v.y, v.z)
}
}
impl From<Vec3> for mint::Vector3<f32> {
fn from(v: Vec3) -> Self {
let (x, y, z) = v.into();
Self { x, y, z }
}
}
impl From<mint::Vector4<f32>> for Vec4 {
fn from(v: mint::Vector4<f32>) -> Self {
Self::new(v.x, v.y, v.z, v.w)
}
}
impl From<Vec4> for mint::Vector4<f32> {
fn from(v: Vec4) -> Self {
let (x, y, z, w) = v.into();
Self { x, y, z, w }
}
}
impl From<mint::Quaternion<f32>> for Quat {
fn from(q: mint::Quaternion<f32>) -> Self {
Self::from_xyzw(q.v.x, q.v.y, q.v.z, q.s)
}
}
impl From<Quat> for mint::Quaternion<f32> {
fn from(q: Quat) -> Self {
let (x, y, z, s) = q.into();
Self {
s,
v: mint::Vector3 { x, y, z },
}
}
}
impl From<mint::RowMatrix2<f32>> for Mat2 {
fn from(m: mint::RowMatrix2<f32>) -> Self {
Self::from_cols(m.x.into(), m.y.into()).transpose()
}
}
impl From<Mat2> for mint::RowMatrix2<f32> {
fn from(m: Mat2) -> Self {
let mt = m.transpose();
Self {
x: mt.x_axis().into(),
y: mt.y_axis().into(),
}
}
}
impl From<mint::ColumnMatrix2<f32>> for Mat2 {
fn from(m: mint::ColumnMatrix2<f32>) -> Self {
Self::from_cols(m.x.into(), m.y.into())
}
}
impl From<Mat2> for mint::ColumnMatrix2<f32> {
fn from(m: Mat2) -> Self {
Self {
x: m.x_axis().into(),
y: m.y_axis().into(),
}
}
}
impl From<mint::RowMatrix3<f32>> for Mat3 {
fn from(m: mint::RowMatrix3<f32>) -> Self {
Self::from_cols(m.x.into(), m.y.into(), m.z.into()).transpose()
}
}
impl From<Mat3> for mint::RowMatrix3<f32> {
fn from(m: Mat3) -> Self {
let mt = m.transpose();
Self {
x: mt.x_axis().into(),
y: mt.y_axis().into(),
z: mt.z_axis().into(),
}
}
}
impl From<mint::ColumnMatrix3<f32>> for Mat3 {
fn from(m: mint::ColumnMatrix3<f32>) -> Self {
Self::from_cols(m.x.into(), m.y.into(), m.z.into())
}
}
impl From<Mat3> for mint::ColumnMatrix3<f32> {
fn from(m: Mat3) -> Self {
Self {
x: m.x_axis().into(),
y: m.y_axis().into(),
z: m.z_axis().into(),
}
}
}
impl From<mint::RowMatrix4<f32>> for Mat4 {
fn from(m: mint::RowMatrix4<f32>) -> Self {
Self::from_cols(m.x.into(), m.y.into(), m.z.into(), m.w.into()).transpose()
}
}
impl From<Mat4> for mint::RowMatrix4<f32> {
fn from(m: Mat4) -> Self {
let mt = m.transpose();
Self {
x: mt.x_axis().into(),
y: mt.y_axis().into(),
z: mt.z_axis().into(),
w: mt.w_axis().into(),
}
}
}
impl From<mint::ColumnMatrix4<f32>> for Mat4 {
fn from(m: mint::ColumnMatrix4<f32>) -> Self {
Self::from_cols(m.x.into(), m.y.into(), m.z.into(), m.w.into())
}
}
impl From<Mat4> for mint::ColumnMatrix4<f32> {
fn from(m: Mat4) -> Self {
Self {
x: m.x_axis().into(),
y: m.y_axis().into(),
z: m.z_axis().into(),
w: m.w_axis().into(),
}
}
}
#[cfg(test)]
mod test {
use mint;
#[test]
fn test_point2() {
use crate::Vec2;
let m = mint::Point2 { x: 1.0, y: 2.0 };
let g = Vec2::from(m);
assert_eq!(g, Vec2::new(1.0, 2.0));
assert_eq!(m, g.into());
}
#[test]
fn test_point3() {
use crate::Vec3;
let m = mint::Point3 {
x: 1.0,
y: 2.0,
z: 3.0,
};
let g = Vec3::from(m);
assert_eq!(g, Vec3::new(1.0, 2.0, 3.0));
assert_eq!(m, g.into());
}
#[test]
fn test_vector2() {
use crate::Vec2;
let m = mint::Vector2 { x: 1.0, y: 2.0 };
let g = Vec2::from(m);
assert_eq!(g, Vec2::new(1.0, 2.0));
assert_eq!(m, g.into());
}
#[test]
fn test_vector3() {
use crate::Vec3;
let m = mint::Vector3 {
x: 1.0,
y: 2.0,
z: 3.0,
};
let g = Vec3::from(m);
assert_eq!(g, Vec3::new(1.0, 2.0, 3.0));
assert_eq!(m, g.into());
}
#[test]
fn test_vector4() {
use crate::Vec4;
let m = mint::Vector4 {
x: 1.0,
y: 2.0,
z: 3.0,
w: 4.0,
};
let g = Vec4::from(m);
assert_eq!(g, Vec4::new(1.0, 2.0, 3.0, 4.0));
assert_eq!(m, g.into());
}
#[test]
fn test_quaternion() {
use crate::Quat;
let m = mint::Quaternion {
v: mint::Vector3 {
x: 1.0,
y: 2.0,
z: 3.0,
},
s: 4.0,
};
let g = Quat::from(m);
assert_eq!(g, Quat::from((1.0, 2.0, 3.0, 4.0)));
assert_eq!(m, g.into());
}
#[test]
fn test_matrix2() {
use crate::Mat2;
let g = Mat2::from_cols_array_2d(&[[1.0, 2.0], [3.0, 4.0]]);
let m = mint::ColumnMatrix2::from(g);
assert_eq!(g, Mat2::from(m));
let mt = mint::RowMatrix2::from(g);
assert_eq!(mt, mint::RowMatrix2::from([[1.0, 3.0], [2.0, 4.0]]));
assert_eq!(g, Mat2::from(mt));
}
#[test]
fn test_matrix3() {
use crate::Mat3;
let g = Mat3::from_cols_array_2d(&[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
let m = mint::ColumnMatrix3::from(g);
assert_eq!(g, Mat3::from(m));
let mt = mint::RowMatrix3::from(g);
assert_eq!(
mt,
mint::RowMatrix3::from([[1.0, 4.0, 7.0], [2.0, 5.0, 8.0], [3.0, 6.0, 9.0]])
);
assert_eq!(g, Mat3::from(mt));
}
#[test]
fn test_matrix4() {
use crate::Mat4;
let g = Mat4::from_cols_array_2d(&[
[1.0, 2.0, 3.0, 4.0],
[5.0, 6.0, 7.0, 8.0],
[9.0, 10.0, 11.0, 12.0],
[13.0, 14.0, 15.0, 16.0],
]);
let m = mint::ColumnMatrix4::from(g);
assert_eq!(g, Mat4::from(m));
let mt = mint::RowMatrix4::from(g);
assert_eq!(
mt,
mint::RowMatrix4::from([
[1.0, 5.0, 9.0, 13.0],
[2.0, 6.0, 10.0, 14.0],
[3.0, 7.0, 11.0, 15.0],
[4.0, 8.0, 12.0, 16.0]
])
);
assert_eq!(g, Mat4::from(mt));
}
}

View file

@ -0,0 +1,59 @@
use super::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
use rand::{
distributions::{Distribution, Standard},
Rng,
};
impl Distribution<Mat2> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Mat2 {
Mat2::from_cols_array(&rng.gen())
}
}
impl Distribution<Mat3> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Mat3 {
Mat3::from_cols_array(&rng.gen())
}
}
impl Distribution<Mat4> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Mat4 {
Mat4::from_cols_array(&rng.gen())
}
}
impl Distribution<Quat> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Quat {
use core::f32::consts::PI;
let yaw = -PI + rng.gen::<f32>() * 2.0 * PI;
let pitch = -PI + rng.gen::<f32>() * 2.0 * PI;
let roll = -PI + rng.gen::<f32>() * 2.0 * PI;
Quat::from_rotation_ypr(yaw, pitch, roll)
}
}
impl Distribution<Vec2> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec2 {
rng.gen::<(f32, f32)>().into()
}
}
impl Distribution<Vec3> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec3 {
rng.gen::<(f32, f32, f32)>().into()
}
}
impl Distribution<Vec4> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec4 {
rng.gen::<[f32; 4]>().into()
}
}

View file

@ -0,0 +1,385 @@
use crate::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
use core::fmt;
use serde::{
de::{self, Deserialize, Deserializer, SeqAccess, Visitor},
ser::{Serialize, SerializeTupleStruct, Serializer},
};
impl Serialize for Vec2 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let (x, y) = (*self).into();
let mut state = serializer.serialize_tuple_struct("Vec2", 2)?;
state.serialize_field(&x)?;
state.serialize_field(&y)?;
state.end()
}
}
#[cfg(feature = "serde")]
impl Serialize for Vec3 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let (x, y, z) = (*self).into();
// 3 is the number of fields in the struct.
let mut state = serializer.serialize_tuple_struct("Vec3", 3)?;
state.serialize_field(&x)?;
state.serialize_field(&y)?;
state.serialize_field(&z)?;
state.end()
}
}
#[cfg(feature = "serde")]
impl Serialize for Vec4 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let (x, y, z, w) = (*self).into();
// 4 is the number of fields in the struct.
let mut state = serializer.serialize_tuple_struct("Vec4", 4)?;
state.serialize_field(&x)?;
state.serialize_field(&y)?;
state.serialize_field(&z)?;
state.serialize_field(&w)?;
state.end()
}
}
#[cfg(feature = "serde")]
impl Serialize for Quat {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let (x, y, z, w) = (*self).into();
// 4 is the number of fields in the struct.
let mut state = serializer.serialize_tuple_struct("Quat", 4)?;
state.serialize_field(&x)?;
state.serialize_field(&y)?;
state.serialize_field(&z)?;
state.serialize_field(&w)?;
state.end()
}
}
#[cfg(feature = "serde")]
impl Serialize for Mat2 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let f: &[f32; 4] = self.as_ref();
let mut state = serializer.serialize_tuple_struct("Mat2", 4)?;
state.serialize_field(&f[0])?;
state.serialize_field(&f[1])?;
state.serialize_field(&f[2])?;
state.serialize_field(&f[3])?;
state.end()
}
}
#[cfg(feature = "serde")]
impl Serialize for Mat3 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let (m00, m01, m02) = self.x_axis.into();
let (m10, m11, m12) = self.y_axis.into();
let (m20, m21, m22) = self.z_axis.into();
let mut state = serializer.serialize_tuple_struct("Mat3", 9)?;
state.serialize_field(&m00)?;
state.serialize_field(&m01)?;
state.serialize_field(&m02)?;
state.serialize_field(&m10)?;
state.serialize_field(&m11)?;
state.serialize_field(&m12)?;
state.serialize_field(&m20)?;
state.serialize_field(&m21)?;
state.serialize_field(&m22)?;
state.end()
}
}
#[cfg(feature = "serde")]
impl Serialize for Mat4 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_tuple_struct("Mat4", 16)?;
for f in self.as_ref() {
state.serialize_field(f)?;
}
state.end()
}
}
impl<'de> Deserialize<'de> for Vec2 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Vec2Visitor;
// TODO: Not sure why this line is reported as uncovered
#[cfg_attr(tarpaulin, skip)]
impl<'de> Visitor<'de> for Vec2Visitor {
type Value = Vec2;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Vec2")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Vec2, V::Error>
where
V: SeqAccess<'de>,
{
let x = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let y = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
Ok(Vec2::new(x, y))
}
}
deserializer.deserialize_tuple_struct("Vec2", 2, Vec2Visitor)
}
}
impl<'de> Deserialize<'de> for Vec3 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Vec3Visitor;
// TODO: Not sure why this line is reported as uncovered
#[cfg_attr(tarpaulin, skip)]
impl<'de> Visitor<'de> for Vec3Visitor {
type Value = Vec3;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Vec2")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Vec3, V::Error>
where
V: SeqAccess<'de>,
{
let x = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let y = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let z = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
Ok(Vec3::new(x, y, z))
}
}
deserializer.deserialize_tuple_struct("Vec3", 3, Vec3Visitor)
}
}
impl<'de> Deserialize<'de> for Vec4 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Vec4Visitor;
// TODO: Not sure why this line is reported as uncovered
#[cfg_attr(tarpaulin, skip)]
impl<'de> Visitor<'de> for Vec4Visitor {
type Value = Vec4;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Vec2")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Vec4, V::Error>
where
V: SeqAccess<'de>,
{
let x = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let y = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let z = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
let w = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(3, &self))?;
Ok(Vec4::new(x, y, z, w))
}
}
deserializer.deserialize_tuple_struct("Vec4", 4, Vec4Visitor)
}
}
impl<'de> Deserialize<'de> for Quat {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct QuatVisitor;
// TODO: Not sure why this line is reported as uncovered
#[cfg_attr(tarpaulin, skip)]
impl<'de> Visitor<'de> for QuatVisitor {
type Value = Quat;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Vec2")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Quat, V::Error>
where
V: SeqAccess<'de>,
{
let x = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let y = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let z = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
let w = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(3, &self))?;
Ok(Quat::from_xyzw(x, y, z, w))
}
}
deserializer.deserialize_tuple_struct("Quat", 4, QuatVisitor)
}
}
impl<'de> Deserialize<'de> for Mat2 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Mat2Visitor;
// TODO: Not sure why this line is reported as uncovered
#[cfg_attr(tarpaulin, skip)]
impl<'de> Visitor<'de> for Mat2Visitor {
type Value = Mat2;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Mat2")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Mat2, V::Error>
where
V: SeqAccess<'de>,
{
let mut f = { [0.0; 4] };
for i in 0..4 {
f[i] = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(i, &self))?;
}
let x = Vec2::new(f[0], f[1]);
let y = Vec2::new(f[2], f[3]);
Ok(Mat2::from_cols(x, y))
}
}
deserializer.deserialize_tuple_struct("Mat2", 4, Mat2Visitor)
}
}
impl<'de> Deserialize<'de> for Mat3 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Mat3Visitor;
// TODO: Not sure why this line is reported as uncovered
#[cfg_attr(tarpaulin, skip)]
impl<'de> Visitor<'de> for Mat3Visitor {
type Value = Mat3;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Mat3")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Mat3, V::Error>
where
V: SeqAccess<'de>,
{
let mut f = { [0.0; 9] };
for i in 0..9 {
f[i] = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(i, &self))?;
}
let x = Vec3::new(f[0], f[1], f[2]);
let y = Vec3::new(f[3], f[4], f[5]);
let z = Vec3::new(f[6], f[7], f[8]);
Ok(Mat3::from_cols(x, y, z))
}
}
deserializer.deserialize_tuple_struct("Mat3", 9, Mat3Visitor)
}
}
impl<'de> Deserialize<'de> for Mat4 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Mat4Visitor;
// TODO: Not sure why this line is reported as uncovered
#[cfg_attr(tarpaulin, skip)]
impl<'de> Visitor<'de> for Mat4Visitor {
type Value = Mat4;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct Mat4")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Mat4, V::Error>
where
V: SeqAccess<'de>,
{
let mut f = { [0.0; 16] };
for i in 0..16 {
f[i] = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(i, &self))?;
}
let x = Vec4::new(f[0], f[1], f[2], f[3]);
let y = Vec4::new(f[4], f[5], f[6], f[7]);
let z = Vec4::new(f[8], f[9], f[10], f[11]);
let w = Vec4::new(f[12], f[13], f[14], f[15]);
Ok(Mat4::from_cols(x, y, z, w))
}
}
deserializer.deserialize_tuple_struct("Mat4", 16, Mat4Visitor)
}
}

View file

@ -0,0 +1,51 @@
use zerocopy::AsBytes;
use crate::{Vec2, Vec3, Vec4, Mat2, Mat3, Mat4, Quat};
unsafe impl AsBytes for Vec2 {
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized {}
}
unsafe impl AsBytes for Vec3 {
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized {}
}
unsafe impl AsBytes for Vec4 {
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized {}
}
unsafe impl AsBytes for Mat2 {
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized {}
}
unsafe impl AsBytes for Mat3 {
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized {}
}
unsafe impl AsBytes for Mat4 {
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized {}
}
unsafe impl AsBytes for Quat {
fn only_derive_is_allowed_to_implement_this_trait()
where
Self: Sized {}
}

View file

@ -0,0 +1,335 @@
use super::{scalar_sin_cos, Vec2, Vec4};
#[cfg(all(vec4sse2, target_arch = "x86",))]
use core::arch::x86::*;
#[cfg(all(vec4sse2, target_arch = "x86_64",))]
use core::arch::x86_64::*;
use core::{
fmt,
ops::{Add, Mul, Sub},
};
#[inline]
pub fn mat2(x_axis: Vec2, y_axis: Vec2) -> Mat2 {
Mat2::from_cols(x_axis, y_axis)
}
/// A 2x2 column major matrix.
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[repr(C)]
pub struct Mat2(pub(crate) Vec4);
impl Default for Mat2 {
#[inline]
fn default() -> Self {
Self::identity()
}
}
impl fmt::Display for Mat2 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}, {}]", self.x_axis(), self.y_axis())
}
}
impl Mat2 {
/// Creates a 2x2 matrix with all elements set to `0.0`.
#[inline]
pub fn zero() -> Self {
Mat2(Vec4::zero())
}
/// Creates a 2x2 identity matrix.
#[inline]
pub fn identity() -> Self {
Self(Vec4::new(1.0, 0.0, 0.0, 1.0))
}
/// Creates a 2x2 matrix from four column vectors.
#[inline]
pub fn from_cols(x_axis: Vec2, y_axis: Vec2) -> Self {
Self(Vec4::new(x_axis.x(), x_axis.y(), y_axis.x(), y_axis.y()))
}
/// Creates a 2x2 matrix from a `[f32; 4]` stored in column major order. If
/// your data is stored in row major you will need to `transpose` the
/// returned matrix.
#[inline]
pub fn from_cols_array(m: &[f32; 4]) -> Self {
Mat2(Vec4::new(m[0], m[1], m[2], m[3]))
}
/// Creates a `[f32; 4]` storing data in column major order.
/// If you require data in row major order `transpose` the matrix first.
#[inline]
pub fn to_cols_array(&self) -> [f32; 4] {
self.0.into()
}
/// Creates a 2x2 matrix from a `[[f32; 2]; 2]` stored in column major
/// order. If your data is in row major order you will need to `transpose`
/// the returned matrix.
#[inline]
pub fn from_cols_array_2d(m: &[[f32; 2]; 2]) -> Self {
Mat2(Vec4::new(m[0][0], m[0][1], m[1][0], m[1][1]))
}
/// Creates a `[[f32; 2]; 2]` storing data in column major order.
/// If you require data in row major order `transpose` the matrix first.
#[inline]
pub fn to_cols_array_2d(&self) -> [[f32; 2]; 2] {
let (x0, y0, x1, y1) = self.0.into();
[[x0, y0], [x1, y1]]
}
/// Creates a 2x2 matrix containing the given `scale` and rotation of
/// `angle` (in radians).
#[inline]
pub fn from_scale_angle(scale: Vec2, angle: f32) -> Self {
let (sin, cos) = scalar_sin_cos(angle);
let (scale_x, scale_y) = scale.into();
Self(Vec4::new(
cos * scale_x,
sin * scale_x,
-sin * scale_y,
cos * scale_y,
))
}
/// Creates a 2x2 matrix containing a rotation of `angle` (in radians).
#[inline]
pub fn from_angle(angle: f32) -> Self {
let (sin, cos) = scalar_sin_cos(angle);
Self(Vec4::new(cos, sin, -sin, cos))
}
/// Creates a 2x2 matrix containing the given non-uniform `scale`.
#[inline]
pub fn from_scale(scale: Vec2) -> Self {
let (x, y) = scale.into();
Self(Vec4::new(x, 0.0, 0.0, y))
}
#[inline]
pub fn set_x_axis(&mut self, x: Vec2) {
let m = self.0.as_mut();
m[0] = x.x();
m[1] = x.y();
}
#[inline]
pub fn set_y_axis(&mut self, y: Vec2) {
let m = self.0.as_mut();
m[2] = y.x();
m[3] = y.y();
}
#[inline]
pub fn x_axis(&self) -> Vec2 {
let (x, y, _, _) = self.0.into();
Vec2::new(x, y)
}
#[inline]
pub fn y_axis(&self) -> Vec2 {
let (_, _, x, y) = self.0.into();
Vec2::new(x, y)
}
// #[inline]
// pub(crate) fn col(&self, index: usize) -> Vec2 {
// match index {
// 0 => self.x_axis(),
// 1 => self.y_axis(),
// _ => panic!(
// "index out of bounds: the len is 2 but the index is {}",
// index
// ),
// }
// }
// #[inline]
// pub(crate) fn col_mut(&mut self, index: usize) -> &mut Vec2 {
// match index {
// 0 => unsafe { &mut *(self.0.as_mut().as_mut_ptr() as *mut Vec2) },
// 1 => unsafe { &mut *(self.0.as_mut()[2..].as_mut_ptr() as *mut Vec2) },
// _ => panic!(
// "index out of bounds: the len is 2 but the index is {}",
// index
// ),
// }
// }
/// Returns the transpose of `self`.
#[inline]
pub fn transpose(&self) -> Self {
#[cfg(vec4sse2)]
unsafe {
let abcd = self.0.into();
let acbd = _mm_shuffle_ps(abcd, abcd, 0b11_01_10_00);
Self(acbd.into())
}
#[cfg(vec4f32)]
{
let (m00, m01, m10, m11) = self.0.into();
Self(Vec4::new(m00, m10, m01, m11))
}
}
/// Returns the determinant of `self`.
#[inline]
pub fn determinant(&self) -> f32 {
#[cfg(vec4sse2)]
unsafe {
let abcd = self.0.into();
let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11);
let prod = _mm_mul_ps(abcd, dcba);
let det = _mm_sub_ps(prod, _mm_shuffle_ps(prod, prod, 0b01_01_01_01));
_mm_cvtss_f32(det)
}
#[cfg(vec4f32)]
{
let (a, b, c, d) = self.0.into();
a * d - b * c
}
}
/// Returns the inverse of `self`.
///
/// If the matrix is not invertible the returned matrix will be invalid.
#[inline]
pub fn inverse(&self) -> Self {
#[cfg(vec4sse2)]
unsafe {
let abcd = self.0.into();
let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11);
let prod = _mm_mul_ps(abcd, dcba);
let sub = _mm_sub_ps(prod, _mm_shuffle_ps(prod, prod, 0b01_01_01_01));
let det = _mm_shuffle_ps(sub, sub, 0b00_00_00_00);
let tmp = _mm_div_ps(_mm_set_ps(1.0, -1.0, -1.0, 1.0), det);
let dbca = _mm_shuffle_ps(abcd, abcd, 0b00_10_01_11);
Self(_mm_mul_ps(dbca, tmp).into())
}
#[cfg(vec4f32)]
{
let (a, b, c, d) = self.0.into();
let det = a * d - b * c;
glam_assert!(det != 0.0);
let tmp = Vec4::new(1.0, -1.0, -1.0, 1.0) / det;
Self(Vec4::new(d, b, c, a) * tmp)
}
}
#[inline]
pub fn mul_vec2(&self, other: Vec2) -> Vec2 {
// TODO: SSE2
let other = Vec4::new(other.x(), other.x(), other.y(), other.y());
let tmp = self.0 * other;
let (x0, y0, x1, y1) = tmp.into();
Vec2::new(x0 + x1, y0 + y1)
}
#[inline]
pub fn mul_mat2(&self, other: &Self) -> Self {
// TODO: SSE2
let (x0, y0, x1, y1) = other.0.into();
Mat2::from_cols(
self.mul_vec2(Vec2::new(x0, y0)),
self.mul_vec2(Vec2::new(x1, y1)),
)
}
#[inline]
pub fn add_mat2(&self, other: &Self) -> Self {
Mat2(self.0 + other.0)
}
#[inline]
pub fn sub_mat2(&self, other: &Self) -> Self {
Mat2(self.0 - other.0)
}
#[inline]
pub fn mul_scalar(&self, other: f32) -> Self {
let s = Vec4::splat(other);
Mat2(self.0 * s)
}
/// Returns true if the absolute difference of all elements between `self`
/// and `other` is less than or equal to `max_abs_diff`.
///
/// This can be used to compare if two `Mat2`'s contain similar elements. It
/// works best when comparing with a known value. The `max_abs_diff` that
/// should be used used depends on the values being compared against.
///
/// For more on floating point comparisons see
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
#[inline]
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
self.0.abs_diff_eq(other.0, max_abs_diff)
}
}
impl AsRef<[f32; 4]> for Mat2 {
#[inline]
fn as_ref(&self) -> &[f32; 4] {
unsafe { &*(self as *const Self as *const [f32; 4]) }
}
}
impl AsMut<[f32; 4]> for Mat2 {
#[inline]
fn as_mut(&mut self) -> &mut [f32; 4] {
unsafe { &mut *(self as *mut Self as *mut [f32; 4]) }
}
}
impl Add<Mat2> for Mat2 {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
self.add_mat2(&other)
}
}
impl Sub<Mat2> for Mat2 {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self {
self.sub_mat2(&other)
}
}
impl Mul<Mat2> for Mat2 {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
self.mul_mat2(&other)
}
}
impl Mul<Vec2> for Mat2 {
type Output = Vec2;
#[inline]
fn mul(self, other: Vec2) -> Vec2 {
self.mul_vec2(other)
}
}
impl Mul<Mat2> for f32 {
type Output = Mat2;
#[inline]
fn mul(self, other: Mat2) -> Mat2 {
other.mul_scalar(self)
}
}
impl Mul<f32> for Mat2 {
type Output = Self;
#[inline]
fn mul(self, other: f32) -> Self {
self.mul_scalar(other)
}
}

View file

@ -0,0 +1,482 @@
use super::{scalar_sin_cos, Quat, Vec2, Vec3};
use core::{
fmt,
ops::{Add, Mul, Sub},
};
#[inline]
pub fn mat3(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3) -> Mat3 {
Mat3 {
x_axis,
y_axis,
z_axis,
}
}
#[inline]
fn quat_to_axes(rotation: Quat) -> (Vec3, Vec3, Vec3) {
glam_assert!(rotation.is_normalized());
let (x, y, z, w) = rotation.into();
let x2 = x + x;
let y2 = y + y;
let z2 = z + z;
let xx = x * x2;
let xy = x * y2;
let xz = x * z2;
let yy = y * y2;
let yz = y * z2;
let zz = z * z2;
let wx = w * x2;
let wy = w * y2;
let wz = w * z2;
let x_axis = Vec3::new(1.0 - (yy + zz), xy + wz, xz - wy);
let y_axis = Vec3::new(xy - wz, 1.0 - (xx + zz), yz + wx);
let z_axis = Vec3::new(xz + wy, yz - wx, 1.0 - (xx + yy));
(x_axis, y_axis, z_axis)
}
/// A 3x3 column major matrix.
///
/// This type is 16 byte aligned.
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[repr(C)]
pub struct Mat3 {
pub(crate) x_axis: Vec3,
pub(crate) y_axis: Vec3,
pub(crate) z_axis: Vec3,
}
impl Default for Mat3 {
#[inline]
fn default() -> Self {
Self::identity()
}
}
impl fmt::Display for Mat3 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}, {}, {}]", self.x_axis, self.y_axis, self.z_axis)
}
}
impl Mat3 {
/// Creates a 3x3 matrix with all elements set to `0.0`.
#[inline]
pub fn zero() -> Self {
Self {
x_axis: Vec3::zero(),
y_axis: Vec3::zero(),
z_axis: Vec3::zero(),
}
}
/// Creates a 3x3 identity matrix.
#[inline]
pub fn identity() -> Self {
Self {
x_axis: Vec3::unit_x(),
y_axis: Vec3::unit_y(),
z_axis: Vec3::unit_z(),
}
}
/// Creates a 3x3 matrix from three column vectors.
#[inline]
pub fn from_cols(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3) -> Self {
Self {
x_axis,
y_axis,
z_axis,
}
}
/// Creates a 3x3 matrix from a `[f32; 9]` stored in column major order.
/// If your data is stored in row major you will need to `transpose` the
/// returned matrix.
#[inline]
pub fn from_cols_array(m: &[f32; 9]) -> Self {
Mat3 {
x_axis: Vec3::new(m[0], m[1], m[2]),
y_axis: Vec3::new(m[3], m[4], m[5]),
z_axis: Vec3::new(m[6], m[7], m[8]),
}
}
/// Creates a `[f32; 9]` storing data in column major order.
/// If you require data in row major order `transpose` the matrix first.
#[inline]
pub fn to_cols_array(&self) -> [f32; 9] {
let (m00, m01, m02) = self.x_axis.into();
let (m10, m11, m12) = self.y_axis.into();
let (m20, m21, m22) = self.z_axis.into();
[m00, m01, m02, m10, m11, m12, m20, m21, m22]
}
/// Creates a 3x3 matrix from a `[[f32; 3]; 3]` stored in column major order.
/// If your data is in row major order you will need to `transpose` the
/// returned matrix.
#[inline]
pub fn from_cols_array_2d(m: &[[f32; 3]; 3]) -> Self {
Mat3 {
x_axis: m[0].into(),
y_axis: m[1].into(),
z_axis: m[2].into(),
}
}
/// Creates a `[[f32; 3]; 3]` storing data in column major order.
/// If you require data in row major order `transpose` the matrix first.
#[inline]
pub fn to_cols_array_2d(&self) -> [[f32; 3]; 3] {
[self.x_axis.into(), self.y_axis.into(), self.z_axis.into()]
}
/// Creates a 3x3 homogeneous transformation matrix from the given `scale`,
/// rotation `angle` (in radians) and `translation`.
///
/// The resulting matrix can be used to transform 2D points and vectors.
#[inline]
pub fn from_scale_angle_translation(scale: Vec2, angle: f32, translation: Vec2) -> Self {
let (sin, cos) = scalar_sin_cos(angle);
let (scale_x, scale_y) = scale.into();
Self {
x_axis: Vec3::new(cos * scale_x, sin * scale_x, 0.0),
y_axis: Vec3::new(-sin * scale_y, cos * scale_y, 0.0),
z_axis: translation.extend(1.0),
}
}
#[inline]
/// Creates a 3x3 rotation matrix from the given quaternion.
pub fn from_quat(rotation: Quat) -> Self {
let (x_axis, y_axis, z_axis) = quat_to_axes(rotation);
Self {
x_axis,
y_axis,
z_axis,
}
}
/// Creates a 3x3 rotation matrix from a normalized rotation `axis` and
/// `angle` (in radians).
#[inline]
pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
glam_assert!(axis.is_normalized());
let (sin, cos) = scalar_sin_cos(angle);
let (x, y, z) = axis.into();
let (xsin, ysin, zsin) = (axis * sin).into();
let (x2, y2, z2) = (axis * axis).into();
let omc = 1.0 - cos;
let xyomc = x * y * omc;
let xzomc = x * z * omc;
let yzomc = y * z * omc;
Self {
x_axis: Vec3::new(x2 * omc + cos, xyomc + zsin, xzomc - ysin),
y_axis: Vec3::new(xyomc - zsin, y2 * omc + cos, yzomc + xsin),
z_axis: Vec3::new(xzomc + ysin, yzomc - xsin, z2 * omc + cos),
}
}
/// Creates a 3x3 rotation matrix from the given Euler angles (in radians).
#[inline]
pub fn from_rotation_ypr(yaw: f32, pitch: f32, roll: f32) -> Self {
let quat = Quat::from_rotation_ypr(yaw, pitch, roll);
Self::from_quat(quat)
}
/// Creates a 3x3 rotation matrix from `angle` (in radians) around the x axis.
#[inline]
pub fn from_rotation_x(angle: f32) -> Self {
let (sina, cosa) = scalar_sin_cos(angle);
Self {
x_axis: Vec3::unit_x(),
y_axis: Vec3::new(0.0, cosa, sina),
z_axis: Vec3::new(0.0, -sina, cosa),
}
}
/// Creates a 3x3 rotation matrix from `angle` (in radians) around the y axis.
#[inline]
pub fn from_rotation_y(angle: f32) -> Self {
let (sina, cosa) = scalar_sin_cos(angle);
Self {
x_axis: Vec3::new(cosa, 0.0, -sina),
y_axis: Vec3::unit_y(),
z_axis: Vec3::new(sina, 0.0, cosa),
}
}
/// Creates a 3x3 rotation matrix from `angle` (in radians) around the z axis.
#[inline]
pub fn from_rotation_z(angle: f32) -> Self {
let (sina, cosa) = scalar_sin_cos(angle);
Self {
x_axis: Vec3::new(cosa, sina, 0.0),
y_axis: Vec3::new(-sina, cosa, 0.0),
z_axis: Vec3::unit_z(),
}
}
/// Creates a 3x3 non-uniform scale matrix.
#[inline]
pub fn from_scale(scale: Vec3) -> Self {
// TODO: should have a affine 2D scale and a 3d scale?
// Do not panic as long as any component is non-zero
glam_assert!(scale.cmpne(Vec3::zero()).any());
let (x, y, z) = scale.into();
Self {
x_axis: Vec3::new(x, 0.0, 0.0),
y_axis: Vec3::new(0.0, y, 0.0),
z_axis: Vec3::new(0.0, 0.0, z),
}
}
#[inline]
pub fn set_x_axis(&mut self, x: Vec3) {
self.x_axis = x;
}
#[inline]
pub fn set_y_axis(&mut self, y: Vec3) {
self.y_axis = y;
}
#[inline]
pub fn set_z_axis(&mut self, z: Vec3) {
self.z_axis = z;
}
#[inline]
pub fn x_axis(&self) -> Vec3 {
self.x_axis
}
#[inline]
pub fn y_axis(&self) -> Vec3 {
self.y_axis
}
#[inline]
pub fn z_axis(&self) -> Vec3 {
self.z_axis
}
// #[inline]
// pub(crate) fn col(&self, index: usize) -> Vec3 {
// match index {
// 0 => self.x_axis,
// 1 => self.y_axis,
// 2 => self.z_axis,
// _ => panic!(
// "index out of bounds: the len is 3 but the index is {}",
// index
// ),
// }
// }
// #[inline]
// pub(crate) fn col_mut(&mut self, index: usize) -> &mut Vec3 {
// match index {
// 0 => &mut self.x_axis,
// 1 => &mut self.y_axis,
// 2 => &mut self.z_axis,
// _ => panic!(
// "index out of bounds: the len is 3 but the index is {}",
// index
// ),
// }
// }
/// Returns the transpose of `self`.
#[inline]
pub fn transpose(&self) -> Self {
#[cfg(vec3sse2)]
{
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
unsafe {
let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
let tmp1 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b11_10_11_10);
Self {
x_axis: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b00_00_10_00).into(),
y_axis: _mm_shuffle_ps(tmp0, self.z_axis.0, 0b01_01_11_01).into(),
z_axis: _mm_shuffle_ps(tmp1, self.z_axis.0, 0b10_10_10_00).into(),
}
}
}
#[cfg(vec3f32)]
{
let (m00, m01, m02) = self.x_axis.into();
let (m10, m11, m12) = self.y_axis.into();
let (m20, m21, m22) = self.z_axis.into();
Self {
x_axis: Vec3::new(m00, m10, m20),
y_axis: Vec3::new(m01, m11, m21),
z_axis: Vec3::new(m02, m12, m22),
}
}
}
/// Returns the determinant of `self`.
#[inline]
pub fn determinant(&self) -> f32 {
self.z_axis.dot(self.x_axis.cross(self.y_axis))
}
/// Returns the inverse of `self`.
///
/// If the matrix is not invertible the returned matrix will be invalid.
pub fn inverse(&self) -> Self {
let tmp0 = self.y_axis.cross(self.z_axis);
let tmp1 = self.z_axis.cross(self.x_axis);
let tmp2 = self.x_axis.cross(self.y_axis);
let det = self.z_axis.dot_as_vec3(tmp2);
glam_assert!(det.cmpne(Vec3::zero()).all());
let inv_det = det.reciprocal();
// TODO: Work out if it's possible to get rid of the transpose
Mat3::from_cols(tmp0 * inv_det, tmp1 * inv_det, tmp2 * inv_det).transpose()
}
/// Transforms a 3D vector.
#[inline]
pub fn mul_vec3(&self, other: Vec3) -> Vec3 {
let mut res = self.x_axis * other.dup_x();
res = self.y_axis.mul_add(other.dup_y(), res);
res = self.z_axis.mul_add(other.dup_z(), res);
res
}
/// Multiplies two 3x3 matrices.
#[inline]
pub fn mul_mat3(&self, other: &Self) -> Self {
Self {
x_axis: self.mul_vec3(other.x_axis),
y_axis: self.mul_vec3(other.y_axis),
z_axis: self.mul_vec3(other.z_axis),
}
}
/// Adds two 3x3 matrices.
#[inline]
pub fn add_mat3(&self, other: &Self) -> Self {
Self {
x_axis: self.x_axis + other.x_axis,
y_axis: self.y_axis + other.y_axis,
z_axis: self.z_axis + other.z_axis,
}
}
/// Subtracts two 3x3 matrices.
#[inline]
pub fn sub_mat3(&self, other: &Self) -> Self {
Self {
x_axis: self.x_axis - other.x_axis,
y_axis: self.y_axis - other.y_axis,
z_axis: self.z_axis - other.z_axis,
}
}
#[inline]
/// Multiplies a 3x3 matrix by a scalar.
pub fn mul_scalar(&self, other: f32) -> Self {
let s = Vec3::splat(other);
Self {
x_axis: self.x_axis * s,
y_axis: self.y_axis * s,
z_axis: self.z_axis * s,
}
}
/// Transforms the given `Vec2` as 2D point.
/// This is the equivalent of multiplying the `Vec2` as a `Vec3` where `w`
/// is `1.0`.
#[inline]
pub fn transform_point2(&self, other: Vec2) -> Vec2 {
// let mut res = self.x_axis * Vec3::splat(other.x());
// res = self.y_axis.mul_add(Vec3::splat(other.y()), res);
// res = self.z_axis + res;
// res.truncate()
self.mul_vec3(other.extend(1.0)).truncate()
}
/// Transforms the given `Vec2` as 2D vector.
/// This is the equivalent of multiplying the `Vec2` as a `Vec3` where `w`
/// is `0.0`.
#[inline]
pub fn transform_vector2(&self, other: Vec2) -> Vec2 {
// TODO: can optimize for w=0.
// let mut res = self.x_axis * Vec3::splat(other.x());
// res = self.y_axis.mul_add(Vec3::splat(other.y()), res);
// res.truncate()
self.mul_vec3(other.extend(0.0)).truncate()
}
/// Returns true if the absolute difference of all elements between `self`
/// and `other` is less than or equal to `max_abs_diff`.
///
/// This can be used to compare if two `Mat3`'s contain similar elements. It
/// works best when comparing with a known value. The `max_abs_diff` that
/// should be used used depends on the values being compared against.
///
/// For more on floating point comparisons see
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
#[inline]
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
self.x_axis.abs_diff_eq(other.x_axis, max_abs_diff)
&& self.y_axis.abs_diff_eq(other.y_axis, max_abs_diff)
&& self.z_axis.abs_diff_eq(other.z_axis, max_abs_diff)
}
}
impl Add<Mat3> for Mat3 {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
self.add_mat3(&other)
}
}
impl Sub<Mat3> for Mat3 {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self {
self.sub_mat3(&other)
}
}
impl Mul<Mat3> for Mat3 {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
self.mul_mat3(&other)
}
}
impl Mul<Vec3> for Mat3 {
type Output = Vec3;
#[inline]
fn mul(self, other: Vec3) -> Vec3 {
self.mul_vec3(other)
}
}
impl Mul<Mat3> for f32 {
type Output = Mat3;
#[inline]
fn mul(self, other: Mat3) -> Mat3 {
other.mul_scalar(self)
}
}
impl Mul<f32> for Mat3 {
type Output = Self;
#[inline]
fn mul(self, other: f32) -> Self {
self.mul_scalar(other)
}
}

View file

@ -0,0 +1,886 @@
use super::{scalar_sin_cos, Mat3, Quat, Vec3, Vec4};
#[cfg(all(vec4sse2, target_arch = "x86"))]
use core::arch::x86::*;
#[cfg(all(vec4sse2, target_arch = "x86_64"))]
use core::arch::x86_64::*;
use core::{
fmt,
ops::{Add, Mul, Sub},
};
#[inline]
pub fn mat4(x_axis: Vec4, y_axis: Vec4, z_axis: Vec4, w_axis: Vec4) -> Mat4 {
Mat4 {
x_axis,
y_axis,
z_axis,
w_axis,
}
}
#[inline]
fn quat_to_axes(rotation: Quat) -> (Vec4, Vec4, Vec4) {
glam_assert!(rotation.is_normalized());
let (x, y, z, w) = rotation.into();
let x2 = x + x;
let y2 = y + y;
let z2 = z + z;
let xx = x * x2;
let xy = x * y2;
let xz = x * z2;
let yy = y * y2;
let yz = y * z2;
let zz = z * z2;
let wx = w * x2;
let wy = w * y2;
let wz = w * z2;
let x_axis = Vec4::new(1.0 - (yy + zz), xy + wz, xz - wy, 0.0);
let y_axis = Vec4::new(xy - wz, 1.0 - (xx + zz), yz + wx, 0.0);
let z_axis = Vec4::new(xz + wy, yz - wx, 1.0 - (xx + yy), 0.0);
(x_axis, y_axis, z_axis)
}
/// A 4x4 column major matrix.
///
/// This type is 16 byte aligned.
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[repr(C)]
pub struct Mat4 {
pub(crate) x_axis: Vec4,
pub(crate) y_axis: Vec4,
pub(crate) z_axis: Vec4,
pub(crate) w_axis: Vec4,
}
impl Default for Mat4 {
#[inline]
fn default() -> Self {
Self::identity()
}
}
impl fmt::Display for Mat4 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"[{}, {}, {}, {}]",
self.x_axis, self.y_axis, self.z_axis, self.w_axis
)
}
}
impl Mat4 {
/// Creates a 4x4 matrix with all elements set to `0.0`.
#[inline]
pub fn zero() -> Self {
Self {
x_axis: Vec4::zero(),
y_axis: Vec4::zero(),
z_axis: Vec4::zero(),
w_axis: Vec4::zero(),
}
}
/// Creates a 4x4 identity matrix.
#[inline]
pub fn identity() -> Self {
Self {
x_axis: Vec4::unit_x(),
y_axis: Vec4::unit_y(),
z_axis: Vec4::unit_z(),
w_axis: Vec4::unit_w(),
}
}
/// Creates a 4x4 matrix from four column vectors.
#[inline]
pub fn from_cols(x_axis: Vec4, y_axis: Vec4, z_axis: Vec4, w_axis: Vec4) -> Self {
Self {
x_axis,
y_axis,
z_axis,
w_axis,
}
}
/// Creates a 4x4 matrix from a `[f32; 16]` stored in column major order.
/// If your data is stored in row major you will need to `transpose` the
/// returned matrix.
#[inline]
pub fn from_cols_array(m: &[f32; 16]) -> Self {
Mat4 {
x_axis: Vec4::new(m[0], m[1], m[2], m[3]),
y_axis: Vec4::new(m[4], m[5], m[6], m[7]),
z_axis: Vec4::new(m[8], m[9], m[10], m[11]),
w_axis: Vec4::new(m[12], m[13], m[14], m[15]),
}
}
/// Creates a `[f32; 16]` storing data in column major order.
/// If you require data in row major order `transpose` the matrix first.
#[inline]
pub fn to_cols_array(&self) -> [f32; 16] {
*self.as_ref()
}
/// Creates a 4x4 matrix from a `[[f32; 4]; 4]` stored in column major
/// order. If your data is in row major order you will need to `transpose`
/// the returned matrix.
#[inline]
pub fn from_cols_array_2d(m: &[[f32; 4]; 4]) -> Self {
Mat4 {
x_axis: m[0].into(),
y_axis: m[1].into(),
z_axis: m[2].into(),
w_axis: m[3].into(),
}
}
/// Creates a `[[f32; 4]; 4]` storing data in column major order.
/// If you require data in row major order `transpose` the matrix first.
#[inline]
pub fn to_cols_array_2d(&self) -> [[f32; 4]; 4] {
[
self.x_axis.into(),
self.y_axis.into(),
self.z_axis.into(),
self.w_axis.into(),
]
}
/// Creates a 4x4 homogeneous transformation matrix from the given `scale`,
/// `rotation` and `translation`.
#[inline]
pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
glam_assert!(rotation.is_normalized());
let (x_axis, y_axis, z_axis) = quat_to_axes(rotation);
let (scale_x, scale_y, scale_z) = scale.into();
Self {
x_axis: x_axis * scale_x,
y_axis: y_axis * scale_y,
z_axis: z_axis * scale_z,
w_axis: translation.extend(1.0),
}
}
/// Creates a 4x4 homogeneous transformation matrix from the given `translation`.
#[inline]
pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
glam_assert!(rotation.is_normalized());
let (x_axis, y_axis, z_axis) = quat_to_axes(rotation);
Self {
x_axis,
y_axis,
z_axis,
w_axis: translation.extend(1.0),
}
}
/// Extracts `scale`, `rotation` and `translation` from `self`. The input matrix is expected to
/// be a 4x4 homogeneous transformation matrix otherwise the output will be invalid.
pub fn to_scale_rotation_translation(&self) -> (Vec3, Quat, Vec3) {
let det = self.determinant();
glam_assert!(det != 0.0);
let scale = Vec3::new(
self.x_axis.length() * det.signum(),
self.y_axis.length(),
self.z_axis.length(),
);
glam_assert!(scale.cmpne(Vec3::zero()).all());
let inv_scale = scale.reciprocal();
let rotation = Quat::from_rotation_mat3(&Mat3::from_cols(
self.x_axis().truncate() * inv_scale.dup_x(),
self.y_axis().truncate() * inv_scale.dup_y(),
self.z_axis().truncate() * inv_scale.dup_z(),
));
let translation = self.w_axis.truncate();
(scale, rotation, translation)
}
/// Creates a 4x4 homogeneous transformation matrix from the given `rotation`.
#[inline]
pub fn from_quat(rotation: Quat) -> Self {
glam_assert!(rotation.is_normalized());
let (x_axis, y_axis, z_axis) = quat_to_axes(rotation);
Self {
x_axis,
y_axis,
z_axis,
w_axis: Vec4::unit_w(),
}
}
/// Creates a 4x4 homogeneous transformation matrix from the given `translation`.
#[inline]
pub fn from_translation(translation: Vec3) -> Self {
Self {
x_axis: Vec4::unit_x(),
y_axis: Vec4::unit_y(),
z_axis: Vec4::unit_z(),
w_axis: translation.extend(1.0),
}
}
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
/// around a normalized rotation `axis` of `angle` (in radians).
#[inline]
pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
glam_assert!(axis.is_normalized());
let (sin, cos) = scalar_sin_cos(angle);
let (x, y, z) = axis.into();
let (xsin, ysin, zsin) = (axis * sin).into();
let (x2, y2, z2) = (axis * axis).into();
let omc = 1.0 - cos;
let xyomc = x * y * omc;
let xzomc = x * z * omc;
let yzomc = y * z * omc;
Self {
x_axis: Vec4::new(x2 * omc + cos, xyomc + zsin, xzomc - ysin, 0.0),
y_axis: Vec4::new(xyomc - zsin, y2 * omc + cos, yzomc + xsin, 0.0),
z_axis: Vec4::new(xzomc + ysin, yzomc - xsin, z2 * omc + cos, 0.0),
w_axis: Vec4::unit_w(),
}
}
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
/// around the given Euler angles (in radians).
#[inline]
pub fn from_rotation_ypr(yaw: f32, pitch: f32, roll: f32) -> Self {
let quat = Quat::from_rotation_ypr(yaw, pitch, roll);
Self::from_quat(quat)
}
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
/// around the x axis of `angle` (in radians).
#[inline]
pub fn from_rotation_x(angle: f32) -> Self {
let (sina, cosa) = scalar_sin_cos(angle);
Self {
x_axis: Vec4::unit_x(),
y_axis: Vec4::new(0.0, cosa, sina, 0.0),
z_axis: Vec4::new(0.0, -sina, cosa, 0.0),
w_axis: Vec4::unit_w(),
}
}
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
/// around the y axis of `angle` (in radians).
#[inline]
pub fn from_rotation_y(angle: f32) -> Self {
let (sina, cosa) = scalar_sin_cos(angle);
Self {
x_axis: Vec4::new(cosa, 0.0, -sina, 0.0),
y_axis: Vec4::unit_y(),
z_axis: Vec4::new(sina, 0.0, cosa, 0.0),
w_axis: Vec4::unit_w(),
}
}
/// Creates a 4x4 homogeneous transformation matrix containing a rotation
/// around the z axis of `angle` (in radians).
#[inline]
pub fn from_rotation_z(angle: f32) -> Self {
let (sina, cosa) = scalar_sin_cos(angle);
Self {
x_axis: Vec4::new(cosa, sina, 0.0, 0.0),
y_axis: Vec4::new(-sina, cosa, 0.0, 0.0),
z_axis: Vec4::unit_z(),
w_axis: Vec4::unit_w(),
}
}
/// Creates a 4x4 homogeneous transformation matrix containing the given
/// non-uniform `scale`.
#[inline]
pub fn from_scale(scale: Vec3) -> Self {
// Do not panic as long as any component is non-zero
glam_assert!(scale.cmpne(Vec3::zero()).any());
let (x, y, z) = scale.into();
Self {
x_axis: Vec4::new(x, 0.0, 0.0, 0.0),
y_axis: Vec4::new(0.0, y, 0.0, 0.0),
z_axis: Vec4::new(0.0, 0.0, z, 0.0),
w_axis: Vec4::unit_w(),
}
}
#[inline]
pub fn set_x_axis(&mut self, x: Vec4) {
self.x_axis = x;
}
#[inline]
pub fn set_y_axis(&mut self, y: Vec4) {
self.y_axis = y;
}
#[inline]
pub fn set_z_axis(&mut self, z: Vec4) {
self.z_axis = z;
}
#[inline]
pub fn set_w_axis(&mut self, w: Vec4) {
self.w_axis = w;
}
#[inline]
pub fn x_axis(&self) -> Vec4 {
self.x_axis
}
#[inline]
pub fn y_axis(&self) -> Vec4 {
self.y_axis
}
#[inline]
pub fn z_axis(&self) -> Vec4 {
self.z_axis
}
#[inline]
pub fn w_axis(&self) -> Vec4 {
self.w_axis
}
// #[inline]
// pub(crate) fn col(&self, index: usize) -> Vec4 {
// match index {
// 0 => self.x_axis,
// 1 => self.y_axis,
// 2 => self.z_axis,
// 3 => self.w_axis,
// _ => panic!(
// "index out of bounds: the len is 4 but the index is {}",
// index
// ),
// }
// }
// #[inline]
// pub(crate) fn col_mut(&mut self, index: usize) -> &mut Vec4 {
// match index {
// 0 => &mut self.x_axis,
// 1 => &mut self.y_axis,
// 2 => &mut self.z_axis,
// 3 => &mut self.w_axis,
// _ => panic!(
// "index out of bounds: the len is 4 but the index is {}",
// index
// ),
// }
// }
/// Returns the transpose of `self`.
#[inline]
pub fn transpose(&self) -> Self {
#[cfg(vec4sse2)]
unsafe {
// sse2 implementation based off DirectXMath XMMatrixInverse (MIT License)
let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
let tmp1 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b11_10_11_10);
let tmp2 = _mm_shuffle_ps(self.z_axis.0, self.w_axis.0, 0b01_00_01_00);
let tmp3 = _mm_shuffle_ps(self.z_axis.0, self.w_axis.0, 0b11_10_11_10);
Self {
x_axis: _mm_shuffle_ps(tmp0, tmp2, 0b10_00_10_00).into(),
y_axis: _mm_shuffle_ps(tmp0, tmp2, 0b11_01_11_01).into(),
z_axis: _mm_shuffle_ps(tmp1, tmp3, 0b10_00_10_00).into(),
w_axis: _mm_shuffle_ps(tmp1, tmp3, 0b11_01_11_01).into(),
}
}
#[cfg(vec4f32)]
{
let (m00, m01, m02, m03) = self.x_axis.into();
let (m10, m11, m12, m13) = self.y_axis.into();
let (m20, m21, m22, m23) = self.z_axis.into();
let (m30, m31, m32, m33) = self.w_axis.into();
Self {
x_axis: Vec4::new(m00, m10, m20, m30),
y_axis: Vec4::new(m01, m11, m21, m31),
z_axis: Vec4::new(m02, m12, m22, m32),
w_axis: Vec4::new(m03, m13, m23, m33),
}
}
}
/// Returns the determinant of `self`.
#[inline]
pub fn determinant(&self) -> f32 {
let (m00, m01, m02, m03) = self.x_axis.into();
let (m10, m11, m12, m13) = self.y_axis.into();
let (m20, m21, m22, m23) = self.z_axis.into();
let (m30, m31, m32, m33) = self.w_axis.into();
let a2323 = m22 * m33 - m23 * m32;
let a1323 = m21 * m33 - m23 * m31;
let a1223 = m21 * m32 - m22 * m31;
let a0323 = m20 * m33 - m23 * m30;
let a0223 = m20 * m32 - m22 * m30;
let a0123 = m20 * m31 - m21 * m30;
m00 * (m11 * a2323 - m12 * a1323 + m13 * a1223)
- m01 * (m10 * a2323 - m12 * a0323 + m13 * a0223)
+ m02 * (m10 * a1323 - m11 * a0323 + m13 * a0123)
- m03 * (m10 * a1223 - m11 * a0223 + m12 * a0123)
}
/// Returns the inverse of `self`.
///
/// If the matrix is not invertible the returned matrix will be invalid.
pub fn inverse(&self) -> Self {
let (m00, m01, m02, m03) = self.x_axis.into();
let (m10, m11, m12, m13) = self.y_axis.into();
let (m20, m21, m22, m23) = self.z_axis.into();
let (m30, m31, m32, m33) = self.w_axis.into();
let coef00 = m22 * m33 - m32 * m23;
let coef02 = m12 * m33 - m32 * m13;
let coef03 = m12 * m23 - m22 * m13;
let coef04 = m21 * m33 - m31 * m23;
let coef06 = m11 * m33 - m31 * m13;
let coef07 = m11 * m23 - m21 * m13;
let coef08 = m21 * m32 - m31 * m22;
let coef10 = m11 * m32 - m31 * m12;
let coef11 = m11 * m22 - m21 * m12;
let coef12 = m20 * m33 - m30 * m23;
let coef14 = m10 * m33 - m30 * m13;
let coef15 = m10 * m23 - m20 * m13;
let coef16 = m20 * m32 - m30 * m22;
let coef18 = m10 * m32 - m30 * m12;
let coef19 = m10 * m22 - m20 * m12;
let coef20 = m20 * m31 - m30 * m21;
let coef22 = m10 * m31 - m30 * m11;
let coef23 = m10 * m21 - m20 * m11;
let fac0 = Vec4::new(coef00, coef00, coef02, coef03);
let fac1 = Vec4::new(coef04, coef04, coef06, coef07);
let fac2 = Vec4::new(coef08, coef08, coef10, coef11);
let fac3 = Vec4::new(coef12, coef12, coef14, coef15);
let fac4 = Vec4::new(coef16, coef16, coef18, coef19);
let fac5 = Vec4::new(coef20, coef20, coef22, coef23);
let vec0 = Vec4::new(m10, m00, m00, m00);
let vec1 = Vec4::new(m11, m01, m01, m01);
let vec2 = Vec4::new(m12, m02, m02, m02);
let vec3 = Vec4::new(m13, m03, m03, m03);
let inv0 = vec1 * fac0 - vec2 * fac1 + vec3 * fac2;
let inv1 = vec0 * fac0 - vec2 * fac3 + vec3 * fac4;
let inv2 = vec0 * fac1 - vec1 * fac3 + vec3 * fac5;
let inv3 = vec0 * fac2 - vec1 * fac4 + vec2 * fac5;
let sign_a = Vec4::new(1.0, -1.0, 1.0, -1.0);
let sign_b = Vec4::new(-1.0, 1.0, -1.0, 1.0);
let inverse = Self {
x_axis: inv0 * sign_a,
y_axis: inv1 * sign_b,
z_axis: inv2 * sign_a,
w_axis: inv3 * sign_b,
};
let col0 = Vec4::new(
inverse.x_axis.x(),
inverse.y_axis.x(),
inverse.z_axis.x(),
inverse.w_axis.x(),
);
let dot0 = self.x_axis * col0;
let dot1 = dot0.x() + dot0.y() + dot0.z() + dot0.w();
glam_assert!(dot1 != 0.0);
let rcp_det = 1.0 / dot1;
inverse * rcp_det
}
#[inline]
// TODO: make public at some point
fn look_to_lh(eye: Vec3, dir: Vec3, up: Vec3) -> Self {
let f = dir.normalize();
let s = up.cross(f).normalize();
let u = f.cross(s);
let (fx, fy, fz) = f.into();
let (sx, sy, sz) = s.into();
let (ux, uy, uz) = u.into();
Mat4::from_cols(
Vec4::new(sx, ux, fx, 0.0),
Vec4::new(sy, uy, fy, 0.0),
Vec4::new(sz, uz, fz, 0.0),
Vec4::new(-s.dot(eye), -u.dot(eye), -f.dot(eye), 1.0),
)
}
#[inline]
pub fn look_at_lh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
glam_assert!(up.is_normalized());
Mat4::look_to_lh(eye, center - eye, up)
}
#[inline]
pub fn look_at_rh(eye: Vec3, center: Vec3, up: Vec3) -> Self {
glam_assert!(up.is_normalized());
Mat4::look_to_lh(eye, eye - center, up)
}
/// Creates a right-handed perspective projection matrix with [-1,1] depth range.
/// This is the same as the OpenGL `gluPerspective` function.
/// See https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
pub fn perspective_rh_gl(
fov_y_radians: f32,
aspect_ratio: f32,
z_near: f32,
z_far: f32,
) -> Self {
let inv_length = 1.0 / (z_near - z_far);
let f = 1.0 / (0.5 * fov_y_radians).tan();
let a = f / aspect_ratio;
let b = (z_near + z_far) * inv_length;
let c = (2.0 * z_near * z_far) * inv_length;
Mat4::from_cols(
Vec4::new(a, 0.0, 0.0, 0.0),
Vec4::new(0.0, f, 0.0, 0.0),
Vec4::new(0.0, 0.0, b, -1.0),
Vec4::new(0.0, 0.0, c, 0.0),
)
}
/// Creates a left-handed perspective projection matrix with [0,1] depth range.
pub fn perspective_lh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32, z_far: f32) -> Self {
glam_assert!(z_near > 0.0 && z_far > 0.0);
let (sin_fov, cos_fov) = scalar_sin_cos(0.5 * fov_y_radians);
let h = cos_fov / sin_fov;
let w = h / aspect_ratio;
let r = z_far / (z_far - z_near);
Mat4::from_cols(
Vec4::new(w, 0.0, 0.0, 0.0),
Vec4::new(0.0, h, 0.0, 0.0),
Vec4::new(0.0, 0.0, r, 1.0),
Vec4::new(0.0, 0.0, -r * z_near, 0.0),
)
}
/// Creates an infinite left-handed perspective projection matrix with [0,1] depth range.
pub fn perspective_infinite_lh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32) -> Self {
glam_assert!(z_near > 0.0);
let (sin_fov, cos_fov) = scalar_sin_cos(0.5 * fov_y_radians);
let h = cos_fov / sin_fov;
let w = h / aspect_ratio;
Mat4::from_cols(
Vec4::new(w, 0.0, 0.0, 0.0),
Vec4::new(0.0, h, 0.0, 0.0),
Vec4::new(0.0, 0.0, 1.0, 1.0),
Vec4::new(0.0, 0.0, -z_near, 0.0),
)
}
/// Creates an infinite left-handed perspective projection matrix with [0,1] depth range.
pub fn perspective_infinite_reverse_lh(
fov_y_radians: f32,
aspect_ratio: f32,
z_near: f32,
) -> Self {
glam_assert!(z_near > 0.0);
let (sin_fov, cos_fov) = scalar_sin_cos(0.5 * fov_y_radians);
let h = cos_fov / sin_fov;
let w = h / aspect_ratio;
Mat4::from_cols(
Vec4::new(w, 0.0, 0.0, 0.0),
Vec4::new(0.0, h, 0.0, 0.0),
Vec4::new(0.0, 0.0, 0.0, 1.0),
Vec4::new(0.0, 0.0, z_near, 0.0),
)
}
#[inline]
#[deprecated(since = "0.8.2", note = "please use `Mat4::perspective_rh_gl` instead")]
pub fn perspective_glu_rh(
fov_y_radians: f32,
aspect_ratio: f32,
z_near: f32,
z_far: f32,
) -> Self {
Mat4::perspective_rh_gl(fov_y_radians, aspect_ratio, z_near, z_far)
}
/// Creates an infinite right-handed perspective projection matrix with
/// [0,1] depth range.
pub fn perspective_infinite_rh(fov_y_radians: f32, aspect_ratio: f32, z_near: f32) -> Self {
let f = 1.0 / (0.5 * fov_y_radians).tan();
Mat4::from_cols(
Vec4::new(f / aspect_ratio, 0.0, 0.0, 0.0),
Vec4::new(0.0, f, 0.0, 0.0),
Vec4::new(0.0, 0.0, -1.0, -1.0),
Vec4::new(0.0, 0.0, -z_near, 0.0),
)
}
/// Creates an infinite reverse right-handed perspective projection matrix
/// with [0,1] depth range.
pub fn perspective_infinite_reverse_rh(
fov_y_radians: f32,
aspect_ratio: f32,
z_near: f32,
) -> Self {
let f = 1.0 / (0.5 * fov_y_radians).tan();
Mat4::from_cols(
Vec4::new(f / aspect_ratio, 0.0, 0.0, 0.0),
Vec4::new(0.0, f, 0.0, 0.0),
Vec4::new(0.0, 0.0, 0.0, -1.0),
Vec4::new(0.0, 0.0, z_near, 0.0),
)
}
/// Creates a right-handed orthographic projection matrix with [-1,1] depth
/// range. This is the same as the OpenGL `glOrtho` function in OpenGL.
/// See
/// https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml
pub fn orthographic_rh_gl(
left: f32,
right: f32,
bottom: f32,
top: f32,
near: f32,
far: f32,
) -> Self {
let a = 2.0 / (right - left);
let b = 2.0 / (top - bottom);
let c = -2.0 / (far - near);
let tx = -(right + left) / (right - left);
let ty = -(top + bottom) / (top - bottom);
let tz = -(far + near) / (far - near);
Mat4::from_cols(
Vec4::new(a, 0.0, 0.0, 0.0),
Vec4::new(0.0, b, 0.0, 0.0),
Vec4::new(0.0, 0.0, c, 0.0),
Vec4::new(tx, ty, tz, 1.0),
)
}
/// Creates a left-handed orthographic projection matrix with [0,1] depth range.
pub fn orthographic_lh(
left: f32,
right: f32,
bottom: f32,
top: f32,
near: f32,
far: f32,
) -> Self {
let rcp_width = 1.0 / (right - left);
let rcp_height = 1.0 / (top - bottom);
let r = 1.0 / (far - near);
Mat4::from_cols(
Vec4::new(rcp_width + rcp_width, 0.0, 0.0, 0.0),
Vec4::new(0.0, rcp_height + rcp_height, 0.0, 0.0),
Vec4::new(0.0, 0.0, r, 0.0),
Vec4::new(
-(left + right) * rcp_width,
-(top + bottom) * rcp_height,
-r * near,
1.0,
),
)
}
/// Creates a right-handed orthographic projection matrix with [0,1] depth range.
pub fn orthographic_rh(
left: f32,
right: f32,
bottom: f32,
top: f32,
near: f32,
far: f32,
) -> Self {
let rcp_width = 1.0 / (right - left);
let rcp_height = 1.0 / (top - bottom);
let r = 1.0 / (near - far);
Mat4::from_cols(
Vec4::new(rcp_width + rcp_width, 0.0, 0.0, 0.0),
Vec4::new(0.0, rcp_height + rcp_height, 0.0, 0.0),
Vec4::new(0.0, 0.0, r, 0.0),
Vec4::new(
-(left + right) * rcp_width,
-(top + bottom) * rcp_height,
r * near,
1.0,
),
)
}
#[inline]
pub fn mul_vec4(&self, other: Vec4) -> Vec4 {
let mut res = self.x_axis * other.dup_x();
res = self.y_axis.mul_add(other.dup_y(), res);
res = self.z_axis.mul_add(other.dup_z(), res);
res = self.w_axis.mul_add(other.dup_w(), res);
res
}
/// Multiplies two 4x4 matrices.
#[inline]
pub fn mul_mat4(&self, other: &Self) -> Self {
Self {
x_axis: self.mul_vec4(other.x_axis),
y_axis: self.mul_vec4(other.y_axis),
z_axis: self.mul_vec4(other.z_axis),
w_axis: self.mul_vec4(other.w_axis),
}
}
#[inline]
pub fn add_mat4(&self, other: &Self) -> Self {
Self {
x_axis: self.x_axis + other.x_axis,
y_axis: self.y_axis + other.y_axis,
z_axis: self.z_axis + other.z_axis,
w_axis: self.w_axis + other.w_axis,
}
}
#[inline]
pub fn sub_mat4(&self, other: &Self) -> Self {
Self {
x_axis: self.x_axis - other.x_axis,
y_axis: self.y_axis - other.y_axis,
z_axis: self.z_axis - other.z_axis,
w_axis: self.w_axis - other.w_axis,
}
}
#[inline]
pub fn mul_scalar(&self, other: f32) -> Self {
let s = Vec4::splat(other);
Self {
x_axis: self.x_axis * s,
y_axis: self.y_axis * s,
z_axis: self.z_axis * s,
w_axis: self.w_axis * s,
}
}
/// Transforms the given `Vec3` as 3D point.
/// This is the equivalent of multiplying the `Vec3` as a `Vec4` where `w`
/// is `1.0`.
#[inline]
pub fn transform_point3(&self, other: Vec3) -> Vec3 {
// TODO: optimized version below probably won't work for perspective projections
// let mut res = self.x_axis.truncate() * other.dup_x();
// res = self.y_axis.truncate().mul_add(other.dup_y(), res);
// res = self.z_axis.truncate().mul_add(other.dup_z(), res);
// // other w = 1
// res = self.w_axis.truncate() + res;
// res
self.mul_vec4(other.extend(1.0)).truncate()
}
/// Transforms the give `Vec3` as 3D vector.
/// This is the equivalent of multiplying the `Vec3` as a `Vec4` where `w`
/// is `0.0`.
#[inline]
pub fn transform_vector3(&self, other: Vec3) -> Vec3 {
// TODO: can optimize for w=0.
// TODO: optimized version below probably won't work for perspective projections
// let mut res = self.x_axis.truncate() * other.dup_x();
// res = self.y_axis.truncate().mul_add(other.dup_y(), res);
// res = self.z_axis.truncate().mul_add(other.dup_z(), res);
// // other w = 0
// res
self.mul_vec4(other.extend(0.0)).truncate()
}
/// Returns true if the absolute difference of all elements between `self`
/// and `other` is less than or equal to `max_abs_diff`.
///
/// This can be used to compare if two `Mat4`'s contain similar elements. It
/// works best when comparing with a known value. The `max_abs_diff` that
/// should be used used depends on the values being compared against.
///
/// For more on floating point comparisons see
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
#[inline]
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
self.x_axis.abs_diff_eq(other.x_axis, max_abs_diff)
&& self.y_axis.abs_diff_eq(other.y_axis, max_abs_diff)
&& self.z_axis.abs_diff_eq(other.z_axis, max_abs_diff)
&& self.w_axis.abs_diff_eq(other.w_axis, max_abs_diff)
}
}
impl AsRef<[f32; 16]> for Mat4 {
#[inline]
fn as_ref(&self) -> &[f32; 16] {
unsafe { &*(self as *const Self as *const [f32; 16]) }
}
}
impl AsMut<[f32; 16]> for Mat4 {
#[inline]
fn as_mut(&mut self) -> &mut [f32; 16] {
unsafe { &mut *(self as *mut Self as *mut [f32; 16]) }
}
}
impl Add<Mat4> for Mat4 {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
self.add_mat4(&other)
}
}
impl Sub<Mat4> for Mat4 {
type Output = Self;
#[inline]
fn sub(self, other: Self) -> Self {
self.sub_mat4(&other)
}
}
impl Mul<Mat4> for Mat4 {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
self.mul_mat4(&other)
}
}
impl Mul<Vec4> for Mat4 {
type Output = Vec4;
#[inline]
fn mul(self, other: Vec4) -> Vec4 {
self.mul_vec4(other)
}
}
impl Mul<Mat4> for f32 {
type Output = Mat4;
#[inline]
fn mul(self, other: Mat4) -> Mat4 {
other.mul_scalar(self)
}
}
impl Mul<f32> for Mat4 {
type Output = Self;
#[inline]
fn mul(self, other: f32) -> Self {
self.mul_scalar(other)
}
}

View file

@ -0,0 +1,47 @@
mod funcs;
mod mat2;
mod mat3;
mod mat4;
mod quat;
#[cfg(feature = "transform-types")]
mod transform;
mod vec2;
mod vec2_mask;
mod vec3;
mod vec3_mask;
mod vec4;
mod vec4_mask;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod x86_utils;
pub(crate) use funcs::{scalar_acos, scalar_sin_cos};
pub use mat2::*;
pub use mat3::*;
pub use mat4::*;
pub use quat::*;
#[cfg(feature = "transform-types")]
pub use transform::*;
pub use vec2::*;
pub use vec2_mask::*;
pub use vec3::*;
pub use vec3_mask::*;
pub use vec4::*;
pub use vec4_mask::*;
#[cfg(feature = "mint")]
mod glam_mint;
#[cfg(feature = "mint")]
pub use glam_mint::*;
#[cfg(feature = "rand")]
mod glam_rand;
#[cfg(feature = "rand")]
pub use glam_rand::*;
#[cfg(feature = "serde")]
mod glam_serde;
#[cfg(feature = "serde")]
pub use glam_serde::*;
mod glam_zerocopy;
pub use glam_zerocopy::*;

View file

@ -0,0 +1,670 @@
use super::{scalar_acos, scalar_sin_cos, Mat3, Mat4, Vec3, Vec4};
#[cfg(all(vec4sse2, target_arch = "x86",))]
use core::arch::x86::*;
#[cfg(all(vec4sse2, target_arch = "x86_64",))]
use core::arch::x86_64::*;
use core::{
cmp::Ordering,
fmt,
ops::{Mul, MulAssign, Neg},
};
/// A quaternion representing an orientation.
///
/// This quaternion is intended to be of unit length but may denormalize due to
/// floating point "error creep" which can occur when successive quaternion
/// operations are applied.
///
/// This type is 16 byte aligned.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Quat(pub(crate) Vec4);
#[inline]
pub fn quat(x: f32, y: f32, z: f32, w: f32) -> Quat {
Quat::from_xyzw(x, y, z, w)
}
impl Quat {
/// Creates a new rotation quaternion.
///
/// This should generally not be called manually unless you know what you are doing. Use one of
/// the other constructors instead such as `identity` or `from_axis_angle`.
///
/// `from_xyzw` is mostly used by unit tests and `serde` deserialization.
#[inline]
pub fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Self {
Self(Vec4::new(x, y, z, w))
}
#[inline]
pub fn identity() -> Self {
Self(Vec4::new(0.0, 0.0, 0.0, 1.0))
}
/// Creates a new rotation quaternion from an unaligned `&[f32]`.
///
/// # Preconditions
///
/// The resulting quaternion is expected to be of unit length.
///
/// # Panics
///
/// Panics if `slice` length is less than 4.
#[inline]
pub fn from_slice_unaligned(slice: &[f32]) -> Self {
let q = Self(Vec4::from_slice_unaligned(slice));
glam_assert!(q.is_normalized());
q
}
/// Writes the quaternion to an unaligned `&mut [f32]`.
///
/// # Panics
///
/// Panics if `slice` length is less than 4.
#[inline]
pub fn write_to_slice_unaligned(self, slice: &mut [f32]) {
self.0.write_to_slice_unaligned(slice)
}
/// Create a new quaterion for a normalized rotation axis and angle
/// (in radians).
#[inline]
pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
glam_assert!(axis.is_normalized());
let (s, c) = scalar_sin_cos(angle * 0.5);
Self((axis * s).extend(c))
}
/// Creates a new quaternion from the angle (in radians) around the x axis.
#[inline]
pub fn from_rotation_x(angle: f32) -> Self {
let (s, c) = scalar_sin_cos(angle * 0.5);
Self::from_xyzw(s, 0.0, 0.0, c)
}
/// Creates a new quaternion from the angle (in radians) around the y axis.
#[inline]
pub fn from_rotation_y(angle: f32) -> Self {
let (s, c) = scalar_sin_cos(angle * 0.5);
Self::from_xyzw(0.0, s, 0.0, c)
}
/// Creates a new quaternion from the angle (in radians) around the z axis.
#[inline]
pub fn from_rotation_z(angle: f32) -> Self {
let (s, c) = scalar_sin_cos(angle * 0.5);
Self::from_xyzw(0.0, 0.0, s, c)
}
#[inline]
/// Create a quaternion from the given yaw (around y), pitch (around x) and roll (around z)
/// in radians.
pub fn from_rotation_ypr(yaw: f32, pitch: f32, roll: f32) -> Self {
// Self::from_rotation_y(yaw) * Self::from_rotation_x(pitch) * Self::from_rotation_z(roll)
let (y0, w0) = scalar_sin_cos(yaw * 0.5);
let (x1, w1) = scalar_sin_cos(pitch * 0.5);
let (z2, w2) = scalar_sin_cos(roll * 0.5);
let x3 = w0 * x1;
let y3 = y0 * w1;
let z3 = -y0 * x1;
let w3 = w0 * w1;
let x4 = x3 * w2 + y3 * z2;
let y4 = -x3 * z2 + y3 * w2;
let z4 = w3 * z2 + z3 * w2;
let w4 = w3 * w2 - z3 * z2;
Self(Vec4::new(x4, y4, z4, w4))
}
#[inline]
fn from_rotation_axes(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3) -> Self {
// from DirectXMath XMQuaternionRotationMatrix
// TODO: sse2 version
let (m00, m01, m02) = x_axis.into();
let (m10, m11, m12) = y_axis.into();
let (m20, m21, m22) = z_axis.into();
if m22 <= 0.0 {
// x^2 + y^2 >= z^2 + w^2
let dif10 = m11 - m00;
let omm22 = 1.0 - m22;
if dif10 <= 0.0 {
// x^2 >= y^2
let four_xsq = omm22 - dif10;
let inv4x = 0.5 / four_xsq.sqrt();
Self::from_xyzw(
four_xsq * inv4x,
(m01 + m10) * inv4x,
(m02 + m20) * inv4x,
(m12 - m21) * inv4x,
)
} else {
// y^2 >= x^2
let four_ysq = omm22 + dif10;
let inv4y = 0.5 / four_ysq.sqrt();
Self::from_xyzw(
(m01 + m10) * inv4y,
four_ysq * inv4y,
(m12 + m21) * inv4y,
(m20 - m02) * inv4y,
)
}
} else {
// z^2 + w^2 >= x^2 + y^2
let sum10 = m11 + m00;
let opm22 = 1.0 + m22;
if sum10 <= 0.0 {
// z^2 >= w^2
let four_zsq = opm22 - sum10;
let inv4z = 0.5 / four_zsq.sqrt();
Self::from_xyzw(
(m02 + m20) * inv4z,
(m12 + m21) * inv4z,
four_zsq * inv4z,
(m01 - m10) * inv4z,
)
} else {
// w^2 >= z^2
let four_wsq = opm22 + sum10;
let inv4w = 0.5 / four_wsq.sqrt();
Self::from_xyzw(
(m12 - m21) * inv4w,
(m20 - m02) * inv4w,
(m01 - m10) * inv4w,
four_wsq * inv4w,
)
}
}
}
/// Creates a new quaternion from a 3x3 rotation matrix.
#[inline]
pub fn from_rotation_mat3(mat: &Mat3) -> Self {
Self::from_rotation_axes(mat.x_axis(), mat.y_axis(), mat.z_axis())
}
/// Creates a new quaternion from a 3x3 rotation matrix inside a homogeneous
/// 4x4 matrix.
#[inline]
pub fn from_rotation_mat4(mat: &Mat4) -> Self {
Self::from_rotation_axes(
mat.x_axis().truncate(),
mat.y_axis().truncate(),
mat.z_axis().truncate(),
)
}
/// Returns the rotation axis and angle of `self`.
#[inline]
pub fn to_axis_angle(self) -> (Vec3, f32) {
const EPSILON: f32 = 1.0e-8;
const EPSILON_SQUARED: f32 = EPSILON * EPSILON;
let (x, y, z, w) = self.0.into();
let angle = scalar_acos(w) * 2.0;
let scale_sq = (1.0 - w * w).max(0.0);
if scale_sq >= EPSILON_SQUARED {
(Vec3::new(x, y, z) / scale_sq.sqrt(), angle)
} else {
(Vec3::unit_x(), angle)
}
}
/// Returns the quaternion conjugate of `self`. For a unit quaternion the
/// conjugate is also the inverse.
#[inline]
pub fn conjugate(self) -> Self {
#[cfg(vec4sse2)]
unsafe {
Self(Vec4(_mm_xor_ps(
(self.0).0,
_mm_set_ps(0.0, -0.0, -0.0, -0.0),
)))
}
#[cfg(vec4f32)]
{
Self::from_xyzw(-(self.0).0, -(self.0).1, -(self.0).2, (self.0).3)
}
}
/// Computes the dot product of `self` and `other`. The dot product is
/// equal to the the cosine of the angle between two quaterion rotations.
#[inline]
pub fn dot(self, other: Self) -> f32 {
self.0.dot(other.0)
}
/// Computes the length of `self`.
#[inline]
pub fn length(self) -> f32 {
self.0.length()
}
/// Computes the squared length of `self`.
///
/// This is generally faster than `Quat::length()` as it avoids a square
/// root operation.
#[inline]
pub fn length_squared(self) -> f32 {
self.0.length_squared()
}
/// Computes `1.0 / Quat::length()`.
///
/// For valid results, `self` must _not_ be of length zero.
#[inline]
pub fn length_reciprocal(self) -> f32 {
1.0 / self.0.length()
}
/// Returns `self` normalized to length 1.0.
///
/// For valid results, `self` must _not_ be of length zero.
#[inline]
pub fn normalize(self) -> Self {
let inv_len = self.0.length_reciprocal();
Self(self.0.mul(inv_len))
}
/// Returns whether `self` of length `1.0` or not.
///
/// Uses a precision threshold of `1e-6`.
#[inline]
pub fn is_normalized(self) -> bool {
is_normalized!(self)
}
#[inline]
pub fn is_near_identity(self) -> bool {
// from rtm quat_near_identity
const THRESHOLD_ANGLE: f32 = 0.002_847_144_6;
// Because of floating point precision, we cannot represent very small rotations.
// The closest f32 to 1.0 that is not 1.0 itself yields:
// 0.99999994.acos() * 2.0 = 0.000690533954 rad
//
// An error threshold of 1.e-6 is used by default.
// (1.0 - 1.e-6).acos() * 2.0 = 0.00284714461 rad
// (1.0 - 1.e-7).acos() * 2.0 = 0.00097656250 rad
//
// We don't really care about the angle value itself, only if it's close to 0.
// This will happen whenever quat.w is close to 1.0.
// If the quat.w is close to -1.0, the angle will be near 2*PI which is close to
// a negative 0 rotation. By forcing quat.w to be positive, we'll end up with
// the shortest path.
let positive_w_angle = scalar_acos(self.0.w().abs()) * 2.0;
positive_w_angle < THRESHOLD_ANGLE
}
/// Returns true if the absolute difference of all elements between `self`
/// and `other` is less than or equal to `max_abs_diff`.
///
/// This can be used to compare if two `Quat`'s contain similar elements. It
/// works best when comparing with a known value. The `max_abs_diff` that
/// should be used used depends on the values being compared against.
///
/// For more on floating point comparisons see
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
#[inline]
pub fn abs_diff_eq(self, other: Self, max_abs_diff: f32) -> bool {
self.0.abs_diff_eq(other.0, max_abs_diff)
}
/// Performs a linear interpolation between `self` and `other` based on
/// the value `s`.
///
/// When `s` is `0.0`, the result will be equal to `self`. When `s`
/// is `1.0`, the result will be equal to `other`.
#[inline]
pub fn lerp(self, end: Self, s: f32) -> Self {
glam_assert!(self.is_normalized());
glam_assert!(end.is_normalized());
#[cfg(vec4sse2)]
unsafe {
let start = self.0;
let end = end.0;
let dot = start.dot_as_vec4(end);
// Calculate the bias, if the dot product is positive or zero, there is no bias
// but if it is negative, we want to flip the 'end' rotation XYZW components
let bias = _mm_and_ps(dot.into(), _mm_set_ps1(-0.0));
let interpolated = Vec4(_mm_add_ps(
_mm_mul_ps(
_mm_sub_ps(_mm_xor_ps(end.into(), bias), start.0),
_mm_set_ps1(s),
),
start.0,
));
Self(interpolated.normalize())
}
#[cfg(vec4f32)]
{
let start = self.0;
let end = end.0;
let dot = start.dot(end);
let bias = if dot >= 0.0 { 1.0 } else { -1.0 };
let interpolated = start + (s * ((end * bias) - start));
Self(interpolated.normalize())
}
}
/// Performs a spherical linear interpolation between `self` and `end`
/// based on the value `s`.
///
/// When `s` is `0.0`, the result will be equal to `self`. When `s`
/// is `1.0`, the result will be equal to `end`.
///
/// Note that a rotation can be represented by two quaternions: `q` and
/// `-q`. The slerp path between `q` and `end` will be different from the
/// path between `-q` and `end`. One path will take the long way around and
/// one will take the short way. In order to correct for this, the `dot`
/// product between `self` and `end` should be positive. If the `dot`
/// product is negative, slerp between `-self` and `end`.
#[inline]
pub fn slerp(self, end: Self, s: f32) -> Self {
// http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
glam_assert!(self.is_normalized());
glam_assert!(end.is_normalized());
const DOT_THRESHOLD: f32 = 0.9995;
let dot = self.dot(end);
if dot > DOT_THRESHOLD {
// assumes lerp returns a normalized quaternion
self.lerp(end, s)
} else {
#[cfg(vec4f32)]
{
// assumes scalar_acos clamps the input to [-1.0, 1.0]
let theta = crate::f32::funcs::scalar_acos(dot);
let scale1 = f32::sin(theta * (1.0 - s));
let scale2 = f32::sin(theta * s);
let theta_sin = f32::sin(theta);
Quat((self.0 * scale1 + end.0 * scale2) * theta_sin.recip())
}
#[cfg(vec4sse2)]
{
// assumes scalar_acos clamps the input to [-1.0, 1.0]
let theta = crate::f32::funcs::scalar_acos(dot);
let x = 1.0 - s;
let y = s;
let z = 1.0;
unsafe {
let tmp = Vec4::splat(theta) * Vec4::new(x, y, z, 0.0);
let tmp = crate::f32::funcs::sse2::m128_sin(tmp.0);
let scale1 = _mm_shuffle_ps(tmp, tmp, 0b00_00_00_00);
let scale2 = _mm_shuffle_ps(tmp, tmp, 0b01_01_01_01);
let theta_sin = _mm_shuffle_ps(tmp, tmp, 0b10_10_10_10);
let theta_sin_recip = Vec4(_mm_rcp_ps(theta_sin));
Quat((self.0 * Vec4(scale1) + end.0 * Vec4(scale2)) * theta_sin_recip)
}
}
}
}
#[inline]
/// Multiplies a quaternion and a 3D vector, rotating it.
pub fn mul_vec3(self, other: Vec3) -> Vec3 {
glam_assert!(self.is_normalized());
#[cfg(vec4sse2)]
{
let w = self.0.dup_w().truncate();
let two = Vec3::splat(2.0);
let b = self.0.truncate();
let b2 = b.dot_as_vec3(b);
other * (w * w - b2) + b * (other.dot_as_vec3(b) * two) + b.cross(other) * (w * two)
}
#[cfg(vec4f32)]
{
let w = self.0.w();
let b = self.0.truncate();
let b2 = b.dot(b);
other * (w * w - b2) + b * (other.dot(b) * 2.0) + b.cross(other) * (w * 2.0)
}
}
#[inline]
/// Multiplies two quaternions.
/// Note that due to floating point rounding the result may not be perfectly normalized.
pub fn mul_quat(self, other: Self) -> Self {
glam_assert!(self.is_normalized());
glam_assert!(other.is_normalized());
#[cfg(vec4sse2)]
unsafe {
// from rtm quat_mul
let lhs = self.0.into();
let rhs = other.0.into();
let control_wzyx = _mm_set_ps(-1.0, 1.0, -1.0, 1.0);
let control_zwxy = _mm_set_ps(-1.0, -1.0, 1.0, 1.0);
let control_yxwz = _mm_set_ps(-1.0, 1.0, 1.0, -1.0);
let r_xxxx = _mm_shuffle_ps(lhs, lhs, 0b00_00_00_00);
let r_yyyy = _mm_shuffle_ps(lhs, lhs, 0b01_01_01_01);
let r_zzzz = _mm_shuffle_ps(lhs, lhs, 0b10_10_10_10);
let r_wwww = _mm_shuffle_ps(lhs, lhs, 0b11_11_11_11);
let lxrw_lyrw_lzrw_lwrw = _mm_mul_ps(r_wwww, rhs);
let l_wzyx = _mm_shuffle_ps(rhs, rhs, 0b00_01_10_11);
let lwrx_lzrx_lyrx_lxrx = _mm_mul_ps(r_xxxx, l_wzyx);
let l_zwxy = _mm_shuffle_ps(l_wzyx, l_wzyx, 0b10_11_00_01);
let lwrx_nlzrx_lyrx_nlxrx = _mm_mul_ps(lwrx_lzrx_lyrx_lxrx, control_wzyx);
let lzry_lwry_lxry_lyry = _mm_mul_ps(r_yyyy, l_zwxy);
let l_yxwz = _mm_shuffle_ps(l_zwxy, l_zwxy, 0b00_01_10_11);
let lzry_lwry_nlxry_nlyry = _mm_mul_ps(lzry_lwry_lxry_lyry, control_zwxy);
let lyrz_lxrz_lwrz_lzrz = _mm_mul_ps(r_zzzz, l_yxwz);
let result0 = _mm_add_ps(lxrw_lyrw_lzrw_lwrw, lwrx_nlzrx_lyrx_nlxrx);
let nlyrz_lxrz_lwrz_wlzrz = _mm_mul_ps(lyrz_lxrz_lwrz_lzrz, control_yxwz);
let result1 = _mm_add_ps(lzry_lwry_nlxry_nlyry, nlyrz_lxrz_lwrz_wlzrz);
Self(Vec4(_mm_add_ps(result0, result1)))
}
#[cfg(vec4f32)]
{
let (x0, y0, z0, w0) = self.0.into();
let (x1, y1, z1, w1) = other.0.into();
Self::from_xyzw(
w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1,
w0 * y1 - x0 * z1 + y0 * w1 + z0 * x1,
w0 * z1 + x0 * y1 - y0 * x1 + z0 * w1,
w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1,
)
}
}
/// Returns element `x`.
#[inline]
pub fn x(self) -> f32 {
self.0.x()
}
/// Returns element `y`.
#[inline]
pub fn y(self) -> f32 {
self.0.y()
}
/// Returns element `z`.
#[inline]
pub fn z(self) -> f32 {
self.0.z()
}
/// Returns element `w`.
#[inline]
pub fn w(self) -> f32 {
self.0.w()
}
}
impl fmt::Debug for Quat {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
#[cfg(vec4sse2)]
{
fmt.debug_tuple("Quat").field(&(self.0).0).finish()
}
#[cfg(vec4f32)]
{
fmt.debug_tuple("Quat")
.field(&self.0.x())
.field(&self.0.y())
.field(&self.0.z())
.field(&self.0.w())
.finish()
}
}
}
impl fmt::Display for Quat {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let (x, y, z, w) = self.0.into();
write!(fmt, "[{}, {}, {}, {}]", x, y, z, w)
}
}
impl Mul<Quat> for Quat {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
self.mul_quat(other)
}
}
impl MulAssign<Quat> for Quat {
#[inline]
fn mul_assign(&mut self, other: Self) {
*self = self.mul_quat(other);
}
}
impl Mul<Vec3> for Quat {
type Output = Vec3;
#[inline]
fn mul(self, other: Vec3) -> Vec3 {
self.mul_vec3(other)
}
}
impl Neg for Quat {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self(-1.0 * self.0)
}
}
impl Default for Quat {
#[inline]
fn default() -> Self {
Self::identity()
}
}
impl PartialEq for Quat {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.0.cmpeq(other.0).all()
}
}
impl PartialOrd for Quat {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.as_ref().partial_cmp(other.as_ref())
}
}
impl AsRef<[f32; 4]> for Quat {
#[inline]
fn as_ref(&self) -> &[f32; 4] {
self.0.as_ref()
}
}
impl AsMut<[f32; 4]> for Quat {
#[inline]
fn as_mut(&mut self) -> &mut [f32; 4] {
self.0.as_mut()
}
}
impl From<Vec4> for Quat {
#[inline]
fn from(v: Vec4) -> Self {
Self(v)
}
}
impl From<Quat> for Vec4 {
#[inline]
fn from(q: Quat) -> Self {
q.0
}
}
impl From<(f32, f32, f32, f32)> for Quat {
#[inline]
fn from(t: (f32, f32, f32, f32)) -> Self {
Quat::from_xyzw(t.0, t.1, t.2, t.3)
}
}
impl From<Quat> for (f32, f32, f32, f32) {
#[inline]
fn from(q: Quat) -> Self {
q.0.into()
}
}
impl From<[f32; 4]> for Quat {
#[inline]
fn from(a: [f32; 4]) -> Self {
Self(a.into())
}
}
impl From<Quat> for [f32; 4] {
#[inline]
fn from(q: Quat) -> Self {
q.0.into()
}
}
#[cfg(vec4sse2)]
impl From<Quat> for __m128 {
// TODO: write test
#[cfg_attr(tarpaulin, skip)]
#[inline]
fn from(q: Quat) -> Self {
(q.0).0
}
}
#[cfg(vec4sse2)]
impl From<__m128> for Quat {
#[inline]
fn from(t: __m128) -> Self {
Self(Vec4(t))
}
}

View file

@ -0,0 +1,361 @@
use super::{Mat4, Quat, Vec3};
use core::ops::Mul;
#[cfg(feature = "rand")]
use rand::{
distributions::{Distribution, Standard},
Rng,
};
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[repr(C)]
pub struct TransformSRT {
pub scale: Vec3,
pub rotation: Quat,
pub translation: Vec3,
}
impl Default for TransformSRT {
#[inline]
fn default() -> Self {
Self {
scale: Vec3::one(),
rotation: Quat::identity(),
translation: Vec3::zero(),
}
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
#[repr(C)]
pub struct TransformRT {
pub rotation: Quat,
pub translation: Vec3,
}
impl Default for TransformRT {
#[inline]
fn default() -> Self {
Self {
rotation: Quat::identity(),
translation: Vec3::zero(),
}
}
}
impl TransformSRT {
#[inline]
pub fn from_scale_rotation_translation(scale: Vec3, rotation: Quat, translation: Vec3) -> Self {
Self {
scale,
rotation,
translation,
}
}
#[inline]
pub fn from_transform_rt(scale: Vec3, rt: &TransformRT) -> Self {
Self {
scale,
rotation: rt.rotation,
translation: rt.translation,
}
}
#[inline]
pub fn identity() -> Self {
Self {
scale: Vec3::one(),
rotation: Quat::identity(),
translation: Vec3::zero(),
}
}
#[inline]
pub fn inverse(&self) -> Self {
let scale = self.scale.reciprocal();
let rotation = self.rotation.conjugate();
let translation = -(rotation * (self.translation * scale));
Self {
scale,
rotation,
translation,
}
}
#[inline]
pub fn normalize(&self) -> Self {
let rotation = self.rotation.normalize();
Self {
scale: self.scale,
rotation,
translation: self.translation,
}
}
#[inline]
pub fn mul_transform(&self, other: &Self) -> Self {
mul_srt_srt(self, other)
}
#[inline]
pub fn transform_vec3(self, other: Vec3) -> Vec3 {
(self.rotation * (other * self.scale)) + self.translation
}
/// Returns true if the absolute difference of all elements between `self`
/// and `other` is less than or equal to `max_abs_diff`.
///
/// This can be used to compare if two `Mat4`'s contain similar elements. It
/// works best when comparing with a known value. The `max_abs_diff` that
/// should be used used depends on the values being compared against.
///
/// For more on floating point comparisons see
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
#[inline]
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
self.scale.abs_diff_eq(other.scale, max_abs_diff)
&& self.rotation.abs_diff_eq(other.rotation, max_abs_diff)
&& self
.translation
.abs_diff_eq(other.translation, max_abs_diff)
}
}
#[inline]
fn mul_srt_srt(lhs: &TransformSRT, rhs: &TransformSRT) -> TransformSRT {
// from rtm qvv_mul
let min_scale = lhs.scale.min(rhs.scale);
let scale = lhs.scale * rhs.scale;
if min_scale.cmplt(Vec3::zero()).any() {
// If negative scale, we go through a matrix
let lhs_mtx =
Mat4::from_scale_rotation_translation(lhs.scale, lhs.rotation, lhs.translation);
let rhs_mtx =
Mat4::from_scale_rotation_translation(rhs.scale, rhs.rotation, rhs.translation);
let mut result_mtx = lhs_mtx * rhs_mtx;
let sign = scale.sign();
result_mtx
.set_x_axis((result_mtx.x_axis().truncate().normalize() * sign.dup_x()).extend(0.0));
result_mtx
.set_y_axis((result_mtx.y_axis().truncate().normalize() * sign.dup_y()).extend(0.0));
result_mtx
.set_z_axis((result_mtx.z_axis().truncate().normalize() * sign.dup_z()).extend(0.0));
let rotation = Quat::from_rotation_mat4(&result_mtx);
let translation = result_mtx.w_axis().truncate();
TransformSRT {
scale,
rotation,
translation,
}
} else {
let rotation = lhs.rotation * rhs.rotation;
let translation = (rhs.rotation * (lhs.translation * rhs.scale)) + rhs.translation;
TransformSRT {
scale,
rotation,
translation,
}
}
}
#[inline]
fn mul_rt_rt(lhs: &TransformRT, rhs: &TransformRT) -> TransformRT {
let rotation = lhs.rotation * rhs.rotation;
let translation = (rhs.rotation * lhs.translation) + rhs.translation;
TransformRT {
rotation,
translation,
}
}
impl TransformRT {
#[inline]
pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
Self {
rotation,
translation,
}
}
#[inline]
pub fn identity() -> Self {
Self {
rotation: Quat::identity(),
translation: Vec3::zero(),
}
}
#[inline]
pub fn inverse(&self) -> Self {
let rotation = self.rotation.conjugate();
let translation = -(rotation * self.translation);
Self {
rotation,
translation,
}
}
#[inline]
pub fn normalize(&self) -> Self {
let rotation = self.rotation.normalize();
Self {
rotation,
translation: self.translation,
}
}
#[inline]
pub fn mul_transform(&self, other: &Self) -> Self {
mul_rt_rt(self, other)
}
#[inline]
pub fn transform_vec3(self, other: Vec3) -> Vec3 {
(self.rotation * other) + self.translation
}
/// Returns true if the absolute difference of all elements between `self`
/// and `other` is less than or equal to `max_abs_diff`.
///
/// This can be used to compare if two `Mat4`'s contain similar elements. It
/// works best when comparing with a known value. The `max_abs_diff` that
/// should be used used depends on the values being compared against.
///
/// For more on floating point comparisons see
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
#[inline]
pub fn abs_diff_eq(&self, other: Self, max_abs_diff: f32) -> bool {
self.rotation.abs_diff_eq(other.rotation, max_abs_diff)
&& self
.translation
.abs_diff_eq(other.translation, max_abs_diff)
}
}
impl AsRef<TransformRT> for TransformSRT {
#[inline]
fn as_ref(&self) -> &TransformRT {
unsafe { &*(self as *const Self as *const TransformRT) }
}
}
impl AsMut<TransformRT> for TransformSRT {
#[inline]
fn as_mut(&mut self) -> &mut TransformRT {
unsafe { &mut *(self as *mut Self as *mut TransformRT) }
}
}
impl Mul<Vec3> for TransformRT {
type Output = Vec3;
#[inline]
fn mul(self, other: Vec3) -> Vec3 {
self.transform_vec3(other)
}
}
impl Mul<Vec3> for TransformSRT {
type Output = Vec3;
#[inline]
fn mul(self, other: Vec3) -> Vec3 {
self.transform_vec3(other)
}
}
impl Mul<TransformRT> for TransformRT {
type Output = TransformRT;
#[inline]
fn mul(self, other: TransformRT) -> TransformRT {
mul_rt_rt(&self, &other)
}
}
impl Mul<TransformSRT> for TransformSRT {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self::Output {
mul_srt_srt(&self, &other)
}
}
impl Mul<TransformRT> for TransformSRT {
type Output = TransformSRT;
#[inline]
fn mul(self, other: TransformRT) -> Self::Output {
mul_srt_srt(&self, &other.into())
}
}
impl Mul<TransformSRT> for TransformRT {
type Output = TransformSRT;
#[inline]
fn mul(self, other: TransformSRT) -> Self::Output {
mul_srt_srt(&self.into(), &other)
}
}
impl From<TransformRT> for TransformSRT {
#[inline]
fn from(tr: TransformRT) -> Self {
Self {
translation: tr.translation,
rotation: tr.rotation,
scale: Vec3::one(),
}
}
}
#[cfg(feature = "rand")]
impl Distribution<TransformRT> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> TransformRT {
TransformRT::from_rotation_translation(
rng.gen::<Quat>(),
Vec3::new(
rng.gen_range(core::f32::MIN, core::f32::MAX),
rng.gen_range(core::f32::MIN, core::f32::MAX),
rng.gen_range(core::f32::MIN, core::f32::MAX),
),
)
}
}
#[cfg(feature = "rand")]
impl Distribution<TransformSRT> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> TransformSRT {
let mut gen_non_zero = || loop {
let f: f32 = rng.gen_range(core::f32::MIN, core::f32::MAX);
if f.abs() > core::f32::MIN_POSITIVE {
return f;
}
};
TransformSRT::from_scale_rotation_translation(
Vec3::new(gen_non_zero(), gen_non_zero(), gen_non_zero()),
rng.gen::<Quat>(),
Vec3::new(
rng.gen_range(core::f32::MIN, core::f32::MAX),
rng.gen_range(core::f32::MIN, core::f32::MAX),
rng.gen_range(core::f32::MIN, core::f32::MAX),
),
)
}
}
impl From<TransformSRT> for Mat4 {
#[inline]
fn from(srt: TransformSRT) -> Self {
Mat4::from_scale_rotation_translation(srt.scale, srt.rotation, srt.translation)
}
}
impl From<TransformRT> for Mat4 {
#[inline]
fn from(rt: TransformRT) -> Self {
Mat4::from_rotation_translation(rt.rotation, rt.translation)
}
}

View file

@ -0,0 +1,509 @@
use crate::f32::{Vec2Mask, Vec3};
use core::{f32, fmt, ops::*};
/// A 2-dimensional vector.
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug, Default)]
#[repr(C)]
pub struct Vec2(pub(crate) f32, pub(crate) f32);
#[inline]
pub fn vec2(x: f32, y: f32) -> Vec2 {
Vec2(x, y)
}
impl Vec2 {
/// Returns a new `Vec4` with elements representing the sign of `self`.
///
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
#[inline]
pub fn sign(self) -> Self {
let mask = self.cmpge(Self::zero());
mask.select(Self::splat(1.0), Self::splat(-1.0))
}
/// Computes the reciprocal `1.0/n` of each element, returning the
/// results in a new `Vec2`.
#[inline]
pub fn reciprocal(self) -> Self {
Self::one() / self
}
/// Performs a linear interpolation between `self` and `other` based on
/// the value `s`.
///
/// When `s` is `0.0`, the result will be equal to `self`. When `s`
/// is `1.0`, the result will be equal to `other`.
#[inline]
pub fn lerp(self, other: Self, s: f32) -> Self {
self + ((other - self) * s)
}
/// Returns whether `self` is length `1.0` or not.
///
/// Uses a precision threshold of `1e-6`.
#[inline]
pub fn is_normalized(self) -> bool {
is_normalized!(self)
}
/// Returns true if the absolute difference of all elements between `self`
/// and `other` is less than or equal to `max_abs_diff`.
///
/// This can be used to compare if two `Vec2`'s contain similar elements. It
/// works best when comparing with a known value. The `max_abs_diff` that
/// should be used used depends on the values being compared against.
///
/// For more on floating point comparisons see
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
#[inline]
pub fn abs_diff_eq(self, other: Self, max_abs_diff: f32) -> bool {
abs_diff_eq!(self, other, max_abs_diff)
}
/// Creates a new `Vec2`.
#[inline]
pub fn new(x: f32, y: f32) -> Vec2 {
Vec2(x, y)
}
/// Creates a new `Vec2` with all elements set to `0.0`.
#[inline]
pub fn zero() -> Vec2 {
Vec2(0.0, 0.0)
}
/// Creates a new `Vec2` with all elements set to `1.0`.
#[inline]
pub fn one() -> Vec2 {
Vec2(1.0, 1.0)
}
/// Creates a new `Vec2` with values `[x: 1.0, y: 0.0]`.
#[inline]
pub fn unit_x() -> Vec2 {
Vec2(1.0, 0.0)
}
/// Creates a new `Vec2` with values `[x: 0.0, y: 1.0]`.
#[inline]
pub fn unit_y() -> Vec2 {
Vec2(0.0, 1.0)
}
/// Creates a new `Vec2` with all elements set to `v`.
#[inline]
pub fn splat(v: f32) -> Vec2 {
Vec2(v, v)
}
/// Creates a new `Vec3` from `self` and the given `z` value.
#[inline]
pub fn extend(self, z: f32) -> Vec3 {
Vec3::new(self.0, self.1, z)
}
/// Returns element `x`.
#[inline]
pub fn x(self) -> f32 {
self.0
}
/// Returns element `y`.
#[inline]
pub fn y(self) -> f32 {
self.1
}
/// Returns a mutable reference to element `x`.
#[inline]
pub fn x_mut(&mut self) -> &mut f32 {
&mut self.0
}
/// Returns a mutable reference to element `y`.
#[inline]
pub fn y_mut(&mut self) -> &mut f32 {
&mut self.1
}
/// Sets element `x`.
#[inline]
pub fn set_x(&mut self, x: f32) {
self.0 = x;
}
/// Sets element `y`.
#[inline]
pub fn set_y(&mut self, y: f32) {
self.1 = y;
}
/// Computes the dot product of `self` and `other`.
#[inline]
pub fn dot(self, other: Vec2) -> f32 {
(self.0 * other.0) + (self.1 * other.1)
}
/// Computes the length of `self`.
#[inline]
pub fn length(self) -> f32 {
self.dot(self).sqrt()
}
/// Computes the squared length of `self`.
///
/// This is generally faster than `Vec2::length()` as it avoids a square
/// root operation.
#[inline]
pub fn length_squared(self) -> f32 {
self.dot(self)
}
/// Computes `1.0 / Vec2::length()`.
///
/// For valid results, `self` must _not_ be of length zero.
#[inline]
pub fn length_reciprocal(self) -> f32 {
1.0 / self.length()
}
/// Returns `self` normalized to length 1.0.
///
/// For valid results, `self` must _not_ be of length zero.
#[inline]
pub fn normalize(self) -> Vec2 {
self * self.length_reciprocal()
}
/// Returns the vertical minimum of `self` and `other`.
///
/// In other words, this computes
/// `[x: min(x1, x2), y: min(y1, y2)]`,
/// taking the minimum of each element individually.
#[inline]
pub fn min(self, other: Vec2) -> Vec2 {
Vec2(self.0.min(other.0), self.1.min(other.1))
}
/// Returns the vertical maximum of `self` and `other`.
///
/// In other words, this computes
/// `[x: max(x1, x2), y: max(y1, y2)]`,
/// taking the maximum of each element individually.
#[inline]
pub fn max(self, other: Vec2) -> Vec2 {
Vec2(self.0.max(other.0), self.1.max(other.1))
}
/// Returns the horizontal minimum of `self`'s elements.
///
/// In other words, this computes `min(x, y)`.
#[inline]
pub fn min_element(self) -> f32 {
self.0.min(self.1)
}
/// Returns the horizontal maximum of `self`'s elements.
///
/// In other words, this computes `max(x, y)`.
#[inline]
pub fn max_element(self) -> f32 {
self.0.max(self.1)
}
/// Performs a vertical `==` comparison between `self` and `other`,
/// returning a `Vec2Mask` of the results.
///
/// In other words, this computes `[x1 == x2, y1 == y2, z1 == z2, w1 == w2]`.
#[inline]
pub fn cmpeq(self, other: Vec2) -> Vec2Mask {
Vec2Mask::new(self.0.eq(&other.0), self.1.eq(&other.1))
}
/// Performs a vertical `!=` comparison between `self` and `other`,
/// returning a `Vec2Mask` of the results.
///
/// In other words, this computes `[x1 != x2, y1 != y2, z1 != z2, w1 != w2]`.
#[inline]
pub fn cmpne(self, other: Vec2) -> Vec2Mask {
Vec2Mask::new(self.0.ne(&other.0), self.1.ne(&other.1))
}
/// Performs a vertical `>=` comparison between `self` and `other`,
/// returning a `Vec2Mask` of the results.
///
/// In other words, this computes `[x1 >= x2, y1 >= y2, z1 >= z2, w1 >= w2]`.
#[inline]
pub fn cmpge(self, other: Vec2) -> Vec2Mask {
Vec2Mask::new(self.0.ge(&other.0), self.1.ge(&other.1))
}
/// Performs a vertical `>` comparison between `self` and `other`,
/// returning a `Vec2Mask` of the results.
///
/// In other words, this computes `[x1 > x2, y1 > y2, z1 > z2, w1 > w2]`.
#[inline]
pub fn cmpgt(self, other: Vec2) -> Vec2Mask {
Vec2Mask::new(self.0.gt(&other.0), self.1.gt(&other.1))
}
/// Performs a vertical `<=` comparison between `self` and `other`,
/// returning a `Vec2Mask` of the results.
///
/// In other words, this computes `[x1 <= x2, y1 <= y2, z1 <= z2, w1 <= w2]`.
#[inline]
pub fn cmple(self, other: Vec2) -> Vec2Mask {
Vec2Mask::new(self.0.le(&other.0), self.1.le(&other.1))
}
/// Performs a vertical `<` comparison between `self` and `other`,
/// returning a `Vec2Mask` of the results.
///
/// In other words, this computes `[x1 < x2, y1 < y2, z1 < z2, w1 < w2]`.
#[inline]
pub fn cmplt(self, other: Vec2) -> Vec2Mask {
Vec2Mask::new(self.0.lt(&other.0), self.1.lt(&other.1))
}
/// Creates a new `Vec2` from the first two values in `slice`.
///
/// # Panics
///
/// Panics if `slice` is less than two elements long.
#[inline]
pub fn from_slice_unaligned(slice: &[f32]) -> Self {
Self(slice[0], slice[1])
}
/// Writes the elements of `self` to the first two elements in `slice`.
///
/// # Panics
///
/// Panics if `slice` is less than two elements long.
#[inline]
pub fn write_to_slice_unaligned(self, slice: &mut [f32]) {
slice[0] = self.0;
slice[1] = self.1;
}
/// Returns a new `Vec2` containing the absolute value of each element of the original
/// `Vec2`.
#[inline]
pub fn abs(self) -> Self {
Self(self.0.abs(), self.1.abs())
}
#[inline]
pub fn round(self) -> Self {
Self(self.0.round(), self.1.round())
}
#[inline]
pub fn floor(self) -> Self {
Self(self.0.floor(), self.1.floor())
}
#[inline]
pub fn ceil(self) -> Self {
Self(self.0.ceil(), self.1.ceil())
}
/// The perpendicular dot product of the vector and `other`.
#[inline]
pub fn perp_dot(self, other: Vec2) -> f32 {
(self.0 * other.1) - (self.1 * other.0)
}
/// Returns the angle between two vectors, in radians.
///
/// The vectors do not need to be unit length, but this function does
/// perform a `sqrt`.
#[inline]
pub fn angle_between(self, other: Self) -> f32 {
let angle = crate::f32::funcs::scalar_acos(
self.dot(other) / (self.dot(self) * other.dot(other)).sqrt(),
);
if self.perp_dot(other) < 0.0 {
-angle
} else {
angle
}
}
}
impl fmt::Display for Vec2 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}, {}]", self.0, self.1)
}
}
impl Div<Vec2> for Vec2 {
type Output = Self;
#[inline]
fn div(self, other: Vec2) -> Self {
Self(self.0 / other.0, self.1 / other.1)
}
}
impl DivAssign<Vec2> for Vec2 {
#[inline]
fn div_assign(&mut self, other: Vec2) {
self.0 /= other.0;
self.1 /= other.1;
}
}
impl Div<f32> for Vec2 {
type Output = Self;
#[inline]
fn div(self, other: f32) -> Self {
Self(self.0 / other, self.1 / other)
}
}
impl DivAssign<f32> for Vec2 {
#[inline]
fn div_assign(&mut self, other: f32) {
self.0 /= other;
self.1 /= other;
}
}
impl Mul<Vec2> for Vec2 {
type Output = Self;
#[inline]
fn mul(self, other: Vec2) -> Self {
Self(self.0 * other.0, self.1 * other.1)
}
}
impl MulAssign<Vec2> for Vec2 {
#[inline]
fn mul_assign(&mut self, other: Vec2) {
self.0 *= other.0;
self.1 *= other.1;
}
}
impl Mul<f32> for Vec2 {
type Output = Self;
#[inline]
fn mul(self, other: f32) -> Self {
Self(self.0 * other, self.1 * other)
}
}
impl MulAssign<f32> for Vec2 {
#[inline]
fn mul_assign(&mut self, other: f32) {
self.0 *= other;
self.1 *= other;
}
}
impl Mul<Vec2> for f32 {
type Output = Vec2;
#[inline]
fn mul(self, other: Vec2) -> Vec2 {
Vec2(self * other.0, self * other.1)
}
}
impl Add for Vec2 {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
Self(self.0 + other.0, self.1 + other.1)
}
}
impl AddAssign for Vec2 {
#[inline]
fn add_assign(&mut self, other: Self) {
self.0 += other.0;
self.1 += other.1;
}
}
impl Sub for Vec2 {
type Output = Self;
#[inline]
fn sub(self, other: Vec2) -> Self {
Self(self.0 - other.0, self.1 - other.1)
}
}
impl SubAssign for Vec2 {
#[inline]
fn sub_assign(&mut self, other: Vec2) {
self.0 -= other.0;
self.1 -= other.1;
}
}
impl Neg for Vec2 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self(-self.0, -self.1)
}
}
impl AsRef<[f32; 2]> for Vec2 {
#[inline]
fn as_ref(&self) -> &[f32; 2] {
unsafe { &*(self as *const Vec2 as *const [f32; 2]) }
}
}
impl AsMut<[f32; 2]> for Vec2 {
#[inline]
fn as_mut(&mut self) -> &mut [f32; 2] {
unsafe { &mut *(self as *mut Vec2 as *mut [f32; 2]) }
}
}
impl Index<usize> for Vec2 {
type Output = f32;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.as_ref()[index]
}
}
impl IndexMut<usize> for Vec2 {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.as_mut()[index]
}
}
impl From<(f32, f32)> for Vec2 {
#[inline]
fn from(t: (f32, f32)) -> Self {
Self(t.0, t.1)
}
}
impl From<Vec2> for (f32, f32) {
#[inline]
fn from(v: Vec2) -> Self {
(v.0, v.1)
}
}
impl From<[f32; 2]> for Vec2 {
#[inline]
fn from(a: [f32; 2]) -> Self {
Self(a[0], a[1])
}
}
impl From<Vec2> for [f32; 2] {
#[inline]
fn from(v: Vec2) -> Self {
[v.0, v.1]
}
}

View file

@ -0,0 +1,126 @@
use super::Vec2;
use core::{fmt, ops::*};
/// A 2-dimensional vector mask.
///
/// This type is typically created by comparison methods on `Vec2`.
#[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[repr(C)]
pub struct Vec2Mask(u32, u32);
impl Vec2Mask {
/// Creates a new `Vec2Mask`.
#[inline]
pub fn new(x: bool, y: bool) -> Self {
const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
Self(MASK[x as usize], MASK[y as usize])
}
/// Returns a bitmask with the lowest two bits set from the elements of
/// the `Vec2Mask`.
///
/// A true element results in a `1` bit and a false element in a `0` bit.
/// Element `x` goes into the first lowest bit, element `y` into the
/// second, etc.
#[inline]
pub fn bitmask(self) -> u32 {
(self.0 & 0x1) | (self.1 & 0x1) << 1
}
/// Returns true if any of the elements are true, false otherwise.
///
/// In other words: `x || y`.
#[inline]
pub fn any(self) -> bool {
// implementaton matches SSE2 `Vec4Mask` version
((self.0 | self.1) & 0x1) != 0
}
/// Returns true if all the elements are true, false otherwise.
///
/// In other words: `x && y`.
#[inline]
pub fn all(self) -> bool {
// implementaton matches SSE2 `Vec4Mask` version
((self.0 & self.1) & 0x1) != 0
}
/// Creates a new `Vec2` from the elements in `if_true` and `if_false`,
/// selecting which to use for each element based on the `Vec2Mask`.
///
/// A true element in the mask uses the corresponding element from
/// `if_true`, and false uses the element from `if_false`.
#[inline]
pub fn select(self, if_true: Vec2, if_false: Vec2) -> Vec2 {
Vec2(
if self.0 != 0 { if_true.0 } else { if_false.0 },
if self.1 != 0 { if_true.1 } else { if_false.1 },
)
}
}
impl BitAnd for Vec2Mask {
type Output = Self;
#[inline]
fn bitand(self, other: Self) -> Self {
Self(self.0 & other.0, self.1 & other.1)
}
}
impl BitAndAssign for Vec2Mask {
#[inline]
fn bitand_assign(&mut self, other: Self) {
self.0 &= other.0;
self.1 &= other.1;
}
}
impl BitOr for Vec2Mask {
type Output = Self;
#[inline]
fn bitor(self, other: Self) -> Self {
Self(self.0 | other.0, self.1 | other.1)
}
}
impl BitOrAssign for Vec2Mask {
#[inline]
fn bitor_assign(&mut self, other: Self) {
self.0 |= other.0;
self.1 |= other.1;
}
}
impl Not for Vec2Mask {
type Output = Self;
#[inline]
fn not(self) -> Self {
Self(!self.0, !self.1)
}
}
impl fmt::Debug for Vec2Mask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Vec2Mask({:#x}, {:#x})", self.0, self.1)
}
}
impl fmt::Display for Vec2Mask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}, {}]", self.0 != 0, self.1 != 0)
}
}
impl From<Vec2Mask> for [u32; 2] {
#[inline]
fn from(mask: Vec2Mask) -> Self {
[mask.0, mask.1]
}
}
impl AsRef<[u32; 2]> for Vec2Mask {
#[inline]
fn as_ref(&self) -> &[u32; 2] {
unsafe { &*(self as *const Self as *const [u32; 2]) }
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,298 @@
use super::Vec3;
use core::{fmt, ops::*};
#[cfg(all(vec3sse2, target_arch = "x86"))]
use core::arch::x86::*;
#[cfg(all(vec3sse2, target_arch = "x86_64"))]
use core::arch::x86_64::*;
#[cfg(vec3sse2)]
use core::{cmp::Ordering, hash};
/// A 3-dimensional vector mask.
///
/// This type is typically created by comparison methods on `Vec3`. It is
/// essentially a vector of three boolean values.
#[cfg(vec3sse2)]
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Vec3Mask(pub(crate) __m128);
/// A 3-dimensional vector mask.
#[cfg(vec3f32)]
#[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
// if compiling with simd enabled assume alignment needs to match the simd type
#[cfg_attr(vec3f32_align16, repr(align(16)))]
#[repr(C)]
pub struct Vec3Mask(pub(crate) u32, pub(crate) u32, pub(crate) u32);
#[cfg(vec3sse2)]
impl Default for Vec3Mask {
#[inline]
fn default() -> Self {
unsafe { Self(_mm_setzero_ps()) }
}
}
#[cfg(vec3sse2)]
impl PartialEq for Vec3Mask {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_ref().eq(other.as_ref())
}
}
#[cfg(vec3sse2)]
impl Eq for Vec3Mask {}
#[cfg(vec3sse2)]
impl Ord for Vec3Mask {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_ref().cmp(other.as_ref())
}
}
#[cfg(vec3sse2)]
impl PartialOrd for Vec3Mask {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(vec3sse2)]
impl hash::Hash for Vec3Mask {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_ref().hash(state);
}
}
impl Vec3Mask {
/// Creates a new `Vec3Mask`.
#[inline]
pub fn new(x: bool, y: bool, z: bool) -> Self {
// A SSE2 mask can be any bit pattern but for the `Vec3Mask` implementation of select we
// expect either 0 or 0xff_ff_ff_ff. This should be a safe assumption as this type can only
// be created via this function or by `Vec3` methods.
const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
#[cfg(vec3sse2)]
unsafe {
Self(_mm_set_ps(
f32::from_bits(MASK[z as usize]),
f32::from_bits(MASK[z as usize]),
f32::from_bits(MASK[y as usize]),
f32::from_bits(MASK[x as usize]),
))
}
#[cfg(vec3f32)]
{
Self(MASK[x as usize], MASK[y as usize], MASK[z as usize])
}
}
/// Returns a bitmask with the lowest three bits set from the elements of
/// the `Vec3Mask`.
///
/// A true element results in a `1` bit and a false element in a `0` bit.
/// Element `x` goes into the first lowest bit, element `y` into the
/// second, etc.
#[inline]
pub fn bitmask(&self) -> u32 {
// _mm_movemask_ps only checks the most significant bit of the u32 is
// true, so we replicate that here with the non-SSE2 version.
#[cfg(vec3sse2)]
unsafe {
(_mm_movemask_ps(self.0) as u32) & 0x7
}
#[cfg(vec3f32)]
{
(self.0 & 0x1) | (self.1 & 0x1) << 1 | (self.2 & 0x1) << 2
}
}
/// Returns true if any of the elements are true, false otherwise.
///
/// In other words: `x || y || z`.
#[inline]
pub fn any(&self) -> bool {
#[cfg(vec3sse2)]
unsafe {
(_mm_movemask_ps(self.0) & 0x7) != 0
}
#[cfg(vec3f32)]
{
((self.0 | self.1 | self.2) & 0x1) != 0
}
}
/// Returns true if all the elements are true, false otherwise.
///
/// In other words: `x && y && z`.
#[inline]
pub fn all(&self) -> bool {
#[cfg(vec3sse2)]
unsafe {
(_mm_movemask_ps(self.0) & 0x7) == 0x7
}
#[cfg(vec3f32)]
{
((self.0 & self.1 & self.2) & 0x1) != 0
}
}
/// Creates a new `Vec3` from the elements in `if_true` and `if_false`,
/// selecting which to use for each element based on the `Vec3Mask`.
///
/// A true element in the mask uses the corresponding element from
/// `if_true`, and false uses the element from `if_false`.
#[inline]
pub fn select(self, if_true: Vec3, if_false: Vec3) -> Vec3 {
// We are assuming that the mask values are either 0 or 0xff_ff_ff_ff for the SSE2 and f32
// to behave the same here.
#[cfg(vec3sse2)]
unsafe {
Vec3(_mm_or_ps(
_mm_andnot_ps(self.0, if_false.0),
_mm_and_ps(if_true.0, self.0),
))
}
#[cfg(vec3f32)]
{
Vec3(
if self.0 != 0 { if_true.0 } else { if_false.0 },
if self.1 != 0 { if_true.1 } else { if_false.1 },
if self.2 != 0 { if_true.2 } else { if_false.2 },
)
}
}
}
impl BitAnd for Vec3Mask {
type Output = Self;
#[inline]
fn bitand(self, other: Self) -> Self {
#[cfg(vec3sse2)]
unsafe {
Self(_mm_and_ps(self.0, other.0))
}
#[cfg(vec3f32)]
{
Self(self.0 & other.0, self.1 & other.1, self.2 & other.2)
}
}
}
impl BitAndAssign for Vec3Mask {
#[inline]
fn bitand_assign(&mut self, other: Self) {
#[cfg(vec3sse2)]
{
self.0 = unsafe { _mm_and_ps(self.0, other.0) };
}
#[cfg(vec3f32)]
{
self.0 &= other.0;
self.1 &= other.1;
self.2 &= other.2;
}
}
}
impl BitOr for Vec3Mask {
type Output = Self;
#[inline]
fn bitor(self, other: Self) -> Self {
#[cfg(vec3sse2)]
unsafe {
Self(_mm_or_ps(self.0, other.0))
}
#[cfg(vec3f32)]
{
Self(self.0 | other.0, self.1 | other.1, self.2 | other.2)
}
}
}
impl BitOrAssign for Vec3Mask {
#[inline]
fn bitor_assign(&mut self, other: Self) {
#[cfg(vec3sse2)]
{
self.0 = unsafe { _mm_or_ps(self.0, other.0) };
}
#[cfg(vec3f32)]
{
self.0 |= other.0;
self.1 |= other.1;
self.2 |= other.2;
}
}
}
impl Not for Vec3Mask {
type Output = Self;
#[inline]
fn not(self) -> Self {
#[cfg(vec3sse2)]
unsafe {
Self(_mm_andnot_ps(
self.0,
_mm_set_ps1(f32::from_bits(0xff_ff_ff_ff)),
))
}
#[cfg(vec3f32)]
{
Self(!self.0, !self.1, !self.2)
}
}
}
impl fmt::Debug for Vec3Mask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(vec3sse2)]
{
let arr = self.as_ref();
write!(f, "Vec3Mask({:#x}, {:#x}, {:#x})", arr[0], arr[1], arr[2])
}
#[cfg(vec3f32)]
{
write!(f, "Vec3Mask({:#x}, {:#x}, {:#x})", self.0, self.1, self.2)
}
}
}
impl fmt::Display for Vec3Mask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let arr = self.as_ref();
write!(f, "[{}, {}, {}]", arr[0] != 0, arr[1] != 0, arr[2] != 0,)
}
}
impl From<Vec3Mask> for [u32; 3] {
#[inline]
fn from(mask: Vec3Mask) -> Self {
*mask.as_ref()
}
}
impl AsRef<[u32; 3]> for Vec3Mask {
#[inline]
fn as_ref(&self) -> &[u32; 3] {
unsafe { &*(self as *const Self as *const [u32; 3]) }
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,329 @@
use crate::Vec4;
use core::{fmt, ops::*};
#[cfg(all(vec4sse2, target_arch = "x86"))]
use core::arch::x86::*;
#[cfg(all(vec4sse2, target_arch = "x86_64"))]
use core::arch::x86_64::*;
#[cfg(vec4sse2)]
use core::{cmp::Ordering, hash};
/// A 4-dimensional vector mask.
///
/// This type is typically created by comparison methods on `Vec4`. It is
/// essentially a vector of four boolean values.
#[cfg(vec4sse2)]
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Vec4Mask(pub(crate) __m128);
#[cfg(vec4f32)]
#[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[cfg_attr(vec4f32_align16, repr(align(16)))]
#[repr(C)]
pub struct Vec4Mask(u32, u32, u32, u32);
#[cfg(vec4sse2)]
impl Default for Vec4Mask {
#[inline]
fn default() -> Self {
unsafe { Self(_mm_setzero_ps()) }
}
}
#[cfg(vec4sse2)]
impl PartialEq for Vec4Mask {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_ref().eq(other.as_ref())
}
}
#[cfg(vec4sse2)]
impl Eq for Vec4Mask {}
#[cfg(vec4sse2)]
impl Ord for Vec4Mask {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_ref().cmp(other.as_ref())
}
}
#[cfg(vec4sse2)]
impl PartialOrd for Vec4Mask {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(vec4sse2)]
impl hash::Hash for Vec4Mask {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_ref().hash(state);
}
}
impl Vec4Mask {
/// Creates a new `Vec4Mask`.
#[inline]
pub fn new(x: bool, y: bool, z: bool, w: bool) -> Self {
// A SSE2 mask can be any bit pattern but for the `Vec4Mask` implementation of select we
// expect either 0 or 0xff_ff_ff_ff. This should be a safe assumption as this type can only
// be created via this function or by `Vec4` methods.
const MASK: [u32; 2] = [0, 0xff_ff_ff_ff];
#[cfg(vec4sse2)]
unsafe {
Self(_mm_set_ps(
f32::from_bits(MASK[w as usize]),
f32::from_bits(MASK[z as usize]),
f32::from_bits(MASK[y as usize]),
f32::from_bits(MASK[x as usize]),
))
}
#[cfg(vec4f32)]
{
Self(
MASK[x as usize],
MASK[y as usize],
MASK[z as usize],
MASK[w as usize],
)
}
}
/// Returns a bitmask with the lowest four bits set from the elements of
/// the `Vec4Mask`.
///
/// A true element results in a `1` bit and a false element in a `0` bit.
/// Element `x` goes into the first lowest bit, element `y` into the
/// second, etc.
#[inline]
pub fn bitmask(self) -> u32 {
// _mm_movemask_ps only checks the most significant bit of the u32 is true, so we replicate
// that here with the non-SSE2 version.
#[cfg(vec4sse2)]
unsafe {
_mm_movemask_ps(self.0) as u32
}
#[cfg(vec4f32)]
{
(self.0 & 0x1) | (self.1 & 0x1) << 1 | (self.2 & 0x1) << 2 | (self.3 & 0x1) << 3
}
}
/// Returns true if any of the elements are true, false otherwise.
///
/// In other words: `x || y || z || w`.
#[inline]
pub fn any(self) -> bool {
#[cfg(vec4sse2)]
unsafe {
_mm_movemask_ps(self.0) != 0
}
#[cfg(vec4f32)]
{
((self.0 | self.1 | self.2 | self.3) & 0x1) != 0
}
}
/// Returns true if all the elements are true, false otherwise.
///
/// In other words: `x && y && z && w`.
#[inline]
pub fn all(self) -> bool {
#[cfg(vec4sse2)]
unsafe {
_mm_movemask_ps(self.0) == 0xf
}
#[cfg(vec4f32)]
{
((self.0 & self.1 & self.2 & self.3) & 0x1) != 0
}
}
/// Creates a new `Vec4` from the elements in `if_true` and `if_false`,
/// selecting which to use for each element based on the `Vec4Mask`.
///
/// A true element in the mask uses the corresponding element from
/// `if_true`, and false uses the element from `if_false`.
#[inline]
pub fn select(self, if_true: Vec4, if_false: Vec4) -> Vec4 {
// We are assuming that the mask values are either 0 or 0xff_ff_ff_ff for the SSE2 and f32
// to behave the same here.
#[cfg(vec4sse2)]
unsafe {
Vec4(_mm_or_ps(
_mm_andnot_ps(self.0, if_false.0),
_mm_and_ps(if_true.0, self.0),
))
}
#[cfg(vec4f32)]
{
Vec4(
if self.0 != 0 { if_true.0 } else { if_false.0 },
if self.1 != 0 { if_true.1 } else { if_false.1 },
if self.2 != 0 { if_true.2 } else { if_false.2 },
if self.3 != 0 { if_true.3 } else { if_false.3 },
)
}
}
}
impl BitAnd for Vec4Mask {
type Output = Self;
#[inline]
fn bitand(self, other: Self) -> Self {
#[cfg(vec4sse2)]
unsafe {
Self(_mm_and_ps(self.0, other.0))
}
#[cfg(vec4f32)]
{
Self(
self.0 & other.0,
self.1 & other.1,
self.2 & other.2,
self.3 & other.3,
)
}
}
}
impl BitAndAssign for Vec4Mask {
#[inline]
fn bitand_assign(&mut self, other: Self) {
#[cfg(vec4sse2)]
{
self.0 = unsafe { _mm_and_ps(self.0, other.0) };
}
#[cfg(vec4f32)]
{
self.0 &= other.0;
self.1 &= other.1;
self.2 &= other.2;
self.3 &= other.3;
}
}
}
impl BitOr for Vec4Mask {
type Output = Self;
#[inline]
fn bitor(self, other: Self) -> Self {
#[cfg(vec4sse2)]
unsafe {
Self(_mm_or_ps(self.0, other.0))
}
#[cfg(vec4f32)]
{
Self(
self.0 | other.0,
self.1 | other.1,
self.2 | other.2,
self.3 | other.3,
)
}
}
}
impl BitOrAssign for Vec4Mask {
#[inline]
fn bitor_assign(&mut self, other: Self) {
#[cfg(vec4sse2)]
{
self.0 = unsafe { _mm_or_ps(self.0, other.0) };
}
#[cfg(vec4f32)]
{
self.0 |= other.0;
self.1 |= other.1;
self.2 |= other.2;
self.3 |= other.3;
}
}
}
impl Not for Vec4Mask {
type Output = Self;
#[inline]
fn not(self) -> Self {
#[cfg(vec4sse2)]
unsafe {
Self(_mm_andnot_ps(
self.0,
_mm_set_ps1(f32::from_bits(0xff_ff_ff_ff)),
))
}
#[cfg(vec4f32)]
{
Self(!self.0, !self.1, !self.2, !self.3)
}
}
}
impl fmt::Debug for Vec4Mask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(vec4sse2)]
{
let arr = self.as_ref();
write!(
f,
"Vec4Mask({:#x}, {:#x}, {:#x}, {:#x})",
arr[0], arr[1], arr[2], arr[3]
)
}
#[cfg(vec4f32)]
{
write!(
f,
"Vec4Mask({:#x}, {:#x}, {:#x}, {:#x})",
self.0, self.1, self.2, self.3
)
}
}
}
impl fmt::Display for Vec4Mask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let arr = self.as_ref();
write!(
f,
"[{}, {}, {}, {}]",
arr[0] != 0,
arr[1] != 0,
arr[2] != 0,
arr[3] != 0
)
}
}
impl From<Vec4Mask> for [u32; 4] {
#[inline]
fn from(mask: Vec4Mask) -> Self {
*mask.as_ref()
}
}
impl AsRef<[u32; 4]> for Vec4Mask {
#[inline]
fn as_ref(&self) -> &[u32; 4] {
unsafe { &*(self as *const Self as *const [u32; 4]) }
}
}

View file

@ -0,0 +1,15 @@
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[allow(dead_code)]
#[repr(C)]
pub(crate) union UnionCast {
pub m128: __m128,
pub m128i: __m128i,
pub f32x4: [f32; 4],
pub i32x4: [i32; 4],
pub u32x4: [u32; 4],
}

167
crates/bevy_glam/src/lib.rs Normal file
View file

@ -0,0 +1,167 @@
/*!
# glam
`glam` is a simple and fast linear algebra library for games and graphics.
## Features
`glam` is built with SIMD in mind. Currently only SSE2 on x86/x86_64 is
supported as this is what stable Rust supports.
* Single precision float (`f32`) support only
* SSE2 implementation for most types, including `Mat2`, `Mat3`, `Mat4`, `Quat`,
`Vec3` and `Vec4`
* Scalar fallback implementations exist when SSE2 is not available
* Most functionality includes unit tests and benchmarks
## Linear algebra conventions
`glam` interprets vectors as column matrices (also known as "column vectors")
meaning when transforming a vector with a matrix the matrix goes on the left.
```
use glam::{Mat3, Vec3};
let m = Mat3::identity();
let x = Vec3::unit_x();
let v = m * x;
assert_eq!(v, x);
```
Matrices are stored in memory in column-major order.
Rotations follow left-hand rule. The direction of the axis gives the direction
of rotation: with the left thumb pointing in the positive direction of the axis
the left fingers curl around the axis in the direction of the rotation.
```
use glam::{Mat3, Vec3};
// rotate +x 90 degrees clockwise around y giving -z
let m = Mat3::from_rotation_y(90.0_f32.to_radians());
let v = m * Vec3::unit_x();
assert!(v.abs_diff_eq(-Vec3::unit_z(), core::f32::EPSILON));
```
## Size and alignment of types
Most `glam` types use SIMD for storage meaning most types are 16 byte aligned.
The only exception is Vec2`. When SSE2 is not available on the target
architecture the types will still be 16 byte aligned, so object sizes and
layouts will not change between architectures.
16 byte alignment means that some types will have a stride larger than their
size resulting in some wasted space.
| Type | f32 bytes | SIMD bytes | Wasted bytes |
|:-----|----------:|-----------:|-------------:|
|`Vec3`| 12| 16| 4|
|`Mat3`| 36| 48| 12|
Despite this wasted space the SIMD version tends to outperform the `f32`
implementation in [**mathbench**](https://github.com/bitshifter/mathbench-rs)
benchmarks.
SIMD support can be disabled entirely using the `scalar-math` feature. This
feature will also disable SIMD alignment meaning most types will use native
`f32` alignment of 4 bytes.
All the main `glam` types are tagged with #[repr(C)], so they are possible
to expose as struct members to C interfaces if desired. Be mindful of Vec3's
extra float though.
## Accessing internal data
The SIMD types that `glam` builds on are opaque and their contents are not
directly accessible. Because of this `glam` types uses getter and setter
methods instead of providing direct access.
```
use glam::Vec3;
let mut v = Vec3::new(1.0, 2.0, 3.0);
assert_eq!(v.y(), 2.0);
v.set_z(1.0);
assert_eq!(v.z(), 1.0);
```
If you need to access multiple elements it is easier to convert the type to a
tuple or array:
```
use glam::Vec3;
let v = Vec3::new(1.0, 2.0, 3.0);
let (x, y, z) = v.into();
assert_eq!((x, y, z), (1.0, 2.0, 3.0));
```
## SIMD and scalar consistency
`glam` types implement `serde` `Serialize` and `Deserialize` traits to ensure
that they will serialize and deserialize exactly the same whether or not
SIMD support is being used.
The SIMD versions implement `core::fmt::Display` traits so they print the same as
the scalar version.
```
use glam::Vec3;
let a = Vec3::new(1.0, 2.0, 3.0);
assert_eq!(format!("{}", a), "[1, 2, 3]");
```
## Feature gates
All `glam` dependencies are optional, however some are required for tests
and benchmarks.
* `std` - the default feature, has no dependencies.
* `rand` - used to generate random values. Used in benchmarks.
* `serde` - used for serialization and deserialization of types.
* `mint` - used for interoperating with other linear algebra libraries.
* `scalar-math` - disables SIMD support and uses native alignment for all
types.
* `debug-glam-assert` - adds assertions in debug builds which check the validity
of parameters passed to `glam` to help catch runtime errors.
* `glam-assert` - adds assertions to all builds which check the validity of
parameters passed to `glam` to help catch runtime errors.
*/
#![doc(html_root_url = "https://docs.rs/glam/0.8.7")]
#[macro_use]
mod macros;
pub mod f32;
pub use self::f32::{
mat2, mat3, mat4, quat, vec2, vec3, vec4, Mat2, Mat3, Mat4, Quat, Vec2, Vec2Mask, Vec3,
Vec3Mask, Vec4, Vec4Mask,
};
#[cfg(feature = "transform-types")]
pub use self::f32::{TransformRT, TransformSRT};
#[repr(align(16))]
pub(crate) struct Align16<T>(T);
impl<T> Align16<T> {
#[allow(dead_code)]
pub fn as_ptr(&self) -> *const T {
&self.0
}
#[allow(dead_code)]
pub fn as_mut_ptr(&mut self) -> *mut T {
&mut self.0
}
}
#[test]
fn test_align16() {
use core::{mem, ptr};
let mut a = Align16::<f32>(1.0);
assert_eq!(mem::align_of_val(&a), 16);
unsafe {
assert_eq!(ptr::read(a.as_ptr()), 1.0);
ptr::write(a.as_mut_ptr(), -1.0);
}
assert_eq!(a.0, -1.0);
}

View file

@ -0,0 +1,32 @@
#[cfg(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
))]
macro_rules! glam_assert {
($($arg:tt)*) => ( assert!($($arg)*); )
}
#[cfg(not(any(
all(debug_assertions, feature = "debug-glam-assert"),
feature = "glam-assert"
)))]
macro_rules! glam_assert {
($($arg:tt)*) => {};
}
macro_rules! is_normalized {
($self:expr, $max_diff:expr) => {
($self.length_squared() - 1.0).abs() <= $max_diff
};
($self:expr) => {
is_normalized!($self, 1e-6)
};
}
macro_rules! abs_diff_eq {
($self:expr, $other:expr, $max_abs_diff:expr) => {
($self - $other)
.abs()
.cmple(Self::splat($max_abs_diff))
.all()
};
}

View file

@ -0,0 +1,186 @@
mod support;
use glam::f32::*;
use support::deg;
const IDENTITY: [[f32; 2]; 2] = [[1.0, 0.0], [0.0, 1.0]];
const MATRIX: [[f32; 2]; 2] = [[1.0, 2.0], [3.0, 4.0]];
const ZERO: [[f32; 2]; 2] = [[0.0; 2]; 2];
#[test]
fn test_mat2_align() {
use std::mem;
assert_eq!(16, mem::size_of::<Mat2>());
if cfg!(feature = "scalar-math") {
assert_eq!(4, mem::align_of::<Mat2>());
} else {
assert_eq!(16, mem::align_of::<Mat2>());
}
}
#[test]
fn test_mat2_identity() {
let identity = Mat2::identity();
assert_eq!(IDENTITY, identity.to_cols_array_2d());
assert_eq!(Mat2::from_cols_array_2d(&IDENTITY), identity);
assert_eq!(identity, identity * identity);
assert_eq!(identity, Mat2::default());
}
#[test]
fn test_mat2_zero() {
assert_eq!(Mat2::from_cols_array_2d(&ZERO), Mat2::zero());
}
#[test]
fn test_mat2_accessors() {
let mut m = Mat2::zero();
m.set_x_axis(Vec2::new(1.0, 2.0));
m.set_y_axis(Vec2::new(3.0, 4.0));
assert_eq!(Mat2::from_cols_array_2d(&MATRIX), m);
assert_eq!(Vec2::new(1.0, 2.0), m.x_axis());
assert_eq!(Vec2::new(3.0, 4.0), m.y_axis());
}
#[test]
fn test_mat2_from_axes() {
let a = Mat2::from_cols_array_2d(&[[1.0, 2.0], [3.0, 4.0]]);
assert_eq!(MATRIX, a.to_cols_array_2d());
let b = Mat2::from_cols(vec2(1.0, 2.0), vec2(3.0, 4.0));
assert_eq!(a, b);
let c = mat2(vec2(1.0, 2.0), vec2(3.0, 4.0));
assert_eq!(a, c);
let d = b.to_cols_array();
let f = Mat2::from_cols_array(&d);
assert_eq!(b, f);
}
#[test]
fn test_mat2_mul() {
let mat_a = Mat2::from_angle(deg(90.0));
let res_a = mat_a * Vec2::unit_y();
assert_approx_eq!(vec2(-1.0, 0.0), res_a);
let res_b = mat_a * Vec2::unit_x();
assert_approx_eq!(vec2(0.0, 1.0), res_b);
}
#[test]
fn test_from_scale() {
let m = Mat2::from_scale(Vec2::new(2.0, 4.0));
assert_approx_eq!(m * Vec2::new(1.0, 1.0), Vec2::new(2.0, 4.0));
assert_approx_eq!(Vec2::unit_x() * 2.0, m.x_axis());
assert_approx_eq!(Vec2::unit_y() * 4.0, m.y_axis());
let rot = Mat2::from_scale_angle(Vec2::new(4.0, 2.0), deg(180.0));
assert_approx_eq!(Vec2::unit_x() * -4.0, rot * Vec2::unit_x(), 1.0e-6);
assert_approx_eq!(Vec2::unit_y() * -2.0, rot * Vec2::unit_y(), 1.0e-6);
}
#[test]
fn test_mat2_transpose() {
let m = mat2(vec2(1.0, 2.0), vec2(3.0, 4.0));
let mt = m.transpose();
assert_eq!(mt.x_axis(), vec2(1.0, 3.0));
assert_eq!(mt.y_axis(), vec2(2.0, 4.0));
}
#[test]
fn test_mat2_det() {
assert_eq!(0.0, Mat2::zero().determinant());
assert_eq!(1.0, Mat2::identity().determinant());
assert_eq!(1.0, Mat2::from_angle(deg(90.0)).determinant());
assert_eq!(1.0, Mat2::from_angle(deg(180.0)).determinant());
assert_eq!(1.0, Mat2::from_angle(deg(270.0)).determinant());
assert_eq!(2.0 * 2.0, Mat2::from_scale(vec2(2.0, 2.0)).determinant());
assert_eq!(
1.0 * 4.0 - 2.0 * 3.0,
Mat2::from_cols_array(&[1.0, 2.0, 3.0, 4.0]).determinant()
);
}
#[test]
fn test_mat2_inverse() {
let inv = Mat2::identity().inverse();
assert_approx_eq!(Mat2::identity(), inv);
let rot = Mat2::from_angle(deg(90.0));
let rot_inv = rot.inverse();
assert_approx_eq!(Mat2::identity(), rot * rot_inv);
assert_approx_eq!(Mat2::identity(), rot_inv * rot);
let scale = Mat2::from_scale(vec2(4.0, 5.0));
let scale_inv = scale.inverse();
assert_approx_eq!(Mat2::identity(), scale * scale_inv);
assert_approx_eq!(Mat2::identity(), scale_inv * scale);
let m = scale * rot;
let m_inv = m.inverse();
assert_approx_eq!(Mat2::identity(), m * m_inv);
assert_approx_eq!(Mat2::identity(), m_inv * m);
assert_approx_eq!(m_inv, rot_inv * scale_inv);
}
#[test]
fn test_mat2_ops() {
let m0 = Mat2::from_cols_array_2d(&MATRIX);
assert_eq!(
Mat2::from_cols_array_2d(&[[2.0, 4.0], [6.0, 8.0]]),
m0 * 2.0
);
assert_eq!(
Mat2::from_cols_array_2d(&[[2.0, 4.0], [6.0, 8.0]]),
2.0 * m0
);
assert_eq!(Mat2::from_cols_array_2d(&[[2.0, 4.0], [6.0, 8.0]]), m0 + m0);
assert_eq!(Mat2::zero(), m0 - m0);
assert_approx_eq!(
Mat2::from_cols_array_2d(&[[1.0, 2.0], [3.0, 4.0]]),
m0 * Mat2::identity()
);
assert_approx_eq!(
Mat2::from_cols_array_2d(&[[1.0, 2.0], [3.0, 4.0]]),
Mat2::identity() * m0
);
}
#[test]
fn test_mat2_fmt() {
let a = Mat2::from_cols_array_2d(&MATRIX);
assert_eq!(format!("{}", a), "[[1, 2], [3, 4]]");
}
#[cfg(feature = "serde")]
#[test]
fn test_mat2_serde() {
let a = Mat2::from_cols(vec2(1.0, 2.0), vec2(3.0, 4.0));
let serialized = serde_json::to_string(&a).unwrap();
assert_eq!(serialized, "[1.0,2.0,3.0,4.0]");
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(a, deserialized);
let deserialized = serde_json::from_str::<Mat2>("[]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat2>("[1.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat2>("[1.0,2.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat2>("[1.0,2.0,3.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat2>("[1.0,2.0,3.0,4.0,5.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat2>("[[1.0,2.0],[3.0,4.0]]");
assert!(deserialized.is_err());
}
#[cfg(feature = "rand")]
#[test]
fn test_mat2_rand() {
use rand::{Rng, SeedableRng};
use rand_xoshiro::Xoshiro256Plus;
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
let a = Mat2::from_cols_array(&rng1.gen::<[f32; 4]>());
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
let b = rng2.gen::<Mat2>();
assert_eq!(a, b);
}

View file

@ -0,0 +1,253 @@
mod support;
use glam::f32::*;
use support::deg;
const IDENTITY: [[f32; 3]; 3] = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
const MATRIX: [[f32; 3]; 3] = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
const ZERO: [[f32; 3]; 3] = [[0.0; 3]; 3];
#[test]
fn test_mat3_align() {
use std::mem;
if cfg!(any(feature = "packed-vec3", feature = "scalar-math")) {
assert_eq!(36, mem::size_of::<Mat3>());
assert_eq!(4, mem::align_of::<Mat3>());
} else {
assert_eq!(48, mem::size_of::<Mat3>());
assert_eq!(16, mem::align_of::<Mat3>());
}
}
#[test]
fn test_mat3_identity() {
let identity = Mat3::identity();
assert_eq!(IDENTITY, identity.to_cols_array_2d());
assert_eq!(Mat3::from_cols_array_2d(&IDENTITY), identity);
assert_eq!(identity, identity * identity);
assert_eq!(identity, Mat3::default());
}
#[test]
fn test_mat3_zero() {
assert_eq!(Mat3::from_cols_array_2d(&ZERO), Mat3::zero());
}
#[test]
fn test_mat3_accessors() {
let mut m = Mat3::zero();
m.set_x_axis(Vec3::new(1.0, 2.0, 3.0));
m.set_y_axis(Vec3::new(4.0, 5.0, 6.0));
m.set_z_axis(Vec3::new(7.0, 8.0, 9.0));
assert_eq!(Mat3::from_cols_array_2d(&MATRIX), m);
assert_eq!(Vec3::new(1.0, 2.0, 3.0), m.x_axis());
assert_eq!(Vec3::new(4.0, 5.0, 6.0), m.y_axis());
assert_eq!(Vec3::new(7.0, 8.0, 9.0), m.z_axis());
}
#[test]
fn test_mat3_from_axes() {
let a = Mat3::from_cols_array_2d(&[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]);
assert_eq!(MATRIX, a.to_cols_array_2d());
let b = Mat3::from_cols(
vec3(1.0, 2.0, 3.0),
vec3(4.0, 5.0, 6.0),
vec3(7.0, 8.0, 9.0),
);
assert_eq!(a, b);
let c = mat3(
vec3(1.0, 2.0, 3.0),
vec3(4.0, 5.0, 6.0),
vec3(7.0, 8.0, 9.0),
);
assert_eq!(a, c);
let d = b.to_cols_array();
let f = Mat3::from_cols_array(&d);
assert_eq!(b, f);
}
#[test]
fn test_from_rotation() {
let rot_x1 = Mat3::from_rotation_x(deg(180.0));
let rot_x2 = Mat3::from_axis_angle(Vec3::unit_x(), deg(180.0));
assert_approx_eq!(rot_x1, rot_x2);
let rot_y1 = Mat3::from_rotation_y(deg(180.0));
let rot_y2 = Mat3::from_axis_angle(Vec3::unit_y(), deg(180.0));
assert_approx_eq!(rot_y1, rot_y2);
let rot_z1 = Mat3::from_rotation_z(deg(180.0));
let rot_z2 = Mat3::from_axis_angle(Vec3::unit_z(), deg(180.0));
assert_approx_eq!(rot_z1, rot_z2);
}
#[test]
fn test_mat3_mul() {
let mat_a = Mat3::from_axis_angle(Vec3::unit_z(), deg(90.0));
let result3 = mat_a * Vec3::unit_y();
assert_approx_eq!(vec3(-1.0, 0.0, 0.0), result3);
}
#[test]
fn test_mat3_transform2d() {
let mat_b = Mat3::from_scale_angle_translation(
Vec2::new(0.5, 1.5),
f32::to_radians(90.0),
Vec2::new(1.0, 2.0),
);
let result2 = mat_b.transform_vector2(Vec2::unit_y());
assert_approx_eq!(vec2(-1.5, 0.0), result2, 1.0e-6);
assert_approx_eq!(result2, (mat_b * Vec2::unit_y().extend(0.0)).truncate());
let result2 = mat_b.transform_point2(Vec2::unit_y());
assert_approx_eq!(vec2(-0.5, 2.0), result2, 1.0e-6);
assert_approx_eq!(result2, (mat_b * Vec2::unit_y().extend(1.0)).truncate());
}
#[test]
fn test_from_ypr() {
let zero = deg(0.0);
let yaw = deg(30.0);
let pitch = deg(60.0);
let roll = deg(90.0);
let y0 = Mat3::from_rotation_y(yaw);
let y1 = Mat3::from_rotation_ypr(yaw, zero, zero);
assert_approx_eq!(y0, y1);
let x0 = Mat3::from_rotation_x(pitch);
let x1 = Mat3::from_rotation_ypr(zero, pitch, zero);
assert_approx_eq!(x0, x1);
let z0 = Mat3::from_rotation_z(roll);
let z1 = Mat3::from_rotation_ypr(zero, zero, roll);
assert_approx_eq!(z0, z1);
let yx0 = y0 * x0;
let yx1 = Mat3::from_rotation_ypr(yaw, pitch, zero);
assert_approx_eq!(yx0, yx1);
let yxz0 = y0 * x0 * z0;
let yxz1 = Mat3::from_rotation_ypr(yaw, pitch, roll);
assert_approx_eq!(yxz0, yxz1, 1e-6);
}
#[test]
fn test_from_scale() {
let m = Mat3::from_scale(Vec3::new(2.0, 4.0, 8.0));
assert_approx_eq!(m * Vec3::new(1.0, 1.0, 1.0), Vec3::new(2.0, 4.0, 8.0));
assert_approx_eq!(Vec3::unit_x() * 2.0, m.x_axis());
assert_approx_eq!(Vec3::unit_y() * 4.0, m.y_axis());
assert_approx_eq!(Vec3::unit_z() * 8.0, m.z_axis());
}
#[test]
fn test_mat3_transpose() {
let m = mat3(
vec3(1.0, 2.0, 3.0),
vec3(4.0, 5.0, 6.0),
vec3(7.0, 8.0, 9.0),
);
let mt = m.transpose();
assert_eq!(mt.x_axis(), vec3(1.0, 4.0, 7.0));
assert_eq!(mt.y_axis(), vec3(2.0, 5.0, 8.0));
assert_eq!(mt.z_axis(), vec3(3.0, 6.0, 9.0));
}
#[test]
fn test_mat3_det() {
assert_eq!(0.0, Mat3::zero().determinant());
assert_eq!(1.0, Mat3::identity().determinant());
assert_eq!(1.0, Mat3::from_rotation_x(deg(90.0)).determinant());
assert_eq!(1.0, Mat3::from_rotation_y(deg(180.0)).determinant());
assert_eq!(1.0, Mat3::from_rotation_z(deg(270.0)).determinant());
assert_eq!(
2.0 * 2.0 * 2.0,
Mat3::from_scale(vec3(2.0, 2.0, 2.0)).determinant()
);
}
#[test]
fn test_mat3_inverse() {
// assert_eq!(None, Mat3::zero().inverse());
let inv = Mat3::identity().inverse();
// assert_ne!(None, inv);
assert_approx_eq!(Mat3::identity(), inv);
let rotz = Mat3::from_rotation_z(deg(90.0));
let rotz_inv = rotz.inverse();
// assert_ne!(None, rotz_inv);
// let rotz_inv = rotz_inv.unwrap();
assert_approx_eq!(Mat3::identity(), rotz * rotz_inv);
assert_approx_eq!(Mat3::identity(), rotz_inv * rotz);
let scale = Mat3::from_scale(vec3(4.0, 5.0, 6.0));
let scale_inv = scale.inverse();
// assert_ne!(None, scale_inv);
// let scale_inv = scale_inv.unwrap();
assert_approx_eq!(Mat3::identity(), scale * scale_inv);
assert_approx_eq!(Mat3::identity(), scale_inv * scale);
let m = scale * rotz;
let m_inv = m.inverse();
// assert_ne!(None, m_inv);
// let m_inv = m_inv.unwrap();
assert_approx_eq!(Mat3::identity(), m * m_inv);
assert_approx_eq!(Mat3::identity(), m_inv * m);
assert_approx_eq!(m_inv, rotz_inv * scale_inv);
}
#[test]
fn test_mat3_ops() {
let m0 = Mat3::from_cols_array_2d(&MATRIX);
let m0x2 = Mat3::from_cols_array_2d(&[[2.0, 4.0, 6.0], [8.0, 10.0, 12.0], [14.0, 16.0, 18.0]]);
assert_eq!(m0x2, m0 * 2.0);
assert_eq!(m0x2, 2.0 * m0);
assert_eq!(m0x2, m0 + m0);
assert_eq!(Mat3::zero(), m0 - m0);
assert_approx_eq!(m0, m0 * Mat3::identity());
assert_approx_eq!(m0, Mat3::identity() * m0);
}
#[test]
fn test_mat3_fmt() {
let a = Mat3::from_cols_array_2d(&MATRIX);
assert_eq!(format!("{}", a), "[[1, 2, 3], [4, 5, 6], [7, 8, 9]]");
}
#[cfg(feature = "serde")]
#[test]
fn test_mat3_serde() {
let a = Mat3::from_cols(
vec3(1.0, 2.0, 3.0),
vec3(4.0, 5.0, 6.0),
vec3(7.0, 8.0, 9.0),
);
let serialized = serde_json::to_string(&a).unwrap();
assert_eq!(serialized, "[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0]");
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(a, deserialized);
let deserialized = serde_json::from_str::<Mat3>("[]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat3>("[1.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat3>("[1.0,2.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat3>("[1.0,2.0,3.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat3>("[1.0,2.0,3.0,4.0,5.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat3>("[[1.0,2.0,3.0],[4.0,5.0,6.0],[7.0,8.0,9.0]]");
assert!(deserialized.is_err());
}
#[cfg(feature = "rand")]
#[test]
fn test_mat3_rand() {
use rand::{Rng, SeedableRng};
use rand_xoshiro::Xoshiro256Plus;
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
let a = Mat3::from_cols_array(&rng1.gen::<[f32; 9]>());
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
let b = rng2.gen::<Mat3>();
assert_eq!(a, b);
}

View file

@ -0,0 +1,483 @@
mod support;
use glam::f32::*;
use support::deg;
const IDENTITY: [[f32; 4]; 4] = [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
];
const MATRIX: [[f32; 4]; 4] = [
[1.0, 2.0, 3.0, 4.0],
[5.0, 6.0, 7.0, 8.0],
[9.0, 10.0, 11.0, 12.0],
[13.0, 14.0, 15.0, 16.0],
];
const ZERO: [[f32; 4]; 4] = [[0.0; 4]; 4];
#[test]
fn test_mat4_align() {
use std::mem;
assert_eq!(64, mem::size_of::<Mat4>());
if cfg!(feature = "scalar-math") {
assert_eq!(4, mem::align_of::<Mat4>());
} else {
assert_eq!(16, mem::align_of::<Mat4>());
}
}
#[test]
fn test_mat4_identity() {
let identity = Mat4::identity();
assert_eq!(IDENTITY, identity.to_cols_array_2d());
assert_eq!(Mat4::from_cols_array_2d(&IDENTITY), identity);
assert_eq!(identity, identity * identity);
assert_eq!(identity, Mat4::default());
}
#[test]
fn test_mat4_zero() {
assert_eq!(Mat4::from_cols_array_2d(&ZERO), Mat4::zero());
}
#[test]
fn test_mat4_accessors() {
let mut m = Mat4::zero();
m.set_x_axis(Vec4::new(1.0, 2.0, 3.0, 4.0));
m.set_y_axis(Vec4::new(5.0, 6.0, 7.0, 8.0));
m.set_z_axis(Vec4::new(9.0, 10.0, 11.0, 12.0));
m.set_w_axis(Vec4::new(13.0, 14.0, 15.0, 16.0));
assert_eq!(Mat4::from_cols_array_2d(&MATRIX), m);
assert_eq!(Vec4::new(1.0, 2.0, 3.0, 4.0), m.x_axis());
assert_eq!(Vec4::new(5.0, 6.0, 7.0, 8.0), m.y_axis());
assert_eq!(Vec4::new(9.0, 10.0, 11.0, 12.0), m.z_axis());
assert_eq!(Vec4::new(13.0, 14.0, 15.0, 16.0), m.w_axis());
}
#[test]
fn test_mat4_from_axes() {
let a = Mat4::from_cols_array_2d(&[
[1.0, 2.0, 3.0, 4.0],
[5.0, 6.0, 7.0, 8.0],
[9.0, 10.0, 11.0, 12.0],
[13.0, 14.0, 15.0, 16.0],
]);
assert_eq!(MATRIX, a.to_cols_array_2d());
let b = Mat4::from_cols(
vec4(1.0, 2.0, 3.0, 4.0),
vec4(5.0, 6.0, 7.0, 8.0),
vec4(9.0, 10.0, 11.0, 12.0),
vec4(13.0, 14.0, 15.0, 16.0),
);
assert_eq!(a, b);
let c = mat4(
vec4(1.0, 2.0, 3.0, 4.0),
vec4(5.0, 6.0, 7.0, 8.0),
vec4(9.0, 10.0, 11.0, 12.0),
vec4(13.0, 14.0, 15.0, 16.0),
);
assert_eq!(a, c);
let d = b.to_cols_array();
let f = Mat4::from_cols_array(&d);
assert_eq!(b, f);
}
#[test]
fn test_mat4_translation() {
let translate = Mat4::from_translation(vec3(1.0, 2.0, 3.0));
assert_eq!(
Mat4::from_cols(
vec4(1.0, 0.0, 0.0, 0.0),
vec4(0.0, 1.0, 0.0, 0.0),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(1.0, 2.0, 3.0, 1.0)
),
translate
);
}
#[test]
fn test_from_rotation() {
let rot_x1 = Mat4::from_rotation_x(deg(180.0));
let rot_x2 = Mat4::from_axis_angle(Vec3::unit_x(), deg(180.0));
assert_approx_eq!(rot_x1, rot_x2);
let rot_y1 = Mat4::from_rotation_y(deg(180.0));
let rot_y2 = Mat4::from_axis_angle(Vec3::unit_y(), deg(180.0));
assert_approx_eq!(rot_y1, rot_y2);
let rot_z1 = Mat4::from_rotation_z(deg(180.0));
let rot_z2 = Mat4::from_axis_angle(Vec3::unit_z(), deg(180.0));
assert_approx_eq!(rot_z1, rot_z2);
}
#[test]
fn test_mat4_mul() {
let mat_a = Mat4::from_axis_angle(Vec3::unit_z(), deg(90.0));
let result3 = mat_a.transform_vector3(Vec3::unit_y());
assert_approx_eq!(vec3(-1.0, 0.0, 0.0), result3);
assert_approx_eq!(result3, (mat_a * Vec3::unit_y().extend(0.0)).truncate());
let result4 = mat_a * Vec4::unit_y();
assert_approx_eq!(vec4(-1.0, 0.0, 0.0, 0.0), result4);
assert_approx_eq!(result4, mat_a * Vec4::unit_y());
let mat_b = Mat4::from_scale_rotation_translation(
Vec3::new(0.5, 1.5, 2.0),
Quat::from_rotation_x(deg(90.0)),
Vec3::new(1.0, 2.0, 3.0),
);
let result3 = mat_b.transform_vector3(Vec3::unit_y());
assert_approx_eq!(vec3(0.0, 0.0, 1.5), result3, 1.0e-6);
assert_approx_eq!(result3, (mat_b * Vec3::unit_y().extend(0.0)).truncate());
let result3 = mat_b.transform_point3(Vec3::unit_y());
assert_approx_eq!(vec3(1.0, 2.0, 4.5), result3, 1.0e-6);
assert_approx_eq!(result3, (mat_b * Vec3::unit_y().extend(1.0)).truncate());
}
#[test]
fn test_from_ypr() {
let zero = deg(0.0);
let yaw = deg(30.0);
let pitch = deg(60.0);
let roll = deg(90.0);
let y0 = Mat4::from_rotation_y(yaw);
let y1 = Mat4::from_rotation_ypr(yaw, zero, zero);
assert_approx_eq!(y0, y1);
let x0 = Mat4::from_rotation_x(pitch);
let x1 = Mat4::from_rotation_ypr(zero, pitch, zero);
assert_approx_eq!(x0, x1);
let z0 = Mat4::from_rotation_z(roll);
let z1 = Mat4::from_rotation_ypr(zero, zero, roll);
assert_approx_eq!(z0, z1);
let yx0 = y0 * x0;
let yx1 = Mat4::from_rotation_ypr(yaw, pitch, zero);
assert_approx_eq!(yx0, yx1);
let yxz0 = y0 * x0 * z0;
let yxz1 = Mat4::from_rotation_ypr(yaw, pitch, roll);
assert_approx_eq!(yxz0, yxz1, 1e-6);
}
#[test]
fn test_from_scale() {
let m = Mat4::from_scale(Vec3::new(2.0, 4.0, 8.0));
assert_approx_eq!(
m.transform_point3(Vec3::new(1.0, 1.0, 1.0)),
Vec3::new(2.0, 4.0, 8.0)
);
assert_approx_eq!(Vec4::unit_x() * 2.0, m.x_axis());
assert_approx_eq!(Vec4::unit_y() * 4.0, m.y_axis());
assert_approx_eq!(Vec4::unit_z() * 8.0, m.z_axis());
assert_approx_eq!(Vec4::unit_w(), m.w_axis());
}
#[test]
fn test_mat4_transpose() {
let m = mat4(
vec4(1.0, 2.0, 3.0, 4.0),
vec4(5.0, 6.0, 7.0, 8.0),
vec4(9.0, 10.0, 11.0, 12.0),
vec4(13.0, 14.0, 15.0, 16.0),
);
let mt = m.transpose();
assert_eq!(mt.x_axis(), vec4(1.0, 5.0, 9.0, 13.0));
assert_eq!(mt.y_axis(), vec4(2.0, 6.0, 10.0, 14.0));
assert_eq!(mt.z_axis(), vec4(3.0, 7.0, 11.0, 15.0));
assert_eq!(mt.w_axis(), vec4(4.0, 8.0, 12.0, 16.0));
}
#[test]
fn test_mat4_det() {
assert_eq!(0.0, Mat4::zero().determinant());
assert_eq!(1.0, Mat4::identity().determinant());
assert_eq!(1.0, Mat4::from_rotation_x(deg(90.0)).determinant());
assert_eq!(1.0, Mat4::from_rotation_y(deg(180.0)).determinant());
assert_eq!(1.0, Mat4::from_rotation_z(deg(270.0)).determinant());
assert_eq!(
2.0 * 2.0 * 2.0,
Mat4::from_scale(vec3(2.0, 2.0, 2.0)).determinant()
);
}
#[test]
fn test_mat4_inverse() {
// assert_eq!(None, Mat4::zero().inverse());
let inv = Mat4::identity().inverse();
// assert_ne!(None, inv);
assert_approx_eq!(Mat4::identity(), inv);
let rotz = Mat4::from_rotation_z(deg(90.0));
let rotz_inv = rotz.inverse();
// assert_ne!(None, rotz_inv);
// let rotz_inv = rotz_inv.unwrap();
assert_approx_eq!(Mat4::identity(), rotz * rotz_inv);
assert_approx_eq!(Mat4::identity(), rotz_inv * rotz);
let trans = Mat4::from_translation(vec3(1.0, 2.0, 3.0));
let trans_inv = trans.inverse();
// assert_ne!(None, trans_inv);
// let trans_inv = trans_inv.unwrap();
assert_approx_eq!(Mat4::identity(), trans * trans_inv);
assert_approx_eq!(Mat4::identity(), trans_inv * trans);
let scale = Mat4::from_scale(vec3(4.0, 5.0, 6.0));
let scale_inv = scale.inverse();
// assert_ne!(None, scale_inv);
// let scale_inv = scale_inv.unwrap();
assert_approx_eq!(Mat4::identity(), scale * scale_inv);
assert_approx_eq!(Mat4::identity(), scale_inv * scale);
let m = scale * rotz * trans;
let m_inv = m.inverse();
// assert_ne!(None, m_inv);
// let m_inv = m_inv.unwrap();
assert_approx_eq!(Mat4::identity(), m * m_inv, 1.0e-5);
assert_approx_eq!(Mat4::identity(), m_inv * m, 1.0e-5);
assert_approx_eq!(m_inv, trans_inv * rotz_inv * scale_inv, 1.0e-6);
}
#[test]
fn test_mat4_decompose() {
// identity
let (out_scale, out_rotation, out_translation) =
Mat4::identity().to_scale_rotation_translation();
assert_approx_eq!(Vec3::one(), out_scale);
assert!(out_rotation.is_near_identity());
assert_approx_eq!(Vec3::zero(), out_translation);
// no scale
let in_scale = Vec3::one();
let in_translation = Vec3::new(-2.0, 4.0, -0.125);
let in_rotation = Quat::from_rotation_ypr(
f32::to_radians(-45.0),
f32::to_radians(180.0),
f32::to_radians(270.0),
);
let in_mat = Mat4::from_scale_rotation_translation(in_scale, in_rotation, in_translation);
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_rotation_translation();
assert_approx_eq!(in_scale, out_scale, 1e-6);
// out_rotation is different but produces the same matrix
// assert_approx_eq!(in_rotation, out_rotation);
assert_approx_eq!(in_translation, out_translation);
assert_approx_eq!(
in_mat,
Mat4::from_scale_rotation_translation(out_scale, out_rotation, out_translation),
1e-6
);
// positive scale
let in_scale = Vec3::new(1.0, 2.0, 4.0);
let in_mat = Mat4::from_scale_rotation_translation(in_scale, in_rotation, in_translation);
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_rotation_translation();
assert_approx_eq!(in_scale, out_scale, 1e-6);
// out_rotation is different but produces the same matrix
// assert_approx_eq!(in_rotation, out_rotation);
assert_approx_eq!(in_translation, out_translation);
assert_approx_eq!(
in_mat,
Mat4::from_scale_rotation_translation(out_scale, out_rotation, out_translation),
1e-6
);
// negative scale
let in_scale = Vec3::new(-4.0, 1.0, 2.0);
let in_mat = Mat4::from_scale_rotation_translation(in_scale, in_rotation, in_translation);
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_rotation_translation();
assert_approx_eq!(in_scale, out_scale, 1e-6);
// out_rotation is different but produces the same matrix
// assert_approx_eq!(in_rotation, out_rotation);
assert_approx_eq!(in_translation, out_translation);
assert_approx_eq!(
in_mat,
Mat4::from_scale_rotation_translation(out_scale, out_rotation, out_translation),
1e-5
);
// negative scale
let in_scale = Vec3::new(4.0, -1.0, -2.0);
let in_mat = Mat4::from_scale_rotation_translation(in_scale, in_rotation, in_translation);
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_rotation_translation();
// out_scale and out_rotation are different but they produce the same matrix
// assert_approx_eq!(in_scale, out_scale, 1e-6);
// assert_approx_eq!(in_rotation, out_rotation);
assert_approx_eq!(in_translation, out_translation);
assert_approx_eq!(
in_mat,
Mat4::from_scale_rotation_translation(out_scale, out_rotation, out_translation),
1e-6
);
}
#[test]
fn test_mat4_look_at() {
let eye = Vec3::new(0.0, 0.0, -5.0);
let center = Vec3::new(0.0, 0.0, 0.0);
let up = Vec3::new(1.0, 0.0, 0.0);
let lh = Mat4::look_at_lh(eye, center, up);
let rh = Mat4::look_at_rh(eye, center, up);
let point = Vec3::new(1.0, 0.0, 0.0);
assert_approx_eq!(lh.transform_point3(point), Vec3::new(0.0, 1.0, 5.0));
assert_approx_eq!(rh.transform_point3(point), Vec3::new(0.0, 1.0, -5.0));
}
#[test]
fn test_mat4_perspective_gl_rh() {
let projection = Mat4::perspective_rh_gl(f32::to_radians(90.0), 2.0, 5.0, 15.0);
let original = Vec3::new(5.0, 5.0, -15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!(Vec4::new(2.5, 5.0, 15.0, 15.0), projected);
let original = Vec3::new(5.0, 5.0, -5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!(Vec4::new(2.5, 5.0, -5.0, 5.0), projected);
}
#[test]
fn test_mat4_perspective_lh() {
let projection = Mat4::perspective_lh(f32::to_radians(90.0), 2.0, 5.0, 15.0);
let original = Vec3::new(5.0, 5.0, 15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!(Vec4::new(2.5, 5.0, 15.0, 15.0), projected);
let original = Vec3::new(5.0, 5.0, 5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!(Vec4::new(2.5, 5.0, 0.0, 5.0), projected);
}
#[test]
fn test_mat4_perspective_infinite_lh() {
let projection = Mat4::perspective_infinite_lh(f32::to_radians(90.0), 2.0, 5.0);
let original = Vec3::new(5.0, 5.0, 15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!(Vec4::new(2.5, 5.0, 10.0, 15.0), projected);
let original = Vec3::new(5.0, 5.0, 5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!(Vec4::new(2.5, 5.0, 0.0, 5.0), projected);
}
#[test]
fn test_mat4_perspective_infinite_reverse_lh() {
let projection = Mat4::perspective_infinite_reverse_lh(f32::to_radians(90.0), 2.0, 5.0);
let original = Vec3::new(5.0, 5.0, 15.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!(Vec4::new(2.5, 5.0, 5.0, 15.0), projected);
let original = Vec3::new(5.0, 5.0, 5.0);
let projected = projection * original.extend(1.0);
assert_approx_eq!(Vec4::new(2.5, 5.0, 5.0, 5.0), projected);
}
#[test]
fn test_mat4_orthographic_gl_rh() {
let projection = Mat4::orthographic_rh_gl(-10.0, 10.0, -5.0, 5.0, 0.0, -10.0);
let original = Vec4::new(5.0, 5.0, -5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, -2.0, 1.0));
}
#[test]
fn test_mat4_orthographic_rh() {
let projection = Mat4::orthographic_rh(-10.0, 10.0, -5.0, 5.0, -10.0, 10.0);
let original = Vec4::new(5.0, 5.0, -5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, 0.75, 1.0));
let original = Vec4::new(5.0, 5.0, 5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, 0.25, 1.0));
}
#[test]
fn test_mat4_orthographic_lh() {
let projection = Mat4::orthographic_lh(-10.0, 10.0, -5.0, 5.0, -10.0, 10.0);
let original = Vec4::new(5.0, 5.0, -5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, 0.25, 1.0));
let original = Vec4::new(5.0, 5.0, 5.0, 1.0);
let projected = projection.mul_vec4(original);
assert_approx_eq!(projected, Vec4::new(0.5, 1.0, 0.75, 1.0));
}
#[test]
fn test_mat4_ops() {
let m0 = Mat4::from_cols_array_2d(&MATRIX);
let m0x2 = Mat4::from_cols_array_2d(&[
[2.0, 4.0, 6.0, 8.0],
[10.0, 12.0, 14.0, 16.0],
[18.0, 20.0, 22.0, 24.0],
[26.0, 28.0, 30.0, 32.0],
]);
assert_eq!(m0x2, m0 * 2.0);
assert_eq!(m0x2, 2.0 * m0);
assert_eq!(m0x2, m0 + m0);
assert_eq!(Mat4::zero(), m0 - m0);
assert_approx_eq!(m0, m0 * Mat4::identity());
assert_approx_eq!(m0, Mat4::identity() * m0);
}
#[test]
fn test_mat4_fmt() {
let a = Mat4::from_cols_array_2d(&MATRIX);
assert_eq!(
format!("{}", a),
"[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]"
);
}
#[cfg(feature = "serde")]
#[test]
fn test_mat4_serde() {
let a = Mat4::from_cols(
vec4(1.0, 2.0, 3.0, 4.0),
vec4(5.0, 6.0, 7.0, 8.0),
vec4(9.0, 10.0, 11.0, 12.0),
vec4(13.0, 14.0, 15.0, 16.0),
);
let serialized = serde_json::to_string(&a).unwrap();
assert_eq!(
serialized,
"[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0]"
);
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(a, deserialized);
let deserialized = serde_json::from_str::<Mat4>("[]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat4>("[1.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat4>("[1.0,2.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat4>("[1.0,2.0,3.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat4>("[1.0,2.0,3.0,4.0,5.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat4>("[[1.0,2.0,3.0],[4.0,5.0,6.0],[7.0,8.0,9.0]]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Mat4>(
"[[1.0,2.0,3.0,4.0],[5.0,6.0,7.0,8.0],[9.0,10.0,11.0,12.0][13.0,14.0,15.0,16.0]]",
);
assert!(deserialized.is_err());
}
#[cfg(feature = "rand")]
#[test]
fn test_mat4_rand() {
use rand::{Rng, SeedableRng};
use rand_xoshiro::Xoshiro256Plus;
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
let a = Mat4::from_cols_array(&rng1.gen::<[f32; 16]>());
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
let b = rng2.gen::<Mat4>();
assert_eq!(a, b);
}

View file

@ -0,0 +1,284 @@
mod support;
use glam::f32::{quat, Mat3, Mat4, Quat, Vec3, Vec4};
use support::{deg, rad};
#[test]
fn test_quat_align() {
use std::mem;
assert_eq!(16, mem::size_of::<Quat>());
if cfg!(feature = "scalar-math") {
assert_eq!(4, mem::align_of::<Quat>());
} else {
assert_eq!(16, mem::align_of::<Quat>());
}
}
#[test]
fn test_quat_rotation() {
let zero = deg(0.0);
let yaw = deg(30.0);
let pitch = deg(60.0);
let roll = deg(90.0);
let y0 = Quat::from_rotation_y(yaw);
assert!(y0.is_normalized());
let (axis, angle) = y0.to_axis_angle();
assert_approx_eq!(axis, Vec3::unit_y(), 1.0e-6);
assert_approx_eq!(angle, yaw);
let y1 = Quat::from_rotation_ypr(yaw, zero, zero);
assert_approx_eq!(y0, y1);
let y2 = Quat::from_axis_angle(Vec3::unit_y(), yaw);
assert_approx_eq!(y0, y2);
let y3 = Quat::from_rotation_mat3(&Mat3::from_rotation_y(yaw));
assert_approx_eq!(y0, y3);
let y4 = Quat::from_rotation_mat3(&Mat3::from_quat(y0));
assert_approx_eq!(y0, y4);
let x0 = Quat::from_rotation_x(pitch);
assert!(x0.is_normalized());
let (axis, angle) = x0.to_axis_angle();
assert_approx_eq!(axis, Vec3::unit_x());
assert_approx_eq!(angle, pitch);
let x1 = Quat::from_rotation_ypr(zero, pitch, zero);
assert_approx_eq!(x0, x1);
let x2 = Quat::from_axis_angle(Vec3::unit_x(), pitch);
assert_approx_eq!(x0, x2);
let x3 = Quat::from_rotation_mat4(&Mat4::from_rotation_x(deg(180.0)));
assert!(x3.is_normalized());
assert_approx_eq!(Quat::from_rotation_x(deg(180.0)), x3);
let z0 = Quat::from_rotation_z(roll);
assert!(z0.is_normalized());
let (axis, angle) = z0.to_axis_angle();
assert_approx_eq!(axis, Vec3::unit_z());
assert_approx_eq!(angle, roll);
let z1 = Quat::from_rotation_ypr(zero, zero, roll);
assert_approx_eq!(z0, z1);
let z2 = Quat::from_axis_angle(Vec3::unit_z(), roll);
assert_approx_eq!(z0, z2);
let z3 = Quat::from_rotation_mat4(&Mat4::from_rotation_z(roll));
assert_approx_eq!(z0, z3);
let yx0 = y0 * x0;
assert!(yx0.is_normalized());
let yx1 = Quat::from_rotation_ypr(yaw, pitch, zero);
assert_approx_eq!(yx0, yx1);
let yxz0 = y0 * x0 * z0;
assert!(yxz0.is_normalized());
let yxz1 = Quat::from_rotation_ypr(yaw, pitch, roll);
assert_approx_eq!(yxz0, yxz1);
// use the conjugate of z0 to remove the rotation from yxz0
let yx2 = yxz0 * z0.conjugate();
assert_approx_eq!(yx0, yx2);
let yxz2 = Quat::from_rotation_mat4(&Mat4::from_quat(yxz0));
assert_approx_eq!(yxz0, yxz2);
// if near identity, just returns x axis and 0 rotation
let (axis, angle) = Quat::identity().to_axis_angle();
assert_eq!(axis, Vec3::unit_x());
assert_eq!(angle, rad(0.0));
}
#[test]
fn test_quat_new() {
let ytheta = deg(45.0);
let q0 = Quat::from_rotation_y(ytheta);
let t1 = (0.0, (ytheta * 0.5).sin(), 0.0, (ytheta * 0.5).cos());
assert_eq!(q0, t1.into());
let q1 = Quat::from(t1);
assert_eq!(t1, q1.into());
assert_eq!(q0, quat(t1.0, t1.1, t1.2, t1.3));
let a1 = [0.0, (ytheta * 0.5).sin(), 0.0, (ytheta * 0.5).cos()];
assert_eq!(q0, a1.into());
let q1 = Quat::from(a1);
let a2: [f32; 4] = q1.into();
assert_eq!(a1, a2);
}
#[test]
fn test_quat_mul_vec() {
let qrz = Quat::from_rotation_z(deg(90.0));
assert_approx_eq!(Vec3::unit_y(), qrz * Vec3::unit_x());
assert_approx_eq!(Vec3::unit_y(), -qrz * Vec3::unit_x());
assert_approx_eq!(-Vec3::unit_x(), qrz * Vec3::unit_y());
assert_approx_eq!(-Vec3::unit_x(), -qrz * Vec3::unit_y());
// check vec3 * mat3 is the same
let mrz = Mat3::from_quat(qrz);
assert_approx_eq!(Vec3::unit_y(), mrz * Vec3::unit_x());
// assert_approx_eq!(Vec3::unit_y(), -mrz * Vec3::unit_x());
assert_approx_eq!(-Vec3::unit_x(), mrz * Vec3::unit_y());
let qrx = Quat::from_rotation_x(deg(90.0));
assert_approx_eq!(Vec3::unit_x(), qrx * Vec3::unit_x());
assert_approx_eq!(Vec3::unit_x(), -qrx * Vec3::unit_x());
assert_approx_eq!(Vec3::unit_z(), qrx * Vec3::unit_y());
assert_approx_eq!(Vec3::unit_z(), -qrx * Vec3::unit_y());
// check vec3 * mat3 is the same
let mrx = Mat3::from_quat(qrx);
assert_approx_eq!(Vec3::unit_x(), mrx * Vec3::unit_x());
assert_approx_eq!(Vec3::unit_z(), mrx * Vec3::unit_y());
let qrxz = qrz * qrx;
assert_approx_eq!(Vec3::unit_y(), qrxz * Vec3::unit_x());
assert_approx_eq!(Vec3::unit_z(), qrxz * Vec3::unit_y());
let mrxz = mrz * mrx;
assert_approx_eq!(Vec3::unit_y(), mrxz * Vec3::unit_x());
assert_approx_eq!(Vec3::unit_z(), mrxz * Vec3::unit_y());
let qrzx = qrx * qrz;
assert_approx_eq!(Vec3::unit_z(), qrzx * Vec3::unit_x());
assert_approx_eq!(-Vec3::unit_x(), qrzx * Vec3::unit_y());
let mrzx = qrx * qrz;
assert_approx_eq!(Vec3::unit_z(), mrzx * Vec3::unit_x());
assert_approx_eq!(-Vec3::unit_x(), mrzx * Vec3::unit_y());
}
#[test]
fn test_quat_funcs() {
let q0 = Quat::from_rotation_ypr(deg(45.0), deg(180.0), deg(90.0));
assert!(q0.is_normalized());
assert_approx_eq!(q0.length_squared(), 1.0);
assert_approx_eq!(q0.length(), 1.0);
assert_approx_eq!(q0.length_reciprocal(), 1.0);
assert_approx_eq!(q0, q0.normalize());
assert_approx_eq!(q0.dot(q0), 1.0);
assert_approx_eq!(q0.dot(q0), 1.0);
let q1 = Quat::from(Vec4::from(q0) * 2.0);
assert!(!q1.is_normalized());
assert_approx_eq!(q1.length_squared(), 4.0, 1.0e-6);
assert_approx_eq!(q1.length(), 2.0);
assert_approx_eq!(q1.length_reciprocal(), 0.5);
assert_approx_eq!(q0, q1.normalize());
assert_approx_eq!(q0.dot(q1), 2.0, 1.0e-6);
}
#[test]
fn test_quat_lerp() {
let q0 = Quat::from_rotation_y(deg(0.0));
let q1 = Quat::from_rotation_y(deg(90.0));
assert_approx_eq!(q0, q0.lerp(q1, 0.0));
assert_approx_eq!(q1, q0.lerp(q1, 1.0));
assert_approx_eq!(Quat::from_rotation_y(deg(45.0)), q0.lerp(q1, 0.5));
}
#[test]
fn test_quat_slerp() {
let q0 = Quat::from_rotation_y(deg(0.0));
let q1 = Quat::from_rotation_y(deg(90.0));
assert_approx_eq!(q0, q0.slerp(q1, 0.0), 1.0e-3);
assert_approx_eq!(q1, q0.slerp(q1, 1.0), 1.0e-3);
assert_approx_eq!(Quat::from_rotation_y(deg(45.0)), q0.slerp(q1, 0.5), 1.0e-3);
}
#[test]
fn test_quat_slerp_constant_speed() {
let step = 0.01;
let mut s = 0.0;
while s <= 1.0 {
let q0 = Quat::from_rotation_y(deg(0.0));
let q1 = Quat::from_rotation_y(deg(90.0));
assert_approx_eq!(
Quat::from_rotation_y(deg(s * 90.0)),
q0.slerp(q1, s),
1.0e-3
);
s += step;
}
}
#[test]
fn test_quat_fmt() {
let a = Quat::identity();
#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
assert_eq!(format!("{:?}", a), "Quat(__m128(0.0, 0.0, 0.0, 1.0))");
#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
assert_eq!(format!("{:?}", a), "Quat(0.0, 0.0, 0.0, 1.0)");
// assert_eq!(
// format!("{:#?}", a),
// "Quat(\n 1.0,\n 2.0,\n 3.0,\n 4.0\n)"
// );
assert_eq!(format!("{}", a), "[0, 0, 0, 1]");
}
#[test]
fn test_quat_identity() {
let identity = Quat::identity();
assert!(identity.is_near_identity());
assert!(identity.is_normalized());
assert_eq!(identity, Quat::from_xyzw(0.0, 0.0, 0.0, 1.0));
assert_eq!(identity, identity * identity);
let q = Quat::from_rotation_ypr(deg(10.0), deg(-10.0), deg(45.0));
assert_eq!(q, q * identity);
assert_eq!(q, identity * q);
assert_eq!(identity, Quat::default());
}
#[test]
fn test_quat_slice() {
let a: [f32; 4] = Quat::from_rotation_ypr(deg(30.0), deg(60.0), deg(90.0)).into();
let b = Quat::from_slice_unaligned(&a);
let c: [f32; 4] = b.into();
assert_eq!(a, c);
let mut d = [0.0, 0.0, 0.0, 0.0];
b.write_to_slice_unaligned(&mut d[..]);
assert_eq!(a, d);
}
#[test]
fn test_quat_elements() {
let x = 1.0;
let y = 2.0;
let z = 3.0;
let w = 4.0;
let a = Quat::from_xyzw(x, y, z, w);
assert!(a.x() == x);
assert!(a.y() == y);
assert!(a.z() == z);
assert!(a.w() == w);
}
#[cfg(feature = "serde")]
#[test]
fn test_quat_serde() {
let a = Quat::from_xyzw(1.0, 2.0, 3.0, 4.0);
let serialized = serde_json::to_string(&a).unwrap();
assert_eq!(serialized, "[1.0,2.0,3.0,4.0]");
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(a, deserialized);
let deserialized = serde_json::from_str::<Quat>("[]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Quat>("[1.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Quat>("[1.0,2.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Quat>("[1.0,2.0,3.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Quat>("[1.0,2.0,3.0,4.0,5.0]");
assert!(deserialized.is_err());
}
#[cfg(feature = "rand")]
#[test]
fn test_quat_rand() {
use rand::{Rng, SeedableRng};
use rand_xoshiro::Xoshiro256Plus;
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
let a: Quat = rng1.gen();
assert!(a.is_normalized());
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
let b: Quat = rng2.gen();
assert_eq!(a, b);
}

View file

@ -0,0 +1,32 @@
#[macro_export]
macro_rules! assert_approx_eq {
($a:expr, $b:expr) => {{
#[allow(unused_imports)]
use support::FloatCompare;
let eps = core::f32::EPSILON;
let (a, b) = (&$a, &$b);
assert!(
a.approx_eq(b, eps),
"assertion failed: `(left !== right)` \
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
*a,
*b,
eps,
a.abs_diff(b)
);
}};
($a:expr, $b:expr, $eps:expr) => {{
use support::FloatCompare;
let (a, b) = (&$a, &$b);
let eps = $eps;
assert!(
a.approx_eq(b, $eps),
"assertion failed: `(left !== right)` \
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
*a,
*b,
eps,
a.abs_diff(b)
);
}};
}

View file

@ -0,0 +1,166 @@
#[macro_use]
mod macros;
use glam::{Mat2, Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
#[cfg(feature = "transform-types")]
use glam::{TransformRT, TransformSRT};
/// Helper function for migrating away from `glam::angle::deg`.
#[allow(dead_code)]
#[inline]
pub fn deg(angle: f32) -> f32 {
angle.to_radians()
}
/// Helper function for migrating away from `glam::angle::rad`.
#[allow(dead_code)]
#[inline]
pub fn rad(angle: f32) -> f32 {
angle
}
/// Trait used by the `assert_approx_eq` macro for floating point comparisons.
pub trait FloatCompare<Rhs: ?Sized = Self> {
/// Return true if the absolute difference between `self` and `other` is
/// less then or equal to `max_abs_diff`.
fn approx_eq(&self, other: &Rhs, max_abs_diff: f32) -> bool;
/// Returns the absolute difference of `self` and `other` which is printed
/// if `assert_approx_eq` fails.
fn abs_diff(&self, other: &Rhs) -> Rhs;
}
impl FloatCompare for f32 {
#[inline]
fn approx_eq(&self, other: &f32, max_abs_diff: f32) -> bool {
(self - other).abs() <= max_abs_diff
}
#[inline]
fn abs_diff(&self, other: &f32) -> f32 {
(self - other).abs()
}
}
impl FloatCompare for Mat2 {
#[inline]
fn approx_eq(&self, other: &Mat2, max_abs_diff: f32) -> bool {
self.abs_diff_eq(*other, max_abs_diff)
}
#[inline]
fn abs_diff(&self, other: &Mat2) -> Mat2 {
Mat2::from_cols(
(self.x_axis() - other.x_axis()).abs(),
(self.y_axis() - other.y_axis()).abs(),
)
}
}
impl FloatCompare for Mat3 {
#[inline]
fn approx_eq(&self, other: &Mat3, max_abs_diff: f32) -> bool {
self.abs_diff_eq(*other, max_abs_diff)
}
#[inline]
fn abs_diff(&self, other: &Mat3) -> Mat3 {
Mat3::from_cols(
(self.x_axis() - other.x_axis()).abs(),
(self.y_axis() - other.y_axis()).abs(),
(self.z_axis() - other.z_axis()).abs(),
)
}
}
impl FloatCompare for Mat4 {
#[inline]
fn approx_eq(&self, other: &Mat4, max_abs_diff: f32) -> bool {
self.abs_diff_eq(*other, max_abs_diff)
}
#[inline]
fn abs_diff(&self, other: &Mat4) -> Mat4 {
Mat4::from_cols(
(self.x_axis() - other.x_axis()).abs(),
(self.y_axis() - other.y_axis()).abs(),
(self.z_axis() - other.z_axis()).abs(),
(self.w_axis() - other.w_axis()).abs(),
)
}
}
impl FloatCompare for Quat {
#[inline]
fn approx_eq(&self, other: &Quat, max_abs_diff: f32) -> bool {
self.abs_diff_eq(*other, max_abs_diff)
}
#[inline]
fn abs_diff(&self, other: &Quat) -> Quat {
let a: Vec4 = (*self).into();
let b: Vec4 = (*other).into();
(a - b).abs().into()
}
}
impl FloatCompare for Vec2 {
#[inline]
fn approx_eq(&self, other: &Vec2, max_abs_diff: f32) -> bool {
self.abs_diff_eq(*other, max_abs_diff)
}
#[inline]
fn abs_diff(&self, other: &Vec2) -> Vec2 {
(*self - *other).abs()
}
}
impl FloatCompare for Vec3 {
#[inline]
fn approx_eq(&self, other: &Vec3, max_abs_diff: f32) -> bool {
self.abs_diff_eq(*other, max_abs_diff)
}
#[inline]
fn abs_diff(&self, other: &Vec3) -> Vec3 {
(*self - *other).abs()
}
}
impl FloatCompare for Vec4 {
#[inline]
fn approx_eq(&self, other: &Vec4, max_abs_diff: f32) -> bool {
self.abs_diff_eq(*other, max_abs_diff)
}
#[inline]
fn abs_diff(&self, other: &Vec4) -> Vec4 {
(*self - *other).abs()
}
}
#[cfg(feature = "transform-types")]
impl FloatCompare for TransformSRT {
#[inline]
fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
self.abs_diff_eq(*other, max_abs_diff)
}
#[inline]
fn abs_diff(&self, other: &Self) -> Self {
Self::from_scale_rotation_translation(
self.scale.abs_diff(&other.scale),
self.rotation.abs_diff(&other.rotation),
self.translation.abs_diff(&other.translation),
)
}
}
#[cfg(feature = "transform-types")]
impl FloatCompare for TransformRT {
#[inline]
fn approx_eq(&self, other: &Self, max_abs_diff: f32) -> bool {
self.abs_diff_eq(*other, max_abs_diff)
}
#[inline]
fn abs_diff(&self, other: &Self) -> Self {
Self::from_rotation_translation(
self.rotation.abs_diff(&other.rotation),
self.translation.abs_diff(&other.translation),
)
}
}

View file

@ -0,0 +1,88 @@
#[cfg(feature = "transform-types")]
#[macro_use]
mod support;
#[cfg(feature = "transform-types")]
mod transform {
use super::support;
use glam::f32::*;
#[test]
fn test_identity() {
let tr = TransformRT::identity();
assert_eq!(tr.rotation, Quat::identity());
assert_eq!(tr.translation, Vec3::zero());
let srt = TransformSRT::identity();
assert_eq!(srt.scale, Vec3::one());
assert_eq!(srt.rotation, Quat::identity());
assert_eq!(srt.translation, Vec3::zero());
assert_eq!(srt, tr.into());
assert_eq!(TransformRT::identity(), TransformRT::default());
assert_eq!(TransformSRT::identity(), TransformSRT::default());
}
#[test]
fn test_new() {
let t = Vec3::new(1.0, 2.0, 3.0);
let r = Quat::from_rotation_y(90.0_f32.to_radians());
let s = Vec3::new(-1.0, -2.0, -3.0);
let tr = TransformRT::from_rotation_translation(r, t);
assert_eq!(tr.rotation, r);
assert_eq!(tr.translation, t);
let srt = TransformSRT::from_scale_rotation_translation(s, r, t);
assert_eq!(srt.scale, s);
assert_eq!(srt.rotation, r);
assert_eq!(srt.translation, t);
assert_eq!(tr, tr);
assert_eq!(srt, srt);
assert_eq!(srt, TransformSRT::from_transform_rt(s, &tr));
}
#[test]
fn test_mul() {
let tr = TransformRT::from_rotation_translation(
Quat::from_rotation_z(-90.0_f32.to_radians()),
Vec3::unit_x(),
);
let v0 = Vec3::unit_y();
let v1 = tr * v0;
assert_approx_eq!(v1, Vec3::unit_x() * 2.0);
assert_approx_eq!(v1, tr * v0);
let inv_tr = tr.inverse();
let v2 = inv_tr * v1;
assert_approx_eq!(v0, v2);
assert_eq!(tr * TransformRT::identity(), tr);
assert_approx_eq!(tr * inv_tr, TransformRT::identity());
assert_eq!(tr * TransformSRT::identity(), TransformSRT::from(tr));
assert_eq!(TransformSRT::identity() * tr, TransformSRT::from(tr));
let s = Vec3::splat(2.0);
let r = Quat::from_rotation_y(180.0_f32.to_radians());
let t = -Vec3::unit_y();
let srt = TransformSRT::from_scale_rotation_translation(s, r, t);
let v0 = Vec3::unit_x();
let v1 = srt * v0;
assert_approx_eq!(v1, (r * (v0 * s)) + t);
assert_approx_eq!(v1, srt * v0);
let inv_srt = srt.inverse();
let v2 = inv_srt * v1;
assert_approx_eq!(v0, v2);
assert_eq!(srt * TransformSRT::identity(), srt);
assert_eq!(srt * inv_srt, TransformSRT::identity());
// negative scale mul test
let s = Vec3::splat(-2.0);
let srt = TransformSRT::from_scale_rotation_translation(s, r, t);
let inv_srt = srt.inverse();
assert_eq!(srt * inv_srt, TransformSRT::identity());
}
}

View file

@ -0,0 +1,489 @@
mod support;
use glam::*;
use std::f32;
#[test]
fn test_vec2_align() {
use core::mem;
assert_eq!(8, mem::size_of::<Vec2>());
assert_eq!(4, mem::align_of::<Vec2>());
assert_eq!(8, mem::size_of::<Vec2Mask>());
assert_eq!(4, mem::align_of::<Vec2Mask>());
}
#[test]
fn test_vec2_new() {
let v = vec2(1.0, 2.0);
assert_eq!(v.x(), 1.0);
assert_eq!(v.y(), 2.0);
let t = (1.0, 2.0);
let v = Vec2::from(t);
assert_eq!(t, v.into());
let a = [1.0, 2.0];
let v = Vec2::from(a);
let a1: [f32; 2] = v.into();
assert_eq!(a, a1);
let v = Vec2::new(t.0, t.1);
assert_eq!(t, v.into());
assert_eq!(Vec2::new(1.0, 0.0), Vec2::unit_x());
assert_eq!(Vec2::new(0.0, 1.0), Vec2::unit_y());
}
#[test]
fn test_vec2_fmt() {
let a = Vec2::new(1.0, 2.0);
assert_eq!(format!("{:?}", a), "Vec2(1.0, 2.0)");
// assert_eq!(format!("{:#?}", a), "Vec2(\n 1.0,\n 2.0\n)");
assert_eq!(format!("{}", a), "[1, 2]");
}
#[test]
fn test_vec2_zero() {
let v = Vec2::zero();
assert_eq!(vec2(0.0, 0.0), v);
assert_eq!(v, Vec2::default());
}
#[test]
fn test_vec2_splat() {
let v = Vec2::splat(1.0);
assert_eq!(vec2(1.0, 1.0), v);
}
#[test]
fn test_vec2_accessors() {
let mut a = Vec2::zero();
a.set_x(1.0);
a.set_y(2.0);
assert_eq!(1.0, a.x());
assert_eq!(2.0, a.y());
assert_eq!(Vec2::new(1.0, 2.0), a);
let mut a = Vec2::zero();
*a.x_mut() = 1.0;
*a.y_mut() = 2.0;
assert_eq!(1.0, a.x());
assert_eq!(2.0, a.y());
assert_eq!(Vec2::new(1.0, 2.0), a);
let mut a = Vec2::zero();
a[0] = 1.0;
a[1] = 2.0;
assert_eq!(1.0, a[0]);
assert_eq!(2.0, a[1]);
assert_eq!(Vec2::new(1.0, 2.0), a);
}
#[test]
fn test_vec2_funcs() {
let x = vec2(1.0, 0.0);
let y = vec2(0.0, 1.0);
assert_eq!(1.0, x.dot(x));
assert_eq!(0.0, x.dot(y));
assert_eq!(-1.0, x.dot(-x));
assert_eq!(4.0, (2.0 * x).length_squared());
assert_eq!(9.0, (-3.0 * y).length_squared());
assert_eq!(2.0, (-2.0 * x).length());
assert_eq!(3.0, (3.0 * y).length());
assert_eq!(x, (2.0 * x).normalize());
assert_eq!(1.0 * 3.0 + 2.0 * 4.0, vec2(1.0, 2.0).dot(vec2(3.0, 4.0)));
assert_eq!(2.0 * 2.0 + 3.0 * 3.0, vec2(2.0, 3.0).length_squared());
assert_eq!((2.0_f32 * 2.0 + 3.0 * 3.0).sqrt(), vec2(2.0, 3.0).length());
assert_eq!(
1.0 / (2.0_f32 * 2.0 + 3.0 * 3.0).sqrt(),
vec2(2.0, 3.0).length_reciprocal()
);
assert!(vec2(2.0, 3.0).normalize().is_normalized());
assert_eq!(
vec2(2.0, 3.0) / (2.0_f32 * 2.0 + 3.0 * 3.0).sqrt(),
vec2(2.0, 3.0).normalize()
);
assert_eq!(vec2(0.5, 0.25), vec2(2.0, 4.0).reciprocal());
}
#[test]
fn test_vec2_ops() {
let a = vec2(1.0, 2.0);
assert_eq!(vec2(2.0, 4.0), (a + a));
assert_eq!(vec2(0.0, 0.0), (a - a));
assert_eq!(vec2(1.0, 4.0), (a * a));
assert_eq!(vec2(2.0, 4.0), (a * 2.0));
assert_eq!(vec2(1.0, 1.0), (a / a));
assert_eq!(vec2(0.5, 1.0), (a / 2.0));
assert_eq!(vec2(-1.0, -2.0), (-a));
}
#[test]
fn test_vec2_assign_ops() {
let a = vec2(1.0, 2.0);
let mut b = a;
b += a;
assert_eq!(vec2(2.0, 4.0), b);
b -= a;
assert_eq!(vec2(1.0, 2.0), b);
b *= a;
assert_eq!(vec2(1.0, 4.0), b);
b /= a;
assert_eq!(vec2(1.0, 2.0), b);
b *= 2.0;
assert_eq!(vec2(2.0, 4.0), b);
b /= 2.0;
assert_eq!(vec2(1.0, 2.0), b);
}
#[test]
fn test_vec2_min_max() {
let a = vec2(-1.0, 2.0);
let b = vec2(1.0, -2.0);
assert_eq!(vec2(-1.0, -2.0), a.min(b));
assert_eq!(vec2(-1.0, -2.0), b.min(a));
assert_eq!(vec2(1.0, 2.0), a.max(b));
assert_eq!(vec2(1.0, 2.0), b.max(a));
}
#[test]
fn test_vec2_hmin_hmax() {
let a = vec2(-1.0, 2.0);
assert_eq!(-1.0, a.min_element());
assert_eq!(2.0, a.max_element());
}
#[test]
fn test_vec2_eq() {
let a = vec2(1.0, 1.0);
let b = vec2(1.0, 2.0);
assert!(a.cmpeq(a).all());
assert!(b.cmpeq(b).all());
assert!(a.cmpne(b).any());
assert!(b.cmpne(a).any());
assert!(b.cmpeq(a).any());
}
#[test]
fn test_vec2_cmp() {
assert!(!Vec2Mask::default().any());
assert!(!Vec2Mask::default().all());
assert_eq!(Vec2Mask::default().bitmask(), 0x0);
let a = vec2(-1.0, -1.0);
let b = vec2(1.0, 1.0);
let c = vec2(-1.0, -1.0);
let d = vec2(1.0, -1.0);
assert_eq!(a.cmplt(a).bitmask(), 0x0);
assert_eq!(a.cmplt(b).bitmask(), 0x3);
assert_eq!(a.cmplt(d).bitmask(), 0x1);
assert_eq!(c.cmple(a).bitmask(), 0x3);
assert!(a.cmplt(b).all());
assert!(a.cmplt(d).any());
assert!(a.cmple(b).all());
assert!(a.cmple(a).all());
assert!(b.cmpgt(a).all());
assert!(b.cmpge(a).all());
assert!(b.cmpge(b).all());
assert!(!(a.cmpge(d).all()));
assert!(c.cmple(c).all());
assert!(c.cmpge(c).all());
assert!(a == a);
assert!(a < b);
assert!(b > a);
}
#[test]
fn test_extend_truncate() {
let a = vec2(1.0, 2.0);
let b = a.extend(3.0);
assert_eq!(vec3(1.0, 2.0, 3.0), b);
}
#[test]
fn test_vec2b() {
// make sure the unused 'w' value doesn't break Vec2b behaviour
let a = Vec3::zero();
let mut b = a.truncate();
b.set_x(1.0);
b.set_y(1.0);
assert!(!b.cmpeq(Vec2::zero()).any());
assert!(b.cmpeq(Vec2::splat(1.0)).all());
}
#[test]
fn test_vec2mask_as_ref() {
assert_eq!(Vec2Mask::new(false, false).as_ref(), &[0, 0]);
assert_eq!(Vec2Mask::new(true, false).as_ref(), &[!0, 0]);
assert_eq!(Vec2Mask::new(false, true).as_ref(), &[0, !0]);
assert_eq!(Vec2Mask::new(true, true).as_ref(), &[!0, !0]);
}
#[test]
fn test_vec2mask_from() {
assert_eq!(Into::<[u32; 2]>::into(Vec2Mask::new(false, false)), [0, 0]);
assert_eq!(Into::<[u32; 2]>::into(Vec2Mask::new(true, false)), [!0, 0]);
assert_eq!(Into::<[u32; 2]>::into(Vec2Mask::new(false, true)), [0, !0]);
assert_eq!(Into::<[u32; 2]>::into(Vec2Mask::new(true, true)), [!0, !0]);
}
#[test]
fn test_vec2mask_bitmask() {
assert_eq!(Vec2Mask::new(false, false).bitmask(), 0b00);
assert_eq!(Vec2Mask::new(true, false).bitmask(), 0b01);
assert_eq!(Vec2Mask::new(false, true).bitmask(), 0b10);
assert_eq!(Vec2Mask::new(true, true).bitmask(), 0b11);
}
#[test]
fn test_vec2mask_any() {
assert_eq!(Vec2Mask::new(false, false).any(), false);
assert_eq!(Vec2Mask::new(true, false).any(), true);
assert_eq!(Vec2Mask::new(false, true).any(), true);
assert_eq!(Vec2Mask::new(true, true).any(), true);
}
#[test]
fn test_vec2mask_all() {
assert_eq!(Vec2Mask::new(false, false).all(), false);
assert_eq!(Vec2Mask::new(true, false).all(), false);
assert_eq!(Vec2Mask::new(false, true).all(), false);
assert_eq!(Vec2Mask::new(true, true).all(), true);
}
#[test]
fn test_vec2mask_select() {
let a = Vec2::new(1.0, 2.0);
let b = Vec2::new(3.0, 4.0);
assert_eq!(Vec2Mask::new(true, true).select(a, b), Vec2::new(1.0, 2.0),);
assert_eq!(Vec2Mask::new(true, false).select(a, b), Vec2::new(1.0, 4.0),);
assert_eq!(Vec2Mask::new(false, true).select(a, b), Vec2::new(3.0, 2.0),);
assert_eq!(
Vec2Mask::new(false, false).select(a, b),
Vec2::new(3.0, 4.0),
);
}
#[test]
fn test_vec2mask_and() {
assert_eq!(
(Vec2Mask::new(false, false) & Vec2Mask::new(false, false)).bitmask(),
0b00,
);
assert_eq!(
(Vec2Mask::new(true, true) & Vec2Mask::new(true, false)).bitmask(),
0b01,
);
assert_eq!(
(Vec2Mask::new(true, false) & Vec2Mask::new(false, true)).bitmask(),
0b00,
);
assert_eq!(
(Vec2Mask::new(true, true) & Vec2Mask::new(true, true)).bitmask(),
0b11,
);
let mut mask = Vec2Mask::new(true, true);
mask &= Vec2Mask::new(true, false);
assert_eq!(mask.bitmask(), 0b01);
}
#[test]
fn test_vec2mask_or() {
assert_eq!(
(Vec2Mask::new(false, false) | Vec2Mask::new(false, false)).bitmask(),
0b00,
);
assert_eq!(
(Vec2Mask::new(false, false) | Vec2Mask::new(false, true)).bitmask(),
0b10,
);
assert_eq!(
(Vec2Mask::new(true, false) | Vec2Mask::new(false, true)).bitmask(),
0b11,
);
assert_eq!(
(Vec2Mask::new(true, true) | Vec2Mask::new(true, true)).bitmask(),
0b11,
);
let mut mask = Vec2Mask::new(true, true);
mask |= Vec2Mask::new(true, false);
assert_eq!(mask.bitmask(), 0b11);
}
#[test]
fn test_vec2mask_not() {
assert_eq!((!Vec2Mask::new(false, false)).bitmask(), 0b11);
assert_eq!((!Vec2Mask::new(true, false)).bitmask(), 0b10);
assert_eq!((!Vec2Mask::new(false, true)).bitmask(), 0b01);
assert_eq!((!Vec2Mask::new(true, true)).bitmask(), 0b00);
}
#[test]
fn test_vec2mask_fmt() {
let a = Vec2Mask::new(true, false);
assert_eq!(format!("{:?}", a), "Vec2Mask(0xffffffff, 0x0)");
assert_eq!(format!("{}", a), "[true, false]");
}
#[test]
fn test_vec2mask_eq() {
let a = Vec2Mask::new(true, false);
let b = Vec2Mask::new(true, false);
let c = Vec2Mask::new(false, true);
assert_eq!(a, b);
assert_eq!(b, a);
assert_ne!(a, c);
assert_ne!(b, c);
assert!(a > c);
assert!(c < a);
}
#[test]
fn test_vec2mask_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
let a = Vec2Mask::new(true, false);
let b = Vec2Mask::new(true, false);
let c = Vec2Mask::new(false, true);
let mut hasher = DefaultHasher::new();
a.hash(&mut hasher);
let a_hashed = hasher.finish();
let mut hasher = DefaultHasher::new();
b.hash(&mut hasher);
let b_hashed = hasher.finish();
let mut hasher = DefaultHasher::new();
c.hash(&mut hasher);
let c_hashed = hasher.finish();
assert_eq!(a, b);
assert_eq!(a_hashed, b_hashed);
assert_ne!(a, c);
assert_ne!(a_hashed, c_hashed);
}
#[test]
fn test_vec2_sign() {
assert_eq!(Vec2::zero().sign(), Vec2::one());
assert_eq!(-Vec2::zero().sign(), -Vec2::one());
assert_eq!(Vec2::one().sign(), Vec2::one());
assert_eq!((-Vec2::one()).sign(), -Vec2::one());
assert_eq!(Vec2::splat(core::f32::NEG_INFINITY).sign(), -Vec2::one());
}
#[test]
fn test_vec2_abs() {
assert_eq!(Vec2::zero().abs(), Vec2::zero());
assert_eq!(Vec2::one().abs(), Vec2::one());
assert_eq!((-Vec2::one()).abs(), Vec2::one());
}
#[test]
fn test_vec2_round() {
assert_eq!(Vec2::new(1.35, 0.0).round().x(), 1.0);
assert_eq!(Vec2::new(0.0, 1.5).round().y(), 2.0);
assert_eq!(Vec2::new(0.0, -15.5).round().y(), -16.0);
assert_eq!(Vec2::new(0.0, 0.0).round().y(), 0.0);
assert_eq!(Vec2::new(0.0, 21.1).round().y(), 21.0);
assert_eq!(Vec2::new(0.0, 11.123).round().y(), 11.0);
assert_eq!(Vec2::new(0.0, 11.499).round().y(), 11.0);
assert_eq!(
Vec2::new(f32::NEG_INFINITY, f32::INFINITY).round(),
Vec2::new(f32::NEG_INFINITY, f32::INFINITY)
);
assert!(Vec2::new(f32::NAN, 0.0).round().x().is_nan());
}
#[test]
fn test_vec2_floor() {
assert_eq!(Vec2::new(1.35, -1.5).floor(), Vec2::new(1.0, -2.0));
assert_eq!(
Vec2::new(f32::INFINITY, f32::NEG_INFINITY).floor(),
Vec2::new(f32::INFINITY, f32::NEG_INFINITY)
);
assert!(Vec2::new(f32::NAN, 0.0).floor().x().is_nan());
assert_eq!(
Vec2::new(-2000000.123, 10000000.123).floor(),
Vec2::new(-2000001.0, 10000000.0)
);
}
#[test]
fn test_vec2_ceil() {
assert_eq!(Vec2::new(1.35, -1.5).ceil(), Vec2::new(2.0, -1.0));
assert_eq!(
Vec2::new(f32::INFINITY, f32::NEG_INFINITY).ceil(),
Vec2::new(f32::INFINITY, f32::NEG_INFINITY)
);
assert!(Vec2::new(f32::NAN, 0.0).ceil().x().is_nan());
assert_eq!(
Vec2::new(-2000000.123, 1000000.123).ceil(),
Vec2::new(-2000000.0, 1000001.0)
);
}
#[test]
fn test_vec2_lerp() {
let v0 = Vec2::new(-1.0, -1.0);
let v1 = Vec2::new(1.0, 1.0);
assert_approx_eq!(v0, v0.lerp(v1, 0.0));
assert_approx_eq!(v1, v0.lerp(v1, 1.0));
assert_approx_eq!(Vec2::zero(), v0.lerp(v1, 0.5));
}
#[test]
fn test_vec2_to_from_slice() {
let v = Vec2::new(1.0, 2.0);
let mut a = [0.0, 0.0];
v.write_to_slice_unaligned(&mut a);
assert_eq!(v, Vec2::from_slice_unaligned(&a));
}
#[test]
fn test_vec2_angle_between() {
let angle = Vec2::new(1.0, 0.0).angle_between(Vec2::new(0.0, 1.0));
assert_approx_eq!(f32::consts::FRAC_PI_2, angle, 1e-6);
let angle = Vec2::new(10.0, 0.0).angle_between(Vec2::new(0.0, 5.0));
assert_approx_eq!(f32::consts::FRAC_PI_2, angle, 1e-6);
let angle = Vec2::new(-1.0, 0.0).angle_between(Vec2::new(0.0, 1.0));
assert_approx_eq!(-f32::consts::FRAC_PI_2, angle, 1e-6);
}
#[cfg(feature = "serde")]
#[test]
fn test_vec2_serde() {
let a = Vec2::new(1.0, 2.0);
let serialized = serde_json::to_string(&a).unwrap();
assert_eq!(serialized, "[1.0,2.0]");
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(a, deserialized);
let deserialized = serde_json::from_str::<Vec2>("[]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Vec2>("[1.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Vec2>("[1.0,2.0,3.0]");
assert!(deserialized.is_err());
}
#[cfg(feature = "rand")]
#[test]
fn test_vec2_rand() {
use rand::{Rng, SeedableRng};
use rand_xoshiro::Xoshiro256Plus;
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
let a: (f32, f32) = rng1.gen();
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
let b: Vec2 = rng2.gen();
assert_eq!(a, b.into());
}

View file

@ -0,0 +1,598 @@
mod support;
use glam::*;
use std::f32;
#[test]
fn test_vec3_align() {
use std::mem;
if cfg!(any(feature = "packed-vec3", feature = "scalar-math")) {
assert_eq!(12, mem::size_of::<Vec3>());
assert_eq!(4, mem::align_of::<Vec3>());
assert_eq!(12, mem::size_of::<Vec3Mask>());
assert_eq!(4, mem::align_of::<Vec3Mask>());
} else {
assert_eq!(16, mem::size_of::<Vec3>());
assert_eq!(16, mem::align_of::<Vec3>());
assert_eq!(16, mem::size_of::<Vec3Mask>());
assert_eq!(16, mem::align_of::<Vec3Mask>());
}
}
#[test]
fn test_vec3_new() {
let v = vec3(1.0, 2.0, 3.0);
assert_eq!(v.x(), 1.0);
assert_eq!(v.y(), 2.0);
assert_eq!(v.z(), 3.0);
let t = (1.0, 2.0, 3.0);
let v = Vec3::from(t);
assert_eq!(t, v.into());
let a = [1.0, 2.0, 3.0];
let v = Vec3::from(a);
let a1: [f32; 3] = v.into();
assert_eq!(a, a1);
let v = Vec3::new(t.0, t.1, t.2);
assert_eq!(t, v.into());
assert_eq!(Vec3::new(1.0, 0.0, 0.0), Vec3::unit_x());
assert_eq!(Vec3::new(0.0, 1.0, 0.0), Vec3::unit_y());
assert_eq!(Vec3::new(0.0, 0.0, 1.0), Vec3::unit_z());
}
#[test]
fn test_vec3_fmt() {
let a = Vec3::new(1.0, 2.0, 3.0);
#[cfg(all(
target_feature = "sse2",
not(feature = "packed-vec3"),
not(feature = "scalar-math")
))]
assert_eq!(format!("{:?}", a), "Vec3(__m128(1.0, 2.0, 3.0, 3.0))");
#[cfg(any(
not(target_feature = "sse2"),
feature = "packed-vec3",
feature = "scalar-math"
))]
assert_eq!(format!("{:?}", a), "Vec3(1.0, 2.0, 3.0)");
// assert_eq!(format!("{:#?}", a), "Vec3(\n 1.0,\n 2.0,\n 3.0\n)");
assert_eq!(format!("{}", a), "[1, 2, 3]");
}
#[test]
fn test_vec3_zero() {
let v = Vec3::zero();
assert_eq!((0.0, 0.0, 0.0), v.into());
assert_eq!(v, Vec3::default());
}
#[test]
fn test_vec3_splat() {
let v = Vec3::splat(1.0);
assert_eq!((1.0, 1.0, 1.0), v.into());
}
#[test]
fn test_vec3_accessors() {
let mut a = Vec3::zero();
a.set_x(1.0);
a.set_y(2.0);
a.set_z(3.0);
assert_eq!(1.0, a.x());
assert_eq!(2.0, a.y());
assert_eq!(3.0, a.z());
assert_eq!((1.0, 2.0, 3.0), a.into());
let mut a = Vec3::zero();
*a.x_mut() = 1.0;
*a.y_mut() = 2.0;
*a.z_mut() = 3.0;
assert_eq!(1.0, a.x());
assert_eq!(2.0, a.y());
assert_eq!(3.0, a.z());
assert_eq!((1.0, 2.0, 3.0), a.into());
let mut a = Vec3::zero();
a[0] = 1.0;
a[1] = 2.0;
a[2] = 3.0;
assert_eq!(1.0, a[0]);
assert_eq!(2.0, a[1]);
assert_eq!(3.0, a[2]);
assert_eq!((1.0, 2.0, 3.0), a.into());
}
#[test]
fn test_vec3_funcs() {
let x = vec3(1.0, 0.0, 0.0);
let y = vec3(0.0, 1.0, 0.0);
let z = vec3(0.0, 0.0, 1.0);
assert_eq!(1.0, x.dot(x));
assert_eq!(0.0, x.dot(y));
assert_eq!(-1.0, z.dot(-z));
assert_eq!(y, z.cross(x));
assert_eq!(z, x.cross(y));
assert_eq!(4.0, (2.0 * x).length_squared());
assert_eq!(9.0, (-3.0 * y).length_squared());
assert_eq!(16.0, (4.0 * z).length_squared());
assert_eq!(2.0, (-2.0 * x).length());
assert_eq!(3.0, (3.0 * y).length());
assert_eq!(4.0, (-4.0 * z).length());
assert_eq!(x, (2.0 * x).normalize());
assert_eq!(
1.0 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0,
vec3(1.0, 2.0, 3.0).dot(vec3(4.0, 5.0, 6.0))
);
assert_eq!(
2.0 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0,
vec3(2.0, 3.0, 4.0).length_squared()
);
assert_eq!(
(2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0).sqrt(),
vec3(2.0, 3.0, 4.0).length()
);
assert_eq!(
1.0 / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0).sqrt(),
vec3(2.0, 3.0, 4.0).length_reciprocal()
);
assert!(vec3(2.0, 3.0, 4.0).normalize().is_normalized());
assert_approx_eq!(
vec3(2.0, 3.0, 4.0) / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0).sqrt(),
vec3(2.0, 3.0, 4.0).normalize()
);
assert_eq!(vec3(0.5, 0.25, 0.125), vec3(2.0, 4.0, 8.0).reciprocal());
}
#[test]
fn test_vec3_ops() {
let a = vec3(1.0, 2.0, 3.0);
assert_eq!((2.0, 4.0, 6.0), (a + a).into());
assert_eq!((0.0, 0.0, 0.0), (a - a).into());
assert_eq!((1.0, 4.0, 9.0), (a * a).into());
assert_eq!((2.0, 4.0, 6.0), (a * 2.0).into());
assert_eq!((1.0, 1.0, 1.0), (a / a).into());
assert_eq!((0.5, 1.0, 1.5), (a / 2.0).into());
assert_eq!((-1.0, -2.0, -3.0), (-a).into());
}
#[test]
fn test_vec3_assign_ops() {
let a = vec3(1.0, 2.0, 3.0);
let mut b = a;
b += a;
assert_eq!((2.0, 4.0, 6.0), b.into());
b -= a;
assert_eq!((1.0, 2.0, 3.0), b.into());
b *= a;
assert_eq!((1.0, 4.0, 9.0), b.into());
b /= a;
assert_eq!((1.0, 2.0, 3.0), b.into());
b *= 2.0;
assert_eq!((2.0, 4.0, 6.0), b.into());
b /= 2.0;
assert_eq!((1.0, 2.0, 3.0), b.into());
}
#[test]
fn test_vec3_min_max() {
let a = vec3(-1.0, 2.0, -3.0);
let b = vec3(1.0, -2.0, 3.0);
assert_eq!((-1.0, -2.0, -3.0), a.min(b).into());
assert_eq!((-1.0, -2.0, -3.0), b.min(a).into());
assert_eq!((1.0, 2.0, 3.0), a.max(b).into());
assert_eq!((1.0, 2.0, 3.0), b.max(a).into());
}
#[test]
fn test_vec3_hmin_hmax() {
let a = vec3(-1.0, 2.0, -3.0);
assert_eq!(-3.0, a.min_element());
assert_eq!(2.0, a.max_element());
}
#[test]
fn test_vec3_eq() {
let a = vec3(1.0, 1.0, 1.0);
let b = vec3(1.0, 2.0, 3.0);
assert!(a.cmpeq(a).all());
assert!(b.cmpeq(b).all());
assert!(a.cmpne(b).any());
assert!(b.cmpne(a).any());
assert!(b.cmpeq(a).any());
}
#[test]
fn test_vec3_cmp() {
assert!(!Vec3Mask::default().any());
assert!(!Vec3Mask::default().all());
assert_eq!(Vec3Mask::default().bitmask(), 0x0);
let a = vec3(-1.0, -1.0, -1.0);
let b = vec3(1.0, 1.0, 1.0);
let c = vec3(-1.0, -1.0, 1.0);
let d = vec3(1.0, -1.0, -1.0);
assert_eq!(a.cmplt(a).bitmask(), 0x0);
assert_eq!(a.cmplt(b).bitmask(), 0x7);
assert_eq!(a.cmplt(c).bitmask(), 0x4);
assert_eq!(c.cmple(a).bitmask(), 0x3);
assert_eq!(a.cmplt(d).bitmask(), 0x1);
assert!(a.cmplt(b).all());
assert!(a.cmplt(c).any());
assert!(a.cmple(b).all());
assert!(a.cmple(a).all());
assert!(b.cmpgt(a).all());
assert!(b.cmpge(a).all());
assert!(b.cmpge(b).all());
assert!(!(a.cmpge(c).all()));
assert!(c.cmple(c).all());
assert!(c.cmpge(c).all());
assert!(a == a);
assert!(a < b);
assert!(b > a);
}
#[test]
fn test_extend_truncate() {
let a = vec3(1.0, 2.0, 3.0);
let b = a.extend(4.0);
assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
let c = b.truncate();
assert_eq!(a, c);
}
#[test]
fn test_vec3b() {
// make sure the unused 'w' value doesn't break Vec3b behaviour
let a = Vec4::zero();
let mut b = a.truncate();
b.set_x(1.0);
b.set_y(1.0);
b.set_z(1.0);
assert!(!b.cmpeq(Vec3::zero()).any());
assert!(b.cmpeq(Vec3::splat(1.0)).all());
}
#[test]
fn test_vec3mask_as_ref() {
assert_eq!(Vec3Mask::new(false, false, false).as_ref(), &[0, 0, 0]);
assert_eq!(Vec3Mask::new(true, false, false).as_ref(), &[!0, 0, 0]);
assert_eq!(Vec3Mask::new(false, true, true).as_ref(), &[0, !0, !0]);
assert_eq!(Vec3Mask::new(false, true, false).as_ref(), &[0, !0, 0]);
assert_eq!(Vec3Mask::new(true, false, true).as_ref(), &[!0, 0, !0]);
assert_eq!(Vec3Mask::new(true, true, true).as_ref(), &[!0, !0, !0]);
}
#[test]
fn test_vec3mask_from() {
assert_eq!(
Into::<[u32; 3]>::into(Vec3Mask::new(false, false, false)),
[0, 0, 0]
);
assert_eq!(
Into::<[u32; 3]>::into(Vec3Mask::new(true, false, false)),
[!0, 0, 0]
);
assert_eq!(
Into::<[u32; 3]>::into(Vec3Mask::new(false, true, true)),
[0, !0, !0]
);
assert_eq!(
Into::<[u32; 3]>::into(Vec3Mask::new(false, true, false)),
[0, !0, 0]
);
assert_eq!(
Into::<[u32; 3]>::into(Vec3Mask::new(true, false, true)),
[!0, 0, !0]
);
assert_eq!(
Into::<[u32; 3]>::into(Vec3Mask::new(true, true, true)),
[!0, !0, !0]
);
}
#[test]
fn test_vec3mask_bitmask() {
assert_eq!(Vec3Mask::new(false, false, false).bitmask(), 0b000);
assert_eq!(Vec3Mask::new(true, false, false).bitmask(), 0b001);
assert_eq!(Vec3Mask::new(false, true, true).bitmask(), 0b110);
assert_eq!(Vec3Mask::new(false, true, false).bitmask(), 0b010);
assert_eq!(Vec3Mask::new(true, false, true).bitmask(), 0b101);
assert_eq!(Vec3Mask::new(true, true, true).bitmask(), 0b111);
}
#[test]
fn test_vec3mask_any() {
assert_eq!(Vec3Mask::new(false, false, false).any(), false);
assert_eq!(Vec3Mask::new(true, false, false).any(), true);
assert_eq!(Vec3Mask::new(false, true, false).any(), true);
assert_eq!(Vec3Mask::new(false, false, true).any(), true);
}
#[test]
fn test_vec3mask_all() {
assert_eq!(Vec3Mask::new(true, true, true).all(), true);
assert_eq!(Vec3Mask::new(false, true, true).all(), false);
assert_eq!(Vec3Mask::new(true, false, true).all(), false);
assert_eq!(Vec3Mask::new(true, true, false).all(), false);
}
#[test]
fn test_vec3mask_select() {
let a = Vec3::new(1.0, 2.0, 3.0);
let b = Vec3::new(4.0, 5.0, 6.0);
assert_eq!(
Vec3Mask::new(true, true, true).select(a, b),
Vec3::new(1.0, 2.0, 3.0),
);
assert_eq!(
Vec3Mask::new(true, false, true).select(a, b),
Vec3::new(1.0, 5.0, 3.0),
);
assert_eq!(
Vec3Mask::new(false, true, false).select(a, b),
Vec3::new(4.0, 2.0, 6.0),
);
assert_eq!(
Vec3Mask::new(false, false, false).select(a, b),
Vec3::new(4.0, 5.0, 6.0),
);
}
#[test]
fn test_vec3mask_and() {
assert_eq!(
(Vec3Mask::new(false, false, false) & Vec3Mask::new(false, false, false)).bitmask(),
0b000,
);
assert_eq!(
(Vec3Mask::new(true, true, true) & Vec3Mask::new(true, true, true)).bitmask(),
0b111,
);
assert_eq!(
(Vec3Mask::new(true, false, true) & Vec3Mask::new(false, true, false)).bitmask(),
0b000,
);
assert_eq!(
(Vec3Mask::new(true, false, true) & Vec3Mask::new(true, true, true)).bitmask(),
0b101,
);
let mut mask = Vec3Mask::new(true, true, false);
mask &= Vec3Mask::new(true, false, false);
assert_eq!(mask.bitmask(), 0b001);
}
#[test]
fn test_vec3mask_or() {
assert_eq!(
(Vec3Mask::new(false, false, false) | Vec3Mask::new(false, false, false)).bitmask(),
0b000,
);
assert_eq!(
(Vec3Mask::new(true, true, true) | Vec3Mask::new(true, true, true)).bitmask(),
0b111,
);
assert_eq!(
(Vec3Mask::new(true, false, true) | Vec3Mask::new(false, true, false)).bitmask(),
0b111,
);
assert_eq!(
(Vec3Mask::new(true, false, true) | Vec3Mask::new(true, false, true)).bitmask(),
0b101,
);
let mut mask = Vec3Mask::new(true, true, false);
mask |= Vec3Mask::new(true, false, false);
assert_eq!(mask.bitmask(), 0b011);
}
#[test]
fn test_vec3mask_not() {
assert_eq!((!Vec3Mask::new(false, false, false)).bitmask(), 0b111);
assert_eq!((!Vec3Mask::new(true, true, true)).bitmask(), 0b000);
assert_eq!((!Vec3Mask::new(true, false, true)).bitmask(), 0b010);
assert_eq!((!Vec3Mask::new(false, true, false)).bitmask(), 0b101);
}
#[test]
fn test_vec3mask_fmt() {
let a = Vec3Mask::new(true, false, false);
// debug fmt
#[cfg(all(
target_feature = "sse2",
not(feature = "packed-vec3"),
not(feature = "scalar-math")
))]
assert_eq!(format!("{:?}", a), "Vec3Mask(0xffffffff, 0x0, 0x0)");
#[cfg(any(
not(target_feature = "sse2"),
feature = "packed-vec3",
feature = "scalar-math"
))]
assert_eq!(format!("{:?}", a), "Vec3Mask(0xffffffff, 0x0, 0x0)");
// display fmt
#[cfg(all(
target_feature = "sse2",
not(feature = "packed-vec3"),
not(feature = "scalar-math")
))]
assert_eq!(format!("{}", a), "[true, false, false]");
#[cfg(any(
not(target_feature = "sse2"),
feature = "packed-vec3",
feature = "scalar-math"
))]
assert_eq!(format!("{}", a), "[true, false, false]");
}
#[test]
fn test_vec3mask_eq() {
let a = Vec3Mask::new(true, false, true);
let b = Vec3Mask::new(true, false, true);
let c = Vec3Mask::new(false, true, true);
assert_eq!(a, b);
assert_eq!(b, a);
assert_ne!(a, c);
assert_ne!(b, c);
assert!(a > c);
assert!(c < a);
}
#[test]
fn test_vec3mask_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
let a = Vec3Mask::new(true, false, true);
let b = Vec3Mask::new(true, false, true);
let c = Vec3Mask::new(false, true, true);
let mut hasher = DefaultHasher::new();
a.hash(&mut hasher);
let a_hashed = hasher.finish();
let mut hasher = DefaultHasher::new();
b.hash(&mut hasher);
let b_hashed = hasher.finish();
let mut hasher = DefaultHasher::new();
c.hash(&mut hasher);
let c_hashed = hasher.finish();
assert_eq!(a, b);
assert_eq!(a_hashed, b_hashed);
assert_ne!(a, c);
assert_ne!(a_hashed, c_hashed);
}
#[test]
fn test_vec3_sign() {
assert_eq!(Vec3::zero().sign(), Vec3::one());
assert_eq!(-Vec3::zero().sign(), -Vec3::one());
assert_eq!(Vec3::one().sign(), Vec3::one());
assert_eq!((-Vec3::one()).sign(), -Vec3::one());
assert_eq!(Vec3::splat(core::f32::NEG_INFINITY).sign(), -Vec3::one());
}
#[test]
fn test_vec3_abs() {
assert_eq!(Vec3::zero().abs(), Vec3::zero());
assert_eq!(Vec3::one().abs(), Vec3::one());
assert_eq!((-Vec3::one()).abs(), Vec3::one());
}
#[test]
fn test_vec3_round() {
assert_eq!(Vec3::new(1.35, 0.0, 0.0).round().x(), 1.0);
assert_eq!(Vec3::new(0.0, 1.5, 0.0).round().y(), 2.0);
assert_eq!(Vec3::new(0.0, 0.0, -15.5).round().z(), -16.0);
assert_eq!(Vec3::new(0.0, 0.0, 0.0).round().z(), 0.0);
assert_eq!(Vec3::new(0.0, 21.1, 0.0).round().y(), 21.0);
assert_eq!(Vec3::new(0.0, 11.123, 0.0).round().y(), 11.0);
assert_eq!(Vec3::new(0.0, 11.499, 0.0).round().y(), 11.0);
assert_eq!(
Vec3::new(f32::NEG_INFINITY, f32::INFINITY, 0.0).round(),
Vec3::new(f32::NEG_INFINITY, f32::INFINITY, 0.0)
);
assert!(Vec3::new(f32::NAN, 0.0, 0.0).round().x().is_nan());
}
#[test]
fn test_vec3_floor() {
assert_eq!(
Vec3::new(1.35, 1.5, -1.5).floor(),
Vec3::new(1.0, 1.0, -2.0)
);
assert_eq!(
Vec3::new(f32::INFINITY, f32::NEG_INFINITY, 0.0).floor(),
Vec3::new(f32::INFINITY, f32::NEG_INFINITY, 0.0)
);
assert!(Vec3::new(f32::NAN, 0.0, 0.0).floor().x().is_nan());
assert_eq!(
Vec3::new(-2000000.123, 10000000.123, 1000.9).floor(),
Vec3::new(-2000001.0, 10000000.0, 1000.0)
);
}
#[test]
fn test_vec3_ceil() {
assert_eq!(Vec3::new(1.35, 1.5, -1.5).ceil(), Vec3::new(2.0, 2.0, -1.0));
assert_eq!(
Vec3::new(f32::INFINITY, f32::NEG_INFINITY, 0.0).ceil(),
Vec3::new(f32::INFINITY, f32::NEG_INFINITY, 0.0)
);
assert!(Vec3::new(f32::NAN, 0.0, 0.0).ceil().x().is_nan());
assert_eq!(
Vec3::new(-2000000.123, 1000000.123, 1000.9).ceil(),
Vec3::new(-2000000.0, 1000001.0, 1001.0)
);
}
#[test]
fn test_vec3_lerp() {
let v0 = Vec3::new(-1.0, -1.0, -1.0);
let v1 = Vec3::new(1.0, 1.0, 1.0);
assert_approx_eq!(v0, v0.lerp(v1, 0.0));
assert_approx_eq!(v1, v0.lerp(v1, 1.0));
assert_approx_eq!(Vec3::zero(), v0.lerp(v1, 0.5));
}
#[test]
fn test_vec3_to_from_slice() {
let v = Vec3::new(1.0, 2.0, 3.0);
let mut a = [0.0, 0.0, 0.0];
v.write_to_slice_unaligned(&mut a);
assert_eq!(v, Vec3::from_slice_unaligned(&a));
}
#[test]
fn test_vec3_angle_between() {
let angle = Vec3::new(1.0, 0.0, 1.0).angle_between(Vec3::new(1.0, 1.0, 0.0));
assert_approx_eq!(f32::consts::FRAC_PI_3, angle, 1e-6);
let angle = Vec3::new(10.0, 0.0, 10.0).angle_between(Vec3::new(5.0, 5.0, 0.0));
assert_approx_eq!(f32::consts::FRAC_PI_3, angle, 1e-6);
let angle = Vec3::new(-1.0, 0.0, -1.0).angle_between(Vec3::new(1.0, -1.0, 0.0));
assert_approx_eq!(2.0 * f32::consts::FRAC_PI_3, angle, 1e-6);
}
#[cfg(feature = "serde")]
#[test]
fn test_vec3_serde() {
let a = Vec3::new(1.0, 2.0, 3.0);
let serialized = serde_json::to_string(&a).unwrap();
assert_eq!(serialized, "[1.0,2.0,3.0]");
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(a, deserialized);
let deserialized = serde_json::from_str::<Vec3>("[]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Vec3>("[1.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Vec3>("[1.0,2.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Vec3>("[1.0,2.0,3.0,4.0]");
assert!(deserialized.is_err());
}
#[cfg(feature = "rand")]
#[test]
fn test_vec3_rand() {
use rand::{Rng, SeedableRng};
use rand_xoshiro::Xoshiro256Plus;
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
let a: (f32, f32, f32) = rng1.gen();
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
let b: Vec3 = rng2.gen();
assert_eq!(a, b.into());
}

View file

@ -0,0 +1,620 @@
mod support;
use glam::*;
use std::f32;
#[test]
fn test_vec4_align() {
use std::mem;
assert_eq!(16, mem::size_of::<Vec4>());
assert_eq!(16, mem::size_of::<Vec4Mask>());
if cfg!(feature = "scalar-math") {
assert_eq!(4, mem::align_of::<Vec4>());
assert_eq!(4, mem::align_of::<Vec4Mask>());
} else {
assert_eq!(16, mem::align_of::<Vec4>());
assert_eq!(16, mem::align_of::<Vec4Mask>());
}
}
#[test]
fn test_vec4_new() {
let v = vec4(1.0, 2.0, 3.0, 4.0);
assert_eq!(v.x(), 1.0);
assert_eq!(v.y(), 2.0);
assert_eq!(v.z(), 3.0);
assert_eq!(v.w(), 4.0);
let t = (1.0, 2.0, 3.0, 4.0);
let v = Vec4::from(t);
assert_eq!(t, v.into());
let a = [1.0, 2.0, 3.0, 4.0];
let v = Vec4::from(a);
let a1: [f32; 4] = v.into();
assert_eq!(a, a1);
let v = Vec4::new(t.0, t.1, t.2, t.3);
assert_eq!(t, v.into());
assert_eq!(Vec4::new(1.0, 0.0, 0.0, 0.0), Vec4::unit_x());
assert_eq!(Vec4::new(0.0, 1.0, 0.0, 0.0), Vec4::unit_y());
assert_eq!(Vec4::new(0.0, 0.0, 1.0, 0.0), Vec4::unit_z());
assert_eq!(Vec4::new(0.0, 0.0, 0.0, 1.0), Vec4::unit_w());
}
#[test]
fn test_vec4_fmt() {
let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
assert_eq!(format!("{:?}", a), "Vec4(__m128(1.0, 2.0, 3.0, 4.0))");
#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
assert_eq!(format!("{:?}", a), "Vec4(1.0, 2.0, 3.0, 4.0)");
// assert_eq!(
// format!("{:#?}", a),
// "Vec4(\n 1.0,\n 2.0,\n 3.0,\n 4.0\n)"
// );
assert_eq!(format!("{}", a), "[1, 2, 3, 4]");
}
#[test]
fn test_vec4_zero() {
let v = Vec4::zero();
assert_eq!((0.0, 0.0, 0.0, 0.0), v.into());
assert_eq!(v, Vec4::default());
}
#[test]
fn test_vec4_splat() {
let v = Vec4::splat(1.0);
assert_eq!((1.0, 1.0, 1.0, 1.0), v.into());
}
#[test]
fn test_vec4_accessors() {
let mut a = Vec4::zero();
a.set_x(1.0);
a.set_y(2.0);
a.set_z(3.0);
a.set_w(4.0);
assert_eq!(1.0, a.x());
assert_eq!(2.0, a.y());
assert_eq!(3.0, a.z());
assert_eq!(4.0, a.w());
assert_eq!((1.0, 2.0, 3.0, 4.0), a.into());
let mut a = Vec4::zero();
*a.x_mut() = 1.0;
*a.y_mut() = 2.0;
*a.z_mut() = 3.0;
*a.w_mut() = 4.0;
assert_eq!(1.0, a.x());
assert_eq!(2.0, a.y());
assert_eq!(3.0, a.z());
assert_eq!(4.0, a.w());
assert_eq!((1.0, 2.0, 3.0, 4.0), a.into());
let mut a = Vec4::zero();
a[0] = 1.0;
a[1] = 2.0;
a[2] = 3.0;
a[3] = 4.0;
assert_eq!(1.0, a[0]);
assert_eq!(2.0, a[1]);
assert_eq!(3.0, a[2]);
assert_eq!(4.0, a[3]);
assert_eq!((1.0, 2.0, 3.0, 4.0), a.into());
}
#[test]
fn test_vec4_funcs() {
let x = vec4(1.0, 0.0, 0.0, 0.0);
let y = vec4(0.0, 1.0, 0.0, 0.0);
let z = vec4(0.0, 0.0, 1.0, 0.0);
let w = vec4(0.0, 0.0, 0.0, 1.0);
assert_eq!(1.0, x.dot(x));
assert_eq!(0.0, x.dot(y));
assert_eq!(-1.0, z.dot(-z));
assert_eq!(4.0, (2.0 * x).length_squared());
assert_eq!(9.0, (-3.0 * y).length_squared());
assert_eq!(16.0, (4.0 * z).length_squared());
assert_eq!(64.0, (8.0 * w).length_squared());
assert_eq!(2.0, (-2.0 * x).length());
assert_eq!(3.0, (3.0 * y).length());
assert_eq!(4.0, (-4.0 * z).length());
assert_eq!(5.0, (-5.0 * w).length());
assert_eq!(x, (2.0 * x).normalize());
assert_eq!(
1.0 * 5.0 + 2.0 * 6.0 + 3.0 * 7.0 + 4.0 * 8.0,
vec4(1.0, 2.0, 3.0, 4.0).dot(vec4(5.0, 6.0, 7.0, 8.0))
);
assert_eq!(
2.0 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0,
vec4(2.0, 3.0, 4.0, 5.0).length_squared()
);
assert_eq!(
(2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0).sqrt(),
vec4(2.0, 3.0, 4.0, 5.0).length()
);
assert_eq!(
1.0 / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0).sqrt(),
vec4(2.0, 3.0, 4.0, 5.0).length_reciprocal()
);
assert!(vec4(2.0, 3.0, 4.0, 5.0).normalize().is_normalized());
assert_approx_eq!(
vec4(2.0, 3.0, 4.0, 5.0) / (2.0_f32 * 2.0 + 3.0 * 3.0 + 4.0 * 4.0 + 5.0 * 5.0).sqrt(),
vec4(2.0, 3.0, 4.0, 5.0).normalize()
);
assert_eq!(
vec4(0.5, 0.25, 0.125, 0.0625),
vec4(2.0, 4.0, 8.0, 16.0).reciprocal()
);
}
#[test]
fn test_vec4_ops() {
let a = vec4(1.0, 2.0, 3.0, 4.0);
assert_eq!((2.0, 4.0, 6.0, 8.0), (a + a).into());
assert_eq!((0.0, 0.0, 0.0, 0.0), (a - a).into());
assert_eq!((1.0, 4.0, 9.0, 16.0), (a * a).into());
assert_eq!((2.0, 4.0, 6.0, 8.0), (a * 2.0).into());
assert_eq!((2.0, 4.0, 6.0, 8.0), (2.0 * a).into());
assert_eq!((1.0, 1.0, 1.0, 1.0), (a / a).into());
assert_eq!((0.5, 1.0, 1.5, 2.0), (a / 2.0).into());
// is this a sensible operator?
// assert_eq!((1.0, 0.5, 1.0/3.0, 0.25), (1.0 / a).into());
assert_eq!((-1.0, -2.0, -3.0, -4.0), (-a).into());
}
#[test]
fn test_vec4_assign_ops() {
let a = vec4(1.0, 2.0, 3.0, 4.0);
let mut b = a;
b += a;
assert_eq!((2.0, 4.0, 6.0, 8.0), b.into());
b -= a;
assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
b *= a;
assert_eq!((1.0, 4.0, 9.0, 16.0), b.into());
b /= a;
assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
b *= 2.0;
assert_eq!((2.0, 4.0, 6.0, 8.0), b.into());
b /= 2.0;
assert_eq!((1.0, 2.0, 3.0, 4.0), b.into());
}
#[test]
fn test_vec4_min_max() {
let a = vec4(-1.0, 2.0, -3.0, 4.0);
let b = vec4(1.0, -2.0, 3.0, -4.0);
assert_eq!((-1.0, -2.0, -3.0, -4.0), a.min(b).into());
assert_eq!((-1.0, -2.0, -3.0, -4.0), b.min(a).into());
assert_eq!((1.0, 2.0, 3.0, 4.0), a.max(b).into());
assert_eq!((1.0, 2.0, 3.0, 4.0), b.max(a).into());
}
#[test]
fn test_vec4_hmin_hmax() {
let a = vec4(-1.0, 4.0, -3.0, 2.0);
assert_eq!(-3.0, a.min_element());
assert_eq!(4.0, a.max_element());
assert_eq!(3.0, vec4(1.0, 2.0, 3.0, 4.0).truncate().max_element());
assert_eq!(-3.0, vec4(-1.0, -2.0, -3.0, -4.0).truncate().min_element());
}
#[test]
fn test_vec4_eq() {
let a = vec4(1.0, 1.0, 1.0, 1.0);
let b = vec4(1.0, 2.0, 3.0, 4.0);
assert!(a.cmpeq(a).all());
assert!(b.cmpeq(b).all());
assert!(a.cmpne(b).any());
assert!(b.cmpne(a).any());
assert!(b.cmpeq(a).any());
}
#[test]
fn test_vec4_cmp() {
assert!(!Vec4Mask::default().any());
assert!(!Vec4Mask::default().all());
assert_eq!(Vec4Mask::default().bitmask(), 0x0);
let a = vec4(-1.0, -1.0, -1.0, -1.0);
let b = vec4(1.0, 1.0, 1.0, 1.0);
let c = vec4(-1.0, -1.0, 1.0, 1.0);
let d = vec4(1.0, -1.0, -1.0, 1.0);
assert_eq!(a.cmplt(a).bitmask(), 0x0);
assert_eq!(a.cmplt(b).bitmask(), 0xf);
assert_eq!(a.cmplt(c).bitmask(), 0xc);
assert_eq!(c.cmple(a).bitmask(), 0x3);
assert_eq!(a.cmplt(d).bitmask(), 0x9);
assert!(a.cmplt(b).all());
assert!(a.cmplt(c).any());
assert!(a.cmple(b).all());
assert!(a.cmple(a).all());
assert!(b.cmpgt(a).all());
assert!(b.cmpge(a).all());
assert!(b.cmpge(b).all());
assert!(!(a.cmpge(c).all()));
assert!(c.cmple(c).all());
assert!(c.cmpge(c).all());
assert!(a.cmpeq(a).all());
assert!(!a.cmpeq(b).all());
assert!(a.cmpeq(c).any());
assert!(!a.cmpne(a).all());
assert!(a.cmpne(b).all());
assert!(a.cmpne(c).any());
assert!(a == a);
assert!(a < b);
assert!(b > a);
}
#[test]
fn test_vec4_slice() {
let a = [1.0, 2.0, 3.0, 4.0];
let b = Vec4::from_slice_unaligned(&a);
let c: [f32; 4] = b.into();
assert_eq!(a, c);
let mut d = [0.0, 0.0, 0.0, 0.0];
b.write_to_slice_unaligned(&mut d[..]);
assert_eq!(a, d);
}
#[test]
fn test_vec4_sign() {
assert_eq!(Vec4::zero().sign(), Vec4::one());
assert_eq!(-Vec4::zero().sign(), -Vec4::one());
assert_eq!(Vec4::one().sign(), Vec4::one());
assert_eq!((-Vec4::one()).sign(), -Vec4::one());
assert_eq!(Vec4::splat(core::f32::NEG_INFINITY).sign(), -Vec4::one());
}
#[test]
fn test_vec4_abs() {
assert_eq!(Vec4::zero().abs(), Vec4::zero());
assert_eq!(Vec4::one().abs(), Vec4::one());
assert_eq!((-Vec4::one()).abs(), Vec4::one());
}
// #[test]
// fn dup_element() {
// let a = vec4(1.0, 2.0, 3.0, 4.0);
// assert_eq!(vec4(1.0, 1.0, 1.0, 1.0), a.dup_x());
// assert_eq!(vec4(2.0, 2.0, 2.0, 2.0), a.dup_y());
// assert_eq!(vec4(3.0, 3.0, 3.0, 3.0), a.dup_z());
// assert_eq!(vec4(4.0, 4.0, 4.0, 4.0), a.dup_w());
// }
#[test]
fn test_vec4mask_as_ref() {
assert_eq!(
Vec4Mask::new(false, false, false, false).as_ref(),
&[0, 0, 0, 0]
);
assert_eq!(
Vec4Mask::new(false, false, true, true).as_ref(),
&[0, 0, !0, !0]
);
assert_eq!(
Vec4Mask::new(true, true, false, false).as_ref(),
&[!0, !0, 0, 0]
);
assert_eq!(
Vec4Mask::new(false, true, false, true).as_ref(),
&[0, !0, 0, !0]
);
assert_eq!(
Vec4Mask::new(true, false, true, false).as_ref(),
&[!0, 0, !0, 0]
);
assert_eq!(
Vec4Mask::new(true, true, true, true).as_ref(),
&[!0, !0, !0, !0]
);
}
#[test]
fn test_vec4mask_from() {
assert_eq!(
Into::<[u32; 4]>::into(Vec4Mask::new(false, false, false, false)),
[0, 0, 0, 0]
);
assert_eq!(
Into::<[u32; 4]>::into(Vec4Mask::new(false, false, true, true)),
[0, 0, !0, !0]
);
assert_eq!(
Into::<[u32; 4]>::into(Vec4Mask::new(true, true, false, false)),
[!0, !0, 0, 0]
);
assert_eq!(
Into::<[u32; 4]>::into(Vec4Mask::new(false, true, false, true)),
[0, !0, 0, !0]
);
assert_eq!(
Into::<[u32; 4]>::into(Vec4Mask::new(true, false, true, false)),
[!0, 0, !0, 0]
);
assert_eq!(
Into::<[u32; 4]>::into(Vec4Mask::new(true, true, true, true)),
[!0, !0, !0, !0]
);
}
#[test]
fn test_vec4mask_bitmask() {
assert_eq!(Vec4Mask::new(false, false, false, false).bitmask(), 0b0000);
assert_eq!(Vec4Mask::new(false, false, true, true).bitmask(), 0b1100);
assert_eq!(Vec4Mask::new(true, true, false, false).bitmask(), 0b0011);
assert_eq!(Vec4Mask::new(false, true, false, true).bitmask(), 0b1010);
assert_eq!(Vec4Mask::new(true, false, true, false).bitmask(), 0b0101);
assert_eq!(Vec4Mask::new(true, true, true, true).bitmask(), 0b1111);
}
#[test]
fn test_vec4mask_any() {
assert_eq!(Vec4Mask::new(false, false, false, false).any(), false);
assert_eq!(Vec4Mask::new(true, false, false, false).any(), true);
assert_eq!(Vec4Mask::new(false, true, false, false).any(), true);
assert_eq!(Vec4Mask::new(false, false, true, false).any(), true);
assert_eq!(Vec4Mask::new(false, false, false, true).any(), true);
}
#[test]
fn test_vec4mask_all() {
assert_eq!(Vec4Mask::new(true, true, true, true).all(), true);
assert_eq!(Vec4Mask::new(false, true, true, true).all(), false);
assert_eq!(Vec4Mask::new(true, false, true, true).all(), false);
assert_eq!(Vec4Mask::new(true, true, false, true).all(), false);
assert_eq!(Vec4Mask::new(true, true, true, false).all(), false);
}
#[test]
fn test_vec4mask_select() {
let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
let b = Vec4::new(5.0, 6.0, 7.0, 8.0);
assert_eq!(
Vec4Mask::new(true, true, true, true).select(a, b),
Vec4::new(1.0, 2.0, 3.0, 4.0),
);
assert_eq!(
Vec4Mask::new(true, false, true, false).select(a, b),
Vec4::new(1.0, 6.0, 3.0, 8.0),
);
assert_eq!(
Vec4Mask::new(false, true, false, true).select(a, b),
Vec4::new(5.0, 2.0, 7.0, 4.0),
);
assert_eq!(
Vec4Mask::new(false, false, false, false).select(a, b),
Vec4::new(5.0, 6.0, 7.0, 8.0),
);
}
#[test]
fn test_vec4mask_and() {
assert_eq!(
(Vec4Mask::new(false, false, false, false) & Vec4Mask::new(false, false, false, false))
.bitmask(),
0b0000,
);
assert_eq!(
(Vec4Mask::new(true, true, true, true) & Vec4Mask::new(true, true, true, true)).bitmask(),
0b1111,
);
assert_eq!(
(Vec4Mask::new(true, false, true, false) & Vec4Mask::new(false, true, false, true))
.bitmask(),
0b0000,
);
assert_eq!(
(Vec4Mask::new(true, false, true, true) & Vec4Mask::new(true, true, true, false)).bitmask(),
0b0101,
);
let mut mask = Vec4Mask::new(true, true, false, false);
mask &= Vec4Mask::new(true, false, true, false);
assert_eq!(mask.bitmask(), 0b0001);
}
#[test]
fn test_vec4mask_or() {
assert_eq!(
(Vec4Mask::new(false, false, false, false) | Vec4Mask::new(false, false, false, false))
.bitmask(),
0b0000,
);
assert_eq!(
(Vec4Mask::new(true, true, true, true) | Vec4Mask::new(true, true, true, true)).bitmask(),
0b1111,
);
assert_eq!(
(Vec4Mask::new(true, false, true, false) | Vec4Mask::new(false, true, false, true))
.bitmask(),
0b1111,
);
assert_eq!(
(Vec4Mask::new(true, false, true, false) | Vec4Mask::new(true, false, true, false))
.bitmask(),
0b0101,
);
let mut mask = Vec4Mask::new(true, true, false, false);
mask |= Vec4Mask::new(true, false, true, false);
assert_eq!(mask.bitmask(), 0b0111);
}
#[test]
fn test_vec4mask_not() {
assert_eq!(
(!Vec4Mask::new(false, false, false, false)).bitmask(),
0b1111
);
assert_eq!((!Vec4Mask::new(true, true, true, true)).bitmask(), 0b0000);
assert_eq!((!Vec4Mask::new(true, false, true, false)).bitmask(), 0b1010);
assert_eq!((!Vec4Mask::new(false, true, false, true)).bitmask(), 0b0101);
}
#[test]
fn test_vec4mask_fmt() {
let a = Vec4Mask::new(true, false, true, false);
#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
assert_eq!(format!("{}", a), "[true, false, true, false]");
#[cfg(all(target_feature = "sse2", not(feature = "scalar-math")))]
assert_eq!(
format!("{:?}", a),
"Vec4Mask(0xffffffff, 0x0, 0xffffffff, 0x0)"
);
#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
assert_eq!(format!("{}", a), "[true, false, true, false]");
#[cfg(any(not(target_feature = "sse2"), feature = "scalar-math"))]
assert_eq!(
format!("{:?}", a),
"Vec4Mask(0xffffffff, 0x0, 0xffffffff, 0x0)"
);
}
#[test]
fn test_vec4mask_eq() {
let a = Vec4Mask::new(true, false, true, false);
let b = Vec4Mask::new(true, false, true, false);
let c = Vec4Mask::new(false, true, true, false);
assert_eq!(a, b);
assert_eq!(b, a);
assert_ne!(a, c);
assert_ne!(b, c);
assert!(a > c);
assert!(c < a);
}
#[test]
fn test_vec4mask_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
let a = Vec4Mask::new(true, false, true, false);
let b = Vec4Mask::new(true, false, true, false);
let c = Vec4Mask::new(false, true, true, false);
let mut hasher = DefaultHasher::new();
a.hash(&mut hasher);
let a_hashed = hasher.finish();
let mut hasher = DefaultHasher::new();
b.hash(&mut hasher);
let b_hashed = hasher.finish();
let mut hasher = DefaultHasher::new();
c.hash(&mut hasher);
let c_hashed = hasher.finish();
assert_eq!(a, b);
assert_eq!(a_hashed, b_hashed);
assert_ne!(a, c);
assert_ne!(a_hashed, c_hashed);
}
#[test]
fn test_vec4_round() {
assert_eq!(Vec4::new(1.35, 0.0, 0.0, 0.0).round().x(), 1.0);
assert_eq!(Vec4::new(0.0, 1.5, 0.0, 0.0).round().y(), 2.0);
assert_eq!(Vec4::new(0.0, 0.0, -15.5, 0.0).round().z(), -16.0);
assert_eq!(Vec4::new(0.0, 0.0, 0.0, 0.0).round().z(), 0.0);
assert_eq!(Vec4::new(0.0, 21.1, 0.0, 0.0).round().y(), 21.0);
assert_eq!(Vec4::new(0.0, 0.0, 0.0, 11.123).round().w(), 11.0);
assert_eq!(Vec4::new(0.0, 0.0, 11.501, 0.0).round().z(), 12.0);
assert_eq!(
Vec4::new(f32::NEG_INFINITY, f32::INFINITY, 1.0, -1.0).round(),
Vec4::new(f32::NEG_INFINITY, f32::INFINITY, 1.0, -1.0)
);
assert!(Vec4::new(f32::NAN, 0.0, 0.0, 1.0).round().x().is_nan());
}
#[test]
fn test_vec4_floor() {
assert_eq!(
Vec4::new(1.35, 1.5, -1.5, 1.999).floor(),
Vec4::new(1.0, 1.0, -2.0, 1.0)
);
assert_eq!(
Vec4::new(f32::INFINITY, f32::NEG_INFINITY, 0.0, 0.0).floor(),
Vec4::new(f32::INFINITY, f32::NEG_INFINITY, 0.0, 0.0)
);
assert!(Vec4::new(0.0, f32::NAN, 0.0, 0.0).floor().y().is_nan());
assert_eq!(
Vec4::new(-0.0, -2000000.123, 10000000.123, 1000.9).floor(),
Vec4::new(-0.0, -2000001.0, 10000000.0, 1000.0)
);
}
#[test]
fn test_vec4_ceil() {
assert_eq!(
Vec4::new(1.35, 1.5, -1.5, 1234.1234).ceil(),
Vec4::new(2.0, 2.0, -1.0, 1235.0)
);
assert_eq!(
Vec4::new(f32::INFINITY, f32::NEG_INFINITY, 0.0, 0.0).ceil(),
Vec4::new(f32::INFINITY, f32::NEG_INFINITY, 0.0, 0.0)
);
assert!(Vec4::new(0.0, 0.0, f32::NAN, 0.0).ceil().z().is_nan());
assert_eq!(
Vec4::new(-1234.1234, -2000000.123, 1000000.123, 1000.9).ceil(),
Vec4::new(-1234.0, -2000000.0, 1000001.0, 1001.0)
);
}
#[test]
fn test_vec4_lerp() {
let v0 = Vec4::new(-1.0, -1.0, -1.0, -1.0);
let v1 = Vec4::new(1.0, 1.0, 1.0, 1.0);
assert_approx_eq!(v0, v0.lerp(v1, 0.0));
assert_approx_eq!(v1, v0.lerp(v1, 1.0));
assert_approx_eq!(Vec4::zero(), v0.lerp(v1, 0.5));
}
#[test]
fn test_vec4_to_from_slice() {
let v = Vec4::new(1.0, 2.0, 3.0, 4.0);
let mut a = [0.0, 0.0, 0.0, 0.0];
v.write_to_slice_unaligned(&mut a);
assert_eq!(v, Vec4::from_slice_unaligned(&a));
}
#[cfg(feature = "serde")]
#[test]
fn test_vec4_serde() {
let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
let serialized = serde_json::to_string(&a).unwrap();
assert_eq!(serialized, "[1.0,2.0,3.0,4.0]");
let deserialized = serde_json::from_str(&serialized).unwrap();
assert_eq!(a, deserialized);
let deserialized = serde_json::from_str::<Vec4>("[]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Vec4>("[1.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Vec4>("[1.0,2.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Vec4>("[1.0,2.0,3.0]");
assert!(deserialized.is_err());
let deserialized = serde_json::from_str::<Vec4>("[1.0,2.0,3.0,4.0,5.0]");
assert!(deserialized.is_err());
}
#[cfg(feature = "rand")]
#[test]
fn test_vec4_rand() {
use rand::{Rng, SeedableRng};
use rand_xoshiro::Xoshiro256Plus;
let mut rng1 = Xoshiro256Plus::seed_from_u64(0);
let a: (f32, f32, f32, f32) = rng1.gen();
let mut rng2 = Xoshiro256Plus::seed_from_u64(0);
let b: Vec4 = rng2.gen();
assert_eq!(a, b.into());
}

View file

@ -15,4 +15,4 @@ bevy_window = { path = "../bevy_window", version = "0.1.0" }
legion = { path = "../bevy_legion"}
zerocopy = "0.3"
glam = "0.8.6"
glam = { path = "../bevy_glam" }

View file

@ -24,7 +24,7 @@ png = "0.16.0"
# misc
log = { version = "0.4", features = ["release_max_level_info"] }
uuid = { version = "0.8", features = ["v4", "serde"] }
glam = "0.8.6"
glam = { path = "../bevy_glam" }
zerocopy = "0.3"
bitflags = "1.0"
smallvec = "1.4.0"

View file

@ -8,7 +8,7 @@ license = "MIT"
[dependencies]
legion = { path = "../bevy_legion", features = ["serialize"]}
glam = "0.8.6"
glam = { path = "../bevy_glam" }
log = "0.4"
rayon = "1.2"
serde = { version = "1", features = ["derive"] }

View file

@ -14,4 +14,6 @@ bevy_render = { path = "../bevy_render" }
bevy_window = { path = "../bevy_window" }
legion = { path = "../bevy_legion", features = ["serialize"] }
glam = "0.8.6"
glam = { path = "../bevy_glam" }
zerocopy = "0.3.0"

View file

@ -1,8 +1,9 @@
use glam::Vec2;
use bevy_render::Color;
use bevy_derive::Uniforms;
use zerocopy::AsBytes;
#[repr(C)]
#[derive(Default, Clone, Copy, Debug, Uniforms)]
#[derive(Default, Clone, Copy, Debug, Uniforms, AsBytes)]
#[module(meta = "false")]
pub struct Rect {
pub position: Vec2,