From 3d156d75de3852dd36d0eeda33b17a2b6f10aa4a Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 7 Jan 2025 21:16:40 +0100 Subject: new proto doc --- a.md | 89 ------------------- client/src/scene_prepare.rs | 25 +++--- doc/other.md | 10 +++ doc/protocol.md | 36 ++++++++ doc/resources.md | 73 ++++++++++++++++ shared/src/resources.rs | 207 ++++++++++++++++++++++++++++++-------------- world/src/main.rs | 8 +- 7 files changed, 280 insertions(+), 168 deletions(-) delete mode 100644 a.md create mode 100644 doc/other.md create mode 100644 doc/protocol.md create mode 100644 doc/resources.md diff --git a/a.md b/a.md deleted file mode 100644 index 22cccd4..0000000 --- a/a.md +++ /dev/null @@ -1,89 +0,0 @@ -## Protocol packets - -Vec is stored as `len:u32 *(data:T)`. All packets are preceeded by a u32 -indicating its length. - -```rs -type Obj = [u8; 16] -type Res = [u8; 32] -00 connect(identity: u128) -ff disconnect() -01 request_resource(name: Res) -02 respond_resource(data: Vec) -03 add(id: Obj, prefab: Res) -04 remove(id: Obj) -05 position(id: Obj, pos: Vec3, rot: Vec3) -06 pose(id: Obj, params: Vec) -07 parent(parent: Obj, child: Obj) -08 sound(id: Obj, data: Vec) -09 prefab_index(res: Res) -``` - -## Resource formats - -### PrefabIndex - -``` -PrefabIndex = *(len_key:u16 len_value:u16 *(key:u8) *(value:u8)) -``` - -Key is string with model name. Value is prefab resource. - -### Prefab - -``` -Prefab = *(pos:f32x3 mat:f32_3x3 part:Res) -``` - -### Part - -``` -Part = *(len_key:u16 len_value:u16 *(key:u8) *(value:u8)) -Attribute = 0x01 constant:f32 / 0x02 buffer:Res / 0x03 texture:Res -NewAttribute = flag:u8 constant:f32 buffer:Res texture:Res -``` - -Combinations of g__, va__ and tex_* are multiplied except normal which is added. -Defaults should be the identity for that operation, so default is 1 / white -except normals are zero. - -| Key | Value Type | | -| ---------------- | ------------- | ------------------ | -| index | Resource | | -| g_metallic | f32 | | -| g_roughness | f32 | | -| g_albedo | \[f32;3\] | | -| g_transmission | f32 | | -| g_emission | \[f32;3\] | | -| va_position | [Resource; 3] | | -| va_normal | [Resource; 3] | | -| va_texcoord | [Resource; 2] | | -| va_roughness | Resource | | -| va_metallic | Resource | | -| va_albedo | [Resource; 3] | | -| va_transmission | Resource | | -| va_emission | Resource | | -| tex_normal | Resource | Use color channels | -| tex_roughness | Resource | Use green channel | -| tex_metallic | Resource | Use blue channel | -| tex_albedo | Resource | Use color channels | -| tex_transmission | Resource | Use alpha channel | -| tex_emission | Resource | Use color channels | - -### Texture - -WebP - -### Shader - -glsl source, todo - -## Player tree - -- LowerTorso (2x leg tilt) - - FootL - - FootR - - UpperTorso (2x arm tilt) - - HandL (5x finger, 5x tilt) - - HandR (5x finger, 5x tilt) - - Head (2x brow, 2x eyelid, 2x eye, 3x mouth) diff --git a/client/src/scene_prepare.rs b/client/src/scene_prepare.rs index 6d50035..e58e77a 100644 --- a/client/src/scene_prepare.rs +++ b/client/src/scene_prepare.rs @@ -11,7 +11,7 @@ use std::{ use weareshared::{ Affine3A, packets::{ReadWrite, Resource}, - resources::{Part, Prefab}, + resources::{MeshPart, Prefab}, }; use wgpu::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource, Buffer, @@ -55,12 +55,12 @@ pub struct ScenePreparer { index_buffers: DemandMap, u32)>, vertex_buffers: DemandMap, u32)>, placeholder_vertex_buffers: DemandMap<(u32, bool), Arc>, - parts: DemandMap>, + mesh_parts: DemandMap>, pub prefabs: DemandMap>, } -pub struct RPrefab(pub Vec<(Affine3A, Arc)>); -pub struct RPart { +pub struct RPrefab(pub Vec<(Affine3A, Arc)>); +pub struct RMeshPart { pub index_count: u32, pub index: Arc, pub position: [Arc; 3], @@ -75,7 +75,7 @@ impl ScenePreparer { texture_bgl, index_buffers: DemandMap::new(), vertex_buffers: DemandMap::new(), - parts: DemandMap::new(), + mesh_parts: DemandMap::new(), prefabs: DemandMap::new(), textures: DemandMap::new(), placeholder_vertex_buffers: DemandMap::new(), @@ -89,12 +89,12 @@ impl ScenePreparer { if let Some(buf) = dls.try_get(pres)? { let prefab = Prefab::read(&mut buf.as_slice()).context("parsing prefab")?; let mut rprefab = RPrefab(Vec::new()); - for (aff, partres) in &prefab.0 { - if let Some(part) = self.parts.try_get(*partres) { + for (aff, partres) in &prefab.mesh { + if let Some(part) = self.mesh_parts.try_get(*partres) { rprefab.0.push((*aff, part.clone())); } } - if rprefab.0.len() == prefab.0.len() { + if rprefab.0.len() == prefab.mesh.len() { self.prefabs.insert(pres, Arc::new(rprefab)); debug!("prefab created ({pres})"); } @@ -169,9 +169,10 @@ impl ScenePreparer { ); self.placeholder_textures.insert(pres, tex_bg); } - for pres in self.parts.needed.clone() { + for pres in self.mesh_parts.needed.clone() { if let Some(buf) = dls.try_get(pres)? { - let part = Part::read(&mut buf.as_slice()).context("parsing part")?; + let part = MeshPart::read(&mut buf.as_slice()).context("parsing part")?; + if let (Some(indexres), Some(positionres)) = (part.index, part.va_position) { let Some((index, index_count)) = self.index_buffers.try_get(indexres) else { self.index_buffers.needed.insert(indexres); @@ -238,9 +239,9 @@ impl ScenePreparer { && texture.is_some() { debug!("part created ({pres})"); - self.parts.insert( + self.mesh_parts.insert( pres, - Arc::new(RPart { + Arc::new(RMeshPart { index_count, index, texcoord: texcoord.try_into().unwrap(), diff --git a/doc/other.md b/doc/other.md new file mode 100644 index 0000000..76c8c37 --- /dev/null +++ b/doc/other.md @@ -0,0 +1,10 @@ + +## Player tree + +- LowerTorso (2x leg tilt) + - FootL + - FootR + - UpperTorso (2x arm tilt) + - HandL (5x finger, 5x tilt) + - HandR (5x finger, 5x tilt) + - Head (2x brow, 2x eyelid, 2x eye, 3x mouth) diff --git a/doc/protocol.md b/doc/protocol.md new file mode 100644 index 0000000..cd4694e --- /dev/null +++ b/doc/protocol.md @@ -0,0 +1,36 @@ +# Protocol + +Default port is 28555 for TCP and UDP transport. + +For TCP, packets are concatenated to a stream. + +For UDP, game packets are concatenated in groups of at least one to form one UDP +packet. + +## Protocol packets + +All packets are preceeded by its length as u32. The next byte indicates the +type. The following bytes are the parameters. + +`Vec` is stored as u32 element count followed by the elements. `Res` is +used to denote 256-bit resource hash referring to data of type T. `Obj` refers +128-bit game objects IDs. + +```rs +00 connect(identity: u128) +ff disconnect() +01 request_resource(name: Res) +02 respond_resource(data: Vec) +03 add(id: Obj, prefab: Res) +04 remove(id: Obj) +05 position(id: Obj, pos: Vec3, rot: Vec3) +06 pose(id: Obj, params: Vec) +07 parent(parent: Obj, child: Obj) +08 sound(id: Obj, data: Vec) +09 prefab_index(res: Res) +``` + +## Resources + +Resources are reusable things. They are identified by the SHA-256 hash of their +serialized content. See [resources.md](./resources.md) diff --git a/doc/resources.md b/doc/resources.md new file mode 100644 index 0000000..3add075 --- /dev/null +++ b/doc/resources.md @@ -0,0 +1,73 @@ +# Resource formats + +## Dictionary format + +``` +[kkkk vvvv KK... VV...]... + ^ ^ ^ ^ + | | | | value + | | | key + | | 16-bit kalue length + | 16-bit key length +``` + +## PrefabIndex + +| Key | Value Type | +| -------- | ---------- | +| \ | Resource | + +## Prefab + +| Key | Value Type | | +| ----------- | ----------------------- | --------- | +| mesh | Matrix3, Vec3, Resource | Multi key | +| light | Vec3, Resource | Multi key | +| environment | Resource | | + +## MeshPart + +Combinations of g__, va__ and tex_* are multiplied except normal which is added. +Defaults should be the identity for that operation, so default is 1 / white +except normals are zero. + +| Key | Value Type | | +| ---------------- | ------------- | ------------------ | +| index | Resource | | +| g_metallic | f32 | | +| g_roughness | f32 | | +| g_albedo | Vec3 | | +| g_transmission | f32 | | +| g_emission | Vec3 | | +| va_position | [Resource; 3] | | +| va_normal | [Resource; 3] | | +| va_texcoord | [Resource; 2] | | +| va_roughness | Resource | | +| va_metallic | Resource | | +| va_albedo | [Resource; 3] | | +| va_transmission | Resource | | +| va_emission | Resource | | +| tex_normal | Resource | Use color channels | +| tex_roughness | Resource | Use green channel | +| tex_metallic | Resource | Use blue channel | +| tex_albedo | Resource | Use color channels | +| tex_transmission | Resource | Use alpha channel | +| tex_emission | Resource | Use color channels | + +## LightPart + +| Key | Value Type | +| -------- | ---------- | +| radius | f32 | +| emission | Vec3 | + +## EnvironmentPart + +| Key | Value Type | | +| ------ | ---------- | ---------------- | +| skybox | Resource | | +| sun | Vec3, Vec3 | Direction, Color | + +## Texture + +WebP diff --git a/shared/src/resources.rs b/shared/src/resources.rs index 5b2e852..d0094fa 100644 --- a/shared/src/resources.rs +++ b/shared/src/resources.rs @@ -8,10 +8,26 @@ use std::{ }; #[derive(Debug, Default, Clone)] -pub struct Prefab(pub Vec<(Affine3A, Resource)>); +pub struct Prefab { + pub mesh: Vec<(Affine3A, Resource)>, + pub light: Vec<(Vec3A, Resource)>, + pub environment: Option, +} + +#[derive(Debug, Default, Clone)] +pub struct LightPart { + emission: Option, + radius: Option, +} #[derive(Debug, Default, Clone)] -pub struct Part { +pub struct EnvironmentPart { + skybox: Option, + sun: Option<(Vec3A, Vec3A)>, +} + +#[derive(Debug, Default, Clone)] +pub struct MeshPart { pub index: Option, pub g_metallic: Option, pub g_roughness: Option, @@ -63,58 +79,69 @@ impl ReadWrite for PrefabIndex { impl ReadWrite for Prefab { fn write(&self, w: &mut dyn Write) -> Result<()> { - for (aff, res) in self.0.clone() { - aff.write(w)?; - res.write(w)?; + for x in &self.mesh { + write_kv_opt(w, b"mesh", &Some(*x))?; + } + for x in &self.light { + write_kv_opt(w, b"light", &Some(*x))?; } + write_kv_opt(w, b"environment", &self.environment)?; Ok(()) } fn read(r: &mut dyn Read) -> Result { - let mut s = Prefab::default(); - let mut g = Vec::new(); - r.read_to_end(&mut g)?; - let mut g = g.as_slice(); - while !g.is_empty() { - s.0.push((Affine3A::read(&mut g)?, Resource::read(&mut g)?)) - } + let mut s = Self::default(); + read_kv_iter(r, |k, v| match k { + b"mesh" => Ok(s.mesh.push(read_slice(v)?)), + b"light" => Ok(s.light.push(read_slice(v)?)), + b"environment" => Ok(s.environment = Some(read_slice(v)?)), + x => Ok(warn!( + "unknown prefab key: {:?}", + String::from_utf8_lossy(x) + )), + })?; Ok(s) } } -impl ReadWrite for IndexArray { +impl ReadWrite for LightPart { fn write(&self, w: &mut dyn Write) -> Result<()> { - for x in self.0.clone() { - w.write_all(x.map(|x| x.to_be_bytes()).as_flattened())?; - } + write_kv_opt(w, b"emission", &self.emission)?; + write_kv_opt(w, b"radius", &self.radius)?; Ok(()) } + fn read(r: &mut dyn Read) -> Result { - let mut s = Self(Vec::new()); - let mut g = Vec::new(); - r.read_to_end(&mut g)?; - for x in g.iter().array_chunks::<2>().array_chunks::<3>() { - s.0.push(x.map(|x| u16::from_be_bytes(x.map(|x| *x)))) - } + let mut s = Self::default(); + read_kv_iter(r, |k, v| match k { + b"emission" => Ok(s.emission = Some(read_slice(v)?)), + b"radius" => Ok(s.radius = Some(read_slice(v)?)), + x => Ok(warn!( + "unknown light part key: {:?}", + String::from_utf8_lossy(x) + )), + })?; Ok(s) } } -impl ReadWrite for AttributeArray { +impl ReadWrite for EnvironmentPart { fn write(&self, w: &mut dyn Write) -> Result<()> { - for x in self.0.clone() { - w.write_all(&x.to_be_bytes())?; - } + write_kv_opt(w, b"skybox", &self.skybox)?; + write_kv_opt(w, b"sun", &self.sun)?; Ok(()) } fn read(r: &mut dyn Read) -> Result { - let mut s = Self(Vec::new()); - let mut g = Vec::new(); - r.read_to_end(&mut g)?; - for x in g.iter().array_chunks::<4>() { - s.0.push(f32::from_be_bytes(x.map(|x| *x))) - } + let mut s = Self::default(); + read_kv_iter(r, |k, v| match k { + b"skybox" => Ok(s.skybox = Some(read_slice(v)?)), + b"sun" => Ok(s.sun = Some(read_slice(v)?)), + x => Ok(warn!( + "unknown environment part key: {:?}", + String::from_utf8_lossy(x) + )), + })?; Ok(s) } } -impl ReadWrite for Part { +impl ReadWrite for MeshPart { fn write(&self, w: &mut dyn Write) -> Result<()> { write_kv_opt(w, b"index", &self.index)?; write_kv_opt(w, b"g_metallic", &self.g_metallic)?; @@ -140,36 +167,32 @@ impl ReadWrite for Part { } fn read(r: &mut dyn Read) -> Result { let mut s = Self::default(); - let mut g = Vec::new(); - r.read_to_end(&mut g)?; - let mut g = g.as_slice(); - while !g.is_empty() { - let (k, v) = read_kv(&mut g)?; - let mut v = v.as_slice(); - match k.as_slice() { - b"index" => s.index = Some(<_ as ReadWrite>::read(&mut v)?), - b"g_metallic" => s.g_metallic = Some(<_ as ReadWrite>::read(&mut v)?), - b"g_roughness" => s.g_roughness = Some(<_ as ReadWrite>::read(&mut v)?), - b"g_albedo" => s.g_albedo = Some(<_ as ReadWrite>::read(&mut v)?), - b"g_transmission" => s.g_transmission = Some(<_ as ReadWrite>::read(&mut v)?), - b"g_emission" => s.g_emission = Some(<_ as ReadWrite>::read(&mut v)?), - b"va_position" => s.va_position = Some(<_ as ReadWrite>::read(&mut v)?), - b"va_normal" => s.va_normal = Some(<_ as ReadWrite>::read(&mut v)?), - b"va_texcoord" => s.va_texcoord = Some(<_ as ReadWrite>::read(&mut v)?), - b"va_roughness" => s.va_roughness = Some(<_ as ReadWrite>::read(&mut v)?), - b"va_metallic" => s.va_metallic = Some(<_ as ReadWrite>::read(&mut v)?), - b"va_albedo" => s.va_albedo = Some(<_ as ReadWrite>::read(&mut v)?), - b"va_transmission" => s.va_transmission = Some(<_ as ReadWrite>::read(&mut v)?), - b"va_emission" => s.va_emission = Some(<_ as ReadWrite>::read(&mut v)?), - b"tex_normal" => s.tex_normal = Some(<_ as ReadWrite>::read(&mut v)?), - b"tex_roughness" => s.tex_roughness = Some(<_ as ReadWrite>::read(&mut v)?), - b"tex_metallic" => s.tex_metallic = Some(<_ as ReadWrite>::read(&mut v)?), - b"tex_albedo" => s.tex_albedo = Some(<_ as ReadWrite>::read(&mut v)?), - b"tex_transmission" => s.tex_transmission = Some(<_ as ReadWrite>::read(&mut v)?), - b"tex_emission" => s.tex_emission = Some(<_ as ReadWrite>::read(&mut v)?), - x => warn!("unknown part key: {:?}", String::from_utf8_lossy(x)), - } - } + read_kv_iter(r, |k, v| match k { + b"index" => Ok(s.index = Some(read_slice(v)?)), + b"g_metallic" => Ok(s.g_metallic = Some(read_slice(v)?)), + b"g_roughness" => Ok(s.g_roughness = Some(read_slice(v)?)), + b"g_albedo" => Ok(s.g_albedo = Some(read_slice(v)?)), + b"g_transmission" => Ok(s.g_transmission = Some(read_slice(v)?)), + b"g_emission" => Ok(s.g_emission = Some(read_slice(v)?)), + b"va_position" => Ok(s.va_position = Some(read_slice(v)?)), + b"va_normal" => Ok(s.va_normal = Some(read_slice(v)?)), + b"va_texcoord" => Ok(s.va_texcoord = Some(read_slice(v)?)), + b"va_roughness" => Ok(s.va_roughness = Some(read_slice(v)?)), + b"va_metallic" => Ok(s.va_metallic = Some(read_slice(v)?)), + b"va_albedo" => Ok(s.va_albedo = Some(read_slice(v)?)), + b"va_transmission" => Ok(s.va_transmission = Some(read_slice(v)?)), + b"va_emission" => Ok(s.va_emission = Some(read_slice(v)?)), + b"tex_normal" => Ok(s.tex_normal = Some(read_slice(v)?)), + b"tex_roughness" => Ok(s.tex_roughness = Some(read_slice(v)?)), + b"tex_metallic" => Ok(s.tex_metallic = Some(read_slice(v)?)), + b"tex_albedo" => Ok(s.tex_albedo = Some(read_slice(v)?)), + b"tex_transmission" => Ok(s.tex_transmission = Some(read_slice(v)?)), + b"tex_emission" => Ok(s.tex_emission = Some(read_slice(v)?)), + x => Ok(warn!( + "unknown mesh part key: {:?}", + String::from_utf8_lossy(x) + )), + })?; Ok(s) } } @@ -188,6 +211,20 @@ fn read_kv(r: &mut &[u8]) -> Result<(Vec, Vec)> { Ok((key, value)) } +fn read_kv_iter(r: &mut dyn Read, mut cb: impl FnMut(&[u8], &[u8]) -> Result<()>) -> Result<()> { + let mut g = Vec::new(); + r.read_to_end(&mut g)?; + let mut g = g.as_slice(); + while !g.is_empty() { + let (k, v) = read_kv(&mut g)?; + cb(&k, &v)? + } + Ok(()) +} +fn read_slice(mut r: &[u8]) -> Result { + T::read(&mut r) +} + fn write_kv_opt(w: &mut dyn Write, key: &[u8], value: &Option) -> Result<()> { if let Some(v) = value { write_kv(w, key, &v.write_alloc())?; @@ -236,3 +273,47 @@ impl ReadWrite for Affine3A { Ok(Self::from_cols_array(&[(); 12].try_map(|()| f32::read(r))?)) } } +impl ReadWrite for IndexArray { + fn write(&self, w: &mut dyn Write) -> Result<()> { + for x in self.0.clone() { + w.write_all(x.map(|x| x.to_be_bytes()).as_flattened())?; + } + Ok(()) + } + fn read(r: &mut dyn Read) -> Result { + let mut s = Self(Vec::new()); + let mut g = Vec::new(); + r.read_to_end(&mut g)?; + for x in g.iter().array_chunks::<2>().array_chunks::<3>() { + s.0.push(x.map(|x| u16::from_be_bytes(x.map(|x| *x)))) + } + Ok(s) + } +} +impl ReadWrite for AttributeArray { + fn write(&self, w: &mut dyn Write) -> Result<()> { + for x in self.0.clone() { + w.write_all(&x.to_be_bytes())?; + } + Ok(()) + } + fn read(r: &mut dyn Read) -> Result { + let mut s = Self(Vec::new()); + let mut g = Vec::new(); + r.read_to_end(&mut g)?; + for x in g.iter().array_chunks::<4>() { + s.0.push(f32::from_be_bytes(x.map(|x| *x))) + } + Ok(s) + } +} +impl ReadWrite for (A, B) { + fn write(&self, w: &mut dyn Write) -> Result<()> { + self.0.write(w)?; + self.1.write(w)?; + Ok(()) + } + fn read(r: &mut dyn Read) -> Result { + Ok((A::read(r)?, B::read(r)?)) + } +} diff --git a/world/src/main.rs b/world/src/main.rs index 2014996..5f72b01 100644 --- a/world/src/main.rs +++ b/world/src/main.rs @@ -15,7 +15,7 @@ use std::{ use weareshared::{ Affine3A, Vec3A, packets::{Data, Object, Packet, ReadWrite, Resource}, - resources::{AttributeArray, IndexArray, Part, Prefab}, + resources::{AttributeArray, IndexArray, MeshPart, Prefab}, store::ResourceStore, vec3a, }; @@ -241,8 +241,8 @@ fn main() -> Result<()> { None }; - let part = store.set( - &Part { + let mesh = store.set( + &MeshPart { g_albedo, g_transmission, g_metallic, @@ -273,7 +273,7 @@ fn main() -> Result<()> { [mat[2][0], mat[2][1], mat[2][2]], [mat[3][0], mat[3][1], mat[3][2]], ]); - prefab.0.push((aff, part)) + prefab.mesh.push((aff, mesh)) } } } -- cgit v1.2.3-70-g09d2