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

# PLYImport/Importer.cpp

    //**************************************************************************/
    // Copyright (c) 2008 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: October 2008
    //**************************************************************************/

    #include "rply.h"

    #include <stdio.h>

    // This is the SDK header, all plugins have to include it.

    #include <QtCore/QCoreApplication>
    #include <QtCore/QFile>
    #include <QtCore/QTextStream>

    #if defined(JAMBUILD)
    #include <Mudbox/mudbox.h>
    #else
    #include "../../include/Mudbox/mudbox.h"
    #endif

    using namespace mudbox;

    // This macro makes a record in the memory to describe the plugin.
    MB_PLUGIN( "PLY Importer", "PLY file import/export plugin", "Autodesk", "http://www.mudbox3d.com", 0 );

    // The following class will implement the Importer interface.
    class PLYImporter : public Importer
    {
        Q_DECLARE_TR_FUNCTIONS(FLYImporter);
    public:

        // The following line must be used in every class which wants to use RTTI.
        DECLARE_CLASS;

        // Just a plain empty constructor to initialize some data.
        PLYImporter( void );
        // And a destructor to destroy some.
        ~PLYImporter( void );
        // The following function is responsible to return the supported file extension(s) by the plugin.
        virtual QString Extension( void ) const { return NTR("ply"); };
        // This will return a description of the file type we are importing.
        virtual QString Description( void ) const { return tr("Polygon File"); };
        QVector<FileExtension> SupportedExtensions( void ) const { QVector<FileExtension> ret; ret.push_back(FileExtension(NTR("ply"), tr("Polygon File"))); return ret; }
        // This is the main function, this will be called when a file should be imported.
        virtual void Import( const QString &sFileName, mudbox::Scene::LoadData &cData );

        // Internal stuff follows. The next function will be called when some new vertex data (coordinate) is read from the file.
        void OnVertexData( float fData );
        // This is called when new face data (vertex index) is read.
        void OnFaceData( int iIndex, int iFaceSize, int iData );

        // Pointer to the result mesh.
        Mesh *m_pMesh;
        // Index of the current vertex which is being read.
        unsigned int m_iVertexIndex;
        // Index of the current corner of the triangle which is being read (possible values are 0, 1, 2).
        unsigned int m_iCornerIndex;
        // Index of the current triangle which is being read.
        unsigned int m_iTriangleIndex;
        // Coordinates of the current vertex.
        Vector m_vPosition;
    };

    // The PLY import code uses C functions from the RPly library. Since we cannot pass instances
    // of Importers into these functions we define a global pointer to the current ply importer.
    PLYImporter* g_pImporter = 0;

    PLYImporter::PLYImporter( void )
    {
        Kernel()->Log( "PLY Import plugin initialized\n" );
        m_pMesh = 0;
        g_pImporter = this;
        m_iVertexIndex = 0;
        m_iCornerIndex = 0;
        m_iTriangleIndex = 0;
    };

    PLYImporter::~PLYImporter( void )
    {
        if( g_pImporter == this )
            g_pImporter = 0;
    };

    // The code reads one coordinate of a vertex at a time, so this function is called three times for each vertex.
    void PLYImporter::OnVertexData( float fData )
    {
        // Store the incoming data to the current vector, and step to the next coordinate of the vector.
        m_vPosition[m_iCornerIndex++] = fData;

        // If the vector is ready (i.e. it has all the three coorinates filled) then store it in the mesh.
        if ( m_iCornerIndex == 3 )
        {
            // Restart filling up the vector
            m_iCornerIndex = 0;

            // Just to be sure we check the number of vertices.
            if ( m_iVertexIndex < m_pMesh->VertexCount() )
            {
                m_pMesh->SetVertexPosition( m_iVertexIndex++, m_vPosition );
                Kernel()->Interface()->ProgressSet( m_iVertexIndex );
            }
            else
                MB_ERROR( "Too much vertex in file" );
        };
    };

    // Same as above, the code reads one index at a time, so this function will be called three times for a triangle.
    void PLYImporter::OnFaceData( int iIndex, int iFaceSize, int iData )
    {
        if ( iIndex == -1 )
        {
            if ( iFaceSize == 3 )
                return;
            else
                MB_ERROR( "Face with more than 3 vertex found in file (not supported)" );
        };

        if ( m_iTriangleIndex < m_pMesh->FaceCount() && iIndex < 3 && iIndex >= 0 )
            m_pMesh->SetTriangleIndex( m_iTriangleIndex, iIndex, iData );
        else
            MB_ERROR( "Vertex index out of range" );

        if ( iIndex == 2 )
        {
            m_iTriangleIndex++;
            Kernel()->Interface()->ProgressSet( m_iVertexIndex+m_iTriangleIndex );
        };
    };

    // This is a callback function for the PLY reader code to use for vertices.
    static int vertex_cb(p_ply_argument argument)
    {
        g_pImporter->OnVertexData( (float)ply_get_argument_value(argument) );
        return 1;
    };

    // And this is for faces.
    static int face_cb(p_ply_argument argument)
    {
        long iLength, iValueIndex;
        ply_get_argument_property(argument, 0, &iLength, &iValueIndex);
        g_pImporter->OnFaceData( iValueIndex, iLength, (unsigned int)ply_get_argument_value(argument) );
        return 1;
    };

    // This is the main function which will do the importing.
    void PLYImporter::Import( const QString &sFileName, mudbox::Scene::LoadData &cData )
    {
        unsigned int iVertexCount, iTriangleCount;

        // Open the file.
        QByteArray qbaFileName = QFile::encodeName(sFileName);
        p_ply ply = ply_open(qbaFileName.constData(), 0);

        // Check for errors. If something is wrong we use the MB_ERROR macro, which will throw an exception, so the execution of the function will be stopped here.
        if ( !ply )
            MB_ERROR( tr("Invalid file: %1").arg(sFileName) );
        if ( !ply_read_header(ply) )
            MB_ERROR( tr("Invalid file: %1").arg(sFileName) );

        // Initialize vertex reading for three coordinates with the same callback.
        iVertexCount = ply_set_read_cb(ply, "vertex", "x", vertex_cb, 0, 0);
        ply_set_read_cb(ply, "vertex", "y", vertex_cb, 0, 0);
        ply_set_read_cb(ply, "vertex", "z", vertex_cb, 0, 1);

        // Initialize face reading.
        iTriangleCount = ply_set_read_cb(ply, "face", "vertex_indices", face_cb, 0, 0);

        // Log some information, just for debugging purposes.
        Kernel()->Log( NTRQ("\t%1 vertices, %2 triangles.").arg(iVertexCount).arg(iTriangleCount) );

        // Create a mesh.
        m_pMesh = Kernel()->Scene()->CreateMesh( Topology::typeTriangular );
        m_pMesh->SetName("PlyMesh");
        // Initialize the mesh to match the specs from the file.
        m_pMesh->SetFaceCount( iTriangleCount );
        m_pMesh->SetVertexCount( iVertexCount );

        // Start a progress bar in Mudbox.
        Kernel()->Interface()->ProgressStart( "importing...", iVertexCount+iTriangleCount );

        // Try to read the file. If something went wrong, just throw an exception.
        if (!ply_read(ply))
            MB_ERROR( tr("Invalid file: %1").arg(sFileName) );

        // Close the file.
        ply_close(ply);

        // Remove the progress bar.
        Kernel()->Interface()->ProgressEnd();

        // Importing is done. Add the converted mesh to the Importer's scene.
        // Mudbox will then merge that scene into it's scene, after this call is done.
        // Note that the importer's scene will be automatically destroyed by Mudbox
        // when it  is no longer needed. Plugin writers do not need to manage memory
        // of objects in the importer's scene.
        AddMesh( m_pMesh );
    };

    // This macro is needed for all classes which want to use RTTI
    IMPLEMENT_CLASS( PLYImporter, Importer, "PLY Importer" );

    // Implementation of the Exporter interface, to handle writting to PLY files.
    class PLYExporter : public Exporter
    {
        Q_DECLARE_TR_FUNCTIONS(PLYExporter)

    public:
        // Needed for RTTI.
        DECLARE_CLASS;

        // Pass file extension to Mudbox.
        QString Extension( void ) const { return NTR("ply";) };
        // And file description. These will be visible in the browse dialog.
        QString Description( void ) const { return tr("Polygon file"); };
        QVector<FileExtension> SupportedExtensions( void ) const { QVector<FileExtension> ret; ret.push_back(FileExtension(NTR("ply"), tr("Polygon File"))); return ret; }

        // Mudbox will call these two functions, to let the exporter know which meshes should be exported. First it tells the number of meshes,
        void SetMeshCount( unsigned int iMeshCount ) { m_aMeshes.SetItemCount( iMeshCount ); };
        // And then a pointer to each mesh.
        void SetMesh( unsigned int iIndex, const Mesh *pMesh ) { m_aMeshes[iIndex] = pMesh; };

        // Internal function, used to write a single line into a text file (such as the PLY).
        void Write( const QString &sText )
        {
            m_pStream << sText;
            m_pStream << NTR("\n");
        };

        // Main function, this is doing the export itself. The function gets the filename as a parameter.
        void Export( const QString &sFileName, const QString & )
        {
            // If there is no mesh to export, we do nothing.
            if ( m_aMeshes.ItemCount() == 0 )
                return;
            // If there are more than one mesh to export, we report an error since this implementation only supports a single mesh.
            if ( m_aMeshes.ItemCount() > 1 )
                MB_ERRORQ( tr("Only a single mesh can be exported to PLY file format.") );

            // Create and open the file as usual.
            m_pFile.setFileName(sFileName);

            if (!m_pFile.open(QIODevice::WriteOnly))
            {
                MB_ERRORQ( tr("Can't open %1 for writing: %2").arg(sFileName).arg(m_pFile.errorString()) );
            }

            m_pStream.setDevice(&m_pFile);
            m_pStream.setCodec(NTR("UTF-8"));

            // Write the header of the file. This is hardcoded, this plugin only writes the most simple format of PLY files.
            Write( NTR("ply") );
            Write( NTR("format ascii 1.0") );
            Write( NTRQ("element vertex %1").arg(m_aMeshes[0]->VertexCount() ) );
            Write( NTR("property float x") );
            Write( NTR("property float y") );
            Write( NTR("property float z") );
            Write( NTRQ("element face %1").arg( m_aMeshes[0]->FaceCount() ) );
            Write( NTR("property list uchar int vertex_indices") );
            Write( NTR("end_header") );

            // Write all the vertex positions into the file.
            for ( unsigned int v = 0; v < m_aMeshes[0]->VertexCount(); v++ )
            {
                Vector p = m_aMeshes[0]->VertexPosition( v );
                Write( NTRQ("%1 %2 %3").arg(p.x).arg(p.y).arg(p.z ) );
            };

            // And then write the vertex indices from the faces.
            if ( m_aMeshes[0]->Type() == Mesh::typeTriangular )
            {
                // If the mesh has triangles, write three indices in a row.
                for ( unsigned int f = 0; f < m_aMeshes[0]->FaceCount(); f++ )
                    Write( NTRQ("3 %1 %2 %3").arg(m_aMeshes[0]->TriangleIndex( f, 0 )).arg( m_aMeshes[0]->TriangleIndex( f, 1 )).arg( m_aMeshes[0]->TriangleIndex( f, 2 ) ) );
            }
            else
            {
                // And write four indices if there are quads in the mesh.
                for ( unsigned int f = 0; f < m_aMeshes[0]->FaceCount(); f++ )
                    Write( NTRQ("4 %1 %2 %3 %4").arg(m_aMeshes[0]->QuadIndex( f, 0 )).arg( m_aMeshes[0]->QuadIndex( f, 1 )).arg( m_aMeshes[0]->QuadIndex( f, 2 )).arg( m_aMeshes[0]->QuadIndex( f, 3 ) ) );
            };

            // Thats all, close the file and we are ready.
            m_pStream.flush();
            m_pFile.close();
        };

        // This array will be used to store the list of meshes which has to be exported.
        Store<const Mesh *> m_aMeshes;
        // A simple handler to the file we are writting to.
        QFile m_pFile;
        QTextStream m_pStream;
    };

    // RTTI information about the PLYExporter class.
    IMPLEMENT_CLASS( PLYExporter, Exporter, QT_TRANSLATE_NOOP("PLYExporter", "PLY Exporter") );