use crate::core::*;
use crate::renderer::*;
#[derive(Clone)]
pub struct DeferredPhysicalMaterial {
pub name: String,
pub albedo: Srgba,
pub albedo_texture: Option<Texture2DRef>,
pub metallic: f32,
pub roughness: f32,
pub metallic_roughness_texture: Option<Texture2DRef>,
pub occlusion_strength: f32,
pub occlusion_texture: Option<Texture2DRef>,
pub normal_scale: f32,
pub normal_texture: Option<Texture2DRef>,
pub render_states: RenderStates,
pub emissive: Srgba,
pub emissive_texture: Option<Texture2DRef>,
pub alpha_cutout: Option<f32>,
}
impl DeferredPhysicalMaterial {
pub fn new(context: &Context, cpu_material: &CpuMaterial) -> Self {
let albedo_texture =
cpu_material
.albedo_texture
.as_ref()
.map(|cpu_texture| match &cpu_texture.data {
TextureData::RgbU8(_) | TextureData::RgbaU8(_) => {
let mut cpu_texture = cpu_texture.clone();
cpu_texture.data.to_linear_srgb();
Texture2DRef::from_cpu_texture(context, &cpu_texture)
}
_ => Texture2DRef::from_cpu_texture(context, cpu_texture),
});
let metallic_roughness_texture =
if let Some(ref cpu_texture) = cpu_material.occlusion_metallic_roughness_texture {
Some(Texture2DRef::from_cpu_texture(context, cpu_texture))
} else {
cpu_material
.metallic_roughness_texture
.as_ref()
.map(|cpu_texture| Texture2DRef::from_cpu_texture(context, cpu_texture))
};
let occlusion_texture = if cpu_material.occlusion_metallic_roughness_texture.is_some() {
metallic_roughness_texture.clone()
} else {
cpu_material
.occlusion_texture
.as_ref()
.map(|cpu_texture| Texture2DRef::from_cpu_texture(context, cpu_texture))
};
let normal_texture = cpu_material
.normal_texture
.as_ref()
.map(|cpu_texture| Texture2DRef::from_cpu_texture(context, cpu_texture));
let emissive_texture =
cpu_material
.emissive_texture
.as_ref()
.map(|cpu_texture| match &cpu_texture.data {
TextureData::RgbU8(_) | TextureData::RgbaU8(_) => {
let mut cpu_texture = cpu_texture.clone();
cpu_texture.data.to_linear_srgb();
Texture2DRef::from_cpu_texture(context, &cpu_texture)
}
_ => Texture2DRef::from_cpu_texture(context, cpu_texture),
});
Self {
name: cpu_material.name.clone(),
albedo: cpu_material.albedo,
albedo_texture,
metallic: cpu_material.metallic,
roughness: cpu_material.roughness,
metallic_roughness_texture,
normal_texture,
normal_scale: cpu_material.normal_scale,
occlusion_texture,
occlusion_strength: cpu_material.occlusion_strength,
render_states: RenderStates::default(),
alpha_cutout: cpu_material.alpha_cutout,
emissive: cpu_material.emissive,
emissive_texture,
}
}
pub fn from_physical_material(physical_material: &PhysicalMaterial) -> Self {
Self {
name: physical_material.name.clone(),
albedo: physical_material.albedo,
albedo_texture: physical_material.albedo_texture.clone(),
metallic: physical_material.metallic,
roughness: physical_material.roughness,
metallic_roughness_texture: physical_material.metallic_roughness_texture.clone(),
normal_texture: physical_material.normal_texture.clone(),
normal_scale: physical_material.normal_scale,
occlusion_texture: physical_material.occlusion_texture.clone(),
occlusion_strength: physical_material.occlusion_strength,
render_states: RenderStates {
write_mask: WriteMask::default(),
blend: Blend::Disabled,
..physical_material.render_states
},
emissive: physical_material.emissive,
emissive_texture: physical_material.emissive_texture.clone(),
alpha_cutout: if physical_material.is_transparent {
Some(0.5)
} else {
None
},
}
}
pub fn lighting_pass(
context: &Context,
viewer: &dyn Viewer,
geometry_pass_color_texture: ColorTexture,
geometry_pass_depth_texture: DepthTexture,
lights: &[&dyn Light],
) {
apply_screen_effect(
context,
lighting_pass::LightingPassEffect {},
viewer,
lights,
Some(geometry_pass_color_texture),
Some(geometry_pass_depth_texture),
);
}
}
impl FromCpuMaterial for DeferredPhysicalMaterial {
fn from_cpu_material(context: &Context, cpu_material: &CpuMaterial) -> Self {
Self::new(context, cpu_material)
}
}
impl Material for DeferredPhysicalMaterial {
fn id(&self) -> EffectMaterialId {
EffectMaterialId::DeferredPhysicalMaterial(
self.albedo_texture.is_some(),
self.metallic_roughness_texture.is_some(),
self.occlusion_texture.is_some(),
self.normal_texture.is_some(),
self.emissive_texture.is_some(),
self.alpha_cutout.is_some(),
)
}
fn fragment_shader_source(&self, _lights: &[&dyn Light]) -> String {
let mut output = include_str!("../../core/shared.frag").to_string();
if self.albedo_texture.is_some()
|| self.metallic_roughness_texture.is_some()
|| self.normal_texture.is_some()
|| self.occlusion_texture.is_some()
|| self.emissive_texture.is_some()
|| self.alpha_cutout.is_some()
{
output.push_str("in vec2 uvs;\n");
if self.albedo_texture.is_some() {
output.push_str("#define USE_ALBEDO_TEXTURE;\n");
}
if self.metallic_roughness_texture.is_some() {
output.push_str("#define USE_METALLIC_ROUGHNESS_TEXTURE;\n");
}
if self.occlusion_texture.is_some() {
output.push_str("#define USE_OCCLUSION_TEXTURE;\n");
}
if self.normal_texture.is_some() {
output.push_str("#define USE_NORMAL_TEXTURE;\nin vec3 tang;\nin vec3 bitang;\n");
}
if self.emissive_texture.is_some() {
output.push_str("#define USE_EMISSIVE_TEXTURE;\n");
}
if let Some(alpha_cutout) = self.alpha_cutout {
output.push_str(
format!("#define ALPHACUT;\nfloat acut = {};", alpha_cutout).as_str(),
);
}
}
output.push_str(include_str!("shaders/deferred_physical_material.frag"));
output
}
fn use_uniforms(&self, program: &Program, _viewer: &dyn Viewer, _lights: &[&dyn Light]) {
program.use_uniform("metallic", self.metallic);
program.use_uniform("roughness", self.roughness);
program.use_uniform("albedo", self.albedo.to_linear_srgb());
program.use_uniform("emissive", self.emissive.to_linear_srgb());
if let Some(ref texture) = self.albedo_texture {
program.use_texture("albedoTexture", texture);
program.use_uniform("albedoTexTransform", texture.transformation);
}
if let Some(ref texture) = self.metallic_roughness_texture {
program.use_texture("metallicRoughnessTexture", texture);
program.use_uniform("metallicRoughnessTexTransform", texture.transformation);
}
if let Some(ref texture) = self.occlusion_texture {
program.use_uniform("occlusionStrength", self.occlusion_strength);
program.use_uniform("occlusionTexTransform", texture.transformation);
program.use_texture("occlusionTexture", texture);
}
if let Some(ref texture) = self.normal_texture {
program.use_uniform("normalScale", self.normal_scale);
program.use_uniform("normalTexTransform", texture.transformation);
program.use_texture("normalTexture", texture);
}
if program.requires_uniform("emissiveTexture") {
if let Some(ref texture) = self.emissive_texture {
program.use_uniform("emissiveTexTransform", texture.transformation);
program.use_texture("emissiveTexture", texture);
}
}
}
fn render_states(&self) -> RenderStates {
self.render_states
}
fn material_type(&self) -> MaterialType {
MaterialType::Deferred
}
}
impl Default for DeferredPhysicalMaterial {
fn default() -> Self {
Self {
name: "default".to_string(),
albedo: Srgba::WHITE,
albedo_texture: None,
metallic: 0.0,
roughness: 1.0,
metallic_roughness_texture: None,
normal_texture: None,
normal_scale: 1.0,
occlusion_texture: None,
occlusion_strength: 1.0,
render_states: RenderStates::default(),
alpha_cutout: None,
emissive: Srgba::BLACK,
emissive_texture: None,
}
}
}