From 74d7a73ddc6c06984c816f56dae326216a5c3148 Mon Sep 17 00:00:00 2001 From: Anthony Tornetta Date: Wed, 15 Jan 2025 19:27:00 -0500 Subject: [PATCH 01/16] Render each item to image every frame --- .../cosmos/images/blocks/glass_purple.png | Bin 460 -> 161 bytes .../material_types/animated_material.rs | 2 +- .../materials/material_types/lod_material.rs | 2 +- .../materials/material_types/main_material.rs | 2 +- cosmos_client/src/ui/item_renderer/mod.rs | 4 + .../src/ui/item_renderer/photo_booth.rs | 162 ++++++++++++++++++ 6 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 cosmos_client/src/ui/item_renderer/photo_booth.rs diff --git a/cosmos_client/assets/cosmos/images/blocks/glass_purple.png b/cosmos_client/assets/cosmos/images/blocks/glass_purple.png index f5eeae4f0452acc9d17fc8712283a5e467d03290..ace3a4ab96d5ab90325495200a86e95cae5ba138 100644 GIT binary patch delta 117 zcmX@ZypVB%aUMg2r;B5VMQ`$)tqCvd*|-?EnVFfLGr~g{EN6vJ*S&q9MW>*O{V-b) zZ=*Dr5zNmvDVE8+LFTLm+XQYmUWrX?3dB(=ue;nWNUGVd-zqp4>WY|cd#~w047 zthR@%VtQni)x6Gd!Coz0bm?lPOj=)MiA?)kQ5939+jt|fg}nZh&hcWio34_5O4c4# zWz`$NLv)xGkAEkqGZ=2%!z$BcVlq5qykMFY?u;Zy%8LDEdt4WjlK}3t={iNP06Dgk zSWk(MX6G`)Jb}}sqY96Do5CFXy`@KmofBi4te8I8RK, unlit: bool) -> AnimatedArr fn create_transparent_material(image_handle: Handle, unlit: bool) -> AnimatedArrayTextureMaterial { AnimatedArrayTextureMaterial { - alpha_mode: AlphaMode::Add, + alpha_mode: AlphaMode::Blend, unlit, metallic: 0.0, reflectance: 0.0, diff --git a/cosmos_client/src/asset/materials/material_types/lod_material.rs b/cosmos_client/src/asset/materials/material_types/lod_material.rs index d59585b37..40e498bb5 100644 --- a/cosmos_client/src/asset/materials/material_types/lod_material.rs +++ b/cosmos_client/src/asset/materials/material_types/lod_material.rs @@ -76,7 +76,7 @@ fn create_main_material(image_handle: Handle, unlit: bool) -> LodArrayTex fn create_transparent_material(image_handle: Handle, unlit: bool) -> LodArrayTextureMaterial { LodArrayTextureMaterial { base_color_texture: Some(image_handle), - alpha_mode: AlphaMode::Add, + alpha_mode: AlphaMode::Blend, unlit, metallic: 0.0, reflectance: 0.0, diff --git a/cosmos_client/src/asset/materials/material_types/main_material.rs b/cosmos_client/src/asset/materials/material_types/main_material.rs index 88ce0a2d2..898f14419 100644 --- a/cosmos_client/src/asset/materials/material_types/main_material.rs +++ b/cosmos_client/src/asset/materials/material_types/main_material.rs @@ -76,7 +76,7 @@ fn create_main_material(image_handle: Handle, unlit: bool) -> ArrayTextur fn create_transparent_material(image_handle: Handle, unlit: bool) -> ArrayTextureMaterial { ArrayTextureMaterial { base_color_texture: Some(image_handle), - alpha_mode: AlphaMode::Add, + alpha_mode: AlphaMode::Blend, unlit, metallic: 0.0, reflectance: 0.0, diff --git a/cosmos_client/src/ui/item_renderer/mod.rs b/cosmos_client/src/ui/item_renderer/mod.rs index 7c9b93705..0adea9ad0 100644 --- a/cosmos_client/src/ui/item_renderer/mod.rs +++ b/cosmos_client/src/ui/item_renderer/mod.rs @@ -22,6 +22,8 @@ use crate::{ use super::{UiMiddleRoot, UiSystemSet, UiTopRoot}; +pub mod photo_booth; + const INVENTORY_SLOT_LAYER: usize = 0b01; const MIDDLE_INVENTORY_SLOT_LAYER: usize = 0b10; @@ -319,6 +321,8 @@ pub enum RenderItemSystemSet { // } pub(super) fn register(app: &mut App) { + photo_booth::register(app); + app.configure_sets( Update, (RenderItemSystemSet::RenderItems.in_set(MaterialsSystemSet::RequestMaterialChanges)) diff --git a/cosmos_client/src/ui/item_renderer/photo_booth.rs b/cosmos_client/src/ui/item_renderer/photo_booth.rs new file mode 100644 index 000000000..042488bcd --- /dev/null +++ b/cosmos_client/src/ui/item_renderer/photo_booth.rs @@ -0,0 +1,162 @@ +use std::{f32::consts::PI, path::Path, time::Duration}; + +use bevy::{ + asset::RenderAssetUsages, + prelude::*, + render::{ + camera::ScalingMode, + render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, + view::RenderLayers, + }, + time::common_conditions::on_timer, +}; +use cosmos_core::{ + blockitems::BlockItems, + item::Item, + registry::{identifiable::Identifiable, Registry}, + state::GameState, + utils::array_utils::expand_2d, +}; + +use crate::{ + asset::{ + asset_loading::ItemRenderingInfo, + materials::{AddMaterialEvent, MaterialType}, + }, + item::item_mesh::ItemMeshMaterial, +}; + +const PHOTO_BOOTH_RENDER_LAYER: usize = 0b100000; + +const PX_PER_ITEM: usize = 100; + +#[derive(Resource, Debug)] +pub struct RenderedItemAtlas { + width: usize, + handle: Handle, +} + +impl RenderedItemAtlas { + pub fn get_item_rect(&self, item: &Item) -> Rect { + let (x, y) = expand_2d(item.id() as usize, self.width); + let (x, y) = (x as f32, y as f32); + const PPI: f32 = PX_PER_ITEM as f32; + + Rect { + min: Vec2::new(PPI * x, PPI * y), + max: Vec2::new(PPI * (x + 1.0), PPI * (y + 1.0)), + } + } + + pub fn get_atlas_handle(&self) -> &Handle { + &self.handle + } +} + +fn setup_rendered_item_atlas(mut images: ResMut>, w: usize, h: usize) -> Handle { + let size = Extent3d { + width: w as u32, + height: h as u32, + ..default() + }; + + // This is the texture that will be rendered to. + let mut image = Image::new_fill( + size, + TextureDimension::D2, + &[0, 0, 0, 0], + TextureFormat::Bgra8UnormSrgb, + RenderAssetUsages::default(), + ); + // You need to set these texture usage flags in order to use the image as a render target + image.texture_descriptor.usage = TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT; + + images.add(image) +} + +fn create_booth( + mut commands: Commands, + mut event_writer: EventWriter, + items: Res>, + item_meshes: Res>, + block_items: Res, + mut images: ResMut>, +) { + let n_items = items.iter().len(); + + let w = (n_items as f32).sqrt().ceil(); + let h = (n_items as f32 / w).ceil(); + + let width = w as usize; + let height = h as usize; + + let image_handle = setup_rendered_item_atlas(images, PX_PER_ITEM * width, PX_PER_ITEM * height); + + const GAP: f32 = 2.0; + let cam_w = w * GAP; + let cam_h = h * GAP; + + commands.spawn(( + Name::new("Photo Booth Camera"), + Camera3d { ..Default::default() }, + Projection::Orthographic(OrthographicProjection { + scaling_mode: ScalingMode::Fixed { + width: cam_w, + height: cam_h, + }, + ..OrthographicProjection::default_3d() + }), + Camera { + order: 10, + clear_color: ClearColorConfig::Custom(Color::NONE), + target: image_handle.clone().into(), + hdr: true, // Transparent stuff fails to render properly if this is off - this may be a bevy bug? + ..Default::default() + }, + Transform::from_xyz(0.0, 0.0, 1.0) + .looking_at(Vec3::ZERO, Vec3::Y) + .with_translation(Vec3::new(cam_w / 2.0 - 1.0, cam_h / 2.0 - 1.0, 1.0)), + RenderLayers::from_layers(&[PHOTO_BOOTH_RENDER_LAYER]), + )); + + commands.insert_resource(RenderedItemAtlas { + width, + handle: image_handle, + }); + + for (i, item) in items.iter().enumerate() { + let Some(item_mat_material) = item_meshes.from_id(item.unlocalized_name()) else { + info!("{item_meshes:?}"); + warn!("Missing rendering material for item {}", item.unlocalized_name()); + return; + }; + + let (x, y) = expand_2d(i, width); + + let rot = if block_items.block_from_item(item).is_some() { + // This makes blocks look cool + Quat::from_xyzw(0.07383737, 0.9098635, 0.18443844, 0.3642514) + } else { + Quat::from_axis_angle(Vec3::X, PI / 2.0) + }; + + let entity = commands + .spawn(( + RenderLayers::from_layers(&[PHOTO_BOOTH_RENDER_LAYER]), + Mesh3d(item_mat_material.mesh_handle().clone_weak()), + Transform::from_xyz(x as f32 * GAP, y as f32 * GAP, 0.0).with_rotation(rot), + )) + .id(); + + event_writer.send(AddMaterialEvent { + entity, + add_material_id: item_mat_material.material_id(), + texture_dimensions_index: item_mat_material.texture_dimension_index(), + material_type: MaterialType::Illuminated, + }); + } +} + +pub(super) fn register(app: &mut App) { + app.add_systems(OnEnter(GameState::LoadingWorld), create_booth); +} From aa08912f05a2ec1287efcad4627c7a2e2058c1d5 Mon Sep 17 00:00:00 2001 From: Anthony Tornetta Date: Wed, 15 Jan 2025 19:47:08 -0500 Subject: [PATCH 02/16] Removing references to target cameras + updating item rendering to use new system --- cosmos_client/src/inventory/mod.rs | 146 +-------- cosmos_client/src/netty/gameplay/receiver.rs | 2 - cosmos_client/src/rendering/panorama/mod.rs | 8 +- .../systems/missile_launcher_system.rs | 12 +- cosmos_client/src/ui/debug_info_display.rs | 10 +- cosmos_client/src/ui/hotbar.rs | 24 +- cosmos_client/src/ui/hud/mod.rs | 11 +- cosmos_client/src/ui/item_renderer/mod.rs | 300 +----------------- .../src/ui/item_renderer/photo_booth.rs | 8 +- cosmos_client/src/ui/main_menu/mod.rs | 4 +- cosmos_client/src/ui/mod.rs | 18 -- 11 files changed, 44 insertions(+), 499 deletions(-) diff --git a/cosmos_client/src/inventory/mod.rs b/cosmos_client/src/inventory/mod.rs index b68d3dd50..4fcc6f447 100644 --- a/cosmos_client/src/inventory/mod.rs +++ b/cosmos_client/src/inventory/mod.rs @@ -26,8 +26,8 @@ use crate::{ show_cursor::no_open_menus, window::{GuiWindow, UiWindowSystemSet}, }, - item_renderer::{ItemRenderLayer, RenderItem}, - OpenMenu, UiMiddleRoot, UiRoot, UiSystemSet, UiTopRoot, + item_renderer::RenderItem, + OpenMenu, UiSystemSet, }, }; @@ -177,8 +177,6 @@ fn toggle_inventory_rendering( mapping: Res, mut removed_components: RemovedComponents, q_block_data: Query<&BlockData>, - q_middle_root: Query>, - q_bottom_root: Query>, ) { for removed in removed_components.read() { let Ok((inventory_holder, mut local_inventory, open_inventory_entity)) = without_needs_displayed_inventories.get_mut(removed) @@ -265,7 +263,7 @@ fn toggle_inventory_rendering( commands.entity(slot).with_children(|p| { let slot = inventory.itemstack_at(slot_number); - create_inventory_slot(inventory_holder, slot_number, p, slot, text_style.clone(), ItemRenderLayer::Middle); + create_inventory_slot(inventory_holder, slot_number, p, slot, text_style.clone()); }); } @@ -285,9 +283,6 @@ fn toggle_inventory_rendering( (Val::Px(100.0), Val::Auto) }; - let middle_root = q_middle_root.single(); - let bottom_root = q_bottom_root.single(); - let width = Val::Px(n_slots_per_row as f32 * slot_size + inventory_border_size * 2.0 + scrollbar_width); let mut ents = vec![]; @@ -306,7 +301,6 @@ fn toggle_inventory_rendering( ents.push( commands .spawn(( - TargetCamera(bottom_root), NonHotbarSlots, Name::new("Rendered Inventory Non-Hotbar Slots"), StyleOffsets { @@ -339,14 +333,7 @@ fn toggle_inventory_rendering( .enumerate() .filter(|(slot, _)| priority_slots.as_ref().map(|x| !x.contains(slot)).unwrap_or(true)) { - create_inventory_slot( - inventory_holder, - slot_number, - slots, - slot.as_ref(), - text_style.clone(), - ItemRenderLayer::Middle, - ); + create_inventory_slot(inventory_holder, slot_number, slots, slot.as_ref(), text_style.clone()); } }); }) @@ -357,7 +344,6 @@ fn toggle_inventory_rendering( ents.push( commands .spawn(( - TargetCamera(middle_root), Name::new("Rendered Inventory Hotbar Slots"), StyleOffsets { left: 0.0, @@ -388,7 +374,6 @@ fn toggle_inventory_rendering( slots, inventory.itemstack_at(slot_number), text_style.clone(), - ItemRenderLayer::Top, ); } }) @@ -401,7 +386,6 @@ fn toggle_inventory_rendering( ents.push( commands .spawn(( - TargetCamera(middle_root), Name::new("Rendered Inventory Title Bar"), RenderedInventory { inventory_holder }, OpenMenu::new(0), @@ -516,21 +500,11 @@ fn on_update_inventory( { displayed_slot.item_stack = inventory.itemstack_at(displayed_slot.slot_number).cloned(); - let render_layer = if inventory - .priority_slots() - .into_iter() - .any(|x| x.contains(&displayed_slot.slot_number)) - { - ItemRenderLayer::Top - } else { - ItemRenderLayer::Middle - }; - let Some(mut ecmds) = commands.get_entity(display_entity) else { continue; }; - rerender_inventory_slot(&mut ecmds, &displayed_slot, &asset_server, true, render_layer); + rerender_inventory_slot(&mut ecmds, &displayed_slot, &asset_server, true); } } } @@ -541,7 +515,7 @@ fn on_update_inventory( displayed_item.item_stack = Some(held_item_stack.0.clone()); if let Some(mut ecmds) = commands.get_entity(entity) { - rerender_inventory_slot(&mut ecmds, &displayed_item, &asset_server, false, ItemRenderLayer::Top); + rerender_inventory_slot(&mut ecmds, &displayed_item, &asset_server, false); } } } @@ -551,7 +525,6 @@ fn rerender_inventory_slot( displayed_item: &DisplayedItemFromInventory, asset_server: &AssetServer, as_child: bool, - render_layer: ItemRenderLayer, ) { ecmds.despawn_descendants(); @@ -573,10 +546,10 @@ fn rerender_inventory_slot( if as_child { ecmds.with_children(|p| { - create_item_stack_slot_data(is, &mut p.spawn_empty(), text_style, quantity, render_layer); + create_item_stack_slot_data(is, &mut p.spawn_empty(), text_style, quantity); }); } else { - create_item_stack_slot_data(is, ecmds, text_style, quantity, render_layer); + create_item_stack_slot_data(is, ecmds, text_style, quantity); } } } @@ -592,7 +565,6 @@ fn create_inventory_slot( slots: &mut ChildBuilder, item_stack: Option<&ItemStack>, text_style: TextFont, - render_layer: ItemRenderLayer, ) { let mut ecmds = slots.spawn(( Name::new("Inventory Item"), @@ -615,7 +587,7 @@ fn create_inventory_slot( if let Some(item_stack) = item_stack { ecmds.with_children(|p| { let mut ecmds = p.spawn_empty(); - create_item_stack_slot_data(item_stack, &mut ecmds, text_style, item_stack.quantity(), render_layer); + create_item_stack_slot_data(item_stack, &mut ecmds, text_style, item_stack.quantity()); }); } } @@ -664,7 +636,7 @@ fn pickup_item_into_cursor( let mut ecmds = commands.spawn(FollowCursor); - create_item_stack_slot_data(&new_is, &mut ecmds, text_style, pickup_quantity, ItemRenderLayer::Top); + create_item_stack_slot_data(&new_is, &mut ecmds, text_style, pickup_quantity); ecmds.insert((displayed_item, HeldItemStack(new_is))); @@ -854,13 +826,7 @@ fn handle_interactions( #[derive(Component)] pub struct TextNeedsTopRoot; -fn create_item_stack_slot_data( - item_stack: &ItemStack, - ecmds: &mut EntityCommands, - text_style: TextFont, - quantity: u16, - render_layer: ItemRenderLayer, -) { +fn create_item_stack_slot_data(item_stack: &ItemStack, ecmds: &mut EntityCommands, text_style: TextFont, quantity: u16) { ecmds .insert(( Name::new("Render Item"), @@ -872,7 +838,6 @@ fn create_item_stack_slot_data( align_items: AlignItems::FlexEnd, ..Default::default() }, - render_layer, InventoryRenderedItem, RenderItem { item_id: item_stack.item_id(), @@ -891,47 +856,6 @@ fn create_item_stack_slot_data( }); } -fn hide_hidden( - q_non_hotbar_slots: Query<(&ComputedNode, &GlobalTransform), With>, - q_parent: Query<&Parent>, - mut q_render_item: Query<(&ComputedNode, &mut Visibility, &Parent, &GlobalTransform), With>, -) { - for (node, mut vis, parent, g_trans) in q_render_item.iter_mut() { - // this is evil. - - // The node should be hidden if parent -> parent -> parent -> parent is a non-hotbar slots rendering. - - *vis = Visibility::Inherited; - - let one = parent.get(); - let Ok(two) = q_parent.get(one) else { - continue; - }; - let Ok(three) = q_parent.get(two.get()) else { - continue; - }; - let Ok(four) = q_parent.get(three.get()) else { - continue; - }; - - let maybe_non_hotbar = four.get(); - - let t = g_trans.translation(); - let this_logical_rect = Rect::from_center_size(Vec2::new(t.x, t.y), node.size()); - - let Ok((non_hotbar_node, g_trans)) = q_non_hotbar_slots.get(maybe_non_hotbar) else { - continue; - }; - - let t = g_trans.translation(); - let dims = Rect::from_center_size(Vec2::new(t.x, t.y), non_hotbar_node.size()); - - if !(dims.contains(this_logical_rect.min) || dims.contains(this_logical_rect.max)) { - *vis = Visibility::Hidden; - } - } -} - fn follow_cursor(mut query: Query<&mut Node, With>, primary_window_query: Query<&Window, With>) { let Some(Some(cursor_pos)) = primary_window_query.get_single().ok().map(|x| x.cursor_position()) else { return; // cursor is outside of window or the window was closed @@ -953,50 +877,6 @@ enum InventorySet { MoveWindows, } -fn rec_make_render_middle_camera( - ent: Entity, - q_mid_cam: &Query>, - q_children: &Query<&Children>, - q_target_cam: &mut Query<(Entity, &mut TargetCamera), (Changed, With)>, - commands: &mut Commands, -) { - let middle_camera_entity = q_mid_cam.single(); - - if let Ok((_, mut target_camera)) = q_target_cam.get_mut(ent) { - if target_camera.0 != middle_camera_entity { - target_camera.0 = middle_camera_entity; - } - if let Ok(children) = q_children.get(ent) { - for c in children.iter() { - rec_make_render_middle_camera(*c, q_mid_cam, q_children, q_target_cam, commands); - } - } - } else { - commands.entity(ent).insert(TargetCamera(middle_camera_entity)); - if let Ok(children) = q_children.get(ent) { - for c in children.iter() { - rec_make_render_middle_camera(*c, q_mid_cam, q_children, q_target_cam, commands); - } - } - } -} - -fn make_render_middle_camera( - q_mid_cam: Query>, - q_children: Query<&Children>, - mut q_target_cam: Query<(Entity, &mut TargetCamera), (Changed, With)>, - q_needs_target_cam: Query, With)>, - mut commands: Commands, -) { - for ent in q_target_cam.iter().map(|x| x.0).collect::>() { - rec_make_render_middle_camera(ent, &q_mid_cam, &q_children, &mut q_target_cam, &mut commands); - } - - for ent in q_needs_target_cam.iter() { - rec_make_render_middle_camera(ent, &q_mid_cam, &q_children, &mut q_target_cam, &mut commands); - } -} - pub(super) fn register(app: &mut App) { app.configure_sets( Update, @@ -1027,9 +907,7 @@ pub(super) fn register(app: &mut App) { on_update_inventory.in_set(InventorySet::UpdateInventory), handle_interactions.in_set(InventorySet::HandleInteractions), follow_cursor.in_set(InventorySet::FollowCursor), - (toggle_inventory_rendering, make_render_middle_camera, hide_hidden) - .chain() - .in_set(InventorySet::ToggleInventoryRendering), + toggle_inventory_rendering.in_set(InventorySet::ToggleInventoryRendering), reposition_window_children.in_set(InventorySet::MoveWindows), ) .in_set(NetworkingSystemsSet::Between) diff --git a/cosmos_client/src/netty/gameplay/receiver.rs b/cosmos_client/src/netty/gameplay/receiver.rs index c975e0a34..0c8c8769c 100644 --- a/cosmos_client/src/netty/gameplay/receiver.rs +++ b/cosmos_client/src/netty/gameplay/receiver.rs @@ -66,7 +66,6 @@ use crate::{ ui::{ crosshair::{CrosshairOffset, CrosshairOffsetSet}, message::{HudMessage, HudMessages}, - UiRoot, }, window::setup::CursorFlagsSet, }; @@ -466,7 +465,6 @@ pub(crate) fn client_sync_players( Name::new("Main Camera"), MainCamera, IsDefaultUiCamera, - UiRoot, AudioReceiver, )); }); diff --git a/cosmos_client/src/rendering/panorama/mod.rs b/cosmos_client/src/rendering/panorama/mod.rs index ba44d8bf0..6bf179127 100644 --- a/cosmos_client/src/rendering/panorama/mod.rs +++ b/cosmos_client/src/rendering/panorama/mod.rs @@ -3,7 +3,7 @@ use std::{f32::consts::PI, time::Duration}; use bevy::{ app::{App, Update}, math::{Quat, Vec3}, - prelude::{not, resource_exists, Commands, Component, Entity, IntoSystemConfigs, Or, Query, Res, ResMut, Resource, With}, + prelude::{not, resource_exists, Commands, Component, Entity, IntoSystemConfigs, Query, Res, ResMut, Resource, With}, render::view::{ screenshot::{save_to_disk, Screenshot}, Visibility, @@ -16,7 +16,7 @@ use cosmos_core::ecs::NeedsDespawned; use crate::{ input::inputs::{CosmosInputs, InputChecker, InputHandler}, - ui::{components::show_cursor::ShowCursor, item_renderer::RenderedItem}, + ui::components::show_cursor::ShowCursor, }; use super::MainCamera; @@ -36,7 +36,7 @@ fn take_panorama( inputs: InputChecker, mut commands: Commands, mut q_camera: Query<&mut Transform, With>, - mut q_ui_elements: Query<(Entity, &mut Visibility), Or<(With, With)>>, + mut q_ui_elements: Query<(Entity, &mut Visibility), With>, ) { if inputs.check_just_pressed(CosmosInputs::PanoramaScreenshot) { commands.insert_resource(PanAngle(0)); @@ -71,7 +71,7 @@ fn taking_panorama(mut pan_angle: ResMut, mut commands: Commands, mut fn restore_ui_after_panorama( mut commands: Commands, q_pan_locked: Query>, - mut q_ui_elements: Query<(Entity, &mut Visibility, &OldVis), Or<(With, With)>>, + mut q_ui_elements: Query<(Entity, &mut Visibility, &OldVis), With>, mut fin: ResMut, time: Res