上下求索
探索DirectX9.0)
 

如果是储存面索引的方法,那么就比原来的实现有些复杂,不过原理还是一样的。

假设所有的物体都是从3DS模型中读入的,我们首先要把物体的基本的数据结构搞清楚。

struct tFace //面

{

     int vertIndex[3];           // 三个顶点

     int coordIndex[3];          // 顶点的纹理坐标

};

struct t3DObject //物体

{

     int  numOfVerts;            // 顶点总数

     int  numOfFaces;            // 面的数目

     int  numTexVertex;          // 纹理坐标的数目

     int  materialID;            // 纹理ID, 通过引用纹理数组的索引引用

     bool bHasTexture;           //

     char strName[255];          // 物体的名字

     UINT      *pIndices;        // 物体的面索引,用于vertex arrays

     CVector3  *pVerts;          // 顶点的指针

     CVector3  *pNormals;        // 物体的法线

     tVector2  *pTexVerts;       // 纹理的UV 坐标

     tFace *pFaces;              // 面

}

 

struct tFaceList

{

     vector<bool> pFaceList;

     int totalFaceCount;

};

和前面一样,建立八叉树前先要获取世界的长宽高,然后把它作为根节点的信息传递给CreateNode函数。

由于我们读入的模型中可能包括很多物体,所以我们需要改变一下,把原来用于保存顶点的vector<bool>改成 vector<tFaceList>,其实也就多了一行代码。

vector<tFaceList> pList1(pWorld->numOfObjects);         // TOP_LEFT_FRONT node

         vector<tFaceList> pList2(pWorld->numOfObjects);         // TOP_LEFT_BACK node

         vector<tFaceList> pList3(pWorld->numOfObjects);         // TOP_RIGHT_BACK node           vector<tFaceList> pList4(pWorld->numOfObjects);        // TOP_RIGHT_FRONT node

         vector<tFaceList> pList5(pWorld->numOfObjects);         // BOTTOM_LEFT_FRONT node

         vector<tFaceList> pList6(pWorld->numOfObjects);         // BOTTOM_LEFT_BACK node

         vector<tFaceList> pList7(pWorld->numOfObjects);         // BOTTOM_RIGHT_BACK node

     vector<tFaceList> pList8(pWorld->numOfObjects);        // BOTTOM_RIGHT_FRONT node

现在我们需要检测每个物体的每个三角形位于哪个位置,和前面的例子,这只是换汤不换药:

         for(int i = 0; i < pWorld->numOfObjects; i++) //遍历世界的每一个物体

         {

              t3DObject *pObject = &(pWorld->pObject[i]); //当前物体

              //调整容器的容量

              pList1[i].pFaceList.resize(pObject->numOfFaces);

              pList2[i].pFaceList.resize(pObject->numOfFaces);

              pList3[i].pFaceList.resize(pObject->numOfFaces);

              pList4[i].pFaceList.resize(pObject->numOfFaces);

              pList5[i].pFaceList.resize(pObject->numOfFaces);

              pList6[i].pFaceList.resize(pObject->numOfFaces);

              pList7[i].pFaceList.resize(pObject->numOfFaces);

              pList8[i].pFaceList.resize(pObject->numOfFaces);

 

              for(int j = 0; j < pObject->numOfFaces; j++)//遍历该物体中所有的三角形

              {

                   for(int whichVertex = 0; whichVertex < 3; whichVertex++)

                   {

                       // 当前顶点

                       CVector3 vPoint = pObject->pVerts[pObject->pFaces[j].vertIndex[whichVertex]];

 

                       // 看是否在 TOP LEFT FRONT node

                       if( (vPoint.x <= vCtr.x) && (vPoint.y >= vCtr.y) && (vPoint.z >= vCtr.z) )

                            pList1[i].pFaceList[j] = true;

 

                       //看是否在TOP LEFT BACK

                       if( (vPoint.x <= vCtr.x) && (vPoint.y >= vCtr.y) && (vPoint.z <= vCtr.z) )

                            pList2[i].pFaceList[j] = true;

 

                       //看是否在TOP RIGHT BACK

                       if( (vPoint.x >= vCtr.x) && (vPoint.y >= vCtr.y) && (vPoint.z <= vCtr.z) )

                            pList3[i].pFaceList[j] = true;

 

                       //看是否在TOP RIGHT FRONT

                       if( (vPoint.x >= vCtr.x) && (vPoint.y >= vCtr.y) && (vPoint.z >= vCtr.z) )

                            pList4[i].pFaceList[j] = true;

 

                       //看是否在BOTTOM LEFT FRONT

                       if( (vPoint.x <= vCtr.x) && (vPoint.y <= vCtr.y) && (vPoint.z >= vCtr.z) )

                            pList5[i].pFaceList[j] = true;

 

                       //看是否在BOTTOM LEFT BACK

                       if( (vPoint.x <= vCtr.x) && (vPoint.y <= vCtr.y) && (vPoint.z <= vCtr.z) )

                            pList6[i].pFaceList[j] = true;

 

                       //看是否在BOTTOM RIGHT BACK

                       if( (vPoint.x >= vCtr.x) && (vPoint.y <= vCtr.y) && (vPoint.z <= vCtr.z) )

                            pList7[i].pFaceList[j] = true;

 

                       //看是否在BOTTOM RIGHT FRONT

                       if( (vPoint.x >= vCtr.x) && (vPoint.y <= vCtr.y) && (vPoint.z >= vCtr.z) )

                            pList8[i].pFaceList[j] = true;

                   }

              }   

 

              // 接下来我们要把每个小区域内部的三角形的数目数出来

             

              pList1[i].totalFaceCount = 0;        pList2[i].totalFaceCount = 0;

              pList3[i].totalFaceCount = 0;        pList4[i].totalFaceCount = 0;

              pList5[i].totalFaceCount = 0;        pList6[i].totalFaceCount = 0;

              pList7[i].totalFaceCount = 0;        pList8[i].totalFaceCount = 0;

         }

