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

Add user supplied mesh tag #17648

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2735,6 +2735,16 @@ description = "A very simple compute shader that writes to a buffer that is read
category = "Shaders"
wasm = false

[[example]]
name = "mesh_instance_index"
path = "examples/shader/mesh_instance_index.rs"

[package.metadata.example.mesh_instance_index]
name = "Mesh Instance Index"
description = "A shader that uses a custom instance index to sample from a texture"
category = "Shaders"
wasm = true

[[example]]
name = "array_texture"
path = "examples/shader/array_texture.rs"
Expand Down
41 changes: 41 additions & 0 deletions assets/shaders/mesh_instance_index.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#import bevy_pbr::{
mesh_functions,
view_transformations::position_world_to_clip
}

@group(2) @binding(0) var texture: texture_2d<f32>;
@group(2) @binding(1) var texture_sampler: sampler;

struct Vertex {
@builtin(instance_index) instance_index: u32,
@location(0) position: vec3<f32>,
};

struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) world_position: vec4<f32>,
@location(1) color: vec4<f32>,
};

@vertex
fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput;
let mesh_instance_index = mesh_functions::get_mesh_instance_index(vertex.instance_index);
var world_from_local = mesh_functions::get_world_from_local(vertex.instance_index);
out.world_position = mesh_functions::mesh_position_local_to_world(world_from_local, vec4(vertex.position, 1.0));
out.clip_position = position_world_to_clip(out.world_position.xyz);

let tex_dim = textureDimensions(texture);
// Find the texel coordinate from the mesh_instance_index
let texel_coord = vec2<u32>(mesh_instance_index % tex_dim.x, mesh_instance_index / tex_dim.x);
tychedelia marked this conversation as resolved.
Show resolved Hide resolved

out.color = textureLoad(texture, texel_coord, 0);
return out;
}

@fragment
fn fragment(
mesh: VertexOutput,
) -> @location(0) vec4<f32> {
return mesh.color;
}
4 changes: 2 additions & 2 deletions assets/shaders/storage_buffer.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
}

@group(2) @binding(0) var<storage, read> colors: array<vec4<f32>, 5>;
@group(2) @binding(1) var<uniform> color_id: u32;

struct Vertex {
@builtin(instance_index) instance_index: u32,
Expand All @@ -20,11 +19,12 @@ struct VertexOutput {
@vertex
fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput;
let mesh_instance_index = mesh_functions::get_mesh_instance_index(vertex.instance_index);
var world_from_local = mesh_functions::get_world_from_local(vertex.instance_index);
out.world_position = mesh_functions::mesh_position_local_to_world(world_from_local, vec4(vertex.position, 1.0));
out.clip_position = position_world_to_clip(out.world_position.xyz);

out.color = colors[color_id];
out.color = colors[mesh_instance_index];
return out;
}

Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/meshlet/instance_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ impl InstanceManager {
None,
None,
None,
None,
);

// Append instance data
Expand Down
24 changes: 19 additions & 5 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use core::mem::size_of;

use crate::material_bind_groups::{MaterialBindGroupIndex, MaterialBindGroupSlot};
use allocator::MeshAllocator;
use bevy_asset::{load_internal_asset, AssetId, UntypedAssetId};
Expand Down Expand Up @@ -327,6 +325,8 @@ pub struct MeshUniform {
/// Low 16 bits: index of the material inside the bind group data.
/// High 16 bits: index of the lightmap in the binding array.
pub material_and_lightmap_bind_group_slot: u32,
/// User supplied index to identify this mesh instance.
pub mesh_instance_index: u32,
}

/// Information that has to be transferred from CPU to GPU in order to produce
Expand Down Expand Up @@ -383,8 +383,8 @@ pub struct MeshInputUniform {
/// Low 16 bits: index of the material inside the bind group data.
/// High 16 bits: index of the lightmap in the binding array.
pub material_and_lightmap_bind_group_slot: u32,
/// Padding.
pub pad_a: u32,
/// User supplied index to identify this mesh instance.
pub mesh_instance_index: u32,
/// Padding.
pub pad_b: u32,
}
tychedelia marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -420,6 +420,7 @@ impl MeshUniform {
maybe_lightmap: Option<(LightmapSlotIndex, Rect)>,
current_skin_index: Option<u32>,
previous_skin_index: Option<u32>,
mesh_instance_index: Option<u32>,
) -> Self {
let (local_from_world_transpose_a, local_from_world_transpose_b) =
mesh_transforms.world_from_local.inverse_transpose_3x3();
Expand All @@ -440,6 +441,7 @@ impl MeshUniform {
previous_skin_index: previous_skin_index.unwrap_or(u32::MAX),
material_and_lightmap_bind_group_slot: u32::from(material_bind_group_slot)
| ((lightmap_bind_group_slot as u32) << 16),
mesh_instance_index: mesh_instance_index.unwrap_or(0),
}
}
}
Expand Down Expand Up @@ -568,6 +570,8 @@ pub struct RenderMeshInstanceShared {
pub material_bindings_index: MaterialBindingId,
/// Various flags.
pub flags: RenderMeshInstanceFlags,
/// User supplied index to identify this mesh instance.
pub mesh_instance_index: u32,
}

