diff options
author | metamuffin <metamuffin@disroot.org> | 2025-03-27 18:17:34 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-03-27 18:17:34 +0100 |
commit | 40afd6fa42ad937148c6812b8df8efff6c78e4b5 (patch) | |
tree | 09b5200598d9a9d93757745461fe34fbe28e1a81 | |
parent | 9f16507564354422b64e437e547d8403b22b70c7 (diff) | |
download | weareserver-40afd6fa42ad937148c6812b8df8efff6c78e4b5.tar weareserver-40afd6fa42ad937148c6812b8df8efff6c78e4b5.tar.bz2 weareserver-40afd6fa42ad937148c6812b8df8efff6c78e4b5.tar.zst |
spatial tree, graphics and some unit tests
-rw-r--r-- | import/src/main.rs | 5 | ||||
-rw-r--r-- | shared/src/graphics.rs | 129 | ||||
-rw-r--r-- | shared/src/helper.rs | 69 | ||||
-rw-r--r-- | shared/src/lib.rs | 3 | ||||
-rw-r--r-- | shared/src/packets.rs | 29 | ||||
-rw-r--r-- | shared/src/resources.rs | 19 | ||||
-rw-r--r-- | shared/src/respack.rs | 10 |
7 files changed, 256 insertions, 8 deletions
diff --git a/import/src/main.rs b/import/src/main.rs index 0a7278b..cfc6f92 100644 --- a/import/src/main.rs +++ b/import/src/main.rs @@ -149,7 +149,10 @@ fn main() -> Result<()> { return Ok(()); } if let Some(outpath) = args.pack { - let entry = store.set(&RespackEntry { c_prefab: prefabs })?; + let entry = store.set(&RespackEntry { + c_prefab: prefabs, + ..Default::default() + })?; let mut resources = Vec::new(); store.iter(|r, _| resources.push(r))?; save_respack( diff --git a/shared/src/graphics.rs b/shared/src/graphics.rs new file mode 100644 index 0000000..ae4ee58 --- /dev/null +++ b/shared/src/graphics.rs @@ -0,0 +1,129 @@ +use crate::helper::ReadWrite; +use glam::{Vec3, vec3}; +use std::io::{Read, Write}; + +#[derive(Debug, Default, Clone)] +pub struct GraphicsPart(Vec<u8>); +impl ReadWrite for GraphicsPart { + fn write(&self, w: &mut dyn Write) -> anyhow::Result<()> { + w.write_all(&self.0)?; + Ok(()) + } + fn read(r: &mut dyn Read) -> anyhow::Result<Self> { + let mut buf = Vec::new(); + r.read_to_end(&mut buf)?; + Ok(Self(buf)) + } +} +impl<'a> IntoIterator for &'a GraphicsPart { + type Item = GraphicsCommand; + type IntoIter = CommandIter<'a>; + fn into_iter(self) -> Self::IntoIter { + CommandIter(&self.0) + } +} + +pub struct CommandIter<'a>(&'a [u8]); + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum GraphicsCommand { + NoFill, + Fill(u8), + + NoStroke, + Stroke(u8), + + StrokeWidth(f32), + + StrokeDash(f32, f32), + + Line(Vec3), + Close, + + Unknown, +} + +impl<'a> Iterator for CommandIter<'a> { + type Item = GraphicsCommand; + fn next(&mut self) -> Option<Self::Item> { + if self.0.len() < 2 { + return None; + } + let step = self.0[0]; + let kind = self.0[1]; + self.0 = &self.0[2..]; + if self.0.len() < step as usize { + return None; + } + let data = &self.0[..step as usize]; + self.0 = &self.0[step as usize..]; + use GraphicsCommand::*; + Some(match (kind, step) { + (0, 0) => NoFill, + (0, 1) => Fill(data[0]), + (1, 0) => NoStroke, + (1, 1) => Stroke(data[0]), + (2, 4) => StrokeWidth(f32::from_le_bytes([data[0], data[1], data[2], data[3]])), + (3, 8) => StrokeDash( + f32::from_le_bytes([data[0], data[1], data[2], data[3]]), + f32::from_le_bytes([data[4], data[5], data[6], data[7]]), + ), + (4, 12) => Line(vec3( + f32::from_le_bytes([data[0], data[1], data[2], data[3]]), + f32::from_le_bytes([data[4], data[5], data[6], data[7]]), + f32::from_le_bytes([data[8], data[9], data[10], data[11]]), + )), + (5, 0) => Close, + _ => Unknown, + }) + } +} +impl GraphicsPart { + pub fn push(&mut self, cmd: GraphicsCommand) { + match cmd { + GraphicsCommand::NoFill => self.0.extend([0, 0]), + GraphicsCommand::Fill(n) => self.0.extend([1, 0, n]), + GraphicsCommand::NoStroke => self.0.extend([0, 1]), + GraphicsCommand::Stroke(n) => self.0.extend([1, 1, n]), + GraphicsCommand::StrokeWidth(w) => { + self.0.extend([4, 2]); + self.0.extend(w.to_le_bytes()); + } + GraphicsCommand::StrokeDash(a, b) => { + self.0.extend([8, 3]); + self.0.extend(a.to_le_bytes()); + self.0.extend(b.to_le_bytes()); + } + GraphicsCommand::Line(p) => { + self.0.extend([12, 4]); + self.0.extend(p.x.to_le_bytes()); + self.0.extend(p.y.to_le_bytes()); + self.0.extend(p.z.to_le_bytes()); + } + GraphicsCommand::Close => self.0.extend([0, 5]), + GraphicsCommand::Unknown => self.0.extend([0, 255]), + } + } +} + +#[test] +#[cfg(test)] +fn test_ser() { + use GraphicsCommand::*; + let cmds = [ + NoFill, + Line(Vec3::NEG_ONE), + Close, + Stroke(4), + StrokeWidth(5.), + StrokeDash(0.5, 1.), + ]; + + let mut p = GraphicsPart::default(); + for c in cmds { + p.push(c); + } + for (i, c) in p.into_iter().enumerate() { + assert_eq!(c, cmds[i], "index={i}"); + } +} diff --git a/shared/src/helper.rs b/shared/src/helper.rs index d6fbe66..1cd9aca 100644 --- a/shared/src/helper.rs +++ b/shared/src/helper.rs @@ -16,13 +16,47 @@ */ use crate::packets::{Data, Message, Object, Resource}; use anyhow::Result; -use glam::{Affine3A, Vec2, Vec3, Vec3A, Vec4}; +use glam::{Affine3A, DAffine3, DVec3, Vec2, Vec3, Vec3A, Vec4}; use std::{ borrow::Cow, io::{Read, Write}, marker::PhantomData, }; +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AABB { + pub min: Vec3, + pub max: Vec3, +} +impl AABB { + pub fn intersects(other: &Self) -> bool { + let _ = other; + todo!() + } + pub fn includes(other: &Self) -> bool { + let _ = other; + todo!() + } + pub fn contains(point: Vec3) -> bool { + let _ = point; + todo!() + } +} + +impl ReadWrite for AABB { + fn write(&self, w: &mut dyn Write) -> Result<()> { + self.min.write(w)?; + self.max.write(w)?; + Ok(()) + } + fn read(r: &mut dyn Read) -> Result<Self> { + Ok(Self { + min: Vec3::read(r)?, + max: Vec3::read(r)?, + }) + } +} + pub trait ReadWrite: Sized { fn write(&self, w: &mut dyn Write) -> Result<()>; fn read(r: &mut dyn Read) -> Result<Self>; @@ -45,6 +79,17 @@ impl ReadWrite for f32 { Ok(f32::from_le_bytes(buf)) } } +impl ReadWrite for f64 { + fn write(&self, w: &mut dyn Write) -> Result<()> { + w.write_all(&self.to_le_bytes())?; + Ok(()) + } + fn read(r: &mut dyn Read) -> Result<Self> { + let mut buf = [0; { size_of::<f64>() }]; + r.read_exact(&mut buf)?; + Ok(f64::from_le_bytes(buf)) + } +} impl ReadWrite for u32 { fn write(&self, w: &mut dyn Write) -> Result<()> { w.write_all(&self.to_le_bytes())?; @@ -450,6 +495,17 @@ impl ReadWrite for Vec3 { Ok(Self::new(f32::read(r)?, f32::read(r)?, f32::read(r)?)) } } +impl ReadWrite for DVec3 { + fn write(&self, w: &mut dyn Write) -> Result<()> { + self.x.write(w)?; + self.y.write(w)?; + self.z.write(w)?; + Ok(()) + } + fn read(r: &mut dyn Read) -> Result<Self> { + Ok(Self::new(f64::read(r)?, f64::read(r)?, f64::read(r)?)) + } +} impl ReadWrite for Affine3A { fn write(&self, w: &mut dyn Write) -> Result<()> { for v in self.to_cols_array() { @@ -461,6 +517,17 @@ impl ReadWrite for Affine3A { Ok(Self::from_cols_array(&[(); 12].try_map(|()| f32::read(r))?)) } } +impl ReadWrite for DAffine3 { + fn write(&self, w: &mut dyn Write) -> Result<()> { + for v in self.to_cols_array() { + v.write(w)? + } + Ok(()) + } + fn read(r: &mut dyn Read) -> Result<Self> { + Ok(Self::from_cols_array(&[(); 12].try_map(|()| f64::read(r))?)) + } +} impl<A: ReadWrite, B: ReadWrite> ReadWrite for (A, B) { fn write(&self, w: &mut dyn Write) -> Result<()> { self.0.write(w)?; diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 61828fc..f4a0408 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -22,12 +22,13 @@ )] #![allow(clippy::unit_arg, clippy::type_complexity)] +pub mod graphics; pub mod helper; +pub mod loader; pub mod packets; pub mod resources; pub mod respack; pub mod store; pub mod tree; -pub mod loader; pub use glam::{Affine3A, Mat3A, Vec2, Vec3A, Vec4, vec2, vec3a, vec4}; diff --git a/shared/src/packets.rs b/shared/src/packets.rs index c03f5e5..c1e9fc0 100644 --- a/shared/src/packets.rs +++ b/shared/src/packets.rs @@ -60,12 +60,12 @@ impl<T> Resource<T> { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Object(pub u128); -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub struct Data(pub Vec<u8>); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Message(pub String); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Packet { Connect(u128), Disconnect, @@ -174,6 +174,7 @@ impl ReadWrite for Packet { let packet_len = u32::read(r)?; Ok(match u8::read(r)? { 0x00 => Packet::Connect(read_u128(r)?), + 0xff => Packet::Disconnect, 0x01 => Packet::RequestResource(Resource::read(r)?), 0x02 => Packet::RespondResource(Resource::read(r)?, Data::read(r)?), 0x03 => Packet::Add(Object::read(r)?, Resource::read(r)?), @@ -249,3 +250,25 @@ impl Display for Object { impl Object { pub const ROOT: Object = Object(0); } + +#[test] +#[cfg(test)] +fn test_ser() { + use std::io::Cursor; + fn a(packet: Packet) { + let mut buf = Vec::new(); + packet.write(&mut buf).unwrap(); + let mut buf = Cursor::new(buf); + let p = Packet::read(&mut buf).unwrap(); + assert_eq!(packet, p); + assert_eq!(buf.get_ref().len(), buf.position() as usize) + } + a(Packet::Add(Object(5), Resource([5; 32], PhantomData))); + a(Packet::Disconnect); + a(Packet::Disconnect); + a(Packet::Chat( + Object(132), + Message("Hello world".to_string()), + )); + a(Packet::Parent(Object(4), Object(5))) +} diff --git a/shared/src/resources.rs b/shared/src/resources.rs index 4d14633..fa3c17b 100644 --- a/shared/src/resources.rs +++ b/shared/src/resources.rs @@ -14,9 +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 crate::{helper::ReadWrite, loader::ResLoader, packets::Resource}; +use crate::{ + graphics::GraphicsPart, + helper::{AABB, ReadWrite}, + loader::ResLoader, + packets::Resource, +}; use anyhow::Result; -use glam::{Affine3A, Vec2, Vec3A, Vec4}; +use glam::{Affine3A, DAffine3, Vec2, Vec3A, Vec4}; use log::warn; use std::{ borrow::Cow, @@ -75,10 +80,20 @@ macro_rules! resource_dicts { resource_dicts!( pub struct RespackEntry { c_prefab[multi]: Resource<Prefab>, + c_spatial_index[multi]: Resource<SpatialIndex>, + } + + pub struct SpatialIndex { + level: u32, + prefab: Resource<Prefab>, + child[multi]: (AABB, Resource<SpatialIndex>), } pub struct Prefab { name: String, + transform: DAffine3, + prefab[multi]: (Affine3A, Resource<Prefab>), + graphics[multi]: (Affine3A, Resource<GraphicsPart>), mesh[multi]: (Affine3A, Resource<MeshPart>), collision[multi]: (Affine3A, Resource<CollisionPart>), light[multi]: (Vec3A, Resource<LightPart>), diff --git a/shared/src/respack.rs b/shared/src/respack.rs index e724fd5..4ec3a70 100644 --- a/shared/src/respack.rs +++ b/shared/src/respack.rs @@ -25,6 +25,16 @@ use std::{ const MAGIC: &[u8; 16] = b"\x0f\x0cWEARE\x01RESPACK\x02"; +pub fn save_full_respack( + output: impl Write, + store: &ResourceStore, + entry: Option<Resource<RespackEntry>>, +) -> Result<()> { + let mut resources = Vec::new(); + store.iter(|r, _| resources.push(r))?; + save_respack(output, store, &resources, entry) +} + pub fn save_respack( mut output: impl Write, store: &ResourceStore, |