基於 HTML5 的 WebGL 3D 智能樓宇監控系統


前言

智能監控的領域已經涉及到了各大領域,工控、電信、電力、軌道交通、航天航空等等,為了減少人員的消耗,監控系統必不可少。之前我寫過一篇 2D 的智能地鐵監控系統廣受好評,突然覺得,既然 2D 的這么受歡迎,那么 3D 的需求量肯定也是非常大的,3D 畢竟比 2D 來說還是更直觀一些,於是有了這個例子以及這篇文章。智能監控系統在 3D 中應用比較廣泛的除了 3D 機房以外,我覺得就是樓宇的監控了,可是之前做了很多關於機房方面的 Demo,所以最終決定做 3D 樓宇監控系統。

代碼生成

場景搭建

整個場景是由 3D 組件搭建而成,配合左側的 listView 列表組件,通過點擊這個 listView 列表組件中的各個項可以自由切換各個監控樓層和樓宇的場景:

dm = new ht.DataModel();
g3d = new ht.graph3d.Graph3dView(dm);

relativeLayout = new ht.ui.RelativeLayout();// 相對布局器 可對界面進行布局
var ht3dView = new ht.ui.HTView(g3d);// 放置 3d 組件
relativeLayout.addView(ht3dView, {// 給相對布局器添加組件顯示,參數一為組件名稱,參數二可設置寬高、對齊方式等屬性
    width: 'match_parent',
    height: 'match_parent'
});
var listView = window.list = new ht.ui.ListView();// 列表組件
for (var i = 1; i <= 15; i++) {
    var data = new ht.Data();// 創建節點
    data.setName('樓層' + i);// 設置節點名稱
    listView.dm().add(data);// 將節點添加進列表組件中
}
relativeLayout.addView(listView, {// 將 listView 組件添加進布局器中
    align: 'left',// 設置對齊方式為左對齊
    vAlign: 'top',// 設置垂直對齊方式為頂部對齊
    marginTop: 120,// 設置外邊距頂部為 120 像素
    marginLeft: 60,// 設置外邊距左側為 60 像素
    width: 80,// 設置寬度為 80
    height: 480,// 設置高度為 480
    index: 100// 設置元素的堆疊順序
});

relativeLayout.addToDOM();// 將組件添加進 body 中

加載模型

進入頁面顯示的就是整個城市的場景,通過 ht.Default.loadObj 方法加載 obj 模型:

var loadCity = function(){
    ht.Default.loadObj('obj/city.obj', 'obj/city.mtl', {// 加載模型
        center: true,// 模型是否居中,默認為false,設置為true則會移動模型位置使其內容居中
        cube: true,// 是否將模型縮放到單位1的尺寸范圍內,默認為false
        prefix: 'obj/',// 圖片路徑前綴,即在map_kd值之前增加的前綴,如果是相對路徑則以加載obj的html頁面的路徑為參考
        shape3d: 'city',// 如果指定了shape3d名稱,則HT將自動將加載解析后的所有材質模型構建成數組的方式,以該名稱進行注冊
        finishFunc: function(modelMap, array, rawS3){// 用於加載 obj 模型后的回調處理
            city.rawS3 = rawS3;// 設置變量 city 對象的 rawS3 屬性 此函數中的 rawS3 屬性為 obj 模型的原始大小
            showCity();// 創建一個節點 設置節點的 shape3d 為 city 顯示 city.obj 與 city.mtl 的內容
        }
    });
}

工控樓層模型的加載也是類似,這里就不再贅述。

直接將組件添加進場景中是不會有相關的操作的, 必須要監聽事件的觸發才可進行后續的操作,這里對數據選中容器中的選中變化事件進行監聽:

//列表點擊
listView.dm().sm().ms(function(e){// 監聽選中變化事件
    if (e.kind === 'set') {// 設置監聽事件
        showFloor();// 顯示樓層
    }
});

調用模型

這里為了簡單就只設置了一個工控樓層的 obj 模型,通過調用不同的 obj 模型可以顯示不同的工控樓層場景,也就是說我們可以自己通過 ht.Default.loadObj 方法加載模型,設置工控樓層模型的 shape3d 屬性,然后設置到節點的 shape3d 屬性上,即可修改;或者直接設置節點的 shape3d 屬性為 json 格式的 obj 文件,這里還是采取第一種方式:

