stereokit_rust/
shader.rs

1use crate::{StereoKitError, system::IAsset};
2use std::{
3    ffi::{CStr, CString, c_void},
4    path::Path,
5    ptr::NonNull,
6};
7
8/// A shader is a piece of code that runs on the GPU, and determines how model data gets transformed into pixels on
9/// screen! It’s more likely that you’ll work more directly with Materials, which shaders are a subset of.
10///
11/// With this particular class, you can mostly just look at it. It doesn’t do a whole lot. Maybe you can swap out the
12/// shader code or something sometimes!
13/// <https://stereokit.net/Pages/StereoKit/Shader.html>
14///
15/// see also [`crate::material::Material`]
16/// ### Examples
17/// ```
18/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
19/// use stereokit_rust::{shader::Shader, material::Material,
20///                      mesh::Mesh, maths::{Matrix, Vec2, Vec3, Vec4}};
21///
22/// let plane = Mesh::generate_plane( Vec2::ONE*2.0, Vec3::NEG_Z, Vec3::X, None, true);
23/// let shader = Shader::from_file("shaders/brick_pbr.hlsl.sks").unwrap();
24/// let mut material = Material::new(shader,Some("my_material"));
25/// material.tex_transform(Vec4::new(0.0, 0.0, 0.03, 0.03));
26///
27/// filename_scr = "screenshots/shaders.jpeg";
28/// test_screenshot!(
29///     plane.draw(token, &material, Matrix::IDENTITY, None, None);
30/// );
31///
32/// ```
33/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/shaders.jpeg" alt="screenshot" width="200">
34#[repr(C)]
35#[derive(Debug, PartialEq)]
36pub struct Shader(pub NonNull<_ShaderT>);
37impl Drop for Shader {
38    fn drop(&mut self) {
39        unsafe { shader_release(self.0.as_ptr()) };
40    }
41}
42impl AsRef<Shader> for Shader {
43    fn as_ref(&self) -> &Shader {
44        self
45    }
46}
47/// StereoKit internal type.
48#[repr(C)]
49#[derive(Debug)]
50pub struct _ShaderT {
51    _unused: [u8; 0],
52}
53/// StereoKit ffi type.
54pub type ShaderT = *mut _ShaderT;
55unsafe extern "C" {
56    pub fn shader_find(id: *const ::std::os::raw::c_char) -> ShaderT;
57    pub fn shader_create_file(filename_utf8: *const ::std::os::raw::c_char) -> ShaderT;
58    pub fn shader_create_mem(data: *mut ::std::os::raw::c_void, data_size: usize) -> ShaderT;
59    pub fn shader_set_id(shader: ShaderT, id: *const ::std::os::raw::c_char);
60    pub fn shader_get_id(shader: ShaderT) -> *const ::std::os::raw::c_char;
61    pub fn shader_get_name(shader: ShaderT) -> *const ::std::os::raw::c_char;
62    pub fn shader_addref(shader: ShaderT);
63    pub fn shader_release(shader: ShaderT);
64}
65
66impl IAsset for Shader {
67    // fn id(&mut self, id: impl AsRef<str>) {
68    //     self.id(id);
69    // }
70
71    fn get_id(&self) -> &str {
72        self.get_id()
73    }
74}
75
76impl Default for Shader {
77    /// This is a fast, general purpose shader. It uses a texture for ‘diffuse’, a ‘color’ property for tinting the
78    /// material, and a ‘tex_scale’ for scaling the UV coordinates. For lighting, it just uses a lookup from the current
79    /// cubemap.
80    /// <https://stereokit.net/Pages/StereoKit/Shader/Default.html>
81    /// ### Examples
82    /// ```
83    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
84    /// use stereokit_rust::{shader::Shader};
85    /// let mut shader = Shader::default();
86    /// assert_eq!(shader.get_id(), "default/shader");
87    /// ```
88    fn default() -> Self {
89        Self::find("default/shader").unwrap()
90    }
91}
92impl Shader {
93    /// Loads an image file stored in memory directly into a texture! Supported formats are: jpg, png, tga, bmp, psd,
94    /// gif, hdr, pic.
95    /// Asset Id will be the same as the filename.
96    /// <https://stereokit.net/Pages/StereoKit/Shader/FromMemory.html>
97    /// * `data` - A precompiled StereoKit Shader file as bytes.
98    ///
99    /// see also [`shader_create_mem`]
100    /// ### Examples
101    /// ```
102    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
103    /// use stereokit_rust::{shader::Shader};
104    /// let shader_data = std::include_bytes!("../assets/shaders/brick_pbr.hlsl.sks");
105    /// let mut shader = Shader::from_memory(shader_data).unwrap();
106    /// assert_eq!(shader.get_name(), "the_name_of_brick_pbr");
107    ///```
108    pub fn from_memory(data: &[u8]) -> Result<Shader, StereoKitError> {
109        Ok(Shader(
110            NonNull::new(unsafe { shader_create_mem(data.as_ptr() as *mut c_void, data.len()) })
111                .ok_or(StereoKitError::ShaderMem)?,
112        ))
113    }
114
115    /// Loads a shader from a precompiled StereoKit Shader (.sks) file! HLSL files can be compiled using the skshaderc
116    /// tool called with `cargo compile_sks` or `cargo build_sk_rs`.
117    /// <https://stereokit.net/Pages/StereoKit/Shader/FromFile.html>
118    /// * `file_utf8` - Path to a precompiled StereoKit Shader file! If no .sks extension is part of this path,
119    ///   StereoKit will automatically add it and check that first.
120    ///
121    /// see also [`crate::material::Material::new`] [`shader_create_file`]
122    /// see example in [`Shader`]
123    pub fn from_file(file_utf8: impl AsRef<Path>) -> Result<Shader, StereoKitError> {
124        let path_buf = file_utf8.as_ref().to_path_buf();
125        let c_str = CString::new(
126            path_buf
127                .clone()
128                .to_str()
129                .ok_or(StereoKitError::ShaderFile(path_buf.clone(), "CString conversion".to_string()))?,
130        )?;
131        Ok(Shader(
132            NonNull::new(unsafe { shader_create_file(c_str.as_ptr()) })
133                .ok_or(StereoKitError::ShaderFile(path_buf.clone(), "shader_create_file failed".to_string()))?,
134        ))
135    }
136
137    /// Looks for a shader asset that’s already loaded, matching the given id!
138    /// <https://stereokit.net/Pages/StereoKit/Shader/Find.html>
139    /// * `id` - For shaders loaded from file, this’ll be the shader’s metadata name!
140    ///
141    /// see also [`shader_find`]
142    /// ### Examples
143    /// ```
144    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
145    /// use stereokit_rust::{shader::Shader};
146    /// let mut shader = Shader::from_file("shaders/brick_pbr.hlsl.sks")
147    ///                              .expect("Brick shader should be there");
148    /// shader.id("my_brick_shader");
149    /// let mut shader_again = Shader::find("my_brick_shader");
150    /// assert!(shader_again.is_ok(), "Failed to find shader");
151    /// assert_eq!(shader_again.unwrap().get_id(), shader.get_id());
152    /// ```
153    pub fn find<S: AsRef<str>>(id: S) -> Result<Shader, StereoKitError> {
154        let c_str = CString::new(id.as_ref())
155            .map_err(|_| StereoKitError::ShaderFind(id.as_ref().into(), "CString conversion".to_string()))?;
156        Ok(Shader(
157            NonNull::new(unsafe { shader_find(c_str.as_ptr()) })
158                .ok_or(StereoKitError::ShaderFind(id.as_ref().into(), "shader_find failed".to_string()))?,
159        ))
160    }
161
162    /// Creates a clone of the same reference. Basically, the new variable is the same asset. This is what you get by
163    /// calling [Shader::find] method.
164    /// <https://stereokit.net/Pages/StereoKit/Shader/Find.html>
165    ///
166    /// see also [`shader_find()`]
167    /// ### Examples
168    /// ```
169    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
170    /// use stereokit_rust::{shader::Shader};
171    /// let mut shader = Shader::from_file("shaders/brick_pbr.hlsl.sks")
172    ///                              .expect("Brick shader should be there");
173    /// shader.id("my_brick_shader");
174    /// let mut shader_again = shader.clone_ref();
175    /// assert_eq!(shader_again.get_id(), "my_brick_shader");
176    /// assert_eq!(shader_again, shader);
177    /// ```
178    pub fn clone_ref(&self) -> Shader {
179        Shader(
180            NonNull::new(unsafe { shader_find(shader_get_id(self.0.as_ptr())) }).expect("<asset>::clone_ref failed!"),
181        )
182    }
183
184    /// Gets or sets the unique identifier of this asset resource! This can be helpful for debugging,
185    /// managing your assets, or finding them later on!
186    /// <https://stereokit.net/Pages/StereoKit/Shader/Id.html>
187    ///
188    /// see also [`shader_set_id`]
189    /// ### Examples
190    /// ```
191    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
192    /// use stereokit_rust::{shader::Shader};
193    /// let mut shader = Shader::from_file("shaders/brick_pbr.hlsl.sks")
194    ///                              .expect("Brick shader should be there");
195    /// shader.id("my_brick_shader");
196    /// assert_eq!(shader.get_id(), "my_brick_shader");
197    /// ```
198    pub fn id<S: AsRef<str>>(&mut self, id: S) -> &mut Self {
199        let c_str = CString::new(id.as_ref()).unwrap();
200        unsafe { shader_set_id(self.0.as_ptr(), c_str.as_ptr()) };
201        self
202    }
203
204    /// The id of this shader
205    /// <https://stereokit.net/Pages/StereoKit/Shader/Id.html>
206    ///
207    /// see also [`shader_get_id`]
208    ///
209    /// see example in [`Shader::id`]
210    pub fn get_id(&self) -> &str {
211        unsafe { CStr::from_ptr(shader_get_id(self.0.as_ptr())) }.to_str().unwrap()
212    }
213
214    /// The name of the shader, provided in the shader file itself. Not the filename or id.
215    /// <https://stereokit.net/Pages/StereoKit/Shader/Name.html>
216    ///
217    /// see also [`shader_get_name`]
218    /// ### Examples
219    /// ```
220    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
221    /// use stereokit_rust::{shader::Shader};
222    /// let shader = Shader::from_file("shaders/brick_pbr.hlsl.sks")
223    ///                              .expect("Brick shader should be there");
224    /// assert_eq!(shader.get_name(), "the_name_of_brick_pbr");
225    /// ```
226    pub fn get_name(&self) -> &str {
227        unsafe { CStr::from_ptr(shader_get_name(self.0.as_ptr())) }.to_str().unwrap()
228    }
229
230    /// <https://stereokit.net/Pages/StereoKit/Shader/Blit.html>
231    ///
232    /// ### Examples
233    /// ```
234    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
235    /// use stereokit_rust::{shader::Shader};
236    /// let mut shader = Shader::blit();
237    /// assert_eq!(shader.get_id(), "default/shader_blit");
238    /// ```
239    pub fn blit() -> Self {
240        Self::find("default/shader_blit").unwrap()
241    }
242
243    /// <https://stereokit.net/Pages/StereoKit/Shader/LightMap.html>
244    ///
245    /// ### Examples
246    /// ```
247    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
248    /// use stereokit_rust::{shader::Shader};
249    /// let mut shader = Shader::light_map();
250    /// assert_eq!(shader.get_id(), "default/shader_lightmap");
251    /// ```
252    pub fn light_map() -> Self {
253        Self::find("default/shader_lightmap").unwrap()
254    }
255
256    /// Sometimes lighting just gets in the way! This is an extremely simple and fast shader that uses a ‘diffuse’
257    /// texture and a ‘color’ tint property to draw a model without any lighting at all!
258    /// <https://stereokit.net/Pages/StereoKit/Shader/Unlit.html>
259    ///
260    /// ### Examples
261    /// ```
262    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
263    /// use stereokit_rust::{shader::Shader};
264    /// let mut shader = Shader::unlit();
265    /// assert_eq!(shader.get_id(), "default/shader_unlit");
266    /// ```
267    pub fn unlit() -> Self {
268        Self::find("default/shader_unlit").unwrap()
269    }
270
271    /// Sometimes lighting just gets in the way! This is an extremely simple and fast shader that uses a ‘diffuse’
272    /// texture and a ‘color’ tint property to
273    /// draw a model without any lighting at all! This shader will also discard pixels with an alpha of zero.
274    /// <https://stereokit.net/Pages/StereoKit/Shader/UnlitClip.html>
275    ///
276    /// ### Examples
277    /// ```
278    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
279    /// use stereokit_rust::{shader::Shader};
280    /// let mut shader = Shader::unlit_clip();
281    /// assert_eq!(shader.get_id(), "default/shader_unlit_clip");
282    /// ```
283    pub fn unlit_clip() -> Self {
284        Self::find("default/shader_unlit_clip").unwrap()
285    }
286
287    /// <https://stereokit.net/Pages/StereoKit/Shader/Font.html>
288    ///
289    /// ### Examples
290    /// ```
291    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
292    /// use stereokit_rust::{shader::Shader};
293    /// let mut shader = Shader::font();
294    /// assert_eq!(shader.get_id(), "default/shader_font");
295    /// ```
296    pub fn font() -> Self {
297        Self::find("default/shader_font").unwrap()
298    }
299
300    /// <https://stereokit.net/Pages/StereoKit/Shader/equirect.html>
301    ///
302    /// ### Examples
303    /// ```
304    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
305    /// use stereokit_rust::{shader::Shader};
306    /// let mut shader = Shader::equirect();
307    /// assert_eq!(shader.get_id(), "default/shader_equirect");
308    /// ```
309    pub fn equirect() -> Self {
310        Self::find("default/shader_equirect").unwrap()
311    }
312
313    /// A shader for UI or interactable elements, this’ll be the same as the Shader, but with an additional finger
314    /// ‘shadow’ and distance circle effect that helps indicate finger distance from the surface of the object.
315    /// <https://stereokit.net/Pages/StereoKit/Shader/UI.html>
316    ///
317    /// ### Examples
318    /// ```
319    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
320    /// use stereokit_rust::{shader::Shader};
321    /// let mut shader = Shader::ui();
322    /// assert_eq!(shader.get_id(), "default/shader_ui");
323    /// ```
324    pub fn ui() -> Self {
325        Self::find("default/shader_ui").unwrap()
326    }
327
328    /// A shader for indicating interaction volumes! It renders a border around the edges of the UV coordinates that
329    /// will ‘grow’ on proximity to the user’s finger. It will discard pixels outside of that border, but will also show
330    /// the finger shadow. This is meant to be an opaque shader, so it works well for depth LSR. This shader works best
331    /// on cube-like meshes where each face has UV coordinates from 0-1.
332    /// Shader Parameters: color - color border_size - meters border_size_grow - meters border_affect_radius - meters
333    /// <https://stereokit.net/Pages/StereoKit/Shader/UIBox.html>
334    ///
335    /// ### Examples
336    /// ```
337    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
338    /// use stereokit_rust::{shader::Shader};
339    /// let mut shader = Shader::ui_box();
340    /// assert_eq!(shader.get_id(), "default/shader_ui_box");
341    /// ```
342    pub fn ui_box() -> Self {
343        Self::find("default/shader_ui_box").unwrap()
344    }
345
346    /// <https://stereokit.net/Pages/StereoKit/Shader.html>
347    ///
348    /// ### Examples
349    /// ```
350    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
351    /// use stereokit_rust::{shader::Shader};
352    /// let mut shader = Shader::ui_quadrant();
353    /// assert_eq!(shader.get_id(), "default/shader_ui_quadrant");
354    /// ```
355    pub fn ui_quadrant() -> Self {
356        Self::find("default/shader_ui_quadrant").unwrap()
357    }
358
359    /// <https://stereokit.net/Pages/StereoKit/Shader.html>
360    ///
361    /// ### Examples
362    /// ```
363    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
364    /// use stereokit_rust::{shader::Shader};
365    /// let mut shader = Shader::sky();
366    /// assert_eq!(shader.get_id(), "default/shader_sky");
367    /// ```
368    pub fn sky() -> Self {
369        Self::find("default/shader_sky").unwrap()
370    }
371
372    /// A physically based shader.
373    /// <https://stereokit.net/Pages/StereoKit/Shader/PBR.html>
374    ///
375    /// ### Examples
376    /// ```
377    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
378    /// use stereokit_rust::{shader::Shader};
379    /// let mut shader = Shader::pbr();
380    /// assert_eq!(shader.get_id(), "default/shader_pbr");
381    /// ```
382    pub fn pbr() -> Self {
383        Self::find("default/shader_pbr").unwrap()
384    }
385
386    /// Same as ShaderPBR, but with a discard clip for transparency.
387    /// <https://stereokit.net/Pages/StereoKit/Shader/PBRClip.html>
388    ///
389    /// ### Examples
390    /// ```
391    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
392    /// use stereokit_rust::{shader::Shader};
393    /// let mut shader = Shader::pbr_clip();
394    /// assert_eq!(shader.get_id(), "default/shader_pbr_clip");
395    /// ```
396    pub fn pbr_clip() -> Self {
397        Self::find("default/shader_pbr_clip").unwrap()
398    }
399}