在上一篇《基於HT for Web向量實現2D葉輪旋轉》中講述了葉輪旋轉在2D上的應用,今天我們就來講講葉輪旋轉在3D上的應用。
在3D拓撲上可以建立各種各樣的圖元,在HT for Web系統中提供了一些常規的3D模型,但是對於那些比較複雜的模型,比如汽車、人物等模型就無能為力了,那再專案中需要用到這樣的模型該腫麼辦呢?這時候就需要藉助專業的3ds Max工具來建模了,然後透過3ds Max工具將模型匯出成obj檔案,然後再專案中引用匯出的obj檔案,這樣就能成功的使用上覆雜的圖元了。
在《HT圖形元件設計之道(四)》一文中有提及HT for Web引入obj檔案的介紹,在這裡我就不做重複介紹了,我們先來看看今天作為演示的Demo模型長什麼樣:
嘿嘿,是不是感覺今天的模型有些大材小用了,沒辦法,怪只怪自己不懂3ds Max工具,只能先用這個大家熟悉的模型來做Demo演示了。
首先我們需要有3ds Max工具將模型匯出成obj及mtl檔案,然後呼叫HT for Web的ht.Default.loadObj()方法讀取並解析模型檔案,在解析完成後,透過呼叫ht.Default.setShape3dModel()方法將模型註冊到系統中,如此在後續的程式碼中就能夠應用到該模型了,模型檔案的讀取及註冊具體程式碼如下:
ht.Default.loadObj('plane.obj', 'plane.mtl', {
center: true,
r3: [0, -Math.PI/2, 0], // make plane face right
s3: [0.15, 0.15, 0.15], // make plane smaller
finishFunc: function(modelMap, array, rawS3){
if(modelMap){
ht.Default.setShape3dModel('plane', array);
var plane = new ht.Node();
plane.s3(rawS3);
plane.s({
'shape3d': 'plane',
'shape3d.scaleable': false,
'wf.visible': true,
'wf.color': 'white',
'wf.short': true
});
dataModel.add(plane);
}
}
});
註冊完3D模型後,我們馬上建立了一個3D圖元,並將其新增到了dataModel容器中,這時我們需要一個3D拓撲來顯示這個3D圖元,具體的建立程式碼如下:
var dataModel = new ht.DataModel();
var g3d = new ht.graph3d.Graph3dView(dataModel);
g3d.setEye(200, 50, 300);
g3d.setDashDisabled(false);
g3d.getView().style.background = '#4C7BBB';
g3d.addToDOM();
在3D拓撲上做了些簡單的屬性設定,讓拓撲看起來舒服些,如此我們就可以看到我們建立出來的飛機模型到底長什麼樣了
怎麼樣,建立一個複雜模型好像並沒有想象中的複雜(複雜的東西都讓美工做完了)。
我們仔細觀察飛機會發現,飛機前面的螺旋槳顏色和機身一樣,一眼看去不太容易注意到它的存在,那能否將其顏色改掉呢?我們可以檢視下mtl檔案,看飛機的螺旋槳是否分離機身獨立成一個材質,mtl檔案的內容如下:
newmtl body
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.3608 0.4353 0.2549
Kd 0.3608 0.4353 0.2549
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
newmtl propeller
Ns 10.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.3608 0.4353 0.2549
Kd 0.3608 0.4353 0.2549
Ks 0.0000 0.0000 0.0000
Ke 0.0000 0.0000 0.0000
正如我們所想,飛機模型的機身和螺旋槳是分開了兩個獨立的材質,並將螺旋槳的材質名字定義為propeller,因此我們可以獨立控制機身及螺旋槳,那麼我們就來修改下螺旋槳的顏色吧,在loadObj()方法中的finishFunc回撥函式中新增上如下程式碼即可:
modelMap.propeller.s3 = [1, 1.2, 1.2];
modelMap.propeller.color = ‘yellow';
在程式碼中,我們不僅改變了螺旋槳的顏色,我們還對螺旋槳做了縮放處理,令螺旋槳的寬度和長度變大一點。
到這裡,模型就算完成了,接下來要做的就是讓螺旋槳動起來,和2D葉輪旋轉類似,在3D模型上也可以做資料繫結,要想讓螺旋槳旋轉起來,我們就需要設定螺旋槳的rotation屬性,和3D上的圖元不同的是,設定3D圖元的rotation屬性需要設定一個陣列,定義3D上三個方向的旋轉值。
我們先來嘗試下讓螺旋槳沿著x軸旋轉45度試下:
modelMap.propeller.r3 = [Math.PI / 4, 0, 0];
果然可以,那麼接下來我們就可以為螺旋槳的rotation屬性做資料繫結的處理了:
modelMap.propeller.r3 = {
func: function(data){
return [data.a('angle'), 0, 0];
}
};
我們將螺旋槳的x軸上的旋轉角度繫結到圖元的angle自定義屬性上,我們可以透過改變angle屬性值令螺旋槳沿著x軸轉動起來,那麼接下來我們就透過定時器來動態改變angle屬性吧,看看螺旋槳是不是真的可以動起來:
window.setInterval(function() {
var rotation = plane.a('angle') + Math.PI / 10;
if (rotation > Math.PI * 2) {
rotation -= Math.PI * 2;
}
plane.a('angle', rotation);
}, 40);
螺旋槳果然動起來了,這個定時器讓螺旋槳做勻速運動,但是飛機的螺旋槳在起飛和降落的時候其旋轉速度都不是勻速,我們要模擬飛機起飛和降落時螺旋槳的旋轉速度該如何處理呢?這個時候我們可以考慮用HT for Web中的動畫來解決這個問題,關於動畫的內容由於比較複雜,在這裡就不深入探討,等以後有機會再和大家分享動畫的相關內容,今天就先講訴下動畫的基本用法,簡單實現螺旋槳模擬起飛和降落的效果,具體的程式碼如下:
var params = {
delay: 1500,
duration: 20000,
easing: function(t){
return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2));
},
action: function(v, t){
plane.a('angle', v*Math.PI*120);
},
finishFunc: function(){
ht.Default.startAnim(params);
}
};
ht.Default.startAnim(params);
我們來分析下程式碼:
1. delay屬性:定義動畫播放前的停頓時間;
2. duration屬性:定義動畫持續時間;
3. easing函式:定義動畫緩動函式;
4. action函式:action函式必須提供,實現動畫過程中的屬性變化,在這裡設定angle屬性;
5. finishFunc函式:動畫結束後呼叫的函式,在這裡又啟動了動畫,讓螺旋槳不斷的旋轉。
執行程式碼,你會發現螺旋槳在1.5秒後進入旋轉狀態,並且旋轉速度由慢變快,再變慢直至停止,然後再過1.5秒後繼續旋轉,如此週而復始。
好了,今天的內容到這裡就結束了,整個Demo的執行效果可以透過下面的影片檢視,最後再附上本次Demo的所有程式碼。
http://v.youku.com/v_show/id_XMTI5NDI5MzYyOA==.html
<!DOCTYPE html>
<html>
<head>
<title>HT for Web - Plane</title>
<meta charset="UTF-8" name="viewport" content="user-scalable=yes, width=600">
<script src="../../../build/ht-debug.js"></script>
<script src="../../../build/ht-obj-debug.js"></script>
<script>
function init(){
var dataModel = new ht.DataModel();
var g3d = new ht.graph3d.Graph3dView(dataModel);
g3d.setEye(200, 50, 300);
g3d.setDashDisabled(false);
g3d.getView().style.background = '#4C7BBB';
g3d.addToDOM();
ht.Default.loadObj('plane.obj', 'plane.mtl', {
center: true,
r3: [0, -Math.PI/2, 0], // make plane face right
s3: [0.15, 0.15, 0.15], // make plane smaller
finishFunc: function(modelMap, array, rawS3){
if(modelMap){
modelMap.propeller.r3 = {
func: function(data){
return [data.a('angle'), 0, 0];
}
};
// make propeller a litter bigger
modelMap.propeller.s3 = [1, 1.2, 1.2];
modelMap.propeller.color = 'yellow';
ht.Default.setShape3dModel('plane', array);
var plane = new ht.Node();
plane.s3(rawS3);
plane.s({
'shape3d': 'plane',
'shape3d.scaleable': false,
'wf.visible': true,
'wf.color': 'white',
'wf.short': true
});
dataModel.add(plane);
var params = {
delay: 1500,
duration: 20000,
easing: function(t){
return (t *= 2) < 1 ? 0.5 * t * t : 0.5 * (1 - (--t) * (t - 2));
},
action: function(v, t){
plane.a('angle', v*Math.PI*120);
},
finishFunc: function(){
ht.Default.startAnim(params);
}
};
ht.Default.startAnim(params);
/*window.setInterval(function() {
var rotation = plane.a('angle') + Math.PI / 10;
if (rotation > Math.PI * 2) {
rotation -= Math.PI * 2;
}
plane.a('angle', rotation);
}, 40);*/
}
}
});
}
</script>
</head>
<body onload="init();">
</body>
</html>