📄 nvmeshmender.cpp
字号:
/*********************************************************************NVMH2****Copyright (C) 1999, 2000, 2001, 2002 NVIDIA CorporationThis file is provided without support, instruction, or implied warranty of anykind. NVIDIA makes no guarantee of its fitness for a particular purpose and isnot liable under any circumstances for any damages or loss whatsoever arisingfrom the use or inability to use this file or items derived from it.Questions to sim.dietrich@nvidia.comComments: This tool is designed to help condition meshes for use in vertex & pixel shaders. It can generate normals, texture coordinates, and perhaps most importantly, texture space basis matrices. It also can fix common texuring problems that come up when bump mapping, including Texture Mirroring - When two one halves of a character use the same part of a texture. Cylindrical TexGen - When the rendering relies on cylindrical wrapping, texture space computation won't work right. Stretched Basis - When two adjacend faces use wildly different texture mappings, causing stretching that can ruin per-pixel lighting. Here is an example usage scenario : Say you have positions & indices, and want textures, normals, and tangents. NVMeshMender aMender; MVMeshMender::VAVector inputAtts; // this is what you have MVMeshMender::VAVector outputAtts; // this is what you want MVMeshMender::VertexAttribute att; // this is my working attribute att.Name_ = "position"; for ( int p = 0; p < number_of_vertices; ++p ) { att.floatVector_.push_back( myPositions[ p ].x ); att.floatVector_.push_back( myPositions[ p ].y ); att.floatVector_.push_back( myPositions[ p ].z ); } att.floatVector.clear(); att.Name_ = "indices"; for ( int i = 0; i < number_of_indices; ++i ) { att.intVector_.push_back( myIndices[ i ] ); } // All inputs except for indices are assumed to be sets of 3 floats // "indices" are assumed to be unsigned ints // "tex0" is used for the tangent space calculation // "tex0" is the only coordinate set generated, and the texture matrix applies only to it // All unknown attribute types, including "tex1", "tex2", "random_attribute", "weights", "bones", etc. // are simply passed through. They will be duplicated as needed just like positions, normals, etc. bool bSuccess = aMender.Munge( inputAtts, // these are my positions & indices outputAtts, // these are the outputs I requested, plus extra stuff generated on my behalf 3.141592654f / 2.5f, // tangent space smooth angle NULL, // no Texture matrix applied to my tex0 coords FixTangents, // fix degenerate bases & texture mirroring FixCylindricalTexGen // handle cylidrically mapped textures via vertex duplication WeightNormalsByFaceSize // weight vertex normals by the triangle's size ); if ( !bSuccess ) return false; // Now the outputAtts may contain more vertex then you sent in ! // This is because in handling tangent space smoothing, and solving texture mirroring & // cylindrical texture wrapping problems, we partially duplicate vertices. // All attributes are duplicated, even unknowns. // You may also get things you didn't ask for. For instance, if you ask for tangent space, // in other words, you ask for "tangent" or "binormal", you will get "normal", "tex0", // "tangent" and "binormal". You can then ignore things in the output vector that you don't want. // If you ask for FixCylindricalTexGen, it will fix any absolute change in texture coordinates > 0.5 // across a single edge. Therefore, if you pass in a single quad or cube, it won't work. Any more // complicated or tessellated mesh should work fine.******************************************************************************/#ifdef _WIN32# pragma warning (disable : 4786)#endif#include "NVMeshMender.h"#include "nv_math/nv_math.h"#include <map>#include <set>#include <assert.h>bool NVMeshMender::Munge( const NVMeshMender::VAVector& input, NVMeshMender::VAVector& output, const float bSmoothCreaseAngleRadians, const float* pTextureMatrix, const Option _FixTangents, const Option _FixCylindricalTexGen, const Option _WeightNormalsByFaceSize ){ typedef std::map< std::string, unsigned int > Mapping; typedef std::set< Edge > EdgeSet; typedef std::vector< std::set< unsigned int > > IdenticalVertices; IdenticalVertices IdenticalVertices_; // make room for potential tex coords, normals, binormals and tangents output.resize( input.size() + 4 ); Mapping inmap; Mapping outmap; for ( unsigned int a = 0; a < input.size(); ++a ) { inmap[ input[ a ].Name_ ] = a; } for ( unsigned int b = 0; b < output.size(); ++b ) { output[ b ].intVector_.clear(); output[ b ].floatVector_.clear(); outmap[ output[ b ].Name_ ] = b; } for ( unsigned int c = 0; c < output.size(); ++c ) { // for every output that has a match in the input, just copy it over Mapping::iterator in = inmap.find( output[ c ].Name_ ); if ( in != inmap.end() ) { // copy over existing indices, position, or whatever output[ c ] = input[ (*in).second ]; } } if ( inmap.find( "indices" ) == inmap.end() ) { SetLastError( "Missing indices from input" ); return false; } if ( outmap.find( "indices" ) == outmap.end() ) { SetLastError( "Missing indices from output" ); return false; } // Go through all required outputs & generate as necessary if ( inmap.find( "position" ) == inmap.end() ) { SetLastError( "Missing position from input" ); return false; } if ( outmap.find( "position" ) == outmap.end() ) { SetLastError( "Missing position from output" ); return false; } Mapping::iterator pos = outmap.find( "position" ); VertexAttribute::FloatVector& positions = output[ (*pos).second ].floatVector_; vec3* pPositions = (vec3*)( &( positions[ 0 ] ) ); std::set< unsigned int > EmptySet; for ( unsigned int i = 0; i < positions.size(); i += 3 ) { IdenticalVertices_.push_back( EmptySet ); } // initialize all attributes for ( unsigned int att = 0; att < output.size(); ++att ) { if ( output[ att ].Name_ != "indices" ) { if ( output[ att ].floatVector_.size() == 0 ) { output[ att ].floatVector_ = positions; } } } Mapping::iterator ind = outmap.find( "indices" ); VertexAttribute::IntVector& indices = output[ (*ind).second ].intVector_; int* pIndices = (int*)( &( indices[ 0 ] ) ); vec3* pNormals = 0; //vec3* pBiNormals = 0; //vec3* pTangents = 0; vec3* pTex0 = 0; bool bNeedNormals = false; bool bNeedTexCoords = false; bool bComputeTangentSpace = false; // see if texture coords are needed if ( outmap.find( "tex0" ) != outmap.end() ) { bNeedTexCoords = true; } // see if tangent or binormal are needed if ( ( outmap.find( "binormal" ) != outmap.end() ) || ( outmap.find( "tangent" ) != outmap.end() ) ) { bComputeTangentSpace = true; } // see if normals are needed if ( outmap.find( "normal" ) != outmap.end() ) { bNeedNormals = true; } // Compute normals. Mapping::iterator want = outmap.find( "normal" ); bool have_normals = ( inmap.find( "normal" ) != inmap.end() ) ? true : false; if ( bNeedNormals || bComputeTangentSpace ) { // see if normals are provided if ( !have_normals ) { // create normals if ( want == outmap.end() ) { VertexAttribute norAtt; norAtt.Name_ = "normal"; output.push_back( norAtt ); outmap[ "normal" ] = output.size() - 1; want = outmap.find( "normal" ); } // just initialize array so it's the correct size output[ (*want).second ].floatVector_ = positions; //VertexAttribute::FloatVector& normals = output[ (*want).second ].floatVector_; // zero out normals for ( unsigned n = 0; n < positions.size(); ++n ) { output[ (*want).second ].floatVector_[ n ] = nv_zero; } pNormals = (vec3*)( &( output[ (*want).second ].floatVector_[0] ) ); // calculate face normals for each face // & add its normal to vertex normal total for ( unsigned int t = 0; t < indices.size(); t += 3 ) { vec3 edge0; vec3 edge1; edge0 = pPositions[ indices[ t + 1 ] ] - pPositions[ indices[ t + 0 ] ]; edge1 = pPositions[ indices[ t + 2 ] ] - pPositions[ indices[ t + 0 ] ]; edge0.normalize(); edge1.normalize(); vec3 faceNormal = edge0 ^ edge1; if ( _WeightNormalsByFaceSize == DontWeightNormalsByFaceSize ) { // Renormalize face normal, so it's not weighted by face size faceNormal.normalize(); } else { // Leave it as-is, to weight by face size naturally by the cross product result } pNormals[ indices[ t + 0 ] ] += faceNormal; pNormals[ indices[ t + 1 ] ] += faceNormal; pNormals[ indices[ t + 2 ] ] += faceNormal; } // Renormalize each vertex normal for ( unsigned int v = 0; v < output[ (*want).second ].floatVector_.size() / 3; ++v ) pNormals[v].normalize(); } } // Compute texture coordinates. if ( bNeedTexCoords || bComputeTangentSpace ) { if ( outmap.find("tex0") == outmap.end() ) { VertexAttribute texCoordAtt; texCoordAtt.Name_ = "tex0"; output.push_back( texCoordAtt ); outmap[ "tex0" ] = output.size() - 1; } want = outmap.find("tex0"); Mapping::iterator have = inmap.find( "tex0" ); bool have_texcoords = (have != inmap.end()); // see if texcoords are provided if ( have_texcoords ) output[ (*want).second ].floatVector_ = input[ (*have).second ].floatVector_; else { // just initialize array so it's the correct size output[ (*want).second ].floatVector_ = positions; pTex0 = (vec3*)( &(output[ (*want).second ].floatVector_[ 0 ]) ); // Generate cylindrical coordinates // Find min and max positions for object bounding box vec3 maxPosition( -FLT_MAX, -FLT_MAX, -FLT_MAX ); vec3 minPosition( FLT_MAX, FLT_MAX, FLT_MAX ); // there are 1/3 as many vectors as floats const unsigned int theCount = static_cast<unsigned int>(positions.size() / 3.0f); for ( unsigned int i = 0; i < theCount; ++i ) { maxPosition.x = nv_max( maxPosition.x, pPositions[ i ].x ); maxPosition.y = nv_max( maxPosition.y, pPositions[ i ].y ); maxPosition.z = nv_max( maxPosition.z, pPositions[ i ].z ); minPosition.x = nv_min( minPosition.x, pPositions[ i ].x ); minPosition.y = nv_min( minPosition.y, pPositions[ i ].y ); minPosition.z = nv_min( minPosition.z, pPositions[ i ].z ); } // Find major, minor and other axis for cylindrical texgen vec3 delta = maxPosition - minPosition; delta.x = (float)fabs( delta.x ); delta.y = (float)fabs( delta.y ); delta.z = (float)fabs( delta.z ); bool maxx,maxy,maxz; maxx = maxy = maxz = false; bool minz,miny,minx; minx = miny = minz = false; float deltaMajor = 0.0; if ( ( delta.x >= delta.y ) && ( delta.x >= delta.z ) ) { maxx = true; deltaMajor = delta.x; if ( delta.y > delta.z ) { minz = true; } else { miny = true; } } else if ( ( delta.z >= delta.y ) && ( delta.z >= delta.x ) ) { maxz = true; deltaMajor = delta.z; if ( delta.y > delta.x ) { minx = true; } else { miny = true; } } else if ( ( delta.y >= delta.z ) && ( delta.y >= delta.x ) ) { maxy = true; deltaMajor = delta.y; if ( delta.x > delta.z ) { minz = true; } else { minx = true; } } for ( unsigned int p = 0; p < theCount; ++p ) { // Find position relative to center of bounding box vec3 texCoords = ( ( maxPosition + minPosition ) / 2.0f ) - pPositions[ p ]; float Major = 0.0, Minor = 0.0, Other = nv_zero; if ( maxx ) { Major = texCoords.x; if ( miny ) { Minor = texCoords.y; Other = texCoords.z; } else { Minor = texCoords.z; Other = texCoords.y; } } else if ( maxy ) { Major = texCoords.y; if ( minx ) { Minor = texCoords.x; Other = texCoords.z; } else { Minor = texCoords.z; Other = texCoords.x; } } else if ( maxz ) { Major = texCoords.z; if ( miny ) { Minor = texCoords.y; Other = texCoords.x; } else { Minor = texCoords.x; Other = texCoords.y; } } float longitude = nv_zero; // Prevent zero or near-zero from being passed into atan2 if ( fabs( Other ) < 0.0001f ) { if ( Other >= nv_zero ) { Other = 0.0001f; } else { Other = -0.0001f; } } // perform cylindrical mapping onto object, and remap from -pi,pi to -1,1 longitude = (float)(( atan2( Minor, Other ) ) / 3.141592654); texCoords.x = 0.5f * longitude + 0.5f; texCoords.y = (Major/deltaMajor) + 0.5f; texCoords.x = nv_max( texCoords.x, nv_zero ); texCoords.y = nv_max( texCoords.y, nv_zero ); texCoords.x = nv_min( texCoords.x, 1.0f );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -