mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
custom glam with zerocopy impls
This commit is contained in:
parent
dac9cac776
commit
41be55bf8d
59 changed files with 12880 additions and 7 deletions
|
@ -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 = [
|
||||
|
|
|
@ -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"
|
5
crates/bevy_glam/.cargo_vcs_info.json
Normal file
5
crates/bevy_glam/.cargo_vcs_info.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"git": {
|
||||
"sha1": "9690d383cfa7d720706d66b25ec862fa6100adf5"
|
||||
}
|
||||
}
|
8
crates/bevy_glam/.github/workflows/CI.yml
vendored
Normal file
8
crates/bevy_glam/.github/workflows/CI.yml
vendored
Normal 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
6
crates/bevy_glam/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
/target
|
||||
*.swp
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
Session.vim
|
||||
.cargo-ok
|
40
crates/bevy_glam/.travis.yml
Normal file
40
crates/bevy_glam/.travis.yml
Normal 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
|
188
crates/bevy_glam/CHANGELOG.md
Normal file
188
crates/bevy_glam/CHANGELOG.md
Normal 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
|
50
crates/bevy_glam/CONTRIBUTING.md
Normal file
50
crates/bevy_glam/CONTRIBUTING.md
Normal 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
|
83
crates/bevy_glam/Cargo.toml
Normal file
83
crates/bevy_glam/Cargo.toml
Normal 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
|
201
crates/bevy_glam/LICENSE-APACHE
Normal file
201
crates/bevy_glam/LICENSE-APACHE
Normal 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.
|
23
crates/bevy_glam/LICENSE-MIT
Normal file
23
crates/bevy_glam/LICENSE-MIT
Normal 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
163
crates/bevy_glam/README.md
Normal 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
|
34
crates/bevy_glam/benches/mat2.rs
Normal file
34
crates/bevy_glam/benches/mat2.rs
Normal 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);
|
36
crates/bevy_glam/benches/mat3.rs
Normal file
36
crates/bevy_glam/benches/mat3.rs
Normal 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);
|
36
crates/bevy_glam/benches/mat4.rs
Normal file
36
crates/bevy_glam/benches/mat4.rs
Normal 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);
|
62
crates/bevy_glam/benches/quat.rs
Normal file
62
crates/bevy_glam/benches/quat.rs
Normal 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);
|
180
crates/bevy_glam/benches/support/macros.rs
Normal file
180
crates/bevy_glam/benches/support/macros.rs
Normal 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();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
96
crates/bevy_glam/benches/support/mod.rs
Normal file
96
crates/bevy_glam/benches/support/mod.rs
Normal 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),
|
||||
)
|
||||
}
|
85
crates/bevy_glam/benches/transform.rs
Normal file
85
crates/bevy_glam/benches/transform.rs
Normal 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);
|
31
crates/bevy_glam/benches/vec2.rs
Normal file
31
crates/bevy_glam/benches/vec2.rs
Normal 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);
|
98
crates/bevy_glam/benches/vec3.rs
Normal file
98
crates/bevy_glam/benches/vec3.rs
Normal 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);
|
20
crates/bevy_glam/benches/vec4.rs
Normal file
20
crates/bevy_glam/benches/vec4.rs
Normal 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
29
crates/bevy_glam/build.rs
Normal 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");
|
||||
}
|
||||
}
|
18
crates/bevy_glam/deny.toml
Normal file
18
crates/bevy_glam/deny.toml
Normal 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",
|
||||
]
|
550
crates/bevy_glam/src/f32/funcs.rs
Normal file
550
crates/bevy_glam/src/f32/funcs.rs
Normal 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());
|
||||
// }
|
309
crates/bevy_glam/src/f32/glam_mint.rs
Normal file
309
crates/bevy_glam/src/f32/glam_mint.rs
Normal 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));
|
||||
}
|
||||
}
|
59
crates/bevy_glam/src/f32/glam_rand.rs
Normal file
59
crates/bevy_glam/src/f32/glam_rand.rs
Normal 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()
|
||||
}
|
||||
}
|
385
crates/bevy_glam/src/f32/glam_serde.rs
Normal file
385
crates/bevy_glam/src/f32/glam_serde.rs
Normal 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)
|
||||
}
|
||||
}
|
51
crates/bevy_glam/src/f32/glam_zerocopy.rs
Normal file
51
crates/bevy_glam/src/f32/glam_zerocopy.rs
Normal 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 {}
|
||||
|
||||
}
|
335
crates/bevy_glam/src/f32/mat2.rs
Normal file
335
crates/bevy_glam/src/f32/mat2.rs
Normal 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)
|
||||
}
|
||||
}
|
482
crates/bevy_glam/src/f32/mat3.rs
Normal file
482
crates/bevy_glam/src/f32/mat3.rs
Normal 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)
|
||||
}
|
||||
}
|
886
crates/bevy_glam/src/f32/mat4.rs
Normal file
886
crates/bevy_glam/src/f32/mat4.rs
Normal 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)
|
||||
}
|
||||
}
|
47
crates/bevy_glam/src/f32/mod.rs
Normal file
47
crates/bevy_glam/src/f32/mod.rs
Normal 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::*;
|
670
crates/bevy_glam/src/f32/quat.rs
Normal file
670
crates/bevy_glam/src/f32/quat.rs
Normal 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))
|
||||
}
|
||||
}
|
361
crates/bevy_glam/src/f32/transform.rs
Normal file
361
crates/bevy_glam/src/f32/transform.rs
Normal 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)
|
||||
}
|
||||
}
|
509
crates/bevy_glam/src/f32/vec2.rs
Normal file
509
crates/bevy_glam/src/f32/vec2.rs
Normal 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]
|
||||
}
|
||||
}
|
126
crates/bevy_glam/src/f32/vec2_mask.rs
Normal file
126
crates/bevy_glam/src/f32/vec2_mask.rs
Normal 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]) }
|
||||
}
|
||||
}
|
1221
crates/bevy_glam/src/f32/vec3.rs
Normal file
1221
crates/bevy_glam/src/f32/vec3.rs
Normal file
File diff suppressed because it is too large
Load diff
298
crates/bevy_glam/src/f32/vec3_mask.rs
Normal file
298
crates/bevy_glam/src/f32/vec3_mask.rs
Normal 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]) }
|
||||
}
|
||||
}
|
1347
crates/bevy_glam/src/f32/vec4.rs
Normal file
1347
crates/bevy_glam/src/f32/vec4.rs
Normal file
File diff suppressed because it is too large
Load diff
329
crates/bevy_glam/src/f32/vec4_mask.rs
Normal file
329
crates/bevy_glam/src/f32/vec4_mask.rs
Normal 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]) }
|
||||
}
|
||||
}
|
15
crates/bevy_glam/src/f32/x86_utils.rs
Normal file
15
crates/bevy_glam/src/f32/x86_utils.rs
Normal 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
167
crates/bevy_glam/src/lib.rs
Normal 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);
|
||||
}
|
32
crates/bevy_glam/src/macros.rs
Normal file
32
crates/bevy_glam/src/macros.rs
Normal 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()
|
||||
};
|
||||
}
|
186
crates/bevy_glam/tests/mat2.rs
Normal file
186
crates/bevy_glam/tests/mat2.rs
Normal 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);
|
||||
}
|
253
crates/bevy_glam/tests/mat3.rs
Normal file
253
crates/bevy_glam/tests/mat3.rs
Normal 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);
|
||||
}
|
483
crates/bevy_glam/tests/mat4.rs
Normal file
483
crates/bevy_glam/tests/mat4.rs
Normal 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);
|
||||
}
|
284
crates/bevy_glam/tests/quat.rs
Normal file
284
crates/bevy_glam/tests/quat.rs
Normal 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);
|
||||
}
|
32
crates/bevy_glam/tests/support/macros.rs
Normal file
32
crates/bevy_glam/tests/support/macros.rs
Normal 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)
|
||||
);
|
||||
}};
|
||||
}
|
166
crates/bevy_glam/tests/support/mod.rs
Normal file
166
crates/bevy_glam/tests/support/mod.rs
Normal 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),
|
||||
)
|
||||
}
|
||||
}
|
88
crates/bevy_glam/tests/transform.rs
Normal file
88
crates/bevy_glam/tests/transform.rs
Normal 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());
|
||||
}
|
||||
}
|
489
crates/bevy_glam/tests/vec2.rs
Normal file
489
crates/bevy_glam/tests/vec2.rs
Normal 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());
|
||||
}
|
598
crates/bevy_glam/tests/vec3.rs
Normal file
598
crates/bevy_glam/tests/vec3.rs
Normal 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());
|
||||
}
|
620
crates/bevy_glam/tests/vec4.rs
Normal file
620
crates/bevy_glam/tests/vec4.rs
Normal 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());
|
||||
}
|
|
@ -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" }
|
|
@ -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"
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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"
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue