aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-04-07 12:39:44 +0200
committermetamuffin <metamuffin@disroot.org>2025-04-07 12:39:44 +0200
commit7317cfa33db2140300ea5f053ba6ff5a547fc371 (patch)
tree62d3b282d983329527d2369acd04d55ebd9a178e
parent09ac749d9c1a602ea3d570d4c2ff5d3bdc08e045 (diff)
downloadweareearth-7317cfa33db2140300ea5f053ba6ff5a547fc371.tar
weareearth-7317cfa33db2140300ea5f053ba6ff5a547fc371.tar.bz2
weareearth-7317cfa33db2140300ea5f053ba6ff5a547fc371.tar.zst
normals working
-rw-r--r--readme.md4
-rw-r--r--src/main.rs8
-rw-r--r--src/mesh.rs152
3 files changed, 142 insertions, 22 deletions
diff --git a/readme.md b/readme.md
index a6cfabb..fe38147 100644
--- a/readme.md
+++ b/readme.md
@@ -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
}