AR Indoor Navigation-Three.js

overview

If you don’t understand, you can add QQ: 2781128388
Source code acquisition : https://mbd.pub/o/bread/Y5uck5lr

This time the AR indoor navigation is made using Hummingbird Cloud Map plus three.js, with indoor floor switching, 2D/3D model switching, compass control, and AR on/off. Simulate the function of indoor navigation, first look at the video effect

AR indoor navigation

Initialize the indoor map

It is very simple to initialize the indoor map of Hummingbird Cloud, and the map data that comes with Hummingbird Cloud is also used to
call mapCreate in the vue file to create a map

this.$nextTick(() => {
    
    
        this.mapCreate();
});

Map configuration parameters, you need to create the key value yourself

options: {
    
    
          appName: '蜂鸟研发SDK_2_0',
          key: '',
          mapID: '1321274646113083394',
          // 缩放级别
          mapZoom: 20,
          // 显示楼层
          visibleLevels: [1, 2, 3, 4, 5],
          // 默认显示几楼
          level: 3
        }

window.map = new fengmap.FMMap(this.options);

At this point, the map creation shows success
insert image description here

Create floor controls

After the map is created, floor controls, compass, and navigation controls are generated

//监听地图加载完成
        map.on('loaded', () => {
    
    
          //创建导航对象
          this.creatNavigation();
          //创建楼层控件
          this.creatFloorControl();
          //创建指北针控件
          this.creatCompassControl();
        });

floor controls

creatFloorControl() {
    
    
        let toolbar = new fengmap.FMToolbar({
    
    
          //默认在右上角
          position: fengmap.FMControlPosition.RIGHT_TOP,
          //初始是否是多层显示,默认单层显示
          allLayer: false,
          //是否显示多层/单层切换按钮
          needAllLayerBtn: true,
          //控件位置x,y的偏移量
          offset: {
    
    
            x: -10,
            y: 320
          }
        });
        toolbar.addTo(map);
      },

compass

let compass = new fengmap.FMCompass({
    
    
          position: fengmap.FMControlPosition.LEFT_TOP,
          width: 40,
          height: 40,
          offset: {
    
    
            x: 12,
            y: 460
          }
        });
        compass.addTo(map);
        compass.on('click', function() {
    
    
          map.setRotation({
    
    
            rotation: 0,
            animate: true,
            duration: 0.3
          });
        });

navigation controls

// FMNaviAnalyser 是可分析最短路径、最快路径并返回分析结果的路径类。可独立于地图工作,支持Web Worker 和 Node
        let analyser = new fengmap.FMNaviAnalyser(
          this.options,
          function() {
    
    
            // FMNavigation 是导航相关的功能类, 可用于模拟导航和真实导航使用
            window.navi = new fengmap.FMNavigation({
    
    
              map: map,
              analyser: analyser,
              locationMarkerUrl: './img/导航.png',
              locationMarkerSize: 32
            });
          },
          (error) => {
    
    
            console.log(error);
          }
        );

insert image description here
At this point, you can switch the floor display and control the 2D/3D conversion

navigation

A UI for inputting the start address and end location, you can just write and write,
insert image description here
then you need to input the start point and end point when you click on the map, you need to bind the click event
isNavBoxShow on the map as the display state of the component, startPointSelect is the state of the starting point, and endPointSelect is the end point state

// //路径规划
        map.on('click', (event) => {
    
    
          if (this.$store.state.isNavBoxShow === true) {
    
    
            if (this.$store.state.startPointSelect === true) {
    
    
              window.routeOpiton.start = {
    
    
                x: event.coords.x,
                y: event.coords.y,
                level: event.targets[0].level,
                url: './img/start.png',
                height: 3
              };
              navi.setStartPoint(window.routeOpiton.start);
              if (event.targets[0].name) {
    
    
                document.getElementById('startInput').value = event.targets[0].name;
              } else {
    
    
                document.getElementById('startInput').value = '当前起点位置';
              }
              this.$store.commit('startPointSelectFalse');
            } else if (this.$store.state.endPointSelect === true) {
    
    
              window.routeOpiton.end = {
    
    
                x: event.coords.x,
                y: event.coords.y,
                level: event.targets[0].level,
                url: './img/end.png',
                height: 3
              }
              navi.setDestPoint(window.routeOpiton.end);

              if (event.targets[0].name) {
    
    
                document.getElementById('endInput').value = event.targets[0].name;
              } else {
    
    
                document.getElementById('endInput').value = '当前终点位置';
              }
              this.$store.commit('endPointSelectFalse');
            }
          }
        });

At this point, we can click on the map module to enter the start point and end point.
insert image description here
Click OK and call the route calculation function
window.routeOpiton as the start point and end point objects.

navi.route(window.routeOpiton, function(result) {
    
    
          let line = navi.drawNaviLine();
          let coordinates = [];
          result.subs.forEach(item => {
    
    
            item.waypoint.points.forEach(point => {
    
    
              coordinates.push(point)
            })
          });
 })

insert image description here

The principle of using Three.js to generate AR modules

Explain the generation steps. The first step is also to verify whether the camera can be turned on, then initialize Three.js, and then use the video map to map the video stream of the camera to the background of three.js, so that it can be presented, and then how to It is not difficult to display the path in the scene. The api of Hummingbird Cloud will return an array of the shortest path. We can calculate the data of the shortest path. First, determine whether the distance between each point is greater than 1. How to calculate two points As for the distance between two points, just take the root of the square of the two points. If it is greater than 1 after calculation, there is a corner. At this time, we need to calculate the angle. The angle is calculated by the arc tangent. Here we need to pay attention It is the rotation direction of the axis. Finally, it is enough to monitor the gyroscope to change the angle of the generated points and lines. Overall, the idea is ok, and then it is enough to turn it into code. It is not difficult to implement the code, mainly the idea~

Initialize Three

//初始参数
    canvas = document.getElementById('webGL3d')
    arWidth = canvas.offsetWidth
    arHeight = canvas.offsetHeight
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(60, arWidth / arHeight, 0.0001, 7000)
    camera.position.set(0, -7, 5)
    // //renderer参数
    let renderParam = {
    
    
      antialias: true, // true/false表示是否开启反锯齿
      // alpha: true, // true/false 表示是否可以设置背景色透明
      precision: 'highp', // highp/mediump/lowp 表示着色精度选择
      premultipliedAlpha: false, 
      maxLights: 3, 
      canvas: canvas 
    }
    renderer = new THREE.WebGLRenderer(renderParam)
    renderer.setSize(arWidth, arHeight)
    orbitControls = new OrbitControls(camera, renderer.domElement)

Determine whether the camera is supported and return the video stream. Here is a small detail. To determine whether it is a mobile phone or a PC, the mobile phone is forced to use the rear camera

let video = document.createElement('video');
  // navigator.mediaDevices.getUserMedia 提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。

  const stream = await navigator.mediaDevices.getUserMedia({
    
    
    // 关闭音频
    audio: false,
    video: {
    
    
      // 在移动设备上面,表示优先使用前置摄像头
      // facingMode: 'user',
      facingMode: isMobile() ? {
    
     exact: "environment" } : 'user',
      width: width,
      height: height
    }
  });

  video.srcObject = stream;
  video.play();
  video.width = width;
  video.height = height;

  return new Promise((resolve) => {
    
    
    // 在视频的元数据加载后执行 JavaScript
    video.onloadedmetadata = () => {
    
    
      resolve(video);
    };
  });
function isMobile() {
    
    
  const isAndroid = /Android/i.test(navigator.userAgent);
  const isiOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
  return isAndroid || isiOS;
}

Paste the video into the background of three.js after getting the video stream

let video = await openCamera(arWidth, arHeight);
    console.log(video);
    videoTexture = new THREE.Texture(video);
    videoTexture.minFilter = THREE.LinearFilter;
    scene.background = videoTexture;

Here we can see the video and
then we create a starting point marker

