@babylonjs/viewer
Version:
The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.
121 lines (118 loc) • 4.06 kB
JavaScript
function GetByteIndex(bitIndex) {
return Math.floor(bitIndex / 8);
}
function GetBitMask(bitIndex) {
return 1 << (bitIndex % 8);
}
/**
* An fixed size array that effectively stores boolean values where each value is a single bit of backing data.
* @remarks
* All bits are initialized to false.
*/
class BitArray {
/**
* Creates a new bit array with a fixed size.
* @param size The number of bits to store.
*/
constructor(size) {
this.size = size;
this._byteArray = new Uint8Array(Math.ceil(this.size / 8));
}
/**
* Gets the current value at the specified index.
* @param bitIndex The index to get the value from.
* @returns The value at the specified index.
*/
get(bitIndex) {
if (bitIndex >= this.size) {
throw new RangeError("Bit index out of range");
}
const byteIndex = GetByteIndex(bitIndex);
const bitMask = GetBitMask(bitIndex);
return (this._byteArray[byteIndex] & bitMask) !== 0;
}
/**
* Sets the value at the specified index.
* @param bitIndex The index to set the value at.
* @param value The value to set.
*/
set(bitIndex, value) {
if (bitIndex >= this.size) {
throw new RangeError("Bit index out of range");
}
const byteIndex = GetByteIndex(bitIndex);
const bitMask = GetBitMask(bitIndex);
if (value) {
this._byteArray[byteIndex] |= bitMask;
}
else {
this._byteArray[byteIndex] &= ~bitMask;
}
}
}
/**
* Sort (in place) the index array so that faces with common indices are close
* @param indices the array of indices to sort
*/
function OptimizeIndices(indices) {
const faces = [];
const faceCount = indices.length / 3;
// Step 1: Break the indices array into faces
for (let i = 0; i < faceCount; i++) {
faces.push([indices[i * 3], indices[i * 3 + 1], indices[i * 3 + 2]]);
}
// Step 2: Build a graph connecting faces sharing a vertex
const vertexToFaceMap = new Map();
for (let faceIndex = 0; faceIndex < faces.length; faceIndex++) {
const face = faces[faceIndex];
for (const vertex of face) {
let face = vertexToFaceMap.get(vertex);
if (!face) {
vertexToFaceMap.set(vertex, (face = []));
}
face.push(faceIndex);
}
}
// Step 3: Traverse faces using DFS to ensure connected faces are close
const visited = new BitArray(faceCount);
const sortedFaces = [];
// Using a stack and not a recursive version to avoid call stack overflow
const deepFirstSearchStack = (startFaceIndex) => {
const stack = [startFaceIndex];
while (stack.length > 0) {
const currentFaceIndex = stack.pop();
if (visited.get(currentFaceIndex)) {
continue;
}
visited.set(currentFaceIndex, true);
sortedFaces.push(faces[currentFaceIndex]);
// Push unvisited neighbors (faces sharing a vertex) onto the stack
for (const vertex of faces[currentFaceIndex]) {
const neighbors = vertexToFaceMap.get(vertex);
if (!neighbors) {
return;
}
for (const neighborFaceIndex of neighbors) {
if (!visited.get(neighborFaceIndex)) {
stack.push(neighborFaceIndex);
}
}
}
}
};
// Start DFS from the first face
for (let i = 0; i < faceCount; i++) {
if (!visited.get(i)) {
deepFirstSearchStack(i);
}
}
// Step 4: Flatten the sorted faces back into an array
let index = 0;
for (const face of sortedFaces) {
indices[index++] = face[0];
indices[index++] = face[1];
indices[index++] = face[2];
}
}
export { OptimizeIndices };
//# sourceMappingURL=mesh.vertexData.functions-DPz5mYFq.esm.js.map