|
@@ -63,6 +63,7 @@ var g_weixinObj = {
|
|
|
};
|
|
|
|
|
|
var settings = {
|
|
|
+ markerHeight: 0.05,//距离地板高出多少,
|
|
|
hotClickEvent: {
|
|
|
video: {
|
|
|
playAndPause: true,
|
|
@@ -1582,6 +1583,290 @@ function initByTHREE(THREE) {
|
|
|
}
|
|
|
window.RoomLabel = RoomLabel;
|
|
|
|
|
|
+ window.initRouteArrow = ()=>{
|
|
|
+ if(window.isEdit)return
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if(!window.DATA.route?.data || Object.keys(window.DATA.route.data).length == 0)return
|
|
|
+
|
|
|
+ let {hide, gradualShow, opacityShine, data={}} = window.DATA.route || {} //hide 默认是否隐藏,若隐藏也可以通过函数展示
|
|
|
+
|
|
|
+ let panos = player.model.panos
|
|
|
+ panos.routeNextMap = {} //下一个
|
|
|
+ panos.routePreMap = {} //上一个
|
|
|
+ panos.list.forEach((pano,i)=>{
|
|
|
+ panos.routePreMap[pano.id] = []
|
|
|
+ panos.routeNextMap[pano.id] = data[pano.id] ? data[pano.id].map(id=>panos.get(id)) : []
|
|
|
+ })
|
|
|
+ for(let panoId in data){
|
|
|
+ data[panoId].forEach(id=>{
|
|
|
+ panos.routePreMap[id].push(panos.get(panoId))
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ var arrowInfo = {
|
|
|
+ animateDur : 5000,
|
|
|
+ showDur: 1000,
|
|
|
+ minOpa: 0.2,
|
|
|
+ maxOpa: 0.5
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ let arrowTex = Texture.load('images/arrow.png')
|
|
|
+ arrowTex.anisotropy = 4
|
|
|
+
|
|
|
+
|
|
|
+ let arrowMat = new THREE.MeshBasicMaterial({
|
|
|
+ name: 'arrow',
|
|
|
+ transparent:true,
|
|
|
+ map:arrowTex,
|
|
|
+ side:2,
|
|
|
+ opacity: arrowInfo.maxOpa,
|
|
|
+ //depthTest:false
|
|
|
+ depthWrite:false, //防止和导览路线重叠闪烁
|
|
|
+ })
|
|
|
+ let mats = {
|
|
|
+ default: arrowMat,
|
|
|
+ fadeIn: arrowMat.clone()
|
|
|
+ }
|
|
|
+ mats.fadeIn.name = 'fadeInArrow'
|
|
|
+ let plane = new THREE.PlaneBufferGeometry(1,1)
|
|
|
+
|
|
|
+
|
|
|
+ let arrows = new THREE.Object3D; arrows.name = 'groundArrows'
|
|
|
+
|
|
|
+ player.model.add(arrows)
|
|
|
+
|
|
|
+
|
|
|
+ var createArrow = function(mat){
|
|
|
+
|
|
|
+ var arrow = new THREE.Mesh(plane, mat)
|
|
|
+ arrow.name = 'arrow'
|
|
|
+
|
|
|
+ let s = 0.15
|
|
|
+ arrow.scale.set(s,s,s)
|
|
|
+
|
|
|
+ arrows.add(arrow)
|
|
|
+ return arrow
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ var updateArrowPose = function(from, to, mat ){
|
|
|
+
|
|
|
+ var vec = to.floorPosition.clone().clone().sub(from.floorPosition).setY(0);
|
|
|
+ let spaceDis = 0.4//箭头之间的间距
|
|
|
+ let margin = 0.3 //marker端需要留一点距离
|
|
|
+ let sliceCount = Math.max(2, Math.round((vec.length()-margin) / spaceDis)) //分段
|
|
|
+ let arrowCount = sliceCount - 1
|
|
|
+
|
|
|
+ let dir = vec.clone().normalize()
|
|
|
+ let dir2d = new THREE.Vector2(dir.x, dir.z)
|
|
|
+ let angle = dir2d.angle() - Math.PI/2
|
|
|
+ let sliceLen = (vec.length()-margin) / sliceCount
|
|
|
+ let i = arrowCount
|
|
|
+
|
|
|
+ while(i>0){
|
|
|
+
|
|
|
+ let pos = from.floorPosition.clone().add(dir.clone().multiplyScalar(margin/2 + i*sliceLen))
|
|
|
+ pos.y+=settings.markerHeight
|
|
|
+ let arrow = createArrow(mat)
|
|
|
+ arrow.name = 'arrow:'+from.id+"-"+to.id+"|"+i
|
|
|
+ arrow.rotation.set(Math.PI/2, 0, angle);
|
|
|
+ arrow.position.copy(pos)
|
|
|
+ i--
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var updateArrowOpacity = function(e){//不停更新所有arrow的透明度
|
|
|
+ var transition = function(a){
|
|
|
+ if(!arrows.visible)return
|
|
|
+
|
|
|
+ var opa = a > 0.5 ? 2-a*2 : 2*a ;
|
|
|
+ opa = arrowInfo.maxOpa*opa+ arrowInfo.minOpa*(1-opa)
|
|
|
+
|
|
|
+ mats.default.opacity = opa
|
|
|
+ mats.fadeIn.opacity = opa * mats.fadeIn.opacity2
|
|
|
+
|
|
|
+ }
|
|
|
+ transitions.start(transition, arrowInfo.animateDur, updateArrowOpacity, 0, easing.easeInOutCubic, "updateArrowOpacity")
|
|
|
+ }
|
|
|
+
|
|
|
+ var fadeInArrow = function(){
|
|
|
+
|
|
|
+ transitions.cancelById('updateArrowOpacity2')
|
|
|
+ var arrows_ = arrows.children.filter(e=> e.material == mats.fadeIn)
|
|
|
+ if(arrows_.length == 0)return
|
|
|
+
|
|
|
+
|
|
|
+ mats.fadeIn.opacity = 0
|
|
|
+
|
|
|
+ var transition = function(a){
|
|
|
+ if(!opacityShine) mats.fadeIn.opacity = a * arrowInfo.maxOpa
|
|
|
+ else mats.fadeIn.opacity2 = a
|
|
|
+ }
|
|
|
+ transitions.start(transition, arrowInfo.showDur, function done(){
|
|
|
+ arrows_.forEach(e=>e.material = mats.default)
|
|
|
+ }, 0, easing.easeInOutCubic, "updateArrowOpacity", "updateArrowOpacity2")
|
|
|
+ }
|
|
|
+
|
|
|
+ var lastArrowPanos = []
|
|
|
+ var updateArrow = function(){//根据当前pano更新
|
|
|
+
|
|
|
+ if(player.mode != 'panorama' || hide){ //飞出
|
|
|
+ arrows.visible = false
|
|
|
+ lastArrowPanos = []
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ arrows.visible = true
|
|
|
+ let currentPano = player.currentPano
|
|
|
+
|
|
|
+
|
|
|
+ //先获取所有需要箭头的pano
|
|
|
+ var maxDistance = 6;//该距离内pano可见箭头
|
|
|
+ var maxPathCount = 8;
|
|
|
+ var dis = 0
|
|
|
+ var curPano = currentPano;
|
|
|
+ var panos_ = [];
|
|
|
+
|
|
|
+ var getAngle = function(pano1,pano2){
|
|
|
+ let dir = new THREE.Vector3().subVectors(pano1.position,pano2.position)
|
|
|
+ dir = new THREE.Vector2(dir.x, dir.z)
|
|
|
+ return dir.angle()
|
|
|
+ }
|
|
|
+ var search = function(pano, path=[pano], angles=[]){//多分支搜寻
|
|
|
+
|
|
|
+ var neighbor = panos.routeNextMap[pano.id];
|
|
|
+ if(!neighbor || !neighbor.length)return //path.length>1 && console.log('branchPath',path);
|
|
|
+
|
|
|
+
|
|
|
+ neighbor.forEach(e=>{
|
|
|
+ let branchPath = path.slice(), angles_ = angles.slice()
|
|
|
+ if(panos_.find(arr=>arr[0]==e))return //console.log('不回头branchPath',branchPath); //不回头
|
|
|
+ dis = e.floorPosition.distanceTo(currentPano.floorPosition)
|
|
|
+
|
|
|
+ branchPath.push(e)
|
|
|
+ if(branchPath.length>2){//不折回,否则感觉在面前饶了一圈回来很难看
|
|
|
+ let i=0
|
|
|
+ while(i<branchPath.length-1){ //补全angles
|
|
|
+ if(angles_[i] == void 0){
|
|
|
+ angles_[i] = getAngle(branchPath[i], branchPath[i+1])
|
|
|
+ }
|
|
|
+ i++
|
|
|
+ }
|
|
|
+ let lastAngle = angles_[branchPath.length-2]//getAngle(branchPath[i], e)
|
|
|
+ let reverse = angles_.find((angle, n)=> {
|
|
|
+ let angleDiff = Math.abs(( lastAngle - angle) % (Math.PI*2) ) //越远限制越大
|
|
|
+ let minDiff = math.linearClamp(branchPath.length,[3,6],[0.2,0.5])
|
|
|
+ if(Math.abs(angleDiff - Math.PI ) < minDiff){
|
|
|
+ //console.log('因为折回而提前结束', n, branchPath)
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if(reverse != void 0){
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if(branchPath.length<3 || dis < maxDistance){
|
|
|
+ panos_.push([pano, e])
|
|
|
+ search(e, branchPath, angles_)
|
|
|
+ }else{
|
|
|
+ //console.log('branchPath',branchPath)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //search(currentPano)
|
|
|
+ let disMap = new Map, cosMap = new Map
|
|
|
+ let camDir = player.getDirection()
|
|
|
+ let neighbors = currentPano.neighbourUUIDs.map(e=>panos.get(e))
|
|
|
+ .filter(p=>{
|
|
|
+ let dir = new THREE.Vector3().subVectors(p.position, currentPano.position)
|
|
|
+ let d = dir.lengthSq()
|
|
|
+ if(d < 15){//最大距离
|
|
|
+ disMap.set(p, d);
|
|
|
+ cosMap.set(p, dir.normalize().dot(camDir))
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }).sort((a,b)=>{
|
|
|
+ let score = disMap.get(a) - disMap.get(b)
|
|
|
+ score += (cosMap.get(b) - cosMap.get(a)) * 5
|
|
|
+ return score
|
|
|
+ })//从近到远,尽量在前方
|
|
|
+
|
|
|
+ let seedPanos = [currentPano, ...neighbors]//如果没有从当前点出发的箭头,就展示隔壁点的(缺点是隔壁点可能是指向当前点的,原规则是不展示来的路径的,所以会有点奇怪。虽然也可以在search后剔除啦)
|
|
|
+ for(let seed of seedPanos){
|
|
|
+ search(seed)
|
|
|
+ if(panos_.length != 0){
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ arrows.children.slice().forEach(child=>arrows.remove(child))
|
|
|
+
|
|
|
+
|
|
|
+ panos_.forEach((panoArr,i)=>{
|
|
|
+ var isNew = !lastArrowPanos.find(e=>e[0]==panoArr[0] && e[1]==panoArr[1])//新出现的点 渐变出现
|
|
|
+ updateArrowPose(panoArr[0], panoArr[1], isNew ? mats.fadeIn : mats.default )//更新位置
|
|
|
+
|
|
|
+ })
|
|
|
+
|
|
|
+ fadeInArrow()
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ lastArrowPanos = panos_
|
|
|
+ }
|
|
|
+
|
|
|
+ let inited
|
|
|
+ let init = ()=>{
|
|
|
+ if(inited)return
|
|
|
+ if(gradualShow){
|
|
|
+ player.on("flying.ended", updateArrow )
|
|
|
+ }else{
|
|
|
+ //展示全部
|
|
|
+ for(let id1 in panos.routeNextMap){
|
|
|
+ for(let pano2 of panos.routeNextMap[id1]){
|
|
|
+ updateArrowPose(panos.get(id1), pano2, mats.default)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ player.on("mode.changed", ()=>{
|
|
|
+ if(hide)return
|
|
|
+ arrows.visible = player.mode == 'panorama'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ opacityShine && updateArrowOpacity()
|
|
|
+ inited = true
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ {//ui控制显示
|
|
|
+ let changeShow = (e)=>{
|
|
|
+ hide = !e.show
|
|
|
+ e.show && init()
|
|
|
+ gradualShow ? updateArrow() : (arrows.visible = e.show)
|
|
|
+ }
|
|
|
+
|
|
|
+ player.on('changeArrowShow',changeShow)
|
|
|
+ changeShow({show:!hide})
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
let f = () => {
|
|
|
window.bus.removeEventListener('playerAndModelReady', f);
|
|
|
|
|
@@ -1606,6 +1891,8 @@ function initByTHREE(THREE) {
|
|
|
});
|
|
|
|
|
|
player.model.hotGroup.children.length ? logSth() : player.on('gotHotAndStartload', logSth);
|
|
|
+
|
|
|
+ initRouteArrow()
|
|
|
};
|
|
|
window.bus.addEventListener('playerAndModelReady', f); //player model currentPano都已有
|
|
|
|