Particle EGUI stuff

This commit is contained in:
Ben Wallis 2021-08-17 21:08:09 +01:00
parent 680492cea6
commit a84b166a77
10 changed files with 393 additions and 34 deletions

5
Cargo.lock generated
View File

@ -6211,6 +6211,8 @@ dependencies = [
"lazy_static",
"native-dialog",
"num 0.4.0",
"num-derive",
"num-traits",
"num_cpus",
"ordered-float 2.6.0",
"profiling",
@ -6283,6 +6285,9 @@ dependencies = [
"egui",
"egui_winit_platform",
"lazy_static",
"strum",
"strum_macros",
"vek",
"veloren-client",
"veloren-common",
"veloren-voxygen-dynlib",

View File

@ -26,6 +26,7 @@ layout(location = 4) in float inst_entropy;
layout(location = 5) in int inst_mode;
layout(location = 6) in vec3 inst_dir;
layout(location = 7) in vec3 inst_pos;
layout(location = 8) in vec3 inst_col;
layout(location = 0) out vec3 f_pos;
layout(location = 1) flat out vec3 f_norm;
@ -194,15 +195,20 @@ void main() {
);
break;
case FIRE:
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
//f_reflect = 0.0; // Fire doesn't reflect light, it emits it
attr = Attr(
linear_motion(
vec3(0.0),
vec3(rand2 * 0.1, rand3 * 0.1, 2.0 + rand4 * 1.0)
),
vec3(1.0),
vec4(2, 1.5 + rand5 * 0.5, 0, start_end(1.0, 0.0)),
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3)
// linear_motion( // Offset
// vec3(0.0),
// vec3(1.0 * 0.1, 1.0 * 0.1, 2.0 + 1.0 * 1.0)
// ),
vec3(0, 0, 1.0 * 5.0) + vec3(
sin(lifetime * 1.0 + 5.0) + sin(lifetime * 7.0 + 0.0) * 0.3,
sin(lifetime * 3.0 + 0.0) + sin(lifetime * 8.0 + 0.0) * 0.3,
sin(lifetime * 2.0 + 0.0) + sin(lifetime * 9.0 + 0.0) * 0.3
),
vec3(3.0), // Scale
vec4(inst_col, 1), // Colour
spin_in_axis(vec3(1,0,0),0) // Rotation
);
break;
case FIRE_BOWL:

View File

@ -104,6 +104,8 @@ image = {version = "0.23.12", default-features = false, features = ["ico", "png"
lazy_static = "1.4.0"
native-dialog = { version = "0.5.2", optional = true }
num = "0.4"
num-traits = "0.2"
num-derive = "0.3"
ordered-float = { version = "2.0.1", default-features = false }
rand = "0.8"
rayon = "1.5"

View File

@ -13,6 +13,9 @@ client = {package = "veloren-client", path = "../../client"}
common = {package = "veloren-common", path = "../../common"}
egui = "0.12"
egui_winit_platform = "0.8"
strum = "0.21"
strum_macros = "0.21"
vek = "=0.14.1"
voxygen-dynlib = {package = "veloren-voxygen-dynlib", path = "../dynlib", optional = true}
# Hot Reloading

View File

@ -14,8 +14,13 @@ use core::mem;
use egui::{
plot::{Plot, Value},
widgets::plot::Curve,
CollapsingHeader, Color32, Grid, Label, ScrollArea, Slider, Ui, Window,
CollapsingHeader, Color32, Grid, Label, Pos2, Rect, ScrollArea, Sense, Slider, Stroke, Ui,
Vec2, Window,
};
use std::string::ToString;
use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter};
use vek::Vec3;
fn two_col_row(ui: &mut Ui, label: impl Into<Label>, content: impl Into<Label>) {
ui.label(label);
@ -23,8 +28,12 @@ fn two_col_row(ui: &mut Ui, label: impl Into<Label>, content: impl Into<Label>)
ui.end_row();
}
use crate::character_states::draw_char_state_group;
use crate::{
character_states::draw_char_state_group,
EguiDebugShapeAction::{AddCylinder, RemoveShape, SetPosAndColor},
};
use common::comp::{aura::AuraKind::Buff, Body, Fluid};
use egui::color_picker::{color_edit_button_srgba, Alpha};
use egui_winit_platform::Platform;
use std::time::Duration;
#[cfg(feature = "use-dyn-lib")]
@ -68,6 +77,9 @@ pub struct EguiInnerState {
max_entity_distance: f32,
selected_entity_cylinder_height: f32,
frame_times: Vec<f32>,
particle_qty: u32,
pixels: Vec<bool>,
pixel_draw_color: Color32,
}
#[derive(Clone, Default)]
@ -77,6 +89,7 @@ pub struct EguiWindows {
egui_memory: bool,
frame_time: bool,
ecs_entities: bool,
particles: bool,
}
impl Default for EguiInnerState {
@ -86,11 +99,14 @@ impl Default for EguiInnerState {
max_entity_distance: 100000.0,
selected_entity_cylinder_height: 10.0,
frame_times: Vec::new(),
particle_qty: 1,
pixels: vec![false; 100],
pixel_draw_color: Color32::from_rgb(255, 0, 0),
}
}
}
pub enum DebugShapeAction {
pub enum EguiDebugShapeAction {
AddCylinder {
radius: f32,
height: f32,
@ -103,9 +119,20 @@ pub enum DebugShapeAction {
},
}
pub enum EguiAction {
DebugShape(EguiDebugShapeAction),
AddParticles {
lifespan: Duration,
mode: u32,
quantity: usize,
offset: Vec3<f32>,
color: Option<Vec3<f32>>,
},
}
#[derive(Default)]
pub struct EguiActions {
pub actions: Vec<DebugShapeAction>,
pub actions: Vec<EguiAction>,
}
#[cfg(feature = "use-dyn-lib")]
@ -216,6 +243,7 @@ pub fn maintain_egui_inner(
ui.vertical(|ui| {
ui.checkbox(&mut egui_windows.ecs_entities, "ECS Entities");
ui.checkbox(&mut egui_windows.frame_time, "Frame Time");
ui.checkbox(&mut egui_windows.particles, "Particles");
});
});
@ -266,6 +294,189 @@ pub fn maintain_egui_inner(
ui.add(plot);
});
Window::new("Particles")
.open(&mut egui_windows.particles)
.default_width(500.0)
.default_height(200.0)
.show(ctx, |ui| {
const PARTICLE_SCALE: f32 = 1.0 / 11.0;
ui.vertical(|ui| {
ui.add(
Slider::new(&mut egui_state.particle_qty, 1..=1000000)
.logarithmic(true)
.clamp_to_range(true)
.text("Particles"),
);
if ui.button("Heart").clicked() {
// egui_actions.actions.push(EguiAction::AddParticles {
// lifespan: Duration::from_secs(2),
// mode: ParticleMode::CampfireFire.into_uint(),
// quantity: (egui_state.particle_qty as usize),
// offset: Vec3::default(),
// color: Some(Vec3::new(2.0, 0.8, 2.0)),
// });
let offsets = vec![
(0.0, 0.0),
(-1.0, 1.0),
(-2.0, 2.0),
(-3.0, 3.0),
(-4.0, 4.0),
(-5.0, 5.0),
(-5.0, 6.0),
(-4.0, 7.0),
(-3.0, 8.0),
(-2.0, 8.0),
(-1.0, 7.0),
(0.0, 6.0),
(1.0, 7.0),
(2.0, 8.0),
(3.0, 8.0),
(4.0, 7.0),
(5.0, 6.0),
(5.0, 5.0),
(4.0, 4.0),
(3.0, 3.0),
(2.0, 2.0),
(1.0, 1.0),
];
for offset in offsets.clone() {
egui_actions.actions.push(EguiAction::AddParticles {
lifespan: Duration::from_secs(4),
mode: ParticleMode::CampfireFire.into_uint(),
quantity: (egui_state.particle_qty as usize),
offset: Vec3::new(0.0, offset.0, offset.1)
* Vec3::new(0.0, 3.0 * PARTICLE_SCALE, 3.0 * PARTICLE_SCALE),
color: Some(Vec3::new(2.0, 0.8, 2.0)),
});
}
}
ui.group(|ui| {
ui.horizontal_wrapped(|ui| {
for particle_mode in ParticleMode::iter() {
if ui.button(particle_mode.to_string()).clicked() {
egui_actions.actions.push(EguiAction::AddParticles {
lifespan: Duration::from_secs(2),
mode: particle_mode.into_uint(),
quantity: (egui_state.particle_qty as usize),
offset: Vec3::default(),
color: None,
});
}
}
});
});
ui.group(|ui| {
ui.vertical(|ui| {
let (response, painter) =
ui.allocate_painter(Vec2::new(500.0, 500.0), Sense::click());
let rows = 10;
let columns = 10;
const CELL_SIZE: f32 = 50.0;
for x in 0..=columns {
for y in 0..=rows {
let vert_start = Pos2::new(
response.rect.left() + x as f32 * CELL_SIZE,
response.rect.top(),
);
let vert_end = Pos2::new(
response.rect.left() + x as f32 * CELL_SIZE,
vert_start.y + rows as f32 * CELL_SIZE,
);
let hor_start = Pos2::new(
response.rect.left(),
response.rect.top() + y as f32 * CELL_SIZE,
);
let hor_end = Pos2::new(
hor_start.x + columns as f32 * CELL_SIZE,
response.rect.top() + y as f32 * CELL_SIZE,
);
painter.line_segment(
[vert_start, vert_end],
Stroke::new(2.0, Color32::GREEN),
);
painter.line_segment(
[hor_start, hor_end],
Stroke::new(2.0, Color32::GREEN),
);
if x < columns && y < rows && egui_state.pixels[(rows * y) + x] {
let rect_top_left = Pos2::new(
response.rect.left() + x as f32 * CELL_SIZE,
response.rect.top() + y as f32 * CELL_SIZE,
);
let rect_bottom_right = Pos2::new(
rect_top_left.x + CELL_SIZE,
rect_top_left.y + CELL_SIZE,
);
painter.rect_filled(
Rect::from([rect_top_left, rect_bottom_right]),
0.0,
Color32::GRAY,
);
}
}
}
if response.clicked() {
if let Some(click_pos) = response.interact_pointer_pos() {
// if click_pos.x < columns && click_pos
let clicked_at = click_pos - response.rect.left_top();
let x = clicked_at.x as usize / CELL_SIZE as usize;
let y = clicked_at.y as usize / CELL_SIZE as usize;
println!("Clicked: {},{}", x, y);
println!("{}", (rows * y) + x);
egui_state.pixels[(rows * y) + x] =
!egui_state.pixels[(rows * y) + x];
}
}
if ui.button("test").clicked() {
for (i, pixel) in egui_state.pixels.iter().rev().enumerate() {
if !pixel {
continue;
}
let y = i / rows;
let x = i % rows;
egui_actions.actions.push(EguiAction::AddParticles {
lifespan: Duration::from_secs(4),
mode: ParticleMode::CampfireFire.into_uint(),
quantity: (egui_state.particle_qty as usize),
offset: Vec3::new(0.0, x as f32, y as f32)
* Vec3::new(
0.0,
3.0 * PARTICLE_SCALE,
3.0 * PARTICLE_SCALE,
),
color: Some(Vec3::new(
(egui_state.pixel_draw_color.r() as f32 / 255.0) * 2.0,
(egui_state.pixel_draw_color.g() as f32 / 255.0) * 2.0,
(egui_state.pixel_draw_color.b() as f32 / 255.0) * 2.0,
)),
});
}
};
if ui.button("clear").clicked() {
egui_state.pixels.fill_with(|| false);
}
let _color_response = color_edit_button_srgba(
ui,
&mut egui_state.pixel_draw_color,
Alpha::Opaque,
);
});
});
});
});
if egui_windows.ecs_entities {
let ecs = client.state().ecs();
@ -331,10 +542,12 @@ pub fn maintain_egui_inner(
mem::take(&mut egui_state.selected_entity_info);
if pos.is_some() {
egui_actions.actions.push(DebugShapeAction::AddCylinder {
radius: 1.0,
height: egui_state.selected_entity_cylinder_height,
});
egui_actions.actions.push(EguiAction::DebugShape(
AddCylinder {
radius: 1.0,
height: egui_state.selected_entity_cylinder_height,
},
));
}
egui_state.selected_entity_info =
Some(SelectedEntityInfo::new(entity.id()));
@ -405,7 +618,7 @@ pub fn maintain_egui_inner(
if let Some(debug_shape_id) = previous.debug_shape_id {
egui_actions
.actions
.push(DebugShapeAction::RemoveShape(debug_shape_id));
.push(EguiAction::DebugShape(RemoveShape(debug_shape_id)));
}
};
@ -416,11 +629,13 @@ pub fn maintain_egui_inner(
{
egui_actions
.actions
.push(DebugShapeAction::RemoveShape(debug_shape_id));
egui_actions.actions.push(DebugShapeAction::AddCylinder {
radius: 1.0,
height: selected_entity_cylinder_height,
});
.push(EguiAction::DebugShape(RemoveShape(debug_shape_id)));
egui_actions
.actions
.push(EguiAction::DebugShape(AddCylinder {
radius: 1.0,
height: selected_entity_cylinder_height,
}));
}
}
};
@ -479,11 +694,13 @@ fn selected_entity_window(
{
if let Some(pos) = pos {
if let Some(shape_id) = selected_entity_info.debug_shape_id {
egui_actions.actions.push(DebugShapeAction::SetPosAndColor {
id: shape_id,
color: [1.0, 1.0, 0.0, 0.5],
pos: [pos.0.x, pos.0.y, pos.0.z + 2.0, 0.0],
});
egui_actions
.actions
.push(EguiAction::DebugShape(SetPosAndColor {
id: shape_id,
color: [1.0, 1.0, 0.0, 0.5],
pos: [pos.0.x, pos.0.y, pos.0.z + 2.0, 0.0],
}));
}
};
@ -695,3 +912,46 @@ fn poise_state_label(ui: &mut Ui, poise: &Poise) {
},
};
}
#[derive(Copy, Clone, Display, EnumIter)]
pub enum ParticleMode {
CampfireSmoke = 0,
CampfireFire = 1,
GunPowderSpark = 2,
Shrapnel = 3,
FireworkBlue = 4,
FireworkGreen = 5,
FireworkPurple = 6,
FireworkRed = 7,
FireworkWhite = 8,
FireworkYellow = 9,
Leaf = 10,
Firefly = 11,
Bee = 12,
GroundShockwave = 13,
EnergyHealing = 14,
EnergyNature = 15,
FlameThrower = 16,
FireShockwave = 17,
FireBowl = 18,
Snow = 19,
Explosion = 20,
Ice = 21,
LifestealBeam = 22,
CultistFlame = 23,
StaticSmoke = 24,
Blood = 25,
Enraged = 26,
BigShrapnel = 27,
Laser = 28,
Bubbles = 29,
Water = 30,
IceSpikes = 31,
Drip = 32,
Tornado = 33,
Death = 34,
}
impl ParticleMode {
pub fn into_uint(self) -> u32 { self as u32 }
}

View File

@ -37,6 +37,9 @@ pub mod window;
pub use crate::error::Error;
pub use i18n;
extern crate num;
#[macro_use] extern crate num_derive;
#[cfg(feature = "singleplayer")]
use crate::singleplayer::Singleplayer;
#[cfg(feature = "egui-ui")]

View File

@ -48,7 +48,7 @@ impl VertexTrait for Vertex {
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
}
#[derive(Copy, Clone)]
#[derive(Copy, Clone, FromPrimitive)]
pub enum ParticleMode {
CampfireSmoke = 0,
CampfireFire = 1,
@ -123,6 +123,9 @@ pub struct Instance {
// - a quad mesh, and 6 or more instances.
// - a cube mesh, and 36 or more instances.
inst_pos: [f32; 3],
// The color of the particle - not used by most particle modes
inst_col: [f32; 3],
}
impl Instance {
@ -131,6 +134,7 @@ impl Instance {
lifespan: f32,
inst_mode: ParticleMode,
inst_pos: Vec3<f32>,
inst_col: Vec3<f32>,
) -> Self {
use rand::Rng;
Self {
@ -140,6 +144,7 @@ impl Instance {
inst_mode: inst_mode as i32,
inst_pos: inst_pos.into_array(),
inst_dir: [0.0, 0.0, 0.0],
inst_col: inst_col.into_array(),
}
}
@ -158,9 +163,12 @@ impl Instance {
inst_mode: inst_mode as i32,
inst_pos: inst_pos.into_array(),
inst_dir: (inst_pos2 - inst_pos).into_array(),
inst_col: Vec3::zero().into_array(),
}
}
pub fn with_color(&mut self, color: Vec3<f32>) { self.inst_col = color.into_array(); }
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 6] = wgpu::vertex_attr_array![2 => Float32, 3 => Float32, 4 => Float32, 5 => Sint32, 6 => Float32x3, 7 => Float32x3];
wgpu::VertexBufferLayout {
@ -172,7 +180,15 @@ impl Instance {
}
impl Default for Instance {
fn default() -> Self { Self::new(0.0, 0.0, ParticleMode::CampfireSmoke, Vec3::zero()) }
fn default() -> Self {
Self::new(
0.0,
0.0,
ParticleMode::CampfireSmoke,
Vec3::zero(),
Vec3::zero(),
)
}
}
pub struct ParticlePipeline {

View File

@ -325,6 +325,9 @@ impl Scene {
/// Get a reference to the scene's particle manager.
pub fn particle_mgr(&self) -> &ParticleMgr { &self.particle_mgr }
/// Get a mutable reference to the scene's particle manager.
pub fn particle_mgr_mut(&mut self) -> &mut ParticleMgr { &mut self.particle_mgr }
/// Get a reference to the scene's figure manager.
pub fn figure_mgr(&self) -> &FigureMgr { &self.figure_mgr }

View File

@ -51,6 +51,25 @@ impl ParticleMgr {
}
}
pub fn add_debug_particles(
&mut self,
qty: usize,
lifespan: Duration,
time: f64,
mode: ParticleMode,
pos: Vec3<f32>,
color: &Option<Vec3<f32>>,
) {
self.particles.resize_with(self.particles.len() + qty, || {
let particle = Particle::new(lifespan, time, mode, pos);
if let Some(color) = color {
particle.with_color(*color);
println!("{:?}", *color);
}
particle
});
}
pub fn handle_outcome(&mut self, outcome: &Outcome, scene_data: &SceneData) {
span!(_guard, "handle_outcome", "ParticleMgr::handle_outcome");
let time = scene_data.state.get_time();
@ -1527,10 +1546,15 @@ impl Particle {
fn new(lifespan: Duration, time: f64, mode: ParticleMode, pos: Vec3<f32>) -> Self {
Particle {
alive_until: time + lifespan.as_secs_f64(),
instance: ParticleInstance::new(time, lifespan.as_secs_f32(), mode, pos),
instance: ParticleInstance::new(time, lifespan.as_secs_f32(), mode, pos, Vec3::zero()),
}
}
fn with_color(mut self, color: Vec3<f32>) -> Self {
self.instance.with_color(color);
self
}
fn new_directed(
lifespan: Duration,
time: f64,

View File

@ -1,11 +1,19 @@
use crate::{
render::pipelines::particle::ParticleMode,
scene::{DebugShape, DebugShapeId, Scene},
window::Window,
};
use client::Client;
use common::comp::Pos;
use egui::FontDefinitions;
use egui_winit_platform::{Platform, PlatformDescriptor};
use voxygen_egui::{DebugShapeAction, EguiDebugInfo, EguiInnerState, EguiWindows};
use specs::WorldExt;
use vek::Vec3;
use voxygen_egui::{
EguiAction, EguiDebugInfo,
EguiDebugShapeAction::{AddCylinder, RemoveShape, SetPosAndColor},
EguiInnerState, EguiWindows,
};
pub struct EguiState {
pub platform: Platform,
@ -48,21 +56,50 @@ impl EguiState {
);
egui_actions.actions.iter().for_each(|action| match action {
DebugShapeAction::AddCylinder { height, radius } => {
EguiAction::DebugShape(AddCylinder { height, radius }) => {
let shape_id = scene.debug.add_shape(DebugShape::Cylinder {
height: *height,
radius: *radius,
});
self.new_debug_shape_id = Some(shape_id.0);
},
DebugShapeAction::RemoveShape(debug_shape_id) => {
EguiAction::DebugShape(RemoveShape(debug_shape_id)) => {
scene.debug.remove_shape(DebugShapeId(*debug_shape_id));
},
DebugShapeAction::SetPosAndColor { id, pos, color } => {
EguiAction::DebugShape(SetPosAndColor { id, pos, color }) => {
scene
.debug
.set_pos_and_color(DebugShapeId(*id), *pos, *color);
},
EguiAction::AddParticles {
lifespan,
mode,
quantity,
offset,
color,
} => {
let time = client.state().get_time();
let player_pos = client
.state()
.ecs()
.read_storage::<Pos>()
.get(client.entity())
.map_or(Vec3::zero(), |pos| {
Vec3::new(pos.0.x, pos.0.y, pos.0.z) + offset
});
let particle_mode: ParticleMode =
num::FromPrimitive::from_u32(*mode).unwrap_or(ParticleMode::Firefly);
scene.particle_mgr_mut().add_debug_particles(
*quantity,
*lifespan,
time,
particle_mode,
player_pos,
color,
);
},
})
}
}