fyrox_material/shader/mod.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Shader is a script for graphics card, it defines how to draw an object. It also defines a set
22//! of external resources needed for the rendering.
23//!
24//! # Structure
25//!
26//! Shader has rigid structure that could be described in this code snipped:
27//!
28//! ```ron
29//! (
30//! name: "MyShader",
31//!
32//! // A set of resources, the maximum amount of resources is limited by your GPU. The engine
33//! // guarantees, that there could at least 16 textures and 16 resource groups per shader.
34//! resources: [
35//! (
36//! // Each resource binding must have a name.
37//! name: "diffuseTexture",
38//!
39//! // Value has limited set of possible variants.
40//! kind: Texture(kind: Sampler2D, fallback: White),
41//!
42//! binding: 0
43//! ),
44//! (
45//! name: "properties",
46//! kind: PropertyGroup([
47//! (
48//! name: "diffuseColor",
49//! kind: Color(r: 255, g: 255, b: 255, a: 255),
50//! ),
51//! ]),
52//! binding: 0
53//! ),
54//! // There are number of built-in property groups which provides useful data for each shader.
55//! // See the full list of built-in property groups below.
56//! (
57//! name: "fyrox_instanceData",
58//! kind: PropertyGroup([
59//! // Autogenerated
60//! ]),
61//! binding: 1
62//! ),
63//! ],
64//!
65//! // A set of passes that are intentionally missing in the shader.
66//! disabled_passes: ["GBuffer", "DirectionalShadow", "PointShadow", "SpotShadow"],
67//!
68//! // A set of render passes (see a section `Render pass` for more info)
69//! passes: [
70//! (
71//! // Name must match with the name of either standard render pass (see below) or
72//! // one of your passes.
73//! name: "Forward",
74//!
75//! // A set of parameters that regulate renderer pipeline state.
76//! // This is mandatory field of each render pass.
77//! draw_parameters: DrawParameters(
78//! // A face to cull. Either Front or Back.
79//! cull_face: Some(Back),
80//!
81//! // Color mask. Defines which colors should be written to render target.
82//! color_write: ColorMask(
83//! red: true,
84//! green: true,
85//! blue: true,
86//! alpha: true,
87//! ),
88//!
89//! // Whether to modify depth buffer or not.
90//! depth_write: true,
91//!
92//! // Whether to use stencil test or not.
93//! stencil_test: None,
94//!
95//! // Whether to perform depth test when drawing.
96//! depth_test: Some(Less),
97//!
98//! // Blending options.
99//! blend: Some(BlendParameters(
100//! func: BlendFunc(
101//! sfactor: SrcAlpha,
102//! dfactor: OneMinusSrcAlpha,
103//! alpha_sfactor: SrcAlpha,
104//! alpha_dfactor: OneMinusSrcAlpha,
105//! ),
106//! equation: BlendEquation(
107//! rgb: Add,
108//! alpha: Add
109//! )
110//! )),
111//!
112//! // Stencil options.
113//! stencil_op: StencilOp(
114//! fail: Keep,
115//! zfail: Keep,
116//! zpass: Keep,
117//! write_mask: 0xFFFF_FFFF,
118//! ),
119//!
120//! // Scissor box. Could be something like this:
121//! //
122//! // scissor_box: Some(ScissorBox(
123//! // x: 10,
124//! // y: 20,
125//! // width: 100,
126//! // height: 30
127//! // ))
128//! scissor_box: None
129//! ),
130//!
131//! // Vertex shader code.
132//! vertex_shader:
133//! r#"
134//! layout(location = 0) in vec3 vertexPosition;
135//! layout(location = 1) in vec2 vertexTexCoord;
136//!
137//! out vec2 texCoord;
138//!
139//! void main()
140//! {
141//! texCoord = vertexTexCoord;
142//! gl_Position = fyrox_instanceData.worldViewProjection * vec4(vertexPosition, 1.0);
143//! }
144//! "#,
145//!
146//! // Fragment shader code.
147//! fragment_shader:
148//! r#"
149//! out vec4 FragColor;
150//!
151//! in vec2 texCoord;
152//!
153//! void main()
154//! {
155//! FragColor = properties.diffuseColor * texture(diffuseTexture, texCoord);
156//! }
157//! "#,
158//! )
159//! ],
160//! )
161//! ```
162//!
163//! Shader should contain at least one render pass to actually do some job. A shader could not
164//! have properties at all. Currently only vertex and fragment programs are supported. Each
165//! program mush be written in GLSL. Comprehensive GLSL documentation can be found
166//! [here](https://www.khronos.org/opengl/wiki/Core_Language_(GLSL))
167//!
168//! # Render pass
169//!
170//! Modern rendering is a very complex thing that requires drawing an object multiple times
171//! with different "scripts". For example to draw an object with shadows you need to draw an
172//! object twice: one directly in a render target, and one in a shadow map. Such stages called
173//! render passes.
174//!
175//! Binding of shaders to render passes is done via names, each render pass has unique name.
176//!
177//! ## Predefined passes
178//!
179//! There are number of predefined render passes:
180//!
181//! - `GBuffer` - A pass that fills a set of textures (render targets) with various data about each
182//! rendered object (depth, normal, albedo, etc.). These textures then are used for physically-based
183//! lighting. Use this pass when you want the standard lighting to work with your objects.
184//!
185//! - `Forward` - A pass that draws an object directly in a render target. It could be used to render
186//! translucent objects.
187//!
188//! - `SpotShadow` - A pass that emits depth values for an object, later this depth map will be
189//! used to render shadows.
190//!
191//! - `PointShadow` - A pass that emits distance from a fragment to a point light, later this depth
192//! map will be used to render shadows.
193//!
194//! - `DirectionalShadow` - A pass that emits depth values for an object, later this depth map will be
195//! used to render shadows for directional cascaded shadows.
196//!
197//! ## Disables passes
198//!
199//! A render pass can be marked as "disabled" to prevent the renderer emitting errors when it tries
200//! to use such render pass. This mechanism of explicit marking the pass disabled is here to ensure
201//! correctness of shaders. To explicitly disable a pass, add its name to `disabled_passes` array:
202//!
203//! ```ron
204//! disabled_passes: ["GBuffer", "DirectionalShadow", "PointShadow", "SpotShadow"],
205//! ```
206//!
207//! # Resources
208//!
209//! Each shader requires a specific set of external resources that will be used during the rendering.
210//! This set is defined in `resources` section of the shader and could contain the following resources:
211//!
212//! - `Texture` - a texture of arbitrary type
213//! - `PropertyGroup` - a group of numeric properties.
214//!
215//! ## Binding points
216//!
217//! Shader resource must define a unique (over its type) binding index. The engine will use these
218//! points to prepare appropriate resource descriptor sets for GPU. Keep in mind, that binding point
219//! indices are **unique** per each type of resource. This means that a set of texture resource could
220//! use the same indices as property groups. The binding points must be unique in its group. If
221//! there are more than one resource of a certain type, that shares the same binding point, the
222//! engine will refuse to use such shader.
223//!
224//! ## Built-in resources
225//!
226//! There are number of built-in resources, that Fyrox will try to assign automatically if they're
227//! defined in your shader, something like this:
228//!
229//! ```ron
230//! (
231//! name: "fyrox_instanceData",
232//! kind: PropertyGroup([
233//! // Autogenerated
234//! ]),
235//! binding: 1
236//! ),
237//! ```
238//!
239//! The full list of built-in resources is defined below.
240//!
241//! ### `fyrox_instanceData`
242//!
243//! Property group. Provided for each rendered surface instance.
244//!
245//! | Name | Type | Description |
246//! |----------------------|------------|---------------------------------------------|
247//! | worldMatrix | `mat4` | Local-to-world transformation. |
248//! | worldViewProjection | `mat4` | Local-to-clip-space transform. |
249//! | blendShapesCount | `int` | Total amount of blend shapes. |
250//! | useSkeletalAnimation | `bool` | Whether skinned meshes is rendering or not. |
251//! | blendShapesWeights | `vec4[32]` | Blend shape weights. |
252//!
253//! ### `fyrox_boneMatrices`
254//!
255//! Property group. Provided for each rendered surface, that has skeletal animation.
256//!
257//! | Name | Type | Description |
258//! |----------|-------------|---------------|
259//! | matrices | `mat4[256]` | Bone matrices |
260//!
261//!
262//! ### `fyrox_cameraData`
263//!
264//! Property group. Contains camera properties. It contains info not only about scene camera,
265//! but also observer info when rendering shadow maps. In other words - it is generic observer
266//! properties.
267//!
268//! | Name | Type | Description |
269//! |----------------------|------------|--------------------------------------------------|
270//! | viewProjectionMatrix | `mat4` | World-to-clip-space transformation. |
271//! | position | `vec3` | World-space position of the camera. |
272//! | upVector | `vec3` | World-space up-vector of the camera. |
273//! | sideVector | `vec3` | World-space side-vector of the camera. |
274//! | zNear | `float` | Near clipping plane location. |
275//! | zFar | `float` | Far clipping plane location. |
276//! | zRange | `float` | `zFar - zNear` |
277//!
278//! ### `fyrox_lightData`
279//!
280//! Property group. Available only in shadow passes.
281//!
282//! | Name | Type | Description |
283//! |-------------------|--------|------------------------------------------------------------|
284//! | lightPosition | `vec3` | World-space light source position. Only for shadow passes. |
285//! | ambientLightColor | `vec4` | Ambient lighting color of the scene. |
286//!
287//! ### `fyrox_lightsBlock`
288//!
289//! Property group. Information about visible light sources
290//!
291//! | Name | Type | Description |
292//! |------------------ |------------|---------------------------------------------------------|
293//! | lightCount | `int` | Total amount of light sources visible on screen. |
294//! | lightsColorRadius | `vec4[16]` | Color (xyz) and radius (w) of light source |
295//! | lightsParameters | `vec2[16]` | Hot-spot cone angle cos (x) and half cone angle cos (y) |
296//! | lightsPosition | `vec3[16]` | World-space light position. |
297//! | lightsDirection | `vec3[16]` | World-space light direction |
298//!
299//! ### `fyrox_graphicsSettings`
300//!
301//! Property group. Contains graphics options of the renderer.
302//!
303//! | Name | Type | Description |
304//! |--------|------------|---------------------------------------------------|
305//! | usePom | `bool` | Whether to use parallax occlusion mapping or not. |
306//!
307//! ### `fyrox_sceneDepth`
308//!
309//! Texture. Contains depth values of scene. Available **only** after opaque geometry is
310//! rendered (read - G-Buffer is filled). Typical usage is something like this:
311//!
312//! ```ron
313//! (
314//! name: "fyrox_sceneDepth",
315//! kind: Texture(kind: Sampler2D, fallback: White),
316//! binding: 1
317//! ),
318//! ```
319//!
320//! ### `fyrox_widgetData`
321//!
322//! Property group. Contains UI widget-specific data, that can be used in UI shaders.
323//!
324//! | Name | Type | Description |
325//! |---------------------|-------------|------------------------------------------------------------|
326//! | worldViewProjection | `mat4` | World-to-clip-space transformation matrix. |
327//! | solidColor | `vec4` | RGBA color. |
328//! | gradientColors | `vec4[16]` | A set of gradient colors. |
329//! | gradientStops | `float[16]` | A set of normalized (0.0-1.0) stops along the axis. |
330//! | gradientOrigin | `vec2` | Origin position of the gradient (local coordinates). |
331//! | gradientEnd | `vec2` | End position of the gradient (local coordinates). |
332//! | gradientPointCount | `int` | Total number of gradient points. |
333//! | resolution | `vec2` | Frame buffer size to which the widget is drawn to. |
334//! | boundsMin | `vec2` | Top-left point of the screen space bounding rectangle. |
335//! | boundsMax | `vec2` | Right-bottom point of the screen space bounding rectangle. |
336//! | isFont | `bool` | `true` if the widget is a text, `false` - otherwise. |
337//! | opacity | `float` | Opacity (0.0-1.0 range). |
338//! | brushType | `int` | Brush type (0 solid, 1-linear gradient, 2-radial gradient |
339//!
340//! # Code generation
341//!
342//! Fyrox automatically generates code for resource bindings. This is made specifically to prevent
343//! subtle mistakes. For example when you define this set of resources:
344//!
345//! ```ron
346//! (
347//! name: "MyShader",
348//!
349//! resources: [
350//! (
351//! name: "diffuseTexture",
352//! kind: Texture(kind: Sampler2D, fallback: White),
353//! binding: 0
354//! ),
355//! (
356//! name: "normalTexture",
357//! kind: Texture(kind: Sampler2D, fallback: Normal),
358//! binding: 1
359//! ),
360//! (
361//! name: "properties",
362//! kind: PropertyGroup([
363//! (
364//! name: "texCoordScale",
365//! kind: Vector2(value: (1.0, 1.0)),
366//! ),
367//! (
368//! name: "diffuseColor",
369//! kind: Color(r: 255, g: 255, b: 255, a: 255),
370//! ),
371//! ]),
372//! binding: 0
373//! ),
374//! ]
375//! )
376//! ```
377//!
378//! The engine generates the following code and adds it to source code of every shader of every pass
379//! automatically:
380//!
381//! ```glsl
382//! uniform sampler2D diffuseTexture;
383//! uniform sampler2D normalTexture;
384//! struct Tproperties {
385//! vec2 texCoordScale;
386//! vec4 diffuseColor;
387//! };
388//! layout(std140) uniform Uproperties { Tproperties properties; }
389//! ```
390//!
391//! The most important thing is that the engine keeps properties in the `struct Tproperties` in
392//! correct order and forces `std140` layout on the generated uniform block. Since the engine knows
393//! the layout of the properties from their definition section, it could easily form a memory block
394//! with all required alignments and paddings that could be uploaded to GPU. The next important thing
395//! is that the engine batches all the data needed into a large chunks of data and uploads them
396//! all at once, which is much faster.
397//!
398//! # Drawing parameters
399//!
400//! Drawing parameters defines which GPU functions to use and at which state. For example, to render
401//! transparent objects you need to enable blending with specific blending rules. Or you need to disable
402//! culling to draw objects from both sides. This is when draw parameters comes in handy.
403//!
404//! There are relatively large list of drawing parameters, and it could confuse a person who didn't get
405//! used to work with graphics. The following list should help you to use drawing parameters correctly.
406//!
407//! - cull_face
408//! - Defines which side of polygon should be culled.
409//! - **Possible values:** `None`, [Some(CullFace::XXX)](fyrox_graphics::CullFace)
410//!
411//! - color_write:
412//! - Defines which components of color should be written to a render target
413//! - **Possible values:** [ColorMask](fyrox_graphics::ColorMask)(...)
414//!
415//! - depth_write:
416//! - Whether to modify depth buffer or not.
417//! - **Possible values:** `true/false`
418//!
419//! - stencil_test:
420//! - Whether to use stencil test or not.
421//! - **Possible values:**
422//! - `None`
423//! - Some([StencilFunc](fyrox_graphics::StencilFunc))
424//!
425//! - depth_test:
426//! - Whether to perform depth test when drawing.
427//! - **Possible values:** `true/false`
428//!
429//! - blend:
430//! - Blending options.
431//! - **Possible values:**
432//! - `None`
433//! - Some([BlendFunc](fyrox_graphics::BlendFunc))
434//!
435//! - stencil_op:
436//! - Stencil options.
437//! - **Possible values:** [StencilOp](fyrox_graphics::StencilOp)
438//!
439//! - `scissor_box`:
440//! - A rectangle that is used for clipping (screen-space). This value can be redefined when
441//! issuing a drawing command.
442//! - `None`
443//! - `Some(ScissorBox(x: 10, y: 20, width: 100, height: 30))`
444//!
445//! # Standard shader
446//!
447//! By default, Fyrox uses standard material for rendering, it covers 95% of uses cases and it is very
448//! flexible. To get standard shader instance, use [`ShaderResource::standard`]
449//!
450//! ```no_run
451//! # use fyrox_material::shader::{ShaderResource, ShaderResourceExtension};
452//!
453//! let standard_shader = ShaderResource::standard();
454//! ```
455//!
456//! Usually you don't need to get this shader manually, using of [Material::standard](super::Material::standard)
457//! is enough.
458
459use fyrox_core::some_or_continue;
460use fyrox_core::{
461 io::FileError, reflect::prelude::*, sparse::AtomicIndex, uuid::Uuid, visitor::prelude::*,
462 TypeUuidProvider,
463};
464pub use fyrox_graphics::gpu_program::{
465 SamplerFallback, ShaderResourceDefinition, ShaderResourceKind,
466};
467use fyrox_graphics::{gpu_program::ShaderProperty, DrawParameters};
468use fyrox_resource::{
469 embedded_data_source, io::ResourceIo, manager::BuiltInResource, untyped::ResourceKind,
470 Resource, ResourceData,
471};
472use ron::ser::PrettyConfig;
473use serde::{Deserialize, Deserializer, Serialize, Serializer};
474use std::ops::{Deref, DerefMut};
475use std::sync::LazyLock;
476use std::{
477 error::Error,
478 fmt::{Display, Formatter},
479 fs::File,
480 io::Write,
481 path::Path,
482 sync::Arc,
483};
484use uuid::uuid;
485
486pub mod loader;
487
488/// A name of the standard shader.
489pub const STANDARD_SHADER_NAME: &str = "Standard";
490
491/// A name of the standard 2D shader.
492pub const STANDARD_2D_SHADER_NAME: &str = "Standard2D";
493
494/// A name of the standard particle system shader.
495pub const STANDARD_PARTICLE_SYSTEM_SHADER_NAME: &str = "StandardParticleSystem";
496
497/// A name of the standard two-sides shader.
498pub const STANDARD_TWOSIDES_SHADER_NAME: &str = "StandardTwoSides";
499
500/// A name of the standard terrain shader.
501pub const STANDARD_TERRAIN_SHADER_NAME: &str = "StandardTerrain";
502
503/// A name of the standard tile shader.
504pub const STANDARD_TILE_SHADER_NAME: &str = "StandardTile";
505
506/// A name of the standard sprite shader.
507pub const STANDARD_SPRITE_SHADER_NAME: &str = "StandardSprite";
508
509/// A name of the standard widget shader.
510pub const STANDARD_WIDGET_SHADER_NAME: &str = "StandardWidget";
511
512/// Internal state of the shader.
513///
514/// # Notes
515///
516/// Usually you don't need to access internals of the shader, but there sometimes could be a need to
517/// read shader definition, to get supported passes and properties.
518#[derive(Default, Debug, Clone, Reflect, Visit)]
519pub struct Shader {
520 /// Shader definition contains description of properties and render passes.
521 #[visit(optional)]
522 pub definition: ShaderDefinition,
523
524 /// An id that can be used to create associated GPU resources.
525 #[reflect(hidden)]
526 #[visit(skip)]
527 pub cache_index: Arc<AtomicIndex>,
528}
529
530impl TypeUuidProvider for Shader {
531 fn type_uuid() -> Uuid {
532 uuid!("f1346417-b726-492a-b80f-c02096c6c019")
533 }
534}
535
536/// A simple wrapper over string to hold shader source code.
537#[derive(Default, Clone, Debug, PartialEq, Eq, Reflect, Visit, TypeUuidProvider)]
538#[type_uuid(id = "d2aa5ba0-59e8-4f21-b7af-d6aab3a65379")]
539pub struct ShaderSourceCode(pub String);
540
541impl Serialize for ShaderSourceCode {
542 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
543 where
544 S: Serializer,
545 {
546 self.0.serialize(serializer)
547 }
548}
549
550impl<'de> Deserialize<'de> for ShaderSourceCode {
551 fn deserialize<D>(deserializer: D) -> Result<ShaderSourceCode, D::Error>
552 where
553 D: Deserializer<'de>,
554 {
555 Ok(Self(String::deserialize(deserializer)?))
556 }
557}
558
559impl Deref for ShaderSourceCode {
560 type Target = String;
561
562 fn deref(&self) -> &Self::Target {
563 &self.0
564 }
565}
566
567impl DerefMut for ShaderSourceCode {
568 fn deref_mut(&mut self) -> &mut Self::Target {
569 &mut self.0
570 }
571}
572
573/// A render pass definition. See [`ShaderResource`] docs for more info about render passes.
574#[derive(
575 Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq, Reflect, Visit, TypeUuidProvider,
576)]
577#[type_uuid(id = "450f2f3a-bdc8-4fd8-b62e-c4c924cd94ca")]
578pub struct RenderPassDefinition {
579 /// A name of render pass.
580 pub name: String,
581 /// A set of parameters that will be used in a render pass.
582 #[serde(default)]
583 pub draw_parameters: DrawParameters,
584 /// A source code of vertex shader.
585 pub vertex_shader: ShaderSourceCode,
586 /// Vertex shader line number.
587 #[serde(default)]
588 #[reflect(hidden)]
589 pub vertex_shader_line: isize,
590 /// A source code of fragment shader.
591 pub fragment_shader: ShaderSourceCode,
592 /// Fragment shader line number.
593 #[serde(default)]
594 #[reflect(hidden)]
595 pub fragment_shader_line: isize,
596}
597
598/// A definition of the shader.
599#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Reflect, Visit)]
600pub struct ShaderDefinition {
601 /// A name of the shader.
602 pub name: String,
603 /// A set of render passes.
604 pub passes: Vec<RenderPassDefinition>,
605 /// A set of resource definitions.
606 pub resources: Vec<ShaderResourceDefinition>,
607 /// A list of names of disabled render passes. It is used to strictly indicate that certain
608 /// passes are intentionally disabled in the rendering process.
609 #[serde(default)]
610 pub disabled_passes: Vec<String>,
611}
612
613impl ShaderDefinition {
614 /// Maximum number of simultaneous light sources that can be passed into a standard lights data
615 /// block.
616 pub const MAX_LIGHTS: usize = 16;
617
618 /// Maximum number of bone matrices per shader.
619 pub const MAX_BONE_MATRICES: usize = 255;
620
621 /// Maximum number of blend shape weight groups (packed weights of blend shapes into vec4).
622 pub const MAX_BLEND_SHAPE_WEIGHT_GROUPS: usize = 32;
623
624 /// Maximum number of gradient values per widget.
625 pub const MAX_GRADIENT_VALUE_COUNT: usize = 16;
626
627 fn find_shader_line_locations(&mut self, str: &str) {
628 let mut line_ends = Vec::new();
629 for (i, ch) in str.bytes().enumerate() {
630 if ch == b'\n' {
631 line_ends.push(i);
632 }
633 }
634 if str.bytes().last().is_some_and(|ch| ch != b'\n') {
635 line_ends.push(str.len());
636 }
637
638 fn find_line(line_ends: &[usize], byte_pos: usize) -> isize {
639 line_ends
640 .windows(2)
641 .enumerate()
642 .find_map(|(line_num, ends)| {
643 if (ends[0]..ends[1]).contains(&byte_pos) {
644 Some(line_num)
645 } else {
646 None
647 }
648 })
649 .unwrap_or(0) as isize
650 + 1
651 }
652
653 let vertex_shader_regex = regex::Regex::new(r#"vertex_shader\s*:\s*r?#*""#).unwrap();
654 let fragment_shader_regex = regex::Regex::new(r#"fragment_shader\s*:\s*r?#*""#).unwrap();
655
656 let mut substr = str;
657 for pass in self.passes.iter_mut() {
658 let name_location = some_or_continue!(substr.find(&format!("\"{}\"", pass.name)));
659 let vertex_shader_location = some_or_continue!(vertex_shader_regex.find(substr));
660 let fragment_shader_location = some_or_continue!(fragment_shader_regex.find(substr));
661 let offset = str.len() - substr.len();
662 pass.vertex_shader_line = find_line(&line_ends, offset + vertex_shader_location.end());
663 pass.fragment_shader_line =
664 find_line(&line_ends, offset + fragment_shader_location.end());
665 let max = name_location
666 .max(vertex_shader_location.end())
667 .max(fragment_shader_location.end());
668 substr = &substr[(max + 1)..];
669 }
670 }
671
672 fn from_str(str: &str) -> Result<Self, ShaderError> {
673 let mut definition: ShaderDefinition = ron::de::from_str(str)?;
674 definition.generate_built_in_resources();
675 definition.find_shader_line_locations(str);
676 Ok(definition)
677 }
678
679 fn generate_built_in_resources(&mut self) {
680 for resource in self.resources.iter_mut() {
681 let ShaderResourceKind::PropertyGroup(ref mut properties) = resource.kind else {
682 continue;
683 };
684
685 match resource.name.as_str() {
686 "fyrox_widgetData" => {
687 properties.clear();
688 properties.extend([
689 ShaderProperty::new_matrix4("worldViewProjection"),
690 ShaderProperty::new_color("solidColor"),
691 ShaderProperty::new_vec4_f32_array(
692 "gradientColors",
693 Self::MAX_GRADIENT_VALUE_COUNT,
694 ),
695 ShaderProperty::new_f32_array(
696 "gradientStops",
697 Self::MAX_GRADIENT_VALUE_COUNT,
698 ),
699 ShaderProperty::new_vector2("gradientOrigin"),
700 ShaderProperty::new_vector2("gradientEnd"),
701 ShaderProperty::new_vector2("resolution"),
702 ShaderProperty::new_vector2("boundsMin"),
703 ShaderProperty::new_vector2("boundsMax"),
704 ShaderProperty::new_bool("isFont"),
705 ShaderProperty::new_float("opacity"),
706 ShaderProperty::new_int("brushType"),
707 ShaderProperty::new_int("gradientPointCount"),
708 ]);
709 }
710 "fyrox_cameraData" => {
711 properties.clear();
712 properties.extend([
713 ShaderProperty::new_matrix4("viewProjectionMatrix"),
714 ShaderProperty::new_vector3("position"),
715 ShaderProperty::new_vector3("upVector"),
716 ShaderProperty::new_vector3("sideVector"),
717 ShaderProperty::new_float("zNear"),
718 ShaderProperty::new_float("zFar"),
719 ShaderProperty::new_float("zRange"),
720 ]);
721 }
722 "fyrox_lightData" => {
723 properties.clear();
724 properties.extend([
725 ShaderProperty::new_vector3("lightPosition"),
726 ShaderProperty::new_vector4("ambientLightColor"),
727 ]);
728 }
729 "fyrox_graphicsSettings" => {
730 properties.clear();
731 properties.extend([ShaderProperty::new_bool("usePOM")]);
732 }
733 "fyrox_lightsBlock" => {
734 properties.clear();
735 properties.extend([
736 ShaderProperty::new_int("lightCount"),
737 ShaderProperty::new_vec4_f32_array("lightsColorRadius", Self::MAX_LIGHTS),
738 ShaderProperty::new_vec2_f32_array("lightsParameters", Self::MAX_LIGHTS),
739 ShaderProperty::new_vec3_f32_array("lightsPosition", Self::MAX_LIGHTS),
740 ShaderProperty::new_vec3_f32_array("lightsDirection", Self::MAX_LIGHTS),
741 ])
742 }
743 "fyrox_instanceData" => {
744 properties.clear();
745 properties.extend([
746 ShaderProperty::new_matrix4("worldMatrix"),
747 ShaderProperty::new_matrix4("worldViewProjection"),
748 ShaderProperty::new_int("blendShapesCount"),
749 ShaderProperty::new_bool("useSkeletalAnimation"),
750 ShaderProperty::new_vec4_f32_array(
751 "blendShapesWeights",
752 Self::MAX_BLEND_SHAPE_WEIGHT_GROUPS,
753 ),
754 ]);
755 }
756 "fyrox_boneMatrices" => {
757 properties.clear();
758 properties.extend([ShaderProperty::new_mat4_f32_array(
759 "matrices",
760 Self::MAX_BONE_MATRICES,
761 )])
762 }
763 _ => (),
764 }
765 }
766 }
767}
768
769impl Shader {
770 /// Creates a shader from file.
771 pub async fn from_file<P: AsRef<Path>>(
772 path: P,
773 io: &dyn ResourceIo,
774 ) -> Result<Self, ShaderError> {
775 let bytes = io.load_file(path.as_ref()).await?;
776 let content = String::from_utf8_lossy(&bytes);
777 Ok(Self {
778 definition: ShaderDefinition::from_str(&content)?,
779 cache_index: Default::default(),
780 })
781 }
782
783 /// Creates a shader from string.
784 pub fn from_string(str: &str) -> Result<Self, ShaderError> {
785 Ok(Self {
786 definition: ShaderDefinition::from_str(str)?,
787 cache_index: Default::default(),
788 })
789 }
790
791 /// Creates a shader from string represented as raw bytes. This function will fail if the `bytes`
792 /// does not contain Utf8-encoded string.
793 pub fn from_string_bytes(bytes: &[u8]) -> Result<Self, ShaderError> {
794 Ok(Self {
795 definition: ShaderDefinition::from_str(
796 std::str::from_utf8(bytes).map_err(|_| ShaderError::NotUtf8Source)?,
797 )?,
798 cache_index: Default::default(),
799 })
800 }
801
802 /// Tries to find a texture resource with the specified name.
803 pub fn find_texture_resource(&self, name: &str) -> Option<&ShaderResourceDefinition> {
804 self.definition.resources.iter().find(|res| {
805 res.name.as_str() == name && matches!(res.kind, ShaderResourceKind::Texture { .. })
806 })
807 }
808
809 /// Tries to find a property group resource with the specified name.
810 pub fn find_property_group_resource(&self, name: &str) -> Option<&ShaderResourceDefinition> {
811 self.definition.resources.iter().find(|res| {
812 res.name.as_str() == name && matches!(res.kind, ShaderResourceKind::PropertyGroup(_))
813 })
814 }
815
816 /// Checks if this shader has a texture resource with the specified name.
817 pub fn has_texture_resource(&self, name: &str) -> bool {
818 self.find_texture_resource(name).is_some()
819 }
820
821 /// Checks if this shader has a property group resource with the specified name.
822 pub fn has_property_group_resource(&self, name: &str) -> bool {
823 self.find_property_group_resource(name).is_some()
824 }
825}
826
827impl ResourceData for Shader {
828 fn type_uuid(&self) -> Uuid {
829 <Self as TypeUuidProvider>::type_uuid()
830 }
831
832 fn save(&mut self, path: &Path) -> Result<(), Box<dyn Error>> {
833 let mut file = File::create(path)?;
834 file.write_all(
835 ron::ser::to_string_pretty(&self.definition, PrettyConfig::default())?.as_bytes(),
836 )?;
837 Ok(())
838 }
839
840 fn can_be_saved(&self) -> bool {
841 true
842 }
843
844 fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
845 Some(Box::new(self.clone()))
846 }
847}
848
849/// A set of possible error variants that can occur during shader loading.
850#[derive(Debug)]
851pub enum ShaderError {
852 /// An i/o error has occurred.
853 Io(FileError),
854
855 /// A parsing error has occurred.
856 ParseError(ron::error::SpannedError),
857
858 /// Bytes does not represent Utf8-encoded string.
859 NotUtf8Source,
860}
861
862impl Display for ShaderError {
863 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
864 match self {
865 ShaderError::Io(v) => {
866 write!(f, "A file load error has occurred {v:?}")
867 }
868 ShaderError::ParseError(v) => {
869 write!(f, "A parsing error has occurred {v:?}")
870 }
871 ShaderError::NotUtf8Source => {
872 write!(f, "Bytes does not represent Utf8-encoded string.")
873 }
874 }
875 }
876}
877
878impl From<ron::error::SpannedError> for ShaderError {
879 fn from(e: ron::error::SpannedError) -> Self {
880 Self::ParseError(e)
881 }
882}
883
884impl From<FileError> for ShaderError {
885 fn from(e: FileError) -> Self {
886 Self::Io(e)
887 }
888}
889
890/// Type alias for shader resources.
891pub type ShaderResource = Resource<Shader>;
892
893/// Extension trait for shader resources.
894pub trait ShaderResourceExtension: Sized {
895 /// Creates new shader from given string. Input string must have the format defined in
896 /// examples for [`ShaderResource`].
897 fn from_str(id: Uuid, str: &str, kind: ResourceKind) -> Result<Self, ShaderError>;
898
899 /// Returns an instance of standard shader.
900 fn standard() -> Self;
901
902 /// Returns an instance of standard 2D shader.
903 fn standard_2d() -> Self;
904
905 /// Returns an instance of standard particle system shader.
906 fn standard_particle_system() -> Self;
907
908 /// Returns an instance of standard sprite shader.
909 fn standard_sprite() -> Self;
910
911 /// Returns an instance of standard terrain shader.
912 fn standard_terrain() -> Self;
913
914 /// Returns an instance of standard tile shader.
915 fn standard_tile() -> Self;
916
917 /// Returns an instance of standard two-sides terrain shader.
918 fn standard_twosides() -> Self;
919
920 /// Returns an instance of standard widget shader.
921 fn standard_widget() -> Self;
922
923 /// Returns a list of standard shader.
924 fn standard_shaders() -> [&'static BuiltInResource<Shader>; 8];
925}
926
927impl ShaderResourceExtension for ShaderResource {
928 fn from_str(id: Uuid, str: &str, kind: ResourceKind) -> Result<Self, ShaderError> {
929 Ok(Resource::new_ok(id, kind, Shader::from_string(str)?))
930 }
931
932 fn standard() -> Self {
933 STANDARD.resource()
934 }
935
936 fn standard_2d() -> Self {
937 STANDARD_2D.resource()
938 }
939
940 fn standard_particle_system() -> Self {
941 STANDARD_PARTICLE_SYSTEM.resource()
942 }
943
944 fn standard_sprite() -> Self {
945 STANDARD_SPRITE.resource()
946 }
947
948 fn standard_terrain() -> Self {
949 STANDARD_TERRAIN.resource()
950 }
951
952 fn standard_tile() -> Self {
953 STANDARD_TILE.resource()
954 }
955
956 fn standard_twosides() -> Self {
957 STANDARD_TWOSIDES.resource()
958 }
959
960 fn standard_widget() -> Self {
961 STANDARD_WIDGET.resource()
962 }
963
964 fn standard_shaders() -> [&'static BuiltInResource<Shader>; 8] {
965 [
966 &STANDARD,
967 &STANDARD_2D,
968 &STANDARD_PARTICLE_SYSTEM,
969 &STANDARD_SPRITE,
970 &STANDARD_TERRAIN,
971 &STANDARD_TWOSIDES,
972 &STANDARD_TILE,
973 &STANDARD_WIDGET,
974 ]
975 }
976}
977
978/// Standard shader.
979pub static STANDARD: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
980 BuiltInResource::new(
981 STANDARD_SHADER_NAME,
982 embedded_data_source!("standard/standard.shader"),
983 |data| {
984 ShaderResource::new_ok(
985 uuid!("87195f6e-cba4-4c27-9f89-d0bf726db965"),
986 ResourceKind::External,
987 Shader::from_string_bytes(data).unwrap(),
988 )
989 },
990 )
991});
992
993/// Standard 2D shader.
994pub static STANDARD_2D: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
995 BuiltInResource::new(
996 STANDARD_2D_SHADER_NAME,
997 embedded_data_source!("standard/standard2d.shader"),
998 |data| {
999 ShaderResource::new_ok(
1000 uuid!("55fa05b0-3c25-4e46-bae7-65f093185b75"),
1001 ResourceKind::External,
1002 Shader::from_string_bytes(data).unwrap(),
1003 )
1004 },
1005 )
1006});
1007
1008/// Standard particle system shader.
1009pub static STANDARD_PARTICLE_SYSTEM: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1010 BuiltInResource::new(
1011 STANDARD_PARTICLE_SYSTEM_SHADER_NAME,
1012 embedded_data_source!("standard/standard_particle_system.shader"),
1013 |data| {
1014 ShaderResource::new_ok(
1015 uuid!("eb474445-6a25-4481-bca9-f919699c300f"),
1016 ResourceKind::External,
1017 Shader::from_string_bytes(data).unwrap(),
1018 )
1019 },
1020 )
1021});
1022
1023/// Standard sprite shader.
1024pub static STANDARD_SPRITE: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1025 BuiltInResource::new(
1026 STANDARD_SPRITE_SHADER_NAME,
1027 embedded_data_source!("standard/standard_sprite.shader"),
1028 |data| {
1029 ShaderResource::new_ok(
1030 uuid!("a135826a-4c1b-46d5-ba1f-0c9a226aa52c"),
1031 ResourceKind::External,
1032 Shader::from_string_bytes(data).unwrap(),
1033 )
1034 },
1035 )
1036});
1037
1038/// Standard terrain shader.
1039pub static STANDARD_TERRAIN: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1040 BuiltInResource::new(
1041 STANDARD_TERRAIN_SHADER_NAME,
1042 embedded_data_source!("standard/terrain.shader"),
1043 |data| {
1044 ShaderResource::new_ok(
1045 uuid!("4911aafe-9bb1-4115-a958-25b57b87b51e"),
1046 ResourceKind::External,
1047 Shader::from_string_bytes(data).unwrap(),
1048 )
1049 },
1050 )
1051});
1052
1053/// Standard tile shader.
1054pub static STANDARD_TILE: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1055 BuiltInResource::new(
1056 STANDARD_TILE_SHADER_NAME,
1057 embedded_data_source!("standard/tile.shader"),
1058 |data| {
1059 ShaderResource::new_ok(
1060 uuid!("5f29dd3a-ea99-480c-bb02-d2c6420843b1"),
1061 ResourceKind::External,
1062 Shader::from_string_bytes(data).unwrap(),
1063 )
1064 },
1065 )
1066});
1067
1068/// Standard two-sides shader.
1069pub static STANDARD_TWOSIDES: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1070 BuiltInResource::new(
1071 STANDARD_TWOSIDES_SHADER_NAME,
1072 embedded_data_source!("standard/standard-two-sides.shader"),
1073 |data| {
1074 ShaderResource::new_ok(
1075 uuid!("f7979409-5185-4e1c-a644-d53cea64af8f"),
1076 ResourceKind::External,
1077 Shader::from_string_bytes(data).unwrap(),
1078 )
1079 },
1080 )
1081});
1082
1083/// Standard widget shader.
1084pub static STANDARD_WIDGET: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1085 BuiltInResource::new(
1086 STANDARD_WIDGET_SHADER_NAME,
1087 embedded_data_source!("standard/widget.shader"),
1088 |data| {
1089 ShaderResource::new_ok(
1090 uuid!("f5908aa4-e187-42a8-95d2-dc6577f6def4"),
1091 ResourceKind::External,
1092 Shader::from_string_bytes(data).unwrap(),
1093 )
1094 },
1095 )
1096});
1097
1098#[cfg(test)]
1099mod test {
1100 use crate::shader::{
1101 RenderPassDefinition, SamplerFallback, ShaderDefinition, ShaderResource,
1102 ShaderResourceDefinition, ShaderResourceExtension, ShaderResourceKind, ShaderSourceCode,
1103 };
1104 use fyrox_graphics::gpu_program::SamplerKind;
1105 use fyrox_resource::untyped::ResourceKind;
1106 use uuid::Uuid;
1107
1108 #[test]
1109 fn test_shader_load() {
1110 let code = r#"
1111 (
1112 name: "TestShader",
1113
1114 resources: [
1115 (
1116 name: "diffuseTexture",
1117 kind: Texture(kind: Sampler2D, fallback: White),
1118 binding: 0
1119 ),
1120 ],
1121
1122 passes: [
1123 (
1124 name: "GBuffer",
1125 draw_parameters: DrawParameters(
1126 cull_face: Some(Back),
1127 color_write: ColorMask(
1128 red: true,
1129 green: true,
1130 blue: true,
1131 alpha: true,
1132 ),
1133 depth_write: true,
1134 stencil_test: None,
1135 depth_test: Some(Less),
1136 blend: None,
1137 stencil_op: StencilOp(
1138 fail: Keep,
1139 zfail: Keep,
1140 zpass: Keep,
1141 write_mask: 0xFFFF_FFFF,
1142 ),
1143 scissor_box: None
1144 ),
1145 vertex_shader: "<CODE>",
1146 fragment_shader: "<CODE>",
1147 ),
1148 ],
1149 )
1150 "#;
1151
1152 let shader =
1153 ShaderResource::from_str(Uuid::new_v4(), code, ResourceKind::External).unwrap();
1154 let data = shader.data_ref();
1155
1156 let reference_definition = ShaderDefinition {
1157 name: "TestShader".to_owned(),
1158 resources: vec![ShaderResourceDefinition {
1159 name: "diffuseTexture".into(),
1160 kind: ShaderResourceKind::Texture {
1161 kind: SamplerKind::Sampler2D,
1162 fallback: SamplerFallback::White,
1163 },
1164 binding: 0,
1165 }],
1166 passes: vec![RenderPassDefinition {
1167 name: "GBuffer".to_string(),
1168 draw_parameters: Default::default(),
1169 vertex_shader: ShaderSourceCode("<CODE>".to_string()),
1170 vertex_shader_line: 35,
1171 fragment_shader: ShaderSourceCode("<CODE>".to_string()),
1172 fragment_shader_line: 36,
1173 }],
1174 disabled_passes: vec![],
1175 };
1176
1177 assert_eq!(data.definition, reference_definition);
1178 }
1179}