Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Mesh vertex buffer layouts #3959

Closed
wants to merge 12 commits into from
Closed
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,10 @@ name = "scene"
path = "examples/scene/scene.rs"

# Shaders
[[example]]
name = "custom_vertex_attribute"
path = "examples/shader/custom_vertex_attribute.rs"

[[example]]
name = "shader_defs"
path = "examples/shader/shader_defs.rs"
Expand Down
40 changes: 40 additions & 0 deletions assets/shaders/custom_vertex_attribute.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#import bevy_pbr::mesh_view_bind_group
#import bevy_pbr::mesh_struct

struct Vertex {
[[location(0)]] position: vec3<f32>;
[[location(1)]] blend_color: vec4<f32>;
};

struct CustomMaterial {
color: vec4<f32>;
};
[[group(1), binding(0)]]
var<uniform> material: CustomMaterial;

[[group(2), binding(0)]]
var<uniform> mesh: Mesh;

struct VertexOutput {
[[builtin(position)]] clip_position: vec4<f32>;
[[location(0)]] blend_color: vec4<f32>;
};

[[stage(vertex)]]
fn vertex(vertex: Vertex) -> VertexOutput {
let world_position = mesh.model * vec4<f32>(vertex.position, 1.0);

var out: VertexOutput;
out.clip_position = view.view_proj * world_position;
out.blend_color = vertex.blend_color;
return out;
}

struct FragmentInput {
[[location(0)]] blend_color: vec4<f32>;
};

[[stage(fragment)]]
fn fragment(input: FragmentInput) -> [[location(0)]] vec4<f32> {
return material.color * input.blend_color;
}
12 changes: 6 additions & 6 deletions crates/bevy_gltf/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,40 +125,40 @@ async fn load_gltf<'a, 'b>(
.read_positions()
.map(|v| VertexAttributeValues::Float32x3(v.collect()))
{
mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vertex_attribute);
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertex_attribute);
}

if let Some(vertex_attribute) = reader
.read_normals()
.map(|v| VertexAttributeValues::Float32x3(v.collect()))
{
mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, vertex_attribute);
mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vertex_attribute);
}

if let Some(vertex_attribute) = reader
.read_tangents()
.map(|v| VertexAttributeValues::Float32x4(v.collect()))
{
mesh.set_attribute(Mesh::ATTRIBUTE_TANGENT, vertex_attribute);
mesh.insert_attribute(Mesh::ATTRIBUTE_TANGENT, vertex_attribute);
}

if let Some(vertex_attribute) = reader
.read_tex_coords(0)
.map(|v| VertexAttributeValues::Float32x2(v.into_f32().collect()))
{
mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, vertex_attribute);
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vertex_attribute);
} else {
let len = mesh.count_vertices();
let uvs = vec![[0.0, 0.0]; len];
bevy_log::debug!("missing `TEXCOORD_0` vertex attribute, loading zeroed out UVs");
mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
}

// if let Some(vertex_attribute) = reader
// .read_colors(0)
// .map(|v| VertexAttributeValues::Float32x4(v.into_rgba_f32().collect()))
// {
// mesh.set_attribute(Mesh::ATTRIBUTE_COLOR, vertex_attribute);
// mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_attribute);
// }

