first pass
This commit is contained in:
97
geometry.js
Normal file
97
geometry.js
Normal file
@@ -0,0 +1,97 @@
|
||||
// geometry.js
|
||||
const FACES = {
|
||||
down: { n: [0, -1, 0], c: [[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1]] },
|
||||
up: { n: [0, 1, 0], c: [[0, 1, 1], [1, 1, 1], [1, 1, 0], [0, 1, 0]] },
|
||||
north: { n: [0, 0, -1], c: [[1, 0, 0], [0, 0, 0], [0, 1, 0], [1, 1, 0]] },
|
||||
south: { n: [0, 0, 1], c: [[0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]] },
|
||||
west: { n: [-1, 0, 0], c: [[0, 0, 0], [0, 0, 1], [0, 1, 1], [0, 1, 0]] },
|
||||
east: { n: [1, 0, 0], c: [[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]] }
|
||||
};
|
||||
|
||||
export function resolveTexture(model, ref) {
|
||||
if (!ref) return null;
|
||||
if (ref[0] !== '#') return ref;
|
||||
|
||||
let key = ref.slice(1);
|
||||
let val = model.textures?.[key];
|
||||
|
||||
// Follow variable chain
|
||||
while (typeof val === 'string' && val[0] === '#') {
|
||||
key = val.slice(1);
|
||||
val = model.textures?.[key];
|
||||
}
|
||||
|
||||
// Handle modern object-style textures
|
||||
if (val && typeof val === 'object' && val.sprite) {
|
||||
val = val.sprite;
|
||||
}
|
||||
|
||||
return val || null;
|
||||
}
|
||||
|
||||
export function buildGeometry(model, atlas) {
|
||||
const pos = [];
|
||||
const norm = [];
|
||||
const uv = [];
|
||||
const tint = [];
|
||||
const idx = [];
|
||||
|
||||
let vOffset = 0;
|
||||
|
||||
for (const el of model.elements || []) {
|
||||
// Map 0-16 voxel scale to 0.0-1.0 world scale
|
||||
const [fx, fy, fz] = el.from.map(x => x / 16);
|
||||
const [tx, ty, tz] = el.to.map(x => x / 16);
|
||||
const size = [tx - fx, ty - fy, tz - fz];
|
||||
|
||||
for (const [name, face] of Object.entries(el.faces || {})) {
|
||||
const tmpl = FACES[name];
|
||||
const texPath = resolveTexture(model, face.texture);
|
||||
const a = texPath ? atlas.map.get(texPath) : null;
|
||||
|
||||
// Skip faces with missing textures in the atlas
|
||||
if (!a) continue;
|
||||
|
||||
// Basic UV fallback if omitted: this is a placeholder.
|
||||
// Real implementation needs mapping based on face direction and element bounds.
|
||||
const u1 = (face.uv?.[0] ?? 0) / 16;
|
||||
const v1 = (face.uv?.[1] ?? 0) / 16;
|
||||
const u2 = (face.uv?.[2] ?? 16) / 16;
|
||||
const v2 = (face.uv?.[3] ?? 16) / 16;
|
||||
|
||||
const au1 = a.u + u1 * a.du;
|
||||
const au2 = a.u + u2 * a.du;
|
||||
// WebGL wants V flipped natively compared to Three.js canvas
|
||||
const av1 = a.v + v1 * a.dv;
|
||||
const av2 = a.v + v2 * a.dv;
|
||||
|
||||
const tintable = face.tintindex !== undefined ? 1.0 : 0.0;
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const [cx, cy, cz] = tmpl.c[i];
|
||||
|
||||
// TODO: Apply el.rotation matrix here later
|
||||
pos.push(fx + cx * size[0], fy + cy * size[1], fz + cz * size[2]);
|
||||
norm.push(...tmpl.n);
|
||||
tint.push(tintable);
|
||||
}
|
||||
|
||||
uv.push(au1, av1, au2, av1, au2, av2, au1, av2);
|
||||
|
||||
// Push 2 triangles per quad
|
||||
idx.push(
|
||||
vOffset, vOffset + 1, vOffset + 2,
|
||||
vOffset, vOffset + 2, vOffset + 3
|
||||
);
|
||||
vOffset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
positions: new Float32Array(pos),
|
||||
normals: new Float32Array(norm),
|
||||
uvs: new Float32Array(uv),
|
||||
tints: new Float32Array(tint),
|
||||
indices: new Uint16Array(idx)
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user