Skip to content

Commit

Permalink
feat(bevy_bones_renderer, bones_render): add 2D line path rendering. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Jan 24, 2023
1 parent fd5c4f2 commit 6abe6ee
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 1 deletion.
75 changes: 75 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/bones_bevy_renderer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ serde_json = "1.0.91"
bones_bevy_asset = { version = "^0.1.0", path = "../bones_bevy_asset" }
# TODO: Update when PR merged: https://github.com/forbjok/bevy_simple_tilemap/pull/9
bevy_simple_tilemap = "0.10.0"
bevy_prototype_lyon = "0.7.2"

[dependencies.bevy]
version = "0.9.1"
Expand Down
93 changes: 93 additions & 0 deletions crates/bones_bevy_renderer/examples/debug_rendering.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use bevy::prelude::*;
use bones_bevy_renderer::*;
use bones_lib::prelude as bones;

#[derive(Deref, DerefMut, Resource)]
struct Session(pub bones_lib::prelude::World);

impl HasBonesWorld for Session {
fn world(&mut self) -> &mut bones::World {
&mut self.0
}
}

pub fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(BonesRendererPlugin::<Session>::new())
.add_startup_system(setup)
.add_system(spin)
.run();
}

/// Setup the game, loading the metadata and starting the game session.
fn setup(mut commands: Commands) {
let mut world = bones::World::new();

commands.spawn(Camera2dBundle::default());

world
.run_system(
|mut entities: bones::ResMut<bones::Entities>,
mut path2ds: bones::CompMut<bones::Path2d>,
mut transforms: bones::CompMut<bones::Transform>| {
const SIZE: f32 = 100.0;

// Draw a red square
let ent = entities.create();
path2ds.insert(
ent,
bones::Path2d {
color: [1.0, 0.0, 0.0, 1.0],
points: vec![
glam::vec2(-SIZE, -SIZE),
glam::vec2(SIZE, -SIZE),
glam::vec2(SIZE, SIZE),
glam::vec2(-SIZE, SIZE),
glam::vec2(-SIZE, -SIZE),
],
thickness: 2.0,
..default()
},
);
transforms.insert(ent, default());

const SIZE2: f32 = SIZE / 2.0;

// Draw two blue lines
let ent = entities.create();
path2ds.insert(
ent,
bones::Path2d {
color: [0.0, 0.0, 1.0, 1.0],
points: vec![
// The first line
glam::vec2(-SIZE2, -SIZE2),
glam::vec2(SIZE2, -SIZE2),
// The second line
glam::vec2(SIZE2, SIZE2),
glam::vec2(-SIZE2, SIZE2),
],
thickness: 4.0,
// This means that it won't connect points with indexes 2 and 3, so that
// they will be separate lines.
line_breaks: vec![2],
},
);
transforms.insert(ent, default());
},
)
.unwrap();

commands.insert_resource(Session(world));
}

fn spin(session: ResMut<Session>) {
session
.run_initialized_system(|mut transforms: bones::CompMut<bones::Transform>| {
transforms.iter_mut().for_each(|trans| {
trans.rotation *= Quat::from_axis_angle(Vec3::Z, f32::to_radians(0.1))
});
})
.unwrap();
}
91 changes: 91 additions & 0 deletions crates/bones_bevy_renderer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use std::marker::PhantomData;

use bevy::{prelude::*, render::camera::ScalingMode};
use bevy_prototype_lyon::prelude as lyon;
use bevy_simple_tilemap::{prelude::TileMapBundle, Tile, TileFlags, TileMap};
use bones_lib::prelude::{self as bones, BitSet, IntoBevy};

Expand Down Expand Up @@ -65,6 +66,7 @@ pub enum BonesStage {
impl<W: HasBonesWorld> Plugin for BonesRendererPlugin<W> {
fn build(&self, app: &mut App) {
app.add_plugin(bevy_simple_tilemap::plugin::SimpleTileMapPlugin)
.add_plugin(lyon::ShapePlugin)
// Install the asset loader for .atlas.yaml files.
.add_asset_loader(asset::TextureAtlasLoader)
// Add the world sync systems
Expand All @@ -74,6 +76,7 @@ impl<W: HasBonesWorld> Plugin for BonesRendererPlugin<W> {
SystemStage::parallel(),
)
.add_system_to_stage(BonesStage::Sync, sync_sprites::<W>)
.add_system_to_stage(BonesStage::Sync, sync_path2ds::<W>)
.add_system_to_stage(BonesStage::Sync, sync_atlas_sprites::<W>)
.add_system_to_stage(BonesStage::Sync, sync_cameras::<W>)
.add_system_to_stage(BonesStage::Sync, sync_clear_color::<W>)
Expand Down Expand Up @@ -426,3 +429,91 @@ fn sync_tilemaps<W: HasBonesWorld>(
));
}
}

