From 01aabb65f9136c6f2374678a29b3e1f9f1bb3153 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Fri, 10 Mar 2023 21:56:15 -0500 Subject: [PATCH] Add custom material for Retro's PBR --- Cargo.lock | 13 +- lib/Cargo.toml | 2 +- lib/src/format/mod.rs | 14 +- retrotool-gui/assets/custom_material.wgsl | 577 ++++++++++++++++++++++ retrotool-gui/src/main.rs | 15 +- retrotool-gui/src/material.rs | 183 +++++++ retrotool-gui/src/tabs/model.rs | 262 ++++++++-- 7 files changed, 1019 insertions(+), 47 deletions(-) create mode 100644 retrotool-gui/assets/custom_material.wgsl create mode 100644 retrotool-gui/src/material.rs diff --git a/Cargo.lock b/Cargo.lock index 322b17d..9762354 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3061,7 +3061,7 @@ dependencies = [ "image", "log", "memmap2", - "tegra_swizzle 0.3.0 (git+https://github.com/ScanMountGoat/tegra_swizzle?rev=f0cf2c61b78b929381f09dd224ca27fe6929e176)", + "tegra_swizzle", "uuid", ] @@ -3083,7 +3083,7 @@ dependencies = [ "png", "retrolib", "serde_json", - "tegra_swizzle 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tegra_swizzle", "uuid", ] @@ -3412,14 +3412,9 @@ dependencies = [ [[package]] name = "tegra_swizzle" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898709aaa04e72af51fafa032802e0dee931eee066894478ac0000b4525d30bf" - -[[package]] -name = "tegra_swizzle" -version = "0.3.0" -source = "git+https://github.com/ScanMountGoat/tegra_swizzle?rev=f0cf2c61b78b929381f09dd224ca27fe6929e176#f0cf2c61b78b929381f09dd224ca27fe6929e176" +checksum = "d5a696f1997cdcb21949d259b95ccfad64fe5207897c4a4aa34faa4c34bc1678" [[package]] name = "termcolor" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 1c55441..0fdda29 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -20,5 +20,5 @@ flate2 = "1.0.25" image = "0.24.5" log = "0.4.17" memmap2 = "0.5.9" -tegra_swizzle = { git = "https://github.com/ScanMountGoat/tegra_swizzle", rev = "f0cf2c61b78b929381f09dd224ca27fe6929e176" } +tegra_swizzle = "0.3.1" uuid = "1.3.0" diff --git a/lib/src/format/mod.rs b/lib/src/format/mod.rs index ec1eca6..4c87cc8 100644 --- a/lib/src/format/mod.rs +++ b/lib/src/format/mod.rs @@ -62,7 +62,7 @@ impl PartialEq<[u8; 4]> for FourCC { pub fn peek_four_cc(data: &[u8]) -> FourCC { FourCC(*array_ref!(data, 0, 4)) } #[binrw] -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct CVector3f { pub x: f32, pub y: f32, @@ -70,7 +70,7 @@ pub struct CVector3f { } #[binrw] -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct CColor4f { pub r: f32, pub g: f32, @@ -78,8 +78,16 @@ pub struct CColor4f { pub a: f32, } +impl CColor4f { + #[inline] + pub fn to_array(self) -> [f32; 4] { [self.r, self.g, self.b, self.a] } +} +impl From for [f32; 4] { + fn from(value: CColor4f) -> Self { value.to_array() } +} + #[binrw] -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct CVector4i { pub x: i32, pub y: i32, diff --git a/retrotool-gui/assets/custom_material.wgsl b/retrotool-gui/assets/custom_material.wgsl new file mode 100644 index 0000000..00a986d --- /dev/null +++ b/retrotool-gui/assets/custom_material.wgsl @@ -0,0 +1,577 @@ +#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::mesh_bindings + +#import bevy_pbr::utils +#import bevy_pbr::clustered_forward +#import bevy_pbr::lighting + +struct CustomMaterial { + base_color: vec4, + base_color_uv_0: u32, + base_color_uv_1: u32, + base_color_uv_2: u32, + base_color_l0: vec4, + base_color_l1: vec4, + base_color_l2: vec4, + normal_map_uv_0: u32, + normal_map_uv_1: u32, + normal_map_uv_2: u32, + normal_map_l0: vec4, + normal_map_l1: vec4, + normal_map_l2: vec4, + metallic_map_uv_0: u32, + metallic_map_uv_1: u32, + metallic_map_uv_2: u32, + metallic_map_l0: vec4, + metallic_map_l1: vec4, + metallic_map_l2: vec4, + emissive_uv: u32, + emissive_color: vec4, +}; +@group(1) @binding(0) +var material: CustomMaterial; +@group(1) @binding(1) +var base_color_texture_0: texture_2d; +@group(1) @binding(2) +var base_color_sampler_0: sampler; +@group(1) @binding(3) +var base_color_texture_1: texture_2d; +@group(1) @binding(4) +var base_color_sampler_1: sampler; +@group(1) @binding(5) +var base_color_texture_2: texture_2d; +@group(1) @binding(6) +var base_color_sampler_2: sampler; +@group(1) @binding(7) +var normal_map_texture_0: texture_2d; +@group(1) @binding(8) +var normal_map_sampler_0: sampler; +@group(1) @binding(9) +var normal_map_texture_1: texture_2d; +@group(1) @binding(10) +var normal_map_sampler_1: sampler; +@group(1) @binding(11) +var normal_map_texture_2: texture_2d; +@group(1) @binding(12) +var normal_map_sampler_2: sampler; +@group(1) @binding(13) +var metallic_map_texture_0: texture_2d; +@group(1) @binding(14) +var metallic_map_sampler_0: sampler; +@group(1) @binding(15) +var metallic_map_texture_1: texture_2d; +@group(1) @binding(16) +var metallic_map_sampler_1: sampler; +@group(1) @binding(17) +var metallic_map_texture_2: texture_2d; +@group(1) @binding(18) +var metallic_map_sampler_2: sampler; +@group(1) @binding(19) +var emissive_texture: texture_2d; +@group(1) @binding(20) +var emissive_sampler: sampler; + +// NOTE: Bindings must come before functions that use them! +#import bevy_pbr::mesh_functions + +struct VertexInput { +#ifdef VERTEX_POSITIONS + @location(0) position: vec3, +#endif +#ifdef VERTEX_NORMALS + @location(1) normal: vec3, +#endif +#ifdef VERTEX_UVS_0 + @location(2) uv_0: vec2, +#endif +#ifdef VERTEX_UVS_1 + @location(3) uv_1: vec2, +#endif +#ifdef VERTEX_UVS_2 + @location(4) uv_2: vec2, +#endif +#ifdef VERTEX_UVS_3 + @location(5) uv_3: vec2, +#endif +#ifdef VERTEX_TANGENTS_0 + @location(6) tangent_0: vec4, +#endif +#ifdef VERTEX_TANGENTS_1 + @location(7) tangent_1: vec4, +#endif +#ifdef VERTEX_TANGENTS_2 + @location(8) tangent_2: vec4, +#endif +#ifdef VERTEX_COLORS + @location(9) color: vec4, +#endif +#ifdef SKINNED + @location(10) joint_indices: vec4, + @location(11) joint_weights: vec4, +#endif +}; + +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) world_position: vec4, +#ifdef VERTEX_NORMALS + @location(1) world_normal: vec3, +#endif +#ifdef VERTEX_UVS_0 + @location(2) uv_0: vec2, +#endif +#ifdef VERTEX_UVS_1 + @location(3) uv_1: vec2, +#endif +#ifdef VERTEX_UVS_2 + @location(4) uv_2: vec2, +#endif +#ifdef VERTEX_TANGENTS_0 + @location(5) world_tangent_0: vec4, +#endif +#ifdef VERTEX_TANGENTS_1 + @location(6) world_tangent_1: vec4, +#endif +#ifdef VERTEX_TANGENTS_2 + @location(7) world_tangent_2: vec4, +#endif +#ifdef VERTEX_COLORS + @location(8) color: vec4, +#endif +}; + +@vertex +fn vertex(in: VertexInput) -> VertexOutput { + var out: VertexOutput; +#ifdef VERTEX_POSITIONS + let position = vec4(in.position, 1.0); +#else + let position = vec4(0.0, 0.0, 0.0, 1.0); +#endif +#ifdef SKINNED + var model = skin_model(in.joint_indices, in.joint_weights); +#else + var model = mesh.model; +#endif + out.position = mesh_position_local_to_clip(model, position); + out.world_position = mesh_position_local_to_world(model, position); +#ifdef VERTEX_NORMALS +#ifdef SKINNED + out.world_normal = skin_normals(model, in.normal); +#else + out.world_normal = mesh_normal_local_to_world(in.normal); +#endif +#endif +#ifdef VERTEX_UVS_0 + out.uv_0 = in.uv_0; +#endif +#ifdef VERTEX_UVS_1 + out.uv_1 = in.uv_1; +#endif +#ifdef VERTEX_UVS_2 + out.uv_2 = in.uv_2; +#endif +#ifdef VERTEX_TANGENTS_0 + out.world_tangent_0 = mesh_tangent_local_to_world(mesh.model, in.tangent_0); +#endif +#ifdef VERTEX_TANGENTS_1 + out.world_tangent_1 = mesh_tangent_local_to_world(mesh.model, in.tangent_1); +#endif +#ifdef VERTEX_TANGENTS_2 + out.world_tangent_2 = mesh_tangent_local_to_world(mesh.model, in.tangent_2); +#endif +#ifdef VERTEX_COLORS + out.color = in.color; +#endif + return out; +} + +fn in_uv(in: ptr, idx: u32) -> vec2 { + switch (idx) { +#ifdef VERTEX_UVS_0 + case 0u: { + return (*in).uv_0; + } +#endif +#ifdef VERTEX_UVS_1 + case 1u: { + return (*in).uv_1; + } +#endif +#ifdef VERTEX_UVS_2 + case 2u: { + return (*in).uv_2; + } +#endif + default: { + return vec2(0.0); + } + } +} + +fn sample_base_color(in: ptr) -> vec4 { + var color = vec4(0.0); + color += textureSample( + base_color_texture_0, + base_color_sampler_0, + in_uv(in, material.base_color_uv_0) + ) * material.base_color_l0; + color += textureSample( + base_color_texture_1, + base_color_sampler_1, + in_uv(in, material.base_color_uv_1) + ) * material.base_color_l1; + color += textureSample( + base_color_texture_2, + base_color_sampler_2, + in_uv(in, material.base_color_uv_2) + ) * material.base_color_l2; + color /= material.base_color_l0 + + material.base_color_l1 + + material.base_color_l2; + return color; +} + +fn sample_normal_map(in: ptr) -> vec4 { + var color = vec4(0.0); + color += textureSample( + normal_map_texture_0, + normal_map_sampler_0, + in_uv(in, material.normal_map_uv_0) + ) * material.normal_map_l0; + color += textureSample( + normal_map_texture_1, + normal_map_sampler_1, + in_uv(in, material.normal_map_uv_1) + ) * material.normal_map_l1; + color += textureSample( + normal_map_texture_2, + normal_map_sampler_2, + in_uv(in, material.normal_map_uv_2) + ) * material.normal_map_l2; + color /= material.normal_map_l0 + + material.normal_map_l1 + + material.normal_map_l2; + return color; +} + +fn sample_metallic_map(in: ptr) -> vec4 { + var color = vec4(0.0); + color += textureSample( + metallic_map_texture_0, + metallic_map_sampler_0, + in_uv(in, material.metallic_map_uv_0) + ) * material.metallic_map_l0; + color += textureSample( + metallic_map_texture_1, + metallic_map_sampler_1, + in_uv(in, material.metallic_map_uv_1) + ) * material.metallic_map_l1; + color += textureSample( + metallic_map_texture_2, + metallic_map_sampler_2, + in_uv(in, material.metallic_map_uv_2) + ) * material.metallic_map_l2; + color /= material.metallic_map_l0 + + material.metallic_map_l1 + + material.metallic_map_l2; + return color; +} + +fn apply_normal_mapping( + world_normal: vec3, +#ifdef VERTEX_TANGENTS_0 + world_tangent: vec4, + in: ptr, +#endif +) -> vec3 { + // NOTE: The mikktspace method of normal mapping explicitly requires that the world normal NOT + // be re-normalized in the fragment shader. This is primarily to match the way mikktspace + // bakes vertex tangents and normal maps so that this is the exact inverse. Blender, Unity, + // Unreal Engine, Godot, and more all use the mikktspace method. Do not change this code + // unless you really know what you are doing. + // http://www.mikktspace.com/ + var N: vec3 = world_normal; + +#ifdef VERTEX_TANGENTS_0 + // NOTE: The mikktspace method of normal mapping explicitly requires that these NOT be + // normalized nor any Gram-Schmidt applied to ensure the vertex normal is orthogonal to the + // vertex tangent! Do not change this code unless you really know what you are doing. + // http://www.mikktspace.com/ + var T: vec3 = world_tangent.xyz; + var B: vec3 = world_tangent.w * cross(N, T); + // Nt is the tangent-space normal. + var Nt = sample_normal_map(in).rgb; + // Only use the xy components and derive z for 2-component normal maps. + Nt = vec3(Nt.rg * 2.0 - 1.0, 0.0); + Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y); + // Normal maps authored for DirectX require flipping the y component + Nt.y = -Nt.y; + // NOTE: The mikktspace method of normal mapping applies maps the tangent-space normal from + // the normal map texture in this way to be an EXACT inverse of how the normal map baker + // calculates the normal maps so there is no error introduced. Do not change this code + // unless you really know what you are doing. + // http://www.mikktspace.com/ + N = Nt.x * T + Nt.y * B + Nt.z * N; +#endif + + return normalize(N); +} + +struct StandardMaterial { + base_color: vec4, + emissive: vec4, + perceptual_roughness: f32, + metallic: f32, + reflectance: f32, + // 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options. + flags: u32, + alpha_cutoff: f32, +}; + +let STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT: u32 = 1u; +let STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT: u32 = 2u; +let STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT: u32 = 4u; +let STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT: u32 = 8u; +let STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT: u32 = 16u; +let STANDARD_MATERIAL_FLAGS_UNLIT_BIT: u32 = 32u; +let STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE: u32 = 64u; +let STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK: u32 = 128u; +let STANDARD_MATERIAL_FLAGS_ALPHA_MODE_BLEND: u32 = 256u; +let STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP: u32 = 512u; +let STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y: u32 = 1024u; + +// Creates a StandardMaterial with default values +fn standard_material_new() -> StandardMaterial { + var material: StandardMaterial; + + // NOTE: Keep in-sync with src/pbr_material.rs! + material.base_color = vec4(1.0, 1.0, 1.0, 1.0); + material.emissive = vec4(0.0, 0.0, 0.0, 1.0); + material.perceptual_roughness = 0.089; + material.metallic = 0.01; + material.reflectance = 0.5; + material.flags = STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE; + material.alpha_cutoff = 0.5; + + return material; +} + +struct PbrInput { + material: StandardMaterial, + occlusion: f32, + frag_coord: vec4, + world_position: vec4, + // Normalized world normal used for shadow mapping as normal-mapping is not used for shadow + // mapping + world_normal: vec3, + // Normalized normal-mapped world normal used for lighting + N: vec3, + // Normalized view vector in world space, pointing from the fragment world position toward the + // view world position + V: vec3, + is_orthographic: bool, +}; + +// Creates a PbrInput with default values +fn pbr_input_new() -> PbrInput { + var pbr_input: PbrInput; + + pbr_input.material = standard_material_new(); + pbr_input.occlusion = 1.0; + + pbr_input.frag_coord = vec4(0.0, 0.0, 0.0, 1.0); + pbr_input.world_position = vec4(0.0, 0.0, 0.0, 1.0); + pbr_input.world_normal = vec3(0.0, 0.0, 1.0); + + pbr_input.is_orthographic = false; + + pbr_input.N = vec3(0.0, 0.0, 1.0); + pbr_input.V = vec3(1.0, 0.0, 0.0); + + return pbr_input; +} + +// NOTE: Correctly calculates the view vector depending on whether +// the projection is orthographic or perspective. +fn calculate_view( + world_position: vec4, + is_orthographic: bool, +) -> vec3 { + var V: vec3; + if (is_orthographic) { + // Orthographic view vector + V = normalize(vec3(view.view_proj[0].z, view.view_proj[1].z, view.view_proj[2].z)); + } else { + // Only valid for a perpective projection + V = normalize(view.world_position.xyz - world_position.xyz); + } + return V; +} + +#ifdef TONEMAP_IN_SHADER +#import bevy_core_pipeline::tonemapping +#endif + +#ifdef TONEMAP_IN_SHADER +fn tone_mapping(in: vec4) -> vec4 { + // tone_mapping + return vec4(reinhard_luminance(in.rgb), in.a); + + // Gamma correction. + // Not needed with sRGB buffer + // output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2)); +} +#endif + +#ifdef DEBAND_DITHER +fn dither(color: vec4, pos: vec2) -> vec4 { + return vec4(color.rgb + screen_space_dither(pos.xy), color.a); +} +#endif + +fn pbr( + in: PbrInput, +) -> vec4 { + var output_color: vec4 = in.material.base_color; + + // TODO use .a for exposure compensation in HDR + let emissive = in.material.emissive; + + // calculate non-linear roughness from linear perceptualRoughness + let metallic = in.material.metallic; + let perceptual_roughness = in.material.perceptual_roughness; + let roughness = perceptualRoughnessToRoughness(perceptual_roughness); + + let occlusion = in.occlusion; + + // output_color = alpha_discard(in.material, output_color); + 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); + + // Remapping [0,1] reflectance to F0 + // See https://google.github.io/filament/Filament.html#materialsystem/parameterization/remapping + let reflectance = in.material.reflectance; + let F0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + output_color.rgb * metallic; + + // Diffuse strength inversely related to metallicity + let diffuse_color = output_color.rgb * (1.0 - metallic); + + let R = reflect(-in.V, in.N); + + // accumulate color + var light_accum: vec3 = vec3(0.0); + + let view_z = dot(vec4( + view.inverse_view[0].z, + view.inverse_view[1].z, + view.inverse_view[2].z, + view.inverse_view[3].z + ), in.world_position); + let cluster_index = fragment_cluster_index(in.frag_coord.xy, view_z, in.is_orthographic); + let offset_and_counts = unpack_offset_and_counts(cluster_index); + + // point lights + for (var i: u32 = offset_and_counts[0]; i < offset_and_counts[0] + offset_and_counts[1]; i = i + 1u) { + let light_id = get_light_id(i); + let light = point_lights.data[light_id]; + var shadow: f32 = 1.0; +// if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u +// && (light.flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { +// shadow = fetch_point_shadow(light_id, in.world_position, in.world_normal); +// } + let light_contrib = point_light(in.world_position.xyz, light, roughness, NdotV, in.N, in.V, R, F0, diffuse_color); + light_accum = light_accum + light_contrib * shadow; + } + + // spot lights + for (var i: u32 = offset_and_counts[0] + offset_and_counts[1]; i < offset_and_counts[0] + offset_and_counts[1] + offset_and_counts[2]; i = i + 1u) { + let light_id = get_light_id(i); + let light = point_lights.data[light_id]; + var shadow: f32 = 1.0; +// if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u +// && (light.flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { +// shadow = fetch_spot_shadow(light_id, in.world_position, in.world_normal); +// } + let light_contrib = spot_light(in.world_position.xyz, light, roughness, NdotV, in.N, in.V, R, F0, diffuse_color); + light_accum = light_accum + light_contrib * shadow; + } + + let n_directional_lights = lights.n_directional_lights; + for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) { + let light = lights.directional_lights[i]; + var shadow: f32 = 1.0; +// if ((mesh.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u +// && (light.flags & DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) { +// shadow = fetch_directional_shadow(i, in.world_position, in.world_normal); +// } + let light_contrib = directional_light(light, roughness, NdotV, in.N, in.V, R, F0, diffuse_color); + light_accum = light_accum + light_contrib * shadow; + } + + let diffuse_ambient = EnvBRDFApprox(diffuse_color, 1.0, NdotV); + let specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV); + + output_color = vec4( + light_accum + + (diffuse_ambient + specular_ambient) * lights.ambient_color.rgb * occlusion + + emissive.rgb * output_color.a, + output_color.a); + + return output_color; +} + +@fragment +fn fragment(in: VertexOutput) -> @location(0) vec4 { + var in2: VertexOutput = in; + + var pbr_input = pbr_input_new(); + pbr_input.material.base_color = sample_base_color(&in2) * material.base_color; +//#ifdef VERTEX_COLORS +// pbr_input.material.base_color *= in.color; +//#endif + + pbr_input.frag_coord = in.position; + pbr_input.world_position = in.world_position; + pbr_input.world_normal = in.world_normal; + pbr_input.is_orthographic = view.projection[3].w == 1.0; + + pbr_input.material.emissive = textureSample( + emissive_texture, + emissive_sampler, + in_uv(&in2, material.emissive_uv) + ) * material.emissive_color; + + let metl_map = sample_metallic_map(&in2); + pbr_input.material.base_color *= metl_map.r; // AO + pbr_input.material.perceptual_roughness = metl_map.g; + pbr_input.material.metallic = metl_map.b; + +#ifdef VERTEX_NORMALS + pbr_input.N = apply_normal_mapping( + in.world_normal, +#ifdef VERTEX_TANGENTS_0 + in.world_tangent_0, + &in2, +#endif + ); +#endif + pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); + + var output_color = pbr(pbr_input); +#ifdef TONEMAP_IN_SHADER + output_color = tone_mapping(output_color); +#endif +#ifdef DEBAND_DITHER + var output_rgb = output_color.rgb; + output_rgb = pow(output_rgb, vec3(1.0 / 2.2)); + output_rgb = output_rgb + screen_space_dither(in.position.xy); + // This conversion back to linear space is required because our output texture format is + // SRGB; the GPU will assume our output is linear and will apply an SRGB conversion. + output_rgb = pow(output_rgb, vec3(2.2)); + output_color = vec4(output_rgb, output_color.a); +#endif + return output_color; +} diff --git a/retrotool-gui/src/main.rs b/retrotool-gui/src/main.rs index 5116ca6..8750eb4 100644 --- a/retrotool-gui/src/main.rs +++ b/retrotool-gui/src/main.rs @@ -1,5 +1,6 @@ mod icon; mod loaders; +mod material; mod tabs; use std::path::PathBuf; @@ -16,7 +17,8 @@ use crate::{ package_loader_system, MaterialAssetLoader, ModelAssetLoader, PackageAssetLoader, PackageDirectory, RetroAssetIoPlugin, TextureAssetLoader, }, - tabs::{load_tab, model::TemporaryTag, project::ProjectTab, TabState, TabType, TabViewer}, + material::CustomMaterial, + tabs::{load_tab, model::TemporaryLabel, project::ProjectTab, TabState, TabType, TabViewer}, }; #[derive(Default, Resource)] @@ -29,12 +31,16 @@ fn main() { } App::new() .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.05))) - .insert_resource(Msaa { samples: 4 }) + // .insert_resource(Msaa { samples: 4 }) .insert_resource(bevy::render::settings::WgpuSettings { features: bevy::render::settings::WgpuFeatures::TEXTURE_COMPRESSION_BC, ..default() }) .insert_resource(bevy::winit::WinitSettings::desktop_app()) + // .insert_resource(AmbientLight { + // color: Color::rgb(1.0, 1.0, 1.0), + // brightness: 0.6, + // }) .insert_resource(file_open) .init_resource::() .init_resource::() @@ -53,6 +59,7 @@ fn main() { }) .add_before::(RetroAssetIoPlugin), ) + .add_plugin(MaterialPlugin::::default()) .add_plugin(PackageAssetLoader) .add_plugin(TextureAssetLoader) .add_plugin(ModelAssetLoader) @@ -165,7 +172,7 @@ fn ui_system(world: &mut World) { // Remove all temporary entities let mut to_remove = vec![]; - for (entity, _) in world.query::<(Entity, With)>().iter(world) { + for (entity, _) in world.query::<(Entity, With)>().iter(world) { to_remove.push(entity); } for entity in to_remove { @@ -191,7 +198,7 @@ fn ui_system(world: &mut World) { if viewer.state.render_layer == 0 { // Spawn a camera to just clear the screen - world.spawn((Camera3dBundle::default(), TemporaryTag)); + world.spawn((Camera3dBundle::default(), TemporaryLabel)); } }); }); diff --git a/retrotool-gui/src/material.rs b/retrotool-gui/src/material.rs new file mode 100644 index 0000000..dcc4b69 --- /dev/null +++ b/retrotool-gui/src/material.rs @@ -0,0 +1,183 @@ +use bevy::{ + pbr::*, + prelude::*, + reflect::TypeUuid, + render::{mesh::*, render_resource::*}, +}; + +// 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 = + MeshVertexAttribute::new("Vertex_Uv_1", 988540917, VertexFormat::Float32x2); +const ATTRIBUTE_UV_2: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Uv_2", 988540918, VertexFormat::Float32x2); +const ATTRIBUTE_UV_3: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Uv_3", 988540919, VertexFormat::Float32x2); +const ATTRIBUTE_TANGENT_1: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Tangent_1", 988540920, VertexFormat::Float32x4); +const ATTRIBUTE_TANGENT_2: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Tangent_2", 988540921, VertexFormat::Float32x4); + +// This is the struct that will be passed to your shader +#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] +pub struct CustomMaterial { + #[uniform(0)] + pub base_color: Color, + #[texture(1)] + #[sampler(2)] + pub base_color_texture_0: Option>, + #[texture(3)] + #[sampler(4)] + pub base_color_texture_1: Option>, + #[texture(5)] + #[sampler(6)] + pub base_color_texture_2: Option>, + #[uniform(0)] + pub base_color_uv_0: u32, + #[uniform(0)] + pub base_color_uv_1: u32, + #[uniform(0)] + pub base_color_uv_2: u32, + #[uniform(0)] + pub base_color_l0: Color, + #[uniform(0)] + 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>, + #[texture(9)] + #[sampler(10)] + pub normal_map_texture_1: Option>, + #[texture(11)] + #[sampler(12)] + pub normal_map_texture_2: Option>, + #[uniform(0)] + pub normal_map_uv_0: u32, + #[uniform(0)] + pub normal_map_uv_1: u32, + #[uniform(0)] + pub normal_map_uv_2: u32, + #[uniform(0)] + pub normal_map_l0: Color, + #[uniform(0)] + pub normal_map_l1: Color, + #[uniform(0)] + pub normal_map_l2: Color, + #[texture(13)] + #[sampler(14)] + pub metallic_map_texture_0: Option>, + #[texture(15)] + #[sampler(16)] + pub metallic_map_texture_1: Option>, + #[texture(17)] + #[sampler(18)] + pub metallic_map_texture_2: Option>, + #[uniform(0)] + pub metallic_map_uv_0: u32, + #[uniform(0)] + pub metallic_map_uv_1: u32, + #[uniform(0)] + pub metallic_map_uv_2: u32, + #[uniform(0)] + pub metallic_map_l0: Color, + #[uniform(0)] + pub metallic_map_l1: Color, + #[uniform(0)] + pub metallic_map_l2: Color, + #[texture(19)] + #[sampler(20)] + pub emissive_texture: Option>, + #[uniform(0)] + pub emissive_uv: u32, + #[uniform(0)] + pub emissive_color: Color, +} + +impl Default for CustomMaterial { + fn default() -> Self { + Self { + base_color: Color::WHITE, + base_color_texture_0: None, + base_color_texture_1: None, + base_color_texture_2: None, + base_color_uv_0: 0, + base_color_uv_1: 0, + base_color_uv_2: 0, + base_color_l0: Color::NONE, + base_color_l1: Color::NONE, + base_color_l2: Color::NONE, + normal_map_texture_0: None, + normal_map_texture_1: None, + normal_map_texture_2: None, + normal_map_uv_0: 0, + normal_map_uv_1: 0, + normal_map_uv_2: 0, + normal_map_l0: Color::NONE, + normal_map_l1: Color::NONE, + normal_map_l2: Color::NONE, + metallic_map_texture_0: None, + metallic_map_texture_1: None, + metallic_map_texture_2: None, + metallic_map_uv_0: 0, + metallic_map_uv_1: 0, + metallic_map_uv_2: 0, + metallic_map_l0: Color::NONE, + metallic_map_l1: Color::NONE, + metallic_map_l2: Color::NONE, + emissive_texture: None, + emissive_uv: 0, + emissive_color: Color::BLACK, + } + } +} + +impl Material for CustomMaterial { + fn vertex_shader() -> ShaderRef { "custom_material.wgsl".into() } + + fn fragment_shader() -> ShaderRef { "custom_material.wgsl".into() } + + fn specialize( + _pipeline: &MaterialPipeline, + descriptor: &mut RenderPipelineDescriptor, + layout: &MeshVertexBufferLayout, + _key: MaterialPipelineKey, + ) -> Result<(), SpecializedMeshPipelineError> { + let mut shader_defs = Vec::new(); + let mut vertex_attributes = Vec::new(); + let mut add_attribute = |attr: MeshVertexAttribute, location: u32, define: &str| { + if layout.contains(attr.clone()) { + shader_defs.push(String::from(define)); + vertex_attributes.push(attr.at_shader_location(location)); + } + }; + add_attribute(Mesh::ATTRIBUTE_POSITION, 0, "VERTEX_POSITIONS"); + add_attribute(Mesh::ATTRIBUTE_NORMAL, 1, "VERTEX_NORMALS"); + add_attribute(Mesh::ATTRIBUTE_UV_0, 2, "VERTEX_UVS_0"); + add_attribute(ATTRIBUTE_UV_1, 3, "VERTEX_UVS_1"); + add_attribute(ATTRIBUTE_UV_2, 4, "VERTEX_UVS_2"); + add_attribute(ATTRIBUTE_UV_3, 5, "VERTEX_UVS_3"); + add_attribute(Mesh::ATTRIBUTE_TANGENT, 6, "VERTEX_TANGENTS_0"); + add_attribute(ATTRIBUTE_TANGENT_1, 7, "VERTEX_TANGENTS_1"); + add_attribute(ATTRIBUTE_TANGENT_2, 8, "VERTEX_TANGENTS_2"); + add_attribute(Mesh::ATTRIBUTE_COLOR, 9, "VERTEX_COLORS"); + + if layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX) + && layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT) + { + shader_defs.push(String::from("SKINNED")); + vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(10)); + vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(11)); + } + + let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?; + descriptor.vertex.buffers = vec![vertex_buffer_layout]; + descriptor.vertex.shader_defs.append(&mut shader_defs.clone()); + descriptor.fragment.as_mut().unwrap().shader_defs.append(&mut shader_defs); + Ok(()) + } +} diff --git a/retrotool-gui/src/tabs/model.rs b/retrotool-gui/src/tabs/model.rs index 87d7594..2c66aa2 100644 --- a/retrotool-gui/src/tabs/model.rs +++ b/retrotool-gui/src/tabs/model.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, ops::Range}; +use std::{borrow::Cow, num::NonZeroU8, ops::Range}; use bevy::{ asset::LoadState, @@ -18,6 +18,7 @@ use bevy::{ }, utils::HashMap, }; +use bevy::core_pipeline::bloom::BloomSettings; use bevy_egui::EguiContext; use egui::{Id, PointerButton, Sense}; use half::f16; @@ -26,13 +27,17 @@ use retrolib::format::{ CMaterialDataInner, EBufferType, EMaterialDataId, EVertexComponent, EVertexDataFormat, ModelData, STextureUsageInfo, SVertexDataComponent, }, - txtr::{ETextureFormat, ETextureType}, + txtr::{ + ETextureAnisotropicRatio, ETextureFilter, ETextureFormat, ETextureMipFilter, ETextureType, + ETextureWrap, STextureSamplerData, + }, }; use uuid::Uuid; use crate::{ icon, loaders::{ModelAsset, TextureAsset}, + material::CustomMaterial, tabs::SystemTab, AssetRef, TabState, }; @@ -100,7 +105,7 @@ fn convert_component( Normal => Mesh::ATTRIBUTE_NORMAL, Tangent0 => Mesh::ATTRIBUTE_TANGENT, TexCoord0 => Mesh::ATTRIBUTE_UV_0, - // Color => Mesh::ATTRIBUTE_COLOR, + Color => Mesh::ATTRIBUTE_COLOR, // BoneIndices => Mesh::ATTRIBUTE_JOINT_INDEX, // BoneWeights => Mesh::ATTRIBUTE_JOINT_WEIGHT, _ => return None, @@ -126,7 +131,7 @@ fn convert_component( })), _ => Unorm8x4(copy_direct(input, component)), }, - Rgba8Uint => Uint8x4(copy_direct(input, component)), + Rgba8Uint => Uint16x4(copy_converting(input, component, |v: [u8; 4]| v.map(|n| n as u16))), Rgba8Snorm => Snorm8x4(copy_direct(input, component)), Rgba8Sint => Sint8x4(copy_direct(input, component)), Rg32Uint => Uint32x2(copy_direct(input, component)), @@ -225,53 +230,92 @@ enum IndicesSlice<'a> { } #[derive(Component)] -pub struct TemporaryTag; +pub struct TemporaryLabel; -fn sampler_descriptor_from_usage<'a>(usage: &STextureUsageInfo) -> SamplerDescriptor<'a> { +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 | u32::MAX => AddressMode::ClampToEdge, + 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 | u32::MAX => AddressMode::ClampToEdge, + 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 | u32::MAX => AddressMode::ClampToEdge, + 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 | u32::MAX => FilterMode::Nearest, + 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 | u32::MAX => FilterMode::Nearest, + 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: FilterMode::Linear, // TODO? + 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: 0.0, + lod_max_clamp: f32::MAX, compare: None, - anisotropy_clamp: None, + anisotropy_clamp: NonZeroU8::new( + data.map(|d| match d.aniso { + ETextureAnisotropicRatio::None => 0, + ETextureAnisotropicRatio::_1 => 1, + ETextureAnisotropicRatio::_2 => 2, + ETextureAnisotropicRatio::_4 => 4, + ETextureAnisotropicRatio::_8 => 8, + ETextureAnisotropicRatio::_16 => 16, + }) + .unwrap_or_default(), + ), + // anisotropy_clamp: NonZeroU8::new(8), border_color: None, } } @@ -280,7 +324,7 @@ impl SystemTab for ModelTab { type LoadParam = ( SCommands, SResMut>, - SResMut>, + SResMut>, SResMut>, SResMut>, SResMut>, @@ -327,15 +371,27 @@ impl SystemTab for ModelTab { match &data.data { CMaterialDataInner::Texture(texture) => { if let Some(usage) = &texture.usage { - sampler_descriptors - .insert(texture.id, sampler_descriptor_from_usage(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(textures) => { - for texture in &textures.textures { + CMaterialDataInner::LayeredTexture(layers) => { + for texture in &layers.textures { if let Some(usage) = &texture.usage { - sampler_descriptors - .insert(texture.id, sampler_descriptor_from_usage(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), + ); } } } @@ -494,14 +550,18 @@ impl SystemTab for ModelTab { // Build materials let mut material_handles = Vec::with_capacity(mtrl.materials.len()); for mat in &mtrl.materials { - let mut out_mat = StandardMaterial::default(); + let mut out_mat = CustomMaterial::default(); println!("Shader {}, unk {}", mat.shader_id, mat.unk_guid); let _ = server.load_untyped(format!("{}.MTRL", mat.shader_id)); for data in &mat.data { match data.data_id { EMaterialDataId::DIFT | EMaterialDataId::BCLR => match &data.data { CMaterialDataInner::Texture(texture) => { - out_mat.base_color_texture = texture_handles.get(&texture.id).cloned(); + 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.flags).unwrap_or_default(); } _ => { log::warn!( @@ -510,6 +570,41 @@ impl SystemTab for ModelTab { ); } }, + EMaterialDataId::BCRL => match &data.data { + CMaterialDataInner::LayeredTexture(layers) => { + // println!("CREATING BCRL!!!"); + 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.flags) + .unwrap_or_default(); + out_mat.base_color_uv_1 = layers.textures[1] + .usage + .as_ref() + .map(|u| u.flags) + .unwrap_or_default(); + out_mat.base_color_uv_2 = layers.textures[2] + .usage + .as_ref() + .map(|u| u.flags) + .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); @@ -520,8 +615,10 @@ impl SystemTab for ModelTab { ), }, EMaterialDataId::ICAN => match &data.data { - CMaterialDataInner::Texture(_texture) => { - // out_mat.emissive_texture = texture_handles.get(&texture.id).cloned(); + CMaterialDataInner::Texture(texture) => { + out_mat.emissive_texture = texture_handles.get(&texture.id).cloned(); + out_mat.emissive_uv = + texture.usage.as_ref().map(|u| u.flags).unwrap_or_default(); } _ => log::warn!( "Unsupported material data type for ICAN {:?}", @@ -529,14 +626,113 @@ impl SystemTab for ModelTab { ), }, EMaterialDataId::ICNC => match &data.data { - CMaterialDataInner::Color(_color) => { - // out_mat.emissive = Color::rgba(color.r, color.g, color.b, color.a); + 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().flags; + } + _ => { + log::warn!( + "Unsupported material data type for NMAP {:?}", + data.data_type + ); + } + }, + EMaterialDataId::NRML => match &data.data { + CMaterialDataInner::LayeredTexture(layers) => { + // println!("CREATING NRML!!!"); + 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.flags) + .unwrap_or_default(); + out_mat.normal_map_uv_1 = layers.textures[1] + .usage + .as_ref() + .map(|u| u.flags) + .unwrap_or_default(); + out_mat.normal_map_uv_2 = layers.textures[2] + .usage + .as_ref() + .map(|u| u.flags) + .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().flags; + } + _ => { + log::warn!( + "Unsupported material data type for METL {:?}", + data.data_type + ); + } + }, + EMaterialDataId::MTLL => match &data.data { + CMaterialDataInner::LayeredTexture(layers) => { + // println!("CREATING MTLL!!!"); + 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.flags) + .unwrap_or_default(); + out_mat.metallic_map_uv_1 = layers.textures[1] + .usage + .as_ref() + .map(|u| u.flags) + .unwrap_or_default(); + out_mat.metallic_map_uv_2 = layers.textures[2] + .usage + .as_ref() + .map(|u| u.flags) + .unwrap_or_default(); + } + _ => { + log::warn!( + "Unsupported material data type for MTLL {:?}", + data.data_type + ); + } + }, id => { log::warn!("Unsupported material data ID {id:?}"); } @@ -574,7 +770,7 @@ impl SystemTab for ModelTab { } entities.push(( commands - .spawn(PbrBundle { + .spawn(MaterialMeshBundle { mesh: meshes.add(out_mesh), material: material_handles[in_mesh.material_idx as usize].clone(), transform: Transform::from_translation((-aabb.center).into()), @@ -712,13 +908,19 @@ impl SystemTab for ModelTab { camera: Camera { viewport: Some(viewport), priority: state.render_layer as isize, + // TOOD bevy 0.10 + // hdr: true, ..default() }, + // TOOD bevy 0.10 + // tonemapping: Tonemapping::TonyMcMapFace, transform: loaded.camera_xf, ..default() }, + // TOOD bevy 0.10 + // BloomSettings::default(), RenderLayers::layer(state.render_layer), - TemporaryTag, + TemporaryLabel, )); commands.spawn(( DirectionalLightBundle { @@ -728,7 +930,7 @@ impl SystemTab for ModelTab { ..default() }, RenderLayers::layer(state.render_layer), - TemporaryTag, + TemporaryLabel, )); egui::Frame::group(ui.style()).show(ui, |ui| {