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;
};
}
}