zoukankan      html  css  js  c++  java
  • 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 () {//重置数据,一般都是根节点调用
    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,返回新创建或者已经存在的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                       

  • 相关阅读:
    数据仓库专题18-数据建模语言IDEF(转载)
    数据仓库专题(14)-数据仓库建设指导原则:一切以就绪数据为主
    数据仓库专题(16)-分布式数据仓库实践指南-目录篇
    解释器模式
    命令模式
    责任链模式
    代理模式
    享元模式
    外观模式
    装饰器模式
  • 原文地址:https://www.cnblogs.com/ccentry/p/10224312.html
Copyright © 2011-2022 走看看