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 ...
随机推荐
- VS2015 无法启动IIS Express Web服务器(已解决)
VS2015 无法启动IIS Express Web服务器 首先说一下我遇到问题的情况.这个项目是在公司电脑创建的,运行一直是正常的.今天把项目拷贝回来做. 可是到自己的电脑上,运行就提示 无法启动I ...
- JDBC规范(转)
公司开发一直用的是ibatis,进来心血来潮想研究一下源码,可是发现自己的JDBC似乎已经忘得差不多了,为了自己能顺利的研读ibatis的源码,于是乎找到了 XIAO_DF的JDBC规范的博客,转到自 ...
- python第三课——数据类型2
day03: 1.列表:list 特点:有序的(有索引.定义和显示顺序是一致的).可变的(既可以改变元素内容也可以自动扩容).可重复的. 可以存储任何的数据类型数据 定义个列表如下: lt = ['宋 ...
- [HNOI2005]汤姆的游戏
嘟嘟嘟 直接O(n ^ 2)暴力判断就行了. 对于圆,判断该点和圆心的距离是否小于半径. 然而为啥我这么写编译不过: scanf("%lf%lf%lf%lf", &a[++ ...
- 7、Android---网络技术
玩手机不能上网是单机的时代 而且现在的流量也出了无限使用 几乎网络离不开人们的日常生活 7.1.WebView的用法 遇到一些特殊的请求 在程序中展示一些网页 加载和显示网页都是浏览器的任务 在不打开 ...
- [luogu1081] 开车旅行
题面 这个题目还是值得思考的. 看到这个题目, 大家应该都想到了这样一个思路, 就是把每个点能够达到的最近的和次近的点都预处理出来, 然后跑就可以了, 现在问题就是难在这个预处理上面, 我们应 ...
- vue打包以后,除了首页意外,其余页面是空白
针对vue项目打包以后,除了首页意外,其余页面是空白的,需要在服务端进行配置. 原因是router中,mode是history引起的 如果是nginx,改成如下: location / { root ...
- active developer path ("/Applications/Xcode.app/Contents/Developer")
-> git xcrun: error: active developer path ("/Applications/Xcode.app/Contents/Developer" ...
- Python读文件报错:SyntaxError: Non-ASCII character in file
打开city.py文件时报错 问题原因: 程序中的编码错误,python默认是acii模式,没有支持utf8.如果代码中有汉字 ,就会报错 解决方案: 源代码文件(city.py)第一行添加:#cod ...
- Service通信
1.简介 Service通信是双向的, 它不仅可以发送消息, 同时还会有反馈. 所以service包括两部分, 一部分是请求方( Clinet) , 另一部分是应答方/服务提供方( Server) . ...