#![warn(missing_docs)]
use crate::{
asset::ResourceState,
core::{
algebra::{Matrix2, Matrix3, Matrix4, Vector2, Vector3, Vector4},
color::Color,
sstorage::ImmutableString,
visitor::prelude::*,
},
engine::resource_manager::ResourceManager,
material::shader::{PropertyKind, SamplerFallback, Shader},
renderer::framework::framebuffer::DrawParameters,
resource::texture::Texture,
};
use fxhash::FxHashMap;
use std::ops::Deref;
pub mod shader;
#[derive(Debug, Visit, Clone)]
pub enum PropertyValue {
Float(f32),
FloatArray(Vec<f32>),
Int(i32),
IntArray(Vec<i32>),
UInt(u32),
UIntArray(Vec<u32>),
Vector2(Vector2<f32>),
Vector2Array(Vec<Vector2<f32>>),
Vector3(Vector3<f32>),
Vector3Array(Vec<Vector3<f32>>),
Vector4(Vector4<f32>),
Vector4Array(Vec<Vector4<f32>>),
Matrix2(Matrix2<f32>),
Matrix2Array(Vec<Matrix2<f32>>),
Matrix3(Matrix3<f32>),
Matrix3Array(Vec<Matrix3<f32>>),
Matrix4(Matrix4<f32>),
Matrix4Array(Vec<Matrix4<f32>>),
Bool(bool),
Color(Color),
Sampler {
value: Option<Texture>,
fallback: SamplerFallback,
},
}
macro_rules! define_as {
($(#[$meta:meta])* $name:ident = $variant:ident -> $ty:ty) => {
$(#[$meta])*
pub fn $name(&self) -> Option<$ty> {
if let PropertyValue::$variant(v) = self {
Some(*v)
} else {
None
}
}
};
}
macro_rules! define_as_ref {
($(#[$meta:meta])* $name:ident = $variant:ident -> $ty:ty) => {
$(#[$meta])*
pub fn $name(&self) -> Option<&$ty> {
if let PropertyValue::$variant(v) = self {
Some(v)
} else {
None
}
}
};
}
impl PropertyValue {
define_as!(
as_float = Float -> f32
);
define_as_ref!(
as_float_array = FloatArray -> [f32]
);
define_as!(
as_int = Int -> i32
);
define_as_ref!(
as_int_array = IntArray -> [i32]
);
define_as!(
as_uint = UInt -> u32
);
define_as_ref!(
as_uint_array = UIntArray -> [u32]
);
define_as!(
as_bool = Bool -> bool
);
define_as!(
as_color = Color -> Color
);
define_as!(
as_vector2 = Vector2 -> Vector2<f32>
);
define_as_ref!(
as_vector2_array = Vector2Array -> [Vector2<f32>]
);
define_as!(
as_vector3 = Vector3 -> Vector3<f32>
);
define_as_ref!(
as_vector3_array = Vector3Array -> [Vector3<f32>]
);
define_as!(
as_vector4 = Vector4 -> Vector4<f32>
);
define_as_ref!(
as_vector4_array = Vector4Array -> [Vector4<f32>]
);
define_as!(
as_matrix2 = Matrix2 -> Matrix2<f32>
);
define_as_ref!(
as_matrix2_array = Matrix2Array -> [Matrix2<f32>]
);
define_as!(
as_matrix3 = Matrix3 -> Matrix3<f32>
);
define_as_ref!(
as_matrix3_array = Matrix3Array -> [Matrix3<f32>]
);
define_as!(
as_matrix4 = Matrix4 -> Matrix4<f32>
);
define_as_ref!(
as_matrix4_array = Matrix4Array -> [Matrix4<f32>]
);
pub fn as_sampler(&self) -> Option<Texture> {
if let PropertyValue::Sampler { value, .. } = self {
value.clone()
} else {
None
}
}
}
impl Default for PropertyValue {
fn default() -> Self {
Self::Float(0.0)
}
}
#[derive(Default, Debug, Visit, Clone)]
pub struct Material {
shader: Shader,
draw_parameters: DrawParameters,
properties: FxHashMap<ImmutableString, PropertyValue>,
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum MaterialError {
#[error("Unable to find material property {}", property_name)]
NoSuchProperty {
property_name: String,
},
#[error(
"Attempt to set a value of wrong type to {} property. Expected: {:?}, given {:?}",
property_name,
expected,
given
)]
TypeMismatch {
property_name: String,
expected: PropertyValue,
given: PropertyValue,
},
}
impl Material {
pub fn standard() -> Self {
Self::from_shader(Shader::standard(), None)
}
pub fn standard_terrain() -> Self {
Self::from_shader(Shader::standard_terrain(), None)
}
pub fn from_shader(shader: Shader, resource_manager: Option<ResourceManager>) -> Self {
let data = shader.data_ref();
let mut property_values = FxHashMap::default();
for property_definition in data.definition.properties.iter() {
let value = match &property_definition.kind {
PropertyKind::Float(value) => PropertyValue::Float(*value),
PropertyKind::Int(value) => PropertyValue::Int(*value),
PropertyKind::UInt(value) => PropertyValue::UInt(*value),
PropertyKind::Vector2(value) => PropertyValue::Vector2(*value),
PropertyKind::Vector3(value) => PropertyValue::Vector3(*value),
PropertyKind::Vector4(value) => PropertyValue::Vector4(*value),
PropertyKind::Color { r, g, b, a } => {
PropertyValue::Color(Color::from_rgba(*r, *g, *b, *a))
}
PropertyKind::Matrix2(value) => PropertyValue::Matrix2(*value),
PropertyKind::Matrix3(value) => PropertyValue::Matrix3(*value),
PropertyKind::Matrix4(value) => PropertyValue::Matrix4(*value),
PropertyKind::Bool(value) => PropertyValue::Bool(*value),
PropertyKind::Sampler {
default,
fallback: usage,
} => PropertyValue::Sampler {
value: default.as_ref().and_then(|path| {
resource_manager.clone().map(|rm| rm.request_texture(path))
}),
fallback: *usage,
},
PropertyKind::FloatArray(value) => PropertyValue::FloatArray(value.clone()),
PropertyKind::IntArray(value) => PropertyValue::IntArray(value.clone()),
PropertyKind::UIntArray(value) => PropertyValue::UIntArray(value.clone()),
PropertyKind::Vector2Array(value) => PropertyValue::Vector2Array(value.clone()),
PropertyKind::Vector3Array(value) => PropertyValue::Vector3Array(value.clone()),
PropertyKind::Vector4Array(value) => PropertyValue::Vector4Array(value.clone()),
PropertyKind::Matrix2Array(value) => PropertyValue::Matrix2Array(value.clone()),
PropertyKind::Matrix3Array(value) => PropertyValue::Matrix3Array(value.clone()),
PropertyKind::Matrix4Array(value) => PropertyValue::Matrix4Array(value.clone()),
};
property_values.insert(ImmutableString::new(&property_definition.name), value);
}
drop(data);
Self {
shader,
draw_parameters: Default::default(),
properties: property_values,
}
}
pub(in crate) fn resolve(&mut self, resource_manager: ResourceManager) {
for value in self.properties.values_mut() {
if let PropertyValue::Sampler {
value: Some(texture),
..
} = value
{
let data = texture.state();
let path = data.path().to_path_buf();
match &*data {
ResourceState::LoadError { .. } => {
drop(data);
*texture = resource_manager.request_texture(path);
}
ResourceState::Ok(texture_state) => {
if !texture_state.is_procedural() {
drop(data);
*texture = resource_manager.request_texture(path);
}
}
ResourceState::Pending { .. } => {}
}
}
}
}
pub fn property_ref(&self, name: &ImmutableString) -> Option<&PropertyValue> {
self.properties.get(name)
}
pub fn set_property(
&mut self,
name: &ImmutableString,
new_value: PropertyValue,
) -> Result<(), MaterialError> {
if let Some(value) = self.properties.get_mut(name) {
match (value, new_value) {
(
PropertyValue::Sampler {
value: old_value,
fallback: old_fallback,
},
PropertyValue::Sampler { value, fallback },
) => {
*old_value = value;
*old_fallback = fallback;
}
(PropertyValue::Float(old_value), PropertyValue::Float(value)) => {
*old_value = value;
}
(PropertyValue::FloatArray(old_value), PropertyValue::FloatArray(value)) => {
*old_value = value;
}
(PropertyValue::Int(old_value), PropertyValue::Int(value)) => {
*old_value = value;
}
(PropertyValue::IntArray(old_value), PropertyValue::IntArray(value)) => {
*old_value = value;
}
(PropertyValue::Bool(old_value), PropertyValue::Bool(value)) => {
*old_value = value;
}
(PropertyValue::UInt(old_value), PropertyValue::UInt(value)) => {
*old_value = value;
}
(PropertyValue::UIntArray(old_value), PropertyValue::UIntArray(value)) => {
*old_value = value;
}
(PropertyValue::Vector2(old_value), PropertyValue::Vector2(value)) => {
*old_value = value;
}
(PropertyValue::Vector2Array(old_value), PropertyValue::Vector2Array(value)) => {
*old_value = value;
}
(PropertyValue::Vector3(old_value), PropertyValue::Vector3(value)) => {
*old_value = value;
}
(PropertyValue::Vector3Array(old_value), PropertyValue::Vector3Array(value)) => {
*old_value = value;
}
(PropertyValue::Vector4(old_value), PropertyValue::Vector4(value)) => {
*old_value = value;
}
(PropertyValue::Vector4Array(old_value), PropertyValue::Vector4Array(value)) => {
*old_value = value;
}
(PropertyValue::Matrix2(old_value), PropertyValue::Matrix2(value)) => {
*old_value = value;
}
(PropertyValue::Matrix2Array(old_value), PropertyValue::Matrix2Array(value)) => {
*old_value = value;
}
(PropertyValue::Matrix3(old_value), PropertyValue::Matrix3(value)) => {
*old_value = value;
}
(PropertyValue::Matrix3Array(old_value), PropertyValue::Matrix3Array(value)) => {
*old_value = value;
}
(PropertyValue::Matrix4(old_value), PropertyValue::Matrix4(value)) => {
*old_value = value;
}
(PropertyValue::Matrix4Array(old_value), PropertyValue::Matrix4Array(value)) => {
*old_value = value;
}
(PropertyValue::Color(old_value), PropertyValue::Color(value)) => {
*old_value = value;
}
(value, new_value) => {
return Err(MaterialError::TypeMismatch {
property_name: name.deref().to_owned(),
expected: value.clone(),
given: new_value,
})
}
}
Ok(())
} else {
Err(MaterialError::NoSuchProperty {
property_name: name.deref().to_owned(),
})
}
}
pub fn shader(&self) -> &Shader {
&self.shader
}
pub fn properties(&self) -> &FxHashMap<ImmutableString, PropertyValue> {
&self.properties
}
}