添加點(diǎn)擊歸位事件芯丧,也就是鼠標(biāo)點(diǎn)到導(dǎo)航盒的某一個(gè)面,讓攝像機(jī)運(yùn)動(dòng)到對(duì)準(zhǔn)那個(gè)面的軸上世曾,這涉及到以下幾個(gè)問題:
1.如何獲取點(diǎn)擊事件缨恒?
2.如何獲取點(diǎn)到的面?
3.如何獲取點(diǎn)到面的法線谴咸?
4.如何將攝像機(jī)移動(dòng)到法線對(duì)應(yīng)的軸?我們依次解決這幾個(gè)問題骗露,首先我們使用scene.onPointerDown來獲取檢測(cè)岭佳,因?yàn)楦眻?chǎng)景的scene是獨(dú)立的,可以直接使用這個(gè)API去獲取點(diǎn)擊事件萧锉,不會(huì)和主場(chǎng)景的檢測(cè)沖突珊随,因?yàn)檫@個(gè)功能是加在副場(chǎng)景的scene上,我們并不需要在dispose中對(duì)這個(gè)功能進(jìn)行注銷處理柿隙。
//注冊(cè)鼠標(biāo)點(diǎn)擊事件
registPointDownEvent() {
this.scene.onPointerDown = (evt, pi) => {
console.log(1);
if (!pi.pickedMesh) return;
const pickMesh = pi.pickedMesh;
if (!pickMesh) return;
console.log(pi);
}
}
//https://playground.babylonjs.com/?#ENABP9#11
通過打印pickInfo叶洞,我們發(fā)現(xiàn)我們可以獲取到碰撞點(diǎn)的faceId,根據(jù)faceId,可以找到該面的三個(gè)頂點(diǎn)的索引號(hào),因?yàn)閯?chuàng)建網(wǎng)格的時(shí)候禀崖,拋開八個(gè)角面衩辟,同一個(gè)平面上面的點(diǎn)相對(duì)于其他面都是獨(dú)立存在的,點(diǎn)的法線就代表著所處平面的法線波附,這樣第二三個(gè)問題迎刃而解艺晴。
//獲取頂點(diǎn)索引
......
const indices = pickMesh.getIndices();
const p1Index = indices[pi.faceId * 3];
// const p2Index = indices[pi.faceId * 3+1];
// const p3Index = indices[pi.faceId * 3+2];
//獲取normal
const normals = pickMesh.getNormalsData();
const normal = new BABYLON.Vector3(normals[p1Index * 3], normals[p1Index * 3 + 1], normals[p1Index * 3 + 2]);
console.log(normal);
//https://playground.babylonjs.com/?#ENABP9#12
此時(shí)我們已經(jīng)獲取了法線,接下來我們通過法線去判定六個(gè)方向掸屡,就可以知道我們即將旋轉(zhuǎn)到的軸的方向封寞,這里可以準(zhǔn)備一個(gè)新的enum和function:
enum IDirection {
forward,
backward,
right,
left,
up,
down
}
......
//獲取normal
const normals = pickMesh.getNormalsData();
const normal = new BABYLON.Vector3(normals[p1Index * 3], normals[p1Index * 3 + 1], normals[p1Index * 3 + 2]);
if (normal.z === -1) {
this.moveCameraToDirection(CameraDirection.forward)
}
if (normal.z === 1) {
this.moveCameraToDirection(CameraDirection.backward)
}
if (normal.x === -1) {
this.moveCameraToDirection(CameraDirection.left);
}
if (normal.x === 1) {
this.moveCameraToDirection(CameraDirection.right);
}
if (normal.y === 1) {
this.moveCameraToDirection(CameraDirection.up);
}
if (normal.y === -1) {
this.moveCameraToDirection(CameraDirection.down);
}
......
//指向
moveCameraToDirection(direction: CameraDirection) {
}
......
//https://playground.babylonjs.com/?#ENABP9#13
前面的幾個(gè)問題都已經(jīng)解決,最后一個(gè)問題就是怎么移動(dòng)折晦?我們先寫一個(gè)簡(jiǎn)單的動(dòng)畫機(jī)ProcessAnimation钥星,當(dāng)然也可以直接使用gsap之類的插件沾瓦。對(duì)于ArcRotateCamera满着,最簡(jiǎn)單的移動(dòng)的方法有兩種,一種是通過插值Position,一種是通過插值A(chǔ)lpha和Beta贯莺。第一種方法簡(jiǎn)單粗暴风喇,第二種方法得去匹配對(duì)應(yīng)的alpha和beta,我們可以分開嘗試缕探。先試試通過插值Position:
moveCameraToDirection(direction: CameraDirection) {
const tmpV3 = B.TmpVectors.Vector3[0];
const startPos = B.TmpVectors.Vector3[1].copyFrom(this.bindCamera.position);
switch (direction) {
case CameraDirection.forward:
tmpV3.set(0, 0, -1);
break;
case CameraDirection.backward:
tmpV3.set(0, 0, 1);
break;
case CameraDirection.right:
tmpV3.set(1, 0, 0);
break;
case CameraDirection.left:
tmpV3.set(-1, 0, 0);
break;
case CameraDirection.up:
tmpV3.set(0, 1, 0);
break;
case CameraDirection.down:
tmpV3.set(0, -1, 0);
break;
}
//最終的Position
tmpV3.scaleInPlace(this.bindCamera.radius).addInPlace(this.bindCamera.target);
this.process.play(0.5, (process) => {
B.Vector3.LerpToRef(startPos, tmpV3, process, this.bindCamera.position);
this.bindCamera.rebuildAnglesAndRadius();
})
}
//https://playground.babylonjs.com/?#ENABP9#14
編寫的過程簡(jiǎn)單粗暴魂莫,效果好些也挺OK,但是多次嘗試會(huì)發(fā)現(xiàn)爹耗,點(diǎn)擊頂和底的移動(dòng)好像有點(diǎn)問題耙考,結(jié)束的時(shí)候方向總是不對(duì)或者會(huì)瞬間旋轉(zhuǎn)。這是因?yàn)橛龅筋愃迫f向死鎖的問題潭兽,當(dāng)Beta不處于0和PI時(shí)倦始,一對(duì)Beta和Alpha有唯一對(duì)應(yīng)的一對(duì)Position和Target,但是當(dāng)Beta處于0或PI時(shí)山卦,相當(dāng)于一個(gè)軸旋轉(zhuǎn)了90度鞋邑,再旋轉(zhuǎn)Alpha時(shí),會(huì)發(fā)現(xiàn)position并不會(huì)改變,也就是說此時(shí)一對(duì)Position和Target對(duì)應(yīng)著無數(shù)個(gè)Alpha枚碗,計(jì)算的alpha可能是0-2Pi中的任意一個(gè)值逾一。接下來試試使用Alpha和Beta插值的方法來進(jìn)行移動(dòng):
moveCameraToDirection(direction: CameraDirection) {
let alpha = 0;
let beta = 0;
switch (direction) {
case CameraDirection.forward:
alpha = - Math.PI / 2;
beta = Math.PI / 2;
break;
case CameraDirection.backward:
alpha = Math.PI / 2;
beta = Math.PI / 2;
break;
case CameraDirection.right:
alpha = 0;
beta = Math.PI / 2;
break;
case CameraDirection.left:
alpha = - Math.PI;
beta = Math.PI / 2;
break;
case CameraDirection.up:
alpha = - Math.PI / 2;
beta = 0;
break;
case CameraDirection.down:
alpha = - Math.PI / 2;
beta = Math.PI;
break;
}
let startBeta = this.bindCamera.beta;
let startAlpha = this.bindCamera.alpha % (Math.PI * 2);
//避免繞大圓
if (startAlpha < 0) startAlpha += Math.PI * 2;
if (Math.abs(alpha - startAlpha) > Math.PI) {
alpha += Math.PI * 2;
}
this.process.play(0.5, (process) => {
this.bindCamera.alpha = BABYLON.Scalar.Lerp(startAlpha, alpha, process);
this.bindCamera.beta = BABYLON.Scalar.Lerp(startBeta, beta, process);
})
}
//https://playground.babylonjs.com/?#ENABP9#15
除了這兩種方法,還可以使用四元數(shù)插值直接計(jì)算矩陣來移動(dòng)視角肮雨,有興趣的小伙伴可以去研究一下遵堵。到此為止,導(dǎo)航盒的基礎(chǔ)功能已經(jīng)完成酷含,下一節(jié)將為盒子添加一個(gè)點(diǎn)擊后高亮的功能鄙早。
PS:本節(jié)結(jié)束PG(https://playground.babylonjs.com/?#ENABP9#16)
PS:后續(xù)修改了部分結(jié)構(gòu)以支持八角定位(https://playground.babylonjs.com/?#ENABP9#24)
下一節(jié):BabylonJS系列:方向?qū)Ш胶?4.點(diǎn)擊面高亮