convert all meshes to non-indexed

This commit is contained in:
Stewart Allen 2022-01-31 13:27:09 -05:00
commit 6993a339e7
4 changed files with 60 additions and 74 deletions

View file

@ -125,7 +125,6 @@
* add section view. local clip. raycast skip points above plane * add section view. local clip. raycast skip points above plane
* add isolate op = separate bodies * add isolate op = separate bodies
* add decimate op = face reduction * add decimate op = face reduction
* add union op (applied after isolate)
* add flatten/crush op: for z bottoms * add flatten/crush op: for z bottoms
* face selection: find regions with < delta to normal * face selection: find regions with < delta to normal
* better z snap using just vertexes from face intersected * better z snap using just vertexes from face intersected

View file

@ -182,7 +182,11 @@ mesh.model = class MeshModel extends mesh.object {
load(vertices, indices, normals) { load(vertices, indices, normals) {
let geo = new BufferGeometry(); let geo = new BufferGeometry();
geo.setAttribute('position', new BufferAttribute(vertices, 3)); geo.setAttribute('position', new BufferAttribute(vertices, 3));
if (indices) geo.setIndex(new BufferAttribute(indices, 1)); if (indices) {
// unroll indexed geometries
geo.setIndex(new BufferAttribute(indices, 1));
geo = geo.toNonIndexed();
}
if (!normals) geo.computeVertexNormals(); if (!normals) geo.computeVertexNormals();
let meh = this.mesh = new Mesh(geo, [ let meh = this.mesh = new Mesh(geo, [
this.mats.normal, this.mats.normal,
@ -199,24 +203,23 @@ mesh.model = class MeshModel extends mesh.object {
// persist in db so it can be restored on page load // persist in db so it can be restored on page load
mapp.db.space.put(this.id, { file: this.file, mesh: vertices }); mapp.db.space.put(this.id, { file: this.file, mesh: vertices });
// sync data to worker // sync data to worker
worker.model_load({id: this.id, name: this.file, vertices, indices}); worker.model_load({id: this.id, name: this.file, vertices});
} }
reload(vertices, indices, normals) { reload(vertices) {
let was = this.wireframe(false); let was = this.wireframe(false);
let geo = this.mesh.geometry; let geo = this.mesh.geometry;
geo.setAttribute('position', new BufferAttribute(vertices, 3)); geo.setAttribute('position', new BufferAttribute(vertices, 3));
geo.setAttribute('normal', undefined); geo.setAttribute('normal', undefined);
// signal util.box3expand that geometry changed // signal util.box3expand that geometry changed
geo._model_invalid = true; geo._model_invalid = true;
if (indices) geo.setIndex(new BufferAttribute(indices, 1)); geo.computeVertexNormals();
if (!normals) geo.computeVertexNormals();
// allows raycasting to work // allows raycasting to work
geo.computeBoundingSphere(); geo.computeBoundingSphere();
// persist in db so it can be restored on page load // persist in db so it can be restored on page load
mapp.db.space.put(this.id, { file: this.file, mesh: vertices }); mapp.db.space.put(this.id, { file: this.file, mesh: vertices });
// sync data to worker // sync data to worker
worker.model_load({id: this.id, name: this.name, vertices, indices}); worker.model_load({id: this.id, name: this.name, vertices});
// restore wireframe state // restore wireframe state
this.wireframe(was); this.wireframe(was);
// fixup normals // fixup normals

View file

@ -24,82 +24,68 @@ mesh.tool = class MeshTool {
constructor(params = {}) { constructor(params = {}) {
this.precision = Math.pow(10, params.precision || 6); this.precision = Math.pow(10, params.precision || 6);
if (params.vertices) { if (params.vertices) {
this.setData(params.vertices, params.faces); this.setVertices(params.vertices);
} }
} }
/** /**
* set vertices and faces of the mesh. if only vertices are provided
* then the data is assumed to be un-indexed and will be indexed into
* faces. if faces are provided, they are assumed to be index references
* into the vertex data. removes invalid faces.
*
* the end result is face data mapped to an indexed set of unique vertices.
*
* @param {number[]} vertices * @param {number[]} vertices
* @param {number[]} faces
*/ */
setData(vertices, faces) { setVertices(vertices, opt = {}) {
if (!vertices) { if (!vertices) {
throw "missing vertices"; throw "missing vertices";
} }
if (vertices.length % 3 !== 0) { if (vertices.length % 3 !== 0) {
throw "invalid vertices"; throw "invalid vertices";
} }
// when face/index data is missing, vertices need to be normalized let faces = [];
if (!faces) { let fcac = {}; // seen face hash
faces = []; let fnew = []; // accumulate vertex triplets
let fcac = {}; // seen face hash let nuvt = []; // new vertex list
let fnew = []; // accumulate vertex triplets let hash = {}; // find vertex matches
let nuvt = []; // new vertex list let prec = this.precision;
let hash = {}; // find vertex matches let dups = 0;
let prec = this.precision; let cull = 0;
let dups = 0; for (let i=0, l=vertices.length; i<l; i += 3) {
let cull = 0; let x = vertices[i];
for (let i=0, l=vertices.length; i<l; i += 3) { let y = vertices[i+1];
let x = vertices[i]; let z = vertices[i+2];
let y = vertices[i+1]; let key = [
let z = vertices[i+2]; (x * prec) | 0,
let key = [ (y * prec) | 0,
(x * prec) | 0, (z * prec) | 0
(y * prec) | 0, ].join('');
(z * prec) | 0 let vpos = hash[key];
].join(''); if (vpos === undefined) {
let vpos = hash[key]; // add new vertex x,y,z and hash entry with index
if (vpos === undefined) { hash[key] = vpos = nuvt.length;
// add new vertex x,y,z and hash entry with index nuvt.push(x);
hash[key] = vpos = nuvt.length; nuvt.push(y);
nuvt.push(x); nuvt.push(z);
nuvt.push(y); }
nuvt.push(z); // add vertex position to face accumulator
} fnew.push(vpos);
// add vertex position to face accumulator if (fnew.length === 3) {
fnew.push(vpos); // check and emit face if it's unique
if (fnew.length === 3) { // cull invalid faces (has 2 or more shared vertices)
// check and emit face if it's unique if (fnew[0] === fnew[1] || fnew[0] === fnew[2] || fnew[1] === fnew[2]) {
// cull invalid faces (has 2 or more shared vertices) fnew = [];
if (fnew[0] === fnew[1] || fnew[0] === fnew[2] || fnew[1] === fnew[2]) { cull++;
fnew = []; continue;
cull++; }
continue; let key = fnew.slice().sort().join('-');
} // drop duplicate faces (sort handles reverse order)
let key = fnew.slice().sort().join('-'); if (!fcac[key]) {
// drop duplicate faces (sort handles reverse order) faces.appendAll(fnew);
if (!fcac[key]) { fcac[key] = key;
faces.appendAll(fnew); } else {
fcac[key] = key; dups++;
} else { }
dups++; fnew = [];
}
fnew = [];
}
} }
vertices = nuvt;
this.stats = {cull, dups, faces:faces.length/3};
// console.log({stats: this.stats});
} else {
faces = faces.map(v => v*3);
} }
vertices = nuvt;
this.stats = {cull, dups, faces:faces.length/3};
// unique vertex array // unique vertex array
this.vertices = vertices; this.vertices = vertices;
// unique face array. elements are offset into vertices // unique face array. elements are offset into vertices
@ -112,7 +98,7 @@ mesh.tool = class MeshTool {
* construct ordered line maps with array of connected edges. * construct ordered line maps with array of connected edges.
* connect lines into polys. earcut polys into new faces. * connect lines into polys. earcut polys into new faces.
*/ */
heal(opt = { merge: true }) { patch(opt = { merge: true }) {
let vertices = this.vertices; let vertices = this.vertices;
let faces = this.faces; let faces = this.faces;
let hash = {}; // key to line map let hash = {}; // key to line map

View file

@ -59,25 +59,23 @@ function translate_encode(id, matrix) {
} }
function analyze(id, opt = {}) { function analyze(id, opt = {}) {
log(`${id} | indexing...`); log(`${id} | analyzing...`);
let geo = cache[id].geo; let geo = cache[id].geo;
let tool = new mesh.tool({ let tool = new mesh.tool({
vertices: geo.attributes.position.array, vertices: geo.attributes.position.array,
faces: geo.index ? geo.index.array : undefined,
debug: false debug: false
}); });
log(`${id} | analyzing...`); log(`${id} | patching...`);
tool.heal(opt); tool.patch(opt);
dbug.log(tool); dbug.log(tool);
return tool; return tool;
} }
let model = { let model = {
load(data) { load(data) {
let { vertices, indices, name, id } = data; let { vertices, name, id } = data;
let geo = new BufferGeometry(); let geo = new BufferGeometry();
geo.setAttribute('position', new BufferAttribute(vertices, 3)); geo.setAttribute('position', new BufferAttribute(vertices, 3));
if (indices) geo.setIndex(new BufferAttribute(indices, 1));
cacheUpdate(id, { name, geo, xmatrix: core_matrix.clone(), trans: undefined }); cacheUpdate(id, { name, geo, xmatrix: core_matrix.clone(), trans: undefined });
}, },