use std::ops::RangeBounds;
use anyhow::{Context as _, Result};
use cfg_if::cfg_if;
use js_sys::{Uint16Array, Uint32Array};
use web_sys::{WebGlBuffer, WebGlRenderingContext};
use crate::{resolve_range, BufferDataUsage, Context, RenderPrimitiveType};
pub struct Indices {
buffer: WebGlBuffer,
len: usize,
ty: u32,
}
impl Indices {
pub fn new(context: &Context, indices: &[u16], usage: BufferDataUsage) -> Result<Self> {
let gl = &context.native;
let buffer = gl
.create_buffer()
.context("Failed to allocate WebGL buffer")?;
gl.bind_buffer(WebGlRenderingContext::ELEMENT_ARRAY_BUFFER, Some(&buffer));
let array = Uint16Array::from(indices);
gl.buffer_data_with_array_buffer_view(
WebGlRenderingContext::ELEMENT_ARRAY_BUFFER,
&array,
usage.to_const(),
);
Ok(Self {
buffer,
len: indices.len(),
ty: WebGlRenderingContext::UNSIGNED_SHORT,
})
}
pub fn new_with_usize(
context: &Context,
indices: &[usize],
usage: BufferDataUsage,
) -> Result<Self> {
let gl = &context.native;
gl.get_extension("OES_element_index_uint")
.ok()
.flatten()
.context("Failed to enable extension for u32 element index")?;
let array;
cfg_if! {
if #[cfg(target_pointer_width = "32")] {
let indices = unsafe {
std::slice::from_raw_parts(indices.as_ptr() as *const u32, indices.len())
};
array = Uint32Array::from(indices);
} else {
use std::convert::TryFrom;
let indices: Vec<u32> = indices.iter().map(|&v| u32::try_from(v).expect("Index is unreasonably large")).collect();
array = Uint32Array::from(&indices[..]);
}
};
let buffer = gl
.create_buffer()
.context("Failed ot allocate WebGL buffer")?;
gl.bind_buffer(WebGlRenderingContext::ELEMENT_ARRAY_BUFFER, Some(&buffer));
gl.buffer_data_with_array_buffer_view(
WebGlRenderingContext::ELEMENT_ARRAY_BUFFER,
&array,
usage.to_const(),
);
Ok(Self {
buffer,
len: indices.len(),
ty: WebGlRenderingContext::UNSIGNED_INT,
})
}
pub(crate) fn draw(
&self,
mode: RenderPrimitiveType,
context: &Context,
items: impl RangeBounds<usize>,
) {
let gl = &context.native;
let (start, end) = resolve_range(items, self.len);
gl.bind_buffer(
WebGlRenderingContext::ELEMENT_ARRAY_BUFFER,
Some(&self.buffer),
);
gl.draw_elements_with_i32(mode.to_const(), end - start, self.ty, start);
}
}