tsMeshFit.cpp 31 KB

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