feat(viewer): ISO / extracted-dir loading pipeline + native file dialog
Some checks failed
CI / Native — ubuntu-latest (push) Failing after 7m14s
CI / WASM — Web (push) Failing after 6m35s
CI / Formatting (push) Failing after 35s
CI / Native — macos-latest (push) Has been cancelled
CI / Native — windows-latest (push) Has been cancelled

WIP: add iso_loader.rs — bridges the async XisoReader / synchronous
GameAssets to Bevy's ECS via background threads + mpsc channels polled
per frame (no main-thread blocking); UI open-iso/open-dir/file-select
events → texture preview. Register IsoLoaderPlugin; wire asset_loader
and the egui UI. Deps: rfd (native file dialog, workspace + viewer),
futures, and bevy tonemapping_luts feature.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-10 07:18:45 +02:00
parent ce9fe08bec
commit d92c7ddaea
7 changed files with 1111 additions and 61 deletions

View File

@@ -12,10 +12,11 @@
//! 4. Bevy uploads it to the GPU automatically
use bevy::asset::io::Reader;
use bevy::asset::{AssetLoader, AsyncReadExt, LoadContext};
use bevy::asset::{AssetLoader, LoadContext};
use bevy::image::ImageSampler;
use bevy::prelude::*;
use bevy::render::render_asset::RenderAssetUsages;
use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat};
use bevy::render::render_resource::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages};
use sylpheed_formats::texture::{X360Texture, X360TextureFormat};
// ── Plugin ────────────────────────────────────────────────────────────────────
@@ -86,24 +87,32 @@ impl AssetLoader for Xpr2TextureLoader {
/// Convert a decoded `X360Texture` into a Bevy-compatible `Image`.
///
/// Maps Xbox 360 D3DFORMAT codes to `wgpu::TextureFormat` values.
/// Bevy uses wgpu internally, so this mapping is direct.
/// Constructs the `Image` struct directly rather than using `Image::new()`,
/// because `Image::new()` calls `pixel_size()` which panics for BCn
/// block-compressed formats.
pub fn x360_texture_to_bevy_image(tex: X360Texture) -> Result<Image, Xpr2LoadError> {
let bevy_format = x360_format_to_wgpu(&tex.format)?;
let format = x360_format_to_wgpu(&tex.format)?;
Ok(Image::new(
Extent3d {
width: tex.width,
height: tex.height,
depth_or_array_layers: 1,
Ok(Image {
data: tex.data,
texture_descriptor: TextureDescriptor {
label: None,
size: Extent3d {
width: tex.width,
height: tex.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format,
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
view_formats: &[],
},
TextureDimension::D2,
tex.data,
bevy_format,
// Make the texture available on both CPU and GPU
// (RenderOnly would be more efficient once RE is complete)
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
))
sampler: ImageSampler::Default,
texture_view_descriptor: None,
asset_usage: RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
})
}
/// Map an `X360TextureFormat` to the corresponding `wgpu::TextureFormat`.
@@ -119,14 +128,12 @@ fn x360_format_to_wgpu(format: &X360TextureFormat) -> Result<TextureFormat, Xpr2
X360TextureFormat::Dxt3 => TextureFormat::Bc2RgbaUnormSrgb,
// DXT5 / BC3
X360TextureFormat::Dxt5 => TextureFormat::Bc3RgbaUnormSrgb,
// DXN / BC5 — normal maps (RG, not sRGB)
// DXN / BC5 — two-channel normal maps (RG, not sRGB)
X360TextureFormat::Dxn => TextureFormat::Bc5RgUnorm,
// Uncompressed ARGB — note Xbox 360 is BGRA order (big-endian)
// We may need to swizzle R and B channels
// DXT5A / BC4 — single-channel (gloss, specular, luminance maps)
X360TextureFormat::Dxt5A => TextureFormat::Bc4RUnorm,
// Uncompressed ARGB — Xbox 360 stores as BGRA in memory
X360TextureFormat::A8R8G8B8 | X360TextureFormat::X8R8G8B8 => {
// Xbox 360 stores as ARGB big-endian (BGRA in memory)
// wgpu uses Rgba8UnormSrgb — swizzle may be needed
// TODO: verify channel order against actual game textures
TextureFormat::Bgra8UnormSrgb
}
})