#pragma warning( disable : 4311 4312 )
#include "displacer.h"
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtGui/QDialog>
#include <QtGui/QDialogButtonBox>
#if defined (JAMBUILD)
#else
#include "../../include/MapExtractorAPI/MapExtractorAPI.h"
#endif
MB_PLUGIN(
"MeshDisplacer",
"Displacement operation for meshes",
"Autodesk",
"http://www.mudbox3d.com", DisplaceOperation::Initializer );
DisplaceOperation::DisplaceOperation( void ) :
Node( "Displacement" ),
m_aTiles( "displaceoperation tilelist" ),
m_pObject( this, "Target Mesh" ),
m_iSubdivisionLevel( this,
NTR(
"Displace To Level") ),
m_iMaskChannel( this,
NTR(
"Use Mask Channel") ),
m_iUDim( this,
NTR(
"URange") ),
m_iFirstTileIndex( this,
NTR(
"Initial Tile Number") ),
m_sTileRange( this,
NTR(
"Use Tile Range") ),
m_sDisplacementFileMask( this,
NTR(
"Map") ),
m_sMaskFileMask( this,
NTR(
"Mask Map") ),
m_iFinalFaceCount( this,
NTR(
"Final Face Count") ),
m_eExecute( this,
NTR(
"Go") ),
m_eDelete( this,
NTR(
"Delete this operation") ),
m_iDisplacementChannel( this,
NTR(
"Use Displacement Channel") ),
m_bSmoothTC( this,
NTR(
"Smooth Texture Coors") ),
m_sNextCommand( this,
NTR(
"Next Command") ),
m_eMapSpace( this,
NTR(
"Map Space") ),
m_fMidvalue( this,
NTR(
"Mid value") ),
m_fMultiplier( this,
NTR(
"Multiplier") )
{
m_eMapSpace.AddItem( tr("Normal (regular displacement map)") );
m_eMapSpace.AddItem( tr("Relative Tangent") );
m_eMapSpace.AddItem( tr("Absolute Tangent") );
m_eMapSpace.AddItem( tr("Object") );
m_eMapSpace.AddItem( tr("World") );
m_eMapSpace.SetCategory( "#top" );
m_eMapSpace = 0;
m_eMapSpace.SetVisible( true );
unsigned int i = 1;
do
{
b = false;
for ( Node *
p = First();
p;
p =
p->Next() )
if (
p->IsKindOf( StaticClass() ) )
{
if ( sName == dynamic_cast<DisplaceOperation *>(
p )->Name() )
{
i++;
b = true;
};
};
} while ( b );
SetName( sName );
m_bFloatMap = false;
m_iBaseU = 0;
m_iBaseV = 0;
m_pObject.m_sNullString = tr("- Select a Mesh -");
LoadTemplate();
m_fMidvalue.SetVisible( true );
m_fMultiplier.SetVisible( true );
m_sMaskFileMask.SetVisible( true );
m_sNextCommand.SetVisible( false );
m_sNextCommand.Connect(
Kernel()->NextCommand );
};
QWidget* DisplaceOperation::CreatePropertiesWindow(
QWidget *pParent )
{
QWidget* pW = TreeNode::CreatePropertiesWindow(pParent);
SetName(sName);
HelpButton* pHelpButton =
new HelpButton( pW,
NTRQ(
"SculptUsingDisplacementHelp") );
pHelpButton->setFixedWidth( 120 );
return pW;
};
void DisplaceOperation::Serialize( Stream &
s )
{
s == m_pObject == m_iSubdivisionLevel == m_iFinalFaceCount == m_iUDim == m_iFirstTileIndex == m_sTileRange == m_sDisplacementFileMask == m_sMaskFileMask == m_iMaskChannel;
if( s.IsOlderThan( 300 ) )
{
s == s1;
SetName( s1 );
};
if ( !s.IsOlderThan( 155+1 ) )
s == m_bSmoothTC;
if ( s.IsNewerThan( 340 ) )
TreeNode::Serialize( s );
if ( s.IsNewerThan( 0, this ) )
s == m_eMapSpace;
};
void DisplaceOperation::OnNodeEvent(
const Attribute &
a,
NodeEventType eType )
{
{
if ( m_pObject )
m_iSubdivisionLevel = m_pObject->LevelCount()-1;
};
if ( (a == m_iSubdivisionLevel || a == m_pObject) && eType ==
etValueChanged )
{
if ( m_pObject.Value() && m_iSubdivisionLevel <
int(m_pObject->LevelCount()-1) )
{
m_iSubdivisionLevel = m_pObject->LevelCount()-1;
return;
};
if ( m_pObject != 0 && m_iSubdivisionLevel != -1)
m_iFinalFaceCount =
int(m_pObject->LowestLevel()->TotalFaceCount()*pow(
float(4),m_iSubdivisionLevel));
else
m_iFinalFaceCount = 0;
};
{
if ( sOp == "meshdisplacement" )
{
if ( sName == Name() )
Do();
};
};
{
QString sFN = m_sDisplacementFileMask.Value();
m_sDisplacementFileMask = FilterFileName( sFN );
};
{
QString sFN = m_sMaskFileMask.Value();
m_sMaskFileMask = FilterFileName( sFN );
};
{
Kernel()->RecordCommand(
NTRQ(
"meshdisplacement \"%1\"").arg(Name()) );
Do();
};
{
Kernel()->Scene()->RemoveChild(
this);
Kernel()->Interface()->RefreshUI();
delete this;
}
}
void DisplaceOperation::Do( void )
{
if ( m_pObject == 0 )
throw new Error( tr("You must select a mesh in the scene first") );
if ( i.suffix() == "ptx" )
{
if ( m_eMapSpace == spaceAbsoluteTangent || m_eMapSpace ==
spaceTangent )
{
m_pObject->ActiveLevel()->RecreateUVs();
if ( !m_pObject->ActiveLevel()->HasTC() || m_pObject->LowestLevel()->UVlessPaintingStatus() )
throw new Error( tr("Tangent space can only be selected if the target mesh has valid texture coordinates") );
};
Kernel()->Interface()->ProgressStartGroup( tr(
"Sculpting mesh..."), 51 );
Geometry *pG = dynamic_cast<Geometry *>( Geometry::CreateInstances() );
m_pObject->LowestLevel()->CopyTo( pG->Level( 0 ) );
pG->SetMaterial( CreateInstance<Material>() );
Transformation *pT = CreateInstance<Transformation>();
pT->AddChild( pG );
aptr<Material> pM; pM = pG->Material();
Layer *pL = pM->ImportPaintLayer( m_sDisplacementFileMask, "Diffuse", pG->Level( 0 ) );
if ( !pL )
MB_ERROR( tr(
"Cannot apply the selected file as a displacement map. Topology in the Ptex file does not match the target model.") );
else
{
if ( m_eMapSpace == spaceAbsoluteTangent )
};
c.
SetVertexFactor( Vector( m_fMultiplier, m_fMultiplier, m_fMultiplier ) );
float o = -m_fMidvalue*m_fMultiplier;
aptr<Material> pLM = pG->Material();
delete pT;
if ( pM )
pM.DeleteTarget();
if ( pLM )
pLM.DeleteTarget();
if ( m_sMaskFileMask != "" )
{
Geometry *pG = dynamic_cast<Geometry *>( Geometry::CreateInstances() );
m_pObject->LowestLevel()->CopyTo( pG->Level( 0 ) );
pG->SetMaterial( CreateInstance<Material>() );
Transformation *pT = CreateInstance<Transformation>();
pT->AddChild( pG );
aptr<Material> pM; pM = pG->Material();
Layer *pL = pM->ImportPaintLayer( m_sMaskFileMask, "Diffuse", pG->Level( 0 ) );
if ( !pL )
MB_ERROR( tr(
"Cannot apply the selected file as a displacement map. Topology in the Ptex file does not match the target model.") );
aptr<Material> pLM = pG->Material();
delete pT;
if ( pM )
pM.DeleteTarget();
if ( pLM )
pLM.DeleteTarget();
};
Kernel()->Interface()->ProgressEnd();
Kernel()->Interface()->RefreshUI();
return;
};
if ( m_pObject->LowestLevel()->AttributeValue(
NTRQ(
"isptexuvlayout") ) ==
"true" )
MB_ERROR( tr(
"You can only use image textures for meshes without Ptex setup. For this mesh, use a ptx file." ) );
AxisAlignedBoundingBox
b;
for ( unsigned int i = 0; i < m_pObject->LowestLevel()->TCCount(); i++ )
b.Extend( Vector( m_pObject->LowestLevel()->m_pTCs[i].m_fU, m_pObject->LowestLevel()->m_pTCs[i].m_fV ) );
if ( float(m_iUDim) < ceilf(b.m_vEnd.x) )
m_iUDim = int(ceilf(b.m_vEnd.x));
m_iBaseU =
int(floorf(b.m_vStart.x));
m_iBaseV =
int(floorf(b.m_vStart.y));
m_aTiles.Clear();
QString sTileRange = m_sTileRange.Value();
{
int p = 0, i[2] = { 0, 0 }, k = 0;
while ( p < sTileRange.
size() )
{
if ( p == sTileRange.
size() || sTileRange[
p] ==
',' )
{
i[1] =
Max( i[1], i[0] );
for (
int h = i[0];
h <= i[1];
h++ )
{
t.m_bZero = false;
m_aTiles.Add( t );
};
i[0] = i[1] = 0;
k = 0;
};
else if ( sTileRange[p] == '-' )
k = 1;
p++;
};
i[1] =
Max( i[1], i[0] );
for (
int h = i[0];
h <= i[1];
h++ )
{
t.m_bZero = false;
m_aTiles.Add( t );
};
int iWidth =
int(ceilf(b.m_vEnd.x)-floorf(b.m_vStart.x));
int iHeight =
int(ceilf(b.m_vEnd.y)-floorf(b.m_vStart.y));
int iStart = m_iFirstTileIndex;
if ( iWidth > m_iUDim )
m_iUDim = iWidth;
int iEnd = m_iFirstTileIndex+m_iUDim*iHeight;
for ( int i = iStart; i < iEnd; i++ )
{
unsigned int j = 0;
while ( j < m_aTiles.ItemCount() && m_aTiles[j].m_iIndex != i )
j++;
if ( j < m_aTiles.ItemCount() )
continue;
t.m_iIndex = i;
t.m_bZero = true;
m_aTiles.Add( t );
};
MB_ASSERT( m_aTiles.ItemCount() == iEnd-iStart );
}
else
{
int iWidth =
int(ceilf(b.m_vEnd.x)-floorf(b.m_vStart.x));
int iHeight =
int(ceilf(b.m_vEnd.y)-floorf(b.m_vStart.y));
int iStart = m_iFirstTileIndex;
if ( iWidth > m_iUDim )
m_iUDim = iWidth;
int iEnd = m_iFirstTileIndex+m_iUDim*iHeight;
for ( int i = iStart; i < iEnd; i++ )
{
t.m_iIndex = i;
t.m_bZero = false;
m_aTiles.Add( t );
};
};
Execute();
};
QString DisplaceOperation::GetFileName(
const QString &sMask,
unsigned int iTile,
unsigned int iUPos,
unsigned int iVPos )
const
{
QDir d = i.absoluteDir();
if ( m_aTiles.ItemCount() == 1 )
g = i.completeBaseName()+"*."+i.suffix();
else
{
int iU = iUPos, iV = iVPos;
if ( iU >= 0 )
iU++;
if ( iV >= 0 )
iV++;
g = i.completeBaseName()+
QString(
"_u%1_v%2*.").
arg(iU).
arg(iV)+i.suffix();
};
{
Kernel()->Interface()->MessageBox(
mudbox::Interface::msgWarning, tr(
"Warning"), tr(
"More than one files beginning with the mask %1 is found. The result of the mesh displacement might be incorrect. Delete the unneeded ones to solve the problem.").arg(g) );
};
return "";
};
QString DisplaceOperation::FilterFileName(
const QString &sFileName )
const
{
m_fMultiplier = 1.0f;
m_fMidvalue = 0.0f;
int k = sFileName.
size();
while ( k > 0 )
{
k--;
if ( sFileName[k] == '/' || sFileName[k] == '\\' )
break;
if ( sFileName[k] == '.' )
{
break;
};
};
for (
int j = s.
size()-1; j >= 0; j-- )
{
if ( s[j] == '_' )
{
if ( j < s.
size()-1 && s[j+1] ==
'g' )
{
float fGain = s.
right( s.
size()-2-j ).toFloat();
m_fMultiplier = fGain;
m_fMidvalue = 0.5f;
};
continue;
};
if ( s[j] != 'u' && s[j] != 'v' && s[j] != 'g' && s[j] != 'e' && s[j] != '-' && s[j] != '+' && s[j] != '.' && (s[j] < '0' || s[j] > '9') )
break;
};
return s+"."+e;
};
void DisplaceOperation::Execute( void )
{
Instance<Image> pDisplacement, pMask;
if ( !m_pObject )
throw new Error( tr("No target specified") );
SubdivisionLevel::SetUVCreation( true );
SubdivisionLevel *pM = m_pObject->LowestLevel();
if ( pM->TCCount() == 0 )
throw new Error(tr("Target mesh has no texture coordinates"));
while ( int(m_pObject->LevelCount()-1) < m_iSubdivisionLevel )
{
m_pObject->HighestLevel()->Subdivide();
if ( m_bSmoothTC )
m_pObject->HighestLevel()->SmoothTextureCoordinates( 1.0f );
};
pM = m_pObject->Level( m_iSubdivisionLevel );
if ( pM == 0 )
MB_ERROR( tr(
"Corrupt subdivision structure") );
m_pObject->SetActiveLevel( pM );
pM->AddFaceComponent( pM->fcTCIndex );
Kernel()->Log(
NTRQ(
"Displacement operation on %1\n").arg(pM->Name()) );
Kernel()->Interface()->ProgressStart( tr(
"applying maps..."), m_aTiles.ItemCount()*pM->FaceCount() );
Store<VertexInfo> aVertices( "displaceoperation vertex tmp" );
TangentGenerator *pTG = NULL;
if ( m_eMapSpace ==
spaceTangent || m_eMapSpace == spaceAbsoluteTangent )
pTG = pM->ChildByClass<TangentGenerator>( true );
for ( unsigned int i = 0; i < m_aTiles.ItemCount(); i++ )
{
unsigned int iTile = m_aTiles[i].m_iIndex;
bool bZero = m_aTiles[i].m_bZero;
int iUPos = (iTile-m_iFirstTileIndex)%m_iUDim+m_iBaseU, iVPos = (iTile-m_iFirstTileIndex)/m_iUDim+m_iBaseV;
Kernel()->Log(
NTRQ(
"Displacing tile %1\n").arg(iTile) );
bool bMask = false;
if ( !bZero )
{
QString sFileName = GetFileName( m_sDisplacementFileMask.Value(), iTile, iUPos, iVPos );
Kernel()->Log(
NTRQ(
"\tLoading %1 for dislacement map").arg(sFileName) );
try
{
pDisplacement->Load( sFileName );
}
catch ( Error *pError )
{
Kernel()->Log(
" - map not found, skipping\n" );
pError->Discard();
continue;
};
if ( m_sMaskFileMask == "" )
bMask = false;
else
{
QString sFileName = GetFileName( m_sMaskFileMask.Value(), iTile, iUPos, iVPos );
Kernel()->Log(
NTRQ(
"\tLoading %1 for mask map").arg(sFileName) );
bMask = true;
try { pMask->Load( sFileName ); }
catch ( Error *pError ) { pError->Discard(); bMask = false; };
if ( bMask )
else
Kernel()->Log(
" - not found\n" );
};
};
unsigned int iBefore = aVertices.ItemCount();
Store<int> aProcessedTC( pM->TCCount(), "displace - processedtc" );
for ( unsigned int j = 0; j < pM->TCCount(); j++ )
aProcessedTC[j] = -1;
float fDisplacementMin = -m_fMidvalue*m_fMultiplier;
float fDisplacementMax = (1-m_fMidvalue)*m_fMultiplier;
Vector vMin( fDisplacementMin, fDisplacementMin, fDisplacementMin );
Vector vMax( fDisplacementMax, fDisplacementMax, fDisplacementMax );
for (
unsigned int v = 0;
v < pM->FaceCount();
v++ )
{
Kernel()->Interface()->ProgressSet( i*pM->FaceCount()+
v );
for ( int c = 0; c < pM->SideCount(); c++ )
{
unsigned int iVertexIndex, iTCIndex;
if ( pM->Type() == Mesh::typeTriangular )
{
iVertexIndex = pM->TriangleIndex(
v, c );
iTCIndex = pM->TriangleTCI(
v, c );
}
else
{
iVertexIndex = pM->QuadIndex(
v, c );
iTCIndex = pM->QuadTCI(
v, c );
};
continue;
if ( pM->VertexStrokeID( iVertexIndex ) == pM->CollectionID() )
continue;
float fU = pM->VertexTC( iTCIndex ).m_fU-float(iUPos), fV = (pM->VertexTC( iTCIndex ).m_fV-float(iVPos));
if ( fU < 0.0f || fU > 1.0f || fV < 0.0f || fV > 1.0f )
continue;
pM->SetVertexStrokeID( iVertexIndex, pM->CollectionID() );
if ( bZero )
{
if ( aProcessedTC[iTCIndex] != -1 )
aVertices[aProcessedTC[iTCIndex]].Refine( Vector(), 1 );
continue;
};
float fMask = 1;
Vector vDisp;
vDisp.x = pDisplacement->ValueAt( fU, fV, 0 );
if ( m_eMapSpace.Value() )
{
vDisp.y = pDisplacement->ValueAt( fU, fV, 1 );
vDisp.z = pDisplacement->ValueAt( fU, fV, 2 );
};
vDisp = vMin+vDisp*(vMax-vMin);
if ( bMask )
fMask = pMask->ValueAt( fU, fV, 0 );
if ( vDisp )
{
if ( aProcessedTC[iTCIndex] != -1 )
{
aVertices[aProcessedTC[iTCIndex]].Refine( vDisp, fMask );
}
else
{
unsigned int iIndex;
iIndex = aVertices.Add( VertexInfo( iVertexIndex, iVertexIndex, vDisp, fMask,
v, c ) );
if ( iIndex == 0xffffffff )
{
Kernel()->Log(
"\tOut of memory\n" );
throw &Error::s_cBadAlloc;
};
aProcessedTC[iTCIndex] = iIndex;
};
};
};
};
Kernel()->Log(
NTRQ(
"\tProcessed %1 vertices\n").arg( aVertices.ItemCount()-iBefore ) );
};
pM->IncreaseCollectionID();
LayerMeshData *pL = pL = pM->AddLayer();
if ( pL == 0 )
throw Error( tr("Unable to create new layer") );
pL->SetName( "Displacement" );
{
Layer *pLayer = pL->Layer();
LayerContainer *pLC = pLayer->Container();
pLC->SetActiveLayer(pLayer);
}
pL->SetVertexCount( aVertices.ItemCount() );
aVertices.Sort();
for ( unsigned int k = 0; k < aVertices.ItemCount(); k++ )
{
Vector vDelta;
if ( m_eMapSpace.Value() )
{
vDelta = aVertices[k].m_vDisplacement;
if ( m_eMapSpace ==
spaceTangent || m_eMapSpace == spaceAbsoluteTangent )
{
{
Base b = pTG->LocalBase( aVertices[k].m_iFaceIndex, aVertices[k].m_iCornerIndex );
{
float al = b.a.Length();
float bl = b.b.Length();
float cl = b.c.Length();
b.b *= sqrtf(al*cl)/bl;
}
else
{
b.c = (b.b&b.a).Normalized();
b.a = (b.c&b.b).Normalized();
}
vDelta = b.TransformFrom( vDelta );
};
};
vDelta = pM->Geometry()->Transformation()->TransformToLocal( vDelta, 0 );
}
else
vDelta = Vector(pM->VertexNormal( aVertices[k].m_iVertexIndex ))*aVertices[k].m_vDisplacement.x;
pL->SetVertexData( k, aVertices[k].m_iGlobalVertexIndex, aVertices[k].m_fMask );
pL->SetVertexDelta( k, aVertices[k].m_iVertexIndex, vDelta );
};
pM->Modified.Trigger();
pM->ContentChanged();
pM->RecalculateNormals();
Kernel()->Interface()->ProgressEnd();
Kernel()->Interface()->RefreshUI();
};
void DisplaceOperation::Initializer( void )
{
};