aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/classes/cubemap.rs79
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
+}