fixes texturing

This commit is contained in:
jack
2026-08-31 00:14:52 +01:00
parent 04d3409297
commit d3d1c12b28
+54 -2
View File
@@ -158,8 +158,8 @@ function parseGeometry(chunk) {
// One draw group per material, so each group can get its own texture. // One draw group per material, so each group can get its own texture.
const order = [...Array(numTriangles).keys()].sort((x, y) => triMaterial[x] - triMaterial[y]); const order = [...Array(numTriangles).keys()].sort((x, y) => triMaterial[x] - triMaterial[y]);
const indices = new Uint32Array(numTriangles * 3); let indices = new Uint32Array(numTriangles * 3);
const groups = []; let groups = [];
let current = null; let current = null;
order.forEach((tri, slot) => { order.forEach((tri, slot) => {
indices[slot * 3] = tris[tri * 3]; indices[slot * 3] = tris[tri * 3];
@@ -173,6 +173,19 @@ function parseGeometry(chunk) {
current.count += 3; current.count += 3;
}); });
// Many GTA:SA exporters leave the per-triangle material index at zero and put
// the real split in the BINMESH extension instead. The struct then yields a
// single group and the whole model renders with material 0's texture, so
// prefer BINMESH whenever it describes more submeshes than the struct did.
const binMesh = extension && extension.find(RW.BINMESH);
if (binMesh) {
const split = parseBinMesh(binMesh, numVertices);
if (split && split.groups.length > groups.length) {
indices = split.indices;
groups = split.groups;
}
}
return { return {
numVertices, numVertices,
positions, positions,
@@ -187,6 +200,45 @@ function parseGeometry(chunk) {
}; };
} }
// The BINMESH extension holds the render-ready index lists, one submesh per
// material. flags bit 0 means the indices are tristrips rather than triangle
// lists; degenerate triangles are the standard way strips stitch together, so
// they are dropped rather than emitted as zero area faces.
function parseBinMesh(chunk, numVertices) {
const r = chunk.reader();
if (r.remaining < 12) return null;
const strip = r.u32() & 1;
const numMeshes = r.u32();
r.u32(); // total index count, recomputed below
const out = [];
const groups = [];
for (let i = 0; i < numMeshes; i++) {
if (r.remaining < 8) return null;
const count = r.u32();
const material = r.u32();
if (r.remaining < count * 4) return null;
const list = new Uint32Array(count);
for (let k = 0; k < count; k++) list[k] = r.u32();
const start = out.length;
if (strip) {
for (let k = 0; k + 2 < count; k++) {
const a = list[k], b = list[k + (k & 1 ? 2 : 1)], c = list[k + (k & 1 ? 1 : 2)];
if (a === b || b === c || a === c) continue; // strip stitch
out.push(a, b, c);
}
} else {
for (let k = 0; k + 2 < count; k += 3) out.push(list[k], list[k + 1], list[k + 2]);
}
if (out.length > start) groups.push({ start, count: out.length - start, material });
}
if (!groups.length) return null;
for (const index of out) {
if (index >= numVertices) return null; // not indices we can trust
}
return { indices: Uint32Array.from(out), groups };
}
export function parseDFF(buffer) { export function parseDFF(buffer) {
const view = new DataView(buffer); const view = new DataView(buffer);
let clump = new Chunk(view, 0); let clump = new Chunk(view, 0);