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}