UNPKG

@xeokit/xeokit-sdk

Version:

3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision

246 lines (185 loc) 10.8 kB
/* Parser for .XKT Format V5 .XKT specifications: https://github.com/xeokit/xeokit-sdk/wiki/XKT-Format */ import {utils} from "../../../viewer/scene/utils.js"; import * as p from "./lib/pako.js"; let pako = window.pako || p; if (!pako.inflate) { // See https://github.com/nodeca/pako/issues/97 pako = pako.default; } function extract(elements) { return { positions: elements[0], normals: elements[1], indices: elements[2], edgeIndices: elements[3], matrices: elements[4], eachPrimitivePositionsAndNormalsPortion: elements[5], eachPrimitiveIndicesPortion: elements[6], eachPrimitiveEdgeIndicesPortion: elements[7], eachPrimitiveColor: elements[8], primitiveInstances: elements[9], eachEntityId: elements[10], eachEntityPrimitiveInstancesPortion: elements[11], eachEntityMatricesPortion: elements[12] }; } function inflate(deflatedData) { return { positions: new Float32Array(pako.inflate(deflatedData.positions).buffer), normals: new Int8Array(pako.inflate(deflatedData.normals).buffer), indices: new Uint32Array(pako.inflate(deflatedData.indices).buffer), edgeIndices: new Uint32Array(pako.inflate(deflatedData.edgeIndices).buffer), matrices: new Float32Array(pako.inflate(deflatedData.matrices).buffer), eachPrimitivePositionsAndNormalsPortion: new Uint32Array(pako.inflate(deflatedData.eachPrimitivePositionsAndNormalsPortion).buffer), eachPrimitiveIndicesPortion: new Uint32Array(pako.inflate(deflatedData.eachPrimitiveIndicesPortion).buffer), eachPrimitiveEdgeIndicesPortion: new Uint32Array(pako.inflate(deflatedData.eachPrimitiveEdgeIndicesPortion).buffer), eachPrimitiveColor: new Uint8Array(pako.inflate(deflatedData.eachPrimitiveColor).buffer), primitiveInstances: new Uint32Array(pako.inflate(deflatedData.primitiveInstances).buffer), eachEntityId: pako.inflate(deflatedData.eachEntityId, {to: 'string'}), eachEntityPrimitiveInstancesPortion: new Uint32Array(pako.inflate(deflatedData.eachEntityPrimitiveInstancesPortion).buffer), eachEntityMatricesPortion: new Uint32Array(pako.inflate(deflatedData.eachEntityMatricesPortion).buffer) }; } const decompressColor = (function () { const color2 = new Float32Array(3); return function (color) { color2[0] = color[0] / 255.0; color2[1] = color[1] / 255.0; color2[2] = color[2] / 255.0; return color2; }; })(); function load(viewer, options, inflatedData, sceneModel, metaModel, manifestCtx) { const modelPartId = manifestCtx.getNextId(); sceneModel.positionsCompression = "disabled"; // Positions in XKT V4 are floats, which we never quantize, for precision with big models sceneModel.normalsCompression = "precompressed"; // Normals are oct-encoded though const positions = inflatedData.positions; const normals = inflatedData.normals; const indices = inflatedData.indices; const edgeIndices = inflatedData.edgeIndices; const matrices = inflatedData.matrices; const eachPrimitivePositionsAndNormalsPortion = inflatedData.eachPrimitivePositionsAndNormalsPortion; const eachPrimitiveIndicesPortion = inflatedData.eachPrimitiveIndicesPortion; const eachPrimitiveEdgeIndicesPortion = inflatedData.eachPrimitiveEdgeIndicesPortion; const eachPrimitiveColor = inflatedData.eachPrimitiveColor; const primitiveInstances = inflatedData.primitiveInstances; const eachEntityId = JSON.parse(inflatedData.eachEntityId); const eachEntityPrimitiveInstancesPortion = inflatedData.eachEntityPrimitiveInstancesPortion; const eachEntityMatricesPortion = inflatedData.eachEntityMatricesPortion; const numPrimitives = eachPrimitivePositionsAndNormalsPortion.length; const numPrimitiveInstances = primitiveInstances.length; const primitiveInstanceCounts = new Uint8Array(numPrimitives); // For each mesh, how many times it is instanced const numEntities = eachEntityId.length; // Count instances of each primitive for (let primitiveInstanceIndex = 0; primitiveInstanceIndex < numPrimitiveInstances; primitiveInstanceIndex++) { const primitiveIndex = primitiveInstances[primitiveInstanceIndex]; primitiveInstanceCounts[primitiveIndex]++; } // Map batched primitives to the entities that will use them const batchedPrimitiveEntityIndexes = {}; for (let entityIndex = 0; entityIndex < numEntities; entityIndex++) { const lastEntityIndex = (numEntities - 1); const atLastEntity = (entityIndex === lastEntityIndex); const firstEntityPrimitiveInstanceIndex = eachEntityPrimitiveInstancesPortion [entityIndex]; const lastEntityPrimitiveInstanceIndex = atLastEntity ? eachEntityPrimitiveInstancesPortion[lastEntityIndex] : eachEntityPrimitiveInstancesPortion[entityIndex + 1]; for (let primitiveInstancesIndex = firstEntityPrimitiveInstanceIndex; primitiveInstancesIndex < lastEntityPrimitiveInstanceIndex; primitiveInstancesIndex++) { const primitiveIndex = primitiveInstances[primitiveInstancesIndex]; const primitiveInstanceCount = primitiveInstanceCounts[primitiveIndex]; const isInstancedPrimitive = (primitiveInstanceCount > 1); if (!isInstancedPrimitive) { batchedPrimitiveEntityIndexes[primitiveIndex] = entityIndex; } } } var countGeometries = 0; // Create geometries for instanced primitives and meshes for batched primitives. for (let primitiveIndex = 0; primitiveIndex < numPrimitives; primitiveIndex++) { const atLastPrimitive = (primitiveIndex === (numPrimitives - 1)); const primitiveInstanceCount = primitiveInstanceCounts[primitiveIndex]; const isInstancedPrimitive = (primitiveInstanceCount > 1); const color = decompressColor(eachPrimitiveColor.subarray((primitiveIndex * 4), (primitiveIndex * 4) + 3)); const opacity = eachPrimitiveColor[(primitiveIndex * 4) + 3] / 255.0; const primitivePositions = positions.subarray(eachPrimitivePositionsAndNormalsPortion [primitiveIndex], atLastPrimitive ? positions.length : eachPrimitivePositionsAndNormalsPortion [primitiveIndex + 1]); const primitiveNormals = normals.subarray(eachPrimitivePositionsAndNormalsPortion [primitiveIndex], atLastPrimitive ? normals.length : eachPrimitivePositionsAndNormalsPortion [primitiveIndex + 1]); const primitiveIndices = indices.subarray(eachPrimitiveIndicesPortion [primitiveIndex], atLastPrimitive ? indices.length : eachPrimitiveIndicesPortion [primitiveIndex + 1]); const primitiveEdgeIndices = edgeIndices.subarray(eachPrimitiveEdgeIndicesPortion [primitiveIndex], atLastPrimitive ? edgeIndices.length : eachPrimitiveEdgeIndicesPortion [primitiveIndex + 1]); if (isInstancedPrimitive) { // Primitive instanced by more than one entity, and has positions in Model-space const geometryId = `${modelPartId}-geometry.${primitiveIndex}`; // These IDs are local to the SceneModel sceneModel.createGeometry({ id: geometryId, primitive: "triangles", positionsCompressed: primitivePositions, normalsCompressed: primitiveNormals, indices: primitiveIndices, edgeIndices: primitiveEdgeIndices }); countGeometries++; } else { // Primitive is used only by one entity, and has positions pre-transformed into World-space const meshId = primitiveIndex; // These IDs are local to the SceneModel const entityIndex = batchedPrimitiveEntityIndexes[primitiveIndex]; const entityId = eachEntityId[entityIndex]; const meshDefaults = {}; // TODO: get from lookup from entity IDs sceneModel.createMesh(utils.apply(meshDefaults, { id: meshId, primitive: "triangles", positionsCompressed: primitivePositions, normalsCompressed: primitiveNormals, indices: primitiveIndices, edgeIndices: primitiveEdgeIndices, color: color, opacity: opacity })); } } let countInstances = 0; for (let entityIndex = 0; entityIndex < numEntities; entityIndex++) { const lastEntityIndex = (numEntities - 1); const atLastEntity = (entityIndex === lastEntityIndex); const entityId = eachEntityId[entityIndex]; const firstEntityPrimitiveInstanceIndex = eachEntityPrimitiveInstancesPortion [entityIndex]; const lastEntityPrimitiveInstanceIndex = atLastEntity ? eachEntityPrimitiveInstancesPortion[lastEntityIndex] : eachEntityPrimitiveInstancesPortion[entityIndex + 1]; const meshIds = []; for (let primitiveInstancesIndex = firstEntityPrimitiveInstanceIndex; primitiveInstancesIndex < lastEntityPrimitiveInstanceIndex; primitiveInstancesIndex++) { const primitiveIndex = primitiveInstances[primitiveInstancesIndex]; const primitiveInstanceCount = primitiveInstanceCounts[primitiveIndex]; const isInstancedPrimitive = (primitiveInstanceCount > 1); if (isInstancedPrimitive) { const meshDefaults = {}; // TODO: get from lookup from entity IDs const meshId = "instance." + countInstances++; const geometryId = "geometry" + primitiveIndex; const matricesIndex = (eachEntityMatricesPortion [entityIndex]) * 16; const matrix = matrices.subarray(matricesIndex, matricesIndex + 16); sceneModel.createMesh(utils.apply(meshDefaults, { id: meshId, geometryId: geometryId, matrix: matrix })); meshIds.push(meshId); } else { meshIds.push(primitiveIndex); } } if (meshIds.length > 0) { const entityDefaults = {}; // TODO: get from lookup from entity IDs sceneModel.createEntity(utils.apply(entityDefaults, { id: entityId, isObject: true, ///////////////// TODO: If metaobject exists meshIds: meshIds })); } } } /** @private */ const ParserV5 = { version: 5, parse: function (viewer, options, elements, sceneModel, metaModel, manifestCtx) { const deflatedData = extract(elements); const inflatedData = inflate(deflatedData); load(viewer, options, inflatedData, sceneModel, metaModel, manifestCtx); } }; export {ParserV5};