use super::RenderEngine;
use crate::array_view::ArrayView;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use web_sys::{WebGl2RenderingContext, WebGlTexture};
#[derive(Debug)]
pub struct Texture {
texture: Rc<WebGlTexture>,
sampler: String,
}
impl Texture {
pub fn new(sampler: String, texture: Rc<WebGlTexture>) -> Texture {
Texture { sampler, texture }
}
pub fn texture(&self) -> &Rc<WebGlTexture> {
&self.texture
}
pub fn sampler(&self) -> &str {
&self.sampler
}
}
pub struct TextureBuilder<'a> {
engine: &'a mut RenderEngine,
texture: Rc<WebGlTexture>,
}
impl<'a> TextureBuilder<'a> {
pub(super) fn new(engine: &mut RenderEngine) -> Result<TextureBuilder<'_>, JsValue> {
let texture = Rc::new(
engine
.gl
.create_texture()
.ok_or("failed to create texture")?,
);
engine.bind_texture(&texture);
Ok(TextureBuilder { engine, texture })
}
pub fn set_parameter(self, parameter: TextureParameter) -> Self {
self.engine.gl.tex_parameteri(
WebGl2RenderingContext::TEXTURE_2D,
parameter.parameter_code(),
parameter.value_code(),
);
self
}
pub fn build(self) -> Rc<WebGlTexture> {
self.texture
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum TextureParameter {
MagFilter(TextureMagFilter),
MinFilter(TextureMinFilter),
WrapS(TextureWrap),
WrapT(TextureWrap),
WrapR(TextureWrap),
}
impl TextureParameter {
fn parameter_code(&self) -> u32 {
match self {
TextureParameter::MagFilter(_) => WebGl2RenderingContext::TEXTURE_MAG_FILTER,
TextureParameter::MinFilter(_) => WebGl2RenderingContext::TEXTURE_MIN_FILTER,
TextureParameter::WrapS(_) => WebGl2RenderingContext::TEXTURE_WRAP_S,
TextureParameter::WrapT(_) => WebGl2RenderingContext::TEXTURE_WRAP_T,
TextureParameter::WrapR(_) => WebGl2RenderingContext::TEXTURE_WRAP_R,
}
}
fn value_code(&self) -> i32 {
match *self {
TextureParameter::MagFilter(a) => a as i32,
TextureParameter::MinFilter(a) => a as i32,
TextureParameter::WrapS(a) => a as i32,
TextureParameter::WrapT(a) => a as i32,
TextureParameter::WrapR(a) => a as i32,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
#[repr(u32)]
pub enum TextureMagFilter {
#[default]
Linear = WebGl2RenderingContext::LINEAR,
Nearest = WebGl2RenderingContext::NEAREST,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
#[repr(u32)]
pub enum TextureMinFilter {
Linear = WebGl2RenderingContext::LINEAR,
Nearest = WebGl2RenderingContext::NEAREST,
NearestMipmapNearest = WebGl2RenderingContext::NEAREST_MIPMAP_NEAREST,
LinearMipmapNearest = WebGl2RenderingContext::LINEAR_MIPMAP_NEAREST,
#[default]
NearestMipmapLinear = WebGl2RenderingContext::NEAREST_MIPMAP_LINEAR,
LinearMipmapLinear = WebGl2RenderingContext::LINEAR_MIPMAP_LINEAR,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
#[repr(u32)]
pub enum TextureWrap {
#[default]
Repeat = WebGl2RenderingContext::REPEAT,
ClampToEdge = WebGl2RenderingContext::CLAMP_TO_EDGE,
MirroredRepeat = WebGl2RenderingContext::MIRRORED_REPEAT,
}
pub trait TextureInternalFormat {
const INTERNAL_FORMAT: i32;
const FORMAT: u32;
type T: ArrayView;
}
macro_rules! new_format {
($doc:expr, $type:ident, $int:expr, $fmt:expr, $t:ty) => {
#[doc = $doc]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct $type {}
impl TextureInternalFormat for $type {
const INTERNAL_FORMAT: i32 = ($int) as i32;
const FORMAT: u32 = $fmt;
type T = $t;
}
};
}
new_format!(
r#"RGB texture internal format.
This uses `u8` as the native Rust type and the `RGB` WebGL2 format as
internal format and format."#,
Rgb,
WebGl2RenderingContext::RGB,
WebGl2RenderingContext::RGB,
u8
);
new_format!(
r#"RGBA texture internal format.
This uses `u8` as the native Rust type and the `RGBA` WebGL2 format as
internal format and format."#,
Rgba,
WebGl2RenderingContext::RGBA,
WebGl2RenderingContext::RGBA,
u8
);
new_format!(
r#"Luminance alpha texture internal format.
This uses `u8` as the native Rust type and the `LUMINANCE_ALPHA` WebGl2
format as internal format and format."#,
LuminanceAlpha,
WebGl2RenderingContext::LUMINANCE_ALPHA,
WebGl2RenderingContext::LUMINANCE_ALPHA,
u8
);
new_format!(
r#"R16F texture internal format.
This uses `f32` as the native Rust type, the `R16F` WebGL2 format as
internal format, and the `RED` WebGL2 format as format."#,
R16f,
WebGl2RenderingContext::R16F,
WebGl2RenderingContext::RED,
f32
);
impl RenderEngine {
pub fn texture_image<F: TextureInternalFormat>(
&mut self,
texture: &Rc<WebGlTexture>,
image: &[F::T],
width: usize,
height: usize,
) -> Result<(), JsValue> {
self.bind_texture(texture);
unsafe {
let view = F::T::view(image);
let level = 0;
let border = 0;
self.gl.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_array_buffer_view(
WebGl2RenderingContext::TEXTURE_2D,
level,
F::INTERNAL_FORMAT,
width as i32,
height as i32,
border,
F::FORMAT,
F::T::GL_TYPE,
Some(&view)
)?;
};
Ok(())
}
pub fn texture_subimage<F: TextureInternalFormat>(
&mut self,
texture: &Rc<WebGlTexture>,
image: &[F::T],
xoffset: usize,
yoffset: usize,
width: usize,
height: usize,
) -> Result<(), JsValue> {
self.bind_texture(texture);
unsafe {
let view = F::T::view(image);
let level = 0;
self.gl
.tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_array_buffer_view(
WebGl2RenderingContext::TEXTURE_2D,
level,
xoffset as i32,
yoffset as i32,
width as i32,
height as i32,
F::FORMAT,
F::T::GL_TYPE,
Some(&view),
)?;
};
Ok(())
}
pub(super) fn texture_from_text_render<F: TextureInternalFormat>(
&mut self,
texture: &Rc<WebGlTexture>,
) -> Result<(), JsValue> {
self.bind_texture(texture);
let level = 0;
self.gl
.pixel_storei(WebGl2RenderingContext::UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
self.gl
.tex_image_2d_with_u32_and_u32_and_html_canvas_element(
WebGl2RenderingContext::TEXTURE_2D,
level,
F::INTERNAL_FORMAT,
F::FORMAT,
F::T::GL_TYPE,
self.text_render.canvas(),
)?;
self.gl
.pixel_storei(WebGl2RenderingContext::UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
Ok(())
}
pub fn generate_mipmap(&mut self, texture: &Rc<WebGlTexture>) {
self.bind_texture(texture);
self.gl.generate_mipmap(WebGl2RenderingContext::TEXTURE_2D);
}
}