WebGL------osg框架学习二
今天我们继续来学习osg.js框架。上一篇我们介绍了DrawActor对象绘制操作类和Drawable可绘制对象类,我们大致知道了osg对Drawable可绘制对象的绘制流程管理。今天我们要继续介绍StateBin状态树节点类。我们来看一下StateBin,他管理的是StateSet状态,他将每个模型节点的StateSet状态信息(shaderLink,材质,depth等)包装成树节点,从而能够将状态节点递归组装成一棵状态树。我们来看看StateBin的构造函数。
/*
状态树结点
保存一个StateSet,每个StateSet都有一个唯一ID
*/
//let MemoryPool = require('../util/MemoryPool'); let StateBin = function () {
this._stateset = undefined;
this._parent = undefined;//StateBin
this._children = {};//属性名为StateSet的ID,值为StateBin
//this._children._keys = [];//StateSet的ID
this._depth = 0;//树的深度值
};
首先我们可以看到StateBin的成员,this._stateset这就是模型节点的状态信息(shaderLink,材质,depth),this._parent该树节点的父节点,this._children该树节点的子节点,this._depth树的深度。这是一个典型的树节点类,熟悉数据结构的同学都明白如何递归构造一棵树,鲫鱼就不再啰嗦了。我们接下来看看StateBin的成员函数有哪些。
StateBin.prototype = {
getStateSet: function () {
return this._stateset;
},
setStateSet: function (s) {
this._stateset = s;
},
getParent: function () {
return this._parent;
},
reset: function () {//重置数据,一般都是根节点调用
this._stateset = undefined;
this._parent = undefined; //之所以遍历是为了StateGraph不被析构以内存复用,而不是每次都重新创建
//然后是StateGraph的数据必须被清空,重新使用时不会出错
// let keys = this._children.keys();
// let l = keys.length;
// for (let i = 0; i < l; i++) {
// let key = keys[i];
// let child = this._children[key];
// child.reset();
// //内存池
// //MemoryPool.StateBin.put(child);
// }
this._children = {};
//this._children._keys.length = 0;
//this._children._keys = [];
this._depth = 0;
},
addStateBinChild: function (bin) {
bin._parent = this;
bin._depth = this._depth + 1;
let id = bin._stateset.getID();
this._children[id] = bin;
},
addStateSetChild: function (stateset) {//添加子节点,以stateset的id为key,返回新创建或者已经存在的StateBin
let id = stateset.getID();
let child = this._children[id];
if (child) {
return child;
} else {
let sg = new StateBin();
//let sg = MemoryPool.StateBin.get();
sg._parent = this;
sg._depth = this._depth + 1;
sg._stateset = stateset;
this._children[id] = sg;
//children._keys.push(id);
return sg;
}
},
removeStateBinChild: function (bin) {
let id = bin._stateset.getID();
let cbin = this._children[id];
if (cbin) {
cbin.parent = undefined;
delete this._children[id];
}
},
removeStateSetChild: function (stateset) {
let id = stateset.getID();
let cbin = this._children[id];
if (cbin) {
cbin.parent = undefined;
delete this._children[id];
}
},
removeChildren: function () {
this._children = {};
},
};
我们一个一个来看,getStateSet获取当前树节点的渲染状态信息this._stateset;setStateSet设置当前树节点的渲染状态信息即修改this._stateset;getParent获取当前树节点的父节点;reset初始化节点数据,将节点属性清空析构;addStateBinChild向当前树节点中加入一个子节点;addStateSetChild如果当前树节点存在id是stateset的id的子节点,就返回该子节点,如果不存在就创建一个stateset状态的子节点并返回;removeStateBinChild删除当前节点的确定id的某个子节点;removeStateSetChild删除当前节点某个状态是stateset的子节点;removeChildren删除该树节点的所有子节点。
我们可以清楚的看到,成员函数基本都是对树结构的操作,最后还有一个方法是对父状态的遍历继承,我们来看一下。
//父状态的匹配
StateBin.moveStateBin = function (glstate, preStateBin, curStateBin) {
if (curStateBin === preStateBin) {//两个相同什么都不做
return;
} if (curStateBin === undefined) {
//curStateBin已经到顶,弹出preStateBin的所有状态
do {
if (preStateBin._stateset !== undefined) {
glstate.popStateSet();
}
preStateBin = preStateBin._parent;
} while (preStateBin);
return;
} if (preStateBin === undefined) {
//preStateBin已经到顶,压入curStateBin的所有状态
//从子节点往根节点遍历获取所有的状态,但是推给glstate必须从根节点往子节点遍历
//所以这里先塞到一个stack里面,然后再遍历stack推给glstate
let stack = [];
do {
if (curStateBin._stateset !== undefined) {
stack.push(curStateBin._stateset);
}
curStateBin = curStateBin._parent;
} while (curStateBin); let size = stack.length - 1;
for (let i = size; i >= 0; --i) {
glstate.pushStateSet(stack[i]);
}
return;
} else if (preStateBin._parent === curStateBin._parent) {
// first handle the typical case which is two glstate groups
// are neighbours. // glstate has changed so need to pop old glstate.
if (preStateBin._stateset !== undefined) {
glstate.popStateSet();
}
// and push new glstate.
if (curStateBin._stateset !== undefined) {
glstate.pushStateSet(curStateBin._stateset);
}
return;
} //先弹出状态,保证preStateBin和curStateBin达到树节点平级
//无法确定两个树节点谁的深度值更多,两个都做一次循环
while (preStateBin._depth > curStateBin._depth) {
if (preStateBin._stateset !== undefined) {
glstate.popStateSet();
}
preStateBin = preStateBin._parent;
} // use return path to trace back steps to curStateBin.
let stack = [];
// need to pop back up to the same depth as the curr glstate group.
while (curStateBin._depth > preStateBin._depth) {
if (curStateBin._stateset !== undefined) {
stack.push(curStateBin._stateset);
}
curStateBin = curStateBin._parent;
} // now pop back up both parent paths until they agree.
// should be this to conform with above case where two StateBin
// nodes have the same parent
//继续遍历直到两个树节点相同
while (preStateBin !== curStateBin) {
if (preStateBin._stateset !== undefined) {//pre的从GLState中出栈
glstate.popStateSet();
}
preStateBin = preStateBin._parent; if (curStateBin._stateset !== undefined) {//当前的入栈,临时保存
stack.push(curStateBin._stateset);
}
curStateBin = curStateBin._parent;
} //遍历结束后,从临时栈中推入GLState里
for (let i = stack.length - 1, l = 0; i >= l; --i) {
glstate.pushStateSet(stack[i]);
}
};
这段代码我们仔细来看一下。
第一件事比较当前节点状态和前一个节点状态,相同则直接返回。
if (curStateBin === preStateBin) {//两个相同什么都不做
return;
}
接下来如果前后节点状态不同,就继续下面的事情,我们来看下面接下来做了什么事。接下来是判断当前遍历到的状态节点是否已经是树的叶子节点,如果是叶子节点就向树根部遍历,依次弹出上一级父节点直到遍历到整棵树的根节点。弹出是靠glstate这个参数来操作实现的注意一下。遍历到根节点并弹出状态后就直接返回了。
if (curStateBin === undefined) {
//curStateBin已经到顶,弹出preStateBin的所有状态
do {
if (preStateBin._stateset !== undefined) {
glstate.popStateSet();
}
preStateBin = preStateBin._parent;
} while (preStateBin);
return;
}
我们再看看接下来还做了什么操作,这个看注释就能理解他的操作。
if (preStateBin === undefined) {
//preStateBin已经到顶,压入curStateBin的所有状态
//从子节点往根节点遍历获取所有的状态,但是推给glstate必须从根节点往子节点遍历
//所以这里先塞到一个stack里面,然后再遍历stack推给glstate
let stack = [];
do {
if (curStateBin._stateset !== undefined) {
stack.push(curStateBin._stateset);
}
curStateBin = curStateBin._parent;
} while (curStateBin); let size = stack.length - 1;
for (let i = size; i >= 0; --i) {
glstate.pushStateSet(stack[i]);
}
return;
} else if (preStateBin._parent === curStateBin._parent) {
// first handle the typical case which is two glstate groups
// are neighbours. // glstate has changed so need to pop old glstate.
if (preStateBin._stateset !== undefined) {
glstate.popStateSet();
}
// and push new glstate.
if (curStateBin._stateset !== undefined) {
glstate.pushStateSet(curStateBin._stateset);
}
return;
}
随后我们看看最后的操作。这波操作就是为了比较currStateBin和preStateBin这两个树节点的深度和对其向树根部的操作。
//先弹出状态,保证preStateBin和curStateBin达到树节点平级
//无法确定两个树节点谁的深度值更多,两个都做一次循环
while (preStateBin._depth > curStateBin._depth) {
if (preStateBin._stateset !== undefined) {
glstate.popStateSet();
}
preStateBin = preStateBin._parent;
} // use return path to trace back steps to curStateBin.
let stack = [];
// need to pop back up to the same depth as the curr glstate group.
while (curStateBin._depth > preStateBin._depth) {
if (curStateBin._stateset !== undefined) {
stack.push(curStateBin._stateset);
}
curStateBin = curStateBin._parent;
} // now pop back up both parent paths until they agree.
// should be this to conform with above case where two StateBin
// nodes have the same parent
//继续遍历直到两个树节点相同
while (preStateBin !== curStateBin) {
if (preStateBin._stateset !== undefined) {//pre的从GLState中出栈
glstate.popStateSet();
}
preStateBin = preStateBin._parent; if (curStateBin._stateset !== undefined) {//当前的入栈,临时保存
stack.push(curStateBin._stateset);
}
curStateBin = curStateBin._parent;
} //遍历结束后,从临时栈中推入GLState里
for (let i = stack.length - 1, l = 0; i >= l; --i) {
glstate.pushStateSet(stack[i]);
}
StateBin渲染状态树是对osg的StateSet单个模型渲染状态的管理数据结构,几乎在整个DrawActor的过程中都要大量应用,他的重要性不言而喻,鲫鱼也是一知半解的在学习这个数据结构,希望大家多提出宝贵的见解,多多斧正,谢谢同学们的支持关注。今天就到这里,下周再见。本文系原创,引用请注明出处:https://www.cnblogs.com/ccentry/p/10224312.html
WebGL------osg框架学习二的更多相关文章
- WebGL——osg框架学习一
从今天开始,我们开始正式的学习osg框架,今天我们学习的是osg的渲染模块,我们来看一下代码结构. 所有DrawXXX的js模块都是渲染的模块,我们逐一来简单介绍一下,第一个Drawable.js,这 ...
- WebGL——osg框架学习三
今天继续来Draw绘制的osg模块的学习,昨天我们学习的是StateBin渲染状态树节点类,今天我们来继续学习下一个Draw的基础类DrawableEntity渲染对象实体类.这个类和Drawable ...
- WebGL——osg框架学习四
这篇我们接着来看一下DrawEntityActor类,我们来看看这个继承DrawActor的类到底做了什么事.我们之前学习了Drawable对应的DrawActor,那么我们类比的来看Drawable ...
- Struts2框架学习(二) Action
Struts2框架学习(二) Action Struts2框架中的Action类是一个单独的javabean对象.不像Struts1中还要去继承HttpServlet,耦合度减小了. 1,流程 拦截器 ...
- Android 学习笔记之AndBase框架学习(二) 使用封装好的进度框,Toast框,弹出框,确认框...
PS:渐渐明白,在实验室呆三年都不如在企业呆一年... 学习内容: 1.使用AbActivity内部封装的方法实现进度框,Toast框,弹出框,确认框... AndBase中AbActivity封 ...
- Hibernate框架学习(二)——api详解
一.Configuration对象 功能:配置加载类,用于加载主配置,orm元数据加载. //1.创建,调用空参构造(还没有读配置文件) Configuration conf=new Configur ...
- python flask框架学习(二)——第一个flask程序
第一个flask程序 学习自:知了课堂Python Flask框架——全栈开发 1.用pycharm新建一个flask项目 2.运行程序 from flask import Flask # 创建一个F ...
- Castle ActiveRecord框架学习(二):快速搭建简单博客网站
一.数据库 1.数据表 Category:类别标签表(字段Type=1为类别,Type=2为标签) Category_Post:类别标签与文章中间表 Post:文章表 Comment:评论表 2.数据 ...
- Spring框架学习(二)
一.依赖注入的三种注入方式 Spring框架为我们提供了三种注入方式:set注入.构造方法注入和接口注入. 1.set注入 规律:无论给什么赋值,配置文件中<property>标签的nam ...
随机推荐
- [微信小程序直播平台开发]___(一)介绍与流程
1.一个可以忽略的前言 最近在做的一个项目,客户要做一个直播平台,主播发起视频直播,然后其他人进入房间观看这样子,跟其他直播平台不同的是,主播可以打赏观众,噗. 因为客户要做的是一个民宿的微信小程序, ...
- Spring MVC Interceptor
1 在spring-servlet.xml中进行如下配置 <mvc:interceptors> <mvc:interceptor> <mvc:mapping path=& ...
- spring中MessageSource的配置使用方法2--ReloadableResourceBundleMessageSource【转】
本文转载仅供自己学习收录,不做任何商业用途,如有需要可访问原地址:http://blog.csdn.net/qyf_5445/article/details/8124362 如何在spring mvc ...
- JavaScript验证字符串只能包含数字或者英文字符的代码实例
验证字符串只能包含数字或者英文字符的代码实例:本章节分享一段代码实例,它实现了验证字符串内容是否只包含英文字符或者数字.代码实例如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...
- Oracle从一个用户导出数据到另一个用户
如果想导入的用户已经存在: 1. 导出用户 expdp user1/pass1 directory=dumpdir dumpfile=user1.dmp 2. 导入用户 impdp user2/pas ...
- golang xorm应用
github.com/go-xorm/xorm xorm库 http://www.xorm.io/docs/ 手册 xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便.xo ...
- php 添加 redis 扩展
Windows下PHP安装Redis扩展的具体步骤方法 下面我们就结合详细的图文,给大家介绍Windows下PHP安装Redis扩展的方法: 首先我们打开这个上面给出的下载链接地址,界面如下: 这里我 ...
- JSP九大内置对象和四大作用域和Servlet的三大作用域对象
一.JSP九大内置对象:内置对象(又叫隐含对象,有9个内置对象):不需要预先声明就可以在脚本代码和表达式中随意使用 内置对象特点: 由JSP规范提供,不用编写者实例化. 通过Web容器实现和管理 所有 ...
- iteritems()与items()
iteritems:以迭代器对象返回字典键值对 item:以列表形式返回字典键值对 >>> dic = {'a':3,'c':1,'b':2} >>> print ...
- Python高级编程和异步IO并发编程
第1章 课程简介介绍如何配置系统的开发环境以及如何加入github私人仓库获取最新源码. 1-1 导学 试看 1-2 开发环境配置 1-3 资源获取方式第2章 python中一切皆对象本章节首先对比静 ...