kepler_ra/techtron/webgl/
texture.rs

1
2// MIT License
3
4// Copyright (c) 2023 Techtron-Lab
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in all
14// copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23
24
25use log::info;
26use once_cell::sync::Lazy;
27use std::borrow::Borrow;
28use std::ops::Deref;
29use std::rc::Rc;
30use std::sync::atomic::{AtomicU32, Ordering};
31use std::sync::Mutex;
32use web_sys::{WebGl2RenderingContext, WebGlTexture};
33
34use super::context::*;
35
36type GL2 = WebGl2RenderingContext;
37
38// pub fn gen_texture_id() -> Result<u32, String> {
39//     // static TEXTURE_ID: AtomicU32 = AtomicU32::new(WebGl2RenderingContext::TEXTURE10);
40//     static TEXTURE_ID: AtomicU32 = AtomicU32::new(10);
41//     let id = TEXTURE_ID.fetch_add(1, Ordering::SeqCst);
42//     if id < WebGl2RenderingContext::MAX_COMBINED_TEXTURE_IMAGE_UNITS {
43//         Ok(id)
44//     } else {
45//         Err(String::from("Texture id is too large."))
46//     }
47// }
48
49const MAX_TEXTURE_ID: u32 =
50    WebGl2RenderingContext::MAX_COMBINED_TEXTURE_IMAGE_UNITS - WebGl2RenderingContext::TEXTURE0 - 1;
51
52static TEXTURE_ID: Lazy<Mutex<Vec<u32>>> =
53    Lazy::new(|| Mutex::new((0..MAX_TEXTURE_ID).rev().collect()));
54
55pub fn gen_texture_id() -> Result<u32, String> {
56    TEXTURE_ID
57        .lock()
58        .or(Err(String::from("Cannot lock TEXTURE_ID.")))
59        .and_then(|mut v| v.pop().ok_or(String::from("Texture id exhausted.")))
60}
61
62pub fn release_texture_id(id: u32) -> Result<(), String> {
63    if id < MAX_TEXTURE_ID {
64        TEXTURE_ID
65        .lock()
66        .or(Err(String::from("Cannot lock TEXTURE_ID.")))
67        .and_then(|mut v| Ok(v.push(id)))
68    } else {
69        Err(String::from("Texture id is out of range."))
70    }
71}
72
73pub trait LoadTexture {
74    fn load_texture(&self, context: &GLContext) -> LoadedTexture;
75}
76
77pub struct Texture3DRGBA16 {
78    width: i32,
79    height: i32,
80    depth: i32,
81    data: Rc<Vec<u16>>,
82}
83
84impl Texture3DRGBA16 {
85    pub fn new(width: i32, height: i32, depth: i32, data: Rc<Vec<u16>>) -> Self {
86        Texture3DRGBA16 {
87            width,
88            height,
89            depth,
90            data,
91        }
92    }
93    
94}
95
96impl LoadTexture for Texture3DRGBA16 {
97    fn load_texture(&self, context: &GLContext) -> LoadedTexture {
98        let gl = context.clone();
99        let level = 0;
100        let border = 0;
101        let internal_format = GL2::RGBA as i32;
102        let source_format = GL2::RGBA;
103        let source_type = GL2::UNSIGNED_SHORT_4_4_4_4;
104        let id = gen_texture_id().expect("Cannot generate texture id.");
105
106        // create texture
107        let handle = gl.create_texture().expect("Failed to crate texture.");
108        gl.bind_texture(GL2::TEXTURE_3D, Some(&handle));
109
110        // copy data go GPU
111        let data: &[u16] = self.data.as_slice();
112        let array = unsafe { &js_sys::Uint16Array::view(data) };
113
114        gl.tex_image_3d_with_opt_array_buffer_view(
115            GL2::TEXTURE_3D,
116            level,
117            internal_format,
118            self.width,
119            self.height,
120            self.depth,
121            border,
122            source_format,
123            source_type,
124            Some(array),
125        );
126        let error = gl.get_error();
127        if error != 0 {
128            panic!("copying error: {}.", error);
129        } else {
130            info!("finish copying.");
131        }
132
133        set_default_texture_param(&gl);
134        LoadedTexture {
135            context: context.clone(),
136            handle,
137            id,
138        }
139        
140    }
141}
142
143#[derive(Debug)]
144pub struct LoadedTexture {
145    pub context: GLContext,
146    pub handle: WebGlTexture,
147    pub id: u32,
148    // pub data: Rc<CTVolume>,
149    // pub data: Vec<u8>,
150}
151impl Drop for LoadedTexture {
152    fn drop(&mut self) {
153        // info!("delete loaded texture: {}", &self.id);
154        release_texture_id(self.id);
155        self.context.delete_texture(Some(&self.handle));
156    }
157}
158
159impl Deref for LoadedTexture {
160    type Target = WebGlTexture;
161    fn deref(&self) -> &Self::Target {
162        &self.handle
163    }
164}
165
166impl LoadedTexture {
167    pub fn activate(&self) {
168        self.context.active_texture(GL2::TEXTURE0 + self.id)
169    }
170}
171
172pub fn set_default_texture_param(gl: &WebGl2RenderingContext) {
173    type GL2 = WebGl2RenderingContext;
174    // gl.bind_texture(GL2::TEXTURE_3D, texture);
175    gl.tex_parameteri(
176        GL2::TEXTURE_3D,
177        GL2::TEXTURE_WRAP_S,
178        GL2::CLAMP_TO_EDGE.try_into().unwrap(),
179    );
180    gl.tex_parameteri(
181        GL2::TEXTURE_3D,
182        GL2::TEXTURE_WRAP_T,
183        GL2::CLAMP_TO_EDGE.try_into().unwrap(),
184    );
185    gl.tex_parameteri(
186        GL2::TEXTURE_3D,
187        GL2::TEXTURE_WRAP_R,
188        GL2::CLAMP_TO_EDGE.try_into().unwrap(),
189    );
190    gl.tex_parameteri(
191        GL2::TEXTURE_3D,
192        GL2::TEXTURE_MIN_FILTER,
193        GL2::LINEAR.try_into().unwrap(),
194        // GL2::NEAREST.try_into().unwrap(),
195    );
196    gl.tex_parameteri(
197        GL2::TEXTURE_3D,
198        GL2::TEXTURE_MAG_FILTER,
199        GL2::LINEAR.try_into().unwrap(),
200        // GL2::NEAREST.try_into().unwrap(),
201    );
202}
203
204
205pub trait GenTexture<T> {
206    fn gen_texture3d(&self) -> T;
207}
208
209pub struct Texture3DRGB8 {
210    width: i32,
211    height: i32,
212    depth: i32,
213    data: Rc<Vec<u8>>,
214}
215
216impl Texture3DRGB8 {
217    pub fn new(width: i32, height: i32, depth: i32, data: Rc<Vec<u8>>) -> Self {
218        Texture3DRGB8 {
219            width,
220            height,
221            depth,
222            data,
223        }
224    }
225    
226}
227
228impl LoadTexture for Texture3DRGB8 {
229    fn load_texture(&self, context: &GLContext) -> LoadedTexture {
230        let gl = context.clone();
231        // let (w, h, d) = (256, 1, 1);
232        let border = 0;
233        let level = 0;
234
235        let data = self.data.as_slice();
236        let lut_array_view = unsafe { js_sys::Uint8Array::view(&data) };
237        let handle = gl.create_texture().expect("Failed to create LUT texture.");
238        let id = gen_texture_id().expect("Cannot generate texture id.");
239        gl.bind_texture(GL2::TEXTURE_3D, Some(&handle));
240        gl.tex_image_3d_with_opt_array_buffer_view(
241            GL2::TEXTURE_3D,
242            level,
243            GL2::RGB8 as i32,
244            self.width,
245            self.height,
246            self.depth,
247            border,
248            GL2::RGB,
249            GL2::UNSIGNED_BYTE,
250            Some(&lut_array_view),
251        )
252        .expect("Failed to copy LUT data to GPU.");
253        set_default_texture_param(&gl);
254
255        LoadedTexture {
256            context: context.clone(),
257            handle,
258            id,
259        }
260    }
261}