mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
parent
579c769f7c
commit
01116b1fdb
16 changed files with 339 additions and 103 deletions
|
@ -59,9 +59,7 @@ impl<'a> Commands<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'a, '_> {
|
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'a, '_> {
|
||||||
self.add(GetOrSpawn {
|
self.add(GetOrSpawn { entity });
|
||||||
entity,
|
|
||||||
});
|
|
||||||
EntityCommands {
|
EntityCommands {
|
||||||
entity,
|
entity,
|
||||||
commands: self,
|
commands: self,
|
||||||
|
@ -292,8 +290,7 @@ pub struct GetOrSpawn {
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command for GetOrSpawn
|
impl Command for GetOrSpawn {
|
||||||
{
|
|
||||||
fn write(self: Box<Self>, world: &mut World) {
|
fn write(self: Box<Self>, world: &mut World) {
|
||||||
world.get_or_spawn(self.entity);
|
world.get_or_spawn(self.entity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::{archetype::ArchetypeGeneration, system::{check_system_change_tick, BoxedSystem, IntoSystem, System, SystemId}, world::World};
|
use crate::{
|
||||||
|
archetype::ArchetypeGeneration,
|
||||||
|
system::{check_system_change_tick, BoxedSystem, IntoSystem, System, SystemId},
|
||||||
|
world::World,
|
||||||
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub trait ExclusiveSystem: Send + Sync + 'static {
|
pub trait ExclusiveSystem: Send + Sync + 'static {
|
||||||
|
|
|
@ -100,7 +100,6 @@ impl World {
|
||||||
&mut self.entities
|
&mut self.entities
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Retrieves this world's [Archetypes] collection
|
/// Retrieves this world's [Archetypes] collection
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn archetypes(&self) -> &Archetypes {
|
pub fn archetypes(&self) -> &Archetypes {
|
||||||
|
|
|
@ -37,14 +37,24 @@ fn setup(
|
||||||
// plane
|
// plane
|
||||||
commands.spawn_bundle(PbrBundle {
|
commands.spawn_bundle(PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
|
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
|
||||||
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
material: materials.add(StandardMaterial {
|
||||||
|
color: Color::INDIGO,
|
||||||
|
roughness: 1.0,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
// cube
|
// cube
|
||||||
commands
|
commands
|
||||||
.spawn_bundle(PbrBundle {
|
.spawn_bundle(PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
material: materials.add(StandardMaterial {
|
||||||
|
color: Color::PINK,
|
||||||
|
roughness: 0.0,
|
||||||
|
metallic: 1.0,
|
||||||
|
reflectance: 1.0,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
|
@ -52,8 +62,14 @@ fn setup(
|
||||||
// sphere
|
// sphere
|
||||||
commands
|
commands
|
||||||
.spawn_bundle(PbrBundle {
|
.spawn_bundle(PbrBundle {
|
||||||
mesh: meshes.add(Mesh::from(shape::UVSphere { radius: 0.5, ..Default::default() })),
|
mesh: meshes.add(Mesh::from(shape::UVSphere {
|
||||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
radius: 0.5,
|
||||||
|
..Default::default()
|
||||||
|
})),
|
||||||
|
material: materials.add(StandardMaterial {
|
||||||
|
color: Color::LIME_GREEN,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
transform: Transform::from_xyz(1.5, 1.0, 1.5),
|
transform: Transform::from_xyz(1.5, 1.0, 1.5),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,7 +9,6 @@ pub use material::*;
|
||||||
pub use render::*;
|
pub use render::*;
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_asset::AddAsset;
|
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_render2::{
|
use bevy_render2::{
|
||||||
core_pipeline,
|
core_pipeline,
|
||||||
|
@ -29,7 +28,7 @@ pub struct PbrPlugin;
|
||||||
|
|
||||||
impl Plugin for PbrPlugin {
|
impl Plugin for PbrPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_asset::<StandardMaterial>();
|
app.add_plugin(StandardMaterialPlugin);
|
||||||
|
|
||||||
let render_app = app.sub_app_mut(0);
|
let render_app = app.sub_app_mut(0);
|
||||||
render_app
|
render_app
|
||||||
|
@ -50,6 +49,7 @@ impl Plugin for PbrPlugin {
|
||||||
.add_system_to_stage(RenderStage::Cleanup, render::cleanup_view_lights.system())
|
.add_system_to_stage(RenderStage::Cleanup, render::cleanup_view_lights.system())
|
||||||
.init_resource::<PbrShaders>()
|
.init_resource::<PbrShaders>()
|
||||||
.init_resource::<ShadowShaders>()
|
.init_resource::<ShadowShaders>()
|
||||||
|
.init_resource::<MaterialMeta>()
|
||||||
.init_resource::<MeshMeta>()
|
.init_resource::<MeshMeta>()
|
||||||
.init_resource::<LightMeta>();
|
.init_resource::<LightMeta>();
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,71 @@
|
||||||
use bevy_reflect::{Reflect, TypeUuid};
|
use bevy_app::{App, CoreStage, EventReader, Plugin};
|
||||||
use bevy_render2::color::Color;
|
use bevy_asset::{AddAsset, AssetEvent, Assets, Handle};
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
use bevy_math::Vec4;
|
||||||
|
use bevy_reflect::TypeUuid;
|
||||||
|
use bevy_render2::{
|
||||||
|
color::Color,
|
||||||
|
render_command::RenderCommandQueue,
|
||||||
|
render_resource::{BufferId, BufferInfo, BufferUsage},
|
||||||
|
renderer::{RenderResourceContext, RenderResources},
|
||||||
|
};
|
||||||
|
use bevy_utils::HashSet;
|
||||||
|
use crevice::std140::{AsStd140, Std140};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, TypeUuid, Reflect)]
|
// TODO: this shouldn't live in the StandardMaterial type
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct StandardMaterialGpuData {
|
||||||
|
pub buffer: BufferId,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A material with "standard" properties used in PBR lighting
|
||||||
|
/// Standard property values with pictures here https://google.github.io/filament/Material%20Properties.pdf
|
||||||
|
#[derive(Debug, Clone, TypeUuid)]
|
||||||
#[uuid = "7494888b-c082-457b-aacf-517228cc0c22"]
|
#[uuid = "7494888b-c082-457b-aacf-517228cc0c22"]
|
||||||
pub struct StandardMaterial {
|
pub struct StandardMaterial {
|
||||||
|
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
||||||
|
/// in between.
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
|
||||||
|
/// Defaults to minimum of 0.089
|
||||||
|
pub roughness: f32,
|
||||||
|
/// From [0.0, 1.0], dielectric to pure metallic
|
||||||
|
pub metallic: f32,
|
||||||
|
/// Specular intensity for non-metals on a linear scale of [0.0, 1.0]
|
||||||
|
/// defaults to 0.5 which is mapped to 4% reflectance in the shader
|
||||||
|
pub reflectance: f32,
|
||||||
|
// Use a color for user friendliness even though we technically don't use the alpha channel
|
||||||
|
// Might be used in the future for exposure correction in HDR
|
||||||
|
pub emissive: Color,
|
||||||
|
pub gpu_data: Option<StandardMaterialGpuData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StandardMaterial {
|
||||||
|
pub fn gpu_data(&self) -> Option<&StandardMaterialGpuData> {
|
||||||
|
self.gpu_data.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StandardMaterial {
|
||||||
|
fn default() -> Self {
|
||||||
|
StandardMaterial {
|
||||||
|
color: Color::rgb(1.0, 1.0, 1.0),
|
||||||
|
// This is the minimum the roughness is clamped to in shader code
|
||||||
|
// See https://google.github.io/filament/Filament.html#materialsystem/parameterization/
|
||||||
|
// It's the minimum floating point value that won't be rounded down to 0 in the
|
||||||
|
// calculations used. Although technically for 32-bit floats, 0.045 could be
|
||||||
|
// used.
|
||||||
|
roughness: 0.089,
|
||||||
|
// Few materials are purely dielectric or metallic
|
||||||
|
// This is just a default for mostly-dielectric
|
||||||
|
metallic: 0.01,
|
||||||
|
// Minimum real-world reflectance is 2%, most materials between 2-5%
|
||||||
|
// Expressed in a linear scale and equivalent to 4% reflectance see https://google.github.io/filament/Material%20Properties.pdf
|
||||||
|
reflectance: 0.5,
|
||||||
|
emissive: Color::BLACK,
|
||||||
|
gpu_data: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Color> for StandardMaterial {
|
impl From<Color> for StandardMaterial {
|
||||||
|
@ -15,3 +76,112 @@ impl From<Color> for StandardMaterial {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, AsStd140)]
|
||||||
|
pub struct StandardMaterialUniformData {
|
||||||
|
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
||||||
|
/// in between.
|
||||||
|
pub color: Vec4,
|
||||||
|
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
|
||||||
|
/// Defaults to minimum of 0.089
|
||||||
|
pub roughness: f32,
|
||||||
|
/// From [0.0, 1.0], dielectric to pure metallic
|
||||||
|
pub metallic: f32,
|
||||||
|
/// Specular intensity for non-metals on a linear scale of [0.0, 1.0]
|
||||||
|
/// defaults to 0.5 which is mapped to 4% reflectance in the shader
|
||||||
|
pub reflectance: f32,
|
||||||
|
// Use a color for user friendliness even though we technically don't use the alpha channel
|
||||||
|
// Might be used in the future for exposure correction in HDR
|
||||||
|
pub emissive: Vec4,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StandardMaterialPlugin;
|
||||||
|
|
||||||
|
impl Plugin for StandardMaterialPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_asset::<StandardMaterial>().add_system_to_stage(
|
||||||
|
CoreStage::PostUpdate,
|
||||||
|
standard_material_resource_system.system(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn standard_material_resource_system(
|
||||||
|
render_resource_context: Res<RenderResources>,
|
||||||
|
mut render_command_queue: ResMut<RenderCommandQueue>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
mut material_events: EventReader<AssetEvent<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
let mut changed_materials = HashSet::default();
|
||||||
|
let render_resource_context = &**render_resource_context;
|
||||||
|
for event in material_events.iter() {
|
||||||
|
match event {
|
||||||
|
AssetEvent::Created { ref handle } => {
|
||||||
|
changed_materials.insert(handle.clone_weak());
|
||||||
|
}
|
||||||
|
AssetEvent::Modified { ref handle } => {
|
||||||
|
changed_materials.insert(handle.clone_weak());
|
||||||
|
// TODO: uncomment this to support mutated materials
|
||||||
|
// remove_current_material_resources(render_resource_context, handle, &mut materials);
|
||||||
|
}
|
||||||
|
AssetEvent::Removed { ref handle } => {
|
||||||
|
remove_current_material_resources(render_resource_context, handle, &mut materials);
|
||||||
|
// if material was modified and removed in the same update, ignore the modification
|
||||||
|
// events are ordered so future modification events are ok
|
||||||
|
changed_materials.remove(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update changed material data
|
||||||
|
for changed_material_handle in changed_materials.iter() {
|
||||||
|
if let Some(material) = materials.get_mut(changed_material_handle) {
|
||||||
|
// TODO: this avoids creating new materials each frame because storing gpu data in the material flags it as
|
||||||
|
// modified. this prevents hot reloading and therefore can't be used in an actual impl.
|
||||||
|
if material.gpu_data.is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = StandardMaterialUniformData {
|
||||||
|
color: material.color.into(),
|
||||||
|
roughness: material.roughness,
|
||||||
|
metallic: material.metallic,
|
||||||
|
reflectance: material.reflectance,
|
||||||
|
emissive: material.emissive.into(),
|
||||||
|
};
|
||||||
|
let value_std140 = value.as_std140();
|
||||||
|
|
||||||
|
let size = StandardMaterialUniformData::std140_size_static();
|
||||||
|
|
||||||
|
let staging_buffer = render_resource_context.create_buffer_with_data(
|
||||||
|
BufferInfo {
|
||||||
|
size,
|
||||||
|
buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
|
||||||
|
mapped_at_creation: true,
|
||||||
|
},
|
||||||
|
value_std140.as_bytes(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let buffer = render_resource_context.create_buffer(BufferInfo {
|
||||||
|
size,
|
||||||
|
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
render_command_queue.copy_buffer_to_buffer(staging_buffer, 0, buffer, 0, size as u64);
|
||||||
|
render_command_queue.free_buffer(staging_buffer);
|
||||||
|
|
||||||
|
material.gpu_data = Some(StandardMaterialGpuData { buffer });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_current_material_resources(
|
||||||
|
render_resource_context: &dyn RenderResourceContext,
|
||||||
|
handle: &Handle<StandardMaterial>,
|
||||||
|
materials: &mut Assets<StandardMaterial>,
|
||||||
|
) {
|
||||||
|
if let Some(gpu_data) = materials.get_mut(handle).and_then(|t| t.gpu_data.take()) {
|
||||||
|
render_resource_context.remove_buffer(gpu_data.buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -325,7 +325,7 @@ impl Node for ShadowPassNode {
|
||||||
world: &World,
|
world: &World,
|
||||||
) -> Result<(), NodeRunError> {
|
) -> Result<(), NodeRunError> {
|
||||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||||
let view_lights = self.main_view_query.get_manual(world, view_entity).unwrap();
|
if let Some(view_lights) = self.main_view_query.get_manual(world, view_entity).ok() {
|
||||||
for view_light_entity in view_lights.lights.iter().copied() {
|
for view_light_entity in view_lights.lights.iter().copied() {
|
||||||
let (view_light, shadow_phase) = self
|
let (view_light, shadow_phase) = self
|
||||||
.view_light_query
|
.view_light_query
|
||||||
|
@ -352,7 +352,8 @@ impl Node for ShadowPassNode {
|
||||||
let mut draw_functions = draw_functions.write();
|
let mut draw_functions = draw_functions.write();
|
||||||
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
||||||
for drawable in shadow_phase.drawn_things.iter() {
|
for drawable in shadow_phase.drawn_things.iter() {
|
||||||
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
|
let draw_function =
|
||||||
|
draw_functions.get_mut(drawable.draw_function).unwrap();
|
||||||
draw_function.draw(
|
draw_function.draw(
|
||||||
world,
|
world,
|
||||||
&mut tracked_pass,
|
&mut tracked_pass,
|
||||||
|
@ -364,6 +365,7 @@ impl Node for ShadowPassNode {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
mod light;
|
mod light;
|
||||||
|
use bevy_utils::HashMap;
|
||||||
pub use light::*;
|
pub use light::*;
|
||||||
|
|
||||||
use crate::StandardMaterial;
|
use crate::{StandardMaterial, StandardMaterialUniformData};
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_ecs::{prelude::*, system::SystemState};
|
use bevy_ecs::{prelude::*, system::SystemState};
|
||||||
use bevy_math::Mat4;
|
use bevy_math::Mat4;
|
||||||
|
@ -11,13 +12,16 @@ use bevy_render2::{
|
||||||
pipeline::*,
|
pipeline::*,
|
||||||
render_graph::{Node, NodeRunError, RenderGraphContext},
|
render_graph::{Node, NodeRunError, RenderGraphContext},
|
||||||
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
|
render_phase::{Draw, DrawFunctions, Drawable, RenderPhase, TrackedRenderPass},
|
||||||
render_resource::{BindGroupBuilder, BindGroupId, BufferId, DynamicUniformVec},
|
render_resource::{
|
||||||
|
BindGroupBuilder, BindGroupId, BufferId, DynamicUniformVec, RenderResourceBinding,
|
||||||
|
},
|
||||||
renderer::{RenderContext, RenderResources},
|
renderer::{RenderContext, RenderResources},
|
||||||
shader::{Shader, ShaderStage, ShaderStages},
|
shader::{Shader, ShaderStage, ShaderStages},
|
||||||
texture::{TextureFormat, TextureSampleType},
|
texture::{TextureFormat, TextureSampleType},
|
||||||
view::{ViewMeta, ViewUniform},
|
view::{ViewMeta, ViewUniform},
|
||||||
};
|
};
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
|
use crevice::std140::AsStd140;
|
||||||
|
|
||||||
pub struct PbrShaders {
|
pub struct PbrShaders {
|
||||||
pipeline: PipelineId,
|
pipeline: PipelineId,
|
||||||
|
@ -143,6 +147,7 @@ struct ExtractedMesh {
|
||||||
vertex_buffer: BufferId,
|
vertex_buffer: BufferId,
|
||||||
index_info: Option<IndexInfo>,
|
index_info: Option<IndexInfo>,
|
||||||
transform_binding_offset: u32,
|
transform_binding_offset: u32,
|
||||||
|
material_buffer: BufferId,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IndexInfo {
|
struct IndexInfo {
|
||||||
|
@ -157,22 +162,27 @@ pub struct ExtractedMeshes {
|
||||||
pub fn extract_meshes(
|
pub fn extract_meshes(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
meshes: Res<Assets<Mesh>>,
|
meshes: Res<Assets<Mesh>>,
|
||||||
_materials: Res<Assets<StandardMaterial>>,
|
materials: Res<Assets<StandardMaterial>>,
|
||||||
query: Query<(&GlobalTransform, &Handle<Mesh>, &Handle<StandardMaterial>)>,
|
query: Query<(&GlobalTransform, &Handle<Mesh>, &Handle<StandardMaterial>)>,
|
||||||
) {
|
) {
|
||||||
let mut extracted_meshes = Vec::new();
|
let mut extracted_meshes = Vec::new();
|
||||||
for (transform, mesh_handle, _material_handle) in query.iter() {
|
for (transform, mesh_handle, material_handle) in query.iter() {
|
||||||
if let Some(mesh) = meshes.get(mesh_handle) {
|
if let Some(mesh) = meshes.get(mesh_handle) {
|
||||||
if let Some(gpu_data) = &mesh.gpu_data() {
|
if let Some(mesh_gpu_data) = &mesh.gpu_data() {
|
||||||
|
if let Some(material) = materials.get(material_handle) {
|
||||||
|
if let Some(material_gpu_data) = &material.gpu_data() {
|
||||||
extracted_meshes.push(ExtractedMesh {
|
extracted_meshes.push(ExtractedMesh {
|
||||||
transform: transform.compute_matrix(),
|
transform: transform.compute_matrix(),
|
||||||
vertex_buffer: gpu_data.vertex_buffer,
|
vertex_buffer: mesh_gpu_data.vertex_buffer,
|
||||||
index_info: gpu_data.index_buffer.map(|i| IndexInfo {
|
index_info: mesh_gpu_data.index_buffer.map(|i| IndexInfo {
|
||||||
buffer: i,
|
buffer: i,
|
||||||
count: mesh.indices().unwrap().len() as u32,
|
count: mesh.indices().unwrap().len() as u32,
|
||||||
}),
|
}),
|
||||||
transform_binding_offset: 0,
|
transform_binding_offset: 0,
|
||||||
})
|
material_buffer: material_gpu_data.buffer,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,6 +221,11 @@ struct MeshViewBindGroups {
|
||||||
mesh_transform_bind_group: BindGroupId,
|
mesh_transform_bind_group: BindGroupId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MaterialMeta {
|
||||||
|
material_bind_groups: Vec<BindGroupId>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn queue_meshes(
|
pub fn queue_meshes(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
draw_functions: Res<DrawFunctions>,
|
draw_functions: Res<DrawFunctions>,
|
||||||
|
@ -218,9 +233,10 @@ pub fn queue_meshes(
|
||||||
pbr_shaders: Res<PbrShaders>,
|
pbr_shaders: Res<PbrShaders>,
|
||||||
shadow_shaders: Res<ShadowShaders>,
|
shadow_shaders: Res<ShadowShaders>,
|
||||||
mesh_meta: Res<MeshMeta>,
|
mesh_meta: Res<MeshMeta>,
|
||||||
|
mut material_meta: ResMut<MaterialMeta>,
|
||||||
light_meta: Res<LightMeta>,
|
light_meta: Res<LightMeta>,
|
||||||
view_meta: Res<ViewMeta>,
|
view_meta: Res<ViewMeta>,
|
||||||
extracted_meshes: Res<ExtractedMeshes>,
|
mut extracted_meshes: ResMut<ExtractedMeshes>,
|
||||||
mut views: Query<(Entity, &ViewLights, &mut RenderPhase<Transparent3dPhase>)>,
|
mut views: Query<(Entity, &ViewLights, &mut RenderPhase<Transparent3dPhase>)>,
|
||||||
mut view_light_shadow_phases: Query<&mut RenderPhase<ShadowPhase>>,
|
mut view_light_shadow_phases: Query<&mut RenderPhase<ShadowPhase>>,
|
||||||
) {
|
) {
|
||||||
|
@ -249,13 +265,38 @@ pub fn queue_meshes(
|
||||||
mesh_transform_bind_group: mesh_transform_bind_group.id,
|
mesh_transform_bind_group: mesh_transform_bind_group.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: free old bind groups? clear_unused_bind_groups() currently does this for us? Moving to RAII would also do this for us?
|
||||||
|
material_meta.material_bind_groups.clear();
|
||||||
|
let mut material_bind_group_indices = HashMap::default();
|
||||||
|
|
||||||
let draw_pbr = draw_functions.read().get_id::<DrawPbr>().unwrap();
|
let draw_pbr = draw_functions.read().get_id::<DrawPbr>().unwrap();
|
||||||
for i in 0..extracted_meshes.meshes.len() {
|
for (i, mesh) in extracted_meshes.meshes.iter_mut().enumerate() {
|
||||||
|
let material_bind_group_index = *material_bind_group_indices
|
||||||
|
.entry(mesh.material_buffer)
|
||||||
|
.or_insert_with(|| {
|
||||||
|
let index = material_meta.material_bind_groups.len();
|
||||||
|
let material_bind_group = BindGroupBuilder::default()
|
||||||
|
.add_binding(
|
||||||
|
0,
|
||||||
|
RenderResourceBinding::Buffer {
|
||||||
|
buffer: mesh.material_buffer,
|
||||||
|
range: 0..StandardMaterialUniformData::std140_size_static() as u64,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.finish();
|
||||||
|
render_resources
|
||||||
|
.create_bind_group(layout.bind_group(2).id, &material_bind_group);
|
||||||
|
material_meta
|
||||||
|
.material_bind_groups
|
||||||
|
.push(material_bind_group.id);
|
||||||
|
index
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: currently there is only "transparent phase". this should pick transparent vs opaque according to the mesh material
|
// TODO: currently there is only "transparent phase". this should pick transparent vs opaque according to the mesh material
|
||||||
transparent_phase.add(Drawable {
|
transparent_phase.add(Drawable {
|
||||||
draw_function: draw_pbr,
|
draw_function: draw_pbr,
|
||||||
draw_key: i,
|
draw_key: i,
|
||||||
sort_key: 0, // TODO: sort back-to-front
|
sort_key: material_bind_group_index, // TODO: sort back-to-front, sorting by material for now
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,6 +353,7 @@ impl Node for PbrNode {
|
||||||
|
|
||||||
type DrawPbrParams<'a> = (
|
type DrawPbrParams<'a> = (
|
||||||
Res<'a, PbrShaders>,
|
Res<'a, PbrShaders>,
|
||||||
|
Res<'a, MaterialMeta>,
|
||||||
Res<'a, ExtractedMeshes>,
|
Res<'a, ExtractedMeshes>,
|
||||||
Query<'a, (&'a ViewUniform, &'a MeshViewBindGroups, &'a ViewLights)>,
|
Query<'a, (&'a ViewUniform, &'a MeshViewBindGroups, &'a ViewLights)>,
|
||||||
);
|
);
|
||||||
|
@ -334,9 +376,9 @@ impl Draw for DrawPbr {
|
||||||
pass: &mut TrackedRenderPass,
|
pass: &mut TrackedRenderPass,
|
||||||
view: Entity,
|
view: Entity,
|
||||||
draw_key: usize,
|
draw_key: usize,
|
||||||
_sort_key: usize,
|
sort_key: usize,
|
||||||
) {
|
) {
|
||||||
let (pbr_shaders, extracted_meshes, views) = self.params.get(world);
|
let (pbr_shaders, material_meta, extracted_meshes, views) = self.params.get(world);
|
||||||
let (view_uniforms, mesh_view_bind_groups, view_lights) = views.get(view).unwrap();
|
let (view_uniforms, mesh_view_bind_groups, view_lights) = views.get(view).unwrap();
|
||||||
let layout = &pbr_shaders.pipeline_descriptor.layout;
|
let layout = &pbr_shaders.pipeline_descriptor.layout;
|
||||||
let extracted_mesh = &extracted_meshes.meshes[draw_key];
|
let extracted_mesh = &extracted_meshes.meshes[draw_key];
|
||||||
|
@ -356,6 +398,12 @@ impl Draw for DrawPbr {
|
||||||
mesh_view_bind_groups.mesh_transform_bind_group,
|
mesh_view_bind_groups.mesh_transform_bind_group,
|
||||||
Some(&[extracted_mesh.transform_binding_offset]),
|
Some(&[extracted_mesh.transform_binding_offset]),
|
||||||
);
|
);
|
||||||
|
pass.set_bind_group(
|
||||||
|
2,
|
||||||
|
layout.bind_group(2).id,
|
||||||
|
material_meta.material_bind_groups[sort_key],
|
||||||
|
None,
|
||||||
|
);
|
||||||
pass.set_vertex_buffer(0, extracted_mesh.vertex_buffer, 0);
|
pass.set_vertex_buffer(0, extracted_mesh.vertex_buffer, 0);
|
||||||
if let Some(index_info) = &extracted_mesh.index_info {
|
if let Some(index_info) = &extracted_mesh.index_info {
|
||||||
pass.set_index_buffer(index_info.buffer, 0, IndexFormat::Uint32);
|
pass.set_index_buffer(index_info.buffer, 0, IndexFormat::Uint32);
|
||||||
|
|
|
@ -18,6 +18,7 @@ struct PointLight {
|
||||||
// TODO: this can be removed if we move to storage buffers for light arrays
|
// TODO: this can be removed if we move to storage buffers for light arrays
|
||||||
const int MAX_POINT_LIGHTS = 10;
|
const int MAX_POINT_LIGHTS = 10;
|
||||||
|
|
||||||
|
// View bindings - set 0
|
||||||
layout(set = 0, binding = 0) uniform View {
|
layout(set = 0, binding = 0) uniform View {
|
||||||
mat4 ViewProj;
|
mat4 ViewProj;
|
||||||
vec3 ViewWorldPosition;
|
vec3 ViewWorldPosition;
|
||||||
|
@ -29,6 +30,18 @@ layout(std140, set = 0, binding = 1) uniform Lights {
|
||||||
layout(set = 0, binding = 2) uniform texture2DArray t_Shadow;
|
layout(set = 0, binding = 2) uniform texture2DArray t_Shadow;
|
||||||
layout(set = 0, binding = 3) uniform samplerShadow s_Shadow;
|
layout(set = 0, binding = 3) uniform samplerShadow s_Shadow;
|
||||||
|
|
||||||
|
// Material bindings - set 2
|
||||||
|
struct StandardMaterial_t {
|
||||||
|
vec4 color;
|
||||||
|
float roughness;
|
||||||
|
float metallic;
|
||||||
|
float reflectance;
|
||||||
|
vec4 emissive;
|
||||||
|
};
|
||||||
|
layout(set = 2, binding = 0) uniform StandardMaterial {
|
||||||
|
StandardMaterial_t Material;
|
||||||
|
};
|
||||||
|
|
||||||
# define saturate(x) clamp(x, 0.0, 1.0)
|
# define saturate(x) clamp(x, 0.0, 1.0)
|
||||||
const float PI = 3.141592653589793;
|
const float PI = 3.141592653589793;
|
||||||
|
|
||||||
|
@ -252,11 +265,11 @@ float fetch_shadow(int light_id, vec4 homogeneous_coords) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color = vec4(0.6, 0.6, 0.6, 1.0);
|
vec4 color = Material.color;
|
||||||
float metallic = 0.01;
|
float metallic = Material.metallic;
|
||||||
float reflectance = 0.5;
|
float reflectance = Material.reflectance;
|
||||||
float perceptual_roughness = 0.089;
|
float perceptual_roughness = Material.roughness;
|
||||||
vec3 emissive = vec3(0.0, 0.0, 0.0);
|
vec3 emissive = Material.emissive.xyz;
|
||||||
vec3 ambient_color = vec3(0.1, 0.1, 0.1);
|
vec3 ambient_color = vec3(0.1, 0.1, 0.1);
|
||||||
float occlusion = 1.0;
|
float occlusion = 1.0;
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,13 @@ layout(location = 0) out vec4 v_WorldPosition;
|
||||||
layout(location = 1) out vec3 v_WorldNormal;
|
layout(location = 1) out vec3 v_WorldNormal;
|
||||||
layout(location = 2) out vec2 v_Uv;
|
layout(location = 2) out vec2 v_Uv;
|
||||||
|
|
||||||
|
// View bindings - set 0
|
||||||
layout(set = 0, binding = 0) uniform View {
|
layout(set = 0, binding = 0) uniform View {
|
||||||
mat4 ViewProj;
|
mat4 ViewProj;
|
||||||
vec3 ViewWorldPosition;
|
vec3 ViewWorldPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Object bindings - set 1
|
||||||
layout(set = 1, binding = 0) uniform MeshTransform {
|
layout(set = 1, binding = 0) uniform MeshTransform {
|
||||||
mat4 Model;
|
mat4 Model;
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,13 +25,11 @@ impl PipelineLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_group(&self, index: u32) -> &BindGroupDescriptor {
|
pub fn bind_group(&self, index: u32) -> &BindGroupDescriptor {
|
||||||
self.get_bind_group(index)
|
self.get_bind_group(index).expect("bind group exists")
|
||||||
.expect("bind group exists")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_group_mut(&mut self, index: u32) -> &mut BindGroupDescriptor {
|
pub fn bind_group_mut(&mut self, index: u32) -> &mut BindGroupDescriptor {
|
||||||
self.get_bind_group_mut(index)
|
self.get_bind_group_mut(index).expect("bind group exists")
|
||||||
.expect("bind group exists")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_shader_layouts(shader_layouts: &mut [ShaderLayout]) -> Self {
|
pub fn from_shader_layouts(shader_layouts: &mut [ShaderLayout]) -> Self {
|
||||||
|
|
|
@ -4,8 +4,8 @@ mod draw_state;
|
||||||
pub use draw::*;
|
pub use draw::*;
|
||||||
pub use draw_state::*;
|
pub use draw_state::*;
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use bevy_ecs::prelude::Query;
|
use bevy_ecs::prelude::Query;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
// TODO: make this configurable per phase?
|
// TODO: make this configurable per phase?
|
||||||
pub struct Drawable {
|
pub struct Drawable {
|
||||||
|
@ -39,7 +39,6 @@ impl<T> RenderPhase<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn sort_phase_system<T: 'static>(mut render_phases: Query<&mut RenderPhase<T>>) {
|
pub fn sort_phase_system<T: 'static>(mut render_phases: Query<&mut RenderPhase<T>>) {
|
||||||
for mut phase in render_phases.iter_mut() {
|
for mut phase in render_phases.iter_mut() {
|
||||||
phase.sort();
|
phase.sort();
|
||||||
|
|
|
@ -55,13 +55,7 @@ impl BindGroupBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_buffer(self, index: u32, buffer: BufferId, range: Range<u64>) -> Self {
|
pub fn add_buffer(self, index: u32, buffer: BufferId, range: Range<u64>) -> Self {
|
||||||
self.add_binding(
|
self.add_binding(index, RenderResourceBinding::Buffer { buffer, range })
|
||||||
index,
|
|
||||||
RenderResourceBinding::Buffer {
|
|
||||||
buffer,
|
|
||||||
range,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(mut self) -> BindGroup {
|
pub fn finish(mut self) -> BindGroup {
|
||||||
|
@ -75,10 +69,7 @@ impl BindGroupBuilder {
|
||||||
|
|
||||||
fn hash_binding(&mut self, binding: &RenderResourceBinding) {
|
fn hash_binding(&mut self, binding: &RenderResourceBinding) {
|
||||||
match binding {
|
match binding {
|
||||||
RenderResourceBinding::Buffer {
|
RenderResourceBinding::Buffer { buffer, range } => {
|
||||||
buffer,
|
|
||||||
range,
|
|
||||||
} => {
|
|
||||||
RenderResourceId::Buffer(*buffer).hash(&mut self.hasher);
|
RenderResourceId::Buffer(*buffer).hash(&mut self.hasher);
|
||||||
range.hash(&mut self.hasher);
|
range.hash(&mut self.hasher);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,7 @@ use std::ops::Range;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum RenderResourceBinding {
|
pub enum RenderResourceBinding {
|
||||||
Buffer {
|
Buffer { buffer: BufferId, range: Range<u64> },
|
||||||
buffer: BufferId,
|
|
||||||
range: Range<u64>,
|
|
||||||
},
|
|
||||||
TextureView(TextureViewId),
|
TextureView(TextureViewId),
|
||||||
Sampler(SamplerId),
|
Sampler(SamplerId),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue