Skip to main content

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 = "Default Shader";
490
491/// A name of the standard 2D shader.
492pub const STANDARD_2D_SHADER_NAME: &str = "2D Shader";
493
494/// A name of the standard particle system shader.
495pub const STANDARD_PARTICLE_SYSTEM_SHADER_NAME: &str = "Particle System Shader";
496
497/// A name of the standard two-sides shader.
498pub const STANDARD_TWOSIDES_SHADER_NAME: &str = "Two Sides Shader";
499
500/// A name of the standard terrain shader.
501pub const STANDARD_TERRAIN_SHADER_NAME: &str = "Terrain Shader";
502
503/// A name of the standard tile shader.
504pub const STANDARD_TILE_SHADER_NAME: &str = "Tile Shader";
505
506/// A name of the standard sprite shader.
507pub const STANDARD_SPRITE_SHADER_NAME: &str = "Sprite Shader";
508
509/// A name of the standard widget shader.
510pub const STANDARD_WIDGET_SHADER_NAME: &str = "Widget Shader";
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("projectionMatrix"),
690                        ShaderProperty::new_matrix3("worldMatrix"),
691                        ShaderProperty::new_color("solidColor"),
692                        ShaderProperty::new_vec4_f32_array(
693                            "gradientColors",
694                            Self::MAX_GRADIENT_VALUE_COUNT,
695                        ),
696                        ShaderProperty::new_f32_array(
697                            "gradientStops",
698                            Self::MAX_GRADIENT_VALUE_COUNT,
699                        ),
700                        ShaderProperty::new_vector2("gradientOrigin"),
701                        ShaderProperty::new_vector2("gradientEnd"),
702                        ShaderProperty::new_vector2("resolution"),
703                        ShaderProperty::new_vector2("boundsMin"),
704                        ShaderProperty::new_vector2("boundsMax"),
705                        ShaderProperty::new_bool("isFont"),
706                        ShaderProperty::new_float("opacity"),
707                        ShaderProperty::new_int("brushType"),
708                        ShaderProperty::new_int("gradientPointCount"),
709                    ]);
710                }
711                "fyrox_cameraData" => {
712                    properties.clear();
713                    properties.extend([
714                        ShaderProperty::new_matrix4("viewProjectionMatrix"),
715                        ShaderProperty::new_vector3("position"),
716                        ShaderProperty::new_vector3("upVector"),
717                        ShaderProperty::new_vector3("sideVector"),
718                        ShaderProperty::new_float("zNear"),
719                        ShaderProperty::new_float("zFar"),
720                        ShaderProperty::new_float("zRange"),
721                    ]);
722                }
723                "fyrox_lightData" => {
724                    properties.clear();
725                    properties.extend([
726                        ShaderProperty::new_vector3("lightPosition"),
727                        ShaderProperty::new_vector4("ambientLightColor"),
728                    ]);
729                }
730                "fyrox_graphicsSettings" => {
731                    properties.clear();
732                    properties.extend([ShaderProperty::new_bool("usePOM")]);
733                }
734                "fyrox_lightsBlock" => {
735                    properties.clear();
736                    properties.extend([
737                        ShaderProperty::new_int("lightCount"),
738                        ShaderProperty::new_vec4_f32_array("lightsColorRadius", Self::MAX_LIGHTS),
739                        ShaderProperty::new_vec2_f32_array("lightsParameters", Self::MAX_LIGHTS),
740                        ShaderProperty::new_vec3_f32_array("lightsPosition", Self::MAX_LIGHTS),
741                        ShaderProperty::new_vec3_f32_array("lightsDirection", Self::MAX_LIGHTS),
742                    ])
743                }
744                "fyrox_instanceData" => {
745                    properties.clear();
746                    properties.extend([
747                        ShaderProperty::new_matrix4("worldMatrix"),
748                        ShaderProperty::new_matrix4("worldViewProjection"),
749                        ShaderProperty::new_int("blendShapesCount"),
750                        ShaderProperty::new_bool("useSkeletalAnimation"),
751                        ShaderProperty::new_vec4_f32_array(
752                            "blendShapesWeights",
753                            Self::MAX_BLEND_SHAPE_WEIGHT_GROUPS,
754                        ),
755                    ]);
756                }
757                "fyrox_boneMatrices" => {
758                    properties.clear();
759                    properties.extend([ShaderProperty::new_mat4_f32_array(
760                        "matrices",
761                        Self::MAX_BONE_MATRICES,
762                    )])
763                }
764                _ => (),
765            }
766        }
767    }
768}
769
770impl Shader {
771    /// Creates a shader from file.
772    pub async fn from_file<P: AsRef<Path>>(
773        path: P,
774        io: &dyn ResourceIo,
775    ) -> Result<Self, ShaderError> {
776        let bytes = io.load_file(path.as_ref()).await?;
777        let content = String::from_utf8_lossy(&bytes);
778        Ok(Self {
779            definition: ShaderDefinition::from_str(&content)?,
780            cache_index: Default::default(),
781        })
782    }
783
784    /// Creates a shader from string.
785    pub fn from_string(str: &str) -> Result<Self, ShaderError> {
786        Ok(Self {
787            definition: ShaderDefinition::from_str(str)?,
788            cache_index: Default::default(),
789        })
790    }
791
792    /// Creates a shader from string represented as raw bytes. This function will fail if the `bytes`
793    /// does not contain Utf8-encoded string.
794    pub fn from_string_bytes(bytes: &[u8]) -> Result<Self, ShaderError> {
795        Ok(Self {
796            definition: ShaderDefinition::from_str(
797                std::str::from_utf8(bytes).map_err(|_| ShaderError::NotUtf8Source)?,
798            )?,
799            cache_index: Default::default(),
800        })
801    }
802
803    /// Tries to find a texture resource with the specified name.
804    pub fn find_texture_resource(&self, name: &str) -> Option<&ShaderResourceDefinition> {
805        self.definition.resources.iter().find(|res| {
806            res.name.as_str() == name && matches!(res.kind, ShaderResourceKind::Texture { .. })
807        })
808    }
809
810    /// Tries to find a property group resource with the specified name.
811    pub fn find_property_group_resource(&self, name: &str) -> Option<&ShaderResourceDefinition> {
812        self.definition.resources.iter().find(|res| {
813            res.name.as_str() == name && matches!(res.kind, ShaderResourceKind::PropertyGroup(_))
814        })
815    }
816
817    /// Checks if this shader has a texture resource with the specified name.
818    pub fn has_texture_resource(&self, name: &str) -> bool {
819        self.find_texture_resource(name).is_some()
820    }
821
822    /// Checks if this shader has a property group resource with the specified name.
823    pub fn has_property_group_resource(&self, name: &str) -> bool {
824        self.find_property_group_resource(name).is_some()
825    }
826}
827
828impl ResourceData for Shader {
829    fn type_uuid(&self) -> Uuid {
830        <Self as TypeUuidProvider>::type_uuid()
831    }
832
833    fn save(&mut self, path: &Path) -> Result<(), Box<dyn Error>> {
834        let mut file = File::create(path)?;
835        file.write_all(
836            ron::ser::to_string_pretty(&self.definition, PrettyConfig::default())?.as_bytes(),
837        )?;
838        Ok(())
839    }
840
841    fn can_be_saved(&self) -> bool {
842        true
843    }
844
845    fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
846        Some(Box::new(self.clone()))
847    }
848}
849
850/// A set of possible error variants that can occur during shader loading.
851#[derive(Debug)]
852pub enum ShaderError {
853    /// An i/o error has occurred.
854    Io(FileError),
855
856    /// A parsing error has occurred.
857    ParseError(ron::error::SpannedError),
858
859    /// Bytes does not represent Utf8-encoded string.
860    NotUtf8Source,
861}
862
863impl Display for ShaderError {
864    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
865        match self {
866            ShaderError::Io(v) => {
867                write!(f, "A file load error has occurred {v:?}")
868            }
869            ShaderError::ParseError(v) => {
870                write!(f, "A parsing error has occurred {v:?}")
871            }
872            ShaderError::NotUtf8Source => {
873                write!(f, "Bytes does not represent Utf8-encoded string.")
874            }
875        }
876    }
877}
878
879impl From<ron::error::SpannedError> for ShaderError {
880    fn from(e: ron::error::SpannedError) -> Self {
881        Self::ParseError(e)
882    }
883}
884
885impl From<FileError> for ShaderError {
886    fn from(e: FileError) -> Self {
887        Self::Io(e)
888    }
889}
890
891/// Type alias for shader resources.
892pub type ShaderResource = Resource<Shader>;
893
894/// Extension trait for shader resources.
895pub trait ShaderResourceExtension: Sized {
896    /// Creates new shader from given string. Input string must have the format defined in
897    /// examples for [`ShaderResource`].
898    fn from_str(id: Uuid, str: &str, kind: ResourceKind) -> Result<Self, ShaderError>;
899
900    /// Returns an instance of standard shader.
901    fn standard() -> Self;
902
903    /// Returns an instance of standard 2D shader.
904    fn standard_2d() -> Self;
905
906    /// Returns an instance of standard particle system shader.
907    fn standard_particle_system() -> Self;
908
909    /// Returns an instance of standard sprite shader.
910    fn standard_sprite() -> Self;
911
912    /// Returns an instance of standard terrain shader.
913    fn standard_terrain() -> Self;
914
915    /// Returns an instance of standard tile shader.
916    fn standard_tile() -> Self;
917
918    /// Returns an instance of standard two-sides terrain shader.
919    fn standard_twosides() -> Self;
920
921    /// Returns an instance of standard widget shader.
922    fn standard_widget() -> Self;
923
924    /// Returns a list of standard shader.
925    fn standard_shaders() -> [&'static BuiltInResource<Shader>; 8];
926}
927
928impl ShaderResourceExtension for ShaderResource {
929    fn from_str(id: Uuid, str: &str, kind: ResourceKind) -> Result<Self, ShaderError> {
930        Ok(Resource::new_ok(id, kind, Shader::from_string(str)?))
931    }
932
933    fn standard() -> Self {
934        STANDARD.resource()
935    }
936
937    fn standard_2d() -> Self {
938        STANDARD_2D.resource()
939    }
940
941    fn standard_particle_system() -> Self {
942        STANDARD_PARTICLE_SYSTEM.resource()
943    }
944
945    fn standard_sprite() -> Self {
946        STANDARD_SPRITE.resource()
947    }
948
949    fn standard_terrain() -> Self {
950        STANDARD_TERRAIN.resource()
951    }
952
953    fn standard_tile() -> Self {
954        STANDARD_TILE.resource()
955    }
956
957    fn standard_twosides() -> Self {
958        STANDARD_TWOSIDES.resource()
959    }
960
961    fn standard_widget() -> Self {
962        STANDARD_WIDGET.resource()
963    }
964
965    fn standard_shaders() -> [&'static BuiltInResource<Shader>; 8] {
966        [
967            &STANDARD,
968            &STANDARD_2D,
969            &STANDARD_PARTICLE_SYSTEM,
970            &STANDARD_SPRITE,
971            &STANDARD_TERRAIN,
972            &STANDARD_TWOSIDES,
973            &STANDARD_TILE,
974            &STANDARD_WIDGET,
975        ]
976    }
977}
978
979/// Standard shader.
980pub static STANDARD: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
981    BuiltInResource::new(
982        STANDARD_SHADER_NAME,
983        embedded_data_source!("standard/standard.shader"),
984        |data| {
985            ShaderResource::new_ok(
986                uuid!("87195f6e-cba4-4c27-9f89-d0bf726db965"),
987                ResourceKind::External,
988                Shader::from_string_bytes(data).unwrap(),
989            )
990        },
991    )
992});
993
994/// Standard 2D shader.
995pub static STANDARD_2D: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
996    BuiltInResource::new(
997        STANDARD_2D_SHADER_NAME,
998        embedded_data_source!("standard/standard2d.shader"),
999        |data| {
1000            ShaderResource::new_ok(
1001                uuid!("55fa05b0-3c25-4e46-bae7-65f093185b75"),
1002                ResourceKind::External,
1003                Shader::from_string_bytes(data).unwrap(),
1004            )
1005        },
1006    )
1007});
1008
1009/// Standard particle system shader.
1010pub static STANDARD_PARTICLE_SYSTEM: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1011    BuiltInResource::new(
1012        STANDARD_PARTICLE_SYSTEM_SHADER_NAME,
1013        embedded_data_source!("standard/standard_particle_system.shader"),
1014        |data| {
1015            ShaderResource::new_ok(
1016                uuid!("eb474445-6a25-4481-bca9-f919699c300f"),
1017                ResourceKind::External,
1018                Shader::from_string_bytes(data).unwrap(),
1019            )
1020        },
1021    )
1022});
1023
1024/// Standard sprite shader.
1025pub static STANDARD_SPRITE: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1026    BuiltInResource::new(
1027        STANDARD_SPRITE_SHADER_NAME,
1028        embedded_data_source!("standard/standard_sprite.shader"),
1029        |data| {
1030            ShaderResource::new_ok(
1031                uuid!("a135826a-4c1b-46d5-ba1f-0c9a226aa52c"),
1032                ResourceKind::External,
1033                Shader::from_string_bytes(data).unwrap(),
1034            )
1035        },
1036    )
1037});
1038
1039/// Standard terrain shader.
1040pub static STANDARD_TERRAIN: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1041    BuiltInResource::new(
1042        STANDARD_TERRAIN_SHADER_NAME,
1043        embedded_data_source!("standard/terrain.shader"),
1044        |data| {
1045            ShaderResource::new_ok(
1046                uuid!("4911aafe-9bb1-4115-a958-25b57b87b51e"),
1047                ResourceKind::External,
1048                Shader::from_string_bytes(data).unwrap(),
1049            )
1050        },
1051    )
1052});
1053
1054/// Standard tile shader.
1055pub static STANDARD_TILE: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1056    BuiltInResource::new(
1057        STANDARD_TILE_SHADER_NAME,
1058        embedded_data_source!("standard/tile.shader"),
1059        |data| {
1060            ShaderResource::new_ok(
1061                uuid!("5f29dd3a-ea99-480c-bb02-d2c6420843b1"),
1062                ResourceKind::External,
1063                Shader::from_string_bytes(data).unwrap(),
1064            )
1065        },
1066    )
1067});
1068
1069/// Standard two-sides shader.
1070pub static STANDARD_TWOSIDES: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1071    BuiltInResource::new(
1072        STANDARD_TWOSIDES_SHADER_NAME,
1073        embedded_data_source!("standard/standard-two-sides.shader"),
1074        |data| {
1075            ShaderResource::new_ok(
1076                uuid!("f7979409-5185-4e1c-a644-d53cea64af8f"),
1077                ResourceKind::External,
1078                Shader::from_string_bytes(data).unwrap(),
1079            )
1080        },
1081    )
1082});
1083
1084/// Standard widget shader.
1085pub static STANDARD_WIDGET: LazyLock<BuiltInResource<Shader>> = LazyLock::new(|| {
1086    BuiltInResource::new(
1087        STANDARD_WIDGET_SHADER_NAME,
1088        embedded_data_source!("standard/widget.shader"),
1089        |data| {
1090            ShaderResource::new_ok(
1091                uuid!("f5908aa4-e187-42a8-95d2-dc6577f6def4"),
1092                ResourceKind::External,
1093                Shader::from_string_bytes(data).unwrap(),
1094            )
1095        },
1096    )
1097});
1098
1099#[cfg(test)]
1100mod test {
1101    use crate::shader::{
1102        RenderPassDefinition, SamplerFallback, ShaderDefinition, ShaderResource,
1103        ShaderResourceDefinition, ShaderResourceExtension, ShaderResourceKind, ShaderSourceCode,
1104    };
1105    use fyrox_graphics::gpu_program::SamplerKind;
1106    use fyrox_resource::untyped::ResourceKind;
1107    use uuid::Uuid;
1108
1109    #[test]
1110    fn test_shader_load() {
1111        let code = r#"
1112            (
1113                name: "TestShader",
1114
1115                resources: [
1116                    (
1117                        name: "diffuseTexture",
1118                        kind: Texture(kind: Sampler2D, fallback: White),
1119                        binding: 0
1120                    ),
1121                ],
1122
1123                passes: [
1124                    (
1125                        name: "GBuffer",
1126                        draw_parameters: DrawParameters(
1127                            cull_face: Some(Back),
1128                            color_write: ColorMask(
1129                                red: true,
1130                                green: true,
1131                                blue: true,
1132                                alpha: true,
1133                            ),
1134                            depth_write: true,
1135                            stencil_test: None,
1136                            depth_test: Some(Less),
1137                            blend: None,
1138                            stencil_op: StencilOp(
1139                                fail: Keep,
1140                                zfail: Keep,
1141                                zpass: Keep,
1142                                write_mask: 0xFFFF_FFFF,
1143                            ),
1144                            scissor_box: None
1145                        ),
1146                        vertex_shader: "<CODE>",
1147                        fragment_shader: "<CODE>",
1148                    ),
1149                ],
1150            )
1151            "#;
1152
1153        let shader =
1154            ShaderResource::from_str(Uuid::new_v4(), code, ResourceKind::External).unwrap();
1155        let data = shader.data_ref();
1156
1157        let reference_definition = ShaderDefinition {
1158            name: "TestShader".to_owned(),
1159            resources: vec![ShaderResourceDefinition {
1160                name: "diffuseTexture".into(),
1161                kind: ShaderResourceKind::Texture {
1162                    kind: SamplerKind::Sampler2D,
1163                    fallback: SamplerFallback::White,
1164                },
1165                binding: 0,
1166            }],
1167            passes: vec![RenderPassDefinition {
1168                name: "GBuffer".to_string(),
1169                draw_parameters: Default::default(),
1170                vertex_shader: ShaderSourceCode("<CODE>".to_string()),
1171                vertex_shader_line: 35,
1172                fragment_shader: ShaderSourceCode("<CODE>".to_string()),
1173                fragment_shader_line: 36,
1174            }],
1175            disabled_passes: vec![],
1176        };
1177
1178        assert_eq!(data.definition, reference_definition);
1179    }
1180}