import * as THREE from "../../libs/three.js/build/three.module.js"; import {PointCloudOctreeGeometry, PointCloudOctreeGeometryNode} from "../PointCloudOctreeGeometry.js"; import {Version} from "../Version.js"; import {XHRFactory} from "../XHRFactory.js"; import {LasLazLoader} from "./LasLazLoader.js"; import {BinaryLoader} from "./BinaryLoader.js"; import {Utils} from "../utils.js"; import {PointAttribute, PointAttributes, PointAttributeTypes} from "./PointAttributes.js"; var transformFrom4dkk = function(name){ var jsAttribute = PointAttribute[name] return jsAttribute /* if(name == "POSITION_CARTESIAN"){ jsAttribute = { description: "", elementSize: 4, elements: 3, name: "POSITION_CARTESIAN", size: 12, type: "int32", } }else if(name == "COLOR_PACKED"){ jsAttribute = { description: "" , elementSize: 1, elements: 4, name: "RGBA", size: 4, type: 'int8', } }else if(name == "NORMAL_OCT16"){ jsAttribute = { description: "" , elementSize: 4, elements: 2, name: "NORMAL_OCT16", size: 12, type: "uint8", } } */ /* var Q = q(L.COLOR_PACKED, K.DATA_TYPE_INT8, 4) POSITION_CARTESIAN: q(L.POSITION_CARTESIAN, K.DATA_TYPE_FLOAT, 3), RGBA_PACKED: Q, COLOR_PACKED: Q, RGB_PACKED: q(L.COLOR_PACKED, K.DATA_TYPE_INT8, 3), NORMAL_FLOATS: q(L.NORMAL_FLOATS, K.DATA_TYPE_FLOAT, 3), FILLER_1B: q(L.FILLER, K.DATA_TYPE_UINT8, 1), INTENSITY: q(L.INTENSITY, K.DATA_TYPE_UINT16, 1), CLASSIFICATION: q(L.CLASSIFICATION, K.DATA_TYPE_UINT8, 1), NORMAL_SPHEREMAPPED: q(L.NORMAL_SPHEREMAPPED, K.DATA_TYPE_UINT8, 2), NORMAL_OCT16: q(L.NORMAL_OCT16, K.DATA_TYPE_UINT8, 2), NORMAL: q(L.NORMAL, K.DATA_TYPE_FLOAT, 3) */ } function parseAttributes(cloudjs){ let version = new Version(cloudjs.version); const replacements = { "COLOR_PACKED": "rgba", "RGBA": "rgba", "INTENSITY": "intensity", "CLASSIFICATION": "classification", "GPS_TIME": "gps-time", }; const replaceOldNames = (old) => { if(replacements[old]){ return replacements[old]; }else{ return old; } }; const pointAttributes = []; if(version.upTo('1.7')){ for(let attributeName of cloudjs.pointAttributes){ const oldAttribute = PointAttribute[attributeName]; const attribute = { name: oldAttribute.name, size: oldAttribute.byteSize, elements: oldAttribute.numElements, elementSize: oldAttribute.byteSize / oldAttribute.numElements, type: oldAttribute.type.name, description: "", }; pointAttributes.push(attribute); } }else{ pointAttributes.push(...cloudjs.pointAttributes); } { const attributes = new PointAttributes(); const typeConversion = { int8: PointAttributeTypes.DATA_TYPE_INT8, int16: PointAttributeTypes.DATA_TYPE_INT16, int32: PointAttributeTypes.DATA_TYPE_INT32, int64: PointAttributeTypes.DATA_TYPE_INT64, uint8: PointAttributeTypes.DATA_TYPE_UINT8, uint16: PointAttributeTypes.DATA_TYPE_UINT16, uint32: PointAttributeTypes.DATA_TYPE_UINT32, uint64: PointAttributeTypes.DATA_TYPE_UINT64, double: PointAttributeTypes.DATA_TYPE_DOUBLE, float: PointAttributeTypes.DATA_TYPE_FLOAT, }; for(let jsAttribute of pointAttributes){ if(jsAttribute.name == void 0){//是来自四维看看的数据 //attribute = transformFrom4dkk(jsAttribute) var attribute_ = PointAttribute[jsAttribute] attributes.add(attribute_); continue; } const name = replaceOldNames(jsAttribute.name); const type = typeConversion[jsAttribute.type]; const numElements = jsAttribute.elements; const description = jsAttribute.description; const attribute = new PointAttribute(name, type, numElements); attributes.add(attribute); } { // check if it has normals let hasNormals = pointAttributes.find(a => a.name === "NormalX") !== undefined && pointAttributes.find(a => a.name === "NormalY") !== undefined && pointAttributes.find(a => a.name === "NormalZ") !== undefined; if(hasNormals){ let vector = { name: "NORMAL", attributes: ["NormalX", "NormalY", "NormalZ"], }; attributes.addVector(vector); } } return attributes; } } function lasLazAttributes(fMno){ const attributes = new PointAttributes(); attributes.add(PointAttribute.POSITION_CARTESIAN); attributes.add(new PointAttribute("rgba", PointAttributeTypes.DATA_TYPE_UINT8, 4)); attributes.add(new PointAttribute("intensity", PointAttributeTypes.DATA_TYPE_UINT16, 1)); attributes.add(new PointAttribute("classification", PointAttributeTypes.DATA_TYPE_UINT8, 1)); attributes.add(new PointAttribute("gps-time", PointAttributeTypes.DATA_TYPE_DOUBLE, 1)); attributes.add(new PointAttribute("number of returns", PointAttributeTypes.DATA_TYPE_UINT8, 1)); attributes.add(new PointAttribute("return number", PointAttributeTypes.DATA_TYPE_UINT8, 1)); attributes.add(new PointAttribute("source id", PointAttributeTypes.DATA_TYPE_UINT16, 1)); //attributes.add(new PointAttribute("pointSourceID", PointAttributeTypes.DATA_TYPE_INT8, 4)); return attributes; } export class POCLoader { static load(url, timeStamp, callback){ //add timeStamp try { let pco = new PointCloudOctreeGeometry(); pco.timeStamp = timeStamp pco.url = url; let xhr = XHRFactory.createXMLHttpRequest(); xhr.open('GET', url+'?m='+timeStamp, true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 0)) { let fMno = JSON.parse(xhr.responseText); let version = new Version(fMno.version); // assume octreeDir is absolute if it starts with http if (fMno.octreeDir.indexOf('http') === 0) { pco.octreeDir = fMno.octreeDir; } else { pco.octreeDir = url + '/../' + fMno.octreeDir; } pco.spacing = fMno.spacing; pco.hierarchyStepSize = fMno.hierarchyStepSize; pco.pointAttributes = fMno.pointAttributes; let min = new THREE.Vector3(fMno.boundingBox.lx, fMno.boundingBox.ly, fMno.boundingBox.lz); let max = new THREE.Vector3(fMno.boundingBox.ux, fMno.boundingBox.uy, fMno.boundingBox.uz); let boundingBox = new THREE.Box3(min, max); let tightBoundingBox = boundingBox.clone(); if (fMno.tightBoundingBox) {//这个才是真实的bounding,前面那个bounding的size是个正方体,似乎取了最长边作为边长 tightBoundingBox.min.copy(new THREE.Vector3(fMno.tightBoundingBox.lx, fMno.tightBoundingBox.ly, fMno.tightBoundingBox.lz)); tightBoundingBox.max.copy(new THREE.Vector3(fMno.tightBoundingBox.ux, fMno.tightBoundingBox.uy, fMno.tightBoundingBox.uz)); } let offset = min.clone(); //将成为点云的position,被我用作旋转中心(但在点云中不那么居中,navvis也是这样, 这样可能是为了让模型在这数据的bounding上) boundingBox.min.sub(offset); //点云的真实坐标的min都是0,0,0吗(我看案例是,因绕角落旋转,也就是原点) boundingBox.max.sub(offset); tightBoundingBox.min.sub(offset); tightBoundingBox.max.sub(offset); //改 //pco.projection = fMno.projection || "+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 +x_0=2600000 +y_0=1200000 +ellps=bessel +towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs ", //"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs" //给地图 pco.boundingBox = boundingBox; pco.tightBoundingBox = tightBoundingBox; pco.boundingSphere = boundingBox.getBoundingSphere(new THREE.Sphere()); pco.tightBoundingSphere = tightBoundingBox.getBoundingSphere(new THREE.Sphere()); pco.offset = offset; if (fMno.pointAttributes === 'LAS') { pco.loader = new LasLazLoader(fMno.version, "las"); pco.pointAttributes = lasLazAttributes(fMno); } else if (fMno.pointAttributes === 'LAZ') { pco.loader = new LasLazLoader(fMno.version, "laz"); pco.pointAttributes = lasLazAttributes(fMno); } else { pco.loader = new BinaryLoader(fMno.version, boundingBox, fMno.scale); pco.pointAttributes = parseAttributes(fMno); } let nodes = {}; { // load root let name = 'r'; let root = new PointCloudOctreeGeometryNode(name, pco, boundingBox); root.level = 0; root.hasChildren = true; root.spacing = pco.spacing; if (version.upTo('1.5')) { root.numPoints = fMno.hierarchy[0][1]; } else { root.numPoints = 0; } pco.root = root; pco.root.load(); nodes[name] = root; } // load remaining hierarchy if (version.upTo('1.4')) { for (let i = 1; i < fMno.hierarchy.length; i++) { let name = fMno.hierarchy[i][0]; let numPoints = fMno.hierarchy[i][1]; let index = parseInt(name.charAt(name.length - 1)); let parentName = name.substring(0, name.length - 1); let parentNode = nodes[parentName]; let level = name.length - 1; //let boundingBox = POCLoader.createChildAABB(parentNode.boundingBox, index); let boundingBox = Utils.createChildAABB(parentNode.boundingBox, index); let node = new PointCloudOctreeGeometryNode(name, pco, boundingBox); node.level = level; node.numPoints = numPoints; node.spacing = pco.spacing / Math.pow(2, level); parentNode.addChild(node); nodes[name] = node; } } pco.nodes = nodes; callback(pco); } }; xhr.send(null); } catch (e) { console.log("loading failed: '" + url + "'"); console.log(e); callback(); } } loadPointAttributes(mno){ let fpa = mno.pointAttributes; let pa = new PointAttributes(); for (let i = 0; i < fpa.length; i++) { let pointAttribute = PointAttribute[fpa[i]]; pa.add(pointAttribute); } return pa; } createChildAABB(aabb, index){ let min = aabb.min.clone(); let max = aabb.max.clone(); let size = new THREE.Vector3().subVectors(max, min); if ((index & 0b0001) > 0) { min.z += size.z / 2; } else { max.z -= size.z / 2; } if ((index & 0b0010) > 0) { min.y += size.y / 2; } else { max.y -= size.y / 2; } if ((index & 0b0100) > 0) { min.x += size.x / 2; } else { max.x -= size.x / 2; } return new THREE.Box3(min, max); } }