//**************************************************************************/ // Copyright (c) 2010 Autodesk, Inc. // All rights reserved. // // Use of this software is subject to the terms of the Autodesk license // agreement provided at the time of installation or download, or which // otherwise accompanies this software in either electronic or hard copy form. // //**************************************************************************/ // DESCRIPTION: // CREATED: August 2010 //**************************************************************************/ #include "PtexLayout.h" #include <QtGui/QWidget> #include <QtGui/QGridLayout> // RTTI macro, needed for each class. IMPLEMENT_SCLASS( PtexLayout, Layout, "ptexlayout", 2 ); // Default constructor, initializes the quality to 0.5, which means 32x32=1024 reference points will be generated for each base level face. PtexLayout::PtexLayout( void ) : m_eQuality( this, "quality" ) { m_eQuality.SetName( QObject::tr("Quality:") ); m_eQuality.SetToolTip( QObject::tr("Mudbox automatically calculates a good per-face resolution for your ptex file. Use this dropdown to choose a lower resolution (saves disk space) or a higher one (provides more detail).") ); m_eQuality.AddItem( QObject::tr("Low") ); m_eQuality.AddItem( QObject::tr("Good") ); m_eQuality.AddItem( QObject::tr("High") ); m_eQuality = 1; m_iDirectResolution = -1; }; // Create the user interface widget for the layout, which consists of a single widget at this moment, the quality slider. QWidget *PtexLayout::UserInterface( void ) { QWidget *w = new QWidget; QGridLayout *l = new QGridLayout; // If there are multiple attributes, they can be added to the QGridLayout object the same way. All added attribute will appear on the UI in a // different row. l->addWidget( m_eQuality.CreateEditorWidget( NULL, 150 ) ); w->setLayout( l ); return w; }; // Main function, it processes the list of the given target surfaces, and collects the reference points on them. void PtexLayout::ProcessSurfaces( QVector<SubdivisionLevel *> aSurfaces ) { // Loop through the target meshes. for ( int s = 0; s < aSurfaces.size(); s++ ) { SubdivisionLevel *t = aSurfaces[s]; // Indicate that a section of reference points has begun. Each mesh will have its own section. BeginSection(); if ( t->Type() == Mesh::typeQuadric ) { // Decide the horizontal and vertical resolution of the grid what we are using for the face. Currently all the faces got the same resolution, but // the ptex file format can handle changing grid sizes for the faces. int iRes = TargetResolution( s ); int iHRes = 1 << iRes; int iVRes = 1 << iRes; // Loop through all the faces of the mesh. for ( unsigned int f = 0; f < t->FaceCount(); f++ ) { // Loop through the points of the grid. for ( int u = 0; u < iHRes; u++ ) for ( int v = 0; v < iVRes; v++ ) { // For each point on the grid we create a reference point, and call the ProcessSurfacePoint function. TargetLocation l; l.m_aLayoutData[dataURes] = u+(iRes<<24); l.m_aLayoutData[dataVRes] = v+(iRes<<24); l.m_aLayoutData[dataFaceID] = f; // To properly define the point on the surface, we use the index of the face, and the coordinates inside the face calculated from the grid coordinate. l.Fill( t, f, (u+0.5f)/(float)iHRes, (v+0.5f)/(float)iVRes ); ProcessSurfacePoint( l ); }; }; // Declaring the end of the section, which will tell the PtexUtilizer objects that no more reference points will be generated for the current mesh, so they can finish // writing the ptex file into the disk. } else { PrepareAdjacency( t ); unsigned int iRes = TargetResolution( s ); unsigned int iFaceID = 0; // NSided case, this is more complicated than the quad one. for ( unsigned int f = 0; f < t->FaceCount(); f++ ) { // We go through all the polygons of the target mesh, so we skip fake triangles. if ( t->IsFakeTriangle( f ) ) continue; // Calculate the number of sides for this poly. int iSideCount = 3, i = f; while ( f < t->FaceCount() && t->IsFakeTriangle( ++i ) ) iSideCount++; // The number of the ptex faces generated for this polygon depends on the number of sides. For quads, we have // to generate a single ptex face, for other polygons we generate one ptex face for each vertex, so the number // of ptex faces in that case is the same as the number of sides in the polygon. int iSubFaceCount = iSideCount; if ( iSideCount == 4 ) iSubFaceCount = 1; // Collect the object space position of the corners of the polygon. Vector aCorners[16]; MB_ONBUG( iSideCount >= 16 ) continue; aCorners[0] = t->TriangleVertexPosition( f, 0 ); aCorners[1] = t->TriangleVertexPosition( f, 1 ); for ( int i = 0; i < iSideCount-2; i++ ) aCorners[i+2] = t->TriangleVertexPosition( f+i, 2 ); // Calculate the position of the center of the polygon/ Vector vCenter; for ( int j = 0; j < iSideCount; j++ ) vCenter += aCorners[j]; vCenter *= 1/(float)iSideCount; // Go through the subfaces. for ( int iSubFace = 0; iSubFace < iSubFaceCount; iSubFace++ ) { Vector vCorner0, vCorner1, vCorner2, vCorner3; int iURes = iRes, iVRes = iRes; if ( iSideCount == 4 ) { // When this polygon is a quad, then there is a single ptex face, which covers the full area of the quad. MB_ASSERT( iSubFace == 0 ); vCorner0 = aCorners[0]; vCorner1 = aCorners[1]; vCorner2 = aCorners[2]; vCorner3 = aCorners[3]; } else { // When the polygon is not a quad, then each vertex will have its own ptex face. This ptex face will // cover a rectangular area surrounded by the vertex, the center of the two edges attached to the // vertex, and the center of the polygon. iURes >>= 1; // Since this is a subface, we halve the resolution. iVRes >>= 1; vCorner0 = aCorners[iSubFace]; vCorner1 = (vCorner0+aCorners[(iSubFace+1)%iSideCount])*0.5f; vCorner2 = vCenter; vCorner3 = (vCorner0+aCorners[(iSubFace-1+iSideCount)%iSideCount])*0.5f; }; // Now that we have the object space position of the corners of the ptex face, we generate reference // points in that area in a grid layout. int iUSize = 1 << iURes, iVSize = 1 << iVRes; for ( int iU = 0; iU < iUSize; iU++ ) for ( int iV = 0; iV < iVSize; iV++ ) { TargetLocation l; l.m_aLayoutData[dataURes] = iU+(iURes<<24); l.m_aLayoutData[dataVRes] = iV+(iVRes<<24); l.m_aLayoutData[dataFaceID] = iFaceID; if ( iSubFaceCount > 1 ) l.m_aLayoutData[dataFaceID] |= 0x80000000; float fU = ((float)iU+0.5f)/iUSize, fV = ((float)iV+0.5f)/iVSize; Vector vH0 = vCorner0+(vCorner1-vCorner0)*fU; Vector vH1 = vCorner3+(vCorner2-vCorner3)*fU; l.FillNSided( t, f, vH0+(vH1-vH0)*fV ); // We initialize the location based on the object space position of the reference point. ProcessSurfacePoint( l ); }; iFaceID++; }; }; }; EndSection(); }; }; // This function prepares the object for the extraction, and returns the number of the expected reference points. unsigned int PtexLayout::Prepare( void ) { // Count the total number of reference points which will be used during the extraction. int iRefCount = 0; for ( int c = 0; c < m_pMapExtractor->TargetContainerCount(); c++ ) for ( int m = 0; m < m_pMapExtractor->TargetMeshCountInContainer( c ); m++ ) { const Mesh *p = m_pMapExtractor->TargetMesh( c, m ); // For full quad meshes, the number of the generated ptex faces is equals to the number of quads in the mesh. if ( p->Type() == Mesh::typeQuadric ) iRefCount += m_pMapExtractor->TargetMesh( c, m )->FaceCount()*(1<<(2*TargetResolution( c ))); else { // For other meshes we have to check all the polygons, and calculate the number of ptex faces. float t = 0; for ( unsigned int i = 0; i < p->FaceCount(); i++ ) { if ( p->IsFakeTriangle( i ) ) continue; unsigned int f = i+1; while ( f < p->FaceCount() && p->IsFakeTriangle( f ) ) f++; unsigned int s = f-i+2; // Quads will have a single ptex face, other polygons will have as much ptex faces as much sides // they have, but with half resolution (i.e. the number of reference points for those subfaces is quarter // of the normal ones. if ( s == 4 ) t++; else t += 0.25*s; }; iRefCount += t*(1<<(2*TargetResolution( c ))); }; }; // The number of expected is the multiplication of the two, since all the faces will get the same number of reference points. return iRefCount; }; // This function calculates the needed resolution for the generated ptex file. The returned value indicates the level used for each face (0=1x1, 1=2x2, 2=4x4, 3=16x16, ... ) int PtexLayout::TargetResolution( int iTargetIndex ) const { int iRes = 0; if ( m_iDirectResolution != -1 ) return m_iDirectResolution; // If the number of target and source meshes are the same, we calculate the needed resolution based on the ratio between their face count. if ( m_pMapExtractor->TargetContainerCount() == (int)m_pMapExtractor->SourceCount() && m_pMapExtractor->TargetMeshCountInContainer( iTargetIndex ) == 1 ) { int iFaceCountFactor = m_pMapExtractor->Source( iTargetIndex )->FaceCount()/m_pMapExtractor->TargetMesh( iTargetIndex, 0 )->FaceCount(); while ( iFaceCountFactor > 1 ) { iRes++; iFaceCountFactor /= 4; }; } else iRes = 4; // Apply the quality attribute. if ( m_eQuality == 0 ) iRes--; if ( m_eQuality == 2 ) iRes++; return Max(1,Min(10,iRes)); }; // Serialize the state of the object. We only need to serialize the only one attribute which belongs to this class. void PtexLayout::Serialize( Stream &s ) { if ( s.IsNewerThan( 0, this ) ) { if ( s.IsNewerThan( 1, this ) ) s == m_eQuality; else { float f; s >> f; }; }; Layout::Serialize( s ); }; // Precalculating the m_aFaceID and m_aTriangle arrays for the current mesh. This is only needed when the target mesh is not a full quad mesh. void PtexLayout::PrepareAdjacency( const Mesh *pMesh ) { // The m_aFaceID array will containt the ID of the first ptex face which belongs to the given triangle. // For example, if a pentagon represented by the triangles 5-6-7 is split into ptex face with the ID of 7-11, then the array will contain values: // m_aFaceID[5] = 7 // ID of the first ptex face belongs to this polygon // m_aFaceID[6] = -1 // because this is a fake triangle // m_aFaceID[7] = -1 // the same // m_aFaceID[8] = 12 // this belongs to the next polygon m_pMesh = pMesh; m_aFaceID.resize( pMesh->FaceCount() ); unsigned int iTFaceID = 0; // This is the ID of the current ptex face. for ( unsigned int i = 0; i < pMesh->FaceCount(); i++ ) { MB_ASSERT( !pMesh->IsFakeTriangle( i ) ); m_aFaceID[i] = iTFaceID; // Calculate the sides of the polygon. int s = 3; while ( i+1 < pMesh->FaceCount() && pMesh->IsFakeTriangle( i+1 ) ) { i++; s++; m_aFaceID[i] = 0xffffffff; }; // If the polygon is a quad, then a single ptex face will be generated for it, otherwise each corner will get its own ptex face. if ( s == 4 ) iTFaceID++; else iTFaceID += s; }; // Fill the m_aTriangle array, which is the opposite of the m_afaceID array, it contains the index of the triangle which represents // the polygon which belongs to the current ptex face. In the same example as above, the array will look like this: // m_aTriangle[7] = 5 // m_aTriangle[8] = 5 // m_aTriangle[9] = 5 // m_aTriangle[10] = 5 // m_aTriangle[11] = 5 // m_aTriangle[12] = 8 // this belongs to the next polygon m_aTriangle.fill( 0xffffffff, iTFaceID ); for ( unsigned int i = 0; i < m_aFaceID.size(); i++ ) if ( m_aFaceID[i] != 0xffffffff ) m_aTriangle[m_aFaceID[i]] = i; for ( unsigned int i = 1; i < m_aTriangle.size(); i++ ) if ( m_aTriangle[i] == 0xffffffff ) m_aTriangle[i] = m_aTriangle[i-1]; }; // This function calculates the adjacency info for an edge of a mudbox triangle. The iSegment parameter controls which part of the // edge we are interested (0=first half, 1=second half). This function is only needed when the target mesh is not a full quad mesh. unsigned int PtexLayout::AdjacentFaceForTriangle( unsigned int iFaceIndex, unsigned int iSide, unsigned int iSegment, unsigned int &iEdge ) const { // Check which is the adjacent triangle (if any) MB_ONBUG( iFaceIndex >= m_pMesh->FaceCount() ) return 0xffffffff; unsigned int a = m_pMesh->TriangleAdjacency( iFaceIndex, iSide ); if ( a == 0xffffffff ) return 0xffffffff; // And calculate the side count for the polygon the triangle represents. unsigned int t = a/3; MB_ONBUG( t >= m_pMesh->FaceCount() ) return 0xffffffff; while ( m_pMesh->IsFakeTriangle( t ) ) { MB_ONBUG( t == 0 ) return 0xffffffff; t--; }; unsigned int h = t+1; while ( h < m_pMesh->FaceCount() && m_pMesh->IsFakeTriangle( h ) ) h++; unsigned int s = h-t+2; MB_ONBUG( s >= 16 ) return 0xffffffff; // Collect vertices of the adjacent polygon into a local array. unsigned int iV[16]; iV[0] = m_pMesh->TriangleIndex( t, 0 ); iV[1] = m_pMesh->TriangleIndex( t, 1 ); for ( int f = 0; f < s-2; f++ ) iV[f+2] = m_pMesh->TriangleIndex( t+f, 2 ); unsigned int iA = m_pMesh->TriangleIndex( iFaceIndex, (iSide+1)%3 ), iB = m_pMesh->TriangleIndex( iFaceIndex, (iSide+2)%3 ); // Search which side of the adjacent poly we are. unsigned int as = 0; while ( iB != iV[as] && iA != iV[(as+1)%s] ) { as++; MB_ONBUG( as == s ) return 0xffffffff; }; // Quads are special case, since they are a single ptex face. In this case the iSegment parameter is ignored. if ( s == 4 ) { iEdge = as; return m_aFaceID[t]; }; // For other polygons, the edge depends on the iSegment value. if ( iSegment ) { as = (as+1)%s; iEdge = 3; } else iEdge = 0; return m_aFaceID[t]+as; }; // This function calculates the adjacency info for an edge of a ptex face. If the edge is an internal edge of a polygon, the // function calculates the values directly. In other cases it calls the function AdjacentFaceForTriangle. This function is only // needed when the target mesh is not a full quad mesh. unsigned int PtexLayout::AdjacentFace( unsigned int iFaceID, unsigned int iSide, unsigned int &iEdge ) const { // Get the index of the triangle which represents the polygon of the ptex face. This must be a real triangle (i.e. the first triangle of // that polygon). unsigned int iFaceIndex = m_aTriangle[iFaceID]; MB_ONBUG( m_pMesh->IsFakeTriangle( iFaceIndex ) ) return 0xffffffff; // Calculate the number of sides of that polygon. unsigned int e = iFaceIndex+1; while ( e < m_pMesh->FaceCount() && m_pMesh->IsFakeTriangle( e ) ) e++; unsigned int s = e-iFaceIndex+2; // If the polygon is a quad, there are no internal edges (since all quads are represented as a single ptex face), // so we call AdjacentFaceForTriangle. If the other side of the edge contains multiple ptex faces (i.e. it belongs to // a polygon which is not a quad), ptex expects the first subface encountered in a counter-clockwise (i.e. edgeid // order) traversal of the face as the adjacent face, so we are looking for the second segment of the edge (iSegment=1) if ( s == 4 ) { unsigned int b[4] = { 0, 0, 1, 1 }; unsigned int s[4] = { 2, 0, 0, 1 }; return AdjacentFaceForTriangle( iFaceIndex+b[iSide], s[iSide], 1, iEdge ); }; // When the current polygon is not a quad, then the edges 0 and 3 are external edges (see http://http://ptex.us/adjdata.html // for more detail), so in that case we call AdjacentFaceForTriangle with the proper data. if ( iSide == 0 || iSide == 3 ) { // First we calculate which edge of the polygon this ptex edge belongs to. unsigned int l = iFaceID-m_aFaceID[iFaceIndex]; MB_ONBUG( l >= 16 ) return 0xffffffff; if ( iSide ) l = (l+s-1)%s; // If it is the first edge, then that is the last edge of the first triangle belongs to the polygon. if ( l == 0 ) return AdjacentFaceForTriangle( iFaceIndex, 2, iSide ? 0 : 1, iEdge ); // If it is the last edge, then it is the middle edge of the last triangle. if ( l == s-1 ) return AdjacentFaceForTriangle( iFaceIndex+s-3, 1, iSide ? 0 : 1, iEdge ); // In other cases, it is the first edge of the corresponding triangle. return AdjacentFaceForTriangle( iFaceIndex+l-1, 0, iSide ? 0 : 1, iEdge ); }; // When it is an internal edge, the adjacent ptex face will be another subface of the same polygon. if ( iSide == 2 ) iEdge = 1; else iEdge = 2; return m_aFaceID[iFaceIndex]+((iFaceID-m_aFaceID[iFaceIndex]+(iSide==2 ? -1 : 1)+s)%s); };