let plane = new THREE.PlaneGeometry(1, 1)
    let map = new THREE.TextureLoader().load(require('@/assets/img/WechatIMG1129.png'))
    let material = new THREE.MeshBasicMaterial({
    
    
      map: map,
      alphaTest: 0.1,
      color: 0xffffff,
      side: THREE.DoubleSide,
    })
    nowPosPic = new THREE.Mesh(plane, material)
    nowPosPic.position.set(0, offsetY, 0)
    scene.add(nowPosPic)
    //添加坐标轴
    let axes = new THREE.AxesHelper(500)
    scene.add(axes)

insert image description here
draw navigation line

if (coordinates.length !== 0) {
    
    
      group = new THREE.Group()
      let starPoint = {
    
    
        x: 0,
        y: 0
      }
      for (let i = 1; i < coordinates.length; i++) {
    
    
        let x = coordinates[i].x - coordinates[0].x
        let y = coordinates[i].y - coordinates[0].y
        // 计算两点的距离
        let distance = Math.sqrt(Math.pow(x - starPoint.x, 2) + Math.pow(y - starPoint.y, 2))
        if (distance >= 1) {
    
    
        // 计算弧度
          let angle = calAngleX(x - starPoint.x, y - starPoint.y)
          // 生成线
          createLine(starPoint, distance, angle)
          starPoint.x = x
          starPoint.y = y
        }
      }
      scene.add(group)
      group.position.y = offsetY
      group.rotation.z = -alpha * Math.PI / 180
    }

Calculate radian code

//计算偏转角度(X逆时针)
  function calAngleX(x, y) {
    
    
    let angle = Math.atan(Math.abs(y) / Math.abs(x))
    if (x >= 0 && y >= 0) {
    
    

    } else if (x <= 0 && y >= 0) {
    
    
      angle = Math.PI - angle
    } else if (x <= 0 && y <= 0) {
    
    
      angle = Math.PI + angle
    } else {
    
    
      angle = Math.PI * 2 - angle
    }
    return angle
  }

generate line

let plane = new THREE.PlaneGeometry(1, 1)
    let map = new THREE.TextureLoader().load(require('@/assets/img/WechatIMG1123.png'))
    let material = new THREE.MeshBasicMaterial({
    
    
      map: map,
      alphaTest: 0.1,
      color: 0xffffff,
      side: THREE.DoubleSide,
    })
    for (let i = 0.6; i <= length; i++) {
    
    
      let mesh = new THREE.Mesh(plane, material)
      let x = starPoint.x + i * Math.cos(angle)
      let y = starPoint.y + i * Math.sin(angle)
      mesh.position.set(x, y, 0)
      let obj = {
    
    
        x: x + coordinates[0].x,
        y: y + coordinates[0].y
      }
      lingMeshArray.push(obj)
      mesh.rotation.z = angle - Math.PI / 2
      group.add(mesh)
    }

You can see the generated line here.
insert image description here
Monitor the gyroscope window.DeviceOrientationEvent
window.DeviceOrientationEvent Description
DeviceOrientationEvent.absolute Read-only
Boolean value used to indicate whether the rotation data provided by the device is absolute positioning.
DeviceOrientationEvent.alpha read-only
A number representing the angle of rotation of the device around the z-axis (range between 0-360)
DeviceOrientationEvent.beta Read only
a number representing the rotation of the device around the x-axis (range between -180 and 180), The direction from front to back is the positive direction.
DeviceOrientationEvent.gamma read only
A number representing the rotation of the device around the y-axis (ranging from -90 to 90), positive direction from left to right.
throttle is just a throttling function

if (window.DeviceOrientationEvent) {
    
    
      window.addEventListener('deviceorientation', throttle(setMeshCamera, 100), false)
    } else {
    
    
      console.log('你的浏览器不支持陀螺仪')
    }

Finally, calculate the rotation angle of the starting point and the line according to the gyroscope.

Guess you like

Origin blog.csdn.net/qq_39503511/article/details/121062399