diff --git a/Cargo.toml b/Cargo.toml index 0ea4b71249..36f8cd4933 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,10 @@ path = "examples/hello_world.rs" name = "contributors" path = "examples/2d/contributors.rs" +[[example]] +name = "mesh" +path = "examples/2d/mesh.rs" + [[example]] name = "many_sprites" path = "examples/2d/many_sprites.rs" diff --git a/examples/2d/mesh.rs b/examples/2d/mesh.rs new file mode 100644 index 0000000000..7d46abaf59 --- /dev/null +++ b/examples/2d/mesh.rs @@ -0,0 +1,137 @@ +use bevy::{ + prelude::*, + render::{ + pipeline::{PipelineDescriptor, RenderPipeline}, + shader::{ShaderStage, ShaderStages}, + }, +}; + +fn main() { + App::build() + .add_plugins(DefaultPlugins) + .add_startup_system(star.system()) + .run(); +} + +fn star( + mut commands: Commands, + // We will add a new Mesh for the star being created + mut meshes: ResMut>, + // A pipeline will be added with custom shaders + mut pipelines: ResMut>, + // Access to add new shaders + mut shaders: ResMut>, +) { + // We first create a pipeline, which is the sequence of steps that are + // needed to get to pixels on the screen starting from a description of the + // geometries in the scene. Pipelines have fixed steps, which sometimes can + // be turned off (for instance, depth and stencil tests) and programmable + // steps, the vertex and fragment shaders, that we can customize writing + // shader programs. + + let pipeline_handle = pipelines.add(PipelineDescriptor::default_config(ShaderStages { + // Vertex shaders are run once for every vertex in the mesh. + // Each vertex can have attributes associated to it (e.g. position, + // color, texture mapping). The output of a shader is per-vertex. + vertex: shaders.add(Shader::from_glsl(ShaderStage::Vertex, VERTEX_SHADER)), + // Fragment shaders are run for each pixel belonging to a triangle on + // the screen. Their output is per-pixel. + fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))), + })); + + // Let's define the mesh for the object we want to draw: a nice star. + // We will specify here what kind of topology is used to define the mesh, + // that is, how triangles are built from the vertices. We will use a + // triangle list, meaning that each vertex of the triangle has to be + // specified. + let mut star = Mesh::new(bevy::render::pipeline::PrimitiveTopology::TriangleList); + + // Vertices need to have a position attribute. We will use the following + // vertices (I hope you can spot the star in the schema). + // + // 1 + // + // 10 2 + // 9 0 3 + // 8 4 + // 6 + // 7 5 + // + // These vertices are specificed in 3D space. + let mut v_pos = vec![[0.0, 0.0, 0.0]]; + for i in 0..10 { + // Angle of each vertex is 1/10 of TAU, plus PI/2 for positioning vertex 0 + let a = std::f32::consts::FRAC_PI_2 - i as f32 * std::f32::consts::TAU / 10.0; + // Radius of internal vertices (2, 4, 6, 8, 10) is 100, it's 200 for external + let r = (1 - i % 2) as f32 * 100.0 + 100.0; + // Add the vertex coordinates + v_pos.push([r * a.cos(), r * a.sin(), 0.0]); + } + // Set the position attribute + star.set_attribute(Mesh::ATTRIBUTE_POSITION, v_pos); + // And a RGB color attribute as well + let mut v_color = vec![[0.0, 0.0, 0.0]]; + v_color.extend_from_slice(&[[1.0, 1.0, 0.0]; 10]); + star.set_attribute("Vertex_Color", v_color); + + // Now, we specify the indices of the vertex that are going to compose the + // triangles in our star. Vertices in triangles have to be specified in CCW + // winding (that will be the front face, colored). Since we are using + // triangle list, we will specify each triangle as 3 vertices + // First triangle: 0, 2, 1 + // Second triangle: 0, 3, 2 + // Third triangle: 0, 4, 3 + // etc + // Last triangle: 0, 1, 10 + let mut indices = vec![0, 1, 10]; + for i in 2..=10 { + indices.extend_from_slice(&[0, i, i - 1]); + } + star.set_indices(Some(bevy::render::mesh::Indices::U32(indices))); + + // We can now spawn the entities for the star and the camera + commands.spawn_bundle(MeshBundle { + mesh: meshes.add(star), + render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::new( + pipeline_handle, + )]), + ..Default::default() + }); + commands + // And use an orthographic projection + .spawn_bundle(OrthographicCameraBundle::new_2d()); +} + +const VERTEX_SHADER: &str = r" +#version 450 + +layout(location = 0) in vec3 Vertex_Position; +layout(location = 1) in vec3 Vertex_Color; + +layout(location = 1) out vec3 v_Color; + +layout(set = 0, binding = 0) uniform CameraViewProj { + mat4 ViewProj; +}; + +layout(set = 1, binding = 0) uniform Transform { + mat4 Model; +}; + +void main() { + v_Color = Vertex_Color; + gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0); +} +"; + +const FRAGMENT_SHADER: &str = r" +#version 450 + +layout(location = 1) in vec3 v_Color; + +layout(location = 0) out vec4 o_Target; + +void main() { + o_Target = vec4(v_Color, 1.0); +} +"; diff --git a/examples/README.md b/examples/README.md index 29979f5498..4cb046bc48 100644 --- a/examples/README.md +++ b/examples/README.md @@ -73,6 +73,7 @@ Example | Main | Description --- | --- | --- `contributors` | [`2d/contributors.rs`](./2d/contributors.rs) | Displays each contributor as a bouncy bevy-ball! `many_sprites` | [`2d/many_sprites.rs`](./2d/many_sprites.rs) | Displays many sprites in a grid arragement! Used for performance testing. +`mesh` | [`2d/mesh.rs`](./2d/mesh.rs) | Renders a custom mesh `sprite` | [`2d/sprite.rs`](./2d/sprite.rs) | Renders a sprite `sprite_sheet` | [`2d/sprite_sheet.rs`](./2d/sprite_sheet.rs) | Renders an animated sprite `text2d` | [`2d/text2d.rs`](./2d/text2d.rs) | Generates text in 2d