diff options
-rw-r--r-- | src/classes/cubemap.rs | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/src/classes/cubemap.rs b/src/classes/cubemap.rs new file mode 100644 index 0000000..bd80408 --- /dev/null +++ b/src/classes/cubemap.rs @@ -0,0 +1,79 @@ +use super::{streaminginfo::StreamingInfo, texture2d::TextureFormat}; +use crate::object::{Value, parser::FromValue}; +use anyhow::Result; +use glam::{UVec2, Vec3Swizzles, uvec2, vec2, vec3}; +use image::{ImageBuffer, Rgba}; +use serde::Serialize; +use std::f32::consts::PI; + +#[derive(Debug, Serialize)] +pub struct Cubemap { + pub width: i32, + pub height: i32, + pub mip_count: i32, + pub name: String, + #[serde(skip)] + pub image_data: Vec<u8>, + pub format: TextureFormat, + pub texture_dimension: i32, + pub stream_data: StreamingInfo, +} + +impl FromValue for Cubemap { + fn from_value(v: Value) -> Result<Self> { + let mut fields = v.as_class("Cubemap")?; + Ok(Cubemap { + width: fields.field("m_Width")?, + height: fields.field("m_Height")?, + mip_count: fields.field("m_MipCount")?, + texture_dimension: fields.field("m_TextureDimension")?, + format: fields.field("m_TextureFormat")?, + name: fields.field("m_Name")?, + stream_data: fields.field("m_StreamData")?, + image_data: fields.remove("image data").unwrap().as_typeless().unwrap(), + }) + } +} + +/// Face ordering is Left, Right, Back, Front, Down, Up +pub fn cubemap_to_equirectangular( + faces: &[ImageBuffer<Rgba<f32>, Vec<f32>>; 6], + output_width: u32, + output_height: u32, +) -> ImageBuffer<Rgba<f32>, Vec<f32>> { + let mut output_image = ImageBuffer::<Rgba<f32>, Vec<f32>>::new(output_width, output_height); + for (er_x, er_y, out) in output_image.enumerate_pixels_mut() { + let xy_angle = er_x as f32 / output_width as f32 * PI * 2.; + let cyl_z = er_y as f32 / output_height as f32 * 2. - 1.; + let sphere_azimuth = cyl_z.asin(); + let r = sphere_azimuth.cos(); + let normal = vec3(xy_angle.sin() * r, xy_angle.cos() * r, cyl_z); + assert!(normal.is_normalized()); + + let normal_abs = normal.abs(); + let (axis, max_comp, other_comps) = if normal_abs.x > normal_abs.y.max(normal_abs.z) { + (0, normal.x, normal.yz()) + } else if normal_abs.y > normal_abs.z { + (1, normal.y, normal.xz()) + } else { + (2, normal.z, normal.xy()) + }; + + let face = axis as usize * 2 + if max_comp < 0. { 1 } else { 0 }; + let flip_face = [1., 1., -1., -1., 1., 1.]; + let rotate_face = if face == 5 { -1. } else { 1. }; + let face_texture = &faces[face]; + let face_size = uvec2(face_texture.width(), face_texture.height()); + let face_uv = + rotate_face * (other_comps / max_comp * vec2(flip_face[face], -max_comp.signum())); + let face_uv = face_uv / 2. + 0.5; + + let face_xy = (face_uv * face_size.as_vec2()) + .floor() + .as_uvec2() + .clamp(UVec2::MIN, face_size - UVec2::ONE); + + out.0 = face_texture.get_pixel(face_xy.x, face_xy.y).0; + } + output_image +} |