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 ...
随机推荐
- python第二十课——math模块中常用的函数
属性: e:自然数 pi:圆周率 函数: ceil():向上取整 floor():向下取整 sqrt():开平方根 radians():角度转弧度 degrees():弧度转角度 import mat ...
- 【转】合格PHP工程师的知识结构
PHP的运行环境 连环境都搞不起来,就是你有多么喜欢PHP,那也是白搭,开始我们大多会使用集成环境软件例如xampp,wamp.随着知识的增加慢慢要学会自己搭建运行环境,例如 Linux(Ubuntu ...
- 多线程之synchronized
Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...
- 一个简单好用的http服务器
http-server 是一个简单的零配置命令行HTTP服务器, 基于 nodeJs. 如果你不想重复的写 nodeJs 的 web-server.js, 则可以使用这个. 安装 (全局安装加 -g) ...
- iptables传输数据包的过程
IPTABLES传输数据包的过程 大概过程如图所示: 1. 数据包进入网卡时,首先进入PREROUTING链,linux内核会判断数据包的目的IP是否为本地主机 2. 如果数据包的目的IP是本地主机, ...
- linux如何查看进程OOM killer
基本概念: Linux 内核有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程 ...
- 数据结构基础(1)--数组C语言实现--动态内存分配
数据结构基础(1)--数组C语言实现--动态内存分配 基本思想:数组是最常用的数据结构,在内存中连续存储,可以静态初始化(int a[2]={1,2}),可以动态初始化 malloc(). 难点就是数 ...
- Ext4文件系统架构分析(一)
本文描述Ext4文件系统磁盘布局和元数据的一些分析,同样适用于Ext3和Ext2文件系统,除了它们不支持的Ext4的特性外.整个分析分两篇博文,分别概述布局和详细介绍各个布局的数据结构及组织寻址方式等 ...
- 【OC底层】KVO原理
KVO的原理是什么?底层是如何实现的? KVO是Key-value observing的缩写. KVO是Objective-C是使用观察者设计模式实现的. Apple使用了isa混写(isa-swiz ...
- ThreadLocal理解
ThreadLocal 概述 ThreadLocal实例仅作为线程局部变量的==操作类==,以及==线程存储局部变量时的Key==.真正的线程局部变量是存储在各自线程的本地,通过Thread类中的Th ...