1 /* 2 Stan Melax Convex Hull Computation 3 Copyright (c) 2003-2006 Stan Melax http://www.melax.com/ 4 5 This software is provided 'as-is', without any express or implied warranty. 6 In no event will the authors be held liable for any damages arising from the use of this software. 7 Permission is granted to anyone to use this software for any purpose, 8 including commercial applications, and to alter it and redistribute it freely, 9 subject to the following restrictions: 10 11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 13 3. This notice may not be removed or altered from any source distribution. 14 */ 15 16 #include <string.h> 17 18 #include "btConvexHull.h" 19 #include "btAlignedObjectArray.h" 20 #include "btMinMax.h" 21 #include "btVector3.h" 22 23 24 25 26 27 //---------------------------------- 28 29 class int3 30 { 31 public: 32 int x,y,z; 33 int3(){}; 34 int3(int _x,int _y, int _z){x=_x;y=_y;z=_z;} 35 const int& operator[](int i) const {return (&x)[i];} 36 int& operator[](int i) {return (&x)[i];} 37 }; 38 39 40 //------- btPlane ---------- 41 42 43 inline btPlane PlaneFlip(const btPlane &plane){return btPlane(-plane.normal,-plane.dist);} 44 inline int operator==( const btPlane &a, const btPlane &b ) { return (a.normal==b.normal && a.dist==b.dist); } 45 inline int coplanar( const btPlane &a, const btPlane &b ) { return (a==b || a==PlaneFlip(b)); } 46 47 48 //--------- Utility Functions ------ 49 50 btVector3 PlaneLineIntersection(const btPlane &plane, const btVector3 &p0, const btVector3 &p1); 51 btVector3 PlaneProject(const btPlane &plane, const btVector3 &point); 52 53 btVector3 ThreePlaneIntersection(const btPlane &p0,const btPlane &p1, const btPlane &p2); 54 btVector3 ThreePlaneIntersection(const btPlane &p0,const btPlane &p1, const btPlane &p2) 55 { 56 btVector3 N1 = p0.normal; 57 btVector3 N2 = p1.normal; 58 btVector3 N3 = p2.normal; 59 60 btVector3 n2n3; n2n3 = N2.cross(N3); 61 btVector3 n3n1; n3n1 = N3.cross(N1); 62 btVector3 n1n2; n1n2 = N1.cross(N2); 63 64 btScalar quotient = (N1.dot(n2n3)); 65 66 btAssert(btFabs(quotient) > btScalar(0.000001)); 67 68 quotient = btScalar(-1.) / quotient; 69 n2n3 *= p0.dist; 70 n3n1 *= p1.dist; 71 n1n2 *= p2.dist; 72 btVector3 potentialVertex = n2n3; 73 potentialVertex += n3n1; 74 potentialVertex += n1n2; 75 potentialVertex *= quotient; 76 77 btVector3 result(potentialVertex.getX(),potentialVertex.getY(),potentialVertex.getZ()); 78 return result; 79 80 } 81 82 btScalar DistanceBetweenLines(const btVector3 &ustart, const btVector3 &udir, const btVector3 &vstart, const btVector3 &vdir, btVector3 *upoint=NULL, btVector3 *vpoint=NULL); 83 btVector3 TriNormal(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2); 84 btVector3 NormalOf(const btVector3 *vert, const int n); 85 86 87 btVector3 PlaneLineIntersection(const btPlane &plane, const btVector3 &p0, const btVector3 &p1) 88 { 89 // returns the point where the line p0-p1 intersects the plane n&d 90 static btVector3 dif; 91 dif = p1-p0; 92 btScalar dn= btDot(plane.normal,dif); 93 btScalar t = -(plane.dist+btDot(plane.normal,p0) )/dn; 94 return p0 + (dif*t); 95 } 96 97 btVector3 PlaneProject(const btPlane &plane, const btVector3 &point) 98 { 99 return point - plane.normal * (btDot(point,plane.normal)+plane.dist); 100 } 101 102 btVector3 TriNormal(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2) 103 { 104 // return the normal of the triangle 105 // inscribed by v0, v1, and v2 106 btVector3 cp=btCross(v1-v0,v2-v1); 107 btScalar m=cp.length(); 108 if(m==0) return btVector3(1,0,0); 109 return cp*(btScalar(1.0)/m); 110 } 111 112 113 btScalar DistanceBetweenLines(const btVector3 &ustart, const btVector3 &udir, const btVector3 &vstart, const btVector3 &vdir, btVector3 *upoint, btVector3 *vpoint) 114 { 115 static btVector3 cp; 116 cp = btCross(udir,vdir).normalized(); 117 118 btScalar distu = -btDot(cp,ustart); 119 btScalar distv = -btDot(cp,vstart); 120 btScalar dist = (btScalar)fabs(distu-distv); 121 if(upoint) 122 { 123 btPlane plane; 124 plane.normal = btCross(vdir,cp).normalized(); 125 plane.dist = -btDot(plane.normal,vstart); 126 *upoint = PlaneLineIntersection(plane,ustart,ustart+udir); 127 } 128 if(vpoint) 129 { 130 btPlane plane; 131 plane.normal = btCross(udir,cp).normalized(); 132 plane.dist = -btDot(plane.normal,ustart); 133 *vpoint = PlaneLineIntersection(plane,vstart,vstart+vdir); 134 } 135 return dist; 136 } 137 138 139 140 141 142 143 144 #define COPLANAR (0) 145 #define UNDER (1) 146 #define OVER (2) 147 #define SPLIT (OVER|UNDER) 148 #define PAPERWIDTH (btScalar(0.001)) 149 150 btScalar planetestepsilon = PAPERWIDTH; 151 152 153 154 typedef ConvexH::HalfEdge HalfEdge; 155 156 ConvexH::ConvexH(int vertices_size,int edges_size,int facets_size) 157 { 158 vertices.resize(vertices_size); 159 edges.resize(edges_size); 160 facets.resize(facets_size); 161 } 162 163 164 int PlaneTest(const btPlane &p, const btVector3 &v); 165 int PlaneTest(const btPlane &p, const btVector3 &v) { 166 btScalar a = btDot(v,p.normal)+p.dist; 167 int flag = (a>planetestepsilon)?OVER:((a<-planetestepsilon)?UNDER:COPLANAR); 168 return flag; 169 } 170 171 int SplitTest(ConvexH &convex,const btPlane &plane); 172 int SplitTest(ConvexH &convex,const btPlane &plane) { 173 int flag=0; 174 for(int i=0;i<convex.vertices.size();i++) { 175 flag |= PlaneTest(plane,convex.vertices[i]); 176 } 177 return flag; 178 } 179 180 class VertFlag 181 { 182 public: 183 unsigned char planetest; 184 unsigned char junk; 185 unsigned char undermap; 186 unsigned char overmap; 187 }; 188 class EdgeFlag 189 { 190 public: 191 unsigned char planetest; 192 unsigned char fixes; 193 short undermap; 194 short overmap; 195 }; 196 class PlaneFlag 197 { 198 public: 199 unsigned char undermap; 200 unsigned char overmap; 201 }; 202 class Coplanar{ 203 public: 204 unsigned short ea; 205 unsigned char v0; 206 unsigned char v1; 207 }; 208 209 210 211 212 213 214 215 216 template<class T> 217 int maxdirfiltered(const T *p,int count,const T &dir,btAlignedObjectArray<int> &allow) 218 { 219 btAssert(count); 220 int m=-1; 221 for(int i=0;i<count;i++) 222 if(allow[i]) 223 { 224 if(m==-1 || btDot(p[i],dir)>btDot(p[m],dir)) 225 m=i; 226 } 227 btAssert(m!=-1); 228 return m; 229 } 230 231 btVector3 orth(const btVector3 &v); 232 btVector3 orth(const btVector3 &v) 233 { 234 btVector3 a=btCross(v,btVector3(0,0,1)); 235 btVector3 b=btCross(v,btVector3(0,1,0)); 236 if (a.length() > b.length()) 237 { 238 return a.normalized(); 239 } else { 240 return b.normalized(); 241 } 242 } 243 244 245 template<class T> 246 int maxdirsterid(const T *p,int count,const T &dir,btAlignedObjectArray<int> &allow) 247 { 248 int m=-1; 249 while(m==-1) 250 { 251 m = maxdirfiltered(p,count,dir,allow); 252 if(allow[m]==3) return m; 253 T u = orth(dir); 254 T v = btCross(u,dir); 255 int ma=-1; 256 for(btScalar x = btScalar(0.0) ; x<= btScalar(360.0) ; x+= btScalar(45.0)) 257 { 258 btScalar s = btSin(SIMD_RADS_PER_DEG*(x)); 259 btScalar c = btCos(SIMD_RADS_PER_DEG*(x)); 260 int mb = maxdirfiltered(p,count,dir+(u*s+v*c)*btScalar(0.025),allow); 261 if(ma==m && mb==m) 262 { 263 allow[m]=3; 264 return m; 265 } 266 if(ma!=-1 && ma!=mb) // Yuck - this is really ugly 267 { 268 int mc = ma; 269 for(btScalar xx = x-btScalar(40.0) ; xx <= x ; xx+= btScalar(5.0)) 270 { 271 btScalar s = btSin(SIMD_RADS_PER_DEG*(xx)); 272 btScalar c = btCos(SIMD_RADS_PER_DEG*(xx)); 273 int md = maxdirfiltered(p,count,dir+(u*s+v*c)*btScalar(0.025),allow); 274 if(mc==m && md==m) 275 { 276 allow[m]=3; 277 return m; 278 } 279 mc=md; 280 } 281 } 282 ma=mb; 283 } 284 allow[m]=0; 285 m=-1; 286 } 287 btAssert(0); 288 return m; 289 } 290 291 292 293 294 int operator ==(const int3 &a,const int3 &b); 295 int operator ==(const int3 &a,const int3 &b) 296 { 297 for(int i=0;i<3;i++) 298 { 299 if(a[i]!=b[i]) return 0; 300 } 301 return 1; 302 } 303 304 305 int above(btVector3* vertices,const int3& t, const btVector3 &p, btScalar epsilon); 306 int above(btVector3* vertices,const int3& t, const btVector3 &p, btScalar epsilon) 307 { 308 btVector3 n=TriNormal(vertices[t[0]],vertices[t[1]],vertices[t[2]]); 309 return (btDot(n,p-vertices[t[0]]) > epsilon); // EPSILON??? 310 } 311 int hasedge(const int3 &t, int a,int b); 312 int hasedge(const int3 &t, int a,int b) 313 { 314 for(int i=0;i<3;i++) 315 { 316 int i1= (i+1)%3; 317 if(t[i]==a && t[i1]==b) return 1; 318 } 319 return 0; 320 } 321 int hasvert(const int3 &t, int v); 322 int hasvert(const int3 &t, int v) 323 { 324 return (t[0]==v || t[1]==v || t[2]==v) ; 325 } 326 int shareedge(const int3 &a,const int3 &b); 327 int shareedge(const int3 &a,const int3 &b) 328 { 329 int i; 330 for(i=0;i<3;i++) 331 { 332 int i1= (i+1)%3; 333 if(hasedge(a,b[i1],b[i])) return 1; 334 } 335 return 0; 336 } 337 338 class btHullTriangle; 339 340 341 342 class btHullTriangle : public int3 343 { 344 public: 345 int3 n; 346 int id; 347 int vmax; 348 btScalar rise; 349 btHullTriangle(int a,int b,int c):int3(a,b,c),n(-1,-1,-1) 350 { 351 vmax=-1; 352 rise = btScalar(0.0); 353 } 354 ~btHullTriangle() 355 { 356 } 357 int &neib(int a,int b); 358 }; 359 360 361 int &btHullTriangle::neib(int a,int b) 362 { 363 static int er=-1; 364 int i; 365 for(i=0;i<3;i++) 366 { 367 int i1=(i+1)%3; 368 int i2=(i+2)%3; 369 if((*this)[i]==a && (*this)[i1]==b) return n[i2]; 370 if((*this)[i]==b && (*this)[i1]==a) return n[i2]; 371 } 372 btAssert(0); 373 return er; 374 } 375 void HullLibrary::b2bfix(btHullTriangle* s,btHullTriangle*t) 376 { 377 int i; 378 for(i=0;i<3;i++) 379 { 380 int i1=(i+1)%3; 381 int i2=(i+2)%3; 382 int a = (*s)[i1]; 383 int b = (*s)[i2]; 384 btAssert(m_tris[s->neib(a,b)]->neib(b,a) == s->id); 385 btAssert(m_tris[t->neib(a,b)]->neib(b,a) == t->id); 386 m_tris[s->neib(a,b)]->neib(b,a) = t->neib(b,a); 387 m_tris[t->neib(b,a)]->neib(a,b) = s->neib(a,b); 388 } 389 } 390 391 void HullLibrary::removeb2b(btHullTriangle* s,btHullTriangle*t) 392 { 393 b2bfix(s,t); 394 deAllocateTriangle(s); 395 396 deAllocateTriangle(t); 397 } 398 399 void HullLibrary::checkit(btHullTriangle *t) 400 { 401 (void)t; 402 403 int i; 404 btAssert(m_tris[t->id]==t); 405 for(i=0;i<3;i++) 406 { 407 int i1=(i+1)%3; 408 int i2=(i+2)%3; 409 int a = (*t)[i1]; 410 int b = (*t)[i2]; 411 412 // release compile fix 413 (void)i1; 414 (void)i2; 415 (void)a; 416 (void)b; 417 418 btAssert(a!=b); 419 btAssert( m_tris[t->n[i]]->neib(b,a) == t->id); 420 } 421 } 422 423 btHullTriangle* HullLibrary::allocateTriangle(int a,int b,int c) 424 { 425 void* mem = btAlignedAlloc(sizeof(btHullTriangle),16); 426 btHullTriangle* tr = new (mem)btHullTriangle(a,b,c); 427 tr->id = m_tris.size(); 428 m_tris.push_back(tr); 429 430 return tr; 431 } 432 433 void HullLibrary::deAllocateTriangle(btHullTriangle* tri) 434 { 435 btAssert(m_tris[tri->id]==tri); 436 m_tris[tri->id]=NULL; 437 tri->~btHullTriangle(); 438 btAlignedFree(tri); 439 } 440 441 442 void HullLibrary::extrude(btHullTriangle *t0,int v) 443 { 444 int3 t= *t0; 445 int n = m_tris.size(); 446 btHullTriangle* ta = allocateTriangle(v,t[1],t[2]); 447 ta->n = int3(t0->n[0],n+1,n+2); 448 m_tris[t0->n[0]]->neib(t[1],t[2]) = n+0; 449 btHullTriangle* tb = allocateTriangle(v,t[2],t[0]); 450 tb->n = int3(t0->n[1],n+2,n+0); 451 m_tris[t0->n[1]]->neib(t[2],t[0]) = n+1; 452 btHullTriangle* tc = allocateTriangle(v,t[0],t[1]); 453 tc->n = int3(t0->n[2],n+0,n+1); 454 m_tris[t0->n[2]]->neib(t[0],t[1]) = n+2; 455 checkit(ta); 456 checkit(tb); 457 checkit(tc); 458 if(hasvert(*m_tris[ta->n[0]],v)) removeb2b(ta,m_tris[ta->n[0]]); 459 if(hasvert(*m_tris[tb->n[0]],v)) removeb2b(tb,m_tris[tb->n[0]]); 460 if(hasvert(*m_tris[tc->n[0]],v)) removeb2b(tc,m_tris[tc->n[0]]); 461 deAllocateTriangle(t0); 462 463 } 464 465 btHullTriangle* HullLibrary::extrudable(btScalar epsilon) 466 { 467 int i; 468 btHullTriangle *t=NULL; 469 for(i=0;i<m_tris.size();i++) 470 { 471 if(!t || (m_tris[i] && t->rise<m_tris[i]->rise)) 472 { 473 t = m_tris[i]; 474 } 475 } 476 return (t->rise >epsilon)?t:NULL ; 477 } 478 479 480 481 482 int4 HullLibrary::FindSimplex(btVector3 *verts,int verts_count,btAlignedObjectArray<int> &allow) 483 { 484 btVector3 basis[3]; 485 basis[0] = btVector3( btScalar(0.01), btScalar(0.02), btScalar(1.0) ); 486 int p0 = maxdirsterid(verts,verts_count, basis[0],allow); 487 int p1 = maxdirsterid(verts,verts_count,-basis[0],allow); 488 basis[0] = verts[p0]-verts[p1]; 489 if(p0==p1 || basis[0]==btVector3(0,0,0)) 490 return int4(-1,-1,-1,-1); 491 basis[1] = btCross(btVector3( btScalar(1),btScalar(0.02), btScalar(0)),basis[0]); 492 basis[2] = btCross(btVector3(btScalar(-0.02), btScalar(1), btScalar(0)),basis[0]); 493 if (basis[1].length() > basis[2].length()) 494 { 495 basis[1].normalize(); 496 } else { 497 basis[1] = basis[2]; 498 basis[1].normalize (); 499 } 500 int p2 = maxdirsterid(verts,verts_count,basis[1],allow); 501 if(p2 == p0 || p2 == p1) 502 { 503 p2 = maxdirsterid(verts,verts_count,-basis[1],allow); 504 } 505 if(p2 == p0 || p2 == p1) 506 return int4(-1,-1,-1,-1); 507 basis[1] = verts[p2] - verts[p0]; 508 basis[2] = btCross(basis[1],basis[0]).normalized(); 509 int p3 = maxdirsterid(verts,verts_count,basis[2],allow); 510 if(p3==p0||p3==p1||p3==p2) p3 = maxdirsterid(verts,verts_count,-basis[2],allow); 511 if(p3==p0||p3==p1||p3==p2) 512 return int4(-1,-1,-1,-1); 513 btAssert(!(p0==p1||p0==p2||p0==p3||p1==p2||p1==p3||p2==p3)); 514 if(btDot(verts[p3]-verts[p0],btCross(verts[p1]-verts[p0],verts[p2]-verts[p0])) <0) {btSwap(p2,p3);} 515 return int4(p0,p1,p2,p3); 516 } 517 518 int HullLibrary::calchullgen(btVector3 *verts,int verts_count, int vlimit) 519 { 520 if(verts_count <4) return 0; 521 if(vlimit==0) vlimit=1000000000; 522 int j; 523 btVector3 bmin(*verts),bmax(*verts); 524 btAlignedObjectArray<int> isextreme; 525 isextreme.reserve(verts_count); 526 btAlignedObjectArray<int> allow; 527 allow.reserve(verts_count); 528 529 for(j=0;j<verts_count;j++) 530 { 531 allow.push_back(1); 532 isextreme.push_back(0); 533 bmin.setMin (verts[j]); 534 bmax.setMax (verts[j]); 535 } 536 btScalar epsilon = (bmax-bmin).length() * btScalar(0.001); 537 btAssert (epsilon != 0.0); 538 539 540 int4 p = FindSimplex(verts,verts_count,allow); 541 if(p.x==-1) return 0; // simplex failed 542 543 544 545 btVector3 center = (verts[p[0]]+verts[p[1]]+verts[p[2]]+verts[p[3]]) / btScalar(4.0); // a valid interior point 546 btHullTriangle *t0 = allocateTriangle(p[2],p[3],p[1]); t0->n=int3(2,3,1); 547 btHullTriangle *t1 = allocateTriangle(p[3],p[2],p[0]); t1->n=int3(3,2,0); 548 btHullTriangle *t2 = allocateTriangle(p[0],p[1],p[3]); t2->n=int3(0,1,3); 549 btHullTriangle *t3 = allocateTriangle(p[1],p[0],p[2]); t3->n=int3(1,0,2); 550 isextreme[p[0]]=isextreme[p[1]]=isextreme[p[2]]=isextreme[p[3]]=1; 551 checkit(t0);checkit(t1);checkit(t2);checkit(t3); 552 553 for(j=0;j<m_tris.size();j++) 554 { 555 btHullTriangle *t=m_tris[j]; 556 btAssert(t); 557 btAssert(t->vmax<0); 558 btVector3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]); 559 t->vmax = maxdirsterid(verts,verts_count,n,allow); 560 t->rise = btDot(n,verts[t->vmax]-verts[(*t)[0]]); 561 } 562 btHullTriangle *te; 563 vlimit-=4; 564 while(vlimit >0 && ((te=extrudable(epsilon)) != 0)) 565 { 566 //int3 ti=*te; 567 int v=te->vmax; 568 btAssert(v != -1); 569 btAssert(!isextreme[v]); // wtf we've already done this vertex 570 isextreme[v]=1; 571 //if(v==p0 || v==p1 || v==p2 || v==p3) continue; // done these already 572 j=m_tris.size(); 573 while(j--) { 574 if(!m_tris[j]) continue; 575 int3 t=*m_tris[j]; 576 if(above(verts,t,verts[v],btScalar(0.01)*epsilon)) 577 { 578 extrude(m_tris[j],v); 579 } 580 } 581 // now check for those degenerate cases where we have a flipped triangle or a really skinny triangle 582 j=m_tris.size(); 583 while(j--) 584 { 585 if(!m_tris[j]) continue; 586 if(!hasvert(*m_tris[j],v)) break; 587 int3 nt=*m_tris[j]; 588 if(above(verts,nt,center,btScalar(0.01)*epsilon) || btCross(verts[nt[1]]-verts[nt[0]],verts[nt[2]]-verts[nt[1]]).length()< epsilon*epsilon*btScalar(0.1) ) 589 { 590 btHullTriangle *nb = m_tris[m_tris[j]->n[0]]; 591 btAssert(nb);btAssert(!hasvert(*nb,v));btAssert(nb->id<j); 592 extrude(nb,v); 593 j=m_tris.size(); 594 } 595 } 596 j=m_tris.size(); 597 while(j--) 598 { 599 btHullTriangle *t=m_tris[j]; 600 if(!t) continue; 601 if(t->vmax>=0) break; 602 btVector3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]); 603 t->vmax = maxdirsterid(verts,verts_count,n,allow); 604 if(isextreme[t->vmax]) 605 { 606 t->vmax=-1; // already done that vertex - algorithm needs to be able to terminate. 607 } 608 else 609 { 610 t->rise = btDot(n,verts[t->vmax]-verts[(*t)[0]]); 611 } 612 } 613 vlimit --; 614 } 615 return 1; 616 } 617 618 int HullLibrary::calchull(btVector3 *verts,int verts_count, TUIntArray& tris_out, int &tris_count,int vlimit) 619 { 620 int rc=calchullgen(verts,verts_count, vlimit) ; 621 if(!rc) return 0; 622 btAlignedObjectArray<int> ts; 623 int i; 624 625 for(i=0;i<m_tris.size();i++) 626 { 627 if(m_tris[i]) 628 { 629 for(int j=0;j<3;j++) 630 ts.push_back((*m_tris[i])[j]); 631 deAllocateTriangle(m_tris[i]); 632 } 633 } 634 tris_count = ts.size()/3; 635 tris_out.resize(ts.size()); 636 637 for (i=0;i<ts.size();i++) 638 { 639 tris_out[i] = static_cast<unsigned int>(ts[i]); 640 } 641 m_tris.resize(0); 642 643 return 1; 644 } 645 646 647 648 649 650 bool HullLibrary::ComputeHull(unsigned int vcount,const btVector3 *vertices,PHullResult &result,unsigned int vlimit) 651 { 652 653 int tris_count; 654 int ret = calchull( (btVector3 *) vertices, (int) vcount, result.m_Indices, tris_count, static_cast<int>(vlimit) ); 655 if(!ret) return false; 656 result.mIndexCount = (unsigned int) (tris_count*3); 657 result.mFaceCount = (unsigned int) tris_count; 658 result.mVertices = (btVector3*) vertices; 659 result.mVcount = (unsigned int) vcount; 660 return true; 661 662 } 663 664 665 void ReleaseHull(PHullResult &result); 666 void ReleaseHull(PHullResult &result) 667 { 668 if ( result.m_Indices.size() ) 669 { 670 result.m_Indices.clear(); 671 } 672 673 result.mVcount = 0; 674 result.mIndexCount = 0; 675 result.mVertices = 0; 676 } 677 678 679 //********************************************************************* 680 //********************************************************************* 681 //******** HullLib header 682 //********************************************************************* 683 //********************************************************************* 684 685 //********************************************************************* 686 //********************************************************************* 687 //******** HullLib implementation 688 //********************************************************************* 689 //********************************************************************* 690 691 HullError HullLibrary::CreateConvexHull(const HullDesc &desc, // describes the input request 692 HullResult &result) // contains the resulst 693 { 694 HullError ret = QE_FAIL; 695 696 697 PHullResult hr; 698 699 unsigned int vcount = desc.mVcount; 700 if ( vcount < 8 ) vcount = 8; 701 702 btAlignedObjectArray<btVector3> vertexSource; 703 vertexSource.resize(static_cast<int>(vcount)); 704 705 btVector3 scale; 706 707 unsigned int ovcount; 708 709 bool ok = CleanupVertices(desc.mVcount,desc.mVertices, desc.mVertexStride, ovcount, &vertexSource[0], desc.mNormalEpsilon, scale ); // normalize point cloud, remove duplicates! 710 711 if ( ok ) 712 { 713 714 715 // if ( 1 ) // scale vertices back to their original size. 716 { 717 for (unsigned int i=0; i<ovcount; i++) 718 { 719 btVector3& v = vertexSource[static_cast<int>(i)]; 720 v[0]*=scale[0]; 721 v[1]*=scale[1]; 722 v[2]*=scale[2]; 723 } 724 } 725 726 ok = ComputeHull(ovcount,&vertexSource[0],hr,desc.mMaxVertices); 727 728 if ( ok ) 729 { 730 731 // re-index triangle mesh so it refers to only used vertices, rebuild a new vertex table. 732 btAlignedObjectArray<btVector3> vertexScratch; 733 vertexScratch.resize(static_cast<int>(hr.mVcount)); 734 735 BringOutYourDead(hr.mVertices,hr.mVcount, &vertexScratch[0], ovcount, &hr.m_Indices[0], hr.mIndexCount ); 736 737 ret = QE_OK; 738 739 if ( desc.HasHullFlag(QF_TRIANGLES) ) // if he wants the results as triangle! 740 { 741 result.mPolygons = false; 742 result.mNumOutputVertices = ovcount; 743 result.m_OutputVertices.resize(static_cast<int>(ovcount)); 744 result.mNumFaces = hr.mFaceCount; 745 result.mNumIndices = hr.mIndexCount; 746 747 result.m_Indices.resize(static_cast<int>(hr.mIndexCount)); 748 749 memcpy(&result.m_OutputVertices[0], &vertexScratch[0], sizeof(btVector3)*ovcount ); 750 751 if ( desc.HasHullFlag(QF_REVERSE_ORDER) ) 752 { 753 754 const unsigned int *source = &hr.m_Indices[0]; 755 unsigned int *dest = &result.m_Indices[0]; 756 757 for (unsigned int i=0; i<hr.mFaceCount; i++) 758 { 759 dest[0] = source[2]; 760 dest[1] = source[1]; 761 dest[2] = source[0]; 762 dest+=3; 763 source+=3; 764 } 765 766 } 767 else 768 { 769 memcpy(&result.m_Indices[0], &hr.m_Indices[0], sizeof(unsigned int)*hr.mIndexCount); 770 } 771 } 772 else 773 { 774 result.mPolygons = true; 775 result.mNumOutputVertices = ovcount; 776 result.m_OutputVertices.resize(static_cast<int>(ovcount)); 777 result.mNumFaces = hr.mFaceCount; 778 result.mNumIndices = hr.mIndexCount+hr.mFaceCount; 779 result.m_Indices.resize(static_cast<int>(result.mNumIndices)); 780 memcpy(&result.m_OutputVertices[0], &vertexScratch[0], sizeof(btVector3)*ovcount ); 781 782 // if ( 1 ) 783 { 784 const unsigned int *source = &hr.m_Indices[0]; 785 unsigned int *dest = &result.m_Indices[0]; 786 for (unsigned int i=0; i<hr.mFaceCount; i++) 787 { 788 dest[0] = 3; 789 if ( desc.HasHullFlag(QF_REVERSE_ORDER) ) 790 { 791 dest[1] = source[2]; 792 dest[2] = source[1]; 793 dest[3] = source[0]; 794 } 795 else 796 { 797 dest[1] = source[0]; 798 dest[2] = source[1]; 799 dest[3] = source[2]; 800 } 801 802 dest+=4; 803 source+=3; 804 } 805 } 806 } 807 ReleaseHull(hr); 808 } 809 } 810 811 return ret; 812 } 813 814 815 816 HullError HullLibrary::ReleaseResult(HullResult &result) // release memory allocated for this result, we are done with it. 817 { 818 if ( result.m_OutputVertices.size()) 819 { 820 result.mNumOutputVertices=0; 821 result.m_OutputVertices.clear(); 822 } 823 if ( result.m_Indices.size() ) 824 { 825 result.mNumIndices=0; 826 result.m_Indices.clear(); 827 } 828 return QE_OK; 829 } 830 831 832 static void addPoint(unsigned int &vcount,btVector3 *p,btScalar x,btScalar y,btScalar z) 833 { 834 // XXX, might be broken 835 btVector3& dest = p[vcount]; 836 dest[0] = x; 837 dest[1] = y; 838 dest[2] = z; 839 vcount++; 840 } 841 842 btScalar GetDist(btScalar px,btScalar py,btScalar pz,const btScalar *p2); 843 btScalar GetDist(btScalar px,btScalar py,btScalar pz,const btScalar *p2) 844 { 845 846 btScalar dx = px - p2[0]; 847 btScalar dy = py - p2[1]; 848 btScalar dz = pz - p2[2]; 849 850 return dx*dx+dy*dy+dz*dz; 851 } 852 853 854 855 bool HullLibrary::CleanupVertices(unsigned int svcount, 856 const btVector3 *svertices, 857 unsigned int stride, 858 unsigned int &vcount, // output number of vertices 859 btVector3 *vertices, // location to store the results. 860 btScalar normalepsilon, 861 btVector3& scale) 862 { 863 if ( svcount == 0 ) return false; 864 865 m_vertexIndexMapping.resize(0); 866 867 868 #define EPSILON btScalar(0.000001) /* close enough to consider two btScalaring point numbers to be 'the same'. */ 869 870 vcount = 0; 871 872 btScalar recip[3]={0.f,0.f,0.f}; 873 874 if ( scale ) 875 { 876 scale[0] = 1; 877 scale[1] = 1; 878 scale[2] = 1; 879 } 880 881 btScalar bmin[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; 882 btScalar bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX }; 883 884 const char *vtx = (const char *) svertices; 885 886 // if ( 1 ) 887 { 888 for (unsigned int i=0; i<svcount; i++) 889 { 890 const btScalar *p = (const btScalar *) vtx; 891 892 vtx+=stride; 893 894 for (int j=0; j<3; j++) 895 { 896 if ( p[j] < bmin[j] ) bmin[j] = p[j]; 897 if ( p[j] > bmax[j] ) bmax[j] = p[j]; 898 } 899 } 900 } 901 902 btScalar dx = bmax[0] - bmin[0]; 903 btScalar dy = bmax[1] - bmin[1]; 904 btScalar dz = bmax[2] - bmin[2]; 905 906 btVector3 center; 907 908 center[0] = dx*btScalar(0.5) + bmin[0]; 909 center[1] = dy*btScalar(0.5) + bmin[1]; 910 center[2] = dz*btScalar(0.5) + bmin[2]; 911 912 if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || svcount < 3 ) 913 { 914 915 btScalar len = FLT_MAX; 916 917 if ( dx > EPSILON && dx < len ) len = dx; 918 if ( dy > EPSILON && dy < len ) len = dy; 919 if ( dz > EPSILON && dz < len ) len = dz; 920 921 if ( len == FLT_MAX ) 922 { 923 dx = dy = dz = btScalar(0.01); // one centimeter 924 } 925 else 926 { 927 if ( dx < EPSILON ) dx = len * btScalar(0.05); // 1/5th the shortest non-zero edge. 928 if ( dy < EPSILON ) dy = len * btScalar(0.05); 929 if ( dz < EPSILON ) dz = len * btScalar(0.05); 930 } 931 932 btScalar x1 = center[0] - dx; 933 btScalar x2 = center[0] + dx; 934 935 btScalar y1 = center[1] - dy; 936 btScalar y2 = center[1] + dy; 937 938 btScalar z1 = center[2] - dz; 939 btScalar z2 = center[2] + dz; 940 941 addPoint(vcount,vertices,x1,y1,z1); 942 addPoint(vcount,vertices,x2,y1,z1); 943 addPoint(vcount,vertices,x2,y2,z1); 944 addPoint(vcount,vertices,x1,y2,z1); 945 addPoint(vcount,vertices,x1,y1,z2); 946 addPoint(vcount,vertices,x2,y1,z2); 947 addPoint(vcount,vertices,x2,y2,z2); 948 addPoint(vcount,vertices,x1,y2,z2); 949 950 return true; // return cube 951 952 953 } 954 else 955 { 956 if ( scale ) 957 { 958 scale[0] = dx; 959 scale[1] = dy; 960 scale[2] = dz; 961 962 recip[0] = 1 / dx; 963 recip[1] = 1 / dy; 964 recip[2] = 1 / dz; 965 966 center[0]*=recip[0]; 967 center[1]*=recip[1]; 968 center[2]*=recip[2]; 969 970 } 971 972 } 973 974 975 976 vtx = (const char *) svertices; 977 978 for (unsigned int i=0; i<svcount; i++) 979 { 980 const btVector3 *p = (const btVector3 *)vtx; 981 vtx+=stride; 982 983 btScalar px = p->getX(); 984 btScalar py = p->getY(); 985 btScalar pz = p->getZ(); 986 987 if ( scale ) 988 { 989 px = px*recip[0]; // normalize 990 py = py*recip[1]; // normalize 991 pz = pz*recip[2]; // normalize 992 } 993 994 // if ( 1 ) 995 { 996 unsigned int j; 997 998 for (j=0; j<vcount; j++) 999 { 1000 /// XXX might be broken 1001 btVector3& v = vertices[j]; 1002 1003 btScalar x = v[0]; 1004 btScalar y = v[1]; 1005 btScalar z = v[2]; 1006 1007 btScalar dx = btFabs(x - px ); 1008 btScalar dy = btFabs(y - py ); 1009 btScalar dz = btFabs(z - pz ); 1010 1011 if ( dx < normalepsilon && dy < normalepsilon && dz < normalepsilon ) 1012 { 1013 // ok, it is close enough to the old one 1014 // now let us see if it is further from the center of the point cloud than the one we already recorded. 1015 // in which case we keep this one instead. 1016 1017 btScalar dist1 = GetDist(px,py,pz,center); 1018 btScalar dist2 = GetDist(v[0],v[1],v[2],center); 1019 1020 if ( dist1 > dist2 ) 1021 { 1022 v[0] = px; 1023 v[1] = py; 1024 v[2] = pz; 1025 1026 } 1027 1028 break; 1029 } 1030 } 1031 1032 if ( j == vcount ) 1033 { 1034 btVector3& dest = vertices[vcount]; 1035 dest[0] = px; 1036 dest[1] = py; 1037 dest[2] = pz; 1038 vcount++; 1039 } 1040 m_vertexIndexMapping.push_back(j); 1041 } 1042 } 1043 1044 // ok..now make sure we didn't prune so many vertices it is now invalid. 1045 // if ( 1 ) 1046 { 1047 btScalar bmin[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; 1048 btScalar bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX }; 1049 1050 for (unsigned int i=0; i<vcount; i++) 1051 { 1052 const btVector3& p = vertices[i]; 1053 for (int j=0; j<3; j++) 1054 { 1055 if ( p[j] < bmin[j] ) bmin[j] = p[j]; 1056 if ( p[j] > bmax[j] ) bmax[j] = p[j]; 1057 } 1058 } 1059 1060 btScalar dx = bmax[0] - bmin[0]; 1061 btScalar dy = bmax[1] - bmin[1]; 1062 btScalar dz = bmax[2] - bmin[2]; 1063 1064 if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || vcount < 3) 1065 { 1066 btScalar cx = dx*btScalar(0.5) + bmin[0]; 1067 btScalar cy = dy*btScalar(0.5) + bmin[1]; 1068 btScalar cz = dz*btScalar(0.5) + bmin[2]; 1069 1070 btScalar len = FLT_MAX; 1071 1072 if ( dx >= EPSILON && dx < len ) len = dx; 1073 if ( dy >= EPSILON && dy < len ) len = dy; 1074 if ( dz >= EPSILON && dz < len ) len = dz; 1075 1076 if ( len == FLT_MAX ) 1077 { 1078 dx = dy = dz = btScalar(0.01); // one centimeter 1079 } 1080 else 1081 { 1082 if ( dx < EPSILON ) dx = len * btScalar(0.05); // 1/5th the shortest non-zero edge. 1083 if ( dy < EPSILON ) dy = len * btScalar(0.05); 1084 if ( dz < EPSILON ) dz = len * btScalar(0.05); 1085 } 1086 1087 btScalar x1 = cx - dx; 1088 btScalar x2 = cx + dx; 1089 1090 btScalar y1 = cy - dy; 1091 btScalar y2 = cy + dy; 1092 1093 btScalar z1 = cz - dz; 1094 btScalar z2 = cz + dz; 1095 1096 vcount = 0; // add box 1097 1098 addPoint(vcount,vertices,x1,y1,z1); 1099 addPoint(vcount,vertices,x2,y1,z1); 1100 addPoint(vcount,vertices,x2,y2,z1); 1101 addPoint(vcount,vertices,x1,y2,z1); 1102 addPoint(vcount,vertices,x1,y1,z2); 1103 addPoint(vcount,vertices,x2,y1,z2); 1104 addPoint(vcount,vertices,x2,y2,z2); 1105 addPoint(vcount,vertices,x1,y2,z2); 1106 1107 return true; 1108 } 1109 } 1110 1111 return true; 1112 } 1113 1114 void HullLibrary::BringOutYourDead(const btVector3* verts,unsigned int vcount, btVector3* overts,unsigned int &ocount,unsigned int *indices,unsigned indexcount) 1115 { 1116 btAlignedObjectArray<int>tmpIndices; 1117 tmpIndices.resize(m_vertexIndexMapping.size()); 1118 int i; 1119 1120 for (i=0;i<m_vertexIndexMapping.size();i++) 1121 { 1122 tmpIndices[i] = m_vertexIndexMapping[i]; 1123 } 1124 1125 TUIntArray usedIndices; 1126 usedIndices.resize(static_cast<int>(vcount)); 1127 memset(&usedIndices[0],0,sizeof(unsigned int)*vcount); 1128 1129 ocount = 0; 1130 1131 for (i=0; i<int (indexcount); i++) 1132 { 1133 unsigned int v = indices[i]; // original array index 1134 1135 btAssert( v >= 0 && v < vcount ); 1136 1137 if ( usedIndices[static_cast<int>(v)] ) // if already remapped 1138 { 1139 indices[i] = usedIndices[static_cast<int>(v)]-1; // index to new array 1140 } 1141 else 1142 { 1143 1144 indices[i] = ocount; // new index mapping 1145 1146 overts[ocount][0] = verts[v][0]; // copy old vert to new vert array 1147 overts[ocount][1] = verts[v][1]; 1148 overts[ocount][2] = verts[v][2]; 1149 1150 for (int k=0;k<m_vertexIndexMapping.size();k++) 1151 { 1152 if (tmpIndices[k]==int(v)) 1153 m_vertexIndexMapping[k]=ocount; 1154 } 1155 1156 ocount++; // increment output vert count 1157 1158 btAssert( ocount >=0 && ocount <= vcount ); 1159 1160 usedIndices[static_cast<int>(v)] = ocount; // assign new index remapping 1161 1162 1163 } 1164 } 1165 1166 1167 } 1168