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

# 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;
            }
        }
    }

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