diff options
-rw-r--r-- | client/src/download.rs | 17 | ||||
-rw-r--r-- | client/src/scene_prepare.rs | 52 | ||||
-rw-r--r-- | client/src/scene_render.rs | 6 | ||||
-rw-r--r-- | doc/protocol.md | 26 | ||||
-rw-r--r-- | server/src/main.rs | 17 | ||||
-rw-r--r-- | shared/src/packets.rs | 99 | ||||
-rw-r--r-- | shared/src/resources.rs | 73 | ||||
-rw-r--r-- | shared/src/store.rs | 18 | ||||
-rw-r--r-- | shared/src/tree.rs | 11 | ||||
-rw-r--r-- | world/src/main.rs | 267 | ||||
-rw-r--r-- | world/src/mesh.rs | 262 |
11 files changed, 502 insertions, 346 deletions
diff --git a/client/src/download.rs b/client/src/download.rs index 95f9cc7..e65ef25 100644 --- a/client/src/download.rs +++ b/client/src/download.rs @@ -17,9 +17,9 @@ use crate::network::Network; use anyhow::Result; use log::debug; -use std::collections::HashSet; +use std::{collections::HashSet, marker::PhantomData}; use weareshared::{ - packets::{Packet, Resource}, + packets::{Packet, ReadWrite, Resource}, store::{ResourceStore, sha256}, }; @@ -39,9 +39,14 @@ impl Downloader { store, } } - pub fn try_get(&mut self, hash: Resource) -> Result<Option<Vec<u8>>> { + pub fn try_get<T: ReadWrite>(&mut self, hash: Resource<T>) -> Result<Option<T>> { + self.try_get_raw(Resource(hash.0, PhantomData))? + .map(|x| T::read(&mut x.as_slice())) + .transpose() + } + pub fn try_get_raw(&mut self, hash: Resource) -> Result<Option<Vec<u8>>> { if self.have.contains(&hash) { - self.store.get(hash) + self.store.get_raw(hash) } else { self.need.insert(hash); Ok(None) @@ -50,8 +55,8 @@ impl Downloader { pub fn packet(&mut self, p: &Packet) -> Result<()> { match p { Packet::RespondResource(d) => { - let key = Resource(sha256(&d.0)); - self.store.set(&d.0)?; + let key = Resource(sha256(&d.0), PhantomData); + self.store.set_raw(&d.0)?; self.need.remove(&key); self.pending.remove(&key); if self.have.insert(key) { diff --git a/client/src/scene_prepare.rs b/client/src/scene_prepare.rs index 6e4cb51..1807e39 100644 --- a/client/src/scene_prepare.rs +++ b/client/src/scene_prepare.rs @@ -15,7 +15,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ use crate::download::Downloader; -use anyhow::{Context, Result}; +use anyhow::Result; use image::ImageReader; use log::debug; use std::{ @@ -26,8 +26,8 @@ use std::{ }; use weareshared::{ Affine3A, - packets::{ReadWrite, Resource}, - resources::{MeshPart, Prefab}, + packets::Resource, + resources::{AttributeArray, Image, IndexArray, MeshPart, Prefab}, }; use wgpu::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource, Buffer, @@ -66,13 +66,13 @@ pub struct ScenePreparer { queue: Arc<Queue>, texture_bgl: BindGroupLayout, - textures: DemandMap<Resource, (Arc<Texture>, Arc<BindGroup>)>, + textures: DemandMap<Resource<Image>, (Arc<Texture>, Arc<BindGroup>)>, placeholder_textures: DemandMap<(), (Arc<Texture>, Arc<BindGroup>)>, - index_buffers: DemandMap<Resource, (Arc<Buffer>, u32)>, - vertex_buffers: DemandMap<Resource, (Arc<Buffer>, u32)>, + index_buffers: DemandMap<Resource<IndexArray>, (Arc<Buffer>, u32)>, + vertex_buffers: DemandMap<Resource<AttributeArray>, (Arc<Buffer>, u32)>, placeholder_vertex_buffers: DemandMap<(u32, bool), Arc<Buffer>>, - mesh_parts: DemandMap<Resource, Arc<RMeshPart>>, - pub prefabs: DemandMap<Resource, Arc<RPrefab>>, + mesh_parts: DemandMap<Resource<MeshPart>, Arc<RMeshPart>>, + pub prefabs: DemandMap<Resource<Prefab>, Arc<RPrefab>>, } pub struct RPrefab(pub Vec<(Affine3A, Arc<RMeshPart>)>); @@ -102,28 +102,26 @@ impl ScenePreparer { } pub fn update(&mut self, dls: &mut Downloader) -> Result<()> { for pres in self.prefabs.needed.clone() { - if let Some(buf) = dls.try_get(pres)? { - let prefab = Prefab::read(&mut buf.as_slice()).context("parsing prefab")?; + if let Some(prefab) = dls.try_get(pres.clone())? { let mut rprefab = RPrefab(Vec::new()); for (aff, partres) in &prefab.mesh { - if let Some(part) = self.mesh_parts.try_get(*partres) { + if let Some(part) = self.mesh_parts.try_get(partres.clone()) { rprefab.0.push((*aff, part.clone())); } } if rprefab.0.len() == prefab.mesh.len() { - self.prefabs.insert(pres, Arc::new(rprefab)); + self.prefabs.insert(pres.clone(), Arc::new(rprefab)); debug!("prefab created ({pres})"); } } } for pres in self.index_buffers.needed.clone() { - if let Some(buf) = dls.try_get(pres)? { + if let Some(buf) = dls.try_get(pres.clone())? { let buf = buf + .0 .into_iter() - .array_chunks::<2>() - .map(u16::from_be_bytes) - .map(u16::to_le_bytes) .flatten() + .flat_map(u16::to_le_bytes) .collect::<Vec<_>>(); let buffer = self.device.create_buffer_init(&BufferInitDescriptor { contents: &buf, @@ -131,16 +129,15 @@ impl ScenePreparer { usage: BufferUsages::INDEX | BufferUsages::COPY_DST, }); self.index_buffers - .insert(pres, (Arc::new(buffer), (buf.len() / 2) as u32)); + .insert(pres.clone(), (Arc::new(buffer), (buf.len() / 2) as u32)); debug!("index buffer created (len={}) {pres}", buf.len() / 2); } } for pres in self.vertex_buffers.needed.clone() { - if let Some(buf) = dls.try_get(pres)? { + if let Some(buf) = dls.try_get(pres.clone())? { let buf = buf + .0 .into_iter() - .array_chunks::<4>() - .map(f32::from_be_bytes) .map(f32::to_le_bytes) .flatten() .collect::<Vec<_>>(); @@ -150,7 +147,7 @@ impl ScenePreparer { usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, }); self.vertex_buffers - .insert(pres, (Arc::new(buffer), (buf.len() / 4) as u32)); + .insert(pres.clone(), (Arc::new(buffer), (buf.len() / 4) as u32)); debug!( "vertex attribute buffer created (len={}) {pres}", buf.len() / 4 @@ -158,8 +155,8 @@ impl ScenePreparer { } } for pres in self.textures.needed.clone() { - if let Some(buf) = dls.try_get(pres)? { - let image = ImageReader::new(Cursor::new(buf)).with_guessed_format()?; + if let Some(buf) = dls.try_get(pres.clone())? { + let image = ImageReader::new(Cursor::new(buf.0)).with_guessed_format()?; let image = image.decode()?; let image = image.to_rgba8(); let image_raw = image.to_vec(); @@ -171,7 +168,7 @@ impl ScenePreparer { image.width(), image.height(), ); - self.textures.insert(pres, tex_bg); + self.textures.insert(pres.clone(), tex_bg); } } for pres in self.placeholder_textures.needed.clone() { @@ -186,11 +183,10 @@ impl ScenePreparer { self.placeholder_textures.insert(pres, tex_bg); } for pres in self.mesh_parts.needed.clone() { - if let Some(buf) = dls.try_get(pres)? { - let part = MeshPart::read(&mut buf.as_slice()).context("parsing part")?; - + if let Some(part) = dls.try_get(pres.clone())? { if let (Some(indexres), Some(positionres)) = (part.index, part.va_position) { - let Some((index, index_count)) = self.index_buffers.try_get(indexres) else { + let Some((index, index_count)) = self.index_buffers.try_get(indexres.clone()) + else { self.index_buffers.needed.insert(indexres); continue; }; diff --git a/client/src/scene_render.rs b/client/src/scene_render.rs index d619f3d..6ec5f25 100644 --- a/client/src/scene_render.rs +++ b/client/src/scene_render.rs @@ -16,7 +16,7 @@ */ use glam::{EulerRot, Mat3, Mat4}; use std::sync::Arc; -use weareshared::{packets::Resource, tree::SceneTree}; +use weareshared::{packets::Resource, resources::Prefab, tree::SceneTree}; use wgpu::{ BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState, Color, ColorTargetState, ColorWrites, CommandEncoder, CompareFunction, DepthBiasState, @@ -164,7 +164,7 @@ impl ScenePipeline { commands: &mut CommandEncoder, target: &TextureView, scene: &SceneTree, - prefabs: &mut DemandMap<Resource, Arc<RPrefab>>, + prefabs: &mut DemandMap<Resource<Prefab>, Arc<RPrefab>>, camera: Mat4, ) { let mut rpass = commands.begin_render_pass(&RenderPassDescriptor { @@ -202,7 +202,7 @@ impl ScenePipeline { ob.rot.z, )) * Mat4::from_translation(ob.pos.into()); - if let Some(prefab) = prefabs.try_get(ob.res) { + if let Some(prefab) = prefabs.try_get(ob.res.clone()) { for (affine, part) in &prefab.0 { let part_projection = prefab_projection * Mat4::from_mat3a(affine.matrix3) diff --git a/doc/protocol.md b/doc/protocol.md index cd4694e..bb20a05 100644 --- a/doc/protocol.md +++ b/doc/protocol.md @@ -14,20 +14,22 @@ type. The following bytes are the parameters. `Vec<T>` is stored as u32 element count followed by the elements. `Res<T>` is used to denote 256-bit resource hash referring to data of type T. `Obj` refers -128-bit game objects IDs. +128-bit game objects IDs. `String` is stored like `Vec<u8>` but contains UTF-8 +encoded text. ```rs -00 connect(identity: u128) -ff disconnect() -01 request_resource(name: Res) -02 respond_resource(data: Vec<u8>) -03 add(id: Obj, prefab: Res<Prefab>) -04 remove(id: Obj) -05 position(id: Obj, pos: Vec3, rot: Vec3) -06 pose(id: Obj, params: Vec<f32>) -07 parent(parent: Obj, child: Obj) -08 sound(id: Obj, data: Vec<u8>) -09 prefab_index(res: Res) +0x00 connect(identity: u128) +0xff disconnect() +0x01 request_resource(name: Res) +0x02 respond_resource(data: Vec<u8>) +0x03 add(id: Obj, prefab: Res<Prefab>) +0x04 remove(id: Obj) +0x05 position(id: Obj, pos: Vec3, rot: Vec3) +0x06 pose(id: Obj, params: Vec<f32>) +0x07 parent(parent: Obj, child: Obj) +0x08 sound(id: Obj, data: Vec<u8>) +0x09 prefab_index(res: Res) +0x0a prefab_name(res: Res, name: String) ``` ## Resources diff --git a/server/src/main.rs b/server/src/main.rs index a293d15..03ebb82 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -23,6 +23,7 @@ use network::ServerNetwork; use std::net::{IpAddr, SocketAddr}; use weareshared::{ packets::{Data, Packet}, + resources::PrefabIndex, store::ResourceStore, tree::SceneTree, }; @@ -38,6 +39,7 @@ struct Args { struct State { store: ResourceStore, tree: SceneTree, + prefab_index: PrefabIndex, } fn main() -> Result<()> { @@ -63,6 +65,7 @@ impl State { &xdg::BaseDirectories::with_prefix("weareserver")?.place_cache_file("resources")?, )?, tree: SceneTree::default(), + prefab_index: PrefabIndex::default(), }) } pub fn handle_packet(&mut self, conn: u128, packet: Packet, net: &ServerNetwork) -> Result<()> { @@ -75,7 +78,7 @@ impl State { } Packet::Disconnect => {} Packet::RequestResource(resource) => { - if let Some(r) = self.store.get(resource)? { + if let Some(r) = self.store.get_raw(resource)? { debug!("resource is cached"); net.send(conn, Packet::RespondResource(Data(r)), true); } else { @@ -84,7 +87,7 @@ impl State { } } Packet::RespondResource(data) => { - self.store.set(&data.0)?; + self.store.set_raw(&data.0)?; net.broadcast(Packet::RespondResource(data), true); } Packet::Add(object, resource) => { @@ -105,6 +108,16 @@ impl State { Packet::Sound(object, vec) => { net.broadcast(Packet::Sound(object, vec), true); } + Packet::PrefabIndex(_) => { + // ok + } + Packet::PrefabName(resource, name) => { + self.prefab_index.0.insert(name, resource); + net.broadcast( + Packet::PrefabIndex(self.store.set(&self.prefab_index)?), + true, + ); + } } Ok(()) } diff --git a/shared/src/packets.rs b/shared/src/packets.rs index 9cecbe2..1d3155f 100644 --- a/shared/src/packets.rs +++ b/shared/src/packets.rs @@ -14,15 +14,31 @@ 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 crate::resources::{Prefab, PrefabIndex}; use anyhow::{Result, bail}; use glam::Vec3A; use std::{ fmt::{Debug, Display}, + hash::Hash, io::{Read, Write}, + marker::PhantomData, }; -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct Resource(pub [u8; 32]); +#[derive(Clone, Copy)] +pub struct Resource<T = ()>(pub [u8; 32], pub PhantomData<T>); + +impl<T> PartialEq for Resource<T> { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl<T> Eq for Resource<T> {} +impl<T> Hash for Resource<T> { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.0.hash(state); + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Object(pub u128); @@ -35,12 +51,14 @@ pub enum Packet { Disconnect, RequestResource(Resource), RespondResource(Data), - Add(Object, Resource), + Add(Object, Resource<Prefab>), Remove(Object), Position(Object, Vec3A, Vec3A), Pose(Object, Vec<f32>), Parent(Object, Object), Sound(Object, Data), + PrefabIndex(Resource<PrefabIndex>), + PrefabName(Resource, String), } pub trait ReadWrite: Sized { @@ -84,17 +102,13 @@ impl Packet { } Packet::Remove(object) => { w.write_all(&[0x04])?; - w.write_all(&object.0.to_be_bytes())?; + object.write(w)?; } Packet::Position(object, pos, rot) => { w.write_all(&[0x05])?; w.write_all(&object.0.to_be_bytes())?; - w.write_all(&pos.x.to_be_bytes())?; - w.write_all(&pos.y.to_be_bytes())?; - w.write_all(&pos.z.to_be_bytes())?; - w.write_all(&rot.x.to_be_bytes())?; - w.write_all(&rot.y.to_be_bytes())?; - w.write_all(&rot.z.to_be_bytes())?; + pos.write(w)?; + rot.write(w)?; } Packet::Pose(object, vec) => { w.write_all(&[0x06])?; @@ -111,6 +125,15 @@ impl Packet { w.write_all(&object.0.to_be_bytes())?; data.write(w)?; } + Packet::PrefabIndex(resource) => { + w.write_all(&[0x09])?; + resource.write(w)?; + } + Packet::PrefabName(resource, name) => { + w.write_all(&[0x0a])?; + resource.write(w)?; + name.write(w)?; + } } Ok(()) } @@ -154,21 +177,42 @@ fn read_u128(r: &mut dyn Read) -> Result<u128> { r.read_exact(&mut buf)?; Ok(u128::from_be_bytes(buf)) } -impl ReadWrite for Resource { +impl<T> ReadWrite for Resource<T> { fn write(&self, w: &mut dyn Write) -> Result<()> { w.write_all(&self.0)?; Ok(()) } fn read(r: &mut dyn Read) -> Result<Self> { - let mut s = Self([0; 32]); + let mut s = Self([0; 32], PhantomData); r.read_exact(&mut s.0)?; Ok(s) } } +impl ReadWrite for Object { + fn write(&self, w: &mut dyn Write) -> Result<()> { + w.write_all(&self.0.to_be_bytes())?; + Ok(()) + } + + fn read(r: &mut dyn Read) -> Result<Self> { + let mut s = [0; 16]; + r.read_exact(&mut s)?; + Ok(Object(u128::from_be_bytes(s))) + } +} + impl ReadWrite for Data { fn write(&self, w: &mut dyn Write) -> Result<()> { - w.write_all(&(self.0.len() as u32).to_be_bytes())?; - w.write_all(&self.0)?; + self.0.write(w) + } + fn read(r: &mut dyn Read) -> Result<Self> { + Ok(Self(Vec::read(r)?)) + } +} +impl ReadWrite for Vec<u8> { + fn write(&self, w: &mut dyn Write) -> Result<()> { + w.write_all(&(self.len() as u32).to_be_bytes())?; + w.write_all(&self)?; Ok(()) } fn read(r: &mut dyn Read) -> Result<Self> { @@ -177,7 +221,22 @@ impl ReadWrite for Data { let size = u32::from_be_bytes(size); let mut buf = vec![0; size as usize]; r.read_exact(&mut buf)?; - Ok(Data(buf)) + Ok(buf) + } +} +impl ReadWrite for String { + fn write(&self, w: &mut dyn Write) -> Result<()> { + w.write_all(&(self.len() as u32).to_be_bytes())?; + w.write_all(self.as_bytes())?; + Ok(()) + } + fn read(r: &mut dyn Read) -> Result<Self> { + let mut size = [0; 4]; + r.read_exact(&mut size)?; + let size = u32::from_be_bytes(size); + let mut buf = vec![0; size as usize]; + r.read_exact(&mut buf)?; + Ok(String::from_utf8(buf)?) } } fn read_params(r: &mut dyn Read) -> Result<Vec<f32>> { @@ -214,7 +273,7 @@ impl ReadWrite for u32 { } } -impl Display for Resource { +impl<T> Display for Resource<T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -226,8 +285,7 @@ impl Display for Resource { ) } } - -impl Debug for Resource { +impl<T> Debug for Resource<T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) } @@ -250,3 +308,8 @@ impl Debug for Data { f.debug_tuple("Data").finish_non_exhaustive() } } +impl Display for Object { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Object").field(&self.0).finish() + } +} diff --git a/shared/src/resources.rs b/shared/src/resources.rs index be34b21..d22e3ea 100644 --- a/shared/src/resources.rs +++ b/shared/src/resources.rs @@ -19,60 +19,64 @@ use anyhow::Result; use glam::{Affine3A, Vec3A}; use log::warn; use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, io::{Read, Write}, }; #[derive(Debug, Default, Clone)] pub struct Prefab { - pub mesh: Vec<(Affine3A, Resource)>, - pub light: Vec<(Vec3A, Resource)>, - pub environment: Option<EnvironmentPart>, + pub mesh: Vec<(Affine3A, Resource<MeshPart>)>, + pub light: Vec<(Vec3A, Resource<LightPart>)>, + pub environment: Option<Resource<EnvironmentPart>>, } #[derive(Debug, Default, Clone)] pub struct LightPart { - emission: Option<Vec3A>, - radius: Option<f32>, + pub emission: Option<Vec3A>, + pub radius: Option<f32>, } #[derive(Debug, Default, Clone)] pub struct EnvironmentPart { - skybox: Option<Resource>, - sun: Option<(Vec3A, Vec3A)>, + pub skybox: Option<Resource>, + pub sun: Option<(Vec3A, Vec3A)>, } #[derive(Debug, Default, Clone)] pub struct MeshPart { - pub index: Option<Resource>, + pub index: Option<Resource<IndexArray>>, pub g_metallic: Option<f32>, pub g_roughness: Option<f32>, pub g_albedo: Option<Vec3A>, pub g_transmission: Option<f32>, pub g_emission: Option<Vec3A>, - pub va_position: Option<[Resource; 3]>, - pub va_normal: Option<[Resource; 3]>, - pub va_texcoord: Option<[Resource; 2]>, - pub va_roughness: Option<Resource>, - pub va_metallic: Option<Resource>, - pub va_albedo: Option<[Resource; 3]>, - pub va_transmission: Option<Resource>, - pub va_emission: Option<[Resource; 3]>, - pub tex_normal: Option<Resource>, - pub tex_roughness: Option<Resource>, - pub tex_metallic: Option<Resource>, - pub tex_albedo: Option<Resource>, - pub tex_transmission: Option<Resource>, - pub tex_emission: Option<Resource>, + pub va_position: Option<[Resource<AttributeArray>; 3]>, + pub va_normal: Option<[Resource<AttributeArray>; 3]>, + pub va_texcoord: Option<[Resource<AttributeArray>; 2]>, + pub va_roughness: Option<Resource<AttributeArray>>, + pub va_metallic: Option<Resource<AttributeArray>>, + pub va_albedo: Option<[Resource<AttributeArray>; 3]>, + pub va_transmission: Option<Resource<AttributeArray>>, + pub va_emission: Option<[Resource<AttributeArray>; 3]>, + pub tex_normal: Option<Resource<Image>>, + pub tex_roughness: Option<Resource<Image>>, + pub tex_metallic: Option<Resource<Image>>, + pub tex_albedo: Option<Resource<Image>>, + pub tex_transmission: Option<Resource<Image>>, + pub tex_emission: Option<Resource<Image>>, } -pub struct PrefabIndex(pub HashMap<String, Resource>); +#[derive(Debug, Default, Clone)] +pub struct PrefabIndex(pub BTreeMap<String, Resource>); #[derive(Debug, Default, Clone)] pub struct AttributeArray(pub Vec<f32>); #[derive(Debug, Default, Clone)] pub struct IndexArray(pub Vec<[u16; 3]>); +#[derive(Debug, Clone)] +pub struct Image(pub Vec<u8>); + impl ReadWrite for PrefabIndex { fn write(&self, w: &mut dyn Write) -> Result<()> { for (k, v) in &self.0 { @@ -81,7 +85,7 @@ impl ReadWrite for PrefabIndex { Ok(()) } fn read(r: &mut dyn Read) -> Result<Self> { - let mut s = Self(HashMap::new()); + let mut s = Self(BTreeMap::new()); let mut g = Vec::new(); r.read_to_end(&mut g)?; let mut g = g.as_slice(); @@ -96,10 +100,10 @@ impl ReadWrite for PrefabIndex { impl ReadWrite for Prefab { fn write(&self, w: &mut dyn Write) -> Result<()> { for x in &self.mesh { - write_kv_opt(w, b"mesh", &Some(*x))?; + write_kv_opt(w, b"mesh", &Some(x.clone()))?; } for x in &self.light { - write_kv_opt(w, b"light", &Some(*x))?; + write_kv_opt(w, b"light", &Some(x.clone()))?; } write_kv_opt(w, b"environment", &self.environment)?; Ok(()) @@ -269,9 +273,9 @@ impl ReadWrite for u8 { impl ReadWrite for Vec3A { fn write(&self, w: &mut dyn Write) -> Result<()> { - w.write_all(&self.x.to_be_bytes())?; - w.write_all(&self.y.to_be_bytes())?; - w.write_all(&self.z.to_be_bytes())?; + self.x.write(w)?; + self.y.write(w)?; + self.z.write(w)?; Ok(()) } fn read(r: &mut dyn Read) -> Result<Self> { @@ -333,3 +337,12 @@ impl<A: ReadWrite, B: ReadWrite> ReadWrite for (A, B) { Ok((A::read(r)?, B::read(r)?)) } } +impl ReadWrite for Image { + fn write(&self, w: &mut dyn Write) -> Result<()> { + self.0.write(w) + } + + fn read(r: &mut dyn Read) -> Result<Self> { + Ok(Self(<Vec<u8> as ReadWrite>::read(r)?)) + } +} diff --git a/shared/src/store.rs b/shared/src/store.rs index 38402fc..f63ed6c 100644 --- a/shared/src/store.rs +++ b/shared/src/store.rs @@ -14,11 +14,11 @@ 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 crate::packets::Resource; +use crate::packets::{ReadWrite, Resource}; use anyhow::Result; use redb::{Database, TableDefinition}; use sha2::{Digest, Sha256}; -use std::{collections::HashMap, path::Path, sync::Mutex}; +use std::{collections::HashMap, marker::PhantomData, path::Path, sync::Mutex}; const T_ENTRIES: TableDefinition<[u8; 32], &[u8]> = TableDefinition::new("e"); @@ -33,7 +33,15 @@ impl ResourceStore { pub fn new_memory() -> Self { Self::Memory(HashMap::new().into()) } - pub fn get(&self, key: Resource) -> Result<Option<Vec<u8>>> { + pub fn get<T: ReadWrite>(&self, key: Resource<T>) -> Result<Option<T>> { + self.get_raw(Resource(key.0, PhantomData))? + .map(|b| T::read(&mut b.as_slice())) + .transpose() + } + pub fn set<T: ReadWrite>(&self, value: &T) -> Result<Resource<T>> { + Ok(Resource(self.set_raw(&value.write_alloc())?.0, PhantomData)) + } + pub fn get_raw(&self, key: Resource) -> Result<Option<Vec<u8>>> { match self { ResourceStore::Redb(database) => { let txn = database.begin_read()?; @@ -46,8 +54,8 @@ impl ResourceStore { ResourceStore::Memory(map) => Ok(map.lock().unwrap().get(&key).map(|x| x.to_vec())), } } - pub fn set(&self, value: &[u8]) -> Result<Resource> { - let key = Resource(sha256(value)); + pub fn set_raw(&self, value: &[u8]) -> Result<Resource> { + let key = Resource(sha256(value), PhantomData); match self { ResourceStore::Redb(database) => { let txn = database.begin_write()?; diff --git a/shared/src/tree.rs b/shared/src/tree.rs index e2c8250..9337cfb 100644 --- a/shared/src/tree.rs +++ b/shared/src/tree.rs @@ -14,7 +14,10 @@ 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 crate::packets::{Object, Packet, Resource}; +use crate::{ + packets::{Object, Packet, Resource}, + resources::Prefab, +}; use glam::Vec3A; use std::collections::HashMap; @@ -26,7 +29,7 @@ pub struct ObjectData { pub rot: Vec3A, pub parent: Object, pub pose: Vec<f32>, - pub res: Resource, + pub res: Resource<Prefab>, } impl Default for SceneTree { fn default() -> Self { @@ -44,7 +47,7 @@ impl SceneTree { pos: Vec3A::ZERO, rot: Vec3A::ZERO, pose: Vec::new(), - res: *res, + res: res.clone(), }); } Packet::Remove(object) => { @@ -75,7 +78,7 @@ impl SceneTree { .iter() .map(|(object, data)| { [ - Packet::Add(*object, data.res), + Packet::Add(*object, data.res.clone()), Packet::Parent(*object, data.parent), Packet::Position(*object, data.pos, data.rot), Packet::Pose(*object, data.pose.clone()), diff --git a/world/src/main.rs b/world/src/main.rs index ce50cea..bbbe4f9 100644 --- a/world/src/main.rs +++ b/world/src/main.rs @@ -15,10 +15,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ #![feature(iter_array_chunks)] +pub mod mesh; + use anyhow::{Result, bail}; use clap::Parser; use gltf::image::Source; -use log::{debug, info}; +use log::info; +use mesh::import_mesh; use rand::random; use std::{ fs::File, @@ -29,9 +32,9 @@ use std::{ time::Duration, }; use weareshared::{ - Affine3A, Vec3A, + Vec3A, packets::{Data, Object, Packet, ReadWrite, Resource}, - resources::{AttributeArray, IndexArray, MeshPart, Prefab}, + resources::{Image, LightPart, Prefab}, store::ResourceStore, vec3a, }; @@ -44,6 +47,8 @@ struct Args { push: bool, #[arg(short, long)] spin: bool, + #[arg(short, long)] + clear: bool, } fn main() -> Result<()> { @@ -61,241 +66,20 @@ fn main() -> Result<()> { let mut prefab = Prefab::default(); for node in gltf.nodes() { if let Some(mesh) = node.mesh() { - for p in mesh.primitives() { - let reader = p.reader(|buf| Some(&buffers[buf.index()])); - - let va_position = reader - .read_positions() - .map(|iter| { - let mut pos_x = vec![]; - let mut pos_y = vec![]; - let mut pos_z = vec![]; - for p in iter { - pos_x.push(p[0]); - pos_y.push(p[1]); - pos_z.push(p[2]); - } - info!("{} vertex positions", pos_x.len()); - Ok::<_, anyhow::Error>([ - store.set(&AttributeArray(pos_x).write_alloc())?, - store.set(&AttributeArray(pos_y).write_alloc())?, - store.set(&AttributeArray(pos_z).write_alloc())?, - ]) - }) - .transpose()?; - - let va_normal = reader - .read_normals() - .map(|iter| { - let mut normal_x = vec![]; - let mut normal_y = vec![]; - let mut normal_z = vec![]; - for p in iter { - normal_x.push(p[0]); - normal_y.push(p[1]); - normal_z.push(p[2]); - } - info!("{} vertex normals", normal_x.len()); - Ok::<_, anyhow::Error>([ - store.set(&AttributeArray(normal_x).write_alloc())?, - store.set(&AttributeArray(normal_y).write_alloc())?, - store.set(&AttributeArray(normal_z).write_alloc())?, - ]) - }) - .transpose()?; - - let va_texcoord = reader - .read_tex_coords(0) - .map(|iter| { - let mut texcoord_u = vec![]; - let mut texcoord_v = vec![]; - for p in iter.into_f32() { - texcoord_u.push(p[0]); - texcoord_v.push(p[1]); - } - info!("{} vertex texture coordinates", texcoord_u.len()); - Ok::<_, anyhow::Error>([ - store.set(&AttributeArray(texcoord_u).write_alloc())?, - store.set(&AttributeArray(texcoord_v).write_alloc())?, - ]) - }) - .transpose()?; - - let va_albedo = reader - .read_colors(0) - .map(|iter| { - let mut color_r = vec![]; - let mut color_g = vec![]; - let mut color_b = vec![]; - for p in iter.into_rgb_f32() { - color_r.push(p[0]); - color_g.push(p[1]); - color_b.push(p[2]); - } - info!("{} vertex colors", color_r.len()); - Ok::<_, anyhow::Error>([ - store.set(&AttributeArray(color_r).write_alloc())?, - store.set(&AttributeArray(color_g).write_alloc())?, - store.set(&AttributeArray(color_b).write_alloc())?, - ]) - }) - .transpose()?; - - let va_transmission = reader - .read_colors(0) - .map(|iter| { - let mut color_a = vec![]; - for p in iter.into_rgba_f32() { - color_a.push(p[3]); - } - let o = if color_a.iter().any(|x| *x != 1.) { - info!("{} vertex transmissions", color_a.len()); - Some(store.set(&AttributeArray(color_a).write_alloc())?) - } else { - debug!("vertex transmission pruned"); - None - }; - Ok::<_, anyhow::Error>(o) - }) - .transpose()? - .flatten(); - - let index = reader - .read_indices() - .unwrap() - .into_u32() - .map(|e| e as u16) - .array_chunks::<3>() - .collect::<Vec<_>>(); - info!("{} indecies", index.len() * 3); - let index = Some(store.set(&IndexArray(index).write_alloc())?); - - let mut tex_albedo = None; - let mut tex_transmission = None; - if let Some(tex) = p.material().pbr_metallic_roughness().base_color_texture() { - let r = load_texture( - "albedo", - &store, - path_base, - &buffers, - &tex.texture().source().source(), - )?; - tex_albedo = Some(r); - tex_transmission = Some(r); - } - let mut tex_normal = None; - if let Some(tex) = p.material().normal_texture() { - tex_normal = Some(load_texture( - "normal", - &store, - path_base, - &buffers, - &tex.texture().source().source(), - )?); - } - let mut tex_emission = None; - if let Some(tex) = p.material().emissive_texture() { - tex_emission = Some(load_texture( - "emission", - &store, - path_base, - &buffers, - &tex.texture().source().source(), - )?); - } - let mut tex_roughness = None; - let mut tex_metallic = None; - if let Some(tex) = p - .material() - .pbr_metallic_roughness() - .metallic_roughness_texture() - { - let r = load_texture( - "metallic+roughness", - &store, - path_base, - &buffers, - &tex.texture().source().source(), - )?; - tex_roughness = Some(r); - tex_metallic = Some(r); - } - - let g_metallic = Some(p.material().pbr_metallic_roughness().metallic_factor()); - let g_roughness = Some(p.material().pbr_metallic_roughness().roughness_factor()); - - let base_color = p.material().pbr_metallic_roughness().base_color_factor(); - - let g_albedo = if base_color[0] != 1. || base_color[1] != 1. || base_color[2] != 1. - { - info!( - "global albedo is r={},g={},b={}", - base_color[0], base_color[1], base_color[2] - ); - Some(Vec3A::new(base_color[0], base_color[1], base_color[2])) - } else { - debug!("global albedo pruned"); - None - }; - let g_transmission = if base_color[3] != 1. { - info!("global transmission is {}", base_color[3]); - Some(base_color[3]) - } else { - debug!("global transmission pruned"); - None - }; - - let emission = p.material().emissive_factor(); - let g_emission = if emission[0] != 0. || emission[1] != 0. || emission[2] != 0. { - info!( - "global emission is r={},g={},b={}", - base_color[0], base_color[1], base_color[2] - ); - Some(Vec3A::new(emission[0], emission[1], emission[2])) - } else { - debug!("global emission pruned"); - None - }; - - let mesh = store.set( - &MeshPart { - g_albedo, - g_transmission, - g_metallic, - g_roughness, - g_emission, - va_position, - va_normal, - va_texcoord, - va_albedo, - va_transmission, - tex_albedo, - tex_normal, - tex_roughness, - tex_metallic, - tex_transmission, - tex_emission, - index, - va_emission: None, // not supported by gltf - va_metallic: None, // not supported by gltf - va_roughness: None, // not supported by gltf - } - .write_alloc(), - )?; - let mat = node.transform().matrix(); - let aff = Affine3A::from_cols_array_2d(&[ - [mat[0][0], mat[0][1], mat[0][2]], - [mat[1][0], mat[1][1], mat[1][2]], - [mat[2][0], mat[2][1], mat[2][2]], - [mat[3][0], mat[3][1], mat[3][2]], - ]); - prefab.mesh.push((aff, mesh)) - } + import_mesh(mesh, &buffers, &store, path_base, &node, &mut prefab)?; } } + prefab.light.push(( + vec3a(5., 5., 5.), + store.set(&LightPart { + emission: Some(vec3a(0.5, 0.1, 1.0)), + radius: Some(0.2), + })?, + )); + let ob = Object::new(); - Packet::Add(ob, store.set(&prefab.write_alloc())?).write(&mut sock)?; + Packet::Add(ob, store.set(&prefab)?).write(&mut sock)?; if args.spin { let mut sock2 = sock.try_clone().unwrap(); @@ -324,11 +108,18 @@ fn main() -> Result<()> { let packet = Packet::read(&mut sock)?; match packet { Packet::RequestResource(hash) => { - if let Some(d) = store.get(hash)? { + if let Some(d) = store.get_raw(hash)? { Packet::RespondResource(Data(d)).write(&mut sock)?; sock.flush()?; } } + Packet::Add(ob_a, _) => { + if ob_a != ob { + info!("removing object {ob_a}"); + Packet::Remove(ob_a).write(&mut sock)?; + sock.flush()?; + } + } _ => (), } } @@ -342,13 +133,13 @@ fn load_texture( path: &Path, buffers: &[gltf::buffer::Data], source: &Source, -) -> Result<Resource> { +) -> Result<Resource<Image>> { match source { gltf::image::Source::View { view, mime_type } => { info!("{name} texture is embedded and of type {mime_type:?}"); let buf = &buffers[view.buffer().index()].0[view.offset()..view.offset() + view.length()]; - return Ok(store.set(buf)?); + return Ok(store.set(&Image(buf.to_vec()))?); } gltf::image::Source::Uri { uri, @@ -358,7 +149,7 @@ fn load_texture( let path = path.join(uri); let mut buf = Vec::new(); File::open(path)?.read_to_end(&mut buf)?; - return Ok(store.set(&buf)?); + return Ok(store.set(&Image(buf))?); } _ => { bail!("texture is external and has no mime type") diff --git a/world/src/mesh.rs b/world/src/mesh.rs new file mode 100644 index 0000000..cbc33c0 --- /dev/null +++ b/world/src/mesh.rs @@ -0,0 +1,262 @@ +/* + 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/>. +*/ +use crate::load_texture; +use anyhow::Result; +use gltf::{Mesh, Node, buffer::Data}; +use log::{debug, info}; +use std::path::Path; +use weareshared::{ + Affine3A, Vec3A, + resources::{AttributeArray, IndexArray, MeshPart, Prefab}, + store::ResourceStore, +}; + +pub fn import_mesh( + mesh: Mesh, + buffers: &[Data], + store: &ResourceStore, + path_base: &Path, + node: &Node, + prefab: &mut Prefab, +) -> Result<()> { + Ok(for p in mesh.primitives() { + let reader = p.reader(|buf| Some(&buffers[buf.index()])); + + let va_position = reader + .read_positions() + .map(|iter| { + let mut pos_x = vec![]; + let mut pos_y = vec![]; + let mut pos_z = vec![]; + for p in iter { + pos_x.push(p[0]); + pos_y.push(p[1]); + pos_z.push(p[2]); + } + info!("{} vertex positions", pos_x.len()); + Ok::<_, anyhow::Error>([ + store.set(&AttributeArray(pos_x))?, + store.set(&AttributeArray(pos_y))?, + store.set(&AttributeArray(pos_z))?, + ]) + }) + .transpose()?; + + let va_normal = reader + .read_normals() + .map(|iter| { + let mut normal_x = vec![]; + let mut normal_y = vec![]; + let mut normal_z = vec![]; + for p in iter { + normal_x.push(p[0]); + normal_y.push(p[1]); + normal_z.push(p[2]); + } + info!("{} vertex normals", normal_x.len()); + Ok::<_, anyhow::Error>([ + store.set(&AttributeArray(normal_x))?, + store.set(&AttributeArray(normal_y))?, + store.set(&AttributeArray(normal_z))?, + ]) + }) + .transpose()?; + + let va_texcoord = reader + .read_tex_coords(0) + .map(|iter| { + let mut texcoord_u = vec![]; + let mut texcoord_v = vec![]; + for p in iter.into_f32() { + texcoord_u.push(p[0]); + texcoord_v.push(p[1]); + } + info!("{} vertex texture coordinates", texcoord_u.len()); + Ok::<_, anyhow::Error>([ + store.set(&AttributeArray(texcoord_u))?, + store.set(&AttributeArray(texcoord_v))?, + ]) + }) + .transpose()?; + + let va_albedo = reader + .read_colors(0) + .map(|iter| { + let mut color_r = vec![]; + let mut color_g = vec![]; + let mut color_b = vec![]; + for p in iter.into_rgb_f32() { + color_r.push(p[0]); + color_g.push(p[1]); + color_b.push(p[2]); + } + info!("{} vertex colors", color_r.len()); + Ok::<_, anyhow::Error>([ + store.set(&AttributeArray(color_r))?, + store.set(&AttributeArray(color_g))?, + store.set(&AttributeArray(color_b))?, + ]) + }) + .transpose()?; + + let va_transmission = reader + .read_colors(0) + .map(|iter| { + let mut color_a = vec![]; + for p in iter.into_rgba_f32() { + color_a.push(p[3]); + } + let o = if color_a.iter().any(|x| *x != 1.) { + info!("{} vertex transmissions", color_a.len()); + Some(store.set(&AttributeArray(color_a))?) + } else { + debug!("vertex transmission pruned"); + None + }; + Ok::<_, anyhow::Error>(o) + }) + .transpose()? + .flatten(); + + let index = reader + .read_indices() + .unwrap() + .into_u32() + .map(|e| e as u16) + .array_chunks::<3>() + .collect::<Vec<_>>(); + info!("{} indecies", index.len() * 3); + let index = Some(store.set(&IndexArray(index))?); + + let mut tex_albedo = None; + let mut tex_transmission = None; + if let Some(tex) = p.material().pbr_metallic_roughness().base_color_texture() { + let r = load_texture( + "albedo", + &store, + path_base, + &buffers, + &tex.texture().source().source(), + )?; + tex_albedo = Some(r.clone()); + tex_transmission = Some(r.clone()); + } + let mut tex_normal = None; + if let Some(tex) = p.material().normal_texture() { + tex_normal = Some(load_texture( + "normal", + &store, + path_base, + &buffers, + &tex.texture().source().source(), + )?); + } + let mut tex_emission = None; + if let Some(tex) = p.material().emissive_texture() { + tex_emission = Some(load_texture( + "emission", + &store, + path_base, + &buffers, + &tex.texture().source().source(), + )?); + } + let mut tex_roughness = None; + let mut tex_metallic = None; + if let Some(tex) = p + .material() + .pbr_metallic_roughness() + .metallic_roughness_texture() + { + let r = load_texture( + "metallic+roughness", + &store, + path_base, + &buffers, + &tex.texture().source().source(), + )?; + tex_roughness = Some(r.clone()); + tex_metallic = Some(r.clone()); + } + + let g_metallic = Some(p.material().pbr_metallic_roughness().metallic_factor()); + let g_roughness = Some(p.material().pbr_metallic_roughness().roughness_factor()); + + let base_color = p.material().pbr_metallic_roughness().base_color_factor(); + + let g_albedo = if base_color[0] != 1. || base_color[1] != 1. || base_color[2] != 1. { + info!( + "global albedo is r={},g={},b={}", + base_color[0], base_color[1], base_color[2] + ); + Some(Vec3A::new(base_color[0], base_color[1], base_color[2])) + } else { + debug!("global albedo pruned"); + None + }; + let g_transmission = if base_color[3] != 1. { + info!("global transmission is {}", base_color[3]); + Some(base_color[3]) + } else { + debug!("global transmission pruned"); + None + }; + + let emission = p.material().emissive_factor(); + let g_emission = if emission[0] != 0. || emission[1] != 0. || emission[2] != 0. { + info!( + "global emission is r={},g={},b={}", + base_color[0], base_color[1], base_color[2] + ); + Some(Vec3A::new(emission[0], emission[1], emission[2])) + } else { + debug!("global emission pruned"); + None + }; + + let mesh = store.set(&MeshPart { + g_albedo, + g_transmission, + g_metallic, + g_roughness, + g_emission, + va_position, + va_normal, + va_texcoord, + va_albedo, + va_transmission, + tex_albedo, + tex_normal, + tex_roughness, + tex_metallic, + tex_transmission, + tex_emission, + index, + va_emission: None, // not supported by gltf + va_metallic: None, // not supported by gltf + va_roughness: None, // not supported by gltf + })?; + let mat = node.transform().matrix(); + let aff = Affine3A::from_cols_array_2d(&[ + [mat[0][0], mat[0][1], mat[0][2]], + [mat[1][0], mat[1][1], mat[1][2]], + [mat[2][0], mat[2][1], mat[2][2]], + [mat[3][0], mat[3][1], mat[3][2]], + ]); + prefab.mesh.push((aff, mesh)) + }) +} |