var showFloor = function(){
    g3d.setCenter([210, 0, 210]);// 設置 3d 組件的“中心”位置
    dm.clear();// 清除數據容器中的所有節點
    var rawS3 = floor.rawS3,// 獲取 obj 模型的原始大小
        node = new ht.Node();// 創建一個新的節點
    node.s({// 設置節點的樣式屬性
        'shape3d': 'floor',// 此項設置的值為 ht.Default.loadObj 中設置的 shape3d 屬性的值
        'wf.visible': 'selected',// 設置選中節點時顯示節點外部的線框
        '3d.selectable': false// 設置節點不可選中
    });
    node.s3(rawS3[0] / 10,rawS3[1]/ 10,rawS3[2] / 10);// 設置節點的大小為原始大小的十分之一
    node.p3(140, 0, 230);// 設置節點的位置

    dm.add(node);// 將節點添加進數據容器中

    // 添加四個“相機”的節點
    createNode([0, 20, 0]);
    createNode([110, 20, 220]);
    createNode([330, 20, 420]);
    createNode([210, 20, 120]);
    createNode([420, 20, 120]);

};

這里順便說一下另一種簡便的調用 obj 模型的方式,直接設置節點的 shape3d 屬性為導入的 json 格式的文件:

var node = new ht.Node();
node.s("shape3d", "./symbols/city.json");

但是這個 json 的內容必須要有以下幾個元素:

{
    "modelType": "obj",// 必須設置此屬性為 obj 格式
    "obj": "./obj/city.obj",// 必須設置 obj 屬性
    "mtl": "./obj/city.mtl"// 此項可寫可不寫,如果需要設置 obj 模型的樣式(如顏色等),則必須設置此項
}

但是這種模式不適用於這個場景,因為我的模型有些大,需要調用到 obj 模型的原始大小 rawS3 屬性除以一定比例后再顯示。

創建面片

上面提到了 createNode 方法,這個方法主要是用來創建顯示為“面片”類型的節點。所謂“面片”,即為只有一個面。通過這種在一個面上顯示一張矢量圖的方式,結果會比在一個六面體上顯示一張圖的性能好,3D 場景簡單的時候可能看不出來效果,如果場景卡頓,立馬就能看出“面片”的優勢了:

function createNode(p3, s3){
    var node = new ht.Node();// 創建一個新的節點
    node.p3(p3);// 設置節點的位置
    // node.s3(s3);
    node.s({
        'shape3d': 'billboard',// 設置節點的 shape3d 類型為 billboard 類型,顯示為“面片”
        'shape3d.image': './symbols/智能樓宇/camera.json',// 3d圖形整體貼圖
        'shape3d.image.cache' : true,// 如果貼圖是矢量,是否緩存(緩存后性能會得到提升)
        'shape3d.autorotate': true,// 是否自動朝向相機
        'shape3d.transparent': true,// 決定3d圖形是否透明
         // 'shape3d.alwaysOnTop': true,// 是否總是在最前
        'shape3d.fixSizeOnScreen': [ 38, 47 ]// 是否無論縮放遠近,在屏幕內呈現固定大小,值可為true(使用圖片或矢量自身大小)/false, 也可以是[100, 200](對應寬高)
    });
    dm.add(node);// 將節點添加進數據容器中

    g3d.invalidateShape3dCachedImage(node);// cache 的代價是,這里需要更新
    return node;
}

事件交互

然后我就在想,既然到了樓層的 3D 模型顯示,那么怎么返回?以哪種方式返回最好?想來想去比較沒有違和感的還是點擊列表組件比較好,就選中了列表組件的頂部:

listView.getView().addEventListener('click', function(e){// 監聽點擊事件
    e.preventDefault();// 阻止默認操作
    if (e.clientY - 120 < 50) {
        showCity();// 顯示初始 3D 樓宇場景
        listView.dm().sm().cs();// 列表設置清除所有選中元素
    }
});

所有代碼結束!

總結

這個 3D 智能樓宇監控系統非常的簡單,對於技術人員來說是完全沒有挑戰性的,主要工作內容在美工上,這么一來,如果要添加比較復雜的需求,技術人員就可以全身心地投入到產品上,而不是一些繁瑣的 3D 模型的搭建了。總而言之,我覺得這個 Demo 非常具有代表性,所以想拿出來跟大家分享一下,一起討論一下前端的趨勢所在。

http://hightopo.com/demo/intelligent-building/index.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM