import * as THREE from "../../libs/three.js/build/three.module.js"; import {Utils} from "../utils.js"; import {LineDraw/* , MeshDraw */} from "../custom/utils/DrawUtil.js"; import History from "../custom/utils/History.js" //问题:如何转换到世界坐标?(缩放方向有bug。) //add------------------------------------- const OpaWhenNotSelect = 0.6 const ScaleRatio = 3 const OutlineColor = 0x666666 //---------------------------------------- const hideFocusHandles = true//add export class TransformationTool extends THREE.EventDispatcher{ constructor(viewer ) { super() this.viewer = viewer; this.modesEnabled = {}//add this.style = 'singleMode' //建mesh时按照singleModes建的 this.scene = new THREE.Scene(); this.selection = []; this.pivot = new THREE.Vector3(); this.dragging = false; this.showPickVolumes = false; this.viewer.inputHandler.registerInteractiveScene(this.scene); this.viewer.inputHandler.addEventListener('selection_changed', (e) => { /* for(let selected of this.selection){ //若不删除,应该会穿过选中的物体选到别的而不是取消选择 this.viewer.inputHandler.blacklist.delete(selected); } */ this.selection = e.selection; /* for(let selected of this.selection){ this.viewer.inputHandler.blacklist.add(selected); } */ }); this.viewer.addEventListener('global_touchstart',(e)=>{ //add this.update() }) this.viewer.addEventListener('global_mousemove',(e)=>{ //add this.onPointerMove() }) let red = Potree.config.axis.x.color let green = Potree.config.axis.y.color let blue = Potree.config.axis.z.color let white = Potree.config.axis.xyz.color this.activeHandle = null; this.scaleHandles = { "scale.x+": {name: "scale.x+", node: new THREE.Object3D(), color: red, alignment: [+1, +0, +0]}, "scale.x-": {name: "scale.x-", node: new THREE.Object3D(), color: red, alignment: [-1, +0, +0]}, "scale.y+": {name: "scale.y+", node: new THREE.Object3D(), color: green, alignment: [+0, +1, +0]}, "scale.y-": {name: "scale.y-", node: new THREE.Object3D(), color: green, alignment: [+0, -1, +0]}, "scale.z+": {name: "scale.z+", node: new THREE.Object3D(), color: blue, alignment: [+0, +0, +1]}, "scale.z-": {name: "scale.z-", node: new THREE.Object3D(), color: blue, alignment: [+0, +0, -1]}, "lines": {name:'lines', node: new THREE.Object3D(), dontScale:true }//add }; this.focusHandles = { "focus.x+": {name: "focus.x+", node: new THREE.Object3D(), color: red, alignment: [+1, +0, +0]}, "focus.x-": {name: "focus.x-", node: new THREE.Object3D(), color: red, alignment: [-1, +0, +0]}, "focus.y+": {name: "focus.y+", node: new THREE.Object3D(), color: green, alignment: [+0, +1, +0]}, "focus.y-": {name: "focus.y-", node: new THREE.Object3D(), color: green, alignment: [+0, -1, +0]}, "focus.z+": {name: "focus.z+", node: new THREE.Object3D(), color: blue, alignment: [+0, +0, +1]}, "focus.z-": {name: "focus.z-", node: new THREE.Object3D(), color: blue, alignment: [+0, +0, -1]}, }; this.translationHandles = { "translation.x": {name: "translation.x", node: new THREE.Object3D(), color: red, alignment: [1, 0, 0]}, "translation.y": {name: "translation.y", node: new THREE.Object3D(), color: green, alignment: [0, 1, 0]}, "translation.z": {name: "translation.z", node: new THREE.Object3D(), color: blue, alignment: [0, 0, 1]}, //add 'translation.xyz':{name: "translation.xyz", node: new THREE.Object3D(),color: white, alignment: [0, 0, 0], alignment2: [1, 1, 1]}, 'translation.plane.xy':{name: "translation.plane.xy", node: new THREE.Object3D(),color: blue,alignment: [0, 0, 1], alignment2: [1, 1, 0]}, 'translation.plane.yz':{name: "translation.plane.yz", node: new THREE.Object3D(),color: red, alignment: [1, 0, 0], alignment2: [0, 1, 1]}, 'translation.plane.xz':{name: "translation.plane.xz", node: new THREE.Object3D(),color: green, alignment: [0, 1, 0], alignment2: [1, 0, 1]}, }; this.rotationHandles = { "rotation.x": {name: "rotation.x", node: new THREE.Object3D(), color: red, alignment: [1, 0, 0]}, "rotation.y": {name: "rotation.y", node: new THREE.Object3D(), color: green, alignment: [0, 1, 0]}, "rotation.z": {name: "rotation.z", node: new THREE.Object3D(), color: blue, alignment: [0, 0, 1]}, }; this.handles = Object.assign({}, this.scaleHandles, hideFocusHandles?{}:this.focusHandles, this.translationHandles, this.rotationHandles); this.pickVolumes = []; this.initializeScaleHandles(); this.initializeFocusHandles(); this.initializeTranslationHandles(); this.initializeRotationHandles(); let boxFrameGeometry = new THREE.Geometry(); { // bottom boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5)); // top boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5)); // sides boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, 0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, -0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(0.5, 0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, -0.5)); boxFrameGeometry.vertices.push(new THREE.Vector3(-0.5, 0.5, -0.5)); } this.frame = new THREE.LineSegments(boxFrameGeometry, new THREE.LineBasicMaterial({color: 0xffff00})); this.scene.add(this.frame); //------------------add----------------------- this.setModeEnable(['scale','translation','rotation']) Potree.Utils.setObjectLayers(this.scene, 'transformationTool' ) this.scene.traverse(e=>{ e.pickDontCheckDis = true; //pick时不需要识别是否在点云之上 }) { let exist = (object)=>{//是否没被删除(暂时不考虑换了parent) while(object.parent){ object = object.parent } if(object instanceof THREE.Scene){ return true } } this.history = new History({ //也可以写到全局,但需要加个判断物品是否存在的函数 applyData: (data)=>{ if(exist(data.object)){//viewer.scene.volumes.includes(data.box) //或许还要识别是否matrixAuto data.matrix.decompose( data.object.position, data.object.quaternion, data.object.scale ); viewer.dispatchEvent('content_changed') return true } } , getData:(data)=>{ return data } }) this.addEventListener('transformed', (e)=>{ let object = viewer.transformationTool.selection[0] this.history.beforeChange({object, matrix:e.matrixBefore.clone()} ) this.viewer.dispatchEvent('content_changed') }) this.addEventListener('stopDrag', (e)=>{ let object = viewer.transformationTool.selection[0] object && this.history.afterChange({object, matrix:object.matrix.clone()} ) }) /* viewer.inputHandler.addEventListener('keydown', (e)=>{ if(e.keyCode == 90 && e.event.ctrlKey){//Z this.history.undo() }else if(e.keyCode == 89 && e.event.ctrlKey){//Y this.history.redo() } }) */ } } setModeEnable(enableModes=[] ){//xzw add let length=0; ['translation','scale','rotation'].forEach(mode=>{ let handels = this[mode + 'Handles'] let enable = enableModes.includes(mode) for(let o in handels){ Potree.Utils.updateVisible(handels[o].node,'modeForce', !!enable) } this.modesEnabled[mode] = !!enable enable && length++ }) if(this.style == 'singleMode' && length > 1){ this.changeStyle('mixedModes') }else if(this.style == 'mixedModes' && length == 1){ this.changeStyle('singleMode') } } changeStyle(style){// 切换单个mode & 多个mode混合 风格(因多个混合比较拥挤,需要做调整) ['x','y','z'].forEach(axis=>{ this.translationHandles['translation.'+axis].node.children.forEach(mesh=>{ if(mesh.name.includes('arrow')){ Potree.Utils.updateVisible(mesh, 'modeStyle', style == 'singleMode') }else if(mesh.name.includes('handle')){ mesh.material.lineWidth = style == 'singleMode' ? 5 : 8 } }) }); ['xy','yz','xz'].forEach(axis=>{ let handle = this.translationHandles['translation.plane.'+axis] let s = style == 'singleMode' ? 15 : 12 handle.node.children[0].scale.set(s,s,s); handle.node.children[0].position.fromArray(handle.alignment2).multiplyScalar(s*1.5) }) Potree.Utils.updateVisible(this.scaleHandles['lines'].node, 'modeStyle', style == 'singleMode') this.style = style } initializeTranslationHandles(){//大改 let boxGeometry = new THREE.BoxGeometry(1, 1, 1); let length = 100 , arrowRadius = 4, arrowHeight = 10 let arrowGeometry = new THREE.CylinderBufferGeometry( 0, arrowRadius, arrowHeight, 12, 1, false );//add 箭头朝(0,1,0) let arrowInitialQua = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), -Math.PI/2 )//先将qua旋转到朝向0,0,-1, 因为一般quaternion不设置时默认表示朝向0,0,-1, let octahedronGeometry = new THREE.OctahedronBufferGeometry( 5, 0 ); let planeGeometry = new THREE.PlaneBufferGeometry( 1, 1 ) for(let handleName of Object.keys(this.translationHandles)){ let handle = this.handles[handleName]; let node = handle.node; this.scene.add(node); node.name = handleName//add let alignment = new THREE.Vector3(...handle.alignment) let geometry , mesh, pickVolume, meshScale, pickScale, rotation, position, lookAtPoint, hasPick = true, meshPickable = false, renderOrder let matProp = { color: handle.color, opacity: OpaWhenNotSelect, transparent: true, side: handleName.includes('plane') ? THREE.DoubleSide : THREE.FrontSide } let material = new THREE.MeshBasicMaterial(matProp) let pickMaterial = new THREE.MeshNormalMaterial({ opacity: 0.2, transparent: true, visible: this.showPickVolumes }); if(handleName.includes('xyz') ){ geometry = octahedronGeometry meshPickable = true renderOrder = 12; }else if(handleName.includes('plane') ){ geometry = planeGeometry meshPickable = true position = new THREE.Vector3(...handle.alignment2) lookAtPoint = alignment if(handleName.includes('xy') ) { }else if(handleName.includes('yz')){ }else if(handleName.includes('xz')){ } }else{ geometry = boxGeometry let point = new THREE.Vector3(0,0,length/2)//new THREE.Vector3().copy(alignment).multiplyScalar(length/2) mesh = LineDraw.createFatLine([point, point.clone().negate()],{lineWidth:5, mat : new LineDraw.createFatLineMat(matProp)}) lookAtPoint = alignment renderOrder = 10; pickScale = new THREE.Vector3(4, 4, length+arrowHeight*2) //pickScale = new THREE.Vector3(4, 4, 1.2) { let arrow = new THREE.Mesh(arrowGeometry, material) arrow.name = `${handleName}.arrow`; arrow.renderOrder = 11; arrow.position.set( ...handle.alignment).multiplyScalar(length/2+arrowHeight/2) arrow.lookAt(0,0,0) node.add(arrow); let arrow2 = arrow.clone(); arrow2.position.negate() arrow2.lookAt(0,0,0) node.add(arrow2); arrow.quaternion.multiply(arrowInitialQua) //乘上初始旋转 arrow2.quaternion.multiply(arrowInitialQua) } } mesh || (mesh = new THREE.Mesh(geometry,material)) mesh.name = `${handleName}.handle`; node.add(mesh); renderOrder && (mesh.renderOrder = renderOrder) meshScale && mesh.scale.copy(meshScale) rotation && mesh.rotation.copy(rotation) lookAtPoint && mesh.lookAt(lookAtPoint) position && mesh.position.copy(position) if(!meshPickable && hasPick){ pickVolume = new THREE.Mesh(geometry, pickMaterial); pickScale && pickVolume.scale.copy(pickScale) }else if(hasPick && meshPickable){ pickVolume = mesh } if(pickVolume){ if(mesh != pickVolume){ mesh.add(pickVolume); pickVolume.name = `${handleName}.pick_volume`; }else{ pickVolume.name += ' & pick_volume' } pickVolume.handle = handleName; this.pickVolumes.push(pickVolume); } node.setOpacity = (target) => { if(handleName.includes('plane')){ let more = 1.5; //减掉更多,使min更低,max还是1 target = 1 - (1-target)*more } let opacity = {x: material.opacity}; let t = new TWEEN.Tween(opacity).to({x: target}, 0); t.onUpdate(() => { mesh.visible = opacity.x > 0; pickVolume && (pickVolume.visible = opacity.x > 0); material.opacity = opacity.x; mesh.material.opacity = opacity.x; //outlineMaterial.opacity = opacity.x; pickMaterial.opacity = opacity.x * 0.5; }); t.start(); }; pickVolume.addEventListener("drag", (e) => {this.dragTranslationHandle(e)}); pickVolume.addEventListener("drop", (e) => {this.dropTranslationHandle(e)}); } } initializeScaleHandles(){ let sgSphere = new THREE.SphereGeometry(1, 32, 32); let sgLowPolySphere = new THREE.SphereGeometry(1, 16, 16); for(let handleName of Object.keys(this.scaleHandles)){ let handle = this.scaleHandles[handleName]; let node = handle.node; node.name = handleName//add this.scene.add(node); if(handleName == 'lines'){ //add ['x','y','z'].forEach(axis=>{ let handle1_ = this.scaleHandles['scale.'+axis+'+'] let handle2_ = this.scaleHandles['scale.'+axis+'-'] let line = LineDraw.createFatLine([ new THREE.Vector3().fromArray(handle1_.alignment).multiplyScalar(0.5), new THREE.Vector3().fromArray(handle2_.alignment).multiplyScalar(0.5) ], {color:handle1_.color, lineWidth:4} // , dontAlwaysSeen:true ) node.add(line) }) node.setOpacity = (opacity) => { opacity *= 0.6 node.children.forEach(e=>e.material.opacity = opacity) }; continue; } node.position.set(...handle.alignment).multiplyScalar(0.5); let material = new THREE.MeshBasicMaterial({ color: handle.color, side:THREE.DoubleSide,//xzw add opacity: OpaWhenNotSelect, transparent: true }); let outlineMaterial = new THREE.MeshBasicMaterial({ color: OutlineColor, side: THREE.BackSide, opacity: OpaWhenNotSelect, transparent: true}); let pickMaterial = new THREE.MeshNormalMaterial({ opacity: 0.2, transparent: true, side:THREE.DoubleSide,//xzw add for orthoCam, 缩小画面时因球体放大导致到相机背面去了而看不到球体正面 visible: this.showPickVolumes}); let sphere = new THREE.Mesh(sgSphere, material); sphere.scale.set(5, 5, 5 ); sphere.name = `${handleName}.handle`; node.add(sphere); sphere.renderOrder = 10 /* let outline = new THREE.Mesh(sgSphere, outlineMaterial); outline.scale.set(1.1, 1.1, 1.1); outline.name = `${handleName}.outline`; sphere.add(outline); */ let pickSphere = new THREE.Mesh(sgLowPolySphere, pickMaterial); pickSphere.name = `${handleName}.pick_volume`; pickSphere.scale.set(1.5, 1.5, 1.5); sphere.add(pickSphere); pickSphere.handle = handleName; this.pickVolumes.push(pickSphere); node.setOpacity = (target) => { let opacity = {x: material.opacity}; let t = new TWEEN.Tween(opacity).to({x: target}, 0); //xzw改 原100毫秒,因为太慢容易选错 t.onUpdate(() => { sphere.visible = opacity.x > 0; pickSphere.visible = opacity.x > 0; material.opacity = opacity.x; outlineMaterial.opacity = opacity.x; pickSphere.material.opacity = opacity.x * 0.5; }); t.start(); }; pickSphere.addEventListener("drag", (e) => this.dragScaleHandle(e)); pickSphere.addEventListener("drop", (e) => this.dropScaleHandle(e)); pickSphere.addEventListener("mouseover", e => { //node.setOpacity(1); }); pickSphere.addEventListener("click", e => { //e.consume(); }); pickSphere.addEventListener("mouseleave", e => { //node.setOpacity(OpaWhenNotSelect); }); } } initializeRotationHandles(){ let boldAdjust = 2.5; let torusGeometry = new THREE.TorusGeometry(1.4, boldAdjust * 0.015, 8, 64, Math.PI / 2); //let outlineGeometry = new THREE.TorusGeometry(1, boldAdjust * 0.018, 8, 64, Math.PI / 2); let pickGeometry = new THREE.TorusGeometry(1.4, boldAdjust * 0.06, 6, 4, Math.PI / 2); for(let handleName of Object.keys(this.rotationHandles)){ let handle = this.handles[handleName]; let node = handle.node; this.scene.add(node); node.name = handleName//add let material = new THREE.MeshBasicMaterial({ color: handle.color, opacity: OpaWhenNotSelect, transparent: true }); /* let outlineMaterial = new THREE.MeshBasicMaterial({ color: OutlineColor, side: THREE.BackSide, opacity: OpaWhenNotSelect, transparent: true }); */ let pickMaterial = new THREE.MeshNormalMaterial({ opacity: 0.2, transparent: true, visible: this.showPickVolumes }); let box = new THREE.Mesh(torusGeometry, material); box.name = `${handleName}.handle`; box.scale.set(30, 30, 30); box.lookAt(new THREE.Vector3(...handle.alignment)); node.add(box); handle.translateNode = box; /* let outline = new THREE.Mesh(outlineGeometry, outlineMaterial); outline.name = `${handleName}.outline`; outline.scale.set(1, 1, 1); outline.renderOrder = 0; box.add(outline); */ let pickVolume = new THREE.Mesh(pickGeometry, pickMaterial); pickVolume.name = `${handleName}.pick_volume`; pickVolume.scale.set(1, 1, 1); pickVolume.handle = handleName; box.add(pickVolume); this.pickVolumes.push(pickVolume); node.setOpacity = (target) => { let opacity = {x: material.opacity}; let t = new TWEEN.Tween(opacity).to({x: target}, 0); t.onUpdate(() => { box.visible = opacity.x > 0; pickVolume.visible = opacity.x > 0; material.opacity = opacity.x; //outlineMaterial.opacity = opacity.x; pickMaterial.opacity = opacity.x * 0.5; }); t.start(); }; //pickVolume.addEventListener("mouseover", (e) => { // //let a = this.viewer.scene.getActiveCamera().getWorldDirection(new THREE.Vector3()).dot(pickVolume.getWorldDirection(new THREE.Vector3())); // console.log(pickVolume.getWorldDirection(new THREE.Vector3())); //}); pickVolume.addEventListener("drag", (e) => {this.dragRotationHandle(e)}); pickVolume.addEventListener("drop", (e) => {this.dropRotationHandle(e)}); } } initializeFocusHandles(){ if(hideFocusHandles)return//add //let sgBox = new THREE.BoxGeometry(1, 1, 1); let sgPlane = new THREE.PlaneGeometry(4, 4, 1, 1); let sgLowPolySphere = new THREE.SphereGeometry(1, 16, 16); let texture = new THREE.TextureLoader().load(`${exports.resourcePath}/icons/eye_2.png`); for(let handleName of Object.keys(this.focusHandles)){ let handle = this.focusHandles[handleName]; let node = handle.node; this.scene.add(node); let align = handle.alignment; node.name = handleName//add //node.lookAt(new THREE.Vector3().addVectors(node.position, new THREE.Vector3(...align))); node.lookAt(new THREE.Vector3(...align)); let off = 0.8; if(align[0] === 1){ node.position.set(1, off, -off).multiplyScalar(0.5); node.rotation.z = Math.PI / 2; }else if(align[0] === -1){ node.position.set(-1, -off, -off).multiplyScalar(0.5); node.rotation.z = Math.PI / 2; }else if(align[1] === 1){ node.position.set(-off, 1, -off).multiplyScalar(0.5); node.rotation.set(Math.PI / 2, Math.PI, 0.0); }else if(align[1] === -1){ node.position.set(off, -1, -off).multiplyScalar(0.5); node.rotation.set(Math.PI / 2, 0.0, 0.0); }else if(align[2] === 1){ node.position.set(off, off, 1).multiplyScalar(0.5); }else if(align[2] === -1){ node.position.set(-off, off, -1).multiplyScalar(0.5); } let material = new THREE.MeshBasicMaterial({ color: handle.color, opacity: 0, transparent: true, map: texture }); //let outlineMaterial = new THREE.MeshBasicMaterial({ // color: 0x000000, // side: THREE.BackSide, // opacity: 0, // transparent: true}); let pickMaterial = new THREE.MeshNormalMaterial({ //opacity: 0, transparent: true, visible: this.showPickVolumes}); let box = new THREE.Mesh(sgPlane, material); box.name = `${handleName}.handle`; box.scale.set(1.5, 1.5, 1.5); box.position.set(0, 0, 0); box.visible = false; node.add(box); //handle.focusNode = box; //let outline = new THREE.Mesh(sgPlane, outlineMaterial); //outline.scale.set(1.4, 1.4, 1.4); //outline.name = `${handleName}.outline`; //box.add(outline); let pickSphere = new THREE.Mesh(sgLowPolySphere, pickMaterial); pickSphere.name = `${handleName}.pick_volume`; pickSphere.scale.set(2, 2, 2); box.add(pickSphere); pickSphere.handle = handleName; this.pickVolumes.push(pickSphere); node.setOpacity = (target) => { let opacity = {x: material.opacity}; let t = new TWEEN.Tween(opacity).to({x: target}, 0); t.onUpdate(() => { pickSphere.visible = opacity.x > 0; box.visible = opacity.x > 0; material.opacity = opacity.x; //outlineMaterial.opacity = opacity.x; pickSphere.material.opacity = opacity.x * 0.5; }); t.start(); }; //pickSphere.addEventListener("drag", e => {}); pickSphere.addEventListener("mouseup", e => { //e.consume(); }); pickSphere.addEventListener("mousedown", e => { //e.consume(); }); pickSphere.addEventListener("click", e => { //e.consume(); let selected = this.selection[0]; let maxScale = Math.max(...selected.scale.toArray()); let minScale = Math.min(...selected.scale.toArray()); let handleLength = Math.abs(selected.scale.dot(new THREE.Vector3(...handle.alignment))); let alignment = new THREE.Vector3(...handle.alignment).multiplyScalar(2 * maxScale / handleLength); alignment.applyMatrix4(selected.matrixWorld); let newCamPos = alignment; let newCamTarget = selected.getWorldPosition(new THREE.Vector3()); Utils.moveTo(this.viewer.scene, newCamPos, newCamTarget); }); pickSphere.addEventListener("mouseover", e => { //box.setOpacity(1); }); pickSphere.addEventListener("mouseleave", e => { //box.setOpacity(OpaWhenNotSelect); }); } } dragRotationHandle(e){ let drag = e.drag; let handle = this.activeHandle; let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera(); if(!handle){ return }; let localNormal = new THREE.Vector3(...handle.alignment); let n = new THREE.Vector3(); n.copy(new THREE.Vector4(...localNormal.toArray(), 0).applyMatrix4(handle.node.matrixWorld)); n.normalize(); if (!drag.intersectionStart){ //this.viewer.scene.scene.remove(this.debug); //this.debug = new THREE.Object3D(); //this.viewer.scene.scene.add(this.debug); //Utils.debugSphere(this.debug, drag.location, 3, 0xaaaaaa); //let debugEnd = drag.location.clone().add(n.clone().multiplyScalar(20)); //Utils.debugLine(this.debug, drag.location, debugEnd, 0xff0000); drag.intersectionStart = drag.location; drag.objectStart = drag.object.getWorldPosition(new THREE.Vector3()); drag.handle = handle; let plane = new THREE.Plane().setFromNormalAndCoplanarPoint(n, drag.intersectionStart); drag.dragPlane = plane; drag.pivot = drag.intersectionStart; }else{ handle = drag.handle; } if(!drag.dragPlane)return//xzw add 因有时候没有 this.dragging = true; let pointer = this.viewer.inputHandler.pointer let domElement = this.viewer.renderer.domElement; let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight); let I = ray.intersectPlane(drag.dragPlane, new THREE.Vector3()); if (I) { let center = this.scene.getWorldPosition(new THREE.Vector3()); let from = drag.pivot; let to = I; let v1 = from.clone().sub(center).normalize(); let v2 = to.clone().sub(center).normalize(); let angle = Math.acos(v1.dot(v2)); let sign = Math.sign(v1.cross(v2).dot(n)); angle = angle * sign; if (Number.isNaN(angle)) { return; } let matrixBefore = this.selection[0].matrix.clone() let normal = new THREE.Vector3(...handle.alignment); for (let selection of this.selection) { selection.rotateOnAxis(normal, angle); selection.dispatchEvent({ type: "orientation_changed", object: selection }); } this.dispatchEvent({type:'transformed', changeType: ['orientation'], matrixBefore })//add drag.pivot = I; } } dropRotationHandle(e){ this.dragging = false; this.setActiveHandle(null); this.dispatchEvent({type:'stopDrag', handle:'rotation'})//add } dragTranslationHandle(e){//---大改,参考transformControls,为了加上xyz xy yz xz 这四个方向的变换。 (但感觉好像plane上有丢丢延迟?是因为drag延迟还是worldmatrix没更新) let drag = e.drag; let handle = this.activeHandle; let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera(); if(handle && this.selection[0]){ let posWorld = this.selection[0].getWorldPosition(new THREE.Vector3()); //是需要世界坐标吗 if(!drag.intersectionStart){ drag.intersectionStart = drag.location; drag.worldPositionStart = posWorld drag.objectQua = this.selection[0].quaternion.clone()//不考虑父级 drag.objectQuaInv = drag.objectQua.clone().invert() this.dragging = true } if(drag.intersectionStart){ let pointer = this.viewer.inputHandler.pointer let ray = Utils.mouseToRay(pointer, camera); let normal = viewer.mainViewport.view.direction drag.dragPlane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, posWorld/* drag.worldPositionStart */)//过center的与视线垂直的平面 let I = ray.intersectPlane(drag.dragPlane, new THREE.Vector3()); if (I) { let offset = new THREE.Vector3().subVectors( I , drag.worldPositionStart ); //let offset = new THREE.Vector3().subVectors(iOnLine, drag.worldPositionStart); if(!drag.pointStart){ drag.pointStart = offset }else{ drag.pointEnd = offset let diff = new THREE.Vector3().subVectors(drag.pointEnd, drag.pointStart) diff.applyQuaternion(drag.objectQuaInv) // 得到在该物体local空间上的offset if(!handle.name.includes('x')) diff.x = 0 if(!handle.name.includes('y')) diff.y = 0 if(!handle.name.includes('z')) diff.z = 0 //恢复为world offset diff.applyQuaternion(drag.objectQua) //------------- let matrixBefore = this.selection[0].matrix.clone() this.selection[0].position.copy( diff ).add( drag.worldPositionStart ); for (let selection of this.selection) { selection.dispatchEvent({ type: "position_changed", object: selection }); } this.dispatchEvent({type:'transformed', changeType: ['position'], matrixBefore})//add } } } } } dropTranslationHandle(e){ this.dragging = false; this.setActiveHandle(null); this.dispatchEvent({type:'stopDrag', handle:'translation'})//add } dropScaleHandle(e){ this.dragging = false; this.setActiveHandle(null); this.dispatchEvent({type:'stopDrag', handle:'scale'})//add } dragScaleHandle(e){ let drag = e.drag; let handle = this.activeHandle; if(!handle)return let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera(); if(!drag.intersectionStart){ drag.intersectionStart = drag.location; drag.objectStart = drag.object.getWorldPosition(new THREE.Vector3()); drag.handle = handle; let start = drag.intersectionStart; let dir = new THREE.Vector4(...handle.alignment, 0).applyMatrix4(this.scene.matrixWorld); let end = new THREE.Vector3().addVectors(start, dir); let line = new THREE.Line3(start.clone(), end.clone()); drag.line = line; let normal if(camera.type == 'OrthographicCamera'){//xzw add normal = new THREE.Vector3(0,0,-1).applyQuaternion(camera.quaternion) }else{ let camOnLine = line.closestPointToPoint(camera.position, false, new THREE.Vector3()); normal = new THREE.Vector3().subVectors(camera.position, camOnLine); } let plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, drag.intersectionStart); //过轴线的一个能铺满屏幕的平面 drag.dragPlane = plane; drag.pivot = drag.intersectionStart; //Utils.debugSphere(viewer.scene.scene, drag.pivot, 0.05); }else{ handle = drag.handle; } this.dragging = true; if(drag.dragPlane){//xzw add 因有时候没有 let pointer = this.viewer.inputHandler.pointer let domElement = this.viewer.renderer.domElement; let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight); let I = ray.intersectPlane(drag.dragPlane, new THREE.Vector3()); if (I) { let iOnLine = drag.line.closestPointToPoint(I, false, new THREE.Vector3()); let direction = handle.alignment.reduce( (a, v) => a + v, 0); let toObjectSpace = this.selection[0].matrixWorld.clone().invert(); let iOnLineOS = iOnLine.clone().applyMatrix4(toObjectSpace); let pivotOS = drag.pivot.clone().applyMatrix4(toObjectSpace); let diffOS = new THREE.Vector3().subVectors(iOnLineOS, pivotOS); let dragDirectionOS = diffOS.clone().normalize(); if(iOnLine.distanceTo(drag.pivot) === 0){ dragDirectionOS.set(0, 0, 0); } let dragDirection = dragDirectionOS.dot(new THREE.Vector3(...handle.alignment)); let diff = new THREE.Vector3().subVectors(iOnLine, drag.pivot); let diffScale = new THREE.Vector3(...handle.alignment).multiplyScalar(diff.length() * direction * dragDirection); let diffPosition = diff.clone().multiplyScalar(0.5); let matrixBefore = this.selection[0].matrix.clone() for (let selection of this.selection) { //xzw 改:否则不跟手 let diffScale_ = diffScale.clone().divide(selection.boundingBox.getSize(new THREE.Vector3)) selection.scale.add(diffScale_); //selection.scale.add(diffScale); selection.scale.x = Math.max(0.1, selection.scale.x); selection.scale.y = Math.max(0.1, selection.scale.y); selection.scale.z = Math.max(0.1, selection.scale.z); selection.position.add(diffPosition); selection.dispatchEvent({ type: "position_changed", object: selection }); selection.dispatchEvent({ type: "scale_changed", object: selection }); this.dispatchEvent({type:'transformed', changeType: ['position','scale'], matrixBefore})//add } drag.pivot.copy(iOnLine); //Utils.debugSphere(viewer.scene.scene, drag.pivot, 0.05); } } } setActiveHandle(handle){ if(this.dragging){ return; } if(this.activeHandle === handle){ return; } this.activeHandle = handle; if(handle === null){ for(let handleName of Object.keys(this.handles)){ let handle = this.handles[handleName]; handle.node.setOpacity(0); } } viewer.dispatchEvent({type:'CursorChange', action: this.activeHandle ? 'add' : 'remove', name:'hoverTranHandle'}) if(!hideFocusHandles){ for(let handleName of Object.keys(this.focusHandles)){ let handle = this.focusHandles[handleName]; if(this.activeHandle === handle){ handle.node.setOpacity(1.0); }else{ handle.node.setOpacity(OpaWhenNotSelect) } } } for(let handleName of Object.keys(this.translationHandles)){ let handle = this.translationHandles[handleName]; if(this.activeHandle === handle){ handle.node.setOpacity(1.0); }else{ handle.node.setOpacity(OpaWhenNotSelect) } } for(let handleName of Object.keys(this.rotationHandles)){ let handle = this.rotationHandles[handleName]; //if(this.activeHandle === handle){ // handle.node.setOpacity(1.0); //}else{ // handle.node.setOpacity(OpaWhenNotSelect) //} handle.node.setOpacity(OpaWhenNotSelect); } for(let handleName of Object.keys(this.scaleHandles)){ let handle = this.scaleHandles[handleName]; if(this.activeHandle === handle){ handle.node.setOpacity(1.0); if(!hideFocusHandles){ let relatedFocusHandle = this.focusHandles[handle.name.replace("scale", "focus")]; let relatedFocusNode = relatedFocusHandle.node; relatedFocusNode.setOpacity(OpaWhenNotSelect); } for(let translationHandleName of Object.keys(this.translationHandles)){ let translationHandle = this.translationHandles[translationHandleName]; translationHandle.node.setOpacity(OpaWhenNotSelect); } //let relatedTranslationHandle = this.translationHandles[ // handle.name.replace("scale", "translation").replace(/[+-]/g, "")]; //let relatedTranslationNode = relatedTranslationHandle.node; //relatedTranslationNode.setOpacity(OpaWhenNotSelect); }else{ handle.node.setOpacity(OpaWhenNotSelect) } } if(handle){ handle.node.setOpacity(1.0); } viewer.dispatchEvent('content_changed') } update () { if(this.selection.length === 1){ this.scene.visible = true; this.scene.updateMatrix(); this.scene.updateMatrixWorld(); let selected = this.selection[0]; let world = selected.matrixWorld; let camera = this.viewer.mainViewport.camera//this.viewer.scene.getActiveCamera(); let domElement = this.viewer.renderer.domElement; let center = selected.boundingBox.getCenter(new THREE.Vector3()).clone().applyMatrix4(selected.matrixWorld); this.scene.scale.copy(selected.boundingBox.getSize(new THREE.Vector3()).multiply(selected.scale)); this.scene.position.copy(center); this.scene.rotation.copy(selected.rotation); //这里只考虑当前子级的scale rotation //如果是世界坐标 (缩放方向有bug。) /* let boundingBox = selected.boundingBox.clone().applyMatrix4(selected.matrixWorld); let center = boundingBox.getCenter(new THREE.Vector3()) this.scene.position.copy(center); this.scene.scale.copy(boundingBox.getSize(new THREE.Vector3())); */ this.scene.updateMatrixWorld(); { // adjust rotation handles if(!this.dragging){ if(this.modesEnabled.rotation || this.modesEnabled.translation){ let tWorld = this.scene.matrixWorld; let tObject = tWorld.clone().invert(); let camObjectPos = camera.getWorldPosition(new THREE.Vector3()).applyMatrix4(tObject); if(this.modesEnabled.translation){//add ['xy','yz','xz'].forEach(axis=>{ let handle = this.translationHandles["translation.plane."+axis] let pos = handle.node.children[0].position camObjectPos.x && (pos.x = Math.sign(camObjectPos.x) * Math.abs(pos.x)) camObjectPos.y && (pos.y = Math.sign(camObjectPos.y) * Math.abs(pos.y)) camObjectPos.z && (pos.z = Math.sign(camObjectPos.z) * Math.abs(pos.z)) }) } if(this.modesEnabled.rotation){ let above = camObjectPos.z > 0; let below = !above; let PI_HALF = Math.PI / 2; let x = this.rotationHandles["rotation.x"].node.rotation; let y = this.rotationHandles["rotation.y"].node.rotation; let z = this.rotationHandles["rotation.z"].node.rotation; x.order = "ZYX"; y.order = "ZYX"; if(above){ if(camObjectPos.x > 0 && camObjectPos.y > 0){ x.x = 1 * PI_HALF; y.y = 3 * PI_HALF; z.z = 0 * PI_HALF; }else if(camObjectPos.x < 0 && camObjectPos.y > 0){ x.x = 1 * PI_HALF; y.y = 2 * PI_HALF; z.z = 1 * PI_HALF; }else if(camObjectPos.x < 0 && camObjectPos.y < 0){ x.x = 2 * PI_HALF; y.y = 2 * PI_HALF; z.z = 2 * PI_HALF; }else if(camObjectPos.x > 0 && camObjectPos.y < 0){ x.x = 2 * PI_HALF; y.y = 3 * PI_HALF; z.z = 3 * PI_HALF; } }else if(below){ if(camObjectPos.x > 0 && camObjectPos.y > 0){ x.x = 0 * PI_HALF; y.y = 0 * PI_HALF; z.z = 0 * PI_HALF; }else if(camObjectPos.x < 0 && camObjectPos.y > 0){ x.x = 0 * PI_HALF; y.y = 1 * PI_HALF; z.z = 1 * PI_HALF; }else if(camObjectPos.x < 0 && camObjectPos.y < 0){ x.x = 3 * PI_HALF; y.y = 1 * PI_HALF; z.z = 2 * PI_HALF; }else if(camObjectPos.x > 0 && camObjectPos.y < 0){ x.x = 3 * PI_HALF; y.y = 0 * PI_HALF; z.z = 3 * PI_HALF; } } } } } // adjust scale of components for(let handleName of Object.keys(this.handles)){ let handle = this.handles[handleName]; let node = handle.node; //xzw add:---- -当该轴正对相机时隐藏。(主要针对ortho类型camera。 if(!Potree.Utils.getObjVisiByReason(node,'modeForce') )continue; let alignment = handle.alignment; if(alignment && (!handleName.includes('rotation') || camera.type == 'OrthographicCamera')){//旋转的话正常都应该显示 let normal let dir = new THREE.Vector3(...alignment).applyQuaternion(this.scene.quaternion) if(camera.type == 'OrthographicCamera'){ normal = new THREE.Vector3(0,0,-1).applyQuaternion(camera.quaternion) }else{ normal = new THREE.Vector3().subVectors(center, camera.position).normalize() } let ifOnLine if(handleName.includes('rotation') || handleName.includes('plane')){ // 旋转轴和视线垂直时隐藏 ifOnLine = Math.abs(dir.dot(normal)) < 0.1 }else{ ifOnLine = Math.abs(dir.dot(normal)) > 0.995 } Potree.Utils.updateVisible(node, 'faceToCamHide', !ifOnLine) }else{ Potree.Utils.updateVisible(node, 'faceToCamHide', true) } if(!node.visible)continue; //------------------------------------------------------------------------ if(handle.dontScale)continue; //add let handlePos = node.getWorldPosition(new THREE.Vector3()); let distance = handlePos.distanceTo(camera.position); let pr = Utils.projectedRadius(1, camera, distance, domElement.clientWidth, domElement.clientHeight); let ws = node.parent.getWorldScale(new THREE.Vector3()); let s = (ScaleRatio / pr); let scale = new THREE.Vector3(s, s, s).divide(ws); let rot = new THREE.Matrix4().makeRotationFromEuler(node.rotation); //需要使用到旋转,所以我把设置scale的移到旋转后了,否则在视图上下旋转的分界线处rotateHandel会被拉长从而闪烁。 let rotInv = rot.clone().invert(); scale.applyMatrix4(rotInv); scale.x = Math.abs(scale.x); scale.y = Math.abs(scale.y); scale.z = Math.abs(scale.z); node.scale.copy(scale); } } }else{ this.scene.visible = false; } } onPointerMove(){ let pointer = this.viewer.inputHandler.pointer; let camera = this.viewer.mainViewport.camera if( this.selection.length === 1 && !this.dragging ){ //xzw 添加dragging条件 let ray = Utils.mouseToRay(pointer, camera ); let raycaster = new THREE.Raycaster(ray.origin, ray.direction); raycaster.layers.enableAll()//add let pickVolumes = this.pickVolumes.filter(v=>{ let mode = v.handle.split('.')[0]; if(!this.modesEnabled[mode])return return v.parent.parent.visible //可能被隐藏该轴 }) let intersects = raycaster.intersectObjects(pickVolumes, true); intersects = intersects.sort(function(a,b){ let order2 = b.object.renderOrder || 0 let order1 = a.object.renderOrder || 0 return order2-order1 }) // 降序 if(intersects.length > 0){ let I = intersects[0]; let handleName = I.object.handle; //console.log(handleName) this.setActiveHandle(this.handles[handleName]); }else{ this.setActiveHandle(null); } } } }; /* note: transformationTool.scene会跟随选中物体,其scale就是boundingbox的大小。因此transformationTool.frame这个框也会跟着缩放 */