Advanced 3D Game Programming with DirectX - phần 8 ppsx

71 328 0
Advanced 3D Game Programming with DirectX - phần 8 ppsx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

498 Figure 9.23: Subdividing edges to add triangles The equation we use to subdivide an edge depends on the valence of its endpoints. The valence of a vertex in this context is defined as the number of other vertices the vertex is adjacent to. There are three possible cases that we have to handle. The first case is when both vertices of a particular edge have a valence = 6. We use a mask on the neighborhood of vertices around the edge. This mask is where the modified butterfly scheme gets its name, because it looks sort of like a butterfly. It appears in Figure 9.24 . Figure 9.24: The butterfly mask The modified butterfly scheme added two points and a tension parameter that lets you control the sharpness of the limit surface. Since this scheme complicates the code, I chose to go with a universal 499 w-value of 0.0 instead (which resolves to the above Figure 9.24 ). The modified butterfly mask appears in Figure 9.25 . Figure 9.25: The modified butterfly mask To compute the location of the subdivided edge vertex (the white circle in both images), we step around the neighborhood of vertices and sum them (multiplying each vector by the weight dictated by the mask). You'll notice that all the weights sum up to 1.0. This is good; it means our subdivided point will be in the right neighborhood compared to the rest of the vertices. You can imagine if the sum was much larger the subdivided vertex would be much farther away from the origin than any of the vertices used to create it, which would be incorrect. When only one of our vertices is regular (i.e., has a valence = 6), we compute the subdivided location using the irregular vertex, otherwise known as a k-vertex. This is where the modified butterfly algorithm shines over the original butterfly algorithm (which handled k-vertices very poorly). An example appears in Figure 9.26 . The right vertex has a valence of 6, and the left vertex has a valence of 9, so we use the left vertex to compute the location for the new vertex (indicated by the white circle). 500 Figure 9.26: Example of a k-vertex The general case for a k-vertex has us step around the vertex, weighting the neighbors using a mask determined by the valence of the k-vertex. Figure 9.27 shows the generic k-vertex and how we name the vertices. Note that the k-vertex itself has a weight of ¾, in all cases. Figure 9.27: Generic k-vertex There are three cases to deal with: k = 3, k = 4, and k = 5. The masks for each of them are: 501 The third and final case we need to worry about is when both endpoints of the current edge are k- vertices. When this occurs we compute the k-vertex for both endpoints using the above weights, and average the results together. Note that we are assuming that our input triangle mesh is closed boundary representation (doesn't have any holes in it). The paper describing the modified butterfly scheme discusses ways to handle holes in the model (with excellent results) but the code we'll write next won't be able to handle holes in the model so we won't discuss it. Using these schema for computing our subdivided locations results in an extremely fair looking surface. Figure 9.28 shows how an octahedron looks as it is repeatedly subdivided. The application we will make next was used to create this image. Levels 0 (8 triangles) through 4 (2048 triangles) are shown. Finally, level 4 mesh is shown in filled mode. Figure 9.28: A subdivided octagon model. Application: SubDiv The SubDiv application implements the modified butterfly subdivision scheme we just discussed. It loads an .o3d file and displays it interactively, giving the user the option of subdividing the model whenever they wish. The model data is represented with an adjacency graph. Each triangle structure holds pointers to the three vertices it is composed of. Each vertex structure has STL vectors that contain pointers to edge structures (one edge for each vertex it's connected to) and triangle structures. The lists are unsorted (which requires linear searching; fixing this to order the edges in clockwise winding order, for example, is left as an exercise for the reader). 502 Listing 9.8 gives the header definitions (and many of the functions) for the vertex, edge, and triangle structures. These classes are all defined inside the subdivision surface class (cSubDivSurf). Listing 9.8: Vertex, edge, and triangle structures /** * Subdivision Surface vertex (name 'sVertex' is used in D3D code) */ struct sVert { /** * These two arrays describe the adjacency information * for a vertex. Each vertex knows who all of its neighboring * edges and triangles are. An important note is that these * lists aren't sorted. We need to search through the list * when we need to get a specific adjacent triangle. * This is, of course, inefficient. Consider sorted insertion * an exercise to the reader. */ std::vector< sTriangle* > m_triList; std::vector< sEdge* > m_edgeList; /** * position/normal information for the vertex */ sVertex m_vert; /** * Each Vertex knows its position in the array it lies in. * This helps when we're constructing the arrays of * subdivided data. */ int m_index; 503 void AddEdge( sEdge* pEdge ) { assert( 0 == std::count( m_edgeList.begin(), m_edgeList.end(), pEdge ) ); m_edgeList.push_back( pEdge ); } void AddTri( sTriangle* pTri ) { assert( 0 == std::count( m_triList.begin(), m_triList.end(), pTri ) ); m_triList.push_back( pTri ); } /** * Valence == How many other vertices are connected to this one * which said another way is how many edges the vert has. */ int Valence() { return m_edgeList.size(); } sVert() : m_triList( 0 ), m_edgeList( 0 ) { 504 } /** * Given a Vertex that we know we are attached to, this function * searches the list of adjacent edges looking for the one that * contains the input vertex. Asserts if there is no edge for * that vertex. */ sEdge* GetEdge( sVert* pOther ) { for( int i=0; i<m_edgeList.size(); i++ ) { if( m_edgeList[i]->Contains( pOther ) ) return m_edgeList[i]; } assert(false); // didn't have it! return NULL; } }; /** * Edge structure that connects two vertices in a SubSurf */ struct sEdge { sVert* m_v[2]; /** * When we perform the subdivision calculations on all the edges * the result is held in this newVLoc strucure. Never has any * connectivity information, just location and color. 505 */ sVert m_newVLoc; /** * true == one of the edges' vertices is the inputted vertex */ bool Contains( sVert* pVert ) { return (m_v[0] == pVert) || m_v[1] == pVert; } /** * retval = the other vertex than the inputted one */ sVert* Other( sVert* pVert ) { return (m_v[0] == pVert) ? m_v[1] : m_v[0]; } void Init( sVert* v0, sVert* v1 ) { m_v[0] = v0; m_v[1] = v1; /** * Note that the edge notifies both of its vertices that it's * connected to them. */ m_v[0]->AddEdge( this ); m_v[1]->AddEdge( this ); } 506 /** * This function takes into consideration the two triangles that * share this edge. It returns the third vertex of the first * triangle it finds that is not equal to 'notThisOne'. So if * want one, notThisOne is passed as NULL. If we want the other * one, we pass the result of the first execution. */ sVert* GetOtherVert( sVert* v0, sVert* v1, sVert* notThisOne ) { sTriangle* pTri; for( int i=0; i<v0->m_triList.size(); i++ ) { pTri = v0->m_triList[i]; if( pTri->Contains( v0 ) && pTri->Contains( v1 ) ) { if( pTri->Other( v0, v1 ) != notThisOne ) return pTri->Other( v0, v1 ); } } // when we support boundary edges, we shouldn't assert assert(false); return NULL; } /** * Calculate the K-Vertex location of 'prim' vertex. For triangles * of valence !=6 */ point3 CalcKVert( int prim, int sec ); /** 507 * Calculate the location of the subdivided point using the * butterfly method. * for edges with both vertices of valence == 6 */ point3 CalcButterfly(); }; /** * Subdivision surface triangle */ struct sTriangle { /** * The three vertices of this triangle */ sVert* m_v[3]; point3 m_normal; void Init( sVert* v0, sVert* v1, sVert* v2 ) { m_v[0] = v0; m_v[1] = v1; m_v[2] = v2; /** * Note that the triangle notifies all 3 of its vertices * that it's connected to them. */ m_v[0]->AddTri( this ); m_v[1]->AddTri( this ); [...]... * m_v[0 ]-> m_vert.loc; out += (1.f/2.f) * m_v[1 ]-> m_vert.loc; // top/bottom ones out += (1.f /8. f) * other[0 ]-> m_vert.loc; out += (1.f /8. f) * other[1 ]-> m_vert.loc; // outside 4 verts out += (-1 .f/16.f) * GetOtherVert( other[0], m_v[0], m_v[1] )-> m_vert.loc; out += (-1 .f/16.f) * GetOtherVert( other[0], m_v[1], m_v[0] )-> m_vert.loc; out += (-1 .f/16.f) * GetOtherVert( other[1], m_v[0], m_v[1] )-> m_vert.loc;... pCurr->m_vert.loc; pTemp = GetOtherVert( m_v[prim], pCurr, pLast ); pLast = pCurr; pCurr = pTemp; } } return out; } void cSubDivSurf::GenD3DData() { /** * Create a vertex buffer */ HRESULT hr; hr = Graphics( )-> GetDevice( )-> CreateVertexBuffer( m_nVerts * sizeof( sVertex ), D3DUSAGE_WRITEONLY, D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1, D3DPOOL_DEFAULT, &m_pVertexBuffer ); 5 18 if( FAILED( hr )) { throw cGameError("Vertex... &m_pTList[i].m_v[0 ]-> GetEdge( 510 m_pTList[i].m_v[1] )-> m_newVLoc; inner[1] = &m_pTList[i].m_v[1 ]-> GetEdge( m_pTList[i].m_v[2] )-> m_newVLoc; inner[2] = &m_pTList[i].m_v[2 ]-> GetEdge( m_pTList[i].m_v[0] )-> m_newVLoc; pNewEdges[currEdge++].Init( &pNewVerts[inner[0 ]-> m_index], &pNewVerts[inner[1 ]-> m_index] ); pNewEdges[currEdge++].Init( &pNewVerts[inner[1 ]-> m_index], &pNewVerts[inner[2 ]-> m_index] ); pNewEdges[currEdge++].Init(... &pNewVerts[inner[2 ]-> m_index], &pNewVerts[inner[0 ]-> m_index] ); } //========== -Step 3: Fill triangle list int currTri = 0; for( i=0; i GetEdge( m_pTList[i].m_v[1] )-> m_newVLoc; inner[1] = &m_pTList[i].m_v[1 ]-> GetEdge( m_pTList[i].m_v[2] )-> m_newVLoc; inner[2] = &m_pTList[i].m_v[2 ]-> GetEdge( m_pTList[i].m_v[0] )-> m_newVLoc;... m_v[prim ]-> m_edgeList[i ]-> Other( m_v[prim] ); if( pOther == m_v[sec] ) out += (5.f/12.f) * pOther->m_vert.loc; else out += (-1 .f/12.f) * pOther->m_vert.loc; } } else if( valence == 4 ) { out += (3.f /8. f) * m_v[sec ]-> m_vert.loc; sVert* pTemp = GetOtherVert( m_v[0], m_v[1], NULL ); // get the one after it sVert* pOther = GetOtherVert( m_v[prim], pTemp, m_v[sec] ); out += (-1 .f /8. f) * pOther->m_vert.loc; } else... &pNewVerts[m_pTList[i].m_v[0 ]-> m_index], &pNewVerts[inner[0 ]-> m_index], &pNewVerts[inner[2 ]-> m_index] ); // 1, inner1, inner0 pNewTris[currTri++].Init( &pNewVerts[m_pTList[i].m_v[1 ]-> m_index], &pNewVerts[inner[1 ]-> m_index], &pNewVerts[inner[0 ]-> m_index] ); // 2, inner2, inner1 pNewTris[currTri++].Init( &pNewVerts[m_pTList[i].m_v[2 ]-> m_index], &pNewVerts[inner[2 ]-> m_index], &pNewVerts[inner[1 ]-> m_index] ); // inner0,... m_pTList[i].m_v[0 ]-> m_index; m_d3dTriList[i].v[1] = m_pTList[i].m_v[1 ]-> m_index; m_d3dTriList[i].v[2] = m_pTList[i].m_v[2 ]-> m_index; } } Progressive Meshes The final multiresolution system we are going to discuss is progressive meshes They're rapidly gaining favor in the game community; many games use them as a way to keep scene detail at a constant level Oftentimes when we're playing a 3D game, many of our objects... failed!\n"); } m_d3dTriList = new sTri[ m_nTris ]; sVertex* pVert; // Lock the vertex buffer hr = m_pVertexBuffer->Lock( 0, 0, (BYTE**)&pVert, 0); if( FAILED( hr )) { throw cGameError("VB Lock failed\n"); } int i; // Copy data into the buffer for( i=0; iUnlock(); for( i=0; i m_index;... out += (-1 .f/16.f) * GetOtherVert( other[1], m_v[1], m_v[0] )-> m_vert.loc; return out; } point3 cSubDivSurf::sEdge::CalcKVert(int prim, int sec) { int valence = m_v[prim ]-> Valence(); point3 out = point3::Zero; out += (3.f / 4.f) * m_v[prim ]-> m_vert.loc; 516 if( valence m_edgeList[i ]-> Other(... -Step 1: Fill vertex list // First batch - the original vertices 509 for( i=0; i . (1.f/2.f) * m_v[0 ]-& gt;m_vert.loc; out += (1.f/2.f) * m_v[1 ]-& gt;m_vert.loc; // top/bottom ones out += (1.f /8. f) * other[0 ]-& gt;m_vert.loc; out += (1.f /8. f) * other[1 ]-& gt;m_vert.loc;. &m_pTList[i].m_v[0 ]-& gt;GetEdge( 511 m_pTList[i].m_v[1] )-& gt;m_newVLoc; inner[1] = &m_pTList[i].m_v[1 ]-& gt;GetEdge( m_pTList[i].m_v[2] )-& gt;m_newVLoc; inner[2] = &m_pTList[i].m_v[2 ]-& gt;GetEdge(. &m_pTList[i].m_v[0 ]-& gt;GetEdge( m_pTList[i].m_v[1] )-& gt;m_newVLoc; inner[1] = &m_pTList[i].m_v[1 ]-& gt;GetEdge( m_pTList[i].m_v[2] )-& gt;m_newVLoc; inner[2] = &m_pTList[i].m_v[2 ]-& gt;GetEdge(

Ngày đăng: 08/08/2014, 23:20

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan