2012-05-29 13:31:11 +00:00
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "std3d.h"
# include "nel/3d/tangent_space_build.h"
# include "nel/3d/index_buffer.h"
# include "nel/3d/vertex_buffer.h"
# include "nel/misc/triangle.h"
namespace NL3D
{
//========================================================================================================================
/** private func : duplicate a mesh build
* It returns the index of the new texture coordinate or 0 if the conversion couldn ' t occur . .
*/
static uint DuplicateMBAndAddTexCoord ( CMesh : : CMeshBuild & outMeshBuild , const CMesh : : CMeshBuild & inMeshBuild )
{
outMeshBuild = inMeshBuild ;
uint numTexCoord = 0 ;
for ( uint k = CVertexBuffer : : FirstTexCoordValue ; k < = CVertexBuffer : : FirstTexCoordValue ; + + k )
{
if ( inMeshBuild . VertexFlags & ( 1 < < k ) ) numTexCoord = 1 + k - CVertexBuffer : : FirstTexCoordValue ;
}
if ( numTexCoord = = CVertexBuffer : : MaxStage ) return 0 ;
outMeshBuild . VertexFlags = inMeshBuild . VertexFlags | ( 1 < < ( numTexCoord + CVertexBuffer : : FirstTexCoordValue ) ) ;
outMeshBuild . NumCoords [ numTexCoord ] = 3 ;
return numTexCoord ;
}
//========== Build one tangent space vector ==================
static void BuildTGSpaceVect ( const NLMISC : : CVector & normal , const NLMISC : : CVector & sGrad , NLMISC : : CUVW & result )
{
// start normalizing the basis
NLMISC : : CVector res = ( sGrad - ( ( sGrad * normal ) * normal ) ) . normed ( ) ;
result . U = res . x ;
result . V = res . y ;
result . W = res . z ;
}
//========== build one tri from a vb and 3 index in it=================
static void BuildTriFromMB ( const CMesh : : CMeshBuild & mb , const uint index [ 3 ] , NLMISC : : CTriangle & tri )
{
tri . V0 = mb . Vertices [ index [ 0 ] ] ;
tri . V1 = mb . Vertices [ index [ 1 ] ] ;
tri . V2 = mb . Vertices [ index [ 2 ] ] ;
}
//========================================================================================================================
bool BuildTangentSpace ( CMesh : : CMeshBuild & outMeshBuild , const CMesh : : CMeshBuild & inMeshBuild )
{
static const NLMISC : : CUVW NullUVW ( 0 , 0 , 0 ) ; // todo add this directly in the CUVW class (for next compile ...)
/// for each face, we must use a previously computed tangent space vector if other vertex datas are similar
nlassert ( & outMeshBuild ! = & inMeshBuild ) ;
uint tgSpaceStage = DuplicateMBAndAddTexCoord ( outMeshBuild , inMeshBuild ) ; // format the resulting vb
if ( tgSpaceStage = = 0 ) return false ; // unable to create the vb
uint l , m , n , k ; // some loop counters
// Tells for each vertex what faces belong to it
std : : vector < std : : vector < uint > > VertToFace ( inMeshBuild . Vertices . size ( ) ) ;
// fill VertToFace
for ( l = 0 ; l < inMeshBuild . Faces . size ( ) ; + + l )
{
for ( m = 0 ; m < 3 ; + + m )
{
outMeshBuild . Faces [ l ] . Corner [ m ] . Uvws [ tgSpaceStage ] = NLMISC : : CUVW ( 0 , 0 , 0 ) ;
VertToFace [ outMeshBuild . Faces [ l ] . Corner [ m ] . Vertex ] . push_back ( l ) ;
}
}
/* TODO: debug this version
std : : vector < NLMISC : : CVector > SGradArray ( outMeshBuild . Faces . size ( ) ) ; // SGradient for each face
// compute sGradient for each face
for ( k = 0 ; k < outMeshBuild . Faces . size ( ) ; + + k )
{
CMesh : : CFace & f = outMeshBuild . Faces [ k ] ;
NLMISC : : CTriangle tri ;
tri . V0 = outMeshBuild . Vertices [ f . Corner [ 0 ] . Vertex ] ;
tri . V1 = outMeshBuild . Vertices [ f . Corner [ 1 ] . Vertex ] ;
tri . V2 = outMeshBuild . Vertices [ f . Corner [ 2 ] . Vertex ] ;
tri . computeGradient ( f . Corner [ 0 ] . Uvws [ 0 ] . U ,
f . Corner [ 1 ] . Uvws [ 0 ] . U ,
f . Corner [ 2 ] . Uvws [ 0 ] . U , SGradArray [ k ] ) ;
SGradArray [ k ] . normalize ( ) ;
}
// for each triangle, add the S gradient contribution to any neighbour vertex for which the owning face has TexCoords that do not mirror with that face
for ( k = 0 ; k < outMeshBuild . Faces . size ( ) ; + + k )
{
CMesh : : CFace & f = outMeshBuild . Faces [ k ] ;
for ( l = 0 ; l < 3 ; + + l )
{
const std : : vector < uint > & neighbours = VertToFace [ f . Corner [ l ] . Vertex ] ;
for ( m = 0 ; m < neighbours . size ( ) ; + + m )
{
// other face must share smoothgroups with this one
if ( f . SmoothGroup & outMeshBuild . Faces [ neighbours [ m ] ] . SmoothGroup )
{
// test if the other face UVs are not mirroring the current ones..
float dp = SGradArray [ k ] * SGradArray [ neighbours [ m ] ] ;
if ( dp > 0.f )
{
f . Corner [ l ] . Uvws [ tgSpaceStage ] + = NLMISC : : CUVW ( SGradArray [ neighbours [ m ] ] . x , SGradArray [ neighbours [ m ] ] . y , SGradArray [ neighbours [ m ] ] . z ) ;
}
}
}
}
}
// normalize each tangent space vector
for ( k = 0 ; k < outMeshBuild . Faces . size ( ) ; + + k )
{
CMesh : : CFace & f = outMeshBuild . Faces [ k ] ;
for ( l = 0 ; l < 3 ; + + l )
{
CMesh : : CCorner & c = f . Corner [ l ] ;
CVector tgs ( c . Uvws [ tgSpaceStage ] . U , c . Uvws [ tgSpaceStage ] . V , c . Uvws [ tgSpaceStage ] . W ) ;
tgs = ( tgs - ( tgs * c . Normal ) * c . Normal ) . normed ( ) ;
c . Uvws [ tgSpaceStage ] . U = tgs . x ;
c . Uvws [ tgSpaceStage ] . V = tgs . y ;
c . Uvws [ tgSpaceStage ] . W = tgs . z ;
}
}
*/
// Old tangent space version (no support for mirrored textures ..)
for ( l = 0 ; l < inMeshBuild . Faces . size ( ) ; + + l )
{
CMesh : : CFace & curF = outMeshBuild . Faces [ l ] ;
// Build each tangent space vector if needed
for ( m = 0 ; m < 3 ; + + m )
{
uint vertIndex = outMeshBuild . Faces [ l ] . Corner [ m ] . Vertex ;
bool found = false ;
NLMISC : : CUVW * tsv = 0 ; // a previously computed tangent space vector
// Test whether it hasn't been built before, by looking in each corner of each face that share that vertex
for ( n = 0 ; n < VertToFace [ vertIndex ] . size ( ) & & ! found ; + + n )
{
CMesh : : CFace & f = outMeshBuild . Faces [ VertToFace [ vertIndex ] [ n ] ] ; // ref to the current face
for ( k = 0 ; k < 3 ; + + k )
{
// can only share with corners that are equal to this one
if ( f . Corner [ k ] . Vertex = = curF . Corner [ m ] . Vertex ) // same position
{
if ( f . Corner [ k ] . Uvws [ tgSpaceStage ] ! = NullUVW ) // must have built the tangent space vector previously.
{
if ( f . Corner [ k ] . Normal = = curF . Corner [ m ] . Normal
& & f . Corner [ k ] . Uvws [ 0 ] = = curF . Corner [ m ] . Uvws [ 0 ]
)
{
// no texture and normal discontinuity
found = true ;
tsv = & f . Corner [ k ] . Uvws [ tgSpaceStage ] ;
}
}
}
}
}
if ( ! found )
{
NLMISC : : CVector grad ( 0 , 0 , 0 ) ;
// walk all the triangles around this vertex to sum the gradients
// Get the s coordinate gradient over that triangle
for ( n = 0 ; n < VertToFace [ vertIndex ] . size ( ) ; + + n )
{
CMesh : : CFace & f = outMeshBuild . Faces [ VertToFace [ vertIndex ] [ n ] ] ; // ref to the current face
// before to sum this face gradient, make sure there's no normal or mapping discontinuity
bool canShare = true ;
for ( k = 0 ; k < 3 ; + + k )
{
// can only share with corners that are equal to this one
if ( f . Corner [ k ] . Vertex = = curF . Corner [ m ] . Vertex ) // same position
{
if ( ! ( f . Corner [ k ] . Normal = = curF . Corner [ m ] . Normal
& & f . Corner [ k ] . Uvws [ 0 ] = = curF . Corner [ m ] . Uvws [ 0 ]
)
)
{
canShare = false ;
break ;
}
}
}
if ( ! canShare ) continue ;
// Get indices of vertices of current tri
const uint indices [ ] = { f . Corner [ 0 ] . Vertex ,
f . Corner [ 1 ] . Vertex ,
f . Corner [ 2 ] . Vertex } ;
NLMISC : : CTriangle tri ;
// Build it
BuildTriFromMB ( outMeshBuild , indices , tri ) ;
// Get s coordinates for each corner
float s [ 3 ] ;
for ( k = 0 ; k < 3 ; + + k )
{
s [ k ] = f . Corner [ k ] . Uvws [ 0 ] . U ;
}
NLMISC : : CVector sGrad ;
tri . computeGradient ( s [ 0 ] , s [ 1 ] , s [ 2 ] , sGrad ) ;
grad + = ( ( tri . V1 - tri . V0 ) ^ ( tri . V2 - tri . V0 ) ) . norm ( ) * sGrad ; // ponderate by twice the area
}
grad . normalize ( ) ;
// build new tangent space vector
BuildTGSpaceVect ( inMeshBuild . Faces [ l ] . Corner [ m ] . Normal , grad , curF . Corner [ m ] . Uvws [ tgSpaceStage ] ) ;
}
else
{
// use previously built vector
curF . Corner [ m ] . Uvws [ tgSpaceStage ] = * tsv ;
}
}
}
return true ;
}
}