bevy/examples/shader/animate_shader.rs
Paweł Grabarz 07ed1d053e Implement and require #[derive(Component)] on all component structs (#2254)
This implements the most minimal variant of #1843 - a derive for marker trait. This is a prerequisite to more complicated features like statically defined storage type or opt-out component reflection.

In order to make component struct's purpose explicit and avoid misuse, it must be annotated with `#[derive(Component)]` (manual impl is discouraged for compatibility). Right now this is just a marker trait, but in the future it might be expanded. Making this change early allows us to make further changes later without breaking backward compatibility for derive macro users.

This already prevents a lot of issues, like using bundles in `insert` calls. Primitive types are no longer valid components as well. This can be easily worked around by adding newtype wrappers and deriving `Component` for them.

One funny example of prevented bad code (from our own tests) is when an newtype struct or enum variant is used. Previously, it was possible to write `insert(Newtype)` instead of `insert(Newtype(value))`. That code compiled, because function pointers (in this case newtype struct constructor) implement `Send + Sync + 'static`, so we allowed them to be used as components. This is no longer the case and such invalid code will trigger a compile error.


Co-authored-by: = <=>
Co-authored-by: TheRawMeatball <therawmeatball@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-10-03 19:23:44 +00:00

125 lines
3.7 KiB
Rust

use bevy::{
prelude::*,
reflect::TypeUuid,
render::{
mesh::shape,
pipeline::{PipelineDescriptor, RenderPipeline},
render_graph::{base, RenderGraph, RenderResourcesNode},
renderer::RenderResources,
shader::{ShaderStage, ShaderStages},
},
};
/// This example shows how to animate a shader, by passing the global `time.seconds_since_startup()`
/// via a 'TimeComponent` to the shader.
pub fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(animate_shader)
.run();
}
#[derive(Component, RenderResources, Default, TypeUuid)]
#[uuid = "463e4b8a-d555-4fc2-ba9f-4c880063ba92"]
struct TimeUniform {
value: f32,
}
const VERTEX_SHADER: &str = r#"
#version 450
layout(location = 0) in vec3 Vertex_Position;
layout(location = 1) in vec2 Vertex_Uv;
layout(location = 0) out vec2 v_Uv;
layout(set = 0, binding = 0) uniform CameraViewProj {
mat4 ViewProj;
};
layout(set = 1, binding = 0) uniform Transform {
mat4 Model;
};
void main() {
gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0);
v_Uv = Vertex_Uv;
}
"#;
const FRAGMENT_SHADER: &str = r#"
#version 450
layout(location = 0) in vec2 v_Uv;
layout(location = 0) out vec4 o_Target;
layout(set = 2, binding = 0) uniform TimeUniform_value {
float time;
};
void main() {
float speed = 0.7;
float translation = sin(time * speed);
float percentage = 0.6;
float threshold = v_Uv.x + translation * percentage;
vec3 red = vec3(1., 0., 0.);
vec3 blue = vec3(0., 0., 1.);
vec3 mixed = mix(red, blue, threshold);
o_Target = vec4(mixed, 1.0);
}
"#;
fn setup(
mut commands: Commands,
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
mut shaders: ResMut<Assets<Shader>>,
mut meshes: ResMut<Assets<Mesh>>,
mut render_graph: ResMut<RenderGraph>,
) {
// Create a new shader pipeline.
let pipeline_handle = pipelines.add(PipelineDescriptor::default_config(ShaderStages {
vertex: shaders.add(Shader::from_glsl(ShaderStage::Vertex, VERTEX_SHADER)),
fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))),
}));
// Add a `RenderResourcesNode` to our `RenderGraph`. This will bind `TimeComponent` to our
// shader.
render_graph.add_system_node(
"time_uniform",
RenderResourcesNode::<TimeUniform>::new(true),
);
// Add a `RenderGraph` edge connecting our new "time_component" node to the main pass node. This
// ensures that "time_component" runs before the main pass.
render_graph
.add_node_edge("time_uniform", base::node::MAIN_PASS)
.unwrap();
// Spawn a quad and insert the `TimeComponent`.
commands
.spawn_bundle(MeshBundle {
mesh: meshes.add(Mesh::from(shape::Quad::new(Vec2::new(5.0, 5.0)))),
render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::new(
pipeline_handle,
)]),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..Default::default()
})
.insert(TimeUniform { value: 0.0 });
// Spawn a camera.
commands.spawn_bundle(PerspectiveCameraBundle {
transform: Transform::from_xyz(0.0, 0.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y),
..Default::default()
});
}
/// In this system we query for the `TimeComponent` and global `Time` resource, and set
/// `time.seconds_since_startup()` as the `value` of the `TimeComponent`. This value will be
/// accessed by the fragment shader and used to animate the shader.
fn animate_shader(time: Res<Time>, mut query: Query<&mut TimeUniform>) {
let mut time_uniform = query.single_mut();
time_uniform.value = time.seconds_since_startup() as f32;
}