6. Creating the cNodeTree Class(创建cNodeTree类)
是时候设计下了。你得先创建一些结构用来描绘顶点(vertices)、多边形(polygons)、节点(nodes)、纹理组(texture Groups):
// 这个sVertex结构体是一个常用的顶点结构体, // 它只包含3D坐标. 这里他被用于从网格顶点缓冲中返回坐标信息 typedef struct sVertex { float x, y, z; } sVertex; // 这个多边形结构体包含一个纹理组(通过数量) // time 是他最后被画的时间(所以在每个框架你不会重绘多次) // 这三个顶点被用于渲染多边形(待会你将看到) typedef struct sPolygon { unsigned long Group; // Texture group(纹理组) unsigned long Time; // Time last drawn(最后绘出时间) unsigned short Vertex[3]; // Vertex index #’s sPolygon() { Group = Time = 0; } // 清空数据 } sPolygon;
// 节点结构体知道在他的3D空间中多边形的数量 // 一组sPolygonList结构体, // 节点的3D坐标 (还有大小, 它是中心到边的距离,使得节点是一个完全的立方体), // 还有一个指向子节点的指针 typedef struct sNode { unsigned long NumPolygons; // 节点中多边形的数量 sPolygon **PolygonList; // 多边形数据列表 float XPos, YPos, ZPos; // 节点中心坐标 float Size; // 节点大小 sNode *Nodes[8]; // 子节点 4=quad, 8=oct // 使用构造函数,清理数据 sNode() { NumPolygons = 0; // 设置为在节点内没有多边形 PolygonList = NULL; // 清空多边形列表 XPos = YPos = ZPos = Size = 0.0f; // 位置和大小 for(short i=0;i<8;i++) // Quadtrees 只使用前四个 Nodes[i] = NULL; // 清空子节点指针 } // 析构函数清理子节点和变量 ~sNode() { delete [] PolygonList; // 删除多边形列表 for(short i=0;i<8;i++) delete Nodes[i]; // 删除子节点 } } sNode;
// 纹理组结构使用一个Core的cVertexBuffer类来保存在一个框架中(frame) // 需要被渲染的多边形 // 还有在一个纹理组中多边形的数量 // 和在每个框架中要画出的多边形的数量. typedef struct sGroup { cVertexBuffer VertexBuffer; // 要画出的顶点数据 char *VertexPtr; // 指向顶点数据的指针 unsigned long NumPolygons; // 在纹理组中多边形的数量 unsigned long NumPolygonsToDraw; // 要画出的多边形的数量 sGroup() { NumPolygons = 0; } // 清空多边形的数量 ~sGroup() { VertexBuffer.Free(); } // 释放顶点缓冲 } sGroup; |
每个结构体保存了网格或NodeTree结构各种不同的信息。sVertex是基础的顶点结构;他直接映射到其他所有顶点结构体。如果你从硬盘装在一个网格,你就能使用sVertex结构体去获得顶点的坐标。
sPolygon结构体可以保存场景中每个多边形的信息。它保存了纹理组的数量(纹理数量从0到纹理数量-1),最后一次绘出时间(用来停止重绘),还有三个用来绘出多边形的顶点。
没错,你直接使用每个顶点的索引值绘出每个多边形来调查网格对象。存取网格顶点将不得不等待直到在下一单元中我们建立起NodeTree类。现在,将你的注意力放到sNode结构体上。
sNode保存在节点3D空间中多边形的数量。每个节点有一个描述节点中心的坐标,还有节点的大小,他告诉我们中心点到一条边的距离(使得每一个节点是一个完全的立方体)。对于节点中的每个多边形,一个用于指向保存各自多变性数据的sPolygon结构体的指针。最后,八个子节点指针组成数结构。注意quadtree模式只使用前四个子节点指针。
最后就是cGroup结构体,它包含纹理组。每个纹理组使用一个cVertexBuffer对象去包含场景中使用的多边形的精确数量。当节点被扫描,所有可见得多边形被加入到他们各自的纹理组的顶点缓冲中。一次所有节点的扫描,每个纹理组就被使用组的顶点缓冲绘出函数(render function)绘出。
对数据结构介绍了一番,现在继续代码。看一下下面的类,他将一个网格读入,并转换为一个你选择的树结构(quadtree 或 octree):
// 两种树结构类型的枚举 enum TreeTypes { QUADTREE = 0, OCTREE }; class cNodeTreeMesh { private: // .. 在这里插入先前的 sVertex, sPolygon, sNode, and sGroup 结构体 int m_TreeType; // 节点树的类型 // QUADTREE or OCTREE cGraphics *m_Graphics; // cGraphics 父对象 cFrustum *m_Frustum; // Viewing frustum unsigned long m_Time; // 当前绘出(render)时间 float m_Size; // 世界立方体的大小 float m_MaxSize; // 节点大小的最大值 sNode *m_ParentNode; // 链接父节点 unsigned long m_NumGroups; // 纹理组数量 sGroup *m_Groups; // 纹理组 unsigned long m_NumPolygons; // 网格中多边形数量 unsigned long m_MaxPolygons; // 节点中多边形的数量最大值 sPolygon *m_PolygonList; // 多边形数据列表 sMesh *m_Mesh; // 父网格 char *m_VertexPtr; // 指向网格顶点 unsigned long m_VertexFVF; // 网格顶点的FVF unsigned long m_VertexSize; // 网格顶点的大小 // SortNode 对多边形进行分组 into nodes // 并分裂节点 as needed. void SortNode(sNode *Node, / float XPos, float YPos, float ZPos, / float Size); // AddNode 在要被绘出的节点列表中增加一个节点. void AddNode(sNode *Node); // 如果一个多边形的盒子 //与指定的立方体空间交叉IsPolygonContained 返回 TRUE BOOL IsPolygonContained(sPolygon *Polygon, / float XPos, float YPos, float ZPos, / float Size); // CountPolygons 返回在3D空间中多边形的数量 unsigned long CountPolygons( / float XPos, float YPos, float ZPos, / float Size);
public: cNodeTreeMesh(); // 构造函数 ~cNodeTreeMesh(); // 析构函数 // Functions to 从一个cMesh对象中创建或释放一个节点/树网格 // 制定一个空间中多边形的最大数量,她是比指定的size要大(强制顶点分裂). BOOL Create(cGraphics *Graphics, cMesh *Mesh, / int TreeType = OCTREE, / float MaxSize = 256.0f, long MaxPolygons = 32); BOOL Free(); // 使用当前视图矩阵渲染当前视图,并重载视角距离 // 也可以使用一个先前的定义的Frustum或自己定义一个Frustum BOOL Render(cFrustum *Frustum = NULL, float ZDistance = 0.0f); }; |
代码的注释已经很好的揭示了每一个变量和函数的作用。在接下来的章节,我将覆盖每个函数去做更多的处理。首先,看下表,他描述了cNodeTree所有变量