WIP CMDL viewer & improvements

This commit is contained in:
Luke Street
2023-03-02 00:38:21 -05:00
parent 06a8dc1081
commit 32ed510fcb
16 changed files with 2129 additions and 409 deletions
Generated
+4
View File
@@ -3055,6 +3055,7 @@ dependencies = [
"binrw",
"binrw_derive",
"ddsfile",
"flate2",
"log",
"memmap2",
"tegra_swizzle",
@@ -3090,10 +3091,13 @@ dependencies = [
"bevy",
"bevy_egui",
"binrw",
"bytemuck",
"egui",
"egui_dock",
"half",
"image",
"log",
"num-traits",
"retrolib",
"uuid",
"walkdir",
+1
View File
@@ -14,6 +14,7 @@ anyhow = "1.0.69"
binrw = "0.11.1"
binrw_derive = "0.11.1"
ddsfile = { git = "https://github.com/encounter/ddsfile", rev = "880f04c1dffa680eab0e9e09cfa58591fe186a31" }
flate2 = "1.0.25"
log = "0.4.17"
memmap2 = "0.5.9"
tegra_swizzle = "0.3.0"
+11 -10
View File
@@ -1,4 +1,4 @@
use std::{borrow::Cow, io::Cursor};
use std::io::Cursor;
use anyhow::{bail, ensure, Result};
use binrw::{binrw, BinReaderExt, Endian};
@@ -745,11 +745,11 @@ pub enum EMaterialFlag {
MFC4 = 26,
}
fn decompress_gpu_buffers<'a>(
file_data: &'a [u8],
fn decompress_gpu_buffers(
file_data: &[u8],
read_info: &[SModelReadBufferInfo],
buffer_info: &[SModelBufferInfo],
) -> Result<Vec<Cow<'a, [u8]>>> {
) -> Result<Vec<Vec<u8>>> {
let mut out = Vec::with_capacity(buffer_info.len());
for info in buffer_info {
let read_info = &read_info[info.read_index as usize];
@@ -757,23 +757,24 @@ fn decompress_gpu_buffers<'a>(
&file_data[read_info.offset as usize..(read_info.offset + read_info.size) as usize];
let comp_buf = &read_buffer[info.offset as usize..(info.offset + info.size) as usize];
let (_, buf) = decompress_buffer(comp_buf, info.dest_size as u64)?;
out.push(buf);
out.push(buf.into_owned());
}
Ok(out)
}
pub struct ModelData<'a> {
#[derive(Debug, Clone)]
pub struct ModelData {
pub head: SModelHeader,
pub mtrl: SMaterialChunk,
pub mesh: SMeshLoadInformation,
pub vbuf: SVertexBufferInfoSection,
pub ibuf: SIndexBufferInfoSection,
pub vtx_buffers: Vec<Cow<'a, [u8]>>,
pub idx_buffers: Vec<Cow<'a, [u8]>>,
pub vtx_buffers: Vec<Vec<u8>>,
pub idx_buffers: Vec<Vec<u8>>,
}
impl ModelData<'_> {
pub fn slice<'a>(data: &'a [u8], meta: &[u8], e: Endian) -> Result<ModelData<'a>> {
impl ModelData {
pub fn slice(data: &[u8], meta: &[u8], e: Endian) -> Result<ModelData> {
let (cmdl_desc, mut cmdl_data, _) = FormDescriptor::slice(data, Endian::Little)?;
ensure!(cmdl_desc.id == K_FORM_CMDL);
ensure!(cmdl_desc.version_a == 114);
+1
View File
@@ -1,6 +1,7 @@
pub mod chunk;
pub mod cmdl;
pub mod foot;
pub mod mtrl;
pub mod pack;
pub mod rfrm;
pub mod txtr;
+43
View File
@@ -0,0 +1,43 @@
use std::io::{Cursor, Read};
use anyhow::{ensure, Result};
use binrw::{binrw, BinReaderExt, Endian};
use flate2::bufread::ZlibDecoder;
use crate::format::{rfrm::FormDescriptor, FourCC};
// Texture
pub const K_FORM_MTRL: FourCC = FourCC(*b"MTRL");
#[binrw]
#[derive(Clone, Debug)]
struct SMaterialMetaData {
unk1: u32, // count?
unk2: u32, // reader version?
compressed_size: u32,
decompressed_size: u32,
file_offset: u32,
}
#[derive(Debug, Clone)]
pub struct MaterialData {
pub decompressed: Vec<u8>,
}
impl MaterialData {
pub fn slice(data: &[u8], meta: &[u8], e: Endian) -> Result<MaterialData> {
let (mtrl_desc, _, _) = FormDescriptor::slice(data, Endian::Little)?;
ensure!(mtrl_desc.id == K_FORM_MTRL);
ensure!(mtrl_desc.version_a == 168);
ensure!(mtrl_desc.version_b == 168);
let meta: SMaterialMetaData = Cursor::new(meta).read_type(e)?;
let mut reader = ZlibDecoder::new(
&data[meta.file_offset as usize..(meta.file_offset + meta.compressed_size) as usize],
);
let mut decompressed = vec![0u8; meta.decompressed_size as usize];
reader.read_exact(&mut decompressed)?;
Ok(MaterialData { decompressed })
}
}
+3
View File
@@ -19,10 +19,13 @@ astc-decode = "0.3.1"
bevy = "0.9.1"
bevy_egui = { git = "https://github.com/mvlabat/bevy_egui", rev = "65f37508339e3a1abe54ae901dad7f49e820cd41" }
binrw = "0.11.1"
bytemuck = "1.13.0"
egui = "0.21.0"
egui_dock = "0.4.0"
half = "2.2.1"
image = "0.24.5"
log = "0.4.17"
num-traits = "0.2.15"
retrolib = { path = "../lib" }
uuid = "1.3.0"
walkdir = "2.3.2"
Binary file not shown.
File diff suppressed because it is too large Load Diff
+111 -2
View File
@@ -9,16 +9,19 @@ use astc_decode::{astc_decode, Footprint};
use bevy::{
app::{App, Plugin},
asset::{
AddAsset, AssetIo, AssetIoError, AssetLoader, BoxedFuture, LoadContext, LoadedAsset,
Metadata,
AddAsset, AssetIo, AssetIoError, AssetLoader, AssetPath, BoxedFuture, LoadContext,
LoadedAsset, Metadata,
},
prelude::*,
utils::HashMap,
};
use binrw::Endian;
use image::RgbaImage;
use retrolib::{
format::{
cmdl::{CMaterialDataInner, ModelData},
foot::locate_meta,
mtrl::MaterialData,
pack::{Package, SparsePackageEntry},
txtr::TextureData,
},
@@ -61,6 +64,7 @@ impl AssetIo for RetroAssetIo {
if let Some(package) =
packages.iter().find(|p| p.entries.iter().any(|e| e.id == id))
{
println!("Loading {} from {}", id, package.path.display());
package_path = Some(package.path.clone());
}
}
@@ -227,3 +231,108 @@ impl AssetLoader for TextureAssetLoader {
fn extensions(&self) -> &[&str] { &["txtr"] }
}
#[derive(Debug, Clone, bevy::reflect::TypeUuid)]
#[uuid = "83269869-1209-408e-8835-bc6f2496e829"]
pub struct ModelAsset {
pub inner: ModelData,
pub textures: HashMap<Uuid, Handle<TextureAsset>>,
}
pub struct ModelAssetLoader;
impl Plugin for ModelAssetLoader {
fn build(&self, app: &mut App) {
app.add_asset::<ModelAsset>().add_asset_loader(ModelAssetLoader);
}
}
impl AssetLoader for ModelAssetLoader {
fn load<'a>(
&'a self,
bytes: &'a [u8],
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, anyhow::Result<(), Error>> {
Box::pin(async move {
let meta = locate_meta(bytes, Endian::Little)?;
let data = ModelData::slice(bytes, meta, Endian::Little)?;
println!("Loaded model {:?}", data.head);
let mut dependencies = HashMap::<Uuid, AssetPath>::new();
for mat in &data.mtrl.materials {
for data in &mat.data {
match &data.data {
CMaterialDataInner::Texture(texture) => {
dependencies.insert(
texture.id,
AssetPath::new(PathBuf::from(format!("{}.TXTR", texture.id)), None),
);
}
CMaterialDataInner::LayeredTexture(texture) => {
for texture in &texture.textures {
if texture.id.is_nil() {
continue;
}
dependencies.insert(
texture.id,
AssetPath::new(
PathBuf::from(format!("{}.TXTR", texture.id)),
None,
),
);
}
}
_ => {}
}
}
}
let textures = dependencies
.iter()
.map(|(u, p)| (*u, load_context.get_handle(p.clone())))
.collect();
load_context.set_default_asset(
LoadedAsset::new(ModelAsset { inner: data, textures })
.with_dependencies(dependencies.into_values().collect()),
);
Ok(())
})
}
fn extensions(&self) -> &[&str] { &["cmdl"] }
}
#[derive(Debug, Clone, bevy::reflect::TypeUuid)]
#[uuid = "83269869-1209-408e-8835-bc6f2496e82a"]
pub struct MaterialAsset {
pub inner: ModelData,
}
pub struct MaterialAssetLoader;
impl Plugin for MaterialAssetLoader {
fn build(&self, app: &mut App) {
app.add_asset::<MaterialAsset>().add_asset_loader(MaterialAssetLoader);
}
}
impl AssetLoader for MaterialAssetLoader {
fn load<'a>(
&'a self,
bytes: &'a [u8],
_load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, anyhow::Result<(), Error>> {
Box::pin(async move {
let meta = locate_meta(bytes, Endian::Little)?;
// let (desc, data, remain) = FormDescriptor::slice(bytes, Endian::Little)?;
// println!("Loading material {:?}", desc);
let _mtrl = MaterialData::slice(bytes, meta, Endian::Little)?;
// fs::write("mtrl.out", &mtrl.decompressed)?;
// load_context.set_default_asset(
// LoadedAsset::new(ModelAsset { inner: data, textures })
// .with_dependencies(dependencies.into_values().collect()),
// );
Ok(())
})
}
fn extensions(&self) -> &[&str] { &["mtrl"] }
}
+101 -391
View File
File diff suppressed because it is too large Load Diff
+109
View File
@@ -0,0 +1,109 @@
pub mod model;
pub mod project;
pub mod texture;
use bevy::{ecs::system::*, prelude::*, render::camera::*};
use bevy_egui::EguiContext;
use crate::AssetRef;
pub enum TabType {
Project(project::ProjectTab),
Texture(texture::TextureTab),
Model(model::ModelTab),
Empty,
}
pub struct TabState {
pub open_assets: Vec<AssetRef>,
pub open_tab: Option<TabType>,
pub viewport: Viewport,
pub render_layer: u8,
}
pub trait SystemTab {
type LoadParam: SystemParam;
type UiParam: SystemParam;
fn load(&mut self, _ctx: &mut EguiContext, _query: SystemParamItem<'_, '_, Self::LoadParam>) {}
fn close(&mut self, _query: SystemParamItem<'_, '_, Self::LoadParam>) {} // , _ctx: &mut EguiContext
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
state: &mut TabState,
);
fn title(&mut self) -> egui::WidgetText;
}
pub fn load_tab<T: SystemTab + 'static>(world: &mut World, ctx: &mut EguiContext, tab: &mut T) {
let mut state: SystemState<T::LoadParam> = SystemState::new(world);
tab.load(ctx, state.get_mut(world));
state.apply(world);
}
fn render_tab<T: SystemTab + 'static>(
world: &mut World,
ui: &mut egui::Ui,
tab: &mut T,
tab_state: &mut TabState,
) {
let mut state: SystemState<T::UiParam> = SystemState::new(world);
tab.ui(ui, state.get_mut(world), tab_state);
state.apply(world);
}
fn close_tab<T: SystemTab + 'static>(world: &mut World, tab: &mut T) {
let mut state: SystemState<T::LoadParam> = SystemState::new(world);
tab.close(state.get_mut(world));
state.apply(world);
}
pub struct TabViewer<'a> {
pub world: &'a mut World,
pub state: TabState,
}
impl egui_dock::TabViewer for TabViewer<'_> {
type Tab = TabType;
fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
match tab {
TabType::Project(tab) => render_tab(self.world, ui, tab, &mut self.state),
TabType::Texture(tab) => render_tab(self.world, ui, tab, &mut self.state),
TabType::Model(tab) => render_tab(self.world, ui, tab, &mut self.state),
TabType::Empty => {}
}
}
fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
match tab {
TabType::Project(tab) => tab.title(),
TabType::Texture(tab) => tab.title(),
TabType::Model(tab) => tab.title(),
TabType::Empty => "".into(),
}
}
fn on_close(&mut self, tab: &mut Self::Tab) -> bool {
match tab {
TabType::Project(_) => false,
TabType::Texture(tab) => {
close_tab(self.world, tab);
true
}
TabType::Model(tab) => {
close_tab(self.world, tab);
true
}
TabType::Empty => false,
}
}
fn clear_background(&self, tab: &Self::Tab) -> bool {
!matches!(tab, TabType::Empty | TabType::Model(_))
}
}
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
use bevy::{
ecs::system::{lifetimeless::*, *},
prelude::*,
};
use egui::{text::LayoutJob, Color32, TextFormat, Widget};
use retrolib::format::{cmdl::K_FORM_CMDL, txtr::K_FORM_TXTR, FourCC};
use crate::{
icon,
loaders::{ModelAsset, PackageDirectory, TextureAsset},
tabs::{model::ModelTab, texture::TextureTab, SystemTab, TabState, TabType},
AssetRef,
};
pub const K_FORM_FMV0: FourCC = FourCC(*b"FMV0");
pub const K_FORM_ROOM: FourCC = FourCC(*b"ROOM");
pub struct ProjectTab;
impl SystemTab for ProjectTab {
type LoadParam = ();
type UiParam = (SRes<AssetServer>, SRes<Assets<PackageDirectory>>);
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
state: &mut TabState,
) {
let (server, packages) = query;
let mut packages_sorted =
packages.iter().map(|(_, p)| p).collect::<Vec<&PackageDirectory>>();
packages_sorted.sort_by_key(|p| &p.name);
for package in packages_sorted {
egui::CollapsingHeader::new(&package.name).show(ui, |ui| {
for entry in &package.entries {
let monospace =
ui.style().text_styles.get(&egui::TextStyle::Monospace).unwrap().clone();
let mut job = LayoutJob::simple(
format!(
"{} {} {}",
match entry.kind {
K_FORM_TXTR => icon::TEXTURE,
K_FORM_CMDL => icon::FILE_3D,
K_FORM_FMV0 => icon::FILE_MOVIE,
K_FORM_ROOM => icon::SCENE_DATA,
_ => icon::FILE,
},
entry.kind,
entry.id
),
monospace.clone(),
Color32::GRAY,
0.0,
);
if let Some(name) = &entry.name {
job.append(
&format!("\n{name}"),
0.0,
TextFormat::simple(monospace, Color32::WHITE),
);
}
let asset_ref = AssetRef { id: entry.id, kind: entry.kind };
if egui::SelectableLabel::new(state.open_assets.contains(&asset_ref), job)
.ui(ui)
.clicked()
{
match entry.kind {
K_FORM_TXTR => {
let handle = server.load::<TextureAsset, _>(format!(
"{}.{}",
entry.id, entry.kind
));
state.open_tab = Some(TabType::Texture(TextureTab {
asset_ref: asset_ref.clone(),
handle,
loaded_texture: None,
}));
}
K_FORM_CMDL => {
let handle = server
.load::<ModelAsset, _>(format!("{}.{}", entry.id, entry.kind));
state.open_tab = Some(TabType::Model(ModelTab {
asset_ref: asset_ref.clone(),
handle,
loaded: None,
}));
}
_ => {}
}
}
}
});
}
}
fn title(&mut self) -> egui::WidgetText { format!("{} Browser", icon::FILEBROWSER).into() }
}
+226
View File
@@ -0,0 +1,226 @@
use bevy::{
asset::LoadState,
ecs::system::{lifetimeless::*, *},
prelude::*,
render::render_resource::*,
};
use bevy_egui::EguiContext;
use retrolib::format::txtr::{ETextureFormat, ETextureType};
use crate::{loaders::TextureAsset, tabs::SystemTab, AssetRef, TabState, icon};
pub struct LoadedTexture {
pub texture_ids: Vec<egui::TextureId>,
}
pub struct TextureTab {
pub asset_ref: AssetRef,
pub handle: Handle<TextureAsset>,
pub loaded_texture: Option<LoadedTexture>,
}
impl SystemTab for TextureTab {
type LoadParam = (SRes<Assets<TextureAsset>>, SResMut<Assets<Image>>);
type UiParam = (SRes<AssetServer>, SRes<Assets<TextureAsset>>);
fn load(&mut self, ctx: &mut EguiContext, query: SystemParamItem<'_, '_, Self::LoadParam>) {
if self.loaded_texture.is_some() {
return;
}
let (textures, mut images) = query;
let Some(txtr) = textures.get(&self.handle) else { return; };
let mut texture_ids = Vec::new();
if let Some(rgba) = &txtr.rgba {
let image_handle = images.add(Image {
data: rgba.clone(),
texture_descriptor: TextureDescriptor {
label: None,
size: Extent3d {
width: txtr.inner.head.width,
height: txtr.inner.head.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: if txtr.inner.head.format.is_srgb() {
TextureFormat::Rgba8UnormSrgb
} else {
TextureFormat::Rgba8Unorm
},
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
},
sampler_descriptor: default(),
texture_view_descriptor: None,
});
texture_ids.push(ctx.add_image(image_handle));
} else {
let array_stride: usize =
(txtr.inner.head.mip_sizes.iter().sum::<u32>() / txtr.inner.head.layers) as usize;
for layer in 0..txtr.inner.head.layers as usize {
let image_handle = images.add(Image {
data: txtr.inner.data
[layer * array_stride..(layer * array_stride) + array_stride]
.to_vec(),
texture_descriptor: TextureDescriptor {
label: None,
size: Extent3d {
width: txtr.inner.head.width,
height: txtr.inner.head.height,
depth_or_array_layers: 1,
},
mip_level_count: txtr.inner.head.mip_sizes.len() as u32,
sample_count: 1,
dimension: match txtr.inner.head.kind {
ETextureType::_1D => TextureDimension::D1,
ETextureType::_2D => TextureDimension::D2,
ETextureType::_3D => TextureDimension::D3,
ETextureType::Cube => TextureDimension::D2,
ETextureType::_1DArray => TextureDimension::D1,
ETextureType::_2DArray => TextureDimension::D2,
ETextureType::_2DMultisample => TextureDimension::D2,
ETextureType::_2DMultisampleArray => TextureDimension::D2,
ETextureType::CubeArray => TextureDimension::D2,
},
format: match txtr.inner.head.format {
ETextureFormat::R8Unorm => TextureFormat::Rgba8Unorm,
ETextureFormat::R8Snorm => TextureFormat::R8Snorm,
ETextureFormat::R8Uint => TextureFormat::R8Uint,
ETextureFormat::R8Sint => TextureFormat::R8Sint,
ETextureFormat::R16Unorm => TextureFormat::R16Unorm,
ETextureFormat::R16Snorm => TextureFormat::R16Snorm,
ETextureFormat::R16Uint => TextureFormat::R16Uint,
ETextureFormat::R16Sint => TextureFormat::R16Sint,
ETextureFormat::R16Float => TextureFormat::R16Float,
ETextureFormat::R32Uint => TextureFormat::R32Uint,
ETextureFormat::R32Sint => TextureFormat::R32Sint,
ETextureFormat::Rgba8Unorm => TextureFormat::Rgba8Unorm,
ETextureFormat::Rgba8Srgb => TextureFormat::Rgba8UnormSrgb,
ETextureFormat::Rgba16Float => TextureFormat::Rgba16Float,
ETextureFormat::Rgba32Float => TextureFormat::Rgba32Float,
ETextureFormat::Depth16Unorm => TextureFormat::Depth16Unorm,
ETextureFormat::Depth16Unorm2 => TextureFormat::Depth16Unorm,
ETextureFormat::Depth24S8Unorm => TextureFormat::Depth24PlusStencil8,
ETextureFormat::Depth32Float => TextureFormat::Depth32Float,
ETextureFormat::RgbaBc1Unorm => TextureFormat::Bc1RgbaUnorm,
ETextureFormat::RgbaBc1Srgb => TextureFormat::Bc1RgbaUnormSrgb,
ETextureFormat::RgbaBc2Unorm => TextureFormat::Bc2RgbaUnorm,
ETextureFormat::RgbaBc2Srgb => TextureFormat::Bc2RgbaUnormSrgb,
ETextureFormat::RgbaBc3Unorm => TextureFormat::Bc3RgbaUnorm,
ETextureFormat::RgbaBc3Srgb => TextureFormat::Bc3RgbaUnormSrgb,
ETextureFormat::RgbaBc4Unorm => TextureFormat::Bc4RUnorm,
ETextureFormat::RgbaBc4Snorm => TextureFormat::Bc4RSnorm,
ETextureFormat::RgbaBc5Unorm => TextureFormat::Bc5RgUnorm,
ETextureFormat::RgbaBc5Snorm => TextureFormat::Bc5RgSnorm,
ETextureFormat::Rg11B10Float => TextureFormat::Rg11b10Float,
ETextureFormat::R32Float => TextureFormat::R32Float,
ETextureFormat::Rg8Unorm => TextureFormat::Rg8Unorm,
ETextureFormat::Rg8Snorm => TextureFormat::Rg8Snorm,
ETextureFormat::Rg8Uint => TextureFormat::Rg8Uint,
ETextureFormat::Rg8Sint => TextureFormat::Rg8Sint,
ETextureFormat::Rg16Float => TextureFormat::Rg16Float,
ETextureFormat::Rg16Unorm => TextureFormat::Rg16Unorm,
ETextureFormat::Rg16Snorm => TextureFormat::Rg16Snorm,
ETextureFormat::Rg16Uint => TextureFormat::Rg16Uint,
ETextureFormat::Rg16Sint => TextureFormat::Rg16Sint,
ETextureFormat::Rgb10A2Unorm => TextureFormat::Rgb10a2Unorm,
ETextureFormat::Rg32Uint => TextureFormat::Rg32Uint,
ETextureFormat::Rg32Sint => TextureFormat::Rg32Sint,
ETextureFormat::Rg32Float => TextureFormat::Rg32Float,
ETextureFormat::Rgba16Unorm => TextureFormat::Rgba16Unorm,
ETextureFormat::Rgba16Snorm => TextureFormat::Rgba16Snorm,
ETextureFormat::Rgba16Uint => TextureFormat::Rgba16Uint,
ETextureFormat::Rgba16Sint => TextureFormat::Rgba16Sint,
ETextureFormat::Rgba32Uint => TextureFormat::Rgba32Uint,
ETextureFormat::Rgba32Sint => TextureFormat::Rgba32Sint,
ETextureFormat::BptcUfloat => TextureFormat::Bc6hRgbUfloat,
ETextureFormat::BptcSfloat => TextureFormat::Bc6hRgbSfloat,
ETextureFormat::BptcUnorm => TextureFormat::Bc7RgbaUnorm,
ETextureFormat::BptcUnormSrgb => TextureFormat::Bc7RgbaUnormSrgb,
_ => todo!(),
},
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
},
sampler_descriptor: default(),
texture_view_descriptor: None,
});
texture_ids.push(ctx.add_image(image_handle));
}
};
self.loaded_texture = Some(LoadedTexture { texture_ids });
}
fn ui(
&mut self,
ui: &mut egui::Ui,
query: SystemParamItem<'_, '_, Self::UiParam>,
_state: &mut TabState,
) {
let (server, textures) = query;
ui.label(format!("{} {}", self.asset_ref.kind, self.asset_ref.id));
match server.get_load_state(&self.handle) {
LoadState::NotLoaded => {
return;
}
LoadState::Loading => {
ui.spinner();
return;
}
LoadState::Loaded => {}
LoadState::Failed => {
ui.colored_label(egui::Color32::RED, "Loading failed");
return;
}
LoadState::Unloaded => {
return;
}
};
let loaded = self.loaded_texture.as_mut().unwrap();
if let Some(txtr) = textures.get(&self.handle) {
ui.label(format!("Type: {:?}", txtr.inner.head.kind));
ui.label(format!("Format: {:?}", txtr.inner.head.format));
ui.label(format!(
"Dimensions: {}x{}x{} (mips: {})",
txtr.inner.head.width,
txtr.inner.head.height,
txtr.inner.head.layers,
txtr.inner.head.mip_sizes.len()
));
if txtr.inner.head.kind == ETextureType::Cube && loaded.texture_ids.len() == 6 {
let width = txtr.inner.head.width;
let height = txtr.inner.head.height;
let (_, rect) =
ui.allocate_space(egui::Vec2 { x: (width * 4) as f32, y: (height * 3) as f32 });
let size = egui::Vec2 { x: width as f32, y: height as f32 };
let mut draw_image = |i: usize, x: u32, y: u32| {
let min = egui::Vec2 { x: (width * x) as f32, y: (height * y) as f32 };
let max =
egui::Vec2 { x: (width * (x + 1)) as f32, y: (height * (y + 1)) as f32 };
egui::widgets::Image::new(loaded.texture_ids[i], size)
.paint_at(ui, egui::Rect { min: rect.min + min, max: rect.min + max });
};
draw_image(2, 1, 0);
draw_image(1, 0, 1);
draw_image(4, 1, 1);
draw_image(0, 2, 1);
draw_image(5, 3, 1);
draw_image(3, 1, 2);
} else {
for image in &loaded.texture_ids {
ui.add(egui::widgets::Image::new(*image, egui::Vec2 {
x: txtr.inner.head.width as f32,
y: txtr.inner.head.height as f32,
}));
}
}
}
}
fn title(&mut self) -> egui::WidgetText {
format!("{} {} {}", icon::TEXTURE, self.asset_ref.kind, self.asset_ref.id).into()
}
}
+1 -3
View File
@@ -11,19 +11,17 @@ readme = "README.md"
categories = ["command-line-utilities"]
[dependencies]
retrolib = { path = "../lib" }
anyhow = "1.0.69"
argh = "0.1.10"
# astc-decode = "0.3.1"
binrw = "0.11.1"
binrw_derive = "0.11.1"
ddsfile = { git = "https://github.com/encounter/ddsfile", rev = "880f04c1dffa680eab0e9e09cfa58591fe186a31" }
env_logger = "0.10.0"
gltf-json = { version = "1.1.0", features = ["names", "extras"] }
half = "2.2.1"
# image = "0.24.5"
log = "0.4.17"
memmap2 = "0.5.8"
retrolib = { path = "../lib" }
serde_json = "1.0.93"
tegra_swizzle = "0.3.0"
uuid = "1.3.0"
+2 -3
View File
@@ -1,5 +1,4 @@
use std::{
borrow::Cow,
collections::HashMap,
fs,
fs::DirBuilder,
@@ -175,7 +174,7 @@ fn convert(args: ConvertArgs) -> Result<()> {
continue;
}
let mut reader = Cursor::new(buf.as_ref());
let mut reader = Cursor::new(&*buf);
let mut new_buf: Vec<u8> =
Vec::with_capacity(info.vertex_count as usize * info.out_stride as usize);
let mut tmp_buf = vec![0u8; 16]; // max size of attribute
@@ -209,7 +208,7 @@ fn convert(args: ConvertArgs) -> Result<()> {
}
}
}
*buf = Cow::Owned(new_buf);
*buf = new_buf;
}
DirBuilder::new().recursive(true).create(&args.out_dir)?;