use std::{
iter::{once, Chain, Once},
marker::PhantomData,
};
use gfx::Primitive;
use amethyst_assets::Handle;
use amethyst_core::nalgebra::{Matrix4, Point3, Rotation3, Translation3, Unit, Vector3};
use crate::{
error::Result,
types::{Factory, RawBuffer, Slice},
vertex::{Attributes, VertexFormat},
};
#[derive(Clone, Debug)]
pub struct VertexBuffer {
attrs: Attributes<'static>,
raw: RawBuffer,
}
#[doc(hidden)]
pub trait VertexData {
const ATTRIBUTES: Attributes<'static>;
fn len(&self) -> usize;
fn build(&self, factory: &mut Factory) -> Result<VertexBuffer>;
}
pub fn vertex_data<D, V>(data: D) -> (D, PhantomData<V>)
where
D: AsRef<[V]>,
V: VertexFormat,
{
(data, PhantomData)
}
impl<D, V> VertexData for (D, PhantomData<V>)
where
D: AsRef<[V]>,
V: VertexFormat,
{
const ATTRIBUTES: Attributes<'static> = V::ATTRIBUTES;
fn len(&self) -> usize {
self.0.as_ref().len()
}
fn build(&self, factory: &mut Factory) -> Result<VertexBuffer> {
use gfx::{
buffer::Role,
memory::{cast_slice, Bind},
Factory,
};
let verts = self.0.as_ref();
let slice = cast_slice(verts);
let stride = slice.len() / verts.len();
let role = Role::Vertex;
let bind = Bind::empty();
let vbuf = factory.create_buffer_immutable_raw(slice, stride, role, bind)?;
Ok(VertexBuffer {
attrs: V::ATTRIBUTES,
raw: vbuf,
})
}
}
#[doc(hidden)]
pub trait VertexDataSet {
type VertexBufferIter: Iterator<Item = VertexBuffer>;
fn len(&self) -> usize;
fn build(&self, factory: &mut Factory) -> Result<Self::VertexBufferIter>;
}
impl<H> VertexDataSet for (H, ())
where
H: VertexData,
{
type VertexBufferIter = Once<VertexBuffer>;
fn len(&self) -> usize {
self.0.len()
}
fn build(&self, factory: &mut Factory) -> Result<Self::VertexBufferIter> {
let (ref head, _) = *self;
Ok(once(head.build(factory)?))
}
}
impl<H, T> VertexDataSet for (H, T)
where
H: VertexData,
T: VertexDataSet,
{
type VertexBufferIter = Chain<Once<VertexBuffer>, T::VertexBufferIter>;
fn len(&self) -> usize {
use std::cmp::min;
min(self.0.len(), self.1.len())
}
fn build(&self, factory: &mut Factory) -> Result<Self::VertexBufferIter> {
let (ref head, ref tail) = *self;
Ok(once(head.build(factory)?).chain(tail.build(factory)?))
}
}
pub type MeshHandle = Handle<Mesh>;
#[derive(Clone, Debug)]
pub struct Mesh {
slice: Slice,
transform: Matrix4<f32>,
vbufs: Vec<VertexBuffer>,
}
impl Mesh {
pub fn build<D, V>(verts: D) -> MeshBuilder<((D, PhantomData<V>), ())>
where
D: AsRef<[V]>,
V: VertexFormat,
{
MeshBuilder::new(verts)
}
pub fn buffer(&self, attributes: Attributes<'_>) -> Option<&RawBuffer> {
for vbuf in self.vbufs.iter() {
let mut find = attributes.iter();
let mut next = find.next();
let mut i = 0;
let mut j = 0;
loop {
let attrs = vbuf.attrs;
match next {
Some(&attr) => {
if i == attrs.len() {
break;
} else if attrs[(i + j) % attrs.len()] == attr {
next = find.next();
j = i;
i = 0;
} else {
i += 1;
}
}
None => {
return Some(&vbuf.raw);
}
}
}
}
None
}
pub fn slice(&self) -> &Slice {
&self.slice
}
pub fn transform(&self) -> Matrix4<f32> {
self.transform
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct MeshBuilder<T> {
prim: Primitive,
transform: Matrix4<f32>,
vertices: T,
}
impl<D, V> MeshBuilder<((D, PhantomData<V>), ())>
where
D: AsRef<[V]>,
V: VertexFormat,
{
pub fn new(verts: D) -> Self {
assert!(check_attributes_are_sorted(V::ATTRIBUTES));
MeshBuilder {
prim: Primitive::TriangleList,
transform: Matrix4::identity(),
vertices: (vertex_data(verts), ()),
}
}
}
impl<T> MeshBuilder<T>
where
T: VertexDataSet,
{
pub fn with_buffer<D, V>(self, verts: D) -> MeshBuilder<((D, PhantomData<V>), T)>
where
D: AsRef<[V]>,
V: VertexFormat,
{
assert!(check_attributes_are_sorted(V::ATTRIBUTES));
MeshBuilder {
prim: self.prim,
transform: self.transform,
vertices: (vertex_data(verts), self.vertices),
}
}
pub fn with_prim_type(mut self, prim: Primitive) -> Self {
self.prim = prim;
self
}
pub fn with_position(mut self, pos: Point3<f32>) -> Self {
let trans = Translation3::new(pos.x, pos.y, pos.z);
self.transform *= trans.to_homogeneous();
self
}
pub fn with_rotation(mut self, axis: Unit<Vector3<f32>>, angle: f32) -> Self {
let rot = Rotation3::from_axis_angle(&axis, angle);
self.transform *= rot.to_homogeneous();
self
}
pub fn with_scale(mut self, val: f32) -> Self {
let scale = Matrix4::new_scaling(val);
self.transform *= scale;
self
}
pub fn with_transform<M: Into<Matrix4<f32>>>(mut self, mat: M) -> Self {
self.transform = mat.into();
self
}
pub fn build(self, fac: &mut Factory) -> Result<Mesh> {
use gfx::IndexBuffer;
let count = self.vertices.len();
let slice = Slice {
start: 0,
end: count as u32,
base_vertex: 0,
instances: None,
buffer: IndexBuffer::Auto,
};
Ok(Mesh {
slice,
transform: self.transform,
vbufs: self.vertices.build(fac)?.collect(),
})
}
}
fn check_attributes_are_sorted(attrs: Attributes<'_>) -> bool {
let mut last = 0;
for attr in attrs {
if last > attr.1.offset {
return false;
}
last = attr.1.offset;
}
true
}