@sauskylark/potree
Version:
WebGL point cloud viewer
372 lines (270 loc) • 8.51 kB
JavaScript
import * as THREE from "../../libs/three.js/build/three.module.js";
import {Annotation} from "../Annotation.js";
import {Measure} from "../utils/Measure.js";
import {CameraAnimation} from "../modules/CameraAnimation/CameraAnimation.js";
import {Utils} from "../utils.js";
import {PointSizeType} from "../defines.js";
function loadPointCloud(viewer, data){
let loadMaterial = (target) => {
if(data.material){
if(data.material.activeAttributeName != null){
target.activeAttributeName = data.material.activeAttributeName;
}
if(data.material.ranges != null){
for(let range of data.material.ranges){
if(range.name === "elevationRange"){
target.elevationRange = range.value;
}else if(range.name === "intensityRange"){
target.intensityRange = range.value;
}else{
target.setRange(range.name, range.value);
}
}
}
if(data.material.size != null){
target.size = data.material.size;
}
if(data.material.minSize != null){
target.minSize = data.material.minSize;
}
if(data.material.pointSizeType != null){
target.pointSizeType = PointSizeType[data.material.pointSizeType];
}
if(data.material.matcap != null){
target.matcap = data.material.matcap;
}
}else if(data.activeAttributeName != null){
target.activeAttributeName = data.activeAttributeName;
}else{
// no material data
}
};
const promise = new Promise((resolve) => {
const names = viewer.scene.pointclouds.map(p => p.name);
const alreadyExists = names.includes(data.name);
if(alreadyExists){
resolve();
return;
}
Potree.loadPointCloud(data.url, data.name, (e) => {
const {pointcloud} = e;
pointcloud.position.set(...data.position);
pointcloud.rotation.set(...data.rotation);
pointcloud.scale.set(...data.scale);
loadMaterial(pointcloud.material);
viewer.scene.addPointCloud(pointcloud);
resolve(pointcloud);
});
});
return promise;
}
function loadMeasurement(viewer, data){
const duplicate = viewer.scene.measurements.find(measure => measure.uuid === data.uuid);
if(duplicate){
return;
}
const measure = new Measure();
measure.uuid = data.uuid;
measure.name = data.name;
measure.showDistances = data.showDistances;
measure.showCoordinates = data.showCoordinates;
measure.showArea = data.showArea;
measure.closed = data.closed;
measure.showAngles = data.showAngles;
measure.showHeight = data.showHeight;
measure.showCircle = data.showCircle;
measure.showAzimuth = data.showAzimuth;
measure.showEdges = data.showEdges;
// color
for(const point of data.points){
const pos = new THREE.Vector3(...point);
measure.addMarker(pos);
}
viewer.scene.addMeasurement(measure);
}
function loadVolume(viewer, data){
const duplicate = viewer.scene.volumes.find(volume => volume.uuid === data.uuid);
if(duplicate){
return;
}
let volume = new Potree[data.type];
volume.uuid = data.uuid;
volume.name = data.name;
volume.position.set(...data.position);
volume.rotation.set(...data.rotation);
volume.scale.set(...data.scale);
volume.visible = data.visible;
volume.clip = data.clip;
viewer.scene.addVolume(volume);
}
function loadCameraAnimation(viewer, data){
const duplicate = viewer.scene.cameraAnimations.find(a => a.uuid === data.uuid);
if(duplicate){
return;
}
const animation = new CameraAnimation(viewer);
animation.uuid = data.uuid;
animation.name = data.name;
animation.duration = data.duration;
animation.t = data.t;
animation.curveType = data.curveType;
animation.visible = data.visible;
animation.controlPoints = [];
for(const cpdata of data.controlPoints){
const cp = animation.createControlPoint();
cp.position.set(...cpdata.position);
cp.target.set(...cpdata.target);
}
viewer.scene.addCameraAnimation(animation);
}
function loadOrientedImages(viewer, images){
const {cameraParamsPath, imageParamsPath} = images;
const duplicate = viewer.scene.orientedImages.find(i => i.imageParamsPath === imageParamsPath);
if(duplicate){
return;
}
Potree.OrientedImageLoader.load(cameraParamsPath, imageParamsPath, viewer).then( images => {
viewer.scene.addOrientedImages(images);
});
}
function loadGeopackage(viewer, geopackage){
const path = geopackage.path;
const duplicate = viewer.scene.geopackages.find(i => i.path === path);
if(duplicate){
return;
}
const projection = viewer.getProjection();
proj4.defs("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
proj4.defs("pointcloud", projection);
const transform = proj4("WGS84", "pointcloud");
const params = {
transform: transform,
};
Potree.GeoPackageLoader.loadUrl(path, params).then(data => {
viewer.scene.addGeopackage(data);
});
}
function loadSettings(viewer, data){
if(!data){
return;
}
viewer.setPointBudget(data.pointBudget);
viewer.setFOV(data.fov);
viewer.setEDLEnabled(data.edlEnabled);
viewer.setEDLRadius(data.edlRadius);
viewer.setEDLStrength(data.edlStrength);
viewer.setBackground(data.background);
viewer.setMinNodeSize(data.minNodeSize);
viewer.setShowBoundingBox(data.showBoundingBoxes);
}
function loadView(viewer, view){
viewer.scene.view.position.set(...view.position);
viewer.scene.view.lookAt(...view.target);
}
function loadAnnotationItem(item){
const annotation = new Annotation({
position: item.position,
title: item.title,
cameraPosition: item.cameraPosition,
cameraTarget: item.cameraTarget,
});
annotation.description = item.description;
annotation.uuid = item.uuid;
if(item.offset){
annotation.offset.set(...item.offset);
}
return annotation;
}
function loadAnnotations(viewer, data){
if(!data){
return;
}
const findDuplicate = (item) => {
let duplicate = null;
viewer.scene.annotations.traverse( a => {
if(a.uuid === item.uuid){
duplicate = a;
}
});
return duplicate;
};
const traverse = (item, parent) => {
const duplicate = findDuplicate(item);
if(duplicate){
return;
}
const annotation = loadAnnotationItem(item);
for(const childItem of item.children){
traverse(childItem, annotation);
}
parent.add(annotation);
};
for(const item of data){
traverse(item, viewer.scene.annotations);
}
}
function loadProfile(viewer, data){
const {name, points} = data;
const duplicate = viewer.scene.profiles.find(profile => profile.uuid === data.uuid);
if(duplicate){
return;
}
let profile = new Potree.Profile();
profile.name = name;
profile.uuid = data.uuid;
profile.setWidth(data.width);
for(const point of points){
profile.addMarker(new THREE.Vector3(...point));
}
viewer.scene.addProfile(profile);
}
function loadClassification(viewer, data){
if(!data){
return;
}
const classifications = data;
viewer.setClassifications(classifications);
}
export async function loadProject(viewer, data){
if(data.type !== "Potree"){
console.error("not a valid Potree project");
return;
}
loadSettings(viewer, data.settings);
loadView(viewer, data.view);
const pointcloudPromises = [];
for(const pointcloud of data.pointclouds){
const promise = loadPointCloud(viewer, pointcloud);
pointcloudPromises.push(promise);
}
for(const measure of data.measurements){
loadMeasurement(viewer, measure);
}
for(const volume of data.volumes){
loadVolume(viewer, volume);
}
for(const animation of data.cameraAnimations){
loadCameraAnimation(viewer, animation);
}
for(const profile of data.profiles){
loadProfile(viewer, profile);
}
if(data.orientedImages){
for(const images of data.orientedImages){
loadOrientedImages(viewer, images);
}
}
loadAnnotations(viewer, data.annotations);
loadClassification(viewer, data.classification);
// need to load at least one point cloud that defines the scene projection,
// before we can load stuff in other projections such as geopackages
//await Promise.any(pointcloudPromises); // (not yet supported)
Utils.waitAny(pointcloudPromises).then( () => {
if(data.geopackages){
for(const geopackage of data.geopackages){
loadGeopackage(viewer, geopackage);
}
}
});
await Promise.all(pointcloudPromises);
}