UNPKG

@xeokit/xeokit-sdk

Version:

3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision

126 lines (105 loc) 4.5 kB
/** * @author https://github.com/tmarti, with support from https://tribia.com/ * @license MIT * * This file takes a geometry given by { positionsCompressed, indices }, and returns * equivalent { positionsCompressed, indices } arrays but which only contain unique * positionsCompressed. * * The time is O(N logN) with the number of positionsCompressed due to a pre-sorting * step, but is much more GC-friendly and actually faster than the classic O(N) * approach based in keeping a hash-based LUT to identify unique positionsCompressed. */ let comparePositions = null; function compareVertex(a, b) { let res; for (let i = 0; i < 3; i++) { if (0 !== (res = comparePositions[a * 3 + i] - comparePositions[b * 3 + i])) { return res; } } return 0; } let seqInit = null; function setMaxNumberOfPositions(maxPositions) { if (seqInit !== null && seqInit.length >= maxPositions) { return; } seqInit = new Uint32Array(maxPositions); for (let i = 0; i < maxPositions; i++) { seqInit[i] = i; } } /** * This function obtains unique positionsCompressed in the provided object * .positionsCompressed array and calculates an index mapping, which is then * applied to the provided object .indices and .edgeindices. * * The input object items are not modified, and instead new set * of positionsCompressed, indices and edgeIndices with the applied optimization * are returned. * * The algorithm, instead of being based in a hash-like LUT for * identifying unique positionsCompressed, is based in pre-sorting the input * positionsCompressed... * * (it's possible to define a _"consistent ordering"_ for the positionsCompressed * as positionsCompressed are quantized and thus not suffer from float number * comparison artifacts) * * ... so same positionsCompressed are adjacent in the sorted array, and then * it's easy to scan linearly the sorted array. During the linear run, * we will know that we found a different position because the comparison * function will return != 0 between current and previous element. * * During this linear traversal of the array, a `unique counter` is used * in order to calculate the mapping between original indices and unique * indices. * * @param {{positionsCompressed: number[],indices: number[], edgeIndices: number[]}} mesh The input mesh to process, with `positionsCompressed`, `indices` and `edgeIndices` keys. * * @returns {[Uint16Array, Uint32Array, Uint32Array]} An array with 3 elements: 0 => the uniquified positionsCompressed; 1 and 2 => the remapped edges and edgeIndices arrays */ export function uniquifyPositions(mesh) { const _positions = mesh.positionsCompressed; const _indices = mesh.indices; const _edgeIndices = mesh.edgeIndices; setMaxNumberOfPositions(_positions.length / 3); const seq = seqInit.slice(0, _positions.length / 3); const remappings = seqInit.slice(0, _positions.length / 3); comparePositions = _positions; seq.sort(compareVertex); let uniqueIdx = 0 remappings[seq[0]] = 0; for (let i = 1, len = seq.length; i < len; i++) { if (0 !== compareVertex(seq[i], seq[i - 1])) { uniqueIdx++; } remappings[seq[i]] = uniqueIdx; } const numUniquePositions = uniqueIdx + 1; const newPositions = new Uint16Array(numUniquePositions * 3); uniqueIdx = 0 newPositions [uniqueIdx * 3 + 0] = _positions [seq[0] * 3 + 0]; newPositions [uniqueIdx * 3 + 1] = _positions [seq[0] * 3 + 1]; newPositions [uniqueIdx * 3 + 2] = _positions [seq[0] * 3 + 2]; for (let i = 1, len = seq.length; i < len; i++) { if (0 !== compareVertex(seq[i], seq[i - 1])) { uniqueIdx++; newPositions [uniqueIdx * 3 + 0] = _positions [seq[i] * 3 + 0]; newPositions [uniqueIdx * 3 + 1] = _positions [seq[i] * 3 + 1]; newPositions [uniqueIdx * 3 + 2] = _positions [seq[i] * 3 + 2]; } remappings[seq[i]] = uniqueIdx; } comparePositions = null; const newIndices = new Uint32Array(_indices.length); for (let i = 0, len = _indices.length; i < len; i++) { newIndices[i] = remappings [_indices[i]]; } const newEdgeIndices = new Uint32Array(_edgeIndices.length); for (let i = 0, len = _edgeIndices.length; i < len; i++) { newEdgeIndices[i] = remappings [_edgeIndices[i]]; } return [newPositions, newIndices, newEdgeIndices]; }