PtexImporter/PtexImporter.cpp


//**************************************************************************/

// Copyright (c) 2011 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: January 2011

//**************************************************************************/



#include "PtexImporter.h"



IMPLEMENT_CLASS( PtexImporter, Importer, "pteximporter" );



void PtexImporter::Import( const QString &sFileName, Scene::LoadData & )

{

    // Step 1: Read the topology data, and create a mesh using that info.

    QByteArray a = QFile::encodeName( sFileName );

    Ptex::String s;

    PtexTexture *pT = PtexTexture::open( a.constData(), s );

    PtexMetaData *pM = pT->getMetaData();

    if ( !pM )

    {

        Kernel()->HUDMessageShow( tr("This PTEX file does not contain mesh information. Mudbox cannot import it."),  Kernel::HUDmsgFade );

        return;

    };

    Mesh *pMesh = CreateMeshFromMetaData( pM );

    if ( pMesh == NULL )

        return;

    pMesh->RecalculateAdjacency();

    AddMesh( pMesh );



    // Step 2: Auto generate UV.

    UVGeneratorNode *pG = pMesh->ChildByClass<UVGeneratorNode>( true );

    for ( unsigned int f = 0; f < pMesh->FaceCount(); f++ )

    {

        const Ptex::FaceInfo &i = pT->getFaceInfo( f );

        pG->SetFaceSize( f, 1<<i.res.ulog2, 1<<i.res.vlog2 );

    };

    pG->GenerateUVs( false );



    // Step 3: Copy the color data to the textures.

    Material *pMat = CreateInstance<Material>();

    pMesh->Geometry()->SetMaterial( pMat );

    Kernel()->ProgressStart( tr("Reading Ptex Texture..."), pMesh->FaceCount() );

    for ( unsigned int j = 0; j < pMat->TextureCount(); j++ )

    {

        TexturePool *pTP = pMat->Texture( j );

        if ( pTP->Name() == NTR("Diffuse") )

        {

            LayerContainer *pC = dynamic_cast<LayerContainer *>( pTP );

            if ( pC )

            {

                Layer *pL = pC->CreateLayer();

                TexturePool *pTP = dynamic_cast<TexturePool *>( pL );

                MB_SAFELY( pTP )

                {

                    QFileInfo i( sFileName );

                    pTP->Node::SetName( i.baseName() );

                    LoadPtexTexture( pT, pTP, pMesh, pG );

                    // Step 4: Fill the UV bleeding area.

                    EdgeBleeding::FillBleedingAreas( pMesh, pTP );

                };

            };

            pTP->SetRenderMode( TexturePool::renderModeTexture );

        };

    };

    Kernel()->ProgressEnd();

};



// This function creates a mesh using the metadata found in the ptex file. Note that not all ptex files contain the needed data.

// This function only works for quad meshes.

Mesh *PtexImporter::CreateMeshFromMetaData( PtexMetaData *pM ) const

{

    // Check the number of sides for the faces in the mesh. They all should be four, since we only support quad meshes.

    int iFaceCount = 0;

    const int *aFaceSizes = NULL;

    pM->getValue( NTR("PtexFaceVertCounts"), aFaceSizes, iFaceCount );

    if ( aFaceSizes == NULL )

        return NULL;

    for ( int i = 0; i < iFaceCount; i++ )

        if ( aFaceSizes[i] != 4 )

        {

            Kernel()->HUDMessageShow( tr("Mudbox can only import PTEX files that contain quad-only meshes."),  Kernel::HUDmsgFade );

            return NULL;

        };



    // Read vertex positions

    int iVertexDataSize = 0;

    const float *aVertexPositions = NULL;

    pM->getValue( NTR("PtexVertPositions"), aVertexPositions, iVertexDataSize );



    if ( iVertexDataSize%3 || aVertexPositions == NULL )

        return NULL;



    // Read face indices

    int iFaceDataSize = 0;

    const int *aFaceData = NULL;

    pM->getValue( NTR("PtexFaceVertIndices"), aFaceData, iFaceDataSize );

    if ( iFaceDataSize != iFaceCount*4 || aFaceData == NULL )

        return NULL;



    // Create the mesh, and fill the vertex position and face index data.

    Mesh *pMesh = Kernel()->CreateMesh( Topology::typeQuadric );

    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] ) );

    pMesh->SetFaceCount( iFaceCount );

    for ( int i = 0; i < iFaceCount; i++ )

    {

        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] );

    };

    return pMesh;

};



// Load the surface data from the ptex file to an existing paint layer

void PtexImporter::LoadPtexTexture( PtexTexture *pT, TexturePool *pTP, Mesh *pMesh, UVGeneratorNode *pG )

