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