| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- import * as THREE from "../../../libs/three.js/build/three.module.js";
- import {LineDraw, MeshDraw} from "../utils/DrawUtil.js";
- import {TextSprite} from './TextSprite.js'
- import Sprite from './Sprite.js'
- import DepthBasicMaterial from "../materials/DepthBasicMaterial.js";
- import CursorDeal from "../utils/CursorDeal.js";
- const depthMatProp = { //为了防止拉远后因放大而一半嵌入墙。
- useDepth : true ,
- startClipDis : 0.5,
- clipDistance : 1,//消失距离
- startOcclusDis: 0.5,
- occlusionDistance: 0.9,//变为backColor距离
- maxOcclusionFactor:0.7,
- maxClipFactor:1
-
- }
-
- const planeGeo = new THREE.PlaneBufferGeometry(1,1)
- let texLoader = new THREE.TextureLoader()
- let lineMat, dragPointMat
-
- const defaultLineLength = 1
- const defaultSpotScale = 0.35
- const titleHeight = {uponSpot:0.1 }//title底部和spot顶端间隔
- const Vectors = {
- UP : new THREE.Vector3(0,1,0),
- ZERO: new THREE.Vector3()
- }
- class Tag extends THREE.Shim.FollowRootObject{
- constructor(o){
-
- super(o.root)
-
- this.title = o.title
- this.fontsize = o.fontsize
- this.lineLength = o.lineLength != void 0 ? o.lineLength : defaultLineLength
- this.position.copy(o.position)
- this.normal = o.normal != void 0 ? o.normal : new THREE.Vector3(0,0, 1)
-
-
- this.build(o)
- this.bindEvent()
- this.dragEnable = true
-
- }
-
- set dragEnable(state){
- this.lineDragPoint.visible = state
- }
- get dragEnable(){
- return this.lineDragPoint.visible
- }
-
-
-
- build(o){
-
- lineMat || (lineMat = LineDraw.createFatLineMat(Object.assign({},depthMatProp, {
- color: '#ffffff', useDepth :true,
- lineWidth: 1
- })))
- let group = new THREE.Object3D()
- this.spot = new THREE.Mesh(planeGeo, new DepthBasicMaterial(Object.assign({},depthMatProp,{
- transparent:true,
- })))
- this.spot.name = 'spot'
- this.spot.scale.set(defaultSpotScale,defaultSpotScale,defaultSpotScale)
- this.spot.renderOrder = this.spot.pickOrder = Potree.config.renderOrders.tag.spot;
- Potree.settings.isOfficial || this.changeMap(Potree.resourcePath+'/textures/spot_default.png')
-
-
- this.line = LineDraw.createFatLine([], {mat:lineMat})
- this.line.name = 'tagLine'
- this.line.renderOrder = this.line.pickOrder = Potree.config.renderOrders.tag.line;
-
- this.titleLabel = new TextSprite(Object.assign({},depthMatProp,{
- root: group, text:'', sizeInfo:{width2d:150},
- textColor:{r:255,g:255,b:255,a:1.0},
- backgroundColor:{r:0,g:0,b:0,a:0.7},
- borderRadius: 6,
- fontsize: this.fontsize || 14, fontWeight:'',//thick
- renderOrder : Potree.config.renderOrders.tag.label,
- pickOrder: Potree.config.renderOrders.tag.label,
- useDepth : true ,
- maxLineWidth: 300,
- transform2Dpercent:{x:0,y:0.5}, //向上移动一半
- textAlign: Potree.settings.isOfficial && 'left'
- })) //更新sprite时,实际更新的是root: spot的矩阵
- this.setTitle(this.title)
- this.updateTitlePos()
- group.add(this.titleLabel)
- group.add(this.spot)
- this.add(group);
- this.add(this.line)
- viewer.tags.add(this)
-
- if(!dragPointMat){
- let map = texLoader.load(Potree.resourcePath+'/textures/whiteCircle.png',()=>{})
- dragPointMat = {
- default: new THREE.MeshBasicMaterial({
- map, transparent:true, color:'#0fe', opacity:0, depthTest:false,
- }),
- hover: new THREE.MeshBasicMaterial({
- map, transparent:true, color:'#0fe', opacity:0.4, depthTest:false,
- })
- }
- }
-
- this.lineDragPoint = new THREE.Mesh(planeGeo, dragPointMat.default) //修改线高度时出现的小圆点
- this.lineDragPoint.scale.set(0.15,0.15,0.15);
- this.lineDragPoint.name = 'lineDragPoint'
- this.lineDragPoint.renderOrder = this.lineDragPoint.pickOrder = Potree.config.renderOrders.tag.spot + 3;
- group.add(this.lineDragPoint)
-
-
- this.updatePose()
-
- }
-
-
-
- bindEvent(){
-
- let hoverState = {}, grabbingObject
-
- let setDragPointState = (state)=>{
- this.lineDragPoint.material = state ? dragPointMat.hover : dragPointMat.default
- this.spot.material.opacity = state ? 0.5 : 1
- this.titleLabel.sprite.material.opacity = state ? 0.5 : 1
- }
-
- {
- //因为只有有intersect时才能拖拽,所以写得比较麻烦
- let cursor = {hoverGrab:0, grabbing:0}
- let setCursor = (name, action)=>{
- let state = action == 'add' ? 1 : 0
- if(state != cursor[name]){
- cursor[name] = state
- viewer.dispatchEvent({
- type : "CursorChange", action, name
- })
- }
- }
-
- [this.line, this.spot, this.lineDragPoint].forEach(e=>e.addEventListener('mousemove',(e)=>{
- hoverState[e.target.name] = 1
- if(this.dragEnable && (viewer.inputHandler.intersect || hoverState['lineDragPoint'])){//能拖拽时
- setCursor('hoverGrab', 'add')
- }else{
- setCursor('hoverGrab', 'remove')
- }
- }));
- [this.line, this.spot, this.lineDragPoint].forEach(e=>e.addEventListener('mouseleave',(e)=>{
- hoverState[e.target.name] = 0
- if(!Object.values(hoverState).some(e=>e)){//都没hover才取消
- setCursor('hoverGrab', 'remove')
- }
- /* if(!hoverState.line && !hoverState.spot && !hoverState.label){
- this.dispatchEvent('mouseleave')
- } */
- }));
-
-
-
- [this.line, this.spot, this.lineDragPoint].forEach(e=>e.addEventListener('drag',(e)=>{
- if(this.dragEnable && cursor.grabbing){
- if(e.target.name == 'lineDragPoint'){
- this.dragLineLen(e)
- }else{
- let info = viewer.tagTool.getPoseByIntersect(e)
- info && this.changePos(info)
- }
- }
- }));
- [this.line, this.spot, this.lineDragPoint].forEach(e=>e.addEventListener('startDragging',(e)=>{
- this.dragEnable && (viewer.inputHandler.intersect || e.target.name == 'lineDragPoint') && setCursor('grabbing', 'add')
- grabbingObject = e.target.name
- grabbingObject == 'lineDragPoint' && setDragPointState(true)
- }));
- [this.line, this.spot, this.lineDragPoint].forEach(e=>e.addEventListener('drop',(e)=>{
- this.dragEnable && setCursor('grabbing', 'remove')
- grabbingObject = null
- hoverState['lineDragPoint'] || setDragPointState(false)
- }));
- //拖拽线来移动。虽然理想方式是拟真,拖拽时不改变在线上的位置,使之平移,但仔细想想似乎办不到。因为墙面normal是不固定的,尤其在交界处难以确定。不知鼠标在空中的位置,即使是平行镜头移动也无法满足所有情况。matterport是加了底座,移动也是改变底座中心。
- }
-
- {
- let mouseover = (e)=>{
- this.dispatchEvent('mouseover')
- }
- let mouseleave = (e)=>{
- //if(!hoverState.line && !hoverState.spot && !hoverState.label){
- this.dispatchEvent('mouseleave')
- //}
- }
- let click = (e)=>{
- this.dispatchEvent('click')
- }
- this.spot.addEventListener('mouseover',mouseover)
- this.spot.addEventListener('mouseleave',mouseleave)
- this.titleLabel.addEventListener('mouseover',mouseover)
- this.titleLabel.addEventListener('mouseleave',mouseleave)
- this.spot.addEventListener('click',click)
- this.titleLabel.addEventListener('click',click)
- }
-
- this.titleLabel.sprite.addEventListener('spriteUpdated',()=>{
- this.updateDepthParams()
- })
-
-
- //-----------set line length
-
- // CursorDeal
- this.lineDragPoint.addEventListener('mouseover',(e)=>{
- grabbingObject || setDragPointState(true)
- })
- this.lineDragPoint.addEventListener('mouseleave',(e)=>{
- grabbingObject != 'lineDragPoint' && setDragPointState(false)
- })
-
-
-
- }
-
-
-
-
- updateDepthParams(){//为了避免热点嵌入墙壁,实时根据其大小更新材质系数。 但是在倾斜的角度看由于遮挡距离很大肯定会嵌入的
- let s = this.titleLabel.parent.scale.x
- let names = ['clipDistance', 'occlusionDistance', 'startClipDis', 'startOcclusDis']
- let titleSize = Math.max(this.titleLabel.sprite.scale.x, this.titleLabel.sprite.scale.y) * s
-
- names.forEach(name=>{
- this.titleLabel.sprite.material.uniforms[name].value = depthMatProp[name] * titleSize
- })
-
- if(this.onMesh){//not sprite,还原。 大概能不被崎岖的3dtiles地面遮住就行
- names.forEach(name=>{
- this.spot.material.uniforms[name].value = depthMatProp[name]
- })
- }else{
- let spotSize = this.spot.scale.x * s
- names.forEach(name=>{
- this.spot.material.uniforms[name].value = depthMatProp[name] * spotSize
- })
-
- }
-
-
- }
-
-
- updatePose( ){
- let endPos = this.normal.clone().multiplyScalar(this.lineLength)
- LineDraw.updateLine(this.line, [new THREE.Vector3(0,0,0), endPos])
- this.titleLabel.parent.position.copy(endPos)
- this.titleLabel.updatePose()
- viewer.dispatchEvent('content_changed')
-
-
- }
-
- changeLineLen(len){
- if(len == this.lineLength)return
- this.lineLength = parseFloat(len)
- this.updatePose()
- }
-
- dragLineLen(e){ //拖拽线的顶端修改线长度
- let endPos = this.normal.clone().multiplyScalar(this.lineLength).applyMatrix4(this.matrixWorld)
- let normal = this.normal.clone().applyQuaternion(this.getWorldQuaternion(new THREE.Quaternion))
- const projected = endPos.clone().project(e.drag.dragViewport.camera);
- projected.x = e.pointer.x
- projected.y = e.pointer.y
-
- const unprojected = projected.clone().unproject(e.drag.dragViewport.camera);
- let moveVec = new THREE.Vector3().subVectors(unprojected, endPos);
- moveVec = moveVec.projectOnVector(normal)
-
-
- let newLength = Math.max(0, this.lineLength + moveVec.dot(normal) )
- //console.log(moveVec,newLength)
- this.changeLineLen(newLength)
- this.dispatchEvent('dragLineLen')
- }
-
-
- changePos(info){//注:onMesh时在非平地上拖拽,热点旋转会一直变
- this.position.copy(info.position)
- this.normal.copy(info.normal)
- let root = this.root
- this.root = info.root
- root != this.root && this.updateMatrixWorld() //防止拖动到另一个scale不同的模型上时sprite会缩放闪烁
- this.setNorQua()
- this.updatePose()
- this.dispatchEvent('posChanged')
- viewer.dispatchEvent('content_changed')
- }
-
- changeOnMesh(onMesh){//是否贴在mesh上
- //if(this.title == 'single2') debugger
- this.onMesh = onMesh
- if(onMesh){//贴mesh上时不是sprite,且可设置旋转值
- this.add(this.spot)
- this.titleLabel.position.y = 0
- this.setNorQua()
- this.spot.renderOrder = Potree.config.renderOrders.tag.onMesh.spot // 防止遮住线
- this.line.renderOrder = Potree.config.renderOrders.tag.onMesh.line
- }else{
- this.titleLabel.parent.add(this.spot)
- this.updateTitlePos()
- this.spot.position.set(0,0,0)
- this.spot.quaternion.set(0,0,0,1)//this.titleLabel.waitUpdate()
- this.realFaceAngle = 0
- this.spot.renderOrder = Potree.config.renderOrders.tag.spot //还原
- this.line.renderOrder = Potree.config.renderOrders.tag.line
- }
- Potree.Utils.updateVisible(this.line,'hideTitle', !this.titleLabel.visible && onMesh ? false : true)
- this.updateDepthParams()
- viewer.dispatchEvent('content_changed')
- }
-
-
- setFaceAngle(faceAngle = 0) {
- //if(this.title == 'single2') debugger
- this.faceAngle = faceAngle //先记录,但非onMesh时不会用
- if(!this.onMesh) return
-
- let delta = faceAngle - (this.realFaceAngle || 0)
- //this.plane.quaternion.setFromAxisAngle(new THREE.Vector3(0, 0, 1), THREE.MathUtils.degToRad(-faceAngle))
- this.spot.rotateOnAxis(new THREE.Vector3(0,0,1), THREE.Math.degToRad(delta) )
- //this.updateLabelPose()
- this.realFaceAngle = faceAngle
- viewer.dispatchEvent('content_changed')
- }
-
-
-
-
- setNorQua() {
- if(!this.onMesh)return
- this.spot.quaternion.setFromRotationMatrix(new THREE.Matrix4().lookAt(this.normal, Vectors.ZERO, Vectors.UP)) //重算quaternion
- this.realFaceAngle = 0 //quaternion被重置了,所以再设置一下faceAngle
- this.setFaceAngle(this.faceAngle)
-
- this.spot.position.copy(this.normal).multiplyScalar(0.01) //在mesh之上偏移一点
-
- }
-
- /*
- 如果要像四维看看那样,在地面上时保持初始转向镜头的话,需要矫正且保存quaternion。且要根据世界normal判断是否在地面, 会随着模型改变, 所以也没法仅保存normal去矫正。
- 要不然就要直接改变faceAngle
- */
-
-
-
-
-
- setTitle(title=''){
- this.titleLabel.setText(title)
- this.setTitleVisi(title instanceof Array || title.trim() != '', 'noText')
- viewer.dispatchEvent('content_changed')
- }
-
- setTitleVisi(v, reason=''){
- Potree.Utils.updateVisible(this.titleLabel, 'hideTitle-'+reason, v)
- //tag.onMesh && Potree.Utils.updateVisible(tag.line, 'hideTitle-'+reason, v)
- //line的可见性比较复杂,所以干脆跟随title的,reason不记录那么多
- this.onMesh && Potree.Utils.updateVisible(this.line, 'hideTitle', this.titleLabel.visible )
- viewer.dispatchEvent('content_changed')
- }
-
- setFontSize(fontsize){
- this.titleLabel.fontsize = this.fontsize = fontsize
- this.titleLabel.updateTexture();
- //this.updateTitlePos()
- viewer.dispatchEvent('content_changed')
- }
-
- changeSpotScale(s){
- s *= defaultSpotScale
- this.spot.scale.set(s,s,s)
- this.updateTitlePos()
- viewer.dispatchEvent('content_changed')
- }
-
- updateTitlePos(){
- this.onMesh || (this.titleLabel.position.y = titleHeight.uponSpot + this.spot.scale.x / 2)
- }
-
- changeMap(url){
- let map = texLoader.load(url,()=>{
- viewer.dispatchEvent('content_changed')
- })
- this.spot.material.map = map
-
- }
-
-
-
-
- dispose(){
- this.parent.remove(this);
- this.titleLabel?.dispose()
- viewer.dispatchEvent('content_changed')
-
- }
-
-
-
- }
-
- export default Tag
|