Rework model loader; start supporting alpha blend materials

This commit is contained in:
Luke Street
2023-03-19 12:24:00 -04:00
parent 4c7843e26e
commit 4e89ad99ac
12 changed files with 536 additions and 433 deletions
Generated
+13 -3
View File
@@ -1575,7 +1575,7 @@ checksum = "e8af5ef47e2ed89d23d0ecbc1b681b30390069de70260937877514377fc24feb"
dependencies = [
"bit_field",
"flume",
"half",
"half 2.2.1",
"lebe",
"miniz_oxide",
"smallvec",
@@ -1849,6 +1849,16 @@ dependencies = [
"crunchy",
]
[[package]]
name = "half"
version = "3.0.0-dev"
source = "git+https://github.com/encounter/half-rs?branch=inline#807f00f6b83419b20942ed240f46ceb36d7de162"
dependencies = [
"bytemuck",
"cfg-if",
"crunchy",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -2844,7 +2854,7 @@ dependencies = [
"ddsfile",
"env_logger",
"gltf-json",
"half",
"half 3.0.0-dev",
"image",
"log",
"memmap2",
@@ -2870,7 +2880,7 @@ dependencies = [
"bytemuck",
"egui",
"egui_dock",
"half",
"half 3.0.0-dev",
"image",
"log",
"num-traits",
+3 -2
View File
@@ -13,6 +13,7 @@ readme = "README.md"
default = []
dynamic = ["bevy/dynamic_linking"]
embed = ["bevy_embedded_assets"]
nightly = ["half/use-intrinsics"]
[dependencies]
anyhow = "1.0.69"
@@ -22,10 +23,10 @@ bevy_embedded_assets = { version = "0.7.0", optional = true }
bevy_mod_raycast = { git = "https://github.com/encounter/bevy_mod_raycast", branch = "updates" }
binrw = "0.11.1"
bit-set = "0.5.3"
bytemuck = "1.13.0"
bytemuck = { version = "1.13.0", features = ["min_const_generics"] }
egui = "0.21.0"
egui_dock = "0.4.0"
half = "2.2.1"
half = { git = "https://github.com/encounter/half-rs", branch = "inline", features = ["bytemuck"] }
image = "0.24.5"
log = "0.4.17"
num-traits = "0.2.15"
+1 -1
View File
@@ -434,7 +434,7 @@ fn pbr(
let occlusion = in.occlusion;
// output_color = alpha_discard(in.material, output_color);
output_color.a = 1.0;
// output_color.a = 1.0;
// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
let NdotV = max(dot(in.N, in.V), 0.0001);
+338 -12
View File
@@ -1,19 +1,38 @@
use std::path::PathBuf;
use std::{num::NonZeroU8, path::PathBuf};
use anyhow::Error;
use anyhow::{Error, Result};
use bevy::{
asset::{AssetLoader, AssetPath, BoxedFuture, LoadContext, LoadState, LoadedAsset},
prelude::*,
utils::HashMap,
render::{render_resource::SamplerDescriptor, texture::ImageSampler},
utils::{hashbrown::hash_map::Entry, HashMap},
};
use binrw::Endian;
use retrolib::format::{
cmdl::{CMaterialDataInner, ModelData, K_FORM_CMDL},
cmdl::{
CMaterialCache, CMaterialDataInner, EMaterialDataId, ModelData, STextureUsageInfo,
K_FORM_CMDL,
},
foot::{locate_asset_id, locate_meta},
txtr::{
ETextureAnisotropicRatio, ETextureFilter, ETextureMipFilter, ETextureWrap,
STextureSamplerData,
},
};
use uuid::Uuid;
use wgpu_types::{AddressMode, Face, FilterMode};
use crate::{loaders::texture::TextureAsset, AssetRef};
use crate::{
loaders::texture::TextureAsset, material::CustomMaterial, render::model::MESH_FLAG_OPAQUE,
AssetRef,
};
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct MaterialKey {
pub material_idx: usize,
pub mesh_flags: u16,
pub mesh_mirrored: bool,
}
#[derive(Debug, Clone, bevy::reflect::TypeUuid)]
#[uuid = "83269869-1209-408e-8835-bc6f2496e829"]
@@ -21,12 +40,8 @@ pub struct ModelAsset {
pub asset_ref: AssetRef,
pub inner: ModelData,
pub textures: HashMap<Uuid, Handle<TextureAsset>>,
}
impl ModelAsset {
pub fn get_load_state(&self, server: &AssetServer) -> LoadState {
server.get_group_load_state(self.textures.values().map(|h| h.id()))
}
pub texture_images: HashMap<Uuid, Handle<Image>>,
pub materials: HashMap<MaterialKey, Handle<CustomMaterial>>,
}
pub struct ModelAssetLoader;
@@ -40,7 +55,7 @@ impl AssetLoader for ModelAssetLoader {
&'a self,
bytes: &'a [u8],
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, anyhow::Result<(), Error>> {
) -> BoxedFuture<'a, Result<(), Error>> {
Box::pin(async move {
let id = locate_asset_id(bytes, Endian::Little)?;
let meta = locate_meta(bytes, Endian::Little)?;
@@ -84,6 +99,8 @@ impl AssetLoader for ModelAssetLoader {
asset_ref: AssetRef { id, kind: K_FORM_CMDL },
inner: data,
textures,
texture_images: default(),
materials: default(),
})
.with_dependencies(dependencies.into_values().collect()),
);
@@ -93,3 +110,312 @@ impl AssetLoader for ModelAssetLoader {
fn extensions(&self) -> &[&str] { &["cmdl", "smdl", "wmdl"] }
}
impl ModelAsset {
pub fn get_load_state(&self, server: &AssetServer) -> LoadState {
server.get_group_load_state(self.textures.values().map(|h| h.id()))
}
pub fn sampler_data<'asset>(
&self,
texture_id: &Uuid,
texture_assets: &'asset Assets<TextureAsset>,
) -> Option<&'asset STextureSamplerData> {
self.textures
.get(texture_id)
.and_then(|handle| texture_assets.get(handle))
.map(|txtr| &txtr.inner.head.sampler_data)
}
pub fn build_texture_images(
&mut self,
texture_assets: &Assets<TextureAsset>,
images: &mut Assets<Image>,
) {
// Build sampler descriptors
let mut sampler_descriptors = HashMap::<Uuid, SamplerDescriptor>::new();
for mat in &self.inner.mtrl.materials {
for data in &mat.data {
match &data.data {
CMaterialDataInner::Texture(texture) => {
if let Some(usage) = &texture.usage {
let sampler_data = self.sampler_data(&texture.id, texture_assets);
sampler_descriptors.insert(
texture.id,
sampler_descriptor_from_usage(usage, sampler_data),
);
}
}
CMaterialDataInner::LayeredTexture(layers) => {
for texture in &layers.textures {
if let Some(usage) = &texture.usage {
let sampler_data = self.sampler_data(&texture.id, texture_assets);
sampler_descriptors.insert(
texture.id,
sampler_descriptor_from_usage(usage, sampler_data),
);
}
}
}
_ => continue,
}
}
}
// Build texture images
for (id, handle) in &self.textures {
let asset = texture_assets.get(handle).unwrap();
let mut image = asset.texture.clone();
if let Some(desc) = sampler_descriptors.get(id) {
image.sampler_descriptor = ImageSampler::Descriptor(desc.clone());
}
self.texture_images.insert(*id, images.add(image));
}
}
pub fn material(
&mut self,
key: &MaterialKey,
assets: &mut Assets<CustomMaterial>,
) -> Result<Handle<CustomMaterial>> {
Ok(match self.materials.entry(*key) {
Entry::Occupied(e) => e.get().clone(),
Entry::Vacant(e) => {
let material =
build_material(key, &self.inner.mtrl.materials, &self.texture_images)?;
let handle = assets.add(material);
e.insert(handle.clone());
handle
}
})
}
}
fn build_material(
key: &MaterialKey,
materials: &[CMaterialCache],
texture_images: &HashMap<Uuid, Handle<Image>>,
) -> Result<CustomMaterial> {
let mut out_mat = CustomMaterial {
alpha_mode: if key.mesh_flags & MESH_FLAG_OPAQUE != 0 {
AlphaMode::Opaque
} else {
AlphaMode::Blend
},
cull_mode: Some(if key.mesh_mirrored { Face::Front } else { Face::Back }),
..default()
};
for data in &materials[key.material_idx].data {
match data.data_id {
EMaterialDataId::DIFT | EMaterialDataId::BCLR => match &data.data {
CMaterialDataInner::Texture(texture) => {
out_mat.base_color_l0 = Color::WHITE;
out_mat.base_color_texture_0 = texture_images.get(&texture.id).cloned();
out_mat.base_color_uv_0 =
texture.usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
}
_ => {
log::warn!("Unsupported material data type for DIFT {:?}", data.data_type);
}
},
EMaterialDataId::BCRL => match &data.data {
CMaterialDataInner::LayeredTexture(layers) => {
out_mat.base_color_l0 = layers.base.colors[0].to_array().into();
out_mat.base_color_l1 = layers.base.colors[1].to_array().into();
out_mat.base_color_l2 = layers.base.colors[2].to_array().into();
out_mat.base_color_texture_0 =
texture_images.get(&layers.textures[0].id).cloned();
out_mat.base_color_texture_1 =
texture_images.get(&layers.textures[1].id).cloned();
out_mat.base_color_texture_2 =
texture_images.get(&layers.textures[2].id).cloned();
out_mat.base_color_uv_0 =
layers.textures[0].usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
out_mat.base_color_uv_1 =
layers.textures[1].usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
out_mat.base_color_uv_2 =
layers.textures[2].usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
}
_ => {
log::warn!("Unsupported material data type for BCRL {:?}", data.data_type);
}
},
EMaterialDataId::DIFC => match &data.data {
CMaterialDataInner::Color(color) => {
out_mat.base_color = Color::rgba(color.r, color.g, color.b, color.a);
}
_ => log::warn!("Unsupported material data type for DIFC {:?}", data.data_type),
},
EMaterialDataId::ICAN => match &data.data {
CMaterialDataInner::Texture(texture) => {
out_mat.emissive_texture = texture_images.get(&texture.id).cloned();
out_mat.emissive_uv =
texture.usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
}
_ => log::warn!("Unsupported material data type for ICAN {:?}", data.data_type),
},
EMaterialDataId::ICNC => match &data.data {
CMaterialDataInner::Color(color) => {
out_mat.emissive_color = Color::rgba(color.r, color.g, color.b, color.a);
}
_ => log::warn!("Unsupported material data type for ICNC {:?}", data.data_type),
},
EMaterialDataId::NMAP => match &data.data {
CMaterialDataInner::Texture(texture) => {
out_mat.normal_map_l0 = Color::WHITE;
out_mat.normal_map_texture_0 = texture_images.get(&texture.id).cloned();
out_mat.normal_map_uv_0 = texture.usage.as_ref().unwrap().tex_coord;
}
_ => {
log::warn!("Unsupported material data type for NMAP {:?}", data.data_type);
}
},
EMaterialDataId::NRML => match &data.data {
CMaterialDataInner::LayeredTexture(layers) => {
out_mat.normal_map_l0 = layers.base.colors[0].to_array().into();
out_mat.normal_map_l1 = layers.base.colors[1].to_array().into();
out_mat.normal_map_l2 = layers.base.colors[2].to_array().into();
out_mat.normal_map_texture_0 =
texture_images.get(&layers.textures[0].id).cloned();
out_mat.normal_map_texture_1 =
texture_images.get(&layers.textures[1].id).cloned();
out_mat.normal_map_texture_2 =
texture_images.get(&layers.textures[2].id).cloned();
out_mat.normal_map_uv_0 =
layers.textures[0].usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
out_mat.normal_map_uv_1 =
layers.textures[1].usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
out_mat.normal_map_uv_2 =
layers.textures[2].usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
}
_ => {
log::warn!("Unsupported material data type for NRML {:?}", data.data_type);
}
},
EMaterialDataId::METL => match &data.data {
CMaterialDataInner::Texture(texture) => {
out_mat.metallic_map_l0 = Color::WHITE;
out_mat.metallic_map_texture_0 = texture_images.get(&texture.id).cloned();
out_mat.metallic_map_uv_0 = texture.usage.as_ref().unwrap().tex_coord;
}
_ => {
log::warn!("Unsupported material data type for METL {:?}", data.data_type);
}
},
EMaterialDataId::MTLL => match &data.data {
CMaterialDataInner::LayeredTexture(layers) => {
out_mat.metallic_map_l0 = layers.base.colors[0].to_array().into();
out_mat.metallic_map_l1 = layers.base.colors[1].to_array().into();
out_mat.metallic_map_l2 = layers.base.colors[2].to_array().into();
out_mat.metallic_map_texture_0 =
texture_images.get(&layers.textures[0].id).cloned();
out_mat.metallic_map_texture_1 =
texture_images.get(&layers.textures[1].id).cloned();
out_mat.metallic_map_texture_2 =
texture_images.get(&layers.textures[2].id).cloned();
out_mat.metallic_map_uv_0 =
layers.textures[0].usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
out_mat.metallic_map_uv_1 =
layers.textures[1].usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
out_mat.metallic_map_uv_2 =
layers.textures[2].usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
}
_ => {
log::warn!("Unsupported material data type for MTLL {:?}", data.data_type);
}
},
id => {
log::warn!("Unsupported material data ID {id:?}");
}
}
}
Ok(out_mat)
}
fn texture_wrap(wrap: ETextureWrap) -> AddressMode {
match wrap {
ETextureWrap::ClampToEdge => AddressMode::ClampToEdge,
ETextureWrap::Repeat => AddressMode::Repeat,
ETextureWrap::MirroredRepeat => AddressMode::MirrorRepeat,
ETextureWrap::MirrorClamp => todo!("Mirror clamp"),
ETextureWrap::ClampToBorder => AddressMode::ClampToBorder,
ETextureWrap::Clamp => todo!("Clamp"),
}
}
fn sampler_descriptor_from_usage<'desc>(
usage: &STextureUsageInfo,
data: Option<&STextureSamplerData>,
) -> SamplerDescriptor<'desc> {
SamplerDescriptor {
label: None,
address_mode_u: match usage.wrap_x {
0 => AddressMode::ClampToEdge,
1 => AddressMode::Repeat,
2 => AddressMode::MirrorRepeat,
3 => todo!("Mirror clamp"),
4 => AddressMode::ClampToBorder,
5 => todo!("Clamp"),
u32::MAX => data.map_or(AddressMode::Repeat, |d| texture_wrap(d.wrap_x)),
n => todo!("wrap {n}"),
},
address_mode_v: match usage.wrap_y {
0 => AddressMode::ClampToEdge,
1 => AddressMode::Repeat,
2 => AddressMode::MirrorRepeat,
3 => todo!("Mirror clamp"),
4 => AddressMode::ClampToBorder,
5 => todo!("Clamp"),
u32::MAX => data.map_or(AddressMode::Repeat, |d| texture_wrap(d.wrap_y)),
n => todo!("wrap {n}"),
},
address_mode_w: match usage.wrap_z {
0 => AddressMode::ClampToEdge,
1 => AddressMode::Repeat,
2 => AddressMode::MirrorRepeat,
3 => todo!("Mirror clamp"),
4 => AddressMode::ClampToBorder,
5 => todo!("Clamp"),
u32::MAX => data.map_or(AddressMode::Repeat, |d| texture_wrap(d.wrap_z)),
n => todo!("wrap {n}"),
},
mag_filter: match usage.filter {
0 => FilterMode::Nearest,
1 => FilterMode::Linear,
u32::MAX => data.map_or(FilterMode::Nearest, |d| match d.filter {
ETextureFilter::Nearest => FilterMode::Nearest,
ETextureFilter::Linear => FilterMode::Linear,
}),
n => todo!("Filter {n}"),
},
min_filter: match usage.filter {
0 => FilterMode::Nearest,
1 => FilterMode::Linear,
u32::MAX => data.map_or(FilterMode::Nearest, |d| match d.filter {
ETextureFilter::Nearest => FilterMode::Nearest,
ETextureFilter::Linear => FilterMode::Linear,
}),
n => todo!("Filter {n}"),
},
mipmap_filter: data.map_or(FilterMode::Nearest, |d| match d.mip_filter {
ETextureMipFilter::Nearest => FilterMode::Nearest,
ETextureMipFilter::Linear => FilterMode::Linear,
}),
lod_min_clamp: 0.0,
lod_max_clamp: f32::MAX,
compare: None,
anisotropy_clamp: NonZeroU8::new(
data.map(|d| match d.aniso {
ETextureAnisotropicRatio::None => 0,
ETextureAnisotropicRatio::Ratio1 => 1,
ETextureAnisotropicRatio::Ratio2 => 2,
ETextureAnisotropicRatio::Ratio4 => 4,
ETextureAnisotropicRatio::Ratio8 => 8,
ETextureAnisotropicRatio::Ratio16 => 16,
})
.unwrap_or_default(),
),
// anisotropy_clamp: NonZeroU8::new(8),
border_color: None,
}
}
+4 -4
View File
@@ -208,18 +208,18 @@ fn ui_system(world: &mut World) {
for tab in tabs {
match tab {
TabType::Project(tab) => {
load_tab(world, &mut ctx, tab);
load_tab(world, &mut ctx, tab.as_mut());
}
TabType::Texture(tab) => {
load_tab(world, &mut ctx, tab);
load_tab(world, &mut ctx, tab.as_mut());
tab_assets.push(tab.asset_ref);
}
TabType::Model(tab) => {
load_tab(world, &mut ctx, tab);
load_tab(world, &mut ctx, tab.as_mut());
tab_assets.push(tab.asset_ref);
}
TabType::ModCon(tab) => {
load_tab(world, &mut ctx, tab);
load_tab(world, &mut ctx, tab.as_mut());
tab_assets.push(tab.asset_ref);
}
TabType::Empty => {}
+12 -8
View File
@@ -7,15 +7,15 @@ use bevy::{
// A "high" random id should be used for custom attributes to ensure consistent sorting and avoid collisions with other attributes.
// See the MeshVertexAttribute docs for more info.
const ATTRIBUTE_UV_1: MeshVertexAttribute =
pub const ATTRIBUTE_UV_1: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Uv_1", 988540917, VertexFormat::Float32x2);
const ATTRIBUTE_UV_2: MeshVertexAttribute =
pub const ATTRIBUTE_UV_2: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Uv_2", 988540918, VertexFormat::Float32x2);
const ATTRIBUTE_UV_3: MeshVertexAttribute =
pub const ATTRIBUTE_UV_3: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Uv_3", 988540919, VertexFormat::Float32x2);
const ATTRIBUTE_TANGENT_1: MeshVertexAttribute =
pub const ATTRIBUTE_TANGENT_1: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Tangent_1", 988540920, VertexFormat::Float32x4);
const ATTRIBUTE_TANGENT_2: MeshVertexAttribute =
pub const ATTRIBUTE_TANGENT_2: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Tangent_2", 988540921, VertexFormat::Float32x4);
// This is the struct that will be passed to your shader
@@ -26,6 +26,8 @@ const ATTRIBUTE_TANGENT_2: MeshVertexAttribute =
pub struct CustomMaterial {
#[reflect(ignore)]
pub cull_mode: Option<Face>,
#[reflect(ignore)]
pub alpha_mode: AlphaMode,
#[uniform(0)]
pub base_color: Color,
#[texture(1)]
@@ -49,8 +51,6 @@ pub struct CustomMaterial {
pub base_color_l1: Color,
#[uniform(0)]
pub base_color_l2: Color,
// pub ican_color: Color,
// pub ican_unmasked_color: Color,
#[texture(7)]
#[sampler(8)]
pub normal_map_texture_0: Option<Handle<Image>>,
@@ -106,6 +106,7 @@ impl Default for CustomMaterial {
fn default() -> Self {
Self {
cull_mode: Some(Face::Back),
alpha_mode: AlphaMode::Opaque,
base_color: Color::WHITE,
base_color_texture_0: None,
base_color_texture_1: None,
@@ -136,7 +137,7 @@ impl Default for CustomMaterial {
metallic_map_l2: Color::NONE,
emissive_texture: None,
emissive_uv: 0,
emissive_color: Color::BLACK,
emissive_color: Color::NONE,
}
}
}
@@ -155,6 +156,9 @@ impl Material for CustomMaterial {
fn fragment_shader() -> ShaderRef { "custom_material.wgsl".into() }
#[inline]
fn alpha_mode(&self) -> AlphaMode { self.alpha_mode }
fn specialize(
_pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor,
+69 -337
View File
@@ -11,32 +11,40 @@ use bevy::{
},
};
use bit_set::BitSet;
use half::f16;
use retrolib::format::{
cmdl::{
CMaterialDataInner, EBufferType, EMaterialDataId, EVertexComponent, EVertexDataFormat,
ModelData, STextureUsageInfo, SVertexDataComponent,
use half::prelude::*;
use retrolib::{
array_ref,
format::{
cmdl::{
CMaterialCache, CMaterialDataInner, EBufferType, EMaterialDataId, EVertexComponent,
EVertexDataFormat, ModelData, STextureUsageInfo, SVertexDataComponent,
},
txtr::{
ETextureAnisotropicRatio, ETextureFilter, ETextureMipFilter, ETextureWrap,
STextureSamplerData,
},
CAABox, CTransform4f,
},
txtr::{
ETextureAnisotropicRatio, ETextureFilter, ETextureMipFilter, ETextureWrap,
STextureSamplerData,
},
CAABox, CTransform4f,
};
use uuid::Uuid;
use wgpu_types::{AddressMode, Face, FilterMode, PrimitiveTopology};
use crate::{
loaders::{model::ModelAsset, texture::TextureAsset},
material::CustomMaterial,
material::{
CustomMaterial, ATTRIBUTE_TANGENT_1, ATTRIBUTE_TANGENT_2, ATTRIBUTE_UV_1, ATTRIBUTE_UV_2,
ATTRIBUTE_UV_3,
},
};
pub const MESH_FLAG_OPAQUE: u16 = 1;
pub struct BuiltMesh {
pub mesh: Handle<Mesh>,
pub material: Handle<CustomMaterial>,
pub mirrored_material: Handle<CustomMaterial>,
pub material_name: String,
pub material_idx: usize,
pub visible: bool,
pub flags: u16,
pub unk_e: u16,
}
pub struct ModelLod {
@@ -47,70 +55,16 @@ pub struct ModelLod {
pub struct BuiltModel {
pub meshes: Vec<BuiltMesh>,
pub lod: Vec<ModelLod>,
pub materials: Vec<CMaterialCache>,
pub aabb: Aabb,
}
pub fn load_model(
asset: &ModelAsset,
_commands: &mut Commands,
// server: &AssetServer,
texture_assets: &Assets<TextureAsset>,
images: &mut Assets<Image>,
materials: &mut Assets<CustomMaterial>,
meshes: &mut Assets<Mesh>,
// center: bool,
) -> Result<BuiltModel> {
pub fn load_model(asset: &ModelAsset, meshes: &mut Assets<Mesh>) -> Result<BuiltModel> {
let ModelAsset {
inner: ModelData { head, mtrl, mesh, vbuf, ibuf, vtx_buffers, idx_buffers },
textures,
..
} = asset;
// Build sampler descriptors
let mut sampler_descriptors = HashMap::<Uuid, SamplerDescriptor>::new();
for mat in &mtrl.materials {
for data in &mat.data {
match &data.data {
CMaterialDataInner::Texture(texture) => {
if let Some(usage) = &texture.usage {
let sampler_data = textures
.get(&texture.id)
.and_then(|handle| texture_assets.get(handle))
.map(|txtr| &txtr.inner.head.sampler_data);
sampler_descriptors
.insert(texture.id, sampler_descriptor_from_usage(usage, sampler_data));
}
}
CMaterialDataInner::LayeredTexture(layers) => {
for texture in &layers.textures {
if let Some(usage) = &texture.usage {
let sampler_data = textures
.get(&texture.id)
.and_then(|handle| texture_assets.get(handle))
.map(|txtr| &txtr.inner.head.sampler_data);
sampler_descriptors.insert(
texture.id,
sampler_descriptor_from_usage(usage, sampler_data),
);
}
}
}
_ => continue,
}
}
}
// Build texture images
let mut texture_handles = HashMap::<Uuid, Handle<Image>>::new();
for (id, handle) in textures {
let asset = texture_assets.get(handle).unwrap();
let mut image = asset.texture.clone();
if let Some(desc) = sampler_descriptors.get(id) {
image.sampler_descriptor = ImageSampler::Descriptor(desc.clone());
}
texture_handles.insert(*id, images.add(image));
}
// Build vertex buffers
let mut buf_infos: Vec<VertexBufferInfo> = Vec::with_capacity(vtx_buffers.len());
let mut cur_buf = 0usize;
@@ -141,168 +95,6 @@ pub fn load_model(
index_buffers.push(out);
}
// Build materials
let mut material_handles = Vec::with_capacity(mtrl.materials.len());
let mut material_handles_mirrored = Vec::with_capacity(mtrl.materials.len());
for mat in &mtrl.materials {
let mut out_mat = CustomMaterial::default();
// log::info!("Shader {}, unk {}", mat.shader_id, mat.unk_guid);
for data in &mat.data {
match data.data_id {
EMaterialDataId::DIFT | EMaterialDataId::BCLR => match &data.data {
CMaterialDataInner::Texture(texture) => {
out_mat.base_color_l0 = Color::WHITE;
out_mat.base_color_texture_0 = texture_handles.get(&texture.id).cloned();
out_mat.base_color_uv_0 =
texture.usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
}
_ => {
log::warn!("Unsupported material data type for DIFT {:?}", data.data_type);
}
},
EMaterialDataId::BCRL => match &data.data {
CMaterialDataInner::LayeredTexture(layers) => {
out_mat.base_color_l0 = layers.base.colors[0].to_array().into();
out_mat.base_color_l1 = layers.base.colors[1].to_array().into();
out_mat.base_color_l2 = layers.base.colors[2].to_array().into();
out_mat.base_color_texture_0 =
texture_handles.get(&layers.textures[0].id).cloned();
out_mat.base_color_texture_1 =
texture_handles.get(&layers.textures[1].id).cloned();
out_mat.base_color_texture_2 =
texture_handles.get(&layers.textures[2].id).cloned();
out_mat.base_color_uv_0 = layers.textures[0]
.usage
.as_ref()
.map(|u| u.tex_coord)
.unwrap_or_default();
out_mat.base_color_uv_1 = layers.textures[1]
.usage
.as_ref()
.map(|u| u.tex_coord)
.unwrap_or_default();
out_mat.base_color_uv_2 = layers.textures[2]
.usage
.as_ref()
.map(|u| u.tex_coord)
.unwrap_or_default();
}
_ => {
log::warn!("Unsupported material data type for BCRL {:?}", data.data_type);
}
},
EMaterialDataId::DIFC => match &data.data {
CMaterialDataInner::Color(color) => {
out_mat.base_color = Color::rgba(color.r, color.g, color.b, color.a);
}
_ => log::warn!("Unsupported material data type for DIFC {:?}", data.data_type),
},
EMaterialDataId::ICAN => match &data.data {
CMaterialDataInner::Texture(texture) => {
out_mat.emissive_texture = texture_handles.get(&texture.id).cloned();
out_mat.emissive_uv =
texture.usage.as_ref().map(|u| u.tex_coord).unwrap_or_default();
}
_ => log::warn!("Unsupported material data type for ICAN {:?}", data.data_type),
},
EMaterialDataId::ICNC => match &data.data {
CMaterialDataInner::Color(color) => {
out_mat.emissive_color = Color::rgba(color.r, color.g, color.b, color.a);
}
_ => log::warn!("Unsupported material data type for ICNC {:?}", data.data_type),
},
EMaterialDataId::NMAP => match &data.data {
CMaterialDataInner::Texture(texture) => {
out_mat.normal_map_l0 = Color::WHITE;
out_mat.normal_map_texture_0 = texture_handles.get(&texture.id).cloned();
out_mat.normal_map_uv_0 = texture.usage.as_ref().unwrap().tex_coord;
}
_ => {
log::warn!("Unsupported material data type for NMAP {:?}", data.data_type);
}
},
EMaterialDataId::NRML => match &data.data {
CMaterialDataInner::LayeredTexture(layers) => {
out_mat.normal_map_l0 = layers.base.colors[0].to_array().into();
out_mat.normal_map_l1 = layers.base.colors[1].to_array().into();
out_mat.normal_map_l2 = layers.base.colors[2].to_array().into();
out_mat.normal_map_texture_0 =
texture_handles.get(&layers.textures[0].id).cloned();
out_mat.normal_map_texture_1 =
texture_handles.get(&layers.textures[1].id).cloned();
out_mat.normal_map_texture_2 =
texture_handles.get(&layers.textures[2].id).cloned();
out_mat.normal_map_uv_0 = layers.textures[0]
.usage
.as_ref()
.map(|u| u.tex_coord)
.unwrap_or_default();
out_mat.normal_map_uv_1 = layers.textures[1]
.usage
.as_ref()
.map(|u| u.tex_coord)
.unwrap_or_default();
out_mat.normal_map_uv_2 = layers.textures[2]
.usage
.as_ref()
.map(|u| u.tex_coord)
.unwrap_or_default();
}
_ => {
log::warn!("Unsupported material data type for NRML {:?}", data.data_type);
}
},
EMaterialDataId::METL => match &data.data {
CMaterialDataInner::Texture(texture) => {
out_mat.metallic_map_l0 = Color::WHITE;
out_mat.metallic_map_texture_0 = texture_handles.get(&texture.id).cloned();
out_mat.metallic_map_uv_0 = texture.usage.as_ref().unwrap().tex_coord;
}
_ => {
log::warn!("Unsupported material data type for METL {:?}", data.data_type);
}
},
EMaterialDataId::MTLL => match &data.data {
CMaterialDataInner::LayeredTexture(layers) => {
out_mat.metallic_map_l0 = layers.base.colors[0].to_array().into();
out_mat.metallic_map_l1 = layers.base.colors[1].to_array().into();
out_mat.metallic_map_l2 = layers.base.colors[2].to_array().into();
out_mat.metallic_map_texture_0 =
texture_handles.get(&layers.textures[0].id).cloned();
out_mat.metallic_map_texture_1 =
texture_handles.get(&layers.textures[1].id).cloned();
out_mat.metallic_map_texture_2 =
texture_handles.get(&layers.textures[2].id).cloned();
out_mat.metallic_map_uv_0 = layers.textures[0]
.usage
.as_ref()
.map(|u| u.tex_coord)
.unwrap_or_default();
out_mat.metallic_map_uv_1 = layers.textures[1]
.usage
.as_ref()
.map(|u| u.tex_coord)
.unwrap_or_default();
out_mat.metallic_map_uv_2 = layers.textures[2]
.usage
.as_ref()
.map(|u| u.tex_coord)
.unwrap_or_default();
}
_ => {
log::warn!("Unsupported material data type for MTLL {:?}", data.data_type);
}
},
id => {
log::warn!("Unsupported material data ID {id:?}");
}
}
}
material_handles.push(materials.add(out_mat.clone()));
out_mat.cull_mode = Some(Face::Front);
material_handles_mirrored.push(materials.add(out_mat));
}
// Process meshes
let aabb = Aabb::from_min_max(
Vec3::new(head.bounds.min.x, head.bounds.min.y, head.bounds.min.z),
@@ -330,10 +122,10 @@ pub fn load_model(
}
out_meshes.push(BuiltMesh {
mesh: meshes.add(out_mesh),
material: material_handles[in_mesh.material_idx as usize].clone(),
mirrored_material: material_handles_mirrored[in_mesh.material_idx as usize].clone(),
material_name: mtrl.materials[in_mesh.material_idx as usize].name.clone(),
material_idx: in_mesh.material_idx as usize,
visible: true,
flags: in_mesh.unk_c,
unk_e: in_mesh.unk_e,
});
}
@@ -348,7 +140,7 @@ pub fn load_model(
lod.push(ModelLod { meshes: visible, distance: mesh.lod_rules.get(idx).map(|r| r.value) });
}
Ok(BuiltModel { meshes: out_meshes, lod, aabb })
Ok(BuiltModel { meshes: out_meshes, lod, materials: mtrl.materials.clone(), aabb })
}
pub fn convert_aabb(aabb: &CAABox) -> Aabb {
@@ -397,6 +189,18 @@ where
out
}
trait HalfArray<const N: usize> {
fn to_f32_array(self) -> [f32; N];
}
impl<const N: usize> HalfArray<N> for [u16; N] {
fn to_f32_array(self) -> [f32; N] {
let mut dst = [0f32; N];
self.reinterpret_cast::<f16>().convert_to_f32_slice(&mut dst);
dst
}
}
fn convert_component(
input: &[u8],
component: &SVertexDataComponent,
@@ -408,11 +212,19 @@ fn convert_component(
Position => Mesh::ATTRIBUTE_POSITION,
Normal => Mesh::ATTRIBUTE_NORMAL,
Tangent0 => Mesh::ATTRIBUTE_TANGENT,
Tangent1 => ATTRIBUTE_TANGENT_1,
Tangent2 => ATTRIBUTE_TANGENT_2,
TexCoord0 => Mesh::ATTRIBUTE_UV_0,
TexCoord1 => ATTRIBUTE_UV_1,
TexCoord2 => ATTRIBUTE_UV_2,
TexCoord3 => ATTRIBUTE_UV_3,
Color => Mesh::ATTRIBUTE_COLOR,
// BoneIndices => Mesh::ATTRIBUTE_JOINT_INDEX,
// BoneWeights => Mesh::ATTRIBUTE_JOINT_WEIGHT,
_ => return None,
_ => {
log::info!("Skipping attribute {:?}", component.component);
return None;
}
};
let values = match component.format {
Rg8Unorm => Unorm8x2(copy_direct(input, component)),
@@ -426,9 +238,7 @@ fn convert_component(
Rg16Uint => Uint16x2(copy_direct(input, component)),
Rg16Snorm => Snorm16x2(copy_direct(input, component)),
Rg16Sint => Sint16x2(copy_direct(input, component)),
Rg16Float => Float32x2(copy_converting(input, component, |v: [u16; 2]| {
v.map(|u| f16::from_bits(u).to_f32())
})),
Rg16Float => Float32x2(copy_converting(input, component, |v: [u16; 2]| v.to_f32_array())),
Rgba8Unorm => match component.component {
Color => Float32x4(copy_converting(input, component, |v: [u8; 4]| {
v.map(|u| u as f32 * 255.0)
@@ -446,15 +256,23 @@ fn convert_component(
Rgba16Snorm => Snorm16x4(copy_direct(input, component)),
Rgba16Sint => Sint16x4(copy_direct(input, component)),
Rgba16Float => match component.component {
Position | Normal => Float32x3(copy_converting(input, component, |v: [u16; 3]| {
v.map(|u| f16::from_bits(u).to_f32())
})),
TexCoord0 => Float32x2(copy_converting(input, component, |v: [u16; 2]| {
v.map(|u| f16::from_bits(u).to_f32())
})),
_ => Float32x4(copy_converting(input, component, |v: [u16; 4]| {
v.map(|u| f16::from_bits(u).to_f32())
Position | Normal => Float32x3(copy_converting(input, component, |v: [u16; 4]| {
debug_assert_eq!(v[3], 1);
*array_ref!(v.to_f32_array(), 0, 3)
})),
TexCoord0 | TexCoord1 | TexCoord2 | TexCoord3 => {
Float32x2(copy_converting(input, component, |v: [u16; 4]| {
let dst = v.to_f32_array();
if component.component == TexCoord1 {
// println!("UV 1: {:?}", values);
// ???
*array_ref!(dst, 2, 2)
} else {
*array_ref!(dst, 0, 2)
}
}))
}
_ => Float32x4(copy_converting(input, component, |v: [u16; 4]| v.to_f32_array())),
},
Rgb32Uint => Uint32x3(copy_direct(input, component)),
Rgb32Sint => Sint32x3(copy_direct(input, component)),
@@ -463,7 +281,9 @@ fn convert_component(
Rgba32Sint => Sint32x4(copy_direct(input, component)),
Rgba32Float => match component.component {
Position | Normal => Float32x3(copy_direct(input, component)),
TexCoord0 => Float32x2(copy_direct(input, component)),
TexCoord0 | TexCoord1 | TexCoord2 | TexCoord3 => {
Float32x2(copy_direct(input, component))
}
_ => Float32x4(copy_direct(input, component)),
},
R16Uint => Uint32(copy_converting(input, component, |v: u16| v as u32)),
@@ -532,91 +352,3 @@ enum IndicesSlice<'a> {
U16(Cow<'a, [u16]>),
U32(Cow<'a, [u32]>),
}
fn texture_wrap(wrap: ETextureWrap) -> AddressMode {
match wrap {
ETextureWrap::ClampToEdge => AddressMode::ClampToEdge,
ETextureWrap::Repeat => AddressMode::Repeat,
ETextureWrap::MirroredRepeat => AddressMode::MirrorRepeat,
ETextureWrap::MirrorClamp => todo!("Mirror clamp"),
ETextureWrap::ClampToBorder => AddressMode::ClampToBorder,
ETextureWrap::Clamp => todo!("Clamp"),
}
}
fn sampler_descriptor_from_usage<'a>(
usage: &STextureUsageInfo,
data: Option<&STextureSamplerData>,
) -> SamplerDescriptor<'a> {
SamplerDescriptor {
label: None,
address_mode_u: match usage.wrap_x {
0 => AddressMode::ClampToEdge,
1 => AddressMode::Repeat,
2 => AddressMode::MirrorRepeat,
3 => todo!("Mirror clamp"),
4 => AddressMode::ClampToBorder,
5 => todo!("Clamp"),
u32::MAX => data.map_or(AddressMode::Repeat, |d| texture_wrap(d.wrap_x)),
n => todo!("wrap {n}"),
},
address_mode_v: match usage.wrap_y {
0 => AddressMode::ClampToEdge,
1 => AddressMode::Repeat,
2 => AddressMode::MirrorRepeat,
3 => todo!("Mirror clamp"),
4 => AddressMode::ClampToBorder,
5 => todo!("Clamp"),
u32::MAX => data.map_or(AddressMode::Repeat, |d| texture_wrap(d.wrap_y)),
n => todo!("wrap {n}"),
},
address_mode_w: match usage.wrap_z {
0 => AddressMode::ClampToEdge,
1 => AddressMode::Repeat,
2 => AddressMode::MirrorRepeat,
3 => todo!("Mirror clamp"),
4 => AddressMode::ClampToBorder,
5 => todo!("Clamp"),
u32::MAX => data.map_or(AddressMode::Repeat, |d| texture_wrap(d.wrap_z)),
n => todo!("wrap {n}"),
},
mag_filter: match usage.filter {
0 => FilterMode::Nearest,
1 => FilterMode::Linear,
u32::MAX => data.map_or(FilterMode::Nearest, |d| match d.filter {
ETextureFilter::Nearest => FilterMode::Nearest,
ETextureFilter::Linear => FilterMode::Linear,
}),
n => todo!("Filter {n}"),
},
min_filter: match usage.filter {
0 => FilterMode::Nearest,
1 => FilterMode::Linear,
u32::MAX => data.map_or(FilterMode::Nearest, |d| match d.filter {
ETextureFilter::Nearest => FilterMode::Nearest,
ETextureFilter::Linear => FilterMode::Linear,
}),
n => todo!("Filter {n}"),
},
mipmap_filter: data.map_or(FilterMode::Nearest, |d| match d.mip_filter {
ETextureMipFilter::Nearest => FilterMode::Nearest,
ETextureMipFilter::Linear => FilterMode::Linear,
}),
lod_min_clamp: 0.0,
lod_max_clamp: f32::MAX,
compare: None,
anisotropy_clamp: NonZeroU8::new(
data.map(|d| match d.aniso {
ETextureAnisotropicRatio::None => 0,
ETextureAnisotropicRatio::Ratio1 => 1,
ETextureAnisotropicRatio::Ratio2 => 2,
ETextureAnisotropicRatio::Ratio4 => 4,
ETextureAnisotropicRatio::Ratio8 => 8,
ETextureAnisotropicRatio::Ratio16 => 16,
})
.unwrap_or_default(),
),
// anisotropy_clamp: NonZeroU8::new(8),
border_color: None,
}
}
+11 -11
View File
@@ -9,10 +9,10 @@ use bevy_egui::EguiContext;
use crate::AssetRef;
pub enum TabType {
Project(project::ProjectTab),
Texture(texture::TextureTab),
Model(model::ModelTab),
ModCon(modcon::ModConTab),
Project(Box<project::ProjectTab>),
Texture(Box<texture::TextureTab>),
Model(Box<model::ModelTab>),
ModCon(Box<modcon::ModConTab>),
Empty,
}
@@ -78,10 +78,10 @@ impl egui_dock::TabViewer for TabViewer<'_> {
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::ModCon(tab) => render_tab(self.world, ui, tab, &mut self.state),
TabType::Project(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::Texture(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::Model(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::ModCon(tab) => render_tab(self.world, ui, tab.as_mut(), &mut self.state),
TabType::Empty => {}
}
}
@@ -100,15 +100,15 @@ impl egui_dock::TabViewer for TabViewer<'_> {
match tab {
TabType::Project(_) => false,
TabType::Texture(tab) => {
close_tab(self.world, tab);
close_tab(self.world, tab.as_mut());
true
}
TabType::Model(tab) => {
close_tab(self.world, tab);
close_tab(self.world, tab.as_mut());
true
}
TabType::ModCon(tab) => {
close_tab(self.world, tab);
close_tab(self.world, tab.as_mut());
true
}
TabType::Empty => false,
+25 -17
View File
@@ -12,7 +12,11 @@ use egui::{Sense, Widget};
use crate::{
icon,
loaders::{modcon::ModConAsset, model::ModelAsset, texture::TextureAsset},
loaders::{
modcon::ModConAsset,
model::{MaterialKey, ModelAsset},
texture::TextureAsset,
},
material::CustomMaterial,
render::{
camera::ModelCamera,
@@ -175,14 +179,8 @@ impl SystemTab for ModConTab {
_ => continue,
}
let result = load_model(
asset,
&mut commands,
&texture_assets,
&mut images,
&mut materials,
&mut meshes,
);
asset.build_texture_images(&texture_assets, &mut images);
let result = load_model(asset, &mut meshes);
let built = match result {
Ok(value) => value,
Err(e) => {
@@ -202,14 +200,24 @@ impl SystemTab for ModConTab {
.with_children(|builder| {
for idx in built.lod[0].meshes.iter() {
let mesh = &built.meshes[idx];
let material = match asset.material(
&MaterialKey {
material_idx: mesh.material_idx,
mesh_flags: mesh.flags,
mesh_mirrored: is_mirrored,
},
&mut materials,
) {
Ok(handle) => handle,
Err(e) => {
log::warn!("Failed to build material: {:?}", e);
continue;
}
};
builder.spawn((
MaterialMeshBundle {
MaterialMeshBundle::<CustomMaterial> {
mesh: mesh.mesh.clone(),
material: if is_mirrored {
mesh.mirrored_material.clone()
} else {
mesh.material.clone()
},
material,
..default()
},
RaycastMesh::<ModConRaycastSet>::default(),
@@ -305,11 +313,11 @@ impl SystemTab for ModConTab {
if ui.button("Open in new tab").clicked() {
let handle = server
.load(format!("{}.{}", selected.asset_ref.id, selected.asset_ref.kind));
state.open_tab = Some(TabType::Model(ModelTab {
state.open_tab = Some(TabType::Model(Box::new(ModelTab {
asset_ref: selected.asset_ref,
handle,
..default()
}));
})));
ui.close_menu();
}
if ui.button("Copy GUID").clicked() {
+49 -31
View File
@@ -7,10 +7,14 @@ use bevy::{
};
use bevy_egui::EguiContext;
use egui::{Sense, Widget};
use retrolib::format::cmdl::CMaterialCache;
use crate::{
icon,
loaders::{model::ModelAsset, texture::TextureAsset},
loaders::{
model::{MaterialKey, ModelAsset},
texture::TextureAsset,
},
material::CustomMaterial,
render::{
camera::ModelCamera,
@@ -23,13 +27,16 @@ use crate::{
pub struct LoadedMesh {
pub entity: Entity,
pub material_name: String,
pub material_idx: usize,
pub visible: bool,
pub unk_c: u16,
pub unk_e: u16,
}
pub struct LoadedModel {
pub meshes: Vec<LoadedMesh>,
pub lod: Vec<ModelLod>,
pub materials: Vec<CMaterialCache>,
}
#[derive(Default)]
@@ -99,14 +106,8 @@ impl SystemTab for ModelTab {
_ => return,
}
let result = load_model(
asset,
&mut commands,
&texture_assets,
&mut images,
&mut materials,
&mut meshes,
);
asset.build_texture_images(&texture_assets, &mut images);
let result = load_model(asset, &mut meshes);
let built = match result {
Ok(value) => value,
Err(e) => {
@@ -114,25 +115,39 @@ impl SystemTab for ModelTab {
return;
}
};
self.loaded = Some(LoadedModel {
meshes: built
.meshes
.into_iter()
.map(|mesh| LoadedMesh {
entity: commands
.spawn(MaterialMeshBundle {
mesh: mesh.mesh,
material: mesh.material,
transform: Transform::from_translation((-built.aabb.center).into()),
..default()
})
.id(),
material_name: mesh.material_name,
visible: mesh.visible,
let mut meshes = Vec::with_capacity(built.meshes.len());
for mesh in built.meshes {
let material = match asset.material(
&MaterialKey {
material_idx: mesh.material_idx,
mesh_flags: mesh.flags,
mesh_mirrored: false,
},
&mut materials,
) {
Ok(handle) => handle,
Err(e) => {
log::warn!("Failed to build material: {:?}", e);
continue;
}
};
let entity = commands
.spawn(MaterialMeshBundle::<CustomMaterial> {
mesh: mesh.mesh,
material,
transform: Transform::from_translation((-built.aabb.center).into()),
..default()
})
.collect(),
lod: built.lod,
});
.id();
meshes.push(LoadedMesh {
entity,
material_idx: mesh.material_idx,
visible: mesh.visible,
unk_c: mesh.flags,
unk_e: mesh.unk_e,
});
}
self.loaded = Some(LoadedModel { meshes, lod: built.lod, materials: built.materials });
self.camera.init(&convert_aabb(&asset.inner.head.bounds), false);
self.diffuse_map = server.load("papermill_diffuse_rgb9e5_zstd.ktx2");
self.specular_map = server.load("papermill_specular_rgb9e5_zstd.ktx2");
@@ -160,8 +175,8 @@ impl SystemTab for ModelTab {
let left_top = rect.left_top().to_vec2() * scale;
let size = rect.size() * scale;
let viewport = Viewport {
physical_position: UVec2 { x: left_top.x as u32, y: left_top.y as u32 },
physical_size: UVec2 { x: size.x as u32, y: size.y as u32 },
physical_position: UVec2::new(left_top.x as u32, left_top.y as u32),
physical_size: UVec2::new(size.x as u32, size.y as u32),
depth: 0.0..1.0,
};
let response =
@@ -226,7 +241,10 @@ impl SystemTab for ModelTab {
let mesh = &mut loaded.meshes[idx];
ui.checkbox(
&mut mesh.visible,
format!("Mesh {idx} ({})", mesh.material_name),
format!(
"Mesh {idx} ({}, {}, {})",
mesh.unk_c, mesh.unk_e, loaded.materials[mesh.material_idx].name
),
);
if let Some(mut commands) = commands.get_entity(mesh.entity) {
commands.insert((
+6 -6
View File
@@ -216,28 +216,28 @@ impl SystemTab for ProjectTab {
"{}.{}",
entry.id, entry.kind
));
state.open_tab = Some(TabType::Texture(TextureTab {
state.open_tab = Some(TabType::Texture(Box::new(TextureTab {
asset_ref,
handle,
..default()
}));
})));
}
K_FORM_CMDL | K_FORM_SMDL | K_FORM_WMDL => {
let handle = server
.load::<ModelAsset, _>(format!("{}.{}", entry.id, entry.kind));
state.open_tab = Some(TabType::Model(ModelTab {
state.open_tab = Some(TabType::Model(Box::new(ModelTab {
asset_ref,
handle,
..default()
}));
})));
}
K_FORM_MCON => {
let handle = server.load(format!("{}.{}", entry.id, entry.kind));
state.open_tab = Some(TabType::ModCon(ModConTab {
state.open_tab = Some(TabType::ModCon(Box::new(ModConTab {
asset_ref,
handle,
..default()
}));
})));
}
_ => {}
}
+5 -1
View File
@@ -10,6 +10,10 @@ repository = "https://github.com/PrimeDecomp/retrotool"
readme = "README.md"
categories = ["command-line-utilities"]
[features]
default = []
nightly = ["half/use-intrinsics"]
[dependencies]
anyhow = "1.0.69"
argh = "0.1.10"
@@ -18,7 +22,7 @@ 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"
half = { git = "https://github.com/encounter/half-rs", branch = "inline" }
image = "0.24.5"
log = "0.4.17"
memmap2 = "0.5.8"