#include "volume.h" #include "myheap.h" // maxheap for correspondence band #include "../filter_plymc/plymc.h" // remove bad triangles from marching cubes #include // mesh smoothing using namespace vcg; /// the array is used to scan a single voxel that contains the triangle in the initialization const Point3i off[8] = { Point3i(0,0,0), Point3i(0,0,1), Point3i(0,1,0), Point3i(0,1,1), Point3i(1,0,0), Point3i(1,0,1), Point3i(1,1,0), Point3i(1,1,1) }; // the array is used to scan the 26-neighborhood const Point3i off26[26] = { Point3i(-1, -1, -1), Point3i(-1, 0, -1), Point3i(-1, +1, -1), Point3i( 0, -1, -1), Point3i( 0, 0, -1), Point3i( 0, +1, -1), Point3i( 1, -1, -1), Point3i( 1, 0, -1), Point3i( 1, +1, -1), Point3i(-1, -1, 0), Point3i(-1, 0, 0), Point3i(-1, +1, 0), Point3i( 0, -1, 0), /* skip center */ Point3i( 0, +1, 0), Point3i( 1, -1, 0), Point3i( 1, 0, 0), Point3i( 1, +1, 0), Point3i(-1, -1, 1), Point3i(-1, 0, 1), Point3i(-1, +1, 1), Point3i( 0, -1, 1), Point3i( 0, 0, 1), Point3i( 0, +1, 1), Point3i( 1, -1, 1), Point3i( 1, 0, 1), Point3i( 1, +1, 1) }; void MyVolume::init( int gridsize, int padsize, vcg::Box3f bbox ){ // Extract length of longest edge int maxdimi = bbox.MaxDim(); Point3f dim = bbox.Dim(); float maxdim = dim[maxdimi]; // qDebug() << "MaxDim: " << maxdim; this->delta = maxdim / ( gridsize ); // qDebug() << "Delta: " << delta; this->padsize = padsize; // qDebug() << "Padsize: " << padsize; this->bbox = bbox; // qDebug() << "bbox_m: " << bbox.min[0] << " " << bbox.min[1] << " " << bbox.min[2]; // Debug Stuff if( false ){ Point3i offmin, offmax; pos2off( bbox.min, offmin ); pos2off( bbox.max, offmax ); qDebug() << "Imin: (" << offmin[0] << " " << offmin[1] << " " << offmin[2] <<")"; qDebug() << "Imax: (" << offmax[0] << " " << offmax[1] << " " << offmax[2] <<")"; } // Initialize the grid (uniform padding) int dimx = ceil( gridsize * bbox.DimX() / maxdim ) + 2*padsize; int dimy = ceil( gridsize * bbox.DimY() / maxdim ) + 2*padsize; int dimz = ceil( gridsize * bbox.DimZ() / maxdim ) + 2*padsize; this->sz = Point3i( dimx, dimy, dimz ); grid.Init( this->sz ); // Brute force initialize the grid... VCG doesn't have much... for(int i=0;i=0 && dim<3 ); float max_val = 0; for(int i=0; i255?255:val; painter.setPen( QColor(val,val,val) ); painter.drawPoint(k,j); } break; case 1: for(int i=0; i255?255:val; painter.setPen( QColor(val,val,val) ); painter.drawPoint(i,k); } break; case 2: for(int i=0; i255?255:val; painter.setPen( QColor(val,val,val) ); // Flip image upside down painter.drawPoint(i,size(1)-j-1); } break; } return slices_2D[dim]; } void MyVolume::initField( const vcg::Box3f& inbbox ){ // Triangulate the bounding box if( true ){ qDebug() << "constructing EDF of box: [" << vcg::toString(inbbox.min) << " " << vcg::toString(inbbox.max) << "]"; Point3f pos; float d; float sgn; for(int i=0; i=2 && i<6 && j>=2 && j<6 && k>=2 && k<6 ) grid.Val(i,j,k) = -1; else grid.Val(i,j,k) = +1; } else if( true ){ // Radius in object space double r = 1; Point3f p; Point3f center = bbox.Center(); qDebug() << "Sphere in: " << center[0] << " " << center[1] << " " << center[2] << endl; center[1] = 0; for(int i=0;i > MyWalker; typedef vcg::tri::MarchingCubes MyMarchingCubes; MyWalker walker; MyMarchingCubes mc(surf, walker); walker.BuildMesh(surf, this->grid, mc, offset); // Rescale the marching cube mesh for(CMeshO::VertexIterator vi=surf.vert.begin();vi!=surf.vert.end();vi++){ (*vi).P()[0] = ((*vi).P()[0]-padsize) * delta + bbox.min[0]; (*vi).P()[1] = ((*vi).P()[1]-padsize) * delta + bbox.min[1]; (*vi).P()[2] = ((*vi).P()[2]-padsize) * delta + bbox.min[2]; } //--- Remove slivers which might crash the whole system (reuses the one defined by plymc) // Paolo: il parametro perc va dato in funzione della grandezza del voxel se gli dai come // perc: perc=voxel.side/4 sei safe surf.vert.EnableMark(); surf.vert.EnableVFAdjacency(); surf.face.EnableVFAdjacency(); tri::UpdateTopology::VertexFace( surf ); tri::MCSimplify( surf, getDelta()/4 ); //--- The simplify operation removed some vertices tri::Allocator::CompactVertexVector( surf ); tri::Allocator::CompactFaceVector( surf ); //--- Final Cleanup surf.face.EnableFFAdjacency(); tri::Clean::RemoveTVertexByFlip(surf,20,true); tri::Clean::RemoveFaceFoldByFlip(surf); #ifdef REMOVE_DEGENERATE_FACES int total = tri::Clean::MergeCloseVertex(surf, getDelta()/8); qDebug("Successfully merged %d vertices", total); // merging close might introduce non-manifolds tri::Clean::RemoveNonManifoldFace(surf); // Make sure this is not messing up the topology tri::Allocator::CompactVertexVector( surf ); tri::Allocator::CompactFaceVector( surf ); surf.face.EnableFFAdjacency(); surf.vert.EnableMark(); surf.vert.EnableVFAdjacency(); surf.face.EnableVFAdjacency(); tri::UpdateTopology::VertexFace( surf ); #endif //--- Post-processing // tri::Smooth::VertexCoordLaplacian(m.cm,stepSmoothNum,Selected,cb); //--- Update surface normals tri::UpdateNormals::PerVertexNormalizedPerFaceNormalized( surf ); //--- Face orientation, needed to have more robust face distance tests tri::UpdateFlags::FaceProjection(surf); //--- Attributes for curvature computation surf.vert.EnableCurvature(); surf.vert.EnableCurvatureDir(); // quadric based needs it } /// gridaccell is needed only to retrieve the correct face=>voxel index void MyVolume::updateSurfaceCorrespondence( CMeshO& surf, GridAccell& gridAccell, float DELTA ){ // The capacity of the band is estiamted to be enough to reach DELTA away MinHeap pq( band.capacity() ); //--- INITIALIZATION Point3i o, newo; Point3f p, newp; float dist; for(CMeshO::FaceIterator fi=surf.face.begin();fi!=surf.face.end();fi++){ // Compute hash index from face center CFaceO& f = *(fi); p = Barycenter( f ); // ADEBUG: skip invalid samples if( math::IsNAN(p[0]) || math::IsNAN(p[1]) || math::IsNAN(p[2]) ) continue; gridAccell.pos2off( p, o ); // Convert faces in OBJ space (OK!) // Initialize the exploration queue for this face: compute the distance value for the 8 corners of the voxel containing // the face making sure to set the right distance when a corner is covered by more than one face. Note that we set the flag // to 2 (FINISHED) so that these values will never be inserted again in the queue after initialization for( int i=0; i<8; i++ ){ // Offset the origin and compute new point newo = Point3i(o[0]+off[i][0], o[1]+off[i][1], o[2]+off[i][2]); MyVoxel& v = grid.V(newo[0], newo[1], newo[2]); off2pos(newo, newp); // Convert offset to position (OK!) float sdist = vcg::SignedFacePointDistance(f, newp) ; dist = fabs(sdist); // If has never been touched... insert it if( v.status == 0 ){ v.field = dist; //if(math::IsNAN(v.sfield)) v.sfield = sdist; v.index = band.size(); v.face = &f; v.status = 2; pq.push(dist, v.index); band.push_back(newo); } // It has been inserted already, but an update is needed else if( v.status == 2 && dist < v.field ){ v.field = dist; //v.sfield = sdist; v.face = &f; pq.push(dist, v.index); } } } //--- EVOLUTION // Expansion front, whenever a new voxel is met, If distance is small enough: // 1) add it to the active band (thus inheriting its position as index) // 2) add neighbors to queue or update their distances if has improved Point3f neigh_p; Point3i neigh_o; float debd; int indx; while( !pq.empty() ){ //--- Retrieve current voxel index and pop it debd = pq.top().first; indx = pq.top().second; if( indx>band.size() ){ qDebug("bindex %d, bandsz %d", indx, int(band.size())); assert( indx < band.size() ); } pq.pop(); //--- Retrieve voxel & mark it as visited, also compute the real signed distance // and set it in the voxel (fast marching used unsigned distance) newo = band.at( indx ); MyVoxel& v = grid.V(newo[0], newo[1], newo[2]); v.status = 2; // Never visit it again CFaceO& f = *(v.face); // Parent supporting face off2pos(newo, newp); v.field = fabs(vcg::SignedFacePointDistance(f, newp)); if(math::IsNAN(v.sfield)) v.sfield= vcg::SignedFacePointDistance(f, newp); //--- Visit its neighbors and (update | add) them to queue for( int i=0; i<26; i++ ){ // Neighbor offset neigh_o = Point3i(newo[0]+off26[i][0], newo[1]+off26[i][1], newo[2]+off26[i][2]); MyVoxel& neigh_v = grid.V(neigh_o[0], neigh_o[1], neigh_o[2]); if( neigh_v.status == 2 ) continue; // skip popped areas off2pos(neigh_o, neigh_p); dist = fabs( vcg::SignedFacePointDistance(f, neigh_p ) ); // Never add samples that go beyond the distance value. Note that the padding // shuld allow for the voxels within this distance to be reached without creating // off2pos assertion error. // Only if it has never been touched, also, Never add samples that go beyond the // DELTA distance value. Note that the padding shuld allow for the voxels within // this distance to be reached without creating off2pos assertion error. if( neigh_v.status == 0 && dist < DELTA ){ neigh_v.field = dist; // set distance neigh_v.face = &f; // save face reference neigh_v.status = 1; // mark as inserted neigh_v.index = band.size(); // compute index pq.push(dist, neigh_v.index); // insert it band.push_back(neigh_o); // add to active voxels } // It has been inserted already, but update is needed else if( neigh_v.status == 1 && dist < neigh_v.field && dist < DELTA ){ neigh_v.field = dist; // set new distance neigh_v.face = &f; // save face reference pq.push(dist, neigh_v.index); // add to active voxels } } } } void MyVolume::render(){ if( !this->isInit() ) return; qDebug() << "Volume::render()"; Point3f p; MyVoxel v; glDisable(GL_LIGHTING); int padsize = 0; for(int i=padsize;i=0; j--){ QString column; for(int i=0; i