437 lines
16 KiB
TypeScript
437 lines
16 KiB
TypeScript
|
|
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);
|
|
});
|
|
}
|
|
}
|