commit 3928496379ab538d69bb84f3aae36a6bb31e5a2e Author: Jan Solanti Date: Thu Aug 23 17:14:09 2018 +0900 WIP: port libsquish c++ code to rust diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe42cc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +**/*.rs.bk +Cargo.lock +.DS_Store diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4a5b581 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "squish-rs" +version = "0.1.0" +authors = ["Jan Solanti "] + +[dependencies] diff --git a/src/colourblock.rs b/src/colourblock.rs new file mode 100644 index 0000000..2484fc2 --- /dev/null +++ b/src/colourblock.rs @@ -0,0 +1,33 @@ +// Copyright (c) 2006 Simon Brown +// Copyright (c) 2018 Jan Solanti +// +// 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. + + +use math::Vec3; +use super::f32_to_i32_clamped; + +fn vec3f_to_565(colour: Vec3) -> [u8; 2] { + let r = f32_to_i32_clamped(31.0*colour.x(), 31) as u8; + let g = f32_to_i32_clamped(63.0*colour.y(), 63) as u8; + let b = f32_to_i32_clamped(31.0*colour.z(), 31) as u8; + + [ r << 3 | (g >> 3), (g << 5) | b] +} \ No newline at end of file diff --git a/src/colourset.rs b/src/colourset.rs new file mode 100644 index 0000000..19c565a --- /dev/null +++ b/src/colourset.rs @@ -0,0 +1,137 @@ +// Copyright (c) 2006 Simon Brown +// Copyright (c) 2018 Jan Solanti +// +// 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. + + +use ::Format; +use math::*; + +pub struct ColourSet { + count: usize, + points: [Vec3; 16], + weights: [f32; 16], + remap: [i8; 16], // enough since there are only 16 possible indices + transparent: bool, +} + +impl ColourSet { + pub fn new(rgba: &[[u8; 4]; 16], mask: u32, format: Format, alpha_weighted: bool) -> ColourSet { + let mut set = ColourSet { + count: 0, + points: [Vec3::new(0f32,0f32,0f32); 16], + weights: [0f32; 16], + remap: [0; 16], + transparent: false, + }; + + // create the minimal set + for i in 0..rgba.len() { + // skip this pixel if it's disabled + let bit = 1u32 << i; + if (mask & bit) == 0 { + set.remap[i] = -1; + continue; + } + + // if using DXT1, skip transparent pixels + if (format == Format::Dxt1) && (rgba[i][3] < 128u8) { + set.remap[i] = -1; + set.transparent = true; + continue; + } + + // loop over previous points in case the colour is duplicated in this block + for j in 0..rgba.len() { + // no duplicates found, store new point + if j == i { + // normalise coordinates to [0,1] + let x = rgba[i][0] as f32 / 255f32; + let y = rgba[i][1] as f32 / 255f32; + let z = rgba[i][2] as f32 / 255f32; + + // ensure weight is always nonzero even when alpha is not + let w = (rgba[i][3] + 1) as f32 / 256f32; + + // store point + set.points[set.count] = Vec3::new(x, y, z); + set.weights[set.count] = if alpha_weighted { w } else { 1f32 }; + + // move to next pixel + set.count += 1; + break; + } + + // check for duplicates + let oldbit = 1u32 << j; + let duplicate = ((mask & oldbit) != 0) + && ( rgba[i][0] == rgba[j][0] ) + && ( rgba[i][1] == rgba[j][1] ) + && ( rgba[i][2] == rgba[j][2] ) + && ( format != Format::Dxt1 || rgba[j][3] >= 128u8 ); + if duplicate { + // get index of duplicate + let index = set.remap[j]; + + // ensure weight is always nonzero even when alpha is not + let w = (rgba[i][3] + 1) as f32 / 256f32; + + // map this point to its duplicate and increase the duplicate's weight + set.weights[index as usize] += if alpha_weighted { w } else { 1f32 }; + set.remap[i] = index; + + // move to next pixel + break; + } + } + } + + // square root the weights + for w in set.weights.iter_mut() { + *w = w.sqrt(); + } + + set + } + + pub fn is_transparent(&self) -> bool { + self.transparent + } + + pub fn points(&self) -> &[Vec3] { + &self.points[..self.count] + } + + pub fn weights(&self) -> &[f32] { + &self.weights[..self.count] + } + + pub fn remap_indices(&mut self, source: &[u8; 16], target: &mut [u8; 16]) { + for i in 0..source.len() { + let j = self.remap[i]; + + if j == -1 { + target[i] = 3; + } else { + target[i] = source[j as usize]; + } + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..fc36ea5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,243 @@ +// Copyright (c) 2006 Simon Brown +// Copyright (c) 2018 Jan Solanti +// +// 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. + + +//! A pure Rust DXT1/3/5 compressor and decompressor based on Simon Brown's +//! **libsquish** + +mod math; +mod colourblock; +mod colourset; + +/// Defines a compression format +#[derive(PartialEq, Eq)] +pub enum Format { + Dxt1, + Dxt3, + Dxt5, +} + +/// Defines a compression algorithm +pub enum CompressionAlgorithm { + /// Fast, low quality + ColourRangeFit, + + /// Slow, high quality (default) + ColourClusterFit, + + /// Very slow, very high quality + ColourIterativeClusterFit, +} + +/// A block of owned compressed data. Variants are for different block sizes +enum CompressedBlock { + Dxt1([u8; 8]), + Dxt3([u8; 8], [u8; 8]), + Dxt5([u8; 16]), +} + +impl Default for CompressionAlgorithm { + fn default() -> Self { CompressionAlgorithm::ColourClusterFit } +} + +/// RGB colour channel weights for use in block fitting +pub type ColourWeights = [f32; 3]; + +/// Weights based on the perceived brightness of each colour channel +pub const COLOUR_WEIGHTS_PERCEPTUAL: ColourWeights = [0.2126, 0.7152, 0.0722]; + +pub struct CompressorParams { + /// The compression algorithm to be used + pub algorithm: CompressionAlgorithm, + + /// Weigh the relative importance of each colour channel when fitting + /// (defaults to equal weights) + pub weights: Option, + + /// Weigh colour by alpha during cluster fit (defaults to false) + /// + /// This can significantly increase perceived quality for images that are rendered + /// using alpha blending. + pub weigh_colour_by_alpha: bool, +} + +impl Default for CompressorParams { + fn default() -> Self { + CompressorParams { + algorithm: CompressionAlgorithm::default(), + weights: None, + weigh_colour_by_alpha: false, + } + } +} + +/// Decompresses an image in memory +/// +/// * `data` - The compressed image data +/// * `width` - The width of the source image +/// * `height` - The height of the source image +/// * `format` - The compression format +pub fn decompress( + data: &[u8], + width: usize, + height: usize, + format: Format, +) -> Vec { + vec![] +} + +/// Computes the amount of space in bytes needed for the compressed image +/// +/// * `width` - Width of the uncompressed image +/// * `height` - Height of the uncompressed image +/// * `format` - The desired compression format +/// +pub fn compute_compressed_size( + width: usize, + height: usize, + format: Format +) -> usize { + // Number of blocks required for image of given dimensions + let n_blocks = ((width + 3) / 4) * ((height + 3) / 4); + + let blocksize = bytes_per_block(format); + + n_blocks * blocksize +} + +/// Compresses a 4x4 block of pixels +/// +/// * `rgba` - The uncompressed block of pixels +/// * `format` - The desired compression format +/// * `params` - Additional compressor parameters +fn compress_block( + rgba: [[u8; 4]; 16], + format: Format, + params: Option +) -> () { + compress_block_masked(rgba, 0xffffffff, format, params) +} + +/// Compresses a 4x4 block of pixels, masking out some pixels e.g. for padding the +/// image to a multiple of the block size. +/// +/// * `rgba` - The uncompressed block of pixels +/// * `mask` - The valid pixel mask +/// * `format` - The desired compression format +/// * `params` - Additional compressor parameters +fn compress_block_masked( + rgba: [[u8; 4]; 16], + mask: u32, + format: Format, + params: Option +) -> () { + let params = params.unwrap_or(CompressorParams::default()); +} + +/// Decompresses a 4x4 block of pixels +/// +/// * `rgba` - The compressed block of pixels +/// * `format` - The compression format of the data +fn decompress_block( + rgba: &CompressedBlock, + format: Format, +) -> () { + +} + +/// Compresses an image in memory +/// +/// * `rgba` - The uncompressed pixel data +/// * `width` - The width of the source image +/// * `height` - The height of the source image +/// * `format` - The desired compression format +/// * `params` - Additional compressor parameters +pub fn compress( + rgba: &[u8], + width: usize, + height: usize, + format: Format, + params: Option +) -> Vec { + vec![] +} + +/// Returns how many bytes a 4x4 block of pixels will take after compression, +/// given the compression format +fn bytes_per_block(format: Format) -> usize { + // Compressed block size in bytes + match format { + Format::Dxt1 => 8, + Format::Dxt3 => 16, + Format::Dxt5 => 16, + } +} + +fn f32_to_i32_clamped(a: f32, limit: i32) -> i32 { + (a.round() as i32).max(0).min(limit) +} + + +//-------------------------------------------------------------------------------- +// Unit tests +//-------------------------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_storage_requirements_dxt1_exact() { + let estimate = compute_compressed_size(16, 32, Format::Dxt1); + assert_eq!(estimate, 256); + } + + #[test] + fn test_storage_requirements_dxt1_padded() { + let estimate = compute_compressed_size(15, 30, Format::Dxt1); + assert_eq!(estimate, 256); + } + + #[test] + fn test_storage_requirements_dxt3_exact() { + let estimate = compute_compressed_size(16, 32, Format::Dxt3); + assert_eq!(estimate, 512); + } + + #[test] + fn test_storage_requirements_dxt3_padded() { + let estimate = compute_compressed_size(15, 30, Format::Dxt3); + assert_eq!(estimate, 512); + } + + #[test] + fn test_storage_requirements_dxt5_exact() { + let estimate = compute_compressed_size(16, 32, Format::Dxt5); + assert_eq!(estimate, 512); + } + + #[test] + fn test_storage_requirements_dxt5_padded() { + let estimate = compute_compressed_size(15, 30, Format::Dxt5); + assert_eq!(estimate, 512); + } +} diff --git a/src/math/mod.rs b/src/math/mod.rs new file mode 100644 index 0000000..68e7fc9 --- /dev/null +++ b/src/math/mod.rs @@ -0,0 +1,97 @@ +// Copyright (c) 2006 Simon Brown +// Copyright (c) 2018 Jan Solanti +// +// 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. + + +use std::f32; + +mod vec3; +pub use self::vec3::*; + +mod vec4; +pub use self::vec4::*; + +/// A type representing a symmetric 3x3 matrix +pub struct Sym3x3 { + x: [f32; 6], +} + +impl Sym3x3 { + fn new(s: f32) -> Self { + Self { x: [s, s, s, s, s, s] } + } + + fn weighted_covariance(points: &[Vec3], weights: &[f32] ) -> Self { + assert!(points.len() == weights.len()); + + // compute the centroid + let total: f32 = weights.iter().sum(); + let centroid: Vec3 = points.iter().zip(weights.iter()) + .map(|(p, &w)| p*w).sum(); + + let centroid = if total > f32::EPSILON { + centroid / total + } else { + centroid + }; + + // accumulate the covariance matrix + let mut covariance = Sym3x3::new(0.0); + + for (p, &w) in points.iter().zip(weights.iter()) { + let a: Vec3 = p - ¢roid; + let b = &a * w; + + covariance.x[..][0] += a.x()*b.x(); + covariance.x[..][1] += a.x()*b.y(); + covariance.x[..][2] += a.x()*b.z(); + covariance.x[..][3] += a.y()*b.y(); + covariance.x[..][4] += a.y()*b.z(); + covariance.x[..][5] += a.z()*b.z(); + } + + covariance + } + + fn principle_component(&self) -> Vec3 { + const POWER_ITERATION_COUNT: usize = 8; + + let row0 = Vec4::new(self.x[0], self.x[1], self.x[2], 0.0); + let row1 = Vec4::new(self.x[0], self.x[1], self.x[2], 0.0); + let row2 = Vec4::new(self.x[0], self.x[1], self.x[2], 0.0); + let mut v = Vec4::new(1.0, 1.0, 1.0, 1.0); + + for _ in 0..POWER_ITERATION_COUNT { + // matrix multiplication + let w = &row0 * v.splat_x(); + let w = Vec4::multiply_add(&row1, &v.splat_y(), &w); + let w = Vec4::multiply_add(&row2, &v.splat_z(), &w); + + // Construct Vec4 with max component from xyz in all channels + let a = w.x().max(w.y().max(w.z())); + let a = Vec4::new(a, a, a, a); + + v = w * a.reciprocal(); + } + + v.to_vec3() + } +} \ No newline at end of file diff --git a/src/math/vec3.rs b/src/math/vec3.rs new file mode 100644 index 0000000..981da8d --- /dev/null +++ b/src/math/vec3.rs @@ -0,0 +1,428 @@ +// Copyright (c) 2006 Simon Brown +// Copyright (c) 2018 Jan Solanti +// +// 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. + + +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; +use std::iter::Sum; + +/// A 3-dimensional vector type +#[derive(Copy, Clone, PartialEq)] +pub struct Vec3 { + x: f32, + y: f32, + z: f32, +} + +impl Vec3 { + pub fn new(x: f32, y: f32, z: f32) -> Self { + Self { x, y, z } + } + + pub fn x(&self) -> f32 { + self.x + } + + pub fn y(&self) -> f32 { + self.y + } + + pub fn z(&self) -> f32 { + self.z + } + + pub fn dot(&self, other: &Vec3) -> f32 { + self.x * other.x + self.y * other.y + self.z * other.z + } + + pub fn length2(&self) -> f32 { + self.x * self.x + self.y * self.y + self.z * self.z + } + + pub fn max(&self, other: Vec3) -> Vec3 { + Vec3::new( + self.x.max(other.x), + self.y.max(other.y), + self.z.max(other.z) + ) + } + + pub fn min(&self, other: Vec3) -> Vec3 { + Vec3::new( + self.x.min(other.x), + self.y.min(other.y), + self.z.min(other.z) + ) + } +} + +impl<'a> Add for &'a Vec3 { + type Output = Vec3; + + fn add(self, other: &'a Vec3) -> Vec3 { + Vec3::new( + self.x + other.x, + self.y + other.y, + self.z + other.z + ) + } +} + +impl Add for Vec3 { + type Output = Vec3; + + fn add(self, other: Vec3) -> Vec3 { + Vec3::new( + self.x + other.x, + self.y + other.y, + self.z + other.z + ) + } +} + +impl AddAssign for Vec3 { + fn add_assign(&mut self, other: Vec3) { + self.x += other.x; + self.y += other.y; + self.z += other.z; + } +} + +impl<'a> Add<&'a Vec3> for Vec3 { + type Output = Vec3; + + fn add(self, other: &'a Vec3) -> Vec3 { + Vec3::new( + self.x + other.x, + self.y + other.y, + self.z + other.z + ) + } +} + +impl<'a> AddAssign<&'a Vec3> for Vec3 { + fn add_assign(&mut self, other: &'a Vec3) { + self.x += other.x; + self.y += other.y; + self.z += other.z; + } +} + +impl Add for Vec3 { + type Output = Vec3; + + fn add(self, other: f32) -> Vec3 { + Vec3::new( + self.x + other, + self.y + other, + self.z + other + ) + } +} + +impl<'a> Add for &'a Vec3 { + type Output = Vec3; + + fn add(self, other: f32) -> Vec3 { + Vec3::new( + self.x + other, + self.y + other, + self.z + other + ) + } +} + +impl AddAssign for Vec3 { + fn add_assign(&mut self, other: f32) { + self.x += other; + self.y += other; + self.z += other; + } +} + + +impl Sub for Vec3 { + type Output = Vec3; + + fn sub(self, other: Vec3) -> Vec3 { + Vec3::new( + self.x - other.x, + self.y - other.y, + self.z - other.z + ) + } +} + +impl SubAssign for Vec3 { + fn sub_assign(&mut self, other: Vec3) { + self.x -= other.x; + self.y -= other.y; + self.z -= other.z; + } +} + +impl<'a> Sub<&'a Vec3> for Vec3 { + type Output = Vec3; + + fn sub(self, other: &'a Vec3) -> Vec3 { + Vec3::new( + self.x - other.x, + self.y - other.y, + self.z - other.z + ) + } +} + +impl<'a> Sub for &'a Vec3 { + type Output = Vec3; + + fn sub(self, other: &'a Vec3) -> Vec3 { + Vec3::new( + self.x - other.x, + self.y - other.y, + self.z - other.z + ) + } +} + +impl<'a> SubAssign<&'a Vec3> for Vec3 { + fn sub_assign(&mut self, other: &'a Vec3) { + self.x -= other.x; + self.y -= other.y; + self.z -= other.z; + } +} + +impl Sub for Vec3 { + type Output = Vec3; + + fn sub(self, other: f32) -> Vec3 { + Vec3::new( + self.x - other, + self.y - other, + self.z - other + ) + } +} + +impl<'a> Sub for &'a Vec3 { + type Output = Vec3; + + fn sub(self, other: f32) -> Vec3 { + Vec3::new( + self.x - other, + self.y - other, + self.z - other + ) + } +} + +impl SubAssign for Vec3 { + fn sub_assign(&mut self, other: f32) { + self.x -= other; + self.y -= other; + self.z -= other; + } +} + + + +impl<'a> Mul for &'a Vec3 { + type Output = Vec3; + + fn mul(self, other: &'a Vec3) -> Vec3 { + Vec3::new( + self.x * other.x, + self.y * other.y, + self.z * other.z + ) + } +} + + +impl Mul for Vec3 { + type Output = Vec3; + + fn mul(self, other: Vec3) -> Vec3 { + Vec3::new( + self.x * other.x, + self.y * other.y, + self.z * other.z + ) + } +} + +impl MulAssign for Vec3 { + fn mul_assign(&mut self, other: Vec3) { + self.x *= other.x; + self.y *= other.y; + self.z *= other.z; + } +} + +impl<'a> Mul<&'a Vec3> for Vec3 { + type Output = Vec3; + + fn mul(self, other: &'a Vec3) -> Vec3 { + Vec3::new( + self.x * other.x, + self.y * other.y, + self.z * other.z + ) + } +} + +impl<'a> MulAssign<&'a Vec3> for Vec3 { + fn mul_assign(&mut self, other: &'a Vec3) { + self.x *= other.x; + self.y *= other.y; + self.z *= other.z; + } +} + + +impl Mul for Vec3 { + type Output = Vec3; + + fn mul(self, other: f32) -> Vec3 { + Vec3::new( + self.x * other, + self.y * other, + self.z * other + ) + } +} + +impl<'a> Mul for &'a Vec3 { + type Output = Vec3; + + fn mul(self, other: f32) -> Vec3 { + Vec3::new( + self.x * other, + self.y * other, + self.z * other + ) + } +} + +impl MulAssign for Vec3 { + fn mul_assign(&mut self, other: f32) { + self.x *= other; + self.y *= other; + self.z *= other; + } +} + + +impl Div for Vec3 { + type Output = Vec3; + + fn div(self, other: Vec3) -> Vec3 { + Vec3::new( + self.x / other.x, + self.y / other.y, + self.z / other.z + ) + } +} + +impl DivAssign for Vec3 { + fn div_assign(&mut self, other: Vec3) { + self.x /= other.x; + self.y /= other.y; + self.z /= other.z; + } +} + +impl<'a> Div<&'a Vec3> for Vec3 { + type Output = Vec3; + + fn div(self, other: &'a Vec3) -> Vec3 { + Vec3::new( + self.x / other.x, + self.y / other.y, + self.z / other.z + ) + } +} + +impl<'a> Div for &'a Vec3 { + type Output = Vec3; + + fn div(self, other: &'a Vec3) -> Vec3 { + Vec3::new( + self.x / other.x, + self.y / other.y, + self.z / other.z + ) + } +} + +impl<'a> DivAssign<&'a Vec3> for Vec3 { + fn div_assign(&mut self, other: &'a Vec3) { + self.x /= other.x; + self.y /= other.y; + self.z /= other.z; + } +} + + +impl Div for Vec3 { + type Output = Vec3; + + fn div(self, other: f32) -> Vec3 { + let t = 1.0 / other; + Vec3::new( + self.x * t, + self.y * t, + self.z * t + ) + } +} + +impl<'a> Div for &'a Vec3 { + type Output = Vec3; + + fn div(self, other: f32) -> Vec3 { + let t = 1.0 / other; + Vec3::new( + self.x * t, + self.y * t, + self.z * t + ) + } +} + +impl DivAssign for Vec3 { + fn div_assign(&mut self, other: f32) { + let t = 1.0 / other; + self.x *= t; + self.y *= t; + self.z *= t; + } +} + + +impl Sum for Vec3 { + fn sum>(iter: I) -> Self { + iter.fold(Vec3::new(0.0, 0.0, 0.0), |a, b| a + b) + } +} \ No newline at end of file diff --git a/src/math/vec4.rs b/src/math/vec4.rs new file mode 100644 index 0000000..56de8f4 --- /dev/null +++ b/src/math/vec4.rs @@ -0,0 +1,417 @@ +// Copyright (c) 2006 Simon Brown +// Copyright (c) 2018 Jan Solanti +// +// 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. + + +use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; + +use super::Vec3; + +pub struct Vec4 { + x: f32, + y: f32, + z: f32, + w: f32, +} + +impl Vec4 { + pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self { + Self { x, y, z, w } + } + + pub fn x(&self) -> f32 { + self.x + } + + pub fn y(&self) -> f32 { + self.y + } + + pub fn z(&self) -> f32 { + self.z + } + + pub fn w(&self) -> f32 { + self.w + } + + pub fn to_vec3(&self) -> Vec3 { + Vec3::new(self.x, self.y, self.z) + } + + pub fn splat_x(&self) -> Vec4 { + Vec4::new(self.x, self.x, self.x, self.x) + } + + pub fn splat_y(&self) -> Vec4 { + Vec4::new(self.y, self.y, self.y, self.y) + } + + pub fn splat_z(&self) -> Vec4 { + Vec4::new(self.z, self.z, self.z, self.z) + } + + pub fn splat_w(&self) -> Vec4 { + Vec4::new(self.w, self.w, self.w, self.w) + } + + pub fn reciprocal(&self) -> Vec4 { + Vec4::new(1.0/self.x, 1.0/self.y, 1.0/self.z, 1.0/self.w) + } + + pub fn any_less_than(&self, other: &Vec4) -> bool { + self.x < other.x || self.y < other.y || self.z < other.z || self.w < other.w + } + + pub fn truncate(&self) -> Vec4 { + Vec4::new(self.x.trunc(), self.y.trunc(), self.z.trunc(), self.w.trunc()) + } + + pub fn multiply_add<'a>(a: &'a Vec4, b: &'a Vec4, c: &'a Vec4) -> Vec4 { + a * b + c + } + + pub fn negative_multiply_subtract<'a>(a: &'a Vec4, b: &'a Vec4, c: &'a Vec4) -> Vec4 { + c - a * b + } +} + +impl Add for Vec4 { + type Output = Vec4; + + fn add(self, other: Vec4) -> Vec4 { + Vec4::new( + self.x + other.x, + self.y + other.y, + self.z + other.z, + self.w + other.w + ) + } +} + +impl<'a> Add for &'a Vec4 { + type Output = Vec4; + + fn add(self, other: &'a Vec4) -> Vec4 { + Vec4::new( + self.x + other.x, + self.y + other.y, + self.z + other.z, + self.w + other.w + ) + } +} + +impl<'a> Add for &'a Vec4 { + type Output = Vec4; + + fn add(self, other: Vec4) -> Vec4 { + Vec4::new( + self.x + other.x, + self.y + other.y, + self.z + other.z, + self.w + other.w + ) + } +} + +impl<'a> Add<&'a Vec4> for Vec4 { + type Output = Vec4; + + fn add(self, other: &'a Vec4) -> Vec4 { + Vec4::new( + self.x + other.x, + self.y + other.y, + self.z + other.z, + self.w + other.w + ) + } +} + +impl Add for Vec4 { + type Output = Vec4; + + fn add(self, other: f32) -> Vec4 { + Vec4::new( + self.x + other, + self.y + other, + self.z + other, + self.w + other + ) + } +} + +impl<'a> Add for &'a Vec4 { + type Output = Vec4; + + fn add(self, other: f32) -> Vec4 { + Vec4::new( + self.x + other, + self.y + other, + self.z + other, + self.w + other + ) + } +} + +impl AddAssign for Vec4 { + fn add_assign(&mut self, other: Vec4) { + self.x += other.x; + self.y += other.y; + self.z += other.z; + self.w += other.w; + } +} + +impl<'a> AddAssign<&'a Vec4> for Vec4 { + fn add_assign(&mut self, other: &'a Vec4) { + self.x += other.x; + self.y += other.y; + self.z += other.z; + self.w += other.w; + } +} + +impl AddAssign for Vec4 { + fn add_assign(&mut self, other: f32) { + self.x += other; + self.y += other; + self.z += other; + self.w += other; + } +} + + + + +impl Sub for Vec4 { + type Output = Vec4; + + fn sub(self, other: Vec4) -> Vec4 { + Vec4::new( + self.x - other.x, + self.y - other.y, + self.z - other.z, + self.w - other.w + ) + } +} + +impl<'a> Sub for &'a Vec4 { + type Output = Vec4; + + fn sub(self, other: &'a Vec4) -> Vec4 { + Vec4::new( + self.x - other.x, + self.y - other.y, + self.z - other.z, + self.w - other.w + ) + } +} + +impl<'a> Sub for &'a Vec4 { + type Output = Vec4; + + fn sub(self, other: Vec4) -> Vec4 { + Vec4::new( + self.x - other.x, + self.y - other.y, + self.z - other.z, + self.w - other.w + ) + } +} + +impl<'a> Sub<&'a Vec4> for Vec4 { + type Output = Vec4; + + fn sub(self, other: &'a Vec4) -> Vec4 { + Vec4::new( + self.x - other.x, + self.y - other.y, + self.z - other.z, + self.w - other.w + ) + } +} + +impl Sub for Vec4 { + type Output = Vec4; + + fn sub(self, other: f32) -> Vec4 { + Vec4::new( + self.x - other, + self.y - other, + self.z - other, + self.w - other + ) + } +} + +impl<'a> Sub for &'a Vec4 { + type Output = Vec4; + + fn sub(self, other: f32) -> Vec4 { + Vec4::new( + self.x - other, + self.y - other, + self.z - other, + self.w - other + ) + } +} + +impl SubAssign for Vec4 { + fn sub_assign(&mut self, other: Vec4) { + self.x -= other.x; + self.y -= other.y; + self.z -= other.z; + self.w -= other.w; + } +} + +impl<'a> SubAssign<&'a Vec4> for Vec4 { + fn sub_assign(&mut self, other: &'a Vec4) { + self.x -= other.x; + self.y -= other.y; + self.z -= other.z; + self.w -= other.w; + } +} + +impl SubAssign for Vec4 { + fn sub_assign(&mut self, other: f32) { + self.x -= other; + self.y -= other; + self.z -= other; + self.w -= other; + } +} + + + + +impl Mul for Vec4 { + type Output = Vec4; + + fn mul(self, other: Vec4) -> Vec4 { + Vec4::new( + self.x * other.x, + self.y * other.y, + self.z * other.z, + self.w * other.w + ) + } +} + +impl<'a> Mul for &'a Vec4 { + type Output = Vec4; + + fn mul(self, other: &'a Vec4) -> Vec4 { + Vec4::new( + self.x * other.x, + self.y * other.y, + self.z * other.z, + self.w * other.w + ) + } +} + +impl<'a> Mul for &'a Vec4 { + type Output = Vec4; + + fn mul(self, other: Vec4) -> Vec4 { + Vec4::new( + self.x * other.x, + self.y * other.y, + self.z * other.z, + self.w * other.w + ) + } +} + +impl<'a> Mul<&'a Vec4> for Vec4 { + type Output = Vec4; + + fn mul(self, other: &'a Vec4) -> Vec4 { + Vec4::new( + self.x * other.x, + self.y * other.y, + self.z * other.z, + self.w * other.w + ) + } +} + +impl Mul for Vec4 { + type Output = Vec4; + + fn mul(self, other: f32) -> Vec4 { + Vec4::new( + self.x * other, + self.y * other, + self.z * other, + self.w * other + ) + } +} + +impl<'a> Mul for &'a Vec4 { + type Output = Vec4; + + fn mul(self, other: f32) -> Vec4 { + Vec4::new( + self.x * other, + self.y * other, + self.z * other, + self.w * other + ) + } +} + +impl MulAssign for Vec4 { + fn mul_assign(&mut self, other: Vec4) { + self.x *= other.x; + self.y *= other.y; + self.z *= other.z; + self.w *= other.w; + } +} + +impl<'a> MulAssign<&'a Vec4> for Vec4 { + fn mul_assign(&mut self, other: &'a Vec4) { + self.x *= other.x; + self.y *= other.y; + self.z *= other.z; + self.w *= other.w; + } +} + +impl MulAssign for Vec4 { + fn mul_assign(&mut self, other: f32) { + self.x *= other; + self.y *= other; + self.z *= other; + self.w *= other; + } +} +