/// Information that is gathered during the parallel portion of mesh extraction
Expand Down Expand Up @@ -647,6 +651,7 @@ impl RenderMeshInstanceShared {
fn from_components(
previous_transform: Option<&PreviousGlobalTransform>,
mesh: &Mesh3d,
mesh_instance_index: Option<&MeshInstanceIndex>,
not_shadow_caster: bool,
no_automatic_batching: bool,
) -> Self {
Expand All @@ -666,6 +671,7 @@ impl RenderMeshInstanceShared {
flags: mesh_instance_flags,
// This gets filled in later, during `RenderMeshGpuBuilder::update`.
material_bindings_index: default(),
mesh_instance_index: mesh_instance_index.map_or(0, |i| **i),
}
}

Expand Down Expand Up @@ -993,7 +999,7 @@ impl RenderMeshInstanceGpuBuilder {
material_and_lightmap_bind_group_slot: u32::from(
self.shared.material_bindings_index.slot,
) | ((lightmap_slot as u32) << 16),
pad_a: 0,
mesh_instance_index: self.shared.mesh_instance_index,
pad_b: 0,
};

Expand Down Expand Up @@ -1129,6 +1135,7 @@ pub fn extract_meshes_for_cpu_building(
&GlobalTransform,
Option<&PreviousGlobalTransform>,
&Mesh3d,
Option<&MeshInstanceIndex>,
Has<NoFrustumCulling>,
Has<NotShadowReceiver>,
Has<TransmittedShadowReceiver>,
Expand All @@ -1147,6 +1154,7 @@ pub fn extract_meshes_for_cpu_building(
transform,
previous_transform,
mesh,
mesh_instance_index,
no_frustum_culling,
not_shadow_receiver,
transmitted_receiver,
Expand Down Expand Up @@ -1174,6 +1182,7 @@ pub fn extract_meshes_for_cpu_building(
let shared = RenderMeshInstanceShared::from_components(
previous_transform,
mesh,
mesh_instance_index,
not_shadow_caster,
no_automatic_batching,
);
Expand Down Expand Up @@ -1235,6 +1244,7 @@ pub fn extract_meshes_for_gpu_building(
Option<&Lightmap>,
Option<&Aabb>,
&Mesh3d,
Option<&MeshInstanceIndex>,
Has<NoFrustumCulling>,
Has<NotShadowReceiver>,
Has<TransmittedShadowReceiver>,
Expand Down Expand Up @@ -1292,6 +1302,7 @@ pub fn extract_meshes_for_gpu_building(
lightmap,
aabb,
mesh,
mesh_instance_index,
no_frustum_culling,
not_shadow_receiver,
transmitted_receiver,
Expand Down Expand Up @@ -1320,6 +1331,7 @@ pub fn extract_meshes_for_gpu_building(
let shared = RenderMeshInstanceShared::from_components(
previous_transform,
mesh,
mesh_instance_index,
not_shadow_caster,
no_automatic_batching,
);
Expand Down Expand Up @@ -1673,6 +1685,7 @@ impl GetBatchData for MeshPipeline {
maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
current_skin_index,
previous_skin_index,
Some(mesh_instance.mesh_instance_index),
),
mesh_instance.should_batch().then_some((
material_bind_group_index.group,
Expand Down Expand Up @@ -1740,6 +1753,7 @@ impl GetFullBatchData for MeshPipeline {
maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
current_skin_index,
previous_skin_index,
Some(mesh_instance.mesh_instance_index),
))
}

Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_pbr/src/render/mesh_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,7 @@ fn get_visibility_range_dither_level(instance_index: u32, world_position: vec4<f
return offset + clamp(level, 0, 16);
}
#endif

fn get_mesh_instance_index(instance_index: u32) -> u32 {
return mesh[instance_index].mesh_instance_index;
}
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/render/mesh_preprocess.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -345,4 +345,5 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
output[mesh_output_index].previous_skin_index = current_input[input_index].previous_skin_index;
output[mesh_output_index].material_and_lightmap_bind_group_slot =
current_input[input_index].material_and_lightmap_bind_group_slot;
output[mesh_output_index].mesh_instance_index = current_input[input_index].mesh_instance_index;
}
2 changes: 2 additions & 0 deletions crates/bevy_pbr/src/render/mesh_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ struct Mesh {
// Low 16 bits: index of the material inside the bind group data.
// High 16 bits: index of the lightmap in the binding array.
material_and_lightmap_bind_group_slot: u32,
// User supplied index to identify the mesh instance
mesh_instance_index: u32,
};

#ifdef SKINNED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ struct MeshInput {
// Low 16 bits: index of the material inside the bind group data.
// High 16 bits: index of the lightmap in the binding array.
material_and_lightmap_bind_group_slot: u32,
pad_a: u32,
// User supplied index to identify the mesh instance
mesh_instance_index: u32,
pad_b: u32,
tychedelia marked this conversation as resolved.
Show resolved Hide resolved
}

Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_render/src/mesh/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,8 @@ pub fn mark_3d_meshes_as_changed_if_their_assets_changed(
}
}
}

/// A component that stores an arbitrary index used to identify the mesh instance when rendering.
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq)]
#[reflect(Component, Default)]
pub struct MeshInstanceIndex(pub u32);
4 changes: 3 additions & 1 deletion crates/bevy_render/src/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ use bevy_ecs::{
SystemParamItem,
},
};
pub use components::{mark_3d_meshes_as_changed_if_their_assets_changed, Mesh2d, Mesh3d};
pub use components::{
mark_3d_meshes_as_changed_if_their_assets_changed, Mesh2d, Mesh3d, MeshInstanceIndex,
};
use wgpu::IndexFormat;

