在移动端实现室内漫游,先看效果截图:
整体的漫游Demo效果看链接:
技术点:
一、加载模型
采用异步加载glb模型。
function loadModel(name) {
var modelName = name + '.glb';
// model
var model = null;
new GLTFLoader().setPath('glb/hp/').load(modelName, function (gltf) {
scene.add(gltf.scene);
model = gltf.scene;
//model.position.x = index * 0.5;
console.log(model.position.x);
model.scale.x = 0.1;
model.scale.y = 0.1;
model.scale.z = 0.1;
// console.log(gltf);
console.log(model);
//获取一个模型的世界坐标之前,需要执行 更新模型的世界矩阵
//scene.updateMatrixWorld(true);
//model.updateMatrixWorld();
// 声明一个三维向量用来保存网格模型的世界坐标
//var worldPosition = new THREE.Vector3();
// 获得世界坐标,执行getWorldPosition方法,提取网格模型的世界坐标结果保存到参数worldPosition中
//model.getWorldPosition(worldPosition)
//console.log(worldPosition);
//console.log("00000000000000000000000000000000000000000000000000");
if (name == 'SM_daohang') { //if (model.name == 'SM_daohang') {
daohangModel = model;
daohangModel.visible = false;
}
loadIndex++;
if (loadIndex < modelList.length) {
updateProgressBar(loadIndex / modelList.length);
loadModel(modelList[loadIndex]);
} else {
hideProgressBar();
//
//hideProgressBar();
// model.children.forEach(mo => {
// console.log(mo.name);
// if (mo.name == 'SM_fangti_01') {
// //console.log("22222");
// //parentObj = mo;
// //mo.visible = false;
// } else if (mo.name.indexOf('shujia') > -1) {
// }
// });
readTxt();
}
});
}
二、空间中路线规划
通过在场景中打点记录下要漫游的路线,打点的方法如下:
function onMouseDBClick(event) {
console.log(camera);
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
// 通过鼠标点的位置和当前相机的矩阵计算出raycaster
raycaster.setFromCamera(mouse, camera);
// 获取raycaster直线和所有模型相交的数组集合
var intersects = raycaster.intersectObjects(scene.children);
// console.log(intersects);
if (intersects.length > 0) {
//SM_fangti_01
//if (intersects[0].object.name == 'SM_fangti_01') {
var index = 0;
//console.log(intersects[0].object.parent);
//intersects[0].object.material.color.set(0xff0000);
var x = intersects[index].point.x;
var y = intersects[index].point.y;
var z = intersects[index].point.z;
//z += 1;//向上抬高
//endPosition = new THREE.Vector3(x, y, z);;
console.log(x, y, z);
pointsList.push(x);
pointsList.push(y);
pointsList.push(z);
//画点
positions = [];
positions.push(x, y, z);
const geometry2 = new THREE.BufferGeometry();
const colors2 = [];
colors2.push(1, 0, 0);
geometry2.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry2.setAttribute('color', new THREE.Float32BufferAttribute(colors2, 3));
geometry2.computeBoundingSphere();
const material2 = new THREE.PointsMaterial({ size: 0.1, vertexColors: true });
// if (myPoints2) {
// scene.remove(myPoints2);
// myPoints2 = null;
// }
var myPoints2 = new THREE.Points(geometry2, material2);
scene.add(myPoints2);
//}
}
}
三、3D场景中画线
画线:通过line2来实现线条的粗细设置
function drawLine2(pointArr) {
// Line2 ( LineGeometry, LineMaterial )
let geometry2 = new LineGeometry();
//var pointArr = [this.model.start.x, this.model.start.y, this.model.start.z, this.model.end.x, this.model.end.y, this.model.end.z]
geometry2.setPositions(pointArr);
let material2 = new LineMaterial({
linewidth: 0.001,
color: 'blue',
dashOffset: 15,
dashScale: 10,
dashSize: 10,
gapSize: 20
});
material2.resolution.set(window.innerWidth + 100, window.innerHeight + 100);//这句如果不加宽度仍然无效
line2 = new Line2(geometry2, material2);
line2.name = "lines";
scene.add(line2);
line2.visible = getBookID();
}
function readTxt() {
positions = [];
var arr = testPoints.split(',');
for (var i = 0; i < arr.length; i++) {
positions.push(arr[i]);
}
//drawLineDashed(positions);
drawLine2(positions);
}
四、自动漫游
采用twenn.js做的漫游动画,只是简单的漫游,很多细节没有做优化
var tween;
var pathIndex = 0;
function cameraRunPath(time) {
isRun = true;
bookClick = false;
var runPosition = runPath[pathIndex];
tween = new TWEEN.Tween(camera.position)
.to({ x: runPosition.x, y: runPosition.y, z: runPosition.z }, time) // 目标位置与时间
.delay(0) // 延迟时间
.easing(TWEEN.Easing.Exponential.Out)
.onUpdate(function () {
//运动过程
//controls.moveForward(0.01 * speed);
//console.log("22222", camera.position);
//camera.position.set(runPosition.x, runPosition.y, runPosition.z);
camera.lookAt(runPosition.x, runPosition.y, runPosition.z);
})
.onComplete(() => {
//console.log("99999", camera.position);
pathIndex++;
if (pathIndex < runPath.length) {
var time = pathIndex == 1 ? 4000 : 3000;
cameraRunPath(time);
} else {
camera.rotation.set(-3.08, 0.0455, 3.1388);
bookClick = true;
isRun = false;
}
});
tween.easing(TWEEN.Easing.Quadratic.InOut);
tween.start();
}
五、移动端点击事件的响应。
//点击事件
document.getElementById("nvImg").onclick = function () { resetButtonEvent(); };
document.getElementById("lineImg").onclick = function () { lineButtonEvent(); };
document.getElementById("testbook").onclick = function () { bookButtonEvent(); };
//window.innerWidth / window.innerHeight
//document.getElementById("testbook").style.left = ;//第一个div的left值
//document.getElementById("testbook").style.top = window.innerHeight / 2 + "px";//第一个div的top值
document.getElementById("testbook").style.display = "none";
// document.getElementById("playDiv").onclick = function () { playButtonEvent(); };
// document.getElementById("take_partDiv").onclick = function () { take_partButtonEvent(); };
// document.getElementById("detailDiv").onclick = function () { detailButtonEvent(); };
// document.getElementById("fireDiv").onclick = function () { fireButtonEvent(); };
// document.getElementById("hideDiv").onclick = function () { hideButtonEvent(); };
if (isPhone()) {
window.addEventListener('touchstart', onMouseClick, false);
// window.addEventListener('touchstart', onMouseDown, false);
// window.addEventListener('touchend', onMouseUp, false);
window.addEventListener('touchmove', onMouseMove, false);
} else {
//dblclick
//window.addEventListener('dblclick', onMouseDBClick, false);
window.addEventListener('click', onMouseClick, false);
// window.addEventListener('mousedown', onMouseDown, false);
// window.addEventListener('mouseup', onMouseUp, false);
window.addEventListener('mousemove', onMouseMove, false);
}
六、最后整体的工程文件供学习