1 /******************************************************************************
3 * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
4 * Copyright 2003-2006 Nils Thuerey
6 * Marching Cubes surface mesh generation
8 *****************************************************************************/
10 #include "isosurface.h"
11 #include "mcubes_tables.h"
12 #include "particletracer.h"
20 // just use default rounding for platforms where its not available
25 /******************************************************************************
27 *****************************************************************************/
28 IsoSurface::IsoSurface(double iso) :
30 mSizex(-1), mSizey(-1), mSizez(-1),
34 mUseFullEdgeArrays(false),
35 mpEdgeVerticesX(NULL), mpEdgeVerticesY(NULL), mpEdgeVerticesZ(NULL),
39 mStart(0.0), mEnd(0.0), mDomainExtent(0.0),
41 mSmoothSurface(0.0), mSmoothNormals(0.0),
42 mAcrossEdge(), mAdjacentFaces(),
43 mCutoff(-1), mCutArray(NULL), // off by default
44 mpIsoParts(NULL), mPartSize(0.), mSubdivs(0),
46 mSCrad1(0.), mSCrad2(0.), mSCcenter(0.)
51 /******************************************************************************
53 *****************************************************************************/
54 void IsoSurface::initializeIsosurface(int setx, int sety, int setz, ntlVec3Gfx extent)
56 // range 1-10 (max due to subd array in triangulate)
57 if(mSubdivs<1) mSubdivs=1;
58 if(mSubdivs>10) mSubdivs=10;
60 // init solver and size
63 if(setz == 1) {// 2D, create thin 2D surface
67 mDomainExtent = extent;
69 /* check triangulation size (for raytraing) */
70 if( ( mStart[0] >= mEnd[0] ) && ( mStart[1] >= mEnd[1] ) && ( mStart[2] >= mEnd[2] ) ){
71 // extent was not set, use normalized one from parametrizer
72 mStart = ntlVec3Gfx(0.0) - extent*0.5;
73 mEnd = ntlVec3Gfx(0.0) + extent*0.5;
80 int nodes = mSizez*mSizey*mSizex;
81 mpData = new float[nodes];
82 for(int i=0;i<nodes;i++) { mpData[i] = 0.0; }
84 // allocate edge arrays (last slices are never used...)
86 if(mUseFullEdgeArrays) {
88 mpEdgeVerticesX = new int[nodes];
89 mpEdgeVerticesY = new int[nodes];
90 mpEdgeVerticesZ = new int[nodes];
93 int sliceNodes = 2*mSizex*mSizey*mSubdivs*mSubdivs;
94 mEdgeArSize = sliceNodes;
95 mpEdgeVerticesX = new int[sliceNodes];
96 mpEdgeVerticesY = new int[sliceNodes];
97 mpEdgeVerticesZ = new int[sliceNodes];
98 initsize = 3*sliceNodes;
100 for(int i=0;i<mEdgeArSize;i++) { mpEdgeVerticesX[i] = mpEdgeVerticesY[i] = mpEdgeVerticesZ[i] = -1; }
101 // WARNING - make sure this is consistent with calculateMemreqEstimate
103 // marching cubes are ready
105 debMsgStd("IsoSurface::initializeIsosurface",DM_MSG,"Inited, edgenodes:"<<initsize<<" subdivs:"<<mSubdivs<<" fulledg:"<<mUseFullEdgeArrays , 10);
110 /*! Reset all values */
111 void IsoSurface::resetAll(gfxReal val) {
112 int nodes = mSizez*mSizey*mSizex;
113 for(int i=0;i<nodes;i++) { mpData[i] = val; }
117 /******************************************************************************
119 *****************************************************************************/
120 IsoSurface::~IsoSurface( void )
122 if(mpData) delete [] mpData;
123 if(mpEdgeVerticesX) delete [] mpEdgeVerticesX;
124 if(mpEdgeVerticesY) delete [] mpEdgeVerticesY;
125 if(mpEdgeVerticesZ) delete [] mpEdgeVerticesZ;
132 /******************************************************************************
133 * triangulate the scalar field given by pointer
134 *****************************************************************************/
135 void IsoSurface::triangulate( void )
137 double gsx,gsy,gsz; // grid spacing in x,y,z direction
138 double px,py,pz; // current position in grid in x,y,z direction
139 IsoLevelCube cubie; // struct for a small subcube
140 myTime_t tritimestart = getTime();
143 errFatal("IsoSurface::triangulate","no LBM object, and no scalar field...!",SIMWORLD_INITERROR);
147 // get grid spacing (-2 to have same spacing as sim)
148 gsx = (mEnd[0]-mStart[0])/(double)(mSizex-2.0);
149 gsy = (mEnd[1]-mStart[1])/(double)(mSizey-2.0);
150 gsz = (mEnd[2]-mStart[2])/(double)(mSizez-2.0);
152 // clean up previous frame
156 // reset edge vertices
157 for(int i=0;i<mEdgeArSize;i++) {
158 mpEdgeVerticesX[i] = -1;
159 mpEdgeVerticesY[i] = -1;
160 mpEdgeVerticesZ[i] = -1;
165 int cubeIndex; // index entry of the cube
166 int triIndices[12]; // vertex indices
170 // edges between which points?
171 const int mcEdges[24] = {
174 0,4, 1,5, 2,6, 3,7 };
176 const int cubieOffsetX[8] = {
178 const int cubieOffsetY[8] = {
180 const int cubieOffsetZ[8] = {
184 // let the cubes march
187 pz = mStart[2]-gsz*0.5;
188 for(int k=1;k<(mSizez-2);k++) {
190 py = mStart[1]-gsy*0.5;
191 for(int j=1;j<(mSizey-2);j++) {
193 px = mStart[0]-gsx*0.5;
194 for(int i=1;i<(mSizex-2);i++) {
197 value[0] = *getData(i ,j ,k );
198 value[1] = *getData(i+1,j ,k );
199 value[2] = *getData(i+1,j+1,k );
200 value[3] = *getData(i ,j+1,k );
201 value[4] = *getData(i ,j ,k+1);
202 value[5] = *getData(i+1,j ,k+1);
203 value[6] = *getData(i+1,j+1,k+1);
204 value[7] = *getData(i ,j+1,k+1);
206 // check intersections of isosurface with edges, and calculate cubie index
208 if (value[0] < mIsoValue) cubeIndex |= 1;
209 if (value[1] < mIsoValue) cubeIndex |= 2;
210 if (value[2] < mIsoValue) cubeIndex |= 4;
211 if (value[3] < mIsoValue) cubeIndex |= 8;
212 if (value[4] < mIsoValue) cubeIndex |= 16;
213 if (value[5] < mIsoValue) cubeIndex |= 32;
214 if (value[6] < mIsoValue) cubeIndex |= 64;
215 if (value[7] < mIsoValue) cubeIndex |= 128;
217 // No triangles to generate?
218 if (mcEdgeTable[cubeIndex] == 0) {
222 // where to look up if this point already exists
224 if(mUseFullEdgeArrays) edgek=k;
225 const int baseIn = ISOLEVEL_INDEX( i+0, j+0, edgek+0);
226 eVert[ 0] = &mpEdgeVerticesX[ baseIn ];
227 eVert[ 1] = &mpEdgeVerticesY[ baseIn + 1 ];
228 eVert[ 2] = &mpEdgeVerticesX[ ISOLEVEL_INDEX( i+0, j+1, edgek+0) ];
229 eVert[ 3] = &mpEdgeVerticesY[ baseIn ];
231 eVert[ 4] = &mpEdgeVerticesX[ ISOLEVEL_INDEX( i+0, j+0, edgek+1) ];
232 eVert[ 5] = &mpEdgeVerticesY[ ISOLEVEL_INDEX( i+1, j+0, edgek+1) ];
233 eVert[ 6] = &mpEdgeVerticesX[ ISOLEVEL_INDEX( i+0, j+1, edgek+1) ];
234 eVert[ 7] = &mpEdgeVerticesY[ ISOLEVEL_INDEX( i+0, j+0, edgek+1) ];
236 eVert[ 8] = &mpEdgeVerticesZ[ baseIn ];
237 eVert[ 9] = &mpEdgeVerticesZ[ ISOLEVEL_INDEX( i+1, j+0, edgek+0) ];
238 eVert[10] = &mpEdgeVerticesZ[ ISOLEVEL_INDEX( i+1, j+1, edgek+0) ];
239 eVert[11] = &mpEdgeVerticesZ[ ISOLEVEL_INDEX( i+0, j+1, edgek+0) ];
242 pos[0] = ntlVec3Gfx(px ,py ,pz);
243 pos[1] = ntlVec3Gfx(px+gsx,py ,pz);
244 pos[2] = ntlVec3Gfx(px+gsx,py+gsy,pz);
245 pos[3] = ntlVec3Gfx(px ,py+gsy,pz);
246 pos[4] = ntlVec3Gfx(px ,py ,pz+gsz);
247 pos[5] = ntlVec3Gfx(px+gsx,py ,pz+gsz);
248 pos[6] = ntlVec3Gfx(px+gsx,py+gsy,pz+gsz);
249 pos[7] = ntlVec3Gfx(px ,py+gsy,pz+gsz);
252 for(int e=0;e<12;e++) {
253 if (mcEdgeTable[cubeIndex] & (1<<e)) {
254 // is the vertex already calculated?
255 if(*eVert[ e ] < 0) {
257 const int e1 = mcEdges[e*2 ];
258 const int e2 = mcEdges[e*2+1];
259 const ntlVec3Gfx p1 = pos[ e1 ]; // scalar field pos 1
260 const ntlVec3Gfx p2 = pos[ e2 ]; // scalar field pos 2
261 const float valp1 = value[ e1 ]; // scalar field val 1
262 const float valp2 = value[ e2 ]; // scalar field val 2
263 const float mu = (mIsoValue - valp1) / (valp2 - valp1);
265 // init isolevel vertex
266 ilv.v = p1 + (p2-p1)*mu;
267 ilv.n = getNormal( i+cubieOffsetX[e1], j+cubieOffsetY[e1], k+cubieOffsetZ[e1]) * (1.0-mu) +
268 getNormal( i+cubieOffsetX[e2], j+cubieOffsetY[e2], k+cubieOffsetZ[e2]) * ( mu) ;
269 mPoints.push_back( ilv );
271 triIndices[e] = (mPoints.size()-1);
273 *eVert[ e ] = triIndices[e];
275 // retrieve from vert array
276 triIndices[e] = *eVert[ e ];
281 if( (i<coAdd+mCutoff) || (j<coAdd+mCutoff) ||
282 ((mCutoff>0) && (k<coAdd)) ||// bottom layer
283 (i>mSizex-2-coAdd-mCutoff) ||
284 (j>mSizey-2-coAdd-mCutoff) ) {
286 if(k < mCutArray[j*this->mSizex+i]) continue;
290 // Create the triangles...
291 for(int e=0; mcTriTable[cubeIndex][e]!=-1; e+=3) {
292 mIndices.push_back( triIndices[ mcTriTable[cubeIndex][e+0] ] );
293 mIndices.push_back( triIndices[ mcTriTable[cubeIndex][e+1] ] );
294 mIndices.push_back( triIndices[ mcTriTable[cubeIndex][e+2] ] );
301 if(!mUseFullEdgeArrays) {
302 for(int j=0;j<(mSizey-0);j++)
303 for(int i=0;i<(mSizex-0);i++) {
305 const int dst = ISOLEVEL_INDEX( i+0, j+0, 0);
306 const int src = ISOLEVEL_INDEX( i+0, j+0, 1);
307 mpEdgeVerticesX[ dst ] = mpEdgeVerticesX[ src ];
308 mpEdgeVerticesY[ dst ] = mpEdgeVerticesY[ src ];
309 mpEdgeVerticesZ[ dst ] = mpEdgeVerticesZ[ src ];
310 mpEdgeVerticesX[ src ]=-1;
311 mpEdgeVerticesY[ src ]=-1;
312 mpEdgeVerticesZ[ src ]=-1;
318 // precalculate normals using an approximation of the scalar field gradient
319 for(int ni=0;ni<(int)mPoints.size();ni++) { normalize( mPoints[ni].n ); }
323 #define EDGEAR_INDEX(Ai,Aj,Ak, Bi,Bj) ((mSizex*mSizey*mSubdivs*mSubdivs*(Ak))+\
324 (mSizex*mSubdivs*((Aj)*mSubdivs+(Bj)))+((Ai)*mSubdivs)+(Bi))
326 #define ISOTRILININT(fi,fj,fk) ( \
327 (1.-(fi))*(1.-(fj))*(1.-(fk))*orgval[0] + \
328 ( (fi))*(1.-(fj))*(1.-(fk))*orgval[1] + \
329 ( (fi))*( (fj))*(1.-(fk))*orgval[2] + \
330 (1.-(fi))*( (fj))*(1.-(fk))*orgval[3] + \
331 (1.-(fi))*(1.-(fj))*( (fk))*orgval[4] + \
332 ( (fi))*(1.-(fj))*( (fk))*orgval[5] + \
333 ( (fi))*( (fj))*( (fk))*orgval[6] + \
334 (1.-(fi))*( (fj))*( (fk))*orgval[7] )
337 gfxReal subdfac = 1./(gfxReal)(mSubdivs);
338 gfxReal orgGsx = gsx;
339 gfxReal orgGsy = gsy;
340 gfxReal orgGsz = gsz;
344 if(mUseFullEdgeArrays) {
345 errMsg("IsoSurface::triangulate","Disabling mUseFullEdgeArrays!");
348 // subdiv local arrays
350 gfxReal subdAr[2][11][11]; // max 10 subdivs!
351 ParticleObject* *arppnt = new ParticleObject*[mSizez*mSizey*mSizex];
353 // construct pointers
359 for(int k=0;k<(mSizez);k++)
360 for(int j=0;j<(mSizey);j++)
361 for(int i=0;i<(mSizex);i++) {
362 arppnt[ISOLEVEL_INDEX(i,j,k)] = NULL;
365 for(vector<ParticleObject>::iterator pit= mpIsoParts->getParticlesBegin();
366 pit!= mpIsoParts->getParticlesEnd(); pit++) {
367 if( (*pit).getActive()==false ) continue;
368 if( (*pit).getType()!=PART_DROP) continue;
369 (*pit).setNext(NULL);
371 // build per node lists
372 for(vector<ParticleObject>::iterator pit= mpIsoParts->getParticlesBegin();
373 pit!= mpIsoParts->getParticlesEnd(); pit++) {
374 if( (*pit).getActive()==false ) continue;
375 if( (*pit).getType()!=PART_DROP) continue;
376 // check lifetime ignored here
377 ParticleObject *p = &(*pit);
378 const ntlVec3Gfx ppos = p->getPos();
379 const int pi= (int)round(ppos[0])+0;
380 const int pj= (int)round(ppos[1])+0;
381 int pk= (int)round(ppos[2])+0;// no offset necessary
382 // 2d should be handled by solver. if(LBMDIM==2) { pk = 0; }
387 if(pi>mSizex-1) continue;
388 if(pj>mSizey-1) continue;
389 if(pk>mSizez-1) continue;
390 ParticleObject* &pnt = arppnt[ISOLEVEL_INDEX(pi,pj,pk)];
393 ParticleObject* listpnt = pnt;
395 if(!listpnt->getNext()) {
396 listpnt->setNext(p); listpnt = NULL;
398 listpnt = listpnt->getNext();
409 debMsgStd("IsoSurface::triangulate",DM_MSG,"Starting. Parts in use:"<<pInUse<<", Subdivs:"<<mSubdivs, 9);
410 pz = mStart[2]-(double)(0.*gsz)-0.5*orgGsz;
411 for(int ok=1;ok<(mSizez-2)*mSubdivs;ok++) {
413 const int k = ok/mSubdivs;
414 if(k<=0) continue; // skip zero plane
415 for(int j=1;j<(mSizey-2);j++) {
416 for(int i=1;i<(mSizex-2);i++) {
418 orgval[0] = *getData(i ,j ,k );
419 orgval[1] = *getData(i+1,j ,k );
420 orgval[2] = *getData(i+1,j+1,k ); // with subdivs
421 orgval[3] = *getData(i ,j+1,k );
422 orgval[4] = *getData(i ,j ,k+1);
423 orgval[5] = *getData(i+1,j ,k+1);
424 orgval[6] = *getData(i+1,j+1,k+1); // with subdivs
425 orgval[7] = *getData(i ,j+1,k+1);
427 // prebuild subsampled array slice
428 const int sdkOffset = ok-k*mSubdivs;
429 for(int sdk=0; sdk<2; sdk++)
430 for(int sdj=0; sdj<mSubdivs+1; sdj++)
431 for(int sdi=0; sdi<mSubdivs+1; sdi++) {
432 subdAr[sdk][sdj][sdi] = ISOTRILININT(sdi*subdfac, sdj*subdfac, (sdkOffset+sdk)*subdfac);
435 const int poDistOffset=2;
436 for(int pok=-poDistOffset; pok<1+poDistOffset; pok++) {
437 if(k+pok<0) continue;
438 if(k+pok>=mSizez-1) continue;
439 for(int poj=-poDistOffset; poj<1+poDistOffset; poj++) {
440 if(j+poj<0) continue;
441 if(j+poj>=mSizey-1) continue;
442 for(int poi=-poDistOffset; poi<1+poDistOffset; poi++) {
443 if(i+poi<0) continue;
444 if(i+poi>=mSizex-1) continue;
446 p = arppnt[ISOLEVEL_INDEX(i+poi,j+poj,k+pok)];
449 for(vector<ParticleObject>::iterator pit= mpIsoParts->getParticlesBegin();
450 pit!= mpIsoParts->getParticlesEnd(); pit++) { { { {
451 // debug test! , full list slow!
452 if(( (*pit).getActive()==false ) || ( (*pit).getType()!=PART_DROP)) continue;
457 ntlVec3Gfx ppos = p->getPos();
458 const int spi= (int)round( (ppos[0]+1.-(gfxReal)i) *(gfxReal)mSubdivs-1.5);
459 const int spj= (int)round( (ppos[1]+1.-(gfxReal)j) *(gfxReal)mSubdivs-1.5);
460 const int spk= (int)round( (ppos[2]+1.-(gfxReal)k) *(gfxReal)mSubdivs-1.5)-sdkOffset; // why -2?
461 // 2d should be handled by solver. if(LBMDIM==2) { spk = 0; }
463 gfxReal pfLen = p->getSize()*1.5*mPartSize; // test, was 1.1
464 const gfxReal minPfLen = subdfac*0.8;
465 if(pfLen<minPfLen) pfLen = minPfLen;
466 //errMsg("ISOPPP"," at "<<PRINT_IJK<<" pp"<<ppos<<" sp"<<PRINT_VEC(spi,spj,spk)<<" pflen"<<pfLen );
467 //errMsg("ISOPPP"," subdfac="<<subdfac<<" size"<<p->getSize()<<" ps"<<mPartSize );
468 const int icellpsize = (int)(1.*pfLen*(gfxReal)mSubdivs)+1;
469 for(int swk=-icellpsize; swk<=icellpsize; swk++) {
470 if(spk+swk< 0) { continue; }
471 if(spk+swk> 1) { continue; } // */
472 for(int swj=-icellpsize; swj<=icellpsize; swj++) {
473 if(spj+swj< 0) { continue; }
474 if(spj+swj>mSubdivs+0) { continue; } // */
475 for(int swi=-icellpsize; swi<=icellpsize; swi++) {
476 if(spi+swi< 0) { continue; }
477 if(spi+swi>mSubdivs+0) { continue; } // */
478 ntlVec3Gfx cellp = ntlVec3Gfx(
479 (1.5+(gfxReal)(spi+swi)) *subdfac + (gfxReal)(i-1),
480 (1.5+(gfxReal)(spj+swj)) *subdfac + (gfxReal)(j-1),
481 (1.5+(gfxReal)(spk+swk)+sdkOffset) *subdfac + (gfxReal)(k-1)
483 //if(swi==0 && swj==0 && swk==0) subdAr[spk][spj][spi] = 1.; // DEBUG
484 // clip domain boundaries again
485 if(cellp[0]<1.) { continue; }
486 if(cellp[1]<1.) { continue; }
487 if(cellp[2]<1.) { continue; }
488 if(cellp[0]>(gfxReal)mSizex-3.) { continue; }
489 if(cellp[1]>(gfxReal)mSizey-3.) { continue; }
490 if(cellp[2]>(gfxReal)mSizez-3.) { continue; }
491 gfxReal len = norm(cellp-ppos);
493 const gfxReal baseIsoVal = mIsoValue*1.1;
495 isoadd = baseIsoVal*1.;
497 // falloff linear with pfLen (kernel size=2pfLen
498 isoadd = baseIsoVal*(1. - (len-pfLen)/(pfLen));
500 if(isoadd<0.) { continue; }
501 //errMsg("ISOPPP"," at "<<PRINT_IJK<<" sp"<<PRINT_VEC(spi+swi,spj+swj,spk+swk)<<" cellp"<<cellp<<" pp"<<ppos << " l"<< len<< " add"<< isoadd);
502 const gfxReal arval = subdAr[spk+swk][spj+swj][spi+swi];
503 if(arval>1.) { continue; }
504 subdAr[spk+swk][spj+swj][spi+swi] = arval + isoadd;
509 } } } // poDist loops */
511 py = mStart[1]+(((double)j-0.5)*orgGsy)-gsy;
512 for(int sj=0;sj<mSubdivs;sj++) {
514 px = mStart[0]+(((double)i-0.5)*orgGsx)-gsx;
515 for(int si=0;si<mSubdivs;si++) {
517 value[0] = subdAr[0+0][sj+0][si+0];
518 value[1] = subdAr[0+0][sj+0][si+1];
519 value[2] = subdAr[0+0][sj+1][si+1];
520 value[3] = subdAr[0+0][sj+1][si+0];
521 value[4] = subdAr[0+1][sj+0][si+0];
522 value[5] = subdAr[0+1][sj+0][si+1];
523 value[6] = subdAr[0+1][sj+1][si+1];
524 value[7] = subdAr[0+1][sj+1][si+0];
526 // check intersections of isosurface with edges, and calculate cubie index
528 if (value[0] < mIsoValue) cubeIndex |= 1;
529 if (value[1] < mIsoValue) cubeIndex |= 2; // with subdivs
530 if (value[2] < mIsoValue) cubeIndex |= 4;
531 if (value[3] < mIsoValue) cubeIndex |= 8;
532 if (value[4] < mIsoValue) cubeIndex |= 16;
533 if (value[5] < mIsoValue) cubeIndex |= 32; // with subdivs
534 if (value[6] < mIsoValue) cubeIndex |= 64;
535 if (value[7] < mIsoValue) cubeIndex |= 128;
537 if (mcEdgeTable[cubeIndex] > 0) {
539 // where to look up if this point already exists
541 const int baseIn = EDGEAR_INDEX( i+0, j+0, edgek+0, si,sj);
542 eVert[ 0] = &mpEdgeVerticesX[ baseIn ];
543 eVert[ 1] = &mpEdgeVerticesY[ baseIn + 1 ];
544 eVert[ 2] = &mpEdgeVerticesX[ EDGEAR_INDEX( i, j, edgek+0, si+0,sj+1) ];
545 eVert[ 3] = &mpEdgeVerticesY[ baseIn ];
547 eVert[ 4] = &mpEdgeVerticesX[ EDGEAR_INDEX( i, j, edgek+1, si+0,sj+0) ];
548 eVert[ 5] = &mpEdgeVerticesY[ EDGEAR_INDEX( i, j, edgek+1, si+1,sj+0) ]; // with subdivs
549 eVert[ 6] = &mpEdgeVerticesX[ EDGEAR_INDEX( i, j, edgek+1, si+0,sj+1) ];
550 eVert[ 7] = &mpEdgeVerticesY[ EDGEAR_INDEX( i, j, edgek+1, si+0,sj+0) ];
552 eVert[ 8] = &mpEdgeVerticesZ[ baseIn ];
553 eVert[ 9] = &mpEdgeVerticesZ[ EDGEAR_INDEX( i, j, edgek+0, si+1,sj+0) ]; // with subdivs
554 eVert[10] = &mpEdgeVerticesZ[ EDGEAR_INDEX( i, j, edgek+0, si+1,sj+1) ];
555 eVert[11] = &mpEdgeVerticesZ[ EDGEAR_INDEX( i, j, edgek+0, si+0,sj+1) ];
558 pos[0] = ntlVec3Gfx(px ,py ,pz);
559 pos[1] = ntlVec3Gfx(px+gsx,py ,pz);
560 pos[2] = ntlVec3Gfx(px+gsx,py+gsy,pz); // with subdivs
561 pos[3] = ntlVec3Gfx(px ,py+gsy,pz);
562 pos[4] = ntlVec3Gfx(px ,py ,pz+gsz);
563 pos[5] = ntlVec3Gfx(px+gsx,py ,pz+gsz);
564 pos[6] = ntlVec3Gfx(px+gsx,py+gsy,pz+gsz); // with subdivs
565 pos[7] = ntlVec3Gfx(px ,py+gsy,pz+gsz);
568 for(int e=0;e<12;e++) {
569 if (mcEdgeTable[cubeIndex] & (1<<e)) {
570 // is the vertex already calculated?
571 if(*eVert[ e ] < 0) {
573 const int e1 = mcEdges[e*2 ];
574 const int e2 = mcEdges[e*2+1];
575 const ntlVec3Gfx p1 = pos[ e1 ]; // scalar field pos 1
576 const ntlVec3Gfx p2 = pos[ e2 ]; // scalar field pos 2
577 const float valp1 = value[ e1 ]; // scalar field val 1
578 const float valp2 = value[ e2 ]; // scalar field val 2
579 const float mu = (mIsoValue - valp1) / (valp2 - valp1);
581 // init isolevel vertex
582 ilv.v = p1 + (p2-p1)*mu; // with subdivs
583 mPoints.push_back( ilv );
584 triIndices[e] = (mPoints.size()-1);
586 *eVert[ e ] = triIndices[e];
588 // retrieve from vert array
589 triIndices[e] = *eVert[ e ];
593 // removed cutoff treatment...
595 // Create the triangles...
596 for(int e=0; mcTriTable[cubeIndex][e]!=-1; e+=3) {
597 mIndices.push_back( triIndices[ mcTriTable[cubeIndex][e+0] ] );
598 mIndices.push_back( triIndices[ mcTriTable[cubeIndex][e+1] ] ); // with subdivs
599 mIndices.push_back( triIndices[ mcTriTable[cubeIndex][e+2] ] );
600 //errMsg("TTT"," i1"<<mIndices[mIndices.size()-3]<<" "<< " i2"<<mIndices[mIndices.size()-2]<<" "<< " i3"<<mIndices[mIndices.size()-1]<<" "<< mIndices.size() );
603 } // triangles in edge table?
612 for(int j=0;j<(mSizey-0)*mSubdivs;j++)
613 for(int i=0;i<(mSizex-0)*mSubdivs;i++) {
615 const int dst = EDGEAR_INDEX( 0, 0, 0, i,j);
616 const int src = EDGEAR_INDEX( 0, 0, 1, i,j);
617 mpEdgeVerticesX[ dst ] = mpEdgeVerticesX[ src ];
618 mpEdgeVerticesY[ dst ] = mpEdgeVerticesY[ src ]; // with subdivs
619 mpEdgeVerticesZ[ dst ] = mpEdgeVerticesZ[ src ];
620 mpEdgeVerticesX[ src ]=-1;
621 mpEdgeVerticesY[ src ]=-1; // with subdivs
622 mpEdgeVerticesZ[ src ]=-1;
626 } // ok, k subdiv loop
634 float smoSubdfac = 1.;
636 //smoSubdfac = 1./(float)(mSubdivs);
637 smoSubdfac = pow(0.55,(double)mSubdivs); // slightly stronger
639 if(mSmoothSurface>0. || mSmoothNormals>0.) debMsgStd("IsoSurface::triangulate",DM_MSG,"Smoothing...",10);
640 if(mSmoothSurface>0.0) {
641 smoothSurface(mSmoothSurface*smoSubdfac, (mSmoothNormals<=0.0) );
643 if(mSmoothNormals>0.0) {
644 smoothNormals(mSmoothNormals*smoSubdfac);
647 myTime_t tritimeend = getTime();
648 debMsgStd("IsoSurface::triangulate",DM_MSG,"took "<< getTimeString(tritimeend-tritimestart)<<", S("<<mSmoothSurface<<","<<mSmoothNormals<<"),"<<
649 " verts:"<<mPoints.size()<<" tris:"<<(mIndices.size()/3)<<" subdivs:"<<mSubdivs
651 if(mpIsoParts) debMsgStd("IsoSurface::triangulate",DM_MSG,"parts:"<<mpIsoParts->getNumParticles(), 10);
658 /******************************************************************************
659 * Get triangles for rendering
660 *****************************************************************************/
661 void IsoSurface::getTriangles(double t, vector<ntlTriangle> *triangles,
662 vector<ntlVec3Gfx> *vertices,
663 vector<ntlVec3Gfx> *normals, int objectId )
666 debugOut("IsoSurface::getTriangles warning: Not initialized! ", 10);
672 /* triangulate field */
674 //errMsg("TRIS"," "<<mIndices.size() );
676 // new output with vertice reuse
677 int iniVertIndex = (*vertices).size();
678 int iniNormIndex = (*normals).size();
679 if(iniVertIndex != iniNormIndex) {
680 errFatal("getTriangles Error","For '"<<mName<<"': Vertices and normal array sizes to not match!!!",SIMWORLD_GENERICERROR);
683 //errMsg("NM"," ivi"<<iniVertIndex<<" ini"<<iniNormIndex<<" vs"<<vertices->size()<<" ns"<<normals->size()<<" ts"<<triangles->size() );
684 //errMsg("NM"," ovs"<<mVertices.size()<<" ons"<<mVertNormals.size()<<" ots"<<mIndices.size() );
686 for(int i=0;i<(int)mPoints.size();i++) {
687 vertices->push_back( mPoints[i].v );
689 for(int i=0;i<(int)mPoints.size();i++) {
690 normals->push_back( mPoints[i].n );
693 //errMsg("N2"," ivi"<<iniVertIndex<<" ini"<<iniNormIndex<<" vs"<<vertices->size()<<" ns"<<normals->size()<<" ts"<<triangles->size() );
694 //errMsg("N2"," ovs"<<mVertices.size()<<" ons"<<mVertNormals.size()<<" ots"<<mIndices.size() );
696 for(int i=0;i<(int)mIndices.size();i+=3) {
697 const int smooth = 1;
698 int t1 = mIndices[i];
699 int t2 = mIndices[i+1];
700 int t3 = mIndices[i+2];
701 //errMsg("NM"," tri"<<t1<<" "<<t2<<" "<<t3 );
705 tri.getPoints()[0] = t1+iniVertIndex;
706 tri.getPoints()[1] = t2+iniVertIndex;
707 tri.getPoints()[2] = t3+iniVertIndex;
711 if(getVisible()){ flag |= TRI_GEOMETRY; }
712 if(getCastShadows() ) {
713 flag |= TRI_CASTSHADOWS; }
715 /* init geo init id */
716 int geoiId = getGeoInitId();
718 flag |= (1<< (geoiId+4));
719 flag |= mGeoInitType;
722 tri.setFlags( flag );
724 /* triangle normal missing */
725 tri.setNormal( ntlVec3Gfx(0.0) );
726 tri.setSmoothNormals( smooth );
727 tri.setObjectId( objectId );
728 triangles->push_back( tri );
730 //errMsg("N3"," ivi"<<iniVertIndex<<" ini"<<iniNormIndex<<" vs"<<vertices->size()<<" ns"<<normals->size()<<" ts"<<triangles->size() );
736 inline ntlVec3Gfx IsoSurface::getNormal(int i, int j,int k) {
737 // WARNING - this requires a security boundary layer...
739 ret[0] = *getData(i-1,j ,k ) -
741 ret[1] = *getData(i ,j-1,k ) -
743 ret[2] = *getData(i ,j ,k-1 ) -
744 *getData(i ,j ,k+1 );
751 /******************************************************************************
753 * Surface improvement, inspired by trimesh2 library
754 * (http://www.cs.princeton.edu/gfx/proj/trimesh2/)
756 *****************************************************************************/
758 void IsoSurface::setSmoothRad(float radi1, float radi2, ntlVec3Gfx mscc) {
759 mSCrad1 = radi1*radi1;
760 mSCrad2 = radi2*radi2;
764 // compute normals for all generated triangles
765 void IsoSurface::computeNormals() {
766 for(int i=0;i<(int)mPoints.size();i++) {
767 mPoints[i].n = ntlVec3Gfx(0.);
770 for(int i=0;i<(int)mIndices.size();i+=3) {
771 const int t1 = mIndices[i];
772 const int t2 = mIndices[i+1];
773 const int t3 = mIndices[i+2];
774 const ntlVec3Gfx p1 = mPoints[t1].v;
775 const ntlVec3Gfx p2 = mPoints[t2].v;
776 const ntlVec3Gfx p3 = mPoints[t3].v;
777 const ntlVec3Gfx n1=p1-p2;
778 const ntlVec3Gfx n2=p2-p3;
779 const ntlVec3Gfx n3=p3-p1;
780 const gfxReal len1 = normNoSqrt(n1);
781 const gfxReal len2 = normNoSqrt(n2);
782 const gfxReal len3 = normNoSqrt(n3);
783 const ntlVec3Gfx norm = cross(n1,n2);
784 mPoints[t1].n += norm * (1./(len1*len3));
785 mPoints[t2].n += norm * (1./(len1*len2));
786 mPoints[t3].n += norm * (1./(len2*len3));
789 for(int i=0;i<(int)mPoints.size();i++) {
790 normalize(mPoints[i].n);
794 // Diffuse a vector field at 1 vertex, weighted by
795 // a gaussian of width 1/sqrt(invsigma2)
796 bool IsoSurface::diffuseVertexField(ntlVec3Gfx *field, const int pointerScale, int src, float invsigma2, ntlVec3Gfx &target)
798 if((neighbors[src].size()<1) || (pointareas[src]<=0.0)) return 0;
799 const ntlVec3Gfx srcp = mPoints[src].v;
800 const ntlVec3Gfx srcn = mPoints[src].n;
801 if(mSCrad1>0.0 && mSCrad2>0.0) {
802 ntlVec3Gfx dp = mSCcenter-srcp; dp[2] = 0.0; // only xy-plane
803 float rd = normNoSqrt(dp);
806 } else if(rd > mSCrad1) {
808 float org = 1.0/sqrt(invsigma2);
809 org *= (1.0- (rd-mSCrad1) / (mSCrad2-mSCrad1));
810 invsigma2 = 1.0/(org*org);
811 //errMsg("TRi","p"<<srcp<<" rd:"<<rd<<" r1:"<<mSCrad1<<" r2:"<<mSCrad2<<" org:"<<org<<" is:"<<invsigma2);
815 target = ntlVec3Gfx(0.0);
816 target += *(field+pointerScale*src) *pointareas[src];
817 float smstrSum = pointareas[src];
822 mDboundary = neighbors[src];
823 while (!mDboundary.empty()) {
824 const int bbn = mDboundary.back();
825 mDboundary.pop_back();
826 if(flags[bbn]==flag) continue;
830 const float nvdot = dot(srcn, mPoints[bbn].n); // faster than before d2 calc?
831 if(nvdot <= 0.0f) continue;
833 // gaussian weight of width 1/sqrt(invsigma2)
834 const float d2 = invsigma2 * normNoSqrt(mPoints[bbn].v - srcp);
835 if(d2 >= 9.0f) continue;
837 // aggressive smoothing factor
838 float smstr = nvdot * pointareas[bbn];
839 // Accumulate weight times field at neighbor
840 target += *(field+pointerScale*bbn)*smstr;
843 for(int i = 0; i < (int)neighbors[bbn].size(); i++) {
844 const int nn = neighbors[bbn][i];
845 if (flags[nn] == flag) continue;
846 mDboundary.push_back(nn);
854 // perform smoothing of the surface (and possible normals)
855 void IsoSurface::smoothSurface(float sigma, bool normSmooth)
857 int nv = mPoints.size();
858 if ((int)flags.size() != nv) flags.resize(nv);
859 int nf = mIndices.size()/3;
862 vector<int> numneighbors(mPoints.size());
864 for (i = 0; i < (int)mIndices.size()/3; i++) {
865 numneighbors[mIndices[i*3+0]]++;
866 numneighbors[mIndices[i*3+1]]++;
867 numneighbors[mIndices[i*3+2]]++;
871 neighbors.resize(mPoints.size());
872 for (i = 0; i < (int)mPoints.size(); i++) {
873 neighbors[i].clear();
874 neighbors[i].reserve(numneighbors[i]+2); // Slop for boundaries
877 for (i = 0; i < (int)mIndices.size()/3; i++) {
878 for (int j = 0; j < 3; j++) {
879 vector<int> &me = neighbors[ mIndices[i*3+j]];
880 int n1 = mIndices[i*3+((j+1)%3)];
881 int n2 = mIndices[i*3+((j+2)%3)];
882 if (std::find(me.begin(), me.end(), n1) == me.end())
884 if (std::find(me.begin(), me.end(), n2) == me.end())
892 pointareas.resize(nv);
894 cornerareas.resize(nf);
896 for (int i = 0; i < nf; i++) {
899 mPoints[mIndices[i*3+2]].v - mPoints[mIndices[i*3+1]].v,
900 mPoints[mIndices[i*3+0]].v - mPoints[mIndices[i*3+2]].v,
901 mPoints[mIndices[i*3+1]].v - mPoints[mIndices[i*3+0]].v };
903 // Compute corner weights
904 float area = 0.5f * norm( cross(e[0], e[1]));
905 float l2[3] = { normNoSqrt(e[0]), normNoSqrt(e[1]), normNoSqrt(e[2]) };
906 float ew[3] = { l2[0] * (l2[1] + l2[2] - l2[0]),
907 l2[1] * (l2[2] + l2[0] - l2[1]),
908 l2[2] * (l2[0] + l2[1] - l2[2]) };
910 cornerareas[i][1] = -0.25f * l2[2] * area /
912 cornerareas[i][2] = -0.25f * l2[1] * area /
914 cornerareas[i][0] = area - cornerareas[i][1] -
916 } else if (ew[1] <= 0.0f) {
917 cornerareas[i][2] = -0.25f * l2[0] * area /
919 cornerareas[i][0] = -0.25f * l2[2] * area /
921 cornerareas[i][1] = area - cornerareas[i][2] -
923 } else if (ew[2] <= 0.0f) {
924 cornerareas[i][0] = -0.25f * l2[1] * area /
926 cornerareas[i][1] = -0.25f * l2[0] * area /
928 cornerareas[i][2] = area - cornerareas[i][0] -
931 float ewscale = 0.5f * area / (ew[0] + ew[1] + ew[2]);
932 for (int j = 0; j < 3; j++)
933 cornerareas[i][j] = ewscale * (ew[(j+1)%3] +
937 // NT important, check this...
939 if(! finite(cornerareas[i][0]) ) cornerareas[i][0]=1e-6;
940 if(! finite(cornerareas[i][1]) ) cornerareas[i][1]=1e-6;
941 if(! finite(cornerareas[i][2]) ) cornerareas[i][2]=1e-6;
943 // FIXME check as well...
944 if(! (cornerareas[i][0]>=0.0) ) cornerareas[i][0]=1e-6;
945 if(! (cornerareas[i][1]>=0.0) ) cornerareas[i][1]=1e-6;
946 if(! (cornerareas[i][2]>=0.0) ) cornerareas[i][2]=1e-6;
949 pointareas[mIndices[i*3+0]] += cornerareas[i][0];
950 pointareas[mIndices[i*3+1]] += cornerareas[i][1];
951 pointareas[mIndices[i*3+2]] += cornerareas[i][2];
957 float invsigma2 = 1.0f / (sigma*sigma);
959 vector<ntlVec3Gfx> dflt(nv);
960 for (int i = 0; i < nv; i++) {
961 if(diffuseVertexField( &mPoints[0].v, 2,
962 i, invsigma2, dflt[i])) {
963 // Just keep the displacement
964 dflt[i] -= mPoints[i].v;
965 } else { dflt[i] = 0.0; } //?mPoints[i].v; }
968 // Slightly better small-neighborhood approximation
969 for (int i = 0; i < nf; i++) {
970 ntlVec3Gfx c = mPoints[mIndices[i*3+0]].v +
971 mPoints[mIndices[i*3+1]].v +
972 mPoints[mIndices[i*3+2]].v;
974 for (int j = 0; j < 3; j++) {
975 int v = mIndices[i*3+j];
976 ntlVec3Gfx d =(c - mPoints[v].v) * 0.5f;
977 dflt[v] += d * (cornerareas[i][j] /
978 pointareas[mIndices[i*3+j]] *
979 exp(-0.5f * invsigma2 * normNoSqrt(d)) );
983 // Filter displacement field
984 vector<ntlVec3Gfx> dflt2(nv);
985 for (int i = 0; i < nv; i++) {
986 if(diffuseVertexField( &dflt[0], 1,
987 i, invsigma2, dflt2[i])) { }
988 else { /*mPoints[i].v=0.0;*/ dflt2[i] = 0.0; }//dflt2[i]; }
991 // Update vertex positions
992 for (int i = 0; i < nv; i++) {
993 mPoints[i].v += dflt[i] - dflt2[i]; // second Laplacian
996 // when normals smoothing off, this cleans up quite well
997 // costs ca. 50% additional time though
999 if(normSmooth) { float ninvsigma2 = 1.0f / (nsFac*nsFac*sigma*sigma);
1000 for (int i = 0; i < nv; i++) {
1001 if( diffuseVertexField( &mPoints[0].n, 2, i, ninvsigma2, dflt[i]) ) {
1004 dflt[i] = mPoints[i].n;
1007 for (int i = 0; i < nv; i++) {
1008 mPoints[i].n = dflt[i];
1010 } // smoothNormals copy */
1012 //errMsg("SMSURF","done v:"<<sigma); // DEBUG
1015 // only smoothen the normals
1016 void IsoSurface::smoothNormals(float sigma) {
1017 // reuse from smoothSurface
1018 if(neighbors.size() != mPoints.size()) {
1020 vector<int> numneighbors(mPoints.size());
1022 for (i = 0; i < (int)mIndices.size()/3; i++) {
1023 numneighbors[mIndices[i*3+0]]++;
1024 numneighbors[mIndices[i*3+1]]++;
1025 numneighbors[mIndices[i*3+2]]++;
1029 neighbors.resize(mPoints.size());
1030 for (i = 0; i < (int)mPoints.size(); i++) {
1031 neighbors[i].clear();
1032 neighbors[i].reserve(numneighbors[i]+2); // Slop for boundaries
1035 for (i = 0; i < (int)mIndices.size()/3; i++) {
1036 for (int j = 0; j < 3; j++) {
1037 vector<int> &me = neighbors[ mIndices[i*3+j]];
1038 int n1 = mIndices[i*3+((j+1)%3)];
1039 int n2 = mIndices[i*3+((j+2)%3)];
1040 if (std::find(me.begin(), me.end(), n1) == me.end())
1042 if (std::find(me.begin(), me.end(), n2) == me.end())
1049 int nf = mIndices.size()/3, nv = mPoints.size();
1051 pointareas.resize(nv);
1052 cornerareas.clear();
1053 cornerareas.resize(nf);
1055 for (int i = 0; i < nf; i++) {
1058 mPoints[mIndices[i*3+2]].v - mPoints[mIndices[i*3+1]].v,
1059 mPoints[mIndices[i*3+0]].v - mPoints[mIndices[i*3+2]].v,
1060 mPoints[mIndices[i*3+1]].v - mPoints[mIndices[i*3+0]].v };
1062 // Compute corner weights
1063 float area = 0.5f * norm( cross(e[0], e[1]));
1064 float l2[3] = { normNoSqrt(e[0]), normNoSqrt(e[1]), normNoSqrt(e[2]) };
1065 float ew[3] = { l2[0] * (l2[1] + l2[2] - l2[0]),
1066 l2[1] * (l2[2] + l2[0] - l2[1]),
1067 l2[2] * (l2[0] + l2[1] - l2[2]) };
1068 if (ew[0] <= 0.0f) {
1069 cornerareas[i][1] = -0.25f * l2[2] * area /
1071 cornerareas[i][2] = -0.25f * l2[1] * area /
1073 cornerareas[i][0] = area - cornerareas[i][1] -
1075 } else if (ew[1] <= 0.0f) {
1076 cornerareas[i][2] = -0.25f * l2[0] * area /
1078 cornerareas[i][0] = -0.25f * l2[2] * area /
1080 cornerareas[i][1] = area - cornerareas[i][2] -
1082 } else if (ew[2] <= 0.0f) {
1083 cornerareas[i][0] = -0.25f * l2[1] * area /
1085 cornerareas[i][1] = -0.25f * l2[0] * area /
1087 cornerareas[i][2] = area - cornerareas[i][0] -
1090 float ewscale = 0.5f * area / (ew[0] + ew[1] + ew[2]);
1091 for (int j = 0; j < 3; j++)
1092 cornerareas[i][j] = ewscale * (ew[(j+1)%3] +
1096 // NT important, check this...
1098 if(! finite(cornerareas[i][0]) ) cornerareas[i][0]=1e-6;
1099 if(! finite(cornerareas[i][1]) ) cornerareas[i][1]=1e-6;
1100 if(! finite(cornerareas[i][2]) ) cornerareas[i][2]=1e-6;
1102 // FIXME check as well...
1103 if(! (cornerareas[i][0]>=0.0) ) cornerareas[i][0]=1e-6;
1104 if(! (cornerareas[i][1]>=0.0) ) cornerareas[i][1]=1e-6;
1105 if(! (cornerareas[i][2]>=0.0) ) cornerareas[i][2]=1e-6;
1108 pointareas[mIndices[i*3+0]] += cornerareas[i][0];
1109 pointareas[mIndices[i*3+1]] += cornerareas[i][1];
1110 pointareas[mIndices[i*3+2]] += cornerareas[i][2];
1115 int nv = mPoints.size();
1116 if ((int)flags.size() != nv) flags.resize(nv);
1117 float invsigma2 = 1.0f / (sigma*sigma);
1119 vector<ntlVec3Gfx> nflt(nv);
1120 for (int i = 0; i < nv; i++) {
1121 if(diffuseVertexField( &mPoints[0].n, 2, i, invsigma2, nflt[i])) {
1123 } else { nflt[i]=mPoints[i].n; }
1127 for (int i = 0; i < nv; i++) { mPoints[i].n = nflt[i]; }