路径漫游系统(完整可用)

1、createPath.html

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>TJH  ar.js</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<style>
			body {
				background:#000;
				color: #fff;
				padding:0;
				margin:0;
				font-weight: bold;
				overflow:hidden;
			}

			#info {
				position: absolute;
				top: 0px; width: 100%;
				color: #ffffff;
				padding: 5px;
				font-family: Monospace;
				font-size: 13px;
				text-align: center;
				z-index:100;
			}

			a { color: red }

		</style>
	</head>

	<body>
	    <div id="container"></div>

		<script src="../build/three.js"></script>
		<script src="../build/three.extension.js"></script>
		<script src="../build/tjh.ar.js"></script>
		<script src="../build/tjh.ar.tools.js"></script>
		<script src="../build/extension/TerrainControls.js"></script>
		<script src="../node_modules/three/examples/js/Detector.js"></script>
		<script src="../node_modules/three/examples/js/libs/stats.min.js"></script>
		<script src="../node_modules/jquery/dist/jquery.min.js"></script>
		<script src="../build/extension/pathCreateTool.js"></script>
		<script src="../build/extension/pathRoam.js"></script>
		<script src="../build/three.loader.js"></script>
		<script>

			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

			var container, stats;

			var camera, scene, renderer;

			var geometry, objects;

			var controls, clock = new THREE.Clock();

			var viewer = new tjh.ar.Viewer();
			viewer.onEndFrame = ()=> {
			    stats.update();
			}

			init();
			viewer.run();

			function init() {
				viewer.maxFPS = 60;
				//
				container = document.getElementById( 'container' );
				document.body.appendChild( container );//增加container节点

                renderer = new THREE.WebGLRenderer();
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );//设置尺寸
                container.appendChild( renderer.domElement ); //增加节点
                viewer.setRenderer(renderer); //
                //
				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1500000 );
				viewer.setCamera(camera);
                //
				//controls = new THREE.TrackballControls( this.camera, this.renderer.domElement );
				//
                //camera.position.set(-618,510,2000);
                //camera.lookAt(new THREE.Vector3(-618, 510, 31.14));

                //camera.position.set(-1274.28,-425.81,2000);
                //camera.lookAt(new THREE.Vector3(-1274.28,-425.81, 52));

				camera.position.set(96389.0-466.53, 2690779.0+23.26, 1500);//相机位置
				camera.up.set(0,1,0);
				camera.lookAt(new THREE.Vector3(96389.0-466.53, 2690779.0+23.26, 0));
				camera.updateMatrixWorld(true);

				scene = new tjh.ar.ARScene();//场景
				scene.fog = new THREE.Fog( 0x000000, 1, 15000 );
				scene.autoUpdate = false;
				viewer.setScene(scene);

                controls = new TerrainControls(viewer,{});
                viewer.pushEventListenerFront(controls);

				viewer.addFramesEndRequester(()=> {
					stats.update();
				});


                var ambientLight = new THREE.AmbientLight( 0xffffff );
                scene.addLightSource( ambientLight );

				var light = new THREE.DirectionalLight( 0xffffff );
				light.position.set( 1, 1, 1 ).normalize();
				//scene.addLightSource( light );

				var dataPager1 = new THREE.DataBasePager(true, 3, viewer.compressTextureSupported);
				dataPager1.maxWaitingLoaded = 10;
				dataPager1.maxSortRequests = 30;
				dataPager1.loadStrategy = THREE.DataBasePager.LOAD_STRATEGY.NORMAL;
				dataPager1.pageScaleFunc = function (contex, node) {
					//
					return 1.0
				};
				// 1
				var terrainLayer1 = new tjh.ar.TerrainLayer(dataPager1, null ,true);
				terrainLayer1.enableClip = false;
				terrainLayer1.keepLevel = 0;
				terrainLayer1.maxLevel = 70;
				terrainLayer1.forceUpdate = true;
				terrainLayer1.dataBasePager.pageScaleFunc = function (contex, node) {
					return 1.0;
				};
				terrainLayer1.addTerrain("http://192.168.10.179:18021/api/Mesh/GetGlbFile?635faf81-aee3-4dd6-a13a-ffdd272cd9ad/root.glb", new THREE.Vector3(96389.0,2690779.0, 0));
				terrainLayer1.receiveShadow = true;
				terrainLayer1.castShadow = true;
				//terrainLayer1.terrain.position.copy(ecefOrign.toVector3());
				//let qua = tjh.geographic.OrientationUtils.quaternionFromCRSToCRS("EPSG:00000", "EPSG:4978", localOrign);
				//terrainLayer1.terrain.quaternion.copy(tjh.geographic.OrientationUtils.quaternionFromCRSToCRS("EPSG:00000", "EPSG:4978", localOrign));
				//terrainLayer1.terrain.updateMatrixWorld(true);
				scene.addTerrainLayer(terrainLayer1);

                let defaultRenderTechnique = new tjh.ar.OutLineTechnique(renderer, scene, camera);
                viewer.pushRenderTechnique(defaultRenderTechnique);
                //
                var pathTool = null;
                var roamTool = new SmartRoam(viewer);
                roamTool.setSpeed(25);
                let roamingMode=0;//设置漫游模式,默认0自由模式,1无人机飞行模式,2车第三人称视角 3车第一人称视角
				roamTool.setroamingMode(roamingMode);  //传入pathRoam.js
				if(roamingMode==0)
				{
					roamTool.hidePath();
					roamTool.hideCamera();
				}
				else if(roamingMode==1)   //如果是无人机模式,导入无人机模型
				{
					roamTool.flyHeight(100);  //相对高程
					roamTool.setScaleUav(0.8); //设置无人机模型尺寸
					roamTool.setModelUrl('models/obj/male02/aigei.obj');
					roamTool.hidePath();
					roamTool.hideCamera();
				}
				else if(roamingMode==2)   //如果是车的第三人称模式,导入汽车模型
				{
					roamTool.setModelUrl('models/obj/male02/obj.obj');
					roamTool.setScaleCar(0.13); //设置汽车尺寸
					roamTool.hidePath();
					roamTool.hideCamera();
				}
				else if(roamingMode==3)   //如果是车的第一人称模式,导入汽车模型
				{
					roamTool.setModelUrl('models/obj/male02/obj.obj');
					roamTool.setScaleCar(0.13); //设置汽车尺寸
					roamTool.hidePath();
					roamTool.hideCamera();
				}
                //
                class SelectEvent extends tjh.ar.WindowEventListener {
                    constructor(viewer) {
                        super(viewer);
                        // 首次按p创建路径工具,按c+鼠标左键点击记录坐标,再次按p根据坐标记录生成路径;s -开始漫游;e -暂停;
						// q- 重新开始
                        this.onKeyDown = (keyboardEvent)=> {
                            if(keyboardEvent.key === 'p') {
                                if (!pathTool) {
                                    pathTool = new PathCreateTool(viewer); //创建路径
                                    viewer.pushEventListenerFront(pathTool);
                                    return true;
                                }
                                else if(pathTool){
                                    viewer.removeEventListenerFront();
                                    //roamTool.setSites(pathTool.pathVertexes);
									roamTool.setPathFromCameras(pathTool.cameras);
                                    pathTool = null;
                                    //
									viewer.pushEventListenerFront(roamTool);
									//
                                    return true;
                                }
                            }
                            else if(keyboardEvent.key === 's') {
                                roamTool.start();
                                return true;
							}
							else if(keyboardEvent.key === 'e') {
                                roamTool.pause();
                                return true;
							}
                            else if(keyboardEvent.key === 'q') {
                                roamTool.resume();
                                return true;
                            }
                        }
                    }
                }
                viewer.pushEventListenerFront(new SelectEvent(viewer));
				//
                stats = new Stats();
                container.appendChild( stats.dom );
			}
		</script>

	</body>
</html>

2.1、pathRoam.js(v1版本,有个重大逻辑bug,如果地图不是水平坐标系,汽车第三人称运动就出问题了,如果参考可以参考v2版本,已经更新在下面。)

/**
 * @classdesc 智能漫游操作器
 * @class
 * @memberOf tjh.ar
 * @param {viewer} viewer -视景器
 */

