kopia lustrzana https://github.com/robhawkes/vizicities
377 wiersze
9.5 KiB
JavaScript
377 wiersze
9.5 KiB
JavaScript
/*
|
|
* BufferGeometry helpers
|
|
*/
|
|
|
|
import 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;
|