PtexImporter/PtexImporter.cpp

//**************************************************************************/
// Copyright (c) 2011, 2012 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: PTEX texture importer
// CREATED: January 2011
//**************************************************************************/

#include "PtexImporter.h"      
#include "PtexPaintLayerImporter.h"

IMPLEMENT_CLASS( PtexImporter, Importer, "pteximporter" );

//------------------------------------------------------------------------------
void PtexImporter::Import( const QString &sFileName, Scene::LoadData & )
{
    mudbox::Mesh *pMesh = CreateMeshFromPtex( sFileName );

    if (!pMesh)
        return;

    PtexPaintLayerImporter *pI = CreateInstance<PtexPaintLayerImporter>();

    if (pI) {
        pI->SetPtexImporter(this);
        pI->Prepare( sFileName, pMesh, false, true );
        pI->Import( sFileName, 0, pMesh, 0 );
    }

    if (m_FaceMap != 0) {
        delete [] m_FaceMap;
        m_FaceMap = 0;
    }

    if (m_ReverseFaceMap != 0) {
        delete [] m_ReverseFaceMap;
        m_ReverseFaceMap = 0;
    }
}


//------------------------------------------------------------------------------
Mesh *PtexImporter::CreateMeshFromPtex( const QString &sFileName, 
                                        bool makeMesh, bool silent )
{
    if (m_FaceMap != 0) {
        delete [] m_FaceMap;
        m_FaceMap = 0;
    }

    if (m_ReverseFaceMap != 0) {
        delete [] m_ReverseFaceMap;
        m_ReverseFaceMap = 0;
    }

    Ptex::String   s;
    QByteArray     a = QFile::encodeName( sFileName );
    PtexTexture  *pT = PtexTexture::open( a.constData(), s );

    if (!pT) {
        Kernel()->Interface()->HUDMessageShow( tr("Unable to open PTEX file. Mudbox cannot import it."),  
                                               mudbox::Interface::HUDmsgFade );
        Kernel()->Log( QString( "Unable to open PTEX file. Mudbox cannot import it.\n" ));
        return false;
    }

    if (pT->meshType() != Ptex::mt_quad) {
        Kernel()->Interface()->HUDMessageShow( tr("This PTEX file is not quadrangular. "
                                                  "Mudbox cannot import it."),  
                                               mudbox::Interface::HUDmsgFade );
        Kernel()->Log( QString( "This PTEX file is not quadrangular. Mudbox cannot import it.\n" ));
        pT->release();
        return false;
    }

    PtexMetaData *pM = pT->getMetaData();
    if ( !pM ) 
    {
        Kernel()->Log(QString("This PTEX file does not contain mesh information. Mudbox cannot import it."));  
        if (!silent) {
            Kernel()->Interface()->HUDMessageShow( tr("This PTEX file does not contain mesh information. "
                                                      "Mudbox cannot import it."),  
                                                   mudbox::Interface::HUDmsgFade );
        }
        return NULL;
    }

    m_iFaceCount = 0;
    const int *aFaceSizes = NULL;
    pM->getValue( NTR("PtexFaceVertCounts"), aFaceSizes, m_iFaceCount );
    if ( aFaceSizes == NULL )
    {
        Kernel()->Log(NTR("This PTEX file does not contain mesh information. "
                          "Mudbox cannot import it. (2)"));
        if (!silent) {
            Kernel()->Interface()->HUDMessageShow( tr("This PTEX file does not contain mesh information. "
                                                      "Mudbox cannot import it."),  
                                                   mudbox::Interface::HUDmsgFade );
        }
        return NULL;
    }

    int pTexSubFaceCount = pT->numFaces();

    memset(m_hist, 0, sizeof(m_hist));                                
    m_allQuads    = true;
    m_maxFaceSize = 0;
    m_minFaceSize = INT_MAX;
    m_iTotalTesselatedFaceCount = 0;

    for ( int i = 0; i < m_iFaceCount; i++ ) {
        int faceSize = aFaceSizes[i];
        if (faceSize < MAX_EDGE_COUNT) { 
            ++m_hist[faceSize];
        } else {
            Kernel()->Log( NTRQ("Mudbox can only import PTEX files that "
                                "contain faces with less than %1 sides").arg(MAX_EDGE_COUNT));
            if (!silent) {
                Kernel()->Interface()->HUDMessageShow( tr("Mudbox can only import PTEX files "
                                                          "that contain faces with less than %1 sides")
                                                       .arg(MAX_EDGE_COUNT),  
                                                       mudbox::Interface::HUDmsgFade );
            }
            return NULL;
        }

        if (faceSize < 3) {
            Kernel()->Log(NTR("Mudbox can only import PTEX files that contain faces with at least 3 sides"));
            if (!silent) {
                Kernel()->Interface()->HUDMessageShow( tr("Mudbox can only import PTEX files that "
                                                          "contain faces with at least 3 sides"),  
                                                       mudbox::Interface::HUDmsgFade );
            }
            return NULL;
        }

        if (faceSize != 4)            m_allQuads    = false;
        if (faceSize > m_maxFaceSize) m_maxFaceSize = faceSize;
        if (faceSize < m_minFaceSize) m_minFaceSize = faceSize;
    }


    if (m_allQuads) {
        m_iTotalTesselatedFaceCount = m_iFaceCount;
    } else {
        for (int i = 3; i < m_maxFaceSize+1; ++i) {
            m_iTotalTesselatedFaceCount += (m_hist[i] * (i - 2)); 
        }
    }


    Kernel()->Log("\nPTex Import VertexCount Histogram;\n");

    for (int i = 0; i < MAX_EDGE_COUNT; ++i) {
        if (m_hist[i] != 0) {
            Kernel()->Log(QString("    Vertex Count %1, number of faces %2\n").arg(i).arg(m_hist[i]));
        }
    }
    Kernel()->Log("\n");

    // Read vertex positions
    int iVertexDataSize = 0;
    const float *aVertexPositions = NULL;
    pM->getValue( NTR("PtexVertPositions"), aVertexPositions, iVertexDataSize );

    if ( iVertexDataSize%3 || aVertexPositions == NULL ) {
        Kernel()->Log(QString("Ptex: Vertex DataSize %1, \n").arg(iVertexDataSize));
        return NULL;
    }


    int           iFaceDataSize = 0;
    const int    *aFaceData     = NULL;
    mudbox::Mesh *pMesh         = NULL;

    // Read face indices
    pM->getValue( NTR("PtexFaceVertIndices"), aFaceData, iFaceDataSize );

    if ( aFaceData == NULL )
        return NULL;

    Kernel()->Log(QString("Ptex: Max Face Size %5, VertexCount %3, FaceCount %1, "
                          "TesselatedFaceCount = %4, FakeTriangleCount %2\n\n").
                          arg(m_iFaceCount).arg(m_iTotalTesselatedFaceCount - m_iFaceCount).
                          arg(iVertexDataSize/3).
                          arg(m_iTotalTesselatedFaceCount).arg(m_maxFaceSize));

                                           
    // Create the mesh, and fill the vertex position and face index data.
    if (makeMesh) {
        if (m_allQuads) {
            pMesh = Kernel()->Scene()->CreateMesh( Topology::typeQuadric );
        } else {
            pMesh = Kernel()->Scene()->CreateMesh( Topology::typeTriangular );
        }
    }
    
    // if the mesh is not all quads, Mudbox wants it tessellated into triangles.
    // each n-gon becomes n-2 triangles. The first triangle of an n-gon is "real"
    // the rest are "fake"

    MB_ASSERT(m_iTotalTesselatedFaceCount >= m_iFaceCount);

    if (pMesh) {
        pMesh->SetFaceCount( m_iTotalTesselatedFaceCount );

        pMesh->SetVertexCount( iVertexDataSize/3 );
        for ( int i = 0; i < iVertexDataSize/3; i++ ) {
                pMesh->SetVertexPosition( i, Vector( aVertexPositions[i*3+0], 
                                                     aVertexPositions[i*3+1], 
                                                     aVertexPositions[i*3+2] ) );
        }

        if (!m_allQuads) {
            pMesh->SetFakeTriangleCount(m_iTotalTesselatedFaceCount - m_iFaceCount);
        }
        pMesh->SetName(sFileName);
    }

    int curTri       = 0;                                           
    int curVertex    = 0;
    int curSubFaceID = 0;

    m_FaceMap        = new FaceMapEntry[m_iFaceCount];
    m_ReverseFaceMap = new int[m_iTotalTesselatedFaceCount];
    memset(m_ReverseFaceMap, 0, m_iTotalTesselatedFaceCount * sizeof(int));
                                                   
    for ( int i = 0; i < m_iFaceCount; i++ )
    {
        if (m_allQuads) {

            if (pMesh) {
                pMesh->SetQuadIndex( i, 0, aFaceData[i * 4 + 0] );
                pMesh->SetQuadIndex( i, 1, aFaceData[i * 4 + 1] );
                pMesh->SetQuadIndex( i, 2, aFaceData[i * 4 + 2] );          
                pMesh->SetQuadIndex( i, 3, aFaceData[i * 4 + 3] );
            }

            m_FaceMap[i].m_NumEdges            = 4;
            m_FaceMap[i].m_NumTessellatedFaces = 1;
            m_FaceMap[i].m_MBFaceID            = i;
            m_FaceMap[i].m_PTexSubfaceID       = curSubFaceID;
            m_ReverseFaceMap[i]                = i;

            curSubFaceID                      += 1; // quads are 1 face.

        } else {
            const int  faceSize      = aFaceSizes[i];
            int        subTri        = 0;

            m_FaceMap[i].m_NumEdges  = faceSize;
            m_FaceMap[i].m_MBFaceID  = curTri;
            m_ReverseFaceMap[curTri] = i;

            if (pMesh) {
                pMesh->SetTriangleIndex( curTri, 0, aFaceData[curVertex + 0] );
                pMesh->SetTriangleIndex( curTri, 1, aFaceData[curVertex + 1] );
                pMesh->SetTriangleIndex( curTri, 2, aFaceData[curVertex + 2] );
            }

            ++curTri; ++subTri;
            for (int j = 3; j < faceSize; ++j) {
                m_ReverseFaceMap[curTri] = i;
                if (pMesh) {
                    pMesh->SetTriangleIndex( curTri, 0, aFaceData[curVertex + 0    ] );
                    pMesh->SetTriangleIndex( curTri, 1, aFaceData[curVertex + j - 1] );
                    pMesh->SetTriangleIndex( curTri, 2, aFaceData[curVertex + j    ] );
                    pMesh->SetFakeTriangle(curTri, true);
                }
                ++curTri; ++subTri;
            }

            curVertex += faceSize;

            m_FaceMap[i].m_NumTessellatedFaces = subTri;
            m_FaceMap[i].m_PTexSubfaceID       = curSubFaceID;

            // if it's a quad, it's one face, if it's an n-gon, it's n quadrangular subfaces.
            // (of course, we've tessellated that into n-2 triangles) 
            curSubFaceID += (faceSize == 4) ? 1 : faceSize;
        }
    }

    m_iSubFaceCount = curSubFaceID;

    Kernel()->Log(QString("Ptex: Computed subface count = %1, actual in file = %2\n").
                          arg(m_iSubFaceCount).arg(pTexSubFaceCount));

    if (pMesh) {
        pMesh->RecalculateAdjacency(false);

        AddMesh( pMesh );

        Material *pMat = CreateInstance<Material>();
        pMesh->Geometry()->SetMaterial( pMat );
        Kernel()->Scene()->AddChild( pMat );
    }

    pT->release();                              

    return pMesh;
}