class SmartRoam extends tjh.ar.WindowEventListener {
    constructor(viewer, pauseWhenStart = false) {
        super(viewer);
        let that = this;
        //
        let roamPath = [];
        let positionSeq = [];
        let quaternionSeq = []; //四元数
        let times = [];
        //
        let nextPosition =[];
        //
        let positionKF = null;
        let quaternionKF = null;
        //
        let updateProxy = new THREE.Object3D();
        let pathMesh = new THREE.Group();
        //pathMesh.disabled = false;
        //pathMesh.visible = true;
        let cameraMesh = new THREE.Group();
        let objModel = new THREE.Group();
        let modelinfor = new THREE.Group();
        //cameraMesh.disabled = false;
        //cameraMesh.visible = true;
        this.tempObj.add(updateProxy);
        this.tempObj.add(pathMesh);
        this.tempObj.add(cameraMesh);
        this.tempObj.add(objModel);
        //
        let  clip = null;
        let mixer = null;
        let cameraX;
        let cameraY;
        let cameraZ;
        let clock = null;
        let roaming = false;
        let speed = 10;
        let minTime = 0.5;;
        let roamingMode=0;//默认0,步行模式;1无人机飞行模式,2车第一人称视角,3车第三人称视角
        let flyingHeight=150; //飞行高度默认150
        let forwardMode;
        let lastCarPosX = 0;
        let lastCarPosY = 0;
        let lastCarPosZ = 0;
        let scaleCar = 0.13;  //汽车尺寸 默认 0.13
        let scaleUAV = 0.8;   //无人机尺寸 默认0.8
        //
        updateProxy.update = (context)=> {
            if(!mixer && positionSeq && quaternionKF) {
                clip = new THREE.AnimationClip('Action', -1, [positionKF, quaternionKF]);
                clip.optimize();
                mixer = new THREE.AnimationMixer(viewer.camera);
                // create a ClipAction and set it to play
                let clipAction = mixer.clipAction(clip);
                let action = clipAction.play();
                action.setLoop(THREE.LoopOnce);//设置循环
                action.clampWhenFinished = true;  //动画将在最后一帧之后自动暂停
                mixer.addEventListener("finished", () => {  //全部动作的结束
                    roaming = false;
                    if (that.onEnd) {
                        that.onEnd();
                    }
                });
            }
            else if(roaming) {
                mixer.update(clock.getDelta());  //推进混合器时间并更新动画
                //获取相机位置
                 cameraX = context.camera.position.x;
                 cameraY = context.camera.position.y;
                 cameraZ = context.camera.position.z;

                let camerqquax = context.camera.quaternion.x;
                let camerqquay = context.camera.quaternion.y;
                let camerqquaz = context.camera.quaternion.z;
                let camerqquaw = context.camera.quaternion.w;

                //更新模型位
                if(roamingMode ==1)   //如果是飞行模式,调整模型位置
                {
                    modelinfor.scale.set(scaleUAV,scaleUAV,scaleUAV);//设置尺寸
                    modelinfor.position.x = cameraX+1.5;
                    modelinfor.position.y = cameraY;
                    modelinfor.position.z = cameraZ-20;
                    context.camera.lookAt(modelinfor.position.x,modelinfor.position.y,0);
                    //context.camera.lookAt(modelinfor.position);
                }

                else if(roamingMode == 2) {   //如果是汽车模式第三人称,调整模型位置
                    modelinfor.scale.set(scaleCar,scaleCar,scaleCar);

                    modelinfor.position.x = cameraX;
                    modelinfor.position.y = cameraY;
                    modelinfor.position.z = 89.5;
                    modelinfor.quaternion.x = camerqquax;
                    modelinfor.quaternion.y = camerqquay;
                    modelinfor.quaternion.z = camerqquaz;
                    modelinfor.quaternion.w = camerqquaw;
                    context.camera.up.x = 0;
                    context.camera.up.y = 0;
                    context.camera.up.z = 1;
                    modelinfor.rotateY(Math.PI);
                    //modelinfor.lookAt(modelinfor.position);
                    if(!lastCarPosX && !lastCarPosY && !lastCarPosZ) {  //初始化各个方向相机的位置

                        if (forwardMode == 0)  //如果是沿着x轴前进(横向正方向
                        {
                            context.camera.position.x = cameraX - 3;
                            context.camera.position.y = cameraY;
                            context.camera.position.z = 92;

                        }
                        else if (forwardMode == 1) {  //如果是沿着x轴前进(横向负方向
                            context.camera.position.x = cameraX + 3;
                            context.camera.position.y = cameraY;
                            context.camera.position.z = 92;

                        }
                        else if (forwardMode == 2) {  //如果是沿着y轴前进(纵向正方向
                            modelinfor.rotateY(Math.PI);
                            context.camera.position.x = cameraX;
                            context.camera.position.y = cameraY - 3;
                            context.camera.position.z = 92;
                            ;
                        }
                        else if (forwardMode == 3) {  //如果是沿着y轴前进(纵向负方向
                            context.camera.position.x = cameraX;
                            context.camera.position.y = cameraY + 3;
                            context.camera.position.z = 92;
                        }
                    }
                    else   //如果不是第一个点,即在漫游的路途中,根据上一个点的坐标和现在点的坐标判断此时移动的方向,然后进行位置的更改
                    {
                        let tempX = cameraX - lastCarPosX;
                        let tempY = cameraY - lastCarPosY;
                        let tempAbX = tempX > 0 ? tempX : -1*tempX;
                        let tempAbY = tempY > 0 ? tempY : -1*tempY;

                       if(tempAbX>tempAbY&&tempX>0)  //x轴正向
                       {
                           context.camera.position.x = cameraX - 5;
                           context.camera.position.y = cameraY;
                           context.camera.position.z = 92;
                       }
                       else if(tempAbX>tempAbY&&tempX<0)  //x轴负向
                       {
                           context.camera.position.x = cameraX + 5;
                           context.camera.position.y = cameraY;
                           context.camera.position.z = 92;
                       }
                       else if(tempAbX<tempAbY&&tempY>0) //y轴正向
                       {
                           context.camera.position.x = cameraX;
                           context.camera.position.y = cameraY - 5;
                           context.camera.position.z = 92;
                       }
                       else if(tempAbX<tempAbY&&tempY<0)  //y轴负向
                       {
                           context.camera.position.x = cameraX;
                           context.camera.position.y = cameraY + 5;
                           context.camera.position.z = 92;
                       }
                    }

                    lastCarPosX = cameraX;
                    lastCarPosY = cameraY;
                    lastCarPosZ = cameraZ;
                }
                else if(roamingMode == 3) {   //如果是汽车模式第三人称,调整模型位置
                    modelinfor.scale.set(0.13,0.13,0.13);

                    modelinfor.position.x = cameraX;
                    modelinfor.position.y = cameraY;
                    modelinfor.position.z = 89.5;
                    modelinfor.quaternion.x = camerqquax;
                    modelinfor.quaternion.y = camerqquay;
                    modelinfor.quaternion.z = camerqquaz;
                    modelinfor.quaternion.w = camerqquaw;
                    context.camera.up.x = 0;
                    context.camera.up.y = 0;
                    context.camera.up.z = 1;
                    modelinfor.rotateY(Math.PI);
                    context.camera.position.x = cameraX;
                    context.camera.position.y = cameraY;
                    context.camera.position.z = 91.2646464;
                }
                modelinfor.updateMatrixWorld(true);

            }
            //
            if(pathMesh.children.length > 0) {
                let resolution = pathMesh.children[0].material.uniforms["resolution"].value;
                if(resolution.x !== context.clientWidth || resolution.y !== context.clientHeight) {
                    pathMesh.children[0].material.uniforms["resolution"].value.set(context.clientWidth, context.clientHeight);
                    pathMesh.children[0].material.needUpdate = true;
                }
            }
        };

        /**
         * 开始漫游
         */
        this.start = function () {
            if(mixer) {
                let clipAction = mixer.clipAction( clip );
                let action = clipAction.play();
                action.reset();
            }
            roaming = true;
            //
            clock = new THREE.Clock();
            //
            if(this.onStart) {
                this.onStart();
            }
        }

        /**
         * 暂停漫游
         */
        this.pause = function () {
            if(mixer && roaming) {
                mixer.timeScale = 0;
                if(this.onPause) {
                    this.onPause();
                }
            }
        }

        /**
         * 重新开始漫游
         */
        this.resume = function () {
            if(mixer && roaming) {
                mixer.timeScale = 1;
                if(this.onResume) {
                    this.onResume();
                }
            }
        }

        /**
         * 设置漫游速度 -- m/s
         */
        this.setSpeed = function (s) {
            speed = s;
        }
        this.getSpeed = function () {
            return speed;
        }

        this.setMinTime = function (t) {
            minTime = t;
        }

        this.getMinTime = function () {
            return minTime;
        }

        /**
         * 设置漫游模式
         */
        this.setroamingMode = function(m)
        {
            roamingMode = m;
        }

        /**
         * 设置飞行高度
         */
        this.flyHeight = function (h) {
            flyingHeight = h;
        }

        this.setScaleCar = function(s){
            scaleCar = s;
        }
        this.setScaleUav = function (s) {
            scaleUAV = s;

        }



        /**
         * 改变相机(通过传入的位置信息,俯仰角,朝向)生成一个新的相机,用于更新漫游相机序列
         * @param  cameras -相机序列
         * @param  parames -相机序列
         * @param {*}
         */
        this.changeCameraByParams  = (camera,pitch,yaw) => {   //pitch 俯仰  yaw 朝向
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();
            let tempEuler = new THREE.Euler();
            //欧拉角
            let newCamera = camera;
            pitch = pitch*Math.PI/180;
            yaw = yaw * Math.PI/180;
            //分解newCamera,分离出位置信息,四元数信息,和scale
            newCamera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
            //为传进来的四元数转化为欧拉角,即旋转角,俯仰角,朝向角
            targetQuaternion.normalize();
            tempEuler.setFromQuaternion(targetQuaternion);
            var pitchAngle = tempEuler.x * 180 / Math.PI;  //初始俯仰角
            var yawAngle = tempEuler.y * 180 / Math.PI;   //初始朝向角
            //根据传入的xyz修改相机的xyz
            //newCamera.position.x = x;
            //newCamera.position.y = y;
            //newCamera.position.z = z;
            tempEuler.x = tempEuler.x+ pitch; //俯仰
            tempEuler.y = tempEuler.y+yaw;  //朝向
            tempEuler.z = tempEuler.z;   //旋转
            targetQuaternion.setFromEuler(tempEuler);
            newCamera.quaternion.w = targetQuaternion.w;
            newCamera.quaternion.x = targetQuaternion.x;
            newCamera.quaternion.y = targetQuaternion.y;
            newCamera.quaternion.z = targetQuaternion.z;
            //返回
            return newCamera;
        }
        let material1 = new THREE.MeshBasicMaterial({transparent:true, opacity:0.3});  //基础网格材质
        let material2 = new THREE.LineBasicMaterial({color: 0x00ff00});  //基础线条材质
        this.getCameraMesh = (camera)=> {
            let projMatrix = camera.projectionMatrix;  //投影变换矩阵
            //
            let perspective = projMatrix.getPerspective();
            let temp_proj_matrix = new THREE.Matrix4();
            temp_proj_matrix.makePerspective(perspective.fovy, perspective.aspectRatio, 5, 50);  //创建一个透视投影矩阵
            let nearPlaneCorners = [];
            nearPlaneCorners[0] = new THREE.Vector3(-1.0, -1.0, -1.0);
            nearPlaneCorners[1] = new THREE.Vector3(1.0, -1.0, -1.0);
            nearPlaneCorners[2] = new THREE.Vector3(1.0, 1.0, -1.0);
            nearPlaneCorners[3] = new THREE.Vector3(-1.0, 1.0, -1.0);

            let inv_proj_matrix = new THREE.Matrix4();
            inv_proj_matrix.getInverse(temp_proj_matrix);
            nearPlaneCorners[0].applyMatrix4(inv_proj_matrix);  //将该向量乘以四阶矩阵(透视投影矩阵
            nearPlaneCorners[1].applyMatrix4(inv_proj_matrix);
            nearPlaneCorners[2].applyMatrix4(inv_proj_matrix);
            nearPlaneCorners[3].applyMatrix4(inv_proj_matrix);
            //
            let vertexes = [0,0,0];
            for(let i=0; i<4; ++i) {
                vertexes.push(nearPlaneCorners[i].x);
                vertexes.push(nearPlaneCorners[i].y);
                vertexes.push(nearPlaneCorners[i].z);
            }
            let plane_indexes = [];
            plane_indexes.push(1); plane_indexes.push(2); plane_indexes.push(3);
            plane_indexes.push(1); plane_indexes.push(3); plane_indexes.push(4);
            let uv = [0,0,1,0,1,1,0,1];
            let plane_geometry = new THREE.BufferGeometry(); //顶点位置,颜色值,uv坐标和自定义缓存属性值。
            plane_geometry.addAttribute('position', new THREE.BufferAttribute( new Float32Array(vertexes), 3));
            plane_geometry.addAttribute('uv', new THREE.BufferAttribute( new Float32Array(uv), 2));
            plane_geometry.setIndex(new THREE.BufferAttribute(new Uint8Array(plane_indexes), 1));
            //
            let edge_indexes = [0,1,0,2,0,3,0,4,1,2,2,3,3,4,4,1];
            let edge_geometry = new THREE.BufferGeometry();
            edge_geometry.addAttribute('position', new THREE.BufferAttribute( new Float32Array(vertexes), 3));
            edge_geometry.setIndex(new THREE.BufferAttribute( new Uint8Array(edge_indexes), 1));
            //
            let plane_mesh = new THREE.Mesh(plane_geometry, material1);
            //plane_mesh.isPlantModel = true;
            //
            let edge_mesh = new THREE.LineSegments(edge_geometry, material2);
            edge_mesh.canNotBeSelected = true;
            //
            let camera_mesh = new THREE.Group();
            //camera_mesh.add(plane_mesh);
            camera_mesh.add(edge_mesh);
            //
            let p = new THREE.Vector3();
            let q = new THREE.Quaternion();
            let s = new THREE.Vector3();
            camera.matrixWorld.decompose(p, q, s);
            camera_mesh.position.copy(p);
            camera_mesh.quaternion.copy(q);
            camera_mesh.scale.copy(s);
            camera_mesh.updateMatrixWorld(true);
            //
            return camera_mesh;
        }

        /**
         * 生成漫游序列
         * @param {array} path -路径坐标数据
         */
        this.setSites = function (path) {
            roamPath = path;
            //
            positionSeq = [];
            quaternionSeq = [];
            times = [];
            //
            let camera = new THREE.PerspectiveCamera(45, 1.0, 1.0, 15000);
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();

            for(let n=0, length = path.length - 1; n<length; ++n) {
                camera.position.copy(path[n]);   //相机的位置
                camera.up.set(0,0,1);
                camera.lookAt(path[n+1]);  //创建一个看着给定目标的观察矩阵
                camera.updateMatrixWorld(true);
                camera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
                //
                quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.w;


                quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                //
                positionSeq[positionSeq.length] = path[n].x;
                positionSeq[positionSeq.length] = path[n].y;
                positionSeq[positionSeq.length] = path[n].z;
                //
                positionSeq[positionSeq.length] = path[n+1].x;
                positionSeq[positionSeq.length] = path[n+1].y;
                positionSeq[positionSeq.length] = path[n+1].z;
                //
                let time = path[n+1].clone().sub(path[n]).length()/speed;
                time = time >= minTime ? time : minTime;
                times[times.length] = times.length === 0 ? 0 : times[times.length - 1] + 1;
                times[times.length] = times[times.length - 1] + time;
            }
            //
            positionKF = new THREE.VectorKeyframeTrack( '.position', times, positionSeq);
            quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', times, quaternionSeq);
            //
            let fitTerrainMaterialGen = new tjh.ar.FitTerrainMaterial();
            let lineMaterial = fitTerrainMaterialGen.getLineMaterial(10);
            lineMaterial.color =  new THREE.Color(0x00ff00);
            lineMaterial.uniforms["linewidth"].value = 2;
            lineMaterial.polygonOffset = true;  //多边形偏移
            lineMaterial.polygonOffsetFactor = -10;  //多边形偏移系数
            lineMaterial.polygonOffsetUnits = -10;  //多边形偏移单位
            lineMaterial.transparent = false;
            let lineGeom =new THREE.LineGeometry();
            lineGeom.setPositions( positionSeq );//位置
            let lineMesh = new THREE.Line2(lineGeom, lineMaterial);
            pathMesh.add(lineMesh);
            //
            clip = null;
            mixer = null;
        };
        /**
         * 生成路径
         * @param {array} path -路径坐标数据
         * @param {THREE.Vector3} lookTarget -目标
         */
        this.setPath = function (path, lookTarget) {
            if(!lookTarget) {
                this.setSites(path);
                return ;
            }
            //
            positionSeq = [path[0].x, path[0].y, path[0].z];
            times[0] = 0;
            for(let n=1, length = path.length; n<length; ++n) {
                positionSeq[positionSeq.length] = path[n].x; //把路径坐标数据放进positionSeq中
                positionSeq[positionSeq.length] = path[n].y;
                positionSeq[positionSeq.length] = path[n].z;
                //
                let time = path[n].clone().sub(path[n-1]).length()/speed;
                time = time >= minTime ? time : minTime;
                times[times.length] = times[times.length - 1] + time;
            }
            //
            quaternionSeq = [];
            let camera = new THREE.PerspectiveCamera(45, 1.0, 1.0, 15000);
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();
            //
            if(lookTarget instanceof  THREE.Vector3) {
                for(let n=0, length = path.length; n<length; ++n) {
                    camera.position.copy(path[n]);
                    camera.up.set(0,0,1);
                    camera.lookAt(lookTarget);
                    camera.updateMatrixWorld(true);
                    camera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
                    //
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                }
            }
            else if(lookTarget instanceof  Array) {

            }
            //
            positionKF = new THREE.VectorKeyframeTrack( '.position', times, positionSeq);
            quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', times, quaternionSeq);
            //
            let fitTerrainMaterialGen = new tjh.ar.FitTerrainMaterial();
            let lineMaterial = fitTerrainMaterialGen.getLineMaterial(10);
            lineMaterial.color =  new THREE.Color(0x00ff00);
            lineMaterial.uniforms["linewidth"].value = 2;
            lineMaterial.polygonOffset = true;
            lineMaterial.polygonOffsetFactor = -10;
            lineMaterial.polygonOffsetUnits = -10;
            lineMaterial.transparent = false;
            let lineGeom =new THREE.LineGeometry();
            lineGeom.setPositions( positionSeq );
            let lineMesh = new THREE.Line2(lineGeom, lineMaterial);
            pathMesh.add(lineMesh);
            //
            clip = null;
            mixer = null;
        }
        //
        /**
         * 生成路径
         * @param {array} cameras -相机序列
         */
        this.setPathFromCameras = function (cameras) {
            if(cameras.length < 2) {
                return ;
            }
            //
            this._cameras_ = cameras;
            //
            positionSeq = [];
            times = [];
            quaternionSeq = [];
            nextPosition = [];
            //
            let preTargetPosition = null;
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();
            //
            let targetPosition1 = new THREE.Vector3();
            let targetQuaternion1 = new THREE.Quaternion();
            let targetScale1 = new THREE.Vector3();
            //
            let camera0 = cameras[0];
            let camera1 = cameras[1];
            camera0.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
            nextPosition[0] = 0;
            //解决汽车模式下模型方向的问题,如果在x轴上比在y轴上动的多则代表是沿着x轴方向移动(横向);
            //如果在y轴上比在x轴上动的多则代表是沿着y轴方向移动(纵向)
            camera1.matrixWorld.decompose(targetPosition1, targetQuaternion1, targetScale1);
            let tempX = targetPosition1.x - targetPosition.x;
            let tempY = targetPosition1.y - targetPosition.y;
            let abTempX = tempX > 0? tempX:-tempX;
            let abTempY = tempY > 0? tempY:-tempY;
            if(abTempX>abTempY && tempX>0)
            {
                forwardMode = 0;  //沿x轴正方向移动
            }
            else if(abTempX>abTempY &&tempX<0)
            {
                forwardMode = 1;  //沿x轴负方向移动
            }
            else if(abTempX<abTempY && tempY>0)
            {
                forwardMode = 2;  //沿y轴正方向移动
            }
            else if(abTempX<abTempY && tempY<0)
            {
                forwardMode = 3;  //沿y轴负方向移动
            }
            //

            if(roamingMode == 0)
            {
                positionSeq = [targetPosition.x, targetPosition.y, targetPosition.z];
                quaternionSeq = [targetQuaternion.x, targetQuaternion.y, targetQuaternion.z, targetQuaternion.w];
            }
            else if(roamingMode == 1)
            {
                positionSeq = [targetPosition.x, targetPosition.y, flyingHeight];
                quaternionSeq = [targetQuaternion.x, targetQuaternion.y, targetQuaternion.z, targetQuaternion.w];
            }
            else if(roamingMode == 2)
            {
                positionSeq = [targetPosition.x, targetPosition.y, 93];
                quaternionSeq = [targetQuaternion.x, targetQuaternion.y, targetQuaternion.z, targetQuaternion.w];
            }
            else if(roamingMode == 3)
            {
                positionSeq = [targetPosition.x, targetPosition.y, 93];
                quaternionSeq = [targetQuaternion.x, targetQuaternion.y, targetQuaternion.z, targetQuaternion.w];
            }
            times = [0];
            //
            {
                let camera_mesh = this.getCameraMesh(camera0);
                cameraMesh.add(camera_mesh);
            }
            //
            preTargetPosition = targetPosition.clone();
            for(let n=1, ncamera = cameras.length; n<ncamera; ++n) {
                let camera = cameras[n];
                camera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
                //
                if (roamingMode == 0) {   //自由模式
                    positionSeq[positionSeq.length] = targetPosition.x;
                    positionSeq[positionSeq.length] = targetPosition.y;
                    positionSeq[positionSeq.length] = targetPosition.z
                    //
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                    //

                }
                else if (roamingMode == 1) {   //飞行模式

                    positionSeq[positionSeq.length] = targetPosition.x;
                    positionSeq[positionSeq.length] = targetPosition.y;
                    positionSeq[positionSeq.length] = targetPosition.z + flyingHeight;

                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;

                }
                else if (roamingMode == 2) {  //车第三视角模式
                    positionSeq[positionSeq.length] = targetPosition.x;
                    positionSeq[positionSeq.length] = targetPosition.y;
                    positionSeq[positionSeq.length] = 93;
                    //
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                    //
                }
                else if (roamingMode == 3) {  //车第一视角模式
                    positionSeq[positionSeq.length] = targetPosition.x;
                    positionSeq[positionSeq.length] = targetPosition.y;
                    positionSeq[positionSeq.length] = 93;
                    //
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                    //
                }
                //
                let roamSpeed = (cameras[n - 1].speed) ? (cameras[n - 1].speed) : speed;
                let time = targetPosition.clone().sub(preTargetPosition).length()/roamSpeed;
                time = time >= minTime ? time : minTime;
                times[times.length] = times[times.length - 1] + time;
                //
                preTargetPosition = targetPosition.clone();
                //
                let camera_mesh = this.getCameraMesh(camera);
                cameraMesh.add(camera_mesh);
            }
            let openOn=0; //开关:如果进行修改更新为1,不修改为0
            if(roamingMode == 0&& openOn == 1) {   //只能在自由模式中修改俯仰角、朝向角
                let tempNum = 1;  //这个为需要修改的相机序列坐标,也就是要修改的路径点坐标
                cameras[tempNum] = this.changeCameraByParams(cameras[1], -30, 0);  //调用changeCameraByParams函数,获得更新之后的相机序列 pitch 朝向 yaw 俯仰角
                {
                    //以下是更新序列中的值
                    positionSeq[tempNum * 3] = cameras[tempNum].position.x;
                    positionSeq[tempNum * 3 + 1] = cameras[tempNum].position.y;
                    positionSeq[tempNum * 3 + 2] = cameras[tempNum].position.z;
                    //
                    quaternionSeq[tempNum * 4] = cameras[tempNum].quaternion.x;
                    quaternionSeq[tempNum * 4 + 1] = cameras[tempNum].quaternion.y;
                    quaternionSeq[tempNum * 4 + 2] = cameras[tempNum].quaternion.z;
                    quaternionSeq[tempNum * 4 + 3] = cameras[tempNum].quaternion.w;
                }
            }
            //
            positionKF = new THREE.VectorKeyframeTrack( '.position', times, positionSeq);
            quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', times, quaternionSeq);
            //
            let fitTerrainMaterialGen = new tjh.ar.FitTerrainMaterial();
            let lineMaterial = fitTerrainMaterialGen.getLineMaterial(10);
            lineMaterial.color =  new THREE.Color(0x00ff00);
            lineMaterial.uniforms["linewidth"].value = 2;
            lineMaterial.polygonOffset = true;
            lineMaterial.polygonOffsetFactor = -10;
            lineMaterial.polygonOffsetUnits = -10;
            lineMaterial.transparent = false;
            let lineGeom =new THREE.LineGeometry();
            let tmp = [];
            for(let i=0, ln=positionSeq.length/3; i<ln; ++i) {
                tmp.push(positionSeq[i*3 + 0] - positionSeq[0]);
                tmp.push(positionSeq[i*3 + 1] - positionSeq[1]);
                tmp.push(positionSeq[i*3 + 2] - positionSeq[2]);
            }
            lineGeom.setPositions( tmp );
            let lineMesh = new THREE.Line2(lineGeom, lineMaterial);
            lineMesh.position.set(positionSeq[0], positionSeq[1], positionSeq[2]);
            lineMesh.updateMatrixWorld(true);
            pathMesh.add(lineMesh);
            //
            clip = null;
            mixer = null;
        }
        //
        this.setPathFromMatrixes = function (viewMts, projMts, speedList) {
            let cameras = [];
            for(let i=0, ln = viewMts.length; i<ln; ++i) {
                let lookAt = viewMts[i].getLookAt();
                let perspective = projMts[i].getPerspective();
                if(perspective) {
                    let camera = new THREE.PerspectiveCamera(perspective.fovy, perspective.aspectRatio, perspective.zNear, perspective.zFar);
                    camera.up.set(0,0,1);
                    camera.position.copy(lookAt.eye);
                    camera.lookAt(lookAt.eye.add(lookAt.lookDirection.multiplyScalar(10)));
                    camera.updateMatrixWorld(true);
                    // 每段路径 相机对应的速度
                    camera.speed = speedList[i] ? speedList[i] : speed;
                    cameras.push(camera);
                }
            }
            //
            this.setPathFromCameras(cameras);
        }
        //
        /**
         * 向路径中添加相机序列
         * @param {array} cameras -相机序列
         */
        this.addCameraToPath = function (cameras) {
            if(cameras.length < 1) {
                return ;
            }
            //
            this._cameras_ = this._cameras_.concat(cameras);
            //
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();
            //
            let preTargetPosition = new THREE.Vector3();
            for(let n=0, ncamera = cameras.length; n<ncamera; ++n) {
                let camera = cameras[n];
                camera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
                //
                if(times.length === 0) {
                    times[times.length] = 0;
                } else {
                    preTargetPosition.x = positionSeq[positionSeq.length - 3];
                    preTargetPosition.y = positionSeq[positionSeq.length - 2];
                    preTargetPosition.z = positionSeq[positionSeq.length - 1];
                    //
                    let time = targetPosition.clone().sub(preTargetPosition).length()/speed;
                    time = time >= minTime ? time : minTime;
                    times[times.length] = times[times.length - 1] + time;
                }
                //
                positionSeq[positionSeq.length] = targetPosition.x;
                positionSeq[positionSeq.length] = targetPosition.y;
                positionSeq[positionSeq.length] = targetPosition.z;
                //
                quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                //
                let camera_mesh = this.getCameraMesh(camera);
                cameraMesh.add(camera_mesh);
            }
            //
            positionKF = new THREE.VectorKeyframeTrack( '.position', times, positionSeq);
            quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', times, quaternionSeq);
            //
            if(pathMesh.children.length !== 0) {
                let tmp = pathMesh.children[0];
                pathMesh.remove(tmp);
                tmp.dispose();
            }
            //
            if(positionSeq.length > 3) {
                let fitTerrainMaterialGen = new tjh.ar.FitTerrainMaterial();
                let lineMaterial = fitTerrainMaterialGen.getLineMaterial(10);
                lineMaterial.color =  new THREE.Color(0x00ff00);
                lineMaterial.uniforms["linewidth"].value = 2;
                lineMaterial.polygonOffset = true;
                lineMaterial.polygonOffsetFactor = -10;
                lineMaterial.polygonOffsetUnits = -10;
                lineMaterial.transparent = false;
                let lineGeom =new THREE.LineGeometry();
                lineGeom.setPositions( positionSeq );
                let lineMesh = new THREE.Line2(lineGeom, lineMaterial);
                pathMesh.add(lineMesh);
            }
            //
            clip = null;
            mixer = null;
        }
        //
        this.showPath = function () {
            pathMesh.disabled = false;
            pathMesh.visible = true;
        }
        this.hidePath = function () {
            pathMesh.disabled = true;
            pathMesh.visible = false;
        }
        this.showCamera = function () {
            cameraMesh.disabled = false;
            cameraMesh.visible = true;
        }
        this.hideCamera = function () {
            cameraMesh.disabled = true;
            cameraMesh.visible = false;
        }
        this.setModelUrl = (url)=>{   //模型导入
                var objLoader = new THREE.OBJLoader2();
                let materialPath = url.substr(0, url.lastIndexOf('.'))+".mtl";
                var callbackOnLoad = ( event )=> {
                    event.detail.loaderRootNode.isPlantModel = true;
                    this.model = event.detail.loaderRootNode;
                    modelinfor = event.detail.loaderRootNode;
                    if(roamingMode == 1)
                    {
                        modelinfor.rotateX(Math.PI/2);
                        modelinfor.rotateY(Math.PI/2);
                    }
                    this.model= modelinfor;  //modelinfor 接受模型信息

                    objModel.add(this.model);
                };
                var onLoadMtl = ( materials )=> {
                    objLoader.setMaterials( materials );
                    objLoader.load( url, callbackOnLoad, null, null, null, false );
                };
                objLoader.loadMtl( materialPath, null, onLoadMtl );
        }
    }
}

