MergeEditor.js 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. import * as THREE from "../../../../libs/three.js/build/three.module.js";
  2. import cameraLight from '../../utils/cameraLight.js'
  3. import math from "../../utils/math.js"
  4. import Common from '../../utils/Common.js'
  5. import {LineDraw, MeshDraw} from "../../utils/DrawUtil.js";
  6. import {transitions, easing, lerp} from '../../utils/transitions.js'
  7. import SplitScreen from "../../utils/SplitScreen.js";
  8. import InfiniteGridHelper from '../../objects/InfiniteGridHelper.js'
  9. import Compass from "../../objects/tool/Compass.js";
  10. import {TransformControls} from "../../objects/tool/TransformControls.js";
  11. import History from "../../utils/History.js"
  12. import {Box3Helper} from "../../../utils/Box3Helper.js";
  13. import {TextSprite} from '../../objects/TextSprite.js'
  14. const texLoader = new THREE.TextureLoader()
  15. texLoader.crossOrigin = "anonymous"
  16. const edgeStrengths = {
  17. pointcloud: 4,
  18. glb: 100
  19. }
  20. const viewportProps = [{
  21. left:0,
  22. bottom:0,
  23. width: 0.5,height:1,
  24. name : 'top',
  25. axis:["x","y"],
  26. direction : new THREE.Vector3(0,0,-1), //镜头朝向
  27. active: true,
  28. //相机位置在z轴正向
  29. limitBound: new THREE.Box3(new THREE.Vector3(-Infinity,-Infinity, 1),new THREE.Vector3(Infinity,Infinity,5000)), //在地面以上
  30. margin:{x:50, y:150} ,
  31. },
  32. {
  33. left:0.5,
  34. bottom:0,
  35. width: 0.5,height:1,
  36. name : 'right',
  37. axis:["y","z"],
  38. direction : new THREE.Vector3(1,0,0),
  39. active: true,
  40. //相机位置在x轴负向 右下角屏
  41. viewContainsPoints:[new THREE.Vector3(0,0,0)],
  42. margin:{x:300, y:250} ,
  43. } ]
  44. let cylinderSkyGeo, oldSkyGeo
  45. let MergeEditor = {
  46. bus:new THREE.EventDispatcher(),
  47. SplitScreen : new SplitScreen(),
  48. init(){
  49. this.boxHelper = new Box3Helper(new THREE.Box3(new THREE.Vector3(-0.5,-0.5,-0.5), new THREE.Vector3(0.5,0.5,0.5)));
  50. viewer.scene.scene.add(this.boxHelper)
  51. Potree.Utils.updateVisible(this.boxHelper,'unselect',false)
  52. this.lastMemoryState = {}
  53. this.history = new History({
  54. applyData: (data)=>{
  55. if(data.object.parent /* && data.object == this.selected */){
  56. data = Potree.Common.CloneObject(data) //避免使用后更改数据又被使用
  57. data.matrix.decompose( data.object.position, data.object.quaternion, data.object.scale );
  58. data.object.boundCenter.copy(data.boundCenter)
  59. data.object.dispatchEvent('changeByHistory')
  60. data.object.dispatchEvent('transformChanged')
  61. viewer.dispatchEvent('content_changed')
  62. return true
  63. }
  64. },
  65. getData:(object)=>{
  66. return {
  67. object,
  68. matrix: object.matrixWorld.clone(),
  69. boundCenter: object.boundCenter.clone()
  70. }
  71. }
  72. })
  73. {
  74. Potree.settings.notAdditiveBlending = true
  75. let ground = this.ground = new InfiniteGridHelper(1, 10000, new THREE.Color('#eee'), 10000, 0.2, 0.3)
  76. viewer.scene.scene.add(ground)
  77. //再加两条线否则在正侧边看不到
  78. let line1 = LineDraw.createLine([new THREE.Vector3(-10000, 0, 0),new THREE.Vector3(10000, 0, 0) ], {color:'#aaa', })
  79. let line2 = LineDraw.createLine([new THREE.Vector3(0, -10000, 0),new THREE.Vector3(0, 10000, 0) ], {mat:line1.material})
  80. ground.renderOrder = Potree.config.renderOrders.model + 1//line1.renderOrder + 1 //要比模型低,否则模型透明时效果不对
  81. ground.add(line1)
  82. ground.add(line2)
  83. ground.material.opacity = 0.9 //为了滞后渲染,否则被rt遮住
  84. ground.material.polygonOffset = true //多边形偏移(视觉上没有移动模型位置),防止闪烁
  85. ground.material.polygonOffsetFactor = 100 //多边形偏移因子
  86. ground.material.polygonOffsetUnits = 10 //多边形偏移单位
  87. ground.material.depthWrite = false
  88. //ground.material.depthTest = false
  89. line1.material.polygonOffset = true
  90. line1.material.polygonOffsetFactor = 130
  91. line1.material.polygonOffsetUnits = 10
  92. line1.material.depthWrite = false
  93. //见笔记:透明物体的材质设置
  94. }
  95. let oriEdgeStrength = viewer.outlinePass.edgeStrength
  96. {
  97. this.transformControls = new TransformControls(viewer.mainViewport.camera, viewer.renderArea,{
  98. //dontHideWhenFaceCamera: true,
  99. showRotXYZE : true
  100. });
  101. //this.transformControls.space = 'local'//为了在当前方向上平移
  102. this.transformControls.setSize(1.5)
  103. viewer.scene.scene.add(this.transformControls)
  104. this.transformControls._gizmo.hideAxis = {rotate:['e'] ,scale:['x','y','z' ] }
  105. this.transformControls._gizmo.showAxis = {scale:['XYZY']} //仅显示 等于而非包含
  106. this.transformControls.setRotateMethod(2)
  107. //右屏
  108. this.transformControls2 = new TransformControls(viewer.mainViewport.camera, viewer.renderArea,{
  109. //dontHideWhenFaceCamera: true,
  110. });
  111. this.transformControls2.setSize(1.5)
  112. viewer.scene.scene.add(this.transformControls2)
  113. Potree.Utils.setObjectLayers(this.transformControls2, 'layer2' )
  114. let mouseDown = (e)=>{
  115. viewer.outlinePass.edgeStrength = 0//暂时消失线
  116. }
  117. let mouseUp = (e)=>{
  118. //this.updateEdgeStrength()
  119. viewer.outlinePass.edgeStrength = oriEdgeStrength
  120. }
  121. this.transformControls.addEventListener('mouseDown',mouseDown)
  122. this.transformControls2.addEventListener('mouseDown',mouseDown)
  123. this.transformControls.addEventListener('mouseUp',mouseUp)
  124. this.transformControls2.addEventListener('mouseUp',mouseUp)
  125. this.transformControls.addEventListener('mouseDown', ()=>{ //dragstart
  126. this.history.beforeChange(this.selected)
  127. })
  128. this.transformControls.addEventListener('mouseUp',()=>{
  129. this.history.afterChange(this.selected)
  130. })
  131. }
  132. {
  133. this.secondCompass = new Compass(null)
  134. }
  135. viewer.setControls(viewer.orbitControls)
  136. //viewer.mainViewport.view.fixZWhenPan = true
  137. viewer.orbitControls.constantlyForward = true
  138. viewer.addEventListener('global_single_click',(e)=>{
  139. if(
  140. e.button != THREE.MOUSE.LEFT
  141. || this.noNeedSelection //如模型查看页
  142. || viewer.scene.cameraAnimations.some(c=>c.onUpdate) //正在播放
  143. || e.drag && e.drag.notPressMouse //在加测量线
  144. || viewer.mainViewport.view.isFlying() //有其他校准
  145. || this.split //分屏中
  146. || e.clickElement //触发别的点击事件,如测量时click marker /* && e.clickElement != e.intersect.object */
  147. ){
  148. return
  149. }
  150. if(e.intersect){
  151. let object = e.intersect.object || e.intersect.pointcloud
  152. let objects = this.getAllObjects()
  153. let posInModel = Potree.Utils.datasetPosTransform({ toDataset: true, position: e.intersect.location.clone(), object })
  154. if(objects.includes(object) && this.selected != object){
  155. this.selectModel(object, posInModel)
  156. }else{
  157. //if(!viewer.inputHandler.selection[0]){//正在平移和旋转,不允许取消
  158. if(this.selected == object && this.transformControls.mode == 'translate'){
  159. this.selectModel(object, posInModel) //update click pos
  160. }else{
  161. this.selectModel(null)
  162. }
  163. //}
  164. }
  165. }else{
  166. //if(!viewer.inputHandler.selection[0]){
  167. this.selectModel(null)
  168. //}
  169. }
  170. })
  171. viewer.inputHandler.addEventListener('keydown', (e)=>{
  172. if((e.event.key).toLowerCase() == "h" ){
  173. this.fadeOutlineAuto = !this.fadeOutlineAuto
  174. this.showModelOutline(this.selected,!!this.selected)
  175. }
  176. })
  177. //viewer.fxaaPass.enabled = false//viewer.ssaaRenderPass.enabled = false
  178. viewer.outlinePass.enabled = true
  179. //Potree.settings.intersectWhenHover = false
  180. //Potree.Utils.updateVisible(viewer.reticule, 'force', false)
  181. viewer.composer.scaleRatio = 1
  182. viewer.composer.readTarget = false
  183. viewer.addEventListener('updateModelBound', (e)=>{
  184. if(this.split){
  185. this.SplitScreen.updateCameraOutOfModel(/* this.selected && [this.selected] */)
  186. }
  187. })
  188. {//校准页面拖拽
  189. //左右屏都可以拖拽模型,旋转只能左屏
  190. let dragInfo
  191. let drag = (e)=>{
  192. if(this.split && this.selected && this.transformState && (e.dragViewport.name == 'top' || this.transformState == 'translate') ){
  193. if(e.type == 'global_mousedown' ){ //开始
  194. //if((e.intersect.object || e.intersect.pointcloud) == this.selected){
  195. if(e.intersect.pointclouds.includes(this.selected) || e.intersect.allElements.some(e=>e.object == this.selected)){
  196. dragInfo = {}
  197. //if(this.selected.isPointcloud){
  198. viewer.outlinePass.edgeStrength = 0//暂时消失线
  199. //}
  200. }
  201. }
  202. if(e.type == 'global_drag' && dragInfo ){
  203. if(this.transformState == 'translate'){
  204. let moveVec = Potree.Utils.getOrthoCameraMoveVec(e.drag.pointerDelta, e.dragViewport.camera )//最近一次移动向量
  205. this.selected.position.add(moveVec)
  206. this.selected.dispatchEvent("position_changed")
  207. }else if(this.transformState == 'rotate'){
  208. let vec = new THREE.Vector3().subVectors(e.intersect.orthoIntersect || e.intersect.location, this.selected.boundCenter).setZ(0)
  209. if(dragInfo.lastVec == void 0){//global_mousedown
  210. dragInfo.lastVec = vec
  211. return
  212. }
  213. let angle = math.getAngle(dragInfo.lastVec, vec, 'z')
  214. dragInfo.lastVec = vec
  215. //this.selected.rotation.z += angle //局部
  216. /* object.quaternion.copy( .setFromAxisAngle( new THREE.Vector3(0,0,1), angle ) );
  217. object.quaternion.multiply( quaternionStart ).normalize(); */
  218. let diffQua = new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3(0,0,1), angle )
  219. this.selected.quaternion.premultiply(diffQua) //世界
  220. this.selected.dispatchEvent("rotation_changed")
  221. }
  222. return {stopContinue:true}
  223. }
  224. }
  225. }
  226. viewer.addEventListener('global_mousedown', drag)
  227. viewer.addEventListener('global_drag', drag, {importance:10})
  228. viewer.addEventListener('global_mousemove', (e)=>{
  229. if(this.split && this.transformState && !e.drag && (e.hoverViewport.name == 'top' || this.transformState == 'translate')){
  230. /* if(this.lastHoverViewport != e.hoverViewport){
  231. this.lastHoverViewport = e.hoverViewport
  232. this.transformControls.view = e.hoverViewport.view
  233. this.transformControls.camera = e.hoverViewport.camera
  234. this.transformControls.hideAxis( this.transformState, e.hoverViewport.name == 'top' ? [z] : [x,y]);
  235. } */
  236. let mouseover = e.intersect.pointclouds.includes(this.selected) || e.intersect.allElements.some(e=>e.object == this.selected)
  237. //let mouseover = (e.intersect.object || e.intersect.pointcloud) == this.selected
  238. if(mouseover){
  239. if(this.transformState == 'translate'){
  240. viewer.dispatchEvent({
  241. type : "CursorChange", action : "add", name:"movePointcloud"
  242. })
  243. }else{
  244. viewer.dispatchEvent({
  245. type : "CursorChange", action : "add", name:"rotatePointcloud"
  246. })
  247. }
  248. }else{
  249. this.clearTranCursor()
  250. }
  251. }
  252. })
  253. viewer.addEventListener('global_drop', (e)=>{
  254. dragInfo = null
  255. this.clearTranCursor()
  256. //this.updateEdgeStrength()
  257. viewer.outlinePass.edgeStrength = oriEdgeStrength
  258. })
  259. }
  260. /* viewer.addEventListener('background_changed',()=>{
  261. }) */
  262. viewer.addEventListener('camera_changed',()=>{//其实静止时内存也会变,因为在加载
  263. this.lastMemoryState.history = [] //clear
  264. Common.intervalTool.isWaiting('updateMemoryUsage', ()=>{
  265. return this.updateMemoryUsage()
  266. }, 1000)
  267. })
  268. viewer.addEventListener('setDisplay',this.updateMemoryUsage.bind(this))
  269. },
  270. clearTranCursor(){
  271. viewer.dispatchEvent({
  272. type : "CursorChange", action : "remove", name:"movePointcloud"
  273. })
  274. viewer.dispatchEvent({
  275. type : "CursorChange", action : "remove", name:"rotatePointcloud"
  276. })
  277. },
  278. enterSplit(){
  279. this.split = true
  280. if(this.selected) this.SplitScreen.focusCenter = this.selected.boundCenter //旋转中心。注意 boundCenter不能直接赋值,否则改变后focusCenter也要改
  281. else this.SplitScreen.focusCenter = null
  282. this.SplitScreen.splitStart(viewportProps)
  283. this.beforeSplit = {
  284. pointDensity: Potree.settings.pointDensity,
  285. }
  286. Potree.settings.pointDensity = 'fourViewports' //强制降低点云质量
  287. viewer.setControls(viewer.fpControls)
  288. let rightViewport = viewer.viewports.find(e=>e.name == 'right')
  289. let topViewport = viewer.viewports.find(e=>e.name == 'top')
  290. topViewport.alignment = true
  291. rightViewport.rotateSide = true
  292. rightViewport.skyboxFixPos = true
  293. rightViewport.skyboxMinZoom = 10
  294. rightViewport.skyboxRenderFun = ()=>{// 使cube的一面永远正向镜头。 因侧视图的camera是ortho类型,需要平视mesh才不会拉伸
  295. viewer.skybox.scene.children[0].rotation.copy(rightViewport.camera.rotation)
  296. }
  297. topViewport.skyboxRenderFun = ()=>{
  298. viewer.skybox.scene.children[0].rotation.set(0,0,0)
  299. }
  300. viewer.viewports[1].layersAdd('layer2')
  301. viewer.viewports[0].layersAdd('layer1')
  302. Potree.Utils.setObjectLayers(this.transformControls, 'layer1' )
  303. this.transformControls.view = viewer.viewports[0].view
  304. this.transformControls.camera = viewer.viewports[0].camera
  305. this.transformControls._gizmo.hideAxis = {translate:['z'], rotate:['x','y','z'] }
  306. this.transformControls2.view = viewer.viewports[1].view
  307. this.transformControls2.camera = viewer.viewports[1].camera
  308. this.transformControls2._gizmo.hideAxis = {translate:['x','y'], rotate:['x','y','z'] }
  309. this.secondCompass.changeViewport(viewer.viewports[0])
  310. this.secondCompass.setDomPos()
  311. this.secondCompass.setDisplay(true)
  312. viewer.compass.changeViewport(viewer.viewports[1])
  313. viewer.compass.setDomPos()
  314. //this.changeSkyboxGeo(true)
  315. },
  316. leaveSplit(){
  317. this.split = false
  318. this.SplitScreen.unSplit()
  319. viewer.setControls(viewer.orbitControls)
  320. Potree.settings.pointDensity = this.beforeSplit.pointDensity
  321. /* if(this.selected && this.selected.isPointcloud){
  322. this.showModelOutline(this.selected, true)
  323. this.selected.material.activeAttributeName = "rgba"
  324. } */
  325. this.transformControls.camera = viewer.viewports[0].camera
  326. this.transformControls.view = viewer.viewports[0].view
  327. this.transformControls._gizmo.hideAxis = {rotate:['e']}
  328. Potree.Utils.setObjectLayers(this.transformControls, 'sceneObjects' ) //恢复
  329. viewer.compass.changeViewport(viewer.viewports[0]) //恢复
  330. viewer.compass.setDomPos()
  331. this.secondCompass.setDisplay(false)
  332. },
  333. rotateSideCamera(angle){
  334. this.SplitScreen.rotateSideCamera(viewer.viewports.find(e=>e.name == 'right'), angle)
  335. },
  336. setTransformState(state){//校准时
  337. this.transformState = state
  338. this.clearTranCursor()
  339. },
  340. //---------------------------
  341. getAllObjects(){
  342. return viewer.objs.children.concat(viewer.scene.pointclouds)
  343. },
  344. getModel(id){
  345. let models = this.getAllObjects()
  346. return models.find(e=>e.dataset_id == id)
  347. },
  348. changeModelPointCount(object, type){
  349. if(object.fileType == '3dTiles' || object.isPointcloud) return
  350. let posCount , texArea
  351. if(type == 'add'){
  352. let o = viewer.getObjectPointCount(object)
  353. posCount = o.posCount, texArea = o.texArea
  354. object.posCount = posCount, object.texArea = texArea
  355. }else{
  356. posCount = -object.posCount, texArea = -object.texArea
  357. }
  358. viewer.memoryModelCountInfo.otherPosCount += posCount
  359. viewer.memoryModelCountInfo.otherTexArea += texArea
  360. },
  361. modelAdded(model){
  362. /* model.addEventListener('isVisible',(e)=>{
  363. if(e.reason == "overlinePass")return
  364. //console.log(e)
  365. viewer.addEventListener('update',()=>{ //下一次更新结束后
  366. this.updateMemoryUsage()
  367. },{once:true})
  368. }) */
  369. let weightUpdate = ()=>{
  370. this.changeModelPointCount(model,'add')
  371. this.updateMemoryUsage()
  372. }
  373. if(model.fileType == 'obj'){//要等待贴图都加载完
  374. if(viewer.fileManager.loading){
  375. viewer.addEventListener('managerOnLoad',(e)=>{
  376. weightUpdate()
  377. },{once:true}) //如果onError了咋办,暂时无法定位manager加载的哪个模型的
  378. }
  379. }else{
  380. weightUpdate()
  381. }
  382. },
  383. removeModel(model){
  384. if(this.selected == model) this.selectModel(null)
  385. let dispose = (e)=>{
  386. e.geometry && e.geometry.dispose()
  387. e.material && e.material.dispose()
  388. }
  389. if(model.isPointcloud){
  390. dispose(model)
  391. viewer.scene.removePointCloud(model)
  392. }else{
  393. model.traverse(e=>{
  394. dispose(e)
  395. })
  396. viewer.objs.remove(model)
  397. }
  398. model.panos?.slice().forEach(e=>{
  399. e.dispose()
  400. })
  401. viewer.images360.tileDownloader.setPanoData(viewer.images360.panos, [] );
  402. if(model.fileType == '3dTiles'){
  403. model.traverse(child=>{
  404. if(child.runtime){
  405. child.runtime.getTileset().destroy()
  406. return {stopContinue:true}
  407. }
  408. })
  409. }
  410. this.changeModelPointCount(model,'sub')
  411. this.updateMemoryUsage()
  412. },
  413. selectModel(model, state=true, fitBound, by2d){
  414. if(!model) {
  415. model = this.selected
  416. state = false
  417. }
  418. if(!by2d && model){
  419. model.dispatchEvent({type:'changeSelect', selected : state, clickPos:state})
  420. }
  421. if(state){
  422. if(this.selected){
  423. if(this.selected == model) return
  424. else{
  425. let transToolAttached = !!this.transformControls.object
  426. this.selectModel(this.selected, false, fitBound, by2d)
  427. transToolAttached && this.transformControls.attach(model)
  428. }
  429. }
  430. this.selected = model
  431. MergeEditor.focusOn(model, 500, !!fitBound) //通过在场景里点击模型的话,不focus
  432. this.showModelOutline(model)
  433. //this.updateEdgeStrength()
  434. //console.log('selectModel', model)
  435. }else{
  436. if(this.selected != model)return //model本来就没选中,不需要处理(防止2d先选中新的再取消旧的)
  437. this.showModelOutline(model, false)
  438. this.selected = null
  439. this.transformControls.detach() //viewer.transformObject(null);
  440. //console.log('selectModel', null)
  441. }
  442. },
  443. updateBoxHelper(model){
  444. let size = new THREE.Vector3
  445. model.boundingBox.getSize(size)
  446. size.multiply(model.scale)
  447. this.boxHelper.scale.copy(size)
  448. let center = new THREE.Vector3
  449. model.boundingBox.getCenter(center)
  450. center.applyMatrix4(model.matrixWorld)
  451. //center.add(model.position)
  452. this.boxHelper.position.copy(center)
  453. this.boxHelper.quaternion.copy(model.quaternion)
  454. viewer.dispatchEvent('content_changed')
  455. },
  456. showModelOutline(model, state){
  457. if(Potree.settings.selectShowBox || (model ? model.fileType == '3dgs' : this.boxHelper.visible) ){//高斯很卡
  458. if(state !== false ){
  459. this.updateBoxHelper(model)
  460. Potree.Utils.updateVisible(this.boxHelper,'unselect',true)
  461. }else{
  462. Potree.Utils.updateVisible(this.boxHelper,'unselect',false)
  463. }
  464. return
  465. }
  466. if(this.fadeOutlineAuto){
  467. if(state === false){
  468. viewer.outlinePass.selectedObjects = []
  469. clearTimeout(this.timer)
  470. return
  471. }
  472. viewer.outlinePass.selectedObjects = [model]
  473. if(this.timer){
  474. clearTimeout(this.timer)
  475. }
  476. this.timer = setTimeout(()=>{
  477. viewer.outlinePass.selectedObjects = []
  478. viewer.dispatchEvent('content_changed')
  479. }, 1000)
  480. }else{
  481. if(state === false){
  482. viewer.outlinePass.selectedObjects = []
  483. }else{
  484. viewer.outlinePass.selectedObjects = [model]
  485. }
  486. }
  487. viewer.dispatchEvent('content_changed')
  488. },
  489. /*updateEdgeStrength(){
  490. if(!this.selected)return
  491. if(this.selected.isPointcloud){
  492. viewer.outlinePass.edgeStrength = edgeStrengths.pointcloud// / this.selected.material.opacity
  493. }else{
  494. viewer.outlinePass.edgeStrength = edgeStrengths.glb
  495. }
  496. },*/
  497. focusOn(objects, duration = 400, fitBound=true, dontLookUp, dir){
  498. if(!(objects instanceof Array)){
  499. objects = [objects]
  500. }
  501. let boundingBox = new THREE.Box3
  502. objects.forEach(object=>{
  503. boundingBox.union(object.boundingBox.clone().applyMatrix4(object.matrixWorld))
  504. })
  505. let len = boundingBox.getSize(new THREE.Vector3).length()
  506. Potree.settings.cameraFar = Math.max( Potree.settings.cameraFar, len*3 )
  507. if(fitBound){
  508. viewer.focusOnObject({boundingBox}, 'boundingBox', duration, {dontLookUp, dontChangeCamDir:dir?false:true, dir})
  509. }else{
  510. /*
  511. let position = viewer.inputHandler.intersect ? viewer.inputHandler.intersect.location : boundingBox.getCenter(new THREE.Vector3)
  512. position && viewer.focusOnObject({position}, 'point', duration, {dontChangePos: true})
  513. */
  514. let position = viewer.inputHandler.intersect ? viewer.inputHandler.intersect.location : boundingBox.getCenter(new THREE.Vector3)
  515. if(!position)return
  516. /* let targetOld = viewer.mainViewport.view.getPivot()
  517. let projected1 = targetOld.clone().project(viewer.mainViewport.camera);
  518. let projected2 = position.clone().project(viewer.mainViewport.camera); //使用其z
  519. let targetNew = projected1.clone().setZ(projected2.z).unproject(viewer.mainViewport.camera);
  520. viewer.mainViewport.view.lookAt(targetNew) */
  521. viewer.mainViewport.view.radius = viewer.mainViewport.camera.position.distanceTo(position)
  522. //为了不改画面,不调节方向了,只能调调radius,一定程度将target靠近model
  523. }
  524. },
  525. moveBoundCenterTo(model,pos){ //使boundCenter在所要的位置
  526. let diff = new THREE.Vector3().subVectors(pos, model.boundCenter)
  527. model.position.add(diff);
  528. },
  529. getBoundCenter(model){
  530. if(!model.boundCenter) {
  531. model.boundCenter = new THREE.Vector3
  532. model.boundSize = new THREE.Vector3
  533. }
  534. let bound = model.boundingBox.clone().applyMatrix4(model.matrixWorld)
  535. bound.getCenter(model.boundCenter)
  536. bound.getSize(model.boundSize)
  537. //model.boundingBox.getCenter(model.boundCenter).applyMatrix4(model.matrixWorld)
  538. },
  539. setModelBtmHeight(model, z ){
  540. //无论模型怎么缩放、旋转,都使最低点为z
  541. if(z == void 0) z = model.btmHeight; //维持离地高度
  542. else model.btmHeight = z;
  543. if(model.btmHeight == void 0)return
  544. model.updateMatrixWorld()
  545. this.getBoundCenter(model)
  546. /* let boundingBox2 = model.boundingBox.clone().applyMatrix4(model.matrixWorld)
  547. let size = boundingBox2.getSize(new THREE.Vector3);
  548. let center = boundingBox2.getCenter(new THREE.Vector3); */
  549. let hopeZ = z + model.boundSize.z / 2
  550. //model.position.z = z + size.z / 2 - center.z
  551. model.position.z += (hopeZ - model.boundCenter.z)
  552. },
  553. computeBtmHeight(model){ //位移之后重新计算btmHeight
  554. model.updateMatrixWorld()
  555. /* let boundingBox2 = model.boundingBox.clone().applyMatrix4(model.matrixWorld)
  556. let size = boundingBox2.getSize(new THREE.Vector3);
  557. let center = boundingBox2.getCenter(new THREE.Vector3); */
  558. this.getBoundCenter(model)
  559. model.btmHeight = model.boundCenter.z - model.boundSize.z / 2
  560. },
  561. maintainBoundXY(model){ //在旋转和缩放后,立即执行这个函数,使boundCenter保持原位
  562. model.updateMatrixWorld()
  563. let center1 = model.boundCenter.clone();//还未更新的
  564. this.getBoundCenter(model)//更新
  565. let center2 = model.boundCenter.clone();
  566. let diff = new THREE.Vector2().subVectors(center1,center2);
  567. model.position.x += diff.x;
  568. model.position.y += diff.y;
  569. model.boundCenter.copy(center1)
  570. },
  571. maintainBoundCenter(model){
  572. model.updateMatrixWorld()
  573. let center1 = model.boundCenter.clone();//还未更新的
  574. this.getBoundCenter(model)//更新
  575. let center2 = model.boundCenter.clone();
  576. let diff = new THREE.Vector3().subVectors(center1,center2);
  577. model.position.add(diff)
  578. model.boundCenter.copy(center1)
  579. },
  580. modelTransformCallback(model,force){
  581. model.updateMatrixWorld()
  582. if(!force && model.matrixWorld.equals(model.lastMatrixWorld))return
  583. viewer.scene.measurements.forEach(measure=>{
  584. let changed
  585. measure.points_datasets.forEach((dataset_id,i)=>{
  586. if(dataset_id == model.dataset_id){
  587. changed = true
  588. measure.points[i] = Potree.Utils.datasetPosTransform({fromDataset:true,datasetId:dataset_id, position:measure.dataset_points[i].clone()})
  589. measure.updateMarker(measure.markers[i], measure.points[i])
  590. }
  591. })
  592. if(changed){//仿transformByPointcloud
  593. measure.getPoint2dInfo(measure.points)
  594. measure.update()
  595. measure.setSelected(false)//隐藏edgelabel
  596. }
  597. })
  598. viewer.tags.children.forEach(tag=>{
  599. if(tag.root == model){
  600. tag.titleLabel.updatePose()
  601. }
  602. })
  603. //反向求transformMatrix 参考Alignment.js 移动漫游点
  604. if(model.isPointcloud && model.transformMatrix){
  605. model.transformMatrix.multiplyMatrices(model.matrix, model.pos1MatrixInvert)
  606. model.rotateMatrix = new THREE.Matrix4().makeRotationFromEuler(model.rotation);
  607. model.panos.forEach(e=>e.transformByPointcloud())
  608. }else if(model.panos){
  609. model.rotateMatrix = new THREE.Matrix4().makeRotationFromEuler(model.rotation).multiply(model.rot1MatrixInvert);
  610. model.transformMatrix.multiplyMatrices(model.matrix, model.posRot1MatrixInvert)
  611. model.panos.forEach(e=>e.transformByPointcloud())
  612. model.bound = model.boundingBox.clone().applyMatrix4(model.matrixWorld)
  613. }
  614. if(model.panos){
  615. model.transformInvMatrix.copy(model.transformMatrix).invert()
  616. model.rotateInvMatrix.copy(model.rotateMatrix).invert()
  617. model.panos.forEach(e=>{
  618. e.marker.scale.copy(model.scale)
  619. })
  620. }
  621. model.lastMatrixWorld = model.matrixWorld.clone()
  622. viewer.dispatchEvent('content_changed')
  623. viewer.mapViewer && Potree.settings.showObjectsOnMap && viewer.mapViewer.dispatchEvent('content_changed')
  624. //--------
  625. this.selected == model && this.updateBoxHelper(model)
  626. },
  627. changeOpacity(model, opacity){
  628. let isRoot = model.dataset_id != void 0 //是否是最外层
  629. if(model.isPointcloud){
  630. model.changePointOpacity(opacity)
  631. //MergeEditor.updateEdgeStrength()
  632. }else{
  633. //model.traverse(e=>e.material && setOp(e, opacity))
  634. model.traverse(mesh=>{
  635. if(mesh.material){
  636. let mats = (mesh.material instanceof Array) ? mesh.material : [mesh.material]
  637. mats.forEach(mat=>{
  638. if(mat.originOpacity == void 0 ){
  639. mat.originOpacity = mesh.material.opacity
  640. }
  641. mat.opacity = mat.originOpacity * opacity
  642. if(mat.opacity<1){
  643. mat.transparent = true
  644. /* if(model.isPointcloud){
  645. mesh.changePointOpacity(realOpacity)
  646. }else{
  647. mesh.material.opacity = realOpacity
  648. } */
  649. mesh.renderOrder = Potree.config.renderOrders.model+1 //如果是一个mesh多个material咋整? obj的。 暂时默认全部opacity一样吧
  650. //mesh.material.depthWrite = false
  651. }else{
  652. mat.transparent = false
  653. mesh.renderOrder = Potree.config.renderOrders.model
  654. //mesh.material.depthWrite = true
  655. }
  656. mat.depthWrite = mat.opacity > 0.5 //防止的mesh之间完全遮挡,去掉write。write为true会完全遮挡后排的物体。没有write之后需要对渲染排序(three会排序,但有的角度会错)
  657. })
  658. }
  659. })
  660. }
  661. isRoot && (model.opacity = opacity)//记录在最外层
  662. viewer.dispatchEvent('content_changed')
  663. },
  664. addTitleForModel(model){
  665. var titleLabel = new TextSprite({
  666. startClipDis : 0.5,
  667. clipDistance : 1,//消失距离
  668. startOcclusDis: 0.5,
  669. occlusionDistance: 0.9,//变为backColor距离
  670. maxOcclusionFactor:0.7,
  671. maxClipFactor:1,
  672. text:model.name, sizeInfo:{width2d:150},
  673. rectBorderThick:1,
  674. borderColor:{r:200,g:200,b:200,a:0.5},
  675. textColor:{r:255 ,g:255,b:255,a:1.0},
  676. textshadowColor:'black',
  677. backgroundColor:{r: 100,g:100,b:100,a:0.3},
  678. borderRadius: 6,
  679. fontsize: 20, fontWeight:'',//thick
  680. renderOrder : Potree.config.renderOrders.tag.label,
  681. pickOrder: Potree.config.renderOrders.tag.label,
  682. useDepth : true ,
  683. maxLineWidth: 300,
  684. transform2Dpercent:{x:0,y:1.3}, //向上移动
  685. textAlign: Potree.settings.isOfficial && 'left'
  686. })
  687. model.titleLabel = titleLabel
  688. viewer.scene.scene.add(titleLabel)
  689. let updatePos = ()=>{
  690. titleLabel.position.copy(model.boundCenter)
  691. titleLabel.position.z += model.boundSize.z / 2 //暂时加载模型顶部,但也可能如果旋转了要在头顶
  692. titleLabel.updatePose()
  693. }
  694. let setVisible = ()=>{
  695. Potree.Utils.updateVisible(titleLabel,'followModel', model.visible)
  696. viewer.dispatchEvent('content_changed')
  697. }
  698. model.addEventListener('transformChanged',updatePos)
  699. model.addEventListener('isVisible',setVisible)
  700. titleLabel.addEventListener('isVisible',()=>{
  701. titleLabel.visible && updatePos()
  702. })
  703. setVisible()
  704. updatePos()
  705. },
  706. updateMemoryUsage1(){
  707. //obj暂时不管其贴图大小, 因为顶点造成的不仅是内存还有卡顿所以先只看顶点
  708. const maxMemory = Potree.config.tiles3DMaxMemory + 100 //M 实际估计是这个的10倍
  709. const eachObjPosWeight = 100/1000/1000 //M 每个顶点pos是3*4个字节?法线3*4和uv2*4 其实还有贴图
  710. const eachCloudPointWeight = 12/1000/1000 //M 每个点 pos + 颜色 + 法线 大概
  711. const eachVisiCPointWeight = eachCloudPointWeight * 5 // 或 maxMemory / (6*1000*1000) 大概值接近 (再除以一个数是因为显示的要比内存中的耗更多资源
  712. const eachGltfPosWeight = 100/1000/1000 //M 每个顶点pos是3*4个字节?法线3*4和uv2*4 其实还有贴图
  713. let posCount=0
  714. let eachTexPxWeight = 1 / 10 / 1000 / 1000 //不知道“图片缓存”会不会引起崩溃, 它不在“内存占用空间”中, 所以单位weight值设置小一点
  715. let panosWeight = viewer.images360.panos.filter(e=>e.depthTex).length * 0.7 + viewer.images360.tileDownloader.tilesCount * 512*512 * eachTexPxWeight //深度图和全景图
  716. let texArea = 0
  717. viewer.objs.children.forEach(e=>{
  718. if(!e.visible)return
  719. if(e.fileType == 'glb' || e.fileType == 'obj'){
  720. e.traverse((mesh)=>{
  721. if(mesh.geometry){
  722. posCount += mesh.geometry.attributes.position.count
  723. }
  724. if(mesh.material?.map){
  725. texArea += mesh.material?.map.image.width * mesh.material?.map.image.height
  726. }
  727. })
  728. }else if(e.fileType == '3dTiles'){
  729. }
  730. })
  731. //获取点云的内存限制
  732. let objWeight = posCount*eachObjPosWeight + texArea * eachTexPxWeight
  733. let laserWeight = Potree.numVisiblePoints * eachCloudPointWeight //点云实际显示所占大小
  734. let laserMemoryWeight = Potree.lru.numPoints * eachCloudPointWeight //点云所使用内存大小
  735. let tiles3DWeight = (viewer.visiVertexCount || 0) * eachGltfPosWeight //M 3dTiles所占内存大小
  736. let tiles3DMemoryWeight = viewer.tiles3dMemoryUsage / 1000 / 1000 //M 3dTiles显示的所占内存大小
  737. /* let min = 0.1, max = 6, minP = 100, maxP = 1000000;
  738. let ratio = Math.round(math.linearClamp(score, minP, maxP, max, min )); */
  739. let rest = maxMemory - objWeight - tiles3DWeight - panosWeight
  740. Potree.pointBudget = THREE.Math.clamp(Math.round(rest/eachVisiCPointWeight), Potree.config.pointDensity.low.pointBudget, 1.5*Potree.config.pointDensity.high.pointBudget)
  741. //Potree.settings.maxLRUPoints
  742. //获取3dTiles的内存限制
  743. let tiles3DMaxMemory = maxMemory - Math.round(objWeight + laserWeight + panosWeight)
  744. window.cesiumViewer && (tiles3DMaxMemory-=30)
  745. Potree.settings.tiles3DMaxMemory = THREE.Math.clamp(tiles3DMaxMemory , 30, Potree.config.tiles3DMaxMemory )
  746. //还存在的问题:仍然有隐患,因为没用到真实缓存的大小: tiles3DMemoryWeight laserMemoryWeight, 它们比真实可见的要多。不使用是因为它们无法反应出实际需要的内存量,缓存是只增不减
  747. //obj等普通mesh限制不了
  748. //console.log('objWeight',objWeight.toFixed(1), 'laserMemoryWeight',laserMemoryWeight.toFixed(1), 'tiles3DWeight',tiles3DWeight.toFixed(1), 'pointBudget',Potree.pointBudget, 'tiles3DMaxMemory',tiles3DMaxMemory)
  749. //总内存 = 内存占用空间+图片缓存 , obj的缓存比较多在图片中
  750. //尽量使任务管理器里的内存占用空间不超过2G
  751. /*
  752. 崩溃历史:
  753. cesium容易先崩溃,并弹窗报An error occurred while rendering. Rendering has stopped.
  754. 体育中心:http://192.168.0.140/index.html?caseId=1&app=1&token=1#/fuseEdit/path
  755. 罗敏电脑崩溃 ,报了ces的崩溃错误。内存占用空间2196M, memory.usedJSheapSize:1812M 崩溃时正在外部浏览模型(导览崩溃过),可能是3dtiles
  756. */
  757. },
  758. updateMemoryUsage(){//新 注意 模型即使隐藏也不会降低内存占用,只是会降低卡顿。 未支持3dgs
  759. //疑问:1 在不超过最大内存的前提下会因为显示太多模型崩溃吗? 2如何获知每个浏览器最大内存支持?看了一圈,jsHeapSizeLimit都一样怎么办?
  760. //3dtiles(或模型)的大小是直接用文件大小还是根据点和贴图尺寸计算呢,感觉它会分成两部分,加载的和显示的,都需要考虑。
  761. //obj暂时不管其贴图大小, 因为顶点造成的不仅是内存还有卡顿所以先只看顶点
  762. if(window.stopUpdateM) return
  763. //let start = performance.now();
  764. let old = {
  765. pointBudget : Potree.pointBudget,
  766. tiles3DMaxMemory: Potree.settings.tiles3DMaxMemory,
  767. maxLRUPoints : Potree.settings.maxLRUPoints
  768. }
  769. const maxMemory = 1500 //1500 //M 整体占用内存限制 (不考虑峰值,要等静止后退下来的值) //什么都不加载可能就占了300M
  770. const eachObjPosWeight = 100/1000/1000 //M 每个顶点pos是3*4个字节?法线3*4和uv2*4 + faceIndex比较难说
  771. const eachCloudPointWeight = 70/1000/1000 //M 每个点 pos + 颜色 + 法线 大概 根据测试算出来也是这个值 0.00007
  772. //const eachVisiCPointWeight = eachCloudPointWeight * 5 // 或 maxMemory / (6*1000*1000) 大概值接近 (再除以一个数是因为显示的要比内存中的耗更多资源
  773. const eachTexPxWeight = 1 / 10 / 1000 / 1000 //每个模型都不一样只能取个大概
  774. const eachTexPxWeightTiles = eachTexPxWeight * 1.5 //感觉3dtiles的比glb和obj都高很多,是因为文件还在缓存里吗
  775. let posCount=0, posCount3dTiles = 0, visiPosCount = 0
  776. let panosWeight = 0.1 * (viewer.images360.panos.filter(e=>e.depthTex).length * 0.7 + viewer.images360.tileDownloader.tilesCount * 512*512 * eachTexPxWeight) //深度图和全景图(但似乎是额外放图片缓存里?因为没有增加tex, panoExit后就dispose了, 所以乘以一个小系数。担心图片缓存也可能导致崩溃)
  777. //let texArea = 0, texArea3dTiles = 0, visiTexArea = 0
  778. //texArea = viewer.memoryModelCountInfo.otherTexArea + viewer.memoryModelCountInfo.tileTexArea
  779. //posCount = viewer.memoryModelCountInfo.otherPosCount + viewer.memoryModelCountInfo.tilePosCount
  780. //posCount3dTiles = viewer.memoryModelCountInfo.tilePosCount
  781. //texArea3dTiles = viewer.memoryModelCountInfo.tileTexArea
  782. let otherModelWeight = viewer.memoryModelCountInfo.otherPosCount*eachObjPosWeight + viewer.memoryModelCountInfo.otherTexArea * eachTexPxWeight
  783. let tilesWeight = (viewer.memoryModelCountInfo.tilePosCount*eachObjPosWeight + viewer.memoryModelCountInfo.tileTexArea * eachTexPxWeightTiles) * 1.5 //3dtiles太占内存了
  784. let modelWeight = tilesWeight + otherModelWeight //posCount*eachObjPosWeight + texArea * eachTexPxWeight
  785. //为何obj、glb过后会降很多,而3dtiles降的很少? 不过考虑到glb更卡,且加载瞬间确实占用高,还是不把otherModelWeight改少了吧
  786. Potree.tilesWeight = tilesWeight
  787. Potree.modelWeight = modelWeight//所有模型
  788. //Potree.modelVisiWeight = visiPosCount*eachObjPosWeight + visiTexArea * eachTexPxWeight
  789. //经测试,发现模型并不会因为不可见而降低内存,3dtiles加载满了之后拉远,可见weight下降了但内存没降。
  790. //so,需要测试崩溃是否只与内存有关,如果内存爆了,但都不可见,非常流畅,还会崩溃吗
  791. let laserVisiWeight = Potree.numVisiblePoints * eachCloudPointWeight //点云实际显示所占大小
  792. let laserWeight = Potree.lru.numPoints * eachCloudPointWeight //点云所使用内存大小
  793. Potree.laserWeight = laserWeight
  794. let fixedPart = modelWeight - tilesWeight + panosWeight + (window.cesiumViewer ? 30:0) //ces:刚加载时60,静止一段时间后20
  795. Potree.allWeight = Potree.laserWeight + fixedPart + tilesWeight
  796. let tiles3dMemoryUsage = viewer.tiles3dMemoryUsage/1024/1024
  797. let overRatio = Potree.allWeight / maxMemory
  798. let visiRatio1 = posCount3dTiles == 0 ? Infinity : viewer.visiVertexCount / posCount3dTiles
  799. let visiRatio2 = Potree.lru.numPoints == 0 ? Infinity : Potree.numVisiblePoints / Potree.lru.numPoints
  800. let resolved
  801. let restMemory = maxMemory - fixedPart //去除固定的part后
  802. if(overRatio > 1){
  803. const MinRestMemory = 400
  804. if(restMemory < MinRestMemory){
  805. Common.intervalTool.isWaiting('updateMemoryUsage', ()=>{
  806. let warnText
  807. if(this.lastMemoryState.restMemory < 0){
  808. warnText = '固定内存已经超额!没有剩余空间给点云和3dtiles。'
  809. }else if(this.lastMemoryState.restMemory < MinRestMemory){
  810. warnText = '固定内存过多!'
  811. }
  812. if(warnText){
  813. warnText += '请减少glb类模型, 当前固定内存占用大小:' + fixedPart
  814. console.error(warnText)
  815. }
  816. }, 10000)
  817. }
  818. if(visiRatio1 < visiRatio2 || Potree.lru.numPoints <= Potree.config.pointDensity.low.pointBudget / 10 * 1.1 ){//占比小的需要缩减缓存容器
  819. if(Potree.settings.tiles3DMaxMemory < tiles3dMemoryUsage / 2 && this.lastMemoryState.tiles3dMemoryUsage == tiles3dMemoryUsage //已经很难减少了,这时候tiles3DMaxMemory很可能降到个位数
  820. || Potree.settings.tiles3DMaxMemory < 2){
  821. resolved = false
  822. }else{
  823. Potree.settings.tiles3DMaxMemory = Math.min(tiles3dMemoryUsage, Potree.settings.tiles3DMaxMemory)//先降到已使用的数据量
  824. Potree.settings.tiles3DMaxMemory > 1 && (Potree.settings.tiles3DMaxMemory *= 0.8 ) //注意,降低该值不一定会降低 tiles3dMemoryUsage,只有后续改变sse才行 viewer.tiles3dMemoryUsage / tiles3DMaxMemory 可能大于1
  825. resolved = true
  826. }
  827. }
  828. if(!resolved){// visiRatio1 > visiRatio2
  829. if( visiRatio2 > 0.9){//压缩太小了,或者说没什么缓存就超额了,只能继续减少显示个数
  830. if(Potree.pointBudget < Potree.config.pointDensity.low.pointBudget){
  831. //console.error('Potree.pointBudget已经很小了!', Potree.pointBudget)
  832. }else{
  833. Potree.pointBudget *= 0.85
  834. resolved = true
  835. }
  836. }else{
  837. Potree.pointBudget = THREE.Math.clamp(Potree.numVisiblePoints, Potree.config.pointDensity.low.pointBudget / 10, Potree.pointBudget*0.8 )//先往降到可见数量方向降 (当画面中可见点确实很少时允许降低到low之下)
  838. Potree.settings.maxLRUPoints = Math.min(Potree.lru.numPoints, Potree.settings.maxLRUPoints);//先降到已使用的数量
  839. Potree.settings.maxLRUPoints *= 0.8
  840. resolved = true
  841. }
  842. }
  843. }else{//恢复部分好难写
  844. //优先恢复实际使用(可见)内存,然后是缓存
  845. //额外缓存少的,实际使用内存被压缩的概率也大
  846. //visiRatio1 == Infinity && (visiRatio1 = 0) //因这里大的才会被改所以改一下
  847. visiRatio1 *= tiles3dMemoryUsage / Potree.settings.tiles3DMaxMemory //用point数量有时候不准,明明内存不够了不可见的tile还存在,所以×memory的占比
  848. visiRatio2 = Potree.pointBudget == 0 ? 0 : Potree.numVisiblePoints / Potree.pointBudget //使用率越高,说明越需要内存. 因为恢复时先提高使用内存所以改一下
  849. let r = Math.min(1, visiRatio1 , visiRatio2 ) //如果另一个visiRatio 很小,s就可以再小一些,使内存超过一点,然后朝另一方倾斜,另一方去缩减
  850. let s = 1 - Math.max(0.8, overRatio * r )
  851. //太难判断是哪个更需要内存了,所以两个都涨吧
  852. if(visiRatio2 > visiRatio1){
  853. let s_ = visiRatio2 / (visiRatio1 + visiRatio2) * s + 1
  854. const addCount = 500000 * s_
  855. if(Potree.pointBudget < Potree.config.pointDensity.high.pointBudget*0.9){
  856. Potree.pointBudget += addCount
  857. }else{
  858. Potree.settings.maxLRUPoints += addCount
  859. }
  860. }
  861. if(Potree.settings.tiles3DMaxMemory < Potree.config.tiles3DMaxMemory ){
  862. let s_ = visiRatio1 / (visiRatio1 + visiRatio2) * s + 1
  863. const addCount = 20 * s_
  864. Potree.settings.tiles3DMaxMemory += addCount
  865. Potree.settings.tiles3DMaxMemory = Math.min(Potree.settings.tiles3DMaxMemory, Potree.config.tiles3DMaxMemory)
  866. }
  867. resolved = true
  868. }
  869. let min = overRatio > 1 ? 1.1 : 1.5, max = 5;
  870. Potree.settings.maxLRUPoints = THREE.Math.clamp(Potree.settings.maxLRUPoints , Potree.pointBudget * min, Potree.pointBudget * max)
  871. //压缩时先压缩缓存,然后是使用内存; 恢复时相反。
  872. //3dtiles不像点云那样可以在加载时拦截,使不高于tiles3DMaxMemory,只能自动动态调节maxSSE,所以有可能调节完还是降低不了
  873. //一次只更新一点,一点点调节,因为可能不是一个原因造成
  874. let changed
  875. if(old.pointBudget != Potree.pointBudget || old.tiles3DMaxMemory != Potree.settings.tiles3DMaxMemory || old.maxLRUPoints != Potree.settings.maxLRUPoints ){
  876. if(old.tiles3DMaxMemory != Potree.settings.tiles3DMaxMemory){
  877. viewer.setAllTilesets(model=>{
  878. model.runtime.getTileset().nextForceUpdate = true
  879. model.runtime.getTileset().needRenderNext = true
  880. })
  881. }else{
  882. viewer.dispatchEvent('content_changed')
  883. }
  884. changed = true
  885. }
  886. {
  887. let mSSE = math.linearClamp(Potree.fpsRendered2, [2, 52], [500, 70]) //有效降低卡顿 和贴图变白概率。越卡越提高msse
  888. let r = tiles3dMemoryUsage / Potree.settings.tiles3DMaxMemory
  889. viewer.setAllTilesets(model=>{
  890. let tileset = model.runtime.getTileset()
  891. tileset.options.initialMaxSSE = mSSE
  892. let e = tileset.options.maximumScreenSpaceError * THREE.Math.clamp(r, 0.8, 1.2)
  893. e = THREE.Math.clamp(e, mSSE, mSSE+400) //如果有很卡的模型可能需要1000,但太高了降回来很慢,就假设模型都正常的吧
  894. tileset.options.maximumScreenSpaceError = e
  895. })
  896. }
  897. //针对很卡的设备:(卡的也容易崩溃)
  898. Potree.config.pointDensity.high.pointBudget = math.linearClamp(Potree.fpsRendered2, [2, 50], [1e6, 6e6])
  899. let history = this.lastMemoryState.history || []
  900. history.push(overRatio)
  901. this.lastMemoryState = {
  902. tiles3dMemoryUsage,
  903. history,
  904. restMemory
  905. }
  906. //console.log('cost', performance.now() - start)
  907. return changed && ((overRatio > 1.1 || overRatio < 0.9) ||
  908. history.slice(-7).length < 7 || history.slice(-7).some(overRatio=>overRatio > 1.1 || overRatio < 0.9) //后十个都在最大内存范围左右抖动,则不继续更新,直到相机变化
  909. )
  910. //难点:要防止抖动,否则3dtiles的sse变大变小,相机静止时还在清晰和模糊间变来变去。好在点云是可以较精细调节,tile是间接控制的
  911. //模型删除后还会占用一定内存,尤其是3dtile几乎降不了,怎么回事?是否删除的模型还要算进来
  912. },
  913. setGroundPlaneImg(src,scale,angle){//设置地面图
  914. this.goundScale = scale || 1, this.goundAngle = angle || 0
  915. let oldSrc = this.curGroundImgSrc
  916. this.curGroundImgSrc = src
  917. const ratio = 0.03
  918. if(src){
  919. if(oldSrc == src && this.groundPlane.material.map.image){ //仅修改大小
  920. const s = ratio * this.goundScale
  921. let {width, height} = this.groundPlane.material.map.image
  922. this.groundPlane.scale.set(width*s, height*s)
  923. viewer.dispatchEvent('content_changed')
  924. this.groundPlane.rotation.z = THREE.Math.degToRad(this.goundAngle)
  925. return
  926. }
  927. let map = texLoader.load(src,(tex)=>{
  928. if(this.curGroundImgSrc == src){
  929. const s = ratio * this.goundScale
  930. this.groundPlane.scale.set(tex.image.width*s, tex.image.height*s)
  931. this.groundPlane.rotation.z = THREE.Math.degToRad(this.goundAngle)
  932. viewer.dispatchEvent('content_changed')
  933. }
  934. })
  935. Potree.Utils.makeTexDontResize(map)
  936. if(!this.groundPlane){
  937. this.groundPlane = new THREE.Mesh(new THREE.PlaneBufferGeometry(1,1,1), new THREE.MeshBasicMaterial({
  938. map,
  939. side : 2,
  940. }))
  941. viewer.scene.scene.add(this.groundPlane)
  942. this.groundPlane.position.z = 0.1
  943. }else{
  944. this.groundPlane.material.map = map
  945. }
  946. Potree.Utils.updateVisible(this.groundPlane,'show',true )
  947. }else{
  948. this.groundPlane && Potree.Utils.updateVisible(this.groundPlane,'show',false )
  949. }
  950. }
  951. }
  952. /*
  953. watch:
  954. performance.memory.usedJSHeapSize / 1e6
  955. Potree.settings.tiles3DMaxMemory
  956. viewer.tiles3dMemoryUsage
  957. Potree.tilesWeight
  958. Potree.modelWeight
  959. Potree.modelVisiWeight
  960. Potree.allWeight
  961. Potree.lru.numPoints
  962. Potree.pointBudget
  963. Potree.numVisiblePoints
  964. Potree.settings.maxLRUPoints
  965. */
  966. export default MergeEditor
  967. /* class titleLabel extends THREE.Shim.FollowRootObject{
  968. constructor(o){
  969. super(o.root)
  970. this.l
  971. }
  972. } */
  973. /*
  974. note:
  975. 要注意getHoveredElements只在getIntersect时才使interactables包含加载的model, 也就是model上不能有使之成为interactables的事件,否则在鼠标hover到模型上开始转动的一瞬间很卡。
  976. 控制台断开完全重合的漫游点的方法
  977. window.pano1 = viewer.modules.PanoEditor.selectedPano //选中第一个点后输入这行,得到第一个漫游点
  978. window.pano2 = viewer.modules.PanoEditor.selectedPano //选中第二个点后输入这行,得到第二个漫游点
  979. viewer.modules.PanoEditor.linkChange(window.pano1, window.pano2, 'remove') //断开链接
  980. 如果直接刷新,内存管理器中的内存会有之前的残留。不知道是不是真实的加入这次的内存了。
  981. 并且,如果第一次加载的是几乎不占内存的模型,如glb,内存也会一下子涨30,不过等一会儿会降。4M的3dtiles会涨60. 每次加载模型后都会内存突增,但过后会降。 为何obj、glb过后会降很多,而3dtiles降的很少。?
  982. 国产系统8192贴图直接出错,4096的贴图过多也出错。texImage: Driver ran out of memory during upload.
  983. */