//------------------------------------------------------------------------------
void  PtexImporter::BuildMapsFromBaseMesh(Mesh *pMesh)
{
    if (!pMesh) 
        return;

    int polyCount = pMesh->FaceCount();

    if ((pMesh->Type() == Topology::typeQuadric)) {
        // handle the simple 1:1 case of an all quad mesh;
        m_allQuads       = true;
        m_maxFaceSize    = 4;
        m_minFaceSize    = 4;
        m_hist[4]        = polyCount;
        m_iFaceCount     = m_iTotalTesselatedFaceCount = m_iSubFaceCount = polyCount;
        m_FaceMap        = new FaceMapEntry[m_iFaceCount];
        m_ReverseFaceMap = new int[m_iTotalTesselatedFaceCount];
        for (int i = 0; i < polyCount; ++i) {
            m_FaceMap[i].m_NumEdges            = 4;
            m_FaceMap[i].m_MBFaceID            = i;        
            m_FaceMap[i].m_PTexSubfaceID       = i;
            m_FaceMap[i].m_NumTessellatedFaces = 1;
            m_ReverseFaceMap[i]                = i; 
        }   

    } else {
        m_allQuads     = false;
        m_maxFaceSize  = 3;
        m_minFaceSize  = 3;

        // need to allocate the arrays first, so we need 2 passes, one to 
        // collect stats and determine how large the maps are, and one to 
        // fill in the maps
        int poly = 0, subface = 0;
        for (int i = 0; i < polyCount; ++i) 
        {
            int edgeCount = 3;
            while (pMesh->IsFakeTriangle(i+1))  // safe as it returns false if i+1 is > polyCount
            {  
                ++i; ++edgeCount;
            }

            ++poly;
            subface  += (edgeCount == 4) ? 1 : edgeCount;

            ++m_hist[edgeCount];
            if (m_minFaceSize > edgeCount) m_minFaceSize = edgeCount;
            if (m_maxFaceSize < edgeCount) m_maxFaceSize = edgeCount;
        }


        m_iFaceCount                = poly;
        m_iTotalTesselatedFaceCount = polyCount;
        m_iSubFaceCount             = subface;

        m_FaceMap         = new FaceMapEntry[m_iFaceCount];
        m_ReverseFaceMap  = new int[m_iTotalTesselatedFaceCount];

        // walk through the triangles looking at the real/fake ones and construct
        // the mappings from poly id to face id and subface id. And back from 
        // subface to poly id. 
        poly = 0, subface = 0;                          
        for (int i = 0; i < polyCount; ++i) 
        {
            const int ff = i;

            m_ReverseFaceMap[i]  = poly;
            int edgeCount = 3;
            while (pMesh->IsFakeTriangle(i+1))  // safe as it returns false if i+1 is > polyCount
            {  
                ++i; ++edgeCount;
                m_ReverseFaceMap[i]  = poly;
            }

            m_FaceMap[poly].m_NumEdges            = edgeCount;
            m_FaceMap[poly].m_MBFaceID            = ff;
            m_FaceMap[poly].m_PTexSubfaceID       = subface;
            m_FaceMap[poly].m_NumTessellatedFaces = edgeCount - 2;

            ++poly;
            subface  += (edgeCount == 4) ? 1 : edgeCount;
        }
    }
}

//------------------------------------------------------------------------------