/// The system that renders the bones world.
fn sync_path2ds<W: HasBonesWorld>(
mut commands: Commands,
world_resource: Option<ResMut<W>>,
mut bevy_bones_path2ds: Query<
(Entity, &mut lyon::Path, &mut lyon::DrawMode, &mut Transform),
With<BevyBonesEntity>,
>,
) {
let Some(mut world_resource) = world_resource else {
bevy_bones_path2ds.for_each(|(e, ..)| commands.entity(e).despawn());
return;
};

let world = world_resource.world();

world.components.init::<bones::Path2d>();
world.components.init::<bones::Transform>();

let entities = world.resources.get::<bones::Entities>();
let entities = entities.borrow();
let path2ds = world.components.get::<bones::Path2d>();
let path2ds = path2ds.borrow();
let transforms = world.components.get::<bones::Transform>();
let transforms = transforms.borrow();

fn get_bevy_components(
bones_path2d: &bones::Path2d,
bones_transform: &bones::Transform,
) -> (lyon::DrawMode, lyon::Path, Transform) {
let draw_mode = lyon::DrawMode::Stroke(lyon::StrokeMode::new(
Color::RgbaLinear {
red: bones_path2d.color[0],
green: bones_path2d.color[1],
blue: bones_path2d.color[2],
alpha: bones_path2d.color[3],
},
bones_path2d.thickness,
));
let new_path = bones_path2d
.points
.iter()
.copied()
.enumerate()
.fold(lyon::PathBuilder::new(), |mut builder, (i, point)| {
if i > 0 && !bones_path2d.line_breaks.contains(&i) {
builder.line_to(point);
}
builder.move_to(point);

builder
})
.build();

let mut transform = bones_transform.into_bevy();
// Offset the path towards the camera slightly to make sure it renders on top of a
// sprite/etc. if it is applied to an entity with both a sprite and a path.
transform.translation.z += 0.0001;
(draw_mode, new_path, transform)
}

// Sync paths
let mut path2ds_bitset = path2ds.bitset().clone();
path2ds_bitset.bit_and(transforms.bitset());
let mut bones_sprite_entity_iter = entities.iter_with_bitset(&path2ds_bitset);
for (bevy_ent, mut path, mut draw_mode, mut transform) in &mut bevy_bones_path2ds {
if let Some(bones_ent) = bones_sprite_entity_iter.next() {
let bones_path2d = path2ds.get(bones_ent).unwrap();
let bones_transform = transforms.get(bones_ent).unwrap();

(*draw_mode, *path, *transform) = get_bevy_components(bones_path2d, bones_transform);
} else {
commands.entity(bevy_ent).despawn();
}
}
for bones_ent in bones_sprite_entity_iter {
let bones_path2d = path2ds.get(bones_ent).unwrap();
let bones_transform = transforms.get(bones_ent).unwrap();

let (draw_mode, path, transform) = get_bevy_components(bones_path2d, bones_transform);

commands.spawn((
lyon::GeometryBuilder::build_as(&path, draw_mode, transform),
BevyBonesEntity,
));
}
}
5 changes: 4 additions & 1 deletion crates/bones_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
pub mod audio;
pub mod camera;
pub mod datatypes;
pub mod line;
pub mod sprite;
pub mod tilemap;
pub mod transform;
Expand All @@ -16,7 +17,9 @@ pub mod transform;
pub mod prelude {
pub use {bones_asset::prelude::*, bones_ecs::prelude::*, glam::*, type_ulid::TypeUlid};

pub use crate::{audio::*, camera::*, datatypes::*, key, sprite::*, tilemap::*, transform::*};
pub use crate::{
audio::*, camera::*, datatypes::*, key, line::*, sprite::*, tilemap::*, transform::*,
};
}

/// Create a new const [`Key`][datatypes] parsed at compile time.
Expand Down
Loading

0 comments on commit 6abe6ee

Please sign in to comment.