新版第四章场景的节点与数据结构课件doc.docx
《新版第四章场景的节点与数据结构课件doc.docx》由会员分享,可在线阅读,更多相关《新版第四章场景的节点与数据结构课件doc.docx(29页珍藏版)》请在冰豆网上搜索。
![新版第四章场景的节点与数据结构课件doc.docx](https://file1.bdocx.com/fileroot1/2022-11/22/f94b2df0-cebc-494c-a49f-45055faab5fc/f94b2df0-cebc-494c-a49f-45055faab5fc1.gif)
新版第四章场景的节点与数据结构课件doc
第四章场景的节点与数据结构
如果将场景比作房间,那么场景的节点就是房间里的床、桌、灯、柜等设施。
著名的3D软件开发工具OpenInventor将应用程序抽象成“ascenegraphplusasetofactions”,所有的图形对象、属性、事件响应全由场景的节点来处理,十分适合于图形的交互式绘制。
Intra3D2.0借鉴了OpenInventor的节点设计。
本章讲述Intra3D2.0的节点基类、形体节点、相机节点、光源节点和组节点的设计与实现。
4.1场景图与节点的概念
从数据结构角度讲,场景是一个有向无环图,称为SceneGraph。
场景的交互式绘制就是对SceneGraph各个节点的遍历绘制。
节点可分为组节点与叶子节点两大类,SceneGraph的根节点总是组节点。
以下“伪代码”用于创建图4.1所示的SceneGraph:
图4.1SceneGraph的组节点与叶子节点
GroupNode*node1=newGroupNode;
GroupNode*node2=newGroupNode;
GroupNode*node4=newGroupNode;
GroupNode*node7=newGroupNode;
LeafNode*node3=newLeafNode;
LeafNode*node5=newLeafNode;
LeafNode*node6=newLeafNode;
LeafNode*node8=newLeafNode;
LeafNode*node9=newLeafNode;
LeafNode*node10=newLeafNode;
node1->AddChild(node2);
node1->AddChild(node6);
node1->AddChild(node7);
node1->AddChild(node9);
node1->AddChild(node10);
node2->AddChild(node3);
node2->AddChild(node4);
node4->AddChild(node5);
node7->AddChild(node8);
如果用深度优先的方法遍历图4.1的SceneGraph,则节点的遍历顺序是1、2、3、4、5、6、7、8、9与10。
SceneGrpah中的叶子节点可以被多个组节点引用,如图4.2所示。
当SceneGrpah比较复杂时,节点之间的引用关系也变得复杂,因此SceneGrpah需要用“对象引用计数”方法来管理节点的内存(见2.5节)。
节点可以显式使用new来创建,但不可显式使用delete删除节点,应该用节点的Release函数。
图4.2叶子节点可以被多个组节点引用
Intra3D将SceneGraph节点分为组节点(GroupNode),形体节点(ShapeNode),光源节点(DirLightNode、PointLightNode、SpotLightNode)和相机节点(CameraNode)。
所有上述节点均从基类节点SceneNode派生。
SceneNode节点中定义了坐标系与丰富的3D交互功能。
由于C++类支持数据(Data)和代码(Implementation)的继承,SceneNode节点功能可以被派生类节点继承。
但是COM对象仅支持接口继承(本质上是继承虚函数的声明),不支持数据和代码的继承。
只能在派生节点中包容ISceneNode并将ISceneNode的函数重新封装为当前节点的函数。
COM库中的节点程序要比C++类库的复杂,4.3节将以ShapeNode为例分别论述类与COM对象的程序设计。
相机是否该成为一种节点是值得商讨的话题。
直观地讲,相机等同于观察者的眼睛,的确不是场景的组成部分。
如果应用程序不涉及相机的交互,那么只需调用OpenGL的几个投影函数就可以实现图形学概念中的相机变换(请参考Intra3D的Window3D程序)。
如果应用程序涉及复杂的相机交互,例如多个相机的切换,场景的漫游;此时将相机作为节点插入SceneGraph中,可以享用到在SceneNode定义的交互功能(请参考Intra3D的SceneView程序)。
使用了相机节点的SceneView程序要比不使用相机节点的Window3D程序复杂。
4.2场景节点的基类
C++类库中的SceneNode程序见Intra3D-DLL\Include\Layer3\SceneNode.h和Intra3D-DLL\Layer3\SceneNode.cpp。
COM库的程序见Intra3D-COM\Layer3\SceneNode.h和SceneNode.cpp。
本节主要论述类SceneNode的设计。
SceneNode为场景的交互式绘制提供了两大功能:
一是坐标系统的定义与图形变换;二是节点的拾取与绘制。
类SceneNode是Container的公有派生类,其数据结构并不复杂,但运算函数较多。
其主要数据成员如下:
protected:
SceneNode*m_pParent;
charm_strNodeName[32];
VECTORm_position,m_oldPosition;
ROTATIONm_rotation,m_oldRotation;
VECTORm_scale,m_oldScale;
Trackballm_trackball;//跟踪球
BOOLm_bSelected;//当前节点被拾取的标志
4.2.1坐标系统的定义与图形变换
每个节点都有标定自己的坐标系,称为对象坐标系(ObjectCoordinateSystem)。
在2.1节已经论述了可用旋转结构(ROTATION)、平移矢量、比例矢量来确定图形变换的状态与过程。
m_position、m_rotation与m_scale三个量分别表示该节点在父节点坐标系中的位置量、旋转量与比例量,执行节点的图形变换函数将改变上述量。
m_oldPosition、m_odlRotation与m_oldScale用于记录原始状态,便于恢复。
节点的相互连接构成了SceneGraph,为了有统一的参照系,把SceneGraph的根节点的坐标系称为世界坐标系(WorldCoordinateSystem)。
场景最后投影到窗口中,即执行了相机变换。
观察者用鼠标对场景节点直接拖动、旋转等操作,实质是执行了相机坐标系(CameraCoordinateSystem)的图形变换。
因此场景的3D交互包含了节点在对象坐标系与相机坐标系的图形变换。
用于定义坐标系统与图形变换的主要函数有:
public:
//定义坐标系统的函数
voidSetPosition(floatx,floaty,floatz);
voidSetRotation(floatangle,floatnx,floatny,floatnz);
voidSetScale(floatsx,floatsy,floatsz);
//对象坐标系的图形变换函数
voidTranslate(VECTORtrans);
voidRotate(floatangle,VECTORaxis);
voidScale(floatsx,floatsy,floatsz);
virtualvoidResetTransform(void);
//相机坐标系的图形变换函数
voidTranslateInCamera(VECTORtrans);
voidRotateInCamera(floatangle,VECTORaxis);
voidMouseTrackStart(intiViewportWidth,intiViewportHeight,intmx,intmy);
voidMouseTracking(intdx,intdy);
protected:
//FWTO:
FromWorldCoordinateToObjectCoordinate
//FOTW:
FromObjectCoordinateToWorldCoordinate
//FCTO:
FromCameraToCoordinateObjectCoordinate
//FOTC:
FromObjectCoordinateToCameraCoordinate
//对象坐标系、世界坐标系、相机坐标系之间的顶点换算
voidVertexTransform_FWTO(float*x,float*y,float*z);
voidVertexTransform_FOTW(float*x,float*y,float*z);
voidVertexTransform_FCTO(float*x,float*y,float*z);
voidVertexTransform_FOTC(float*x,float*y,float*z);
//对象坐标系、世界坐标系、相机坐标系之间的矢量换算
voidVectorTransform_FWTO(VECTOR*V);
voidVectorTransform_FOTW(VECTOR*V);
voidVectorTransform_FCTO(VECTOR*V);
voidVectorTransform_FOTC(VECTOR*V);
函数SetPosition、SetRotation与SetScale用于设置该节点在父节点坐标系中的位置量、旋转量与比例量。
voidSceneNode:
:
SetPosition(floatx,floaty,floatz)
{
m_position.x=x;
m_position.y=y;
m_position.z=z;
m_oldPosition=m_position;//记录原始状态
}
voidSceneNode:
:
SetRotation(floatangle,floatnx,floatny,floatnz)
{
m_rotation.angle=angle;
m_rotation.axis.x=nx;
m_rotation.axis.y=ny;
m_rotation.axis.z=nz;
VectorNormalize(&m_rotation.axis);
m_oldRotation=m_rotation;//记录原始状态
}
voidSceneNode:
:
SetScale(floatsx,floatsy,floatsz)
{
m_scale.x=sx;
m_scale.y=sy;
m_scale.z=sz;
m_oldScale=m_scale;//记录原始状态
}
函数Translate、Rotate与Scale用于实现对象坐标系中的平移变换、旋转变换与比例变换。
ResetTransform函数用于恢复原始状态。
voidSceneNode:
:
Translate(VECTORtrans)
{
//注意理解为什么要用VectorTransform
trans=VectorTransform(trans,m_rotation);//见Rotation.h
m_position.x+=trans.x;
m_position.y+=trans.y;
m_position.z+=trans.z;
}
voidSceneNode:
:
Rotate(floatangle,VECTORaxis)
{
ROTATIONR(axis,angle);
VectorNormalize(&R.axis);
//注意理解为什么要用VectorTransform
R.axis=VectorTransform(R.axis,m_rotation);
m_rotation=m_rotation*R;//见Rotation.h
}
voidSceneNode:
:
Scale(floatsx,floatsy,floatsz)
{
m_scale.x*=sx;
m_scale.y*=sy;
m_scale.z*=sz;
}
voidSceneNode:
:
ResetTransform(void)
{//恢复初始状态
m_position=m_oldPosition;
m_rotation=m_oldRotation;
m_scale=m_oldScale;
}
TranslateInCamera与RotateInCamera是相机坐标系的平移变换与旋转变换函数。
MouseTrackStart与MouseTracking用于鼠标跟踪球交互。
voidSceneNode:
:
TranslateInCamera(VECTORtrans)
{
if(GetType()!
=CAMERA_NODE)
VectorTransform_FCTO(&trans);
Translate(trans);
}
voidSceneNode:
:
RotateInCamera(floatangle,VECTORaxis)
{
if(GetType()!
=CAMERA_NODE)
VectorTransform_FCTO(&axis);
Rotate(angle,axis);
}
voidSceneNode:
:
MouseTrackStart(intwidth,intheight,intmx,intmy)
{
//计算旋转中心在窗口中的坐标
floatcx=0,cy=0,cz=0;
if(GetType()==CAMERA_NODE)
{
cx=width/2.0;
cy=height/2.0;
}
else
{
VertexTransform_ObjectToWindow(&cx,&cy,&cz);
cy=height-cy;//OpenGLWindow与MFCWindow在Y方向反向
}
m_trackball.SetTrackWindow(width,height,int(cx),int(cy));
m_trackball.Start(mx,my);
}
voidSceneNode:
:
MouseTracking(intdx,intdy)
{
VECTORaxis;
floatangle;
m_trackball.Tracking(dx,dy,&axis,&angle);
RotateInCamera(angle,axis);
}
4.2.2节点的拾取与绘制
用于拾取与绘制节点的主要函数有:
public:
virtualSceneNode*Search(char*name){returnNULL;}
virtualvoidSelected(void){m_bSelected=TRUE;}
virtualvoidUnselected(void){m_bSelected=FALSE;}
virtualvoidDraw(void){};
virtualvoidDrawForMouseSelect(void){};
voidLoadMatrix(void);//在Draw,DrawForMouseSelect之前调用
一般情况下,节点用Draw函数绘制。
当场景的节点处于拾取状态时,OpenGL的绘制模式为GL_SELECT,此时要用DrawForMouseSelect函数绘制节点。
在执行Draw或DrawForMouseSelect函数前,应调用LoadMatrix函数。
Search、Selected、Unselected、Draw与DrawForMouseSelect均为虚函数,具体行为由派生类定义。
voidSceneNode:
:
LoadMatrix(void)
{
glTranslatef(m_position.x,m_position.y,m_position.z);
glRotatef(m_rotation.angle,m_rotation.axis.x,m_rotation.axis.y,m_rotation.axis.z);
glScalef(m_scale.x,m_scale.y,m_scale.z);
}
COM接口ISceneNode定义如下:
interfaceISceneNode:
IDispatch
{
HRESULTSetName(BSTRstrName);
HRESULTGetName(BSTR*strName);
HRESULTSetPosition(floatx,floaty,floatz);
HRESULTGetPosition([out]float*x,[out]float*y,[out]float*z);
HRESULTSetRotation(floatangle,floatnx,floatny,floatnz);
HRESULTGetRotation([out]float*angle,[out]float*nx,[out]float*ny,[out]float*nz);
HRESULTSetScale(floatsx,floatsy,floatsz);
HRESULTGetScale([out]float*sx,[out]float*sy,[out]float*sz);
HRESULTSetRotateStep(floatfRotateStep);
HRESULTGetRotateStep([out,retval]float*step);
HRESULTGetTranslateStep([out,retval]float*step);
HRESULTSetTranslateStep(floatfTransStep);
HRESULTSetScaleStep(floatfScaleStep);
HRESULTGetScaleStep([out,retval]float*step);
HRESULTResetTransform();
HRESULTScale(floatsx,floatsy,floatsz);
HRESULTTranslate(floatdx,floatdy,floatdz);
HRESULTRotate(floatangle,floatnx,floatny,floatnz);
HRESULTTranslateInCamera(floatdx,floatdy,floatdz);
HRESULTRotateInCamera(floatangle,floatnx,floatny,floatnz);
HRESULTMouseTrackStart(intiViewportWidth,intiViewportHeight,intmx,intmy);
HRESULTMouseTracking(intdx,intdy);
HRESULTLoadMatrix();
HRESULTLoadInverseMatrix();
HRESULTLoadMatrix_FWTO();
HRESULTLoadMatrix_FOTW();
HRESULTVertexTransform_FWTO([in,out]float*x,[in,out]float*y,[in,out]float*z);
HRESULTVertexTransform_FOTW([in,out]float*x,[in,out]float*y,[in,out]float*z);
HRESULTVertexTransform_FCTO([in,out]float*x,[in,out]float*y,[in,out]float*z);
HRESULTVertexTransform_FOTC([in,out]float*x,[in,out]float*y,[in,out]float*z);
HRESULTSetParent(ISceneNode*node);
HRESULTGetParent([out,retval]ISceneNode**node);
HRESULTGetType([out,retval]EnumNodeType*type);
HRESULTSearch(BSTRname,[out,retval]ISceneNode**node);
HRESULTDraw();
HRESULTDrawForMouseSelect();
HRESULTSelected();
HRESULTUnselected();
HRESULTIsSelected([out,retval]BOOL*bFlag);
};
4.3形体节点
形体节点可以引用图形对象,3D交互由节点提供,绘制则由所引用的图形对象实
现。
这样设计可使形体节点既简单又通用。
C++类库中的ShapeNode程序见Intra3D-DLL\Include\Layer3\ShapeNode.h和Intra3D-DLL\Layer3\ShapeNode.cpp。
COM库中的程序见Intra3D-COM\Layer3\ShapeNode.h和ShapeNode.cpp。
类ShapeNode的声明如下:
classShapeNode:
publicSceneNode
{
public:
voidSetBoundColor(floatred,floatgreen,floatblue);
voidShowBoundBox(void);
voidHideBoundBox(void);
voidSetGraphicalObject(GraphicalObject*object);
virtualvoidDraw(void);
virtualvoidDrawForMouseSelect(void);
...
protected:
virtualvoidFinalRelease(void);//Container.h
protec