#include "PtexPaintExporter.h"
#include "PtexUtilizer.h"
#include <math.h>
IMPLEMENT_CLASS( PtexPaintExporter, PaintLayerExporter, "ptexpaintexporter" );
Preferences::Bool g_bIncludeBaseMesh(
NTR("Save mesh data in PTEX files"),
NTR("Files"),
QObject::tr("Save mesh data in PTEX files"),
QObject::tr("Files"),
true );
QVector<FileExtension> PtexPaintExporter::SupportedExtensions( void ) const
{
QVector<FileExtension> s;
s.append( FileExtension( "ptx", QObject::tr("Ptex file [8 bit Integer, RGBA]"), Image::e8integer ) );
s.append( FileExtension( "ptx", QObject::tr("Ptex file [16 bit Integer, RGBA]"), Image::e16integer ) );
s.append( FileExtension( "ptx", QObject::tr("Ptex file [16 bit Floating point, RGBA]"), Image::e16float ) );
s.append( FileExtension( "ptx", QObject::tr("Ptex file [32 bit Floating point, RGBA]"), Image::e32float ) );
return s;
};
PtexPaintExporter::PtexPaintExporter() :
m_bUseBaseLevel(this, NTR("Use Base Level"))
{
m_bUseBaseLevel.SetValue( true );
};
void PtexPaintExporter::Export( const QString &sFileName, int iFileTypeIndex, const Mesh *pSourceSurface, TexturePool *pSource )
{
if( m_bUseBaseLevel )
{
pSourceSurface = pSourceSurface->Geometry()->LowestLevel();
};
if ( pSourceSurface->Type() == Mesh::typeQuadric )
{
const UVGeneratorNode *pG = pSourceSurface->ChildByClass<UVGeneratorNode>( false );
if ( pG )
{
switch ( iFileTypeIndex )
{
case 0:
FastExport<unsigned char, 255>( sFileName, iFileTypeIndex, pSourceSurface, pSource, pG );
return;
case 1:
FastExport<unsigned short, 65525>( sFileName, iFileTypeIndex, pSourceSurface, pSource, pG );
return;
case 2:
FastExport<half_, 1>( sFileName, iFileTypeIndex, pSourceSurface, pSource, pG );
return;
case 3:
FastExport<float, 1>( sFileName, iFileTypeIndex, pSourceSurface, pSource, pG );
return;
default:
MB_ERROR( "Unknown filetype" );
};
};
};
SubdivisionLevel *pSL = dynamic_cast<SubdivisionLevel *>( (Mesh *)pSourceSurface );
SubdivisionLevel *pBL = m_bUseBaseLevel ? pSL->Geometry()->LowestLevel() : pSL;
Instance<MapExtractor> m;
m->SetTargetCount( 1 );
m->SetTarget( 0, pBL );
m->SetSourceCount( 1 );
m->SetSource( 0, pBL->Geometry()->HighestLevel() );
m->SetUtilizerType( PtexUtilizer::StaticClass() );
m->SetLocatorType( ClassDesc::ByName( NTR( "SubdivisionLocator" ) ) );
Component *pC = m->ComponentByClassName( "ColorTransfer" );
MB_ONBUG( !pC )
return;
const Layer *pL = dynamic_cast<const Layer *>( pSource );
MB_ONBUG( !pL )
return;
const LayerContainer *pLC = pL->Container();
MB_ONBUG( !pLC )
return;
pC->SetAttributeValue( "channelname", pLC->Name() );
pC->SetAttributeValue( "layerindex", QString("%1").arg(pLC->LayerIndex( pL )) );
pC->m_bEnabled.SetValue( true );
Utilizer *pU = pC->m_pUtilizer;
MB_SAFELY( pU )
{
pU->SetAttributeValue( "filename", sFileName );
pU->SetAttributeValue( "includemeshdata", g_bIncludeBaseMesh ? "true" : "false" );
};
PtexLayout *pPL = dynamic_cast<PtexLayout *>( m->Layout() );
MB_SAFELY( pPL )
{
if ( pBL->UVlessPaintingStatus() == 2 )
pPL->m_eDistribution = PtexLayout::distCustom;
else
{
unsigned int iPixelCount = 0;
for ( unsigned int t = 0; t < pSource->TileCount(); t++ )
{
const Texture *pT = pSource->Tile( t );
iPixelCount += pT->Width()*pT->Height();
};
if ( pSource->TileCount() > 0 )
{
pPL->m_eDistribution = PtexLayout::distUVArea;
pPL->m_iDesiredTexelCount = pSource->Tile( 0 )->Width()*pSource->Tile( 0 )->Height()/2*pSource->TileCount();
};
};
};
PtexUtilizer *pPU = dynamic_cast<PtexUtilizer *>( pU );
MB_SAFELY( pPU )
pPU->SetFormat( (PtexUtilizer::Format)iFileTypeIndex );
m->Execute( false );
};
inline bool isUVRightHanded(const Mesh & mesh, unsigned face) {
MB_ASSERT(mesh.Type() == Topology::typeQuadric);
TC o = mesh.QuadVertexTC(face, 0);
TC a = mesh.QuadVertexTC(face, 1) - o;
TC b = mesh.QuadVertexTC(face, 3) - o;
float zOfCrossProduct = a.u*b.v - b.u*a.v;
return zOfCrossProduct > 0;
}
template < typename tType, int iMultiplier >
void PtexPaintExporter::FastExport( const QString &sFileName, int iFileTypeIndex, const Mesh *pSourceSurface, TexturePool *pSource, const UVGeneratorNode *pG )
{
Kernel()->Interface()->ProgressStart( QObject::tr("Exporting to Ptex file..."), pSourceSurface->FaceCount() );
unsigned int iTileWidth = 0, iTileHeight = 0;
AxisAlignedBoundingBox d;
for ( unsigned int i = 0; i < pSource->TileCount(); i++ )
d.Extend( pSource->TileArea( i ) );
unsigned int iXW = ceilf( d.m_vEnd.x ), iYW = ceilf( d.m_vEnd.y );
QVector<Image *> aImages( iXW*iYW, NULL );
for ( unsigned int x = 0; x < iXW; x++ )
for ( unsigned int y = 0; y < iYW; y++ )
{
Texture *pT = pSource->Tile( AxisAlignedBoundingBox( Vector( x, y, 0.5f ), Vector( x+1, y+1, 0.5f ) ) );
if ( pT )
{
unsigned char iLevel = pT->getProxyLevel();
pT->setProxyLevel(0);
Image *pI = aImages[x+y*iXW] = CreateInstance<Image>();
pT->CopyTo( pI, false );
pT->setProxyLevel(iLevel);
if ( iTileWidth == 0 )
{
iTileWidth = pT->Width();
iTileHeight = pT->Height();
}
else if( pT->Location() != TexturePool::locationUnknown && pT->Format() != Image::eUnknown )
{
MB_ONBUG( iTileWidth != pT->Width() || iTileHeight != pT->Height() )
{
for ( unsigned int j = 0; j < iXW*iYW; j++ )
delete aImages[j];
return;
};
MB_ONBUG( iTileWidth != pI->Width() || iTileHeight != pI->Height() )
{
for ( unsigned int j = 0; j < iXW*iYW; j++ )
delete aImages[j];
return;
};
};
};
};
Ptex::String sError;
QByteArray qbaFileMask = QFile::encodeName( sFileName );
Ptex::DataType aFormats[4] = { Ptex::dt_uint8, Ptex::dt_uint16, Ptex::dt_half, Ptex::dt_float };
PtexWriter *pWriter = PtexWriter::open( qbaFileMask.constData(), Ptex::mt_quad, aFormats[iFileTypeIndex], 4, 3, pSourceSurface->FaceCount(), sError );
MB_ONBUG( pWriter == NULL || iTileWidth == 0 || iTileHeight == 0 )
{
for ( unsigned int j = 0; j < iXW*iYW; j++ )
delete aImages[j];
return;
};
for ( unsigned int f = 0; f < pSourceSurface->FaceCount(); f++ )
{
bool swapUV = !isUVRightHanded(*pSourceSurface, f);
unsigned int iOrientation = pG->FaceOrientation( f );
Ptex::FaceInfo sInfo;
unsigned int af[4], ae[4];
for ( int c = 0; c < 4; c++ )
{
unsigned int a = pSourceSurface->QuadAdjacency( f, c );
if ( a == 0xffffffff )
{
af[c] = 0xffffffff;
ae[c] = 0;
}
else
{
af[c] = a/4;
ae[c] = a%4;
};
};
sInfo.setadjfaces( af[0], af[1], af[2], af[3] );
sInfo.setadjedges( ae[0], ae[1], ae[2], ae[3] );
unsigned int iXS = pG->FaceUVPosition(f)[0];
unsigned int iYS = pG->FaceUVPosition(f)[1];
unsigned int iXD = pG->FaceSizeExponent(f)[0];
unsigned int iYD = pG->FaceSizeExponent(f)[1];
unsigned int iTX = pG->FaceUVArea(f)[0], iTY = pG->FaceUVArea(f)[1];
Image *i = aImages[iTX+iTY*iXW];
MB_ONBUG( i == NULL )
{
for ( unsigned int j = 0; j < iXW*iYW; j++ )
delete aImages[j];
return;
};
unsigned int iXX = 1, iXY = 0, iYX = 0, iYY = 1;
if ( swapUV && iOrientation % 2 )
iOrientation = 4 - iOrientation;
switch ( iOrientation )
{
case 0:
break;
case 1:
{
iYS += (1<<iXD)-1;
iXX = iYY = 0;
iXY = 1;
iYX = 0xffffffff;
};
break;
case 2:
iXX = iYY = 0xffffffff;
iXS += (1<<iXD);
iYS += (1<<iYD);
break;
case 3:
{
iXS += (1<<iYD)-1;
iXX = iYY = 0;
iXY = 0xffffffff;
iYX = 1;
};
break;
};
unsigned iW = swapUV ? 1<<iYD : 1<<iXD,
iH = swapUV ? 1<<iXD : 1<<iYD;
sInfo.res = swapUV ? Ptex::Res( iYD, iXD ) : Ptex::Res( iXD, iYD );
tType *pData = new tType[iW*iH*4];
for ( unsigned int y = 0; y < iH; y++ )
for ( unsigned int x = 0; x < iW; x++ )
{
Color c;
if (swapUV)
c = i->ColorAt( iXS+y*iXX+x*iXY, iYS+y*iYX+x*iYY );
else
c = i->ColorAt( iXS+x*iXX+y*iXY, iYS+x*iYX+y*iYY );
if ( c.a )
{
float f = 1/c.a;
c.r *= f;
c.g *= f;
c.b *= f;
};
unsigned index = x+y*iW;
pData[index*4+0] = c.r*iMultiplier;
pData[index*4+1] = c.g*iMultiplier;
pData[index*4+2] = c.b*iMultiplier;
pData[index*4+3] = c.a*iMultiplier;
};
pWriter->writeFace( f, sInfo, pData );
delete pData;
Kernel()->Interface()->ProgressAdd();
};
if ( g_bIncludeBaseMesh )
PtexUtilizer::WriteMeshData( pWriter, pSourceSurface );
pWriter->close( sError );
pWriter->release();
for ( unsigned int j = 0; j < iXW*iYW; j++ )
delete aImages[j];
Kernel()->Interface()->ProgressEnd();
};