From caa0e43c9c7bd69559069df61c483faff2fe3d7e Mon Sep 17 00:00:00 2001 From: Azorlogh Date: Thu, 27 Jun 2024 10:26:59 +0200 Subject: [PATCH] first commit --- .gitignore | 3 + Cargo.lock | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 ++ README.md | 9 ++ run.sh | 17 ++++ src/main.rs | 122 ++++++++++++++++++++++++ 6 files changed, 420 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100755 run.sh create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fdfcc69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/output +kloofendal.hdr diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0a3e02b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,260 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "glam" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "remap_hdri" +version = "0.1.0" +dependencies = [ + "clap", + "glam", + "zune-hdr", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-hdr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ff474631f80b14afc6dbc6bab78702ed2de7cd6af85f7a4ab21d3b08686426" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6d1c6ec --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "remap_hdri" +version = "0.1.0" +edition = "2021" + +[dependencies] +clap = { version = "4.5.7", features = ["derive"] } +glam = "0.28.0" +zune-hdr = "0.4" diff --git a/README.md b/README.md new file mode 100644 index 0000000..b8e94f5 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Hacky HDRi Rotator Tool + +To get textures to use in bevy: + +``` +remap_hdri <> kloofendal.hdr -o output/kloofendal_rotated.hdr +``` + +Example usage to get textures in bevy in `run.sh` diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..f2edd1f --- /dev/null +++ b/run.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +rm -r output + +mkdir -p output + +name=$( basename "$1" .hdr ) + +cargo run --release -- -i "$1" -o output/"$name"_rotated.hdr + +cd output + +magick "$name"_rotated.hdr debug.png + +ibl-sampler-cli -inputPath "$name"_rotated.hdr -outCubeMap "$name"_specular.ktx2 -distribution GGX -cubeMapResolution 512 +ibl-sampler-cli -inputPath "$name"_rotated.hdr -outCubeMap "$name"_diffuse.ktx2 -distribution Lambertian -cubeMapResolution 32 +bevy_mod_environment_map_tools --inputs "$name"_diffuse.ktx2,"$name"_specular.ktx2 --outputs "$name"_diffuse_rgb9e5_zstd.ktx2,"$name"_specular_rgb9e5_zstd.ktx2 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..2d938a5 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,122 @@ +use std::{f64::consts::TAU, path::PathBuf}; + +use clap::Parser; +use glam::{DQuat, DVec3, IVec2, Vec3}; +use zune_hdr::{ + zune_core::{bit_depth::BitDepth, options::EncoderOptions}, + HdrDecoder, HdrEncoder, +}; + +#[derive(Debug, Parser)] +pub struct Args { + #[arg(long, short)] + input: PathBuf, + #[arg(long, short)] + output: PathBuf, +} + +fn main() -> Result<(), Box> { + let args = Args::parse(); + let contents = std::fs::read(args.input)?; + let mut data = HdrDecoder::new(contents); + let pix: Vec = data.decode()?; + + let (in_w, in_h) = data.get_dimensions().unwrap(); + const OUTW: usize = 1024; + const OUTH: usize = 512; + + let mut out: Vec = vec![0.0; OUTW * OUTH * 3]; + // Mask telling us which pixels have been filled + let mut mask: Vec = vec![false; OUTW * OUTH]; + + // The rotation to apply + let rotation = DQuat::from_axis_angle(DVec3::X, -TAU / 4.0); + + // Copy texels from the original texture into the new one + for y in 0..in_h { + for x in 0..in_w { + for i in 0..3 { + let p = pix[y * in_w * 3 + x * 3 + i]; + let lat = (y as f64 / in_h as f64 - 0.5) * (TAU / 2.0); + let lon = (x as f64 / in_w as f64 - 0.5) * TAU; + let point = DQuat::from_axis_angle(DVec3::Z, lon) + * DQuat::from_axis_angle(DVec3::Y, -lat) + * DVec3::X; + + let point = rotation * point; + + let new_lat = point.z.asin(); + let new_lon = point.truncate().normalize().to_angle(); + + let new_r = ((new_lat / (TAU / 2.0) + 0.5) * (OUTH as f64)).floor() as usize; + let new_c = ((new_lon / TAU + 0.5) * (OUTW as f64)).floor() as usize; + out[(new_r * OUTW * 3 + new_c * 3 + i).min(OUTW * OUTH * 3 - 1)] = p; + mask[(new_r * OUTW + new_c).min(OUTW * OUTH - 1)] = true; + } + } + } + + // Some texels of the output texture were never written to + // fill them in by using flood fill + let mut complete = false; + while !complete { + // slow, but we don't care :D + let mut staging_out: Vec = out.clone(); + let mut staging_mask: Vec = mask.clone(); + + complete = true; + for y in 0..OUTH { + for x in 0..OUTW { + let coord = IVec2::new(x as i32, y as i32); + if !mask[y * OUTW + x] { + // average out neighboring colors + let (sum_color, sum) = [ + IVec2::new(-1, 0), + IVec2::new(1, 0), + IVec2::new(0, -1), + IVec2::new(0, 1), + ] + .into_iter() + .fold((Vec3::ZERO, 0.0), |acc, offset| { + let c = coord + offset; + if c.clamp(IVec2::ZERO, IVec2::new(OUTW as i32 - 1, OUTH as i32 - 1)) == c { + let col = &out[(c.y * OUTW as i32 * 3 + c.x * 3) as usize..][..3]; + if mask[(c.y * OUTW as i32 + c.x) as usize] { + return (acc.0 + Vec3::new(col[0], col[1], col[2]), acc.1 + 1.0); + } + } + acc + }); + if sum == 0.0 { + // no neighboring colors are filled in, skip it + complete = false; + continue; + } else { + // set the average as color + let color = sum_color / sum; + staging_out[y * OUTW * 3 + x * 3 + 0] = color.x; + staging_out[y * OUTW * 3 + x * 3 + 1] = color.y; + staging_out[y * OUTW * 3 + x * 3 + 2] = color.z; + staging_mask[y * OUTW + x] = true; + } + } + } + } + + out = staging_out; + mask = staging_mask; + } + + let encoder = HdrEncoder::new( + &out, + EncoderOptions::new( + OUTW, + OUTH, + data.get_colorspace().unwrap(), + BitDepth::Float32, + ), + ); + std::fs::write(args.output, encoder.encode().unwrap()).unwrap(); + + Ok(()) +}