fyrox_impl/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//!         // The following property groups are built-in and provides useful data for each shader.
55//!         (
56//!             name: "fyrox_instanceData",
57//!             kind: PropertyGroup([
58//!                 // Autogenerated
59//!             ]),
60//!             binding: 1
61//!         ),
62//!         (
63//!             name: "fyrox_boneMatrices",
64//!             kind: PropertyGroup([
65//!                 // Autogenerated
66//!             ]),
67//!             binding: 2
68//!         ),
69//!         (
70//!             name: "fyrox_graphicsSettings",
71//!             kind: PropertyGroup([
72//!                 // Autogenerated
73//!             ]),
74//!             binding: 3
75//!         ),
76//!         (
77//!             name: "fyrox_cameraData",
78//!             kind: PropertyGroup([
79//!                 // Autogenerated
80//!             ]),
81//!             binding: 4
82//!         ),
83//!         (
84//!             name: "fyrox_lightData",
85//!             kind: PropertyGroup([
86//!                 // Autogenerated
87//!             ]),
88//!             binding: 5
89//!         ),
90//!     ],
91//!
92//!     // A set of render passes (see a section `Render pass` for more info)
93//!     passes: [
94//!         (
95//!             // Name must match with the name of either standard render pass (see below) or
96//!             // one of your passes.
97//!             name: "Forward",
98//!
99//!             // A set of parameters that regulate renderer pipeline state.
100//!             // This is mandatory field of each render pass.
101//!             draw_parameters: DrawParameters(
102//!                 // A face to cull. Either Front or Back.
103//!                 cull_face: Some(Back),
104//!
105//!                 // Color mask. Defines which colors should be written to render target.
106//!                 color_write: ColorMask(
107//!                     red: true,
108//!                     green: true,
109//!                     blue: true,
110//!                     alpha: true,
111//!                 ),
112//!
113//!                 // Whether to modify depth buffer or not.
114//!                 depth_write: true,
115//!
116//!                 // Whether to use stencil test or not.
117//!                 stencil_test: None,
118//!
119//!                 // Whether to perform depth test when drawing.
120//!                 depth_test: Some(Less),
121//!
122//!                 // Blending options.
123//!                 blend: Some(BlendParameters(
124//!                     func: BlendFunc(
125//!                         sfactor: SrcAlpha,
126//!                         dfactor: OneMinusSrcAlpha,
127//!                         alpha_sfactor: SrcAlpha,
128//!                         alpha_dfactor: OneMinusSrcAlpha,
129//!                     ),
130//!                     equation: BlendEquation(
131//!                         rgb: Add,
132//!                         alpha: Add
133//!                     )
134//!                 )),
135//!
136//!                 // Stencil options.
137//!                 stencil_op: StencilOp(
138//!                     fail: Keep,
139//!                     zfail: Keep,
140//!                     zpass: Keep,
141//!                     write_mask: 0xFFFF_FFFF,
142//!                 ),
143//!
144//!                 // Scissor box. Could be something like this:
145//! 				//
146//! 				// scissor_box: Some(ScissorBox(
147//!                 //    x: 10,
148//!                 //    y: 20,
149//!                 //    width: 100,
150//!                 //    height: 30
151//!                 // ))
152//!                 scissor_box: None
153//!             ),
154//!
155//!             // Vertex shader code.
156//!             vertex_shader:
157//!                 r#"
158//!                 layout(location = 0) in vec3 vertexPosition;
159//!                 layout(location = 1) in vec2 vertexTexCoord;
160//!
161//!                 out vec2 texCoord;
162//!
163//!                 void main()
164//!                 {
165//!                     texCoord = vertexTexCoord;
166//!                     gl_Position = fyrox_instanceData.worldViewProjection * vec4(vertexPosition, 1.0);
167//!                 }
168//!                 "#,
169//!
170//!             // Fragment shader code.
171//!             fragment_shader:
172//!                 r#"
173//!                 out vec4 FragColor;
174//!
175//!                 in vec2 texCoord;
176//!
177//!                 void main()
178//!                 {
179//!                     FragColor = properties.diffuseColor * texture(diffuseTexture, texCoord);
180//!                 }
181//!                 "#,
182//!         )
183//!     ],
184//! )
185//! ```
186//!
187//! Shader should contain at least one render pass to actually do some job. A shader could not
188//! have properties at all. Currently only vertex and fragment programs are supported. Each
189//! program mush be written in GLSL. Comprehensive GLSL documentation can be found
190//! [here](https://www.khronos.org/opengl/wiki/Core_Language_(GLSL))
191//!
192//! # Render pass
193//!
194//! Modern rendering is a very complex thing that requires drawing an object multiple times
195//! with different "scripts". For example to draw an object with shadows you need to draw an
196//! object twice: one directly in a render target, and one in a shadow map. Such stages called
197//! render passes.
198//!
199//! Binding of shaders to render passes is done via names, each render pass has unique name.
200//!
201//! ## Predefined passes
202//!
203//! There are number of predefined render passes:
204//!
205//! - `GBuffer` - A pass that fills a set of textures (render targets) with various data about each
206//! rendered object (depth, normal, albedo, etc.). These textures then are used for physically-based
207//! lighting. Use this pass when you want the standard lighting to work with your objects.
208//!
209//! - `Forward` - A pass that draws an object directly in a render target. It could be used to render
210//! translucent objects.
211//!
212//! - `SpotShadow` - A pass that emits depth values for an object, later this depth map will be
213//! used to render shadows.
214//!
215//! - `PointShadow` - A pass that emits distance from a fragment to a point light, later this depth
216//! map will be used to render shadows.
217//!
218//! - `DirectionalShadow` - A pass that emits depth values for an object, later this depth map will be
219//! used to render shadows for directional cascaded shadows.
220//!
221//! # Resources
222//!
223//! Each shader requires a specific set of external resources that will be used during the rendering.
224//! This set is defined in `resources` section of the shader and could contain the following resources:
225//!
226//! - `Texture` - a texture of arbitrary type
227//! - `PropertyGroup` - a group of numeric properties.
228//!
229//! ## Binding points
230//!
231//! Shader resource must define a unique (over its type) binding index. The engine will use these
232//! points to prepare appropriate resource descriptor sets for GPU. Keep in mind, that binding point
233//! indices are **unique** per each type of resource. This means that a set of texture resource could
234//! use the same indices as property groups. The binding points must be unique in its group. If
235//! there are more than one resource of a certain type, that shares the same binding point, the
236//! engine will refuse to use such shader.
237//!
238//! ## Built-in resources
239//!
240//! There are number of built-in resources, that Fyrox will try to assign automatically if they're
241//! defined in your shader, something like this:
242//!
243//! ```ron
244//! (
245//!     name: "fyrox_instanceData",
246//!     kind: PropertyGroup([
247//!         // Autogenerated
248//!     ]),
249//!     binding: 1
250//! ),
251//! ```
252//!
253//! The full list of built-in resources is defined below.
254//!
255//! ### `fyrox_instanceData`
256//!
257//! Property group. Provided for each rendered surface instance.
258//!
259//! | Name                 | Type       | Description                                 |
260//! |----------------------|------------|---------------------------------------------|
261//! | worldMatrix          | `mat4`     | Local-to-world transformation.              |
262//! | worldViewProjection  | `mat4`     | Local-to-clip-space transform.              |
263//! | blendShapesCount     | `int`      | Total amount of blend shapes.               |
264//! | useSkeletalAnimation | `bool`     | Whether skinned meshes is rendering or not. |
265//! | blendShapesWeights   | `vec4[32]` | Blend shape weights.                        |
266//!
267//! ### `fyrox_boneMatrices`
268//!
269//! Property group. Provided for each rendered surface, that has skeletal animation.
270//!
271//! | Name     | Type        | Description   |
272//! |----------|-------------|---------------|
273//! | matrices | `mat4[256]` | Bone matrices |
274//!
275//!
276//! ### `fyrox_cameraData`
277//!
278//! Property group. Contains camera properties. It contains info not only about scene camera,
279//! but also observer info when rendering shadow maps. In other words - it is generic observer
280//! properties.
281//!
282//! | Name                 | Type       | Description                                      |
283//! |----------------------|------------|--------------------------------------------------|
284//! | viewProjectionMatrix | `mat4`     | World-to-clip-space transformation.              |
285//! | position             | `vec3`     | World-space position of the camera.              |
286//! | upVector             | `vec3`     | World-space up-vector of the camera.             |
287//! | sideVector           | `vec3`     | World-space side-vector of the camera.           |
288//! | zNear                | `float`    | Near clipping plane location.                    |
289//! | zFar                 | `float`    | Far clipping plane location.                     |
290//! | zRange               | `float`    | `zFar - zNear`                                   |
291//!
292//! ### `fyrox_lightData`
293//!
294//! Property group. Available only in shadow passes.
295//!
296//! | Name              | Type   | Description                                                |
297//! |-------------------|--------|------------------------------------------------------------|
298//! | lightPosition     | `vec3` | World-space light source position. Only for shadow passes. |
299//! | ambientLightColor | `vec4` | Ambient lighting color of the scene.                       |
300//!
301//! ### `fyrox_lightsBlock`
302//!
303//! Property group. Information about visible light sources
304//!
305//! | Name              | Type       | Description                                             |
306//! |------------------ |------------|---------------------------------------------------------|
307//! | lightCount        | `int`      | Total amount of light sources visible on screen.        |
308//! | lightsColorRadius | `vec4[16]` | Color (xyz) and radius (w) of light source              |
309//! | lightsParameters  | `vec2[16]` | Hot-spot cone angle cos (x) and half cone angle cos (y) |
310//! | lightsPosition    | `vec3[16]` | World-space light position.                             |
311//! | lightsDirection   | `vec3[16]` | World-space light direction                             |
312//!
313//! ### `fyrox_graphicsSettings`
314//!
315//! Property group. Contains graphics options of the renderer.
316//!
317//! | Name   | Type       | Description                                       |
318//! |--------|------------|---------------------------------------------------|
319//! | usePom | `bool`     | Whether to use parallax occlusion mapping or not. |
320//!
321//! ### `fyrox_sceneDepth`
322//!
323//! Texture. Contains depth values of scene. Available **only** after opaque geometry is
324//! rendered (read - G-Buffer is filled). Typical usage is something like this:
325//!
326//! ```ron
327//! (
328//!     name: "fyrox_sceneDepth",
329//!     kind: Texture(kind: Sampler2D, fallback: White),
330//!     binding: 1
331//! ),
332//! ```
333//!
334//! # Code generation
335//!
336//! Fyrox automatically generates code for resource bindings. This is made specifically to prevent
337//! subtle mistakes. For example when you define this set of resources:
338//!
339//! ```ron
340//! (
341//!     name: "MyShader",
342//!
343//!     resources: [
344//!         (
345//!             name: "diffuseTexture",
346//!             kind: Texture(kind: Sampler2D, fallback: White),
347//!             binding: 0
348//!         ),
349//!         (
350//!             name: "normalTexture",
351//!             kind: Texture(kind: Sampler2D, fallback: Normal),
352//!             binding: 1
353//!         ),
354//!         (
355//!             name: "properties",
356//!             kind: PropertyGroup([
357//!                 (
358//!                     name: "texCoordScale",
359//!                     kind: Vector2((1.0, 1.0)),
360//!                 ),
361//!                 (
362//!                     name: "diffuseColor",
363//!                     kind: Color(r: 255, g: 255, b: 255, a: 255),
364//!                 ),
365//!             ]),
366//!             binding: 0
367//!         ),
368//!     ]
369//! )
370//! ```
371//!
372//! The engine generates the following code and adds it to source code of every shader of every pass
373//! automatically:
374//!
375//! ```glsl
376//! uniform sampler2D diffuseTexture;
377//! uniform sampler2D normalTexture;
378//! struct Tproperties {
379//!     vec2 texCoordScale;
380//!     vec4 diffuseColor;
381//! };
382//! layout(std140) uniform Uproperties { Tproperties properties; }
383//! ```
384//!
385//! The most important thing is that the engine keeps properties in the `struct Tproperties` in
386//! correct order and forces `std140` layout on the generated uniform block. Since the engine knows
387//! the layout of the properties from their definition section, it could easily form a memory block
388//! with all required alignments and paddings that could be uploaded to GPU. The next important thing
389//! is that the engine batches all the data needed into a large chunks of data and uploads them
390//! all at once, which is much faster.
391//!
392//! # Drawing parameters
393//!
394//! Drawing parameters defines which GPU functions to use and at which state. For example, to render
395//! transparent objects you need to enable blending with specific blending rules. Or you need to disable
396//! culling to draw objects from both sides. This is when draw parameters comes in handy.
397//!
398//! There are relatively large list of drawing parameters, and it could confuse a person who didn't get
399//! used to work with graphics. The following list should help you to use drawing parameters correctly.
400//!
401//! - cull_face
402//!     - Defines which side of polygon should be culled.
403//!     - **Possible values:** `None`, [Some(CullFace::XXX)](crate::renderer::framework::CullFace)
404//!
405//! - color_write:
406//!     - Defines which components of color should be written to a render target
407//!     - **Possible values:** [ColorMask](crate::renderer::framework::ColorMask)(...)
408//!
409//!  - depth_write:
410//!     - Whether to modify depth buffer or not.
411//!     - **Possible values:** `true/false`
412//!
413//!  - stencil_test:
414//!     - Whether to use stencil test or not.
415//!     - **Possible values:**
416//!         - `None`
417//!         - Some([StencilFunc](crate::renderer::framework::StencilFunc))
418//!
419//!  - depth_test:
420//!      - Whether to perform depth test when drawing.
421//!      - **Possible values:** `true/false`
422//!
423//!   - blend:
424//!      - Blending options.
425//!      - **Possible values:**
426//!         - `None`
427//!         - Some([BlendFunc](crate::renderer::framework::BlendFunc))
428//!
429//!   - stencil_op:
430//!      - Stencil options.
431//!      - **Possible values:** [StencilOp](crate::renderer::framework::StencilOp)
432//!
433//! # Standard shader
434//!
435//! By default, Fyrox uses standard material for rendering, it covers 95% of uses cases and it is very
436//! flexible. To get standard shader instance, use [`ShaderResource::standard`]
437//!
438//! ```no_run
439//! # use fyrox_impl::material::shader::{ShaderResource, ShaderResourceExtension};
440//!
441//! let standard_shader = ShaderResource::standard();
442//! ```
443//!
444//! Usually you don't need to get this shader manually, using of [Material::standard](super::Material::standard)
445//! is enough.
446
447use crate::{
448    asset::{
449        embedded_data_source, io::ResourceIo, manager::BuiltInResource, untyped::ResourceKind,
450        Resource, ResourceData, SHADER_RESOURCE_UUID,
451    },
452    core::{
453        io::FileLoadError, reflect::prelude::*, sparse::AtomicIndex, uuid::Uuid,
454        visitor::prelude::*, TypeUuidProvider,
455    },
456    lazy_static::lazy_static,
457    renderer::framework::{
458        gpu_program::{ShaderProperty, ShaderPropertyKind},
459        DrawParameters,
460    },
461};
462use fyrox_core::algebra;
463pub use fyrox_graphics::gpu_program::{
464    SamplerFallback, ShaderResourceDefinition, ShaderResourceKind,
465};
466use ron::ser::PrettyConfig;
467use serde::{Deserialize, Serialize};
468use std::{
469    error::Error,
470    fmt::{Display, Formatter},
471    fs::File,
472    io::{Cursor, Write},
473    path::Path,
474    sync::Arc,
475};
476
477pub mod loader;
478
479/// A name of the standard shader.
480pub const STANDARD_SHADER_NAME: &str = "Standard";
481
482/// A source code of the standard shader.
483pub const STANDARD_SHADER_SRC: &str = include_str!("standard/standard.shader");
484
485/// A name of the standard 2D shader.
486pub const STANDARD_2D_SHADER_NAME: &str = "Standard2D";
487
488/// A source code of the standard 2D shader.
489pub const STANDARD_2D_SHADER_SRC: &str = include_str!("standard/standard2d.shader");
490
491/// A name of the standard particle system shader.
492pub const STANDARD_PARTICLE_SYSTEM_SHADER_NAME: &str = "StandardParticleSystem";
493
494/// A source code of the standard particle system shader.
495pub const STANDARD_PARTICLE_SYSTEM_SHADER_SRC: &str =
496    include_str!("standard/standard_particle_system.shader");
497
498/// A source code of the standard sprite shader.
499pub const STANDARD_SPRITE_SHADER_SRC: &str = include_str!("standard/standard_sprite.shader");
500
501/// A name of the standard two-sides shader.
502pub const STANDARD_TWOSIDES_SHADER_NAME: &str = "StandardTwoSides";
503
504/// A source code of the standard two-sides shader.
505pub const STANDARD_TWOSIDES_SHADER_SRC: &str = include_str!("standard/standard-two-sides.shader");
506
507/// A name of the standard terrain shader.
508pub const STANDARD_TERRAIN_SHADER_NAME: &str = "StandardTerrain";
509
510/// A name of the standard tile shader.
511pub const STANDARD_TILE_SHADER_NAME: &str = "StandardTile";
512
513/// A name of the standard sprite shader.
514pub const STANDARD_SPRITE_SHADER_NAME: &str = "StandardSprite";
515
516/// A source code of the standard terrain shader.
517pub const STANDARD_TERRAIN_SHADER_SRC: &str = include_str!("standard/terrain.shader");
518
519/// A list of names of standard shaders.
520pub const STANDARD_SHADER_NAMES: [&str; 6] = [
521    STANDARD_SHADER_NAME,
522    STANDARD_2D_SHADER_NAME,
523    STANDARD_PARTICLE_SYSTEM_SHADER_NAME,
524    STANDARD_SPRITE_SHADER_NAME,
525    STANDARD_TWOSIDES_SHADER_NAME,
526    STANDARD_TERRAIN_SHADER_NAME,
527];
528
529/// A list of source code of standard shaders.
530pub const STANDARD_SHADER_SOURCES: [&str; 6] = [
531    STANDARD_SHADER_SRC,
532    STANDARD_2D_SHADER_SRC,
533    STANDARD_PARTICLE_SYSTEM_SHADER_SRC,
534    STANDARD_SPRITE_SHADER_SRC,
535    STANDARD_TWOSIDES_SHADER_SRC,
536    STANDARD_TERRAIN_SHADER_SRC,
537];
538
539/// Internal state of the shader.
540///
541/// # Notes
542///
543/// Usually you don't need to access internals of the shader, but there sometimes could be a need to
544/// read shader definition, to get supported passes and properties.
545#[derive(Default, Debug, Reflect, Visit)]
546pub struct Shader {
547    /// Shader definition contains description of properties and render passes.
548    #[visit(optional)]
549    pub definition: ShaderDefinition,
550
551    #[reflect(hidden)]
552    #[visit(skip)]
553    pub(crate) cache_index: Arc<AtomicIndex>,
554}
555
556impl TypeUuidProvider for Shader {
557    fn type_uuid() -> Uuid {
558        SHADER_RESOURCE_UUID
559    }
560}
561
562/// A render pass definition. See [`ShaderResource`] docs for more info about render passes.
563#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Reflect, Visit)]
564pub struct RenderPassDefinition {
565    /// A name of render pass.
566    pub name: String,
567    /// A set of parameters that will be used in a render pass.
568    pub draw_parameters: DrawParameters,
569    /// A source code of vertex shader.
570    pub vertex_shader: String,
571    /// A source code of fragment shader.
572    pub fragment_shader: String,
573}
574
575/// A definition of the shader.
576#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Reflect, Visit)]
577pub struct ShaderDefinition {
578    /// A name of the shader.
579    pub name: String,
580    /// A set of render passes.
581    pub passes: Vec<RenderPassDefinition>,
582    /// A set of resource definitions.
583    pub resources: Vec<ShaderResourceDefinition>,
584}
585
586impl ShaderDefinition {
587    /// Maximum amount of simultaneous light sources that can be passed into a standard lights data
588    /// block.
589    pub const MAX_LIGHTS: usize = 16;
590
591    /// Maximum amount of bone matrices per shader.
592    pub const MAX_BONE_MATRICES: usize = 255;
593
594    /// Maximum amount of blend shape weight groups (packed weights of blend shapes into vec4).
595    pub const MAX_BLEND_SHAPE_WEIGHT_GROUPS: usize = 32;
596
597    fn from_buf(buf: Vec<u8>) -> Result<Self, ShaderError> {
598        let mut definition: ShaderDefinition = ron::de::from_reader(Cursor::new(buf))?;
599        definition.generate_built_in_resources();
600        Ok(definition)
601    }
602
603    fn from_str(str: &str) -> Result<Self, ShaderError> {
604        let mut definition: ShaderDefinition = ron::de::from_str(str)?;
605        definition.generate_built_in_resources();
606        Ok(definition)
607    }
608
609    fn generate_built_in_resources(&mut self) {
610        for resource in self.resources.iter_mut() {
611            let ShaderResourceKind::PropertyGroup(ref mut properties) = resource.kind else {
612                continue;
613            };
614
615            use ShaderPropertyKind::*;
616            match resource.name.as_str() {
617                "fyrox_cameraData" => {
618                    properties.clear();
619                    properties.extend([
620                        ShaderProperty::new(
621                            "viewProjectionMatrix",
622                            Matrix4(algebra::Matrix4::identity()),
623                        ),
624                        ShaderProperty::new("position", Vector3(Default::default())),
625                        ShaderProperty::new("upVector", Vector3(Default::default())),
626                        ShaderProperty::new("sideVector", Vector3(Default::default())),
627                        ShaderProperty::new("zNear", Float(0.0)),
628                        ShaderProperty::new("zFar", Float(0.0)),
629                        ShaderProperty::new("zRange", Float(0.0)),
630                    ]);
631                }
632                "fyrox_lightData" => {
633                    properties.clear();
634                    properties.extend([
635                        ShaderProperty::new("lightPosition", Vector3(Default::default())),
636                        ShaderProperty::new("ambientLightColor", Vector4(Default::default())),
637                    ]);
638                }
639                "fyrox_graphicsSettings" => {
640                    properties.clear();
641                    properties.extend([ShaderProperty::new("usePOM", Bool(false))]);
642                }
643                "fyrox_lightsBlock" => {
644                    properties.clear();
645                    properties.extend([
646                        ShaderProperty::new("lightCount", Int(0)),
647                        ShaderProperty::new(
648                            "lightsColorRadius",
649                            Vector4Array {
650                                value: Default::default(),
651                                max_len: Self::MAX_LIGHTS,
652                            },
653                        ),
654                        ShaderProperty::new(
655                            "lightsParameters",
656                            Vector2Array {
657                                value: Default::default(),
658                                max_len: Self::MAX_LIGHTS,
659                            },
660                        ),
661                        ShaderProperty::new(
662                            "lightsPosition",
663                            Vector3Array {
664                                value: Default::default(),
665                                max_len: Self::MAX_LIGHTS,
666                            },
667                        ),
668                        ShaderProperty::new(
669                            "lightsDirection",
670                            Vector3Array {
671                                value: Default::default(),
672                                max_len: Self::MAX_LIGHTS,
673                            },
674                        ),
675                    ])
676                }
677                "fyrox_instanceData" => {
678                    properties.clear();
679                    properties.extend([
680                        ShaderProperty::new("worldMatrix", Matrix4(algebra::Matrix4::identity())),
681                        ShaderProperty::new(
682                            "worldViewProjection",
683                            Matrix4(algebra::Matrix4::identity()),
684                        ),
685                        ShaderProperty::new("blendShapesCount", Int(0)),
686                        ShaderProperty::new("useSkeletalAnimation", Bool(false)),
687                        ShaderProperty::new(
688                            "blendShapesWeights",
689                            Vector4Array {
690                                value: Default::default(),
691                                max_len: Self::MAX_BLEND_SHAPE_WEIGHT_GROUPS,
692                            },
693                        ),
694                    ]);
695                }
696                "fyrox_boneMatrices" => {
697                    properties.clear();
698                    properties.extend([ShaderProperty::new(
699                        "matrices",
700                        Matrix4Array {
701                            value: Default::default(),
702                            max_len: Self::MAX_BONE_MATRICES,
703                        },
704                    )])
705                }
706                _ => (),
707            }
708        }
709    }
710}
711
712impl Shader {
713    /// Creates a shader from file.
714    pub async fn from_file<P: AsRef<Path>>(
715        path: P,
716        io: &dyn ResourceIo,
717    ) -> Result<Self, ShaderError> {
718        let content = io.load_file(path.as_ref()).await?;
719        Ok(Self {
720            definition: ShaderDefinition::from_buf(content)?,
721            cache_index: Default::default(),
722        })
723    }
724
725    /// Creates a shader from string.
726    pub fn from_string(str: &str) -> Result<Self, ShaderError> {
727        Ok(Self {
728            definition: ShaderDefinition::from_str(str)?,
729            cache_index: Default::default(),
730        })
731    }
732
733    /// Creates a shader from string represented as raw bytes. This function will fail if the `bytes`
734    /// does not contain Utf8-encoded string.
735    pub fn from_string_bytes(bytes: &[u8]) -> Result<Self, ShaderError> {
736        Ok(Self {
737            definition: ShaderDefinition::from_str(
738                std::str::from_utf8(bytes).map_err(|_| ShaderError::NotUtf8Source)?,
739            )?,
740            cache_index: Default::default(),
741        })
742    }
743}
744
745impl ResourceData for Shader {
746    fn type_uuid(&self) -> Uuid {
747        <Self as TypeUuidProvider>::type_uuid()
748    }
749
750    fn save(&mut self, path: &Path) -> Result<(), Box<dyn Error>> {
751        let mut file = File::create(path)?;
752        file.write_all(
753            ron::ser::to_string_pretty(&self.definition, PrettyConfig::default())?.as_bytes(),
754        )?;
755        Ok(())
756    }
757
758    fn can_be_saved(&self) -> bool {
759        true
760    }
761}
762
763/// A set of possible error variants that can occur during shader loading.
764#[derive(Debug)]
765pub enum ShaderError {
766    /// An i/o error has occurred.
767    Io(FileLoadError),
768
769    /// A parsing error has occurred.
770    ParseError(ron::error::SpannedError),
771
772    /// Bytes does not represent Utf8-encoded string.
773    NotUtf8Source,
774}
775
776impl Display for ShaderError {
777    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
778        match self {
779            ShaderError::Io(v) => {
780                write!(f, "A file load error has occurred {v:?}")
781            }
782            ShaderError::ParseError(v) => {
783                write!(f, "A parsing error has occurred {v:?}")
784            }
785            ShaderError::NotUtf8Source => {
786                write!(f, "Bytes does not represent Utf8-encoded string.")
787            }
788        }
789    }
790}
791
792impl From<ron::error::SpannedError> for ShaderError {
793    fn from(e: ron::error::SpannedError) -> Self {
794        Self::ParseError(e)
795    }
796}
797
798impl From<FileLoadError> for ShaderError {
799    fn from(e: FileLoadError) -> Self {
800        Self::Io(e)
801    }
802}
803
804/// Type alias for shader resources.
805pub type ShaderResource = Resource<Shader>;
806
807/// Extension trait for shader resources.
808pub trait ShaderResourceExtension: Sized {
809    /// Creates new shader from given string. Input string must have the format defined in
810    /// examples for [`ShaderResource`].
811    fn from_str(str: &str, kind: ResourceKind) -> Result<Self, ShaderError>;
812
813    /// Returns an instance of standard shader.
814    fn standard() -> Self;
815
816    /// Returns an instance of standard 2D shader.
817    fn standard_2d() -> Self;
818
819    /// Returns an instance of standard particle system shader.
820    fn standard_particle_system() -> Self;
821
822    /// Returns an instance of standard sprite shader.
823    fn standard_sprite() -> Self;
824
825    /// Returns an instance of standard terrain shader.
826    fn standard_terrain() -> Self;
827
828    /// Returns an instance of standard tile shader.
829    fn standard_tile() -> Self;
830
831    /// Returns an instance of standard two-sides terrain shader.
832    fn standard_twosides() -> Self;
833
834    /// Returns a list of standard shader.
835    fn standard_shaders() -> [&'static BuiltInResource<Shader>; 7];
836}
837
838impl ShaderResourceExtension for ShaderResource {
839    fn from_str(str: &str, kind: ResourceKind) -> Result<Self, ShaderError> {
840        Ok(Resource::new_ok(kind, Shader::from_string(str)?))
841    }
842
843    fn standard() -> Self {
844        STANDARD.resource()
845    }
846
847    fn standard_2d() -> Self {
848        STANDARD_2D.resource()
849    }
850
851    fn standard_particle_system() -> Self {
852        STANDARD_PARTICLE_SYSTEM.resource()
853    }
854
855    fn standard_sprite() -> Self {
856        STANDARD_SPRITE.resource()
857    }
858
859    fn standard_terrain() -> Self {
860        STANDARD_TERRAIN.resource()
861    }
862
863    fn standard_tile() -> Self {
864        STANDARD_TILE.resource()
865    }
866
867    fn standard_twosides() -> Self {
868        STANDARD_TWOSIDES.resource()
869    }
870
871    fn standard_shaders() -> [&'static BuiltInResource<Shader>; 7] {
872        [
873            &STANDARD,
874            &STANDARD_2D,
875            &STANDARD_PARTICLE_SYSTEM,
876            &STANDARD_SPRITE,
877            &STANDARD_TERRAIN,
878            &STANDARD_TWOSIDES,
879            &STANDARD_TILE,
880        ]
881    }
882}
883
884lazy_static! {
885    static ref STANDARD: BuiltInResource<Shader> =
886        BuiltInResource::new(embedded_data_source!("standard/standard.shader"), |data| {
887            ShaderResource::new_ok(
888                STANDARD_SHADER_NAME.into(),
889                Shader::from_string_bytes(data).unwrap(),
890            )
891        });
892    static ref STANDARD_2D: BuiltInResource<Shader> = BuiltInResource::new(
893        embedded_data_source!("standard/standard2d.shader"),
894        |data| ShaderResource::new_ok(
895            STANDARD_2D_SHADER_NAME.into(),
896            Shader::from_string_bytes(data).unwrap(),
897        )
898    );
899    static ref STANDARD_PARTICLE_SYSTEM: BuiltInResource<Shader> = BuiltInResource::new(
900        embedded_data_source!("standard/standard_particle_system.shader"),
901        |data| ShaderResource::new_ok(
902            STANDARD_PARTICLE_SYSTEM_SHADER_NAME.into(),
903            Shader::from_string_bytes(data).unwrap(),
904        )
905    );
906    static ref STANDARD_SPRITE: BuiltInResource<Shader> = BuiltInResource::new(
907        embedded_data_source!("standard/standard_sprite.shader"),
908        |data| ShaderResource::new_ok(
909            STANDARD_SPRITE_SHADER_NAME.into(),
910            Shader::from_string_bytes(data).unwrap(),
911        )
912    );
913    static ref STANDARD_TERRAIN: BuiltInResource<Shader> =
914        BuiltInResource::new(embedded_data_source!("standard/terrain.shader"), |data| {
915            ShaderResource::new_ok(
916                STANDARD_TERRAIN_SHADER_NAME.into(),
917                Shader::from_string_bytes(data).unwrap(),
918            )
919        });
920    static ref STANDARD_TILE: BuiltInResource<Shader> =
921        BuiltInResource::new(embedded_data_source!("standard/tile.shader"), |data| {
922            ShaderResource::new_ok(
923                STANDARD_TILE_SHADER_NAME.into(),
924                Shader::from_string_bytes(data).unwrap(),
925            )
926        });
927    static ref STANDARD_TWOSIDES: BuiltInResource<Shader> = BuiltInResource::new(
928        embedded_data_source!("standard/standard-two-sides.shader"),
929        |data| ShaderResource::new_ok(
930            STANDARD_TWOSIDES_SHADER_NAME.into(),
931            Shader::from_string_bytes(data).unwrap(),
932        )
933    );
934}
935
936#[cfg(test)]
937mod test {
938    use crate::material::shader::{
939        RenderPassDefinition, SamplerFallback, ShaderDefinition, ShaderResource,
940        ShaderResourceDefinition, ShaderResourceExtension, ShaderResourceKind,
941    };
942    use fyrox_graphics::gpu_program::SamplerKind;
943
944    #[test]
945    fn test_shader_load() {
946        let code = r#"
947            (
948                name: "TestShader",
949
950                resources: [
951                    (
952                        name: "diffuseTexture",
953                        kind: Texture(kind: Sampler2D, fallback: White),
954                        binding: 0
955                    ),
956                ],
957
958                passes: [
959                    (
960                        name: "GBuffer",
961                        draw_parameters: DrawParameters(
962                            cull_face: Some(Back),
963                            color_write: ColorMask(
964                                red: true,
965                                green: true,
966                                blue: true,
967                                alpha: true,
968                            ),
969                            depth_write: true,
970                            stencil_test: None,
971                            depth_test: Some(Less),
972                            blend: None,
973                            stencil_op: StencilOp(
974                                fail: Keep,
975                                zfail: Keep,
976                                zpass: Keep,
977                                write_mask: 0xFFFF_FFFF,
978                            ),
979                            scissor_box: None
980                        ),
981                        vertex_shader: "<CODE>",
982                        fragment_shader: "<CODE>",
983                    ),
984                ],
985            )
986            "#;
987
988        let shader = ShaderResource::from_str(code, "test".into()).unwrap();
989        let data = shader.data_ref();
990
991        let reference_definition = ShaderDefinition {
992            name: "TestShader".to_owned(),
993            resources: vec![ShaderResourceDefinition {
994                name: "diffuseTexture".into(),
995                kind: ShaderResourceKind::Texture {
996                    kind: SamplerKind::Sampler2D,
997                    fallback: SamplerFallback::White,
998                },
999                binding: 0,
1000            }],
1001            passes: vec![RenderPassDefinition {
1002                name: "GBuffer".to_string(),
1003                draw_parameters: Default::default(),
1004                vertex_shader: "<CODE>".to_string(),
1005                fragment_shader: "<CODE>".to_string(),
1006            }],
1007        };
1008
1009        assert_eq!(data.definition, reference_definition);
1010    }
1011}