1use super::RenderEngine;
2use crate::array_view::ArrayView;
3use std::rc::Rc;
4use wasm_bindgen::prelude::*;
5use web_sys::{WebGl2RenderingContext, WebGlTexture};
6
7#[derive(Debug)]
13pub struct Texture {
14 texture: Rc<WebGlTexture>,
15 sampler: String,
16}
17
18impl Texture {
19 pub fn new(sampler: String, texture: Rc<WebGlTexture>) -> Texture {
21 Texture { sampler, texture }
22 }
23
24 pub fn texture(&self) -> &Rc<WebGlTexture> {
26 &self.texture
27 }
28
29 pub fn sampler(&self) -> &str {
31 &self.sampler
32 }
33}
34
35pub struct TextureBuilder<'a> {
40 engine: &'a mut RenderEngine,
41 texture: Rc<WebGlTexture>,
42}
43
44impl TextureBuilder<'_> {
45 pub(super) fn new(engine: &mut RenderEngine) -> Result<TextureBuilder<'_>, JsValue> {
46 let texture = Rc::new(
47 engine
48 .gl
49 .create_texture()
50 .ok_or("failed to create texture")?,
51 );
52 engine.bind_texture(&texture);
53 Ok(TextureBuilder { engine, texture })
54 }
55
56 pub fn set_parameter(self, parameter: TextureParameter) -> Self {
58 self.engine.gl.tex_parameteri(
59 WebGl2RenderingContext::TEXTURE_2D,
60 parameter.parameter_code(),
61 parameter.value_code(),
62 );
63 self
64 }
65
66 pub fn build(self) -> Rc<WebGlTexture> {
68 self.texture
69 }
70}
71
72#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
76pub enum TextureParameter {
77 MagFilter(TextureMagFilter),
79 MinFilter(TextureMinFilter),
81 WrapS(TextureWrap),
83 WrapT(TextureWrap),
85 WrapR(TextureWrap),
87}
88
89impl TextureParameter {
90 fn parameter_code(&self) -> u32 {
91 match self {
92 TextureParameter::MagFilter(_) => WebGl2RenderingContext::TEXTURE_MAG_FILTER,
93 TextureParameter::MinFilter(_) => WebGl2RenderingContext::TEXTURE_MIN_FILTER,
94 TextureParameter::WrapS(_) => WebGl2RenderingContext::TEXTURE_WRAP_S,
95 TextureParameter::WrapT(_) => WebGl2RenderingContext::TEXTURE_WRAP_T,
96 TextureParameter::WrapR(_) => WebGl2RenderingContext::TEXTURE_WRAP_R,
97 }
98 }
99
100 fn value_code(&self) -> i32 {
101 match *self {
102 TextureParameter::MagFilter(a) => a as i32,
103 TextureParameter::MinFilter(a) => a as i32,
104 TextureParameter::WrapS(a) => a as i32,
105 TextureParameter::WrapT(a) => a as i32,
106 TextureParameter::WrapR(a) => a as i32,
107 }
108 }
109}
110
111#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
115#[repr(u32)]
116pub enum TextureMagFilter {
117 #[default]
119 Linear = WebGl2RenderingContext::LINEAR,
120 Nearest = WebGl2RenderingContext::NEAREST,
122}
123
124#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
128#[repr(u32)]
129pub enum TextureMinFilter {
130 Linear = WebGl2RenderingContext::LINEAR,
132 Nearest = WebGl2RenderingContext::NEAREST,
134 NearestMipmapNearest = WebGl2RenderingContext::NEAREST_MIPMAP_NEAREST,
136 LinearMipmapNearest = WebGl2RenderingContext::LINEAR_MIPMAP_NEAREST,
138 #[default]
140 NearestMipmapLinear = WebGl2RenderingContext::NEAREST_MIPMAP_LINEAR,
141 LinearMipmapLinear = WebGl2RenderingContext::LINEAR_MIPMAP_LINEAR,
143}
144
145#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
149#[repr(u32)]
150pub enum TextureWrap {
151 #[default]
153 Repeat = WebGl2RenderingContext::REPEAT,
154 ClampToEdge = WebGl2RenderingContext::CLAMP_TO_EDGE,
156 MirroredRepeat = WebGl2RenderingContext::MIRRORED_REPEAT,
158}
159
160pub trait TextureInternalFormat {
167 const INTERNAL_FORMAT: i32;
169 const FORMAT: u32;
171 type T: ArrayView;
173}
174
175macro_rules! new_format {
176 ($doc:expr, $type:ident, $int:expr, $fmt:expr, $t:ty) => {
177 #[doc = $doc]
178 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
179 pub struct $type {}
180
181 impl TextureInternalFormat for $type {
182 const INTERNAL_FORMAT: i32 = ($int) as i32;
183 const FORMAT: u32 = $fmt;
184 type T = $t;
185 }
186 };
187}
188
189new_format!(
190 r#"RGB texture internal format.
191
192This uses `u8` as the native Rust type and the `RGB` WebGL2 format as
193internal format and format."#,
194 Rgb,
195 WebGl2RenderingContext::RGB,
196 WebGl2RenderingContext::RGB,
197 u8
198);
199new_format!(
200 r#"RGBA texture internal format.
201
202This uses `u8` as the native Rust type and the `RGBA` WebGL2 format as
203internal format and format."#,
204 Rgba,
205 WebGl2RenderingContext::RGBA,
206 WebGl2RenderingContext::RGBA,
207 u8
208);
209new_format!(
210 r#"Luminance alpha texture internal format.
211
212This uses `u8` as the native Rust type and the `LUMINANCE_ALPHA` WebGl2
213format as internal format and format."#,
214 LuminanceAlpha,
215 WebGl2RenderingContext::LUMINANCE_ALPHA,
216 WebGl2RenderingContext::LUMINANCE_ALPHA,
217 u8
218);
219new_format!(
220 r#"R16F texture internal format.
221
222This uses `f32` as the native Rust type, the `R16F` WebGL2 format as
223internal format, and the `RED` WebGL2 format as format."#,
224 R16f,
225 WebGl2RenderingContext::R16F,
226 WebGl2RenderingContext::RED,
227 f32
228);
229
230impl RenderEngine {
231 pub fn texture_image<F: TextureInternalFormat>(
241 &mut self,
242 texture: &Rc<WebGlTexture>,
243 image: &[F::T],
244 width: usize,
245 height: usize,
246 ) -> Result<(), JsValue> {
247 self.bind_texture(texture);
248 unsafe {
249 let view = F::T::view(image);
250 let level = 0;
251 let border = 0;
252 self.gl.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_array_buffer_view(
253 WebGl2RenderingContext::TEXTURE_2D,
254 level,
255 F::INTERNAL_FORMAT,
256 width as i32,
257 height as i32,
258 border,
259 F::FORMAT,
260 F::T::GL_TYPE,
261 Some(&view)
262 )?;
263 };
264 Ok(())
265 }
266
267 pub fn texture_subimage<F: TextureInternalFormat>(
276 &mut self,
277 texture: &Rc<WebGlTexture>,
278 image: &[F::T],
279 xoffset: usize,
280 yoffset: usize,
281 width: usize,
282 height: usize,
283 ) -> Result<(), JsValue> {
284 self.bind_texture(texture);
285 unsafe {
286 let view = F::T::view(image);
287 let level = 0;
288 self.gl
289 .tex_sub_image_2d_with_i32_and_i32_and_u32_and_type_and_opt_array_buffer_view(
290 WebGl2RenderingContext::TEXTURE_2D,
291 level,
292 xoffset as i32,
293 yoffset as i32,
294 width as i32,
295 height as i32,
296 F::FORMAT,
297 F::T::GL_TYPE,
298 Some(&view),
299 )?;
300 };
301 Ok(())
302 }
303
304 pub(super) fn texture_from_text_render<F: TextureInternalFormat>(
305 &mut self,
306 texture: &Rc<WebGlTexture>,
307 ) -> Result<(), JsValue> {
308 self.bind_texture(texture);
309 let level = 0;
310
311 self.gl
324 .pixel_storei(WebGl2RenderingContext::UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
325
326 self.gl
327 .tex_image_2d_with_u32_and_u32_and_html_canvas_element(
328 WebGl2RenderingContext::TEXTURE_2D,
329 level,
330 F::INTERNAL_FORMAT,
331 F::FORMAT,
332 F::T::GL_TYPE,
333 self.text_render.canvas(),
334 )?;
335
336 self.gl
337 .pixel_storei(WebGl2RenderingContext::UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
338
339 Ok(())
340 }
341
342 pub fn generate_mipmap(&mut self, texture: &Rc<WebGlTexture>) {
346 self.bind_texture(texture);
347 self.gl.generate_mipmap(WebGl2RenderingContext::TEXTURE_2D);
348 }
349}