#include "balloon.h" #include "float.h" #include "math.h" #include "vcg/complex/allocate.h" // AddPerVertexAttribute #include "vcg/complex/algorithms/update/selection.h" #include "vcg/complex/algorithms/update/color.h" #include "vcg/complex/algorithms/update/curvature.h" #include "vcg/complex/algorithms/update/curvature_fitting.h" // Quadric based curvature computation using namespace vcg; //---------------------------------------------------------------------------------------// // // LOGIC // //---------------------------------------------------------------------------------------// void Balloon::init( int gridsize, int gridpad ){ //--- Reset the iteration counter numiterscompleted = 0; isCurvatureUpdated = false; isDistanceFieldInit = false; isDistanceFieldUpdated = false; isWeightFieldUpdated = false; //--- Instantiate a properly sized wrapping volume vol.init( gridsize, gridpad, cloud.bbox ); qDebug() << "Created a volume of sizes: " << vol.size(0) << " " << vol.size(1) << " " << vol.size(2); //--- Compute hashing of ray intersections (using similar space structure of volume) gridAccell.init( vol, cloud ); qDebug() << "Finished hashing rays into the volume"; //--- Construct EDF of initial wrapping volume (BBOX) // Instead of constructing isosurface exactly on the bounding box, stay a bit large, // so that ray-isosurface intersections will not fail for that region. // Remember that rays will take a step JUST in their direction, so if they lie exactly // on the bbox, they would go outside. The code below corrects this from happening. Box3f enlargedbb = cloud.bbox; // float del = .99*vol.getDelta(); // almost +1 voxel in each direction float del = 1.99*vol.getDelta(); // USED FOR DEBUG // float del = .50*vol.getDelta(); // ADEBUG: almost to debug correspondences Point3f offset( del,del,del ); enlargedbb.Offset( offset ); vol.initField( enlargedbb ); // init volumetric field with the bounding box //--- Extract initial zero level set surface vol.isosurface( surf, 0 ); // qDebug() << "Extracted balloon isosurface (" << surf.vn << " " << surf.fn << ")"; //--- Clear band for next isosurface, clearing the corresponding computation field for(unsigned int i=0; i::AddPerVertexAttribute(surf, "Viewpoint distance field"); if( !vcg::tri::HasPerVertexAttribute (surf,std::string("Data support weight field")) ) surf_wf = vcg::tri::Allocator::AddPerVertexAttribute(surf, "Data support weight field"); //--- Update correspondences & band vol.band.clear(); vol.band.reserve(5*surf.fn); vol.updateSurfaceCorrespondence( surf, gridAccell, 2*vol.getDelta() ); } bool Balloon::init_fields(){ //-----------------------------------------------------------------------------------------------------------// // SETUP THE INTERPOLATOR SYSTEM // //-----------------------------------------------------------------------------------------------------------// surf.face.EnableQuality(); tri::UpdateQuality::FaceConstant(surf, 0); tri::UpdateSelection::AllFace(surf); bool op_succeed = true; op_succeed &= dinterp.Init( &surf, 1, COTANGENT ); op_succeed &= winterp.Init( &surf, 1, COTANGENT ); if( !op_succeed ){ dinterp.ColorizeIllConditioned( COTANGENT ); return false; } //-----------------------------------------------------------------------------------------------------------// // INIT PSEUDO-HEAT WEIGHT SYSTEM // // Assign a value of 0 to all vertices of the mesh. A value of 0 will be associated to parts of the mesh // which are considered "close" to data. Interpolating the field in a smooth way will end up generating // an heat-like scalar field distribution on the surface. //-----------------------------------------------------------------------------------------------------------// { for(CMeshO::VertexIterator vi=surf.vert.begin();vi!=surf.vert.end();vi++) winterp.AddConstraint( tri::Index(surf,*vi), OMEGA_WEIGHT_FIELD, 1 ); } //-----------------------------------------------------------------------------------------------------------// // ASSIGN A "MINIMAL" FACE TO EACH RAY // // In this first phase, we scan through faces and we update the information contained in the rays. We try to // have a many-1 correspondence in between rays and faces: each face can have more than one ray, but one ray // can only have one face associated with it. This face can either be behind or in front of the ray // startpoint. In between all the choices available, we choose the face which is closest to the startpoint //-----------------------------------------------------------------------------------------------------------// { // Ray-Triangle intersection parameters float t,u,v; // Clear the meta-data for correspondence for( unsigned int i=0; i line(pray.ray.Origin(), pray.ray.Direction()); if( IntersectionLineTriangle(line, f.P(0), f.P(1), f.P(2), t, u, v) ){ if( pray.f==NULL || fabs(t) tot_w( surf.fn, 0 ); for(unsigned int i=0; i(ray, f.P(0), f.P(1), f.P(2), t, u, v); assert( u>=0 && u<=1 && v>=0 && v<=1 ); //--- Add the barycenter-weighted constraints to the vertices of the face dinterp.AddConstraint( tri::Index(surf,f.V(0)), OMEGA_VIEW_FIELD*(1-u-v), t ); dinterp.AddConstraint( tri::Index(surf,f.V(1)), OMEGA_VIEW_FIELD*(u), t ); dinterp.AddConstraint( tri::Index(surf,f.V(2)), OMEGA_VIEW_FIELD*(v), t ); //--- Whenever we have an intersection, we have data winterp.AddConstraint( tri::Index(surf,f.V(0)), OMEGA_WEIGHT_FIELD*(1-u-v), 0 ); winterp.AddConstraint( tri::Index(surf,f.V(1)), OMEGA_WEIGHT_FIELD*(u), 0 ); winterp.AddConstraint( tri::Index(surf,f.V(2)), OMEGA_WEIGHT_FIELD*(v), 0 ); } //--- Normalize in case there is more than 1 ray per-face for(CMeshO::FaceIterator fi=surf.face.begin();fi!=surf.face.end();fi++){ if( tot_w[ tri::Index(surf,*fi) ] > 0 ) fi->Q() /= tot_w[ tri::Index(surf,*fi) ]; } } isDistanceFieldInit = true; return true; } bool Balloon::interp_fields(){ // Cannot interpolate if computation has failed if( isDistanceFieldInit == false ) return false; isDistanceFieldUpdated = true; dinterp.SolveInAttribute( surf_df ); isWeightFieldUpdated = true; winterp.SolveInAttribute( surf_wf ); } bool Balloon::compute_curvature(){ isCurvatureUpdated = true; tri::UpdateCurvatureFitting::computeCurvature( surf ); for(CMeshO::VertexIterator vi = surf.vert.begin(); vi != surf.vert.end(); ++vi){ (*vi).Kh() = ( (*vi).K1() + (*vi).K2() ) / 2; } return true; } bool Balloon::evolve(){ numiterscompleted++; std::vector updates_view(vol.band.size(),0); std::vector updates_whgt(vol.band.size(),0); std::vector updates_curv(vol.band.size(),0); float view_max_absdst = -FLT_MAX; float view_max_dst = -FLT_MAX; float view_min_dst = +FLT_MAX; float view_min_weight = +FLT_MAX; float view_max_weight = -FLT_MAX; float curv_maxval = -FLT_MAX; // Only meaningful if it has been computed.. qDebug("Delta: %f", vol.getDelta()); //-----------------------------------------------------------------------------------------------------------// // 1) TRANSFER PROPERTIES FROM VERTICES TO SURROUNDING GRID BAND // // We have a band of voxels around the current surface. In this first step we transfer the information stored // on the surface to the surrounding voxels. First, we project the voxel center onto the explicit surface. // Once there, we estimate where within the corresponding face we are located, and we interpolate the field // values accordingly. We also keep track of the ranges so that we can determine which elements //-----------------------------------------------------------------------------------------------------------// { Point3f c; // barycentric coefficients Point3f voxp; for(unsigned int i=0; i triFace( f.P(0), f.P(1), f.P(2) ); // Paolo, is this really necessary? int axis; if (f.Flags() & CFaceO::NORMX ) axis = 0; else if(f.Flags() & CFaceO::NORMY ) axis = 1; else axis = 2; vcg::InterpolationParameters(triFace, axis, proj, c); if( isDistanceFieldUpdated ){ updates_view[i] = c[0]*surf_df[f.V(0)] + c[1]*surf_df[f.V(1)] + c[2]*surf_df[f.V(2)]; view_max_absdst = (fabs(updates_view[i])>view_max_absdst) ? fabs(updates_view[i]) : view_max_absdst; view_max_dst = updates_view[i]>view_max_dst ? updates_view[i] : view_max_dst; view_min_dst = updates_view[i]view_max_weight ? updates_whgt[i] : view_max_weight; } if( isCurvatureUpdated ){ updates_curv[i] = c[0]*f.V(0)->Kh() + c[1]*f.V(1)->Kh() + c[2]*f.V(2)->Kh(); curv_maxval = (fabs(updates_curv[i])>curv_maxval) ? fabs(updates_curv[i]) : curv_maxval; } } if( isWeightFieldUpdated ) qDebug("weight field: min %.3f max %.3f", view_min_dst, view_max_dst); if( isDistanceFieldUpdated ) qDebug("distance field: min %.3f max %.3f", view_min_weight, view_max_weight); if( isCurvatureUpdated ) qDebug("curvat field: max %.3f", curv_maxval); } //-----------------------------------------------------------------------------------------------------------// // 2) COMBINE THE VARIOUS UPDATES MEANINGFULLY // TODO // //-----------------------------------------------------------------------------------------------------------// { // Max evolution speed is proportional to grid size float max_speed = vol.getDelta()/2; // bound on energy balance: E = E_view + alpha*E_smooth float alpha = .5; // small (slows down) when worst case scenario is approaching grid size float sigma2 = vol.getDelta()*vol.getDelta(); float k_notconverging = 1 - exp( -powf(view_max_absdst,2) / sigma2 ); // high (speed up ) when vertex is far away from surface float k_highdist; // high (speed up ) when vertex has high local curvature float k_highcurv; // For every element of the active band for(unsigned int i=0; irm ^= SURF_VCOLOR; // disable vertex color this->rm |= SURF_FCOLOR; // enable face color surf.face.EnableColor(); tri::UpdateColor::FaceConstant(surf, Color4b::White); tri::UpdateColor::FaceQualityRamp(surf, true); } void Balloon::wfieldToVertexColor(){ // Enable vertex coloring rm &= ~SURF_FCOLOR; rm |= SURF_VCOLOR; // Transfer from wfield to vertex quality for( CMeshO::VertexIterator vi=surf.vert.begin(); vi!=surf.vert.end(); vi++ ) (*vi).Q() = surf_wf[vi]; // Build histogram and map range to colors Histogram H; tri::Stat::ComputePerVertexQualityHistogram(surf,H); tri::UpdateColor::VertexQualityRamp(surf,H.Percentile(0.0f),H.Percentile(1.0f)); } void Balloon::dfieldToVertexColor(){ // Enable vertex coloring rm &= ~SURF_FCOLOR; rm |= SURF_VCOLOR; // Transfer from dfield to vertex quality for( CMeshO::VertexIterator vi=surf.vert.begin(); vi!=surf.vert.end(); vi++ ) (*vi).Q() = surf_df[vi]; // Build histogram and map range to colors Histogram H; tri::Stat::ComputePerVertexQualityHistogram(surf,H); tri::UpdateColor::VertexQualityRamp(surf,H.Percentile(0.0f),H.Percentile(1.0f)); } void Balloon::KhToVertexColor(){ if( !surf.vert.CurvatureEnabled ) return; // Disable face coloring and enable vertex this->rm &= ~SURF_FCOLOR; this->rm |= SURF_VCOLOR; // Compute curvature bounds float absmax = -FLT_MAX; for(CMeshO::VertexIterator vi = surf.vert.begin(); vi != surf.vert.end(); ++vi){ float cabs = fabs((*vi).Kh()); absmax = (cabs>absmax) ? cabs : absmax; } //--- Map curvature to two color ranges: // - Blue => Yellow: negative values // - Yellow => Red: positive values for(CMeshO::VertexIterator vi = surf.vert.begin(); vi != surf.vert.end(); ++vi){ if( (*vi).Kh() < 0 ) (*vi).C().lerp(Color4b::Yellow, Color4b::Blue, fabs((*vi).Kh())/absmax ); else (*vi).C().lerp(Color4b::Yellow, Color4b::Red, (*vi).Kh()/absmax); } } void Balloon::render_cloud(){ // Draw the ray/rays from their origin up to some distance away glDisable(GL_LIGHTING); glColor3f(.5, .5, .5); for(CMeshO::VertexIterator vi=cloud.vert.begin(); vi!=cloud.vert.end(); ++vi){ Point3f p1 = (*vi).P(); Point3f n = (*vi).N(); // n[0] *= .1; n[1] *= .1; n[2] *= .1; // Scale the viewdir Point3f p2 = (*vi).P() + n; glBegin(GL_LINES); glVertex3f(p1[0],p1[1],p1[2]); glVertex3f(p2[0],p2[1],p2[2]); glEnd(); } glEnable(GL_LIGHTING); } void Balloon::render_isosurface(GLArea* gla){ GLW::DrawMode dm = GLW::DMFlatWire; GLW::ColorMode cm = GLW::CMPerVert; GLW::TextureMode tm = GLW::TMNone; // By default vertColor is defined, so let's check if we need/want to // draw the face colors first. if( (rm & SURF_FCOLOR) && tri::HasPerFaceColor(surf) ){ gla->rm.colorMode = vcg::GLW::CMPerFace; // Corrects MESHLAB BUG cm = GLW::CMPerFace; } else if( (rm & SURF_VCOLOR) && tri::HasPerVertexColor(surf) ){ gla->rm.colorMode = vcg::GLW::CMPerVert; // Corrects MESHLAB BUG cm = GLW::CMPerVert; } GlTrimesh surf_renderer; surf_renderer.m = &surf; surf_renderer.Draw(dm, cm, tm); } void Balloon::render_surf_to_acc(){ gridAccell.render(); #if 0 glDisable( GL_LIGHTING ); const float ONETHIRD = 1.0f/3.0f; Point3f fcenter; Point3i off, o; glColor3f(1.0, 0.0, 0.0); for(unsigned int fi =0; fi