src/utils/geometry.js
import { vec3 } from 'gl-matrix';
export function flatten(arr) {
let output = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i]) || arr[i] instanceof Float32Array) {
output = output.concat(flatten(arr[i]));
} else {
output.push(arr[i]);
}
}
return output;
}
export function unflatten(arr, amount) {
const output = [];
for (let i = 0; i < arr.length; i += amount) {
const value = [];
for (let j = 0; j < amount; j++) {
value.push(arr[i + j]);
}
output.push(value);
}
return output;
}
export function generateVertexNormals(positions, indices) {
const faces = unflatten(indices, 3);
const vertices = unflatten(positions, 3);
const temp = [];
const cb = vec3.create();
const ab = vec3.create();
const cross = vec3.create();
let vA;
let vB;
let vC;
for (let i = 0; i < faces.length; i++) {
const face = faces[i];
const a = face[0];
const b = face[1];
const c = face[2];
vA = vertices[a];
vB = vertices[b];
vC = vertices[c];
vec3.subtract(cb, vC, vB);
vec3.subtract(ab, vA, vB);
vec3.cross(cross, cb, ab);
if (temp[a] === undefined) {
temp[a] = vec3.create();
}
if (temp[b] === undefined) {
temp[b] = vec3.create();
}
if (temp[c] === undefined) {
temp[c] = vec3.create();
}
vec3.add(temp[a], temp[a], cross);
vec3.add(temp[b], temp[b], cross);
vec3.add(temp[c], temp[c], cross);
}
for (let i = 0; i < temp.length; i++) {
vec3.normalize(temp[i], temp[i]);
}
return flatten(temp, 3);
}
export function mergeVertices(data) {
const positions = unflatten(data.positions, 3);
const verticesMap = {};
const unique = [];
const changes = [];
const precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
const precision = Math.pow(10, precisionPoints); // eslint-disable-line
// remove duplicated positions
for (let i = 0; i < positions.length; i++) {
const v = positions[i];
const key = `
${Math.round(v[0] * precision)}_
${Math.round(v[1] * precision)}_
${Math.round(v[2] * precision)}
`;
if (verticesMap[key] === undefined) {
verticesMap[key] = i;
unique.push(positions[i]);
changes[i] = unique.length - 1;
} else {
// console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
changes[i] = changes[verticesMap[key]];
}
}
// remove duplicated faces
const faceIndicesToRemove = [];
const faces = unflatten(data.indices, 3);
for (let i = 0; i < faces.length; i++) {
const face = faces[i];
face[0] = changes[face[0]];
face[1] = changes[face[1]];
face[2] = changes[face[2]];
const indices = [face[0], face[1], face[2]];
for (let n = 0; n < 3; n++) {
if (indices[n] === indices[(n + 1) % 3]) {
faceIndicesToRemove.push(i);
break;
}
}
}
// remove duplicated uvs
for (let i = faceIndicesToRemove.length - 1; i >= 0; i--) {
const idx = faceIndicesToRemove[i];
faces.splice(idx, 1);
for (let j = 0; j < this.faceVertexUvs.length; j++) {
this.faceVertexUvs[j].splice(idx, 1);
}
}
const p = flatten(unique, 3);
const f = flatten(faces, 3);
return {
positions: new Float32Array(p),
indices: new Uint16Array(f),
normals: new Float32Array(generateVertexNormals(p, f)),
};
}
export function subdivide(data) {
// subdivision geometry
const positions = unflatten(data.positions, 3);
console.log('positions', positions);
return data;
}