class ImportBlendMesh { static eps = 1.0 / 32767; static run = (path: string, replaceExisting = true) => { Data.getBlob(path, (b: ArrayBuffer) => { let bl = new ParserBlend(b); if (bl.dna == null) { Console.error(Strings.error3()); return; } let obs = bl.get("Object"); if (obs == null || obs.length == 0) { ImportMesh.makeMesh(null, path); return; } let first = true; for (let ob of obs) { if (ob.get("type") != 1) continue; let name: string = ob.get("id").get("name"); name = name.substring(2, name.length); let m: any = ob.get("data", 0, "Mesh"); if (m == null) continue; let totpoly = m.get("totpoly"); if (totpoly == 0) continue; let numtri = 0; for (let i = 0; i < totpoly; ++i) { let poly = m.get("mpoly", i); let totloop = poly.get("totloop"); numtri += totloop - 2; } let inda = new Uint32Array(numtri * 3); for (let i = 0; i < inda.length; ++i) inda[i] = i; let posa32 = new Float32Array(numtri * 3 * 4); let posa = new Int16Array(numtri * 3 * 4); let nora = new Int16Array(numtri * 3 * 2); // pdata, 25 == CD_MPOLY // let vdata: any = m.get("vdata"); // let codata: any = null; // let codata_pos = 0; // for (let i = 0; i < vdata.get("totlayer"); ++i) { // let l = vdata.get("layers", i); // if (l.get("type") == 0) { // CD_MVERT // let ptr: any = l.get("data"); // codata_pos = bl.map.get(ptr).pos; // codata = l; // } // } let ldata: any = m.get("ldata"); let uvdata: any = null; let uvdata_pos = 0; let coldata: any = null; let coldata_pos = 0; for (let i = 0; i < ldata.get("totlayer"); ++i) { let l = ldata.get("layers", i); if (l.get("type") == 16) { // CD_MLOOPUV let ptr: any = l.get("data"); uvdata_pos = bl.map.get(ptr).pos; uvdata = l; } else if (l.get("type") == 17) { // CD_PROP_BYTE_COLOR let ptr: any = l.get("data"); coldata_pos = bl.map.get(ptr).pos; coldata = l; } // CD_MLOOP == 26 } let hasuv = uvdata != null; let texa = hasuv ? new Int16Array(numtri * 3 * 2) : null; let hascol = Context.raw.parseVCols && coldata != null; let cola = hascol ? new Int16Array(numtri * 3 * 3) : null; let tri = 0; let vec0 = new Vec4(); let vec1 = new Vec4(); let vec2 = new Vec4(); for (let i = 0; i < totpoly; ++i) { let poly = m.get("mpoly", i); // let smooth = poly.get("flag") & 1 == 1; // ME_SMOOTH let smooth = false; // TODO: fetch smooth normals let loopstart = poly.get("loopstart"); let totloop = poly.get("totloop"); if (totloop <= 4) { // Convex, fan triangulation let v0 = m.get("mvert", m.get("mloop", loopstart + totloop - 1).get("v")); let v1 = m.get("mvert", m.get("mloop", loopstart).get("v")); let co0 = v0.get("co"); let co1 = v1.get("co"); let no0 = v0.get("no"); let no1 = v1.get("no"); if (smooth) { vec0.set(no0[0] / 32767, no0[1] / 32767, no0[2] / 32767).normalize(); // shortmax vec1.set(no1[0] / 32767, no1[1] / 32767, no1[2] / 32767).normalize(); } let uv0: Float32Array = null; let uv1: Float32Array = null; let uv2: Float32Array = null; if (hasuv) { bl.pos = uvdata_pos + (loopstart + totloop - 1) * 4 * 3; // * 3 = x, y, flag uv0 = bl.readf32array(2); if (uv0[0] > 1.0 + ImportBlendMesh.eps) uv0[0] = uv0[0] - Math.floor(uv0[0]); if (uv0[1] > 1.0 + ImportBlendMesh.eps) uv0[1] = uv0[1] - Math.floor(uv0[1]); bl.pos = uvdata_pos + (loopstart) * 4 * 3; uv1 = bl.readf32array(2); if (uv1[0] > 1.0 + ImportBlendMesh.eps) uv1[0] = uv1[0] - Math.floor(uv1[0]); if (uv1[1] > 1.0 + ImportBlendMesh.eps) uv1[1] = uv1[1] - Math.floor(uv1[1]); } let col0r: i32 = 0; let col0g: i32 = 0; let col0b: i32 = 0; let col1r: i32 = 0; let col1g: i32 = 0; let col1b: i32 = 0; let col2r: i32 = 0; let col2g: i32 = 0; let col2b: i32 = 0; if (hascol) { bl.pos = coldata_pos + (loopstart + totloop - 1) * 1 * 4; // * 4 = r, g, b, a col0r = bl.read8(); col0g = bl.read8(); col0b = bl.read8(); bl.pos = coldata_pos + (loopstart) * 1 * 4; col1r = bl.read8(); col1g = bl.read8(); col1b = bl.read8(); } for (let j = 0; j < totloop - 2; ++j) { let v2 = m.get("mvert", m.get("mloop", loopstart + j + 1).get("v")); let co2 = v2.get("co"); let no2 = v2.get("no"); if (smooth) { vec2.set(no2[0] / 32767, no2[1] / 32767, no2[2] / 32767).normalize(); } else { vec2.set(co2[0], co2[1], co2[2]); vec1.set(co1[0], co1[1], co1[2]); vec0.subvecs(vec2, vec1); vec2.set(co0[0], co0[1], co0[2]); vec1.subvecs(vec2, vec1); vec0.cross(vec1); vec0.normalize(); } posa32[tri * 9 ] = co0[0]; posa32[tri * 9 + 1] = co0[1]; posa32[tri * 9 + 2] = co0[2]; posa32[tri * 9 + 3] = co1[0]; posa32[tri * 9 + 4] = co1[1]; posa32[tri * 9 + 5] = co1[2]; posa32[tri * 9 + 6] = co2[0]; posa32[tri * 9 + 7] = co2[1]; posa32[tri * 9 + 8] = co2[2]; posa[tri * 12 + 3] = Math.floor(vec0.z * 32767); posa[tri * 12 + 7] = Math.floor((smooth ? vec1.z : vec0.z) * 32767); posa[tri * 12 + 11] = Math.floor((smooth ? vec2.z : vec0.z) * 32767); nora[tri * 6 ] = Math.floor(vec0.x * 32767); nora[tri * 6 + 1] = Math.floor(vec0.y * 32767); nora[tri * 6 + 2] = Math.floor((smooth ? vec1.x : vec0.x) * 32767); nora[tri * 6 + 3] = Math.floor((smooth ? vec1.y : vec0.y) * 32767); nora[tri * 6 + 4] = Math.floor((smooth ? vec2.x : vec0.x) * 32767); nora[tri * 6 + 5] = Math.floor((smooth ? vec2.y : vec0.y) * 32767); co1 = co2; no1 = no2; vec1.setFrom(vec2); if (hasuv) { bl.pos = uvdata_pos + (loopstart + j + 1) * 4 * 3; uv2 = bl.readf32array(2); if (uv2[0] > 1.0 + ImportBlendMesh.eps) uv2[0] = uv2[0] - Math.floor(uv2[0]); if (uv2[1] > 1.0 + ImportBlendMesh.eps) uv2[1] = uv2[1] - Math.floor(uv2[1]); texa[tri * 6 ] = Math.floor(uv0[0] * 32767); texa[tri * 6 + 1] = Math.floor((1.0 - uv0[1]) * 32767); texa[tri * 6 + 2] = Math.floor(uv1[0] * 32767); texa[tri * 6 + 3] = Math.floor((1.0 - uv1[1]) * 32767); texa[tri * 6 + 4] = Math.floor(uv2[0] * 32767); texa[tri * 6 + 5] = Math.floor((1.0 - uv2[1]) * 32767); uv1 = uv2; } if (hascol) { bl.pos = coldata_pos + (loopstart + j + 1) * 1 * 4; col2r = bl.read8(); col2g = bl.read8(); col2b = bl.read8(); cola[tri * 9 ] = col0r * 128; cola[tri * 9 + 1] = col0g * 128; cola[tri * 9 + 2] = col0b * 128; cola[tri * 9 + 3] = col1r * 128; cola[tri * 9 + 4] = col1g * 128; cola[tri * 9 + 5] = col1b * 128; cola[tri * 9 + 6] = col2r * 128; cola[tri * 9 + 7] = col2g * 128; cola[tri * 9 + 8] = col2b * 128; col1r = col2r; col1g = col2g; col1b = col2b; } tri++; } } else { // Convex or concave, ear clipping let va: i32[] = []; for (let i = 0; i < totloop; ++i) va.push(loopstart + i); let co0 = m.get("mvert", m.get("mloop", loopstart).get("v")).get("co"); let co1 = m.get("mvert", m.get("mloop", loopstart + 1).get("v")).get("co"); let co2 = m.get("mvert", m.get("mloop", loopstart + 2).get("v")).get("co"); vec2.set(co2[0], co2[1], co2[2]); vec1.set(co1[0], co1[1], co1[2]); vec0.subvecs(vec2, vec1); vec2.set(co0[0], co0[1], co0[2]); vec1.subvecs(vec2, vec1); vec0.cross(vec1); vec0.normalize(); let nx = vec0.x; let ny = vec0.y; let nz = vec0.z; let nxabs = Math.abs(nx); let nyabs = Math.abs(ny); let nzabs = Math.abs(nz); let flip = nx + ny + nz > 0; let axis = nxabs > nyabs && nxabs > nzabs ? 0 : nyabs > nxabs && nyabs > nzabs ? 1 : 2; let axis0 = axis == 0 ? (flip ? 2 : 1) : axis == 1 ? (flip ? 0 : 2) : (flip ? 1 : 0); let axis1 = axis == 0 ? (flip ? 1 : 2) : axis == 1 ? (flip ? 2 : 0) : (flip ? 0 : 1); let winding = 0.0; for (let i = 0; i < totloop; ++i) { let co0 = m.get("mvert", m.get("mloop", loopstart + i).get("v")).get("co"); let co1 = m.get("mvert", m.get("mloop", loopstart + ((i + 1) % totloop)).get("v")).get("co"); winding += (co1[axis0] - co0[axis0]) * (co1[axis1] + co0[axis1]); } flip = winding > 0 ? nx + ny + nz > 0 : nx + ny + nz < 0; axis0 = axis == 0 ? (flip ? 2 : 1) : axis == 1 ? (flip ? 0 : 2) : (flip ? 1 : 0); axis1 = axis == 0 ? (flip ? 1 : 2) : axis == 1 ? (flip ? 2 : 0) : (flip ? 0 : 1); let vi = totloop; let loops = 0; let i = -1; while (vi > 2 && loops++ < vi) { i = (i + 1) % vi; let i1 = (i + 1) % vi; let i2 = (i + 2) % vi; let v0 = m.get("mvert", m.get("mloop", va[i ]).get("v")); let v1 = m.get("mvert", m.get("mloop", va[i1]).get("v")); let v2 = m.get("mvert", m.get("mloop", va[i2]).get("v")); let co0 = v0.get("co"); let co1 = v1.get("co"); let co2 = v2.get("co"); let v0x = co0[axis0]; let v0y = co0[axis1]; let v1x = co1[axis0]; let v1y = co1[axis1]; let v2x = co2[axis0]; let v2y = co2[axis1]; let e0x = v0x - v1x; // Not an interior vertex let e0y = v0y - v1y; let e1x = v2x - v1x; let e1y = v2y - v1y; let cross = e0x * e1y - e0y * e1x; if (cross <= 0) continue; let overlap = false; // Other vertex found inside this triangle for (let j = 0; j < vi - 3; ++j) { let j0 = (i + 3 + j) % vi; let co = m.get("mvert", m.get("mloop", va[j0]).get("v")).get("co"); let px = co[axis0]; let py = co[axis1]; if (UtilMesh.pnpoly(v0x, v0y, v1x, v1y, v2x, v2y, px, py)) { overlap = true; break; } } if (overlap) continue; // Found ear { let no0 = v0.get("no"); let no1 = v1.get("no"); let no2 = v2.get("no"); if (smooth) { vec0.set(no0[0] / 32767, no0[1] / 32767, no0[2] / 32767).normalize(); // shortmax vec1.set(no1[0] / 32767, no1[1] / 32767, no1[2] / 32767).normalize(); vec2.set(no2[0] / 32767, no2[1] / 32767, no2[2] / 32767).normalize(); } else { vec2.set(co2[0], co2[1], co2[2]); vec1.set(co1[0], co1[1], co1[2]); vec0.subvecs(vec2, vec1); vec2.set(co0[0], co0[1], co0[2]); vec1.subvecs(vec2, vec1); vec0.cross(vec1); vec0.normalize(); } let uv0: Float32Array = null; let uv1: Float32Array = null; let uv2: Float32Array = null; if (hasuv) { bl.pos = uvdata_pos + (va[i ]) * 4 * 3; uv0 = bl.readf32array(2); if (uv0[0] > 1.0 + ImportBlendMesh.eps) uv0[0] = uv0[0] - Math.floor(uv0[0]); if (uv0[1] > 1.0 + ImportBlendMesh.eps) uv0[1] = uv0[1] - Math.floor(uv0[1]); bl.pos = uvdata_pos + (va[i1]) * 4 * 3; uv1 = bl.readf32array(2); if (uv1[0] > 1.0 + ImportBlendMesh.eps) uv1[0] = uv1[0] - Math.floor(uv1[0]); if (uv1[1] > 1.0 + ImportBlendMesh.eps) uv1[1] = uv1[1] - Math.floor(uv1[1]); bl.pos = uvdata_pos + (va[i2]) * 4 * 3; uv2 = bl.readf32array(2); if (uv2[0] > 1.0 + ImportBlendMesh.eps) uv2[0] = uv2[0] - Math.floor(uv2[0]); if (uv2[1] > 1.0 + ImportBlendMesh.eps) uv2[1] = uv2[1] - Math.floor(uv2[1]); } let col0r: i32 = 0; let col0g: i32 = 0; let col0b: i32 = 0; let col1r: i32 = 0; let col1g: i32 = 0; let col1b: i32 = 0; let col2r: i32 = 0; let col2g: i32 = 0; let col2b: i32 = 0; if (hascol) { bl.pos = coldata_pos + (va[i ]) * 1 * 4; col0r = bl.read8(); col0g = bl.read8(); col0b = bl.read8(); bl.pos = coldata_pos + (va[i1]) * 1 * 4; col1r = bl.read8(); col1g = bl.read8(); col1b = bl.read8(); bl.pos = coldata_pos + (va[i2]) * 1 * 4; col2r = bl.read8(); col2g = bl.read8(); col2b = bl.read8(); } posa32[tri * 9 ] = co0[0]; posa32[tri * 9 + 1] = co0[1]; posa32[tri * 9 + 2] = co0[2]; posa32[tri * 9 + 3] = co1[0]; posa32[tri * 9 + 4] = co1[1]; posa32[tri * 9 + 5] = co1[2]; posa32[tri * 9 + 6] = co2[0]; posa32[tri * 9 + 7] = co2[1]; posa32[tri * 9 + 8] = co2[2]; posa[tri * 12 + 3] = Math.floor(vec0.z * 32767); posa[tri * 12 + 7] = Math.floor((smooth ? vec1.z : vec0.z) * 32767); posa[tri * 12 + 11] = Math.floor((smooth ? vec2.z : vec0.z) * 32767); nora[tri * 6 ] = Math.floor(vec0.x * 32767); nora[tri * 6 + 1] = Math.floor(vec0.y * 32767); nora[tri * 6 + 2] = Math.floor((smooth ? vec1.x : vec0.x) * 32767); nora[tri * 6 + 3] = Math.floor((smooth ? vec1.y : vec0.y) * 32767); nora[tri * 6 + 4] = Math.floor((smooth ? vec2.x : vec0.x) * 32767); nora[tri * 6 + 5] = Math.floor((smooth ? vec2.y : vec0.y) * 32767); if (hasuv) { texa[tri * 6 ] = Math.floor(uv0[0] * 32767); texa[tri * 6 + 1] = Math.floor((1.0 - uv0[1]) * 32767); texa[tri * 6 + 2] = Math.floor(uv1[0] * 32767); texa[tri * 6 + 3] = Math.floor((1.0 - uv1[1]) * 32767); texa[tri * 6 + 4] = Math.floor(uv2[0] * 32767); texa[tri * 6 + 5] = Math.floor((1.0 - uv2[1]) * 32767); } if (hascol) { cola[tri * 9 ] = col0r * 128; cola[tri * 9 + 1] = col0g * 128; cola[tri * 9 + 2] = col0b * 128; cola[tri * 9 + 3] = col1r * 128; cola[tri * 9 + 4] = col1g * 128; cola[tri * 9 + 5] = col1b * 128; cola[tri * 9 + 6] = col2r * 128; cola[tri * 9 + 7] = col2g * 128; cola[tri * 9 + 8] = col2b * 128; } tri++; } for (let j = ((i + 1) % vi); j < vi - 1; ++j) { // Consume vertex va[j] = va[j + 1]; } vi--; i--; loops = 0; } } } // Apply world matrix let obmat = ob.get("obmat", 0, "float", 16); let mat = Mat4.fromFloat32Array(obmat).transpose(); let v = new Vec4(); for (let i = 0; i < Math.floor(posa32.length / 3); ++i) { v.set(posa32[i * 3], posa32[i * 3 + 1], posa32[i * 3 + 2]); v.applymat4(mat); posa32[i * 3 ] = v.x; posa32[i * 3 + 1] = v.y; posa32[i * 3 + 2] = v.z; } mat.getInverse(mat); mat.transpose3x3(); mat._30 = mat._31 = mat._32 = mat._33 = 0; for (let i = 0; i < Math.floor(nora.length / 2); ++i) { v.set(nora[i * 2] / 32767, nora[i * 2 + 1] / 32767, posa[i * 4 + 3] / 32767); v.applymat(mat); v.normalize(); nora[i * 2 ] = Math.floor(v.x * 32767); nora[i * 2 + 1] = Math.floor(v.y * 32767); posa[i * 4 + 3] = Math.floor(v.z * 32767); } // Pack positions to (-1, 1) range let scalePos = 0.0; for (let i = 0; i < posa32.length; ++i) { let f = Math.abs(posa32[i]); if (scalePos < f) scalePos = f; } let inv = 1 / scalePos; for (let i = 0; i < Math.floor(posa32.length / 3); ++i) { posa[i * 4 ] = Math.floor(posa32[i * 3 ] * 32767 * inv); posa[i * 4 + 1] = Math.floor(posa32[i * 3 + 1] * 32767 * inv); posa[i * 4 + 2] = Math.floor(posa32[i * 3 + 2] * 32767 * inv); } let obj = {posa: posa, nora: nora, texa: texa, cola: cola, inda: inda, name: name, scalePos: scalePos, scaleTes: 1.0}; (first && replaceExisting) ? ImportMesh.makeMesh(obj, path) : ImportMesh.addMesh(obj); first = false; } Data.deleteBlob(path); }); } }