import * as THREE from "../../libs/three.js/build/three.module.js";
import {ClipTask, ClipMethod, CameraMode, LengthUnits, ElevationGradientRepeat} from "../defines.js";
import {Renderer} from "../PotreeRenderer.js";
import {PotreeRenderer} from "./PotreeRenderer.js";
import {EDLRenderer} from "./EDLRenderer.js";
import {HQSplatRenderer} from "./HQSplatRenderer.js";
import {Scene} from "./Scene.js";
import {ClippingTool} from "../utils/ClippingTool.js";
import {TransformationTool} from "../utils/TransformationTool.js";
import {Utils} from "../utils.js";
import {MapView} from "./map.js";
import {MapViewer} from "./map/MapViewer.js";
import {ProfileWindow, ProfileWindowController} from "./profile.js";
import {BoxVolume} from "../utils/Volume.js";
import {Features} from "../Features.js";
import {Message} from "../utils/Message.js";
import {Sidebar} from "./sidebar.js";
import {AnnotationTool} from "../utils/AnnotationTool.js";
import {MeasuringTool} from "../utils/MeasuringTool.js";
import {ProfileTool} from "../utils/ProfileTool.js";
import {VolumeTool} from "../utils/VolumeTool.js";
import {InputHandler} from "../navigation/InputHandler.js";
import {NavigationCube} from "./NavigationCube.js";
import {Compass} from "../utils/Compass.js";
import {OrbitControls} from "../navigation/OrbitControls.js";
import {FirstPersonControls} from "../navigation/FirstPersonControls.js";
import {EarthControls} from "../navigation/EarthControls.js";
import {DeviceOrientationControls} from "../navigation/DeviceOrientationControls.js";
import {VRControls} from "../navigation/VRControls.js";
import { ClassificationScheme } from "../materials/ClassificationScheme.js";
import { VRButton } from '../../libs/three.js/extra/VRButton.js';
import {transitions, easing, lerp} from '../utils/transitions.js'
import JSON5 from "../../libs/json5-2.1.3/json5.mjs";
import CursorDeal from '../utils/CursorDeal'
import Common from '../utils/Common'
import {Clip} from '../modules/clipModel/Clip'
import {Alignment} from "../modules/datasetAlignment/Alignment.js";
import {SiteModel} from "../modules/siteModel/SiteModel.js";
import Magnifier from "../utils/Magnifier.js";
import Reticule from "../navigation/Reticule.js";
import Viewport from "./Viewport.js"
import {ViewerBase} from "./viewerBase.js"
import SplitScreen from '../utils/SplitScreen'
import cameraLight from "../utils/cameraLight.js";
import math from "../utils/math.js";
import {UoMService} from '../utils/UnitConvert'
import {RouteGuider} from '../navigation/RouteGuider'
let mapArea;
export class Viewer extends ViewerBase{
constructor(domElement, mapArea_, args = {}){
super(domElement,args);
window.viewer = this
this.modules = { //add
Clip : Clip,
Alignment : Alignment,
SiteModel : SiteModel,
RouteGuider : new RouteGuider,
}
//add --------
this.navigateMode = 'free' // 'panorama'; 'free'自由模式是只显示点云或者未进入到漫游点,
this.isEdit = true
this.waitQueue = []
this.unitConvert = new UoMService();
mapArea = mapArea_
//-------------
this.guiLoaded = false;
this.guiLoadTasks = [];
this.onVrListeners = [];
this.messages = [];
this.elMessages = $(`
`);
$(domElement).append(this.elMessages);
try{
if(!Potree.settings.isOfficial)
{ // generate missing dom hierarchy
if ($(domElement).find('#potree_map').length === 0) {
let potreeMap = $(`
`);
$(domElement).append(potreeMap);
}
if ($(domElement).find('#potree_description').length === 0) {
let potreeDescription = $(`
`);
$(domElement).append(potreeDescription);
}
if ($(domElement).find('#potree_annotations').length === 0) {
let potreeAnnotationContainer = $(`
`);
$(domElement).append(potreeAnnotationContainer);
}
if ($(domElement).find('#potree_quick_buttons').length === 0) {
let potreeMap = $(`
`);
$(domElement).append(potreeMap);
}
//add
{
if(!mapArea){
$(domElement).append($("
"))
mapArea = $("
")
$(domElement).append(mapArea)
mapArea = mapArea[0]
}
}
let domRoot = this.renderer.domElement.parentElement;
let elAttach = $(" ");
elAttach.css({
position : "absolute",
right : '10%',
bottom: '20px',
zIndex: "10000",
fontSize:'1em', color:"black",
background:'rgba(255,255,255,0.8)',
})
let state = false
elAttach.on("click", () => {
window.buttonFunction && window.buttonFunction()
});
domRoot.appendChild(elAttach[0]);
}
this.pointCloudLoadedCallback = args.onPointCloudLoaded || function () {};
// if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
// defaultSettings.navigation = "Orbit";
// }
this.server = null;
this.fov = 60;
this.isFlipYZ = false;
this.useDEMCollisions = false;
this.generateDEM = false;
this.minNodeSize = 30;
this.edlStrength = 1.0;
this.edlRadius = 1.4;
this.edlOpacity = 1.0;
this.useEDL = false;
this.description = "";
this.classifications = ClassificationScheme.DEFAULT;
this.moveSpeed = 10;
this.lengthUnit = LengthUnits.METER;
this.lengthUnitDisplay = LengthUnits.METER;
this.showBoundingBox = false;
this.showAnnotations = true;
this.freeze = false;
this.clipTask = ClipTask.HIGHLIGHT;
this.clipMethod = ClipMethod.INSIDE_ANY;
this.elevationGradientRepeat = ElevationGradientRepeat.CLAMP;
this.filterReturnNumberRange = [0, 7];
this.filterNumberOfReturnsRange = [0, 7];
this.filterGPSTimeRange = [-Infinity, Infinity];
this.filterPointSourceIDRange = [0, 65535];
this.potreeRenderer = null;
this.edlRenderer = null;
this.pRenderer = null;
this.scene = null;
this.sceneVR = null;
this.overlay = null;
this.overlayCamera = null;
this.inputHandler = null;
this.controls = null;
this.clippingTool = null;
this.transformationTool = null;
this.navigationCube = null;
this.compass = null;
this.skybox = null;
this.clock = new THREE.Clock();
this.background = null;
if(args.noDragAndDrop){
}else{
this.initDragAndDrop();
}
if(typeof Stats !== "undefined"){
this.stats = new Stats();
this.stats.showPanel( 0 ); // 0: fps, 1: ms, 2: mb, 3+: custom
document.body.appendChild( this.stats.dom );
}
{
let canvas = this.renderer.domElement;
canvas.addEventListener("webglcontextlost", (e) => {
console.log(e);
this.postMessage("WebGL context lost. \u2639");
let gl = this.renderer.getContext();
let error = gl.getError();
console.log(error);
}, false);
}
{
this.overlay = new THREE.Scene();
this.overlayCamera = new THREE.OrthographicCamera(
0, 1,
1, 0,
-1000, 1000
);
}
this.pRenderer = new Renderer(this.renderer);
{
let near = 2.5;
let far = 10.0;
let fov = 90;
this.shadowTestCam = new THREE.PerspectiveCamera(90, 1, near, far);
this.shadowTestCam.position.set(3.50, -2.80, 8.561);
this.shadowTestCam.lookAt(new THREE.Vector3(0, 0, 4.87));
}
let scene = new Scene(this.renderer);
{ // create VR scene
this.sceneVR = new THREE.Scene();
// let texture = new THREE.TextureLoader().load(`${Potree.resourcePath}/images/vr_controller_help.jpg`);
// let plane = new THREE.PlaneBufferGeometry(1, 1, 1, 1);
// let infoMaterial = new THREE.MeshBasicMaterial({map: texture});
// let infoNode = new THREE.Mesh(plane, infoMaterial);
// infoNode.position.set(-0.5, 1, 0);
// infoNode.scale.set(0.4, 0.3, 1);
// infoNode.lookAt(0, 1, 0)
// this.sceneVR.add(infoNode);
// window.infoNode = infoNode;
}
this.setScene(scene);
{
this.inputHandler = new InputHandler(this, this.scene.scene);
//this.inputHandler.setScene(this.scene);
//this.inputHandler.addInputListener(this);//add
this.clippingTool = new ClippingTool(this);
this.transformationTool = new TransformationTool(this);
this.navigationCube = new NavigationCube(this);
this.navigationCube.visible = false;
this.compass = new Compass(this);
//add----------
this.magnifier = new Magnifier(this);
this.reticule = new Reticule(this)
this.scene.scene.add(this.magnifier)
this.scene.scene.add(this.reticule)
this.mainViewport = new Viewport( this.scene.view, this.scene.cameraP, {
left:0, bottom:0, width:1, height: 1, name:'MainView'
})
this.viewports = [this.mainViewport]
this.mapViewer = new MapViewer(mapArea/* $('#mapGaode')[0] */)
CursorDeal.init(this)//ADD
this.modules.SiteModel.init()
//-----------
this.createControls();
this.clippingTool.setScene(this.scene);
let onPointcloudAdded = (e) => {
if (this.scene.pointclouds.length === 1) {
let speed = e.pointcloud.boundingBox.getSize(new THREE.Vector3()).length();
speed = speed / 2000;
this.setMoveSpeed(speed);
}
};
let onVolumeRemoved = (e) => {
this.inputHandler.deselect(e.volume);
};
this.addEventListener('scene_changed', (e) => {
this.inputHandler.setScene(e.scene);
this.clippingTool.setScene(this.scene);
if(!e.scene.hasEventListener("pointcloud_added", onPointcloudAdded)){
e.scene.addEventListener("pointcloud_added", onPointcloudAdded);
}
if(!e.scene.hasEventListener("volume_removed", onPointcloudAdded)){
e.scene.addEventListener("volume_removed", onVolumeRemoved);
}
});
this.scene.addEventListener("volume_removed", onVolumeRemoved);
this.scene.addEventListener('pointcloud_added', onPointcloudAdded);
}
{ // set defaults
this.setFOV(60);
this.setEDLEnabled(false);
this.setEDLRadius(1.4);
this.setEDLStrength(0.4);
this.setEDLOpacity(1.0);
this.setClipTask(ClipTask.HIGHLIGHT);
this.setClipMethod(ClipMethod.INSIDE_ANY);
this.setPointBudget(1*1000*1000);
this.setShowBoundingBox(false);
this.setFreeze(false);
this.setControls(this.fpControls/* orbitControls */);
this.setBackground('gradient');
this.scaleFactor = 1;
this.loadSettingsFromURL();
}
// start rendering!
//if(args.useDefaultRenderLoop === undefined || args.useDefaultRenderLoop === true){
//requestAnimationFrame(this.loop.bind(this));
//}
this.renderer.setAnimationLoop(this.loop.bind(this));
this.loadGUI = this.loadGUI.bind(this);
this.annotationTool = new AnnotationTool(this);
this.measuringTool = new MeasuringTool(this);
this.profileTool = new ProfileTool(this);
this.volumeTool = new VolumeTool(this);
}catch(e){
this.onCrash(e);
}
{//add
let pointDensity = ''
Object.defineProperty(Potree.settings , "pointDensity",{
get: function() {
return pointDensity
},
set: (density)=>{
if(pointDensity != density){
var config = Potree.config.pointDensity[density];
viewer.setPointBudget(config.pointBudget );
Potree.maxPointLevel = config.maxLevel
viewer.scene.pointclouds.forEach(e=>{
e.maxLevel = config.maxLevel
})
}
}
})
}
{
let cameraFar = Potree.settings.cameraFar
Object.defineProperty(Potree.settings , "cameraFar",{
get: function() {
return cameraFar
},
set: (far)=>{
if(far != cameraFar){
if(Potree.settings.displayMode != 'showPanos'){
this.mainViewport.camera.far = far;
this.mainViewport.camera.updateProjectionMatrix()
}
}
}
})
}
}
onCrash(error){
$(this.renderArea).empty();
if ($(this.renderArea).find('#potree_failpage').length === 0) {
let elFailPage = $(`
Potree Encountered An Error
This may happen if your browser or graphics card is not supported.
We recommend to use
Chrome
or
Firefox .
Please also visit webglreport.com and
check whether your system supports WebGL.
If you are already using one of the recommended browsers and WebGL is enabled,
consider filing an issue report at github ,
including your operating system, graphics card, browser and browser version, as well as the
error message below.
Please do not report errors on unsupported browsers.
`);
let elErrorMessage = elFailPage.find('#potree_error_console');
elErrorMessage.html(error.stack);
$(this.renderArea).append(elFailPage);
}
throw error;
}
// ------------------------------------------------------------------------------------
// Viewer API
// ------------------------------------------------------------------------------------
setScene (scene) {
if (scene === this.scene) {
return;
}
let oldScene = this.scene;
this.scene = scene;
this.dispatchEvent({
type: 'scene_changed',
oldScene: oldScene,
scene: scene
});
{ // Annotations
$('.annotation').detach();
// for(let annotation of this.scene.annotations){
// this.renderArea.appendChild(annotation.domElement[0]);
// }
this.scene.annotations.traverse(annotation => {
this.renderArea.appendChild(annotation.domElement[0]);
});
if (!this.onAnnotationAdded) {
this.onAnnotationAdded = e => {
// console.log("annotation added: " + e.annotation.title);
e.annotation.traverse(node => {
$("#potree_annotation_container").append(node.domElement);
//this.renderArea.appendChild(node.domElement[0]);
node.scene = this.scene;
});
};
}
if (oldScene) {
oldScene.annotations.removeEventListener('annotation_added', this.onAnnotationAdded);
}
this.scene.annotations.addEventListener('annotation_added', this.onAnnotationAdded);
}
};
setControls(controls/* , setSpeed */){
if (controls !== this.controls) {
if (this.controls) {
this.controls.setEnable(false)
//this.inputHandler.removeInputListener(this.controls);
this.controls.moveSpeed = this.moveSpeed; //记录 (因为orbit的radius很大,转为firstPerson时要缩小)
}
this.controls = controls;
controls.moveSpeed && this.setMoveSpeed(controls.moveSpeed) //add
this.controls.setEnable(true)
//this.inputHandler.addInputListener(this.controls);
}
}
getControls () {
if(this.renderer.xr.isPresenting){
return this.vrControls;
}else{
return this.controls;
}
}
getMinNodeSize () {
return this.minNodeSize;
};
setMinNodeSize (value) {
if (this.minNodeSize !== value) {
this.minNodeSize = value;
this.dispatchEvent({'type': 'minnodesize_changed', 'viewer': this});
}
};
getBackground () {
return this.background;
}
setBackground(bg){
if (this.background === bg) {
return;
}
if(bg === "skybox"){
this.skybox = Utils.loadSkybox(new URL(Potree.resourcePath + '/textures/skybox2/').href);
}
this.background = bg;
this.backgroundOpacity = 1//add
this.dispatchEvent({'type': 'background_changed', 'viewer': this});
}
setDescription (value) {
this.description = value;
$('#potree_description').html(value);
//$('#potree_description').text(value);
}
getDescription(){
return this.description;
}
setShowBoundingBox (value) {
if (this.showBoundingBox !== value) {
this.showBoundingBox = value;
this.dispatchEvent({'type': 'show_boundingbox_changed', 'viewer': this});
}
};
getShowBoundingBox () {
return this.showBoundingBox;
};
setMoveSpeed (value) {
if (this.getMoveSpeed() !== value) {
this.mainViewport.setMoveSpeed(value)
this.dispatchEvent({'type': 'move_speed_changed', 'viewer': this, 'speed': value});
}
};
getMoveSpeed () {
return this.mainViewport.moveSpeed;
};
setWeightClassification (w) {
for (let i = 0; i < this.scene.pointclouds.length; i++) {
this.scene.pointclouds[i].material.weightClassification = w;
this.dispatchEvent({'type': 'attribute_weights_changed' + i, 'viewer': this});
}
};
setFreeze (value) {
value = Boolean(value);
if (this.freeze !== value) {
this.freeze = value;
this.dispatchEvent({'type': 'freeze_changed', 'viewer': this});
}
};
getFreeze () {
return this.freeze;
};
getClipTask(){
return this.clipTask;
}
getClipMethod(){
return this.clipMethod;
}
setClipTask(value){
if(this.clipTask !== value){
this.clipTask = value;
this.dispatchEvent({
type: "cliptask_changed",
viewer: this});
}
}
setClipMethod(value){
if(this.clipMethod !== value){
this.clipMethod = value;
this.dispatchEvent({
type: "clipmethod_changed",
viewer: this});
}
}
setElevationGradientRepeat(value){
if(this.elevationGradientRepeat !== value){
this.elevationGradientRepeat = value;
this.dispatchEvent({
type: "elevation_gradient_repeat_changed",
viewer: this});
}
}
setPointBudget (value) {
if (Potree.pointBudget !== value) {
Potree.pointBudget = parseInt(value);
this.dispatchEvent({'type': 'point_budget_changed', 'viewer': this});
}
};
getPointBudget () {
return Potree.pointBudget;
};
setShowAnnotations (value) {
if (this.showAnnotations !== value) {
this.showAnnotations = value;
this.dispatchEvent({'type': 'show_annotations_changed', 'viewer': this});
}
}
getShowAnnotations () {
return this.showAnnotations;
}
setDEMCollisionsEnabled(value){
if(this.useDEMCollisions !== value){
this.useDEMCollisions = value;
this.dispatchEvent({'type': 'use_demcollisions_changed', 'viewer': this});
};
};
getDEMCollisionsEnabled () {
return this.useDEMCollisions;
};
setEDLEnabled (value) {
value = Boolean(value) && Features.SHADER_EDL.isSupported();
if (this.useEDL !== value) {
this.useEDL = value;
this.dispatchEvent({'type': 'use_edl_changed', 'viewer': this});
}
};
getEDLEnabled () {
return this.useEDL;
};
setEDLRadius (value) {
if (this.edlRadius !== value) {
this.edlRadius = value;
this.dispatchEvent({'type': 'edl_radius_changed', 'viewer': this});
}
};
getEDLRadius () {
return this.edlRadius;
};
setEDLStrength (value) {
if (this.edlStrength !== value) {
this.edlStrength = value;
this.dispatchEvent({'type': 'edl_strength_changed', 'viewer': this});
}
};
getEDLStrength () {
return this.edlStrength;
};
setEDLOpacity (value) {
if (this.edlOpacity !== value) {
this.edlOpacity = value;
this.dispatchEvent({'type': 'edl_opacity_changed', 'viewer': this});
}
};
getEDLOpacity () {
return this.edlOpacity;
};
setFOV (value) {
if (this.fov !== value) {
this.fov = value;
this.scene.cameraP.fov = this.fov;
this.scene.cameraP.updateProjectionMatrix()
this.dispatchEvent({'type': 'fov_changed', 'viewer': this});
}
};
getFOV () {
return this.fov;
};
disableAnnotations () {
this.scene.annotations.traverse(annotation => {
annotation.domElement.css('pointer-events', 'none');
// return annotation.visible;
});
};
enableAnnotations () {
this.scene.annotations.traverse(annotation => {
annotation.domElement.css('pointer-events', 'auto');
// return annotation.visible;
});
}
setClassifications(classifications){
this.classifications = classifications;
this.dispatchEvent({'type': 'classifications_changed', 'viewer': this});
}
setClassificationVisibility (key, value) {
if (!this.classifications[key]) {
this.classifications[key] = {visible: value, name: 'no name'};
this.dispatchEvent({'type': 'classification_visibility_changed', 'viewer': this});
} else if (this.classifications[key].visible !== value) {
this.classifications[key].visible = value;
this.dispatchEvent({'type': 'classification_visibility_changed', 'viewer': this});
}
}
toggleAllClassificationsVisibility(){
let numVisible = 0;
let numItems = 0;
for(const key of Object.keys(this.classifications)){
if(this.classifications[key].visible){
numVisible++;
}
numItems++;
}
let visible = true;
if(numVisible === numItems){
visible = false;
}
let somethingChanged = false;
for(const key of Object.keys(this.classifications)){
if(this.classifications[key].visible !== visible){
this.classifications[key].visible = visible;
somethingChanged = true;
}
}
if(somethingChanged){
this.dispatchEvent({'type': 'classification_visibility_changed', 'viewer': this});
}
}
setFilterReturnNumberRange(from, to){
this.filterReturnNumberRange = [from, to];
this.dispatchEvent({'type': 'filter_return_number_range_changed', 'viewer': this});
}
setFilterNumberOfReturnsRange(from, to){
this.filterNumberOfReturnsRange = [from, to];
this.dispatchEvent({'type': 'filter_number_of_returns_range_changed', 'viewer': this});
}
setFilterGPSTimeRange(from, to){
this.filterGPSTimeRange = [from, to];
this.dispatchEvent({'type': 'filter_gps_time_range_changed', 'viewer': this});
}
setFilterPointSourceIDRange(from, to){
this.filterPointSourceIDRange = [from, to]
this.dispatchEvent({'type': 'filter_point_source_id_range_changed', 'viewer': this});
}
setLengthUnit (value) {
switch (value) {
case 'm':
this.lengthUnit = LengthUnits.METER;
this.lengthUnitDisplay = LengthUnits.METER;
break;
case 'ft':
this.lengthUnit = LengthUnits.FEET;
this.lengthUnitDisplay = LengthUnits.FEET;
break;
case 'in':
this.lengthUnit = LengthUnits.INCH;
this.lengthUnitDisplay = LengthUnits.INCH;
break;
}
this.dispatchEvent({ 'type': 'length_unit_changed', 'viewer': this, value: value});
};
setLengthUnitAndDisplayUnit(lengthUnitValue, lengthUnitDisplayValue) {
switch (lengthUnitValue) {
case 'm':
this.lengthUnit = LengthUnits.METER;
break;
case 'ft':
this.lengthUnit = LengthUnits.FEET;
break;
case 'in':
this.lengthUnit = LengthUnits.INCH;
break;
}
switch (lengthUnitDisplayValue) {
case 'm':
this.lengthUnitDisplay = LengthUnits.METER;
break;
case 'ft':
this.lengthUnitDisplay = LengthUnits.FEET;
break;
case 'in':
this.lengthUnitDisplay = LengthUnits.INCH;
break;
}
this.dispatchEvent({ 'type': 'length_unit_changed', 'viewer': this, value: lengthUnitValue });
};
zoomTo(node, factor, animationDuration = 0){
let view = this.scene.view;
let camera = this.scene.cameraP.clone();
camera.rotation.copy(this.scene.cameraP.rotation);
camera.rotation.order = "ZXY";
camera.rotation.x = Math.PI / 2 + view.pitch;
camera.rotation.z = view.yaw;
camera.updateMatrix();
camera.updateMatrixWorld();
camera.zoomTo(node, factor);
let bs;
if (node.boundingSphere) {
bs = node.boundingSphere;
} else if (node.geometry && node.geometry.boundingSphere) {
bs = node.geometry.boundingSphere;
} else {
bs = node.boundingBox.getBoundingSphere(new THREE.Sphere());
}
bs = bs.clone().applyMatrix4(node.matrixWorld);
let startPosition = view.position.clone();
let endPosition = camera.position.clone();
let startTarget = view.getPivot();
let endTarget = bs.center;
let startRadius = view.radius;
let endRadius = endPosition.distanceTo(endTarget);
let easing = TWEEN.Easing.Quartic.Out;
{ // animate camera position
let pos = startPosition.clone();
let tween = new TWEEN.Tween(pos).to(endPosition, animationDuration);
tween.easing(easing);
tween.onUpdate(() => {
view.position.copy(pos);
});
tween.start();
}
{ // animate camera target
let target = startTarget.clone();
let tween = new TWEEN.Tween(target).to(endTarget, animationDuration);
tween.easing(easing);
tween.onUpdate(() => {
view.lookAt(target);
});
tween.onComplete(() => {
view.lookAt(target);
this.dispatchEvent({type: 'focusing_finished', target: this});
});
this.dispatchEvent({type: 'focusing_started', target: this});
tween.start();
}
};
moveToGpsTimeVicinity(time){
const result = Potree.Utils.findClosestGpsTime(time, viewer);
const box = result.node.pointcloud.deepestNodeAt(result.position).getBoundingBox();
const diameter = box.min.distanceTo(box.max);
const camera = this.scene.getActiveCamera();
const offset = camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(diameter);
const newCamPos = result.position.clone().sub(offset);
this.scene.view.position.copy(newCamPos);
this.scene.view.lookAt(result.position);
}
showAbout () {
$(function () {
$('#about-panel').dialog();
});
};
getGpsTimeExtent(){
const range = [Infinity, -Infinity];
for(const pointcloud of this.scene.pointclouds){
const attributes = pointcloud.pcoGeometry.pointAttributes.attributes;
const aGpsTime = attributes.find(a => a.name === "gps-time");
if(aGpsTime){
range[0] = Math.min(range[0], aGpsTime.range[0]);
range[1] = Math.max(range[1], aGpsTime.range[1]);
}
}
return range;
}
fitToScreen (factor = 1, animationDuration = 0) {
let box = this.getBoundingBox(this.scene.pointclouds);
let node = new THREE.Object3D();
node.boundingBox = box;
this.zoomTo(node, factor, animationDuration);
this.controls.stop();
};
toggleNavigationCube() {
this.navigationCube.visible = !this.navigationCube.visible;
}
/* setView(pos, view) {
if(!pos) return;
switch(pos) {
case "F":
this.setFrontView(view);
break;
case "B":
this.setBackView(view);
break;
case "L":
this.setLeftView(view);
break;
case "R":
this.setRightView(view);
break;
case "U":
this.setTopView(view);
break;
case "D":
this.setBottomView(view);
break;
}
} */
setTopView(view){
view = view || this.scene.view
view.setCubeView("Top")
this.fitToScreen();
};
setBottomView(){
this.scene.view.yaw = -Math.PI;
this.scene.view.pitch = Math.PI / 2;
this.fitToScreen();
};
setFrontView(view){
view = view || this.scene.view
view.yaw = 0;
view.pitch = 0;
this.fitToScreen();
};
setBackView(view){
view = view || this.scene.view
view.yaw = Math.PI;
view.pitch = 0;
this.fitToScreen();
};
setLeftView(){
this.scene.view.yaw = -Math.PI / 2;
this.scene.view.pitch = 0;
this.fitToScreen();
};
setRightView () {
this.scene.view.yaw = Math.PI / 2;
this.scene.view.pitch = 0;
this.fitToScreen();
};
flipYZ () {
this.isFlipYZ = !this.isFlipYZ;
// TODO flipyz
console.log('TODO');
}
setCameraMode(mode){
this.scene.cameraMode = mode;
for(let pointcloud of this.scene.pointclouds) {
pointcloud.material.useOrthographicCamera = mode == CameraMode.ORTHOGRAPHIC;
}
}
getProjection(){
const pointcloud = this.scene.pointclouds[0];
if(pointcloud){
return pointcloud.projection;
}else{
return null;
}
}
async loadProject(url){
const response = await fetch(url);
if(response.ok){
const text = await response.text();
const json = JSON5.parse(text);
// const json = JSON.parse(text);
if(json.type === "Potree"){
Potree.loadProject(viewer, json);
}
}else{
console.warn("未能加载:"+url )
}
}
saveProject(){
return Potree.saveProject(this);
}
loadSettingsFromURL(){
if(Utils.getParameterByName("pointSize")){
this.setPointSize(parseFloat(Utils.getParameterByName("pointSize")));
}
if(Utils.getParameterByName("FOV")){
this.setFOV(parseFloat(Utils.getParameterByName("FOV")));
}
if(Utils.getParameterByName("opacity")){
this.setOpacity(parseFloat(Utils.getParameterByName("opacity")));
}
if(Utils.getParameterByName("edlEnabled")){
let enabled = Utils.getParameterByName("edlEnabled") === "true";
this.setEDLEnabled(enabled);
}
if (Utils.getParameterByName('edlRadius')) {
this.setEDLRadius(parseFloat(Utils.getParameterByName('edlRadius')));
}
if (Utils.getParameterByName('edlStrength')) {
this.setEDLStrength(parseFloat(Utils.getParameterByName('edlStrength')));
}
if (Utils.getParameterByName('pointBudget')) {
this.setPointBudget(parseFloat(Utils.getParameterByName('pointBudget')));
}
if (Utils.getParameterByName('showBoundingBox')) {
let enabled = Utils.getParameterByName('showBoundingBox') === 'true';
if (enabled) {
this.setShowBoundingBox(true);
} else {
this.setShowBoundingBox(false);
}
}
if (Utils.getParameterByName('material')) {
let material = Utils.getParameterByName('material');
this.setMaterial(material);
}
if (Utils.getParameterByName('pointSizing')) {
let sizing = Utils.getParameterByName('pointSizing');
this.setPointSizing(sizing);
}
if (Utils.getParameterByName('quality')) {
let quality = Utils.getParameterByName('quality');
this.setQuality(quality);
}
if (Utils.getParameterByName('position')) {
let value = Utils.getParameterByName('position');
value = value.replace('[', '').replace(']', '');
let tokens = value.split(';');
let x = parseFloat(tokens[0]);
let y = parseFloat(tokens[1]);
let z = parseFloat(tokens[2]);
this.scene.view.position.set(x, y, z);
}
if (Utils.getParameterByName('target')) {
let value = Utils.getParameterByName('target');
value = value.replace('[', '').replace(']', '');
let tokens = value.split(';');
let x = parseFloat(tokens[0]);
let y = parseFloat(tokens[1]);
let z = parseFloat(tokens[2]);
this.scene.view.lookAt(new THREE.Vector3(x, y, z));
}
if (Utils.getParameterByName('background')) {
let value = Utils.getParameterByName('background');
this.setBackground(value);
}
// if(Utils.getParameterByName("elevationRange")){
// let value = Utils.getParameterByName("elevationRange");
// value = value.replace("[", "").replace("]", "");
// let tokens = value.split(";");
// let x = parseFloat(tokens[0]);
// let y = parseFloat(tokens[1]);
//
// this.setElevationRange(x, y);
// //this.scene.view.target.set(x, y, z);
// }
};
// ------------------------------------------------------------------------------------
// Viewer Internals
// ------------------------------------------------------------------------------------
createControls () {
{ // create FIRST PERSON CONTROLS
this.fpControls = new FirstPersonControls(this, this.mainViewport);
this.fpControls.enabled = false;
this.fpControls.addEventListener('start', this.disableAnnotations.bind(this));
this.fpControls.addEventListener('end', this.enableAnnotations.bind(this));
this.addEventListener("loadPointCloudDone", ()=>{
let boundPlane = new THREE.Box3()
boundPlane.expandByPoint(this.bound.boundingBox.min.clone())//最低高度为bound的最低
boundPlane.expandByPoint(this.bound.boundingBox.max.clone().setZ(this.bound.center.z))//最高高度为bound的中心高度
FirstPersonControls.boundPlane = boundPlane
FirstPersonControls.standardSpeed = THREE.Math.clamp( Math.sqrt(this.bound.boundSize.length() )/ 100 , 0.02,0.5); //在这个boundPlane中的速度
})
}
// { // create GEO CONTROLS
// this.geoControls = new GeoControls(this.scene.camera, this.renderer.domElement);
// this.geoControls.enabled = false;
// this.geoControls.addEventListener("start", this.disableAnnotations.bind(this));
// this.geoControls.addEventListener("end", this.enableAnnotations.bind(this));
// this.geoControls.addEventListener("move_speed_changed", (event) => {
// this.setMoveSpeed(this.geoControls.moveSpeed);
// });
// }
{ // create ORBIT CONTROLS
this.orbitControls = new OrbitControls(this);
this.orbitControls.enabled = false;
this.orbitControls.addEventListener('start', this.disableAnnotations.bind(this));
this.orbitControls.addEventListener('end', this.enableAnnotations.bind(this));
}
{ // create EARTH CONTROLS
this.earthControls = new EarthControls(this);
this.earthControls.enabled = false;
this.earthControls.addEventListener('start', this.disableAnnotations.bind(this));
this.earthControls.addEventListener('end', this.enableAnnotations.bind(this));
}
{ // create DEVICE ORIENTATION CONTROLS
this.deviceControls = new DeviceOrientationControls(this);
this.deviceControls.enabled = false;
this.deviceControls.addEventListener('start', this.disableAnnotations.bind(this));
this.deviceControls.addEventListener('end', this.enableAnnotations.bind(this));
}
{ // create VR CONTROLS
this.vrControls = new VRControls(this);
this.vrControls.enabled = false;
this.vrControls.addEventListener('start', this.disableAnnotations.bind(this));
this.vrControls.addEventListener('end', this.enableAnnotations.bind(this));
}
};
toggleSidebar () {
let renderArea = $('#potree_render_area');
let isVisible = renderArea.css('left') !== '0px';
if (isVisible) {
renderArea.css('left', '0px');
} else {
renderArea.css('left', '300px');
}
};
toggleMap () {
// let map = $('#potree_map');
// map.toggle(100);
if (this.mapView) {
this.mapView.toggle();
}
};
onGUILoaded(callback){
if(this.guiLoaded){
callback();
}else{
this.guiLoadTasks.push(callback);
}
}
promiseGuiLoaded(){
return new Promise( resolve => {
if(this.guiLoaded){
resolve();
}else{
this.guiLoadTasks.push(resolve);
}
});
}
loadGUI(callback){
if(callback){
this.onGUILoaded(callback);
}
let viewer = this;
let sidebarContainer = $('#potree_sidebar_container');
sidebarContainer.load(new URL(Potree.scriptPath + '/sidebar.html').href, () => {
sidebarContainer.css('width', '300px');
sidebarContainer.css('height', '100%');
let imgMenuToggle = document.createElement('img');
imgMenuToggle.src = new URL(Potree.resourcePath + '/icons/menu_button.svg').href;
imgMenuToggle.onclick = this.toggleSidebar;
imgMenuToggle.classList.add('potree_menu_toggle');
let imgMapToggle = document.createElement('img');
imgMapToggle.src = new URL(Potree.resourcePath + '/icons/map_icon.png').href;
imgMapToggle.style.display = 'none';
imgMapToggle.onclick = e => { this.toggleMap(); };
imgMapToggle.id = 'potree_map_toggle';
let elButtons = $("#potree_quick_buttons").get(0);
elButtons.append(imgMenuToggle);
elButtons.append(imgMapToggle);
/*
VRButton.createButton(this.renderer).then(vrButton => {
if(vrButton == null){
console.log("VR not supported or active.");
return;
}
this.renderer.xr.enabled = true;
let element = vrButton.element;
element.style.position = "";
element.style.bottom = "";
element.style.left = "";
element.style.margin = "4px";
element.style.fontSize = "100%";
element.style.width = "2.5em";
element.style.height = "2.5em";
element.style.padding = "0";
element.style.textShadow = "black 2px 2px 2px";
element.style.display = "block";
elButtons.append(element);
vrButton.onStart(() => {
this.dispatchEvent({type: "vr_start"});
});
vrButton.onEnd(() => {
this.dispatchEvent({type: "vr_end"});
});
});
this.mapView = new MapView(this);
this.mapView.init(); */
i18n.init({
lng: 'en',
resGetPath: Potree.resourcePath + '/lang/__lng__/__ns__.json',
preload: ['en', 'fr', 'de', 'jp', 'se', 'es', 'zh'],
getAsync: true,
debug: false
}, function (t) {
// Start translation once everything is loaded
$('body').i18n();
});
$(() => {
//initSidebar(this);
let sidebar = new Sidebar(this);
sidebar.init();
this.sidebar = sidebar;
//if (callback) {
// $(callback);
//}
let elProfile = $('').load(new URL(Potree.scriptPath + '/profile.html').href, () => {
$(document.body).append(elProfile.children());
this.profileWindow = new ProfileWindow(this);
this.profileWindowController = new ProfileWindowController(this);
$('#profile_window').draggable({
handle: $('#profile_titlebar'),
containment: $(document.body)
});
$('#profile_window').resizable({
containment: $(document.body),
handles: 'n, e, s, w'
});
$(() => {
this.guiLoaded = true;
for(let task of this.guiLoadTasks){
task();
}
});
});
});
});
return this.promiseGuiLoaded();
}
setLanguage (lang) {
i18n.setLng(lang);
$('body').i18n();
}
setServer (server) {
this.server = server;
}
initDragAndDrop(){
function allowDrag(e) {
e.dataTransfer.dropEffect = 'copy';
e.preventDefault();
}
let dropHandler = async (event) => {
console.log(event);
event.preventDefault();
for(const item of event.dataTransfer.items){
console.log(item);
if(item.kind !== "file"){
continue;
}
const file = item.getAsFile();
const isJson = file.name.toLowerCase().endsWith(".json");
const isGeoPackage = file.name.toLowerCase().endsWith(".gpkg");
if(isJson){
try{
const text = await file.text();
const json = JSON.parse(text);
if(json.type === "Potree"){
Potree.loadProject(viewer, json);
}
}catch(e){
console.error("failed to parse the dropped file as JSON");
console.error(e);
}
}else if(isGeoPackage){
const hasPointcloud = viewer.scene.pointclouds.length > 0;
if(!hasPointcloud){
let msg = "At least one point cloud is needed that specifies the ";
msg += "coordinate reference system before loading vector data.";
console.error(msg);
}else{
proj4.defs("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
proj4.defs("pointcloud", this.getProjection());
let transform = proj4("WGS84", "pointcloud");
const buffer = await file.arrayBuffer();
const params = {
transform: transform,
source: file.name,
};
const geo = await Potree.GeoPackageLoader.loadBuffer(buffer, params);
viewer.scene.addGeopackage(geo);
}
}
}
};
$("body")[0].addEventListener("dragenter", allowDrag);
$("body")[0].addEventListener("dragover", allowDrag);
$("body")[0].addEventListener("drop", dropHandler);
}
updateAnnotations () {
if(!this.visibleAnnotations){
this.visibleAnnotations = new Set();
}
this.scene.annotations.updateBounds();
this.scene.cameraP.updateMatrixWorld();
this.scene.cameraO.updateMatrixWorld();
let distances = [];
let renderAreaSize = this.renderer.getSize(new THREE.Vector2());
let viewer = this;
let visibleNow = [];
this.scene.annotations.traverse(annotation => {
if (annotation === this.scene.annotations) {
return true;
}
if (!annotation.visible) {
return false;
}
annotation.scene = this.scene;
let element = annotation.domElement;
let position = annotation.position.clone();
position.add(annotation.offset);
if (!position) {
position = annotation.boundingBox.getCenter(new THREE.Vector3());
}
let distance = viewer.scene.cameraP.position.distanceTo(position);
let radius = annotation.boundingBox.getBoundingSphere(new THREE.Sphere()).radius;
let screenPos = new THREE.Vector3();
let screenSize = 0;
{
// SCREEN POS
screenPos.copy(position).project(this.scene.getActiveCamera());
screenPos.x = renderAreaSize.x * (screenPos.x + 1) / 2;
screenPos.y = renderAreaSize.y * (1 - (screenPos.y + 1) / 2);
// SCREEN SIZE
if(viewer.scene.cameraMode == CameraMode.PERSPECTIVE) {
let fov = Math.PI * viewer.scene.cameraP.fov / 180;
let slope = Math.tan(fov / 2.0);
let projFactor = 0.5 * renderAreaSize.y / (slope * distance);
screenSize = radius * projFactor;
} else {
screenSize = Utils.projectedRadiusOrtho(radius, viewer.scene.cameraO.projectionMatrix, renderAreaSize.x, renderAreaSize.y);
}
}
element.css("left", screenPos.x + "px");
element.css("top", screenPos.y + "px");
//element.css("display", "block");
let zIndex = 10000000 - distance * (10000000 / this.scene.cameraP.far);
if(annotation.descriptionVisible){
zIndex += 10000000;
}
element.css("z-index", parseInt(zIndex));
if(annotation.children.length > 0){
let expand = screenSize > annotation.collapseThreshold || annotation.boundingBox.containsPoint(this.scene.getActiveCamera().position);
annotation.expand = expand;
if (!expand) {
//annotation.display = (screenPos.z >= -1 && screenPos.z <= 1);
let inFrustum = (screenPos.z >= -1 && screenPos.z <= 1);
if(inFrustum){
visibleNow.push(annotation);
}
}
return expand;
} else {
//annotation.display = (screenPos.z >= -1 && screenPos.z <= 1);
let inFrustum = (screenPos.z >= -1 && screenPos.z <= 1);
if(inFrustum){
visibleNow.push(annotation);
}
}
});
let notVisibleAnymore = new Set(this.visibleAnnotations);
for(let annotation of visibleNow){
annotation.display = true;
notVisibleAnymore.delete(annotation);
}
this.visibleAnnotations = visibleNow;
for(let annotation of notVisibleAnymore){
annotation.display = false;
}
}
updateMaterialDefaults(pointcloud){
// PROBLEM STATEMENT:
// * [min, max] of intensity, source id, etc. are computed as point clouds are loaded
// * the point cloud material won't know the range it should use until some data is loaded
// * users can modify the range at runtime, but sensible default ranges should be
// applied even if no GUI is present
// * display ranges shouldn't suddenly change even if the actual range changes over time.
// e.g. the root node has intensity range [1, 478]. One of the descendants increases range to
// [0, 2047]. We should not automatically change to the new range because that would result
// in sudden and drastic changes of brightness. We should adjust the min/max of the sidebar slider.
const material = pointcloud.material;
const attIntensity = pointcloud.getAttribute("intensity");
if(attIntensity != null && material.intensityRange[0] === Infinity){
material.intensityRange = [...attIntensity.range];
}
// const attIntensity = pointcloud.getAttribute("intensity");
// if(attIntensity && material.intensityRange[0] === Infinity){
// material.intensityRange = [...attIntensity.range];
// }
// let attributes = pointcloud.getAttributes();
// for(let attribute of attributes.attributes){
// if(attribute.range){
// let range = [...attribute.range];
// material.computedRange.set(attribute.name, range);
// //material.setRange(attribute.name, range);
// }
// }
}
update(delta, timestamp){
if(Potree.measureTimings) performance.mark("update-start");
this.dispatchEvent({
type: 'update_start',
delta: delta,
timestamp: timestamp});
this.updateScreenSize() //判断是否改变canvas大小
const scene = this.scene;
const camera = scene.getActiveCamera();
const visiblePointClouds = this.scene.pointclouds.filter(pc => pc.visible)
Potree.pointLoadLimit = Potree.pointBudget * 2;
const lTarget = camera.position.clone().add(camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(1000));
this.scene.directionalLight.position.copy(camera.position);
this.scene.directionalLight.lookAt(lTarget);
for (let pointcloud of visiblePointClouds) {
pointcloud.showBoundingBox = this.showBoundingBox;
pointcloud.generateDEM = this.generateDEM;
pointcloud.minimumNodePixelSize = this.minNodeSize;
let material = pointcloud.material;
material.uniforms.uFilterReturnNumberRange.value = this.filterReturnNumberRange;
material.uniforms.uFilterNumberOfReturnsRange.value = this.filterNumberOfReturnsRange;
material.uniforms.uFilterGPSTimeClipRange.value = this.filterGPSTimeRange;
material.uniforms.uFilterPointSourceIDClipRange.value = this.filterPointSourceIDRange;
material.classification = this.classifications;
material.recomputeClassification();
this.updateMaterialDefaults(pointcloud);
}
{
if(this.showBoundingBox){
let bbRoot = this.scene.scene.getObjectByName("potree_bounding_box_root");
if(!bbRoot){
let node = new THREE.Object3D();
node.name = "potree_bounding_box_root";
this.scene.scene.add(node);
bbRoot = node;
}
let visibleBoxes = [];
for(let pointcloud of this.scene.pointclouds){
for(let node of pointcloud.visibleNodes.filter(vn => vn.boundingBoxNode !== undefined)){
let box = node.boundingBoxNode;
visibleBoxes.push(box);
}
}
bbRoot.children = visibleBoxes;
}
}
if (!this.freeze) {
/*let cameraGroup = []
let size = this.renderer.getSize(new THREE.Vector2())
if(this.viewports){
this.viewports.forEach(viewport=>{
if(!viewport.active)return
cameraGroup.push({camera:viewport.camera, areaSize:new THREE.Vector2(Math.floor(size.x * viewport.width), Math.floor(size.y * viewport.height))})
})
}else{
cameraGroup.push({camera, areaSize:size})
}
let result = Potree.updatePointClouds(scene.pointclouds, cameraGroup );
*/
// DEBUG - ONLY DISPLAY NODES THAT INTERSECT MOUSE
//if(false){
// let renderer = viewer.renderer;
// let mouse = viewer.inputHandler.mouse;
// let nmouse = {
// x: (mouse.x / renderer.domElement.clientWidth) * 2 - 1,
// y: -(mouse.y / renderer.domElement.clientHeight) * 2 + 1
// };
// let pickParams = {};
// //if(params.pickClipped){
// // pickParams.pickClipped = params.pickClipped;
// //}
// pickParams.x = mouse.x;
// pickParams.y = renderer.domElement.clientHeight - mouse.y;
// let raycaster = new THREE.Raycaster();
// raycaster.setFromCamera(nmouse, camera);
// let ray = raycaster.ray;
// for(let pointcloud of scene.pointclouds){
// let nodes = pointcloud.nodesOnRay(pointcloud.visibleNodes, ray);
// pointcloud.visibleNodes = nodes;
// }
//}
// const tStart = performance.now();
// const worldPos = new THREE.Vector3();
// const camPos = viewer.scene.getActiveCamera().getWorldPosition(new THREE.Vector3());
// let lowestDistance = Infinity;
// let numNodes = 0;
// viewer.scene.scene.traverse(node => {
// node.getWorldPosition(worldPos);
// const distance = worldPos.distanceTo(camPos);
// lowestDistance = Math.min(lowestDistance, distance);
// numNodes++;
// if(Number.isNaN(distance)){
// console.error(":(");
// }
// });
// const duration = (performance.now() - tStart).toFixed(2);
// Potree.debug.computeNearDuration = duration;
// Potree.debug.numNodes = numNodes;
//console.log(lowestDistance.toString(2), duration);
//搬走
/* const tStart = performance.now();
const campos = camera.position;
let closestImage = Infinity;
for(const images of this.scene.orientedImages){
for(const image of images.images){
const distance = image.mesh.position.distanceTo(campos);
closestImage = Math.min(closestImage, distance);
}
}
const tEnd = performance.now();
if(result.lowestSpacing !== Infinity){
let near = result.lowestSpacing * 10.0;
let far = -this.getBoundingBox().applyMatrix4(camera.matrixWorldInverse).min.z;
far = Math.max(far * 1.5, 10000);
near = Math.min(100.0, Math.max(0.01, near));
near = Math.min(near, closestImage);
far = Math.max(far, near + 10000);
if(near === Infinity){
near = 0.1;
}
camera.near = near;
camera.far = far;
}else{
// don't change near and far in this case
}
if(this.scene.cameraMode == CameraMode.ORTHOGRAPHIC) {
camera.near = -camera.far;
}*/
}
this.scene.cameraP.fov = this.fov;
let controls = this.getControls();
if (controls === this.deviceControls) {
this.controls.setScene(scene);
this.controls.update(delta);
this.scene.cameraP.position.copy(scene.view.position);
this.scene.cameraO.position.copy(scene.view.position);
} else if (controls !== null) {
controls.setScene(scene);
controls.update(delta);
//更新camera
this.viewports.forEach(viewport=>{
if(!viewport.active)return
viewport.view.applyToCamera(viewport.camera)
})
}
/* this.viewports.forEach(e=>{//判断camera画面是否改变
if(e.cameraChanged()){
this.dispatchEvent({
type: "camera_changed",
camera: e.camera,
viewport : e
})
}
}) */
this.cameraChanged()//判断camera画面是否改变
/* {//判断camera画面是否改变
if(this._previousCamera === undefined){
this._previousCamera = this.scene.getActiveCamera().clone();
this._previousCamera.rotation.copy(this.scene.getActiveCamera().rotation);
}
if(!this._previousCamera.matrixWorld.equals(camera.matrixWorld) ||
!this._previousCamera.projectionMatrix.equals(camera.projectionMatrix)
){
this.dispatchEvent({
type: "camera_changed",
previous: this._previousCamera,
camera: camera
});
}
this._previousCamera = this.scene.getActiveCamera().clone();
this._previousCamera.rotation.copy(this.scene.getActiveCamera().rotation);
} */
{ // update clip boxes
let boxes = [];
// volumes with clipping enabled
//boxes.push(...this.scene.volumes.filter(v => (v.clip)));
boxes.push(...this.scene.volumes.filter(v => (v.clip && v instanceof BoxVolume)));
// profile segments
for(let profile of this.scene.profiles){
boxes.push(...profile.boxes);
}
// Needed for .getInverse(), pre-empt a determinant of 0, see #815 / #816
let degenerate = (box) => box.matrixWorld.determinant() !== 0;
let clipBoxes = boxes.filter(degenerate).map( box => {
box.updateMatrixWorld();
let boxInverse = box.matrixWorld.clone().invert();
let boxPosition = box.getWorldPosition(new THREE.Vector3());
return {box: box, inverse: boxInverse, position: boxPosition};
});
let clipPolygons = this.scene.polygonClipVolumes.filter(vol => vol.initialized);
// set clip volumes in material
for(let pointcloud of visiblePointClouds){
pointcloud.material.setClipBoxes(clipBoxes);
pointcloud.material.setClipPolygons(clipPolygons, this.clippingTool.maxPolygonVertices);
pointcloud.material.clipTask = this.clipTask;
pointcloud.material.clipMethod = this.clipMethod;
}
}
{
for(let pointcloud of visiblePointClouds){
pointcloud.material.elevationGradientRepeat = this.elevationGradientRepeat;
}
}
{ // update navigation cube
this.navigationCube.update(camera.rotation);
}
this.updateAnnotations();
if(this.mapView){
this.mapView.update(delta);
if(this.mapView.sceneProjection){
$( "#potree_map_toggle" ).css("display", "block");
}
}
TWEEN.update(timestamp);
transitions.update(delta);
this.dispatchEvent({
type: 'update',
delta: delta,
timestamp: timestamp});
if(Potree.measureTimings) {
performance.mark("update-end");
performance.measure("update", "update-start", "update-end");
}
//add ------
this.reticule.updateVisible()
this.mapViewer.update(delta)
}
updateViewPointcloud(camera, areaSize, isViewport){
let result = Potree.updatePointClouds(this.scene.pointclouds, camera, areaSize );
//if(isViewport)return
const tStart = performance.now();
const campos = camera.position;
let closestImage = Infinity;
for(const images of this.scene.orientedImages){
for(const image of images.images){
const distance = image.mesh.position.distanceTo(campos);
closestImage = Math.min(closestImage, distance);
}
}
const tEnd = performance.now();
//改:不根据点云修改视野near far
var near = camera.near, far = camera.far
if(!camera.limitFar && result.lowestSpacing !== Infinity){
let near = result.lowestSpacing * 10.0;
let far = -this.getBoundingBox().applyMatrix4(camera.matrixWorldInverse).min.z;
far = Math.max(far * 1.5, 10000);
near = Math.min(100.0, Math.max(0.01, near));
near = Math.min(near, closestImage);
far = Math.max(far, near + 10000);
if(near === Infinity){
near = 0.1;
}
camera.near = near;
camera.far = far;
}
if(this.scene.cameraMode == CameraMode.ORTHOGRAPHIC) {//???
camera.near = -camera.far;
}
if(near != camera.near || far != camera.far){
camera.updateProjectionMatrix()
}
//注:pointcloud.visibleNodes会随着near far自动更新
}
getPRenderer(){
if(this.useHQ){
if (!this.hqRenderer) {
this.hqRenderer = new HQSplatRenderer(this);
}
this.hqRenderer.useEDL = this.useEDL;
return this.hqRenderer;
}else{
/* if (this.useEDL && Features.SHADER_EDL.isSupported()) {
if (!this.edlRenderer) {
this.edlRenderer = new EDLRenderer(this);
}
return this.edlRenderer;
} else {
if (!this.potreeRenderer) {
this.potreeRenderer = new PotreeRenderer(this);
}
return this.potreeRenderer;
} */
if (!this.edlRenderer) {
this.edlRenderer = new EDLRenderer(this);
}
return this.edlRenderer;
}
}
renderVR(){
let renderer = this.renderer;
renderer.setClearColor(0x550000, 0);
renderer.clear();
let xr = renderer.xr;
let dbg = new THREE.PerspectiveCamera();
let xrCameras = xr.getCamera(dbg);
if(xrCameras.cameras.length !== 2){
return;
}
let makeCam = this.vrControls.getCamera.bind(this.vrControls);
{ // clear framebuffer
if(viewer.background === "skybox"){
renderer.setClearColor(0xff0000, 1);
}else if(viewer.background === "gradient"){
renderer.setClearColor(0x112233, 1);
}else if(viewer.background === "black"){
renderer.setClearColor(0x000000, 1);
}else if(viewer.background === "white"){
renderer.setClearColor(0xFFFFFF, 1);
}else{
renderer.setClearColor(0x000000, 0);
}
renderer.clear();
}
// render background
if(this.background === "skybox"){
let {skybox} = this;
let cam = makeCam();
skybox.camera.rotation.copy(cam.rotation);
skybox.camera.fov = cam.fov;
skybox.camera.aspect = cam.aspect;
// let dbg = new THREE.Object3D();
let dbg = skybox.parent;
// dbg.up.set(0, 0, 1);
dbg.rotation.x = Math.PI / 2;
// skybox.camera.parent = dbg;
// dbg.children.push(skybox.camera);
dbg.updateMatrix();
dbg.updateMatrixWorld();
skybox.camera.updateMatrix();
skybox.camera.updateMatrixWorld();
skybox.camera.updateProjectionMatrix();
renderer.render(skybox.scene, skybox.camera);
// renderer.render(skybox.scene, cam);
}else if(this.background === "gradient"){
// renderer.render(this.scene.sceneBG, this.scene.cameraBG);
}
this.renderer.xr.getSession().updateRenderState({
depthNear: 0.1,
depthFar: 10000
});
let cam = null;
let view = null;
{ // render world scene
cam = makeCam();
cam.position.z -= 0.8 * cam.scale.x;
cam.parent = null;
// cam.near = 0.05;
cam.near = viewer.scene.getActiveCamera().near;
cam.far = viewer.scene.getActiveCamera().far;
cam.updateMatrix();
cam.updateMatrixWorld();
this.scene.scene.updateMatrix();
this.scene.scene.updateMatrixWorld();
this.scene.scene.matrixAutoUpdate = false;
let camWorld = cam.matrixWorld.clone();
view = camWorld.clone().invert();
this.scene.scene.matrix.copy(view);
this.scene.scene.matrixWorld.copy(view);
cam.matrix.identity();
cam.matrixWorld.identity();
cam.matrixWorldInverse.identity();
renderer.render(this.scene.scene, cam);
this.scene.scene.matrixWorld.identity();
}
for(let pointcloud of this.scene.pointclouds){
let viewport = xrCameras.cameras[0].viewport;
pointcloud.material.useEDL = false;
pointcloud.screenHeight = viewport.height;
pointcloud.screenWidth = viewport.width;
// automatically switch to paraboloids because they cause far less flickering in VR,
// when point sizes are larger than around 2 pixels
// if(Features.SHADER_INTERPOLATION.isSupported()){
// pointcloud.material.shape = Potree.PointShape.PARABOLOID;
// }
}
// render point clouds
for(let xrCamera of xrCameras.cameras){
let v = xrCamera.viewport;
renderer.setViewport(v.x, v.y, v.width, v.height);
// xrCamera.fov = 90;
{ // estimate VR fov
let proj = xrCamera.projectionMatrix;
let inv = proj.clone().invert();
let p1 = new THREE.Vector4(0, 1, -1, 1).applyMatrix4(inv);
let rad = p1.y
let fov = 180 * (rad / Math.PI);
xrCamera.fov = fov;
}
for(let pointcloud of this.scene.pointclouds){
const {material} = pointcloud;
material.useEDL = false;
}
let vrWorld = view.clone().invert();
vrWorld.multiply(xrCamera.matrixWorld);
let vrView = vrWorld.clone().invert();
this.pRenderer.render(this.scene.scenePointCloud, xrCamera, null, {
viewOverride: vrView,
});
}
{ // render VR scene
let cam = makeCam();
cam.parent = null;
renderer.render(this.sceneVR, cam);
}
renderer.resetState();
}
clear(params={}){
let background = params.background || this.background;
let backgroundOpacity = params.backgroundOpacity == void 0 ? this.backgroundOpacity : params.backgroundOpacity//如果想完全透明,只需要backgroundOpacity为0
let renderer = this.renderer
//let gl = renderer.getContext()
if(background instanceof THREE.Color){ //add
renderer.setClearColor(background, backgroundOpacity);
}else if(background === "skybox"){
renderer.setClearColor(0x000000, 0);
} else if (background === 'gradient') {
renderer.setClearColor(0x000000, 0);
} else if (background === 'black') {
renderer.setClearColor(0x000000, 1);
} else if (background === 'white') {
renderer.setClearColor(0xFFFFFF, 1);
} else {
renderer.setClearColor(0x000000, 0);
}
params.target || renderer.clear();
}
renderDefault(params_={}){
let pRenderer = this.getPRenderer();
let renderSize
if(params_.target){
renderSize = new THREE.Vector2(params_.target.width, params_.target.height)
if(!params_.viewports){
console.warn('必须指定target的viewport! 且target大小和viewport.resolution2相同')
}
}else{
renderSize = this.renderer.getSize(new THREE.Vector2());
}
var viewports = params_.viewports || this.viewports
let needSResize = viewports.filter(e=>e.active).length > 1 || params_.resize
viewports.forEach(view=>{
let params = $.extend({},params_);
//if(!params.target){
params.camera = params.camera || view.camera;
params.extraEnableLayers = view.extraEnableLayers
//}
if(!view.active)return
var left,bottom,width,height
{
left = Math.floor(renderSize.x * view.left)
bottom = Math.floor(renderSize.y * view.bottom)
/* if(params_.target){//有target时最好viewport是专门建出来的
width = Math.floor(renderSize.x * view.width)
height = Math.floor(renderSize.y * view.height)
}else{ */
width = view.resolution.x // 用的是client的width和height
height = view.resolution.y
//}
if(width == 0 || height == 0)return
let scissorTest = view.width<1 || view.height<1
if(params_.target){
params_.target.viewport.set(left, bottom, width, height);
scissorTest && params_.target.scissor.set(left, bottom, width, height);
params_.target.scissorTest = scissorTest
}else{
this.renderer.setViewport(left, bottom, width, height) //规定视口,影响图形变换
scissorTest && this.renderer.setScissor( left, bottom, width, height );//规定渲染范围
this.renderer.setScissorTest( scissorTest );//开启WebGL剪裁测试功能,如果不开启,.setScissor方法设置的范围不起作用 | width==1且height==1时开启会只有鼠标的地方刷新,很奇怪
}
}
if(needSResize){
this.emitResizeMsg( { viewport:view} )
}
//needSResize && this.emitResizeMsg({resolution: params_.target ? new THREE.Vector2(width,height) : view.resolution2, left:view.left, bottom:view.bottom })//resize everything such as lines targets
viewer.dispatchEvent({type: "render.begin", viewer: viewer, viewport:view, params });
if(view.render){
view.render({
target: params_.target, renderer:this.renderer, clear:this.clear.bind(this),
renderOverlay: this.renderOverlay.bind(this), force:!view.noPointcloud //如果要渲染点云,必须也一直渲染地图,否则地图会被覆盖(点云目前未能获取是否改变,也可能有其他动态物体,所以还是一直渲染的好)
})
}
if(!view.noPointcloud ){
//if(!params.target){
params.width = width; params.height = height;
//}
if(view.render){
params.noBG = true
}
view.beforeRender && view.beforeRender(view)
this.updateViewPointcloud(params.camera, new THREE.Vector2(params.width,params.height), true)
params.background = view.background
params.backgroundColor = view.backgroundColor
params.backgroundOpacity = view.backgroundOpacity
view.render || this.clear(params)
pRenderer.clearTargets(params);
pRenderer.render(params);
this.renderOverlay(params)
}
this.dispatchEvent({type: "render.end", viewer: this, viewport:view });
})
this.renderer.setRenderTarget(null)
}
renderOverlay(params){
let camera = params.camera ? params.camera : this.scene.getActiveCamera();
if(!params.magnifier){//为什么要在点云之后渲染,否则透明失效 、 会被点云覆盖
this.setCameraLayers(camera, ['sceneObjects','marker','reticule','route'], params.extraEnableLayers) //透明贴图层 skybox 、reticule marker 不能遮住测量线
this.renderer.render(this.scene.scene, camera);
}
this.dispatchEvent({type: "render.pass.scene", viewer: viewer});
//清除深度 !!!!
this.renderer.clearDepth();
this.transformationTool.update();
if(!params.magnifier){
//测量线
this.dispatchEvent({type: "render.pass.perspective_overlay", camera});
if(!params.screenshot && !params.isMap){
this.setCameraLayers(camera, ['magnifier']) //magnifier 遮住测量线
this.renderer.render(this.scene.scene, camera);
}
}
this.setCameraLayers(camera, ['volume','transformationTool'])
this.renderer.render(this.clippingTool.sceneVolume, camera);
this.renderer.render(this.transformationTool.scene, camera);
}
setCameraLayers(camera, enableLayers, extraEnableLayers=[]){//add
camera.layers.disableAll()
enableLayers.concat(extraEnableLayers).forEach(e=>{
let layer = Potree.config.renderLayers[e]
if(layer == void 0){
console.error('setCameraLayer没找到layer!');
return
}
camera.layers.enable(layer)
})
}
setObjectLayers(object, layerName){//add
let layer = Potree.config.renderLayers[layerName]
if(layer == void 0){
console.error('setCameraLayer没找到layer!');
return
}
object.traverse(e=>{
e.layers.set(layer)
})
}
updateVisible(object, reason, ifShow){//当所有加入的条件都不为false时才显示. reason='force'一般是强制、临时的
if(!object.unvisibleReasons) object.unvisibleReasons = []; //如果length>0代表不可见
if(ifShow){
var index = object.unvisibleReasons.indexOf(reason)
index > -1 && object.unvisibleReasons.splice(index, 1)
if(object.unvisibleReasons.length == 0){
object.visible = true;
object.dispatchEvent({
type: 'isVisible',
visible:true
})
}
}else{
var visiBefore = object.visible
if(!object.unvisibleReasons.includes(reason)) object.unvisibleReasons.push(reason)
object.visible = false
visiBefore && object.dispatchEvent({
type: 'isVisible',
visible:false,
reason,
})
}
}
getObjVisiByReason(object,reason){//获取在某条件下是否可见. 注: 用户在数据集选择可不可见为"datasetSelection"
if(object.visible)return true
else{
return !object.unvisibleReasons || !object.unvisibleReasons.includes(reason)
}
}
render(params){//add params
if(Potree.measureTimings) performance.mark("render-start");
//try{
const vrActive = this.renderer.xr.isPresenting;
if(vrActive){
this.renderVR();
}else{
this.renderDefault(params);
}
/* }catch(e){
this.onCrash(e);
} */
if(Potree.measureTimings){
performance.mark("render-end");
performance.measure("render", "render-start", "render-end");
}
}
startScreenshot(info={}, width=800, height=400, compressRatio){//add
//抗锯齿待加 1 post处理 2截图大张再抗锯齿缩小
let deferred = $.Deferred();
var screenshot = ()=>{
var { buffer } = this.makeScreenshot( new THREE.Vector2(width,height) );
var dataUrl = Potree.Utils.pixelsArrayToDataUrl(buffer, width, height, compressRatio)
if(!Potree.settings.isOfficial){
Common.downloadFile(dataUrl, 'screenshot.jpg')
}
deferred.resolve(dataUrl)
{//恢复:
if(info.type == 'measure'){
this.scene.measurements.forEach(e=>this.updateVisible(e, 'screenshot',true))
}
this.images360.panos.forEach(pano=>{
viewer.updateVisible(pano, 'screenshot', true)
})
viewer.updateVisible(this.reticule, 'screenshot', true)
viewer.updateVisible(this.mapViewer.cursor, 'screenshot', true)
if(oldStates.attachedToViewer != this.mapViewer.attachedToViewer){
if(info.type == 'measure'){
this.mapViewer.attachToMainViewer(false)
}
}
mapViewport.camera.zoom = oldStates.mapZoom
oldStates.viewports.forEach(old=>{//恢复相机
var viewport = [mapViewport, mainViewport].find(v=>v.name == old.name);
viewport.left = old.left;
viewport.width = old.width;
viewport.view.copy(old.view)
viewport.view.applyToCamera(viewport.camera);
})
viewer.updateScreenSize({forceUpdateSize:true})//更新像素
oldStates.viewports.forEach(old=>{//恢复相机
var viewport = [mapViewport, mainViewport].find(v=>v.name == old.name);
this.dispatchEvent({ //update map
type: "camera_changed",
camera: viewport.camera,
viewport : viewport
})
})
}
}
let mapViewport = this.mapViewer.viewports[0]
let mainViewport = this.mainViewport
let oldStates = {
attachedToViewer : this.mapViewer.attachedToViewer,
viewports : [mapViewport, mainViewport].map(e=>{
return e.clone()
}),
mapZoom: mapViewport.camera.zoom
}
//令漫游点不可见
this.images360.panos.forEach(pano=>{
viewer.updateVisible(pano, 'screenshot', false)
})
//令reticule不可见
viewer.updateVisible(this.reticule, 'screenshot', false)
//令mapCursor不可见
viewer.updateVisible(this.mapViewer.cursor, 'screenshot', false)
if(info.type == 'measure'){//要截图双屏
this.scene.measurements.forEach(e=>this.updateVisible(e,'screenshot',e == info.measurement) )
this.mapViewer.attachToMainViewer(true, 'measure', 0.5)
viewer.updateScreenSize({forceUpdateSize:true, width, height}) //更新viewports相机透视
//不同角度截图 得到三维的会不一样,因为focusOnObject是根据方向的
let promise = this.focusOnObject(info.measurement, 'measure', 0, /* {mapDont:true} */)
promise.done(()=>{
this.viewports.forEach(e=>{
e.view.applyToCamera(e.camera)
this.dispatchEvent({ //update map
type: "camera_changed",
camera: e.camera,
viewport : e
})
})
this.mapViewer.waitLoadDone(screenshot.bind(this))//等待地图所有加载完
})
}else{
screenshot()
}
return deferred.promise()
}
focusOnObject(object, type, duration, o={} ) {
//飞向热点、测量线等 。
let deferred = $.Deferred();
let target = new THREE.Vector3,
position = new THREE.Vector3,
dis;
duration = duration == void 0 ? 2000 : duration;
let camera = viewer.scene.getActiveCamera()
if (type == 'measure') {
let bound = new THREE.Box3()
object.points.forEach(p => bound.expandByPoint(p))
let boundSize = bound.getSize(new THREE.Vector3)
let {center,radius} = bound.getBoundingSphere({center:new THREE.Vector3}) //用不box的算不准,就保守用sphere了,缺点是无法占满屏,优点是不会靠太近
target.copy(object.getCenter())//copy(center)
let aspect = 1
if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定
dis = radius/ THREE.Math.degToRad(camera.fov / 2)
}else{
let hfov = cameraLight.getHFOVForCamera(camera, camera.aspect, 1 );
dis = radius / THREE.Math.degToRad(hfov / 2)
}
if(o.mapDont || this.mapViewer.attachedToViewer){
this.mapViewer.fitToBound(target.clone(), boundSize.clone().multiplyScalar(2), duration)
}
if(Potree.settings.displayMode == 'showPointCloud'){
let dir = new THREE.Vector3().subVectors(camera.position, target).normalize()
position.copy(target).add(dir.multiplyScalar(dis))
}else if(Potree.settings.displayMode == 'showPanos'){
let pano = viewer.images360.fitPanoTowardPoint({
point : target,
bestDistance : dis * 0.5 //乘以小数是为了尽量靠近
})
pano && viewer.images360.flyToPano({pano, target, duration, deferred })
return deferred
}
} else if (type == 'tag') {
const bestDistance = 2
target.copy(object.position)
if(Potree.settings.displayMode == 'showPointCloud'){
dis = bestDistance
let dir = new THREE.Vector3().subVectors(camera.position, target).normalize()
position.copy(target).add(dir.multiplyScalar(dis))
}else if(Potree.settings.displayMode == 'showPanos'){
let pano = viewer.images360.fitPanoTowardPoint({
point : target,
bestDistance //越近越好,但不要太近,bestDistance左右差不多
})
pano && viewer.images360.flyToPano({pano, target, duration, deferred })
return deferred
}
}
viewer.scene.view.setView(position, target, duration, ()=>{
deferred.resolve()
})
return deferred.promise()
}
resolveTimings(timestamp){
if(Potree.measureTimings){
if(!this.toggle){
this.toggle = timestamp;
}
let duration = timestamp - this.toggle;
if(duration > 1000.0){
let measures = performance.getEntriesByType("measure");
let names = new Set();
for(let measure of measures){
names.add(measure.name);
}
let groups = new Map();
for(let name of names){
groups.set(name, {
measures: [],
sum: 0,
n: 0,
min: Infinity,
max: -Infinity
});
}
for(let measure of measures){
let group = groups.get(measure.name);
group.measures.push(measure);
group.sum += measure.duration;
group.n++;
group.min = Math.min(group.min, measure.duration);
group.max = Math.max(group.max, measure.duration);
}
let glQueries = Potree.resolveQueries(this.renderer.getContext());
for(let [key, value] of glQueries){
let group = {
measures: value.map(v => {return {duration: v}}),
sum: value.reduce( (a, i) => a + i, 0),
n: value.length,
min: Math.min(...value),
max: Math.max(...value)
};
let groupname = `[tq] ${key}`;
groups.set(groupname, group);
names.add(groupname);
}
for(let [name, group] of groups){
group.mean = group.sum / group.n;
group.measures.sort( (a, b) => a.duration - b.duration );
if(group.n === 1){
group.median = group.measures[0].duration;
}else if(group.n > 1){
group.median = group.measures[parseInt(group.n / 2)].duration;
}
}
let cn = Array.from(names).reduce( (a, i) => Math.max(a, i.length), 0) + 5;
let cmin = 10;
let cmed = 10;
let cmax = 10;
let csam = 6;
let message = ` ${"NAME".padEnd(cn)} |`
+ ` ${"MIN".padStart(cmin)} |`
+ ` ${"MEDIAN".padStart(cmed)} |`
+ ` ${"MAX".padStart(cmax)} |`
+ ` ${"SAMPLES".padStart(csam)} \n`;
message += ` ${"-".repeat(message.length) }\n`;
names = Array.from(names).sort();
for(let name of names){
let group = groups.get(name);
let min = group.min.toFixed(3);
let median = group.median.toFixed(3);
let max = group.max.toFixed(3);
let n = group.n;
message += ` ${name.padEnd(cn)} |`
+ ` ${min.padStart(cmin)} |`
+ ` ${median.padStart(cmed)} |`
+ ` ${max.padStart(cmax)} |`
+ ` ${n.toString().padStart(csam)}\n`;
}
message += `\n`;
console.log(message);
performance.clearMarks();
performance.clearMeasures();
this.toggle = timestamp;
}
}
}
loop(timestamp){
if(this.stats){
this.stats.begin();
}
if(Potree.measureTimings){
performance.mark("loop-start");
}
this.update(this.clock.getDelta(), timestamp);
this.magnifier.render();
this.render();
// let vrActive = viewer.renderer.xr.isPresenting;
// if(vrActive){
// this.update(this.clock.getDelta(), timestamp);
// this.render();
// }else{
// this.update(this.clock.getDelta(), timestamp);
// this.render();
// }
if(Potree.measureTimings){
performance.mark("loop-end");
performance.measure("loop", "loop-start", "loop-end");
}
this.resolveTimings(timestamp);
Potree.framenumber++;
if(this.stats){
this.stats.end();
}
}
postError(content, params = {}){
let message = this.postMessage(content, params);
message.element.addClass("potree_message_error");
return message;
}
postMessage(content, params = {}){
let message = new Message(content);
let animationDuration = 100;
message.element.css("display", "none");
message.elClose.click( () => {
message.element.slideToggle(animationDuration);
let index = this.messages.indexOf(message);
if(index >= 0){
this.messages.splice(index, 1);
}
});
this.elMessages.prepend(message.element);
message.element.slideToggle(animationDuration);
this.messages.push(message);
if(params.duration !== undefined){
let fadeDuration = 500;
let slideOutDuration = 200;
setTimeout(() => {
message.element.animate({
opacity: 0
}, fadeDuration);
message.element.slideToggle(slideOutDuration);
}, params.duration)
}
return message;
}
getBoundingBox (pointclouds) {
//可以直接返回viewer.bound
if(!this.bound){
this.updateModelBound()
}
return this.bound.boundingBox.clone()//this.scene.getBoundingBox(pointclouds);
};
updateModelBound(){
this.bound = Utils.computePointcloudsBound(this.scene.pointclouds)
viewer.farWhenShowPano = this.bound.boundSize.length() * 2//全景漫游时要能看到整个skybox
}
waitForLoad(object, isLoadedCallback){//等待加载时显示loading。主要是贴图
this.waitQueue.push({
object,
isLoadedCallback,
})
1 === this.waitQueue.length && this.emit("loading", true)
}
ifAllLoaded(object){
if(this.waitQueue.length>0){
this.waitQueue = this.waitQueue.filter(function(e) {
return !e.isLoadedCallback()
})
}
0 === this.waitQueue.length && this.emit("loading", false)
}
setView(o={}){
let callback = ()=>{
if(o.displayMode){
Potree.settings.displayMode = o.displayMode
}
o.callback && o.callback()
}
if(o.pano != void 0){//pano 权重高于 position
this.images360.flyToPano(o)
}else{
this.scene.view.setView(o.position, o.target, o.duration, callback)
}
}
};
/* t.prototype.getTransformationMatrix = function(t) {//点云截取 所有点在乘上这个矩阵后, 还能落在 1 * 1 * 1的box内的点就是所裁剪的
var e = this.ViewService.mainView.getVolumeClippingLayer().getBoxFrame()
, n = (new o.Matrix4).getInverse(e.matrixWorld)
, i = t.m2w_;
return (new o.Matrix4).multiplyMatrices(n, i).transpose() */