diff --git a/Cargo.lock b/Cargo.lock index 1e9bae1..2ed66ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3018,7 +3018,7 @@ dependencies = [ [[package]] name = "tes3map" -version = "0.2.0" +version = "0.2.1" dependencies = [ "eframe", "egui", diff --git a/Cargo.toml b/Cargo.toml index e5b8ffb..8b45594 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tes3map" -version = "0.2.0" +version = "0.2.1" authors = ["Moritz Baron "] edition = "2021" diff --git a/assets/ui_conflicts_01.png b/assets/ui_conflicts_01.png new file mode 100644 index 0000000..1bfb5c0 Binary files /dev/null and b/assets/ui_conflicts_01.png differ diff --git a/assets/ui_landscape_02.png b/assets/ui_landscape_02.png new file mode 100644 index 0000000..314db00 Binary files /dev/null and b/assets/ui_landscape_02.png differ diff --git a/src/app.rs b/src/app.rs index af1778c..9911072 100644 --- a/src/app.rs +++ b/src/app.rs @@ -67,7 +67,7 @@ pub struct TemplateApp { #[serde(skip)] pub texture_map_resolution: usize, #[serde(skip)] - pub texture_map: HashMap, + pub texture_map: HashMap, // runtime data #[serde(skip)] @@ -157,45 +157,47 @@ impl TemplateApp { let key = data[dy][dx] as u32; - // lazy load texture - if let std::collections::hash_map::Entry::Vacant(e) = - self.texture_map.entry(key) - { - // load texture - if let Some(ltex) = self.ltex_records.get(&key) { - if let Some(tex) = load_texture(&self.data_files, ltex) - { - // transform texture and downsize - - // scale texture to fit the texture_size - let mut pixels = vec![ - Color32::TRANSPARENT; - texture_size * texture_size - ]; - - // textures per tile - for x in 0..texture_size { - for y in 0..texture_size { - // pick every nth pixel from the texture to downsize - let sx = - x * (TEXTURE_MAX_SIZE / texture_size); - let sy = - y * (TEXTURE_MAX_SIZE / texture_size); - let index = (sy * texture_size) + sx; - let color = tex.pixels[index]; - - let i = (y * texture_size) + x; - pixels[i] = color; - } + // load texture + if let Some(ltex) = self.ltex_records.get(&key) { + // texture name + let texture_name = ltex.file_name.clone(); + if self.texture_map.contains_key(&texture_name) { + continue; + } + + if let Some(tex) = load_texture(&self.data_files, ltex) { + // transform texture and downsize + + // scale texture to fit the texture_size + let mut pixels = vec![ + Color32::TRANSPARENT; + texture_size * texture_size + ]; + + // textures per tile + for x in 0..texture_size { + for y in 0..texture_size { + // pick every nth pixel from the texture to downsize + let sx = x * (TEXTURE_MAX_SIZE / texture_size); + let sy = y * (TEXTURE_MAX_SIZE / texture_size); + let index = (sy * texture_size) + sx; + let color = tex.pixels[index]; + + let i = (y * texture_size) + x; + pixels[i] = color; } + } - let downsized_texture = ColorImage { - size: [texture_size, texture_size], - pixels, - }; + let downsized_texture = ColorImage { + size: [texture_size, texture_size], + pixels, + }; - e.insert(downsized_texture); - } + info!("Loaded texture: {}", ltex.file_name); + self.texture_map + .insert(texture_name, downsized_texture); + } else { + error!("Failed to load texture: {}", ltex.file_name); } } } @@ -275,6 +277,7 @@ impl TemplateApp { if let Some(i) = compute_landscape_image( dimensions, &self.land_records, + &self.ltex_records, &self.heights, &self.texture_map, ) { diff --git a/src/background/landscape.rs b/src/background/landscape.rs index a1bb56d..b0f8f2e 100644 --- a/src/background/landscape.rs +++ b/src/background/landscape.rs @@ -2,19 +2,20 @@ use std::collections::HashMap; use egui::{Color32, ColorImage}; use log::info; -use tes3::esp::{Landscape, LandscapeFlags}; +use tes3::esp::{Landscape, LandscapeFlags, LandscapeTexture}; use crate::{ - CellKey, DEFAULT_COLOR, Dimensions, GRID_SIZE, height_from_screen_space, - overlay_colors_with_alpha, VERTEX_CNT, + height_from_screen_space, overlay_colors_with_alpha, CellKey, Dimensions, DEFAULT_COLOR, + GRID_SIZE, VERTEX_CNT, }; /// Compute a landscape image from the given landscape records and texture map. pub fn compute_landscape_image( dimensions: &Dimensions, landscape_records: &HashMap, + ltex_records: &HashMap, heights: &[f32], - texture_map: &HashMap, + texture_map: &HashMap, ) -> Option { let d = dimensions; let size = d.pixel_size(d.cell_size()); @@ -43,8 +44,15 @@ pub fn compute_landscape_image( let dy = (4 * (gy / 4)) + (gx / 4); let key = data[dy][dx] as u32; + let mut texture_name = String::new(); + if let Some(ltex) = ltex_records.get(&key) { + texture_name.clone_from(<ex.file_name); + } + if texture_name.is_empty() { + continue; + } - if let Some(texture) = texture_map.get(&key) { + if let Some(texture) = texture_map.get(&texture_name) { for x in 0..d.texture_size { for y in 0..d.texture_size { let index = (y * d.texture_size) + x; diff --git a/src/lib.rs b/src/lib.rs index 33c23dd..afb58ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,10 +8,10 @@ use std::{ use egui::{Color32, ColorImage, Pos2}; use image::{ - DynamicImage, - error::{ImageFormatHint, UnsupportedError, UnsupportedErrorKind}, ImageError, RgbaImage, + error::{ImageFormatHint, UnsupportedError, UnsupportedErrorKind}, + DynamicImage, ImageError, RgbaImage, }; -use log::info; +use log::{info, warn}; use seahash::hash; use serde::{Deserialize, Serialize}; use tes3::esp::{ @@ -343,28 +343,54 @@ pub fn get_layered_image(dimensions: &Dimensions, img: ColorImage, img2: ColorIm } fn load_texture(data_files: &Option, ltex: &LandscapeTexture) -> Option { - // data files let data_files = data_files.as_ref()?; - let texture = ltex.file_name.clone(); - let tex_path = data_files.join("Textures").join(texture); - if !tex_path.exists() { + let _tex_path = data_files.join("Textures").join(ltex.file_name.clone()); + + let tga_path = _tex_path.with_extension("tga"); + let dds_path = _tex_path.with_extension("dds"); + let bmp_path = _tex_path.with_extension("bmp"); + + if !tga_path.exists() && !dds_path.exists() && !bmp_path.exists() { + warn!("Texture not found: {:?}", _tex_path); return None; } // decode image + if let Some(value) = decode_image(dds_path) { + return Some(value); + } + if let Some(value) = decode_image(tga_path) { + return Some(value); + } + if let Some(value) = decode_image(bmp_path) { + return Some(value); + } + + None +} + +fn decode_image(tex_path: PathBuf) -> Option { if let Ok(mut reader) = image::io::Reader::open(&tex_path) { - let ext = tex_path.extension().unwrap().to_string_lossy(); + let ext = tex_path + .extension() + .unwrap() + .to_string_lossy() + .to_lowercase(); if ext.contains("tga") { reader.set_format(image::ImageFormat::Tga); } else if ext.contains("dds") { reader.set_format(image::ImageFormat::Dds); + } else if ext.contains("bmp") { + reader.set_format(image::ImageFormat::Bmp); } else { // not supported + warn!("Texture format not supported: {:?}", tex_path); return None; } let Ok(image) = reader.decode() else { + log::error!("Error decoding texture: {:?}", tex_path); return None; }; @@ -373,7 +399,6 @@ fn load_texture(data_files: &Option, ltex: &LandscapeTexture) -> Option let pixels = image_buffer.as_flat_samples(); return Some(ColorImage::from_rgba_unmultiplied(size, pixels.as_slice())); } - None }