summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--client/Cargo.toml3
-rw-r--r--client/src/camera.rs6
-rw-r--r--client/src/main.rs2
-rw-r--r--client/src/network.rs5
-rw-r--r--client/src/scene_prepare.rs10
-rw-r--r--client/src/ui.rs238
-rw-r--r--client/src/ui.wgsl43
-rw-r--r--world/src/mesh.rs3
9 files changed, 284 insertions, 27 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 904c78a..0f36056 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3008,6 +3008,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"audiopus",
+ "bytemuck",
"clap",
"cpal",
"egui",
diff --git a/client/Cargo.toml b/client/Cargo.toml
index de8bf01..a020315 100644
--- a/client/Cargo.toml
+++ b/client/Cargo.toml
@@ -18,4 +18,5 @@ rand = "0.9.0-beta.1"
glam = "0.29.2"
image = "0.25.5"
egui-wgpu = "0.30.0"
-egui = "0.30.0"
+egui = { version = "0.30.0", features = ["bytemuck"] }
+bytemuck = "1.21.0"
diff --git a/client/src/camera.rs b/client/src/camera.rs
index 3876e8a..d7a69c5 100644
--- a/client/src/camera.rs
+++ b/client/src/camera.rs
@@ -27,7 +27,7 @@ impl Camera {
pub fn new() -> Self {
Self {
aspect: 1.,
- fov: 0.5,
+ fov: 1.,
pos: Vec3::Z * 3.,
rot: Vec3::ZERO,
}
@@ -41,10 +41,6 @@ impl Camera {
Mat3::from_euler(EulerRot::YXZ, self.rot.x, self.rot.y, self.rot.z)
}
pub fn to_matrix(&self) -> Mat4 {
- // let tdir =
- // Mat3::from_euler(EulerRot::ZXY, self.rot.x, self.rot.y, self.rot.z) * Vec3::NEG_Z;
- // * Mat4::look_at_rh(self.pos, self.pos + tdir, Vec3::Y)
-
Mat4::perspective_infinite_reverse_rh(self.fov, self.aspect, 0.1)
* Mat4::from_mat3(self.rotation_mat().inverse())
* Mat4::from_translation(-self.pos)
diff --git a/client/src/main.rs b/client/src/main.rs
index 40c6e3e..0ee665c 100644
--- a/client/src/main.rs
+++ b/client/src/main.rs
@@ -23,7 +23,7 @@ pub mod scene_prepare;
pub mod scene_render;
pub mod state;
pub mod window;
-// pub mod ui;
+pub mod ui;
use anyhow::Result;
use clap::Parser;
diff --git a/client/src/network.rs b/client/src/network.rs
index 57b637a..73c4ab2 100644
--- a/client/src/network.rs
+++ b/client/src/network.rs
@@ -14,15 +14,14 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+use anyhow::Result;
+use log::{debug, info, warn};
use std::{
io::{BufReader, BufWriter, Write},
net::TcpStream,
sync::mpsc::{Receiver, Sender, channel},
thread::spawn,
};
-
-use anyhow::Result;
-use log::{debug, info, warn};
use weareshared::{helper::ReadWrite, packets::Packet};
pub struct Network {
diff --git a/client/src/scene_prepare.rs b/client/src/scene_prepare.rs
index 1807e39..f74277c 100644
--- a/client/src/scene_prepare.rs
+++ b/client/src/scene_prepare.rs
@@ -30,9 +30,9 @@ use weareshared::{
resources::{AttributeArray, Image, IndexArray, MeshPart, Prefab},
};
use wgpu::{
- BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource, Buffer,
- BufferUsages, Device, Extent3d, Queue, SamplerDescriptor, Texture, TextureDescriptor,
- TextureDimension, TextureFormat, TextureUsages, TextureViewDescriptor,
+ AddressMode, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource,
+ Buffer, BufferUsages, Device, Extent3d, FilterMode, Queue, SamplerDescriptor, Texture,
+ TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureViewDescriptor,
util::{BufferInitDescriptor, DeviceExt, TextureDataOrder},
};
@@ -299,6 +299,10 @@ fn create_texture(
);
let textureview = texture.create_view(&TextureViewDescriptor::default());
let sampler = device.create_sampler(&SamplerDescriptor {
+ address_mode_u: AddressMode::Repeat,
+ address_mode_v: AddressMode::Repeat,
+ mag_filter: FilterMode::Linear,
+ min_filter: FilterMode::Linear,
..Default::default()
});
let bindgroup = device.create_bind_group(&BindGroupDescriptor {
diff --git a/client/src/ui.rs b/client/src/ui.rs
index b99ae19..638978a 100644
--- a/client/src/ui.rs
+++ b/client/src/ui.rs
@@ -14,57 +14,267 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-use egui::{Context, epaint::Primitive};
-use wgpu::{Buffer, BufferDescriptor, BufferUsages, Device, Queue};
+use egui::{
+ Context, TextureId,
+ epaint::{Primitive, Vertex},
+};
+use std::{collections::HashMap, num::NonZeroU64};
+use wgpu::{
+ BindGroup, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState, Buffer,
+ BufferDescriptor, BufferUsages, Color, ColorTargetState, ColorWrites, CommandEncoder,
+ CompareFunction, DepthBiasState, DepthStencilState, Device, FragmentState, FrontFace,
+ IndexFormat, LoadOp, MultisampleState, Operations, PipelineCompilationOptions,
+ PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, PushConstantRange,
+ Queue, RenderPassColorAttachment, RenderPassDescriptor, RenderPipeline,
+ RenderPipelineDescriptor, SamplerBindingType, ShaderStages, StencilState, StoreOp, Texture,
+ TextureFormat, TextureSampleType, TextureView, TextureViewDimension, VertexAttribute,
+ VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, include_wgsl,
+};
pub struct UiRenderer {
ctx: Context,
+
+ pipeline: RenderPipeline,
+
+ index: Buffer,
+ vertex: Buffer,
+ textures: HashMap<TextureId, (BindGroup, Texture)>,
}
impl UiRenderer {
- pub fn new(device: &Device) -> Self {
+ pub fn new(device: &Device, format: TextureFormat) -> Self {
let index = device.create_buffer(&BufferDescriptor {
label: None,
- size: 1,
+ size: size_of::<f32>() as u64 * 1024 * 1024,
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let vertex = device.create_buffer(&BufferDescriptor {
label: None,
- size: 1,
+ size: size_of::<f32>() as u64 * 1024 * 1024,
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
+ let module = device.create_shader_module(include_wgsl!("ui.wgsl"));
+ let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
+ entries: &[
+ BindGroupLayoutEntry {
+ binding: 0,
+ count: None,
+ visibility: ShaderStages::FRAGMENT,
+ ty: BindingType::Texture {
+ sample_type: TextureSampleType::Float { filterable: true },
+ view_dimension: TextureViewDimension::D2,
+ multisampled: false,
+ },
+ },
+ BindGroupLayoutEntry {
+ binding: 1,
+ count: None,
+ visibility: ShaderStages::FRAGMENT,
+ ty: BindingType::Sampler(SamplerBindingType::Filtering),
+ },
+ ],
+ label: None,
+ });
+ let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
+ label: None,
+ bind_group_layouts: &[&bind_group_layout],
+ push_constant_ranges: &[PushConstantRange {
+ range: 0..(4 * 4 * size_of::<f32>() as u32),
+ stages: ShaderStages::VERTEX,
+ }],
+ });
+ let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
+ label: None,
+ layout: Some(&pipeline_layout),
+ fragment: Some(FragmentState {
+ module: &module,
+ entry_point: Some("fs_main"),
+ targets: &[Some(ColorTargetState {
+ blend: Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING),
+ format,
+ write_mask: ColorWrites::all(),
+ })],
+ compilation_options: PipelineCompilationOptions::default(),
+ }),
+ vertex: VertexState {
+ module: &module,
+ entry_point: Some("vs_main"),
+ buffers: &[VertexBufferLayout {
+ array_stride: size_of::<Vertex>() as u64,
+ step_mode: VertexStepMode::Vertex,
+ attributes: &[
+ VertexAttribute {
+ format: VertexFormat::Float32x2,
+ offset: 0,
+ shader_location: 0,
+ },
+ VertexAttribute {
+ format: VertexFormat::Float32x2,
+ offset: size_of::<f32>() as u64 * 2,
+ shader_location: 1,
+ },
+ VertexAttribute {
+ format: VertexFormat::Float32x3,
+ offset: size_of::<f32>() as u64 * 4,
+ shader_location: 2,
+ },
+ ],
+ }],
+ compilation_options: PipelineCompilationOptions::default(),
+ },
+ primitive: PrimitiveState {
+ topology: PrimitiveTopology::TriangleList,
+ front_face: FrontFace::Ccw,
+ cull_mode: None, //Some(Face::Back),
+ polygon_mode: PolygonMode::Fill,
+ ..Default::default()
+ },
+ depth_stencil: Some(DepthStencilState {
+ depth_compare: CompareFunction::Greater,
+ depth_write_enabled: true,
+ format: TextureFormat::Depth32Float,
+ bias: DepthBiasState::default(),
+ stencil: StencilState::default(),
+ }),
+ multisample: MultisampleState::default(),
+ multiview: None,
+ cache: None,
+ });
Self {
ctx: Context::default(),
+ pipeline,
index,
vertex,
+ textures: HashMap::new(),
}
}
- pub fn draw(&self, queue: &Queue) {
+ pub fn create_texture() {
+ // let texture = device.create_texture_with_data(
+ // &queue,
+ // &TextureDescriptor {
+ // label: None,
+ // size: Extent3d {
+ // depth_or_array_layers: 1,
+ // width,
+ // height,
+ // },
+ // mip_level_count: 1,
+ // sample_count: 1,
+ // dimension: TextureDimension::D2,
+ // format: TextureFormat::Rgba8UnormSrgb,
+ // usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
+ // view_formats: &[],
+ // },
+ // TextureDataOrder::LayerMajor,
+ // data,
+ // );
+ // let textureview = texture.create_view(&TextureViewDescriptor::default());
+ // let sampler = device.create_sampler(&SamplerDescriptor {
+ // ..Default::default()
+ // });
+ // let bindgroup = device.create_bind_group(&BindGroupDescriptor {
+ // label: None,
+ // layout: &bgl,
+ // entries: &[
+ // BindGroupEntry {
+ // binding: 0,
+ // resource: BindingResource::TextureView(&textureview),
+ // },
+ // BindGroupEntry {
+ // binding: 1,
+ // resource: BindingResource::Sampler(&sampler),
+ // },
+ // ],
+ // });
+ }
+
+ pub fn draw(&self, queue: &Queue, commands: &mut CommandEncoder, target: &TextureView) {
let raw_input = egui::RawInput::default();
let full_output = self.ctx.run(raw_input, |ctx| {
egui::CentralPanel::default().show(&ctx, |ui| {
ui.label("Hello world!");
- if ui.button("Click me").clicked() {
- // take some action here
- }
+ ui.button("Click me").clicked();
});
});
+ for (texid, delta) in full_output.textures_delta.set {}
- // handle_platform_output(full_output.platform_output);
let clipped_primitives = self
.ctx
.tessellate(full_output.shapes, full_output.pixels_per_point);
+ let mut index_count = 0;
+ let mut vertex_count = 0;
+ for p in &clipped_primitives {
+ if let Primitive::Mesh(mesh) = &p.primitive {
+ vertex_count += mesh.vertices.len();
+ index_count += mesh.vertices.len();
+ }
+ }
+
+ // TODO realloc buffers if overflowing
+
+ let mut mapped_index = queue
+ .write_buffer_with(
+ &self.index,
+ 0,
+ NonZeroU64::new((size_of::<u32>() * index_count) as u64).unwrap(),
+ )
+ .expect("ui index buffer overflow");
+ let mut mapped_vertex = queue
+ .write_buffer_with(
+ &self.index,
+ 0,
+ NonZeroU64::new((size_of::<Vertex>() * vertex_count) as u64).unwrap(),
+ )
+ .expect("ui vertex buffer overflow");
+
+ let mut index_offset = 0;
+ let mut vertex_offset = 0;
+ let mut slices = Vec::new();
for p in clipped_primitives {
- match p.primitive {
- Primitive::Mesh(mesh) => {}
- _ => unreachable!(),
+ if let Primitive::Mesh(mesh) = p.primitive {
+ mapped_index[index_offset..index_offset + (mesh.indices.len() * size_of::<u32>())]
+ .copy_from_slice(bytemuck::cast_slice(&mesh.indices));
+ mapped_vertex
+ [vertex_offset..vertex_offset + (mesh.vertices.len() * size_of::<Vertex>())]
+ .copy_from_slice(bytemuck::cast_slice(&mesh.vertices));
+ index_offset += mesh.indices.len();
+ vertex_offset += mesh.vertices.len();
+ slices.push((
+ index_offset as u32..index_offset as u32 + mesh.indices.len() as u32,
+ vertex_offset as i32,
+ ));
}
}
- // paint(full_output.textures_delta, clipped_primitives);
+
+ let mut rpass = commands.begin_render_pass(&RenderPassDescriptor {
+ label: None,
+ color_attachments: &[Some(RenderPassColorAttachment {
+ view: target,
+ resolve_target: None,
+ ops: Operations {
+ store: StoreOp::Store,
+ load: LoadOp::Clear(Color {
+ r: 0.01,
+ g: 0.01,
+ b: 0.01,
+ a: 1.,
+ }),
+ },
+ })],
+ ..Default::default()
+ });
+
+ rpass.set_pipeline(&self.pipeline);
+ rpass.set_index_buffer(self.index.slice(..), IndexFormat::Uint32);
+ rpass.set_vertex_buffer(0, self.vertex.slice(..));
+ for (index, base_vertex) in slices {
+ // rpass.set_bind_group(0, &self.bind_group, &[]);
+ rpass.draw_indexed(index, base_vertex, 0..1);
+ }
}
}
diff --git a/client/src/ui.wgsl b/client/src/ui.wgsl
new file mode 100644
index 0000000..174f20c
--- /dev/null
+++ b/client/src/ui.wgsl
@@ -0,0 +1,43 @@
+// wearechat - generic multiplayer game with voip
+// Copyright (C) 2025 metamuffin
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, version 3 of the License only.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+struct VertexIn {
+ @location(0) pos: vec2<f32>,
+ @location(1) uv: vec2<f32>,
+ @location(2) color: vec3<f32>,
+}
+struct VertexOut {
+ @builtin(position) clip: vec4<f32>,
+ @location(0) uv: vec2<f32>,
+ @location(1) color: vec3<f32>,
+}
+
+@group(0) @binding(0) var texture: texture_2d<f32>;
+@group(0) @binding(1) var texture_sampler: sampler;
+var<push_constant> project: mat4x4<f32>;
+
+@vertex
+fn vs_main(vi: VertexIn) -> VertexOut {
+ var clip = project * vec4(vi.pos, 0., -1.);
+ clip /= clip.w;
+ clip.x *= -1.;
+ clip.y *= -1.;
+ let vo = VertexOut(clip, vi.uv, vi.color);
+ return vo;
+}
+@fragment
+fn fs_main(vo: VertexOut) -> @location(0) vec4<f32> {
+ return textureSample(texture, texture_sampler, vo.uv) * vec4(vo.color, 1.);
+}
diff --git a/world/src/mesh.rs b/world/src/mesh.rs
index 7d17cc9..c621753 100644
--- a/world/src/mesh.rs
+++ b/world/src/mesh.rs
@@ -314,6 +314,9 @@ pub fn import_mesh(
att
});
let g_refractive_index = p.material().ior();
+ if let Some(i) = g_refractive_index {
+ info!("refractive index is {i}");
+ }
let g_thickness = p.material().volume().map(|v| v.thickness_factor());
let name = mesh.name().map(|e| e.to_owned());