use super::primitive::TextureRef;
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct MaterialDef {
#[serde(default)]
pub label: String,
pub base_color: [f32; 4],
#[serde(default)]
pub base_color_texture: Option<TextureRef>,
pub metallic: f32,
pub roughness: f32,
#[serde(default)]
pub metallic_roughness_texture: Option<TextureRef>,
pub emissive: [f32; 3],
#[serde(default)]
pub emissive_texture: Option<TextureRef>,
#[serde(default)]
pub normal_texture: Option<TextureRef>,
#[serde(default = "default_one")]
pub normal_scale: f32,
#[serde(default)]
pub occlusion_texture: Option<TextureRef>,
#[serde(default = "default_one")]
pub occlusion_strength: f32,
pub double_sided: bool,
pub vertex_colors_enabled: bool,
#[serde(default)]
pub alpha_mode: MaterialAlphaMode,
pub shading: MaterialShading,
#[serde(default)]
pub extensions: PbrExtensions,
}
fn default_one() -> f32 {
1.0
}
impl Default for MaterialDef {
fn default() -> Self {
Self {
label: String::new(),
base_color: [1.0, 1.0, 1.0, 1.0],
base_color_texture: None,
metallic: 0.0,
roughness: 0.7,
metallic_roughness_texture: None,
emissive: [0.0, 0.0, 0.0],
emissive_texture: None,
normal_texture: None,
normal_scale: 1.0,
occlusion_texture: None,
occlusion_strength: 1.0,
double_sided: false,
vertex_colors_enabled: false,
alpha_mode: MaterialAlphaMode::Opaque,
shading: MaterialShading::Pbr,
extensions: PbrExtensions::default(),
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum MaterialAlphaMode {
#[default]
Opaque,
Mask {
cutoff: f32,
},
Blend,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Copy)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum MaterialShading {
Pbr,
Unlit,
Toon {
diffuse_bands: u32,
rim_strength: f32,
#[serde(default = "default_specular_steps")]
specular_steps: u32,
#[serde(default = "default_shininess")]
shininess: f32,
#[serde(default = "default_rim_power")]
rim_power: f32,
},
FlipBook {
cols: u32,
rows: u32,
frame_count: u32,
fps: f32,
#[serde(default)]
time_offset: f32,
#[serde(default)]
mode: FlipBookPlayMode,
#[serde(default)]
flip_y: bool,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum FlipBookPlayMode {
#[default]
Loop,
PingPong,
Clamp,
Once,
}
fn default_specular_steps() -> u32 {
2
}
fn default_shininess() -> f32 {
32.0
}
fn default_rim_power() -> f32 {
2.0
}
#[derive(Clone, Copy, Debug, PartialEq, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case", default)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct PbrExtensions {
pub emissive_strength: Option<EmissiveStrengthExt>,
pub ior: Option<IorExt>,
pub specular: Option<SpecularExt>,
pub transmission: Option<TransmissionExt>,
pub diffuse_transmission: Option<DiffuseTransmissionExt>,
pub volume: Option<VolumeExt>,
pub clearcoat: Option<ClearcoatExt>,
pub sheen: Option<SheenExt>,
pub dispersion: Option<DispersionExt>,
pub anisotropy: Option<AnisotropyExt>,
pub iridescence: Option<IridescenceExt>,
}
macro_rules! ext_struct {
($(#[$m:meta])* $name:ident { $($field:ident : $ty:ty = $def:expr),* $(,)? }) => {
$(#[$m])*
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub struct $name { $(#[serde(default)] pub $field: $ty),* }
impl Default for $name {
fn default() -> Self { Self { $($field: $def),* } }
}
};
}
ext_struct!( EmissiveStrengthExt { strength: f32 = 2.0 });
ext_struct!( IorExt { ior: f32 = 1.5 });
ext_struct!( SpecularExt { factor: f32 = 1.0, color_factor: [f32; 3] = [1.0, 1.0, 1.0],
tex: Option<TextureRef> = None, color_tex: Option<TextureRef> = None });
ext_struct!( TransmissionExt { factor: f32 = 1.0, tex: Option<TextureRef> = None });
ext_struct!( DiffuseTransmissionExt { factor: f32 = 1.0, color_factor: [f32; 3] = [1.0, 1.0, 1.0],
tex: Option<TextureRef> = None, color_tex: Option<TextureRef> = None });
ext_struct!( VolumeExt { thickness_factor: f32 = 1.0, attenuation_distance: f32 = 1.0, attenuation_color: [f32; 3] = [1.0, 1.0, 1.0],
thickness_tex: Option<TextureRef> = None });
ext_struct!( ClearcoatExt { factor: f32 = 1.0, roughness_factor: f32 = 0.0, normal_scale: f32 = 1.0,
tex: Option<TextureRef> = None, roughness_tex: Option<TextureRef> = None, normal_tex: Option<TextureRef> = None });
ext_struct!( SheenExt { roughness_factor: f32 = 0.3, color_factor: [f32; 3] = [1.0, 1.0, 1.0],
color_tex: Option<TextureRef> = None, roughness_tex: Option<TextureRef> = None });
ext_struct!( DispersionExt { dispersion: f32 = 0.1 });
ext_struct!( AnisotropyExt { strength: f32 = 1.0, rotation: f32 = 0.0, tex: Option<TextureRef> = None });
ext_struct!( IridescenceExt { factor: f32 = 1.0, ior: f32 = 1.3, thickness_min: f32 = 100.0, thickness_max: f32 = 400.0,
tex: Option<TextureRef> = None, thickness_tex: Option<TextureRef> = None });
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProceduralTextureDef {
Checker {
width: u32,
height: u32,
cells_x: u32,
cells_y: u32,
color_a: [f32; 4],
color_b: [f32; 4],
},
Gradient {
width: u32,
height: u32,
color_a: [f32; 4],
color_b: [f32; 4],
horizontal: bool,
},
Noise {
width: u32,
height: u32,
seed: u32,
scale: f32,
},
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TextureDef {
Raster { display_name: String },
Procedural(ProceduralTextureDef),
}
pub const MESH_FILE_EXTENSION: &str = "mesh.bin";
pub fn mesh_asset_filename(asset_id: super::assets::AssetId) -> String {
format!("{}.{}", asset_id.0, MESH_FILE_EXTENSION)
}