#include "PtexLayout.h"
#include <QtGui/QWidget>
#include <QtGui/QGridLayout>
#define RES_TOL (0.1f)
PtexLayout::PtexLayout( void ) :
m_eDistribution( this, "distribution" ),
m_fDensity( this, "density" ),
m_sTexelCount( this, "texelcount" )
{
m_eDistribution.SetName( QObject::tr("Texel Distribution:") );
m_eDistribution.SetToolTip( QObject::tr("Controls how the sample points are distributed across the surface") );
m_eDistribution.AddItem( QObject::tr("Uniform") );
m_eDistribution.AddItem( QObject::tr("Based on Face Size") );
m_eDistribution.AddItem( QObject::tr("Based on UV Size") );
m_eDistribution.AddItem( QObject::tr("Use PTEX Setup") );
m_eDistribution = 0;
m_fDensity.SetName( QObject::tr("Density:") );
m_fDensity = 0.5f;
m_sTexelCount.SetName( QObject::tr("Number of texels:") );
m_sTexelCount = "100000";
m_iDesiredTexelCount = 100000;
};
QWidget *PtexLayout::UserInterface(
void )
{
QLabel *
t =
new QLabel( QObject::tr(
"Ptex resolution options") );
l->
addWidget( m_fDensity.CreateEditorWidget( NULL, 150 ) );
l->
addWidget( m_sTexelCount.CreateEditorWidget( NULL, 150 ) );
l->
addWidget( m_eDistribution.CreateEditorWidget( NULL, 150 ) );
};
float TriangleArea( const Vector &vC0, const Vector &vC1, const Vector &vC2 )
{
float a = (vC0-vC1).Length();
float b = (vC0-vC2).Length();
float c = (vC2-vC1).Length();
return sqrtf(s*(s-a)*(s-b)*(s-c));
};
void PtexLayout::ProcessSurface( SubdivisionLevel *pSurface )
{
bool bFiltering = m_eDistribution != distCustom;
InitializeResolution( pSurface );
BeginSection();
if ( pSurface->Type() == Mesh::typeQuadric )
{
for ( unsigned int f = 0; f < pSurface->FaceCount(); f++ )
{
int iHRes = 3, iVRes = 3;
CalculateQuadResolution( f, iHRes, iVRes );
int iHSize = 1<<iHRes, iVSize = 1<<iVRes;
for ( int u = 0; u < iHSize; u++ )
for (
int v = 0;
v < iVSize;
v++ )
{
TargetLocation l;
l.m_aLayoutData[dataURes] = u+(iHRes<<24);
l.m_aLayoutData[dataVRes] =
v+(iVRes<<24);
l.m_aLayoutData[dataFaceID] =
f;
l.Fill( pSurface, f, (u+0.5f)/(
float)iHSize, (
v+0.5f)/(
float)iVSize );
if ( !bFiltering )
l.m_fDiameter = 0.0f;
ProcessSurfacePoint( l );
};
};
}
else
{
PrepareAdjacency( pSurface );
unsigned int iFaceID = 0;
for ( unsigned int f = 0; f < pSurface->FaceCount(); f++ )
{
if ( pSurface->IsFakeTriangle( f ) )
continue;
int iSideCount = 3, i =
f;
while (
f < pSurface->FaceCount() && pSurface->IsFakeTriangle( ++i ) )
iSideCount++;
int iSubFaceCount = iSideCount;
if ( iSideCount == 4 )
iSubFaceCount = 1;
Vector aCorners[16];
Vector aUVCorners[16];
continue;
aCorners[0] = pSurface->TriangleVertexPosition( f, 0 );
aCorners[1] = pSurface->TriangleVertexPosition( f, 1 );
for ( int i = 0; i < iSideCount-2; i++ )
aCorners[i+2] = pSurface->TriangleVertexPosition( f+i, 2 );
if ( pSurface->HasTC() )
{
aUVCorners[0] = pSurface->TriangleVertexTC( f, 0 );
aUVCorners[1] = pSurface->TriangleVertexTC( f, 1 );
for ( int i = 0; i < iSideCount-2; i++ )
aUVCorners[i+2] = pSurface->TriangleVertexTC( f+i, 2 );
};
Vector vCenter;
for ( int j = 0; j < iSideCount; j++ )
vCenter += aCorners[j];
vCenter *= 1/(float)iSideCount;
Vector vUVCenter;
for ( int j = 0; j < iSideCount; j++ )
vUVCenter += aUVCorners[j];
vUVCenter *= 1/(float)iSideCount;
for ( int iSubFace = 0; iSubFace < iSubFaceCount; iSubFace++ )
{
Vector vCorner0, vCorner1, vCorner2, vCorner3;
Vector vUVCorner0, vUVCorner1, vUVCorner2, vUVCorner3;
int iURes = 3, iVRes = 3;
if ( iSideCount == 4 )
{
vCorner0 = aCorners[0];
vCorner1 = aCorners[1];
vCorner2 = aCorners[2];
vCorner3 = aCorners[3];
vUVCorner0 = aUVCorners[0];
vUVCorner1 = aUVCorners[1];
vUVCorner2 = aUVCorners[2];
vUVCorner3 = aUVCorners[3];
}
else
{
iURes--;
iVRes--;
vCorner0 = aCorners[iSubFace];
vCorner1 = (vCorner0+aCorners[(iSubFace+1)%iSideCount])*0.5f;
vCorner2 = vCenter;
vCorner3 = (vCorner0+aCorners[(iSubFace-1+iSideCount)%iSideCount])*0.5f;
vUVCorner0 = aUVCorners[iSubFace];
vUVCorner1 = (vUVCorner0+aUVCorners[(iSubFace+1)%iSideCount])*0.5f;
vUVCorner2 = vUVCenter;
vUVCorner3 = (vUVCorner0+aUVCorners[(iSubFace-1+iSideCount)%iSideCount])*0.5f;
};
if ( m_eDistribution != distUVArea )
CalculateFaceResolution( vCorner0, vCorner1, vCorner2, vCorner3, f, iURes, iVRes );
else
CalculateFaceResolution( vUVCorner0, vUVCorner1, vUVCorner2, vUVCorner3, f, iURes, iVRes );
int iUSize = 1 << iURes, iVSize = 1 << iVRes;
for ( int iU = 0; iU < iUSize; iU++ )
for ( int iV = 0; iV < iVSize; iV++ )
{
TargetLocation l;
l.m_aLayoutData[dataURes] = iU+(iURes<<24);
l.m_aLayoutData[dataVRes] = iV+(iVRes<<24);
l.m_aLayoutData[dataFaceID] = iFaceID;
if ( iSubFaceCount > 1 )
l.m_aLayoutData[dataFaceID] |= 0x80000000;
float fU = ((float)iU+0.5f)/iUSize, fV = ((float)iV+0.5f)/iVSize;
Vector vH0 = vCorner0+(vCorner1-vCorner0)*fU;
Vector vH1 = vCorner3+(vCorner2-vCorner3)*fU;
l.FillNSided( pSurface, f, vH0+(vH1-vH0)*fV );
if ( iSubFaceCount == 1 && !bFiltering )
l.m_fDiameter = 0.0f;
ProcessSurfacePoint( l );
};
iFaceID++;
};
};
};
EndSection();
if ( m_eDistribution != distCustom )
Kernel()->Log(
QString(
"Mesh %1 processed with %2 samples (desired: %3).\n" ).arg( pSurface->Name() ).arg( m_sRes.m_iTexelCount ).
arg( m_iDesiredTexelCount ) );
else
Kernel()->Log(
QString(
"Mesh %1 processed with %2 samples.\n" ).arg( pSurface->Name() ).arg( m_sRes.m_iTexelCount ) );
};
void PtexLayout::InitializeResolution( const Mesh *pMesh )
{
m_sRes.m_pMesh = pMesh;
m_sRes.m_iTexelCount = 0;
if ( m_eDistribution == distCustom )
return;
m_sRes.m_fTotalSurfaceArea = 0.0f;
if ( m_eDistribution == distWorldArea || m_eDistribution == distUVArea )
{
for ( unsigned int i = 0; i < pMesh->FaceCount(); i++ )
{
if ( pMesh->Type() == Mesh::typeTriangular )
{
if ( m_eDistribution == distUVArea )
{
v0 = pMesh->TriangleVertexTC( i, 0 );
v1 = pMesh->TriangleVertexTC( i, 1 );
v2 = pMesh->TriangleVertexTC( i, 2 );
}
else
{
v0 = pMesh->TriangleVertexPosition( i, 0 );
v1 = pMesh->TriangleVertexPosition( i, 1 );
v2 = pMesh->TriangleVertexPosition( i, 2 );
};
m_sRes.m_fTotalSurfaceArea += TriangleArea( v0, v1, v2 );
}
else
{
if ( m_eDistribution == distUVArea )
{
v0 = pMesh->QuadVertexTC( i, 0 );
v1 = pMesh->QuadVertexTC( i, 1 );
v2 = pMesh->QuadVertexTC( i, 2 );
v3 = pMesh->QuadVertexTC( i, 3 );
}
else
{
v0 = pMesh->QuadVertexPosition( i, 0 );
v1 = pMesh->QuadVertexPosition( i, 1 );
v2 = pMesh->QuadVertexPosition( i, 2 );
v3 = pMesh->QuadVertexPosition( i, 3 );
};
m_sRes.m_fTotalSurfaceArea += TriangleArea( v0, v1, v2 );
m_sRes.m_fTotalSurfaceArea += TriangleArea( v0, v2, v3 );
};
};
}
else
{
if ( pMesh->Type() == Mesh::typeQuadric )
m_sRes.m_fTotalSurfaceArea = (float)pMesh->FaceCount();
else
{
int s = 3;
for ( unsigned int f = 0; f < pMesh->FaceCount(); f++ )
{
if (
f < pMesh->FaceCount()-1 && pMesh->IsFakeTriangle( f+1 ) )
s++;
else
{
if ( s == 4 )
m_sRes.m_fTotalSurfaceArea++;
else
m_sRes.m_fTotalSurfaceArea +=
s;
s = 3;
};
};
};
};
if ( m_eDistribution == distUniform )
m_sRes.m_fTolerance = 0.5f;
else
m_sRes.m_fTolerance = RES_TOL;
m_sRes.m_fProcessedArea = 0.0f;
};
void PtexLayout::CalculateQuadResolution( unsigned int iQuadIndex, int &iHRes, int &iVRes )
{
const Mesh *pMesh = m_sRes.m_pMesh;
if ( m_eDistribution == distUVArea )
CalculateFaceResolution( pMesh->QuadVertexTC( iQuadIndex, 0 ), pMesh->QuadVertexTC( iQuadIndex, 1 ), pMesh->QuadVertexTC( iQuadIndex, 2 ), pMesh->QuadVertexTC( iQuadIndex, 3 ), iQuadIndex, iHRes, iVRes );
else
CalculateFaceResolution( pMesh->QuadVertexPosition( iQuadIndex, 0 ), pMesh->QuadVertexPosition( iQuadIndex, 1 ), pMesh->QuadVertexPosition( iQuadIndex, 2 ), pMesh->QuadVertexPosition( iQuadIndex, 3 ), iQuadIndex, iHRes, iVRes );
};
int PtexLayout::Calculate1DResolution( float fIdeal )
{
float f = logf(fIdeal)/logf(2.0f);
float fRac = f-floorf(f);
if ( f < 0 )
f = 0;
if ( fRac < 0.5f-m_sRes.m_fTolerance )
if ( fRac > 0.5f+m_sRes.m_fTolerance )
unsigned int iExpectedTexelCount = m_sRes.m_iTexelCount*(m_sRes.m_fTotalSurfaceArea/m_sRes.m_fProcessedArea);
if ( iExpectedTexelCount < m_iDesiredTexelCount )
else
};
void PtexLayout::CalculateFaceResolution( const Vector &vC0, const Vector &vC1, const Vector &vC2, const Vector &vC3, unsigned int iFaceIndex, int &iHRes, int &iVRes )
{
if ( m_eDistribution == distCustom )
{
return;
iHRes = m_sRes.m_pUVGen->FaceSizeExponent( iFaceIndex )[0];
iVRes = m_sRes.m_pUVGen->FaceSizeExponent( iFaceIndex )[1];
m_sRes.m_iTexelCount += (1<<iHRes)*(1<<iVRes);
return;
};
float fFaceArea = TriangleArea( vC0, vC1, vC2 )+TriangleArea( vC1, vC2, vC3 );
if ( m_eDistribution == distUniform )
fFaceArea = 1.0;
float fIdealTexelCount = m_iDesiredTexelCount*fFaceArea/m_sRes.m_fTotalSurfaceArea;
float fWidth = ((vC0-vC1).Length()+(vC2-vC3).Length())*0.5f;
float fHeight = ((vC0-vC3).Length()+(vC1-vC2).Length())*0.5f;
float fAspect = fWidth/fHeight;
float fIdealWidth = sqrtf(fIdealTexelCount*fAspect);
iHRes = Calculate1DResolution( fIdealWidth );
float fIdealHeight = fIdealTexelCount/(1<<iHRes);
iVRes = Calculate1DResolution( fIdealHeight );
iHRes = 0;
iVRes = 0;
m_sRes.m_fProcessedArea += fFaceArea;
m_sRes.m_iTexelCount += (1<<iHRes)*(1<<iVRes);
return;
};
unsigned int PtexLayout::Prepare( void )
{
if ( m_eDistribution == distCustom )
{
unsigned int iRefCount = 0;
for ( unsigned int c = 0; c < Extractor()->TargetCount(); c++ )
{
const Mesh *
p = Extractor()->TargetMesh( c );
if ( pUVGen == 0 )
{
m_eDistribution = distUniform;
MB_ERROR( QObject::tr(
"\"Use PTEX Setup\" can only be selected if the target mesh is already set up for PTEX.") );
};
for ( unsigned int f = 0; f < p->FaceCount(); f++ )
{
if ( p->Type() == Mesh::typeTriangular && p->IsFakeTriangle( f ) )
continue;
UVGeneratorNode::DimData4 d = pUVGen->
FaceSize( f );
iRefCount += d.m_iData[0]*d.m_iData[1];
};
};
return iRefCount;
};
if ( m_eDistribution == distUVArea )
{
for ( unsigned int i = 0; i < Extractor()->TargetCount(); i++ )
if ( !Extractor()->TargetMesh( i )->HasTC() )
{
m_eDistribution = distUniform;
MB_ERROR( QObject::tr(
"\"Based on UV Size\" can only be selected if the target mesh has texture coordinates.") );
};
};
return m_iDesiredTexelCount;
};
void PtexLayout::Serialize( Stream &s )
{
if ( s.IsNewerThan( 0, this ) )
{
if ( s.IsNewerThan( 2, this ) )
s == m_fDensity == m_eDistribution;
if ( s.IsNewerThan( 3, this ) )
s == m_iDesiredTexelCount;
else
{
if ( s.IsNewerThan( 1, this ) )
{
int i;
s >> i;
}
else
{
};
};
};
if ( s.IsNewerThan( 4, this ) )
s == m_sTexelCount;
Layout::Serialize( s );
};
void PtexLayout::OnNodeEvent(
const Attribute &a,
NodeEventType e )
{
{
if ( m_fDensity > 1 )
m_fDensity = 1;
if ( m_fDensity < 0 )
m_fDensity = 0;
int iTargetSampleCount = 10000*powf(100, m_fDensity);
m_sTexelCount.SetValue(
QString(
"%1").arg(iTargetSampleCount),
true );
m_iDesiredTexelCount = iTargetSampleCount;
};
{
if ( m_eDistribution == distCustom )
{
for ( unsigned int i = 0; i < Extractor()->TargetCount(); i++ )
{
if ( !pG )
{
m_eDistribution.SetValue( distUniform, true );
return;
};
};
};
m_fDensity.SetConst( m_eDistribution == distCustom );
m_sTexelCount.SetConst( m_eDistribution == distCustom );
};
{
qlonglong iDesiredTexelCount = m_sTexelCount.Value().toLongLong();
if ( iDesiredTexelCount < 1 )
{
iDesiredTexelCount = 1;
m_sTexelCount = "1";
};
if ( iDesiredTexelCount > 100000000 )
{
iDesiredTexelCount = 100000000;
m_sTexelCount = "100000000";
};
m_iDesiredTexelCount = iDesiredTexelCount;
float fDensity = logf(m_iDesiredTexelCount/10000)/logf(100.0f);
fDensity =
Min(
Max(fDensity,0.0f),1.0f);
m_fDensity = fDensity;
};
};
void PtexLayout::PrepareAdjacency( const Mesh *pMesh )
{
m_pMesh = pMesh;
m_aFaceID.resize( pMesh->FaceCount() );
unsigned int iTFaceID = 0;
for ( unsigned int i = 0; i < pMesh->FaceCount(); i++ )
{
m_aFaceID[i] = iTFaceID;
int s = 3;
while ( i+1 < pMesh->FaceCount() && pMesh->IsFakeTriangle( i+1 ) )
{
i++;
s++;
m_aFaceID[i] = 0xffffffff;
};
if ( s == 4 )
iTFaceID++;
else
};
m_aTriangle.fill( 0xffffffff, iTFaceID );
for ( int i = 0; i < m_aFaceID.size(); i++ )
if ( m_aFaceID[i] != 0xffffffff )
m_aTriangle[m_aFaceID[i]] = i;
for ( int i = 1; i < m_aTriangle.size(); i++ )
if ( m_aTriangle[i] == 0xffffffff )
m_aTriangle[i] = m_aTriangle[i-1];
};
unsigned int PtexLayout::AdjacentFaceForTriangle( unsigned int iFaceIndex, unsigned int iSide, unsigned int iSegment, unsigned int &iEdge ) const
{
MB_ONBUG( iFaceIndex >= m_pMesh->FaceCount() )
return 0xffffffff;
unsigned int a = m_pMesh->TriangleAdjacency( iFaceIndex, iSide );
if ( a == 0xffffffff )
return 0xffffffff;
unsigned int t = a/3;
return 0xffffffff;
while ( m_pMesh->IsFakeTriangle( t ) )
{
return 0xffffffff;
t--;
};
while (
h < m_pMesh->FaceCount() && m_pMesh->IsFakeTriangle(
h ) )
unsigned int s = h-t+2;
return 0xffffffff;
unsigned int iV[16];
iV[0] = m_pMesh->TriangleIndex( t, 0 );
iV[1] = m_pMesh->TriangleIndex( t, 1 );
for ( unsigned int f = 0; f < s-2; f++ )
iV[f+2] = m_pMesh->TriangleIndex( t+f, 2 );
unsigned int iA = m_pMesh->TriangleIndex( iFaceIndex, (iSide+1)%3 ), iB = m_pMesh->TriangleIndex( iFaceIndex, (iSide+2)%3 );
unsigned int as = 0;
while ( iB != iV[as] && iA != iV[(as+1)%s] )
{
as++;
return 0xffffffff;
};
if ( s == 4 )
{
iEdge = as;
};
if ( iSegment )
{
as = (as+1)%s;
iEdge = 3;
}
else
iEdge = 0;
};
unsigned int PtexLayout::AdjacentFace( unsigned int iFaceID, unsigned int iSide, unsigned int &iEdge ) const
{
unsigned int iFaceIndex = m_aTriangle[iFaceID];
MB_ONBUG( m_pMesh->IsFakeTriangle( iFaceIndex ) )
return 0xffffffff;
unsigned int e = iFaceIndex+1;
while ( e < m_pMesh->FaceCount() && m_pMesh->IsFakeTriangle( e ) )
e++;
unsigned int s = e-iFaceIndex+2;
if ( s == 4 )
{
unsigned int b[4] = { 0, 0, 1, 1 };
unsigned int s[4] = { 2, 0, 0, 1 };
return AdjacentFaceForTriangle( iFaceIndex+b[iSide], s[iSide], 1, iEdge );
};
if ( iSide == 0 || iSide == 3 )
{
unsigned int l = iFaceID-m_aFaceID[iFaceIndex];
return 0xffffffff;
if ( iSide )
l = (l+s-1)%s;
if ( l == 0 )
return AdjacentFaceForTriangle( iFaceIndex, 2, iSide ? 0 : 1, iEdge );
if ( l == s-1 )
return AdjacentFaceForTriangle( iFaceIndex+s-3, 1, iSide ? 0 : 1, iEdge );
return AdjacentFaceForTriangle( iFaceIndex+l-1, 0, iSide ? 0 : 1, iEdge );
};
if ( iSide == 2 )
iEdge = 1;
else
iEdge = 2;
return m_aFaceID[iFaceIndex]+((iFaceID-m_aFaceID[iFaceIndex]+(iSide==2 ? -1 : 1)+s)%s);
};