Skip to content

Commit

Permalink
feat: render ordering by Z (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
pengiie authored Dec 21, 2023
1 parent ba9f731 commit 4783108
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 128 deletions.
8 changes: 1 addition & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,7 @@ A bevy plugin which provides rendering support for [lottie](https://lottiefiles.

## Features

- Spawn vector graphics on separate layers
|Layer|Render order|
|---|---|
|Background|Always behind all other layers|
|Ground|2.5D-style render ordering via Y-coordinate|
|Foreground|Always on top of Ground/Background|
|UI|On top of Foreground layer; shows Bevy UI Nodes bundled with a `VelloVector` |
- Spawn vector graphics rendering either in screen-space or world-space coordinates.
- Support for fonts
- NOTE: to avoid conflict with bevy's built-in font loader, rename fonts used by `bevy-vello` to something else (example: `*.vtff`). This can probably be an improvement in the future.
- Debug draw gizmos for the objects local origin (red X) and canvas size (white bounding box)
Expand Down
1 change: 0 additions & 1 deletion demo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ fn setup_vector_graphics(mut commands: Commands, asset_server: ResMut<AssetServe
commands.spawn(Camera2dBundle::default());
commands
.spawn(VelloVectorBundle {
layer: bevy_vello::Layer::Background,
origin: bevy_vello::Origin::Center,
// Can only load *.json (Lottie animations) and *.svg (static vector graphics)
vector: asset_server.load("../assets/squid.json"),
Expand Down
21 changes: 9 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,12 @@ impl Plugin for VelloPlugin {
}
}

#[derive(PartialEq, Component, Default, Copy, Clone, Debug, Reflect)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Component, Default, Copy, Clone, Debug, Reflect)]
#[reflect(Component)]
pub enum Layer {
Background,
Shadow,
pub enum RenderMode {
#[default]
Ground,
Foreground,
UI,
WorldSpace = 0,
ScreenSpace = 1,
}

#[derive(PartialEq, Component, Default, Copy, Clone, Debug, Reflect)]
Expand Down Expand Up @@ -129,8 +126,8 @@ impl ColorPaletteSwap {
#[derive(Bundle)]
pub struct VelloVectorBundle {
pub vector: Handle<VelloVector>,
/// Configures the draw order within the vello canvas
pub layer: Layer,
/// The coordinate space in which this vector should be rendered.
pub render_mode: RenderMode,
/// This object's transform local origin. Enable debug visualizations to visualize (red X)
pub origin: Origin,
pub transform: Transform,
Expand All @@ -149,7 +146,7 @@ impl Default for VelloVectorBundle {
fn default() -> Self {
Self {
vector: Default::default(),
layer: Default::default(),
render_mode: RenderMode::WorldSpace,
origin: Default::default(),
transform: Default::default(),
global_transform: Default::default(),
Expand All @@ -172,7 +169,7 @@ pub struct VelloText {
pub struct VelloTextBundle {
pub font: Handle<VelloFont>,
pub text: VelloText,
pub layer: Layer,
pub render_mode: RenderMode,
pub transform: Transform,
pub global_transform: GlobalTransform,
/// User indication of whether an entity is visible
Expand All @@ -188,7 +185,7 @@ impl Default for VelloTextBundle {
Self {
font: Default::default(),
text: Default::default(),
layer: Layer::Foreground,
render_mode: RenderMode::WorldSpace,
transform: Default::default(),
global_transform: Default::default(),
visibility: Visibility::Inherited,
Expand Down
21 changes: 12 additions & 9 deletions src/renderer/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ use bevy::{
window::PrimaryWindow,
};

use crate::{font::VelloFont, ColorPaletteSwap, Layer, Origin, VelloText, VelloVector};
use crate::{font::VelloFont, ColorPaletteSwap, Origin, RenderMode, VelloText, VelloVector};

#[derive(Component, Clone)]
pub struct ExtractedRenderVector {
pub vector_handle: Handle<VelloVector>,
pub render_data: VelloVector,
pub transform: GlobalTransform,
pub layer: Layer,
pub render_mode: RenderMode,
pub origin: Origin,
pub color_pallette_swap: Option<ColorPaletteSwap>,
pub ui_node: Option<Node>,
Expand All @@ -22,7 +22,7 @@ pub fn vector_instances(
query_vectors: Extract<
Query<(
&Handle<VelloVector>,
&Layer,
&RenderMode,
Option<&Origin>,
&GlobalTransform,
Option<&ColorPaletteSwap>,
Expand All @@ -35,7 +35,7 @@ pub fn vector_instances(
) {
for (
vello_vector_handle,
layer,
render_mode,
origin,
transform,
color_pallette_swap,
Expand All @@ -50,7 +50,7 @@ pub fn vector_instances(
vector_handle: vello_vector_handle.clone(),
render_data: asset_data.to_owned(),
transform: *transform,
layer: *layer,
render_mode: *render_mode,
origin: origin.copied().unwrap_or_default(),
color_pallette_swap: color_pallette_swap.cloned(),
ui_node: ui_node.cloned(),
Expand All @@ -65,28 +65,31 @@ pub struct ExtractedRenderText {
pub font: Handle<VelloFont>,
pub text: VelloText,
pub transform: GlobalTransform,
pub layer: Layer,
pub render_mode: RenderMode,
}

impl ExtractComponent for ExtractedRenderText {
type Query = (
&'static Handle<VelloFont>,
&'static VelloText,
&'static GlobalTransform,
&'static Layer,
&'static RenderMode,
);

type Filter = ();
type Out = Self;

fn extract_component(
(vello_font_handle, text, transform, layer): bevy::ecs::query::QueryItem<'_, Self::Query>,
(vello_font_handle, text, transform, render_mode): bevy::ecs::query::QueryItem<
'_,
Self::Query,
>,
) -> Option<Self> {
Some(Self {
font: vello_font_handle.clone(),
text: text.clone(),
transform: *transform,
layer: *layer,
render_mode: *render_mode,
})
}
}
Expand Down
17 changes: 8 additions & 9 deletions src/renderer/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use vello::kurbo::Affine;

use crate::{
assets::vector::{Vector, VelloVector},
ColorPaletteSwap, Layer,
ColorPaletteSwap, RenderMode,
};

use super::extract::{ExtractedPixelScale, ExtractedRenderText, ExtractedRenderVector};
Expand Down Expand Up @@ -96,12 +96,11 @@ pub fn prepare_vector_affines(
None => continue,
};

// The vello scene transform is world-space for all normal vectors and screen-space for UI vectors
let raw_transform = match render_vector.layer {
Layer::UI => {
let raw_transform = match render_vector.render_mode {
RenderMode::ScreenSpace => {
let mut model_matrix = world_transform.compute_matrix().mul_scalar(pixel_scale.0);

// Make the UI vector instance sized to fill the entire UI Node box if it's bundled with a Node
// Make the screen space vector instance sized to fill the entire UI Node box if it's bundled with a Node
if let Some(node) = &render_vector.ui_node {
let fill_scale = node.size() / vector_size;
model_matrix.x_axis.x *= fill_scale.x;
Expand All @@ -112,7 +111,7 @@ pub fn prepare_vector_affines(
local_center_matrix.w_axis.y *= -1.0;
model_matrix * local_center_matrix
}
_ => {
RenderMode::WorldSpace => {
let local_matrix = match render_vector.origin {
crate::Origin::BottomCenter => local_bottom_center_matrix,
crate::Origin::Center => local_center_matrix,
Expand Down Expand Up @@ -189,9 +188,9 @@ pub fn prepare_text_affines(
let view_proj_matrix = projection_mat * view_mat.inverse();
let vello_matrix = ndc_to_pixels_matrix * view_proj_matrix;

let raw_transform = match render_text.layer {
Layer::UI => world_transform.compute_matrix().mul_scalar(pixel_scale.0),
_ => vello_matrix * model_matrix,
let raw_transform = match render_text.render_mode {
RenderMode::ScreenSpace => world_transform.compute_matrix().mul_scalar(pixel_scale.0),
RenderMode::WorldSpace => vello_matrix * model_matrix,
};

let transform: [f32; 16] = raw_transform.to_cols_array();
Expand Down
147 changes: 59 additions & 88 deletions src/renderer/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use vello::{RenderParams, Scene, SceneBuilder};
use crate::{
assets::vector::{Vector, VelloVector},
font::VelloFont,
Layer,
RenderMode,
};

use super::{
Expand Down Expand Up @@ -104,100 +104,71 @@ pub fn render_scene(
let mut scene = Scene::default();
let mut builder = SceneBuilder::for_scene(&mut scene);

// Background items: z ordered
let mut vector_render_queue: Vec<(_, _)> = render_vectors
.iter()
.filter(|(_, v)| v.layer == Layer::Background)
.collect();
vector_render_queue.sort_by(|(_, a), (_, b)| {
let a = a.transform.translation().z;
let b = b.transform.translation().z;
a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)
});

// Shadow items: z ordered
let mut shadow_items: Vec<(_, _)> = render_vectors
.iter()
.filter(|(_, v)| v.layer == Layer::Shadow)
.collect();
shadow_items.sort_by(|(_, a), (_, b)| {
let a = a.transform.translation().z;
let b = b.transform.translation().z;
a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)
});
vector_render_queue.append(&mut shadow_items);

// Ground items: y ordered
let mut middle_items: Vec<(_, _)> = render_vectors
.iter()
.filter(|(_, v)| v.layer == Layer::Ground)
.collect();
middle_items.sort_by(|(_, a), (_, b)| {
let a = a.transform.translation().y;
let b = b.transform.translation().y;
b.partial_cmp(&a).unwrap_or(std::cmp::Ordering::Equal)
});
vector_render_queue.append(&mut middle_items);

// Foreground items: z ordered
let mut fg_items: Vec<(_, _)> = render_vectors
.iter()
.filter(|(_, v)| v.layer == Layer::Foreground)
.collect();
fg_items.sort_by(|(_, a), (_, b)| {
let a = a.transform.translation().z;
let b = b.transform.translation().z;
a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)
});
vector_render_queue.append(&mut fg_items);

// UI items
let mut ui_items: Vec<(_, _)> = render_vectors
.iter()
.filter(|(_, v)| v.layer == Layer::UI)
.collect();
ui_items.sort_by(|(_, a), (_, b)| {
let a = a.transform.translation().z;
let b = b.transform.translation().z;
a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)
});
vector_render_queue.append(&mut ui_items);
enum RenderItem<'a> {
Vector(&'a ExtractedRenderVector),
Text(&'a ExtractedRenderText),
}
let mut render_queue: Vec<(f32, RenderMode, (&PreparedAffine, RenderItem))> =
render_vectors
.iter()
.map(|(a, b)| {
(
b.transform.translation().z,
b.render_mode,
(a, RenderItem::Vector(b)),
)
})
.collect();
render_queue.extend(query_render_texts.iter().map(|(a, b)| {
(
b.transform.translation().z,
b.render_mode,
(a, RenderItem::Text(b)),
)
}));

// Sort by render mode with screen space on top, then by z-index
render_queue.sort_by(
|(a_z_index, a_render_mode, _), (b_z_index, b_render_mode, _)| {
let z_index = a_z_index
.partial_cmp(b_z_index)
.unwrap_or(std::cmp::Ordering::Equal);
let render_mode = a_render_mode.cmp(b_render_mode);

render_mode.then(z_index)
},
);

// Apply transforms to the respective fragments and add them to the
// scene to be rendered
for (
PreparedAffine(affine),
ExtractedRenderVector {
render_data: vector_data,
..
},
) in vector_render_queue.iter()
{
match &vector_data.data {
Vector::Static(fragment) => {
builder.append(fragment, Some(*affine));
}
Vector::Animated(composition) => {
velato_renderer.0.render(
composition,
time.elapsed_seconds(),
*affine,
1.0,
&mut builder,
);
for (_, _, (&PreparedAffine(affine), render_item)) in render_queue.iter() {
match render_item {
RenderItem::Vector(ExtractedRenderVector {
render_data: vector_data,
..
}) => match &vector_data.data {
Vector::Static(fragment) => {
builder.append(fragment, Some(affine));
}
Vector::Animated(composition) => {
velato_renderer.0.render(
composition,
time.elapsed_seconds(),
affine,
1.0,
&mut builder,
);
}
},
RenderItem::Text(ExtractedRenderText { font, text, .. }) => {
if let Some(font) = font_render_assets.get_mut(font) {
font.render_centered(&mut builder, text.size, affine, &text.content);
}
}
}
}

for (&PreparedAffine(affine), ExtractedRenderText { font, text, .. }) in
query_render_texts.iter()
{
if let Some(font) = font_render_assets.get_mut(font) {
font.render_centered(&mut builder, text.size, affine, &text.content);
}
}

if !vector_render_queue.is_empty() {
if !render_queue.is_empty() {
renderer
.0
.render_to_texture(
Expand Down
4 changes: 2 additions & 2 deletions src/rendertarget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use bevy::{
window::{WindowResized, WindowResolution},
};

use crate::{renderer::SSRenderTarget, Layer, SSRT_SHADER_HANDLE};
use crate::{renderer::SSRenderTarget, RenderMode, SSRT_SHADER_HANDLE};

#[derive(Component)]
struct MainCamera;
Expand Down Expand Up @@ -199,7 +199,7 @@ impl Material2d for VelloCanvasMaterial {
/// Hide the RenderTarget canvas if there is nothing to render
pub fn clear_when_empty(
mut query_render_target: Query<&mut Visibility, With<SSRenderTarget>>,
render_items: Query<(&mut Layer, &ViewVisibility)>,
render_items: Query<(&mut RenderMode, &ViewVisibility)>,
) {
if let Ok(mut visibility) = query_render_target.get_single_mut() {
if render_items.is_empty() {
Expand Down

0 comments on commit 4783108

Please sign in to comment.