Skip to content

Commit ec6057e

Browse files
committed
Sprite3d (bevyengine#2786)
# Objective Make Sprites usable in 3d space. ## Solution Rename Sprite to Sprite2d and add Sprite3d. Add Example 3d_sprite_pipelined. NOTE: Currently, differences between Sprite2d and Sprite3d and its rendering pipelines are minor. Implementations might diverge to greater extend with future iterations.
1 parent 7117136 commit ec6057e

File tree

9 files changed

+758
-68
lines changed

9 files changed

+758
-68
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ path = "examples/3d/3d_scene.rs"
159159
name = "3d_scene_pipelined"
160160
path = "examples/3d/3d_scene_pipelined.rs"
161161

162+
[[example]]
163+
name = "3d_sprite_pipelined"
164+
path = "examples/3d/3d_sprite_pipelined.rs"
165+
162166
[[example]]
163167
name = "cornell_box_pipelined"
164168
path = "examples/3d/cornell_box_pipelined.rs"

crates/bevy_internal/src/default_plugins.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,10 @@ impl PluginGroup for PipelinedDefaultPlugins {
129129
group.add(bevy_core_pipeline::CorePipelinePlugin::default());
130130

131131
#[cfg(feature = "bevy_sprite2")]
132-
group.add(bevy_sprite2::SpritePlugin::default());
132+
group.add(bevy_sprite2::Sprite2dPlugin::default());
133+
134+
#[cfg(feature = "bevy_sprite2")]
135+
group.add(bevy_sprite2::Sprite3dPlugin::default());
133136

134137
#[cfg(feature = "bevy_pbr2")]
135138
group.add(bevy_pbr2::PbrPlugin::default());

examples/2d/pipelined_texture_atlas.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ use bevy::{
77
},
88
render2::{camera::OrthographicCameraBundle, texture::Image},
99
sprite2::{
10-
PipelinedSpriteBundle, PipelinedSpriteSheetBundle, TextureAtlas, TextureAtlasBuilder,
11-
TextureAtlasEntry,
10+
Sprite2dBundle, Sprite2dSheetBundle, TextureAtlas, TextureAtlasBuilder, TextureAtlasEntry,
1211
},
1312
PipelinedDefaultPlugins,
1413
};
@@ -75,7 +74,7 @@ fn setup(
7574
// set up a scene to display our texture atlas
7675
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
7776
// draw a sprite from the atlas
78-
commands.spawn_bundle(PipelinedSpriteSheetBundle {
77+
commands.spawn_bundle(Sprite2dSheetBundle {
7978
transform: Transform {
8079
translation: Vec3::new(150.0, 0.0, 0.0),
8180
scale: Vec3::splat(4.0),
@@ -86,7 +85,7 @@ fn setup(
8685
..Default::default()
8786
});
8887
// draw the atlas itself
89-
commands.spawn_bundle(PipelinedSpriteBundle {
88+
commands.spawn_bundle(Sprite2dBundle {
9089
texture: texture_atlas_texture,
9190
transform: Transform::from_xyz(-300.0, 0.0, 0.0),
9291
..Default::default()

examples/3d/3d_sprite_pipelined.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use bevy::{
2+
asset::LoadState,
3+
ecs::query::With,
4+
math::Quat,
5+
prelude::{
6+
App, AssetServer, Commands, HandleUntyped, Query, Res, ResMut, State, SystemSet, Time,
7+
Transform,
8+
},
9+
render2::{camera::PerspectiveCameraBundle, texture::Image},
10+
PipelinedDefaultPlugins,
11+
};
12+
13+
use bevy::prelude::*;
14+
use bevy::sprite2::Sprite3dBundle;
15+
16+
fn main() {
17+
App::new()
18+
.init_resource::<TexHandle>()
19+
.add_plugins(PipelinedDefaultPlugins)
20+
.add_state(AppState::Setup)
21+
.add_system_set(SystemSet::on_enter(AppState::Setup).with_system(load_textures))
22+
.add_system_set(SystemSet::on_update(AppState::Setup).with_system(check_textures))
23+
.add_system_set(SystemSet::on_enter(AppState::Finished).with_system(setup))
24+
.add_system_set(SystemSet::on_update(AppState::Finished).with_system(rotation_system))
25+
.add_system_set(SystemSet::on_update(AppState::Finished).with_system(zoom_system))
26+
.run();
27+
}
28+
29+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
30+
enum AppState {
31+
Setup,
32+
Finished,
33+
}
34+
35+
#[derive(Default)]
36+
struct TexHandle {
37+
handle: Option<HandleUntyped>,
38+
}
39+
40+
fn load_textures(mut tex_handle: ResMut<TexHandle>, asset_server: Res<AssetServer>) {
41+
tex_handle.handle = Some(
42+
asset_server
43+
.load::<Image, _>("branding/icon.png")
44+
.clone_untyped(),
45+
);
46+
}
47+
48+
fn check_textures(
49+
mut state: ResMut<State<AppState>>,
50+
tex_handle: ResMut<TexHandle>,
51+
asset_server: Res<AssetServer>,
52+
) {
53+
if let LoadState::Loaded = asset_server.get_load_state(tex_handle.handle.as_ref().unwrap().id) {
54+
state.set(AppState::Finished).unwrap();
55+
}
56+
}
57+
58+
fn setup(mut commands: Commands, tex_handle: Res<TexHandle>) {
59+
let entity = commands
60+
.spawn_bundle(Sprite3dBundle {
61+
texture: tex_handle.handle.as_ref().unwrap().clone().typed(),
62+
transform: Transform::from_xyz(0.0, 0.0, 0.0),
63+
..Default::default()
64+
})
65+
.id();
66+
67+
let mut camera = PerspectiveCameraBundle::new_3d();
68+
camera.transform = Transform {
69+
translation: Vec3::new(0.0, 0.0, 1000.0),
70+
..Default::default()
71+
};
72+
73+
commands.spawn_bundle(camera).insert(Rotate).insert(Zoom {
74+
target: entity,
75+
zooming: Zooming::In,
76+
});
77+
}
78+
79+
struct Rotate;
80+
81+
fn rotation_system(time: Res<Time>, mut query: Query<&mut Transform, With<Rotate>>) {
82+
for mut transform in query.iter_mut() {
83+
*transform = Transform::from_rotation(Quat::from_rotation_z(
84+
(4.0 * std::f32::consts::PI / 30.0) * time.delta_seconds(),
85+
)) * Transform::from_rotation(Quat::from_rotation_y(
86+
(4.0 * std::f32::consts::PI / 20.0) * time.delta_seconds(),
87+
)) * Transform::from_rotation(Quat::from_rotation_x(
88+
(4.0 * std::f32::consts::PI / 16.0) * time.delta_seconds(),
89+
)) * *transform;
90+
}
91+
}
92+
93+
struct Zoom {
94+
target: Entity,
95+
zooming: Zooming,
96+
}
97+
98+
enum Zooming {
99+
In,
100+
Out,
101+
}
102+
103+
fn zoom_system(
104+
time: Res<Time>,
105+
mut queries: QuerySet<(
106+
Query<(&mut Transform, &mut Zoom)>,
107+
Query<&Transform, Without<Zoom>>,
108+
)>,
109+
) {
110+
for (mut camera_transform, mut zoom) in queries.q0_mut().iter_mut() {
111+
let target_transform = queries.q1().get(zoom.target).unwrap();
112+
113+
let diff = camera_transform.translation - target_transform.translation;
114+
let distance = diff.length();
115+
let dir = diff.normalize();
116+
117+
match zoom.zooming {
118+
Zooming::In => {
119+
if distance < 100.0 {
120+
zoom.zooming = Zooming::Out;
121+
}
122+
camera_transform.translation = dir * (distance - 1000.0 * time.delta_seconds());
123+
}
124+
Zooming::Out => {
125+
if distance > 5000.0 {
126+
zoom.zooming = Zooming::In;
127+
}
128+
camera_transform.translation = dir * (distance + 1000.0 * time.delta_seconds());
129+
}
130+
}
131+
}
132+
}

examples/tools/bevymark_pipelined.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use bevy::{
66
math::Vec3,
77
prelude::{App, AssetServer, Handle, MouseButton, Transform},
88
render2::{camera::OrthographicCameraBundle, color::Color, texture::Image},
9-
sprite2::PipelinedSpriteBundle,
9+
sprite2::Sprite2dBundle,
1010
window::WindowDescriptor,
1111
PipelinedDefaultPlugins,
1212
};
@@ -174,7 +174,7 @@ fn spawn_birds(
174174
for count in 0..spawn_count {
175175
let bird_z = (counter.count + count) as f32 * 0.00001;
176176
commands
177-
.spawn_bundle(PipelinedSpriteBundle {
177+
.spawn_bundle(Sprite2dBundle {
178178
// material: bird_material.0.clone(),
179179
texture: texture.clone(),
180180
transform: Transform {

pipelined/bevy_sprite2/src/bundle.rs

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
use crate::{
22
texture_atlas::{TextureAtlas, TextureAtlasEntry},
3-
Sprite,
3+
Sprite2d, Sprite3d,
44
};
55
use bevy_asset::Handle;
66
use bevy_ecs::bundle::Bundle;
77
use bevy_render2::texture::Image;
88
use bevy_transform::components::{GlobalTransform, Transform};
99

1010
#[derive(Bundle, Clone)]
11-
pub struct PipelinedSpriteBundle {
12-
pub sprite: Sprite,
11+
pub struct Sprite2dBundle {
12+
pub sprite: Sprite2d,
1313
pub transform: Transform,
1414
pub global_transform: GlobalTransform,
1515
pub texture: Handle<Image>,
1616
}
1717

18-
impl Default for PipelinedSpriteBundle {
18+
impl Default for Sprite2dBundle {
1919
fn default() -> Self {
2020
Self {
2121
sprite: Default::default(),
@@ -29,8 +29,8 @@ impl Default for PipelinedSpriteBundle {
2929
/// A Bundle of components for drawing a single sprite from a sprite sheet (also referred
3030
/// to as a `TextureAtlas`)
3131
#[derive(Bundle, Clone)]
32-
pub struct PipelinedSpriteSheetBundle {
33-
pub sprite: Sprite,
32+
pub struct Sprite2dSheetBundle {
33+
pub sprite: Sprite2d,
3434
/// The specific sprite from the texture atlas to be drawn
3535
pub texture_atlas_entry: TextureAtlasEntry,
3636
/// A handle to the texture atlas that holds the sprite images
@@ -40,7 +40,52 @@ pub struct PipelinedSpriteSheetBundle {
4040
pub global_transform: GlobalTransform,
4141
}
4242

43-
impl Default for PipelinedSpriteSheetBundle {
43+
impl Default for Sprite2dSheetBundle {
44+
fn default() -> Self {
45+
Self {
46+
sprite: Default::default(),
47+
texture_atlas_entry: Default::default(),
48+
texture_atlas: Default::default(),
49+
transform: Default::default(),
50+
global_transform: Default::default(),
51+
}
52+
}
53+
}
54+
55+
#[derive(Bundle, Clone)]
56+
pub struct Sprite3dBundle {
57+
pub sprite: Sprite3d,
58+
pub transform: Transform,
59+
pub global_transform: GlobalTransform,
60+
pub texture: Handle<Image>,
61+
}
62+
63+
impl Default for Sprite3dBundle {
64+
fn default() -> Self {
65+
Self {
66+
sprite: Default::default(),
67+
transform: Default::default(),
68+
global_transform: Default::default(),
69+
texture: Default::default(),
70+
}
71+
}
72+
}
73+
74+
/// A Bundle of components for drawing a single sprite from a sprite sheet (also referred
75+
/// to as a `TextureAtlas`)
76+
#[derive(Bundle, Clone)]
77+
pub struct Sprite3dSheetBundle {
78+
pub sprite: Sprite3d,
79+
/// The specific sprite from the texture atlas to be drawn
80+
pub texture_atlas_entry: TextureAtlasEntry,
81+
/// A handle to the texture atlas that holds the sprite images
82+
pub texture_atlas: Handle<TextureAtlas>,
83+
/// Data pertaining to how the sprite is drawn on the screen
84+
pub transform: Transform,
85+
pub global_transform: GlobalTransform,
86+
}
87+
88+
impl Default for Sprite3dSheetBundle {
4489
fn default() -> Self {
4590
Self {
4691
sprite: Default::default(),

pipelined/bevy_sprite2/src/lib.rs

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,61 @@ use bevy_render2::{
2121
};
2222

2323
#[derive(Default)]
24-
pub struct SpritePlugin;
24+
pub struct Sprite2dPlugin;
2525

26-
impl Plugin for SpritePlugin {
26+
impl Plugin for Sprite2dPlugin {
2727
fn build(&self, app: &mut App) {
28-
app.add_asset::<TextureAtlas>().register_type::<Sprite>();
28+
app.add_asset::<TextureAtlas>().register_type::<Sprite2d>();
2929
let render_app = app.sub_app(RenderApp);
3030
render_app
31-
.init_resource::<ExtractedSprites>()
32-
.add_system_to_stage(RenderStage::Extract, render::extract_atlases)
33-
.add_system_to_stage(RenderStage::Extract, render::extract_sprites)
34-
.add_system_to_stage(RenderStage::Prepare, render::prepare_sprites)
35-
.add_system_to_stage(RenderStage::Queue, queue_sprites)
36-
.init_resource::<SpriteShaders>()
37-
.init_resource::<SpriteMeta>();
38-
let draw_sprite = DrawSprite::new(&mut render_app.world);
31+
.init_resource::<ExtractedSprites2d>()
32+
.add_system_to_stage(RenderStage::Extract, render::extract_atlases_2d)
33+
.add_system_to_stage(RenderStage::Extract, render::extract_sprites_2d)
34+
.add_system_to_stage(RenderStage::Prepare, render::prepare_sprites_2d)
35+
.add_system_to_stage(RenderStage::Queue, render::queue_sprites_2d)
36+
.init_resource::<Sprite2dShaders>()
37+
.init_resource::<Sprite2dMeta>();
38+
let draw_sprite_2d = DrawSprite2d::new(&mut render_app.world);
3939
render_app
4040
.world
4141
.get_resource::<DrawFunctions>()
4242
.unwrap()
4343
.write()
44-
.add(draw_sprite);
44+
.add(draw_sprite_2d);
4545
let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
46-
graph.add_node("sprite", SpriteNode);
46+
graph.add_node("sprite2d", Sprite2dNode);
4747
graph
48-
.add_node_edge("sprite", bevy_core_pipeline::node::MAIN_PASS_DEPENDENCIES)
48+
.add_node_edge("sprite2d", bevy_core_pipeline::node::MAIN_PASS_DEPENDENCIES)
49+
.unwrap();
50+
}
51+
}
52+
53+
#[derive(Default)]
54+
pub struct Sprite3dPlugin;
55+
56+
impl Plugin for Sprite3dPlugin {
57+
fn build(&self, app: &mut App) {
58+
app.add_asset::<TextureAtlas>().register_type::<Sprite3d>();
59+
let render_app = app.sub_app(RenderApp);
60+
render_app
61+
.init_resource::<ExtractedSprites3d>()
62+
.add_system_to_stage(RenderStage::Extract, render::extract_atlases_3d)
63+
.add_system_to_stage(RenderStage::Extract, render::extract_sprites_3d)
64+
.add_system_to_stage(RenderStage::Prepare, render::prepare_sprites_3d)
65+
.add_system_to_stage(RenderStage::Queue, render::queue_sprites_3d)
66+
.init_resource::<Sprite3dShaders>()
67+
.init_resource::<Sprite3dMeta>();
68+
let draw_sprite_3d = DrawSprite3d::new(&mut render_app.world);
69+
render_app
70+
.world
71+
.get_resource::<DrawFunctions>()
72+
.unwrap()
73+
.write()
74+
.add(draw_sprite_3d);
75+
let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
76+
graph.add_node("sprite3d", Sprite3dNode);
77+
graph
78+
.add_node_edge("sprite3d", bevy_core_pipeline::node::MAIN_PASS_DEPENDENCIES)
4979
.unwrap();
5080
}
5181
}

0 commit comments

Comments
 (0)