2.2、pathRoam.js v2版本(可用)

/**
 * @classdesc 智能漫游操作器
 * @class
 * @memberOf tjh.ar
 * @param {viewer} viewer -视景器
 */

class SmartRoam extends tjh.ar.WindowEventListener {
    constructor(viewer, pauseWhenStart = false) {
        super(viewer);
        let that = this;
        //
        let roamPath = [];
        let positionSeq = [];
        let quaternionSeq = []; //四元数
        let times = [];
        //
        let nextPosition =[];
        //
        let positionKF = null;
        let quaternionKF = null;
        //
        let updateProxy = new THREE.Object3D();
        let pathMesh = new THREE.Group();
        //pathMesh.disabled = false;
        //pathMesh.visible = true;
        let cameraMesh = new THREE.Group();
        let objModel = new THREE.Group();
        let modelinfor = new THREE.Group();
        //cameraMesh.disabled = false;
        //cameraMesh.visible = true;
        this.tempObj.add(updateProxy);
        this.tempObj.add(pathMesh);
        this.tempObj.add(cameraMesh);

        //
        let  clip = null;
        let mixer = null;
        let cameraX;
        let cameraY;
        let cameraZ;
        let clock = null;
        let roaming = false;
        let speed = 10;
        let minTime = 0.5;
        let roamingMode;//默认0,自由模式;1无人机飞行模式,2车第一人称视角,3车第三人称视角
        let flyingHeight=150; //飞行高度默认150
        let forwardMode;
        let lastCarPosX = 0;
        let lastCarPosY = 0;
        let lastCarPosZ = 0;
        let scaleCar = 0.13;  //汽车尺寸 默认 0.13
        let scaleUAV = 0.8;   //无人机尺寸 默认0.8
        let carCameraHeight = 93;  //汽车模式下相机的高度,根据地形设置
        //
        updateProxy.update = (context)=> {
            if(!mixer && positionSeq && quaternionKF) {
                clip = new THREE.AnimationClip('Action', -1, [positionKF, quaternionKF]);
                clip.optimize();
                mixer = new THREE.AnimationMixer(viewer.camera);
                // create a ClipAction and set it to play
                let clipAction = mixer.clipAction(clip);
                let action = clipAction.play();
                action.setLoop(THREE.LoopOnce);//设置循环
                action.clampWhenFinished = true;  //动画将在最后一帧之后自动暂停
                mixer.addEventListener("finished", () => {  //全部动作的结束
                    roaming = false;
                    if (that.onEnd) {
                        that.onEnd();
                    }
                });
            }
            else if(roaming) {
                mixer.update(clock.getDelta());  //推进混合器时间并更新动画
                //获取相机位置
                 cameraX = context.camera.position.x;
                 cameraY = context.camera.position.y;
                 cameraZ = context.camera.position.z;

                let camerqquax = context.camera.quaternion.x;
                let camerqquay = context.camera.quaternion.y;
                let camerqquaz = context.camera.quaternion.z;
                let camerqquaw = context.camera.quaternion.w;

                //更新模型位
                if(roamingMode ==1)   //如果是飞行模式,调整模型位置
                {
                    modelinfor.scale.set(scaleUAV,scaleUAV,scaleUAV);//设置尺寸
                    modelinfor.position.x = cameraX+1.5;
                    modelinfor.position.y = cameraY;
                    modelinfor.position.z = cameraZ-20;
                    context.camera.lookAt(modelinfor.position.x,modelinfor.position.y,0);
                    //context.camera.lookAt(modelinfor.position);
                }

                else if(roamingMode == 2) {   //如果是汽车模式第三人称,调整模型位置
                    modelinfor.scale.set(scaleCar,scaleCar,scaleCar);

                    modelinfor.position.x = 0;
                    modelinfor.position.y = -3.5/93 * carCameraHeight;
                    modelinfor.position.z = -10;
                    context.camera.up.x = 0;
                    context.camera.up.y = 0;
                    context.camera.up.z = 1;
                    //modelinfor.quaternion.x = camerqquax;
                    //modelinfor.quaternion.y = camerqquay;
                    //modelinfor.quaternion.z = camerqquaz;
                    //modelinfor.quaternion.w = camerqquaw;

                    //modelinfor.rotateY(Math.PI);
                    //modelinfor.rotateX(Math.PI/2);
                    //modelinfor.lookAt(modelinfor.position);
                }
                else if(roamingMode == 3) {   //如果是汽车模式第一人称,调整模型位置
                    modelinfor.scale.set(scaleCar,scaleCar,scaleCar);

                    modelinfor.position.x = 0.4;
                    modelinfor.position.y = -1.58/93 * carCameraHeight;
                    modelinfor.position.z = 0.8 ;
                    context.camera.up.x = 0;
                    context.camera.up.y = 0;
                    context.camera.up.z = 1;
                }
                modelinfor.updateMatrixWorld(true);

            }
            //
            if(pathMesh.children.length > 0) {
                let resolution = pathMesh.children[0].material.uniforms["resolution"].value;
                if(resolution.x !== context.clientWidth || resolution.y !== context.clientHeight) {
                    pathMesh.children[0].material.uniforms["resolution"].value.set(context.clientWidth, context.clientHeight);
                    pathMesh.children[0].material.needUpdate = true;
                }
            }
        };

        /**
         * 开始漫游
         */
        this.start = function () {
            if(mixer) {
                let clipAction = mixer.clipAction( clip );
                let action = clipAction.play();
                action.reset();
            }
            roaming = true;
            //
            clock = new THREE.Clock();
            //
            if(this.onStart) {
                this.onStart();
            }
        }

        /**
         * 暂停漫游
         */
        this.pause = function () {
            if(mixer && roaming) {
                mixer.timeScale = 0;
                if(this.onPause) {
                    this.onPause();
                }
            }
        }

        /**
         * 重新开始漫游
         */
        this.resume = function () {
            if(mixer && roaming) {
                mixer.timeScale = 1;
                if(this.onResume) {
                    this.onResume();
                }
            }
        }

        /**
         * 设置漫游速度 -- m/s
         */
        this.setSpeed = function (s) {
            speed = s;
        }
        this.getSpeed = function () {
            return speed;
        }

        this.setMinTime = function (t) {
            minTime = t;
        }

        this.getMinTime = function () {
            return minTime;
        }

        /**
         * 设置漫游模式
         */
        this.setroamingMode = function(m)
        {
            roamingMode = m;
        }

        /**
         * 设置飞行高度
         */
        this.flyHeight = function (h) {
            flyingHeight = h;
        }

        this.setScaleCar = function(s){
            scaleCar = s;
        }
        this.setScaleUav = function (s) {
            scaleUAV = s;

        }



        /**
         * 改变相机(通过传入的位置信息,俯仰角,朝向)生成一个新的相机,用于更新漫游相机序列
         * @param  cameras -相机序列
         * @param  parames -相机序列
         * @param {*}
         */
        this.changeCameraByParams  = (camera,pitch,yaw) => {   //pitch 俯仰  yaw 朝向
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();
            let tempEuler = new THREE.Euler();
            //欧拉角
            let newCamera = camera;
            pitch = pitch*Math.PI/180;
            yaw = yaw * Math.PI/180;
            //分解newCamera,分离出位置信息,四元数信息,和scale
            newCamera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
            //为传进来的四元数转化为欧拉角,即旋转角,俯仰角,朝向角
            targetQuaternion.normalize();
            tempEuler.setFromQuaternion(targetQuaternion);
            var pitchAngle = tempEuler.x * 180 / Math.PI;  //初始俯仰角
            var yawAngle = tempEuler.y * 180 / Math.PI;   //初始朝向角
            //根据传入的xyz修改相机的xyz
            //newCamera.position.x = x;
            //newCamera.position.y = y;
            //newCamera.position.z = z;
            tempEuler.x = tempEuler.x+ pitch; //俯仰
            tempEuler.y = tempEuler.y+yaw;  //朝向
            tempEuler.z = tempEuler.z;   //旋转
            targetQuaternion.setFromEuler(tempEuler);
            newCamera.quaternion.w = targetQuaternion.w;
            newCamera.quaternion.x = targetQuaternion.x;
            newCamera.quaternion.y = targetQuaternion.y;
            newCamera.quaternion.z = targetQuaternion.z;
            //返回
            return newCamera;
        }
        let material1 = new THREE.MeshBasicMaterial({transparent:true, opacity:0.3});  //基础网格材质
        let material2 = new THREE.LineBasicMaterial({color: 0x00ff00});  //基础线条材质
        this.getCameraMesh = (camera)=> {
            let projMatrix = camera.projectionMatrix;  //投影变换矩阵
            //
            let perspective = projMatrix.getPerspective();
            let temp_proj_matrix = new THREE.Matrix4();
            temp_proj_matrix.makePerspective(perspective.fovy, perspective.aspectRatio, 5, 50);  //创建一个透视投影矩阵
            let nearPlaneCorners = [];
            nearPlaneCorners[0] = new THREE.Vector3(-1.0, -1.0, -1.0);
            nearPlaneCorners[1] = new THREE.Vector3(1.0, -1.0, -1.0);
            nearPlaneCorners[2] = new THREE.Vector3(1.0, 1.0, -1.0);
            nearPlaneCorners[3] = new THREE.Vector3(-1.0, 1.0, -1.0);

            let inv_proj_matrix = new THREE.Matrix4();
            inv_proj_matrix.getInverse(temp_proj_matrix);
            nearPlaneCorners[0].applyMatrix4(inv_proj_matrix);  //将该向量乘以四阶矩阵(透视投影矩阵
            nearPlaneCorners[1].applyMatrix4(inv_proj_matrix);
            nearPlaneCorners[2].applyMatrix4(inv_proj_matrix);
            nearPlaneCorners[3].applyMatrix4(inv_proj_matrix);
            //
            let vertexes = [0,0,0];
            for(let i=0; i<4; ++i) {
                vertexes.push(nearPlaneCorners[i].x);
                vertexes.push(nearPlaneCorners[i].y);
                vertexes.push(nearPlaneCorners[i].z);
            }
            let plane_indexes = [];
            plane_indexes.push(1); plane_indexes.push(2); plane_indexes.push(3);
            plane_indexes.push(1); plane_indexes.push(3); plane_indexes.push(4);
            let uv = [0,0,1,0,1,1,0,1];
            let plane_geometry = new THREE.BufferGeometry(); //顶点位置,颜色值,uv坐标和自定义缓存属性值。
            plane_geometry.addAttribute('position', new THREE.BufferAttribute( new Float32Array(vertexes), 3));
            plane_geometry.addAttribute('uv', new THREE.BufferAttribute( new Float32Array(uv), 2));
            plane_geometry.setIndex(new THREE.BufferAttribute(new Uint8Array(plane_indexes), 1));
            //
            let edge_indexes = [0,1,0,2,0,3,0,4,1,2,2,3,3,4,4,1];
            let edge_geometry = new THREE.BufferGeometry();
            edge_geometry.addAttribute('position', new THREE.BufferAttribute( new Float32Array(vertexes), 3));
            edge_geometry.setIndex(new THREE.BufferAttribute( new Uint8Array(edge_indexes), 1));
            //
            let plane_mesh = new THREE.Mesh(plane_geometry, material1);
            //plane_mesh.isPlantModel = true;
            //
            let edge_mesh = new THREE.LineSegments(edge_geometry, material2);
            edge_mesh.canNotBeSelected = true;
            //
            let camera_mesh = new THREE.Group();
            //camera_mesh.add(plane_mesh);
            camera_mesh.add(edge_mesh);
            //
            let p = new THREE.Vector3();
            let q = new THREE.Quaternion();
            let s = new THREE.Vector3();
            camera.matrixWorld.decompose(p, q, s);
            camera_mesh.position.copy(p);
            camera_mesh.quaternion.copy(q);
            camera_mesh.scale.copy(s);
            camera_mesh.updateMatrixWorld(true);
            //
            return camera_mesh;
        }

        /**
         * 生成漫游序列
         * @param {array} path -路径坐标数据
         */
        this.setSites = function (path) {
            roamPath = path;
            //
            positionSeq = [];
            quaternionSeq = [];
            times = [];
            //
            let camera = new THREE.PerspectiveCamera(45, 1.0, 1.0, 15000);
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();

            for(let n=0, length = path.length - 1; n<length; ++n) {
                camera.position.copy(path[n]);   //相机的位置
                camera.up.set(0,0,1);
                camera.lookAt(path[n+1]);  //创建一个看着给定目标的观察矩阵
                camera.updateMatrixWorld(true);
                camera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
                //
                quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.w;


                quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                //
                positionSeq[positionSeq.length] = path[n].x;
                positionSeq[positionSeq.length] = path[n].y;
                positionSeq[positionSeq.length] = path[n].z;
                //
                positionSeq[positionSeq.length] = path[n+1].x;
                positionSeq[positionSeq.length] = path[n+1].y;
                positionSeq[positionSeq.length] = path[n+1].z;
                //
                let time = path[n+1].clone().sub(path[n]).length()/speed;
                time = time >= minTime ? time : minTime;
                times[times.length] = times.length === 0 ? 0 : times[times.length - 1] + 1;
                times[times.length] = times[times.length - 1] + time;
            }
            //
            positionKF = new THREE.VectorKeyframeTrack( '.position', times, positionSeq);
            quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', times, quaternionSeq);
            //
            let fitTerrainMaterialGen = new tjh.ar.FitTerrainMaterial();
            let lineMaterial = fitTerrainMaterialGen.getLineMaterial(10);
            lineMaterial.color =  new THREE.Color(0x00ff00);
            lineMaterial.uniforms["linewidth"].value = 2;
            lineMaterial.polygonOffset = true;  //多边形偏移
            lineMaterial.polygonOffsetFactor = -10;  //多边形偏移系数
            lineMaterial.polygonOffsetUnits = -10;  //多边形偏移单位
            lineMaterial.transparent = false;
            let lineGeom =new THREE.LineGeometry();
            lineGeom.setPositions( positionSeq );//位置
            let lineMesh = new THREE.Line2(lineGeom, lineMaterial);
            pathMesh.add(lineMesh);
            //
            clip = null;
            mixer = null;
        };
        /**
         * 生成路径
         * @param {array} path -路径坐标数据
         * @param {THREE.Vector3} lookTarget -目标
         */
        this.setPath = function (path, lookTarget) {
            if(!lookTarget) {
                this.setSites(path);
                return ;
            }
            //
            positionSeq = [path[0].x, path[0].y, path[0].z];
            times[0] = 0;
            for(let n=1, length = path.length; n<length; ++n) {
                positionSeq[positionSeq.length] = path[n].x; //把路径坐标数据放进positionSeq中
                positionSeq[positionSeq.length] = path[n].y;
                positionSeq[positionSeq.length] = path[n].z;
                //
                let time = path[n].clone().sub(path[n-1]).length()/speed;
                time = time >= minTime ? time : minTime;
                times[times.length] = times[times.length - 1] + time;
            }
            //
            quaternionSeq = [];
            let camera = new THREE.PerspectiveCamera(45, 1.0, 1.0, 15000);
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();
            //
            if(lookTarget instanceof  THREE.Vector3) {
                for(let n=0, length = path.length; n<length; ++n) {
                    camera.position.copy(path[n]);
                    camera.up.set(0,0,1);
                    camera.lookAt(lookTarget);
                    camera.updateMatrixWorld(true);
                    camera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
                    //
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                }
            }
            else if(lookTarget instanceof  Array) {

            }
            //
            positionKF = new THREE.VectorKeyframeTrack( '.position', times, positionSeq);
            quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', times, quaternionSeq);
            //
            let fitTerrainMaterialGen = new tjh.ar.FitTerrainMaterial();
            let lineMaterial = fitTerrainMaterialGen.getLineMaterial(10);
            lineMaterial.color =  new THREE.Color(0x00ff00);
            lineMaterial.uniforms["linewidth"].value = 2;
            lineMaterial.polygonOffset = true;
            lineMaterial.polygonOffsetFactor = -10;
            lineMaterial.polygonOffsetUnits = -10;
            lineMaterial.transparent = false;
            let lineGeom =new THREE.LineGeometry();
            lineGeom.setPositions( positionSeq );
            let lineMesh = new THREE.Line2(lineGeom, lineMaterial);
            pathMesh.add(lineMesh);
            //
            clip = null;
            mixer = null;
        }
        //
        /**
         * 生成路径
         * @param {array} cameras -相机序列
         */
        this.setPathFromCameras = function (cameras) {
            if(cameras.length < 2) {
                return ;
            }
            //
            this._cameras_ = cameras;
            //
            positionSeq = [];
            times = [];
            quaternionSeq = [];
            nextPosition = [];
            //
            let preTargetPosition = null;
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();
            //
            let camera0 = cameras[0];
            camera0.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);

            if(roamingMode == 0)
            {
                positionSeq = [targetPosition.x, targetPosition.y, targetPosition.z];
                quaternionSeq = [targetQuaternion.x, targetQuaternion.y, targetQuaternion.z, targetQuaternion.w];
            }
            else if(roamingMode == 1)
            {
                positionSeq = [targetPosition.x, targetPosition.y, targetPosition.z+flyingHeight];
                quaternionSeq = [targetQuaternion.x, targetQuaternion.y, targetQuaternion.z, targetQuaternion.w];
            }
            else if(roamingMode == 2)
            {
                positionSeq = [targetPosition.x, targetPosition.y, carCameraHeight];
                quaternionSeq = [targetQuaternion.x, targetQuaternion.y, targetQuaternion.z, targetQuaternion.w];
            }
            else if(roamingMode == 3)
            {
                positionSeq = [targetPosition.x, targetPosition.y, carCameraHeight];
                quaternionSeq = [targetQuaternion.x, targetQuaternion.y, targetQuaternion.z, targetQuaternion.w];
            }
            times = [0];
            //
            {
                let camera_mesh = this.getCameraMesh(camera0);
                cameraMesh.add(camera_mesh);
            }
            //
            preTargetPosition = targetPosition.clone();
            for(let n=1, ncamera = cameras.length; n<ncamera; ++n) {
                let camera = cameras[n];
                camera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
                //
                if (roamingMode == 0) {   //自由模式
                    positionSeq[positionSeq.length] = targetPosition.x;
                    positionSeq[positionSeq.length] = targetPosition.y;
                    positionSeq[positionSeq.length] = targetPosition.z
                    //
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                    //

                }
                else if (roamingMode == 1) {   //飞行模式

                    positionSeq[positionSeq.length] = targetPosition.x;
                    positionSeq[positionSeq.length] = targetPosition.y;
                    positionSeq[positionSeq.length] = targetPosition.z + flyingHeight;

                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;

                }
                else if (roamingMode == 2) {  //车第三视角模式
                    positionSeq[positionSeq.length] = targetPosition.x;
                    positionSeq[positionSeq.length] = targetPosition.y;
                    positionSeq[positionSeq.length] = carCameraHeight;
                    //
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                    //
                }
                else if (roamingMode == 3) {  //车第一视角模式
                    positionSeq[positionSeq.length] = targetPosition.x;
                    positionSeq[positionSeq.length] = targetPosition.y;
                    positionSeq[positionSeq.length] = carCameraHeight;
                    //
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                    quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                    //
                }
                //
                let roamSpeed = (cameras[n - 1].speed) ? (cameras[n - 1].speed) : speed;
                let time = targetPosition.clone().sub(preTargetPosition).length()/roamSpeed;
                time = time >= minTime ? time : minTime;
                times[times.length] = times[times.length - 1] + time;
                //
                preTargetPosition = targetPosition.clone();
                //
                let camera_mesh = this.getCameraMesh(camera);
                cameraMesh.add(camera_mesh);
            }
            let openOn=1; //开关:如果进行修改更新为1,不修改为0
            if(roamingMode == 0&& openOn == 1) {   //只能在自由模式中修改俯仰角、朝向角
                let tempNum = 1;  //这个为需要修改的相机序列坐标,也就是要修改的路径点坐标
                cameras[tempNum] = this.changeCameraByParams(cameras[1], -30, 0);  //调用changeCameraByParams函数,获得更新之后的相机序列 pitch 朝向 yaw 俯仰角
                {
                    //以下是更新序列中的值
                    positionSeq[tempNum * 3] = cameras[tempNum].position.x;
                    positionSeq[tempNum * 3 + 1] = cameras[tempNum].position.y;
                    positionSeq[tempNum * 3 + 2] = cameras[tempNum].position.z;
                    //
                    quaternionSeq[tempNum * 4] = cameras[tempNum].quaternion.x;
                    quaternionSeq[tempNum * 4 + 1] = cameras[tempNum].quaternion.y;
                    quaternionSeq[tempNum * 4 + 2] = cameras[tempNum].quaternion.z;
                    quaternionSeq[tempNum * 4 + 3] = cameras[tempNum].quaternion.w;
                }
            }
            //
            positionKF = new THREE.VectorKeyframeTrack( '.position', times, positionSeq);
            quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', times, quaternionSeq);
            //
            let fitTerrainMaterialGen = new tjh.ar.FitTerrainMaterial();
            let lineMaterial = fitTerrainMaterialGen.getLineMaterial(10);
            lineMaterial.color =  new THREE.Color(0x00ff00);
            lineMaterial.uniforms["linewidth"].value = 2;
            lineMaterial.polygonOffset = true;
            lineMaterial.polygonOffsetFactor = -10;
            lineMaterial.polygonOffsetUnits = -10;
            lineMaterial.transparent = false;
            let lineGeom =new THREE.LineGeometry();
            let tmp = [];
            for(let i=0, ln=positionSeq.length/3; i<ln; ++i) {
                tmp.push(positionSeq[i*3 + 0] - positionSeq[0]);
                tmp.push(positionSeq[i*3 + 1] - positionSeq[1]);
                tmp.push(positionSeq[i*3 + 2] - positionSeq[2]);
            }
            lineGeom.setPositions( tmp );
            let lineMesh = new THREE.Line2(lineGeom, lineMaterial);
            lineMesh.position.set(positionSeq[0], positionSeq[1], positionSeq[2]);
            lineMesh.updateMatrixWorld(true);
            pathMesh.add(lineMesh);
            //
            clip = null;
            mixer = null;
        }
        //
        this.setPathFromMatrixes = function (viewMts, projMts, speedList) {
            let cameras = [];
            for(let i=0, ln = viewMts.length; i<ln; ++i) {
                let lookAt = viewMts[i].getLookAt();
                let perspective = projMts[i].getPerspective();
                if(perspective) {
                    let camera = new THREE.PerspectiveCamera(perspective.fovy, perspective.aspectRatio, perspective.zNear, perspective.zFar);
                    camera.up.set(0,0,1);
                    camera.position.copy(lookAt.eye);
                    camera.lookAt(lookAt.eye.add(lookAt.lookDirection.multiplyScalar(10)));
                    camera.updateMatrixWorld(true);
                    // 每段路径 相机对应的速度
                    camera.speed = speedList[i] ? speedList[i] : speed;
                    cameras.push(camera);
                }
            }
            //
            this.setPathFromCameras(cameras);
        }
        //
        /**
         * 向路径中添加相机序列
         * @param {array} cameras -相机序列
         */
        this.addCameraToPath = function (cameras) {
            if(cameras.length < 1) {
                return ;
            }
            //
            this._cameras_ = this._cameras_.concat(cameras);
            //
            let targetPosition = new THREE.Vector3();
            let targetQuaternion = new THREE.Quaternion();
            let targetScale = new THREE.Vector3();
            //
            let preTargetPosition = new THREE.Vector3();
            for(let n=0, ncamera = cameras.length; n<ncamera; ++n) {
                let camera = cameras[n];
                camera.matrixWorld.decompose(targetPosition, targetQuaternion, targetScale);
                //
                if(times.length === 0) {
                    times[times.length] = 0;
                } else {
                    preTargetPosition.x = positionSeq[positionSeq.length - 3];
                    preTargetPosition.y = positionSeq[positionSeq.length - 2];
                    preTargetPosition.z = positionSeq[positionSeq.length - 1];
                    //
                    let time = targetPosition.clone().sub(preTargetPosition).length()/speed;
                    time = time >= minTime ? time : minTime;
                    times[times.length] = times[times.length - 1] + time;
                }
                //
                positionSeq[positionSeq.length] = targetPosition.x;
                positionSeq[positionSeq.length] = targetPosition.y;
                positionSeq[positionSeq.length] = targetPosition.z;
                //
                quaternionSeq[quaternionSeq.length] = targetQuaternion.x;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.y;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.z;
                quaternionSeq[quaternionSeq.length] = targetQuaternion.w;
                //
                let camera_mesh = this.getCameraMesh(camera);
                cameraMesh.add(camera_mesh);
            }
            //
            positionKF = new THREE.VectorKeyframeTrack( '.position', times, positionSeq);
            quaternionKF = new THREE.QuaternionKeyframeTrack( '.quaternion', times, quaternionSeq);
            //
            if(pathMesh.children.length !== 0) {
                let tmp = pathMesh.children[0];
                pathMesh.remove(tmp);
                tmp.dispose();
            }
            //
            if(positionSeq.length > 3) {
                let fitTerrainMaterialGen = new tjh.ar.FitTerrainMaterial();
                let lineMaterial = fitTerrainMaterialGen.getLineMaterial(10);
                lineMaterial.color =  new THREE.Color(0x00ff00);
                lineMaterial.uniforms["linewidth"].value = 2;
                lineMaterial.polygonOffset = true;
                lineMaterial.polygonOffsetFactor = -10;
                lineMaterial.polygonOffsetUnits = -10;
                lineMaterial.transparent = false;
                let lineGeom =new THREE.LineGeometry();
                lineGeom.setPositions( positionSeq );
                let lineMesh = new THREE.Line2(lineGeom, lineMaterial);
                pathMesh.add(lineMesh);
            }
            //
            clip = null;
            mixer = null;
        }
        //
        this.showPath = function () {
            pathMesh.disabled = false;
            pathMesh.visible = true;
        }
        this.hidePath = function () {
            pathMesh.disabled = true;
            pathMesh.visible = false;
        }
        this.showCamera = function () {
            cameraMesh.disabled = false;
            cameraMesh.visible = true;
        }
        this.hideCamera = function () {
            cameraMesh.disabled = true;
            cameraMesh.visible = false;
        }
        this.setModelUrl = (url)=>{   //模型导入
                var objLoader = new THREE.OBJLoader2();
                let materialPath = url.substr(0, url.lastIndexOf('.'))+".mtl";
                var callbackOnLoad = ( event )=> {
                    event.detail.loaderRootNode.isPlantModel = true;
                    this.model = event.detail.loaderRootNode;
                    modelinfor = event.detail.loaderRootNode;
                    if(roamingMode == 1)
                    {
                        modelinfor.rotateX(Math.PI/2);
                        modelinfor.rotateY(Math.PI/2);
                    }
                    else
                    {
                        modelinfor.rotateY(Math.PI);
                    }
                    this.model= modelinfor;  //modelinfor 接受模型信息
                    objModel.add(this.model);
                    if(roamingMode ==2 || roamingMode ==3) {
                        camera.add(objModel);
                        this.tempObj.add(camera);
                    }
                    else{
                        this.tempObj.add(objModel);
                    }

                };
                var onLoadMtl = ( materials )=> {
                    objLoader.setMaterials( materials );
                    objLoader.load( url, callbackOnLoad, null, null, null, false );
                };
                objLoader.loadMtl( materialPath, null, onLoadMtl );
        }
    }
}