知道了三角形的数目我们就可以调用CreateNewNode了:

         for(i = 0; i < pWorld->numOfObjects; i++)

         {

              //便利所有物体的三角形

              for(int j = 0; j < pWorld->pObject[i].numOfFaces; j++)

              {

                   if(pList1[i].pFaceList[j])  { pList1[i].totalFaceCount++; triCount1++; }

                   if(pList2[i].pFaceList[j])  { pList2[i].totalFaceCount++; triCount2++; }

                   if(pList3[i].pFaceList[j])  { pList3[i].totalFaceCount++; triCount3++; }

                   if(pList4[i].pFaceList[j])  { pList4[i].totalFaceCount++; triCount4++; }

                   if(pList5[i].pFaceList[j])  { pList5[i].totalFaceCount++; triCount5++; }

                   if(pList6[i].pFaceList[j])  { pList6[i].totalFaceCount++; triCount6++; }

                   if(pList7[i].pFaceList[j])  { pList7[i].totalFaceCount++; triCount7++; }

                   if(pList8[i].pFaceList[j])  { pList8[i].totalFaceCount++; triCount8++; }

              }

         }

 

         CreateNewNode(pWorld, pList1, triCount1, vCenter, width, TOP_LEFT_FRONT);

         CreateNewNode(pWorld, pList2, triCount2, vCenter, width, TOP_LEFT_BACK);

         CreateNewNode(pWorld, pList3, triCount3, vCenter, width, TOP_RIGHT_BACK);

         CreateNewNode(pWorld, pList4, triCount4, vCenter, width, TOP_RIGHT_FRONT);

         CreateNewNode(pWorld, pList5, triCount5, vCenter, width, BOTTOM_LEFT_FRONT);

         CreateNewNode(pWorld, pList6, triCount6, vCenter, width, BOTTOM_LEFT_BACK);

         CreateNewNode(pWorld, pList7, triCount7, vCenter, width, BOTTOM_RIGHT_BACK);

     CreateNewNode(pWorld, pList8, triCount8, vCenter, width, BOTTOM_RIGHT_FRONT);

现在CreateNewNode有了较大的改变,参数还是一样,在调用CreateNode之前,我们需要做一些工作,这些工作是要确定面列表,到最后把它传给显示列表:

 

void COctree::CreateNewNode(t3DModel *pWorld, vector<tFaceList> pList, int triangleCount,

                                CVector3 vCenter, float width,                int nodeID)

{

     if(!triangleCount) return;

     //临时变量,并初始化,保存叶结点中所有的物体

     t3DModel *pTempWorld = new t3DModel;

     memset(pTempWorld, 0, sizeof(t3DModel));

     pTempWorld->numOfObjects = pWorld->numOfObjects;

    

     // 遍历由参数传递进来的分区的所有物体

     for(int i = 0; i < pWorld->numOfObjects; i++)

     {

         t3DObject *pObject = &(pWorld->pObject[i]);

         // Create a new object, initialize it, then add it to our temp partition

         t3DObject newObject;

         memset(&newObject, 0, sizeof(t3DObject));

         pTempWorld->pObject.push_back(newObject);

 

         // Assign the new node's face count, material ID, texture boolean and

         // vertices to the new object.  Notice that it's not that pObject's face

         // count, but the pList's.  Also, we are just assigning the pointer to the

         // vertices, not copying them.

         pTempWorld->pObject[i].numOfFaces  = pList[i].totalFaceCount;

         pTempWorld->pObject[i].materialID  = pObject->materialID;

         pTempWorld->pObject[i].bHasTexture = pObject->bHasTexture;

         pTempWorld->pObject[i].pVerts      = pObject->pVerts;

 

         pTempWorld->pObject[i].pFaces = new tFace [pTempWorld->pObject[i].numOfFaces];//建立空的面列表

         int index = 0;

         // 遍历所有的面,如果在节点内,就保存面到面列表

         for(int j = 0; j < pObject->numOfFaces; j++)

         {

              if(pList[i].pFaceList[j])  

              {

                   pTempWorld->pObject[i].pFaces[index] = pObject->pFaces[j];

                   index++;

              }

         }

     }

这里我们做的所有的工作都是为了这个pTempWorld,这是一个临时变量,保存了当前分区的所有物体,接下来要做的就是为新的节点分配内存

 

 

m_pOctreeNodes[nodeID] = new COctree;

     CVector3 vNodeCenter = GetNewNodeCenter(vCenter, width, nodeID);

     g_CurrentSubdivision++;

     m_pOctreeNodes[nodeID]->CreateNode(pTempWorld, triangleCount, vNodeCenter, width / 2);

g_CurrentSubdivision--;

最后一步释放临时变量:

// 遍历临时变量的所有物体,释放前面创建的面列表

     for(i = 0; i < pWorld->numOfObjects; i++)

     {

         if(pTempWorld->pObject[i].pFaces)

              delete [] pTempWorld->pObject[i].pFaces;

     }

     delete pTempWorld;

 

到现在为止我们还没有把面索引的信息拷贝给叶结点,这个工作的思想是,对叶结点的分区的每一个物体遍历,把物体的每一个面(三角形)的三个顶点按顺序储存在一个数组里面,每一个物体都持有这样的一个数组

AssignTrianglesToNode(t3DModel *pWorld, int numberOfTriangles)

{

     //我们把pWorld分区的信息传递给面列表,它保存了我们渲染需要的面索引,由于我们使用顶点数组的缘故我们不能使用tFace结构作为索引的值,我们需要建立这样一个数组,他连续的保存了面索引。这就是pIndices数组,他的型别是无符号整数,必须是无符号整数。

     m_bSubDivided = false;

     m_TriangleCount = numberOfTriangles;

     m_pWorld = new t3DModel;

     memset(m_pWorld, 0, sizeof(t3DModel));

 

     // Assign the number of objects to our face index list

     m_pWorld->numOfObjects = pWorld->numOfObjects;

     // Go through all of the objects in the partition that was passed in

     for(int i = 0; i < m_pWorld->numOfObjects; i++)

     {

         // Create a pointer to the current object

         t3DObject *pObject = &(pWorld->pObject[i]);

 

         // Create and init a new object to hold the face index information

         t3DObject newObject;

         memset(&newObject, 0, sizeof(t3DObject));

 

         // If this object has face information, add it's index to our object index list

         if(pObject->numOfFaces)

              AddObjectIndexToList(i); //为每个物体建立索引

 

         // Add our new object to our face index list

         m_pWorld->pObject.push_back(newObject);

         int numOfFaces = pObject->numOfFaces;

         m_pWorld->pObject[i].numOfFaces = numOfFaces;

 

         // 为面索引分配空间  pIndices将传递给顶点数组. 

         m_pWorld->pObject[i].pFaces = new tFace [numOfFaces];

         m_pWorld->pObject[i].pIndices = new UINT [numOfFaces * 3];

 

         // 初始化

         memset(m_pWorld->pObject[i].pIndices, 0, sizeof(UINT) * numOfFaces * 3);

         // 拷贝面信息

         memcpy(m_pWorld->pObject[i].pFaces, pObject->pFaces, sizeof(tFace) * numOfFaces);

 

         // 由于使用顶点数组,我们需要一个储存了所有的面信息的数组,这将作为参数传递给

         // glDrawElements(). 

         // Go through all the faces and assign them in a row to our pIndices array

         for(int j = 0; j < numOfFaces * 3; j += 3)

         {

     m_pWorld->pObject[i].pIndices[j]     = m_pWorld->pObject[i].pFaces[j / 3].vertIndex[0];

     m_pWorld->pObject[i].pIndices[j + 1] = m_pWorld->pObject[i].pFaces[j / 3].vertIndex[1];

     m_pWorld->pObject[i].pIndices[j + 2] = m_pWorld->pObject[i].pFaces[j / 3].vertIndex[2];

         }

 

         // 我们只需要使用pIndcies,所以释放不需要的变量

         delete [] m_pWorld->pObject[i].pFaces;

         m_pWorld->pObject[i].pFaces = NULL;

     }

     m_DisplayListID = g_EndNodeCount;

     g_EndNodeCount++;

}

到此,每个物体的面索引就都储存在每个物体内部的pIndices数组内,这个数组最终将作为顶点数组渲染的对象。

为了使程序看上去完整,顺带把显示列表也提一下,我们将为每个叶结点分配一个显示列表的ID,每个叶结点中的物体保存了pIndices数组,在构造显示列表的时候用顶点数组来实现立即绘制:

CreateDisplayList(COctree *pNode, t3DModel *pRootWorld, int displayListOffset)

{

     if(!pNode) return;

     // 检查是不是叶结点,如果不是就深入

     if(pNode->IsSubDivided())

     {

         // Recurse down to each one of the children until we reach the end nodes

CreateDisplayList(pNode->m_pOctreeNodes[TOP_LEFT_FRONT],     pRootWorld, displayListOffset);

CreateDisplayList(pNode->m_pOctreeNodes[TOP_LEFT_BACK],      pRootWorld, displayListOffset);

CreateDisplayList(pNode->m_pOctreeNodes[TOP_RIGHT_BACK],     pRootWorld, displayListOffset);

CreateDisplayList(pNode->m_pOctreeNodes[TOP_RIGHT_FRONT],    pRootWorld, displayListOffset);

CreateDisplayList(pNode->m_pOctreeNodes[BOTTOM_LEFT_FRONT],  pRootWorld, displayListOffset);

CreateDisplayList(pNode->m_pOctreeNodes[BOTTOM_LEFT_BACK],   pRootWorld, displayListOffset);

CreateDisplayList(pNode->m_pOctreeNodes[BOTTOM_RIGHT_BACK],  pRootWorld, displayListOffset);

CreateDisplayList(pNode->m_pOctreeNodes[BOTTOM_RIGHT_FRONT],pRootWorld, displayListOffset);

     }

     else

     {

         if(!pNode->m_pWorld) return;

         // 显示列表的ID偏移量

         pNode->m_DisplayListID += displayListOffset;

//********************************建立显示列表开始*****************************************

         // Start the display list and assign it to the end nodes ID

         glNewList(pNode->m_DisplayListID,GL_COMPILE);

         // 储存物体数量

         int counter = 0;

         // Store the object count and material count in some local variables for optimization

         int objectCount = pNode->m_pObjectList.size(); //物体数量

         int materialCount = pRootWorld->pMaterials.size(); //材质数量

 

         // 遍历所有物体

         while(counter < objectCount)

         {

              // Get the first object index into our root world

              // 取得整个世界中第一个物体的索引

              int i = pNode->m_pObjectList[counter];

 

              // Store pointers to the current face list and the root object

              // that holds all the data (verts, texture coordinates, normals, etc..)

              // 保存指向现在的面列表的指针和整个世界的指针,他们包含了所有的数据

              t3DObject *pObject     = &pNode->m_pWorld->pObject[i];

              t3DObject *pRootObject = &pRootWorld->pObject[i];

 

              // Check to see if this object has a texture map, if so, bind the texture to it.

              // 检查当前物体是否包括贴图,如果有,邦定贴图

              if(pRootObject->bHasTexture)

              {

                   // Turn on texture mapping and turn off color

                   glEnable(GL_TEXTURE_2D);

                   // Reset the color to normal again

                   glColor3ub(255, 255, 255);

                   glBindTexture(GL_TEXTURE_2D, g_Texture[pRootObject->materialID]);

              }

              else

              {

                   glDisable(GL_TEXTURE_2D);

                   glColor3ub(255, 255, 255);

              }

 

              // Check to see if there is a valid material assigned to this object

              // 检查材质

              if(materialCount && pRootObject->materialID >= 0)

              {

                   // Get and set the color that the object is, since it must not have a texture

                   BYTE *pColor = pRootWorld->pMaterials[pRootObject->materialID].color;

 

                   // Assign the current color to this model

                   glColor3ub(pColor[0], pColor[1], pColor[2]);

              }

              // Make sure we have texture coordinates to render

              // 是否有纹理坐标,如果有,需要启用纹理坐标数组

              if(pRootObject->pTexVerts)

              {

                   // Turn on the texture coordinate state

                   glEnableClientState(GL_TEXTURE_COORD_ARRAY);

                   glTexCoordPointer(2, GL_FLOAT, 0, pRootObject->pTexVerts);

              }

 

              // Make sure we have vertices to render

              // 确定是否有顶点等待渲染,如果有建立顶点数组

              if(pRootObject->pVerts)

              {

                   glEnableClientState(GL_VERTEX_ARRAY);

                   glVertexPointer(3, GL_FLOAT, 0, pRootObject->pVerts);  // pRootObject->pVerts指向顶点数组

              }

 

              // Make sure we have normals to render

              // 确定是否有法线需要渲染,如果有建立法线数组

              if(pRootObject->pNormals)

              {

                   // Turn on the normals state

                   glEnableClientState(GL_NORMAL_ARRAY);

                   glNormalPointer(GL_FLOAT, 0, pRootObject->pNormals);

              }

              // 注意,这里的pObject是  &pNode->m_pWorld->pObject[i];

              glDrawElements(GL_TRIANGLES,    pObject->numOfFaces * 3,

                               GL_UNSIGNED_INT, pObject->pIndices);

              counter++;

         }

 

         // End the display list for this ID

         glEndList();

//********************************建立显示列表结束****************************************

 

     }

}

最后一步,把八叉树画出来,思想也很简单,首先深入到叶结点,然后执行显示列表,当然,在最开始还是要判断以下视景体所在的位置:

DrawOctree(COctree *pNode, t3DModel *pRootWorld)

{

     if(!pNode) return;

     if(!g_Frustum.CubeInFrustum(pNode->m_vCenter.x, pNode->m_vCenter.y,

                                     pNode->m_vCenter.z, pNode->m_Width / 2) )

     {

         return;

     }

     if(pNode->IsSubDivided())

     {

         // Recurse to the bottom of these nodes and draw the end node's vertices

         // Like creating the octree, we need to recurse through each of the 8 nodes.

         DrawOctree(pNode->m_pOctreeNodes[TOP_LEFT_FRONT],       pRootWorld);

         DrawOctree(pNode->m_pOctreeNodes[TOP_LEFT_BACK],        pRootWorld);

         DrawOctree(pNode->m_pOctreeNodes[TOP_RIGHT_BACK],       pRootWorld);

         DrawOctree(pNode->m_pOctreeNodes[TOP_RIGHT_FRONT],      pRootWorld);

         DrawOctree(pNode->m_pOctreeNodes[BOTTOM_LEFT_FRONT],    pRootWorld);

         DrawOctree(pNode->m_pOctreeNodes[BOTTOM_LEFT_BACK],     pRootWorld);

         DrawOctree(pNode->m_pOctreeNodes[BOTTOM_RIGHT_BACK],    pRootWorld);

         DrawOctree(pNode->m_pOctreeNodes[BOTTOM_RIGHT_FRONT],   pRootWorld);

     }

     else

     {

         // Increase the amount of nodes in our viewing frustum (camera's view)

         g_TotalNodesDrawn++;

         // Make sure we have valid data assigned to this node

         if(!pNode->m_pWorld) return;

         // Call the list with our end node's display list ID

         glCallList(pNode->m_DisplayListID);

 

     }

}

posted on 2005-09-16 13:12  大河马和小魔鱼  阅读(2630)  评论(0编辑  收藏  举报