Skip to content

Commit 334a857

Browse files
authored
StandardMaterial flat values (#3)
StandardMaterial flat values
1 parent 53a442f commit 334a857

File tree

16 files changed

+341
-105
lines changed

16 files changed

+341
-105
lines changed

crates/bevy_ecs/src/system/commands.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,7 @@ impl<'a> Commands<'a> {
8585
}
8686

8787
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'a, '_> {
88-
self.add(GetOrSpawn {
89-
entity,
90-
});
88+
self.add(GetOrSpawn { entity });
9189
EntityCommands {
9290
entity,
9391
commands: self,
@@ -318,8 +316,7 @@ pub struct GetOrSpawn {
318316
entity: Entity,
319317
}
320318

321-
impl Command for GetOrSpawn
322-
{
319+
impl Command for GetOrSpawn {
323320
fn write(self: Box<Self>, world: &mut World) {
324321
world.get_or_spawn(self.entity);
325322
}

crates/bevy_ecs/src/system/exclusive_system.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use crate::{archetype::ArchetypeGeneration, system::{check_system_change_tick, BoxedSystem, IntoSystem, System, SystemId}, world::World};
1+
use crate::{
2+
archetype::ArchetypeGeneration,
3+
system::{check_system_change_tick, BoxedSystem, IntoSystem, System, SystemId},
4+
world::World,
5+
};
26
use std::borrow::Cow;
37

48
pub trait ExclusiveSystem: Send + Sync + 'static {

crates/bevy_ecs/src/world/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ impl World {
100100
&mut self.entities
101101
}
102102

103-
104103
/// Retrieves this world's [Archetypes] collection
105104
#[inline]
106105
pub fn archetypes(&self) -> &Archetypes {

crates/crevice/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,4 @@ pub mod internal;
139139

140140
mod mint;
141141

142-
mod glam;
142+
mod glam;

examples/3d/3d_scene_pipelined.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,39 @@ fn setup(
3737
// plane
3838
commands.spawn_bundle(PbrBundle {
3939
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
40-
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
40+
material: materials.add(StandardMaterial {
41+
color: Color::INDIGO,
42+
roughness: 1.0,
43+
..Default::default()
44+
}),
4145
..Default::default()
4246
});
4347
// cube
4448
commands
4549
.spawn_bundle(PbrBundle {
4650
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
47-
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
51+
material: materials.add(StandardMaterial {
52+
color: Color::PINK,
53+
roughness: 0.0,
54+
metallic: 1.0,
55+
reflectance: 1.0,
56+
..Default::default()
57+
}),
4858
transform: Transform::from_xyz(0.0, 1.0, 0.0),
4959
..Default::default()
5060
})
5161
.insert(Movable);
5262
// sphere
5363
commands
5464
.spawn_bundle(PbrBundle {
55-
mesh: meshes.add(Mesh::from(shape::UVSphere { radius: 0.5, ..Default::default() })),
56-
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
65+
mesh: meshes.add(Mesh::from(shape::UVSphere {
66+
radius: 0.5,
67+
..Default::default()
68+
})),
69+
material: materials.add(StandardMaterial {
70+
color: Color::LIME_GREEN,
71+
..Default::default()
72+
}),
5773
transform: Transform::from_xyz(1.5, 1.0, 1.5),
5874
..Default::default()
5975
})

pipelined/bevy_pbr2/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ pub use material::*;
99
pub use render::*;
1010

1111
use bevy_app::prelude::*;
12-
use bevy_asset::AddAsset;
1312
use bevy_ecs::prelude::*;
1413
use bevy_render2::{
1514
core_pipeline,
@@ -29,7 +28,7 @@ pub struct PbrPlugin;
2928

3029
impl Plugin for PbrPlugin {
3130
fn build(&self, app: &mut App) {
32-
app.add_asset::<StandardMaterial>();
31+
app.add_plugin(StandardMaterialPlugin);
3332

3433
let render_app = app.sub_app_mut(0);
3534
render_app
@@ -50,6 +49,7 @@ impl Plugin for PbrPlugin {
5049
.add_system_to_stage(RenderStage::Cleanup, render::cleanup_view_lights.system())
5150
.init_resource::<PbrShaders>()
5251
.init_resource::<ShadowShaders>()
52+
.init_resource::<MaterialMeta>()
5353
.init_resource::<MeshMeta>()
5454
.init_resource::<LightMeta>();
5555

pipelined/bevy_pbr2/src/material.rs

Lines changed: 173 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,71 @@
1-
use bevy_reflect::{Reflect, TypeUuid};
2-
use bevy_render2::color::Color;
1+
use bevy_app::{App, CoreStage, EventReader, Plugin};
2+
use bevy_asset::{AddAsset, AssetEvent, Assets, Handle};
3+
use bevy_ecs::prelude::*;
4+
use bevy_math::Vec4;
5+
use bevy_reflect::TypeUuid;
6+
use bevy_render2::{
7+
color::Color,
8+
render_command::RenderCommandQueue,
9+
render_resource::{BufferId, BufferInfo, BufferUsage},
10+
renderer::{RenderResourceContext, RenderResources},
11+
};
12+
use bevy_utils::HashSet;
13+
use crevice::std140::{AsStd140, Std140};
314

4-
#[derive(Debug, Default, Clone, TypeUuid, Reflect)]
15+
// TODO: this shouldn't live in the StandardMaterial type
16+
#[derive(Debug, Clone, Copy)]
17+
pub struct StandardMaterialGpuData {
18+
pub buffer: BufferId,
19+
}
20+
21+
/// A material with "standard" properties used in PBR lighting
22+
/// Standard property values with pictures here https://google.github.io/filament/Material%20Properties.pdf
23+
#[derive(Debug, Clone, TypeUuid)]
524
#[uuid = "7494888b-c082-457b-aacf-517228cc0c22"]
625
pub struct StandardMaterial {
26+
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
27+
/// in between.
728
pub color: Color,
29+
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
30+
/// Defaults to minimum of 0.089
31+
pub roughness: f32,
32+
/// From [0.0, 1.0], dielectric to pure metallic
33+
pub metallic: f32,
34+
/// Specular intensity for non-metals on a linear scale of [0.0, 1.0]
35+
/// defaults to 0.5 which is mapped to 4% reflectance in the shader
36+
pub reflectance: f32,
37+
// Use a color for user friendliness even though we technically don't use the alpha channel
38+
// Might be used in the future for exposure correction in HDR
39+
pub emissive: Color,
40+
pub gpu_data: Option<StandardMaterialGpuData>,
41+
}
42+
43+
impl StandardMaterial {
44+
pub fn gpu_data(&self) -> Option<&StandardMaterialGpuData> {
45+
self.gpu_data.as_ref()
46+
}
47+
}
48+
49+
impl Default for StandardMaterial {
50+
fn default() -> Self {
51+
StandardMaterial {
52+
color: Color::rgb(1.0, 1.0, 1.0),
53+
// This is the minimum the roughness is clamped to in shader code
54+
// See https://google.github.io/filament/Filament.html#materialsystem/parameterization/
55+
// It's the minimum floating point value that won't be rounded down to 0 in the
56+
// calculations used. Although technically for 32-bit floats, 0.045 could be
57+
// used.
58+
roughness: 0.089,
59+
// Few materials are purely dielectric or metallic
60+
// This is just a default for mostly-dielectric
61+
metallic: 0.01,
62+
// Minimum real-world reflectance is 2%, most materials between 2-5%
63+
// Expressed in a linear scale and equivalent to 4% reflectance see https://google.github.io/filament/Material%20Properties.pdf
64+
reflectance: 0.5,
65+
emissive: Color::BLACK,
66+
gpu_data: None,
67+
}
68+
}
869
}
970

1071
impl From<Color> for StandardMaterial {
@@ -15,3 +76,112 @@ impl From<Color> for StandardMaterial {
1576
}
1677
}
1778
}
79+
80+
#[derive(Clone, AsStd140)]
81+
pub struct StandardMaterialUniformData {
82+
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
83+
/// in between.
84+
pub color: Vec4,
85+
/// Linear perceptual roughness, clamped to [0.089, 1.0] in the shader
86+
/// Defaults to minimum of 0.089
87+
pub roughness: f32,
88+
/// From [0.0, 1.0], dielectric to pure metallic
89+
pub metallic: f32,
90+
/// Specular intensity for non-metals on a linear scale of [0.0, 1.0]
91+
/// defaults to 0.5 which is mapped to 4% reflectance in the shader
92+
pub reflectance: f32,
93+
// Use a color for user friendliness even though we technically don't use the alpha channel
94+
// Might be used in the future for exposure correction in HDR
95+
pub emissive: Vec4,
96+
}
97+
98+
pub struct StandardMaterialPlugin;
99+
100+
impl Plugin for StandardMaterialPlugin {
101+
fn build(&self, app: &mut App) {
102+
app.add_asset::<StandardMaterial>().add_system_to_stage(
103+
CoreStage::PostUpdate,
104+
standard_material_resource_system.system(),
105+
);
106+
}
107+
}
108+
109+
pub fn standard_material_resource_system(
110+
render_resource_context: Res<RenderResources>,
111+
mut render_command_queue: ResMut<RenderCommandQueue>,
112+
mut materials: ResMut<Assets<StandardMaterial>>,
113+
mut material_events: EventReader<AssetEvent<StandardMaterial>>,
114+
) {
115+
let mut changed_materials = HashSet::default();
116+
let render_resource_context = &**render_resource_context;
117+
for event in material_events.iter() {
118+
match event {
119+
AssetEvent::Created { ref handle } => {
120+
changed_materials.insert(handle.clone_weak());
121+
}
122+
AssetEvent::Modified { ref handle } => {
123+
changed_materials.insert(handle.clone_weak());
124+
// TODO: uncomment this to support mutated materials
125+
// remove_current_material_resources(render_resource_context, handle, &mut materials);
126+
}
127+
AssetEvent::Removed { ref handle } => {
128+
remove_current_material_resources(render_resource_context, handle, &mut materials);
129+
// if material was modified and removed in the same update, ignore the modification
130+
// events are ordered so future modification events are ok
131+
changed_materials.remove(handle);
132+
}
133+
}
134+
}
135+
136+
// update changed material data
137+
for changed_material_handle in changed_materials.iter() {
138+
if let Some(material) = materials.get_mut(changed_material_handle) {
139+
// TODO: this avoids creating new materials each frame because storing gpu data in the material flags it as
140+
// modified. this prevents hot reloading and therefore can't be used in an actual impl.
141+
if material.gpu_data.is_some() {
142+
continue;
143+
}
144+
145+
let value = StandardMaterialUniformData {
146+
color: material.color.into(),
147+
roughness: material.roughness,
148+
metallic: material.metallic,
149+
reflectance: material.reflectance,
150+
emissive: material.emissive.into(),
151+
};
152+
let value_std140 = value.as_std140();
153+
154+
let size = StandardMaterialUniformData::std140_size_static();
155+
156+
let staging_buffer = render_resource_context.create_buffer_with_data(
157+
BufferInfo {
158+
size,
159+
buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
160+
mapped_at_creation: true,
161+
},
162+
value_std140.as_bytes(),
163+
);
164+
165+
let buffer = render_resource_context.create_buffer(BufferInfo {
166+
size,
167+
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
168+
mapped_at_creation: false,
169+
});
170+
171+
render_command_queue.copy_buffer_to_buffer(staging_buffer, 0, buffer, 0, size as u64);
172+
render_command_queue.free_buffer(staging_buffer);
173+
174+
material.gpu_data = Some(StandardMaterialGpuData { buffer });
175+
}
176+
}
177+
}
178+
179+
fn remove_current_material_resources(
180+
render_resource_context: &dyn RenderResourceContext,
181+
handle: &Handle<StandardMaterial>,
182+
materials: &mut Assets<StandardMaterial>,
183+
) {
184+
if let Some(gpu_data) = materials.get_mut(handle).and_then(|t| t.gpu_data.take()) {
185+
render_resource_context.remove_buffer(gpu_data.buffer);
186+
}
187+
}

pipelined/bevy_pbr2/src/render/light.rs

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -325,44 +325,46 @@ impl Node for ShadowPassNode {
325325
world: &World,
326326
) -> Result<(), NodeRunError> {
327327
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
328-
let view_lights = self.main_view_query.get_manual(world, view_entity).unwrap();
329-
for view_light_entity in view_lights.lights.iter().copied() {
330-
let (view_light, shadow_phase) = self
331-
.view_light_query
332-
.get_manual(world, view_light_entity)
333-
.unwrap();
334-
let pass_descriptor = PassDescriptor {
335-
color_attachments: Vec::new(),
336-
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
337-
attachment: TextureAttachment::Id(view_light.depth_texture),
338-
depth_ops: Some(Operations {
339-
load: LoadOp::Clear(1.0),
340-
store: true,
328+
if let Some(view_lights) = self.main_view_query.get_manual(world, view_entity).ok() {
329+
for view_light_entity in view_lights.lights.iter().copied() {
330+
let (view_light, shadow_phase) = self
331+
.view_light_query
332+
.get_manual(world, view_light_entity)
333+
.unwrap();
334+
let pass_descriptor = PassDescriptor {
335+
color_attachments: Vec::new(),
336+
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
337+
attachment: TextureAttachment::Id(view_light.depth_texture),
338+
depth_ops: Some(Operations {
339+
load: LoadOp::Clear(1.0),
340+
store: true,
341+
}),
342+
stencil_ops: None,
341343
}),
342-
stencil_ops: None,
343-
}),
344-
sample_count: 1,
345-
};
346-
347-
let draw_functions = world.get_resource::<DrawFunctions>().unwrap();
348-
349-
render_context.begin_render_pass(
350-
&pass_descriptor,
351-
&mut |render_pass: &mut dyn RenderPass| {
352-
let mut draw_functions = draw_functions.write();
353-
let mut tracked_pass = TrackedRenderPass::new(render_pass);
354-
for drawable in shadow_phase.drawn_things.iter() {
355-
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
356-
draw_function.draw(
357-
world,
358-
&mut tracked_pass,
359-
view_light_entity,
360-
drawable.draw_key,
361-
drawable.sort_key,
362-
);
363-
}
364-
},
365-
);
344+
sample_count: 1,
345+
};
346+
347+
let draw_functions = world.get_resource::<DrawFunctions>().unwrap();
348+
349+
render_context.begin_render_pass(
350+
&pass_descriptor,
351+
&mut |render_pass: &mut dyn RenderPass| {
352+
let mut draw_functions = draw_functions.write();
353+
let mut tracked_pass = TrackedRenderPass::new(render_pass);
354+
for drawable in shadow_phase.drawn_things.iter() {
355+
let draw_function =
356+
draw_functions.get_mut(drawable.draw_function).unwrap();
357+
draw_function.draw(
358+
world,
359+
&mut tracked_pass,
360+
view_light_entity,
361+
drawable.draw_key,
362+
drawable.sort_key,
363+
);
364+
}
365+
},
366+
);
367+
}
366368
}
367369

368370
Ok(())

0 commit comments

Comments
 (0)