From fbee0a415a40cd4bd97e525fe6f87ce8b3337eb2 Mon Sep 17 00:00:00 2001 From: Elias Stepanik Date: Sun, 23 Mar 2025 22:20:22 +0100 Subject: [PATCH] Initial commit --- .gitignore | 19 + Cargo.toml | 17 + assets/fonts/minecraft_font.ttf | Bin 0 -> 15700 bytes src/app.rs | 80 ++++ src/helper/debug_gizmos.rs | 21 ++ src/helper/egui_dock.rs | 343 ++++++++++++++++++ src/helper/mod.rs | 2 + src/main.rs | 88 +++++ src/plugins/camera/camera_plugin.rs | 12 + src/plugins/camera/mod.rs | 2 + src/plugins/camera/systems/camera_system.rs | 167 +++++++++ src/plugins/camera/systems/mod.rs | 1 + src/plugins/environment/environment_plugin.rs | 15 + src/plugins/environment/mod.rs | 2 + .../environment/systems/environment_system.rs | 11 + src/plugins/environment/systems/mod.rs | 1 + src/plugins/mod.rs | 3 + src/plugins/ui/mod.rs | 2 + src/plugins/ui/systems/mod.rs | 1 + src/plugins/ui/systems/ui_system.rs | 64 ++++ src/plugins/ui/ui_plugin.rs | 15 + 21 files changed, 866 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 assets/fonts/minecraft_font.ttf create mode 100644 src/app.rs create mode 100644 src/helper/debug_gizmos.rs create mode 100644 src/helper/egui_dock.rs create mode 100644 src/helper/mod.rs create mode 100644 src/main.rs create mode 100644 src/plugins/camera/camera_plugin.rs create mode 100644 src/plugins/camera/mod.rs create mode 100644 src/plugins/camera/systems/camera_system.rs create mode 100644 src/plugins/camera/systems/mod.rs create mode 100644 src/plugins/environment/environment_plugin.rs create mode 100644 src/plugins/environment/mod.rs create mode 100644 src/plugins/environment/systems/environment_system.rs create mode 100644 src/plugins/environment/systems/mod.rs create mode 100644 src/plugins/mod.rs create mode 100644 src/plugins/ui/mod.rs create mode 100644 src/plugins/ui/systems/mod.rs create mode 100644 src/plugins/ui/systems/ui_system.rs create mode 100644 src/plugins/ui/ui_plugin.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2d4d3a --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ +Cargo.lock +combine.sh + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1da8bdc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "bevy-project-template" +version = "0.1.0" +edition = "2021" + +[dependencies] +bevy = { version = "0.15.1", features = ["jpeg", "trace_tracy", "trace_tracy_memory"] } +bevy_egui = "0.31.1" +bevy_asset = "0.15.0" +bevy-inspector-egui = "0.28.0" +bevy_reflect = "0.15.0" +bevy_render = "0.15.0" +bevy_window = "0.15.0" +egui_dock = "0.14.0" +bytemuck = "1.13" +bevy_mod_debugdump = "0.12.1" +log = "0.4.25" \ No newline at end of file diff --git a/assets/fonts/minecraft_font.ttf b/assets/fonts/minecraft_font.ttf new file mode 100644 index 0000000000000000000000000000000000000000..61b4610bd20cd58a9782bd5e1cc8f217a9858f30 GIT binary patch literal 15700 zcmeHOON<=V6}|oQ*dG5d81u2?viW~J;}3FxASK3pY z1W74j!GaJLS+IyAq9g=GL?R0$7AAqniWCWnETSX}7D(hLC5o~L5>oJV&bja2s_N?M z@$gF)p+>Lod-vVXx%b`os=B9JN+PmaVyQ^<<)fo}D#up*LuAc^;65}yQ=hwH_m-E? zM=PJ5oIW$L{)abQj`_DmM$S$(>L-qG=sqh_nS#u|DKIMW;s+Q%kMY%0Go90SNa>dt zpA#v)I^7zti!5vt`NDG;pPi|no|D<`8zQyGAs@}wXBy{zaQjw_zbmrhwYk=ON6Ovv z@OPH;Xl1kt+9h}e^3s%4OZQ`Zg=_$4HF|61Yw~^hk-R9skUz+u<^AaLXlu8N8O}wS zxj$jf_fFPb{(%Xg97&HO6oyDxDgtQ;oYZ_5_+>-uM{UHCu|k8z!mf8-<(2 zoAM!BwnjdAS?aPEqSSMHl%-A8aSGlb8%5X#$u7lrJ5DLfTomaf_$Xu8$QvZ@HnO&z z=T!A!OKsCuYByFH@JMT#vq;#vf_D%-5TTJEiU-ulBb zQu(I!*T_SapILvc9Ijqu{dMwSwQl{3ZQ=FYU6lT)4O@WO0;vT^)Q z+}}<{dv@*KeP467G2X6EbdEG8Pfgd`2V1k9qn-At@lKRXC7sBGjy2lz&DLzRdt_HK zmr-oaNA;-FuAgYk)Y~Vc)3;ramWUq%BRElqu;*gsrh4vr@;nA;;xTnTE``oW#r?6!~ua z-3RGe%#K5^E)&o?g3+X$f@~eUgV2}-_b6H$+;O$ruhq|qa+Z$4Zd>Nzj}{~N9s!1a zy<(3|XhuL3DULcecSB|rA){#lc?7IS<&aXxJcXG#a1Ln<^v`T`3_~3;MsgSIjLF@a zu@0nfmr?vpSlsiOxJM9u3(^C!8g1#1CfW)Q3-C$wW5{0UF)8;PR?V{p92c+7NUO`KEk@pfEQNmH0T)QPGH|m zV*Vav{~+?weC?(SF;6r5sge8STh zZB)`Hg!3^ISB^^q^;8GCkbwcd%`vJ^6Pl}v;2l?Q&;=vdAO>$en80^GS-#7X;<-k( z7ZpcVI)^LqpxQ8O^SV}wmn*eiF6Q%20nU^KUB=5mY0G``aT;jwaAG{C;L#VZI5b5- zycna;zdmZb;9&@Q$aGRqM7jAwIH^M`#6;SM3+yc4Q5i=jC`DAQ-JoHm% zIld`pw$(bvYQ-oqeR$tEKbW6z?y*h_GvId=KY* z!WHwHv8m&J4*lo~K-4M)Z=+baP&XtY?-+r{vl8Qs4BuU00mdE38-V z-d-|i)DAWV(m25n9|@kAa57`B75BY<8D|PCGXq|yzFr=&&S75hf%=&kINhXPvA;Nr zra1?trfZ$&D%u?3b8lfU<<^yajVMOPnhNVkZ$rk#2YF#VR-hAX)$nd~E#9ea5`3yS z_-LX$ZPQxz{G=ZrJMuJEM}>G*o3#|;@v+YIuEh{c25jlxx$qeuhs^b1As_bo_~0qt z9Ihap*2WSzQOOycgZ6g}AvRudXjv>=b>d4-Su%b60`<|)Ni=+4y+h@GV6*wd-u}4LSvBO#Yl0*>P_S3u41Jn z-uv={$=>|0hn@TdA@||bcN#Mg5N7WkM56fyRg|A>ExJcg;?ewoa)m5c7{Py%1I5Gq zEX~iKU&H!T)SnN_kNSpBz2e>qVbp_h1stdhsCIewLE5b?rfU=y4>MRI?%@Opq&=3P z%h+HieAEhiSaimJAw4}mu|mm{Mp!4Q@E+t(uNOQsJWfBu^VHE6&^X`>`M~S)#vw02 zjq2;m3oFw1rdtHUVEH4(C%mAqR{_n(>to16n(Oc(DgI?dUwzN#$@=FvC|BQnDCLmj z{F#;CA?c0?+CJ{Pn^bAA;&n0jDGWX}{}e}`&9Sks>jE~eIAq3FD`pKbZ={(Wz{4x# zf$yaH;^(aThy&%B@MZI9AW!xp{r8HC)_a;5PBS@2Ce)JrSOp3t+o;N83!w`uFi z0V|#X=Em`xb>-mvuo=$kUIC>9n?76qm;CzbkrqDF7M+=prF^m?=o2@`YR}`3L&f)1 zz9!k~Yi&NGzW$mOSo9Nw6!!3%tgZHaeIdF6EnBzCc`~L-9GX|$1>;KM3urt(iHSXP z@5!#=5Z>7p)Q(5%RZ<7C`MgLJ^N8E#Abv_*1p0HRAgH=F`^@bIo&(riQ9?HZG zYkH1T2YowgI6~92qBm#xvkX#2l?Ow?Moe|uaVj+o@-SL)NE z$Lu-qocK<0>U@`TJzFial%CPSdO_7Bxur#qP3fnwj?7oj=Q_b>yo7K6jAn`Z7h=;W zk}7~zy>gVfA_C+HjWPCrHUlKe4^;b*&D`rVdrK!M?E=X?#%pEydx6*!Z)US-Y*IiO z_JY@F-*_p^Frt{@2Nzy7E1+7-Sa*JUi~D8zezLHJsZ4FJJ+DRbve*$DIGNgt2aXX= zH&!$_PPjQ>Nzp;uZNMDg`x?Fk86_#B7bvXYRrLbp0R=Aj75&sN(M&d>dUG3$%I}2a zF*=JBqi!4!sWiqxB8k*|8O9~=L+S$tve{#k!#e%0x$2zDIgRk@{tG5cNrC<>2pgrkXhM-~sBv5dxeC;`F z`YM-_8av#0-?ZIIu!S=L*3fc3YG8%6^z&?qdkHgjbZZqU1&CQ}_of18b(HW2XD)Bos=zXg2f(9>UoqgSBptP)WpuQ$v zKjD8MKI=65ybdIl;?rqRth7Q5o;iMooW_@a1_qHDT3T!28xcj-rXi_6D#WZHwV#|? zWZOJu5i$Rkq9$(NIVm`;Jd5GPhI!cYrWw@{J$W9?qvjjlo=B7IZO2#z0+C^Ty*j>=kBh zz~@zz$!h9K#>&;z^_`eXv6l1iPYjV(vh7iS4qjr9a;4ear{0BNT`!|rr;3m}C)5UB zFM%sjz2tnO z`7xIlaV2Y<+%@vHlh_^W3ZUi+@xGRxKZ$<&E-mzEk5#1?sP08jy)x8S3w&@{%|>-q z({DRC9~ljD`nw3!3r~?0kAIi#CVZWBAhtYK9|MD0rw6SsQ16ra4t45v$2m5S(fO3~ zzREaur!l#nf&SPrtMhu!phjS8fV}aC1Xnq(hBXS`oY^Ydmy-!vAl4){j2#(ydJ2Q8 zClFF^nG*|K#u3gFjYy?D3zH=~YUYg>04 zrsU~dJBKlTUt4+!2o9sYkE8MJQ2W)#K{2j%&|XLTBmOyrZvwBl8SO=pp?lGu!_Tm8 zL4(dJ{BCyDoA~kXgJ_T+#&39sp}%Gq8v1KDpxur3Pmy&o8s^qNfc6{w{0X);z}5!% zxaexM@1VUSvhij#;JA1V+O23W51>7Z_JPP2VA}#)TMwh175Us2w5QNuV;it+LqEC<4f7Ff zZilVy(Af^ZJ78xAbgsG;?K!l!@iS%cu6|kMnkUizDst^ZXy9LW2^xG}_b-v_Z$^7x g Self { + InspectorVisible(false) + } +} +impl Plugin for AppPlugin { + fn build(&self, app: &mut App) { + app.insert_resource(UiState::new()); + app.insert_resource(InspectorVisible(true)); + + + + app.add_plugins(crate::plugins::camera::camera_plugin::CameraPlugin); + app.add_plugins(crate::plugins::ui::ui_plugin::UiPlugin); + + app.add_plugins(crate::plugins::environment::environment_plugin::EnvironmentPlugin); + + + + + + + + + + + app.add_systems(Update, (debug_gizmos, toggle_ui_system)); + app.add_systems( + PostUpdate, + show_ui_system + .before(EguiSet::ProcessOutput) + .before(bevy_egui::systems::end_pass_system) + .before(TransformSystem::TransformPropagate) + .run_if(should_display_inspector), + ); + app.add_systems(PostUpdate, (set_camera_viewport.after(show_ui_system).run_if(should_display_inspector), reset_camera_viewport.run_if(should_not_display_inspector).after(set_camera_viewport))); + app.add_systems(Update, set_gizmo_mode); + app.register_type::>>(); + app.register_type::(); + } + + +} + + + +fn toggle_ui_system( + + keyboard_input: Res>, + mut inspector_visible: ResMut, +){ + // ======================= + // 6) Hide Inspector + // ======================= + if keyboard_input.just_pressed(KeyCode::F1) { + inspector_visible.0 = !inspector_visible.0 + } + +} + + +fn should_display_inspector(inspector_visible: Res) -> bool { + inspector_visible.0 +} + +fn should_not_display_inspector(inspector_visible: Res) -> bool { + !inspector_visible.0 +} diff --git a/src/helper/debug_gizmos.rs b/src/helper/debug_gizmos.rs new file mode 100644 index 0000000..c0e48d3 --- /dev/null +++ b/src/helper/debug_gizmos.rs @@ -0,0 +1,21 @@ +use bevy::color::palettes::css::{BLUE, GREEN, RED}; +use bevy::prelude::*; + +pub fn debug_gizmos(mut gizmos: Gizmos) { + + + /* // Draw a line + gizmos.line( + Vec3::ZERO, + Vec3::new(1.0, 1.0, 1.0), + RED, + ); + + // Draw a sphere + gizmos.sphere(Vec3::new(2.0, 2.0, 2.0), 0.5, BLUE); + + // Draw a wireframe cube + gizmos.rect(Isometry3d::IDENTITY, Vec2::ONE, GREEN);*/ + +} + diff --git a/src/helper/egui_dock.rs b/src/helper/egui_dock.rs new file mode 100644 index 0000000..540c5c0 --- /dev/null +++ b/src/helper/egui_dock.rs @@ -0,0 +1,343 @@ +use bevy::prelude::*; +use bevy_asset::{ReflectAsset, UntypedAssetId}; +use bevy_egui::{egui, EguiContext}; +use bevy_inspector_egui::bevy_inspector::hierarchy::{hierarchy_ui, SelectedEntities}; +use bevy_inspector_egui::bevy_inspector::{ + self, ui_for_entities_shared_components, ui_for_entity_with_children, +}; +use std::any::TypeId; +use bevy_reflect::TypeRegistry; +use bevy_render::camera::{CameraProjection, Viewport}; +use bevy_window::{PrimaryWindow, Window}; +use egui_dock::{DockArea, DockState, NodeIndex, Style}; + +#[cfg(egui_dock_gizmo)] +use transform_gizmo_egui::GizmoMode; + +/// Placeholder type if gizmo is disabled. +#[cfg(not(egui_dock_gizmo))] +#[derive(Clone, Copy)] +pub struct GizmoMode; + + +#[derive(Component)] +pub struct MainCamera; + +pub fn show_ui_system(world: &mut World) { + let Ok(egui_context) = world + .query_filtered::<&mut EguiContext, With>() + .get_single(world) + else { + return; + }; + let mut egui_context = egui_context.clone(); + + world.resource_scope::(|world, mut ui_state| { + ui_state.ui(world, egui_context.get_mut()) + }); +} + +// make camera only render to view not obpub structed by UI +pub fn set_camera_viewport( + ui_state: Res, + primary_window: Query<&mut Window, With>, + egui_settings: Query<&bevy_egui::EguiSettings>, + mut cameras: Query<&mut Camera, With>, +) { + let mut cam = cameras.single_mut(); + + let Ok(window) = primary_window.get_single() else { + return; + }; + + let scale_factor = window.scale_factor() * egui_settings.single().scale_factor; + + let viewport_pos = ui_state.viewport_rect.left_top().to_vec2() * scale_factor; + let viewport_size = ui_state.viewport_rect.size() * scale_factor; + + let physical_position = UVec2::new(viewport_pos.x as u32, viewport_pos.y as u32); + let physical_size = UVec2::new(viewport_size.x as u32, viewport_size.y as u32); + + // The desired viewport rectangle at its offset in "physical pixel space" + let rect = physical_position + physical_size; + + let window_size = window.physical_size(); + // wgpu will panic if trying to set a viewport rect which has coordinates extending + // past the size of the render target, i.e. the physical window in our case. + // Typically this shouldn't happen- but during init and resizing etc. edge cases might occur. + // Simply do nothing in those cases. + if rect.x <= window_size.x && rect.y <= window_size.y { + cam.viewport = Some(Viewport { + physical_position, + physical_size, + depth: 0.0..1.0, + }); + } +} + +pub fn reset_camera_viewport(mut cameras: Query<&mut Camera, With>) { + if let Ok(mut cam) = cameras.get_single_mut() { + cam.viewport = None; // Reset the viewport to its default state + } +} +pub fn set_gizmo_mode(input: Res>, mut ui_state: ResMut) { + #[cfg(egui_dock_gizmo)] + let keybinds = [ + (KeyCode::KeyR, GizmoMode::Rotate), + (KeyCode::KeyT, GizmoMode::Translate), + (KeyCode::KeyS, GizmoMode::Scale), + ]; + #[cfg(not(egui_dock_gizmo))] + let keybinds = []; + for (key, mode) in keybinds { + if input.just_pressed(key) { + ui_state.gizmo_mode = mode; + } + } +} + +#[derive(Eq, PartialEq)] +pub enum InspectorSelection { + Entities, + Resource(TypeId, String), + Asset(TypeId, String, UntypedAssetId), +} + +#[derive(Resource)] +pub struct UiState { + state: DockState, + viewport_rect: egui::Rect, + selected_entities: SelectedEntities, + selection: InspectorSelection, + gizmo_mode: GizmoMode, +} + +impl UiState { + pub fn new() -> Self { + let mut state = DockState::new(vec![EguiWindow::GameView]); + let tree = state.main_surface_mut(); + let [game, _inspector] = + tree.split_right(NodeIndex::root(), 0.75, vec![EguiWindow::Inspector]); + let [game, _hierarchy] = tree.split_left(game, 0.2, vec![EguiWindow::Hierarchy]); + let [_game, _bottom] = + tree.split_below(game, 0.8, vec![EguiWindow::Resources, EguiWindow::Assets]); + + Self { + state, + selected_entities: SelectedEntities::default(), + selection: InspectorSelection::Entities, + viewport_rect: egui::Rect::NOTHING, + #[cfg(egui_dock_gizmo)] + gizmo_mode: GizmoMode::Translate, + #[cfg(not(egui_dock_gizmo))] + gizmo_mode: GizmoMode, + } + } + + pub fn ui(&mut self, world: &mut World, ctx: &mut egui::Context) { + let mut tab_viewer = TabViewer { + world, + viewport_rect: &mut self.viewport_rect, + selected_entities: &mut self.selected_entities, + selection: &mut self.selection, + gizmo_mode: self.gizmo_mode, + }; + DockArea::new(&mut self.state) + .style(Style::from_egui(ctx.style().as_ref())) + .show(ctx, &mut tab_viewer); + } +} + +#[derive(Debug)] +pub enum EguiWindow { + GameView, + Hierarchy, + Resources, + Assets, + Inspector, +} + +pub struct TabViewer<'a> { + world: &'a mut World, + selected_entities: &'a mut SelectedEntities, + selection: &'a mut InspectorSelection, + viewport_rect: &'a mut egui::Rect, + gizmo_mode: GizmoMode, +} + +impl egui_dock::TabViewer for TabViewer<'_> { + type Tab = EguiWindow; + + fn ui(&mut self, ui: &mut egui_dock::egui::Ui, window: &mut Self::Tab) { + let type_registry = self.world.resource::().0.clone(); + let type_registry = type_registry.read(); + + match window { + EguiWindow::GameView => { + *self.viewport_rect = ui.clip_rect(); + + draw_gizmo(ui, self.world, self.selected_entities, self.gizmo_mode); + } + EguiWindow::Hierarchy => { + let selected = hierarchy_ui(self.world, ui, self.selected_entities); + if selected { + *self.selection = InspectorSelection::Entities; + } + } + EguiWindow::Resources => select_resource(ui, &type_registry, self.selection), + EguiWindow::Assets => select_asset(ui, &type_registry, self.world, self.selection), + EguiWindow::Inspector => match *self.selection { + InspectorSelection::Entities => match self.selected_entities.as_slice() { + &[entity] => ui_for_entity_with_children(self.world, entity, ui), + entities => ui_for_entities_shared_components(self.world, entities, ui), + }, + InspectorSelection::Resource(type_id, ref name) => { + ui.label(name); + bevy_inspector::by_type_id::ui_for_resource( + self.world, + type_id, + ui, + name, + &type_registry, + ) + } + InspectorSelection::Asset(type_id, ref name, handle) => { + ui.label(name); + bevy_inspector::by_type_id::ui_for_asset( + self.world, + type_id, + handle, + ui, + &type_registry, + ); + } + }, + } + } + + fn title(&mut self, window: &mut Self::Tab) -> egui_dock::egui::WidgetText { + format!("{window:?}").into() + } + + fn clear_background(&self, window: &Self::Tab) -> bool { + !matches!(window, EguiWindow::GameView) + } +} + +#[allow(unused)] +pub fn draw_gizmo( + ui: &mut egui::Ui, + world: &mut World, + selected_entities: &SelectedEntities, + gizmo_mode: GizmoMode, +) { + let (cam_transform, projection) = world + .query_filtered::<(&GlobalTransform, &Projection), With>() + .single(world); + let view_matrix = Mat4::from(cam_transform.affine().inverse()); + let projection_matrix = projection.get_clip_from_view(); + + if selected_entities.len() != 1 { + #[allow(clippy::needless_return)] + return; + } + + /*for selected in selected_entities.iter() { + let Some(transform) = world.get::(selected) else { + continue; + }; + let model_matrix = transform.compute_matrix(); + + let mut gizmo = Gizmo::new(GizmoConfig { + view_matrix: view_matrix.into(), + projection_matrix: projection_matrix.into(), + orientation: GizmoOrientation::Local, + modes: EnumSet::from(gizmo_mode), + ..Default::default() + }); + let Some([result]) = gizmo + .interact(ui, model_matrix.into()) + .map(|(_, res)| res.as_slice()) + else { + continue; + }; + + let mut transform = world.get_mut::(selected).unwrap(); + transform = Transform { + translation: Vec3::from(<[f64; 3]>::from(result.translation)), + rotation: Quat::from_array(<[f64; 4]>::from(result.rotation)), + scale: Vec3::from(<[f64; 3]>::from(result.scale)), + }; + }*/ +} + +pub fn select_resource( + ui: &mut egui::Ui, + type_registry: &TypeRegistry, + selection: &mut InspectorSelection, +) { + let mut resources: Vec<_> = type_registry + .iter() + .filter(|registration| registration.data::().is_some()) + .map(|registration| { + ( + registration.type_info().type_path_table().short_path(), + registration.type_id(), + ) + }) + .collect(); + resources.sort_by(|(name_a, _), (name_b, _)| name_a.cmp(name_b)); + + for (resource_name, type_id) in resources { + let selected = match *selection { + InspectorSelection::Resource(selected, _) => selected == type_id, + _ => false, + }; + + if ui.selectable_label(selected, resource_name).clicked() { + *selection = InspectorSelection::Resource(type_id, resource_name.to_string()); + } + debug!("{}", resource_name); + } +} + +pub fn select_asset( + ui: &mut egui::Ui, + type_registry: &TypeRegistry, + world: &World, + selection: &mut InspectorSelection, +) { + let mut assets: Vec<_> = type_registry + .iter() + .filter_map(|registration| { + let reflect_asset = registration.data::()?; + Some(( + registration.type_info().type_path_table().short_path(), + registration.type_id(), + reflect_asset, + )) + }) + .collect(); + assets.sort_by(|(name_a, ..), (name_b, ..)| name_a.cmp(name_b)); + + for (asset_name, asset_type_id, reflect_asset) in assets { + let handles: Vec<_> = reflect_asset.ids(world).collect(); + + ui.collapsing(format!("{asset_name} ({})", handles.len()), |ui| { + for handle in handles { + let selected = match *selection { + InspectorSelection::Asset(_, _, selected_id) => selected_id == handle, + _ => false, + }; + + if ui + .selectable_label(selected, format!("{:?}", handle)) + .clicked() + { + *selection = + InspectorSelection::Asset(asset_type_id, asset_name.to_string(), handle); + } + } + }); + } +} diff --git a/src/helper/mod.rs b/src/helper/mod.rs new file mode 100644 index 0000000..ec80961 --- /dev/null +++ b/src/helper/mod.rs @@ -0,0 +1,2 @@ +pub mod egui_dock; +pub mod debug_gizmos; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c9bc0e1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,88 @@ +mod plugins; +mod app; +mod helper; +use bevy::DefaultPlugins; +use bevy::gizmos::{AppGizmoBuilder, GizmoPlugin}; +use bevy::log::info; +use bevy::prelude::{default, App, GizmoConfigGroup, PluginGroup, Reflect, Res, Resource}; +use bevy::render::RenderPlugin; +use bevy::render::settings::{Backends, RenderCreation, WgpuSettings}; +use bevy_egui::{EguiPlugin}; +use bevy_inspector_egui::DefaultInspectorConfigPlugin; +use bevy_window::{PresentMode, Window, WindowPlugin}; +use crate::app::AppPlugin; + +const TITLE: &str = "Project Template"; +const RESOLUTION: (f32,f32) = (1920f32, 1080f32); +const RESIZABLE: bool = true; +const DECORATIONS: bool = true; +const TRANSPARENT: bool = true; +const PRESENT_MODE: PresentMode = PresentMode::AutoVsync; + + + +fn main() { + let mut app = App::new(); + register_platform_plugins(&mut app); + + app.add_plugins(AppPlugin); + app.add_plugins(EguiPlugin); + app.add_plugins(DefaultInspectorConfigPlugin); + /*app.add_plugins(GizmoPlugin);*/ + + app.run(); +} + + + +#[derive(Resource)] +pub struct InspectorVisible(bool); +fn register_platform_plugins(app: &mut App) { + #[cfg(target_os = "windows")] + { + // Register Windows-specific plugins + info!("Adding Windows-specific plugins"); + app.add_plugins(DefaultPlugins + .set(RenderPlugin { + render_creation: RenderCreation::Automatic(WgpuSettings { + backends: Some(Backends::VULKAN), + ..default() + }), + ..default() + }) + .set(WindowPlugin { + primary_window: Some(Window { + title: TITLE.to_string(), // Window title + resolution: RESOLUTION.into(), // Initial resolution (width x height) + resizable: RESIZABLE, // Allow resizing + decorations: DECORATIONS, // Enable window decorations + transparent: TRANSPARENT, // Opaque background + present_mode: PRESENT_MODE, // VSync mode + ..default() + }), + ..default() + }) + ); + } + + #[cfg(target_os = "macos")] + { + info!("Adding macOS-specific plugins"); + app.add_plugins(DefaultPlugins) + .set(WindowPlugin { + primary_window: Some(Window { + title: crate::TITLE.to_string(), // Window title + resolution: crate::RESOLUTION.into(), // Initial resolution (width x height) + resizable: crate::RESIZABLE, // Allow resizing + decorations: crate::DECORATIONS, // Enable window decorations + transparent: crate::TRANSPARENT, // Opaque background + present_mode: crate::PRESENT_MODE, // VSync mode + ..default() + }), + ..default() + }); + } +} +fn should_display_inspector(inspector_visible: Res) -> bool { + inspector_visible.0 +} \ No newline at end of file diff --git a/src/plugins/camera/camera_plugin.rs b/src/plugins/camera/camera_plugin.rs new file mode 100644 index 0000000..4c42f7b --- /dev/null +++ b/src/plugins/camera/camera_plugin.rs @@ -0,0 +1,12 @@ +use bevy::a11y::AccessibilitySystem::Update; +use bevy::app::{App, Plugin, PreUpdate, Startup}; + +pub struct CameraPlugin; +impl Plugin for CameraPlugin { + fn build(&self, _app: &mut App) { + _app.add_systems(Startup, (crate::plugins::camera::systems::camera_system::setup)); + _app.add_systems(PreUpdate, (crate::plugins::camera::systems::camera_system::camera_controller_system)); + } + + +} diff --git a/src/plugins/camera/mod.rs b/src/plugins/camera/mod.rs new file mode 100644 index 0000000..cda78b2 --- /dev/null +++ b/src/plugins/camera/mod.rs @@ -0,0 +1,2 @@ +pub mod camera_plugin; +pub mod systems; \ No newline at end of file diff --git a/src/plugins/camera/systems/camera_system.rs b/src/plugins/camera/systems/camera_system.rs new file mode 100644 index 0000000..6b6735d --- /dev/null +++ b/src/plugins/camera/systems/camera_system.rs @@ -0,0 +1,167 @@ +use bevy::input::mouse::{MouseMotion, MouseWheel}; +use bevy::math::{Vec3}; +use bevy::prelude::*; +use bevy_render::camera::{Exposure, PhysicalCameraParameters, Projection}; +use bevy_window::CursorGrabMode; +use crate::helper::egui_dock::MainCamera; + +#[derive(Component)] +pub struct CameraController { + pub yaw: f32, + pub pitch: f32, + pub speed: f32, + pub sensitivity: f32, +} + + +impl Default for CameraController { + fn default() -> Self { + Self { + yaw: 0.0, + pitch: 0.0, + speed: 10.0, + sensitivity: 0.1, + } + } +} + +pub fn setup(mut commands: Commands,){ + + + + commands.spawn(( + Transform::from_xyz(0.0, 0.0, 10.0), // initial f32 + GlobalTransform::default(), + Camera3d::default(), + Projection::from(PerspectiveProjection{ + near: 0.0001, + ..default() + }), + MainCamera, + CameraController::default(), + Exposure::from_physical_camera(PhysicalCameraParameters { + aperture_f_stops: 1.0, + shutter_speed_s: 1.0 / 125.0, + sensitivity_iso: 100.0, + sensor_height: 0.01866, + }), + )); + + +} + +/// Example system to control a camera using double-precision for position. +pub fn camera_controller_system( + time: Res