summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/src/download.rs17
-rw-r--r--client/src/scene_prepare.rs52
-rw-r--r--client/src/scene_render.rs6
-rw-r--r--doc/protocol.md26
-rw-r--r--server/src/main.rs17
-rw-r--r--shared/src/packets.rs99
-rw-r--r--shared/src/resources.rs73
-rw-r--r--shared/src/store.rs18
-rw-r--r--shared/src/tree.rs11
-rw-r--r--world/src/main.rs267
-rw-r--r--world/src/mesh.rs262
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))
+ })
+}