3、pathCreateTool.js

/**
 * @classdesc 路径创建工具
 * @class
 * @memberOf tjh.ar
 * @param {viewer} viewer -视景器
 */

class PathCreateTool extends  tjh.ar.tools.ToolBase {
    constructor(viewer) {
        super(viewer);
        //
        let pathMesh = new THREE.Group();
        pathMesh.visible = true;
        let cameraMesh = new THREE.Group();
        cameraMesh.visible = true;
        this.tempObj.add(pathMesh);
        this.tempObj.add(cameraMesh);
        //
        this.cameras = [];
        //
        let fitTerrainMaterialGen = new tjh.ar.FitTerrainMaterial();
        let lineMaterial = fitTerrainMaterialGen.getLineMaterial(10);
        lineMaterial.color =  new THREE.Color(0x00ff00);
        lineMaterial.uniforms["linewidth"].value = 2;
        /**
         * 路径坐标 pathVertexes
         * @type {array}
         */
        this.pathVertexes = [];
        let pathPoints = [];
        //
        let material1 = new THREE.MeshBasicMaterial({transparent:true, opacity:0.3});
        let material2 = new THREE.LineBasicMaterial({color: 0x00ff00});
        this.getCameraMesh = (camera)=> {
            let projMatrix = camera.projectionMatrix;
            //
            let perspective = projMatrix.getPerspective();
            let temp_proj_matrix = new THREE.Matrix4();
            temp_proj_matrix.makePerspective(perspective.fovy, perspective.aspectRatio, 5, 50);
            let nearPlaneCorners = [];
            nearPlaneCorners[0] = new THREE.Vector3(-1.0, -1.0, -1.0);
            nearPlaneCorners[1] = new THREE.Vector3(1.0, -1.0, -1.0);
            nearPlaneCorners[2] = new THREE.Vector3(1.0, 1.0, -1.0);
            nearPlaneCorners[3] = new THREE.Vector3(-1.0, 1.0, -1.0);

            let inv_proj_matrix = new THREE.Matrix4();
            inv_proj_matrix.getInverse(temp_proj_matrix);
            nearPlaneCorners[0].applyMatrix4(inv_proj_matrix);
            nearPlaneCorners[1].applyMatrix4(inv_proj_matrix);
            nearPlaneCorners[2].applyMatrix4(inv_proj_matrix);
            nearPlaneCorners[3].applyMatrix4(inv_proj_matrix);
            //
            let vertexes = [0,0,0];
            for(let i=0; i<4; ++i) {
                vertexes.push(nearPlaneCorners[i].x);
                vertexes.push(nearPlaneCorners[i].y);
                vertexes.push(nearPlaneCorners[i].z);
            }
            let plane_indexes = [];
            plane_indexes.push(1); plane_indexes.push(2); plane_indexes.push(3);
            plane_indexes.push(1); plane_indexes.push(3); plane_indexes.push(4);
            let uv = [0,0,1,0,1,1,0,1];
            let plane_geometry = new THREE.BufferGeometry();
            plane_geometry.addAttribute('position', new THREE.BufferAttribute( new Float32Array(vertexes), 3));
            plane_geometry.addAttribute('uv', new THREE.BufferAttribute( new Float32Array(uv), 2));
            plane_geometry.setIndex(new THREE.BufferAttribute(new Uint8Array(plane_indexes), 1));
            //
            let edge_indexes = [0,1,0,2,0,3,0,4,1,2,2,3,3,4,4,1];
            let edge_geometry = new THREE.BufferGeometry();
            edge_geometry.addAttribute('position', new THREE.BufferAttribute( new Float32Array(vertexes), 3));
            edge_geometry.setIndex(new THREE.BufferAttribute( new Uint8Array(edge_indexes), 1));
            //
            let plane_mesh = new THREE.Mesh(plane_geometry, material1);
            //plane_mesh.isPlantModel = true;
            //
            let edge_mesh = new THREE.LineSegments(edge_geometry, material2);
            edge_mesh.canNotBeSelected = true;
            //
            let camera_mesh = new THREE.Group();
            //camera_mesh.add(plane_mesh);
            camera_mesh.add(edge_mesh);
            //
            let p = new THREE.Vector3();
            let q = new THREE.Quaternion();
            let s = new THREE.Vector3();
            camera.matrixWorld.decompose(p, q, s);
            camera_mesh.position.copy(p);
            camera_mesh.quaternion.copy(q);
            camera_mesh.scale.copy(s);
            camera_mesh.updateMatrixWorld(true);
            //
            return camera_mesh;
        }
        //
        this.tempObj.update = (context)=> {
            if(pathMesh.children.length > 0) {
                let resolution = pathMesh.children[0].material.uniforms["resolution"].value;
                if(resolution.x !== context.clientWidth || resolution.y !== context.clientHeight) {
                    pathMesh.children[0].material.uniforms["resolution"].value.set(context.clientWidth, context.clientHeight);
                    pathMesh.children[0].material.needUpdate = true;
                }
            }
        }
        //
        this.onKeyDown = function (keyboardEvent) {
            if(keyboardEvent.key === 'c') {
                this.cameras.push(this.viewer.camera.clone());
                //
                let camera_mesh = this.getCameraMesh(this.viewer.camera);
                cameraMesh.add(camera_mesh);
                //
                let lookAt = this.viewer.camera.matrixWorldInverse.getLookAt();
                this.pathVertexes.push(new THREE.Vector3(lookAt.eye.x, lookAt.eye.y, lookAt.eye.z));
                pathPoints.push(lookAt.eye.x);
                pathPoints.push(lookAt.eye.y);
                pathPoints.push(lookAt.eye.z);
                //
                if(pathMesh.children.length !== 0) {
                    let tmp = pathMesh.children[0];
                    pathMesh.remove(tmp);
                    tmp.dispose();
                }
                //
                if(this.cameras.length > 1) {
                    lineMaterial.polygonOffset = true;
                    lineMaterial.polygonOffsetFactor = -10;
                    lineMaterial.polygonOffsetUnits = -10;
                    lineMaterial.transparent = false;
                    let lineGeom =new THREE.LineGeometry();
                    lineGeom.setPositions( pathPoints );
                    let lineMesh = new THREE.Line2(lineGeom, lineMaterial);
                    pathMesh.add(lineMesh);
                }
                //
                return true;
            }
            //
            return false;
        };
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对整threeJS体系进行全面剖析。整理出全面的教学大纲,涵盖内容面非常广。此教学版本为threeJS107版本。关于版本不建议大家使用低于90的版本学习。以下是课程目录1-ThreeJS概览(基本图形简介,什么是点线面如何绘制点线面,什么是材质,什么是几何体,什么是相机,什么是渲染器,什么是场景)2-相机和渲染器(详解相机类型,渲染器如何使用,针对不同场景怎么用,怎么调效果,怎么渲染,怎么输出画布,如何解决透明问题等等)3-创建平面几何(常见的几何体如何使用,如何使用简单的几何体绘制出自定义自己想要的几何体,关于几何体的性能剖析,如何解决性能,几何体的渲染原理)4-高级图形算法常见库(求直线的斜率  计算线段与圆的交点 计算线段的长度 判断折线是否在多边形内 等等)5-sprite精灵(怎么让一个图标永远朝向屏幕,精灵的属性,精灵材质原理等,广告提示框必用)6-骨骼游戏动画(什么是模型动画,常见游戏案例,如何让人头进行各种攻击动作)7-3d模型加载(常见模型格式,如何渲染不同格式,不同格式的特点,什么格式性能优越,模型渲染异常,贴图不显示等问题详解)8-高阶动态纹理(你所不知道的纹理用法,我说你不知道,你肯定不知道)9-漫游轨迹以及其动画路径(怎么绘制贝塞尔曲线,如何使用曲线上的路径,跟随路径移动的原理,相机如何运动,物体如何运动)10-着色器(什么是着色器。初识着色器基础,着色器材质怎么用,怎么使用着色器库)11-常见渲染以及透明度问题12-对象拾取以及拖拽(3d世界里面如何拖拽物体,拖拽的原理,mousemove mouseon等的事件效果)13-世界坐标以及组的问题(什么是相对坐标,什么是世界坐标,什么是当前坐标,怎么转化父子坐标系,组的优化,为什么用组,组的优势)14-指定对象旋转中心(什么是物体的几何体中心,如何改变中心,如何绕轴转动)15-层级对象渲染(多个场景一键切换,切换的优势,针对大项目的用法)16-拓展了解系列(不定期不断更新案例,各种酷炫效果bloom,halo等,以及各种3d图表,粒子案例等,不断构建你的3d实践能力)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值