summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-01-19 16:44:06 +0100
committermetamuffin <metamuffin@disroot.org>2025-01-19 16:44:06 +0100
commit2d6f319dfccf6339ed1a3bbfb003b8b2dde82383 (patch)
tree4229b75a26d6e02d1eb15c84096c7020875e650e
parent2c737d660cab38fdf4ff3e940395df396a75f959 (diff)
downloadweareserver-2d6f319dfccf6339ed1a3bbfb003b8b2dde82383.tar
weareserver-2d6f319dfccf6339ed1a3bbfb003b8b2dde82383.tar.bz2
weareserver-2d6f319dfccf6339ed1a3bbfb003b8b2dde82383.tar.zst
client: normal maps
-rw-r--r--Cargo.lock1
-rw-r--r--client/Cargo.toml2
-rw-r--r--client/src/main.rs1
-rw-r--r--client/src/meshops.rs64
-rw-r--r--client/src/renderer.rs2
-rw-r--r--client/src/scene_prepare.rs191
-rw-r--r--client/src/scene_render.rs29
-rw-r--r--client/src/shader.wgsl29
-rw-r--r--shared/src/helper.rs32
-rw-r--r--shared/src/resources.rs3
-rw-r--r--world/src/mesh.rs11
11 files changed, 294 insertions, 71 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a9cf0fe..7759a9d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1160,6 +1160,7 @@ version = "0.29.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
dependencies = [
+ "bytemuck",
"serde",
]
diff --git a/client/Cargo.toml b/client/Cargo.toml
index 823160e..762e711 100644
--- a/client/Cargo.toml
+++ b/client/Cargo.toml
@@ -15,7 +15,7 @@ wgpu = "23.0.1"
winit = "0.30.8"
weareshared = { path = "../shared" }
rand = "0.9.0-beta.1"
-glam = "0.29.2"
+glam = { version = "0.29.2", features = ["bytemuck"] }
image = "0.25.5"
egui-wgpu = "0.30.0"
egui = { version = "0.30.0", features = ["bytemuck"] }
diff --git a/client/src/main.rs b/client/src/main.rs
index a1213d1..cd77266 100644
--- a/client/src/main.rs
+++ b/client/src/main.rs
@@ -29,6 +29,7 @@ pub mod ui;
pub mod window;
pub mod audio;
pub mod interfaces;
+pub mod meshops;
use anyhow::Result;
use clap::Parser;
diff --git a/client/src/meshops.rs b/client/src/meshops.rs
new file mode 100644
index 0000000..e76e02a
--- /dev/null
+++ b/client/src/meshops.rs
@@ -0,0 +1,64 @@
+use glam::{Vec2, Vec3, Vec3A};
+
+pub fn generate_normals(index: &[[u32; 3]], position: &[Vec3]) -> Vec<Vec3> {
+ let mut normal_denom = vec![0; position.len()];
+ let mut normal = vec![Vec3::ZERO; position.len()];
+
+ for &[a, b, c] in index {
+ let pos_a = position[a as usize];
+ let pos_b = position[b as usize];
+ let pos_c = position[c as usize];
+
+ // TODO is this right?
+ let norm = (pos_b - pos_a).cross(pos_c - pos_a).normalize();
+
+ normal[a as usize] += norm;
+ normal[b as usize] += norm;
+ normal[c as usize] += norm;
+ normal_denom[a as usize] += 1;
+ normal_denom[b as usize] += 1;
+ normal_denom[c as usize] += 1;
+ }
+ for (denom, tang) in normal_denom.iter().zip(normal.iter_mut()) {
+ *tang /= *denom as f32;
+ }
+
+ normal
+}
+
+pub fn generate_tangents(index: &[[u32; 3]], position: &[Vec3], texcoord: &[Vec2]) -> Vec<Vec3> {
+ let mut tangent_denom = vec![0; position.len()];
+ let mut tangent = vec![Vec3::ZERO; position.len()];
+
+ for &[a, b, c] in index {
+ let (pos_a, uv_a) = (position[a as usize], texcoord[a as usize]);
+ let (pos_b, uv_b) = (position[b as usize], texcoord[b as usize]);
+ let (pos_c, uv_c) = (position[c as usize], texcoord[c as usize]);
+
+ let pd_ba = pos_b - pos_a;
+ let pd_ca = pos_c - pos_a;
+ let td_ba = uv_b - uv_a;
+ let td_ca = uv_c - uv_a;
+
+ let face_tangent =
+ (pd_ba * td_ca.y - pd_ca * td_ba.y) * (td_ba.x * td_ca.y - td_ba.y * td_ca.x);
+
+ tangent[a as usize] += face_tangent;
+ tangent[b as usize] += face_tangent;
+ tangent[c as usize] += face_tangent;
+ tangent_denom[a as usize] += 1;
+ tangent_denom[b as usize] += 1;
+ tangent_denom[c as usize] += 1;
+ }
+ for (denom, tang) in tangent_denom.iter().zip(tangent.iter_mut()) {
+ *tang /= *denom as f32;
+ }
+
+ tangent
+}
+
+pub fn generate_texcoords(index: &[[u32; 3]], position: &[Vec3A]) -> Vec<Vec2> {
+ let _ = (index, position);
+ // TODO implement equirectangular projection
+ todo!()
+}
diff --git a/client/src/renderer.rs b/client/src/renderer.rs
index 01d6fc4..6a37320 100644
--- a/client/src/renderer.rs
+++ b/client/src/renderer.rs
@@ -73,7 +73,7 @@ impl<'a> Renderer<'a> {
&DeviceDescriptor {
required_features: Features::PUSH_CONSTANTS,
required_limits: Limits {
- max_push_constant_size: 64,
+ max_push_constant_size: 112,
max_vertex_buffers: 16,
..Limits::default()
},
diff --git a/client/src/scene_prepare.rs b/client/src/scene_prepare.rs
index 5c5102d..8941fa7 100644
--- a/client/src/scene_prepare.rs
+++ b/client/src/scene_prepare.rs
@@ -14,12 +14,13 @@
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::download::Downloader;
+use crate::{download::Downloader, meshops::generate_tangents};
use anyhow::Result;
use egui::{Grid, Widget};
+use glam::{Vec2, Vec3, Vec3A};
use humansize::DECIMAL;
use image::ImageReader;
-use log::debug;
+use log::{debug, trace};
use std::{
collections::{HashMap, HashSet},
hash::Hash,
@@ -85,10 +86,12 @@ pub struct ScenePreparer {
texture_bgl: BindGroupLayout,
textures: DemandMap<Resource<Image<'static>>, (Arc<Texture>, Arc<BindGroup>)>,
- placeholder_textures: DemandMap<bool, (Arc<Texture>, Arc<BindGroup>)>,
+ placeholder_textures: DemandMap<TextureIdentityKind, (Arc<Texture>, Arc<BindGroup>)>,
index_buffers: DemandMap<Resource<Vec<[u32; 3]>>, (Arc<Buffer>, u32)>,
- vertex_buffers: DemandMap<Resource<Vec<f32>>, (Arc<Buffer>, u32)>,
- placeholder_vertex_buffers: DemandMap<(u32, bool), Arc<Buffer>>,
+ vertex_buffers: DemandMap<Resource<Vec<f32>>, Arc<Buffer>>,
+ generated_tangent_buffers: DemandMap<TangentBufferSpec, Arc<Buffer>>,
+ generated_normal_buffers: DemandMap<NormalBufferSpec, Arc<Buffer>>,
+ generated_texcoord_buffers: DemandMap<TexcoordBufferSpec, Arc<Buffer>>,
mesh_parts: DemandMap<Resource<MeshPart>, Arc<RMeshPart>>,
pub prefabs: DemandMap<Resource<Prefab>, Arc<RPrefab>>,
}
@@ -99,12 +102,37 @@ pub struct RMeshPart {
pub index: Arc<Buffer>,
pub va_position: Arc<Buffer>,
pub va_normal: Arc<Buffer>,
+ pub va_tangent: Arc<Buffer>,
pub va_texcoord: Arc<Buffer>,
pub tex_albedo: Arc<BindGroup>,
pub tex_normal: Arc<BindGroup>,
pub double_sided: bool,
}
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+struct TangentBufferSpec {
+ index: Resource<Vec<[u32; 3]>>,
+ position: Resource<Vec<Vec3>>,
+ texcoord: Resource<Vec<Vec2>>,
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+struct NormalBufferSpec {
+ index: Resource<Vec<[u32; 3]>>,
+ position: Resource<Vec<Vec3A>>,
+}
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+struct TexcoordBufferSpec {
+ index: Resource<Vec<[u32; 3]>>,
+ position: Resource<Vec<Vec3A>>,
+}
+
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+enum TextureIdentityKind {
+ Normal,
+ Multiply,
+}
+
impl ScenePreparer {
pub fn new(device: Arc<Device>, queue: Arc<Queue>, texture_bgl: BindGroupLayout) -> Self {
Self {
@@ -114,8 +142,10 @@ impl ScenePreparer {
mesh_parts: DemandMap::new(),
prefabs: DemandMap::new(),
textures: DemandMap::new(),
- placeholder_vertex_buffers: DemandMap::new(),
placeholder_textures: DemandMap::new(),
+ generated_tangent_buffers: DemandMap::new(),
+ generated_normal_buffers: DemandMap::new(),
+ generated_texcoord_buffers: DemandMap::new(),
device,
queue,
}
@@ -140,19 +170,14 @@ impl ScenePreparer {
for pres in self.index_buffers.needed() {
let start = Instant::now();
if let Some(buf) = dls.try_get(pres.clone())? {
- let buf = buf
- .into_iter()
- .flatten()
- .flat_map(u32::to_le_bytes)
- .collect::<Vec<_>>();
let buffer = self.device.create_buffer_init(&BufferInitDescriptor {
- contents: &buf,
+ contents: bytemuck::cast_slice(buf.as_slice()),
label: None,
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
});
self.index_buffers.insert(
pres.clone(),
- (Arc::new(buffer), (buf.len() / size_of::<u32>()) as u32),
+ (Arc::new(buffer), (buf.len() * 3) as u32),
buf.len(),
);
debug!(
@@ -166,20 +191,13 @@ impl ScenePreparer {
for pres in self.vertex_buffers.needed() {
let start = Instant::now();
if let Some(buf) = dls.try_get(pres.clone())? {
- let buf = buf
- .into_iter()
- .flat_map(f32::to_le_bytes)
- .collect::<Vec<_>>();
let buffer = self.device.create_buffer_init(&BufferInitDescriptor {
- contents: &buf,
+ contents: bytemuck::cast_slice(buf.as_slice()),
label: None,
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
});
- self.vertex_buffers.insert(
- pres.clone(),
- (Arc::new(buffer), (buf.len() / size_of::<f32>()) as u32),
- buf.len(),
- );
+ self.vertex_buffers
+ .insert(pres.clone(), Arc::new(buffer), buf.len());
debug!(
"vertex attribute buffer created (len={}, took {:?}) {pres}",
buf.len() / size_of::<f32>(),
@@ -214,47 +232,62 @@ impl ScenePreparer {
num_done += 1;
}
}
- for variant in self.placeholder_textures.needed() {
- let v = if variant { 255 } else { 0 };
- let tex_bg = create_texture(
- &self.device,
- &self.queue,
- &self.texture_bgl,
- &[v, v, v, 255],
- 1,
- 1,
- );
- self.placeholder_textures.insert(variant, tex_bg, 4);
+ for kind in self.placeholder_textures.needed() {
+ let color = match kind {
+ TextureIdentityKind::Normal => [128, 128, 255, 255],
+ TextureIdentityKind::Multiply => [255, 255, 255, 255],
+ };
+ let tex_bg = create_texture(&self.device, &self.queue, &self.texture_bgl, &color, 1, 1);
+ self.placeholder_textures.insert(kind, tex_bg, 4);
num_done += 1;
}
+ for spec in self.generated_tangent_buffers.needed() {
+ if let (Some(index), Some(position), Some(texcoord)) = (
+ dls.try_get(spec.index.clone())?,
+ dls.try_get(spec.position.clone())?,
+ dls.try_get(spec.texcoord.clone())?,
+ ) {
+ let tangents = generate_tangents(&index, &position, &texcoord);
+ let buffer = self.device.create_buffer_init(&BufferInitDescriptor {
+ label: None,
+ usage: BufferUsages::COPY_DST | BufferUsages::VERTEX,
+ contents: bytemuck::cast_slice(tangents.as_slice()),
+ });
+ self.generated_tangent_buffers.insert(
+ spec,
+ Arc::new(buffer),
+ size_of::<f32>() * tangents.len() * 3,
+ );
+ }
+ }
for pres in self.mesh_parts.needed() {
let start = Instant::now();
if let Some(part) = dls.try_get(pres.clone())? {
if let (Some(indexres), Some(positionres)) = (part.index, part.va_position) {
- let index = self.index_buffers.try_get(indexres);
+ let index = self.index_buffers.try_get(indexres.clone());
let position = self
.vertex_buffers
.try_get(Resource(positionres.0, PhantomData));
- let vertex_count = position.as_ref().map(|(_, c)| *c / 3);
- let normal = if let Some(res) = part.va_normal {
- self.vertex_buffers
- .try_get(Resource(res.0, PhantomData))
- .map(|e| e.0)
+ let normal = if let Some(res) = part.va_normal.clone() {
+ self.vertex_buffers.try_get(Resource(res.0, PhantomData))
} else {
- vertex_count
- .map(|vc| self.placeholder_vertex_buffers.try_get((vc * 4, false)))
- .flatten()
+ todo!()
};
- let texcoord = if let Some(res) = part.va_texcoord {
- self.vertex_buffers
- .try_get(Resource(res.0, PhantomData))
- .map(|e| e.0)
+ let texcoord = if let Some(res) = part.va_texcoord.clone() {
+ self.vertex_buffers.try_get(Resource(res.0, PhantomData))
} else {
- vertex_count
- .map(|vc| self.placeholder_vertex_buffers.try_get((vc * 2, false)))
- .flatten()
+ todo!()
+ };
+ let tangent = if let Some(res) = part.va_tangent.clone() {
+ self.vertex_buffers.try_get(Resource(res.0, PhantomData))
+ } else {
+ self.generated_tangent_buffers.try_get(TangentBufferSpec {
+ index: indexres,
+ position: Resource(positionres.0, PhantomData),
+ texcoord: part.va_texcoord.expect("TODO"),
+ })
};
let mut tex_albedo = None;
@@ -263,7 +296,10 @@ impl ScenePreparer {
tex_albedo = Some(bg)
}
} else {
- if let Some((_tex, bg)) = self.placeholder_textures.try_get(true) {
+ if let Some((_tex, bg)) = self
+ .placeholder_textures
+ .try_get(TextureIdentityKind::Multiply)
+ {
tex_albedo = Some(bg)
}
}
@@ -273,20 +309,25 @@ impl ScenePreparer {
tex_normal = Some(bg)
}
} else {
- if let Some((_tex, bg)) = self.placeholder_textures.try_get(false) {
+ if let Some((_tex, bg)) = self
+ .placeholder_textures
+ .try_get(TextureIdentityKind::Normal)
+ {
tex_normal = Some(bg)
}
}
if let (
- Some(va_normal),
Some((index, index_count)),
+ Some(va_normal),
+ Some(va_tangent),
Some(va_texcoord),
- Some((va_position, _)),
+ Some(va_position),
Some(tex_normal),
Some(tex_albedo),
- ) = (normal, index, texcoord, position, tex_normal, tex_albedo)
- {
+ ) = (
+ index, normal, tangent, texcoord, position, tex_normal, tex_albedo,
+ ) {
debug!("part created (took {:?}) {pres}", start.elapsed());
self.mesh_parts.insert(
pres,
@@ -294,6 +335,7 @@ impl ScenePreparer {
index_count,
index,
va_normal,
+ va_tangent,
va_position,
va_texcoord,
tex_albedo,
@@ -307,6 +349,7 @@ impl ScenePreparer {
}
}
}
+ self.print_missing();
Ok(num_done)
}
}
@@ -373,6 +416,34 @@ impl<K, V> Widget for &DemandMap<K, V> {
ui.response()
}
}
+impl ScenePreparer {
+ pub fn print_missing(&self) {
+ fn visit<K, V>(name: &str, m: &DemandMap<K, V>)
+ where
+ K: Clone,
+ K: Hash,
+ K: std::cmp::Eq,
+ V: Clone,
+ {
+ let nl = m.needed().len();
+ if nl > 0 {
+ trace!("{name}: need {nl}")
+ }
+ }
+ visit("prefabs", &self.prefabs);
+ visit("mesh_parts", &self.mesh_parts);
+ visit("vertex_buffers", &self.vertex_buffers);
+ visit("index_buffers", &self.index_buffers);
+ visit("placeholder_textures", &self.placeholder_textures);
+ visit("generated_tangent_buffers", &self.generated_tangent_buffers);
+ visit("generated_normal_buffers", &self.generated_normal_buffers);
+ visit(
+ "generated_texcoord_buffers",
+ &self.generated_texcoord_buffers,
+ );
+ visit("textures", &self.textures);
+ }
+}
impl Widget for &ScenePreparer {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
@@ -389,8 +460,12 @@ impl Widget for &ScenePreparer {
self.index_buffers.ui(ui);
ui.label("placeholder_textures");
self.placeholder_textures.ui(ui);
- ui.label("placeholder_vertex_buffers");
- self.placeholder_vertex_buffers.ui(ui);
+ ui.label("generated_tangent_buffers");
+ self.generated_tangent_buffers.ui(ui);
+ ui.label("generated_normal_buffers");
+ self.generated_normal_buffers.ui(ui);
+ ui.label("generated_texcoord_buffers");
+ self.generated_texcoord_buffers.ui(ui);
ui.label("textures");
self.textures.ui(ui);
})
diff --git a/client/src/scene_render.rs b/client/src/scene_render.rs
index 03b077c..3ec96b3 100644
--- a/client/src/scene_render.rs
+++ b/client/src/scene_render.rs
@@ -65,7 +65,7 @@ impl ScenePipeline {
label: None,
bind_group_layouts: &[&bind_group_layout, &bind_group_layout],
push_constant_ranges: &[PushConstantRange {
- range: 0..(4 * 4 * size_of::<f32>() as u32),
+ range: 0..((4 * 4 + 3 * 4) * size_of::<f32>() as u32),
stages: ShaderStages::VERTEX,
}],
});
@@ -87,6 +87,7 @@ impl ScenePipeline {
module: &module,
entry_point: Some("vs_main"),
buffers: &[
+ // position
VertexBufferLayout {
step_mode: VertexStepMode::Vertex,
array_stride: 3 * size_of::<f32>() as u64,
@@ -96,6 +97,7 @@ impl ScenePipeline {
shader_location: 0,
}],
},
+ // normal
VertexBufferLayout {
step_mode: VertexStepMode::Vertex,
array_stride: 3 * size_of::<f32>() as u64,
@@ -105,13 +107,24 @@ impl ScenePipeline {
shader_location: 1,
}],
},
+ // tangent
+ VertexBufferLayout {
+ step_mode: VertexStepMode::Vertex,
+ array_stride: 3 * size_of::<f32>() as u64,
+ attributes: &[VertexAttribute {
+ format: VertexFormat::Float32x3,
+ offset: 0,
+ shader_location: 2,
+ }],
+ },
+ // texcoord
VertexBufferLayout {
step_mode: VertexStepMode::Vertex,
array_stride: 2 * size_of::<f32>() as u64,
attributes: &[VertexAttribute {
format: VertexFormat::Float32x2,
offset: 0,
- shader_location: 2,
+ shader_location: 3,
}],
},
],
@@ -195,6 +208,14 @@ impl ScenePipeline {
* Mat4::from_translation(affine.translation.into())
* Mat4::from_mat3a(affine.matrix3);
let projection = part_projection.to_cols_array().map(|v| v.to_le_bytes());
+ let mb = affine.matrix3.to_cols_array(); // TODO apply object rotation
+ // add padding for gpu mat3x3 repr
+ let model_basis = [
+ mb[0], mb[1], mb[2], 0., //
+ mb[3], mb[4], mb[5], 0., //
+ mb[6], mb[7], mb[8], 0., //
+ ];
+ let model_basis = bytemuck::cast_slice(&model_basis);
let pipeline = if part.double_sided {
&self.pipeline_no_cull
@@ -206,10 +227,12 @@ impl ScenePipeline {
rpass.set_bind_group(0, &*part.tex_albedo, &[]);
rpass.set_bind_group(1, &*part.tex_normal, &[]);
rpass.set_push_constants(ShaderStages::VERTEX, 0, projection.as_flattened());
+ rpass.set_push_constants(ShaderStages::VERTEX, 64, model_basis);
rpass.set_index_buffer(part.index.slice(..), IndexFormat::Uint32);
rpass.set_vertex_buffer(0, part.va_position.slice(..));
rpass.set_vertex_buffer(1, part.va_normal.slice(..));
- rpass.set_vertex_buffer(2, part.va_texcoord.slice(..));
+ rpass.set_vertex_buffer(2, part.va_tangent.slice(..));
+ rpass.set_vertex_buffer(3, part.va_texcoord.slice(..));
rpass.draw_indexed(0..part.index_count, 0, 0..1);
}
}
diff --git a/client/src/shader.wgsl b/client/src/shader.wgsl
index 993c9b5..e8d3276 100644
--- a/client/src/shader.wgsl
+++ b/client/src/shader.wgsl
@@ -16,39 +16,54 @@
struct VertexIn {
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
- @location(2) texcoord: vec2<f32>,
+ @location(2) tangent: vec3<f32>, // TODO maybe compress this
+ @location(3) texcoord: vec2<f32>,
}
struct VertexOut {
@builtin(position) clip: vec4<f32>,
@location(0) normal: vec3<f32>,
- @location(1) texcoord: vec2<f32>,
+ @location(1) tangent: vec3<f32>,
+ @location(2) texcoord: vec2<f32>,
+}
+
+struct PushConst {
+ modelview: mat4x4<f32>,
+ model_basis: mat3x3<f32>,
}
@group(0) @binding(0) var tex_albedo: texture_2d<f32>;
@group(0) @binding(1) var tex_albedo_sampler: sampler;
@group(1) @binding(0) var tex_normal: texture_2d<f32>;
@group(1) @binding(1) var tex_normal_sampler: sampler;
-var<push_constant> project: mat4x4<f32>;
+ var<push_constant> pc: PushConst;
const LIGHT: vec3<f32> = vec3(0.64, 0.64, 0.64);
@vertex
fn vs_main(vi: VertexIn) -> VertexOut {
- var clip = project * vec4(vi.position, 1.);
- let vo = VertexOut(clip, vi.normal, vi.texcoord);
+ let clip = pc.modelview * vec4(vi.position, 1.);
+ let vo = VertexOut(
+ clip,
+ normalize(pc.model_basis * vi.normal),
+ normalize(pc.model_basis * vi.tangent),
+ vi.texcoord
+ );
return vo;
}
+
@fragment
fn fs_main(vo: VertexOut) -> @location(0) vec4<f32> {
let t_albedo = textureSample(tex_albedo, tex_albedo_sampler, vo.texcoord);
let t_normal = textureSample(tex_normal, tex_normal_sampler, vo.texcoord);
+ let tangent_basis = mat3x3(vo.tangent, cross(vo.tangent, vo.normal), vo.normal);
+ let normal = tangent_basis * normalize(t_normal.rgb * 2. - 1.);
- let lighting = mix(1., saturate(dot(LIGHT, vo.normal)), 0.9);
+ let lighting = mix(1., saturate(dot(LIGHT, normal)), 0.9);
let alpha = t_albedo.a;
let color = t_albedo.rgb * lighting;
- // let color = vo.normal + t_normal.rgb;
+ // let color = normal * 0.5 + 0.5;
if fract(dot(sin(vo.clip * 123.) * 1213., vec4(3., 2., 1., 4.))) > alpha {
discard;
diff --git a/shared/src/helper.rs b/shared/src/helper.rs
index eb1f321..d46a830 100644
--- a/shared/src/helper.rs
+++ b/shared/src/helper.rs
@@ -16,7 +16,7 @@
*/
use crate::packets::{Data, Object, Resource};
use anyhow::Result;
-use glam::{Affine3A, Vec2, Vec3A};
+use glam::{Affine3A, Vec2, Vec3, Vec3A};
use std::{
borrow::Cow,
io::{Read, Write},
@@ -103,6 +103,25 @@ impl ReadWrite for Vec<Vec3A> {
.collect())
}
}
+impl ReadWrite for Vec<Vec3> {
+ fn write(&self, w: &mut dyn Write) -> Result<()> {
+ for e in self {
+ e.write(w)?;
+ }
+ Ok(())
+ }
+ fn read(r: &mut dyn Read) -> Result<Self> {
+ let mut buf = Vec::new();
+ r.read_to_end(&mut buf)?;
+ Ok(buf
+ .into_iter()
+ .array_chunks::<{ size_of::<f32>() }>()
+ .map(f32::from_be_bytes)
+ .array_chunks::<3>()
+ .map(Vec3::from_array)
+ .collect())
+ }
+}
impl ReadWrite for Vec<Vec2> {
fn write(&self, w: &mut dyn Write) -> Result<()> {
for e in self {
@@ -249,6 +268,17 @@ impl ReadWrite for Vec3A {
Ok(Self::new(f32::read(r)?, f32::read(r)?, f32::read(r)?))
}
}
+impl ReadWrite for Vec3 {
+ 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(f32::read(r)?, f32::read(r)?, f32::read(r)?))
+ }
+}
impl ReadWrite for Affine3A {
fn write(&self, w: &mut dyn Write) -> Result<()> {
for v in self.to_cols_array() {
diff --git a/shared/src/resources.rs b/shared/src/resources.rs
index b2708cb..54a34b8 100644
--- a/shared/src/resources.rs
+++ b/shared/src/resources.rs
@@ -64,6 +64,7 @@ pub struct MeshPart {
pub g_double_sided: Option<()>,
pub va_position: Option<Resource<Vec<Vec3A>>>,
pub va_normal: Option<Resource<Vec<Vec3A>>>,
+ pub va_tangent: Option<Resource<Vec<Vec3A>>>,
pub va_texcoord: Option<Resource<Vec<Vec2>>>,
pub va_roughness: Option<Resource<Vec<f32>>>,
pub va_metallic: Option<Resource<Vec<f32>>>,
@@ -240,6 +241,7 @@ impl ReadWrite for MeshPart {
write_kv_opt(w, b"g_double_sided", &self.g_double_sided)?;
write_kv_opt(w, b"va_position", &self.va_position)?;
write_kv_opt(w, b"va_normal", &self.va_normal)?;
+ write_kv_opt(w, b"va_tangent", &self.va_tangent)?;
write_kv_opt(w, b"va_texcoord", &self.va_texcoord)?;
write_kv_opt(w, b"va_roughness", &self.va_roughness)?;
write_kv_opt(w, b"va_metallic", &self.va_metallic)?;
@@ -272,6 +274,7 @@ impl ReadWrite for MeshPart {
b"g_double_sided" => Ok(s.g_double_sided = 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_tangent" => Ok(s.va_tangent = 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)?)),
diff --git a/world/src/mesh.rs b/world/src/mesh.rs
index 79e0edc..393a91c 100644
--- a/world/src/mesh.rs
+++ b/world/src/mesh.rs
@@ -64,6 +64,16 @@ pub fn import_mesh(
})
.transpose()?;
+ let va_tangent = reader
+ .read_tangents()
+ .map(|iter| {
+ // TODO dont ignore handedness
+ let a = iter.map(|[x, y, z, _h]| vec3a(x, y, z)).collect::<Vec<_>>();
+ debug!("{} vertex tangents", a.len());
+ Ok::<_, anyhow::Error>(store.set(&a)?)
+ })
+ .transpose()?;
+
let va_texcoord = reader
.read_tex_coords(0)
.map(|iter| {
@@ -332,6 +342,7 @@ pub fn import_mesh(
g_double_sided,
va_position,
va_normal,
+ va_tangent,
va_texcoord,
va_albedo,
va_alpha,