-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
use bevy::{ | ||
core_pipeline::{draw_3d_graph, node, AlphaMask3d, Opaque3d, Transparent3d}, | ||
prelude::*, | ||
reflect::TypeUuid, | ||
render::{ | ||
camera::{ActiveCameras, Camera, CameraProjection, ExtractedCameraNames, RenderTarget}, | ||
render_graph::{NodeRunError, RenderGraph, RenderGraphContext, SlotValue}, | ||
render_phase::RenderPhase, | ||
render_resource::{ | ||
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, | ||
}, | ||
renderer::RenderContext, | ||
view::RenderLayers, | ||
RenderApp, RenderStage, | ||
}, | ||
}; | ||
|
||
pub const RENDER_IMAGE_HANDLE: HandleUntyped = | ||
HandleUntyped::weak_from_u64(Image::TYPE_UUID, 13378939762009864029); | ||
|
||
pub const FIRST_PASS_DRIVER: &str = "first_pass_driver"; | ||
pub const FIRST_PASS_CAMERA: &str = "first_pass_camera"; | ||
|
||
#[derive(Component)] | ||
struct FirstPassCube; | ||
#[derive(Component)] | ||
struct MainPassCube; | ||
|
||
/// rotates the inner cube (first pass) | ||
fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<FirstPassCube>>) { | ||
for mut transform in query.iter_mut() { | ||
transform.rotation *= Quat::from_rotation_x(1.5 * time.delta_seconds()); | ||
transform.rotation *= Quat::from_rotation_z(1.3 * time.delta_seconds()); | ||
} | ||
} | ||
|
||
/// rotates the outer cube (main pass) | ||
fn cube_rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<MainPassCube>>) { | ||
for mut transform in query.iter_mut() { | ||
transform.rotation *= Quat::from_rotation_x(1.0 * time.delta_seconds()); | ||
transform.rotation *= Quat::from_rotation_y(0.7 * time.delta_seconds()); | ||
} | ||
} | ||
|
||
fn setup( | ||
mut commands: Commands, | ||
mut meshes: ResMut<Assets<Mesh>>, | ||
mut materials: ResMut<Assets<StandardMaterial>>, | ||
mut active_cameras: ResMut<ActiveCameras>, | ||
mut images: ResMut<Assets<Image>>, | ||
mut clear_color: ResMut<ClearColor>, | ||
) { | ||
let size = Extent3d { | ||
width: 512, | ||
height: 512, | ||
..Default::default() | ||
}; | ||
let image = Image { | ||
data: vec![0; size.width as usize * size.height as usize * 4], | ||
texture_descriptor: TextureDescriptor { | ||
label: None, | ||
size, | ||
dimension: TextureDimension::D2, | ||
format: TextureFormat::Bgra8UnormSrgb, | ||
mip_level_count: 1, | ||
sample_count: 1, | ||
usage: TextureUsages::TEXTURE_BINDING | ||
| TextureUsages::COPY_DST | ||
| TextureUsages::RENDER_ATTACHMENT, | ||
}, | ||
..Default::default() | ||
}; | ||
let image_handle = images.set(RENDER_IMAGE_HANDLE, image); | ||
|
||
let cube_handle = meshes.add(Mesh::from(shape::Cube { size: 4.0 })); | ||
let cube_material_handle = materials.add(StandardMaterial { | ||
base_color: Color::rgb(0.8, 0.7, 0.6), | ||
reflectance: 0.02, | ||
unlit: false, | ||
..Default::default() | ||
}); | ||
|
||
commands | ||
.spawn_bundle(PbrBundle { | ||
mesh: cube_handle, | ||
material: cube_material_handle, | ||
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 1.0)), | ||
..Default::default() | ||
}) | ||
.insert(FirstPassCube) | ||
.insert(RenderLayers::layer(1)); | ||
|
||
// light | ||
// note: currently lights are shared between passes! | ||
commands.spawn_bundle(PointLightBundle { | ||
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), | ||
..Default::default() | ||
}); | ||
|
||
// camera | ||
let render_target = RenderTarget::Image(image_handle); | ||
clear_color.insert(render_target.clone(), Color::WHITE); | ||
let mut first_pass_camera = PerspectiveCameraBundle { | ||
camera: Camera { | ||
name: Some(FIRST_PASS_CAMERA.to_string()), | ||
target: render_target, | ||
..Default::default() | ||
}, | ||
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) | ||
.looking_at(Vec3::default(), Vec3::Y), | ||
..Default::default() | ||
}; | ||
active_cameras.add(FIRST_PASS_CAMERA); | ||
|
||
let camera_projection = &mut first_pass_camera.perspective_projection; | ||
camera_projection.update(size.width as f32, size.height as f32); | ||
first_pass_camera.camera.projection_matrix = camera_projection.get_projection_matrix(); | ||
first_pass_camera.camera.depth_calculation = camera_projection.depth_calculation(); | ||
|
||
commands | ||
.spawn_bundle(first_pass_camera) | ||
.insert(RenderLayers::layer(1)); | ||
// NOTE: omitting the RenderLayers component may cause a validation error: | ||
// | ||
// thread 'main' panicked at 'wgpu error: Validation Error | ||
// | ||
// Caused by: | ||
// In a RenderPass | ||
// note: encoder = `<CommandBuffer-(0, 1, Metal)>` | ||
// In a pass parameter | ||
// note: command buffer = `<CommandBuffer-(0, 1, Metal)>` | ||
// Attempted to use texture (5, 1, Metal) mips 0..1 layers 0..1 as a combination of COLOR_TARGET within a usage scope. | ||
|
||
let cube_size = 4.0; | ||
let cube_handle = meshes.add(Mesh::from(shape::Box::new(cube_size, cube_size, cube_size))); | ||
|
||
let material_handle = materials.add(StandardMaterial { | ||
base_color_texture: Some(RENDER_IMAGE_HANDLE.typed()), | ||
reflectance: 0.02, | ||
unlit: false, | ||
..Default::default() | ||
}); | ||
|
||
// add entities to the world | ||
commands | ||
.spawn_bundle(PbrBundle { | ||
mesh: cube_handle, | ||
material: material_handle, | ||
transform: Transform { | ||
translation: Vec3::new(0.0, 0.0, 1.5), | ||
rotation: Quat::from_rotation_x(-std::f32::consts::PI / 5.0), | ||
..Default::default() | ||
}, | ||
..Default::default() | ||
}) | ||
.insert(MainPassCube); | ||
|
||
commands.spawn_bundle(PerspectiveCameraBundle { | ||
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) | ||
.looking_at(Vec3::default(), Vec3::Y), | ||
..Default::default() | ||
}); | ||
} | ||
|
||
fn main() { | ||
let mut app = App::new(); | ||
app.insert_resource(Msaa { samples: 4 }) | ||
.add_plugins(DefaultPlugins) | ||
.add_startup_system(setup) | ||
.add_system(cube_rotator_system) | ||
.add_system(rotator_system); | ||
|
||
let render_app = app.sub_app(RenderApp); | ||
render_app.add_system_to_stage(RenderStage::Extract, extract_first_pass_camera_phases); | ||
let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap(); | ||
graph.add_node(FIRST_PASS_DRIVER, FirstPassCameraDriver); | ||
graph | ||
.add_node_edge(node::MAIN_PASS_DEPENDENCIES, FIRST_PASS_DRIVER) | ||
.unwrap(); | ||
graph | ||
.add_node_edge(node::CLEAR_PASS_DRIVER, FIRST_PASS_DRIVER) | ||
.unwrap(); | ||
graph | ||
.add_node_edge(FIRST_PASS_DRIVER, node::MAIN_PASS_DRIVER) | ||
.unwrap(); | ||
app.run(); | ||
} | ||
|
||
fn extract_first_pass_camera_phases(mut commands: Commands, active_cameras: Res<ActiveCameras>) { | ||
if let Some(camera) = active_cameras.get(FIRST_PASS_CAMERA) { | ||
if let Some(entity) = camera.entity { | ||
commands.get_or_spawn(entity).insert_bundle(( | ||
RenderPhase::<Opaque3d>::default(), | ||
RenderPhase::<AlphaMask3d>::default(), | ||
RenderPhase::<Transparent3d>::default(), | ||
)); | ||
} | ||
} | ||
} | ||
|
||
struct FirstPassCameraDriver; | ||
impl bevy::render::render_graph::Node for FirstPassCameraDriver { | ||
fn run( | ||
&self, | ||
graph: &mut RenderGraphContext, | ||
_render_context: &mut RenderContext, | ||
world: &World, | ||
) -> Result<(), NodeRunError> { | ||
let extracted_cameras = world.get_resource::<ExtractedCameraNames>().unwrap(); | ||
if let Some(camera_3d) = extracted_cameras.entities.get(FIRST_PASS_CAMERA) { | ||
// *** | ||
graph.run_sub_graph(draw_3d_graph::NAME, vec![SlotValue::Entity(*camera_3d)])?; | ||
} | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters