From 47e5b44576e581ae0b62ad1e3bed444b8a82cefd Mon Sep 17 00:00:00 2001 From: metamuffin Date: Fri, 10 Jan 2025 15:35:23 +0100 Subject: spawn ui in world with click --- client/src/camera.rs | 8 +++- client/src/renderer.rs | 2 +- client/src/state.rs | 34 +++++++++++-- client/src/ui.rs | 126 +++++++++++++++++++++++++++++++++++++------------ client/src/ui.wgsl | 2 +- client/src/window.rs | 8 +++- 6 files changed, 142 insertions(+), 38 deletions(-) (limited to 'client/src') diff --git a/client/src/camera.rs b/client/src/camera.rs index f5a911b..9228482 100644 --- a/client/src/camera.rs +++ b/client/src/camera.rs @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -use glam::{EulerRot, Mat3, Mat4, Vec2, Vec3, vec3}; +use glam::{Affine3A, EulerRot, Mat3, Mat4, Vec2, Vec3, vec3}; pub struct Camera { pos: Vec3, @@ -45,4 +45,10 @@ impl Camera { * Mat4::from_mat3(self.rotation_mat().inverse()) * Mat4::from_translation(-self.pos) } + pub fn new_ui_affine(&self) -> Affine3A { + Affine3A::from_mat3_translation( + self.rotation_mat(), + self.pos + self.rotation_mat() * vec3(0., 0., -3.), + ) + } } diff --git a/client/src/renderer.rs b/client/src/renderer.rs index 7bd98f0..1fa6991 100644 --- a/client/src/renderer.rs +++ b/client/src/renderer.rs @@ -36,7 +36,7 @@ pub struct Renderer<'a> { device: Arc, surface_configuration: SurfaceConfiguration, scene_pipeline: ScenePipeline, - ui_renderer: UiRenderer, + pub ui_renderer: UiRenderer, pub scene_prepare: ScenePreparer, surface_needs_reconfigure: bool, depth: TextureView, diff --git a/client/src/state.rs b/client/src/state.rs index b0d19e6..70746d8 100644 --- a/client/src/state.rs +++ b/client/src/state.rs @@ -14,13 +14,17 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -use crate::{camera::Camera, download::Downloader, network::Network, renderer::Renderer}; +use crate::{ + camera::Camera, download::Downloader, network::Network, renderer::Renderer, ui::UiSurface, +}; use anyhow::{Context, Result}; +use egui::ViewportId; use glam::{Vec2, Vec3}; use log::{info, warn}; -use std::{net::TcpStream, time::Instant}; +use rand::random; +use std::{net::TcpStream, sync::Arc, time::Instant}; use weareshared::{store::ResourceStore, tree::SceneTree}; -use winit::window::Window; +use winit::event::MouseButton; pub struct State<'a> { pub network: Network, @@ -35,10 +39,11 @@ pub struct DeltaState { time: Instant, pub move_dir: Vec3, pub mouse_acc: Vec2, + pub cursor_pos: Vec2, } impl<'a> State<'a> { - pub fn new(conn: TcpStream, window: &'a Window) -> Result> { + pub fn new(conn: TcpStream, window: &'a winit::window::Window) -> Result> { info!("new state"); Ok(Self { camera: Camera::new(), @@ -50,6 +55,7 @@ impl<'a> State<'a> { time: Instant::now(), move_dir: Vec3::ZERO, mouse_acc: Vec2::ZERO, + cursor_pos: Vec2::ZERO, }, }) } @@ -62,6 +68,26 @@ impl<'a> State<'a> { self.renderer.resize(width, height); self.camera.aspect = width as f32 / height as f32; } + pub fn click(&mut self, button: MouseButton, down: bool) { + if !down || button != MouseButton::Right { + return; + } + self.renderer.ui_renderer.surfaces.insert( + ViewportId::from_hash_of(random::()), + UiSurface { + transform: self.camera.new_ui_affine(), + content: Arc::new(|ctx| { + egui::Window::new("Funny window") + .default_open(true) + .show(ctx, |ui| { + ui.label("world space ui actually kinda works now"); + ui.label("Does input work?"); + ui.button("Yes").clicked(); + }); + }), + }, + ); + } pub fn update(&mut self) -> Result<()> { let now = Instant::now(); let dt = (now - self.delta.time).as_secs_f32(); diff --git a/client/src/ui.rs b/client/src/ui.rs index c3e2e38..e2ba989 100644 --- a/client/src/ui.rs +++ b/client/src/ui.rs @@ -15,22 +15,23 @@ along with this program. If not, see . */ use egui::{ - Context, ImageData, TextureId, + Context, ImageData, TextureId, ViewportBuilder, ViewportId, ViewportInfo, epaint::{Primitive, Vertex}, }; -use glam::{Mat2, Mat3, Mat4}; +use glam::{Affine3A, Mat2, Mat3, Mat4}; use log::info; -use std::{collections::HashMap, num::NonZeroU64}; +use std::{collections::HashMap, num::NonZeroU64, sync::Arc}; use wgpu::{ AddressMode, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BlendState, Buffer, BufferDescriptor, BufferUsages, ColorTargetState, ColorWrites, CommandEncoder, CompareFunction, DepthStencilState, Device, Extent3d, FilterMode, FragmentState, FrontFace, - IndexFormat, LoadOp, MultisampleState, Operations, PipelineCompilationOptions, - PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, PushConstantRange, - Queue, RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, - RenderPipeline, RenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor, ShaderStages, - StoreOp, Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, + ImageCopyTexture, ImageDataLayout, IndexFormat, LoadOp, MultisampleState, Operations, Origin3d, + PipelineCompilationOptions, PipelineLayoutDescriptor, PolygonMode, PrimitiveState, + PrimitiveTopology, PushConstantRange, Queue, RenderPassColorAttachment, + RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipeline, + RenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor, ShaderStages, StoreOp, + Texture, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages, TextureView, TextureViewDescriptor, TextureViewDimension, VertexBufferLayout, VertexState, VertexStepMode, include_wgsl, util::{DeviceExt, TextureDataOrder}, @@ -45,7 +46,14 @@ pub struct UiRenderer { index: Buffer, vertex: Buffer, - textures: HashMap, + textures: HashMap, + + pub surfaces: HashMap, +} + +pub struct UiSurface { + pub transform: Affine3A, + pub content: Arc, } impl UiRenderer { @@ -140,6 +148,7 @@ impl UiRenderer { vertex, bind_group_layout, textures: HashMap::new(), + surfaces: HashMap::new(), } } @@ -154,19 +163,65 @@ impl UiRenderer { depth: &TextureView, projection: Mat4, ) { - let raw_input = egui::RawInput::default(); + let mut raw_input = egui::RawInput::default(); + raw_input.viewport_id = self + .surfaces + .keys() + .next() + .copied() + .unwrap_or(ViewportId::ROOT); + raw_input.viewports = self + .surfaces + .keys() + .chain(Some(ViewportId::ROOT).iter()) + .map(|key| { + (*key, ViewportInfo { + native_pixels_per_point: Some(2.), + ..Default::default() + }) + }) + .collect(); + let full_output = self.ctx.run(raw_input, |ctx| { - egui::CentralPanel::default().show(&ctx, |ui| { - ui.label("Hello world!"); - ui.label("I dont think this will ever work..."); - ui.button("Click me").clicked(); - }); + for (id, surf) in &self.surfaces { + ctx.show_viewport_immediate(*id, ViewportBuilder::default(), |ctx, _class| { + (surf.content)(ctx) + }); + } }); for (texid, delta) in full_output.textures_delta.set { - if let Some((texbg, tex)) = self.textures.get_mut(&texid) { - drop((texbg, tex)); - todo!() + let size = Extent3d { + depth_or_array_layers: 1, + width: delta.image.width() as u32, + height: delta.image.height() as u32, + }; + let pixels = match &delta.image { + ImageData::Color(color_image) => color_image.pixels.clone(), + ImageData::Font(font_image) => font_image.srgba_pixels(None).collect(), + }; + + if let Some((_texbg, tex, texsize)) = self.textures.get_mut(&texid) { + let pos = delta.pos.unwrap_or([0, 0]); + queue.write_texture( + ImageCopyTexture { + texture: &tex, + mip_level: 0, + origin: Origin3d { + x: pos[0] as u32, + y: pos[1] as u32, + z: 0, + }, + aspect: TextureAspect::All, + }, + bytemuck::cast_slice::<_, u8>(&pixels), + ImageDataLayout { + offset: 0, + bytes_per_row: Some(texsize[0] * 4), + rows_per_image: None, + }, + size, + ); } else { assert_eq!( delta.pos, None, @@ -177,20 +232,12 @@ impl UiRenderer { delta.image.width(), delta.image.height() ); - let pixels = match &delta.image { - ImageData::Color(color_image) => color_image.pixels.clone(), - ImageData::Font(font_image) => font_image.srgba_pixels(None).collect(), - }; let texture = device.create_texture_with_data( &queue, &TextureDescriptor { label: None, - size: Extent3d { - depth_or_array_layers: 1, - width: delta.image.width() as u32, - height: delta.image.height() as u32, - }, + size, mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, @@ -223,7 +270,10 @@ impl UiRenderer { }, ], }); - self.textures.insert(texid, (bindgroup, texture)); + self.textures.insert( + texid, + (bindgroup, texture, delta.image.size().map(|e| e as u32)), + ); } } @@ -240,7 +290,11 @@ impl UiRenderer { } } - // TODO realloc buffers if overflowing + if index_count == 0 || vertex_count == 0 { + return; + } + + // TODO realloc buffers and retry if overflowing let mut mapped_index = queue .write_buffer_with( @@ -302,8 +356,20 @@ impl UiRenderer { ..Default::default() }); + let affine = self + .surfaces + .values() + .next() + .map(|s| s.transform) + .unwrap_or(Affine3A::IDENTITY); + + let scale = 0.03; let projection = projection - * Mat4::from_mat3(Mat3::from_mat2(Mat2::from_cols_array(&[1., 0., 0., -1.]))); + * Mat4::from_mat3a(affine.matrix3) + * Mat4::from_translation(affine.translation.into()) + * Mat4::from_mat3(Mat3::from_mat2(Mat2::from_cols_array(&[ + scale, 0., 0., -scale, + ]))); let projection = projection.to_cols_array().map(|v| v.to_le_bytes()); diff --git a/client/src/ui.wgsl b/client/src/ui.wgsl index 6361782..4f55e25 100644 --- a/client/src/ui.wgsl +++ b/client/src/ui.wgsl @@ -46,5 +46,5 @@ fn vs_main(@builtin(vertex_index) vindex: u32, vi: VertexIn) -> VertexOut { } @fragment fn fs_main(vo: VertexOut) -> @location(0) vec4 { - return textureSample(texture, texture_sampler, vo.uv) * vo.color; + return pow(textureSample(texture, texture_sampler, vo.uv) * vo.color, vec4(2.2)); } diff --git a/client/src/window.rs b/client/src/window.rs index 72658e5..7da0d74 100644 --- a/client/src/window.rs +++ b/client/src/window.rs @@ -15,7 +15,7 @@ along with this program. If not, see . */ use crate::state::State; -use glam::Vec3; +use glam::{Vec3, vec2}; use log::{info, warn}; use std::net::TcpStream; use winit::{ @@ -96,6 +96,12 @@ impl ApplicationHandler for WindowState { ElementState::Released => -1., }; } + WindowEvent::MouseInput { state, button, .. } => { + sta.click(button, state.is_pressed()); + } + WindowEvent::CursorMoved { position, .. } => { + sta.delta.cursor_pos = vec2(position.x as f32, position.y as f32); + } WindowEvent::CloseRequested => { event_loop.exit(); } -- cgit v1.2.3-70-g09d2