summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-03-27 18:17:34 +0100
committermetamuffin <metamuffin@disroot.org>2025-03-27 18:17:34 +0100
commit40afd6fa42ad937148c6812b8df8efff6c78e4b5 (patch)
tree09b5200598d9a9d93757745461fe34fbe28e1a81
parent9f16507564354422b64e437e547d8403b22b70c7 (diff)
downloadweareserver-40afd6fa42ad937148c6812b8df8efff6c78e4b5.tar
weareserver-40afd6fa42ad937148c6812b8df8efff6c78e4b5.tar.bz2
weareserver-40afd6fa42ad937148c6812b8df8efff6c78e4b5.tar.zst
spatial tree, graphics and some unit tests
-rw-r--r--import/src/main.rs5
-rw-r--r--shared/src/graphics.rs129
-rw-r--r--shared/src/helper.rs69
-rw-r--r--shared/src/lib.rs3
-rw-r--r--shared/src/packets.rs29
-rw-r--r--shared/src/resources.rs19
-rw-r--r--shared/src/respack.rs10
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,