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}