* Added new Admin Commands window to egui, including Give Items and Kits sections

* Added widgets.rs to egui for reusable widgets
* Added filterable_list egui widget
* Reworked DebugShapeAction to be a more generic EguiAction which now allows for ChatCommands (used by admin tools) as well as DebugShape drawing requests.
* Fixed egui event handling so that typing/clicking within egui windows now correctly doesn't pass these events onto the game itself
* Removed /give_item limit for stackable items
This commit is contained in:
Ben Wallis 2021-08-21 10:23:26 +01:00
parent 2f1fe39e04
commit d665ce329d
10 changed files with 267 additions and 76 deletions

View File

@ -255,9 +255,11 @@ lazy_static! {
}) })
}; };
static ref KITS: Vec<String> = { pub static ref KITS: Vec<String> = {
if let Ok(kits) = KitManifest::load(KIT_MANIFEST_PATH) { if let Ok(kits) = KitManifest::load(KIT_MANIFEST_PATH) {
kits.read().0.keys().cloned().collect() let mut kits = kits.read().0.keys().cloned().collect::<Vec<String>>();
kits.sort();
kits
} else { } else {
Vec::new() Vec::new()
} }

View File

@ -472,7 +472,16 @@ fn handle_give_item(
if let Ok(item) = Item::new_from_asset(&item_name.replace('/', ".").replace("\\", ".")) { if let Ok(item) = Item::new_from_asset(&item_name.replace('/', ".").replace("\\", ".")) {
let mut item: Item = item; let mut item: Item = item;
let mut res = Ok(()); let mut res = Ok(());
if let Ok(()) = item.set_amount(give_amount.min(2000)) {
const MAX_GIVE_AMOUNT: u32 = 2000;
// Cap give_amount for non-stackable items
let give_amount = if item.is_stackable() {
give_amount
} else {
give_amount.min(MAX_GIVE_AMOUNT)
};
if let Ok(()) = item.set_amount(give_amount) {
server server
.state .state
.ecs() .ecs()

View File

@ -5,7 +5,7 @@ edition = "2018"
version = "0.9.0" version = "0.9.0"
[features] [features]
use-dyn-lib = ["lazy_static", "voxygen-dynlib"] use-dyn-lib = ["voxygen-dynlib"]
be-dyn-lib = [] be-dyn-lib = []
[dependencies] [dependencies]
@ -13,8 +13,7 @@ client = {package = "veloren-client", path = "../../client"}
common = {package = "veloren-common", path = "../../common"} common = {package = "veloren-common", path = "../../common"}
egui = "0.12" egui = "0.12"
egui_winit_platform = "0.8" egui_winit_platform = "0.8"
lazy_static = "1.4.0"
voxygen-dynlib = {package = "veloren-voxygen-dynlib", path = "../dynlib", optional = true} voxygen-dynlib = {package = "veloren-voxygen-dynlib", path = "../dynlib", optional = true}
# Hot Reloading
lazy_static = {version = "1.4.0", optional = true}

94
voxygen/egui/src/admin.rs Normal file
View File

@ -0,0 +1,94 @@
use crate::{AdminCommandState, EguiAction, EguiActions, EguiWindows};
use common::cmd::ChatCommand;
use egui::{CollapsingHeader, CtxRef, Resize, Slider, Ui, Vec2, Window};
use lazy_static::lazy_static;
lazy_static! {
static ref ITEM_SPECS: Vec<String> = {
let mut item_specs = common::cmd::ITEM_SPECS
.iter()
.map(|item_desc| item_desc.replace("common.items.", ""))
.collect::<Vec<String>>();
item_specs.sort();
item_specs
};
}
pub fn draw_admin_commands_window(
ctx: &CtxRef,
state: &mut AdminCommandState,
windows: &mut EguiWindows,
egui_actions: &mut EguiActions,
) {
Window::new("Admin Commands")
.open(&mut windows.admin_commands)
.default_width(400.0)
.default_height(600.0)
.show(ctx, |ui| {
ui.spacing_mut().item_spacing = Vec2::new(10.0, 10.0);
ui.vertical(|ui| {
CollapsingHeader::new("Give Items")
.default_open(true)
.show(ui, |ui| {
draw_give_items(ui, state, egui_actions);
});
CollapsingHeader::new("Kits")
.default_open(false)
.show(ui, |ui| {
draw_kits(ui, state, egui_actions);
});
});
});
}
fn draw_kits(ui: &mut Ui, state: &mut AdminCommandState, egui_actions: &mut EguiActions) {
ui.vertical(|ui| {
if ui.button("Give Kit").clicked() {
egui_actions.actions.push(EguiAction::ChatCommand {
cmd: ChatCommand::Kit,
args: vec![common::cmd::KITS[state.kits_selected_idx].clone()],
});
};
crate::widgets::filterable_list(ui, &common::cmd::KITS, "", &mut state.kits_selected_idx)
});
}
fn draw_give_items(ui: &mut Ui, state: &mut AdminCommandState, egui_actions: &mut EguiActions) {
ui.spacing_mut().window_padding = Vec2::new(10.0, 10.0);
Resize::default()
.default_size([400.0, 200.0])
.show(ui, |ui| {
ui.horizontal(|ui| {
ui.add(
Slider::new(&mut state.give_item_qty, 1..=100000)
.logarithmic(true)
.clamp_to_range(true)
.text("Qty"),
);
if ui.button("Give Items").clicked() {
egui_actions.actions.push(EguiAction::ChatCommand {
cmd: ChatCommand::GiveItem,
args: vec![
format!(
"common.items.{}",
ITEM_SPECS[state.give_item_selected_idx].clone()
),
format!("{}", state.give_item_qty),
],
});
};
});
ui.horizontal(|ui| {
ui.label("Filter:");
ui.text_edit_singleline(&mut state.give_item_search_text);
});
crate::widgets::filterable_list(
ui,
&ITEM_SPECS,
&state.give_item_search_text,
&mut state.give_item_selected_idx,
);
});
}

View File

@ -1,4 +1,4 @@
use crate::{two_col_row, SelectedEntityInfo}; use crate::{widgets::two_col_row, SelectedEntityInfo};
use common::{ use common::{
comp::CharacterState, comp::CharacterState,
states::{charged_melee, combo_melee, dash_melee, leap_melee}, states::{charged_melee, combo_melee, dash_melee, leap_melee},

View File

@ -3,7 +3,9 @@
#[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))] #[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))]
compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once"); compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once");
mod admin;
mod character_states; mod character_states;
mod widgets;
use client::{Client, Join, World, WorldExt}; use client::{Client, Join, World, WorldExt};
use common::{ use common::{
@ -14,17 +16,17 @@ use core::mem;
use egui::{ use egui::{
plot::{Plot, Value}, plot::{Plot, Value},
widgets::plot::Curve, widgets::plot::Curve,
CollapsingHeader, Color32, Grid, Label, Pos2, ScrollArea, Slider, Ui, Window, CollapsingHeader, Color32, Grid, Pos2, ScrollArea, Slider, Ui, Window,
}; };
fn two_col_row(ui: &mut Ui, label: impl Into<Label>, content: impl Into<Label>) { use crate::{
ui.label(label); admin::draw_admin_commands_window, character_states::draw_char_state_group,
ui.label(content); widgets::two_col_row,
ui.end_row(); };
} use common::{
cmd::ChatCommand,
use crate::character_states::draw_char_state_group; comp::{aura::AuraKind::Buff, Body, Fluid},
use common::comp::{aura::AuraKind::Buff, Body, Fluid}; };
use egui_winit_platform::Platform; use egui_winit_platform::Platform;
use std::time::Duration; use std::time::Duration;
#[cfg(feature = "use-dyn-lib")] #[cfg(feature = "use-dyn-lib")]
@ -58,6 +60,24 @@ impl SelectedEntityInfo {
} }
} }
pub struct AdminCommandState {
give_item_qty: u32,
give_item_selected_idx: usize,
give_item_search_text: String,
kits_selected_idx: usize,
}
impl AdminCommandState {
fn new() -> Self {
Self {
give_item_qty: 1,
give_item_selected_idx: 0,
give_item_search_text: String::new(),
kits_selected_idx: 0,
}
}
}
pub struct EguiDebugInfo { pub struct EguiDebugInfo {
pub frame_time: Duration, pub frame_time: Duration,
pub ping_ms: f64, pub ping_ms: f64,
@ -65,13 +85,16 @@ pub struct EguiDebugInfo {
pub struct EguiInnerState { pub struct EguiInnerState {
selected_entity_info: Option<SelectedEntityInfo>, selected_entity_info: Option<SelectedEntityInfo>,
admin_command_state: AdminCommandState,
max_entity_distance: f32, max_entity_distance: f32,
selected_entity_cylinder_height: f32, selected_entity_cylinder_height: f32,
frame_times: Vec<f32>, frame_times: Vec<f32>,
windows: EguiWindows,
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct EguiWindows { pub struct EguiWindows {
admin_commands: bool,
egui_inspection: bool, egui_inspection: bool,
egui_settings: bool, egui_settings: bool,
egui_memory: bool, egui_memory: bool,
@ -82,15 +105,17 @@ pub struct EguiWindows {
impl Default for EguiInnerState { impl Default for EguiInnerState {
fn default() -> Self { fn default() -> Self {
Self { Self {
admin_command_state: AdminCommandState::new(),
selected_entity_info: None, selected_entity_info: None,
max_entity_distance: 100000.0, max_entity_distance: 100000.0,
selected_entity_cylinder_height: 10.0, selected_entity_cylinder_height: 10.0,
frame_times: Vec::new(), frame_times: Vec::new(),
windows: EguiWindows::default(),
} }
} }
} }
pub enum DebugShapeAction { pub enum EguiDebugShapeAction {
AddCylinder { AddCylinder {
radius: f32, radius: f32,
height: f32, height: f32,
@ -103,9 +128,14 @@ pub enum DebugShapeAction {
}, },
} }
pub enum EguiAction {
ChatCommand { cmd: ChatCommand, args: Vec<String> },
DebugShape(EguiDebugShapeAction),
}
#[derive(Default)] #[derive(Default)]
pub struct EguiActions { pub struct EguiActions {
pub actions: Vec<DebugShapeAction>, pub actions: Vec<EguiAction>,
} }
#[cfg(feature = "use-dyn-lib")] #[cfg(feature = "use-dyn-lib")]
@ -114,7 +144,6 @@ pub fn init() { lazy_static::initialize(&LIB); }
pub fn maintain( pub fn maintain(
platform: &mut Platform, platform: &mut Platform,
egui_state: &mut EguiInnerState, egui_state: &mut EguiInnerState,
egui_windows: &mut EguiWindows,
client: &Client, client: &Client,
debug_info: Option<EguiDebugInfo>, debug_info: Option<EguiDebugInfo>,
added_cylinder_shape_id: Option<u64>, added_cylinder_shape_id: Option<u64>,
@ -124,7 +153,6 @@ pub fn maintain(
maintain_egui_inner( maintain_egui_inner(
platform, platform,
egui_state, egui_state,
egui_windows,
client, client,
debug_info, debug_info,
added_cylinder_shape_id, added_cylinder_shape_id,
@ -141,7 +169,6 @@ pub fn maintain(
fn( fn(
&mut Platform, &mut Platform,
&mut EguiInnerState, &mut EguiInnerState,
&mut EguiWindows,
&Client, &Client,
Option<EguiDebugInfo>, Option<EguiDebugInfo>,
Option<u64>, Option<u64>,
@ -160,7 +187,6 @@ pub fn maintain(
maintain_fn( maintain_fn(
platform, platform,
egui_state, egui_state,
egui_windows,
client, client,
debug_info, debug_info,
added_cylinder_shape_id, added_cylinder_shape_id,
@ -172,7 +198,6 @@ pub fn maintain(
pub fn maintain_egui_inner( pub fn maintain_egui_inner(
platform: &mut Platform, platform: &mut Platform,
egui_state: &mut EguiInnerState, egui_state: &mut EguiInnerState,
egui_windows: &mut EguiWindows,
client: &Client, client: &Client,
debug_info: Option<EguiDebugInfo>, debug_info: Option<EguiDebugInfo>,
added_cylinder_shape_id: Option<u64>, added_cylinder_shape_id: Option<u64>,
@ -184,6 +209,7 @@ pub fn maintain_egui_inner(
let mut previous_selected_entity: Option<SelectedEntityInfo> = None; let mut previous_selected_entity: Option<SelectedEntityInfo> = None;
let mut max_entity_distance = egui_state.max_entity_distance; let mut max_entity_distance = egui_state.max_entity_distance;
let mut selected_entity_cylinder_height = egui_state.selected_entity_cylinder_height; let mut selected_entity_cylinder_height = egui_state.selected_entity_cylinder_height;
let mut windows = egui_state.windows.clone();
// If a debug cylinder was added in the last frame, store it against the // If a debug cylinder was added in the last frame, store it against the
// selected entity // selected entity
@ -216,8 +242,9 @@ pub fn maintain_egui_inner(
}); });
ui.group(|ui| { ui.group(|ui| {
ui.vertical(|ui| { ui.vertical(|ui| {
ui.checkbox(&mut egui_windows.ecs_entities, "ECS Entities"); ui.checkbox(&mut windows.admin_commands, "Admin Commands");
ui.checkbox(&mut egui_windows.frame_time, "Frame Time"); ui.checkbox(&mut windows.ecs_entities, "ECS Entities");
ui.checkbox(&mut windows.frame_time, "Frame Time");
}); });
}); });
@ -225,36 +252,36 @@ pub fn maintain_egui_inner(
ui.vertical(|ui| { ui.vertical(|ui| {
ui.label("Show EGUI Windows"); ui.label("Show EGUI Windows");
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.checkbox(&mut egui_windows.egui_inspection, "🔍 Inspection"); ui.checkbox(&mut windows.egui_inspection, "🔍 Inspection");
ui.checkbox(&mut egui_windows.egui_settings, "🔧 Settings"); ui.checkbox(&mut windows.egui_settings, "🔧 Settings");
ui.checkbox(&mut egui_windows.egui_memory, "📝 Memory"); ui.checkbox(&mut windows.egui_memory, "📝 Memory");
}) })
}) })
}); });
}); });
Window::new("🔧 Settings") Window::new("🔧 Settings")
.open(&mut egui_windows.egui_settings) .open(&mut windows.egui_settings)
.scroll(true) .scroll(true)
.show(ctx, |ui| { .show(ctx, |ui| {
ctx.settings_ui(ui); ctx.settings_ui(ui);
}); });
Window::new("🔍 Inspection") Window::new("🔍 Inspection")
.open(&mut egui_windows.egui_inspection) .open(&mut windows.egui_inspection)
.scroll(true) .scroll(true)
.show(ctx, |ui| { .show(ctx, |ui| {
ctx.inspection_ui(ui); ctx.inspection_ui(ui);
}); });
Window::new("📝 Memory") Window::new("📝 Memory")
.open(&mut egui_windows.egui_memory) .open(&mut windows.egui_memory)
.resizable(false) .resizable(false)
.show(ctx, |ui| { .show(ctx, |ui| {
ctx.memory_ui(ui); ctx.memory_ui(ui);
}); });
Window::new("Frame Time") Window::new("Frame Time")
.open(&mut egui_windows.frame_time) .open(&mut windows.frame_time)
.default_width(200.0) .default_width(200.0)
.default_height(200.0) .default_height(200.0)
.show(ctx, |ui| { .show(ctx, |ui| {
@ -268,14 +295,14 @@ pub fn maintain_egui_inner(
ui.add(plot); ui.add(plot);
}); });
if egui_windows.ecs_entities { if windows.ecs_entities {
let ecs = client.state().ecs(); let ecs = client.state().ecs();
let positions = client.state().ecs().read_storage::<comp::Pos>(); let positions = client.state().ecs().read_storage::<comp::Pos>();
let client_pos = positions.get(client.entity()); let client_pos = positions.get(client.entity());
egui::Window::new("ECS Entities") egui::Window::new("ECS Entities")
.open(&mut egui_windows.ecs_entities) .open(&mut windows.ecs_entities)
.default_width(500.0) .default_width(500.0)
.default_height(500.0) .default_height(500.0)
.show(ctx, |ui| { .show(ctx, |ui| {
@ -333,10 +360,12 @@ pub fn maintain_egui_inner(
mem::take(&mut egui_state.selected_entity_info); mem::take(&mut egui_state.selected_entity_info);
if pos.is_some() { if pos.is_some() {
egui_actions.actions.push(DebugShapeAction::AddCylinder { egui_actions.actions.push(EguiAction::DebugShape(
radius: 1.0, EguiDebugShapeAction::AddCylinder {
height: egui_state.selected_entity_cylinder_height, radius: 1.0,
}); height: egui_state.selected_entity_cylinder_height,
},
));
} }
egui_state.selected_entity_info = egui_state.selected_entity_info =
Some(SelectedEntityInfo::new(entity.id())); Some(SelectedEntityInfo::new(entity.id()));
@ -403,11 +432,20 @@ pub fn maintain_egui_inner(
} }
} }
draw_admin_commands_window(
ctx,
&mut egui_state.admin_command_state,
&mut windows,
&mut egui_actions,
);
if let Some(previous) = previous_selected_entity { if let Some(previous) = previous_selected_entity {
if let Some(debug_shape_id) = previous.debug_shape_id { if let Some(debug_shape_id) = previous.debug_shape_id {
egui_actions egui_actions
.actions .actions
.push(DebugShapeAction::RemoveShape(debug_shape_id)); .push(EguiAction::DebugShape(EguiDebugShapeAction::RemoveShape(
debug_shape_id,
)));
} }
}; };
@ -416,19 +454,22 @@ pub fn maintain_egui_inner(
if (egui_state.selected_entity_cylinder_height - selected_entity_cylinder_height).abs() if (egui_state.selected_entity_cylinder_height - selected_entity_cylinder_height).abs()
> f32::EPSILON > f32::EPSILON
{ {
egui_actions egui_actions.actions.push(EguiAction::DebugShape(
.actions EguiDebugShapeAction::RemoveShape(debug_shape_id),
.push(DebugShapeAction::RemoveShape(debug_shape_id)); ));
egui_actions.actions.push(DebugShapeAction::AddCylinder { egui_actions.actions.push(EguiAction::DebugShape(
radius: 1.0, EguiDebugShapeAction::AddCylinder {
height: selected_entity_cylinder_height, radius: 1.0,
}); height: selected_entity_cylinder_height,
},
));
} }
} }
}; };
egui_state.max_entity_distance = max_entity_distance; egui_state.max_entity_distance = max_entity_distance;
egui_state.selected_entity_cylinder_height = selected_entity_cylinder_height; egui_state.selected_entity_cylinder_height = selected_entity_cylinder_height;
egui_state.windows = windows;
egui_actions egui_actions
} }
@ -481,11 +522,13 @@ fn selected_entity_window(
{ {
if let Some(pos) = pos { if let Some(pos) = pos {
if let Some(shape_id) = selected_entity_info.debug_shape_id { if let Some(shape_id) = selected_entity_info.debug_shape_id {
egui_actions.actions.push(DebugShapeAction::SetPosAndColor { egui_actions.actions.push(EguiAction::DebugShape(
id: shape_id, EguiDebugShapeAction::SetPosAndColor {
color: [1.0, 1.0, 0.0, 0.5], id: shape_id,
pos: [pos.0.x, pos.0.y, pos.0.z + 2.0, 0.0], color: [1.0, 1.0, 0.0, 0.5],
}); pos: [pos.0.x, pos.0.y, pos.0.z + 2.0, 0.0],
},
));
} }
}; };

View File

@ -0,0 +1,34 @@
use egui::{Label, ScrollArea, Ui, Vec2};
pub(crate) fn filterable_list(
ui: &mut Ui,
list_items: &[String],
search_text: &str,
selected_index: &mut usize,
) {
let scroll_area = ScrollArea::auto_sized();
scroll_area.show(ui, |ui| {
ui.spacing_mut().item_spacing = Vec2::new(0.0, 2.0);
let search_text = search_text.to_lowercase();
for (i, list_item) in list_items.iter().enumerate().filter_map(|(i, list_item)| {
if search_text.is_empty() || list_item.to_lowercase().contains(&search_text) {
Some((i, list_item))
} else {
None
}
}) {
if ui
.selectable_label(i == *selected_index, list_item)
.clicked()
{
*selected_index = i;
};
}
});
}
pub(crate) fn two_col_row(ui: &mut Ui, label: impl Into<Label>, content: impl Into<Label>) {
ui.label(label);
ui.label(content);
ui.end_row();
}

View File

@ -32,7 +32,13 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) {
*control_flow = winit::event_loop::ControlFlow::Poll; *control_flow = winit::event_loop::ControlFlow::Poll;
#[cfg(feature = "egui-ui")] #[cfg(feature = "egui-ui")]
global_state.egui_state.platform.handle_event(&event); {
global_state.egui_state.platform.handle_event(&event);
if global_state.egui_state.platform.captures_event(&event) {
return;
}
}
// Get events for the ui. // Get events for the ui.
if let Some(event) = ui::Event::try_from(&event, global_state.window.window()) { if let Some(event) = ui::Event::try_from(&event, global_state.window.window()) {
global_state.window.send_event(Event::Ui(event)); global_state.window.send_event(Event::Ui(event));

View File

@ -1091,7 +1091,7 @@ impl PlayState for SessionState {
#[cfg(feature = "egui-ui")] #[cfg(feature = "egui-ui")]
if global_state.settings.interface.egui_enabled() { if global_state.settings.interface.egui_enabled() {
global_state.egui_state.maintain( global_state.egui_state.maintain(
&self.client.borrow(), &mut self.client.borrow_mut(),
&mut self.scene, &mut self.scene,
debug_info.map(|debug_info| EguiDebugInfo { debug_info.map(|debug_info| EguiDebugInfo {
frame_time: debug_info.frame_time, frame_time: debug_info.frame_time,

View File

@ -5,12 +5,11 @@ use crate::{
use client::Client; use client::Client;
use egui::FontDefinitions; use egui::FontDefinitions;
use egui_winit_platform::{Platform, PlatformDescriptor}; use egui_winit_platform::{Platform, PlatformDescriptor};
use voxygen_egui::{DebugShapeAction, EguiDebugInfo, EguiInnerState, EguiWindows}; use voxygen_egui::{EguiAction, EguiDebugInfo, EguiDebugShapeAction, EguiInnerState};
pub struct EguiState { pub struct EguiState {
pub platform: Platform, pub platform: Platform,
egui_inner_state: EguiInnerState, egui_inner_state: EguiInnerState,
egui_windows: EguiWindows,
new_debug_shape_id: Option<u64>, new_debug_shape_id: Option<u64>,
} }
@ -27,43 +26,48 @@ impl EguiState {
Self { Self {
platform, platform,
egui_inner_state: EguiInnerState::default(), egui_inner_state: EguiInnerState::default(),
egui_windows: EguiWindows::default(),
new_debug_shape_id: None, new_debug_shape_id: None,
} }
} }
pub fn maintain( pub fn maintain(
&mut self, &mut self,
client: &Client, client: &mut Client,
scene: &mut Scene, scene: &mut Scene,
debug_info: Option<EguiDebugInfo>, debug_info: Option<EguiDebugInfo>,
) { ) {
let egui_actions = voxygen_egui::maintain( let egui_actions = voxygen_egui::maintain(
&mut self.platform, &mut self.platform,
&mut self.egui_inner_state, &mut self.egui_inner_state,
&mut self.egui_windows,
client, client,
debug_info, debug_info,
self.new_debug_shape_id.take(), self.new_debug_shape_id.take(),
); );
egui_actions.actions.iter().for_each(|action| match action { egui_actions
DebugShapeAction::AddCylinder { height, radius } => { .actions
let shape_id = scene.debug.add_shape(DebugShape::Cylinder { .into_iter()
height: *height, .for_each(|action| match action {
radius: *radius, EguiAction::ChatCommand { cmd, args } => {
}); client.send_command(cmd.keyword().into(), args);
self.new_debug_shape_id = Some(shape_id.0); },
}, EguiAction::DebugShape(debug_shape_action) => match debug_shape_action {
DebugShapeAction::RemoveShape(debug_shape_id) => { EguiDebugShapeAction::AddCylinder { height, radius } => {
scene.debug.remove_shape(DebugShapeId(*debug_shape_id)); let shape_id = scene
}, .debug
DebugShapeAction::SetPosAndColor { id, pos, color } => { .add_shape(DebugShape::Cylinder { height, radius });
let identity_ori = [0.0, 0.0, 0.0, 1.0]; self.new_debug_shape_id = Some(shape_id.0);
scene },
.debug EguiDebugShapeAction::RemoveShape(debug_shape_id) => {
.set_context(DebugShapeId(*id), *pos, *color, identity_ori); scene.debug.remove_shape(DebugShapeId(debug_shape_id));
}, },
}) EguiDebugShapeAction::SetPosAndColor { id, pos, color } => {
let identity_ori = [0.0, 0.0, 0.0, 1.0];
scene
.debug
.set_context(DebugShapeId(id), pos, color, identity_ori);
},
},
})
} }
} }