if let Some(indices) = reader.read_indices() {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use bevy_render::{
prelude::Color,
render_graph::RenderGraph,
render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions},
render_resource::{Shader, SpecializedPipelines},
render_resource::{Shader, SpecializedMeshPipelines},
view::VisibilitySystems,
RenderApp, RenderStage,
};
Expand Down Expand Up @@ -178,7 +178,7 @@ impl Plugin for PbrPlugin {
.init_resource::<DrawFunctions<Shadow>>()
.init_resource::<LightMeta>()
.init_resource::<GlobalLightMeta>()
.init_resource::<SpecializedPipelines<ShadowPipeline>>();
.init_resource::<SpecializedMeshPipelines<ShadowPipeline>>();

let shadow_pass_node = ShadowPassNode::new(&mut render_app.world);
render_app.add_render_command::<Shadow, DrawShadowMesh>();
Expand Down
178 changes: 109 additions & 69 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use bevy_ecs::{
world::FromWorld,
};
use bevy_render::{
mesh::Mesh,
mesh::{Mesh, MeshVertexBufferLayout},
render_asset::{RenderAsset, RenderAssetPlugin, RenderAssets},
render_component::ExtractComponentPlugin,
render_phase::{
Expand All @@ -24,12 +24,13 @@ use bevy_render::{
},
render_resource::{
BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader,
SpecializedPipeline, SpecializedPipelines,
SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines,
},
renderer::RenderDevice,
view::{ExtractedView, Msaa, VisibleEntities},
RenderApp, RenderStage,
};
use bevy_utils::tracing::error;
use std::hash::Hash;
use std::marker::PhantomData;

Expand Down Expand Up @@ -72,6 +73,16 @@ pub trait Material: Asset + RenderAsset {
fn dynamic_uniform_indices(material: &<Self as RenderAsset>::PreparedAsset) -> &[u32] {
&[]
}

/// Customizes the default [`RenderPipelineDescriptor`].
#[allow(unused_variables)]
#[inline]
fn specialize(
descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout,
) -> Result<(), SpecializedMeshPipelineError> {
Ok(())
}
}

impl<M: Material> SpecializedMaterial for M {
Expand All @@ -81,7 +92,13 @@ impl<M: Material> SpecializedMaterial for M {
fn key(_material: &<Self as RenderAsset>::PreparedAsset) -> Self::Key {}

#[inline]
fn specialize(_key: Self::Key, _descriptor: &mut RenderPipelineDescriptor) {}
fn specialize(
_descriptor: &mut RenderPipelineDescriptor,
_key: Self::Key,
_layout: &MeshVertexBufferLayout,
) -> Result<(), SpecializedMeshPipelineError> {
cart marked this conversation as resolved.
Show resolved Hide resolved
<M as Material>::specialize(_descriptor, _layout)
}

#[inline]
fn bind_group(material: &<Self as RenderAsset>::PreparedAsset) -> &BindGroup {
Expand Down Expand Up @@ -130,7 +147,11 @@ pub trait SpecializedMaterial: Asset + RenderAsset {
fn key(material: &<Self as RenderAsset>::PreparedAsset) -> Self::Key;

/// Specializes the given `descriptor` according to the given `key`.
fn specialize(key: Self::Key, descriptor: &mut RenderPipelineDescriptor);
fn specialize(
descriptor: &mut RenderPipelineDescriptor,
key: Self::Key,
layout: &MeshVertexBufferLayout,
) -> Result<(), SpecializedMeshPipelineError>;

/// Returns this material's [`BindGroup`]. This should match the layout returned by [`SpecializedMaterial::bind_group_layout`].
fn bind_group(material: &<Self as RenderAsset>::PreparedAsset) -> &BindGroup;
Expand Down Expand Up @@ -188,12 +209,18 @@ impl<M: SpecializedMaterial> Plugin for MaterialPlugin<M> {
.add_render_command::<Opaque3d, DrawMaterial<M>>()
.add_render_command::<AlphaMask3d, DrawMaterial<M>>()
.init_resource::<MaterialPipeline<M>>()
.init_resource::<SpecializedPipelines<MaterialPipeline<M>>>()
.init_resource::<SpecializedMeshPipelines<MaterialPipeline<M>>>()
.add_system_to_stage(RenderStage::Queue, queue_material_meshes::<M>);
}
}
}

#[derive(Eq, PartialEq, Clone, Hash)]
pub struct MaterialPipelineKey<T> {
mesh_key: MeshPipelineKey,
material_key: T,
}

pub struct MaterialPipeline<M: SpecializedMaterial> {
pub mesh_pipeline: MeshPipeline,
pub material_layout: BindGroupLayout,
Expand All @@ -202,11 +229,15 @@ pub struct MaterialPipeline<M: SpecializedMaterial> {
marker: PhantomData<M>,
}

impl<M: SpecializedMaterial> SpecializedPipeline for MaterialPipeline<M> {
type Key = (MeshPipelineKey, M::Key);
impl<M: SpecializedMaterial> SpecializedMeshPipeline for MaterialPipeline<M> {
type Key = MaterialPipelineKey<M::Key>;

fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
let mut descriptor = self.mesh_pipeline.specialize(key.0);
fn specialize(
&self,
key: Self::Key,
layout: &MeshVertexBufferLayout,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?;
if let Some(vertex_shader) = &self.vertex_shader {
descriptor.vertex.shader = vertex_shader.clone();
}
Expand All @@ -220,8 +251,8 @@ impl<M: SpecializedMaterial> SpecializedPipeline for MaterialPipeline<M> {
self.mesh_pipeline.mesh_layout.clone(),
]);

M::specialize(key.1, &mut descriptor);
descriptor
M::specialize(&mut descriptor, key.material_key, layout)?;
Ok(descriptor)
}
}

Expand Down Expand Up @@ -275,7 +306,7 @@ pub fn queue_material_meshes<M: SpecializedMaterial>(
alpha_mask_draw_functions: Res<DrawFunctions<AlphaMask3d>>,
transparent_draw_functions: Res<DrawFunctions<Transparent3d>>,
material_pipeline: Res<MaterialPipeline<M>>,
mut pipelines: ResMut<SpecializedPipelines<MaterialPipeline<M>>>,
mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
mut pipeline_cache: ResMut<RenderPipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
Expand Down Expand Up @@ -307,72 +338,81 @@ pub fn queue_material_meshes<M: SpecializedMaterial>(

let inverse_view_matrix = view.transform.compute_matrix().inverse();
let inverse_view_row_2 = inverse_view_matrix.row(2);
let mesh_key = MeshPipelineKey::from_msaa_samples(msaa.samples);
let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples);

for visible_entity in &visible_entities.entities {
if let Ok((material_handle, mesh_handle, mesh_uniform)) =
material_meshes.get(*visible_entity)
{
if let Some(material) = render_materials.get(material_handle) {
let mut mesh_key = mesh_key;
if let Some(mesh) = render_meshes.get(mesh_handle) {
if mesh.has_tangents {
mesh_key |= MeshPipelineKey::VERTEX_TANGENTS;
let mut mesh_key =
MeshPipelineKey::from_primitive_topology(mesh.primitive_topology)
| msaa_key;
let alpha_mode = M::alpha_mode(material);
if let AlphaMode::Blend = alpha_mode {
mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS;
}
mesh_key |=
MeshPipelineKey::from_primitive_topology(mesh.primitive_topology);
}
let alpha_mode = M::alpha_mode(material);
if let AlphaMode::Blend = alpha_mode {
mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS;
}

let specialized_key = M::key(material);
let pipeline_id = pipelines.specialize(
&mut pipeline_cache,
&material_pipeline,
(mesh_key, specialized_key),
);

// NOTE: row 2 of the inverse view matrix dotted with column 3 of the model matrix
// gives the z component of translation of the mesh in view space
let mesh_z = inverse_view_row_2.dot(mesh_uniform.transform.col(3));
match alpha_mode {
AlphaMode::Opaque => {
opaque_phase.add(Opaque3d {
entity: *visible_entity,
draw_function: draw_opaque_pbr,
pipeline: pipeline_id,
// NOTE: Front-to-back ordering for opaque with ascending sort means near should have the
// lowest sort key and getting further away should increase. As we have
// -z in front of the camera, values in view space decrease away from the
// camera. Flipping the sign of mesh_z results in the correct front-to-back ordering
distance: -mesh_z,
});
}
AlphaMode::Mask(_) => {
alpha_mask_phase.add(AlphaMask3d {
entity: *visible_entity,
draw_function: draw_alpha_mask_pbr,
pipeline: pipeline_id,
// NOTE: Front-to-back ordering for alpha mask with ascending sort means near should have the
// lowest sort key and getting further away should increase. As we have
// -z in front of the camera, values in view space decrease away from the
// camera. Flipping the sign of mesh_z results in the correct front-to-back ordering
distance: -mesh_z,
});
}
AlphaMode::Blend => {
transparent_phase.add(Transparent3d {
entity: *visible_entity,
draw_function: draw_transparent_pbr,
pipeline: pipeline_id,
// NOTE: Back-to-front ordering for transparent with ascending sort means far should have the
// lowest sort key and getting closer should increase. As we have
// -z in front of the camera, the largest distance is -far with values increasing toward the
// camera. As such we can just use mesh_z as the distance
distance: mesh_z,
});
let material_key = M::key(material);

let pipeline_id = pipelines.specialize(
&mut pipeline_cache,
&material_pipeline,
MaterialPipelineKey {
mesh_key,
material_key,
},
&mesh.layout,
);
let pipeline_id = match pipeline_id {
Ok(id) => id,
Err(err) => {
error!("{}", err);
continue;
}
};

// NOTE: row 2 of the inverse view matrix dotted with column 3 of the model matrix
// gives the z component of translation of the mesh in view space
let mesh_z = inverse_view_row_2.dot(mesh_uniform.transform.col(3));
match alpha_mode {
AlphaMode::Opaque => {
opaque_phase.add(Opaque3d {
entity: *visible_entity,
draw_function: draw_opaque_pbr,
pipeline: pipeline_id,
// NOTE: Front-to-back ordering for opaque with ascending sort means near should have the
// lowest sort key and getting further away should increase. As we have
// -z in front of the camera, values in view space decrease away from the
// camera. Flipping the sign of mesh_z results in the correct front-to-back ordering
distance: -mesh_z,
});
}
AlphaMode::Mask(_) => {
alpha_mask_phase.add(AlphaMask3d {
entity: *visible_entity,
draw_function: draw_alpha_mask_pbr,
pipeline: pipeline_id,
// NOTE: Front-to-back ordering for alpha mask with ascending sort means near should have the
// lowest sort key and getting further away should increase. As we have
// -z in front of the camera, values in view space decrease away from the
// camera. Flipping the sign of mesh_z results in the correct front-to-back ordering
distance: -mesh_z,
});
}
AlphaMode::Blend => {
transparent_phase.add(Transparent3d {
entity: *visible_entity,
draw_function: draw_transparent_pbr,
pipeline: pipeline_id,
// NOTE: Back-to-front ordering for transparent with ascending sort means far should have the
// lowest sort key and getting closer should increase. As we have
// -z in front of the camera, the largest distance is -far with values increasing toward the
// camera. As such we can just use mesh_z as the distance
distance: mesh_z,
});
}
}
}
}
Expand Down
Loading