forked from djeedai/bevy_hanabi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathordering.rs
203 lines (183 loc) Β· 7.13 KB
/
ordering.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Ordering
//!
//! This example demonstrates occluding particle effects behind opaque and
//! partially transparent objects. The occlusion is based on the built-in Bevy
//! ordering of transparent objects, which are sorted by their distance to the
//! camera, and therefore only works for non-overlapping objects. For Hanabi
//! effects, the origin of the emitter (the Entity with the ParticleEffect
//! component) is used as the origin of the object, and therefore the point from
//! which the distance to the camera is calculated. In this example, we
//! therefore ensure that the rectangles in front and behind the particle effect
//! do not overlap the bounding box of the effect itself.
use bevy::{
core_pipeline::{bloom::BloomSettings, tonemapping::Tonemapping},
log::LogPlugin,
prelude::*,
};
use bevy_hanabi::prelude::*;
#[cfg(feature = "examples_world_inspector")]
use bevy_inspector_egui::quick::WorldInspectorPlugin;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut app = App::default();
app.add_plugins(
DefaultPlugins
.set(LogPlugin {
level: bevy::log::Level::WARN,
filter: "bevy_hanabi=warn,firework=trace".to_string(),
update_subscriber: None,
})
.set(WindowPlugin {
primary_window: Some(Window {
title: "π Hanabi β ordering".to_string(),
..default()
}),
..default()
}),
)
.add_systems(Update, bevy::window::close_on_esc)
.add_plugins(HanabiPlugin);
#[cfg(feature = "examples_world_inspector")]
app.add_plugins(WorldInspectorPlugin::default());
app.add_systems(Startup, setup).run();
Ok(())
}
/// Create the firework particle effect which will be rendered in-between other
/// PBR objects.
fn make_firework() -> EffectAsset {
// Set the particles bright white (HDR; value=4.) so we can see the effect of
// any colored object covering them.
let mut color_gradient1 = Gradient::new();
color_gradient1.add_key(0.0, Vec4::new(4.0, 4.0, 4.0, 1.0));
color_gradient1.add_key(1.0, Vec4::new(4.0, 4.0, 4.0, 0.0));
// Keep the size large so we can more visibly see the particles for longer, and
// see the effect of alpha blending.
let mut size_gradient1 = Gradient::new();
size_gradient1.add_key(0.0, Vec2::ONE);
size_gradient1.add_key(0.1, Vec2::ONE);
size_gradient1.add_key(1.0, Vec2::ZERO);
let writer = ExprWriter::new();
// Give a bit of variation by randomizing the age per particle. This will
// control the starting color and starting size of particles.
let age = writer.lit(0.).uniform(writer.lit(0.2)).expr();
let init_age = SetAttributeModifier::new(Attribute::AGE, age);
// Give a bit of variation by randomizing the lifetime per particle
let lifetime = writer.lit(2.).uniform(writer.lit(3.)).expr();
let init_lifetime = SetAttributeModifier::new(Attribute::LIFETIME, lifetime);
// Add constant downward acceleration to simulate gravity
let accel = writer.lit(Vec3::Y * -8.).expr();
let update_accel = AccelModifier::new(accel);
// Add drag to make particles slow down a bit after the initial explosion
let drag = writer.lit(5.).expr();
let update_drag = LinearDragModifier::new(drag);
let init_pos = SetPositionSphereModifier {
center: writer.lit(Vec3::ZERO).expr(),
radius: writer.lit(2.).expr(),
dimension: ShapeDimension::Volume,
};
// Give a bit of variation by randomizing the initial speed
let init_vel = SetVelocitySphereModifier {
center: writer.lit(Vec3::ZERO).expr(),
speed: (writer.rand(ScalarType::Float) * writer.lit(20.) + writer.lit(60.)).expr(),
};
EffectAsset::new(vec![2048], Spawner::rate(128.0.into()), writer.finish())
.with_name("firework")
.init(init_pos)
.init(init_vel)
.init(init_age)
.init(init_lifetime)
.update(update_drag)
.update(update_accel)
// Note: we (ab)use the ColorOverLifetimeModifier to set a fixed color hard-coded in the
// render shader, without having to store a per-particle color. This is an optimization.
.render(ColorOverLifetimeModifier {
gradient: color_gradient1,
})
.render(SizeOverLifetimeModifier {
gradient: size_gradient1,
screen_space_size: false,
})
}
fn setup(
mut commands: Commands,
mut effects: ResMut<Assets<EffectAsset>>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn((
Camera3dBundle {
transform: Transform::from_translation(Vec3::new(0., 0., 50.)),
camera: Camera {
hdr: true,
clear_color: Color::BLACK.into(),
..default()
},
tonemapping: Tonemapping::None,
..default()
},
BloomSettings::default(),
));
let effect1 = effects.add(make_firework());
commands.spawn((
Name::new("firework"),
ParticleEffectBundle {
effect: ParticleEffect::new(effect1),
transform: Transform {
translation: Vec3::Z,
..default()
},
..default()
},
));
// Red background at origin, with alpha blending
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(Rectangle {
half_size: Vec2 { x: 0.5, y: 0.5 },
})),
material: materials.add(StandardMaterial {
base_color: Color::rgba(1., 0., 0., 0.5),
alpha_mode: bevy::pbr::AlphaMode::Blend,
..default()
}),
transform: Transform {
scale: Vec3::splat(50.),
..default()
},
..default()
});
// Blue rectangle in front of particles, with alpha blending
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(Rectangle {
half_size: Vec2 { x: 0.5, y: 0.5 },
})),
material: materials.add(StandardMaterial {
// Keep the alpha quite high, because the particles are very bright (HDR, value=4.)
// so otherwise we can't see the attenuation of the blue box over the white particles.
base_color: Color::rgba(0., 0., 1., 0.95),
alpha_mode: bevy::pbr::AlphaMode::Blend,
..default()
}),
transform: Transform {
translation: Vec3::Y * 6. + Vec3::Z * 40.,
scale: Vec3::splat(10.),
..default()
},
..default()
});
// Green square in front of particles, without alpha blending
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(Rectangle {
half_size: Vec2 { x: 0.5, y: 0.5 },
})),
material: materials.add(StandardMaterial {
base_color: Color::GREEN,
alpha_mode: bevy::pbr::AlphaMode::Opaque,
..default()
}),
transform: Transform {
translation: Vec3::Y * -6. + Vec3::Z * 40.,
scale: Vec3::splat(10.),
..default()
},
..default()
});
}