Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

459 lines
13 KiB

  1. //============ Copyright (c) Valve Corporation, All rights reserved. ==========
  2. //
  3. // Function which do validation tests on UVs values at the s_source_t level
  4. //
  5. //=============================================================================
  6. #include "tier1/fmtstr.h"
  7. #include "tier1/utlmap.h"
  8. #include "studiomdl.h"
  9. #include "checkuv.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. //-----------------------------------------------------------------------------
  13. //
  14. //-----------------------------------------------------------------------------
  15. CCheckUVCmd::CCheckUVCmd()
  16. {
  17. Clear();
  18. }
  19. //-----------------------------------------------------------------------------
  20. //
  21. //-----------------------------------------------------------------------------
  22. void CCheckUVCmd::Clear()
  23. {
  24. ClearCheck( CHECK_UV_ALL_FLAGS );
  25. m_nOptGutterTexWidth = 512;
  26. m_nOptGutterTexHeight = 512;
  27. m_nOptGutterMin = 5;
  28. }
  29. //-----------------------------------------------------------------------------
  30. // Dumps an s_soruce_t as an OBJ file
  31. //-----------------------------------------------------------------------------
  32. static void WriteOBJ( const char *pszFilename, const s_source_t *pSource )
  33. {
  34. FILE *pFile = fopen( pszFilename, "w" );
  35. fprintf( pFile, "#\n" );
  36. fprintf( pFile, "# s_source_t: %s\n", pSource->filename );
  37. fprintf( pFile, "# Bone Count: %d\n", pSource->numbones );
  38. for ( int i = 0; i < pSource->numbones; ++i )
  39. {
  40. if ( pSource->localBone[i].parent >= 0 )
  41. {
  42. fprintf( pFile, "# Bone %3d: %s Parent %3d: %s\n", i, pSource->localBone[i].name, pSource->localBone[i].parent, pSource->localBone[pSource->localBone[i].parent].name );
  43. }
  44. else
  45. {
  46. fprintf( pFile, "# Bone %3d: %s\n", i, pSource->localBone[i].name );
  47. }
  48. }
  49. fprintf( pFile, "# Mesh Count: %d\n", pSource->nummeshes );
  50. fprintf( pFile, "# Vertex Count: %d\n", pSource->numvertices );
  51. fprintf( pFile, "# Face Count: %d\n", pSource->numfaces );
  52. fprintf( pFile, "#\n" );
  53. fprintf( pFile, "# positions\n" );
  54. fprintf( pFile, "#\n" );
  55. for ( int i = 0; i < pSource->numvertices; ++i )
  56. {
  57. const s_vertexinfo_t &v = pSource->vertex[i];
  58. fprintf( pFile, "v %.4f %.4f %.4f\n", v.position.x, v.position.y, v.position.z );
  59. }
  60. fprintf( pFile, "#\n" );
  61. fprintf( pFile, "# texture coordinates\n" );
  62. fprintf( pFile, "#\n" );
  63. for ( int i = 0; i < pSource->numvertices; ++i )
  64. {
  65. const s_vertexinfo_t &v = pSource->vertex[i];
  66. fprintf( pFile, "vt %.4f %.4f\n", v.texcoord.x, v.texcoord.y );
  67. }
  68. fprintf( pFile, "#\n" );
  69. fprintf( pFile, "# normals\n" );
  70. fprintf( pFile, "#\n" );
  71. for ( int i = 0; i < pSource->numvertices; ++i )
  72. {
  73. const s_vertexinfo_t &v = pSource->vertex[i];
  74. fprintf( pFile, "vn %.4f %.4f %.4f\n", v.normal.x, v.normal.y, v.normal.z );
  75. }
  76. for ( int i = 0; i < pSource->nummeshes; ++i )
  77. {
  78. const s_mesh_t &m = pSource->mesh[i];
  79. const s_texture_t &t = g_texture[pSource->meshindex[i]];
  80. fprintf( pFile, "#\n" );
  81. fprintf( pFile, "# mesh %d - %s\n", i, t.name );
  82. fprintf( pFile, "# Face Count: %d\n", m.numfaces );
  83. fprintf( pFile, "#\n" );
  84. fprintf( pFile, "usemtl %s\n", t.name );
  85. for ( int j = 0; j < m.numfaces; ++j )
  86. {
  87. const s_face_t &f = pSource->face[m.faceoffset + j];
  88. fprintf( pFile, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
  89. f.a, f.a, f.a,
  90. f.b, f.b, f.b,
  91. f.c, f.c, f.c );
  92. }
  93. }
  94. fclose( pFile );
  95. }
  96. //-----------------------------------------------------------------------------
  97. //
  98. //-----------------------------------------------------------------------------
  99. bool CCheckUVCmd::CheckUVs( const s_source_t *const *pSourceList, int nSourceCount ) const
  100. {
  101. if ( !DoAnyCheck() || nSourceCount <= 0 )
  102. return true;
  103. bool bRet = true;
  104. for ( int i = 0; i < nSourceCount; ++i )
  105. {
  106. const s_source_t *pSource = pSourceList[i];
  107. bRet &= CheckNormalized( pSource );
  108. bRet &= CheckOverlap( pSource );
  109. bRet &= CheckInverse( pSource );
  110. bRet &= CheckGutter( pSource );
  111. }
  112. return bRet;
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Check that all UVs are in the [0, 1] range
  116. //-----------------------------------------------------------------------------
  117. bool CCheckUVCmd::CheckNormalized( const struct s_source_t *pSource ) const
  118. {
  119. if ( !DoCheck( CHECK_UV_FLAG_NORMALIZED ) )
  120. return true;
  121. CUtlRBTree< int > badVertexIndices( CDefOps< int >::LessFunc );
  122. for ( int i = 0; i < pSource->numvertices; ++i )
  123. {
  124. const s_vertexinfo_t &v = pSource->vertex[i];
  125. if (
  126. v.texcoord.x < 0.0f || v.texcoord.x > 1.0f ||
  127. v.texcoord.y < 0.0f || v.texcoord.y > 1.0f )
  128. {
  129. badVertexIndices.InsertIfNotFound( i );
  130. }
  131. }
  132. if ( badVertexIndices.Count() <= 0 )
  133. return true;
  134. Msg( "Error! %s\n", pSource->filename );
  135. Msg( " UVs outside of [0, 1] range\n" );
  136. for ( int i = 0; i < pSource->nummeshes; ++i )
  137. {
  138. const s_mesh_t &m = pSource->mesh[i];
  139. const s_texture_t &t = g_texture[pSource->meshindex[i]];
  140. CUtlRBTree< int > badMeshVertexIndices( CDefOps< int >::LessFunc );
  141. for ( int j = 0; j < m.numfaces; ++j )
  142. {
  143. const s_face_t &f = pSource->face[m.faceoffset + j];
  144. if ( badVertexIndices.HasElement( f.a ) )
  145. {
  146. badMeshVertexIndices.InsertIfNotFound( f.a );
  147. }
  148. if ( badVertexIndices.HasElement( f.b ) )
  149. {
  150. badMeshVertexIndices.InsertIfNotFound( f.b );
  151. }
  152. if ( badVertexIndices.HasElement( f.c ) )
  153. {
  154. badMeshVertexIndices.InsertIfNotFound( f.c );
  155. }
  156. }
  157. for ( auto vIt = badMeshVertexIndices.FirstInorder(); badMeshVertexIndices.IsValidIndex( vIt ); vIt = badMeshVertexIndices.NextInorder( vIt ) )
  158. {
  159. PrintVertex( pSource->vertex[badMeshVertexIndices.Element( vIt )], t );
  160. }
  161. }
  162. return false;
  163. }
  164. //-----------------------------------------------------------------------------
  165. // Check that all polygons in UV do not overlap
  166. //-----------------------------------------------------------------------------
  167. bool CCheckUVCmd::CheckOverlap( const struct s_source_t *pSource ) const
  168. {
  169. if ( !DoCheck( CHECK_UV_FLAG_OVERLAP ) )
  170. return true;
  171. bool bRet = true;
  172. CUtlVector< CUtlVector< int > > faceOverlapMap;
  173. faceOverlapMap.SetCount( pSource->numfaces );
  174. for ( int i = 0; i < pSource->numfaces; ++i )
  175. {
  176. const s_face_t &fA = pSource->face[i];
  177. const Vector2D &tAA = pSource->vertex[fA.a].texcoord;
  178. const Vector2D &tAB = pSource->vertex[fA.b].texcoord;
  179. const Vector2D &tAC = pSource->vertex[fA.c].texcoord;
  180. for ( int j = i + 1; j < pSource->numfaces; ++j )
  181. {
  182. const s_face_t &fB = pSource->face[j];
  183. const Vector2D tB[] = {
  184. pSource->vertex[fB.a].texcoord,
  185. pSource->vertex[fB.b].texcoord,
  186. pSource->vertex[fB.c].texcoord };
  187. for ( int k = 0; k < ARRAYSIZE( tB ); ++k )
  188. {
  189. const Vector vCheck = Barycentric( tB[k], tAA, tAB, tAC );
  190. if ( vCheck.x > 0.0f && vCheck.y > 0.0f && vCheck.z > 0.0f )
  191. {
  192. if ( bRet )
  193. {
  194. Msg( "Error! %s\n", pSource->filename );
  195. Msg( " Overlapping UV faces\n" );
  196. bRet = false;
  197. }
  198. faceOverlapMap[i].AddToTail( j );
  199. break;
  200. }
  201. }
  202. }
  203. }
  204. for ( int i = 0; i < faceOverlapMap.Count(); ++i )
  205. {
  206. const CUtlVector< int > &overlapList = faceOverlapMap[i];
  207. if ( overlapList.IsEmpty() )
  208. continue;;
  209. const int nFaceA = i;
  210. const int nMeshA = FindMeshIndex( pSource, nFaceA );
  211. PrintFace( pSource, nMeshA, nFaceA );
  212. Msg( " Overlaps\n" );
  213. for ( int j = 0; j < overlapList.Count(); ++j )
  214. {
  215. const int nFaceB = overlapList[j];
  216. const int nMeshB = FindMeshIndex( pSource, nFaceB );
  217. PrintFace( pSource, nMeshB, nFaceB, " " );
  218. }
  219. }
  220. return bRet;
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Check that all polygons in UV have the correct winding, i.e. the cross
  224. // product of edge AB x BC points the right direction
  225. //-----------------------------------------------------------------------------
  226. bool CCheckUVCmd::CheckInverse( const struct s_source_t *pSource ) const
  227. {
  228. if ( !DoCheck( CHECK_UV_FLAG_INVERSE ) )
  229. return true;
  230. bool bRetVal = true;
  231. for ( int i = 0; i < pSource->nummeshes; ++i )
  232. {
  233. const s_mesh_t &m = pSource->mesh[i];
  234. for ( int j = 0; j < m.numfaces; ++j )
  235. {
  236. const int nFaceIndex = m.faceoffset + j;
  237. const s_face_t &f = pSource->face[nFaceIndex];
  238. const Vector2D &tA = pSource->vertex[f.a].texcoord;
  239. const Vector2D &tB = pSource->vertex[f.b].texcoord;
  240. const Vector2D &tC = pSource->vertex[f.c].texcoord;
  241. const Vector vA( tA.x, tA.y, 0.0f );
  242. const Vector vB( tB.x, tB.y, 0.0f );
  243. const Vector vC( tC.x, tC.y, 0.0f );
  244. const Vector vAB = vB - vA;
  245. const Vector vBC = vC - vB;
  246. const Vector vUVNormal = CrossProduct( vAB, vBC );
  247. const float flDot = DotProduct( vUVNormal, Vector( 0.0f, 0.0f, 1.0f ) );
  248. if ( flDot < 0.0f )
  249. {
  250. if ( bRetVal )
  251. {
  252. Msg( "Error! %s\n", pSource->filename );
  253. Msg( " Inverse UV faces\n" );
  254. bRetVal = false;
  255. }
  256. PrintFace( pSource, i, nFaceIndex );
  257. }
  258. }
  259. }
  260. return bRetVal;
  261. }
  262. //-----------------------------------------------------------------------------
  263. // Check that the distance between edges in UV islands is a minimum number of pixels for a given texture size
  264. //-----------------------------------------------------------------------------
  265. bool CCheckUVCmd::CheckGutter( const struct s_source_t *pSource ) const
  266. {
  267. if ( !DoCheck( CHECK_UV_FLAG_GUTTER ) )
  268. return true;
  269. // TODO: Implement me!
  270. return true;
  271. }
  272. //-----------------------------------------------------------------------------
  273. //
  274. //-----------------------------------------------------------------------------
  275. Vector CCheckUVCmd::Barycentric( const Vector2D &vP, const Vector2D &vA, const Vector2D &vB, const Vector2D &vC )
  276. {
  277. const Vector2D v0 = vB - vA;
  278. const Vector2D v1 = vC - vA;
  279. const Vector2D v2 = vP - vA;
  280. const float d00 = DotProduct2D( v0, v0 );
  281. const float d01 = DotProduct2D( v0, v1 );
  282. const float d11 = DotProduct2D( v1, v1 );
  283. const float d20 = DotProduct2D( v2, v0 );
  284. const float d21 = DotProduct2D( v2, v1 );
  285. const float flDenom = d00 * d11 - d01 * d01;
  286. const float flV = ( d11 * d20 - d01 * d21 ) / flDenom;
  287. const float flW = ( d00 * d21 - d01 * d20 ) / flDenom;
  288. const float flU = 1.0f - flV - flW;
  289. return Vector( flV, flW, flU );
  290. }
  291. //-----------------------------------------------------------------------------
  292. //
  293. //-----------------------------------------------------------------------------
  294. Vector CCheckUVCmd::Barycentric( const Vector &vP, const Vector &vA, const Vector &vB, const Vector &vC )
  295. {
  296. const Vector v0 = vB - vA;
  297. const Vector v1 = vC - vA;
  298. const Vector v2 = vP - vA;
  299. const float d00 = DotProduct( v0, v0 );
  300. const float d01 = DotProduct( v0, v1 );
  301. const float d11 = DotProduct( v1, v1 );
  302. const float d20 = DotProduct( v2, v0 );
  303. const float d21 = DotProduct( v2, v1 );
  304. const float flDenom = d00 * d11 - d01 * d01;
  305. const float flV = ( d11 * d20 - d01 * d21 ) / flDenom;
  306. const float flW = ( d00 * d21 - d01 * d20 ) / flDenom;
  307. const float flU = 1.0f - flV - flW;
  308. return Vector( flV, flW, flU );
  309. }
  310. //-----------------------------------------------------------------------------
  311. //
  312. //-----------------------------------------------------------------------------
  313. int CCheckUVCmd::FindMeshIndex( const struct s_source_t *pSource, int nFaceIndex )
  314. {
  315. for ( int i = 1; i < pSource->nummeshes; ++i )
  316. {
  317. if ( nFaceIndex <= pSource->mesh[i].faceoffset )
  318. return i;
  319. }
  320. return 0;
  321. }
  322. //-----------------------------------------------------------------------------
  323. //
  324. //-----------------------------------------------------------------------------
  325. void CCheckUVCmd::PrintVertex( const s_vertexinfo_t &v, const char *pszPrefix /* = " " */ )
  326. {
  327. Msg( "%sP: %8.4f %8.4f %8.4f T: %8.4f %8.4f\n",
  328. pszPrefix,
  329. v.position.x, v.position.y, v.position.z,
  330. v.texcoord.x, v.texcoord.y );
  331. }
  332. //-----------------------------------------------------------------------------
  333. //
  334. //-----------------------------------------------------------------------------
  335. void CCheckUVCmd::PrintVertex( const s_vertexinfo_t &v, const s_texture_t &t, const char *pszPrefix /* = " " */ )
  336. {
  337. Msg( "%sP: %8.4f %8.4f %8.4f T: %8.4f %8.4f M: %s\n",
  338. pszPrefix,
  339. v.position.x, v.position.y, v.position.z,
  340. v.texcoord.x, v.texcoord.y,
  341. t.name );
  342. }
  343. //-----------------------------------------------------------------------------
  344. //
  345. //-----------------------------------------------------------------------------
  346. void CCheckUVCmd::PrintFace( const s_source_t *pSource, const int nMesh, const int nFace, const char *pszPrefix /* = " " */ )
  347. {
  348. const s_texture_t &t = g_texture[pSource->meshindex[nMesh]];
  349. const s_face_t &f = pSource->face[nFace];
  350. Msg( "%sF: %4d %s\n", pszPrefix, nFace, t.name );
  351. PrintVertex( pSource->vertex[f.a], pszPrefix );
  352. PrintVertex( pSource->vertex[f.b], pszPrefix );
  353. PrintVertex( pSource->vertex[f.c], pszPrefix );
  354. }