Coin3D三维可视化教程4
场景数据库
Inventor场景数据库是由代表一个或多个 3D场景的数据信息所组成的。如图 3-1 所示,数据库SoDB可以包含多个图形场景。每个场景都是由一组相关的 3D对象和属性构成的。例如,在图 3-1 中,场景可以表示为一辆汽车、一栋小房子、或者另外一辆汽车、一栋大房子和一个人。
场景数据库有两个基本操作(或方法)可供调用。首先需要初始化数据库:
SoDB::init()
这必须是在我们编写Inventor程序时的第一个Inventor函数调用。如果在我们开发的程序中使用了Inventor组件库的话,那么当调用SoXt::init()的时候,场景数据库将会自动被初始化(见第 16 章“Inventor组件库”)。如果没有使用组件库,但使用了有交互作用的节点( 例如操作器,译者注例如操作器,译者注 )或节点工具包(Node Kits)时,必须首先调用SoInteraction::init()函数,
这个函数会同时初始化数据库、交互作用模块和节点工具包。其次,可以将一个 Inventor 文件读到场景数据库中,增加新的图形场景。
其次,可以将一个 Inventor 文件读到场景数据库中,增加新的图形场景。
SoSeparator* readAll(SoInput *in )
//or
SbBool read(SoInput * in, SoNode *& rootNode ) const
//or
SbBool read(SoInput * in, SoPath *& path ) const
使用第一种调用方法,Inventor 将从参数 in 所指定文件中读取场景数据,并返回一个指向 r SoSeparator 节点的指针,这个 SoSeparator 节点将是所读文件中所有场景的根节点。使用第二种调用方法,Inventor 将从参数 in 所指定文件中读取场景数据,然后将文件中的场景根节点作为结果返回给 rootNode 参数。使用第三种调用方法,Inventor 将从参数 in所指定文件中读取场景数据,然后将文件中的场景根节点作为结果返回给 path 参数。如果读取的过程中发生了错误,函数将返回 FALSE。(查看第 11 章“文件格式”,了解更多有关SoInput 的内容)。
图形场景
图形场景是由一个或多个代表形体、属性、组等节点对象组成的。通过向组节点增加子节点,可以创建出一种分层场景 ( Hierarchical scenes)。分层场景是一个有向无环图(directedacyclic graph) 。
图 3-1 展示了一个包含有五个图形场景的简单数据库。图形场景中最顶层的节点称作根节点(节点 A 至节点 E)。请注意观察,节点 H 是如何连接到两个不同的父节点上的。这种情况叫做共享实例(shared instancing)。还请注意,节点 E 没有和数据库中任何其它的节点相连接。通常这只是一种临时的状态,随着不断地增加场景内容,这个节点就应该会和其它的节点相连接了。
节点的类型
节点是组成图形场景的基本元素。它内部包含用于定义 3D 形体、属性、或组的数据和方法。当创建了一个节点,这个节点会被当作根节点自动地保存到场景数据库中(When a node is created, it is automatically inserted into the database as a root node。 这句话的意思有些让人费解,请读者自己甄别。译者注这句话的意思有些让人费解,请读者自己甄别。译者注 )。通常的情况下,可以通过将节点与数据库中的其它节点相连接,构造出一种具有分层关系的场景。
节点可以分为三大基本类:
- 形体节点(Shape nodes),代表 3D 几何模型。
- 属性节点(Property nodes),表现对象的外观或其它场景特征。
- 组节点(Group nodes),是一种将节点聚合包含进场景中的容器(containers)。
这些分类的划分并不是特别严格的,这样做不过是可以帮助我们更好地学习 Inventor所包含的类。
创建节点
使用 C++ new 操作符来创建一个节点,例如:
SoSphere *headSphere = new SoSphere;
注意:不要创建节点数组。(具体原因见后面的“怎样删除节点”章节)。 (即不能这样程序,SoSphere *headSphere = new SoSphere[5]。译者注 )
注意 : 尽管是使用 new 操作符创建了新节点,但却不能使用 delete 操作符删除它们。在后面的“如何删除节点”章节中,将详细讨论如何在 Inventor 中删除节点,以及节点何时被删除的问题。理解“引用计数”的原理对于我们使用 Inventor 是至关重要的,因为我们必须知道在什么样的条件下,节点会被自动删除。
节点里有什么 ?
每个节点都是由一组叫做“域”的用于描述节点参数的数据元素组成的。例如,一个点光源节点(SoPointLight)包含了四个域:intensity(亮度)、color(颜色)、location(位置)和on(开关)。intensity里包含有一个从 0.0 (无光)到 1.0 (最亮)的变量值。color用于设置光源的红/绿/蓝照明色。location用于指定光源的位置。on指定灯光是否处于开的状态。
Inventor定义了许多域的类型。每种域类型都有唯一的方法来访问其数据。在每个节点中,根据域的用途,每个域都被命名了相应的名称。例如:下面是一些节点和它们包含的域:
节点 | 域 |
SoCoordinate3 | point |
SoNormal | vector |
SoMaterial | ambientColor diffuseColor specularColor emissiveColor shininess transparency |
SoPerspectiveCamera | viewportMapping position orientation |
当对节点执行动作时发生了什么? ( 高级内容)
每个节点都会执行自己的动作行为(Each node implements its own action behavior)。当需要对场景执行一个特定的动作时,我们必须首先要创建一个动作类的实例(例如,SoGLRenderAction 或 SoGetBoundingBoxAction),然后再将这个动作应用到场景的根节点上。数据库对每个动作都管理着一个叫做遍历状态(traversal state)的数据结构,这个数据结构是在某个给定时刻内动作的元素与参数的集合(For each action, the database manages a traversal state, which is a collection of elements or parameters in the action at a given time)。通常,当执行一次动作时,动作将按照从上到下,从左到右的顺序遍历整个场景。在遍历的过
程中,节点将根据它们对此次动作所做的特定行为来修改遍历状态。
举例来说,SoMaterial节点用于设置遍历状态中各种材质元素的当前值,SoDrawStyle节点用于设置遍历状态中绘制风格元素的当前值。还有像SoSphere这类的形体节点,对渲染遍历来说是尤其重要的。因为在渲染的时候,这类节点会使用遍历状态的当前参数,将它们自己形状绘制在屏幕上。
形体节点
形体节点用于表示一种 3D几何对象。它们比较特殊,因为它们代表的是一些受属性节点和组节点影响的实际物体。在执行渲染动作期间,它们实际上会将自己形状绘制在屏幕上。形体节点类包括:SoSphere、SoIndexedFaceSet 和 SoText3 等类。图 3-2 展示了部分形体节点类的类树图:
属性节点
属性节点代表的是像“表面材质”、“绘制风格”、或“几何变换”等这些用于展现场景外观和特有性质的节点。图 3-3 展示了包含属性节点的部分类树图。属性节点可以分成若干类,场景图一般使用三种不同的图标来表示它们:
- 变换类图标:表示执行几何变换的属性节点,像SoTransform、 、SoRotation、 、SoScale、SoTranslation 、SoRotationXYZ 和SoResetTransform等节点。这些节点都是从SoTransformation派生出来的。
- 外观类图标:表示可以修改对象外观的属性节点,像SoMaterial、SoBaseColor 、SoMaterialBinding、SoComplexity、SoDrawStyle、SoLightModel 和SoFont。
- 度量类图标:示包含有坐标系、法线、或其它几何信息的节点,像SoCoordinate3、SoCoordinate4、SoProfileCoordinate2、SoProfileCoordinate3、SoNormal 和 SoNormalBinding。
通常,在遍历过程中,属性节点将自己的新值替换掉在遍历状态中相应元素的值。但是几何变换节点是一个例外,它们会和当前几何变换进行累积运算。
让我们以材质节点为例。材质节点用来表现物体的表面和颜色的属性。假设我们希望创建一种类似青铜的材质效果,首先要创建一个材质节点,然后设置适当的域值。
SoMaterial *bronze = new SoMaterial; //黄铜色材料
// set field values
bronze->ambientColor.setValue(.33,.22, .27);
bronze->diffuseColor.setValue(.78, .57, .11);
bronze->specularColor.setValue(.99, .94, .81);
bronze->shininess = .28;
这种材料的渲染效果如下:
如果没有显式地为节点设置域值,Inventor 将会对这些域使用缺省值(具体请查阅 Open Inventor C++ Reference Manual)。例如,在上面的例子中,没有对材质节点的 transparency 域赋值,Inventor 将会对 transparency 使用其缺省值 0.0。
SoTransform节点可以产生几何变换,它包含有缩放域、旋转域、平移域。下面的代码定义了一个沿着y轴方向平移-1 单位的几何变换节点。为了使这个变换产生效果,这个节点必须要插入到场景的某个适当位置上。(这里是插在需要平移的形体节点之前)。
SoTransform *myXform = new SoTransform;
// set field value
myXform->translation.setValue(0.0, -1.0, 0.0);