uid issue
This commit is contained in:
393
meshutils/uvparam.cpp
Normal file
393
meshutils/uvparam.cpp
Normal file
@ -0,0 +1,393 @@
|
||||
//=========== Copyright <20> Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Mesh class UV parameterization operations.
|
||||
//
|
||||
//===========================================================================//
|
||||
#include "mesh.h"
|
||||
#include "tier1/utlbuffer.h"
|
||||
|
||||
Vector4D PlaneFromTriangle( Vector &A, Vector &B, Vector &C )
|
||||
{
|
||||
// Calculate normal
|
||||
Vector vAB = B - A;
|
||||
Vector vAC = C - A;
|
||||
Vector vNorm = -CrossProduct( vAC, vAB );
|
||||
vNorm.NormalizeInPlace();
|
||||
|
||||
float d = DotProduct( A, vNorm );
|
||||
return Vector4D( vNorm.x, vNorm.y, vNorm.z, d );
|
||||
}
|
||||
|
||||
Vector4D CMesh::PlaneFromTriangle( int nTriangle ) const
|
||||
{
|
||||
Vector A = *(Vector*)GetVertex( m_pIndices[ nTriangle * 3 ] );
|
||||
Vector B = *(Vector*)GetVertex( m_pIndices[ nTriangle * 3 + 1 ] );
|
||||
Vector C = *(Vector*)GetVertex( m_pIndices[ nTriangle * 3 + 2 ] );
|
||||
|
||||
return ::PlaneFromTriangle( A, B, C );
|
||||
}
|
||||
|
||||
int AddTriangleToChart( const CMesh &inputMesh, int nTriangle, UVChart_t *pChart, float flThreshold, CUtlVector<bool> &usedTriangles, int* pAdjacency )
|
||||
{
|
||||
if ( usedTriangles[ nTriangle ] == true )
|
||||
return 0;
|
||||
|
||||
Vector4D vTriPlane = inputMesh.PlaneFromTriangle( nTriangle );
|
||||
float flDot = DotProduct( vTriPlane.AsVector3D(), pChart->m_vPlane.AsVector3D() );
|
||||
if ( flDot < flThreshold )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add this triangle to the chart
|
||||
pChart->m_TriangleList.AddToTail( nTriangle );
|
||||
usedTriangles[ nTriangle ] = true;
|
||||
|
||||
int nAdded = 1;
|
||||
|
||||
// Add any adjacent triangles to the chart
|
||||
for ( int i=0; i<3; ++i )
|
||||
{
|
||||
int nAdj = pAdjacency[ nTriangle * 3 + i ];
|
||||
if ( nAdj != -1)
|
||||
{
|
||||
nAdded += AddTriangleToChart( inputMesh, nAdj, pChart, flThreshold, usedTriangles, pAdjacency );
|
||||
}
|
||||
}
|
||||
|
||||
return nAdded;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// CreateUniqueUVParameterization
|
||||
//
|
||||
// Creates a unique parameterization for the mesh in 0..1 UV space. The mesh is assumed
|
||||
// to be welded and clean before this is called.
|
||||
//--------------------------------------------------------------------------------------
|
||||
bool CreateUniqueUVParameterization( CMesh *pMeshOut, const CMesh &inputMesh, float flThreshold, int nAtlasTextureSizeX, int nAtlasTextureSizeY, float flGutterSize )
|
||||
{
|
||||
int nMaxCharts = 10000;
|
||||
|
||||
// Generate adjacency
|
||||
int *pAdjacencyBuffer = new int[ inputMesh.m_nIndexCount ];
|
||||
if ( !inputMesh.CalculateAdjacency( pAdjacencyBuffer, inputMesh.m_nIndexCount ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int nPosOffset = inputMesh.FindFirstAttributeOffset( VERTEX_ELEMENT_POSITION );
|
||||
int nTexOffset = inputMesh.FindFirstAttributeOffset( VERTEX_ELEMENT_TEXCOORD2D_0 );
|
||||
if ( nTexOffset == -1 )
|
||||
{
|
||||
nTexOffset = inputMesh.FindFirstAttributeOffset( VERTEX_ELEMENT_TEXCOORD3D_0 );
|
||||
}
|
||||
if ( nTexOffset == -1 || nPosOffset == -1)
|
||||
{
|
||||
Warning( "Cannot create UV parameterization without position or texcoords!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now go through the triangles and add them to charts based upon minimum angle between charts
|
||||
CUtlVector<int> triangleIndices;
|
||||
CUtlVector<bool> usedTriangles;
|
||||
CUtlVector<UVChart_t*> chartList;
|
||||
int nTris = inputMesh.m_nIndexCount / 3;
|
||||
triangleIndices.EnsureCount( nTris );
|
||||
usedTriangles.EnsureCount( nTris );
|
||||
for( int t=0; t<nTris; ++t )
|
||||
{
|
||||
triangleIndices[t] = t;
|
||||
usedTriangles[t] = false;
|
||||
}
|
||||
|
||||
bool *pUsedTriangles = usedTriangles.Base();
|
||||
|
||||
int nRemaining = nTris;
|
||||
while( nRemaining > 0 )
|
||||
{
|
||||
// Select a random triangle
|
||||
int nTriangle = -1;
|
||||
for( int t=0; t<nTris; ++t )
|
||||
{
|
||||
if ( pUsedTriangles[t] == false )
|
||||
{
|
||||
nTriangle = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( nTriangle > -1 )
|
||||
{
|
||||
Vector4D vTriPlane = inputMesh.PlaneFromTriangle( nTriangle );
|
||||
|
||||
if ( vTriPlane.AsVector3D().LengthSqr() < 0.9f )
|
||||
{
|
||||
// degenerate tri, just get rid of it
|
||||
pUsedTriangles[ nTriangle ] = true;
|
||||
nRemaining --;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create a new chart
|
||||
UVChart_t *pNewChart = new UVChart_t;
|
||||
pNewChart->m_vPlane = vTriPlane;
|
||||
pNewChart->m_vMinUV = Vector2D( FLT_MAX, FLT_MAX );
|
||||
pNewChart->m_vMaxUV = Vector2D( -FLT_MAX, -FLT_MAX );
|
||||
|
||||
int nAdded = AddTriangleToChart( inputMesh, nTriangle, pNewChart, flThreshold, usedTriangles, pAdjacencyBuffer );
|
||||
if ( nAdded < 1 )
|
||||
{
|
||||
Msg( "Error: didn't add any triangles to chart: %d\n", nTriangle );
|
||||
}
|
||||
nRemaining -= nAdded;
|
||||
|
||||
// Add the chart to the list
|
||||
chartList.AddToTail( pNewChart );
|
||||
|
||||
if ( chartList.Count() > nMaxCharts )
|
||||
{
|
||||
nRemaining = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( nRemaining == 0 );
|
||||
nRemaining = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
delete []pAdjacencyBuffer;
|
||||
pAdjacencyBuffer = NULL;
|
||||
|
||||
// create a local texture vector
|
||||
CUtlVector<AtlasChart_t> atlasChartVector;
|
||||
|
||||
CMesh tempMesh;
|
||||
int nTotalChartTriangles = 0;
|
||||
int nCharts = chartList.Count();
|
||||
float flTotalArea = 0;
|
||||
if ( nCharts < nMaxCharts )
|
||||
{
|
||||
// Average each chart's plane and create a new texture for each chart
|
||||
int nCharts = chartList.Count();
|
||||
atlasChartVector.EnsureCount( nCharts );
|
||||
|
||||
for ( int c=0; c<nCharts; ++c )
|
||||
{
|
||||
UVChart_t *pChart = chartList[c];
|
||||
|
||||
int nTris = pChart->m_TriangleList.Count();
|
||||
Vector4D vPlane(0,0,0,0);
|
||||
for ( int p=0; p<nTris; ++p )
|
||||
{
|
||||
int iTri = pChart->m_TriangleList[p];
|
||||
vPlane += inputMesh.PlaneFromTriangle( iTri );
|
||||
}
|
||||
|
||||
nTotalChartTriangles += nTris;
|
||||
|
||||
Vector vNorm = vPlane.AsVector3D().Normalized();
|
||||
pChart->m_vPlane = Vector4D( vNorm.x, vNorm.y, vNorm.z, 0 );
|
||||
|
||||
AtlasChart_t AtlasChart;
|
||||
AtlasChart.m_bAtlased = false;
|
||||
AtlasChart.m_vAtlasMin.Init( 0, 0 );
|
||||
AtlasChart.m_vAtlasMax.Init( 0, 0 );
|
||||
AtlasChart.m_vMaxTextureSize.Init( 0, 0 );
|
||||
atlasChartVector[ c ] = AtlasChart;
|
||||
}
|
||||
|
||||
// Create a new temporary mesh
|
||||
tempMesh.AllocateMesh( nTotalChartTriangles * 3, nTotalChartTriangles * 3, inputMesh.m_nVertexStrideFloats, inputMesh.m_pAttributes, inputMesh.m_nAttributeCount );
|
||||
|
||||
// loop through all charts and add the vertices and indices to the new mesh
|
||||
int nNewVertex = 0;
|
||||
flTotalArea = 0.0f;
|
||||
for ( int c=0; c<nCharts; ++c )
|
||||
{
|
||||
UVChart_t *pChart = chartList[c];
|
||||
|
||||
Vector vNorm = pChart->m_vPlane.AsVector3D();
|
||||
Vector vUp(0,1,0);
|
||||
if ( DotProduct( vNorm, vUp ) > 0.95f )
|
||||
vUp = Vector(0,0,1);
|
||||
|
||||
Vector vRight = CrossProduct( vNorm, vUp );
|
||||
vRight.NormalizeInPlace();
|
||||
vUp = CrossProduct( vRight, vNorm );
|
||||
vUp.NormalizeInPlace();
|
||||
|
||||
pChart->m_nVertexStart = nNewVertex;
|
||||
|
||||
// project the vertices onto the chart plane
|
||||
// and find the min and max plane boundaries
|
||||
int nTris = pChart->m_TriangleList.Count();
|
||||
for ( int t=0; t<nTris; ++t )
|
||||
{
|
||||
int iTri = pChart->m_TriangleList[t];
|
||||
|
||||
for ( int i=0; i<3; ++i )
|
||||
{
|
||||
int nIndex = inputMesh.m_pIndices[ iTri * 3 + i ];
|
||||
float *pVert = (float*)inputMesh.GetVertex( nIndex );
|
||||
Vector &vPos = *( ( Vector* )( pVert + nPosOffset ) );
|
||||
|
||||
Vector2D vTexcoord;
|
||||
vTexcoord.x = DotProduct( vPos, vRight );
|
||||
vTexcoord.y = DotProduct( vPos, vUp );
|
||||
|
||||
pChart->m_vMinUV.x = MIN( vTexcoord.x, pChart->m_vMinUV.x );
|
||||
pChart->m_vMinUV.y = MIN( vTexcoord.y, pChart->m_vMinUV.y );
|
||||
pChart->m_vMaxUV.x = MAX( vTexcoord.x, pChart->m_vMaxUV.x );
|
||||
pChart->m_vMaxUV.y = MAX( vTexcoord.y, pChart->m_vMaxUV.y );
|
||||
|
||||
float *pNewVert = tempMesh.GetVertex( nNewVertex );
|
||||
|
||||
// New vertex
|
||||
CopyVertex( pNewVert, pVert, inputMesh.m_nVertexStrideFloats );
|
||||
Vector2D *pNewTex = (Vector2D*)( pNewVert + nTexOffset );
|
||||
*pNewTex = vTexcoord;
|
||||
|
||||
// New index
|
||||
tempMesh.m_pIndices[ nNewVertex ] = nNewVertex;
|
||||
|
||||
nNewVertex++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pChart->m_nVertexCount = nNewVertex - pChart->m_nVertexStart;
|
||||
|
||||
// update size of the texture
|
||||
AtlasChart_t &chart = atlasChartVector[ c ];
|
||||
chart.m_vMaxTextureSize.x = pChart->m_vMaxUV.x - pChart->m_vMinUV.x;
|
||||
chart.m_vMaxTextureSize.y = pChart->m_vMaxUV.y - pChart->m_vMinUV.y;
|
||||
flTotalArea += chart.m_vMaxTextureSize.x * chart.m_vMaxTextureSize.y;
|
||||
|
||||
Vector2D vChartUVDelta = pChart->m_vMaxUV - pChart->m_vMinUV;
|
||||
|
||||
// Normalize texture coordinates within the chart plane boundaries
|
||||
if ( vChartUVDelta.x == 0 || vChartUVDelta.y == 0 )
|
||||
{
|
||||
// Zero texcoords if our chart is infintesimally small
|
||||
for ( int v=pChart->m_nVertexStart; v<pChart->m_nVertexStart + pChart->m_nVertexCount; ++v )
|
||||
{
|
||||
float *pNewVert = tempMesh.GetVertex( v );
|
||||
Vector2D *pNewTex = (Vector2D*)( pNewVert + nTexOffset );
|
||||
*pNewTex = Vector2D(0,0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int v=pChart->m_nVertexStart; v<pChart->m_nVertexStart + pChart->m_nVertexCount; ++v )
|
||||
{
|
||||
float *pNewVert = tempMesh.GetVertex( v );
|
||||
Vector2D *pNewTex = (Vector2D*)( pNewVert + nTexOffset );
|
||||
|
||||
Vector2D vNewTex = ( *pNewTex - pChart->m_vMinUV ) / vChartUVDelta;
|
||||
|
||||
*pNewTex = vNewTex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool bMadeAtlas = false;
|
||||
|
||||
if ( nCharts < nMaxCharts )
|
||||
{
|
||||
// We made a chart
|
||||
bMadeAtlas = true;
|
||||
|
||||
// Create an atlas
|
||||
Msg( "Attempting to atlas %d charts\n", nCharts );
|
||||
|
||||
int nAtlasSideSize = (int)(sqrtf( flTotalArea ));
|
||||
int nGrowAmount = MAX( 8, nAtlasSideSize / 64 );
|
||||
nAtlasSideSize -= nGrowAmount * 2;
|
||||
|
||||
PackChartsIntoAtlas( atlasChartVector.Base(), atlasChartVector.Count(), nAtlasSideSize, nAtlasSideSize, nGrowAmount );
|
||||
|
||||
Vector2D vTextureSize( nAtlasTextureSizeX, nAtlasTextureSizeY );
|
||||
Vector2D vGutterOffset( flGutterSize / vTextureSize.x, flGutterSize / vTextureSize.y );
|
||||
|
||||
// Update triangle coordinates to fit into this atlas
|
||||
for ( int c=0; c<nCharts; ++c )
|
||||
{
|
||||
UVChart_t *pChart = chartList[ c ];
|
||||
AtlasChart_t &atlasData = atlasChartVector[ c ];
|
||||
Vector2D vAtlasMin = ( atlasData.m_vAtlasMin ) + vGutterOffset;
|
||||
Vector2D vAtlasMax = ( atlasData.m_vAtlasMax ) - vGutterOffset;
|
||||
Vector2D vDeltaAtlas = vAtlasMax - vAtlasMin;
|
||||
Vector2D vDeltaBounds(1,1);
|
||||
Vector2D vAtlasUVSize(0,0);
|
||||
if ( vDeltaBounds.x != 0.0f && vDeltaBounds.y != 0.0f )
|
||||
vAtlasUVSize = vDeltaAtlas / vDeltaBounds;
|
||||
Vector2D vShift = vAtlasMin - Vector2D(0,0) * vAtlasUVSize;
|
||||
|
||||
for ( int v=pChart->m_nVertexStart; v<pChart->m_nVertexStart + pChart->m_nVertexCount; ++v )
|
||||
{
|
||||
float *pNewVert = tempMesh.GetVertex( v );
|
||||
Vector2D *pNewTex = (Vector2D*)( pNewVert + nTexOffset );
|
||||
|
||||
pNewTex->x = pNewTex->x * vAtlasUVSize.x + vShift.x;
|
||||
pNewTex->y = pNewTex->y * vAtlasUVSize.y + vShift.y;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean and weld the mesh
|
||||
float flEpsilon = 1e-6;
|
||||
float *pEpsilons = new float[ tempMesh.m_nVertexStrideFloats ];
|
||||
for ( int e=0; e<tempMesh.m_nVertexStrideFloats; ++e )
|
||||
{
|
||||
pEpsilons[ e ] = flEpsilon;
|
||||
}
|
||||
WeldVertices( pMeshOut, tempMesh, pEpsilons, tempMesh.m_nVertexStrideFloats );
|
||||
|
||||
delete []pEpsilons;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We didn't make a chart
|
||||
bMadeAtlas = false;
|
||||
|
||||
// Create an atlas
|
||||
Msg( "Too many charts (%d), creating planar mapping\n", nCharts );
|
||||
|
||||
// Create a planar projection
|
||||
Vector vMinBounds;
|
||||
Vector vMaxBounds;
|
||||
inputMesh.CalculateBounds( &vMinBounds, &vMaxBounds );
|
||||
|
||||
// BBox delta
|
||||
Vector vBoundsDelta = vMaxBounds - vMinBounds;
|
||||
|
||||
DuplicateMesh( pMeshOut, inputMesh );
|
||||
|
||||
// Update UVs based on... shakes magic 8 ball... XZ projection, for now
|
||||
for ( int v=0; v<pMeshOut->m_nVertexCount; ++v )
|
||||
{
|
||||
float *pNewVert = pMeshOut->GetVertex( v );
|
||||
Vector *pPos = ( Vector* )( pNewVert + nPosOffset );
|
||||
Vector2D *pNewTex = ( Vector2D* )( pNewVert + nTexOffset );
|
||||
|
||||
pNewTex->x = ( pPos->x - vMinBounds.x ) / vBoundsDelta.x;
|
||||
pNewTex->y = ( pPos->z - vMinBounds.z ) / vBoundsDelta.z;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the charts
|
||||
nCharts = chartList.Count();
|
||||
for ( int c=0; c<nCharts; ++c )
|
||||
{
|
||||
UVChart_t *pChart = chartList[c];
|
||||
delete pChart;
|
||||
}
|
||||
chartList.Purge();
|
||||
|
||||
return bMadeAtlas;
|
||||
}
|
Reference in New Issue
Block a user