diff --git a/Cargo.toml b/Cargo.toml index 27cb015..ee8270a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,14 +46,14 @@ serde = ["dep:serde", "half/serde", "cgmath/serde"] [dependencies] cgmath = "0.18" half = {version="2", features=["std", "num-traits", "zerocopy"]} -thiserror = "1" -reqwest = {version = "0.11", optional = true, default-features = false } +thiserror = "2" +reqwest = {version = "0.12", optional = true, default-features = false } gltf = { version = "1", optional = true, features=["KHR_materials_ior", "KHR_materials_transmission"] } wavefront_obj = { version = "10", optional = true } stl_io = { version = "0.8.2", optional = true } -image = { version = "0.24", optional = true, default-features = false} -resvg = { version = "0.43.0", optional = true } -pcd-rs = { version = "0.10", optional = true, features = ["derive"] } +image = { version = "0.25", optional = true, default-features = false} +resvg = { version = "0.44.0", optional = true } +pcd-rs = { version = "0.12", optional = true, features = ["derive"] } data-url = {version = "0.3", optional = true } serde = {version= "1", optional = true, features = ["derive", "rc"] } diff --git a/src/camera.rs b/src/camera.rs index 9f802f4..3a2518f 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -567,12 +567,20 @@ impl Camera { } /// - /// Returns the up direction of this camera (might not be orthogonal to the view direction). + /// Returns the up direction of this camera. + /// This will probably not be orthogonal to the view direction, use [up_orthogonal](Camera::up_orthogonal) instead if that is needed. /// pub fn up(&self) -> &Vec3 { &self.up } + /// + /// Returns the up direction of this camera that is orthogonal to the view direction. + /// + pub fn up_orthogonal(&self) -> Vec3 { + self.right_direction().cross(self.view_direction()) + } + /// /// Returns the view direction of this camera, ie. the direction the camera is looking. /// @@ -605,11 +613,14 @@ impl Camera { fn update_screen2ray(&mut self) { let mut v = self.view; + v /= v[3][3]; if let ProjectionType::Perspective { .. } = self.projection_type { v /= v[3][3]; v[3] = vec4(0.0, 0.0, 0.0, 1.0); } - self.screen2ray = (self.projection * v).invert().unwrap(); + self.screen2ray = (self.projection * v) + .invert() + .unwrap_or_else(|| Mat4::identity()); } fn update_frustrum(&mut self) { @@ -723,8 +734,17 @@ impl Camera { } } + /// + /// Moves the camera towards the camera target by the amount delta while keeping the given minimum and maximum distance to the target. + /// + pub fn zoom(&mut self, delta: f32, minimum_distance: f32, maximum_distance: f32) { + let target = self.target; + self.zoom_towards(&target, delta, minimum_distance, maximum_distance); + } + /// /// Moves the camera towards the given point by the amount delta while keeping the given minimum and maximum distance to the point. + /// Note that the camera target is also updated so that the view direction is the same. /// pub fn zoom_towards( &mut self, @@ -741,11 +761,15 @@ impl Camera { let position = *self.position(); let distance = point.distance(position); - let direction = (point - position).normalize(); - let target = self.target; - let up = self.up; - let new_distance = (distance - delta).clamp(minimum_distance, maximum_distance); - let new_position = point - direction * new_distance; - self.set_view(new_position, target, up); + if distance > f32::EPSILON { + let delta_clamped = + distance - (distance - delta).clamp(minimum_distance, maximum_distance); + let v = (point - position) * delta_clamped / distance; + self.set_view( + self.position + v, + self.target + v - v.project_on(self.view_direction()), + self.up, + ); + } } } diff --git a/src/io/img.rs b/src/io/img.rs index a925c42..0338835 100644 --- a/src/io/img.rs +++ b/src/io/img.rs @@ -1,5 +1,5 @@ use crate::{io::RawAssets, texture::*, Error, Result}; -use image::{io::Reader, *}; +use image::*; use std::io::Cursor; use std::path::Path; @@ -10,34 +10,13 @@ pub fn deserialize_img(path: impl AsRef, bytes: &[u8]) -> Result>(), - ), - width: metadata.width, - height: metadata.height, - ..Default::default() - }); - } let img: DynamicImage = reader.decode()?; let width = img.width(); let height = img.height(); @@ -61,6 +40,12 @@ pub fn deserialize_img(path: impl AsRef, bytes: &[u8]) -> Result>(), ), + DynamicImage::ImageRgb32F(img) => TextureData::RgbF32( + img.into_raw() + .chunks(3) + .map(|c| [c[0], c[1], c[2]]) + .collect::>(), + ), _ => unimplemented!(), }; Ok(Texture2D { @@ -114,42 +99,42 @@ pub fn deserialize_svg(path: impl AsRef, bytes: &[u8]) -> Result Result { #![allow(unreachable_code)] #![allow(unused_variables)] - let format: image::ImageOutputFormat = match path.extension().unwrap().to_str().unwrap() { + let format: ImageFormat = match path.extension().unwrap().to_str().unwrap() { "png" => { #[cfg(not(feature = "png"))] return Err(Error::FeatureMissing("png".to_string())); #[cfg(feature = "png")] - image::ImageOutputFormat::Png + ImageFormat::Png } "jpeg" | "jpg" => { #[cfg(not(feature = "jpeg"))] return Err(Error::FeatureMissing("jpeg".to_string())); #[cfg(feature = "jpeg")] - image::ImageOutputFormat::Jpeg(100) + ImageFormat::Jpeg } "bmp" => { #[cfg(not(feature = "bmp"))] return Err(Error::FeatureMissing("bmp".to_string())); #[cfg(feature = "bmp")] - image::ImageOutputFormat::Bmp + ImageFormat::Bmp } "tga" => { #[cfg(not(feature = "tga"))] return Err(Error::FeatureMissing("tga".to_string())); #[cfg(feature = "tga")] - image::ImageOutputFormat::Tga + ImageFormat::Tga } "tiff" | "tif" => { #[cfg(not(feature = "tiff"))] return Err(Error::FeatureMissing("tiff".to_string())); #[cfg(feature = "tiff")] - image::ImageOutputFormat::Tiff + ImageFormat::Tiff } "gif" => { #[cfg(not(feature = "gif"))] return Err(Error::FeatureMissing("gif".to_string())); #[cfg(feature = "gif")] - image::ImageOutputFormat::Gif + ImageFormat::Gif } _ => return Err(Error::FailedSerialize(path.to_str().unwrap().to_string())), }; @@ -173,14 +158,29 @@ pub fn serialize_img(tex: &Texture2D, path: &Path) -> Result { ) .unwrap(), ), - TextureData::RgbaU8(data) => DynamicImage::ImageRgba8( - ImageBuffer::from_raw( - tex.width, - tex.height, - data.iter().flat_map(|v| *v).collect::>(), - ) - .unwrap(), - ), + TextureData::RgbaU8(data) => { + if format == ImageFormat::Jpeg { + DynamicImage::ImageRgb8( + ImageBuffer::from_raw( + tex.width, + tex.height, + data.iter() + .flat_map(|v| [v[0], v[1], v[2]]) + .collect::>(), + ) + .unwrap(), + ) + } else { + DynamicImage::ImageRgba8( + ImageBuffer::from_raw( + tex.width, + tex.height, + data.iter().flat_map(|v| *v).collect::>(), + ) + .unwrap(), + ) + } + } _ => unimplemented!(), }; let mut bytes: Vec = Vec::new(); @@ -212,7 +212,11 @@ mod test { if format == "jpeg" || format == "jpg" { if let crate::TextureData::RgbU8(data) = tex.data { - assert_eq!(data, vec![[4, 0, 0], [250, 0, 1], [0, 254, 1], [1, 2, 253]]); + // Jpeg is not lossless + assert_eq!( + data, + vec![[47, 0, 18], [227, 0, 0], [0, 245, 0], [15, 32, 255]] + ); } else { panic!("Wrong texture data: {:?}", tex.data) } diff --git a/src/io/pcd.rs b/src/io/pcd.rs index ace1e1f..59696a7 100644 --- a/src/io/pcd.rs +++ b/src/io/pcd.rs @@ -13,7 +13,7 @@ pub fn deserialize_pcd(raw_assets: &mut RawAssets, path: &PathBuf) -> Result>>()?; + let points = reader.collect::>>()?; let positions = points .iter() .map(|p| { diff --git a/src/lib.rs b/src/lib.rs index 7df2c3e..34aef5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -229,7 +229,7 @@ pub enum Error { #[cfg(feature = "pcd")] #[error("error while parsing an .pcd file")] - Pcd(#[from] pcd_rs::anyhow::Error), + Pcd(#[from] pcd_rs::Error), #[cfg(any(not(target_arch = "wasm32"), feature = "stl"))] #[error("io error")] diff --git a/test_data/test.jpeg b/test_data/test.jpeg index a255563..7c9e4f5 100644 Binary files a/test_data/test.jpeg and b/test_data/test.jpeg differ diff --git a/test_data/test.jpg b/test_data/test.jpg index a255563..7c9e4f5 100644 Binary files a/test_data/test.jpg and b/test_data/test.jpg differ diff --git a/test_data/test.png b/test_data/test.png index 2eed7d0..9667fcc 100644 Binary files a/test_data/test.png and b/test_data/test.png differ diff --git a/test_data/test.tga b/test_data/test.tga index b3a8be4..6d01fda 100644 Binary files a/test_data/test.tga and b/test_data/test.tga differ