Reference Source

src/plugins/XKTLoaderPlugin/parsers/ParserV3.js

/*

Parser for .XKT Format V3

.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";
import {math} from "../../../viewer/scene/math/math.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],
        meshPositions: elements[4],
        meshIndices: elements[5],
        meshEdgesIndices: elements[6],
        meshColors: elements[7],
        entityIDs: elements[8],
        entityMeshes: elements[9],
        entityIsObjects: elements[10],
        instancedPositionsDecodeMatrix: elements[11],
        batchedPositionsDecodeMatrix: elements[12],
        entityMeshIds: elements[13],
        entityMatrices: elements[14],
        entityUsesInstancing: elements[15]
    };
}

function inflate(deflatedData) {
    return {
        positions: new Uint16Array(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),
        meshPositions: new Uint32Array(pako.inflate(deflatedData.meshPositions).buffer),
        meshIndices: new Uint32Array(pako.inflate(deflatedData.meshIndices).buffer),
        meshEdgesIndices: new Uint32Array(pako.inflate(deflatedData.meshEdgesIndices).buffer),
        meshColors: new Uint8Array(pako.inflate(deflatedData.meshColors).buffer),
        entityIDs: pako.inflate(deflatedData.entityIDs, {to: 'string'}),
        entityMeshes: new Uint32Array(pako.inflate(deflatedData.entityMeshes).buffer),
        entityIsObjects: new Uint8Array(pako.inflate(deflatedData.entityIsObjects).buffer),
        instancedPositionsDecodeMatrix: new Float32Array(pako.inflate(deflatedData.instancedPositionsDecodeMatrix).buffer),
        batchedPositionsDecodeMatrix: new Float32Array(pako.inflate(deflatedData.batchedPositionsDecodeMatrix).buffer),
        entityMeshIds: new Uint32Array(pako.inflate(deflatedData.entityMeshIds).buffer),
        entityMatrices: new Float32Array(pako.inflate(deflatedData.entityMatrices).buffer),
        entityUsesInstancing: new Uint8Array(pako.inflate(deflatedData.entityUsesInstancing).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 = "precompressed";
    sceneModel.normalsCompression = "precompressed";

    const positions = inflatedData.positions;
    const normals = inflatedData.normals;
    const indices = inflatedData.indices;
    const edgeIndices = inflatedData.edgeIndices;
    const meshPositions = inflatedData.meshPositions;
    const meshIndices = inflatedData.meshIndices;
    const meshEdgesIndices = inflatedData.meshEdgesIndices;
    const meshColors = inflatedData.meshColors;
    const entityIDs = JSON.parse(inflatedData.entityIDs);
    const entityMeshes = inflatedData.entityMeshes;
    const entityIsObjects = inflatedData.entityIsObjects;
    const entityMeshIds = inflatedData.entityMeshIds;
    const entityMatrices = inflatedData.entityMatrices;
    const entityUsesInstancing = inflatedData.entityUsesInstancing;

    const numMeshes = meshPositions.length;
    const numEntities = entityMeshes.length;

    const _alreadyCreatedGeometries = {};

    for (let i = 0; i < numEntities; i++) {

        const xktEntityId = entityIDs [i];
        const entityId = options.globalizeObjectIds ? math.globalizeObjectId(sceneModel.id, xktEntityId) : xktEntityId;
        const metaObject = viewer.metaScene.metaObjects[entityId];
        const entityDefaults = {};
        const meshDefaults = {};
        const entityMatrix = entityMatrices.subarray((i * 16), (i * 16) + 16);

        if (metaObject) {

            if (options.excludeTypesMap && metaObject.type && options.excludeTypesMap[metaObject.type]) {
                continue;
            }

            if (options.includeTypesMap && metaObject.type && (!options.includeTypesMap[metaObject.type])) {
                continue;
            }

            const props = options.objectDefaults ? options.objectDefaults[metaObject.type] || options.objectDefaults["DEFAULT"] : null;

            if (props) {
                if (props.visible === false) {
                    entityDefaults.visible = false;
                }
                if (props.pickable === false) {
                    entityDefaults.pickable = false;
                }
                if (props.colorize) {
                    meshDefaults.color = props.colorize;
                }
                if (props.opacity !== undefined && props.opacity !== null) {
                    meshDefaults.opacity = props.opacity;
                }
            }
        } else {
            if (options.excludeUnclassifiedObjects) {
                continue;
            }
        }

        const lastEntity = (i === numEntities - 1);

        const meshIds = [];

        for (let j = entityMeshes [i], jlen = lastEntity ? entityMeshIds.length : entityMeshes [i + 1]; j < jlen; j++) {
            var jj = entityMeshIds [j];

            const lastMesh = (jj === (numMeshes - 1));
            const meshId = `${modelPartId}.${entityId}.mesh.${jj}`;

            const color = decompressColor(meshColors.subarray((jj * 4), (jj * 4) + 3));
            const opacity = meshColors[(jj * 4) + 3] / 255.0;

            var tmpPositions = positions.subarray(meshPositions [jj], lastMesh ? positions.length : meshPositions [jj + 1]);
            var tmpNormals = normals.subarray(meshPositions [jj], lastMesh ? positions.length : meshPositions [jj + 1]);
            var tmpIndices = indices.subarray(meshIndices [jj], lastMesh ? indices.length : meshIndices [jj + 1]);
            var tmpEdgeIndices = edgeIndices.subarray(meshEdgesIndices [jj], lastMesh ? edgeIndices.length : meshEdgesIndices [jj + 1]);

            if (entityUsesInstancing [i] === 1) {

                const geometryId = `${modelPartId}.geometry.${meshId}.${jj}`;

                if (!(geometryId in _alreadyCreatedGeometries)) {

                    sceneModel.createGeometry({
                        id: geometryId,
                        positionsCompressed: tmpPositions,
                        normalsCompressed: tmpNormals,
                        indices: tmpIndices,
                        edgeIndices: tmpEdgeIndices,
                        primitive: "triangles",
                        positionsDecodeMatrix: inflatedData.instancedPositionsDecodeMatrix
                    });

                    _alreadyCreatedGeometries [geometryId] = true;
                }

                sceneModel.createMesh(utils.apply(meshDefaults, {
                    id: meshId,
                    color: color,
                    opacity: opacity,
                    matrix: entityMatrix,
                    geometryId,
                }));

                meshIds.push(meshId);

            } else {

                sceneModel.createMesh(utils.apply(meshDefaults, {
                    id: meshId,
                    primitive: "triangles",
                    positionsCompressed: tmpPositions,
                    normalsCompressed: tmpNormals,
                    indices: tmpIndices,
                    edgeIndices: tmpEdgeIndices,
                    positionsDecodeMatrix: inflatedData.batchedPositionsDecodeMatrix,
                    color: color,
                    opacity: opacity
                }));

                meshIds.push(meshId);
            }
        }

        if (meshIds.length) {
            sceneModel.createEntity(utils.apply(entityDefaults, {
                id: entityId,
                isObject: (entityIsObjects [i] === 1),
                meshIds: meshIds
            }));
        }
    }
}

/** @private */
const ParserV3 = {
    version: 3,
    parse: function (viewer, options, elements, sceneModel, metaModel, manifestCtx) {
        const deflatedData = extract(elements);
        const inflatedData = inflate(deflatedData);
        load(viewer, options, inflatedData, sceneModel, metaModel, manifestCtx);
    }
};

export {ParserV3};