Skip to content

Commit

Permalink
Fix render layers (#68)
Browse files Browse the repository at this point in the history
This is a rebased version of #49 which looks like it only needed merge
conflicts resolved

---------

Co-authored-by: Conner Petzold <[email protected]>
Co-authored-by: Spencer C. Imbleau <[email protected]>
  • Loading branch information
3 people authored Jul 27, 2024
1 parent 5882f36 commit ad322d2
Show file tree
Hide file tree
Showing 13 changed files with 747 additions and 306 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Subheadings to categorize changes are `added, changed, deprecated, removed, fixe
### Added

- There is now a `default_font` feature that uses the same `FiraMono-subset.ttf` font used in the bevy/default_font feature.
- There is now a `render_layers` example.
- There is now a `cube_3d` example.

### Changed

Expand All @@ -24,11 +26,16 @@ Subheadings to categorize changes are `added, changed, deprecated, removed, fixe
- The field `VelloAssetBundle.vector` was renamed to `VelloAssetBundle.asset`.
- Renamed `VelloAssetAlignment` to `VelloAssetAnchor`. Fields were renamed `alignment` were renamed to `asset_anchor`.
- Renamed `VelloTextAlignment` to `VelloTextAnchor`. Fields were renamed `alignment` were renamed to `text_anchor`.
- The `SSRenderTarget` (fullscreen quad that renders your frame) no longer renders at a zepth of `-0.001`. This was a legacy hack used to ensure Gizmos rendered on-top.

### Removed

- Removed `ZFunction`s from the render pipeline. Now ordering is based solely on the `Transform`'s z component. If you dependeded on this behavior, you'll need to adjust the transform Z in a system prior to render.

### Fixed

- Text, assets, and scenes rendered will now correctly respect camera `RenderLayers`.

## 0.5.1

### Fixed
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ members = [
"examples/svg",
"examples/lottie",
"examples/scene_ui",
"examples/render_layers",
"examples/cube3d",
]

[workspace.package]
Expand Down
12 changes: 12 additions & 0 deletions examples/cube3d/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "cube3d"
version.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bevy_vello = { path = "../../" }
bevy = { workspace = true }
169 changes: 169 additions & 0 deletions examples/cube3d/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use bevy::{
prelude::*,
render::{
extract_component::{ExtractComponent, ExtractComponentPlugin},
render_asset::RenderAssets,
render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
},
renderer::{RenderDevice, RenderQueue},
texture::GpuImage,
Render, RenderApp, RenderSet,
},
};
use bevy_vello::{prelude::*, render::VelloRenderer, VelloPlugin};

#[derive(Component)]
pub struct VelloTarget(Handle<Image>);

impl ExtractComponent for VelloTarget {
type QueryData = &'static VelloTarget;
type QueryFilter = ();
type Out = Self;
fn extract_component(target: bevy::ecs::query::QueryItem<'_, Self::QueryData>) -> Option<Self> {
Some(Self(target.0.clone()))
}
}

// Marks the main pass cube, to which the texture is applied.
#[derive(Component)]
struct MainPassCube;

fn main() {
let mut app = App::new();

app.add_plugins(DefaultPlugins)
.add_plugins(VelloPlugin)
.add_systems(Startup, setup)
.add_systems(Update, cube_rotator_system)
.add_plugins(ExtractComponentPlugin::<VelloTarget>::default());

let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.add_systems(
Render,
render_texture
.in_set(RenderSet::Render)
.run_if(resource_exists::<RenderDevice>),
);

app.run();
}

fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut images: ResMut<Assets<Image>>,
) {
let size = Extent3d {
width: 512,
height: 512,
..default()
};
// This is the texture that will be rendered to.
let mut image = Image {
texture_descriptor: TextureDescriptor {
label: None,
size,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8Unorm,
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_DST
| TextureUsages::STORAGE_BINDING,
view_formats: &[],
},
..default()
};

// fill image.data with zeroes
image.resize(size);

let image_handle = images.add(image);

// This material has the texture that has been rendered.
let material_handle = materials.add(StandardMaterial {
base_color_texture: Some(image_handle.clone()),
reflectance: 0.02,
unlit: false,
..default()
});
// Main pass cube, with material containing the rendered first pass texture.
commands.spawn((
PbrBundle {
mesh: meshes.add(Cuboid::new(4.0, 4.0, 4.0)),
material: material_handle,
transform: Transform::from_xyz(0.0, 0.0, 1.5)
.with_rotation(Quat::from_rotation_x(-std::f32::consts::PI / 5.0)),
..default()
},
MainPassCube,
));
// The main pass camera.
commands.spawn(PointLightBundle {
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)),
..default()
});
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0.0, 0.0, 15.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
commands.spawn(VelloTarget(image_handle));
}

fn render_texture(
mut vello_renderer: Local<Option<VelloRenderer>>,
target: Query<&VelloTarget>,
device: Res<RenderDevice>,
gpu_images: Res<RenderAssets<GpuImage>>,
queue: Res<RenderQueue>,
time: Res<Time>,
) {
let renderer =
vello_renderer.get_or_insert_with(|| VelloRenderer::from_device(device.wgpu_device()));
let target = target.single();

let mut scene = VelloScene::default();
// Animate the scene
let sin_time = time.elapsed_seconds().sin().mul_add(0.5, 0.5);
let c = Vec3::lerp(
Vec3::new(-1.0, 0.0, 1.0),
Vec3::new(1.0, 0.0, 1.0),
sin_time + 0.5,
);
scene.fill(
peniko::Fill::NonZero,
kurbo::Affine::translate((128.0, 128.0)),
peniko::Color::rgb(c.x as f64, c.y as f64, c.z as f64),
None,
&kurbo::RoundedRect::new(0.0, 0.0, 256.0, 256.0, (sin_time as f64) * 128.0),
);

let gpu_image = gpu_images.get(&target.0).unwrap();
let params = vello::RenderParams {
base_color: vello::peniko::Color::WHITE,
width: gpu_image.size.x,
height: gpu_image.size.y,
antialiasing_method: vello::AaConfig::Area,
};
renderer
.render_to_texture(
device.wgpu_device(),
&queue,
&scene,
&gpu_image.texture_view,
&params,
)
.unwrap();
}

/// Rotates the outer cube (main pass)
fn cube_rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<MainPassCube>>) {
for mut transform in &mut query {
transform.rotate_x(1.0 * time.delta_seconds());
transform.rotate_y(0.7 * time.delta_seconds());
}
}
12 changes: 12 additions & 0 deletions examples/render_layers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "render_layers"
version.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bevy_vello = { path = "../../" }
bevy = { workspace = true, features = ["bevy_gizmos"] }
148 changes: 148 additions & 0 deletions examples/render_layers/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//! Shows how to use render layers.

use bevy::{color::palettes::css, prelude::*, render::view::RenderLayers};
use bevy_vello::{prelude::*, VelloPlugin};

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(VelloPlugin)
.add_systems(Startup, (setup_animation, setup_background))
.add_systems(
Update,
(
layer0_animation,
layer1_animation,
layer2_background,
run_gizmos,
),
)
.run();
}

/// A tag that will mark the scene on RenderLayer 0.
#[derive(Component)]
struct Layer0Scene;

/// A tag that will mark the scene on RenderLayer 1.
#[derive(Component)]
struct Layer1Scene;

/// A tag that will mark the scene on RenderLayer 2.
#[derive(Component)]
struct Layer2Scene;

fn setup_animation(mut commands: Commands) {
const LAYER_0: RenderLayers = RenderLayers::layer(0);
const LAYER_1: RenderLayers = RenderLayers::layer(1);

// This camera can see everything on Layer 1 and Layer 2.
commands.spawn((
Camera2dBundle {
camera: Camera {
// This camera will render AFTER the blue background camera!
order: 1,
..default()
},
..default()
},
LAYER_0.union(&LAYER_1),
));

commands.spawn((VelloSceneBundle::default(), Layer0Scene, LAYER_0));
commands.spawn((VelloSceneBundle::default(), Layer1Scene, LAYER_1));
}

