UNPKG

@animech-public/playcanvas

Version:
228 lines (226 loc) 7.45 kB
function DracoWorker(jsUrl, wasmUrl) { let draco; const POSITION_ATTRIBUTE = 0; const NORMAL_ATTRIBUTE = 1; const wrap = (typedArray, dataType) => { switch (dataType) { case draco.DT_INT8: return new Int8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength); case draco.DT_INT16: return new Int16Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength / 2); case draco.DT_INT32: return new Int32Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength / 4); case draco.DT_UINT8: return new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength); case draco.DT_UINT16: return new Uint16Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength / 2); case draco.DT_UINT32: return new Uint32Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength / 4); case draco.DT_FLOAT32: return new Float32Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength / 4); } return null; }; const componentSizeInBytes = dataType => { switch (dataType) { case draco.DT_INT8: return 1; case draco.DT_INT16: return 2; case draco.DT_INT32: return 4; case draco.DT_UINT8: return 1; case draco.DT_UINT16: return 2; case draco.DT_UINT32: return 4; case draco.DT_FLOAT32: return 4; } return 1; }; const attributeSizeInBytes = attribute => { return attribute.num_components() * componentSizeInBytes(attribute.data_type()); }; const attributeOrder = { 0: 0, 1: 1, 5: 2, 2: 3, 7: 4, 8: 5, 4: 6, 3: 7 }; const generateNormals = (vertices, indices) => { const subtract = (dst, a, b) => { dst[0] = a[0] - b[0]; dst[1] = a[1] - b[1]; dst[2] = a[2] - b[2]; }; const cross = (dst, a, b) => { dst[0] = a[1] * b[2] - b[1] * a[2]; dst[1] = a[2] * b[0] - b[2] * a[0]; dst[2] = a[0] * b[1] - b[0] * a[1]; }; const normalize = (dst, offset) => { const a = dst[offset + 0]; const b = dst[offset + 1]; const c = dst[offset + 2]; const l = 1.0 / Math.sqrt(a * a + b * b + c * c); dst[offset + 0] *= l; dst[offset + 1] *= l; dst[offset + 2] *= l; }; const copy = (dst, src, srcOffset) => { for (let i = 0; i < 3; ++i) { dst[i] = src[srcOffset + i]; } }; const numTriangles = indices.length / 3; const numVertices = vertices.length / 3; const result = new Float32Array(vertices.length); const a = [0, 0, 0], b = [0, 0, 0], c = [0, 0, 0], t1 = [0, 0, 0], t2 = [0, 0, 0], n = [0, 0, 0]; for (let i = 0; i < numTriangles; ++i) { const v0 = indices[i * 3 + 0] * 3; const v1 = indices[i * 3 + 1] * 3; const v2 = indices[i * 3 + 2] * 3; copy(a, vertices, v0); copy(b, vertices, v1); copy(c, vertices, v2); subtract(t1, b, a); subtract(t2, c, a); cross(n, t1, t2); normalize(n, 0); for (let j = 0; j < 3; ++j) { result[v0 + j] += n[j]; result[v1 + j] += n[j]; result[v2 + j] += n[j]; } } for (let i = 0; i < numVertices; ++i) { normalize(result, i * 3); } return new Uint8Array(result.buffer); }; const decodeMesh = inputBuffer => { const result = {}; const buffer = new draco.DecoderBuffer(); buffer.Init(inputBuffer, inputBuffer.length); const decoder = new draco.Decoder(); if (decoder.GetEncodedGeometryType(buffer) !== draco.TRIANGULAR_MESH) { result.error = 'Failed to decode draco mesh: not a mesh'; return result; } const mesh = new draco.Mesh(); const status = decoder.DecodeBufferToMesh(buffer, mesh); if (!status || !status.ok() || mesh.ptr === 0) { result.error = 'Failed to decode draco asset'; return result; } const numIndices = mesh.num_faces() * 3; const shortIndices = mesh.num_points() <= 65535; const indicesSize = numIndices * (shortIndices ? 2 : 4); const indicesPtr = draco._malloc(indicesSize); if (shortIndices) { decoder.GetTrianglesUInt16Array(mesh, indicesSize, indicesPtr); result.indices = new Uint16Array(draco.HEAPU16.buffer, indicesPtr, numIndices).slice().buffer; } else { decoder.GetTrianglesUInt32Array(mesh, indicesSize, indicesPtr); result.indices = new Uint32Array(draco.HEAPU32.buffer, indicesPtr, numIndices).slice().buffer; } draco._free(indicesPtr); const attributes = []; for (let i = 0; i < mesh.num_attributes(); ++i) { attributes.push(decoder.GetAttribute(mesh, i)); } attributes.sort((a, b) => { var _attributeOrder$a$att, _attributeOrder$b$att; return ((_attributeOrder$a$att = attributeOrder[a.attribute_type()]) != null ? _attributeOrder$a$att : attributeOrder.length) - ((_attributeOrder$b$att = attributeOrder[b.attribute_type()]) != null ? _attributeOrder$b$att : attributeOrder.length); }); result.attributes = attributes.map(a => a.unique_id()); let totalVertexSize = 0; const offsets = attributes.map(a => { const offset = totalVertexSize; totalVertexSize += Math.ceil(attributeSizeInBytes(a) / 4) * 4; return offset; }); const hasNormals = attributes.some(a => a.attribute_type() === NORMAL_ATTRIBUTE); const normalOffset = offsets[1]; if (!hasNormals) { for (let i = 1; i < offsets.length; ++i) { offsets[i] += 12; } totalVertexSize += 12; } result.vertices = new ArrayBuffer(mesh.num_points() * totalVertexSize); const dst = new Uint8Array(result.vertices); for (let i = 0; i < mesh.num_attributes(); ++i) { const attribute = attributes[i]; const sizeInBytes = attributeSizeInBytes(attribute); const ptrSize = mesh.num_points() * sizeInBytes; const ptr = draco._malloc(ptrSize); decoder.GetAttributeDataArrayForAllPoints(mesh, attribute, attribute.data_type(), ptrSize, ptr); const src = new Uint8Array(draco.HEAPU8.buffer, ptr, ptrSize); for (let j = 0; j < mesh.num_points(); ++j) { for (let c = 0; c < sizeInBytes; ++c) { dst[j * totalVertexSize + offsets[i] + c] = src[j * sizeInBytes + c]; } } if (!hasNormals && attribute.attribute_type() === POSITION_ATTRIBUTE) { const normals = generateNormals(wrap(src, attribute.data_type()), shortIndices ? new Uint16Array(result.indices) : new Uint32Array(result.indices)); for (let j = 0; j < mesh.num_points(); ++j) { for (let c = 0; c < 12; ++c) { dst[j * totalVertexSize + normalOffset + c] = normals[j * 12 + c]; } } } draco._free(ptr); } draco.destroy(mesh); draco.destroy(decoder); draco.destroy(buffer); return result; }; const decode = data => { const result = decodeMesh(new Uint8Array(data.buffer)); self.postMessage({ jobId: data.jobId, error: result.error, indices: result.indices, vertices: result.vertices, attributes: result.attributes }, [result.indices, result.vertices].filter(t => t != null)); }; const workQueue = []; self.onmessage = message => { const data = message.data; switch (data.type) { case 'init': self.DracoDecoderModule({ instantiateWasm: (imports, successCallback) => { WebAssembly.instantiate(data.module, imports).then(result => successCallback(result)).catch(reason => console.error(`instantiate failed + ${reason}`)); return {}; } }).then(instance => { draco = instance; workQueue.forEach(data => decode(data)); }); break; case 'decodeMesh': if (draco) { decode(data); } else { workQueue.push(data); } break; } }; } export { DracoWorker };