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.

453 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #include "stdafx.h"
  3. #include "simplify.h"
  4. extern IPhysicsCollision *physcollision;
  5. extern bool g_bQuiet;
  6. const float DIST_EPSILON = 1.0f / 32.0f;
  7. // this is the list of candidate planes that will be added one by one to the convex hull
  8. // until none of the surface lies outside the tolerance
  9. struct planetest_t
  10. {
  11. Vector normal;
  12. float dist;
  13. int inUse;
  14. float bestDist;
  15. void Init( int axis, float sign, float _dist, bool _inUse = false )
  16. {
  17. memset( this, 0, sizeof(*this) );
  18. normal[axis] = sign;
  19. dist = sign*_dist;
  20. inUse = _inUse;
  21. bestDist = -1;
  22. }
  23. void Init( const Vector &a, const Vector &b, const Vector &c, bool _inUse = false )
  24. {
  25. Vector e0 = b-a;
  26. Vector e1 = c-a;
  27. normal = CrossProduct( e1, e0 );
  28. VectorNormalize( normal );
  29. dist = DotProduct( normal, a );
  30. inUse = _inUse;
  31. bestDist = -1;
  32. }
  33. };
  34. CPhysConvex *ConvertPlaneListToConvex( CUtlVector<planetest_t> &list )
  35. {
  36. float temp[4 * 2048];
  37. struct listplane_t
  38. {
  39. float plane[4];
  40. };
  41. int planeCount = 0;
  42. listplane_t *pList = (listplane_t *)temp;
  43. for ( int i = 0; i < list.Count(); i++ )
  44. {
  45. if ( list[i].inUse )
  46. {
  47. list[i].normal.CopyToArray( pList[planeCount].plane );
  48. pList[planeCount].plane[3] = list[i].dist;
  49. planeCount++;
  50. }
  51. }
  52. return physcollision->ConvexFromPlanes( temp, planeCount, 0.25f );
  53. }
  54. Vector BoxSupport( const Vector &dir, const Vector &mins, const Vector &maxs )
  55. {
  56. Vector out;
  57. for ( int i = 0; i < 3; i++ )
  58. {
  59. out[i] = (dir[i] >= 0) ? maxs[i] : mins[i];
  60. }
  61. return out;
  62. }
  63. struct convexoptimize_t
  64. {
  65. CUtlVector<planetest_t> list;
  66. float targetTolerance;
  67. void InitPlanes( CPhysCollide *pCollide, bool addAABBToSimplifiedHull )
  68. {
  69. Vector mins, maxs;
  70. physcollision->CollideGetAABB( &mins, &maxs, pCollide, vec3_origin, vec3_angle );
  71. if ( !addAABBToSimplifiedHull )
  72. {
  73. mins -= Vector(targetTolerance,targetTolerance,targetTolerance);
  74. maxs += Vector(targetTolerance,targetTolerance,targetTolerance);
  75. }
  76. int i;
  77. for ( i = 0; i < 3; i++ )
  78. {
  79. planetest_t &elem = list[list.AddToTail()];
  80. elem.Init( i, 1.0f, maxs[i], true );
  81. planetest_t &elem2 = list[list.AddToTail()];
  82. elem2.Init( i, -1.0f, mins[i], true );
  83. }
  84. ICollisionQuery *pQuery = physcollision->CreateQueryModel( pCollide );
  85. Vector triVerts[3];
  86. for ( i = 0; i < pQuery->TriangleCount(0); i++ )
  87. {
  88. pQuery->GetTriangleVerts( 0, i, triVerts );
  89. planetest_t &elem = list[list.AddToTail()];
  90. elem.Init( triVerts[0], triVerts[1], triVerts[2], false );
  91. elem.bestDist = DotProduct( elem.normal, BoxSupport(elem.normal, mins, maxs) ) - elem.dist;
  92. }
  93. physcollision->DestroyQueryModel( pQuery );
  94. }
  95. CPhysConvex *ConvertToConvex()
  96. {
  97. return ::ConvertPlaneListToConvex( list );
  98. }
  99. int FindBestPlane( float dist )
  100. {
  101. int best = -1;
  102. for ( int i = 6; i < list.Count(); i++ )
  103. {
  104. if ( list[i].inUse )
  105. continue;
  106. if ( dist >= list[i].bestDist )
  107. continue;
  108. dist = list[i].bestDist;
  109. best = i;
  110. }
  111. return best;
  112. }
  113. bool AddBestPlane()
  114. {
  115. convertconvexparams_t params;
  116. params.Defaults();
  117. CPhysConvex *pConvex = ConvertPlaneListToConvex( list );
  118. CPhysCollide *pCurrentCollide = physcollision->ConvertConvexToCollideParams( &pConvex, 1, params );
  119. int bestIndex = -1;
  120. float bestDist = 0;
  121. while ( true )
  122. {
  123. if ( bestIndex >= 0 )
  124. {
  125. list[bestIndex].inUse = true;
  126. }
  127. int test = FindBestPlane( bestDist );
  128. if ( test < 0 )
  129. break;
  130. if ( bestIndex >= 0 )
  131. {
  132. list[bestIndex].inUse = false;
  133. }
  134. Vector dir = list[test].normal;
  135. Vector point = physcollision->CollideGetExtent( pCurrentCollide, vec3_origin, vec3_angle, dir );
  136. float before = DotProduct( dir, point );
  137. list[test].inUse = true;
  138. pConvex = ConvertToConvex();
  139. list[test].inUse = false;
  140. CPhysCollide *pCollide = physcollision->ConvertConvexToCollideParams( &pConvex, 1, params );
  141. Vector p2 = physcollision->CollideGetExtent( pCollide, vec3_origin, vec3_angle, dir );
  142. physcollision->DestroyCollide( pCollide );
  143. float after = DotProduct( dir, p2 );
  144. list[test].bestDist = fabs(before-after);
  145. if ( list[test].bestDist > bestDist )
  146. {
  147. bestDist = list[test].bestDist;
  148. bestIndex = test;
  149. }
  150. }
  151. physcollision->DestroyCollide( pCurrentCollide );
  152. if ( bestIndex >= 0 && bestDist >= targetTolerance )
  153. {
  154. list[bestIndex].inUse = true;
  155. return true;
  156. }
  157. return false;
  158. }
  159. };
  160. CPhysConvex *SimplifyConvexFromVerts( Vector **verts, int vertCount, bool addAABBToSimplifiedHull, float tolerance, int index )
  161. {
  162. CPhysConvex *pConvex = physcollision->ConvexFromVerts( verts, vertCount );
  163. float targetVolume = physcollision->ConvexVolume( pConvex );
  164. // can't simplify this polyhedron
  165. if ( vertCount <= 8 )
  166. return pConvex;
  167. convexoptimize_t opt;
  168. memset( &opt, 0, sizeof(opt));
  169. opt.targetTolerance = tolerance;
  170. convertconvexparams_t params;
  171. params.Defaults();
  172. CPhysCollide *pRef = physcollision->ConvertConvexToCollideParams( &pConvex, 1, params );
  173. opt.InitPlanes( pRef, addAABBToSimplifiedHull );
  174. physcollision->DestroyCollide( pRef );
  175. // Simplify until you hit the tolerance
  176. int i;
  177. for ( i = 0; i < vertCount; i++ )
  178. {
  179. if ( !opt.AddBestPlane() )
  180. break;
  181. }
  182. // Create the output shape
  183. pConvex = opt.ConvertToConvex();
  184. float currentVolume = physcollision->ConvexVolume( pConvex );
  185. //Msg("%d iterations, for convex %d\n", i, index );
  186. return pConvex;
  187. }
  188. inline int AddVert( Vector **ppVerts, int vertCount, const Vector &newVert )
  189. {
  190. for ( int i = 0; i < vertCount; i++ )
  191. {
  192. if ( fabs(ppVerts[i]->x - newVert.x) < DIST_EPSILON &&
  193. fabs(ppVerts[i]->y - newVert.y) < DIST_EPSILON &&
  194. fabs(ppVerts[i]->z - newVert.z) < DIST_EPSILON )
  195. return vertCount;
  196. }
  197. *ppVerts[vertCount] = newVert;
  198. return vertCount+1;
  199. }
  200. void BuildSingleConvex( CPhysConvex **convexListOut, ICollisionQuery *pQuery, Vector **ppVerts, const simplifyparams_t &params )
  201. {
  202. int vertCount = 0;
  203. for ( int i = 0; i < pQuery->ConvexCount(); i++ )
  204. {
  205. Vector v[3];
  206. for ( int j = 0; j < pQuery->TriangleCount(i); j++ )
  207. {
  208. pQuery->GetTriangleVerts( i, j, v );
  209. vertCount = AddVert( ppVerts, vertCount, v[0] );
  210. vertCount = AddVert( ppVerts, vertCount, v[1] );
  211. vertCount = AddVert( ppVerts, vertCount, v[2] );
  212. }
  213. }
  214. convexListOut[0] = SimplifyConvexFromVerts( ppVerts, vertCount, params.addAABBToSimplifiedHull, params.tolerance, 0 );
  215. physcollision->SetConvexGameData( convexListOut[0], pQuery->GetGameData( 0 ) );
  216. }
  217. void SimplifyConvexElements( CPhysConvex **convexListOut, ICollisionQuery *pQuery, Vector **ppVerts, const simplifyparams_t &params )
  218. {
  219. for ( int i = 0; i < pQuery->ConvexCount(); i++ )
  220. {
  221. int vertCount = 0;
  222. Vector v[3];
  223. for ( int j = 0; j < pQuery->TriangleCount(i); j++ )
  224. {
  225. pQuery->GetTriangleVerts( i, j, v );
  226. vertCount = AddVert( ppVerts, vertCount, v[0] );
  227. vertCount = AddVert( ppVerts, vertCount, v[1] );
  228. vertCount = AddVert( ppVerts, vertCount, v[2] );
  229. }
  230. convexListOut[i] = SimplifyConvexFromVerts( ppVerts, vertCount, params.addAABBToSimplifiedHull, params.tolerance, i );
  231. physcollision->SetConvexGameData( convexListOut[i], pQuery->GetGameData( i ) );
  232. }
  233. }
  234. struct mergeconvex_t
  235. {
  236. byte mergeCount;
  237. byte list[255];
  238. };
  239. void MergeElems( CUtlVector<mergeconvex_t> &elems, int index0, int index1 )
  240. {
  241. Assert( index0 < index1 );
  242. for (int i = 0; i < elems[index1].mergeCount; i++)
  243. {
  244. elems[index0].list[i+elems[index0].mergeCount] = elems[index1].list[i];
  245. }
  246. elems[index0].mergeCount += elems[index1].mergeCount;
  247. elems.FastRemove(index1);
  248. }
  249. int VertsForElem( ICollisionQuery *pQuery, Vector **ppVerts, const mergeconvex_t &elems0, int vertCount )
  250. {
  251. for ( int i = 0; i < elems0.mergeCount; i++ )
  252. {
  253. int convexId = elems0.list[i];
  254. Vector v[3];
  255. for ( int j = 0; j < pQuery->TriangleCount(convexId); j++ )
  256. {
  257. pQuery->GetTriangleVerts( convexId, j, v );
  258. vertCount = AddVert( ppVerts, vertCount, v[0] );
  259. vertCount = AddVert( ppVerts, vertCount, v[1] );
  260. vertCount = AddVert( ppVerts, vertCount, v[2] );
  261. }
  262. }
  263. return vertCount;
  264. }
  265. void PlanesForElem( ICollisionQuery *pQuery, CUtlVector<float> &planes, const mergeconvex_t &elem0 )
  266. {
  267. for ( int i = 0; i < elem0.mergeCount; i++ )
  268. {
  269. int convexId = elem0.list[i];
  270. Vector v[3];
  271. for ( int j = 0; j < pQuery->TriangleCount(convexId); j++ )
  272. {
  273. pQuery->GetTriangleVerts( convexId, j, v );
  274. Vector e0 = v[1]-v[0];
  275. Vector e1 = v[2]-v[0];
  276. Vector normal = CrossProduct( e1, e0 );
  277. VectorNormalize( normal );
  278. float dist = DotProduct( normal, v[0] );
  279. planes.AddToTail( normal.x );
  280. planes.AddToTail( normal.y );
  281. planes.AddToTail( normal.z );
  282. planes.AddToTail( dist );
  283. }
  284. }
  285. }
  286. float ConvexVolumeFromPlanes( CUtlVector<float> &planes )
  287. {
  288. CPhysConvex *pConvex = planes.Count() ? physcollision->ConvexFromPlanes( planes.Base(), planes.Count()/4, DIST_EPSILON ) : NULL;
  289. float volume = 0;
  290. if ( pConvex )
  291. {
  292. volume = physcollision->ConvexVolume(pConvex);
  293. physcollision->ConvexFree(pConvex);
  294. }
  295. return volume;
  296. }
  297. float MergedDeltaVolume( ICollisionQuery *pQuery, Vector **ppVerts, const mergeconvex_t &elem0, const mergeconvex_t &elem1 )
  298. {
  299. // build vert list
  300. int vertCount = VertsForElem( pQuery, ppVerts, elem0, 0 );
  301. // merge in next element
  302. vertCount = VertsForElem( pQuery, ppVerts, elem1, vertCount);
  303. CPhysConvex *pConvex = physcollision->ConvexFromVerts( ppVerts, vertCount );
  304. float finalVolume = physcollision->ConvexVolume(pConvex);
  305. physcollision->ConvexFree(pConvex);
  306. CUtlVector<float> planes;
  307. PlanesForElem( pQuery, planes, elem0 );
  308. float vol0 = ConvexVolumeFromPlanes( planes );
  309. planes.RemoveAll();
  310. PlanesForElem( pQuery, planes, elem1 );
  311. float vol1 = ConvexVolumeFromPlanes( planes );
  312. PlanesForElem( pQuery, planes, elem0 );
  313. float volInt = ConvexVolumeFromPlanes( planes );
  314. return finalVolume - (vol0+vol1-volInt);
  315. }
  316. int MergeAndSimplifyConvexElements( CPhysConvex **convexListOut, const CPhysCollide *pCollideIn, ICollisionQuery *pQuery, Vector **ppVerts, const simplifyparams_t &params )
  317. {
  318. Assert( pQuery->ConvexCount() < 256 );
  319. if ( pQuery->ConvexCount() > 256 )
  320. {
  321. SimplifyConvexElements(convexListOut, pQuery, ppVerts, params);
  322. return pQuery->ConvexCount();
  323. }
  324. CUtlVector<mergeconvex_t> elems;
  325. int i;
  326. elems.EnsureCount(pQuery->ConvexCount());
  327. float totalVolume = physcollision->CollideVolume( (CPhysCollide *)pCollideIn );
  328. for ( i = 0; i < pQuery->ConvexCount(); i++ )
  329. {
  330. elems[i].mergeCount = 1;
  331. elems[i].list[0] = i;
  332. }
  333. loop:
  334. for ( i = 0; i < elems.Count(); i++ )
  335. {
  336. for ( int j = i+1; j < elems.Count(); j++ )
  337. {
  338. float volume = fabs(MergedDeltaVolume( pQuery, ppVerts, elems[i], elems[j] ));
  339. volume /= totalVolume;
  340. if ( volume < params.mergeConvexTolerance )
  341. {
  342. MergeElems( elems, i, j );
  343. goto loop;
  344. }
  345. }
  346. }
  347. for ( i = 0; i < elems.Count(); i++ )
  348. {
  349. int vertCount = VertsForElem( pQuery, ppVerts, elems[i], 0 );
  350. convexListOut[i] = SimplifyConvexFromVerts( ppVerts, vertCount, params.addAABBToSimplifiedHull, params.tolerance, i );
  351. physcollision->SetConvexGameData( convexListOut[i], pQuery->GetGameData( elems[i].list[0] ) );
  352. }
  353. return elems.Count();
  354. }
  355. CPhysCollide *SimplifyCollide( CPhysCollide *pCollideIn, int indexIn, const simplifyparams_t &params )
  356. {
  357. int sizeIn = physcollision->CollideSize( pCollideIn );
  358. ICollisionQuery *pQuery = physcollision->CreateQueryModel( pCollideIn );
  359. int maxVertCount = 0;
  360. int i;
  361. for ( i = pQuery->ConvexCount(); --i >= 0; )
  362. {
  363. int vertCount = pQuery->TriangleCount(i)*3;
  364. maxVertCount += vertCount;
  365. }
  366. Vector **ppVerts = new Vector *[maxVertCount];
  367. Vector *verts = new Vector[maxVertCount];
  368. for ( i = 0; i < maxVertCount; i++ )
  369. {
  370. ppVerts[i] = &verts[i];
  371. }
  372. int outputConvexCount = params.forceSingleConvex ? 1 : pQuery->ConvexCount();
  373. CPhysConvex **convexList = new CPhysConvex *[outputConvexCount];
  374. if ( params.forceSingleConvex )
  375. {
  376. BuildSingleConvex( convexList, pQuery, ppVerts, params );
  377. }
  378. else if ( params.mergeConvexElements && pQuery->ConvexCount() > 1 )
  379. {
  380. outputConvexCount = MergeAndSimplifyConvexElements( convexList, pCollideIn, pQuery, ppVerts, params );
  381. if ( !g_bQuiet && pQuery->ConvexCount() != outputConvexCount)
  382. {
  383. Msg("Simplified %d to %d elements\n", pQuery->ConvexCount(), outputConvexCount );
  384. }
  385. }
  386. else
  387. {
  388. SimplifyConvexElements( convexList, pQuery, ppVerts, params );
  389. }
  390. convertconvexparams_t params;
  391. params.Defaults();
  392. params.buildOuterConvexHull = true;
  393. params.buildDragAxisAreas = false;
  394. CPhysCollide *pCollideOut = physcollision->ConvertConvexToCollideParams( convexList, outputConvexCount, params );
  395. // copy the drag axis areas from the source
  396. Vector dragAxisAreas = physcollision->CollideGetOrthographicAreas( pCollideIn );
  397. physcollision->CollideSetOrthographicAreas( pCollideOut, dragAxisAreas );
  398. physcollision->DestroyQueryModel( pQuery );
  399. delete[] convexList;
  400. delete[] verts;
  401. delete[] ppVerts;
  402. if ( physcollision->CollideSize(pCollideOut) >= sizeIn )
  403. {
  404. // make a copy of the input collide
  405. physcollision->DestroyCollide(pCollideOut);
  406. char *pBuf = new char[sizeIn];
  407. physcollision->CollideWrite( pBuf, pCollideIn );
  408. pCollideOut = physcollision->UnserializeCollide( pBuf, sizeIn, indexIn );
  409. delete[] pBuf;
  410. }
  411. return pCollideOut;
  412. }