use std::fmt;
use std::num::from_uint;
use std::cmp::Ordering;
use device::{PrimitiveType, ProgramHandle};
use device::shade::ProgramInfo;
use render::mesh;
use render::mesh::ToSlice;
use shade::{ParameterError, ShaderParam};
use render::state::DrawState;
#[derive(Clone, Debug)]
pub enum MeshError {
AttributeMissing(String),
AttributeType,
MeshLink(mesh::LinkError),
}
#[derive(Clone, Debug)]
pub enum BatchError {
Mesh(MeshError),
Parameters(ParameterError),
ContextFull,
}
pub fn link_mesh(mesh: &mesh::Mesh, pinfo: &ProgramInfo) -> Result<mesh::Link, MeshError> {
let mut indices = Vec::new();
for sat in pinfo.attributes.iter() {
match mesh.attributes.iter().enumerate()
.find(|&(_, a)| a.name == sat.name) {
Some((attrib_id, vat)) => match vat.format.elem_type.is_compatible(sat.base_type) {
Ok(_) => indices.push(attrib_id),
Err(_) => return Err(MeshError::AttributeType),
},
None => return Err(MeshError::AttributeMissing(sat.name.clone())),
}
}
mesh::Link::from_iter(indices.into_iter())
.map_err(|e| MeshError::MeshLink(e))
}
pub trait Batch {
fn get_data(&self) -> (&mesh::Mesh, &mesh::Link, &mesh::Slice, &ProgramHandle, &DrawState);
fn fill_params(&self, ::shade::ParamValues);
}
pub struct OwnedBatch<T: ShaderParam> {
mesh: mesh::Mesh,
mesh_link: mesh::Link,
pub slice: mesh::Slice,
pub param: T,
program: ProgramHandle,
param_link: T::Link,
pub state: DrawState,
}
impl<T: ShaderParam> OwnedBatch<T> {
pub fn new(mesh: mesh::Mesh, program: ProgramHandle, param: T)
-> Result<OwnedBatch<T>, BatchError> {
let slice = mesh.to_slice(PrimitiveType::TriangleList);
let mesh_link = match link_mesh(&mesh, program.get_info()) {
Ok(l) => l,
Err(e) => return Err(BatchError::Mesh(e)),
};
let param_link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
Ok(l) => l,
Err(e) => return Err(BatchError::Parameters(e)),
};
Ok(OwnedBatch {
mesh: mesh,
mesh_link: mesh_link,
slice: slice,
program: program,
param: param,
param_link: param_link,
state: DrawState::new(),
})
}
}
impl<T: ShaderParam> Batch for OwnedBatch<T> {
fn get_data(&self) -> (&mesh::Mesh, &mesh::Link, &mesh::Slice, &ProgramHandle, &DrawState) {
(&self.mesh, &self.mesh_link, &self.slice, &self.program, &self.state)
}
fn fill_params(&self, values: ::shade::ParamValues) {
self.param.fill_params(&self.param_link, values);
}
}
type Index = u16;
struct Id<T>(Index);
impl<T> Copy for Id<T> {}
impl<T> Id<T> {
fn unwrap(&self) -> Index {
let Id(i) = *self;
i
}
}
impl<T> fmt::Debug for Id<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Id(i) = *self;
write!(f, "Id({})", i)
}
}
impl<T> PartialEq for Id<T> {
fn eq(&self, other: &Id<T>) -> bool {
self.unwrap() == other.unwrap()
}
}
impl<T> Eq for Id<T> {}
impl<T> PartialOrd for Id<T> {
fn partial_cmp(&self, other: &Id<T>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for Id<T> {
fn cmp(&self, other: &Id<T>) -> Ordering {
self.unwrap().cmp(&other.unwrap())
}
}
struct Array<T> {
data: Vec<T>,
}
impl<T> Array<T> {
fn new() -> Array<T> {
Array {
data: Vec::new(),
}
}
fn get(&self, id: Id<T>) -> &T {
let Id(i) = id;
&self.data[i as usize]
}
}
impl<T: Clone + PartialEq> Array<T> {
fn find_or_insert(&mut self, value: &T) -> Option<Id<T>> {
match self.data.iter().position(|v| v == value) {
Some(i) => from_uint::<Index>(i).map(|id| Id(id)),
None => {
from_uint::<Index>(self.data.len()).map(|id| {
self.data.push(value.clone());
Id(id)
})
},
}
}
}
pub struct RefBatch<T: ShaderParam> {
mesh_id: Id<mesh::Mesh>,
mesh_link: mesh::Link,
pub slice: mesh::Slice,
program_id: Id<ProgramHandle>,
param_link: T::Link,
state_id: Id<DrawState>,
}
impl<T: ShaderParam> Copy for RefBatch<T> where T::Link: Copy {}
impl<T> fmt::Debug for RefBatch<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RefBatch(mesh: {:?}, slice: {:?}, program: {:?}, state: {:?})",
self.mesh_id, self.slice, self.program_id, self.state_id)
}
}
impl<T> PartialEq for RefBatch<T> {
fn eq(&self, other: &RefBatch<T>) -> bool {
self.program_id == other.program_id &&
self.state_id == other.state_id &&
self.mesh_id == other.mesh_id
}
}
impl<T> Eq for RefBatch<T> {}
impl<T> PartialOrd for RefBatch<T> {
fn partial_cmp(&self, other: &RefBatch<T>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for RefBatch<T> {
fn cmp(&self, other: &RefBatch<T>) -> Ordering {
(&self.program_id, &self.state_id, &self.mesh_id).cmp(
&(&other.program_id, &other.state_id, &other.mesh_id))
}
}
pub struct Context {
meshes: Array<mesh::Mesh>,
programs: Array<ProgramHandle>,
states: Array<DrawState>,
}
impl Context {
pub fn new() -> Context {
Context {
meshes: Array::new(),
programs: Array::new(),
states: Array::new(),
}
}
}
impl Context {
pub fn make_batch<T: ShaderParam>(&mut self,
program: &ProgramHandle,
mesh: &mesh::Mesh,
slice: mesh::Slice,
state: &DrawState)
-> Result<RefBatch<T>, BatchError> {
let mesh_link = match link_mesh(mesh, program.get_info()) {
Ok(l) => l,
Err(e) => return Err(BatchError::Mesh(e)),
};
let link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
Ok(l) => l,
Err(e) => return Err(BatchError::Parameters(e))
};
let mesh_id = match self.meshes.find_or_insert(mesh) {
Some(id) => id,
None => return Err(BatchError::ContextFull),
};
let program_id = match self.programs.find_or_insert(program) {
Some(id) => id,
None => return Err(BatchError::ContextFull),
};
let state_id = match self.states.find_or_insert(state) {
Some(id) => id,
None => return Err(BatchError::ContextFull),
};
Ok(RefBatch {
mesh_id: mesh_id,
mesh_link: mesh_link,
slice: slice,
program_id: program_id,
param_link: link,
state_id: state_id,
})
}
}
impl<'a, T: ShaderParam> Batch for (&'a RefBatch<T>, &'a T, &'a Context) {
fn get_data(&self) -> (&mesh::Mesh, &mesh::Link, &mesh::Slice, &ProgramHandle, &DrawState) {
let (b, _, ctx) = *self;
(ctx.meshes.get(b.mesh_id),
&b.mesh_link,
&b.slice,
ctx.programs.get(b.program_id),
ctx.states.get(b.state_id))
}
fn fill_params(&self, values: ::shade::ParamValues) {
let (b, data, _) = *self;
data.fill_params(&b.param_link, values);
}
}