Automatic cruise

Make writing a habit together! This is the second day of my participation in the “Gold Digging Day New Plan · April More text challenge”. Click here for more details.

There is a need to simulate security patrols and similar Tours along the track automatically, through parsing. You can connect a line through a series of points, and move the camera or security model along the line, and adjust its Angle to keep it along the tangent direction ~Copy the code

Analyze the

Graph LR Mouse pick up memory points -> Multi-point line -> Streamer animation -> camera or model moves along
Demand decomposition: - mouse pick up points in 3D coordinates (used to connect points into a line) - Multi-point line (used to connect points into a channel by using channels) - streamer effect realization of line segments - camera or model moving along the lineCopy the code
Solution: - Through mouse events, pick up 2d coordinates and convert them to 3D coordinates - storage point 3D coordinate array, generate the corresponding pipeline - by controlling the movement of map, realize the streamer effect - real-time change and update the camera or model position and orientation, realize the movement along the lineCopy the code

Mouse pick up three – dimensional points

- Pick up 2d coordinates on the screen by mouse - convert 2D coordinates to 3D coordinates - record point coordinates - record the previous pipes every time and generate new pipesCopy the code
// Double-click the event
this.el.addEventListener('dblclick'.function (e) {
  const mouse = new THREE.Vector2()
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1
  mouse.y = -(e.clientY / window.innerHeight) * 2 + 1
  // Here we only check the selected model
  raycaster.setFromCamera(mouse, that.camera)
  const intersects = raycaster.intersectObjects(that.scene.children, true)
  if (intersects.length > 0) {
    var selected = intersects[0]// take the first object
    this.pointList.push(new THREE.Vector3(selected.point.x, 5, selected.point.z))// Record the point
    this.makeLine()/ / line}})Copy the code
Class to generate a parade class, passing in the scene, camera hierarchy, camera, animate functions, and creating streamers by destroying and generating new pipesCopy the code
makeLine = function () {
  if (!this.zhdParade){// Check whether the class exists
    this.zhdParade = new zhdParade({
      scene:this.scene, / / the scene
      layers:this.currentlayers, / / hierarchy
      camera:this.camera, // Camera or model
      renderFunction:this.renderFunction //animate})}else{
    this.zhdParade.removeLine() // Each time you create a line, you destroy it first
  }
  this.zhdParade.makeLine(this.pointList) / / line
}
Copy the code

Automatic cruise class encapsulation

Since the last article, a brother said is too much code, the code after this time I labeled packaging, although there is no use ts, but this time should not be very disorderly ~ ~ what does not understand place can leave a message, the amount of code less than before and easier ~ the encapsulation of the following is a general, base class inheritance is not looseCopy the code
export class zhdParade extends zhdObject { / / the base class
  constructor(options) {
    super(a)this.type = 'zhdParade'
    this.line = null
    this.texture = null
    this.camera=options.camera
    this.layers = options.layers
    this.scene = options.scene
    this.renderFunction = options.renderFunction
    this.texture_animate()
  }
}
Copy the code

Line drawing

Below, the CatmullRomCurve3 channel method is used to generate pipes and lines by picking up the point array with the mouse. The generation of pipes is mainly used for moving cruise, and the lines are convenient for viewing the shape of pipesCopy the code
makeLine(pointList) {
  if (pointList.length < 2) {return
  }
  this.curve = new THREE.CatmullRomCurve3(pointList) / / piping
  this.curve.arcLengthDivisions = 1000
  this.texture = new THREE.TextureLoader().load('static/images/line.png') // Map pipe streamer effect
  this.texture.wrapS = this.texture.wrapT = THREE.RepeatWrapping // Repeat each one
  this.texture.repeat.set(1.1)
  
  let tubeGeometry = new THREE.TubeGeometry(curve, 80.1)
  let material = new THREE.MeshBasicMaterial({
    map: texture,
    side: THREE.BackSide,
    transparent: true
  })
  this.line = new THREE.Mesh(tubeGeometry, material)
  this.line.layers.set(this.layers)
  this.scene.add(this.line)
}
Copy the code

Time animation

Create a streamer effect by changing the position of the line mapCopy the code
texture_animate() {
  this.renderFunction.push(() = > { //animate
    if (this.texture) this.texture.offset.x -= 0.01 // Change the map position to create a streamer animation})}Copy the code

Remove the pipe

To avoid line stacking, which occupies memory, remove existing lines before creating new onesCopy the code
removeLine() {
  if (this.line) {
    this.scene.remove(this.line)
    this.line = null}}Copy the code

Automatic cruise

Control speed factors: - distance between two points - progress cumulative value size - four dimensional matrix change adjust camera Angle simple description: through the progress 0-1 value to obtain the coordinates of a point on the line, through the coordinates to adjust the camera position and Angle and lookAt positionCopy the code
autoParade() {
  let progress = 0
  this.renderFunction.push(() = > {/ / the animate function
    let offsetAngle = Math.PI*2 / / Angle
    if (this.carDirection === 'GO') { // Follow the line
      if (progress > 1.0) {
        this.carDirection = 'BACK' // Follow the line back
      } else {
        progress += 0.0019 //}}else {
      // offsetAngle = -Math.PI / 2
      if (progress < 0) {
        this.carDirection = 'GO'
      } else {
        progress -= 0.0019}}if (this.curve && this.camera) {
      let point = this.curve.getPoint(progress) // Get the coordinates of a progress point on the progress line
      // The model offset
      // Create a 4-dimensional matrix
      let mtx = new THREE.Matrix4()
      mtx.lookAt(this.camera.position.clone(), point, this.camera.up) // The camera looks at the position
      mtx.multiply(new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(0, offsetAngle, 0)))
      // Calculate the quaternion value to be rotated
      let toRot = new THREE.Quaternion().setFromRotationMatrix(mtx)
      // Adjust the Angle based on the above values
      this.camera.quaternion.slerp(toRot, 1)
      this.camera.position.set(point.x, point.y, point.z)
    }
  })
}
Copy the code