UNPKG

@pnext/three-loader

Version:

Potree loader for ThreeJS, converted and adapted to Typescript.

183 lines (157 loc) 5.43 kB
// ------------------------------------------------------------------------------------------------- // Converted to Typescript and adapted from https://github.com/potree/potree // ------------------------------------------------------------------------------------------------- import { Box3, Vector3 } from 'three'; import { PointAttributes, PointAttributeStringName } from '../point-attributes'; import { PointCloudOctreeGeometry } from '../point-cloud-octree-geometry'; import { PointCloudOctreeGeometryNode } from '../point-cloud-octree-geometry-node'; import { createChildAABB } from '../utils/bounds'; import { getIndexFromName, handleFailedRequest } from '../utils/utils'; import { Version } from '../version'; import { BinaryLoader } from './binary-loader'; import { GetUrlFn, XhrRequest } from './types'; interface BoundingBoxData { lx: number; ly: number; lz: number; ux: number; uy: number; uz: number; } interface POCJson { version: string; octreeDir: string; projection: string; points: number; boundingBox: BoundingBoxData; tightBoundingBox?: BoundingBoxData; pointAttributes: PointAttributeStringName[]; spacing: number; scale: number; hierarchyStepSize: number; hierarchy: [string, number][]; // [name, numPoints][] } /** * * @param url * The url of the point cloud file (usually cloud.js). * @param getUrl * Function which receives the relative URL of a point cloud chunk file which is to be loaded * and shoud return a new url (e.g. signed) in the form of a string or a promise. * @param xhrRequest An arrow function for a fetch request * @returns * An observable which emits once when the first LOD of the point cloud is loaded. */ export function loadPOC( url: string, getUrl: GetUrlFn, xhrRequest: XhrRequest, ): Promise<PointCloudOctreeGeometry> { return Promise.resolve(getUrl(url)).then((transformedUrl) => { return xhrRequest(transformedUrl, { mode: 'cors' }) .then((res) => handleFailedRequest(res)) .then((okRes) => okRes.json()) .then(parse(transformedUrl, getUrl, xhrRequest)); }); } function parse(url: string, getUrl: GetUrlFn, xhrRequest: XhrRequest) { return (data: POCJson): Promise<PointCloudOctreeGeometry> => { const { offset, boundingBox, tightBoundingBox } = getBoundingBoxes(data); const loader = new BinaryLoader({ getUrl, version: data.version, boundingBox, scale: data.scale, xhrRequest, }); const pco = new PointCloudOctreeGeometry( loader, boundingBox, tightBoundingBox, offset, xhrRequest, ); pco.url = url; pco.octreeDir = data.octreeDir; pco.needsUpdate = true; pco.spacing = data.spacing; pco.hierarchyStepSize = data.hierarchyStepSize; pco.projection = data.projection; pco.offset = offset; pco.pointAttributes = new PointAttributes(data.pointAttributes); const nodes: Record<string, PointCloudOctreeGeometryNode> = {}; const version = new Version(data.version); return loadRoot(pco, data, nodes, version).then(() => { if (version.upTo('1.4')) { loadRemainingHierarchy(pco, data, nodes); } pco.nodes = nodes; return pco; }); }; } function getBoundingBoxes(data: POCJson): { offset: Vector3; boundingBox: Box3; tightBoundingBox: Box3; } { const min = new Vector3(data.boundingBox.lx, data.boundingBox.ly, data.boundingBox.lz); const max = new Vector3(data.boundingBox.ux, data.boundingBox.uy, data.boundingBox.uz); const boundingBox = new Box3(min, max); const tightBoundingBox = boundingBox.clone(); const offset = min.clone(); if (data.tightBoundingBox) { const { lx, ly, lz, ux, uy, uz } = data.tightBoundingBox; tightBoundingBox.min.set(lx, ly, lz); tightBoundingBox.max.set(ux, uy, uz); } boundingBox.min.sub(offset); boundingBox.max.sub(offset); tightBoundingBox.min.sub(offset); tightBoundingBox.max.sub(offset); return { offset, boundingBox, tightBoundingBox }; } function loadRoot( pco: PointCloudOctreeGeometry, data: POCJson, nodes: Record<string, PointCloudOctreeGeometryNode>, version: Version, ): Promise<void> { const name = 'r'; const root = new PointCloudOctreeGeometryNode(name, pco, pco.boundingBox); root.hasChildren = true; root.spacing = pco.spacing; if (version.upTo('1.5')) { root.numPoints = data.hierarchy[0][1]; } else { root.numPoints = 0; } pco.root = root; nodes[name] = root; return pco.root.load(); } function loadRemainingHierarchy( pco: PointCloudOctreeGeometry, data: POCJson, nodes: Record<string, PointCloudOctreeGeometryNode>, ): void { for (let i = 1; i < data.hierarchy.length; i++) { const [name, numPoints] = data.hierarchy[i]; const { index, parentName, level } = parseName(name); const parentNode = nodes[parentName]; const boundingBox = createChildAABB(parentNode.boundingBox, index); const node = new PointCloudOctreeGeometryNode(name, pco, boundingBox); node.level = level; node.numPoints = numPoints; node.spacing = pco.spacing / Math.pow(2, node.level); nodes[name] = node; parentNode.addChild(node); } } function parseName(name: string): { index: number; parentName: string; level: number } { return { index: getIndexFromName(name), parentName: name.substring(0, name.length - 1), level: name.length - 1, }; }