UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

241 lines (220 loc) 7.33 kB
import { m as macro } from '../../macros2.js'; import BinaryHelper from '../Core/BinaryHelper.js'; import DataAccessHelper from '../Core/DataAccessHelper.js'; import vtkDracoReader from './DracoReader.js'; import { parseGLTF, createVTKObjects, GLTFCameraToVTKCamera, applyTransformToCamera, createPropertyFromGLTFMaterial } from './GLTFImporter/Reader.js'; import parseGLB from './GLTFImporter/Decoder.js'; import { createAnimationMixer } from './GLTFImporter/Animations.js'; import { BINARY_HEADER_MAGIC } from './GLTFImporter/Constants.js'; const { vtkDebugMacro, vtkErrorMacro } = macro; // ---------------------------------------------------------------------------- // vtkGLTFImporter methods // ---------------------------------------------------------------------------- function vtkGLTFImporter(publicAPI, model) { // Set our className model.classHierarchy.push('vtkGLTFImporter'); // Create default dataAccessHelper if not available if (!model.dataAccessHelper) { model.dataAccessHelper = DataAccessHelper.get('http'); } // Internal method to fetch Array 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 }); } // Set DataSet url publicAPI.setUrl = (url, option = { binary: true }) => { model.url = url; // Remove the file in the URL const path = url.split('/'); path.pop(); model.baseURL = path.join('/'); model.compression = option.compression; model.sceneId = option.sceneId ? option.sceneId : 0; // Fetch metadata return publicAPI.loadData({ progressCallback: option.progressCallback, binary: !!option.binary }); }; // Fetch the actual data arrays publicAPI.loadData = (option = {}) => { const promise = fetchData(model.url, option); promise.then(publicAPI.parse); return promise; }; publicAPI.parse = content => { if (typeof content === 'string') { publicAPI.parseAsText(content); } else { publicAPI.parseAsBinary(content); } }; publicAPI.parseAsBinary = async content => { if (!content) { return; } if (content !== model.parseData) { publicAPI.modified(); } else { return; } const glTF = {}; const options = { baseUri: model.baseURL }; const magic = BinaryHelper.arrayBufferToString(new Uint8Array(content, 0, 4)); if (magic === BINARY_HEADER_MAGIC) { const { json, buffers } = parseGLB(content); vtkDebugMacro('Loaded GLB', json, buffers); glTF.glbBuffers = buffers; glTF.json = json; } else { glTF.json = JSON.parse(BinaryHelper.arrayBufferToString(content)); } if (glTF.json.asset === undefined || glTF.json.asset.version[0] < 2) { vtkErrorMacro('Unsupported asset. glTF versions >=2.0 are supported.'); return; } model.glTFTree = await parseGLTF(glTF, options); model.actors = new Map(); model.cameras = new Map(); model.lights = new Map(); model.animations = []; model.variants = []; model.variantMappings = new Map(); await createVTKObjects(model); model.scenes = model.glTFTree.scenes; publicAPI.invokeReady(); }; publicAPI.parseAsText = content => { if (!content) { return; } if (content !== model.parseData) { publicAPI.modified(); } else { return; } model.parseData = content; }; publicAPI.requestData = (inData, outData) => { publicAPI.parse(model.parseData); }; publicAPI.setDracoDecoder = async dracoDecoder => { await vtkDracoReader.setDracoDecoder(dracoDecoder); }; publicAPI.importActors = () => { // Add actors to renderer model.actors.forEach(actor => model.renderer.addActor(actor)); }; publicAPI.importCameras = () => { // Set up camera model.glTFTree.cameras?.forEach(glTFcamera => { const camera = GLTFCameraToVTKCamera(glTFcamera); model.cameras.set(glTFcamera.id, camera); }); model.scenes.forEach(scene => { scene.nodes.forEach(node => { const camera = model.cameras.get(node.camera?.id); if (camera) { applyTransformToCamera(camera, node.transform); } }); }); }; publicAPI.importAnimations = () => { // Set up animations if (model.glTFTree.animations?.length > 0) { model.animationMixer = createAnimationMixer(model.actors, model.glTFTree.accessors); model.glTFTree.animations.forEach(animation => { model.animationMixer.addAnimation(animation); }); } model.animations = model.glTFTree.animations || []; }; publicAPI.importLights = () => { // Set up lights model.lights?.forEach(light => { vtkDebugMacro('Adding light', light); model.renderer.addLight(light); }); }; publicAPI.setCamera = cameraId => { const camera = model.cameras.get(cameraId); if (!camera) { vtkErrorMacro(`Camera ${cameraId} not found`); return; } vtkDebugMacro('Setting camera', camera); model.renderer.setActiveCamera(camera); }; publicAPI.switchToVariant = async variantIndex => { const promises = Array.from(model.actors).map(async ([nodeId, actor]) => { vtkDebugMacro('Switching to variant', variantIndex, 'for node', nodeId); const variantMappings = model.variantMappings.get(nodeId); if (variantMappings) { const mapping = variantMappings.find(m => m.variants.includes(variantIndex)); if (mapping) { const variantMaterial = model.glTFTree.materials[mapping.material]; await createPropertyFromGLTFMaterial(model, variantMaterial, actor); } } }); await Promise.all(promises); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { // baseURL: null, // dataAccessHelper: null, // url: null, }; // ---------------------------------------------------------------------------- function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API macro.obj(publicAPI, model); macro.get(publicAPI, model, ['url', 'baseURL', 'actors', 'scenes', 'cameras', 'animations', 'animationMixer', 'variants', 'variantMappings']); macro.set(publicAPI, model, ['renderer', 'dracoDecoder']); macro.event(publicAPI, model, 'ready'); // vtkGLTFImporter methods vtkGLTFImporter(publicAPI, model); // To support destructuring if (!model.compression) { model.compression = null; } if (!model.progressCallback) { model.progressCallback = null; } } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkGLTFImporter'); // ---------------------------------------------------------------------------- var vtkGLTFImporter$1 = { extend, newInstance }; export { vtkGLTFImporter$1 as default, extend, newInstance };