use crate::ecs::asset_id::{MaterialId, TextureId};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MaterialRef {
pub name: String,
#[serde(skip)]
pub id: Option<MaterialId>,
}
impl MaterialRef {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
id: None,
}
}
pub fn with_id(name: impl Into<String>, id: MaterialId) -> Self {
Self {
name: name.into(),
id: Some(id),
}
}
}
impl Default for MaterialRef {
fn default() -> Self {
Self {
name: "Default".to_string(),
id: None,
}
}
}
impl From<String> for MaterialRef {
fn from(name: String) -> Self {
Self { name, id: None }
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct MaterialVariants {
pub default_material_name: String,
pub mappings: Vec<MaterialVariantMapping>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MaterialVariantMapping {
pub variant_names: Vec<String>,
pub material_name: String,
}
impl MaterialVariants {
pub fn material_for_variant(&self, variant_name: &str) -> &str {
for mapping in &self.mappings {
if mapping.variant_names.iter().any(|n| n == variant_name) {
return &mapping.material_name;
}
}
&self.default_material_name
}
}
impl From<&str> for MaterialRef {
fn from(name: &str) -> Self {
Self {
name: name.to_string(),
id: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct TextureTransform {
pub offset: [f32; 2],
pub rotation: f32,
pub scale: [f32; 2],
pub uv_set: u32,
}
impl Default for TextureTransform {
fn default() -> Self {
Self {
offset: [0.0, 0.0],
rotation: 0.0,
scale: [1.0, 1.0],
uv_set: 0,
}
}
}
impl TextureTransform {
pub const IDENTITY: Self = Self {
offset: [0.0, 0.0],
rotation: 0.0,
scale: [1.0, 1.0],
uv_set: 0,
};
pub fn to_packed(&self) -> ([f32; 3], [f32; 3]) {
let cos_r = self.rotation.cos();
let sin_r = self.rotation.sin();
let m00 = cos_r * self.scale[0];
let m01 = sin_r * self.scale[1];
let m02 = self.offset[0];
let m10 = -sin_r * self.scale[0];
let m11 = cos_r * self.scale[1];
let m12 = self.offset[1];
([m00, m01, m02], [m10, m11, m12])
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
pub enum AlphaMode {
#[default]
Opaque,
Mask,
Blend,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Material {
pub base_color: [f32; 4],
pub emissive_factor: [f32; 3],
pub alpha_mode: AlphaMode,
pub alpha_cutoff: f32,
#[serde(default = "default_blend_opaque_alpha_threshold")]
pub blend_opaque_alpha_threshold: f32,
pub base_texture: Option<String>,
#[serde(default)]
pub base_texture_transform: TextureTransform,
pub emissive_texture: Option<String>,
#[serde(default)]
pub emissive_texture_transform: TextureTransform,
pub normal_texture: Option<String>,
#[serde(default)]
pub normal_texture_transform: TextureTransform,
#[serde(default = "default_normal_scale")]
pub normal_scale: f32,
#[serde(default)]
pub normal_map_flip_y: bool,
#[serde(default)]
pub normal_map_two_component: bool,
pub metallic_roughness_texture: Option<String>,
#[serde(default)]
pub metallic_roughness_texture_transform: TextureTransform,
pub occlusion_texture: Option<String>,
#[serde(default)]
pub occlusion_texture_transform: TextureTransform,
#[serde(default = "default_occlusion_strength")]
pub occlusion_strength: f32,
pub roughness: f32,
pub metallic: f32,
pub unlit: bool,
#[serde(default)]
pub double_sided: bool,
#[serde(default)]
pub transmission_factor: f32,
#[serde(default)]
pub transmission_texture: Option<String>,
#[serde(default)]
pub transmission_texture_transform: TextureTransform,
#[serde(default)]
pub thickness: f32,
#[serde(default)]
pub thickness_texture: Option<String>,
#[serde(default)]
pub thickness_texture_transform: TextureTransform,
#[serde(default = "default_attenuation_color")]
pub attenuation_color: [f32; 3],
#[serde(default)]
pub attenuation_distance: f32,
#[serde(default = "default_ior")]
pub ior: f32,
#[serde(default = "default_specular_factor")]
pub specular_factor: f32,
#[serde(default = "default_specular_color_factor")]
pub specular_color_factor: [f32; 3],
#[serde(default)]
pub specular_texture: Option<String>,
#[serde(default)]
pub specular_texture_transform: TextureTransform,
#[serde(default)]
pub specular_color_texture: Option<String>,
#[serde(default)]
pub specular_color_texture_transform: TextureTransform,
#[serde(default = "default_emissive_strength")]
pub emissive_strength: f32,
#[serde(default)]
pub diffuse_transmission_factor: f32,
#[serde(default)]
pub diffuse_transmission_texture: Option<String>,
#[serde(default)]
pub diffuse_transmission_texture_transform: TextureTransform,
#[serde(default = "default_diffuse_transmission_color")]
pub diffuse_transmission_color_factor: [f32; 3],
#[serde(default)]
pub diffuse_transmission_color_texture: Option<String>,
#[serde(default)]
pub diffuse_transmission_color_texture_transform: TextureTransform,
#[serde(default)]
pub dispersion: f32,
#[serde(default)]
pub anisotropy_strength: f32,
#[serde(default)]
pub anisotropy_rotation: f32,
#[serde(default)]
pub anisotropy_texture: Option<String>,
#[serde(default)]
pub anisotropy_texture_transform: TextureTransform,
#[serde(default)]
pub iridescence_factor: f32,
#[serde(default)]
pub iridescence_texture: Option<String>,
#[serde(default)]
pub iridescence_texture_transform: TextureTransform,
#[serde(default = "default_iridescence_ior")]
pub iridescence_ior: f32,
#[serde(default = "default_iridescence_thickness_min")]
pub iridescence_thickness_minimum: f32,
#[serde(default = "default_iridescence_thickness_max")]
pub iridescence_thickness_maximum: f32,
#[serde(default)]
pub iridescence_thickness_texture: Option<String>,
#[serde(default)]
pub iridescence_thickness_texture_transform: TextureTransform,
#[serde(default)]
pub sheen_color_factor: [f32; 3],
#[serde(default)]
pub sheen_color_texture: Option<String>,
#[serde(default)]
pub sheen_color_texture_transform: TextureTransform,
#[serde(default)]
pub sheen_roughness_factor: f32,
#[serde(default)]
pub sheen_roughness_texture: Option<String>,
#[serde(default)]
pub sheen_roughness_texture_transform: TextureTransform,
#[serde(default)]
pub clearcoat_factor: f32,
#[serde(default)]
pub clearcoat_texture: Option<String>,
#[serde(default)]
pub clearcoat_texture_transform: TextureTransform,
#[serde(default)]
pub clearcoat_roughness_factor: f32,
#[serde(default)]
pub clearcoat_roughness_texture: Option<String>,
#[serde(default)]
pub clearcoat_roughness_texture_transform: TextureTransform,
#[serde(default)]
pub clearcoat_normal_texture: Option<String>,
#[serde(default)]
pub clearcoat_normal_texture_transform: TextureTransform,
#[serde(default = "default_normal_scale")]
pub clearcoat_normal_scale: f32,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct MaterialTextureIds {
pub base: Option<TextureId>,
pub emissive: Option<TextureId>,
pub normal: Option<TextureId>,
pub metallic_roughness: Option<TextureId>,
pub occlusion: Option<TextureId>,
pub transmission: Option<TextureId>,
pub thickness: Option<TextureId>,
pub specular: Option<TextureId>,
pub specular_color: Option<TextureId>,
pub diffuse_transmission: Option<TextureId>,
pub diffuse_transmission_color: Option<TextureId>,
pub anisotropy: Option<TextureId>,
pub iridescence: Option<TextureId>,
pub iridescence_thickness: Option<TextureId>,
pub sheen_color: Option<TextureId>,
pub sheen_roughness: Option<TextureId>,
pub clearcoat: Option<TextureId>,
pub clearcoat_roughness: Option<TextureId>,
pub clearcoat_normal: Option<TextureId>,
}
fn default_blend_opaque_alpha_threshold() -> f32 {
0.99
}
fn default_normal_scale() -> f32 {
1.0
}
fn default_occlusion_strength() -> f32 {
1.0
}
fn default_attenuation_color() -> [f32; 3] {
[1.0, 1.0, 1.0]
}
fn default_ior() -> f32 {
1.5
}
fn default_specular_factor() -> f32 {
1.0
}
fn default_specular_color_factor() -> [f32; 3] {
[1.0, 1.0, 1.0]
}
fn default_emissive_strength() -> f32 {
1.0
}
fn default_diffuse_transmission_color() -> [f32; 3] {
[1.0, 1.0, 1.0]
}
fn default_iridescence_ior() -> f32 {
1.3
}
fn default_iridescence_thickness_min() -> f32 {
100.0
}
fn default_iridescence_thickness_max() -> f32 {
400.0
}
impl Material {
pub fn is_transparent(&self) -> bool {
matches!(self.alpha_mode, AlphaMode::Mask | AlphaMode::Blend)
}
pub fn texture_names(&self) -> impl Iterator<Item = &str> {
[
self.base_texture.as_deref(),
self.emissive_texture.as_deref(),
self.normal_texture.as_deref(),
self.metallic_roughness_texture.as_deref(),
self.occlusion_texture.as_deref(),
self.transmission_texture.as_deref(),
self.thickness_texture.as_deref(),
self.specular_texture.as_deref(),
self.specular_color_texture.as_deref(),
self.diffuse_transmission_texture.as_deref(),
self.diffuse_transmission_color_texture.as_deref(),
self.anisotropy_texture.as_deref(),
self.iridescence_texture.as_deref(),
self.iridescence_thickness_texture.as_deref(),
self.sheen_color_texture.as_deref(),
self.sheen_roughness_texture.as_deref(),
self.clearcoat_texture.as_deref(),
self.clearcoat_roughness_texture.as_deref(),
self.clearcoat_normal_texture.as_deref(),
]
.into_iter()
.flatten()
}
}
impl Default for Material {
fn default() -> Self {
Self {
base_color: [0.7, 0.7, 0.7, 1.0],
emissive_factor: [0.0, 0.0, 0.0],
alpha_mode: AlphaMode::Opaque,
alpha_cutoff: 0.5,
blend_opaque_alpha_threshold: 0.99,
base_texture: None,
base_texture_transform: TextureTransform::IDENTITY,
emissive_texture: None,
emissive_texture_transform: TextureTransform::IDENTITY,
normal_texture: None,
normal_texture_transform: TextureTransform::IDENTITY,
normal_scale: 1.0,
normal_map_flip_y: false,
normal_map_two_component: false,
metallic_roughness_texture: None,
metallic_roughness_texture_transform: TextureTransform::IDENTITY,
occlusion_texture: None,
occlusion_texture_transform: TextureTransform::IDENTITY,
occlusion_strength: 1.0,
roughness: 0.5,
metallic: 0.0,
unlit: false,
double_sided: false,
transmission_factor: 0.0,
transmission_texture: None,
transmission_texture_transform: TextureTransform::IDENTITY,
thickness: 0.0,
thickness_texture: None,
thickness_texture_transform: TextureTransform::IDENTITY,
attenuation_color: [1.0, 1.0, 1.0],
attenuation_distance: 0.0,
ior: 1.5,
specular_factor: 1.0,
specular_color_factor: [1.0, 1.0, 1.0],
specular_texture: None,
specular_texture_transform: TextureTransform::IDENTITY,
specular_color_texture: None,
specular_color_texture_transform: TextureTransform::IDENTITY,
emissive_strength: 1.0,
diffuse_transmission_factor: 0.0,
diffuse_transmission_texture: None,
diffuse_transmission_texture_transform: TextureTransform::IDENTITY,
diffuse_transmission_color_factor: [1.0, 1.0, 1.0],
diffuse_transmission_color_texture: None,
diffuse_transmission_color_texture_transform: TextureTransform::IDENTITY,
dispersion: 0.0,
anisotropy_strength: 0.0,
anisotropy_rotation: 0.0,
anisotropy_texture: None,
anisotropy_texture_transform: TextureTransform::IDENTITY,
iridescence_factor: 0.0,
iridescence_texture: None,
iridescence_texture_transform: TextureTransform::IDENTITY,
iridescence_ior: 1.3,
iridescence_thickness_minimum: 100.0,
iridescence_thickness_maximum: 400.0,
iridescence_thickness_texture: None,
iridescence_thickness_texture_transform: TextureTransform::IDENTITY,
sheen_color_factor: [0.0, 0.0, 0.0],
sheen_color_texture: None,
sheen_color_texture_transform: TextureTransform::IDENTITY,
sheen_roughness_factor: 0.0,
sheen_roughness_texture: None,
sheen_roughness_texture_transform: TextureTransform::IDENTITY,
clearcoat_factor: 0.0,
clearcoat_texture: None,
clearcoat_texture_transform: TextureTransform::IDENTITY,
clearcoat_roughness_factor: 0.0,
clearcoat_roughness_texture: None,
clearcoat_roughness_texture_transform: TextureTransform::IDENTITY,
clearcoat_normal_texture: None,
clearcoat_normal_texture_transform: TextureTransform::IDENTITY,
clearcoat_normal_scale: 1.0,
}
}
}