WebGL------osg框架學習二
今天我們繼續來學習osg.js框架。上一篇我們介紹了DrawActor物件繪製操作類和Drawable可繪製物件類,我們大致知道了osg對Drawable可繪製物件的繪製流程管理。今天我們要繼續介紹StateBin狀態樹節點類。我們來看一下StateBin,他管理的是StateSet狀態,他將每個模型節點的StateSet狀態資訊(shaderLink,材質,depth等)包裝成樹節點,從而能夠將狀態節點遞迴組裝成一棵狀態樹。我們來看看StateBin的建構函式。
1 /* 2 狀態樹結點 3 儲存一個StateSet,每個StateSet都有一個唯一ID 4 */ 5 //let MemoryPool = require('../util/MemoryPool');6 7 let StateBin = function () { 8 this._stateset = undefined; 9 this._parent = undefined;//StateBin 10 this._children = {};//屬性名為StateSet的ID,值為StateBin 11 //this._children._keys = [];//StateSet的ID 12 this._depth = 0;//樹的深度值 13 };
首先我們可以看到StateBin的成員,this._stateset這就是模型節點的狀態資訊(shaderLink,材質,depth),this._parent該樹節點的父節點,this._children該樹節點的子節點,this._depth樹的深度。這是一個典型的樹節點類,熟悉資料結構的同學都明白如何遞迴構造一棵樹,鯽魚就不再囉嗦了。我們接下來看看StateBin的成員函式有哪些。
1 StateBin.prototype = { 2 getStateSet: function () { 3 return this._stateset; 4 }, 5 setStateSet: function (s) { 6 this._stateset = s; 7 }, 8 getParent: function () { 9 return this._parent; 10 }, 11 reset: function () {//重置資料,一般都是根節點呼叫 12this._stateset = undefined; 13 this._parent = undefined; 14 15 //之所以遍歷是為了StateGraph不被析構以記憶體複用,而不是每次都重新建立 16 //然後是StateGraph的資料必須被清空,重新使用時不會出錯 17 // let keys = this._children.keys(); 18 // let l = keys.length; 19 // for (let i = 0; i < l; i++) { 20 // let key = keys[i]; 21 // let child = this._children[key]; 22 // child.reset(); 23 // //記憶體池 24 // //MemoryPool.StateBin.put(child); 25 // } 26 this._children = {}; 27 //this._children._keys.length = 0; 28 //this._children._keys = []; 29 this._depth = 0; 30 }, 31 addStateBinChild: function (bin) { 32 bin._parent = this; 33 bin._depth = this._depth + 1; 34 let id = bin._stateset.getID(); 35 this._children[id] = bin; 36 }, 37 addStateSetChild: function (stateset) {//新增子節點,以stateset的id為key,返回新建立或者已經存在的StateBin 38 let id = stateset.getID(); 39 let child = this._children[id]; 40 if (child) { 41 return child; 42 } else { 43 let sg = new StateBin(); 44 //let sg = MemoryPool.StateBin.get(); 45 sg._parent = this; 46 sg._depth = this._depth + 1; 47 sg._stateset = stateset; 48 this._children[id] = sg; 49 //children._keys.push(id); 50 return sg; 51 } 52 }, 53 removeStateBinChild: function (bin) { 54 let id = bin._stateset.getID(); 55 let cbin = this._children[id]; 56 if (cbin) { 57 cbin.parent = undefined; 58 delete this._children[id]; 59 } 60 }, 61 removeStateSetChild: function (stateset) { 62 let id = stateset.getID(); 63 let cbin = this._children[id]; 64 if (cbin) { 65 cbin.parent = undefined; 66 delete this._children[id]; 67 } 68 }, 69 removeChildren: function () { 70 this._children = {}; 71 }, 72 };
我們一個一個來看,getStateSet獲取當前樹節點的渲染狀態資訊this._stateset;setStateSet設定當前樹節點的渲染狀態資訊即修改this._stateset;getParent獲取當前樹節點的父節點;reset初始化節點資料,將節點屬性清空析構;addStateBinChild向當前樹節點中加入一個子節點;addStateSetChild如果當前樹節點存在id是stateset的id的子節點,就返回該子節點,如果不存在就建立一個stateset狀態的子節點並返回;removeStateBinChild刪除當前節點的確定id的某個子節點;removeStateSetChild刪除當前節點某個狀態是stateset的子節點;removeChildren刪除該樹節點的所有子節點。
我們可以清楚的看到,成員函式基本都是對樹結構的操作,最後還有一個方法是對父狀態的遍歷繼承,我們來看一下。
1 //父狀態的匹配 2 StateBin.moveStateBin = function (glstate, preStateBin, curStateBin) { 3 if (curStateBin === preStateBin) {//兩個相同什麼都不做 4 return; 5 } 6 7 if (curStateBin === undefined) { 8 //curStateBin已經到頂,彈出preStateBin的所有狀態 9 do { 10 if (preStateBin._stateset !== undefined) { 11 glstate.popStateSet(); 12 } 13 preStateBin = preStateBin._parent; 14 } while (preStateBin); 15 return; 16 } 17 18 if (preStateBin === undefined) { 19 //preStateBin已經到頂,壓入curStateBin的所有狀態 20 //從子節點往根節點遍歷獲取所有的狀態,但是推給glstate必須從根節點往子節點遍歷 21 //所以這裡先塞到一個stack裡面,然後再遍歷stack推給glstate 22 let stack = []; 23 do { 24 if (curStateBin._stateset !== undefined) { 25 stack.push(curStateBin._stateset); 26 } 27 curStateBin = curStateBin._parent; 28 } while (curStateBin); 29 30 let size = stack.length - 1; 31 for (let i = size; i >= 0; --i) { 32 glstate.pushStateSet(stack[i]); 33 } 34 return; 35 } else if (preStateBin._parent === curStateBin._parent) { 36 // first handle the typical case which is two glstate groups 37 // are neighbours. 38 39 // glstate has changed so need to pop old glstate. 40 if (preStateBin._stateset !== undefined) { 41 glstate.popStateSet(); 42 } 43 // and push new glstate. 44 if (curStateBin._stateset !== undefined) { 45 glstate.pushStateSet(curStateBin._stateset); 46 } 47 return; 48 } 49 50 //先彈出狀態,保證preStateBin和curStateBin達到樹節點平級 51 //無法確定兩個樹節點誰的深度值更多,兩個都做一次迴圈 52 while (preStateBin._depth > curStateBin._depth) { 53 if (preStateBin._stateset !== undefined) { 54 glstate.popStateSet(); 55 } 56 preStateBin = preStateBin._parent; 57 } 58 59 // use return path to trace back steps to curStateBin. 60 let stack = []; 61 // need to pop back up to the same depth as the curr glstate group. 62 while (curStateBin._depth > preStateBin._depth) { 63 if (curStateBin._stateset !== undefined) { 64 stack.push(curStateBin._stateset); 65 } 66 curStateBin = curStateBin._parent; 67 } 68 69 // now pop back up both parent paths until they agree. 70 // should be this to conform with above case where two StateBin 71 // nodes have the same parent 72 //繼續遍歷直到兩個樹節點相同 73 while (preStateBin !== curStateBin) { 74 if (preStateBin._stateset !== undefined) {//pre的從GLState中出棧 75 glstate.popStateSet(); 76 } 77 preStateBin = preStateBin._parent; 78 79 if (curStateBin._stateset !== undefined) {//當前的入棧,臨時儲存 80 stack.push(curStateBin._stateset); 81 } 82 curStateBin = curStateBin._parent; 83 } 84 85 //遍歷結束後,從臨時棧中推入GLState裡 86 for (let i = stack.length - 1, l = 0; i >= l; --i) { 87 glstate.pushStateSet(stack[i]); 88 } 89 };
這段程式碼我們仔細來看一下。
第一件事比較當前節點狀態和前一個節點狀態,相同則直接返回。
1 if (curStateBin === preStateBin) {//兩個相同什麼都不做 2 return; 3 }
接下來如果前後節點狀態不同,就繼續下面的事情,我們來看下面接下來做了什麼事。接下來是判斷當前遍歷到的狀態節點是否已經是樹的葉子節點,如果是葉子節點就向樹根部遍歷,依次彈出上一級父節點直到遍歷到整棵樹的根節點。彈出是靠glstate這個引數來操作實現的注意一下。遍歷到根節點並彈出狀態後就直接返回了。
1 if (curStateBin === undefined) { 2 //curStateBin已經到頂,彈出preStateBin的所有狀態 3 do { 4 if (preStateBin._stateset !== undefined) { 5 glstate.popStateSet(); 6 } 7 preStateBin = preStateBin._parent; 8 } while (preStateBin); 9 return; 10 }
我們再看看接下來還做了什麼操作,這個看註釋就能理解他的操作。
1 if (preStateBin === undefined) { 2 //preStateBin已經到頂,壓入curStateBin的所有狀態 3 //從子節點往根節點遍歷獲取所有的狀態,但是推給glstate必須從根節點往子節點遍歷 4 //所以這裡先塞到一個stack裡面,然後再遍歷stack推給glstate 5 let stack = []; 6 do { 7 if (curStateBin._stateset !== undefined) { 8 stack.push(curStateBin._stateset); 9 } 10 curStateBin = curStateBin._parent; 11 } while (curStateBin); 12 13 let size = stack.length - 1; 14 for (let i = size; i >= 0; --i) { 15 glstate.pushStateSet(stack[i]); 16 } 17 return; 18 } else if (preStateBin._parent === curStateBin._parent) { 19 // first handle the typical case which is two glstate groups 20 // are neighbours. 21 22 // glstate has changed so need to pop old glstate. 23 if (preStateBin._stateset !== undefined) { 24 glstate.popStateSet(); 25 } 26 // and push new glstate. 27 if (curStateBin._stateset !== undefined) { 28 glstate.pushStateSet(curStateBin._stateset); 29 } 30 return; 31 }
隨後我們看看最後的操作。這波操作就是為了比較currStateBin和preStateBin這兩個樹節點的深度和對其向樹根部的操作。
1 //先彈出狀態,保證preStateBin和curStateBin達到樹節點平級 2 //無法確定兩個樹節點誰的深度值更多,兩個都做一次迴圈 3 while (preStateBin._depth > curStateBin._depth) { 4 if (preStateBin._stateset !== undefined) { 5 glstate.popStateSet(); 6 } 7 preStateBin = preStateBin._parent; 8 } 9 10 // use return path to trace back steps to curStateBin. 11 let stack = []; 12 // need to pop back up to the same depth as the curr glstate group. 13 while (curStateBin._depth > preStateBin._depth) { 14 if (curStateBin._stateset !== undefined) { 15 stack.push(curStateBin._stateset); 16 } 17 curStateBin = curStateBin._parent; 18 } 19 20 // now pop back up both parent paths until they agree. 21 // should be this to conform with above case where two StateBin 22 // nodes have the same parent 23 //繼續遍歷直到兩個樹節點相同 24 while (preStateBin !== curStateBin) { 25 if (preStateBin._stateset !== undefined) {//pre的從GLState中出棧 26 glstate.popStateSet(); 27 } 28 preStateBin = preStateBin._parent; 29 30 if (curStateBin._stateset !== undefined) {//當前的入棧,臨時儲存 31 stack.push(curStateBin._stateset); 32 } 33 curStateBin = curStateBin._parent; 34 } 35 36 //遍歷結束後,從臨時棧中推入GLState裡 37 for (let i = stack.length - 1, l = 0; i >= l; --i) { 38 glstate.pushStateSet(stack[i]); 39 }
StateBin渲染狀態樹是對osg的StateSet單個模型渲染狀態的管理資料結構,幾乎在整個DrawActor的過程中都要大量應用,他的重要性不言而喻,鯽魚也是一知半解的在學習這個資料結構,希望大家多提出寶貴的見解,多多斧正,謝謝同學們的支援關注。今天就到這裡,下週再見。本文系原創,引用請註明出處:https://www.cnblogs.com/ccentry/p/10224312.html