
塌陷不能把模型變小,但是可以減少對記憶體的占用。ThingJS是做3D輕量化開發的PAAS平台,在模型導入後進行更多事件控制。
今天來講講如何基于js文法來開發一個物體模型拆解展開的效果,專業名稱叫“物體爆炸圖”,标準ThingJS體系模型出于互動模型性能考慮,都要求在模型上傳前做塌陷,這種模組化細節對于提升3D開發效率很有必要,目的是減少模型對記憶體的占用。
在3D開發之前,模型本身會重疊很多指令,占用很大一部分記憶體和CPU,拖慢電腦,是以針對模型進行塌陷後(指的是把很多個物體合并成一個,轉換為一個指令可編輯多邊形或可編輯網格),就會去除這些多餘的指令參數,不再花時間記錄和存儲,進而加快運作速度。
制作物體模型時,根據爆炸圖中各個零件的拆分需要,針對子模型或子節點定義并命名物體子對象,在3DMAX等模組化軟體裡就能建立子對象。這些子對象在ThingJS線上開發中可作為模型子節點來控制,能夠像單獨模型對象物體一樣進行移動、添加事件等操作。
拆分後磨性子節點如果有多材質或點數超過上限,那在ThingJS開發中會繼續拆分,并在子節點中被命名成組,組内繼續拆分“01”、“02”等對象。例如:3dmax命名, 一個子節點名字為“box”,由于該子節點使用了多種材質,該子節點在線上開發中會被命名成組,組内會被拆分并命名為“box_0”,“box_1”等對象。
注意經塌陷的模型不再有子節點保留,隻有上述分項控制模型局部要求,保留已命名的子對象資訊,最大程度上提高開發性能,又滿足模型拆分的特殊需要。
官方示例請各位看官參考:
// 加載場景代碼
var app = new THING.App({
url: '/api/scene/406e419fae9000a47a4a8899'
});
// 發電機模型節點資料
var nodeObjData = {
'1': {name: '機座', offset: [0, 0, -1]},
'2': {name: '保護裝置', offset: [0, -1, 0]},
'3': {name: '電瓶', offset: [0, -1, 0]},
'4': {name: '排氣口', offset: [0, 0, 1]},
'5_0': {name: '過濾器', offset: [0, 0, 1]},
'5_1': {name: '過濾網', offset: [0.5, 0, 1]},
'6': {name: '供給裝置', offset: [0, 0, 1]},
'7': {name: '煙囪', offset: [-1, 0, 0]},
'8': {name: '發電機'},
'9': {name: '控制器', offset: [0, 1, 0]}
}
// 發電機模型節點對象
var nodeJsonData = null;
// 發電機對象
var generatorObj = null;
// 發電機展開狀态
var expandState = false;
// 發電機展開次數
var expandCount = 0;
// 場景加載完成後執行
app.on('load', function (ev) {
// 查詢發電機對象
generatorObj = app.query('#generator')[0]
// 擷取發電機模型節點對象
nodeJsonData = getNode(generatorObj);
// 建立測試按鈕
new THING.widget.Button('展開', expandObj);
new THING.widget.Button('還原', unexpandObj);
new THING.widget.Button('頂牌顯示', createAllPanel);
new THING.widget.Button('頂牌隐藏', hiddenAllPanel);
})
/**
* 說明:顯示所有頂牌
*/
function createAllPanel(){
for (let key in nodeObjData) {
nodeJsonData[key].name = nodeObjData[key].name;
createPanel(nodeJsonData[key]);
}
}
/**
* 說明:隐藏所有頂牌
*/
function hiddenAllPanel(){
for (let key in nodeObjData) {
hiddenPanel(nodeJsonData[key]);
}
}
/**
* 說明:展開物體
*/
function expandObj() {
// 防止發電機在執行一次展開過程中多次點選
if (expandState) {
return;
}
expandState = true;
expandCount++;
for (let key in nodeObjData) {
// 各子節點進行偏移
objOffset(nodeJsonData[key], nodeObjData[key].offset);
}
}
/**
* 說明:還原物體
*/
function unexpandObj() {
// 展開次數為0,代表未展開
if (expandCount == 0) {
return;
}
for (let key in nodeObjData) {
if(nodeObjData[key].offset){
// 計算還原時子節點需要進行的偏移量,數值為 -1 * 展開次數 * nodeObjData中定義的該子節點對應的偏移量
let offsetValue = [-1 * expandCount * nodeObjData[key].offset[0], -1 * expandCount * nodeObjData[key].offset[1], -1 * expandCount * nodeObjData[key].offset[2]]
objOffset(nodeJsonData[key], offsetValue);
}
}
expandCount = 0;
}
/**
* 說明:擷取節點對象
*/
function getNode(obj) {
let nodeJson = {};
// obj.subNodes 即可擷取到一個物體的所有子節點
for (let i = 0; i < obj.subNodes.length; i++) {
let subnode = obj.subNodes[i];
// 擷取物體子節點對象中node屬性的type值,隻有當type值為Mesh時,才能對物體添加事件
let type = subnode.node.type;
if(type == 'Mesh'){
nodeJson[subnode.name] = subnode;
}
}
return nodeJson;
}
/**
* 說明:物體偏移
*/
function objOffset(obj, value) {
if (!value) {
return;
}
// 物體移動
obj.moveTo({
offsetPosition: value, // 自身坐标系下的相對位置
time: 500, // 移動完成需要的時間
complete: function () {
expandState = false;
}
});
}
/**
* 說明:建立面闆
*/
function createPanel(obj) {
// 判斷是否已經建立過面闆,如果已建立,顯示,否則建立面闆
var panel = obj.getAttribute('panel');
if (panel != null) {
panel.visible = true;
return;
}
// 建立panel
panel = new THING.widget.Panel({
// 設定面闆寬度
width: '100px',
// 沒有角标 none ,沒有線的角标 noline ,折線角标 polyline
cornerType: 'polyline'
})
// 綁定物體身上相應的屬性資料
panel.addString(obj, 'name').caption('');
// 建立UIAnchor面闆
var uiAnchor = app.create({
// 類型
type: 'UIAnchor',
// 設定父物體
parent: obj,
// 要綁定的dom元素對象
element: panel.domElement,
// 設定 localPosition 為 [0, 0, 0]
localPosition: [0, 0, 0],
// 相對于面闆左上角的偏移像素值,目前用值是角标的中心點
pivotPixel: [-16, 109]
});
// 更改面闆文本樣式
$('.ThingJS_wrap .main .ThingJS_UI .ThingJS_string-value').css('text-align', 'center');
obj.setAttribute('panel', uiAnchor);
}
/**
* 說明:隐藏面闆
*/
function hiddenPanel(obj) {
var panel = obj.getAttribute('panel');
if (panel != null) {
panel.visible = false;
}
}
ThingJS讓3D開發更加簡單,讓3D創業團隊松一口氣啦!