mirror of
https://github.com/PrimeDecomp/retrotool.git
synced 2026-07-12 18:18:58 -07:00
WIP CMDL viewer & improvements
This commit is contained in:
Generated
+4
@@ -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",
|
||||
|
||||
@@ -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
@@ -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,6 +1,7 @@
|
||||
pub mod chunk;
|
||||
pub mod cmdl;
|
||||
pub mod foot;
|
||||
pub mod mtrl;
|
||||
pub mod pack;
|
||||
pub mod rfrm;
|
||||
pub mod txtr;
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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() }
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
Reference in New Issue
Block a user