//**************************************************************************/
// Copyright (c) 2009 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: Sample stereolithography (STL) file exporter.
//
//   Limitations 
//     - requires a single, closed mesh to work. 
//     - There will be no decimation done, so the mesh will be saved at full density, 
//         which might be too big for stereolithography shops. 
//     - Self-intersecting meshes 
//         are not corrected, which may cause problems with the resulting STL file.
//
// CREATED: February 2009
//**************************************************************************/

#include <stdio.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <assert.h>

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

#include "utility.hpp"

namespace mudbox
{

// Creates a record in the memory to describe the plugin to Mudbox
MB_PLUGIN( "STL Exporter", "STL file export plugin", "Autodesk", "http://www.mudbox3d.com", 0 );

// Main plug-in declaration
class STLExporter : 
    public BaseExporter, 
    public Singleton<STLExporter>
{
public:

	// The following line is used to expose RTTI information to Mudbox
	DECLARE_CLASS;

    // Default constructor, does nothing.
    STLExporter() {
    }

    // Return the supported file extension(s) by the plugin.
	virtual QString Extension() { return "stl"; };
	
    //! Return a description of the file type we are exporting.
	virtual QString Description() { return "Stereolithography File"; };

	//! Main function called when a file is exported.
	void Export( const QString &sFileName )
    {
        if (GetNumMeshes() == 0)
            MB_ERROR("There are no meshes to export");
        if (GetNumMeshes() > 1) 
            MB_ERROR("Multiple meshes can not be exported to this format");

        const Mesh* m = GetMesh(0);
     
        if (IsMeshOpen(m)) 
            MB_ERROR("Cannot export a mesh with holes");

        std::ofstream ofile(sFileName.toAscii(), std::ios::out | std::ios::binary);

        const uint STL_HEADER_SIZE = 80;
        char header[STL_HEADER_SIZE] = "";
        std::fill(header, header + STL_HEADER_SIZE, 0);
        ofile.write(header, STL_HEADER_SIZE);
        
        // output the total number of faces
        uint32 cnt = m->FaceCount();        
        if ( m->Type() == Mesh::typeQuadric ) 
            cnt *= 2;
        WriteUInt32(ofile, cnt);

		// Write each triangle face to file
        if ( m->Type() == Mesh::typeTriangular ) 
        {
	        for ( uint i = 0; i < m->FaceCount(); ++i ) {
                // Write the normal vector
                const Vector& normal = m->FaceNormal(i);
                WriteVectorFloat32(ofile, normal);

                // three sets of three 32-bit floats specifying the position of each vertex
                for (uint j = 0; j < 3; ++j) {
                    const Vector& v = m->TriangleVertexPosition( i, j );
                    WriteVectorFloat32(ofile, v);
                }

                // 2-byte padding 
                WriteUInt16(ofile, 0);
	        }
        }
        // Write each quadric face as two triangles
        else if ( m->Type() == Mesh::typeQuadric ) 
        {
	        for ( uint i = 0; i < m->FaceCount(); ++i ) {
                // Write the normal vector
                const Vector& normal = m->FaceNormal(i);
                WriteVectorFloat32(ofile, normal);

                // Get first sub-triangle using corners 0, 2, and 3 of quad
                for (uint j = 0; j < 4; ++j) {
                    if (j != 1) {
    		            const Vector& v = m->QuadVertexPosition( i, j );
                        WriteVectorFloat32(ofile, v);
                    }
                }

                // 2-byte padding 
                WriteUInt16(ofile, 0);

                // Write the normal vector again (both triangles share the same normal)
                WriteVectorFloat32(ofile, normal);

                // Get second sub-triangle using corners 0, 1, and 2 of quad
                for (uint j = 0; j < 4; ++j) {
                    if (j != 3) {
    		            const Vector& v = m->QuadVertexPosition( i, j );
                        WriteVectorFloat32(ofile, v);
                    }
                }

                // 2-byte padding 
                WriteUInt16(ofile, 0);
	        }
        }

        ofile.close();
	};
};

// RTTI information 
IMPLEMENT_CLASS( STLExporter, Exporter, "STL Exporter" );

} // namespace mudbox

