UNPKG

xeokit-viewer

Version:

BIM viewer built on xeokit and Bootstrap

287 lines (264 loc) 10.1 kB
import {Mesh} from "../../viewer/scene/mesh/Mesh.js"; import {ReadableGeometry} from "../../viewer/scene/geometry/ReadableGeometry.js"; import {MetallicMaterial} from "../../viewer/scene/materials/MetallicMaterial.js"; import {math} from "../../viewer/scene/math/math.js"; import {core} from "../../viewer/scene/core.js"; /** * @private */ class STLLoader { load(plugin, modelNode, src, options, ok, error) { options = options || {}; var spinner = plugin.viewer.scene.canvas.spinner; spinner.processes++; load(plugin, modelNode, src, options, function () { spinner.processes--; core.scheduleTask(function () { modelNode.fire("loaded", true, true); }); if (ok) { ok(); } }, function (msg) { spinner.processes--; plugin.error(msg); if (error) { error(msg); } modelNode.fire("error", msg); }); } } var load = (function () { function loadData(src, ok, error) { var request = new XMLHttpRequest(); request.overrideMimeType("application/json"); request.open('GET', src, true); request.responseType = 'arraybuffer'; request.onreadystatechange = function () { if (request.readyState == 4 && request.status == "200") { ok(request.response, this); } }; request.send(null); } return function (plugin, model, src, options, ok, error) { loadData(src, function (data) { // OK parse(data, plugin, model, options); ok(); }, error); }; })(); function parse(data, plugin, modelNode, options) { var entityCount = 0; function isBinary(data) { var reader = new DataView(data); var numFaces = reader.getUint32(80, true); var faceSize = (32 / 8 * 3) + ((32 / 8 * 3) * 3) + (16 / 8); var numExpectedBytes = 80 + (32 / 8) + (numFaces * faceSize); if (numExpectedBytes === reader.byteLength) { return true; } var solid = [115, 111, 108, 105, 100]; for (var i = 0; i < 5; i++) { if (solid[i] !== reader.getUint8(i, false)) { return true; } } return false; } function parseBinary(data, plugin, modelNode, options) { var reader = new DataView(data); var faces = reader.getUint32(80, true); var r; var g; var b; var hasColors = false; var colors; var defaultR; var defaultG; var defaultB; var lastR = null; var lastG = null; var lastB = null; var newMesh = false; var alpha; for (var index = 0; index < 80 - 10; index++) { if ((reader.getUint32(index, false) == 0x434F4C4F /*COLO*/) && (reader.getUint8(index + 4) == 0x52 /*'R'*/) && (reader.getUint8(index + 5) == 0x3D /*'='*/)) { hasColors = true; colors = []; defaultR = reader.getUint8(index + 6) / 255; defaultG = reader.getUint8(index + 7) / 255; defaultB = reader.getUint8(index + 8) / 255; alpha = reader.getUint8(index + 9) / 255; } } var material = new MetallicMaterial(modelNode, { // Share material with all meshes roughness: 0.5 }); // var material = new PhongMaterial(modelNode, { // Share material with all meshes // diffuse: [0.4, 0.4, 0.4], // reflectivity: 1, // specular: [0.5, 0.5, 1.0] // }); var dataOffset = 84; var faceLength = 12 * 4 + 2; var positions = []; var normals = []; var splitMeshes = options.splitMeshes; for (var face = 0; face < faces; face++) { var start = dataOffset + face * faceLength; var normalX = reader.getFloat32(start, true); var normalY = reader.getFloat32(start + 4, true); var normalZ = reader.getFloat32(start + 8, true); if (hasColors) { var packedColor = reader.getUint16(start + 48, true); if ((packedColor & 0x8000) === 0) { r = (packedColor & 0x1F) / 31; g = ((packedColor >> 5) & 0x1F) / 31; b = ((packedColor >> 10) & 0x1F) / 31; } else { r = defaultR; g = defaultG; b = defaultB; } if (splitMeshes && r !== lastR || g !== lastG || b !== lastB) { if (lastR !== null) { newMesh = true; } lastR = r; lastG = g; lastB = b; } } for (var i = 1; i <= 3; i++) { var vertexstart = start + i * 12; positions.push(reader.getFloat32(vertexstart, true)); positions.push(reader.getFloat32(vertexstart + 4, true)); positions.push(reader.getFloat32(vertexstart + 8, true)); normals.push(normalX, normalY, normalZ); if (hasColors) { colors.push(r, g, b, 1); // TODO: handle alpha } } if (splitMeshes && newMesh) { addMesh(modelNode, positions, normals, colors, material, options); positions = []; normals = []; colors = colors ? [] : null; newMesh = false; } } if (positions.length > 0) { addMesh(modelNode, positions, normals, colors, material, options); } } function parseASCII(data, plugin, modelNode, options) { var faceRegex = /facet([\s\S]*?)endfacet/g; var faceCounter = 0; var floatRegex = /[\s]+([+-]?(?:\d+.\d+|\d+.|\d+|.\d+)(?:[eE][+-]?\d+)?)/.source; var vertexRegex = new RegExp('vertex' + floatRegex + floatRegex + floatRegex, 'g'); var normalRegex = new RegExp('normal' + floatRegex + floatRegex + floatRegex, 'g'); var positions = []; var normals = []; var colors = null; var normalx; var normaly; var normalz; var result; var verticesPerFace; var normalsPerFace; var text; while ((result = faceRegex.exec(data)) !== null) { verticesPerFace = 0; normalsPerFace = 0; text = result[0]; while ((result = normalRegex.exec(text)) !== null) { normalx = parseFloat(result[1]); normaly = parseFloat(result[2]); normalz = parseFloat(result[3]); normalsPerFace++; } while ((result = vertexRegex.exec(text)) !== null) { positions.push(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3])); normals.push(normalx, normaly, normalz); verticesPerFace++; } if (normalsPerFace !== 1) { plugin.error("Error in normal of face " + faceCounter); } if (verticesPerFace !== 3) { plugin.error("Error in positions of face " + faceCounter); } faceCounter++; } var material = new MetallicMaterial(modelNode, { roughness: 0.5 }); // var material = new PhongMaterial(modelNode, { // diffuse: [0.4, 0.4, 0.4], // reflectivity: 1, // specular: [0.5, 0.5, 1.0] // }); addMesh(modelNode, positions, normals, colors, material, options); } function addMesh(modelNode, positions, normals, colors, material, options) { var indices = new Int32Array(positions.length / 3); for (var ni = 0, len = indices.length; ni < len; ni++) { indices[ni] = ni; } normals = normals && normals.length > 0 ? normals : null; colors = colors && colors.length > 0 ? colors : null; if (options.smoothNormals) { math.faceToVertexNormals(positions, normals, options); } var geometry = new ReadableGeometry(modelNode, { primitive: "triangles", positions: positions, normals: normals, colors: colors, indices: indices }); var mesh = new Mesh(modelNode, { id: modelNode.id + "#" + entityCount++, geometry: geometry, material: material, edges: options.edges }); modelNode.addChild(mesh); } function ensureString(buffer) { if (typeof buffer !== 'string') { return decodeText(new Uint8Array(buffer)); } return buffer; } function ensureBinary(buffer) { if (typeof buffer === 'string') { var arrayBuffer = new Uint8Array(buffer.length); for (var i = 0; i < buffer.length; i++) { arrayBuffer[i] = buffer.charCodeAt(i) & 0xff; // implicitly assumes little-endian } return arrayBuffer.buffer || arrayBuffer; } else { return buffer; } } function decodeText(array) { if (typeof TextDecoder !== 'undefined') { return new TextDecoder().decode(array); } var s = ''; for (var i = 0, il = array.length; i < il; i++) { s += String.fromCharCode(array[i]); // Implicitly assumes little-endian. } return decodeURIComponent(escape(s)); } var binData = ensureBinary(data); return isBinary(binData) ? parseBinary(binData, plugin, modelNode, options) : parseASCII(ensureString(data), plugin, modelNode, options); } export {STLLoader}