{

    const unsigned int iTileSize = pG->UVTileSize();



    // Find out how many tiles we have to process.

    unsigned int iUS = 0, iUE = 0, iVS = 0, iVE = 0;

    for ( unsigned int f = 0; f < pMesh->FaceCount(); f++ )

    {

        UVGeneratorNode::DimData2 d = pG->FaceUVArea( f );

        iUS = Min( iUS, d[0] );

        iUE = Max( iUE, d[0]+1 );

        iVS = Min( iVS, d[1] );

        iVE = Max( iVE, d[1]+1 );

    };



    // Loop through these tiles.

    for ( unsigned int u = iUS; u < iUE; u++ )

        for ( unsigned int v = iVS; v < iVE; v++ )

        {

            // Create an image with the same format as the ptex file to avoid quality loss.

            Instance<Image> i;

            enum Image::Format aPtexFormats[4] = { Image::e8integer, Image::e16integer, Image::e16float, Image::e32float };

            i->Create( iTileSize, iTileSize, 4, aPtexFormats[pT->dataType()] );



            // Loop through all the faces of the mesh.

            for ( unsigned int f = 0; f < pMesh->FaceCount(); f++ )

            { 

                MB_ASSERT( pG->FaceSizeExponent( f )[0] == pT->getFaceInfo( f ).res.ulog2 );

                MB_ASSERT( pG->FaceSizeExponent( f )[1] == pT->getFaceInfo( f ).res.vlog2 );

                UVGeneratorNode::DimData2 d = pG->FaceUVPosition( f );



                // Check which tile the face belongs to, and if it is something else than the current one, skip it.

                if ( pG->FaceUVArea( f )[0] != u || pG->FaceUVArea( f )[1] != v )

                    continue;



                // Prepare the surface data in a buffer.

                char iOrientation = pG->FaceOrientation( f );

                void *p = NULL;

                switch ( pT->dataType() )

                {

                case Ptex::dt_uint8:

                    p = PrepareFaceBuffer<unsigned char,255>( pT, f, iOrientation );

                    break;

                case Ptex::dt_uint16:

                    p = PrepareFaceBuffer<unsigned short,65535>( pT, f, iOrientation );

                    break;

                case Ptex::dt_half:

                    p = PrepareFaceBuffer<half_,1>( pT, f, iOrientation );

                    break;

                case Ptex::dt_float:

                    p = PrepareFaceBuffer<float,1>( pT, f, iOrientation );

                    break;

                };



                // Copy the buffer onto the image, and delete the buffer.

                i->setTile( d[0]%iTileSize, d[1]%iTileSize, pG->FaceUVSize( f )[0], pG->FaceUVSize( f )[1], p );

                delete p;

                Kernel()->ProgressAdd();

            };



            // Fill the texture tile using the image we prepared.

            Texture *pO = pTP->Tile( AxisAlignedBoundingBox( Vector( u, v, 0.5 ), Vector( u+1, v+1, 0.5 ) ) );

            pO->CopyFrom( i );

        };

};



// The following function reads the color data from the ptex file, and prepares a buffer. During the preparation it 

// rotates the data to the desired direction, and extends it (if necessary) to four channels.

template <typename eFormat, int iMax>

void *PtexImporter::PrepareFaceBuffer( PtexTexture *pTexture, unsigned int iFaceIndex, char iDirection )

{

    int iChannelCount = pTexture->numChannels();

    int iU = 1<<pTexture->getFaceInfo( iFaceIndex ).res.ulog2, iV = 1<<pTexture->getFaceInfo( iFaceIndex ).res.vlog2;

    eFormat *pTmp = new eFormat[iU*iV*iChannelCount];

    // First read the data from the ptex file into a temporary buffer.

    pTexture->getData( iFaceIndex, pTmp, iU*iChannelCount*sizeof(eFormat) );



    eFormat *pBuffer = new eFormat[4*iU*iV];



    // Copy the content of the temporary buffer into the final one, and during the copy, extend it to four channels, and rotate if necessary.

    for ( int y = 0; y < iV; y++ )

        for ( int x = 0; x < iU; x++ )

        {

            // Calculate the position where the data should be written to, this depends on the orientation of the face.

            unsigned int d = 0;

            switch ( iDirection )

            {

                case 0:

                    d = 4*(x+y*iU);

                    break;

                case 1:

                    d = 4*(y+(iU-x-1)*iV);

                    break;

                case 2:

                    d = 4*((iU-x-1)+(iV-y-1)*iU);

                    break;

                case 3:

                    d = 4*((iV-y-1)+x*iV);

                    break;

            };



            // Copy the data which exist in the ptex file.

            int c = 0;

            while ( c < iChannelCount )

            {

                pBuffer[d+c] = pTmp[iChannelCount*(x+y*iU)+c];

                c++;

            };



            // Fill the rest using default data (1 for the alpha channel, zero for others).

            while ( c < 4 )

            {

                if ( c == 3 )

                    pBuffer[d+c] = iMax;

                else

                    pBuffer[d+c] = 0;

                c++;

            };

        };



    return pBuffer;

};