use std::mem;
use std::marker::PhantomData;
use state::{State, Capabilities};
use buffer::BufferRange;
use gl;
use gl::types::*;
use ::Vao;
use mopa;
use std::borrow::Cow;
use std::hash::BuildHasher;
#[cfg(not(any(feature="gles", feature="webgl")))]
use std::os::raw::c_void;
pub trait DynVertexBufferBinding: mopa::Any{
fn index(&self) -> usize;
fn offset_in_buffer(&self) -> usize;
fn stride(&self) -> usize;
fn divisor(&self) -> usize;
fn enable_for(&self, vao: &Vao, state: &mut State, capabilities: &Capabilities);
fn len(&self) -> usize;
fn buffer_id(&self) -> u32;
fn is_empty(&self) -> bool{
self.len() == 0
}
}
mopafy!(DynVertexBufferBinding);
#[derive(Debug,Clone)]
pub struct VertexBufferBinding<T,B: BufferRange<T>>{
pub index: usize,
pub buffer: B,
pub divisor: usize,
pub marker: PhantomData<T>,
}
impl<T,B: BufferRange<T>> VertexBufferBinding<T,B>{
pub fn divisor(self, divisor: usize) -> VertexBufferBinding<T,B>{
VertexBufferBinding{
index: self.index,
buffer: self.buffer,
divisor: divisor,
marker: PhantomData,
}
}
}
impl<T: 'static, B: BufferRange<T> + 'static> DynVertexBufferBinding for VertexBufferBinding<T,B>{
fn index(&self) -> usize{
self.index
}
fn offset_in_buffer(&self) -> usize{
self.buffer.start() * self.stride()
}
fn stride(&self) -> usize{
self.buffer.stride()
}
fn divisor(&self) -> usize{
self.divisor
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
fn enable_for(&self, vao: &Vao, gl: &mut State, capabilities: &Capabilities){
if capabilities.supports_dsa(){
unsafe{
gl.VertexArrayVertexBuffer(vao.id() as u32,
self.index as u32,
self.buffer.id(),
self.offset_in_buffer() as GLintptr,
self.stride() as GLsizei);
gl.VertexArrayBindingDivisor(vao.id() as u32,
self.index as u32,
self.divisor as u32);
}
}else if capabilities.supports_vertex_attrib_binding(){
unsafe{
gl.bind_vao(vao.id());
gl.BindVertexBuffer(
self.index as u32,
self.buffer.id(),
self.offset_in_buffer() as GLintptr,
self.stride() as GLsizei);
gl.VertexBindingDivisor(self.index as u32, self.divisor as u32);
}
}
}
#[cfg(feature = "gles")]
fn enable_for(&self, vao: &Vao, gl: &mut State, capabilities: &Capabilities){
if capabilities.supports_vertex_attrib_binding(){
unsafe{
gl.bind_vao(vao.id());
gl.BindVertexBuffer(
self.index as u32,
self.buffer.id(),
self.offset_in_buffer() as GLintptr,
self.stride() as GLsizei);
gl.VertexBindingDivisor(self.index as u32, self.divisor as u32);
}
}
}
#[cfg(feature = "webgl")]
fn enable_for(&self, _vao: &Vao, _gl: &mut State, _capabilities: &Capabilities){
}
fn len(&self) -> usize{
self.buffer.len()
}
fn buffer_id(&self) -> u32 {
self.buffer.id()
}
}
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
#[repr(u32)]
pub enum Type{
I8 = gl::BYTE,
U8 = gl::UNSIGNED_BYTE,
I16 = gl::SHORT,
U16 = gl::UNSIGNED_SHORT,
I32 = gl::INT,
U32 = gl::UNSIGNED_INT,
Bool = gl::BOOL,
Float = gl::FLOAT,
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
Double = gl::DOUBLE,
}
impl Default for Type{
fn default() -> Type{
Type::Float
}
}
#[derive(Clone, Default, Debug, Hash, Eq, PartialEq)]
pub struct Format{
pub name: Cow<'static, str>,
pub location: u32,
pub num_coords: usize,
pub src_type: Type,
pub dst_type: Type,
pub normalize: bool,
pub offset_in_vertex: usize,
}
impl Format{
pub fn source_from<V: DynVertexBufferBinding + Clone>(&self, vertex_binding: &V) -> (Format, V){
(self.clone(), vertex_binding.clone())
}
}
#[derive(Clone, Default, Debug, Hash, Eq, PartialEq)]
pub struct MatFormat{
pub name: Cow<'static, str>,
pub location: u32,
pub cols: usize,
pub rows: usize,
pub offset_in_vertex: usize,
}
impl MatFormat{
pub fn into_row_formats(self) -> Vec<Format>{
(0..self.rows).map(|row|{
let row_location = self.location + row as u32;
Format{
name: Cow::Owned(format!("{}{}", self.name, row)),
location: row_location,
num_coords: self.cols,
offset_in_vertex: self.offset_in_vertex + mem::size_of::<f32>() * row * self.cols,
.. Default::default()
}
})
.collect()
}
pub fn source_from<B: DynVertexBufferBinding + Clone>(&self, buffer: &B) -> Vec<(Format, B)>{
self.clone().into_row_formats()
.into_iter()
.map(|row_format| (row_format, buffer.clone()))
.collect()
}
}
impl Into<Vec<Format>> for MatFormat{
fn into(self) -> Vec<Format>{
self.into_row_formats()
}
}
impl Into<Vec<Format>> for Format{
fn into(self) -> Vec<Format>{
vec![self]
}
}
pub trait VertexFormat{
fn attributes_formats(bindings: &dyn Bindings) -> Vec<Format>;
}
pub trait AttributeBufferBinding{
fn enable_for(&self, vao: &Vao, state: &mut State, capabilities: &Capabilities);
fn disable_for(&self, vao: &Vao, state: &mut State, capabilities: &Capabilities);
}
impl<'a, B: DynVertexBufferBinding + ?Sized> AttributeBufferBinding for (&'a Format, &'a B){
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
fn enable_for(&self, vao: &Vao, gl: &mut State, capabilities: &Capabilities){
let attribute = &self.0;
let buffer_binding = &self.1;
if capabilities.supports_dsa(){
unsafe{
gl.VertexArrayAttribBinding(vao.id() as u32,
attribute.location,
buffer_binding.index() as u32);
match attribute.dst_type as GLenum{
gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT | gl::INT |
gl::UNSIGNED_INT => gl.VertexArrayAttribIFormat(vao.id() as u32,
attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
attribute.offset_in_vertex as u32),
gl::DOUBLE => gl.VertexArrayAttribLFormat(vao.id() as u32,
attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
attribute.offset_in_vertex as u32),
gl::FLOAT => gl.VertexArrayAttribFormat(vao.id() as u32,
attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
if attribute.normalize {gl::TRUE} else {gl::FALSE},
attribute.offset_in_vertex as u32),
_ => {}
}
gl.EnableVertexArrayAttrib(vao.id() as u32, attribute.location as u32);
}
}else if capabilities.supports_vertex_attrib_binding(){
unsafe{
gl.bind_vao(vao.id());
gl.EnableVertexAttribArray(attribute.location);
match attribute.dst_type as GLenum{
gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT | gl::INT |
gl::UNSIGNED_INT => gl.VertexAttribIFormat(
attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
attribute.offset_in_vertex as u32),
gl::DOUBLE => gl.VertexAttribLFormat(
attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
attribute.offset_in_vertex as u32),
gl::FLOAT => gl.VertexAttribFormat(
attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
if attribute.normalize {gl::TRUE} else {gl::FALSE},
attribute.offset_in_vertex as u32),
_ => {}
}
gl.VertexAttribBinding(attribute.location, buffer_binding.index() as u32);
}
}else{
unsafe{
gl.bind_vao(vao.id());
gl.BindBuffer(gl::ARRAY_BUFFER, buffer_binding.buffer_id());
gl.EnableVertexAttribArray(attribute.location);
match attribute.dst_type as GLenum{
gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT | gl::INT |
gl::UNSIGNED_INT => gl.VertexAttribIPointer(attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
buffer_binding.stride() as i32,
(buffer_binding.offset_in_buffer() + attribute.offset_in_vertex) as *const c_void),
gl::DOUBLE => gl.VertexAttribLPointer(attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
buffer_binding.stride() as i32,
(buffer_binding.offset_in_buffer() + attribute.offset_in_vertex) as *const c_void),
gl::FLOAT => gl.VertexAttribPointer(attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
if attribute.normalize {gl::TRUE} else {gl::FALSE},
buffer_binding.stride() as i32,
(buffer_binding.offset_in_buffer() + attribute.offset_in_vertex) as *const c_void),
_ => {}
}
gl.VertexAttribDivisor(attribute.location, buffer_binding.divisor() as u32);
}
}
}
#[cfg(any(feature = "gles", feature="webgl"))]
fn enable_for(&self, vao: &Vao, gl: &mut State, capabilities: &Capabilities){
let attribute = &self.0;
let buffer_binding = &self.1;
if capabilities.supports_vertex_attrib_binding(){
#[cfg(not(feature="webgl"))]
unsafe{
gl.bind_vao(vao.id());
gl.EnableVertexAttribArray(attribute.location);
match attribute.dst_type as GLenum{
gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT | gl::INT |
gl::UNSIGNED_INT => gl.VertexAttribIFormat(
attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
attribute.offset_in_vertex as u32),
gl::FLOAT => gl.VertexAttribFormat(
attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
if attribute.normalize {gl::TRUE} else {gl::FALSE},
attribute.offset_in_vertex as u32),
_ => {}
}
gl.VertexAttribBinding(attribute.location, buffer_binding.index() as u32);
}
}else{
unsafe{
gl.bind_vao(vao.id());
gl.BindBuffer(gl::ARRAY_BUFFER, buffer_binding.buffer_id());
gl.EnableVertexAttribArray(attribute.location);
match attribute.dst_type as GLenum{
gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT | gl::INT |
gl::UNSIGNED_INT => gl.VertexAttribIPointer(attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
buffer_binding.stride() as i32,
mem::transmute(buffer_binding.offset_in_buffer() + attribute.offset_in_vertex)),
gl::FLOAT => gl.VertexAttribPointer(attribute.location,
attribute.num_coords as i32,
attribute.src_type as GLenum,
if attribute.normalize {gl::TRUE} else {gl::FALSE},
buffer_binding.stride() as i32,
mem::transmute(buffer_binding.offset_in_buffer() + attribute.offset_in_vertex)),
_ => {}
}
gl.VertexAttribDivisor(attribute.location, buffer_binding.divisor() as u32);
}
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
fn disable_for(&self, vao: &::Vao, gl: &mut State, capabilities: &Capabilities){
unsafe{
if capabilities.supports_dsa(){
gl.DisableVertexArrayAttrib(vao.id(), self.0.location);
}else{
gl.bind_vao(vao.id());
gl.DisableVertexAttribArray(self.0.location);
}
}
}
#[cfg(any(feature = "gles", feature="webgl"))]
fn disable_for(&self, vao: &::Vao, gl: &mut State, _capabilities: &Capabilities){
unsafe{
gl.bind_vao(vao.id());
gl.DisableVertexAttribArray(self.0.location);
}
}
}
impl<'a, B: BufferRange<::IndexT> + ?Sized> AttributeBufferBinding for B{
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
fn enable_for(&self, vao: &::Vao, gl: &mut State, capabilities: &Capabilities){
unsafe{
if capabilities.supports_dsa(){
gl.VertexArrayElementBuffer(vao.id(), self.id());
}else{
gl.bind_vao(vao.id());
gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.id());
}
}
}
#[cfg(any(feature = "gles", feature="webgl"))]
fn enable_for(&self, vao: &::Vao, gl: &mut State, _capabilities: &Capabilities){
unsafe{
gl.bind_vao(vao.id());
gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.id());
}
}
#[cfg(all(not(feature = "gles"), not(feature="webgl")))]
fn disable_for(&self, vao: &::Vao, gl: &mut State, capabilities: &Capabilities){
unsafe{
if capabilities.supports_dsa(){
gl.VertexArrayElementBuffer(vao.id(), 0);
}else{
gl.bind_vao(vao.id());
gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
}
}
}
#[cfg(any(feature = "gles", feature="webgl"))]
fn disable_for(&self, vao: &::Vao, gl: &mut State, _capabilities: &Capabilities){
unsafe{
gl.bind_vao(vao.id());
gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
}
}
}
pub trait Bindings{
fn attribute_location(&self, name: &str) -> Option<u32>;
fn attribute_bindings(&self) -> indexmap::map::Iter<String, u32>;
}
impl<H: BuildHasher + Clone> Bindings for indexmap::IndexMap<String, u32, H>{
fn attribute_location(&self, name: &str) -> Option<u32>{
self.get(name).cloned()
}
fn attribute_bindings(&self) -> indexmap::map::Iter<String, u32>{
self.iter()
}
}
impl Bindings for ::Program{
fn attribute_location(&self, name: &str) -> Option<u32>{
self.attribute_location(name)
}
fn attribute_bindings(&self) -> indexmap::map::Iter<String, u32>{
self.attribute_bindings().iter()
}
}