/// Adds the [`Mesh`] as an asset and makes sure that they are extracted and prepared for the GPU.
Expand Down
56 changes: 51 additions & 5 deletions examples/2d/sprite.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,64 @@
//! Displays a single [`Sprite`], created from an image.

use bevy::prelude::*;
use bevy::window::{WindowRef, WindowResolution};
use bevy_render::camera::RenderTarget;
use bevy_render::view::RenderLayers;

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
resolution: WindowResolution::new(1000., 1000.).with_scale_factor_override(1.),
title: "Window 1".to_string(),
..default()
}),
..default()
}))
.add_systems(Startup, setup)
.run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
fn setup(mut commands: Commands) {
// The primary window is the default camera target and 0 is the default render layer, there's no need to set these normally
// the explicitness is just to make this example as clear as possible.

commands.spawn(Sprite::from_image(
asset_server.load("branding/bevy_bird_dark.png"),
commands.spawn((
Camera2d::default(),
Camera {
target: RenderTarget::Window(WindowRef::Primary),
..default()
},
RenderLayers::layer(0),
));

// Spawn a second window
let window_2 = commands
.spawn(Window {
title: "Window 2".to_owned(),
resolution: WindowResolution::new(1000., 1000.).with_scale_factor_override(1.),
..default()
})
.id();

commands.spawn((
Camera2d::default(),
Camera {
target: RenderTarget::Window(WindowRef::Entity(window_2)),
..default()
},
RenderLayers::layer(1),
));

// Text drawn to window 1
commands.spawn((Text2d::new("1"), RenderLayers::layer(0)));

// Text drawn to window 2
commands.spawn((Text2d::new("2"), RenderLayers::layer(1)));

commands.spawn((
Text2d::new("3"),
Transform::from_translation(-25. * Vec3::Y),
RenderLayers::from_layers(&[0, 1]),
));
}
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ Example | Description
[GPU readback](../examples/shader/gpu_readback.rs) | A very simple compute shader that writes to a buffer that is read by the cpu
[Instancing](../examples/shader/custom_shader_instancing.rs) | A shader that renders a mesh multiple times in one draw call using low level rendering api
[Instancing](../examples/shader/automatic_instancing.rs) | Shows that multiple instances of a cube are automatically instanced in one draw call
[Instancing - Custom Index](../examples/shader/mesh_instance_index.rs) | Demonstrates how to use a custom mesh instance index in a shader
[Material](../examples/shader/shader_material.rs) | A shader and a material that uses it
[Material](../examples/shader/shader_material_2d.rs) | A shader and a material that uses it on a 2d mesh
[Material - Bindless](../examples/shader/shader_material_bindless.rs) | Demonstrates how to make materials that use bindless textures
Expand Down
Loading
Loading