tsMeshFit.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "console/consoleTypes.h"
  24. #include "core/resourceManager.h"
  25. #include "ts/tsShapeConstruct.h"
  26. #include "console/engineAPI.h"
  27. // define macros required for ConvexDecomp headers
  28. #if defined( _WIN32 ) && !defined( WIN32 )
  29. #define WIN32
  30. #elif defined( __MACOSX__ ) && !defined( APPLE )
  31. #define APPLE
  32. #endif
  33. #include "convexDecomp/NvFloatMath.h"
  34. #include "convexDecomp/NvConvexDecomposition.h"
  35. #include "convexDecomp/NvStanHull.h"
  36. //-----------------------------------------------------------------------------
  37. static const Point3F sFacePlanes[] = {
  38. Point3F( -1.0f, 0.0f, 0.0f ),
  39. Point3F( 1.0f, 0.0f, 0.0f ),
  40. Point3F( 0.0f, -1.0f, 0.0f ),
  41. Point3F( 0.0f, 1.0f, 0.0f ),
  42. Point3F( 0.0f, 0.0f, -1.0f ),
  43. Point3F( 0.0f, 0.0f, 1.0f ),
  44. };
  45. static const Point3F sXEdgePlanes[] = {
  46. Point3F( 0.0f, -0.7071f, -0.7071f ),
  47. Point3F( 0.0f, -0.7071f, 0.7071f ),
  48. Point3F( 0.0f, 0.7071f, -0.7071f ),
  49. Point3F( 0.0f, 0.7071f, 0.7071f ),
  50. };
  51. static const Point3F sYEdgePlanes[] = {
  52. Point3F( -0.7071f, 0.0f, -0.7071f ),
  53. Point3F( -0.7071f, 0.0f, 0.7071f ),
  54. Point3F( 0.7071f, 0.0f, -0.7071f ),
  55. Point3F( 0.7071f, 0.0f, 0.7071f ),
  56. };
  57. static const Point3F sZEdgePlanes[] = {
  58. Point3F( -0.7071f, -0.7071f, 0.0f ),
  59. Point3F( -0.7071f, 0.7071f, 0.0f ),
  60. Point3F( 0.7071f, -0.7071f, 0.0f ),
  61. Point3F( 0.7071f, 0.7071f, 0.0f ),
  62. };
  63. static const Point3F sCornerPlanes[] = {
  64. Point3F( -0.5774f, -0.5774f, -0.5774f ),
  65. Point3F( -0.5774f, -0.5774f, 0.5774f ),
  66. Point3F( -0.5774f, 0.5774f, -0.5774f ),
  67. Point3F( -0.5774f, 0.5774f, 0.5774f ),
  68. Point3F( 0.5774f, -0.5774f, -0.5774f ),
  69. Point3F( 0.5774f, -0.5774f, 0.5774f ),
  70. Point3F( 0.5774f, 0.5774f, -0.5774f ),
  71. Point3F( 0.5774f, 0.5774f, 0.5774f ),
  72. };
  73. //-----------------------------------------------------------------------------
  74. /** A helper class for fitting primitives (Box, Sphere, Capsule) to a triangulated mesh */
  75. struct PrimFit
  76. {
  77. MatrixF mBoxTransform;
  78. Point3F mBoxSides;
  79. Point3F mSphereCenter;
  80. F32 mSphereRadius;
  81. MatrixF mCapTransform;
  82. F32 mCapRadius;
  83. F32 mCapHeight;
  84. public:
  85. PrimFit() :
  86. mBoxTransform(true), mBoxSides(1,1,1),
  87. mSphereCenter(0,0,0), mSphereRadius(1),
  88. mCapTransform(true), mCapRadius(1), mCapHeight(1)
  89. {
  90. }
  91. inline F32 getBoxVolume() const { return mBoxSides.x * mBoxSides.y * mBoxSides.z; }
  92. inline F32 getSphereVolume() const { return 4.0f / 3.0f * M_PI * mPow( mSphereRadius, 3 ); }
  93. inline F32 getCapsuleVolume() const { return 2 * M_PI * mPow( mCapRadius, 2 ) * (4.0f / 3.0f * mCapRadius + mCapHeight); }
  94. void fitBox( U32 vertCount, const F32* verts )
  95. {
  96. CONVEX_DECOMPOSITION::fm_computeBestFitOBB( vertCount, verts, sizeof(F32)*3, (F32*)mBoxSides, (F32*)mBoxTransform );
  97. mBoxTransform.transpose();
  98. }
  99. void fitSphere( U32 vertCount, const F32* verts )
  100. {
  101. mSphereRadius = CONVEX_DECOMPOSITION::fm_computeBestFitSphere( vertCount, verts, sizeof(F32)*3, (F32*)mSphereCenter );
  102. }
  103. void fitCapsule( U32 vertCount, const F32* verts )
  104. {
  105. CONVEX_DECOMPOSITION::fm_computeBestFitCapsule( vertCount, verts, sizeof(F32)*3, mCapRadius, mCapHeight, (F32*)mCapTransform );
  106. mCapTransform.transpose();
  107. }
  108. };
  109. class MeshFit
  110. {
  111. public:
  112. enum eMeshType
  113. {
  114. Box = 0,
  115. Sphere,
  116. Capsule,
  117. Hull,
  118. };
  119. struct Mesh
  120. {
  121. eMeshType type;
  122. MatrixF transform;
  123. TSMesh *tsmesh;
  124. };
  125. private:
  126. TSShape *mShape; ///!< Source geometry shape
  127. Vector<Point3F> mVerts; ///!< Source geometry verts (all meshes)
  128. Vector<U32> mIndices; ///!< Source geometry indices (triangle lists, all meshes)
  129. bool mIsReady; ///!< Flag indicating whether we are ready to fit/create meshes
  130. Vector<Mesh> mMeshes; ///!< Fitted meshes
  131. void addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh );
  132. TSMesh* initMeshFromFile( const String& filename ) const;
  133. TSMesh* createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const;
  134. F32 maxDot( const VectorF& v ) const;
  135. void fitK_DOP( const Vector<Point3F>& planes );
  136. public:
  137. MeshFit(TSShape* shape) : mShape(shape), mIsReady(false) { }
  138. void setReady() { mIsReady = true; }
  139. bool isReady() const { return mIsReady; }
  140. void initSourceGeometry( const String& target );
  141. S32 getMeshCount() const { return mMeshes.size(); }
  142. Mesh* getMesh( S32 index ) { return &(mMeshes[index]); }
  143. // Box
  144. void addBox( const Point3F& sides, const MatrixF& mat );
  145. void fitOBB();
  146. // Sphere
  147. void addSphere( F32 radius, const Point3F& center );
  148. void fitSphere();
  149. // Capsule
  150. void addCapsule( F32 radius, F32 height, const MatrixF& mat );
  151. void fitCapsule();
  152. // k-DOP
  153. void fit10_DOP_X();
  154. void fit10_DOP_Y();
  155. void fit10_DOP_Z();
  156. void fit18_DOP();
  157. void fit26_DOP();
  158. // Convex Hulls
  159. void fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts,
  160. F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError );
  161. };
  162. void MeshFit::initSourceGeometry( const String& target )
  163. {
  164. mMeshes.clear();
  165. mVerts.clear();
  166. mIndices.clear();
  167. if ( target.equal( "bounds", String::NoCase ) )
  168. {
  169. // Add all geometry in the highest detail level
  170. S32 dl = 0;
  171. S32 ss = mShape->details[dl].subShapeNum;
  172. if ( ss < 0 )
  173. return;
  174. S32 od = mShape->details[dl].objectDetailNum;
  175. S32 start = mShape->subShapeFirstObject[ss];
  176. S32 end = start + mShape->subShapeNumObjects[ss];
  177. for ( S32 i = start; i < end; i++ )
  178. {
  179. const TSShape::Object &obj = mShape->objects[i];
  180. const TSMesh* mesh = ( od < obj.numMeshes ) ? mShape->meshes[obj.startMeshIndex + od] : NULL;
  181. if ( mesh )
  182. addSourceMesh( obj, mesh );
  183. }
  184. }
  185. else
  186. {
  187. // Add highest detail mesh from this object
  188. S32 objIndex = mShape->findObject( target );
  189. if ( objIndex == -1 )
  190. return;
  191. const TSShape::Object &obj = mShape->objects[objIndex];
  192. for ( S32 i = 0; i < obj.numMeshes; i++ )
  193. {
  194. const TSMesh* mesh = mShape->meshes[obj.startMeshIndex + i];
  195. if ( mesh )
  196. {
  197. addSourceMesh( obj, mesh );
  198. break;
  199. }
  200. }
  201. }
  202. mIsReady = ( !mVerts.empty() && !mIndices.empty() );
  203. }
  204. void MeshFit::addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh )
  205. {
  206. // Add indices
  207. S32 indicesBase = mIndices.size();
  208. for ( S32 i = 0; i < mesh->primitives.size(); i++ )
  209. {
  210. const TSDrawPrimitive& draw = mesh->primitives[i];
  211. if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
  212. {
  213. mIndices.merge( &mesh->indices[draw.start], draw.numElements );
  214. }
  215. else
  216. {
  217. U32 idx0 = mesh->indices[draw.start + 0];
  218. U32 idx1;
  219. U32 idx2 = mesh->indices[draw.start + 1];
  220. U32 *nextIdx = &idx1;
  221. for ( S32 j = 2; j < draw.numElements; j++ )
  222. {
  223. *nextIdx = idx2;
  224. nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
  225. idx2 = mesh->indices[draw.start + j];
  226. if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
  227. continue;
  228. mIndices.push_back( idx0 );
  229. mIndices.push_back( idx1 );
  230. mIndices.push_back( idx2 );
  231. }
  232. }
  233. }
  234. // Offset indices for already added verts
  235. for ( S32 j = indicesBase; j < mIndices.size(); j++ )
  236. mIndices[j] += mVerts.size();
  237. // Add verts
  238. S32 count, stride;
  239. U8* pVert;
  240. if ( mesh->mVertexData.isReady() )
  241. {
  242. count = mesh->mVertexData.size();
  243. stride = mesh->mVertexData.vertSize();
  244. pVert = (U8*)mesh->mVertexData.address();
  245. }
  246. else
  247. {
  248. count = mesh->verts.size();
  249. stride = sizeof(Point3F);
  250. pVert = (U8*)mesh->verts.address();
  251. }
  252. MatrixF objMat;
  253. mShape->getNodeWorldTransform( obj.nodeIndex, &objMat );
  254. mVerts.reserve( mVerts.size() + count );
  255. for ( S32 j = 0; j < count; j++, pVert += stride )
  256. {
  257. mVerts.increment();
  258. objMat.mulP( *(Point3F*)pVert, &mVerts.last() );
  259. }
  260. }
  261. TSMesh* MeshFit::initMeshFromFile( const String& filename ) const
  262. {
  263. // Open the source shape file and make a copy of the mesh
  264. Resource<TSShape> hShape = ResourceManager::get().load(filename);
  265. if (!bool(hShape) || !((TSShape*)hShape)->meshes.size())
  266. {
  267. Con::errorf("TSShape::createMesh: Could not load source mesh from %s", filename.c_str());
  268. return NULL;
  269. }
  270. TSMesh* srcMesh = ((TSShape*)hShape)->meshes[0];
  271. return mShape->copyMesh( srcMesh );
  272. }
  273. TSMesh* MeshFit::createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const
  274. {
  275. TSMesh* mesh = mShape->copyMesh( NULL );
  276. mesh->numFrames = 1;
  277. mesh->numMatFrames = 1;
  278. mesh->vertsPerFrame = numVerts;
  279. mesh->setFlags(0);
  280. mesh->mHasColor = false;
  281. mesh->mHasTVert2 = false;
  282. mesh->mNumVerts = numVerts;
  283. mesh->indices.reserve( numTris * 3 );
  284. for ( S32 i = 0; i < numTris; i++ )
  285. {
  286. mesh->indices.push_back( indices[i*3 + 0] );
  287. mesh->indices.push_back( indices[i*3 + 2] );
  288. mesh->indices.push_back( indices[i*3 + 1] );
  289. }
  290. mesh->verts.set( verts, numVerts );
  291. // Compute mesh normals
  292. mesh->norms.setSize( mesh->verts.size() );
  293. for (S32 iNorm = 0; iNorm < mesh->norms.size(); iNorm++)
  294. mesh->norms[iNorm] = Point3F::Zero;
  295. // Sum triangle normals for each vertex
  296. for (S32 iInd = 0; iInd < mesh->indices.size(); iInd += 3)
  297. {
  298. // Compute the normal for this triangle
  299. S32 idx0 = mesh->indices[iInd + 0];
  300. S32 idx1 = mesh->indices[iInd + 1];
  301. S32 idx2 = mesh->indices[iInd + 2];
  302. const Point3F& v0 = mesh->verts[idx0];
  303. const Point3F& v1 = mesh->verts[idx1];
  304. const Point3F& v2 = mesh->verts[idx2];
  305. Point3F n;
  306. mCross(v2 - v0, v1 - v0, &n);
  307. n.normalize(); // remove this to use 'weighted' normals (large triangles will have more effect)
  308. mesh->norms[idx0] += n;
  309. mesh->norms[idx1] += n;
  310. mesh->norms[idx2] += n;
  311. }
  312. // Normalize the vertex normals (this takes care of averaging the triangle normals)
  313. for (S32 iNorm = 0; iNorm < mesh->norms.size(); iNorm++)
  314. mesh->norms[iNorm].normalize();
  315. // Set some dummy UVs
  316. mesh->tverts.setSize( numVerts );
  317. for ( S32 j = 0; j < mesh->tverts.size(); j++ )
  318. mesh->tverts[j].set( 0, 0 );
  319. // Add a single triangle-list primitive
  320. mesh->primitives.increment();
  321. mesh->primitives.last().start = 0;
  322. mesh->primitives.last().numElements = mesh->indices.size();
  323. mesh->primitives.last().matIndex = TSDrawPrimitive::Triangles |
  324. TSDrawPrimitive::Indexed |
  325. TSDrawPrimitive::NoMaterial;
  326. mesh->createTangents( mesh->verts, mesh->norms );
  327. mesh->encodedNorms.set( NULL,0 );
  328. return mesh;
  329. }
  330. F32 MeshFit::maxDot( const VectorF& v ) const
  331. {
  332. F32 maxDot = -FLT_MAX;
  333. for ( S32 i = 0; i < mVerts.size(); i++ )
  334. maxDot = getMax( maxDot, mDot( v, mVerts[i] ) );
  335. return maxDot;
  336. }
  337. //---------------------------
  338. // Best-fit oriented bounding box
  339. void MeshFit::addBox( const Point3F& sides, const MatrixF& mat )
  340. {
  341. TSMesh* mesh = initMeshFromFile( TSShapeConstructor::getCubeShapePath() );
  342. if ( !mesh )
  343. return;
  344. for ( S32 i = 0; i < mesh->mVertexData.size(); i++ )
  345. {
  346. Point3F v = mesh->mVertexData[i].vert();
  347. v.convolve( sides );
  348. mesh->mVertexData[i].vert( v );
  349. }
  350. mesh->computeBounds();
  351. mMeshes.increment();
  352. mMeshes.last().type = MeshFit::Box;
  353. mMeshes.last().transform = mat;
  354. mMeshes.last().tsmesh = mesh;
  355. }
  356. void MeshFit::fitOBB()
  357. {
  358. PrimFit primFitter;
  359. primFitter.fitBox( mVerts.size(), (F32*)mVerts.address() );
  360. addBox( primFitter.mBoxSides, primFitter.mBoxTransform );
  361. }
  362. //---------------------------
  363. // Best-fit sphere
  364. void MeshFit::addSphere( F32 radius, const Point3F& center )
  365. {
  366. TSMesh* mesh = initMeshFromFile( TSShapeConstructor::getSphereShapePath() );
  367. if ( !mesh )
  368. return;
  369. for ( S32 i = 0; i < mesh->mVertexData.size(); i++ )
  370. {
  371. Point3F v = mesh->mVertexData[i].vert();
  372. mesh->mVertexData[i].vert( v * radius );
  373. }
  374. mesh->computeBounds();
  375. mMeshes.increment();
  376. MeshFit::Mesh& lastMesh = mMeshes.last();
  377. lastMesh.type = MeshFit::Sphere;
  378. lastMesh.transform.identity();
  379. lastMesh.transform.setPosition(center);
  380. lastMesh.tsmesh = mesh;
  381. }
  382. void MeshFit::fitSphere()
  383. {
  384. PrimFit primFitter;
  385. primFitter.fitSphere( mVerts.size(), (F32*)mVerts.address() );
  386. addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter );
  387. }
  388. //---------------------------
  389. // Best-fit capsule
  390. void MeshFit::addCapsule( F32 radius, F32 height, const MatrixF& mat )
  391. {
  392. TSMesh* mesh = initMeshFromFile( TSShapeConstructor::getCapsuleShapePath() );
  393. if ( !mesh )
  394. return;
  395. // Translate and scale the mesh verts
  396. height = mMax( 0, height );
  397. F32 offset = ( height / ( 2 * radius ) ) - 0.5f;
  398. for ( S32 i = 0; i < mesh->mVertexData.size(); i++ )
  399. {
  400. Point3F v = mesh->mVertexData[i].vert();
  401. v.y += ( ( v.y > 0 ) ? offset : -offset );
  402. mesh->mVertexData[i].vert( v * radius );
  403. }
  404. mesh->computeBounds();
  405. mMeshes.increment();
  406. mMeshes.last().type = MeshFit::Capsule;
  407. mMeshes.last().transform = mat;
  408. mMeshes.last().tsmesh = mesh;
  409. }
  410. void MeshFit::fitCapsule()
  411. {
  412. PrimFit primFitter;
  413. primFitter.fitCapsule( mVerts.size(), (F32*)mVerts.address() );
  414. addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
  415. }
  416. //---------------------------
  417. // Best-fit k-discrete-oriented-polytope (where k is the number of axis-aligned planes)
  418. // All faces + 4 edges (aligned to X axis) of the unit cube
  419. void MeshFit::fit10_DOP_X()
  420. {
  421. Vector<Point3F> planes;
  422. planes.setSize( 10 );
  423. dCopyArray( planes.address(), sFacePlanes, 6 );
  424. dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
  425. fitK_DOP( planes );
  426. }
  427. // All faces + 4 edges (aligned to Y axis) of the unit cube
  428. void MeshFit::fit10_DOP_Y()
  429. {
  430. Vector<Point3F> planes;
  431. planes.setSize( 10 );
  432. dCopyArray( planes.address(), sFacePlanes, 6 );
  433. dCopyArray( planes.address()+6, sYEdgePlanes, 4 );
  434. fitK_DOP( planes );
  435. }
  436. // All faces + 4 edges (aligned to Z axis) of the unit cube
  437. void MeshFit::fit10_DOP_Z()
  438. {
  439. Vector<Point3F> planes;
  440. planes.setSize( 10 );
  441. dCopyArray( planes.address(), sFacePlanes, 6 );
  442. dCopyArray( planes.address()+6, sZEdgePlanes, 4 );
  443. fitK_DOP( planes );
  444. }
  445. // All faces and edges of the unit cube
  446. void MeshFit::fit18_DOP()
  447. {
  448. Vector<Point3F> planes;
  449. planes.setSize( 18 );
  450. dCopyArray( planes.address(), sFacePlanes, 6 );
  451. dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
  452. dCopyArray( planes.address()+10, sYEdgePlanes, 4 );
  453. dCopyArray( planes.address()+14, sZEdgePlanes, 4 );
  454. fitK_DOP( planes );
  455. }
  456. // All faces, edges and corners of the unit cube
  457. void MeshFit::fit26_DOP()
  458. {
  459. Vector<Point3F> planes;
  460. planes.setSize( 26 );
  461. dCopyArray( planes.address(), sFacePlanes, 6 );
  462. dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
  463. dCopyArray( planes.address()+10, sYEdgePlanes, 4 );
  464. dCopyArray( planes.address()+14, sZEdgePlanes, 4 );
  465. dCopyArray( planes.address()+18, sCornerPlanes, 8 );
  466. fitK_DOP( planes );
  467. }
  468. void MeshFit::fitK_DOP( const Vector<Point3F>& planes )
  469. {
  470. // Push the planes up against the mesh
  471. Vector<F32> planeDs;
  472. for ( S32 i = 0; i < planes.size(); i++ )
  473. planeDs.push_back( maxDot( planes[i] ) );
  474. // Collect the intersection points of any 3 planes that lie inside
  475. // the maximum distances found above
  476. Vector<Point3F> points;
  477. for ( S32 i = 0; i < planes.size()-2; i++ )
  478. {
  479. for ( S32 j = i+1; j < planes.size()-1; j++ )
  480. {
  481. for ( S32 k = j+1; k < planes.size(); k++ )
  482. {
  483. Point3F v23 = mCross( planes[j], planes[k] );
  484. F32 denom = mDot( planes[i], v23 );
  485. if ( denom == 0 )
  486. continue;
  487. Point3F v31 = mCross( planes[k], planes[i] );
  488. Point3F v12 = mCross( planes[i], planes[j] );
  489. Point3F p = ( planeDs[i]*v23 + planeDs[j]*v31 + planeDs[k]*v12 ) / denom;
  490. // Ignore intersection points outside the volume
  491. // described by the planes
  492. bool addPoint = true;
  493. for ( S32 n = 0; n < planes.size(); n++ )
  494. {
  495. if ( ( mDot( p, planes[n] ) - planeDs[n] ) > 0.005f )
  496. {
  497. addPoint = false;
  498. break;
  499. }
  500. }
  501. if ( addPoint )
  502. points.push_back( p );
  503. }
  504. }
  505. }
  506. // Create a convex hull from the point set
  507. CONVEX_DECOMPOSITION::HullDesc hd;
  508. hd.mVcount = points.size();
  509. hd.mVertices = (F32*)points.address();
  510. hd.mVertexStride = sizeof(Point3F);
  511. hd.mMaxVertices = 64;
  512. hd.mSkinWidth = 0.0f;
  513. CONVEX_DECOMPOSITION::HullLibrary hl;
  514. CONVEX_DECOMPOSITION::HullResult result;
  515. hl.CreateConvexHull( hd, result );
  516. // Create TSMesh from convex hull
  517. mMeshes.increment();
  518. MeshFit::Mesh& lastMesh = mMeshes.last();
  519. lastMesh.type = MeshFit::Hull;
  520. lastMesh.transform.identity();
  521. lastMesh.tsmesh = createTriMesh(result.mOutputVertices, result.mNumOutputVertices,
  522. result.mIndices, result.mNumFaces );
  523. lastMesh.tsmesh->computeBounds();
  524. }
  525. //---------------------------
  526. // Best-fit set of convex hulls
  527. void MeshFit::fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts,
  528. F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError )
  529. {
  530. const F32 SkinWidth = 0.0f;
  531. const F32 SplitThreshold = 2.0f;
  532. CONVEX_DECOMPOSITION::iConvexDecomposition *ic = CONVEX_DECOMPOSITION::createConvexDecomposition();
  533. for ( S32 i = 0; i < mIndices.size(); i += 3 )
  534. {
  535. ic->addTriangle( (F32*)mVerts[mIndices[i]],
  536. (F32*)mVerts[mIndices[i+1]],
  537. (F32*)mVerts[mIndices[i+2]] );
  538. }
  539. ic->computeConvexDecomposition(
  540. SkinWidth,
  541. depth,
  542. maxHullVerts,
  543. concavityThreshold,
  544. mergeThreshold,
  545. SplitThreshold,
  546. true,
  547. false,
  548. false );
  549. // Add a TSMesh for each hull
  550. for ( S32 i = 0; i < ic->getHullCount(); i++ )
  551. {
  552. CONVEX_DECOMPOSITION::ConvexHullResult result;
  553. ic->getConvexHullResult( i, result );
  554. eMeshType meshType = MeshFit::Hull;
  555. // Check if we can use a box, sphere or capsule primitive for this hull
  556. if (( boxMaxError > 0 ) || ( sphereMaxError > 0 ) || ( capsuleMaxError > 0 ))
  557. {
  558. // Compute error between actual mesh and fitted primitives
  559. F32 meshVolume = CONVEX_DECOMPOSITION::fm_computeMeshVolume( result.mVertices, result.mTcount, result.mIndices );
  560. PrimFit primFitter;
  561. F32 boxError = 100.0f, sphereError = 100.0f, capsuleError = 100.0f;
  562. if ( boxMaxError > 0 )
  563. {
  564. primFitter.fitBox( result.mVcount, result.mVertices );
  565. boxError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getBoxVolume() ) );
  566. }
  567. if ( sphereMaxError > 0 )
  568. {
  569. primFitter.fitSphere( result.mVcount, result.mVertices );
  570. sphereError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getSphereVolume() ) );
  571. }
  572. if ( capsuleMaxError > 0 )
  573. {
  574. primFitter.fitCapsule( result.mVcount, result.mVertices );
  575. capsuleError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getCapsuleVolume() ) );
  576. }
  577. // Use the primitive type with smallest error less than the respective
  578. // max error, or Hull if none
  579. F32 minError = FLT_MAX;
  580. if ( ( boxError < boxMaxError ) && ( boxError < minError ) )
  581. {
  582. meshType = MeshFit::Box;
  583. minError = boxError;
  584. }
  585. if ( ( sphereError < sphereMaxError ) && ( sphereError < minError ) )
  586. {
  587. meshType = MeshFit::Sphere;
  588. minError = sphereError;
  589. }
  590. if ( ( capsuleError < capsuleMaxError ) && ( capsuleError < minError ) )
  591. {
  592. meshType = MeshFit::Capsule;
  593. minError = capsuleError;
  594. }
  595. if ( meshType == MeshFit::Box )
  596. addBox( primFitter.mBoxSides, primFitter.mBoxTransform );
  597. else if ( meshType == MeshFit::Sphere )
  598. addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter );
  599. else if ( meshType == MeshFit::Capsule )
  600. addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
  601. // else fall through to Hull processing
  602. }
  603. if ( meshType == MeshFit::Hull )
  604. {
  605. // Create TSMesh from convex hull
  606. mMeshes.increment();
  607. MeshFit::Mesh& lastMesh = mMeshes.last();
  608. lastMesh.type = MeshFit::Hull;
  609. lastMesh.transform.identity();
  610. lastMesh.tsmesh = createTriMesh(result.mVertices, result.mVcount, result.mIndices, result.mTcount);
  611. lastMesh.tsmesh->computeBounds();
  612. }
  613. }
  614. CONVEX_DECOMPOSITION::releaseConvexDecomposition( ic );
  615. }
  616. //-----------------------------------------------------------------------------
  617. DefineTSShapeConstructorMethod( addPrimitive, bool, ( const char* meshName, const char* type, const char* params, TransformF txfm, const char* nodeName ),,
  618. ( meshName, type, params, txfm, nodeName ), false,
  619. "Add a new mesh primitive to the shape.\n"
  620. "@param meshName full name (object name + detail size) of the new mesh. If "
  621. "no detail size is present at the end of the name, a value of 2 is used.<br>"
  622. "An underscore before the number at the end of the name will be interpreted as "
  623. "a negative sign. eg. \"MyMesh_4\" will be interpreted as \"MyMesh-4\".\n"
  624. "@param type one of: \"box\", \"sphere\", \"capsule\"\n"
  625. "@param params mesh primitive parameters:\n"
  626. "<ul>"
  627. "<li>for box: \"size_x size_y size_z\"</li>"
  628. "<li>for sphere: \"radius\"</li>"
  629. "<li>for capsule: \"height radius\"</li>"
  630. "</ul>"
  631. "</ul>\n"
  632. "@param txfm local transform offset from the node for this mesh\n"
  633. "@param nodeName name of the node to attach the new mesh to (will change the "
  634. "object's node if adding a new mesh to an existing object)\n"
  635. "@return true if successful, false otherwise\n\n"
  636. "@tsexample\n"
  637. "%this.addMesh( \"Box4\", \"box\", \"2 4 2\", \"0 2 0 0 0 1 0\", \"eye\" );\n"
  638. "%this.addMesh( \"Sphere256\", \"sphere\", \"2\", \"0 0 0 0 0 1 0\", \"root\" );\n"
  639. "%this.addMesh( \"MyCapsule-1\", \"capsule\", \"2 5\", \"0 0 2 0 0 1 0\", \"base01\" );\n"
  640. "@endtsexample\n" )
  641. {
  642. MeshFit fit( mShape );
  643. if ( !dStricmp( type, "box" ) )
  644. {
  645. // Parse box parameters
  646. Point3F sides;
  647. if ( dSscanf( params, "%g %g %g", &sides.x, &sides.y, &sides.z ) == 3 )
  648. {
  649. fit.addBox( sides, MatrixF::Identity );
  650. fit.setReady();
  651. }
  652. }
  653. else if ( !dStricmp( type, "sphere" ) )
  654. {
  655. // Parse sphere parameters
  656. F32 radius;
  657. if ( dSscanf( params, "%g", &radius ) == 1)
  658. {
  659. fit.addSphere( radius, Point3F::Zero );
  660. fit.setReady();
  661. }
  662. }
  663. else if ( !dStricmp( type, "capsule" ) )
  664. {
  665. // Parse capsule parameters
  666. F32 radius, height;
  667. if ( dSscanf( params, "%g %g", &radius, &height ) == 1)
  668. {
  669. fit.addCapsule( radius, height, MatrixF::Identity );
  670. fit.setReady();
  671. }
  672. }
  673. if ( !fit.isReady() )
  674. {
  675. Con::errorf( "TSShapeConstructor::addPrimitive: Invalid params: '%s' for type '%s'",
  676. params, type );
  677. return false;
  678. }
  679. TSMesh* mesh = fit.getMesh( 0 )->tsmesh;
  680. MatrixF mat( txfm.getMatrix() );
  681. // Transform the mesh vertices
  682. if ( mesh->mVertexData.isReady() )
  683. {
  684. for (S32 i = 0; i < mesh->mVertexData.size(); i++)
  685. {
  686. Point3F v;
  687. mat.mulP( mesh->mVertexData[i].vert(), &v );
  688. mesh->mVertexData[i].vert( v );
  689. }
  690. }
  691. else
  692. {
  693. for (S32 i = 0; i < mesh->verts.size(); i++)
  694. {
  695. Point3F v(mesh->verts[i]);
  696. mat.mulP( v, &mesh->verts[i] );
  697. }
  698. }
  699. // Add the mesh to the shape at the right node
  700. mShape->addMesh( mesh, meshName );
  701. S32 dummy;
  702. String objName = String::GetTrailingNumber( meshName, dummy );
  703. setObjectNode( objName, nodeName );
  704. mShape->init();
  705. ADD_TO_CHANGE_SET();
  706. return true;
  707. }}
  708. DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char* type, const char* target, S32 depth, F32 merge, F32 concavity, S32 maxVerts, F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError ), ( 4, 30, 30, 32, 0, 0, 0 ),
  709. ( size, type, target, depth, merge, concavity, maxVerts, boxMaxError, sphereMaxError, capsuleMaxError ), false,
  710. "Autofit a mesh primitive or set of convex hulls to the shape geometry. Hulls "
  711. "may optionally be converted to boxes, spheres and/or capsules based on their "
  712. "volume.\n"
  713. "@param size size for this detail level\n"
  714. "@param type one of: box, sphere, capsule, 10-dop x, 10-dop y, 10-dop z, 18-dop, "
  715. "26-dop, convex hulls. See the Shape Editor documentation for more details "
  716. "about these types.\n"
  717. "@param target geometry to fit collision mesh(es) to; either \"bounds\" (for the "
  718. "whole shape), or the name of an object in the shape\n"
  719. "@param depth maximum split recursion depth (hulls only)\n"
  720. "@param merge volume % threshold used to merge hulls together (hulls only)\n"
  721. "@param concavity volume % threshold used to detect concavity (hulls only)\n"
  722. "@param maxVerts maximum number of vertices per hull (hulls only)\n"
  723. "@param boxMaxError max % volume difference for a hull to be converted to a "
  724. "box (hulls only)\n"
  725. "@param sphereMaxError max % volume difference for a hull to be converted to "
  726. "a sphere (hulls only)\n"
  727. "@param capsuleMaxError max % volume difference for a hull to be converted to "
  728. "a capsule (hulls only)\n"
  729. "@return true if successful, false otherwise\n\n"
  730. "@tsexample\n"
  731. "%this.addCollisionDetail( -1, \"box\", \"bounds\" );\n"
  732. "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 0, 0, 0 );\n"
  733. "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 50, 50, 50 );\n"
  734. "@endtsexample\n" )
  735. {
  736. MeshFit fit( mShape );
  737. fit.initSourceGeometry( target );
  738. if ( !fit.isReady() )
  739. {
  740. Con::errorf( "TSShapeConstructor::addCollisionDetail: Failed to initialise mesh fitter "
  741. "using target: %s", target );
  742. return false;
  743. }
  744. if ( !dStricmp( type, "box" ) )
  745. fit.fitOBB();
  746. else if ( !dStricmp( type, "sphere" ) )
  747. fit.fitSphere();
  748. else if ( !dStricmp( type, "capsule" ) )
  749. fit.fitCapsule();
  750. else if ( !dStricmp( type, "10-dop x" ) )
  751. fit.fit10_DOP_X();
  752. else if ( !dStricmp( type, "10-dop y" ) )
  753. fit.fit10_DOP_Y();
  754. else if ( !dStricmp( type, "10-dop z" ) )
  755. fit.fit10_DOP_Z();
  756. else if ( !dStricmp( type, "18-dop" ) )
  757. fit.fit18_DOP();
  758. else if ( !dStricmp( type, "26-dop" ) )
  759. fit.fit26_DOP();
  760. else if ( !dStricmp( type, "convex hulls" ) )
  761. {
  762. fit.fitConvexHulls( depth, merge, concavity, maxVerts,
  763. boxMaxError, sphereMaxError, capsuleMaxError );
  764. }
  765. else
  766. {
  767. Con::errorf( "TSShape::addCollisionDetail: Invalid type: '%s'", type );
  768. return false;
  769. }
  770. // Now add the fitted meshes to the shape:
  771. // - primitives (box, sphere, capsule) need their own node (with appropriate
  772. // transform set) so that we can use the mesh bounds to compute the real
  773. // collision primitive at load time without having to examine the geometry.
  774. // - convex meshes may be added at the default node, with identity transform
  775. // - since all meshes are in the same detail level, they all get a unique
  776. // object name
  777. const String colNodeName( String::ToString( "Col%d", size ) );
  778. // Add the default node with identity transform
  779. S32 nodeIndex = mShape->findNode( colNodeName );
  780. if ( nodeIndex == -1 )
  781. {
  782. addNode( colNodeName, "" );
  783. }
  784. else
  785. {
  786. MatrixF mat;
  787. mShape->getNodeWorldTransform( nodeIndex, &mat );
  788. if ( !mat.isIdentity() )
  789. setNodeTransform( colNodeName, TransformF::Identity );
  790. }
  791. // Add the meshes to the shape =>
  792. for ( S32 i = 0; i < fit.getMeshCount(); i++ )
  793. {
  794. MeshFit::Mesh* mesh = fit.getMesh( i );
  795. // Determine a unique name for this mesh
  796. String objName;
  797. switch ( mesh->type )
  798. {
  799. case MeshFit::Box: objName = "ColBox"; break;
  800. case MeshFit::Sphere: objName = "ColSphere"; break;
  801. case MeshFit::Capsule: objName = "ColCapsule"; break;
  802. default: objName = "ColConvex"; break;
  803. }
  804. for ( S32 suffix = i; suffix != 0; suffix /= 26 )
  805. objName += ('A' + ( suffix % 26 ) );
  806. String meshName = objName + String::ToString( "%d", size );
  807. mShape->addMesh( mesh->tsmesh, meshName );
  808. // Add a node for this object if needed (non-identity transform)
  809. if ( mesh->transform.isIdentity() )
  810. {
  811. mShape->setObjectNode( objName, colNodeName );
  812. }
  813. else
  814. {
  815. addNode( meshName, colNodeName, TransformF( mesh->transform ) );
  816. mShape->setObjectNode( objName, meshName );
  817. }
  818. }
  819. mShape->init();
  820. ADD_TO_CHANGE_SET();
  821. return true;
  822. }}