diff options
author | metamuffin <metamuffin@disroot.org> | 2025-04-07 12:39:44 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-04-07 12:39:44 +0200 |
commit | 7317cfa33db2140300ea5f053ba6ff5a547fc371 (patch) | |
tree | 62d3b282d983329527d2369acd04d55ebd9a178e | |
parent | 09ac749d9c1a602ea3d570d4c2ff5d3bdc08e045 (diff) | |
download | weareearth-7317cfa33db2140300ea5f053ba6ff5a547fc371.tar weareearth-7317cfa33db2140300ea5f053ba6ff5a547fc371.tar.bz2 weareearth-7317cfa33db2140300ea5f053ba6ff5a547fc371.tar.zst |
normals working
-rw-r--r-- | readme.md | 4 | ||||
-rw-r--r-- | src/main.rs | 8 | ||||
-rw-r--r-- | src/mesh.rs | 152 |
3 files changed, 142 insertions, 22 deletions
@@ -2,6 +2,10 @@ Imports 3D satellite data from Google Earth to wearechat resource packs. +This project made use of reverse engineering work done by +https://github.com/retroplasma/earth-reverse-engineering, it would not have been +possible without it. + ## License AGPL-3.0-only; See [COPYING](./COPYING) diff --git a/src/main.rs b/src/main.rs index 988a5ec..6bebf4b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,9 @@ pub mod mesh; use anyhow::{Result, bail}; -use glam::{DAffine3, DMat4, Vec3}; +use glam::{DAffine3, DMat4}; use log::{debug, error}; -use mesh::convert_mesh; +use mesh::{convert_mesh, decode_normal_table}; use prost::{Message, bytes::Bytes}; use proto::{BulkMetadata, NodeData, NodeMetadata, PlanetoidMetadata}; use reqwest::{ @@ -50,9 +50,11 @@ async fn main() -> Result<()> { let transform = DMat4::from_cols_slice(&node_data.matrix_globe_from_mesh); + let for_normals = decode_normal_table(node_data.for_normals()); + let mut meshes = Vec::new(); for m in node_data.meshes { - let mesh = convert_mesh(m, &store)?; + let mesh = convert_mesh(m, &store, &for_normals)?; meshes.push(( Affine3A::from_mat4((transform / 3_000_000.).as_mat4()), mesh, diff --git a/src/mesh.rs b/src/mesh.rs index d491a97..b387721 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -1,14 +1,69 @@ use crate::proto::Mesh; use anyhow::Result; -use glam::vec3a; +use glam::{Vec2, Vec3A, vec2, vec3a}; use weareshared::{packets::Resource, resources::MeshPart, store::ResourceStore}; -pub fn convert_mesh(m: Mesh, store: &ResourceStore) -> Result<Resource<MeshPart>> { +pub fn convert_mesh( + m: Mesh, + store: &ResourceStore, + normal_table: &[Vec3A], +) -> Result<Resource<MeshPart>> { + let index = decode_indices(&m.indices); + let positions = decode_positions(m.vertices()); + let texcoords = decode_texcoords(m.texture_coords()); + let normals = decode_normals(m.normals(), normal_table); + + + Ok(store.set(&MeshPart { + index: Some(store.set(&index)?), + va_position: Some(store.set(&positions)?), + va_texcoord: texcoords.map(|x| store.set(&x)).transpose()?, + va_normal: Some(store.set(&normals)?), + g_double_sided: Some(()), + ..Default::default() + })?) +} + +fn decode_texcoords(uv: &[u8]) -> Option<Vec<Vec2>> { + if uv.is_empty() { + return None; + } + let count = (uv.len() - 4) / 4; + let u_mod = 1 + u16::from_le_bytes([uv[0], uv[1]]); + let v_mod = 1 + u16::from_le_bytes([uv[2], uv[3]]); + let uv = &uv[4..]; + let (mut u, mut v) = (0u16, 0u16); + let mut texcoords = Vec::new(); + for i in 0..count { + u = (u + (uv[count * 0 + i] as u16) + ((uv[count * 2 + i] as u16) << 8)) % u_mod; + v = (v + (uv[count * 1 + i] as u16) + ((uv[count * 3 + i] as u16) << 8)) % v_mod; + texcoords.push(vec2( + u as f32 / u_mod as f32 + 0.5, + v as f32 / v_mod as f32 + 0.5, + )); + } + Some(texcoords) +} + +fn decode_positions(vert: &[u8]) -> Vec<Vec3A> { + let mut positions = Vec::new(); + let vertex_count = vert.len() / 3; + let (mut x, mut y, mut z) = (0u8, 0u8, 0u8); + for i in 0..vertex_count { + x = x.wrapping_add(vert[vertex_count * 0 + i]); + y = y.wrapping_add(vert[vertex_count * 1 + i]); + z = z.wrapping_add(vert[vertex_count * 2 + i]); + positions.push(vec3a(x as f32, y as f32, z as f32)); + } + positions +} + +fn decode_indices(ind: &[i32]) -> Vec<[u32; 3]> { let mut index_strip = Vec::new(); - let strip_len = m.indices[0]; + let strip_len = ind[0]; let mut zeros = 0; for i in 0..strip_len { - let val = m.indices[i as usize + 1]; + let val = ind[i as usize + 1]; index_strip.push((zeros - val) as u32); if val == 0 { zeros += 1; @@ -22,22 +77,81 @@ pub fn convert_mesh(m: Mesh, store: &ResourceStore) -> Result<Resource<MeshPart> index.push([index_strip[i + 0], index_strip[i + 2], index_strip[i + 1]]); } } + index +} - let mut positions = Vec::new(); - let vert = m.vertices(); - let vertex_count = vert.len() / 3; - let (mut x, mut y, mut z) = (0u8, 0u8, 0u8); - for i in 0..vertex_count { - x = x.wrapping_add(vert[vertex_count * 0 + i]); - y = y.wrapping_add(vert[vertex_count * 1 + i]); - z = z.wrapping_add(vert[vertex_count * 2 + i]); - positions.push(vec3a(x as f32, y as f32, z as f32)); +pub fn decode_normals(data: &[u8], normal_table: &[Vec3A]) -> Vec<Vec3A> { + let count = data.len() / 2; + let mut normals = Vec::new(); + for i in 0..count { + let ind = data[i] as usize + ((data[i + count] as usize) << 8); + normals.push(normal_table[ind]); } + normals +} - Ok(store.set(&MeshPart { - index: Some(store.set(&index)?), - va_position: Some(store.set(&positions)?), - g_double_sided: Some(()), - ..Default::default() - })?) +pub fn decode_normal_table(data: &[u8]) -> Vec<Vec3A> { + // Pretty much taken straight from client/rocktree_decoder.h:111, no idea what f1 does. + let f1 = |v: i32, l: i32| { + if 4 >= l { + (v << l) + (v & (1 << l) - 1) + } else if 6 >= l { + let r = 8 - l; + (v << l) + (v << l >> r) + (v << l >> r >> r) + (v << l >> r >> r >> r) + } else { + -(v & 1) + } + }; + let count = u16::from_le_bytes([data[0], data[1]]); + let s = data[2] as i32; + let data = &data[3..]; + + let mut output = Vec::new(); + + for i in 0..count { + let mut a = f1(data[(count * 0 + i) as usize] as i32, s) as f32 / 255.; + let f = f1(data[(count * 1 + i) as usize] as i32, s) as f32 / 255.; + + let mut b = a; + let mut c = f; + let mut g = c + b; + let mut h = b - c; + + let mut sign = 1.; + + if !(0.5 <= g && 1.5 >= g && -0.5 <= h && 0.5 >= h) { + sign = -1.; + if 0.5 >= g { + b = 0.5 - f; + c = 0.5 - a; + } else { + if 1.5 <= g { + b = 1.5 - f; + c = 1.5 - a; + } else { + if -0.5 >= h { + b = f - 0.5; + c = a + 0.5; + } else { + b = f + 0.5; + c = a - 0.5; + } + } + } + g = b + c; + h = b - c; + } + let fmin = |a: f32, b: f32| a.min(b); + a = fmin( + fmin(2. * g - 1., 3. - 2. * g), + fmin(2. * h + 1., 1. - 2. * h), + ) * sign; + + b = 2. * b - 1.; + c = 2. * c - 1.; + + output.push(vec3a(a, b, c).normalize_or_zero()); + } + + output } |