fn setup_background(mut commands: Commands) {
const LAYER: RenderLayers = RenderLayers::layer(2);
commands.spawn((
Camera2dBundle {
camera: Camera {
// Render first
order: -1,
..default()
},
..default()
},
LAYER,
));
commands.spawn((VelloSceneBundle::default(), Layer2Scene, LAYER));
}

fn layer0_animation(
mut query_scene: Query<(&mut Transform, &mut VelloScene), With<Layer0Scene>>,
time: Res<Time>,
) {
let sin_time = time.elapsed_seconds().sin().mul_add(0.5, 0.5);
let (mut transform, mut scene) = query_scene.single_mut();
// Reset scene every frame
*scene = VelloScene::default();

// Animate color green to blue
let c = Vec3::lerp(
Vec3::new(0.0, 1.0, -1.0),
Vec3::new(0.0, 1.0, 1.0),
sin_time + 0.5,
);

// Animate the corner radius
scene.fill(
peniko::Fill::NonZero,
kurbo::Affine::default(),
peniko::Color::rgb(c.x as f64, c.y as f64, c.z as f64),
None,
&kurbo::RoundedRect::new(-50.0, -50.0, 50.0, 50.0, (sin_time as f64) * 50.0),
);

transform.scale = Vec3::lerp(Vec3::ONE * 0.5, Vec3::ONE * 1.0, sin_time);
transform.translation =
Vec3::lerp(Vec3::X * -100.0, Vec3::X * 100.0, sin_time) - Vec3::Y * 100.0;
transform.rotation = Quat::from_rotation_z(-std::f32::consts::TAU * sin_time);
}

fn layer1_animation(
mut query_scene: Query<(&mut Transform, &mut VelloScene), With<Layer1Scene>>,
time: Res<Time>,
) {
let sin_time = time.elapsed_seconds().sin().mul_add(0.5, 0.5);
let (mut transform, mut scene) = query_scene.single_mut();
// Reset scene every frame
*scene = VelloScene::default();

// Animate color green to blue
let c = Vec3::lerp(
Vec3::new(1.0, -1.0, 0.0),
Vec3::new(1.0, 1.0, 0.0),
sin_time + 0.5,
);

// Animate the corner radius
scene.fill(
peniko::Fill::NonZero,
kurbo::Affine::default(),
peniko::Color::rgb(c.x as f64, c.y as f64, c.z as f64),
None,
&kurbo::RoundedRect::new(-50.0, -50.0, 50.0, 50.0, (sin_time as f64) * 50.0),
);

transform.scale = Vec3::lerp(Vec3::ONE * 0.5, Vec3::ONE * 1.0, sin_time);
transform.translation =
Vec3::lerp(Vec3::X * -100.0, Vec3::X * 100.0, sin_time) * Vec3::NEG_X + Vec3::Y * 100.0;
transform.rotation = Quat::from_rotation_z(-std::f32::consts::TAU * sin_time);
}

fn layer2_background(mut query_scene: Query<&mut VelloScene, With<Layer2Scene>>) {
let mut scene = query_scene.single_mut();
*scene = VelloScene::default();
scene.fill(
peniko::Fill::NonZero,
kurbo::Affine::default(),
peniko::Color::rgb(0.0, 0.0, 1.0),
None,
&kurbo::RoundedRect::new(-200.0, -200.0, 200.0, 200.0, 0.0),
);
}

fn run_gizmos(mut gizmos: Gizmos) {
gizmos.circle_2d(Vec2::splat(0.0), 20.0, css::RED);
}
2 changes: 1 addition & 1 deletion examples/text/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bevy_vello = { path = "../../", features = ["default_font"] }
bevy = { workspace = true }
bevy = { workspace = true, features = ["default_font"] }
Loading

0 comments on commit ad322d2

Please sign in to comment.