|
|
@@ -1,6 +1,6 @@
|
|
|
|
|
|
|
|
|
-import * as GaussianSplats3D from "../../../libs/gaussian/gaussian-splats-3d.module.js";
|
|
|
+//import * as GaussianSplats3D from "../../../libs/gaussian/gaussian-splats-3d.module.js";
|
|
|
|
|
|
import {Loader3DTiles} from '../../../libs/three.js/3dtiles/three-loader-3dtiles.esm.js';
|
|
|
import {OBJLoader} from "../../../libs/three.js/loaders/OBJLoader.js";
|
|
|
@@ -93,12 +93,15 @@ import BasicMaterial from '../materials/BasicMaterial.js'
|
|
|
import {FirstPersonControls} from "../../navigation/FirstPersonControlsNew.js";
|
|
|
import {OrbitControls} from "../../navigation/OrbitControlsNew.js";
|
|
|
//import {VRControls} from "../../navigation/VRControlsNew.js";
|
|
|
-
|
|
|
+import { CharacterControl} from "../../navigation/CharacterControl.js";
|
|
|
|
|
|
import { ClassificationScheme } from "../../materials/ClassificationScheme.js";
|
|
|
import { VRButton } from '../../../libs/three.js/extra/VRButton.js';
|
|
|
import DxfLoader from '../../loader/DxfLoader.js'
|
|
|
|
|
|
+
|
|
|
+import JoyStick from '../../navigation/JoyStick'
|
|
|
+
|
|
|
//import {Splat} from '../objects/3dgs/Splat.js'
|
|
|
|
|
|
|
|
|
@@ -128,9 +131,11 @@ THREE.Cache.enabled = true //这样不会重复网络请求相同的图(如热
|
|
|
export class Viewer extends ViewerBase{
|
|
|
|
|
|
constructor(domElement, mapArea_, args = {}){
|
|
|
- super(domElement, $.extend(args,{name:'mainViewer', antialias: !browser.urlHasValue('noAA'), preserveDrawingBuffer:true}));
|
|
|
+ super(domElement, $.extend(args,{name:'mainViewer', antialias: !Potree.settings.noAA, preserveDrawingBuffer:true}));
|
|
|
//注:viewer因为要分屏,尤其是四屏,preserveDrawingBuffer需要为true, 否则无法局部clear
|
|
|
|
|
|
+
|
|
|
+
|
|
|
window.viewer = this
|
|
|
mapArea = mapArea_
|
|
|
|
|
|
@@ -149,7 +154,7 @@ export class Viewer extends ViewerBase{
|
|
|
if(this.renderer.capabilities.isWebGL2){
|
|
|
Potree.settings.isWebgl2 = true //是否启用webgl2
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
if(Potree.settings.editType == "pano" || Potree.settings.editType == "merge" ){
|
|
|
this.modules = {
|
|
|
@@ -358,7 +363,11 @@ export class Viewer extends ViewerBase{
|
|
|
try{
|
|
|
if(e.event.ctrlKey){
|
|
|
if(e.event.key.toLowerCase() == 'c'){
|
|
|
- let info = JSON.stringify( this.mainViewport.view.getJson() );
|
|
|
+ let info = this.mainViewport.view.getJson()
|
|
|
+ if(this.controls instanceof CharacterControl){
|
|
|
+ info.modelPos = this.controls.modelPos.toObject()
|
|
|
+ }
|
|
|
+ info = JSON.stringify( info );
|
|
|
console.log(`Copy view params: ${info}`),
|
|
|
navigator.clipboard.writeText(info) //need https
|
|
|
|
|
|
@@ -367,6 +376,14 @@ export class Viewer extends ViewerBase{
|
|
|
let info = JSON.parse(A);
|
|
|
if(info.yaw != void 0){
|
|
|
this.mainViewport.view.applyJson(info)
|
|
|
+ if(this.controls instanceof CharacterControl){
|
|
|
+ if(info.modelPos){
|
|
|
+ this.controls.modelPos.copy(info.modelPos)
|
|
|
+ }else{
|
|
|
+ this.controls.modelPos.copy(this.mainViewport.view.position)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
console.log(`pasteViewParams ${A}`)
|
|
|
}
|
|
|
})
|
|
|
@@ -446,9 +463,10 @@ export class Viewer extends ViewerBase{
|
|
|
this.initDragAndDrop();
|
|
|
}
|
|
|
|
|
|
- if(Potree.settings.isTest){
|
|
|
+ if(Potree.settings.isTest || Potree.settings.showStats ){
|
|
|
this.stats = new Stats();
|
|
|
- this.stats.showPanel( 2 ); // 0: fps, 1: ms, 2: mb, 3+: custom
|
|
|
+ console.log('state created')
|
|
|
+ //this.stats.showPanel( 2 ); // 0: fps, 1: ms, 2: mb, 3+: custom
|
|
|
document.body.appendChild( this.stats.dom );
|
|
|
}
|
|
|
|
|
|
@@ -600,7 +618,7 @@ export class Viewer extends ViewerBase{
|
|
|
|
|
|
|
|
|
|
|
|
- this.createControls();
|
|
|
+ this.createControls(args.enableJoy);
|
|
|
|
|
|
this.clippingTool.setScene(this.scene);
|
|
|
|
|
|
@@ -785,8 +803,20 @@ export class Viewer extends ViewerBase{
|
|
|
})
|
|
|
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+ {
|
|
|
+ let maxPixelRatio_ = Potree.settings.maxPixelRatio
|
|
|
+ Object.defineProperty(Potree.settings , "maxPixelRatio",{
|
|
|
+ get: function() {
|
|
|
+ return maxPixelRatio_
|
|
|
+ },
|
|
|
+ set: (max)=>{
|
|
|
+ if(maxPixelRatio_ != max){
|
|
|
+ maxPixelRatio_ = max
|
|
|
+ this.updateScreenSize({forceUpdateSize:true})
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
/* this.reticule.addEventListener('update',(e)=>{
|
|
|
this.needRender = true
|
|
|
if(this.mapViewer && this.mapViewer.attachedToViewer)this.mapViewer.needRender = true //分屏时mapViewer也有reticule
|
|
|
@@ -1085,7 +1115,11 @@ export class Viewer extends ViewerBase{
|
|
|
object.traverse((mesh, o={})=>{
|
|
|
//let visi = o.visible === false ? false : mesh.visible //mesh.realVisible() //如果祖先不可见,此mesh一定不可见
|
|
|
if(mesh.geometry){
|
|
|
- posCount += mesh.geometry.attributes.position.count
|
|
|
+ if(mesh.geometry instanceof THREE.BufferGeometry ){
|
|
|
+ posCount += mesh.geometry.attributes.position.count
|
|
|
+ }else{
|
|
|
+ posCount += mesh.geometry.vertices.length
|
|
|
+ }
|
|
|
//visi && (visiPosCount += mesh.geometry.attributes.position.count)
|
|
|
}
|
|
|
/* if(mesh.material && !mesh.material.map){
|
|
|
@@ -1768,13 +1802,14 @@ export class Viewer extends ViewerBase{
|
|
|
if (this.controls) {
|
|
|
this.controls.setEnable(false)
|
|
|
//this.inputHandler.removeInputListener(this.controls);
|
|
|
- this.controls.moveSpeed = this.moveSpeed; //记录 (因为orbit的radius很大,转为firstPerson时要缩小)
|
|
|
+ //this.controls.moveSpeed = this.getMoveSpeed()//this.moveSpeed; //记录???? (因为orbit的radius很大,转为firstPerson时要缩小)
|
|
|
}
|
|
|
this.controls = controls;
|
|
|
- controls.moveSpeed && this.setMoveSpeed(controls.moveSpeed) //add
|
|
|
+ controls.moveSpeed && this.setMoveSpeed(controls.moveSpeed) //add
|
|
|
|
|
|
this.controls.setEnable(true)
|
|
|
|
|
|
+ this.dispatchEvent('setControls')
|
|
|
//this.inputHandler.addInputListener(this.controls);
|
|
|
}
|
|
|
}
|
|
|
@@ -2458,7 +2493,7 @@ export class Viewer extends ViewerBase{
|
|
|
// Viewer Internals
|
|
|
// ------------------------------------------------------------------------------------
|
|
|
|
|
|
- createControls () {
|
|
|
+ createControls (enableJoy) {
|
|
|
{ // create FIRST PERSON CONTROLS
|
|
|
this.fpControls = new FirstPersonControls(this, this.mainViewport);
|
|
|
this.fpControls.enabled = false;
|
|
|
@@ -2473,6 +2508,14 @@ export class Viewer extends ViewerBase{
|
|
|
}) */
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ {
|
|
|
+ this.characterCtrl = new CharacterControl(this );
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
|
|
|
// { // create GEO CONTROLS
|
|
|
// this.geoControls = new GeoControls(this.scene.camera, this.renderer.domElement);
|
|
|
@@ -2490,6 +2533,13 @@ export class Viewer extends ViewerBase{
|
|
|
this.orbitControls.addEventListener('start', this.disableAnnotations.bind(this));
|
|
|
this.orbitControls.addEventListener('end', this.enableAnnotations.bind(this));
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ if(enableJoy){
|
|
|
+ this.joyStick = new JoyStick(this.renderArea.parentElement, [this.fpControls, this.characterCtrl])
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
/* { // create EARTH CONTROLS
|
|
|
this.earthControls = new EarthControls(this);
|
|
|
@@ -2993,7 +3043,12 @@ export class Viewer extends ViewerBase{
|
|
|
this.scene.cameraO.position.copy(scene.view.position);
|
|
|
} else if (controls !== null) {
|
|
|
controls.setScene(scene);
|
|
|
- controls.update(delta);
|
|
|
+
|
|
|
+
|
|
|
+ let count = viewer.collider && window.multiUpdate ? 6 : 1
|
|
|
+ for (let s = 0; s < count; ++s)
|
|
|
+ controls.update(delta/count);
|
|
|
+
|
|
|
|
|
|
//更新camera
|
|
|
this.viewports.forEach(viewport=>{
|
|
|
@@ -3490,11 +3545,11 @@ export class Viewer extends ViewerBase{
|
|
|
}))) continue//return
|
|
|
}else{
|
|
|
this.clear(params)
|
|
|
- pRenderer.clearTargets(params);
|
|
|
-
|
|
|
-
|
|
|
+ pRenderer.clearTargets(params);
|
|
|
this.renderBG(viewport)
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+ viewer.dispatchEvent({type: "render.begin3", viewer: viewer, viewport, params });
|
|
|
if(this.splatMesh){
|
|
|
Potree.Utils.setCameraLayers(params.camera, ['model'])
|
|
|
this.renderer.render(this.splatMesh, params.camera)
|
|
|
@@ -3705,6 +3760,7 @@ export class Viewer extends ViewerBase{
|
|
|
let renderer = params.renderer || this.renderer
|
|
|
let camera = params.camera ? params.camera : this.scene.getActiveCamera();
|
|
|
//清除深度 !!!!
|
|
|
+ this.dispatchEvent({type: "render.beforeClearDepth", viewer: this, viewport:params.viewport });
|
|
|
renderer.clearDepth();
|
|
|
|
|
|
if(!params.magnifier){
|
|
|
@@ -5164,16 +5220,19 @@ export class Viewer extends ViewerBase{
|
|
|
{
|
|
|
let hasAnimation
|
|
|
window.pauseAni || this.objs.children.forEach(model=>{
|
|
|
- if(model.mixer && model.mixer._nActiveActions){
|
|
|
+ //model.traverse(object=>object.isSkinnedMesh && object.computeBoundingSphere())
|
|
|
+
|
|
|
+ if(model.visible && model.mixer && (model.clipChanged || model.actions.some(a=>a._mixer._isActiveAction( a ) && !a.paused ))){ //播放中或者动作状态改变
|
|
|
hasAnimation = true
|
|
|
+ model.clipChanged = false
|
|
|
model.mixer.update(deltaTime)
|
|
|
+ //console.log('mixer update', model.name)
|
|
|
}
|
|
|
}) //以后有空的话用frust判断是否在画面内,不在的话即使有动画也不要 update 和 render, 如果paused的话是不是也可以不update
|
|
|
hasAnimation && (this.dispatchEvent('content_changed'))
|
|
|
}
|
|
|
|
|
|
|
|
|
-
|
|
|
// let vrActive = viewer.renderer.xr.isPresenting;
|
|
|
// if(vrActive){
|
|
|
// this.update(this.clock.getDelta(), timestamp);
|
|
|
@@ -5654,10 +5713,11 @@ export class Viewer extends ViewerBase{
|
|
|
getObjLoader(){
|
|
|
let loader = loaders.objLoaders.find(e=>!e.inUse)
|
|
|
if(!loader){
|
|
|
- loader = new OBJLoader( this.fileManager )
|
|
|
- loader.inUse = true
|
|
|
+ loader = new OBJLoader( this.fileManager )
|
|
|
+
|
|
|
loaders.objLoaders.push(loader)
|
|
|
}
|
|
|
+ loader.inUse = true
|
|
|
return loader
|
|
|
}
|
|
|
|
|
|
@@ -5782,6 +5842,9 @@ export class Viewer extends ViewerBase{
|
|
|
//获取在scale为1时,表现出的大小
|
|
|
boundingBox.union(boundingBox_.clone().applyMatrix4(child.matrixWorld)) //但感觉如果最外层object大小不为1,要还原下scale再乘
|
|
|
|
|
|
+ if(child instanceof THREE.SkinnedMesh){
|
|
|
+ child.boundingBox = null //delete 动画会导致bound改变, raycast干脆不用boundingBox了,否则之后要实时重计算
|
|
|
+ }
|
|
|
|
|
|
let changeMat = (oldMat)=>{
|
|
|
let mat = oldMat
|
|
|
@@ -5807,7 +5870,10 @@ export class Viewer extends ViewerBase{
|
|
|
}
|
|
|
|
|
|
|
|
|
+ if(fileInfo_.prop?.is4dkkModel){
|
|
|
|
|
|
+ child.material.color.set(1,1,1); //看到有obj不是白色
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
} );
|
|
|
@@ -5942,25 +6008,9 @@ export class Viewer extends ViewerBase{
|
|
|
loaders.glbLoader.unlitMat = true//!!fileInfo.unlit
|
|
|
loaders.glbLoader.load(fileInfo.url, ( gltf, total )=>{
|
|
|
console.log('loadGLTF', gltf, 'aniCount:',gltf.animations.length)
|
|
|
- let model = gltf.scene
|
|
|
- if(gltf.animations.length){
|
|
|
- let skeleton = new THREE.SkeletonHelper( model );
|
|
|
- //skeleton.visible = false;
|
|
|
- viewer.scene.scene.add(skeleton)
|
|
|
- model.skeletonHelper = skeleton //注意:不能覆盖model.skeleton,因其另有 */
|
|
|
- skeleton.material.opacity = 0.1
|
|
|
- let mixer = new THREE.AnimationMixer( model);
|
|
|
- model.actions = []
|
|
|
- gltf.animations.forEach(ani=>{
|
|
|
- if(ani.tracks.filter(e=>e instanceof THREE.QuaternionKeyframeTrack).length > 1){ //>一帧的
|
|
|
- model.actions.push(mixer.clipAction( ani ));
|
|
|
- }
|
|
|
- })
|
|
|
- model.mixer = mixer
|
|
|
- }
|
|
|
- model.gltf = gltf
|
|
|
-
|
|
|
-
|
|
|
+ let model = gltf.scene
|
|
|
+ model.gltf = gltf
|
|
|
+ this.gltfAddAnimation(model)
|
|
|
loadDone(model)
|
|
|
}, onProgress, onError)
|
|
|
|
|
|
@@ -5991,8 +6041,9 @@ export class Viewer extends ViewerBase{
|
|
|
//maximumMemoryUsage: 100, //缓存大小,见tiles3DMaxMemory。单位M(但实际结果是 2.5*maximumMemoryUsage + 750 。超过2G会崩, 所以应该小于540) 若太小,密集的tile反复加载很卡. (任务管理器刷新网页后若内存不掉就要结束进程否则虚高)
|
|
|
debug: browser.urlHasValue('tilesBox'), //show box
|
|
|
parent: this.scene.scene,
|
|
|
- is4dkk: fileInfo.is4dkk,//是否是4dkk中的模型. 通常maximumScreenSpaceError需要10
|
|
|
+ is4dkkModel: fileInfo.is4dkkModel, //是否是4dkk中的模型. 通常maximumScreenSpaceError需要10
|
|
|
updateTime: fileInfo.updateTime, //加后缀防止缓存
|
|
|
+ //cesiumIONToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5OTc4MTFiYS1hYzhlLTQ3ZjYtYWJmMi1hODMwMWMxZGRiYTQiLCJpZCI6ODU1NDksImlhdCI6MTY1Mzc5NDc5N30.ldTi8bF3XvSOgnZrMITokRW4kE3i8Mwbarhk5OQbPsI',
|
|
|
},
|
|
|
})
|
|
|
//console.log(result)
|
|
|
@@ -6013,7 +6064,7 @@ export class Viewer extends ViewerBase{
|
|
|
if(master.panos) viewer.images360.judgeModelMat(e.tileContent)
|
|
|
//set Layers ?
|
|
|
Potree.Utils.setObjectLayers(e.tileContent, 'model')
|
|
|
-
|
|
|
+ fileInfo.side && e.tileContent.traverse(e=>e.material && (e.material.side = fileInfo.side))//新软件导出的带坐标的box型模型要反面才看的到,干脆双面
|
|
|
})
|
|
|
|
|
|
{
|
|
|
@@ -6029,10 +6080,10 @@ export class Viewer extends ViewerBase{
|
|
|
})
|
|
|
}
|
|
|
let v = true
|
|
|
- result.model.visiChangeCallback = ()=>{
|
|
|
+ result.model.visiChangeCallback = (force)=>{
|
|
|
let visi = result.model.realVisible()
|
|
|
tileset.visible = visi; //同步,使不加载
|
|
|
- if(v != visi){
|
|
|
+ if(force || v != visi){
|
|
|
tileset.nextForceUpdate = true
|
|
|
v = visi
|
|
|
}
|
|
|
@@ -6149,6 +6200,39 @@ export class Viewer extends ViewerBase{
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ gltfAddAnimation(model){
|
|
|
+
|
|
|
+ if(model.gltf?.animations.length){
|
|
|
+ let skeleton = new THREE.SkeletonHelper( model );
|
|
|
+
|
|
|
+ viewer.scene.scene.add(skeleton)
|
|
|
+ model.skeletonHelper = skeleton //注意:不能覆盖model.skeleton,因其另有 */
|
|
|
+ //if(Potree.settings.isOfficial){
|
|
|
+ Potree.Utils.updateVisible(skeleton,'hide',false)
|
|
|
+ /* }else{
|
|
|
+ skeleton.material.opacity = 0.1
|
|
|
+ skeleton.bones.forEach((bone,i)=>{
|
|
|
+ let label = new Potree.TextSprite({
|
|
|
+ text:`${i} : ${bone.name}`, sizeInfo:{width2d:130}, fontWeight:'', fontsize:20,
|
|
|
+ backgroundColor:{r:255,g:255,b:255,a:0}, textColor:{r:255,g:255,b:255,a:1},
|
|
|
+ })
|
|
|
+ label.sprite.material.depthTest = false
|
|
|
+ bone.add(label)
|
|
|
+ })
|
|
|
+ } */
|
|
|
+ let mixer = new THREE.AnimationMixer( model);
|
|
|
+ model.actions = []
|
|
|
+ model.gltf.animations.forEach(ani=>{
|
|
|
+ if(ani.tracks.filter(e=>e instanceof THREE.QuaternionKeyframeTrack).length > 1){ //>一帧的
|
|
|
+ model.actions.push(mixer.clipAction( ani ));
|
|
|
+ }
|
|
|
+ })
|
|
|
+ model.mixer = mixer
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
|
|
|
setAllTilesets(){//让所有tileset执行fun。 objs里每个model最多两层tileset
|
|
|
@@ -6528,8 +6612,8 @@ export class Viewer extends ViewerBase{
|
|
|
"pitch": -1,
|
|
|
"videoType": 1
|
|
|
}
|
|
|
- let monitor = new Monitor(data)
|
|
|
- model.add(monitor)
|
|
|
+ let monitor = new Monitor(data, model)
|
|
|
+
|
|
|
this.scene.monitors.push(monitor)
|
|
|
this.dispatchEvent('content_changed')
|
|
|
}
|