UNPKG

potree

Version:

WebGL point cloud viewer - WORK IN PROGRESS

403 lines (313 loc) 9.51 kB
// // index is in order xyzxyzxyz // Potree.DEMNode = class DEMNode { constructor (name, box, tileSize) { this.name = name; this.box = box; this.tileSize = tileSize; this.level = this.name.length - 1; this.data = new Float32Array(tileSize * tileSize); this.data.fill(-Infinity); this.children = []; this.mipMap = [this.data]; this.mipMapNeedsUpdate = true; } createMipMap () { this.mipMap = [this.data]; let sourceSize = this.tileSize; let mipSize = parseInt(sourceSize / 2); let mipSource = this.data; while (mipSize > 1) { let mipData = new Float32Array(mipSize * mipSize); for (let i = 0; i < mipSize; i++) { for (let j = 0; j < mipSize; j++) { let h00 = mipSource[2 * i + 0 + 2 * j * sourceSize]; let h01 = mipSource[2 * i + 0 + 2 * j * sourceSize + sourceSize]; let h10 = mipSource[2 * i + 1 + 2 * j * sourceSize]; let h11 = mipSource[2 * i + 1 + 2 * j * sourceSize + sourceSize]; let [height, weight] = [0, 0]; if (isFinite(h00)) { height += h00; weight += 1; }; if (isFinite(h01)) { height += h01; weight += 1; }; if (isFinite(h10)) { height += h10; weight += 1; }; if (isFinite(h11)) { height += h11; weight += 1; }; height = height / weight; // let hs = [h00, h01, h10, h11].filter(h => isFinite(h)); // let height = hs.reduce( (a, v, i) => a + v, 0) / hs.length; mipData[i + j * mipSize] = height; } } this.mipMap.push(mipData); mipSource = mipData; sourceSize = mipSize; mipSize = parseInt(mipSize / 2); } this.mipMapNeedsUpdate = false; } uv (position) { let boxSize = this.box.getSize(); let u = (position.x - this.box.min.x) / boxSize.x; let v = (position.y - this.box.min.y) / boxSize.y; return [u, v]; } heightAtMipMapLevel (position, mipMapLevel) { let uv = this.uv(position); let tileSize = parseInt(this.tileSize / parseInt(2 ** mipMapLevel)); let data = this.mipMap[mipMapLevel]; let i = Math.min(uv[0] * tileSize, tileSize - 1); let j = Math.min(uv[1] * tileSize, tileSize - 1); let a = i % 1; let b = j % 1; let [i0, i1] = [Math.floor(i), Math.ceil(i)]; let [j0, j1] = [Math.floor(j), Math.ceil(j)]; let h00 = data[i0 + tileSize * j0]; let h01 = data[i0 + tileSize * j1]; let h10 = data[i1 + tileSize * j0]; let h11 = data[i1 + tileSize * j1]; let wh00 = isFinite(h00) ? (1 - a) * (1 - b) : 0; let wh01 = isFinite(h01) ? (1 - a) * b : 0; let wh10 = isFinite(h10) ? a * (1 - b) : 0; let wh11 = isFinite(h11) ? a * b : 0; let wsum = wh00 + wh01 + wh10 + wh11; wh00 = wh00 / wsum; wh01 = wh01 / wsum; wh10 = wh10 / wsum; wh11 = wh11 / wsum; if (wsum === 0) { return null; } let h = 0; if (isFinite(h00)) h += h00 * wh00; if (isFinite(h01)) h += h01 * wh01; if (isFinite(h10)) h += h10 * wh10; if (isFinite(h11)) h += h11 * wh11; return h; } height (position) { let h = null; for (let i = 0; i < this.mipMap.length; i++) { h = this.heightAtMipMapLevel(position, i); if (h !== null) { return h; } } return h; } traverse (handler, level = 0) { handler(this, level); for (let child of this.children.filter(c => c !== undefined)) { child.traverse(handler, level + 1); } } }; Potree.DEM = class DEM { constructor (pointcloud) { this.pointcloud = pointcloud; this.matrix = null; this.boundingBox = null; this.tileSize = 64; this.root = null; this.version = 0; } // expands the tree to all nodes that intersect <box> at <level> // returns the intersecting nodes at <level> expandAndFindByBox (box, level) { if (level === 0) { return [this.root]; } let result = []; let stack = [this.root]; while (stack.length > 0) { let node = stack.pop(); let nodeBoxSize = node.box.getSize(); // check which children intersect by transforming min/max to quadrants let min = { x: (box.min.x - node.box.min.x) / nodeBoxSize.x, y: (box.min.y - node.box.min.y) / nodeBoxSize.y}; let max = { x: (box.max.x - node.box.max.x) / nodeBoxSize.x, y: (box.max.y - node.box.max.y) / nodeBoxSize.y}; min.x = min.x < 0.5 ? 0 : 1; min.y = min.y < 0.5 ? 0 : 1; max.x = max.x < 0.5 ? 0 : 1; max.y = max.y < 0.5 ? 0 : 1; let childIndices; if (min.x === 0 && min.y === 0 && max.x === 1 && max.y === 1) { childIndices = [0, 1, 2, 3]; } else if (min.x === max.x && min.y === max.y) { childIndices = [(min.x << 1) | min.y]; } else { childIndices = [(min.x << 1) | min.y, (max.x << 1) | max.y]; } for (let index of childIndices) { if (node.children[index] === undefined) { let childBox = node.box.clone(); if ((index & 2) > 0) { childBox.min.x += nodeBoxSize.x / 2.0; } else { childBox.max.x -= nodeBoxSize.x / 2.0; } if ((index & 1) > 0) { childBox.min.y += nodeBoxSize.y / 2.0; } else { childBox.max.y -= nodeBoxSize.y / 2.0; } let child = new Potree.DEMNode(node.name + index, childBox, this.tileSize); node.children[index] = child; } let child = node.children[index]; if (child.level < level) { stack.push(child); } else { result.push(child); } } } return result; } childIndex (uv) { let [x, y] = uv.map(n => n < 0.5 ? 0 : 1); let index = (x << 1) | y; return index; } height (position) { // return this.root.height(position); if (!this.root) { return 0; } let height = null; let list = [this.root]; while (true) { let node = list[list.length - 1]; let currentHeight = node.height(position); if (currentHeight !== null) { height = currentHeight; } let uv = node.uv(position); let childIndex = this.childIndex(uv); if (node.children[childIndex]) { list.push(node.children[childIndex]); } else { break; } } return height + this.pointcloud.position.z; } update (visibleNodes) { if (Potree.getDEMWorkerInstance().working) { return; } // check if point cloud transformation changed if (this.matrix === null || !this.matrix.equals(this.pointcloud.matrixWorld)) { this.matrix = this.pointcloud.matrixWorld.clone(); this.boundingBox = this.pointcloud.boundingBox.clone().applyMatrix4(this.matrix); this.root = new Potree.DEMNode('r', this.boundingBox, this.tileSize); this.version++; } // find node to update let node = null; for (let vn of visibleNodes) { if (vn.demVersion === undefined || vn.demVersion < this.version) { node = vn; break; } } if (node === null) { return; } // update node let projectedBox = node.getBoundingBox().clone().applyMatrix4(this.matrix); let projectedBoxSize = projectedBox.getSize(); let targetNodes = this.expandAndFindByBox(projectedBox, node.getLevel()); node.demVersion = this.version; Potree.getDEMWorkerInstance().onmessage = (e) => { let data = new Float32Array(e.data.dem.data); for (let demNode of targetNodes) { let boxSize = demNode.box.getSize(); for (let i = 0; i < this.tileSize; i++) { for (let j = 0; j < this.tileSize; j++) { let u = (i / (this.tileSize - 1)); let v = (j / (this.tileSize - 1)); let x = demNode.box.min.x + u * boxSize.x; let y = demNode.box.min.y + v * boxSize.y; let ix = this.tileSize * (x - projectedBox.min.x) / projectedBoxSize.x; let iy = this.tileSize * (y - projectedBox.min.y) / projectedBoxSize.y; if (ix < 0 || ix > this.tileSize) { continue; } if (iy < 0 || iy > this.tileSize) { continue; } ix = Math.min(Math.floor(ix), this.tileSize - 1); iy = Math.min(Math.floor(iy), this.tileSize - 1); demNode.data[i + this.tileSize * j] = data[ix + this.tileSize * iy]; } } demNode.createMipMap(); demNode.mipMapNeedsUpdate = true; Potree.getDEMWorkerInstance().working = false; } // TODO only works somewhat if there is no rotation to the point cloud // let target = targetNodes[0]; // target.data = new Float32Array(data); // // /// /node.dem = e.data.dem; // // Potree.getDEMWorkerInstance().working = false; // // { // create scene objects for debugging // //for(let demNode of targetNodes){ // var bb = new Potree.Box3Helper(box); // viewer.scene.scene.add(bb); // // createDEMMesh(this, target); // //} // // } }; let position = node.geometryNode.geometry.attributes.position.array; let message = { boundingBox: { min: node.getBoundingBox().min.toArray(), max: node.getBoundingBox().max.toArray() }, position: new Float32Array(position).buffer }; let transferables = [message.position]; Potree.getDEMWorkerInstance().working = true; Potree.getDEMWorkerInstance().postMessage(message, transferables); } }; Potree.PointCloudTreeNode = class { getChildren () { throw new Error('override function'); } getBoundingBox () { throw new Error('override function'); } isLoaded () { throw new Error('override function'); } isGeometryNode () { throw new Error('override function'); } isTreeNode () { throw new Error('override function'); } getLevel () { throw new Error('override function'); } getBoundingSphere () { throw new Error('override function'); } }; Potree.PointCloudTree = class PointCloudTree extends THREE.Object3D { constructor () { super(); this.dem = new Potree.DEM(this); } initialized () { return this.root !== null; } };