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;//StateBin10 this._children = {};//属性名为StateSet的ID,值为StateBin11 //this._children._keys = [];//StateSet的ID12 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 () {//重置数据,一般都是根节点调用12 this._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,返回新创建或者已经存在的StateBin38 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推给glstate22 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 groups37 // 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 StateBin71 // nodes have the same parent72 //继续遍历直到两个树节点相同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 groups20 // 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 StateBin22 // nodes have the same parent23 //继续遍历直到两个树节点相同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                       

相关文章