382 lines
14 KiB
TypeScript
382 lines
14 KiB
TypeScript
|
|
class UtilMesh {
|
|
|
|
static unwrappers: Map<string, ((a: any)=>void)> = new Map();
|
|
|
|
static mergeMesh = (paintObjects: MeshObject[] = null) => {
|
|
if (paintObjects == null) paintObjects = Project.paintObjects;
|
|
if (paintObjects.length == 0) return;
|
|
Context.raw.mergedObjectIsAtlas = paintObjects.length < Project.paintObjects.length;
|
|
let vlen = 0;
|
|
let ilen = 0;
|
|
let maxScale = 0.0;
|
|
for (let i = 0; i < paintObjects.length; ++i) {
|
|
vlen += paintObjects[i].data.raw.vertex_arrays[0].values.length;
|
|
ilen += paintObjects[i].data.raw.index_arrays[0].values.length;
|
|
if (paintObjects[i].data.scalePos > maxScale) maxScale = paintObjects[i].data.scalePos;
|
|
}
|
|
vlen = Math.floor(vlen / 4);
|
|
let va0 = new Int16Array(vlen * 4);
|
|
let va1 = new Int16Array(vlen * 2);
|
|
let va2 = new Int16Array(vlen * 2);
|
|
let va3 = paintObjects[0].data.raw.vertex_arrays.length > 3 ? new Int16Array(vlen * 3) : null; // +1 padding
|
|
let ia = new Uint32Array(ilen);
|
|
|
|
let voff = 0;
|
|
let ioff = 0;
|
|
for (let i = 0; i < paintObjects.length; ++i) {
|
|
let vas = paintObjects[i].data.raw.vertex_arrays;
|
|
let ias = paintObjects[i].data.raw.index_arrays;
|
|
let scale = paintObjects[i].data.scalePos;
|
|
|
|
// Pos
|
|
for (let j = 0; j < vas[0].values.length; ++j) va0[j + voff * 4] = vas[0].values[j];
|
|
|
|
// Translate
|
|
///if is_forge
|
|
for (let j = 0; j < Math.floor(va0.length / 4); ++j) {
|
|
va0[j * 4 + voff * 4] += Math.floor(paintObjects[i].transform.worldx() * 32767);
|
|
va0[j * 4 + 1 + voff * 4] += Math.floor(paintObjects[i].transform.worldy() * 32767);
|
|
va0[j * 4 + 2 + voff * 4] += Math.floor(paintObjects[i].transform.worldz() * 32767);
|
|
}
|
|
///end
|
|
|
|
// Re-scale
|
|
for (let j = 0; j < Math.floor(va0.length / 4); ++j) {
|
|
va0[j * 4 + voff * 4] = Math.floor((va0[j * 4 + voff * 4] * scale) / maxScale);
|
|
va0[j * 4 + 1 + voff * 4] = Math.floor((va0[j * 4 + 1 + voff * 4] * scale) / maxScale);
|
|
va0[j * 4 + 2 + voff * 4] = Math.floor((va0[j * 4 + 2 + voff * 4] * scale) / maxScale);
|
|
}
|
|
// Nor
|
|
for (let j = 0; j < vas[1].values.length; ++j) va1[j + voff * 2] = vas[1].values[j];
|
|
// Tex
|
|
for (let j = 0; j < vas[2].values.length; ++j) va2[j + voff * 2] = vas[2].values[j];
|
|
// Col
|
|
if (va3 != null) for (let j = 0; j < vas[3].values.length; ++j) va3[j + voff * 3] = vas[3].values[j];
|
|
// Indices
|
|
for (let j = 0; j < ias[0].values.length; ++j) ia[j + ioff] = ias[0].values[j] + voff;
|
|
|
|
voff += Math.floor(vas[0].values.length / 4);
|
|
ioff += Math.floor(ias[0].values.length);
|
|
}
|
|
|
|
let raw: TMeshData = {
|
|
name: Context.raw.paintObject.name,
|
|
vertex_arrays: [
|
|
{ values: va0, attrib: "pos", data: "short4norm" },
|
|
{ values: va1, attrib: "nor", data: "short2norm" },
|
|
{ values: va2, attrib: "tex", data: "short2norm" }
|
|
],
|
|
index_arrays: [
|
|
{ values: ia, material: 0 }
|
|
],
|
|
scale_pos: maxScale,
|
|
scale_tex: 1.0
|
|
};
|
|
if (va3 != null) raw.vertex_arrays.push({ values: va3, attrib: "col", data: "short4norm", padding: 1 });
|
|
|
|
UtilMesh.removeMergedMesh();
|
|
new MeshData(raw, (md: MeshData) => {
|
|
Context.raw.mergedObject = new MeshObject(md, Context.raw.paintObject.materials);
|
|
Context.raw.mergedObject.name = Context.raw.paintObject.name + "_merged";
|
|
Context.raw.mergedObject.force_context = "paint";
|
|
Context.raw.mergedObject.setParent(Context.mainObject());
|
|
});
|
|
|
|
///if (krom_direct3d12 || krom_vulkan || krom_metal)
|
|
RenderPathRaytrace.ready = false;
|
|
///end
|
|
}
|
|
|
|
static removeMergedMesh = () => {
|
|
if (Context.raw.mergedObject != null) {
|
|
Context.raw.mergedObject.data.delete();
|
|
Context.raw.mergedObject.remove();
|
|
Context.raw.mergedObject = null;
|
|
}
|
|
}
|
|
|
|
static swapAxis = (a: i32, b: i32) => {
|
|
let objects = Project.paintObjects;
|
|
for (let o of objects) {
|
|
// Remapping vertices, buckle up
|
|
// 0 - x, 1 - y, 2 - z
|
|
let vas = o.data.raw.vertex_arrays;
|
|
let pa = vas[0].values;
|
|
let na0 = a == 2 ? vas[0].values : vas[1].values;
|
|
let na1 = b == 2 ? vas[0].values : vas[1].values;
|
|
let c = a == 2 ? 3 : a;
|
|
let d = b == 2 ? 3 : b;
|
|
let e = a == 2 ? 4 : 2;
|
|
let f = b == 2 ? 4 : 2;
|
|
|
|
for (let i = 0; i < Math.floor(pa.length / 4); ++i) {
|
|
let t = pa[i * 4 + a];
|
|
pa[i * 4 + a] = pa[i * 4 + b];
|
|
pa[i * 4 + b] = -t;
|
|
t = na0[i * e + c];
|
|
na0[i * e + c] = na1[i * f + d];
|
|
na1[i * f + d] = -t;
|
|
}
|
|
|
|
let g = o.data;
|
|
let l = g.structLength;
|
|
let vertices = g.vertexBuffer.lock(); // posnortex
|
|
for (let i = 0; i < Math.floor(vertices.byteLength / 2 / l); ++i) {
|
|
vertices.setInt16((i * l ) * 2, vas[0].values[i * 4 ], true);
|
|
vertices.setInt16((i * l + 1) * 2, vas[0].values[i * 4 + 1], true);
|
|
vertices.setInt16((i * l + 2) * 2, vas[0].values[i * 4 + 2], true);
|
|
vertices.setInt16((i * l + 3) * 2, vas[0].values[i * 4 + 3], true);
|
|
vertices.setInt16((i * l + 4) * 2, vas[1].values[i * 2 ], true);
|
|
vertices.setInt16((i * l + 5) * 2, vas[1].values[i * 2 + 1], true);
|
|
}
|
|
g.vertexBuffer.unlock();
|
|
}
|
|
|
|
UtilMesh.removeMergedMesh();
|
|
UtilMesh.mergeMesh();
|
|
}
|
|
|
|
static flipNormals = () => {
|
|
let objects = Project.paintObjects;
|
|
for (let o of objects) {
|
|
let vas = o.data.raw.vertex_arrays;
|
|
let va0 = vas[0].values;
|
|
let va1 = vas[1].values;
|
|
let g = o.data;
|
|
let l = g.structLength;
|
|
let vertices = g.vertexBuffer.lock(); // posnortex
|
|
for (let i = 0; i < Math.floor(vertices.byteLength / 2 / l); ++i) {
|
|
va0[i * 4 + 3] = -va0[i * 4 + 3];
|
|
va1[i * 2] = -va1[i * 2];
|
|
va1[i * 2 + 1] = -va1[i * 2 + 1];
|
|
vertices.setInt16((i * l + 3) * 2, -vertices.getInt16((i * l + 3) * 2, true), true);
|
|
vertices.setInt16((i * l + 4) * 2, -vertices.getInt16((i * l + 4) * 2, true), true);
|
|
vertices.setInt16((i * l + 5) * 2, -vertices.getInt16((i * l + 5) * 2, true), true);
|
|
}
|
|
g.vertexBuffer.unlock();
|
|
}
|
|
|
|
///if (krom_direct3d12 || krom_vulkan || krom_metal)
|
|
RenderPathRaytrace.ready = false;
|
|
///end
|
|
}
|
|
|
|
static calcNormals = (smooth = false) => {
|
|
let va = new Vec4();
|
|
let vb = new Vec4();
|
|
let vc = new Vec4();
|
|
let cb = new Vec4();
|
|
let ab = new Vec4();
|
|
let objects = Project.paintObjects;
|
|
for (let o of objects) {
|
|
let g = o.data;
|
|
let l = g.structLength;
|
|
let inda = g.indices[0];
|
|
let vertices = g.vertexBuffer.lock(); // posnortex
|
|
for (let i = 0; i < Math.floor(inda.length / 3); ++i) {
|
|
let i1 = inda[i * 3 ];
|
|
let i2 = inda[i * 3 + 1];
|
|
let i3 = inda[i * 3 + 2];
|
|
va.set(vertices.getInt16((i1 * l) * 2, true), vertices.getInt16((i1 * l + 1) * 2, true), vertices.getInt16((i1 * l + 2) * 2, true));
|
|
vb.set(vertices.getInt16((i2 * l) * 2, true), vertices.getInt16((i2 * l + 1) * 2, true), vertices.getInt16((i2 * l + 2) * 2, true));
|
|
vc.set(vertices.getInt16((i3 * l) * 2, true), vertices.getInt16((i3 * l + 1) * 2, true), vertices.getInt16((i3 * l + 2) * 2, true));
|
|
cb.subvecs(vc, vb);
|
|
ab.subvecs(va, vb);
|
|
cb.cross(ab);
|
|
cb.normalize();
|
|
vertices.setInt16((i1 * l + 4) * 2, Math.floor(cb.x * 32767), true);
|
|
vertices.setInt16((i1 * l + 5) * 2, Math.floor(cb.y * 32767), true);
|
|
vertices.setInt16((i1 * l + 3) * 2, Math.floor(cb.z * 32767), true);
|
|
vertices.setInt16((i2 * l + 4) * 2, Math.floor(cb.x * 32767), true);
|
|
vertices.setInt16((i2 * l + 5) * 2, Math.floor(cb.y * 32767), true);
|
|
vertices.setInt16((i2 * l + 3) * 2, Math.floor(cb.z * 32767), true);
|
|
vertices.setInt16((i3 * l + 4) * 2, Math.floor(cb.x * 32767), true);
|
|
vertices.setInt16((i3 * l + 5) * 2, Math.floor(cb.y * 32767), true);
|
|
vertices.setInt16((i3 * l + 3) * 2, Math.floor(cb.z * 32767), true);
|
|
}
|
|
|
|
if (smooth) {
|
|
let shared = new Uint32Array(1024);
|
|
let sharedLen = 0;
|
|
let found: i32[] = [];
|
|
for (let i = 0; i < (inda.length - 1); ++i) {
|
|
if (found.indexOf(i) >= 0) continue;
|
|
let i1 = inda[i];
|
|
sharedLen = 0;
|
|
shared[sharedLen++] = i1;
|
|
for (let j = (i + 1); j < inda.length; ++j) {
|
|
let i2 = inda[j];
|
|
let i1l = i1 * l;
|
|
let i2l = i2 * l;
|
|
if (vertices.getInt16((i1l ) * 2, true) == vertices.getInt16((i2l ) * 2, true) &&
|
|
vertices.getInt16((i1l + 1) * 2, true) == vertices.getInt16((i2l + 1) * 2, true) &&
|
|
vertices.getInt16((i1l + 2) * 2, true) == vertices.getInt16((i2l + 2) * 2, true)) {
|
|
// if (n1.dot(n2) > 0)
|
|
shared[sharedLen++] = i2;
|
|
found.push(j);
|
|
if (sharedLen >= 1024) break;
|
|
}
|
|
}
|
|
if (sharedLen > 1) {
|
|
va.set(0, 0, 0);
|
|
for (let j = 0; j < sharedLen; ++j) {
|
|
let i1 = shared[j];
|
|
let i1l = i1 * l;
|
|
va.addf(vertices.getInt16((i1l + 4) * 2, true), vertices.getInt16((i1l + 5) * 2, true), vertices.getInt16((i1l + 3) * 2, true));
|
|
}
|
|
va.mult(1 / sharedLen);
|
|
va.normalize();
|
|
let vax = Math.floor(va.x * 32767);
|
|
let vay = Math.floor(va.y * 32767);
|
|
let vaz = Math.floor(va.z * 32767);
|
|
for (let j = 0; j < sharedLen; ++j) {
|
|
let i1 = shared[j];
|
|
let i1l = i1 * l;
|
|
vertices.setInt16((i1l + 4) * 2, vax, true);
|
|
vertices.setInt16((i1l + 5) * 2, vay, true);
|
|
vertices.setInt16((i1l + 3) * 2, vaz, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
g.vertexBuffer.unlock();
|
|
|
|
let va0 = o.data.raw.vertex_arrays[0].values;
|
|
let va1 = o.data.raw.vertex_arrays[1].values;
|
|
for (let i = 0; i < Math.floor(vertices.byteLength / 4 / l); ++i) {
|
|
va1[i * 2 ] = vertices.getInt16((i * l + 4) * 2, true);
|
|
va1[i * 2 + 1] = vertices.getInt16((i * l + 5) * 2, true);
|
|
va0[i * 4 + 3] = vertices.getInt16((i * l + 3) * 2, true);
|
|
}
|
|
}
|
|
|
|
///if (krom_direct3d12 || krom_vulkan || krom_metal)
|
|
UtilMesh.mergeMesh();
|
|
RenderPathRaytrace.ready = false;
|
|
///end
|
|
}
|
|
|
|
static toOrigin = () => {
|
|
let dx = 0.0;
|
|
let dy = 0.0;
|
|
let dz = 0.0;
|
|
for (let o of Project.paintObjects) {
|
|
let l = 4;
|
|
let sc = o.data.scalePos / 32767;
|
|
let va = o.data.raw.vertex_arrays[0].values;
|
|
let minx = va[0];
|
|
let maxx = va[0];
|
|
let miny = va[1];
|
|
let maxy = va[1];
|
|
let minz = va[2];
|
|
let maxz = va[2];
|
|
for (let i = 1; i < Math.floor(va.length / l); ++i) {
|
|
if (va[i * l] < minx) minx = va[i * l];
|
|
else if (va[i * l] > maxx) maxx = va[i * l];
|
|
if (va[i * l + 1] < miny) miny = va[i * l + 1];
|
|
else if (va[i * l + 1] > maxy) maxy = va[i * l + 1];
|
|
if (va[i * l + 2] < minz) minz = va[i * l + 2];
|
|
else if (va[i * l + 2] > maxz) maxz = va[i * l + 2];
|
|
}
|
|
dx += (minx + maxx) / 2 * sc;
|
|
dy += (miny + maxy) / 2 * sc;
|
|
dz += (minz + maxz) / 2 * sc;
|
|
}
|
|
dx /= Project.paintObjects.length;
|
|
dy /= Project.paintObjects.length;
|
|
dz /= Project.paintObjects.length;
|
|
|
|
for (let o of Project.paintObjects) {
|
|
let g = o.data;
|
|
let sc = o.data.scalePos / 32767;
|
|
let va = o.data.raw.vertex_arrays[0].values;
|
|
let maxScale = 0.0;
|
|
for (let i = 0; i < Math.floor(va.length / 4); ++i) {
|
|
if (Math.abs(va[i * 4 ] * sc - dx) > maxScale) maxScale = Math.abs(va[i * 4 ] * sc - dx);
|
|
if (Math.abs(va[i * 4 + 1] * sc - dy) > maxScale) maxScale = Math.abs(va[i * 4 + 1] * sc - dy);
|
|
if (Math.abs(va[i * 4 + 2] * sc - dz) > maxScale) maxScale = Math.abs(va[i * 4 + 2] * sc - dz);
|
|
}
|
|
o.transform.scaleWorld = o.data.scalePos = o.data.raw.scale_pos = maxScale;
|
|
o.transform.buildMatrix();
|
|
|
|
for (let i = 0; i < Math.floor(va.length / 4); ++i) {
|
|
va[i * 4 ] = Math.floor((va[i * 4 ] * sc - dx) / maxScale * 32767);
|
|
va[i * 4 + 1] = Math.floor((va[i * 4 + 1] * sc - dy) / maxScale * 32767);
|
|
va[i * 4 + 2] = Math.floor((va[i * 4 + 2] * sc - dz) / maxScale * 32767);
|
|
}
|
|
|
|
let l = g.structLength;
|
|
let vertices = g.vertexBuffer.lock(); // posnortex
|
|
for (let i = 0; i < Math.floor(vertices.byteLength / 2 / l); ++i) {
|
|
vertices.setInt16((i * l ) * 2, va[i * 4 ], true);
|
|
vertices.setInt16((i * l + 1) * 2, va[i * 4 + 1], true);
|
|
vertices.setInt16((i * l + 2) * 2, va[i * 4 + 2], true);
|
|
}
|
|
g.vertexBuffer.unlock();
|
|
}
|
|
|
|
UtilMesh.mergeMesh();
|
|
}
|
|
|
|
static applyDisplacement = (texpaint_pack: Image, strength = 0.1, uvScale = 1.0) => {
|
|
let height = texpaint_pack.getPixels();
|
|
let heightView = new DataView(height);
|
|
let res = texpaint_pack.width;
|
|
let o = Project.paintObjects[0];
|
|
let g = o.data;
|
|
let l = g.structLength;
|
|
let vertices = g.vertexBuffer.lock(); // posnortex
|
|
for (let i = 0; i < Math.floor(vertices.byteLength / 2 / l); ++i) {
|
|
let x = Math.floor(vertices.getInt16((i * l + 6) * 2, true) / 32767 * res);
|
|
let y = Math.floor(vertices.getInt16((i * l + 7) * 2, true) / 32767 * res);
|
|
let xx = Math.floor(x * uvScale) % res;
|
|
let yy = Math.floor(y * uvScale) % res;
|
|
let h = (1.0 - heightView.getUint8((yy * res + xx) * 4 + 3) / 255) * strength;
|
|
vertices.setInt16((i * l ) * 2, vertices.getInt16((i * l ) * 2, true) - Math.floor(vertices.getInt16((i * l + 4) * 2, true) * h), true);
|
|
vertices.setInt16((i * l + 1) * 2, vertices.getInt16((i * l + 1) * 2, true) - Math.floor(vertices.getInt16((i * l + 5) * 2, true) * h), true);
|
|
vertices.setInt16((i * l + 2) * 2, vertices.getInt16((i * l + 2) * 2, true) - Math.floor(vertices.getInt16((i * l + 3) * 2, true) * h), true);
|
|
}
|
|
g.vertexBuffer.unlock();
|
|
|
|
let va0 = o.data.raw.vertex_arrays[0].values;
|
|
for (let i = 0; i < Math.floor(vertices.byteLength / 4 / l); ++i) {
|
|
va0[i * 4 ] = vertices.getInt16((i * l ) * 2, true);
|
|
va0[i * 4 + 1] = vertices.getInt16((i * l + 1) * 2, true);
|
|
va0[i * 4 + 2] = vertices.getInt16((i * l + 2) * 2, true);
|
|
}
|
|
}
|
|
|
|
static equirectUnwrap = (mesh: any) => {
|
|
let verts = Math.floor(mesh.posa.length / 4);
|
|
mesh.texa = new Int16Array(verts * 2);
|
|
let n = new Vec4();
|
|
for (let i = 0; i < verts; ++i) {
|
|
n.set(mesh.posa[i * 4] / 32767, mesh.posa[i * 4 + 1] / 32767, mesh.posa[i * 4 + 2] / 32767).normalize();
|
|
// Sphere projection
|
|
// mesh.texa[i * 2 ] = Math.atan2(n.x, n.y) / (Math.PI * 2) + 0.5;
|
|
// mesh.texa[i * 2 + 1] = n.z * 0.5 + 0.5;
|
|
// Equirect
|
|
mesh.texa[i * 2 ] = Math.floor(((Math.atan2(-n.z, n.x) + Math.PI) / (Math.PI * 2)) * 32767);
|
|
mesh.texa[i * 2 + 1] = Math.floor((Math.acos(n.y) / Math.PI) * 32767);
|
|
}
|
|
}
|
|
|
|
static pnpoly = (v0x: f32, v0y: f32, v1x: f32, v1y: f32, v2x: f32, v2y: f32, px: f32, py: f32): bool => {
|
|
// https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
|
|
let c = false;
|
|
if (((v0y > py) != (v2y > py)) && (px < (v2x - v0x) * (py - v0y) / (v2y - v0y) + v0x)) c = !c;
|
|
if (((v1y > py) != (v0y > py)) && (px < (v0x - v1x) * (py - v1y) / (v0y - v1y) + v1x)) c = !c;
|
|
if (((v2y > py) != (v1y > py)) && (px < (v1x - v2x) * (py - v2y) / (v1y - v2y) + v2x)) c = !c;
|
|
return c;
|
|
}
|
|
|
|
static calcNormal = (p0: Vec4, p1: Vec4, p2: Vec4): Vec4 => {
|
|
let cb = new Vec4().subvecs(p2, p1);
|
|
let ab = new Vec4().subvecs(p0, p1);
|
|
cb.cross(ab);
|
|
cb.normalize();
|
|
return cb;
|
|
}
|
|
}
|