import DataAccessHelper from 'vtk.js/Sources/IO/Core/DataAccessHelper'; import macro from 'vtk.js/Sources/macros'; import vtkCellArray from 'vtk.js/Sources/Common/Core/CellArray'; import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';
import 'vtk.js/Sources/IO/Core/DataAccessHelper/LiteHttpDataAccessHelper';
const { vtkErrorMacro } = macro; let decoderModule = {};
function setWasmBinary(url, binaryName) { const dracoDecoderType = {};
return new Promise((resolve, reject) => { dracoDecoderType.wasmBinaryFile = binaryName;
const xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer';
xhr.onload = () => { if (xhr.status === 200) { dracoDecoderType.wasmBinary = xhr.response; Promise.resolve(window.DracoDecoderModule(dracoDecoderType)).then( (module) => { decoderModule = module; resolve(true); }, reject ); } else { reject(Error(`WASM binary could not be loaded: ${xhr.statusText}`)); } }; xhr.send(null); }); }
function setDracoDecoder(createDracoModule) { decoderModule = createDracoModule({}); }
function getDracoDecoder() { return decoderModule; }
function decodeBuffer(buffer) { const byteArray = new Int8Array(buffer); const decoder = new decoderModule.Decoder(); const decoderBuffer = new decoderModule.DecoderBuffer(); decoderBuffer.Init(byteArray, byteArray.length);
const geometryType = decoder.GetEncodedGeometryType(decoderBuffer);
let dracoGeometry; if (geometryType === decoderModule.TRIANGULAR_MESH) { dracoGeometry = new decoderModule.Mesh(); const status = decoder.DecodeBufferToMesh(decoderBuffer, dracoGeometry); if (!status.ok()) { vtkErrorMacro(`Could not decode Draco file: ${status.error_msg()}`); } } else { vtkErrorMacro('Wrong geometry type, expected mesh, got point cloud.'); }
decoderModule.destroy(decoderBuffer); decoderModule.destroy(decoder); return dracoGeometry; }
function getDracoAttributeAsFloat32Array(dracoGeometry, attributeId) { const decoder = new decoderModule.Decoder(); const attribute = decoder.GetAttribute(dracoGeometry, attributeId); const numberOfComponents = attribute.num_components(); const numberOfPoints = dracoGeometry.num_points();
const attributeData = new decoderModule.DracoFloat32Array(); decoder.GetAttributeFloatForAllPoints( dracoGeometry, attribute, attributeData );
let i = numberOfPoints * numberOfComponents; const attributeArray = new Float32Array(i); while (i--) { attributeArray[i] = attributeData.GetValue(i); }
return attributeArray; }
function getPolyDataFromDracoGeometry(dracoGeometry) { const decoder = new decoderModule.Decoder();
const positionAttributeId = decoder.GetAttributeId( dracoGeometry, decoderModule.POSITION );
if (positionAttributeId === -1) { console.error('No position attribute found in the decoded model.'); decoderModule.destroy(decoder); decoderModule.destroy(dracoGeometry); return null; }
const positionArray = getDracoAttributeAsFloat32Array( dracoGeometry, positionAttributeId, decoderModule );
let i = dracoGeometry.num_faces(); const indices = new Uint32Array(i * 4); const indicesArray = new decoderModule.DracoInt32Array(); while (i--) { decoder.GetFaceFromMesh(dracoGeometry, i, indicesArray); const index = i * 4; indices[index] = 3; indices[index + 1] = indicesArray.GetValue(0); indices[index + 2] = indicesArray.GetValue(1); indices[index + 3] = indicesArray.GetValue(2); }
const cellArray = vtkCellArray.newInstance({ values: indices }); const polyData = vtkPolyData.newInstance({ polys: cellArray }); polyData.getPoints().setData(positionArray);
const pointData = polyData.getPointData();
const normalAttributeId = decoder.GetAttributeId( dracoGeometry, decoderModule.NORMAL );
if (normalAttributeId !== -1) { const normalArray = getDracoAttributeAsFloat32Array( dracoGeometry, decoderModule.NORMAL, decoderModule );
const normals = vtkDataArray.newInstance({ numberOfComponents: 3, values: normalArray, name: 'Normals', }); pointData.setNormals(normals); }
const texCoordAttributeId = decoder.GetAttributeId( dracoGeometry, decoderModule.TEX_COORD );
if (texCoordAttributeId !== -1) { const texCoordArray = getDracoAttributeAsFloat32Array( dracoGeometry, texCoordAttributeId, decoderModule );
const texCoords = vtkDataArray.newInstance({ numberOfComponents: 2, values: texCoordArray, name: 'TCoords', }); pointData.setTCoords(texCoords); }
const colorAttributeId = decoder.GetAttributeId( dracoGeometry, decoderModule.COLOR );
if (colorAttributeId !== -1) { const colorArray = getDracoAttributeAsFloat32Array( dracoGeometry, colorAttributeId, decoderModule );
const scalars = vtkDataArray.newInstance({ numberOfComponents: 3, values: colorArray, name: 'Scalars', });
pointData.setScalars(scalars); }
decoderModule.destroy(decoder); return polyData; }
function vtkDracoReader(publicAPI, model) { model.classHierarchy.push('vtkDracoReader');
if (!model.dataAccessHelper) { model.dataAccessHelper = DataAccessHelper.get('http'); }
function fetchData(url, option = {}) { const { compression, progressCallback } = model; if (option.binary) { return model.dataAccessHelper.fetchBinary(url, { compression, progressCallback, }); } return model.dataAccessHelper.fetchText(publicAPI, url, { compression, progressCallback, }); }
publicAPI.setUrl = (url, option = { binary: true }) => { model.url = url;
const path = url.split('/'); path.pop(); model.baseURL = path.join('/');
model.compression = option.compression;
return publicAPI.loadData({ progressCallback: option.progressCallback, binary: !!option.binary, }); };
publicAPI.loadData = (option = {}) => { const promise = fetchData(model.url, option); promise.then(publicAPI.parse); return promise; };
publicAPI.parse = (content) => { publicAPI.parseAsArrayBuffer(content); };
publicAPI.parseAsArrayBuffer = (content) => { if (!content) { return; } if (content !== model.parseData) { publicAPI.modified(); } else { return; }
model.parseData = content; const dracoGeometry = decodeBuffer(content); const polyData = getPolyDataFromDracoGeometry(dracoGeometry); decoderModule.destroy(dracoGeometry); model.output[0] = polyData; };
publicAPI.requestData = () => { publicAPI.parse(model.parseData); }; }
const DEFAULT_VALUES = { };
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues);
macro.obj(publicAPI, model); macro.get(publicAPI, model, ['url', 'baseURL']); macro.setGet(publicAPI, model, ['dataAccessHelper']); macro.algo(publicAPI, model, 0, 1);
vtkDracoReader(publicAPI, model);
if (!model.compression) { model.compression = null; } if (!model.progressCallback) { model.progressCallback = null; } }
export const newInstance = macro.newInstance(extend, 'vtkDracoReader');
export default { extend, newInstance, setDracoDecoder, setWasmBinary, getDracoDecoder, };
|