use std::mem;
use glsl_layout::*;
use amethyst_assets::AssetStorage;
use amethyst_core::{
nalgebra::Matrix4,
specs::prelude::{Join, Read, ReadStorage},
GlobalTransform,
};
use crate::{
cam::{ActiveCamera, Camera},
mesh::Mesh,
mtl::{Material, MaterialDefaults, TextureOffset},
pass::set_skinning_buffers,
pipe::{Effect, EffectBuilder},
skinning::JointTransforms,
tex::Texture,
types::Encoder,
vertex::Attributes,
Rgba,
};
pub(crate) enum TextureType {
Albedo,
Emission,
Normal,
Metallic,
Roughness,
AmbientOcclusion,
Caveat,
}
#[repr(C, align(16))]
#[derive(Clone, Copy, Debug, Uniform)]
pub(crate) struct ViewArgs {
proj: mat4,
view: mat4,
}
#[repr(C, align(16))]
#[derive(Clone, Copy, Debug, Uniform)]
pub(crate) struct VertexArgs {
proj: mat4,
view: mat4,
model: mat4,
rgba: vec4,
}
#[repr(C, align(16))]
#[derive(Clone, Copy, Debug, Uniform)]
pub(crate) struct TextureOffsetPod {
pub u_offset: vec2,
pub v_offset: vec2,
}
impl TextureOffsetPod {
pub(crate) fn from_offset(offset: &TextureOffset) -> Self {
TextureOffsetPod {
u_offset: [offset.u.0, offset.u.1].into(),
v_offset: [offset.v.0, offset.v.1].into(),
}
}
}
pub(crate) fn set_attribute_buffers(
effect: &mut Effect,
mesh: &Mesh,
attributes: &[Attributes<'static>],
) -> bool {
for attr in attributes.iter() {
match mesh.buffer(attr) {
Some(vbuf) => effect.data.vertex_bufs.push(vbuf.clone()),
None => {
error!(
"Required vertex attribute buffer with format {:?} missing in mesh",
attr
);
return false;
}
}
}
true
}
pub(crate) fn add_texture(effect: &mut Effect, texture: &Texture) {
effect.data.textures.push(texture.view().clone());
effect.data.samplers.push(texture.sampler().clone());
}
pub(crate) fn setup_textures(builder: &mut EffectBuilder<'_>, types: &[TextureType]) {
use self::TextureType::*;
for ty in types {
match *ty {
Albedo => builder.with_texture("albedo"),
Emission => builder.with_texture("emission"),
Normal => builder.with_texture("normal"),
Metallic => builder.with_texture("metallic"),
Roughness => builder.with_texture("roughness"),
AmbientOcclusion => builder.with_texture("ambient_occlusion"),
Caveat => builder.with_texture("caveat"),
};
}
setup_texture_offsets(builder, types);
}
pub(crate) fn add_textures(
effect: &mut Effect,
encoder: &mut Encoder,
storage: &AssetStorage<Texture>,
material: &Material,
default: &Material,
types: &[TextureType],
) {
use self::TextureType::*;
for ty in types {
let texture = match *ty {
Albedo => storage
.get(&material.albedo)
.or_else(|| storage.get(&default.albedo)),
Emission => storage
.get(&material.emission)
.or_else(|| storage.get(&default.emission)),
Normal => storage
.get(&material.normal)
.or_else(|| storage.get(&default.normal)),
Metallic => storage
.get(&material.metallic)
.or_else(|| storage.get(&default.metallic)),
Roughness => storage
.get(&material.roughness)
.or_else(|| storage.get(&default.roughness)),
AmbientOcclusion => storage
.get(&material.ambient_occlusion)
.or_else(|| storage.get(&default.ambient_occlusion)),
Caveat => storage
.get(&material.caveat)
.or_else(|| storage.get(&default.caveat)),
};
add_texture(effect, texture.expect("Texture missing in asset storage"));
}
set_texture_offsets(effect, encoder, material, types);
}
pub(crate) fn setup_texture_offsets(builder: &mut EffectBuilder<'_>, types: &[TextureType]) {
use self::TextureType::*;
for ty in types {
match *ty {
Albedo => builder.with_raw_constant_buffer(
"AlbedoOffset",
mem::size_of::<<TextureOffsetPod as Uniform>::Std140>(),
1,
),
Emission => builder.with_raw_constant_buffer(
"EmissionOffset",
mem::size_of::<<TextureOffsetPod as Uniform>::Std140>(),
1,
),
Normal => builder.with_raw_constant_buffer(
"NormalOffset",
mem::size_of::<<TextureOffsetPod as Uniform>::Std140>(),
1,
),
Metallic => builder.with_raw_constant_buffer(
"MetallicOffset",
mem::size_of::<<TextureOffsetPod as Uniform>::Std140>(),
1,
),
Roughness => builder.with_raw_constant_buffer(
"RoughnessOffset",
mem::size_of::<<TextureOffsetPod as Uniform>::Std140>(),
1,
),
AmbientOcclusion => builder.with_raw_constant_buffer(
"AmbientOcclusionOffset",
mem::size_of::<<TextureOffsetPod as Uniform>::Std140>(),
1,
),
Caveat => builder.with_raw_constant_buffer(
"CaveatOffset",
mem::size_of::<<TextureOffsetPod as Uniform>::Std140>(),
1,
),
};
}
}
pub(crate) fn set_texture_offsets(
effect: &mut Effect,
encoder: &mut Encoder,
material: &Material,
types: &[TextureType],
) {
use self::TextureType::*;
for ty in types {
match *ty {
Albedo => effect.update_constant_buffer(
"AlbedoOffset",
&TextureOffsetPod::from_offset(&material.albedo_offset).std140(),
encoder,
),
Emission => effect.update_constant_buffer(
"EmissionOffset",
&TextureOffsetPod::from_offset(&material.emission_offset).std140(),
encoder,
),
Normal => effect.update_constant_buffer(
"NormalOffset",
&TextureOffsetPod::from_offset(&material.normal_offset).std140(),
encoder,
),
Metallic => effect.update_constant_buffer(
"MetallicOffset",
&TextureOffsetPod::from_offset(&material.metallic_offset).std140(),
encoder,
),
Roughness => effect.update_constant_buffer(
"RoughnessOffset",
&TextureOffsetPod::from_offset(&material.roughness_offset).std140(),
encoder,
),
AmbientOcclusion => effect.update_constant_buffer(
"AmbientOcclusionOffset",
&TextureOffsetPod::from_offset(&material.ambient_occlusion_offset).std140(),
encoder,
),
Caveat => effect.update_constant_buffer(
"CaveatOffset",
&TextureOffsetPod::from_offset(&material.caveat_offset).std140(),
encoder,
),
};
}
}
pub(crate) fn setup_vertex_args(builder: &mut EffectBuilder<'_>) {
builder.with_raw_constant_buffer(
"VertexArgs",
mem::size_of::<<VertexArgs as Uniform>::Std140>(),
1,
);
}
pub fn set_vertex_args(
effect: &mut Effect,
encoder: &mut Encoder,
camera: Option<(&Camera, &GlobalTransform)>,
global: &GlobalTransform,
rgba: Rgba,
) {
let vertex_args = camera
.as_ref()
.map(|&(ref cam, ref transform)| {
let proj: [[f32; 4]; 4] = cam.proj.into();
let view: [[f32; 4]; 4] = transform
.0
.try_inverse()
.expect("Unable to get inverse of camera transform")
.into();
let model: [[f32; 4]; 4] = global.0.into();
VertexArgs {
proj: proj.into(),
view: view.into(),
model: model.into(),
rgba: rgba.into(),
}
})
.unwrap_or_else(|| {
let proj: [[f32; 4]; 4] = Matrix4::identity().into();
let view: [[f32; 4]; 4] = Matrix4::identity().into();
let model: [[f32; 4]; 4] = global.0.into();
VertexArgs {
proj: proj.into(),
view: view.into(),
model: model.into(),
rgba: rgba.into(),
}
});
effect.update_constant_buffer("VertexArgs", &vertex_args.std140(), encoder);
}
pub fn set_view_args(
effect: &mut Effect,
encoder: &mut Encoder,
camera: Option<(&Camera, &GlobalTransform)>,
) {
let view_args = camera
.as_ref()
.map(|&(ref cam, ref transform)| {
let proj: [[f32; 4]; 4] = cam.proj.into();
let view: [[f32; 4]; 4] = transform
.0
.try_inverse()
.expect("Unable to get inverse of camera transform")
.into();
ViewArgs {
proj: proj.into(),
view: view.into(),
}
})
.unwrap_or_else(|| {
let identity: [[f32; 4]; 4] = Matrix4::identity().into();
ViewArgs {
proj: identity.clone().into(),
view: identity.into(),
}
});
effect.update_constant_buffer("ViewArgs", &view_args.std140(), encoder);
}
pub(crate) fn draw_mesh(
encoder: &mut Encoder,
effect: &mut Effect,
skinning: bool,
mesh: Option<&Mesh>,
joint: Option<&JointTransforms>,
tex_storage: &AssetStorage<Texture>,
material: Option<&Material>,
material_defaults: &MaterialDefaults,
rgba: Option<&Rgba>,
camera: Option<(&Camera, &GlobalTransform)>,
global: Option<&GlobalTransform>,
attributes: &[Attributes<'static>],
textures: &[TextureType],
) {
let (mesh, material, global) = match (mesh, material, global) {
(Some(v1), Some(v2), Some(v3)) => (v1, v2, v3),
_ => return,
};
if !set_attribute_buffers(effect, mesh, attributes)
|| (skinning && !set_skinning_buffers(effect, mesh))
{
effect.clear();
return;
}
set_vertex_args(
effect,
encoder,
camera,
global,
rgba.cloned().unwrap_or(Rgba::WHITE),
);
if skinning {
if let Some(joint) = joint {
effect.update_buffer("JointTransforms", &joint.matrices[..], encoder);
}
}
add_textures(
effect,
encoder,
&tex_storage,
material,
&material_defaults.0,
textures,
);
effect.draw(mesh.slice(), encoder);
effect.clear();
}
pub fn get_camera<'a>(
active: Read<'a, ActiveCamera>,
camera: &'a ReadStorage<'a, Camera>,
global: &'a ReadStorage<'a, GlobalTransform>,
) -> Option<(&'a Camera, &'a GlobalTransform)> {
active
.entity
.and_then(|entity| {
let cam = camera.get(entity);
let transform = global.get(entity);
cam.into_iter().zip(transform.into_iter()).next()
})
.or_else(|| (camera, global).join().next())
}