diff --git a/codegen/templates/vec.rs.tera b/codegen/templates/vec.rs.tera index 5c8c080c..51026c7a 100644 --- a/codegen/templates/vec.rs.tera +++ b/codegen/templates/vec.rs.tera @@ -1976,6 +1976,44 @@ impl {{ self_t }} { {% endif %} } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: {{ scalar_t }}) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + {% if dim == 2 %} /// Creates a 2D vector containing `[angle.cos(), angle.sin()]`. This can be used in /// conjunction with the [`rotate()`][Self::rotate()] method, e.g. diff --git a/src/f32/coresimd/vec3a.rs b/src/f32/coresimd/vec3a.rs index ae769120..9e633ca4 100644 --- a/src/f32/coresimd/vec3a.rs +++ b/src/f32/coresimd/vec3a.rs @@ -824,6 +824,44 @@ impl Vec3A { Self(self.0.mul_add(a.0, b.0)) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Returns the angle (in radians) between two vectors in the range `[0, +π]`. /// /// The inputs do not need to be unit vectors however they must be non-zero. diff --git a/src/f32/coresimd/vec4.rs b/src/f32/coresimd/vec4.rs index f06bac8d..62208e7c 100644 --- a/src/f32/coresimd/vec4.rs +++ b/src/f32/coresimd/vec4.rs @@ -811,6 +811,44 @@ impl Vec4 { Self(self.0.mul_add(a.0, b.0)) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Casts all elements of `self` to `f64`. #[inline] #[must_use] diff --git a/src/f32/neon/vec3a.rs b/src/f32/neon/vec3a.rs index 8cb986ed..0440891b 100644 --- a/src/f32/neon/vec3a.rs +++ b/src/f32/neon/vec3a.rs @@ -868,6 +868,44 @@ impl Vec3A { Self(unsafe { vfmaq_f32(b.0, self.0, a.0) }) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Returns the angle (in radians) between two vectors in the range `[0, +π]`. /// /// The inputs do not need to be unit vectors however they must be non-zero. diff --git a/src/f32/neon/vec4.rs b/src/f32/neon/vec4.rs index be7b2e28..613841dc 100644 --- a/src/f32/neon/vec4.rs +++ b/src/f32/neon/vec4.rs @@ -845,6 +845,44 @@ impl Vec4 { Self(unsafe { vfmaq_f32(b.0, self.0, a.0) }) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Casts all elements of `self` to `f64`. #[inline] #[must_use] diff --git a/src/f32/scalar/vec3a.rs b/src/f32/scalar/vec3a.rs index 7186f9ec..a55920f7 100644 --- a/src/f32/scalar/vec3a.rs +++ b/src/f32/scalar/vec3a.rs @@ -871,6 +871,44 @@ impl Vec3A { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Returns the angle (in radians) between two vectors in the range `[0, +π]`. /// /// The inputs do not need to be unit vectors however they must be non-zero. diff --git a/src/f32/scalar/vec4.rs b/src/f32/scalar/vec4.rs index 081cb3bb..4fdba002 100644 --- a/src/f32/scalar/vec4.rs +++ b/src/f32/scalar/vec4.rs @@ -930,6 +930,44 @@ impl Vec4 { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Casts all elements of `self` to `f64`. #[inline] #[must_use] diff --git a/src/f32/sse2/vec3a.rs b/src/f32/sse2/vec3a.rs index c48fdb43..44e1fca6 100644 --- a/src/f32/sse2/vec3a.rs +++ b/src/f32/sse2/vec3a.rs @@ -876,6 +876,44 @@ impl Vec3A { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Returns the angle (in radians) between two vectors in the range `[0, +π]`. /// /// The inputs do not need to be unit vectors however they must be non-zero. diff --git a/src/f32/sse2/vec4.rs b/src/f32/sse2/vec4.rs index c272e552..dabdb387 100644 --- a/src/f32/sse2/vec4.rs +++ b/src/f32/sse2/vec4.rs @@ -863,6 +863,44 @@ impl Vec4 { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Casts all elements of `self` to `f64`. #[inline] #[must_use] diff --git a/src/f32/vec2.rs b/src/f32/vec2.rs index 6f52f8eb..283948af 100644 --- a/src/f32/vec2.rs +++ b/src/f32/vec2.rs @@ -793,6 +793,44 @@ impl Vec2 { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Creates a 2D vector containing `[angle.cos(), angle.sin()]`. This can be used in /// conjunction with the [`rotate()`][Self::rotate()] method, e.g. /// `Vec2::from_angle(PI).rotate(Vec2::Y)` will create the vector `[-1, 0]` diff --git a/src/f32/vec3.rs b/src/f32/vec3.rs index a8b5e684..aca3925f 100644 --- a/src/f32/vec3.rs +++ b/src/f32/vec3.rs @@ -861,6 +861,44 @@ impl Vec3 { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Returns the angle (in radians) between two vectors in the range `[0, +π]`. /// /// The inputs do not need to be unit vectors however they must be non-zero. diff --git a/src/f32/wasm32/vec3a.rs b/src/f32/wasm32/vec3a.rs index 74bb8f6d..3cfec78d 100644 --- a/src/f32/wasm32/vec3a.rs +++ b/src/f32/wasm32/vec3a.rs @@ -839,6 +839,44 @@ impl Vec3A { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Returns the angle (in radians) between two vectors in the range `[0, +π]`. /// /// The inputs do not need to be unit vectors however they must be non-zero. diff --git a/src/f32/wasm32/vec4.rs b/src/f32/wasm32/vec4.rs index 5e28a49e..603579f1 100644 --- a/src/f32/wasm32/vec4.rs +++ b/src/f32/wasm32/vec4.rs @@ -833,6 +833,44 @@ impl Vec4 { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f32) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Casts all elements of `self` to `f64`. #[inline] #[must_use] diff --git a/src/f64/dvec2.rs b/src/f64/dvec2.rs index c225b761..bdec24d3 100644 --- a/src/f64/dvec2.rs +++ b/src/f64/dvec2.rs @@ -793,6 +793,44 @@ impl DVec2 { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f64) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Creates a 2D vector containing `[angle.cos(), angle.sin()]`. This can be used in /// conjunction with the [`rotate()`][Self::rotate()] method, e.g. /// `DVec2::from_angle(PI).rotate(DVec2::Y)` will create the vector `[-1, 0]` diff --git a/src/f64/dvec3.rs b/src/f64/dvec3.rs index 9ad3da5d..398490dc 100644 --- a/src/f64/dvec3.rs +++ b/src/f64/dvec3.rs @@ -861,6 +861,44 @@ impl DVec3 { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f64) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Returns the angle (in radians) between two vectors in the range `[0, +π]`. /// /// The inputs do not need to be unit vectors however they must be non-zero. diff --git a/src/f64/dvec4.rs b/src/f64/dvec4.rs index ec4bd013..b09f95d8 100644 --- a/src/f64/dvec4.rs +++ b/src/f64/dvec4.rs @@ -919,6 +919,44 @@ impl DVec4 { ) } + /// Returns the reflection vector for a given incident vector `self` and surface normal + /// `normal`. + /// + /// `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn reflect(self, normal: Self) -> Self { + glam_assert!(normal.is_normalized()); + self - 2.0 * self.dot(normal) * normal + } + + /// Returns the refraction direction for a given incident vector `self`, surface normal + /// `normal` and ratio of indices of refraction, `eta`. When total internal reflection occurs, + /// a zero vector will be returned. + /// + /// `self` and `normal` must be normalized. + /// + /// # Panics + /// + /// Will panic if `self` or `normal` is not normalized when `glam_assert` is enabled. + #[inline] + #[must_use] + pub fn refract(self, normal: Self, eta: f64) -> Self { + glam_assert!(self.is_normalized()); + glam_assert!(normal.is_normalized()); + let n_dot_i = normal.dot(self); + let k = 1.0 - eta * eta * (1.0 - n_dot_i * n_dot_i); + if k >= 0.0 { + eta * self - (eta * n_dot_i + math::sqrt(k)) * normal + } else { + Self::ZERO + } + } + /// Casts all elements of `self` to `f32`. #[inline] #[must_use] diff --git a/tests/vec2.rs b/tests/vec2.rs index c63d5c4b..a8b46885 100644 --- a/tests/vec2.rs +++ b/tests/vec2.rs @@ -1001,6 +1001,22 @@ macro_rules! impl_vec2_float_tests { assert_approx_eq!(vec, $vec2::new(0.0, -1.0)); assert_approx_eq!(vec.to_angle(), angle); }); + + glam_test!(test_reflect, { + let incident = $vec2::new(1.0, -1.0); + let normal = $vec2::Y; + assert_approx_eq!(incident.reflect(normal), $vec2::ONE); + }); + + glam_test!(test_refract, { + let incident = $vec2::NEG_ONE.normalize(); + let normal = $vec2::ONE.normalize(); + assert_approx_eq!(incident.refract(normal, 0.5), incident); + + let incident = $vec2::new(1.0, -1.0).normalize(); + let normal = $vec2::Y; + assert_approx_eq!(incident.refract(normal, 1.5), $vec2::ZERO); + }); }; } diff --git a/tests/vec3.rs b/tests/vec3.rs index cf3ac92e..b5e62d38 100644 --- a/tests/vec3.rs +++ b/tests/vec3.rs @@ -1098,6 +1098,22 @@ macro_rules! impl_vec3_float_tests { let a = $vec3::new(1.0, 2.0, 3.0); assert_eq!(format!("{:.2}", a), "[1.00, 2.00, 3.00]"); }); + + glam_test!(test_reflect, { + let incident = $vec3::new(1.0, -1.0, 1.0); + let normal = $vec3::Y; + assert_approx_eq!(incident.reflect(normal), $vec3::ONE); + }); + + glam_test!(test_refract, { + let incident = $vec3::NEG_ONE.normalize(); + let normal = $vec3::ONE.normalize(); + assert_approx_eq!(incident.refract(normal, 0.5), incident); + + let incident = $vec3::new(1.0, -1.0, 0.0).normalize(); + let normal = $vec3::Y; + assert_approx_eq!(incident.refract(normal, 1.5), $vec3::ZERO); + }); }; } diff --git a/tests/vec4.rs b/tests/vec4.rs index 74154b1b..b0b09902 100644 --- a/tests/vec4.rs +++ b/tests/vec4.rs @@ -1196,6 +1196,22 @@ macro_rules! impl_vec4_float_tests { let a = $vec4::new(1.0, 2.0, 3.0, 4.0); assert_eq!(format!("{:.2}", a), "[1.00, 2.00, 3.00, 4.00]"); }); + + glam_test!(test_reflect, { + let incident = $vec4::new(1.0, -1.0, 1.0, 1.0); + let normal = $vec4::Y; + assert_approx_eq!(incident.reflect(normal), $vec4::ONE); + }); + + glam_test!(test_refract, { + let incident = $vec4::NEG_ONE.normalize(); + let normal = $vec4::ONE.normalize(); + assert_approx_eq!(incident.refract(normal, 0.5), incident); + + let incident = $vec4::new(1.0, -1.0, 0.0, 0.0).normalize(); + let normal = $vec4::Y; + assert_approx_eq!(incident.refract(normal, 1.5), $vec4::ZERO); + }); }; }