vizicities/src/util/Buffer.js

377 wiersze
9.5 KiB
JavaScript
Executable File

/*
* BufferGeometry helpers
*/
import * as THREE from 'three';
import {TextEncoder, TextDecoder} from 'text-encoding';
var Buffer = (function() {
// Merge TypedArrays of the same type
// Returns merged array as well as indexes for splitting the array
var mergeFloat32Arrays = function(arrays) {
var size = 0;
var map = new Int32Array(arrays.length * 2);
var lastIndex = 0;
var length;
// Find size of each array
arrays.forEach((_array, index) => {
length = _array.length;
size += length;
map.set([lastIndex, lastIndex + length], index * 2);
lastIndex += length;
});
// Create a new array of total size
var mergedArray = new Float32Array(size);
// Add each array to the new array
arrays.forEach((_array, index) => {
mergedArray.set(_array, map[index * 2]);
});
return [
mergedArray,
map
];
};
var splitFloat32Array = function(data) {
var arr = data[0];
var map = data[1];
var start;
var arrays = [];
// Iterate over map
for (var i = 0; i < map.length / 2; i++) {
start = i * 2;
arrays.push(arr.subarray(map[start], map[start + 1]));
}
return arrays;
};
// TODO: Create a generic method that can work for any typed array
var mergeUint8Arrays = function(arrays) {
var size = 0;
var map = new Int32Array(arrays.length * 2);
var lastIndex = 0;
var length;
// Find size of each array
arrays.forEach((_array, index) => {
length = _array.length;
size += length;
map.set([lastIndex, lastIndex + length], index * 2);
lastIndex += length;
});
// Create a new array of total size
var mergedArray = new Uint8Array(size);
// Add each array to the new array
arrays.forEach((_array, index) => {
mergedArray.set(_array, map[index * 2]);
});
return [
mergedArray,
map
];
};
// TODO: Dedupe with splitFloat32Array
var splitUint8Array = function(data) {
var arr = data[0];
var map = data[1];
var start;
var arrays = [];
// Iterate over map
for (var i = 0; i < map.length / 2; i++) {
start = i * 2;
arrays.push(arr.subarray(map[start], map[start + 1]));
}
return arrays;
};
// Merge multiple attribute objects into a single attribute object
//
// Attribute objects must all use the same attribute keys
var mergeAttributes = function(attributes) {
var lengths = {};
// Find array lengths
attributes.forEach(_attributes => {
for (var k in _attributes) {
if (!lengths[k]) {
lengths[k] = 0;
}
lengths[k] += _attributes[k].length;
}
});
var mergedAttributes = {};
// Set up arrays to merge into
for (var k in lengths) {
mergedAttributes[k] = new Float32Array(lengths[k]);
}
var lastLengths = {};
attributes.forEach(_attributes => {
for (var k in _attributes) {
if (!lastLengths[k]) {
lastLengths[k] = 0;
}
mergedAttributes[k].set(_attributes[k], lastLengths[k]);
lastLengths[k] += _attributes[k].length;
}
});
return mergedAttributes;
};
var createLineGeometry = function(lines, offset) {
var geometry = new THREE.BufferGeometry();
var vertices = new Float32Array(lines.verticesCount * 3);
var colours = new Float32Array(lines.verticesCount * 3);
var pickingIds;
if (lines.pickingIds) {
// One component per vertex (1)
pickingIds = new Float32Array(lines.verticesCount);
}
var _vertices;
var _colour;
var _pickingId;
var lastIndex = 0;
for (var i = 0; i < lines.vertices.length; i++) {
_vertices = lines.vertices[i];
_colour = lines.colours[i];
if (pickingIds) {
_pickingId = lines.pickingIds[i];
}
for (var j = 0; j < _vertices.length; j++) {
var ax = _vertices[j][0] + offset.x;
var ay = _vertices[j][1];
var az = _vertices[j][2] + offset.y;
var c1 = _colour[j];
vertices[lastIndex * 3 + 0] = ax;
vertices[lastIndex * 3 + 1] = ay;
vertices[lastIndex * 3 + 2] = az;
colours[lastIndex * 3 + 0] = c1[0];
colours[lastIndex * 3 + 1] = c1[1];
colours[lastIndex * 3 + 2] = c1[2];
if (pickingIds) {
pickingIds[lastIndex] = _pickingId;
}
lastIndex++;
}
}
// itemSize = 3 because there are 3 values (components) per vertex
geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.addAttribute('color', new THREE.BufferAttribute(colours, 3));
if (pickingIds) {
geometry.addAttribute('pickingId', new THREE.BufferAttribute(pickingIds, 1));
}
geometry.computeBoundingBox();
return geometry;
};
// TODO: Make picking IDs optional
var createGeometry = function(attributes, offset) {
var geometry = new THREE.BufferGeometry();
// Three components per vertex per face (3 x 3 = 9)
var vertices = new Float32Array(attributes.facesCount * 9);
var normals = new Float32Array(attributes.facesCount * 9);
var colours = new Float32Array(attributes.facesCount * 9);
var pickingIds;
if (attributes.pickingIds) {
// One component per vertex per face (1 x 3 = 3)
pickingIds = new Float32Array(attributes.facesCount * 3);
}
var pA = new THREE.Vector3();
var pB = new THREE.Vector3();
var pC = new THREE.Vector3();
var cb = new THREE.Vector3();
var ab = new THREE.Vector3();
var index;
var _faces;
var _vertices;
var _colour;
var _pickingId;
var lastIndex = 0;
for (var i = 0; i < attributes.faces.length; i++) {
_faces = attributes.faces[i];
_vertices = attributes.vertices[i];
_colour = attributes.colours[i];
if (pickingIds) {
_pickingId = attributes.pickingIds[i];
}
for (var j = 0; j < _faces.length; j++) {
// Array of vertex indexes for the face
index = _faces[j][0];
var ax = _vertices[index][0] + offset.x;
var ay = _vertices[index][1];
var az = _vertices[index][2] + offset.y;
var c1 = _colour[j][0];
index = _faces[j][1];
var bx = _vertices[index][0] + offset.x;
var by = _vertices[index][1];
var bz = _vertices[index][2] + offset.y;
var c2 = _colour[j][1];
index = _faces[j][2];
var cx = _vertices[index][0] + offset.x;
var cy = _vertices[index][1];
var cz = _vertices[index][2] + offset.y;
var c3 = _colour[j][2];
// Flat face normals
// From: http://threejs.org/examples/webgl_buffergeometry.html
pA.set(ax, ay, az);
pB.set(bx, by, bz);
pC.set(cx, cy, cz);
cb.subVectors(pC, pB);
ab.subVectors(pA, pB);
cb.cross(ab);
cb.normalize();
var nx = cb.x;
var ny = cb.y;
var nz = cb.z;
vertices[lastIndex * 9 + 0] = ax;
vertices[lastIndex * 9 + 1] = ay;
vertices[lastIndex * 9 + 2] = az;
normals[lastIndex * 9 + 0] = nx;
normals[lastIndex * 9 + 1] = ny;
normals[lastIndex * 9 + 2] = nz;
colours[lastIndex * 9 + 0] = c1[0];
colours[lastIndex * 9 + 1] = c1[1];
colours[lastIndex * 9 + 2] = c1[2];
vertices[lastIndex * 9 + 3] = bx;
vertices[lastIndex * 9 + 4] = by;
vertices[lastIndex * 9 + 5] = bz;
normals[lastIndex * 9 + 3] = nx;
normals[lastIndex * 9 + 4] = ny;
normals[lastIndex * 9 + 5] = nz;
colours[lastIndex * 9 + 3] = c2[0];
colours[lastIndex * 9 + 4] = c2[1];
colours[lastIndex * 9 + 5] = c2[2];
vertices[lastIndex * 9 + 6] = cx;
vertices[lastIndex * 9 + 7] = cy;
vertices[lastIndex * 9 + 8] = cz;
normals[lastIndex * 9 + 6] = nx;
normals[lastIndex * 9 + 7] = ny;
normals[lastIndex * 9 + 8] = nz;
colours[lastIndex * 9 + 6] = c3[0];
colours[lastIndex * 9 + 7] = c3[1];
colours[lastIndex * 9 + 8] = c3[2];
if (pickingIds) {
pickingIds[lastIndex * 3 + 0] = _pickingId;
pickingIds[lastIndex * 3 + 1] = _pickingId;
pickingIds[lastIndex * 3 + 2] = _pickingId;
}
lastIndex++;
}
}
// itemSize = 3 because there are 3 values (components) per vertex
geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));
geometry.addAttribute('color', new THREE.BufferAttribute(colours, 3));
if (pickingIds) {
geometry.addAttribute('pickingId', new THREE.BufferAttribute(pickingIds, 1));
}
geometry.computeBoundingBox();
return geometry;
};
var textEncoder = new TextEncoder('utf-8');
var textDecoder = new TextDecoder('utf-8');
var stringToUint8Array = function(str) {
return textEncoder.encode(str);
};
var uint8ArrayToString = function(ab) {
return textDecoder.decode(ab);
};
var fillTypedArray = function(arr, value) {
for (var i = 0; i < arr.length; i++) {
arr[i] = value;
}
};
return {
mergeFloat32Arrays: mergeFloat32Arrays,
splitFloat32Array: splitFloat32Array,
mergeUint8Arrays: mergeUint8Arrays,
splitUint8Array: splitUint8Array,
mergeAttributes: mergeAttributes,
createLineGeometry: createLineGeometry,
createGeometry: createGeometry,
stringToUint8Array: stringToUint8Array,
uint8ArrayToString: uint8ArrayToString,
fillTypedArray: fillTypedArray
};
})();
export default Buffer;