PtexImporter/PtexImporter.cpp

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."),
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."),
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."),
}
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."),
}
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),
}
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"),
}
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);
}
QFileInfo cFileInfo( sFileName );
pMesh->SetName( cFileInfo.baseName().isEmpty() ? NTR("PTEX_Mesh") : cFileInfo.baseName() );
}
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;
}
}
}
//------------------------------------------------------------------------------