use gl;
use gl::types::*;
use std::error::Error;
use std::fmt;
use std::marker::PhantomData;
use std::mem::size_of;
use std::ops::{Range, RangeFull, RangeFrom, RangeTo};
use std::os::raw::c_void;
use std::ptr;
use buffer::{Buffer, BufferError, BufferSlice, BufferSliceMut, RawBuffer};
use context::GraphicsContext;
use vertex::{Dim, Type, Vertex, VertexComponentFormat};
#[derive(Copy, Clone, Debug)]
pub enum Mode {
Point,
Line,
LineStrip,
Triangle,
TriangleFan,
TriangleStrip
}
#[derive(Debug, Eq, PartialEq)]
pub enum TessMapError {
VertexBufferMapFailed(BufferError),
ForbiddenAttributelessMapping
}
impl fmt::Display for TessMapError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
TessMapError::VertexBufferMapFailed(ref e) => {
write!(f, "cannot map tessallation buffer: {}", e)
}
TessMapError::ForbiddenAttributelessMapping => {
f.write_str("cannot map an attributeless buffer")
}
}
}
}
impl Error for TessMapError {
fn cause(&self) -> Option<&Error> {
match *self {
TessMapError::VertexBufferMapFailed(ref e) => Some(e),
_ => None
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum TessVertices<'a, T> where T: 'a + Vertex {
Fill(&'a [T]),
Reserve(usize)
}
impl<'a, T> From<&'a [T]> for TessVertices<'a, T> where T: 'a + Vertex {
fn from(slice: &'a [T]) -> Self {
TessVertices::Fill(slice)
}
}
pub struct Tess<V> {
mode: GLenum,
vert_nb: usize,
vao: GLenum,
vbo: Option<RawBuffer>, ibo: Option<RawBuffer>,
_v: PhantomData<V>
}
impl<V> Tess<V> where V: Vertex {
pub fn new<'a, C, W, I>(ctx: &mut C, mode: Mode, vertices: W, indices: I) -> Self
where C: GraphicsContext,
TessVertices<'a, V>: From<W>,
V: 'a + Vertex,
I: Into<Option<&'a[u32]>> {
let vertices = vertices.into();
let mut vao: GLuint = 0;
let vert_nb = match vertices {
TessVertices::Fill(slice) => slice.len(),
TessVertices::Reserve(nb) => nb
};
unsafe {
gl::GenVertexArrays(1, &mut vao);
ctx.state().borrow_mut().bind_vertex_array(vao);
let vertex_buffer = Buffer::new(ctx, vert_nb);
if let TessVertices::Fill(verts) = vertices {
vertex_buffer.fill(verts).unwrap();
}
let raw_vbo = vertex_buffer.to_raw();
ctx.state().borrow_mut().bind_array_buffer(raw_vbo.handle()); set_vertex_pointers(&V::vertex_format());
if let Some(indices) = indices.into() {
let ind_nb = indices.len();
let index_buffer = Buffer::new(ctx, ind_nb);
index_buffer.fill(indices).unwrap();
let raw_ibo = index_buffer.to_raw();
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, raw_ibo.handle());
ctx.state().borrow_mut().bind_vertex_array(vao);
Tess {
mode: opengl_mode(mode),
vert_nb: ind_nb,
vao: vao,
vbo: Some(raw_vbo),
ibo: Some(raw_ibo),
_v: PhantomData
}
} else {
ctx.state().borrow_mut().bind_vertex_array(vao);
Tess {
mode: opengl_mode(mode),
vert_nb: vert_nb,
vao: vao,
vbo: Some(raw_vbo),
ibo: None,
_v: PhantomData
}
}
}
}
fn render<C>(
&self,
ctx: &mut C,
start_index: usize,
vert_nb: usize,
inst_nb: usize
)
where C: GraphicsContext {
let vert_nb = vert_nb as GLsizei;
let inst_nb = inst_nb as GLsizei;
unsafe {
ctx.state().borrow_mut().bind_vertex_array(self.vao);
if self.ibo.is_some() { let first = (size_of::<u32>() * start_index) as *const c_void;
if inst_nb == 1 {
gl::DrawElements(self.mode, vert_nb, gl::UNSIGNED_INT, first);
} else if inst_nb > 1 {
gl::DrawElementsInstanced(self.mode, vert_nb, gl::UNSIGNED_INT, first, inst_nb);
} else {
panic!("cannot index-render 0 instance");
}
} else { let first = start_index as GLint;
if inst_nb == 1 {
gl::DrawArrays(self.mode, first, vert_nb);
} else if inst_nb > 1 {
gl::DrawArraysInstanced(self.mode, first, vert_nb, inst_nb);
} else {
panic!("cannot render 0 instance");
}
}
}
}
pub fn as_slice(&self) -> Result<BufferSlice<V>, TessMapError> {
self.vbo.as_ref()
.ok_or(TessMapError::ForbiddenAttributelessMapping)
.and_then(|raw| RawBuffer::as_slice(raw).map_err(TessMapError::VertexBufferMapFailed))
}
pub fn as_slice_mut<C>(&mut self) -> Result<BufferSliceMut<V>, TessMapError> {
self.vbo.as_mut()
.ok_or(TessMapError::ForbiddenAttributelessMapping)
.and_then(|raw| RawBuffer::as_slice_mut(raw).map_err(TessMapError::VertexBufferMapFailed))
}
}
impl Tess<()> {
pub fn attributeless<C>(
ctx: &mut C,
mode: Mode,
vert_nb: usize
) -> Self
where C: GraphicsContext {
let mut gfx_state = ctx.state().borrow_mut();
let mut vao = 0;
unsafe {
gl::GenVertexArrays(1, &mut vao);
gfx_state.bind_vertex_array(vao);
gfx_state.bind_vertex_array(0);
Tess {
mode: opengl_mode(mode),
vert_nb: vert_nb,
vao: vao,
vbo: None,
ibo: None,
_v: PhantomData
}
}
}
}
impl<V> Drop for Tess<V> {
fn drop(&mut self) {
unsafe {
gl::DeleteVertexArrays(1, &self.vao);
}
}
}
fn set_vertex_pointers(formats: &[VertexComponentFormat]) {
let offsets = aligned_offsets(formats);
let vertex_weight = offset_based_vertex_weight(formats, &offsets) as GLsizei;
for (i, (format, off)) in formats.iter().zip(offsets).enumerate() {
set_component_format(i as u32, vertex_weight, off, format);
}
}
fn aligned_offsets(formats: &[VertexComponentFormat]) -> Vec<usize> {
let mut offsets = Vec::with_capacity(formats.len());
let mut off = 0;
for f in formats {
off = off_align(off, f.align); offsets.push(off);
off += component_weight(f); }
offsets
}
#[inline]
fn off_align(off: usize, align: usize) -> usize {
let a = align - 1;
(off + a) & !a
}
fn component_weight(f: &VertexComponentFormat) -> usize {
dim_as_size(&f.dim) as usize * f.unit_size
}
fn dim_as_size(d: &Dim) -> GLint {
match *d {
Dim::Dim1 => 1,
Dim::Dim2 => 2,
Dim::Dim3 => 3,
Dim::Dim4 => 4
}
}
fn offset_based_vertex_weight(formats: &[VertexComponentFormat], offsets: &[usize]) -> usize {
if formats.is_empty() || offsets.is_empty() {
return 0;
}
off_align(offsets[offsets.len() - 1] + component_weight(&formats[formats.len() - 1]), formats[0].align)
}
fn set_component_format(i: u32, stride: GLsizei, off: usize, f: &VertexComponentFormat) {
match f.comp_type {
Type::Floating => {
unsafe {
gl::VertexAttribPointer(i as GLuint, dim_as_size(&f.dim), opengl_sized_type(&f), gl::FALSE, stride, ptr::null::<c_void>().offset(off as isize));
}
},
Type::Integral | Type::Unsigned | Type::Boolean => {
unsafe {
gl::VertexAttribIPointer(i as GLuint, dim_as_size(&f.dim), opengl_sized_type(&f), stride, ptr::null::<c_void>().offset(off as isize));
}
}
}
unsafe {
gl::EnableVertexAttribArray(i as GLuint);
}
}
fn opengl_sized_type(f: &VertexComponentFormat) -> GLenum {
match (f.comp_type, f.unit_size) {
(Type::Integral, 1) => gl::BYTE,
(Type::Integral, 2) => gl::SHORT,
(Type::Integral, 4) => gl::INT,
(Type::Unsigned, 1) | (Type::Boolean, 1) => gl::UNSIGNED_BYTE,
(Type::Unsigned, 2) => gl::UNSIGNED_SHORT,
(Type::Unsigned, 4) => gl::UNSIGNED_INT,
(Type::Floating, 4) => gl::FLOAT,
_ => panic!("unsupported vertex component format: {:?}", f)
}
}
fn opengl_mode(mode: Mode) -> GLenum {
match mode {
Mode::Point => gl::POINTS,
Mode::Line => gl::LINES,
Mode::LineStrip => gl::LINE_STRIP,
Mode::Triangle => gl::TRIANGLES,
Mode::TriangleFan => gl::TRIANGLE_FAN,
Mode::TriangleStrip => gl::TRIANGLE_STRIP
}
}
#[derive(Clone)]
pub struct TessSlice<'a, V> where V: 'a {
tess: &'a Tess<V>,
start_index: usize,
vert_nb: usize,
inst_nb: usize
}
impl<'a, V> TessSlice<'a, V> {
pub fn one_whole(tess: &'a Tess<V>) -> Self {
TessSlice {
tess: tess,
start_index: 0,
vert_nb: tess.vert_nb,
inst_nb: 1
}
}
pub fn one_sub(tess: &'a Tess<V>, vert_nb: usize) -> Self {
if vert_nb > tess.vert_nb {
panic!("cannot render {} vertices for a tessellation which vertex capacity is {}", vert_nb, tess.vert_nb);
}
TessSlice {
tess: tess,
start_index: 0,
vert_nb: vert_nb,
inst_nb: 1
}
}
pub fn one_slice(tess: &'a Tess<V>, start: usize, nb: usize) -> Self {
if start > tess.vert_nb {
panic!("cannot render {} vertices starting at vertex {} for a tessellation which vertex capacity is {}", nb, start, tess.vert_nb);
}
if nb > tess.vert_nb {
panic!("cannot render {} vertices for a tessellation which vertex capacity is {}", nb, tess.vert_nb);
}
TessSlice {
tess: tess,
start_index: start,
vert_nb: nb,
inst_nb: 1
}
}
pub fn render<C>(&self, ctx: &mut C) where C: GraphicsContext, V: Vertex {
self.tess.render(ctx, self.start_index, self.vert_nb, self.inst_nb);
}
}
impl<'a, V> From<&'a Tess<V>> for TessSlice<'a, V> {
fn from(tess: &'a Tess<V>) -> Self {
TessSlice::one_whole(tess)
}
}
pub trait TessSliceIndex<Idx, V> {
fn slice<'a>(&'a self, idx: Idx) -> TessSlice<'a, V>;
}
impl<V> TessSliceIndex<RangeFull, V> for Tess<V> {
fn slice<'a>(&'a self, _: RangeFull) -> TessSlice<'a, V> {
TessSlice::one_whole(self)
}
}
impl<V> TessSliceIndex<RangeTo<usize>, V> for Tess<V> {
fn slice<'a>(&'a self, to: RangeTo<usize>) -> TessSlice<'a, V> {
TessSlice::one_sub(self, to.end)
}
}
impl<V> TessSliceIndex<RangeFrom<usize>, V> for Tess<V> {
fn slice<'a>(&'a self, from: RangeFrom<usize>) -> TessSlice<'a, V> {
TessSlice::one_slice(self, from.start, self.vert_nb)
}
}
impl<V> TessSliceIndex<Range<usize>, V> for Tess<V> {
fn slice<'a>(&'a self, range: Range<usize>) -> TessSlice<'a, V> {
TessSlice::one_slice(self, range.start, range.end)
}
}