123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- import * as THREE from "../../../../libs/three.js/build/three.module.js";
- import {PointAttribute, PointAttributes, PointAttributeTypes} from "../../../loader/PointAttributes.js";
- import {OctreeGeometry, OctreeGeometryNode} from "./OctreeGeometry.js";
- // let loadedNodes = new Set();
- export class NodeLoader{
- constructor(url){
- this.url = url;
- }
- async load(node){
- if(node.loaded || node.loading){
- return;
- }
- node.loading = true;
- Potree.numNodesLoading++;
- // console.log(node.name, node.numPoints);
- // if(loadedNodes.has(node.name)){
- // // debugger;
- // }
- // loadedNodes.add(node.name);
- try{
- if(node.nodeType === 2){
- await this.loadHierarchy(node);
- }
- let {byteOffset, byteSize} = node;
- let urlOctree = `${this.url}/../octree.bin`;
- let first = byteOffset;
- let last = byteOffset + byteSize - 1n; //1n这种写法部分浏览器不支持
- let buffer;
- if(byteSize === 0n){
- buffer = new ArrayBuffer(0);
- console.warn(`loaded node with 0 bytes: ${node.name}`);
- }else{
- let response = await fetch(urlOctree, {
- headers: {
- 'content-type': 'multipart/byteranges',
- 'Range': `bytes=${first}-${last}`,
- },
- });
- buffer = await response.arrayBuffer();
- }
- let workerPath;
- if(this.metadata.encoding === "BROTLI"){
- workerPath = Potree.scriptPath + '/workers/2.0/DecoderWorker_brotli.js';
- }else{
- workerPath = Potree.scriptPath + '/workers/2.0/DecoderWorker.js';
- }
- let worker = Potree.workerPool.getWorker(workerPath);
- worker.onmessage = function (e) {
- let data = e.data;
- let buffers = data.attributeBuffers;
- Potree.workerPool.returnWorker(workerPath, worker);
- let geometry = new THREE.BufferGeometry();
-
- for(let property in buffers){
- let buffer = buffers[property].buffer;
- if(property === "position"){
- geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(buffer), 3));
- }else if(property === "rgba"){
- geometry.setAttribute('rgba', new THREE.BufferAttribute(new Uint8Array(buffer), 4, true));
- }else if(property === "NORMAL"){
- //geometry.setAttribute('rgba', new THREE.BufferAttribute(new Uint8Array(buffer), 4, true));
- geometry.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(buffer), 3));
- }else if (property === "INDICES") {
- let bufferAttribute = new THREE.BufferAttribute(new Uint8Array(buffer), 4);
- bufferAttribute.normalized = true;
- geometry.setAttribute('indices', bufferAttribute);
- }else{
- const bufferAttribute = new THREE.BufferAttribute(new Float32Array(buffer), 1);
- let batchAttribute = buffers[property].attribute;
- bufferAttribute.potree = {
- offset: buffers[property].offset,
- scale: buffers[property].scale,
- preciseBuffer: buffers[property].preciseBuffer,
- range: batchAttribute.range,
- };
- geometry.setAttribute(property, bufferAttribute);
- }
- }
- // indices ??
- node.density = data.density;
- node.geometry = geometry;
- node.loaded = true;
- node.loading = false;
- Potree.numNodesLoading--;
- };
- let pointAttributes = node.octreeGeometry.pointAttributes;
- let scale = node.octreeGeometry.scale;
- let box = node.boundingBox;
- let min = node.octreeGeometry.offset.clone().add(box.min);
- let size = box.max.clone().sub(box.min);
- let max = min.clone().add(size);
- let numPoints = node.numPoints;
- let offset = node.octreeGeometry.loader.offset;
- let message = {
- name: node.name,
- buffer: buffer,
- pointAttributes: pointAttributes,
- scale: scale,
- min: min,
- max: max,
- size: size,
- offset: offset,
- numPoints: numPoints
- };
- worker.postMessage(message, [message.buffer]);
- }catch(e){
- node.loaded = false;
- node.loading = false;
- Potree.numNodesLoading--;
- console.log(`failed to load ${node.name}`);
- console.log(e);
- console.log(`trying again!`);
- }
- }
- parseHierarchy(node, buffer){
- let view = new DataView(buffer);
- let tStart = performance.now();
- let bytesPerNode = 22;
- let numNodes = buffer.byteLength / bytesPerNode;
- let octree = node.octreeGeometry;
- // let nodes = [node];
- let nodes = new Array(numNodes);
- nodes[0] = node;
- let nodePos = 1;
- for(let i = 0; i < numNodes; i++){
- let current = nodes[i];
- let type = view.getUint8(i * bytesPerNode + 0);
- let childMask = view.getUint8(i * bytesPerNode + 1);
- let numPoints = view.getUint32(i * bytesPerNode + 2, true);
- let byteOffset = view.getBigInt64(i * bytesPerNode + 6, true);
- let byteSize = view.getBigInt64(i * bytesPerNode + 14, true);
- // if(byteSize === 0n){
- // // debugger;
- // }
- if(current.nodeType === 2){
- // replace proxy with real node
- current.byteOffset = byteOffset;
- current.byteSize = byteSize;
- current.numPoints = numPoints;
- }else if(type === 2){
- // load proxy
- current.hierarchyByteOffset = byteOffset;
- current.hierarchyByteSize = byteSize;
- current.numPoints = numPoints;
- }else{
- // load real node
- current.byteOffset = byteOffset;
- current.byteSize = byteSize;
- current.numPoints = numPoints;
- }
-
- current.nodeType = type;
- if(current.nodeType === 2){
- continue;
- }
- for(let childIndex = 0; childIndex < 8; childIndex++){
- let childExists = ((1 << childIndex) & childMask) !== 0;
- if(!childExists){
- continue;
- }
- let childName = current.name + childIndex;
- let childAABB = createChildAABB(current.boundingBox, childIndex);
- let child = new OctreeGeometryNode(childName, octree, childAABB);
- child.name = childName;
- child.spacing = current.spacing / 2;
- child.level = current.level + 1;
- current.children[childIndex] = child;
- child.parent = current;
- // nodes.push(child);
- nodes[nodePos] = child;
- nodePos++;
- }
- // if((i % 500) === 0){
- // yield;
- // }
- }
- let duration = (performance.now() - tStart);
- // if(duration > 20){
- // let msg = `duration: ${duration}ms, numNodes: ${numNodes}`;
- // console.log(msg);
- // }
- }
- async loadHierarchy(node){
- let {hierarchyByteOffset, hierarchyByteSize} = node;
- let hierarchyPath = `${this.url}/../hierarchy.bin`;
-
- let first = hierarchyByteOffset;
- let last = first + hierarchyByteSize - 1n;
- let response = await fetch(hierarchyPath, {
- headers: {
- 'content-type': 'multipart/byteranges',
- 'Range': `bytes=${first}-${last}`,
- },
- });
- let buffer = await response.arrayBuffer();
- this.parseHierarchy(node, buffer);
- // let promise = new Promise((resolve) => {
- // let generator = this.parseHierarchy(node, buffer);
- // let repeatUntilDone = () => {
- // let result = generator.next();
- // if(result.done){
- // resolve();
- // }else{
- // requestAnimationFrame(repeatUntilDone);
- // }
- // };
-
- // repeatUntilDone();
- // });
- // await promise;
-
- }
- }
- let tmpVec3 = new THREE.Vector3();
- function createChildAABB(aabb, index){
- let min = aabb.min.clone();
- let max = aabb.max.clone();
- let size = tmpVec3.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);
- }
- let typenameTypeattributeMap = {
- "double": PointAttributeTypes.DATA_TYPE_DOUBLE,
- "float": PointAttributeTypes.DATA_TYPE_FLOAT,
- "int8": PointAttributeTypes.DATA_TYPE_INT8,
- "uint8": PointAttributeTypes.DATA_TYPE_UINT8,
- "int16": PointAttributeTypes.DATA_TYPE_INT16,
- "uint16": PointAttributeTypes.DATA_TYPE_UINT16,
- "int32": PointAttributeTypes.DATA_TYPE_INT32,
- "uint32": PointAttributeTypes.DATA_TYPE_UINT32,
- "int64": PointAttributeTypes.DATA_TYPE_INT64,
- "uint64": PointAttributeTypes.DATA_TYPE_UINT64,
- }
- export class OctreeLoader{
- static parseAttributes(jsonAttributes){
- let attributes = new PointAttributes();
- let replacements = {
- "rgb": "rgba",
- };
- for (const jsonAttribute of jsonAttributes) {
- let {name, description, size, numElements, elementSize, min, max} = jsonAttribute;
- let type = typenameTypeattributeMap[jsonAttribute.type];
- let potreeAttributeName = replacements[name] ? replacements[name] : name;
- let attribute = new PointAttribute(potreeAttributeName, type, numElements);
- if(numElements === 1){
- attribute.range = [min[0], max[0]];
- }else{
- attribute.range = [min, max];
- }
- if (name === "gps-time") { // HACK: Guard against bad gpsTime range in metadata, see potree/potree#909
- if (attribute.range[0] === attribute.range[1]) {
- attribute.range[1] += 1;
- }
- }
- attribute.initialRange = attribute.range;
- attributes.add(attribute);
- }
- {
- // check if it has normals
- let hasNormals =
- attributes.attributes.find(a => a.name === "NormalX") !== undefined &&
- attributes.attributes.find(a => a.name === "NormalY") !== undefined &&
- attributes.attributes.find(a => a.name === "NormalZ") !== undefined;
- if(hasNormals){
- let vector = {
- name: "NORMAL",
- attributes: ["NormalX", "NormalY", "NormalZ"],
- };
- attributes.addVector(vector);
- }
- }
- return attributes;
- }
- static async load(url){
- let response = await fetch(url);
- let metadata = await response.json();
- let attributes = OctreeLoader.parseAttributes(metadata.attributes);
- let loader = new NodeLoader(url);
- loader.metadata = metadata;
- loader.attributes = attributes;
- loader.scale = metadata.scale;
- loader.offset = metadata.offset;
- let octree = new OctreeGeometry();
- octree.url = url;
- octree.spacing = metadata.spacing;
- octree.scale = metadata.scale;
- // let aPosition = metadata.attributes.find(a => a.name === "position");
- // octree
- let min = new THREE.Vector3(...metadata.boundingBox.min);
- let max = new THREE.Vector3(...metadata.boundingBox.max);
- let boundingBox = new THREE.Box3(min, max);
- let offset = min.clone();
- boundingBox.min.sub(offset);
- boundingBox.max.sub(offset);
- octree.projection = metadata.projection;
- octree.boundingBox = boundingBox;
- octree.tightBoundingBox = boundingBox.clone();
- octree.boundingSphere = boundingBox.getBoundingSphere(new THREE.Sphere());
- octree.tightBoundingSphere = boundingBox.getBoundingSphere(new THREE.Sphere());
- octree.offset = offset;
- octree.pointAttributes = OctreeLoader.parseAttributes(metadata.attributes);
- octree.loader = loader;
- let root = new OctreeGeometryNode("r", octree, boundingBox);
- root.level = 0;
- root.nodeType = 2;
- root.hierarchyByteOffset = 0n;
- root.hierarchyByteSize = BigInt(metadata.hierarchy.firstChunkSize);
- root.hasChildren = false;
- root.spacing = octree.spacing;
- root.byteOffset = 0;
- octree.root = root;
- loader.load(root);
- let result = {
- geometry: octree,
- };
- return result;
- }
- };
|