天天看點

ThingJS教你開發物體爆炸圖,3D創業團隊松一口氣啦!

ThingJS教你開發物體爆炸圖,3D創業團隊松一口氣啦!

塌陷不能把模型變小,但是可以減少對記憶體的占用。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創業團隊松一口氣啦!

繼續閱讀