tsMeshFit.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  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 ENABLE_VHACD_IMPLEMENTATION 1
  28. #define VHACD_DISABLE_THREADING 0
  29. #include "ts/vhacd/VHACD.h"
  30. #include <FloatMath.h>
  31. //-----------------------------------------------------------------------------
  32. static const Point3F sFacePlanes[] = {
  33. Point3F( -1.0f, 0.0f, 0.0f ),
  34. Point3F( 1.0f, 0.0f, 0.0f ),
  35. Point3F( 0.0f, -1.0f, 0.0f ),
  36. Point3F( 0.0f, 1.0f, 0.0f ),
  37. Point3F( 0.0f, 0.0f, -1.0f ),
  38. Point3F( 0.0f, 0.0f, 1.0f ),
  39. };
  40. static const Point3F sXEdgePlanes[] = {
  41. Point3F( 0.0f, -0.7071f, -0.7071f ),
  42. Point3F( 0.0f, -0.7071f, 0.7071f ),
  43. Point3F( 0.0f, 0.7071f, -0.7071f ),
  44. Point3F( 0.0f, 0.7071f, 0.7071f ),
  45. };
  46. static const Point3F sYEdgePlanes[] = {
  47. Point3F( -0.7071f, 0.0f, -0.7071f ),
  48. Point3F( -0.7071f, 0.0f, 0.7071f ),
  49. Point3F( 0.7071f, 0.0f, -0.7071f ),
  50. Point3F( 0.7071f, 0.0f, 0.7071f ),
  51. };
  52. static const Point3F sZEdgePlanes[] = {
  53. Point3F( -0.7071f, -0.7071f, 0.0f ),
  54. Point3F( -0.7071f, 0.7071f, 0.0f ),
  55. Point3F( 0.7071f, -0.7071f, 0.0f ),
  56. Point3F( 0.7071f, 0.7071f, 0.0f ),
  57. };
  58. static const Point3F sCornerPlanes[] = {
  59. Point3F( -0.5774f, -0.5774f, -0.5774f ),
  60. Point3F( -0.5774f, -0.5774f, 0.5774f ),
  61. Point3F( -0.5774f, 0.5774f, -0.5774f ),
  62. Point3F( -0.5774f, 0.5774f, 0.5774f ),
  63. Point3F( 0.5774f, -0.5774f, -0.5774f ),
  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. };
  68. //-----------------------------------------------------------------------------
  69. /** A helper class for fitting primitives (Box, Sphere, Capsule) to a triangulated mesh */
  70. struct PrimFit
  71. {
  72. MatrixF mBoxTransform;
  73. Point3F mBoxSides;
  74. Point3F mSphereCenter;
  75. F32 mSphereRadius;
  76. MatrixF mCapTransform;
  77. F32 mCapRadius;
  78. F32 mCapHeight;
  79. public:
  80. PrimFit() :
  81. mBoxTransform(true), mBoxSides(1,1,1),
  82. mSphereCenter(0,0,0), mSphereRadius(1),
  83. mCapTransform(true), mCapRadius(1), mCapHeight(1)
  84. {
  85. }
  86. inline F32 getBoxVolume() const { return mBoxSides.x * mBoxSides.y * mBoxSides.z; }
  87. inline F32 getSphereVolume() const { return 4.0f / 3.0f * M_PI * mPow( mSphereRadius, 3 ); }
  88. inline F32 getCapsuleVolume() const { return 2 * M_PI * mPow( mCapRadius, 2 ) * (4.0f / 3.0f * mCapRadius + mCapHeight); }
  89. void fitBox( U32 vertCount, const F32* verts )
  90. {
  91. FLOAT_MATH::fm_computeBestFitOBB( vertCount, verts, sizeof(F32)*3, (F32*)mBoxSides, (F32*)mBoxTransform, false );
  92. mBoxTransform.transpose();
  93. }
  94. void fitSphere( U32 vertCount, const F32* verts )
  95. {
  96. mSphereRadius = FLOAT_MATH::fm_computeBestFitSphere( vertCount, verts, sizeof(F32)*3, (F32*)mSphereCenter );
  97. }
  98. void fitCapsule( U32 vertCount, const F32* verts )
  99. {
  100. FLOAT_MATH::fm_computeBestFitCapsule( vertCount, verts, sizeof(F32)*3, mCapRadius, mCapHeight, (F32*)mCapTransform );
  101. mCapTransform.transpose();
  102. }
  103. };
  104. class MeshFit
  105. {
  106. public:
  107. enum eMeshType
  108. {
  109. Box = 0,
  110. Sphere,
  111. Capsule,
  112. Hull,
  113. };
  114. struct Mesh
  115. {
  116. eMeshType type;
  117. MatrixF transform;
  118. TSMesh *tsmesh;
  119. };
  120. private:
  121. TSShape *mShape; ///!< Source geometry shape
  122. Vector<Point3F> mVerts; ///!< Source geometry verts (all meshes)
  123. Vector<U32> mIndices; ///!< Source geometry indices (triangle lists, all meshes)
  124. bool mIsReady; ///!< Flag indicating whether we are ready to fit/create meshes
  125. Vector<Mesh> mMeshes; ///!< Fitted meshes
  126. void addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh );
  127. TSMesh* initMeshFromFile( const String& filename ) const;
  128. TSMesh* createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const;
  129. F32 maxDot( const VectorF& v ) const;
  130. void fitK_DOP( const Vector<Point3F>& planes );
  131. public:
  132. MeshFit(TSShape* shape) : mShape(shape), mIsReady(false) { }
  133. void setReady() { mIsReady = true; }
  134. bool isReady() const { return mIsReady; }
  135. void initSourceGeometry( const String& target );
  136. S32 getMeshCount() const { return mMeshes.size(); }
  137. Mesh* getMesh( S32 index ) { return &(mMeshes[index]); }
  138. // Box
  139. void addBox( const Point3F& sides, const MatrixF& mat );
  140. void fitOBB(const char* target);
  141. // Sphere
  142. void addSphere( F32 radius, const Point3F& center );
  143. void fitSphere(const char* target);
  144. // Capsule
  145. void addCapsule( F32 radius, F32 height, const MatrixF& mat );
  146. void fitCapsule(const char* target);
  147. // k-DOP
  148. void fit10_DOP_X();
  149. void fit10_DOP_Y();
  150. void fit10_DOP_Z();
  151. void fit18_DOP();
  152. void fit26_DOP();
  153. // Convex Hulls
  154. void fitConvexHulls( const char* target, U32 depth, U32 fillType, F32 minPercentage, U32 maxHulls, U32 maxHullVerts,
  155. F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError );
  156. };
  157. void MeshFit::initSourceGeometry( const String& target )
  158. {
  159. mMeshes.clear();
  160. mVerts.clear();
  161. mIndices.clear();
  162. if ( target.equal( "bounds", String::NoCase ) )
  163. {
  164. // Add all geometry in the highest detail level
  165. S32 dl = 0;
  166. S32 ss = mShape->details[dl].subShapeNum;
  167. if ( ss < 0 )
  168. return;
  169. S32 od = mShape->details[dl].objectDetailNum;
  170. S32 start = mShape->subShapeFirstObject[ss];
  171. S32 end = start + mShape->subShapeNumObjects[ss];
  172. for ( S32 i = start; i < end; i++ )
  173. {
  174. const TSShape::Object &obj = mShape->objects[i];
  175. const TSMesh* mesh = ( od < obj.numMeshes ) ? mShape->meshes[obj.startMeshIndex + od] : NULL;
  176. if ( mesh )
  177. addSourceMesh( obj, mesh );
  178. }
  179. }
  180. else
  181. {
  182. // Add highest detail mesh from this object
  183. S32 objIndex = mShape->findObject( target );
  184. if ( objIndex == -1 )
  185. return;
  186. const TSShape::Object &obj = mShape->objects[objIndex];
  187. for ( S32 i = 0; i < obj.numMeshes; i++ )
  188. {
  189. const TSMesh* mesh = mShape->meshes[obj.startMeshIndex + i];
  190. if ( mesh )
  191. {
  192. addSourceMesh( obj, mesh );
  193. break;
  194. }
  195. }
  196. }
  197. mIsReady = ( !mVerts.empty() && !mIndices.empty() );
  198. }
  199. void MeshFit::addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh )
  200. {
  201. // Add indices
  202. S32 indicesBase = mIndices.size();
  203. for ( S32 i = 0; i < mesh->mPrimitives.size(); i++ )
  204. {
  205. const TSDrawPrimitive& draw = mesh->mPrimitives[i];
  206. if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles )
  207. {
  208. mIndices.merge( &mesh->mIndices[draw.start], draw.numElements );
  209. }
  210. else
  211. {
  212. U32 idx0 = mesh->mIndices[draw.start + 0];
  213. U32 idx1;
  214. U32 idx2 = mesh->mIndices[draw.start + 1];
  215. U32 *nextIdx = &idx1;
  216. for ( S32 j = 2; j < draw.numElements; j++ )
  217. {
  218. *nextIdx = idx2;
  219. nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
  220. idx2 = mesh->mIndices[draw.start + j];
  221. if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 )
  222. continue;
  223. mIndices.push_back( idx0 );
  224. mIndices.push_back( idx1 );
  225. mIndices.push_back( idx2 );
  226. }
  227. }
  228. }
  229. // Offset indices for already added verts
  230. for ( S32 j = indicesBase; j < mIndices.size(); j++ )
  231. mIndices[j] += mVerts.size();
  232. // Add verts
  233. S32 count, stride;
  234. U8* pVert;
  235. if ( mesh->mVertexData.isReady() && mesh->mVerts.size() == 0 )
  236. {
  237. count = mesh->mVertexData.size();
  238. stride = mesh->mVertexData.vertSize();
  239. pVert = (U8*)mesh->mVertexData.address();
  240. }
  241. else
  242. {
  243. count = mesh->mVerts.size();
  244. stride = sizeof(Point3F);
  245. pVert = (U8*)mesh->mVerts.address();
  246. }
  247. MatrixF objMat;
  248. mShape->getNodeWorldTransform( obj.nodeIndex, &objMat );
  249. mVerts.reserve( mVerts.size() + count );
  250. for ( S32 j = 0; j < count; j++, pVert += stride )
  251. {
  252. mVerts.increment();
  253. objMat.mulP( *(Point3F*)pVert, &mVerts.last() );
  254. }
  255. }
  256. TSMesh* MeshFit::initMeshFromFile( const String& filename ) const
  257. {
  258. // Open the source shape file and make a copy of the mesh
  259. Resource<TSShape> hShape = ResourceManager::get().load(filename);
  260. if (!bool(hShape) || !((TSShape*)hShape)->meshes.size())
  261. {
  262. Con::errorf("TSShape::createMesh: Could not load source mesh from %s", filename.c_str());
  263. return NULL;
  264. }
  265. TSMesh* srcMesh = ((TSShape*)hShape)->meshes[0];
  266. return mShape->copyMesh( srcMesh );
  267. }
  268. TSMesh* MeshFit::createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const
  269. {
  270. TSMesh* mesh = mShape->copyMesh( NULL );
  271. mesh->numFrames = 1;
  272. mesh->numMatFrames = 1;
  273. mesh->vertsPerFrame = numVerts;
  274. mesh->setFlags(0);
  275. mesh->mNumVerts = numVerts;
  276. mesh->mIndices.reserve( numTris * 3 );
  277. for ( S32 i = 0; i < numTris; i++ )
  278. {
  279. mesh->mIndices.push_back( indices[i*3 + 0] );
  280. mesh->mIndices.push_back( indices[i*3 + 2] );
  281. mesh->mIndices.push_back( indices[i*3 + 1] );
  282. }
  283. mesh->mVerts.set( verts, numVerts );
  284. // Compute mesh normals
  285. mesh->mNorms.setSize( mesh->mVerts.size() );
  286. for (S32 iNorm = 0; iNorm < mesh->mNorms.size(); iNorm++)
  287. mesh->mNorms[iNorm] = Point3F::Zero;
  288. // Sum triangle normals for each vertex
  289. for (S32 iInd = 0; iInd < mesh->mIndices.size(); iInd += 3)
  290. {
  291. // Compute the normal for this triangle
  292. S32 idx0 = mesh->mIndices[iInd + 0];
  293. S32 idx1 = mesh->mIndices[iInd + 1];
  294. S32 idx2 = mesh->mIndices[iInd + 2];
  295. const Point3F& v0 = mesh->mVerts[idx0];
  296. const Point3F& v1 = mesh->mVerts[idx1];
  297. const Point3F& v2 = mesh->mVerts[idx2];
  298. Point3F n;
  299. mCross(v2 - v0, v1 - v0, &n);
  300. n.normalize(); // remove this to use 'weighted' normals (large triangles will have more effect)
  301. mesh->mNorms[idx0] += n;
  302. mesh->mNorms[idx1] += n;
  303. mesh->mNorms[idx2] += n;
  304. }
  305. // Normalize the vertex normals (this takes care of averaging the triangle normals)
  306. for (S32 iNorm = 0; iNorm < mesh->mNorms.size(); iNorm++)
  307. mesh->mNorms[iNorm].normalize();
  308. // Set some dummy UVs
  309. mesh->mTverts.setSize( numVerts );
  310. for ( S32 j = 0; j < mesh->mTverts.size(); j++ )
  311. mesh->mTverts[j].set( 0, 0 );
  312. // Add a single triangle-list primitive
  313. mesh->mPrimitives.increment();
  314. mesh->mPrimitives.last().start = 0;
  315. mesh->mPrimitives.last().numElements = mesh->mIndices.size();
  316. mesh->mPrimitives.last().matIndex = TSDrawPrimitive::Triangles |
  317. TSDrawPrimitive::Indexed |
  318. TSDrawPrimitive::NoMaterial;
  319. mesh->createTangents( mesh->mVerts, mesh->mNorms);
  320. mesh->mEncodedNorms.set( NULL,0 );
  321. return mesh;
  322. }
  323. F32 MeshFit::maxDot( const VectorF& v ) const
  324. {
  325. F32 maxDot = -FLT_MAX;
  326. for ( S32 i = 0; i < mVerts.size(); i++ )
  327. maxDot = getMax( maxDot, mDot( v, mVerts[i] ) );
  328. return maxDot;
  329. }
  330. //---------------------------
  331. // Best-fit oriented bounding box
  332. void MeshFit::addBox( const Point3F& sides, const MatrixF& mat )
  333. {
  334. TSMesh* mesh = initMeshFromFile( TSShapeConstructor::getCubeShapePath() );
  335. if ( !mesh )
  336. return;
  337. if (mesh->mVerts.size() > 0)
  338. {
  339. for (S32 i = 0; i < mesh->mVerts.size(); i++)
  340. {
  341. Point3F v = mesh->mVerts[i];
  342. v.convolve(sides);
  343. mat.mulP(v);
  344. mesh->mVerts[i] = v;
  345. }
  346. mesh->mVertexData.setReady(false);
  347. }
  348. else
  349. {
  350. for (S32 i = 0; i < mesh->mVertexData.size(); i++)
  351. {
  352. TSMesh::__TSMeshVertexBase &vdata = mesh->mVertexData.getBase(i);
  353. Point3F v = vdata.vert();
  354. v.convolve(sides);
  355. mat.mulP(v);
  356. vdata.vert(v);
  357. }
  358. }
  359. mesh->computeBounds();
  360. mMeshes.last().type = MeshFit::Box;
  361. mMeshes.last().tsmesh = mesh;
  362. }
  363. void MeshFit::fitOBB(const char* target)
  364. {
  365. mMeshes.increment();
  366. MatrixF worldtrans;
  367. worldtrans.identity();
  368. mShape->getNodeWorldTransform(mShape->findNode(target), &worldtrans);
  369. mMeshes.last().transform = worldtrans;
  370. PrimFit primFitter;
  371. primFitter.fitBox( mVerts.size(), (F32*)mVerts.address() );
  372. addBox( primFitter.mBoxSides, worldtrans.inverse() * primFitter.mBoxTransform );
  373. }
  374. //---------------------------
  375. // Best-fit sphere
  376. void MeshFit::addSphere( F32 radius, const Point3F& center )
  377. {
  378. TSMesh* mesh = initMeshFromFile( TSShapeConstructor::getSphereShapePath() );
  379. if ( !mesh )
  380. return;
  381. MatrixF sphereMat(true);
  382. sphereMat.setPosition(center);
  383. if (mesh->mVerts.size() > 0)
  384. {
  385. for (S32 i = 0; i < mesh->mVerts.size(); i++)
  386. {
  387. Point3F v = mesh->mVerts[i];
  388. sphereMat.mulP(v);
  389. mesh->mVerts[i] = v * radius;
  390. }
  391. mesh->mVertexData.setReady(false);
  392. }
  393. else
  394. {
  395. for (S32 i = 0; i < mesh->mVertexData.size(); i++)
  396. {
  397. TSMesh::__TSMeshVertexBase& vdata = mesh->mVertexData.getBase(i);
  398. Point3F v = vdata.vert();
  399. sphereMat.mulP(v);
  400. vdata.vert(v * radius);
  401. }
  402. }
  403. mesh->computeBounds();
  404. mMeshes.last().type = MeshFit::Sphere;
  405. mMeshes.last().tsmesh = mesh;
  406. }
  407. void MeshFit::fitSphere(const char* target)
  408. {
  409. mMeshes.increment();
  410. MatrixF worldtrans;
  411. worldtrans.identity();
  412. mShape->getNodeWorldTransform(mShape->findNode(target), &worldtrans);
  413. mMeshes.last().transform = worldtrans;
  414. PrimFit primFitter;
  415. primFitter.fitSphere( mVerts.size(), (F32*)mVerts.address() );
  416. worldtrans.inverse();
  417. worldtrans.mulP(primFitter.mSphereCenter);
  418. addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter);
  419. }
  420. //---------------------------
  421. // Best-fit capsule
  422. void MeshFit::addCapsule( F32 radius, F32 height, const MatrixF& mat )
  423. {
  424. TSMesh* mesh = initMeshFromFile( TSShapeConstructor::getCapsuleShapePath() );
  425. if ( !mesh )
  426. return;
  427. MatrixF capTrans = mMeshes.last().transform * mat;
  428. // Translate and scale the mesh verts
  429. height = mMax( 0, height );
  430. F32 offset = ( height / ( 2 * radius ) ) - 0.5f;
  431. if (mesh->mVerts.size() > 0)
  432. {
  433. for (S32 i = 0; i < mesh->mVerts.size(); i++)
  434. {
  435. Point3F v = mesh->mVerts[i];
  436. v.y += ((v.y > 0) ? offset : -offset);
  437. capTrans.mulP(v);
  438. mesh->mVerts[i] = v * radius;
  439. }
  440. mesh->mVertexData.setReady(false);
  441. }
  442. else
  443. {
  444. for (S32 i = 0; i < mesh->mVertexData.size(); i++)
  445. {
  446. TSMesh::__TSMeshVertexBase& vdata = mesh->mVertexData.getBase(i);
  447. Point3F v = vdata.vert();
  448. v.y += ((v.y > 0) ? offset : -offset);
  449. capTrans.mulP(v);
  450. vdata.vert(v * radius);
  451. }
  452. }
  453. mesh->computeBounds();
  454. mMeshes.last().type = MeshFit::Capsule;
  455. mMeshes.last().tsmesh = mesh;
  456. }
  457. void MeshFit::fitCapsule(const char* target)
  458. {
  459. mMeshes.increment();
  460. MatrixF worldtrans;
  461. worldtrans.identity();
  462. mShape->getNodeWorldTransform(mShape->findNode(target), &worldtrans);
  463. mMeshes.last().transform = worldtrans;
  464. PrimFit primFitter;
  465. primFitter.fitCapsule( mVerts.size(), (F32*)mVerts.address() );
  466. addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
  467. }
  468. //---------------------------
  469. // Best-fit k-discrete-oriented-polytope (where k is the number of axis-aligned planes)
  470. // All faces + 4 edges (aligned to X axis) of the unit cube
  471. void MeshFit::fit10_DOP_X()
  472. {
  473. Vector<Point3F> planes;
  474. planes.setSize( 10 );
  475. dCopyArray( planes.address(), sFacePlanes, 6 );
  476. dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
  477. fitK_DOP( planes );
  478. }
  479. // All faces + 4 edges (aligned to Y axis) of the unit cube
  480. void MeshFit::fit10_DOP_Y()
  481. {
  482. Vector<Point3F> planes;
  483. planes.setSize( 10 );
  484. dCopyArray( planes.address(), sFacePlanes, 6 );
  485. dCopyArray( planes.address()+6, sYEdgePlanes, 4 );
  486. fitK_DOP( planes );
  487. }
  488. // All faces + 4 edges (aligned to Z axis) of the unit cube
  489. void MeshFit::fit10_DOP_Z()
  490. {
  491. Vector<Point3F> planes;
  492. planes.setSize( 10 );
  493. dCopyArray( planes.address(), sFacePlanes, 6 );
  494. dCopyArray( planes.address()+6, sZEdgePlanes, 4 );
  495. fitK_DOP( planes );
  496. }
  497. // All faces and edges of the unit cube
  498. void MeshFit::fit18_DOP()
  499. {
  500. Vector<Point3F> planes;
  501. planes.setSize( 18 );
  502. dCopyArray( planes.address(), sFacePlanes, 6 );
  503. dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
  504. dCopyArray( planes.address()+10, sYEdgePlanes, 4 );
  505. dCopyArray( planes.address()+14, sZEdgePlanes, 4 );
  506. fitK_DOP( planes );
  507. }
  508. // All faces, edges and corners of the unit cube
  509. void MeshFit::fit26_DOP()
  510. {
  511. Vector<Point3F> planes;
  512. planes.setSize( 26 );
  513. dCopyArray( planes.address(), sFacePlanes, 6 );
  514. dCopyArray( planes.address()+6, sXEdgePlanes, 4 );
  515. dCopyArray( planes.address()+10, sYEdgePlanes, 4 );
  516. dCopyArray( planes.address()+14, sZEdgePlanes, 4 );
  517. dCopyArray( planes.address()+18, sCornerPlanes, 8 );
  518. fitK_DOP( planes );
  519. }
  520. void MeshFit::fitK_DOP( const Vector<Point3F>& planes )
  521. {
  522. // Push the planes up against the mesh
  523. Vector<F32> planeDs;
  524. for ( S32 i = 0; i < planes.size(); i++ )
  525. planeDs.push_back( maxDot( planes[i] ) );
  526. // Collect the intersection points of any 3 planes that lie inside
  527. // the maximum distances found above
  528. Vector<Point3F> points;
  529. Vector<U32> pointIndices;
  530. for ( S32 i = 0; i < planes.size()-2; i++ )
  531. {
  532. for ( S32 j = i+1; j < planes.size()-1; j++ )
  533. {
  534. for ( S32 k = j+1; k < planes.size(); k++ )
  535. {
  536. Point3F v23 = mCross( planes[j], planes[k] );
  537. F32 denom = mDot( planes[i], v23 );
  538. if ( denom == 0 )
  539. continue;
  540. Point3F v31 = mCross( planes[k], planes[i] );
  541. Point3F v12 = mCross( planes[i], planes[j] );
  542. Point3F p = ( planeDs[i]*v23 + planeDs[j]*v31 + planeDs[k]*v12 ) / denom;
  543. // Ignore intersection points outside the volume
  544. // described by the planes
  545. bool addPoint = true;
  546. for ( S32 n = 0; n < planes.size(); n++ )
  547. {
  548. if ( ( mDot( p, planes[n] ) - planeDs[n] ) > 0.005f )
  549. {
  550. addPoint = false;
  551. break;
  552. }
  553. }
  554. if (addPoint)
  555. {
  556. points.push_back(p);
  557. pointIndices.push_back(points.size() - 1);
  558. }
  559. }
  560. }
  561. }
  562. VHACD::IVHACD::Parameters p;
  563. p.m_fillMode = VHACD::FillMode::FLOOD_FILL;
  564. p.m_maxNumVerticesPerCH = 64;
  565. p.m_shrinkWrap = true;
  566. p.m_maxRecursionDepth = 64;
  567. p.m_minimumVolumePercentErrorAllowed = 10;
  568. p.m_resolution = 10000;
  569. p.m_maxConvexHulls = 1;
  570. VHACD::IVHACD* iface = VHACD::CreateVHACD();
  571. iface->Compute((F32*)points.address(), points.size(), (U32*)pointIndices.address(), pointIndices.size() / 3, p);
  572. // safety loop.
  573. while (!iface->IsReady())
  574. {
  575. Platform::sleep(1000);
  576. }
  577. // we only get the 1 in dop?
  578. VHACD::IVHACD::ConvexHull ch;
  579. iface->GetConvexHull(0, ch);
  580. // Create TSMesh from convex hull
  581. mMeshes.increment();
  582. MeshFit::Mesh& lastMesh = mMeshes.last();
  583. lastMesh.type = MeshFit::Hull;
  584. lastMesh.transform.identity();
  585. U32* indices = new U32[ch.m_triangles.size() * 3];
  586. for (U32 ind = 0; ind < ch.m_triangles.size(); ind++)
  587. {
  588. indices[ind * 3 + 0] = ch.m_triangles[ind].mI0;
  589. indices[ind * 3 + 1] = ch.m_triangles[ind].mI1;
  590. indices[ind * 3 + 2] = ch.m_triangles[ind].mI2;
  591. }
  592. F32* resultPts = new F32[ch.m_points.size() * 3];
  593. for (U32 pts = 0; pts < ch.m_points.size(); pts++)
  594. {
  595. resultPts[pts * 3 + 0] = ch.m_points[pts].mX;
  596. resultPts[pts * 3 + 1] = ch.m_points[pts].mY;
  597. resultPts[pts * 3 + 2] = ch.m_points[pts].mZ;
  598. }
  599. lastMesh.tsmesh = createTriMesh(resultPts, (S32)ch.m_points.size(),
  600. indices, (S32)ch.m_triangles.size());
  601. lastMesh.tsmesh->computeBounds();
  602. iface->Release();
  603. delete[] resultPts;
  604. delete[] indices;
  605. }
  606. //---------------------------
  607. // Best-fit set of convex hulls
  608. void MeshFit::fitConvexHulls(const char* target, U32 depth, U32 fillType, F32 minPercentage, U32 maxHulls, U32 maxHullVerts,
  609. F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError )
  610. {
  611. VHACD::IVHACD::Parameters p;
  612. p.m_fillMode = (VHACD::FillMode)fillType;
  613. p.m_maxNumVerticesPerCH = maxHullVerts;
  614. p.m_shrinkWrap = true;
  615. p.m_maxRecursionDepth = depth;
  616. p.m_minimumVolumePercentErrorAllowed = minPercentage;
  617. p.m_resolution = 10000;
  618. p.m_maxConvexHulls = maxHulls;
  619. VHACD::IVHACD* iface = VHACD::CreateVHACD_ASYNC();
  620. iface->Compute((F32*)mVerts.address(), mVerts.size(), mIndices.address(), mIndices.size() / 3, p);
  621. // safety loop.
  622. while (!iface->IsReady())
  623. {
  624. Platform::sleep(1000);
  625. }
  626. // Add a TSMesh for each hull
  627. for ( S32 i = 0; i < iface->GetNConvexHulls(); i++ )
  628. {
  629. VHACD::IVHACD::ConvexHull ch;
  630. iface->GetConvexHull(i, ch);
  631. mMeshes.increment();
  632. MeshFit::Mesh& lastMesh = mMeshes.last();
  633. eMeshType meshType = MeshFit::Hull;
  634. MatrixF worldtrans;
  635. worldtrans.identity();
  636. mShape->getNodeWorldTransform(mShape->findNode(target), &worldtrans);
  637. lastMesh.transform = worldtrans;
  638. worldtrans.inverse();
  639. // Compute error between actual mesh and fitted primitives
  640. F32* points = new F32[ch.m_points.size() * 3];
  641. for (U32 pt = 0; pt < ch.m_points.size(); pt++)
  642. {
  643. Point3F point(ch.m_points[pt].mX, ch.m_points[pt].mY, ch.m_points[pt].mZ);
  644. worldtrans.mulP(point);
  645. points[pt * 3 + 0] = point.x;
  646. points[pt * 3 + 1] = point.y;
  647. points[pt * 3 + 2] = point.z;
  648. }
  649. U32* indices = new U32[ch.m_triangles.size() * 3];
  650. for (U32 ind = 0; ind < ch.m_triangles.size(); ind++)
  651. {
  652. indices[ind * 3 + 0] = ch.m_triangles[ind].mI0;
  653. indices[ind * 3 + 1] = ch.m_triangles[ind].mI1;
  654. indices[ind * 3 + 2] = ch.m_triangles[ind].mI2;
  655. }
  656. // Check if we can use a box, sphere or capsule primitive for this hull
  657. if (( boxMaxError > 0 ) || ( sphereMaxError > 0 ) || ( capsuleMaxError > 0 ))
  658. {
  659. F32 meshVolume = FLOAT_MATH::fm_computeMeshVolume(points, ch.m_triangles.size(), indices);
  660. PrimFit primFitter;
  661. F32 boxError = 100.0f, sphereError = 100.0f, capsuleError = 100.0;
  662. if ( boxMaxError > 0 )
  663. {
  664. primFitter.fitBox(ch.m_points.size(), points);
  665. boxError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getBoxVolume() ) );
  666. }
  667. if ( sphereMaxError > 0 )
  668. {
  669. primFitter.fitSphere(ch.m_points.size(), points);
  670. sphereError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getSphereVolume()));
  671. }
  672. if ( capsuleMaxError > 0 )
  673. {
  674. primFitter.fitCapsule(ch.m_points.size(), points);
  675. capsuleError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getCapsuleVolume() ) );
  676. }
  677. // Use the primitive type with smallest error less than the respective
  678. // max error, or Hull if none
  679. F32 minError = FLT_MAX;
  680. if ( ( boxError < boxMaxError ) && ( boxError < minError ) )
  681. {
  682. meshType = MeshFit::Box;
  683. minError = boxError;
  684. }
  685. if ( ( sphereError < sphereMaxError ) && ( sphereError < minError ) )
  686. {
  687. meshType = MeshFit::Sphere;
  688. minError = sphereError;
  689. }
  690. if ( ( capsuleError < capsuleMaxError ) && ( capsuleError < minError ) )
  691. {
  692. meshType = MeshFit::Capsule;
  693. minError = capsuleError;
  694. }
  695. if ( meshType == MeshFit::Box )
  696. addBox( primFitter.mBoxSides, primFitter.mBoxTransform );
  697. else if ( meshType == MeshFit::Sphere )
  698. addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter );
  699. else if ( meshType == MeshFit::Capsule )
  700. addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform );
  701. // else fall through to Hull processing
  702. }
  703. if ( meshType == MeshFit::Hull )
  704. {
  705. // Create TSMesh from convex hull
  706. lastMesh.type = MeshFit::Hull;
  707. lastMesh.tsmesh = createTriMesh(points, ch.m_points.size(), indices, ch.m_triangles.size());
  708. lastMesh.tsmesh->computeBounds();
  709. }
  710. delete[] points;
  711. delete[] indices;
  712. }
  713. iface->Release();
  714. }
  715. //-----------------------------------------------------------------------------
  716. DefineTSShapeConstructorMethod( addPrimitive, bool, ( const char* meshName, const char* type, const char* params, TransformF txfm, const char* nodeName ),,
  717. ( meshName, type, params, txfm, nodeName ), false,
  718. "Add a new mesh primitive to the shape.\n"
  719. "@param meshName full name (object name + detail size) of the new mesh. If "
  720. "no detail size is present at the end of the name, a value of 2 is used.<br>"
  721. "An underscore before the number at the end of the name will be interpreted as "
  722. "a negative sign. eg. \"MyMesh_4\" will be interpreted as \"MyMesh-4\".\n"
  723. "@param type one of: \"box\", \"sphere\", \"capsule\"\n"
  724. "@param params mesh primitive parameters:\n"
  725. "<ul>"
  726. "<li>for box: \"size_x size_y size_z\"</li>"
  727. "<li>for sphere: \"radius\"</li>"
  728. "<li>for capsule: \"height radius\"</li>"
  729. "</ul>"
  730. "</ul>\n"
  731. "@param txfm local transform offset from the node for this mesh\n"
  732. "@param nodeName name of the node to attach the new mesh to (will change the "
  733. "object's node if adding a new mesh to an existing object)\n"
  734. "@return true if successful, false otherwise\n\n"
  735. "@tsexample\n"
  736. "%this.addMesh( \"Box4\", \"box\", \"2 4 2\", \"0 2 0 0 0 1 0\", \"eye\" );\n"
  737. "%this.addMesh( \"Sphere256\", \"sphere\", \"2\", \"0 0 0 0 0 1 0\", \"root\" );\n"
  738. "%this.addMesh( \"MyCapsule-1\", \"capsule\", \"2 5\", \"0 0 2 0 0 1 0\", \"base01\" );\n"
  739. "@endtsexample\n" )
  740. {
  741. MeshFit fit( mShape );
  742. if ( !dStricmp( type, "box" ) )
  743. {
  744. // Parse box parameters
  745. Point3F sides;
  746. if ( dSscanf( params, "%g %g %g", &sides.x, &sides.y, &sides.z ) == 3 )
  747. {
  748. fit.addBox( sides, MatrixF::Identity );
  749. fit.setReady();
  750. }
  751. }
  752. else if ( !dStricmp( type, "sphere" ) )
  753. {
  754. // Parse sphere parameters
  755. F32 radius;
  756. if ( dSscanf( params, "%g", &radius ) == 1)
  757. {
  758. fit.addSphere( radius, Point3F::Zero );
  759. fit.setReady();
  760. }
  761. }
  762. else if ( !dStricmp( type, "capsule" ) )
  763. {
  764. // Parse capsule parameters
  765. F32 radius, height;
  766. if ( dSscanf( params, "%g %g", &radius, &height ) == 1)
  767. {
  768. fit.addCapsule( radius, height, MatrixF::Identity );
  769. fit.setReady();
  770. }
  771. }
  772. if ( !fit.isReady() )
  773. {
  774. Con::errorf( "TSShapeConstructor::addPrimitive: Invalid params: '%s' for type '%s'",
  775. params, type );
  776. return false;
  777. }
  778. TSMesh* mesh = fit.getMesh( 0 )->tsmesh;
  779. MatrixF mat( txfm.getMatrix() );
  780. // Transform the mesh vertices
  781. if ( mesh->mVertexData.isReady() && mesh->mVerts.size() == 0 )
  782. {
  783. for (S32 i = 0; i < mesh->mVertexData.size(); i++)
  784. {
  785. TSMesh::__TSMeshVertexBase &vdata = mesh->mVertexData.getBase(i);
  786. Point3F v;
  787. mat.mulP( vdata.vert(), &v );
  788. vdata.vert( v );
  789. }
  790. }
  791. else
  792. {
  793. for (S32 i = 0; i < mesh->mVerts.size(); i++)
  794. {
  795. Point3F v(mesh->mVerts[i]);
  796. mat.mulP( v, &mesh->mVerts[i] );
  797. }
  798. }
  799. // Add the mesh to the shape at the right node
  800. mShape->addMesh( mesh, meshName );
  801. S32 dummy;
  802. String objName = String::GetTrailingNumber( meshName, dummy );
  803. setObjectNode( objName, nodeName );
  804. mShape->init();
  805. ADD_TO_CHANGE_SET();
  806. return true;
  807. }}
  808. DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char* type, const char* target, S32 depth, F32 minPercentage, S32 maxHulls, S32 maxVerts, F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError, const char* fillMode), ( "bounds", 4, 10, 30, 32, 0, 0, 0, "flood fill"),
  809. ( size, type, target, depth, minPercentage, maxHulls, maxVerts, boxMaxError, sphereMaxError, capsuleMaxError, fillMode), false,
  810. "Autofit a mesh primitive or set of convex hulls to the shape geometry. Hulls "
  811. "may optionally be converted to boxes, spheres and/or capsules based on their "
  812. "volume.\n"
  813. "@param size size for this detail level\n"
  814. "@param type one of: box, sphere, capsule, 10-dop x, 10-dop y, 10-dop z, 18-dop, "
  815. "26-dop, convex hulls. See the Shape Editor documentation for more details "
  816. "about these types.\n"
  817. "@param target geometry to fit collision mesh(es) to; either \"bounds\" (for the whole shape), or the name of an object in the shape\n"
  818. "@param depth maximum split recursion depth (hulls only)\n"
  819. "@param minPercentage volume % error threshold (hulls only)\n"
  820. "@param maxHulls allowed to be generated (hulls only)\n"
  821. "@param maxVerts maximum number of vertices per hull (hulls only)\n"
  822. "@param boxMaxError max % volume difference for a hull to be converted to a box (hulls only)\n"
  823. "@param sphereMaxError max % volume difference for a hull to be converted to a sphere (hulls only)\n"
  824. "@param capsuleMaxError max % volume difference for a hull to be converted to a capsule (hulls only)\n"
  825. "@param fillMode method for filling the voxels in the volume (hulls only)\n"
  826. "@return true if successful, false otherwise\n\n"
  827. "@tsexample\n"
  828. "%this.addCollisionDetail( -1, \"box\", \"bounds\" );\n"
  829. "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 10, 30, 32, 0, 0, 0 );\n"
  830. "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\",\"flood fill\", 4, 10, 30, 32, 50, 50, 50 );\n"
  831. "@endtsexample\n" )
  832. {
  833. MeshFit fit( mShape );
  834. fit.initSourceGeometry( target );
  835. if ( !fit.isReady() )
  836. {
  837. Con::errorf( "TSShapeConstructor::addCollisionDetail: Failed to initialise mesh fitter "
  838. "using target: %s", target );
  839. return false;
  840. }
  841. if ( !dStricmp( type, "box" ) )
  842. fit.fitOBB(target);
  843. else if ( !dStricmp( type, "sphere" ) )
  844. fit.fitSphere(target);
  845. else if ( !dStricmp( type, "capsule" ) )
  846. fit.fitCapsule(target);
  847. else if ( !dStricmp( type, "10-dop x" ) )
  848. fit.fit10_DOP_X();
  849. else if ( !dStricmp( type, "10-dop y" ) )
  850. fit.fit10_DOP_Y();
  851. else if ( !dStricmp( type, "10-dop z" ) )
  852. fit.fit10_DOP_Z();
  853. else if ( !dStricmp( type, "18-dop" ) )
  854. fit.fit18_DOP();
  855. else if ( !dStricmp( type, "26-dop" ) )
  856. fit.fit26_DOP();
  857. else if ( !dStricmp( type, "convex hulls" ) )
  858. {
  859. U32 fillType = 0;
  860. if (!dStricmp(fillMode, "surface only"))
  861. fillType = 1;
  862. if (!dStricmp(fillMode, "raycast fill"))
  863. fillType = 2;
  864. fit.fitConvexHulls( target, depth, fillType, minPercentage, maxHulls, maxVerts,
  865. boxMaxError, sphereMaxError, capsuleMaxError );
  866. }
  867. else
  868. {
  869. Con::errorf( "TSShape::addCollisionDetail: Invalid type: '%s'", type );
  870. return false;
  871. }
  872. // Now add the fitted meshes to the shape:
  873. // - primitives (box, sphere, capsule) need their own node (with appropriate
  874. // transform set) so that we can use the mesh bounds to compute the real
  875. // collision primitive at load time without having to examine the geometry.
  876. // - convex meshes may be added at the default node, with identity transform
  877. // - since all meshes are in the same detail level, they all get a unique
  878. // object name
  879. const String colNodeName( String::ToString( "Col%d", size ) );
  880. // Add the default node with identity transform
  881. S32 nodeIndex = mShape->findNode( colNodeName );
  882. if ( nodeIndex == -1 )
  883. {
  884. addNode( colNodeName, "" );
  885. }
  886. else
  887. {
  888. MatrixF mat;
  889. mShape->getNodeWorldTransform( nodeIndex, &mat );
  890. if ( !mat.isIdentity() )
  891. setNodeTransform( colNodeName, TransformF::Identity );
  892. // clean node commands that are related to this target.
  893. cleanTargetNodes(colNodeName, target);
  894. }
  895. // Add the meshes to the shape =>
  896. for ( S32 i = 0; i < fit.getMeshCount(); i++ )
  897. {
  898. MeshFit::Mesh* mesh = fit.getMesh( i );
  899. // Determine a unique name for this mesh
  900. String objName;
  901. switch ( mesh->type )
  902. {
  903. case MeshFit::Box: objName = "ColBox"; break;
  904. case MeshFit::Sphere: objName = "ColSphere"; break;
  905. case MeshFit::Capsule: objName = "ColCapsule"; break;
  906. default: objName = "ColConvex"; break;
  907. }
  908. S32 suffix = i;
  909. while (true)
  910. {
  911. String tempName = objName;
  912. for (S32 s = suffix; s != 0; s /= 26) {
  913. tempName += ('A' + (s % 26));
  914. }
  915. if (mShape->findName(tempName) == -1)
  916. break;
  917. suffix++;
  918. }
  919. for (S32 s = suffix; s != 0; s /= 26) {
  920. objName += ('A' + (s % 26));
  921. }
  922. String meshName = objName + String::ToString("%d", size);
  923. mShape->addMesh( mesh->tsmesh, meshName );
  924. // Add a node for this object if needed (non-identity transform)
  925. addNode( meshName, colNodeName, TransformF( mesh->transform ), false, target);
  926. mShape->setObjectNode( objName, meshName );
  927. }
  928. mShape->init();
  929. ADD_TO_CHANGE_SET();
  930. return true;
  931. }}