fix(3d): rewrite wrl fallback parser to maintain hierarchy and fix phase extraction

This commit is contained in:
2026-09-24 17:47:45 +00:00
parent b226fc09fe
commit bfb2464c03
+86 -54
View File
@@ -191,71 +191,103 @@ function parseVrmlDirectFallback(
const group = new THREE.Group(); const group = new THREE.Group();
group.name = 'WRL_Direct_Fallback_Root'; group.name = 'WRL_Direct_Fallback_Root';
const defRegex = /DEF\s+([^\s\{\[\(\)]+)[\s\S]*?(?:point\s*\[([\s\S]*?)\])[\s\S]*?(?:coordIndex\s*\[([\s\S]*?)\])/gi; const tokenRegex = /(DEF\s+([^\s\{\[\(\)]+))|(\{)|(\})|(point\s*\[([\s\S]*?)\])|(coordIndex\s*\[([\s\S]*?)\])/gi;
let match: RegExpExecArray | null; let match: RegExpExecArray | null;
const stack: { name: string; group: THREE.Group }[] = [{ name: 'ROOT', group }];
let currentGroup = group;
let lastDef: string | null = null;
let currentPoints: number[] | null = null;
let count = 0; let count = 0;
while ((match = defRegex.exec(vrmlText)) !== null) { while ((match = tokenRegex.exec(vrmlText)) !== null) {
count++; if (match[1]) {
const rawDefName = match[1].trim(); lastDef = match[2];
const originalName = nameMap.get(rawDefName) || rawDefName; } else if (match[3]) {
const pointStr = match[2]; const newGroup = new THREE.Group();
const indexStr = match[3]; if (lastDef) {
newGroup.name = nameMap.get(lastDef) || lastDef;
const rawCoords = pointStr.trim().split(/[\s,]+/).filter(Boolean).map(Number); lastDef = null;
if (rawCoords.length < 9) continue; } else {
newGroup.name = 'Unnamed_Group';
const vertices: [number, number, number][] = [];
for (let i = 0; i < rawCoords.length; i += 3) {
if (!isNaN(rawCoords[i]) && !isNaN(rawCoords[i + 1]) && !isNaN(rawCoords[i + 2])) {
vertices.push([rawCoords[i], rawCoords[i + 1], rawCoords[i + 2]]);
} }
} currentGroup.add(newGroup);
stack.push({ name: newGroup.name, group: newGroup });
currentGroup = newGroup;
} else if (match[4]) {
if (stack.length > 1) {
stack.pop();
currentGroup = stack[stack.length - 1].group;
}
} else if (match[5]) {
const rawCoords = match[6].trim().split(/[\s,]+/).filter(Boolean).map(Number);
if (rawCoords.length >= 9) {
currentPoints = rawCoords;
}
} else if (match[7]) {
if (!currentPoints) continue;
if (vertices.length === 0) continue; const indexStr = match[8];
const rawIndices = indexStr.trim().split(/[\s,]+/).filter(Boolean).map(Number);
const rawIndices = indexStr.trim().split(/[\s,]+/).filter(Boolean).map(Number); const vertices: [number, number, number][] = [];
const triangleIndices: number[] = []; for (let i = 0; i < currentPoints.length; i += 3) {
let currentPoly: number[] = []; if (!isNaN(currentPoints[i]) && !isNaN(currentPoints[i + 1]) && !isNaN(currentPoints[i + 2])) {
vertices.push([currentPoints[i], currentPoints[i + 1], currentPoints[i + 2]]);
}
}
for (const idx of rawIndices) { const triangleIndices: number[] = [];
if (idx === -1) { let currentPoly: number[] = [];
if (currentPoly.length >= 3) {
for (let p = 1; p < currentPoly.length - 1; p++) { for (const idx of rawIndices) {
triangleIndices.push(currentPoly[0], currentPoly[p], currentPoly[p + 1]); if (idx === -1) {
if (currentPoly.length >= 3) {
for (let p = 1; p < currentPoly.length - 1; p++) {
triangleIndices.push(currentPoly[0], currentPoly[p], currentPoly[p + 1]);
}
}
currentPoly = [];
} else {
currentPoly.push(idx);
}
}
if (currentPoly.length >= 3) {
for (let p = 1; p < currentPoly.length - 1; p++) {
triangleIndices.push(currentPoly[0], currentPoly[p], currentPoly[p + 1]);
}
}
if (triangleIndices.length === 0) continue;
const positions: number[] = [];
for (const tIdx of triangleIndices) {
const v = vertices[tIdx];
if (v) positions.push(v[0], v[1], v[2]);
}
if (positions.length > 0) {
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.computeVertexNormals();
const mesh = new THREE.Mesh(geometry);
let meshName = `PECA-${++count}`;
for (let i = stack.length - 1; i >= 0; i--) {
if (stack[i].name && stack[i].name !== 'Unnamed_Group' && stack[i].name !== 'ROOT') {
meshName = stack[i].name;
break;
} }
} }
currentPoly = []; mesh.name = meshName;
} else { mesh.castShadow = true;
currentPoly.push(idx); mesh.receiveShadow = true;
currentGroup.add(mesh);
} }
} }
if (currentPoly.length >= 3) {
for (let p = 1; p < currentPoly.length - 1; p++) {
triangleIndices.push(currentPoly[0], currentPoly[p], currentPoly[p + 1]);
}
}
if (triangleIndices.length === 0) continue;
const positions: number[] = [];
for (const tIdx of triangleIndices) {
const v = vertices[tIdx];
if (v) positions.push(v[0], v[1], v[2]);
}
if (positions.length === 0) continue;
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.computeVertexNormals();
const mesh = new THREE.Mesh(geometry);
mesh.name = originalName;
mesh.castShadow = true;
mesh.receiveShadow = true;
group.add(mesh);
} }
if (count === 0) { if (count === 0) {