- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files
- Directories
- Examples

# PtexExtractor/PtexUtilizer.cpp

    //**************************************************************************/
    // 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 "PtexUtilizer.h"
    #include <omp.h>

    Preferences::String g_sPtexPath(
        NTR("Map Extraction Ptex Folder"),
        NTR("Paths"),
        QObject::tr("Map Extraction Ptex Folder"),
        QObject::tr("Paths"),
        Kernel()->Preferences()->m_sTexturePath.Value(),
        false );

    // RTTI macro, needed for each class.
    IMPLEMENT_SCLASS( PtexUtilizer, Utilizer, "ptexutilizer", 1 );

    PtexUtilizer::PtexUtilizer( void ) :
        m_pWriter( NULL ),
        m_iChannelCount( 3 ),
        m_bIncludeMeshData( this, "includemeshdata" ),
        m_eFormat( this, "format" ),
        m_iPtexFaceCount( 0 ),
        m_sFileName( this, NTRQ("filename"), false ),
        m_sFilePath( this, "filepath" )
    {
        m_bIncludeMeshData.SetName( QObject::tr( "Include Mesh Data:" ) );
        m_bIncludeMeshData.SetToolTip( QObject::tr( "Store the mesh data with the ptex file.  This is useful if you are using a ptex viewer program to preview the ptex file." ) );
        m_bIncludeMeshData = true;
        m_eFormat.SetName( QObject::tr( "Data Format:" ) );
        m_eFormat.SetToolTip( QObject::tr( "Format of the data in the file. This affects precision and file size." ) );
        m_eFormat.AddItem( QObject::tr( "8 bit integer" ) );
        m_eFormat.AddItem( QObject::tr( "16 bit integer" ) );
        m_eFormat.AddItem( QObject::tr( "16 bit float" ) );
        m_eFormat.AddItem( QObject::tr( "32 bit float" ) );
        m_eFormat = 3;
        m_sFileName.m_sFilter = QObject::tr( "Ptex files (*.ptx)" );
        m_sFileName.SetInstantEdit( true );
        m_sFileName.SetName( QObject::tr( "File Name:" ) );
        m_sFilePath.Connect( *g_sPtexPath.m_pAttribute );
        m_sFilePath.SetVisible( false );
    };

    void PtexUtilizer::OnNodeEvent( const Attribute &a, NodeEventType t )
    {
        if ( a == m_sFilePath && t == etValueChanged )
            m_sFileName.m_sPath = m_sFilePath;
        if ( a == m_sFileName && t == etValueChanged )
            m_pComponent->m_pMapExtractor->ValidityChanged();
        if ( a == m_sFileName && t == etEditingFinished )
            g_sPtexPath.m_pAttribute->SetValue( m_sFileName.m_sPath );
    };

    #define WRITEFACE( type, multiplier ) \
    { \
        type *p = new type[d.m_aData.size()]; \
        for ( int i = 0; i < d.m_aData.size() && p; i++ ) \
            p[i] = (type)(multiplier*d.m_aData[i]); \
        m_pWriter->writeFace( iFaceID, sInfo, p ); \
        delete [] p; \
    };

    // Main function. This is called once for each reference point with the corresponding data. The function must store the data, and later write it to the ptex file.
    // This function must be thread safe, so we cannot use a single FaceData, because the data might arrive in a changed order.
    void PtexUtilizer::StoreData( const Data &cData, const TargetLocation &cTarget, bool /*bHit*/ )
    {
        // First we obtain some important data from the reference point. These were added to the structure by the PtexLayout class. See PtexLayout::ProcessSurface.
        int u = cTarget.m_aLayoutData[PtexLayout::dataURes]&0xffffff, v = cTarget.m_aLayoutData[PtexLayout::dataVRes]&0xffffff;
        int ures = cTarget.m_aLayoutData[PtexLayout::dataURes]>>24, vres = cTarget.m_aLayoutData[PtexLayout::dataVRes]>>24;
        unsigned int iFaceID = cTarget.m_aLayoutData[PtexLayout::dataFaceID];
        bool bSubface = iFaceID & 0x80000000;
        iFaceID &= 0x7fffffff;
        int w = 1 << ures, h = 1 << vres;

        // If the m_aFaces array is empty, we initialize it with NULL pointers for each face. NULL pointer means that the processing of that face is not yet begun,
        // or it is already finished, so we don't store any data for that face in the memory.
        if ( m_aFaces.size() == 0 )
        {
    #pragma omp critical (initfaces)
            {
                if ( m_aFaces.size() == 0 )
                {
                    // Calculate the number of ptex faces needed for the mesh.
                    const Mesh *pMesh = cTarget.Mesh();
                    if ( pMesh->Type() == Mesh::typeQuadric )
                        m_iPtexFaceCount = pMesh->FaceCount();
                    else
                    {
                        // For each polygon, first calculate the number of sides.
                        m_iPtexFaceCount = 0;
                        unsigned int i = 0;
                        while ( i < pMesh->FaceCount() )
                        {
                            int s = 3;
                            i++;
                            while ( i < pMesh->FaceCount() && pMesh->IsFakeTriangle( i ) )
                            {
                                i++; s++;
                            };

                            // If it is a quad (i.e. has 4 sides) then it will be represented with a single ptex face, otherwise each vertex
                            // will have its own ptex face.
                            if ( s == 4 )
                                m_iPtexFaceCount++;
                            else
                                m_iPtexFaceCount += s;
                        };
                    };

                    m_iFacesProcessed = 0;
                    m_aFaces.fill( NULL, m_iPtexFaceCount );
                };
            };
        };

        // If the given face has no data yet, then we allocate a structure.
        if ( !m_aFaces[iFaceID] )
        {
    #pragma omp critical (insertface)
            {
                if ( !m_aFaces[iFaceID] )
                    m_aFaces[iFaceID] = new FaceData( w, h, m_iChannelCount );
            };
        };

        // We simply store the data in the structure which belongs to the face.
        FaceData &d = *m_aFaces[iFaceID];
        for ( int c = 0; c < m_iChannelCount; c++ )
            d.m_aData[c+(u+v*w)*m_iChannelCount] = ((const float *)(cData.As_Color()))[c];

    #pragma omp atomic
        d.m_iSamplesLeft--;

        // Chech the number of stored reference points for the face. If we exceed the total number of reference points, we can write the data related to the face to the ptex file
        // and delete the memory used.
        if ( d.m_iSamplesLeft == 0 )
        {
    #pragma omp critical (flushface)
            {
                if ( d.m_iSamplesLeft == 0 )
                {
                    // If no writer object is created yet, then we create one for this mesh.
                    if ( !m_pWriter )
                        OpenFile( cTarget.Mesh() );

                    MB_ASSERT( m_pMesh == cTarget.Mesh() );

                    // Create and fill a structure for the face.
                    Ptex::FaceInfo sInfo;
                    if ( bSubface )
                        sInfo.flags |= Ptex::FaceInfo::flag_subface;
                    sInfo.res = Ptex::Res( ures, vres );

                    unsigned int af[4], ae[4];

                    // Store the adjacency information for the face. This is used by the ptex library to do filtering at face edges.
                    if ( m_pMesh->Type() == Mesh::typeQuadric )
                    {
                        for ( int c = 0; c < 4; c++ )
                        {
                            unsigned int a = cTarget.m_pMesh->QuadAdjacency( cTarget.m_iFaceIndex, c );
                            // When the returned value is 0xffffffff it means there is no adjacent face in that direction, so it is an open edge.
                            if ( a == 0xffffffff )
                            {
                                // When there is no adjacent face, the adjacent face index must be set to -1 (edge index is ignored)
                                af[c] = 0xffffffff;
                                ae[c] = 0;
                            }
                            else
                            {
                                af[c] = a/4;
                                ae[c] = a%4;
                            };
                        };
                    }
                    else
                    {
                        for ( unsigned int c = 0; c < 4; c++ )
                            af[c] = m_pLayout->AdjacentFace( iFaceID, c, ae[c] );
                    };
                    sInfo.setadjfaces( af[0], af[1], af[2], af[3] );
                    sInfo.setadjedges( ae[0], ae[1], ae[2], ae[3] );

                    // When all the data is ready, we pass the FaceInfo structure along with the data collected to the ptex library.
                    void *pData;
                    switch ( m_eFormat )
                    {
                        case eFormat8bitInteger:
                            WRITEFACE( unsigned char, 255 );
                            break;
                        case eFormat16bitInteger:
                            WRITEFACE( unsigned short, 65536 );
                            break;
                        case eFormat16bitFloat:
                            WRITEFACE( half_, 1 );
                            break;
                        case eFormat32bitFloat:
                            WRITEFACE( float, 1 );
                    };

                    // Since the face won't get any further data, we can remove the related structure from the memory.
                    delete m_aFaces[iFaceID];
                    m_aFaces[iFaceID] = NULL;
                    m_iFacesProcessed++;
                };
            };
        };
    };

    // This function is called by the map extraction framework when an event occurs related to the reference points.
    void PtexUtilizer::OnPhaseEvent( PhaseEventType t )
    {
        // The only one event what we are interested in is the end of a section. When that happens, we close the current ptex
        // file since all the faces are processed for the current mesh.
        if ( t == eventSectionEnd )
        {
            for ( unsigned int i = 0; i < m_iPtexFaceCount; i++ )
                MB_ASSERT( m_aFaces[i] == 0 );
            m_aFaces.clear();
            if ( m_pWriter )
                CloseFile();
        };
    };

    // Create a new PtexWriter object for the given mesh, which will be used to write data to the ptex file.
    void PtexUtilizer::OpenFile( const Mesh *pMesh )
    {
        MB_ASSERT( !m_pWriter );
        // Since each mesh uses its own section of reference points, different meshes will never be mixed. So we can safely use a single pointer
        // for the current mesh.
        m_pMesh = pMesh;

        // Create and open the ptex file using the ptex library.
        Ptex::String sError;
        QByteArray qbaFileMask = QFile::encodeName(m_sFileName.Value());
        Ptex::DataType aFormats[4] = { Ptex::dt_uint8, Ptex::dt_uint16, Ptex::dt_half, Ptex::dt_float };
        m_pWriter = PtexWriter::open( qbaFileMask.constData(), Ptex::mt_quad, aFormats[m_eFormat], m_iChannelCount, -1, m_iPtexFaceCount, sError );
        if ( !m_pWriter )
            MB_ERROR( sError.c_str() );
    };

    // This function writes the topology data to the ptex file (if needed) and closes the file.
    void PtexUtilizer::CloseFile( void )
    {
        MB_ONBUG( !m_pWriter )
            return;

        // We only write topology data to the file if the m_bIncludeData attribute is set to true. It is recommended to write the topology data to the file since some
        // tools can use the data when processing the file, but it is optional. The default is on.
        if ( m_bIncludeMeshData )
            WriteMeshData( m_pWriter, m_pMesh );

        // Close the file, and release the object.
        Ptex::String sError;
        bool bOk = m_pWriter->close( sError );
        m_pWriter->release();
        if ( !bOk )
            MB_ERROR( sError.c_str() );

        m_pWriter = NULL;
    };

    void PtexUtilizer::WriteMeshData( PtexWriter *pWriter, const Mesh *pMesh )
    {
        // First write the position of the vertices into the file as metadata.
        QVector<float> aVertexPositions( pMesh->VertexCount()*3 );
        for ( unsigned int c = 0; c < pMesh->VertexCount(); c++ )
        {
            Vector v = pMesh->VertexPosition( c );
            aVertexPositions[c*3+0] = v.x;
            aVertexPositions[c*3+1] = v.y;
            aVertexPositions[c*3+2] = v.z;
        };
        pWriter->writeMeta( "PtexVertPositions", aVertexPositions.data(), aVertexPositions.size() );

        QVector<int> aFaceVertexIndices;
        QVector<int> aFaceVertexCounts;
        if ( pMesh->Type() == Mesh::typeQuadric )
        {
            // Write the vertex indices for the faces.
            aFaceVertexIndices.resize( pMesh->FaceCount()*4 );
            for ( unsigned int f = 0; f < pMesh->FaceCount(); f++ )
                for ( int c = 0; c < 4; c++ )
                    aFaceVertexIndices[f*4+c] = pMesh->QuadIndex( f, c );

            // Write the number of vertices for the faces into the file. Since we are only processing quad meshes, this array is full of 4.
            aFaceVertexCounts.fill( 4, pMesh->FaceCount() );
        }
        else
        {
            // Count the number of polygons.
            unsigned int iPolygonCount = 0;
            for ( unsigned int f = 0; f < pMesh->FaceCount(); f++ )
                if ( !pMesh->IsFakeTriangle( f ) )
                    iPolygonCount++;
            aFaceVertexIndices.resize( pMesh->FaceCount()+2*(iPolygonCount) );
            aFaceVertexCounts.resize( iPolygonCount );

            unsigned int iVertexIndex = 0, iPolygonIndex = 0;
            for ( unsigned int f = 0, p = 0; f < pMesh->FaceCount(); f++ )
            {
                unsigned int iSideCount = 3;
                aFaceVertexIndices[iVertexIndex++] = pMesh->TriangleIndex( f, 0 );
                aFaceVertexIndices[iVertexIndex++] = pMesh->TriangleIndex( f, 1 );
                aFaceVertexIndices[iVertexIndex++] = pMesh->TriangleIndex( f, 2 );
                while ( f < pMesh->FaceCount()-1 && pMesh->IsFakeTriangle( f+1 ) )
                {
                    f++;
                    iSideCount++;
                    aFaceVertexIndices[iVertexIndex++] = pMesh->TriangleIndex( f, 2 );
                };
                aFaceVertexCounts[iPolygonIndex++] = iSideCount;
            };
            MB_ASSERT( (int)iVertexIndex == aFaceVertexIndices.size() );
            MB_ASSERT( (int)iPolygonIndex == aFaceVertexCounts.size() );
        };
        pWriter->writeMeta( "PtexFaceVertIndices", aFaceVertexIndices.data(), aFaceVertexIndices.size() );
        pWriter->writeMeta( "PtexFaceVertCounts", aFaceVertexCounts.data(), aFaceVertexCounts.size() );
    };

    // This fuction must serialize the state of the object. Since there is only a single attribute, this only serializes that one.
    void PtexUtilizer::Serialize( Stream &s )
    {
        if ( s.IsNewerThan( 0, this ) )
            s == m_bIncludeMeshData;
        Utilizer::Serialize( s );
    };

    // This function initializes an instance of the FaceData structure which is used to hold data about the face.
    PtexUtilizer::FaceData::FaceData( unsigned int iWidth, unsigned int iHeight, unsigned int iChannelCount )
    {
        m_iWidth = iWidth;
        m_iHeight = iHeight;
        m_iSamplesLeft = m_iWidth*m_iHeight;
        // Allocate the array where we store the data for the face.
        m_aData.resize( m_iSamplesLeft*iChannelCount );
    };

    void PtexUtilizer::Initialize( Layout *pLayout, Component *pComponent )
    {
        struct Component::DataInfo i = m_pComponent->DataInfo();
        if ( i.m_ePrecision == Component::ePrecFloatOnly )
            m_eFormat = m_eFormat+2;
        m_iChannelCount = pComponent->DataInfo().m_iChannelCount;

        m_pLayout = dynamic_cast<const PtexLayout *>( pLayout );
        Utilizer::Initialize( pLayout, pComponent );
    };

    // This function is only used to disable integer formats when they are not allowed.
    QWidget *PtexUtilizer::UserInterface( void )
    {
        struct Component::DataInfo i = m_pComponent->DataInfo();
        if ( i.m_ePrecision == Component::ePrecFloatOnly )
        {
            m_eFormat.Clear();
            m_eFormat.AddItem( QObject::tr( "16 bit float" ) );
            m_eFormat.AddItem( QObject::tr( "32 bit float" ) );
            m_eFormat = 1;
        };
        return Utilizer::UserInterface();
    };