Octree(2)
如果是儲存面索引的方法,那么就比原來的實現有些復雜,不過原理還是一樣的。
假設所有的物體都是從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);
?
???? }
}
轉載于:https://www.cnblogs.com/sssa2000/archive/2005/09/16/238306.html
總結
- 上一篇: 气死我的存储过程和用户定义函数
- 下一篇: Some best freeware