mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-15 17:14:39 +00:00
554 lines
24 KiB
C++
554 lines
24 KiB
C++
#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<vol.band.size(); i++){
|
|
Point3i& voxi = vol.band[i];
|
|
MyVoxel& v = vol.Voxel(voxi);
|
|
v.status = 0;
|
|
v.face = 0;
|
|
v.index = 0;
|
|
v.field = NAN;
|
|
}
|
|
|
|
//--- Create extra space to store temporary variables
|
|
if( !vcg::tri::HasPerVertexAttribute (surf,std::string("Viewpoint distance field")) )
|
|
surf_df = vcg::tri::Allocator<CMeshO>::AddPerVertexAttribute<float>(surf, "Viewpoint distance field");
|
|
if( !vcg::tri::HasPerVertexAttribute (surf,std::string("Data support weight field")) )
|
|
surf_wf = vcg::tri::Allocator<CMeshO>::AddPerVertexAttribute<float>(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<CMeshO>::FaceConstant(surf, 0);
|
|
tri::UpdateSelection<CMeshO>::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<gridAccell.rays.size(); i++ ){
|
|
gridAccell.rays[i].f = NULL;
|
|
gridAccell.rays[i].t = +FLT_MAX;
|
|
}
|
|
// Assign one face to every ray
|
|
for(CMeshO::FaceIterator fi=surf.face.begin();fi!=surf.face.end();fi++){
|
|
CFaceO& f = *(fi);
|
|
//--- Clear quality and selection flags
|
|
f.ClearS();
|
|
f.Q() = 0;
|
|
//--- Store closest face to ray
|
|
for(gridAccell.iter.first(f); !gridAccell.iter.isDone(); gridAccell.iter.next()){
|
|
PokingRay& pray = gridAccell.iter.currentItem();
|
|
Line3<float> 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)<fabs(pray.t) ){
|
|
pray.f=&f;
|
|
pray.t=t;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------------//
|
|
// ASSIGN WEIGHT FROM EACH RAY TO FACES
|
|
//
|
|
// 1) visit the face and we set the field constraint imposed by the poking ray
|
|
// 2) every intersection which is very close to the data generates a confidence contribution.
|
|
//
|
|
// NOTE: The use of vcg::Simplify to remove degenerate triangles and any bug in the DDR iterator could cause a ray
|
|
// to fail to detect an intersection. If this takes place, a warning message is displayed and the insertion
|
|
// of the constraint gets skipped.
|
|
//-----------------------------------------------------------------------------------------------------------//
|
|
{
|
|
// Ray-Triangle intersection parameters
|
|
float t,u,v;
|
|
// Debug: total weight to take average of dist on face and display it
|
|
std::vector<float> tot_w( surf.fn, 0 );
|
|
for(unsigned int i=0; i<gridAccell.rays.size(); i++){
|
|
//--- Retrieve the corresponding face and signed distance
|
|
Ray3f& ray = gridAccell.rays[i].ray;
|
|
if( gridAccell.rays[i].f == NULL){
|
|
qDebug() << "warning: ray #" << i << "has provided NULL intersection"; // << toString(ray.Origin()) << " " << toString(ray.Direction());
|
|
continue;
|
|
}
|
|
CFaceO& f = *(gridAccell.rays[i].f);
|
|
t = gridAccell.rays[i].t;
|
|
assert( !math::IsNAN(t) );
|
|
|
|
// Color the faces, if more than one, take average
|
|
tot_w[ tri::Index(surf,f) ]++;
|
|
f.Q() += t; // normalize with tot_w after
|
|
f.SetS(); // enable the face for coloring
|
|
|
|
// I was lazy and didn't store the u,v... we need to recompute them
|
|
vcg::IntersectionRayTriangle<float>(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<CMeshO>::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<float> updates_view(vol.band.size(),0);
|
|
std::vector<float> updates_whgt(vol.band.size(),0);
|
|
std::vector<float> 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<vol.band.size(); i++){
|
|
Point3i& voxi = vol.band[i];
|
|
MyVoxel& v = vol.Voxel(voxi);
|
|
CFaceO& f = *v.face;
|
|
|
|
// extract projected points on surface and obtain barycentric coordinates
|
|
// TODO: double work, it was already computed during correspodence, write a new function?
|
|
Point3f proj;
|
|
vol.off2pos(voxi, voxp);
|
|
vcg::SignedFacePointDistance(f, voxp, proj);
|
|
Triangle3<float> 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_min_dst ? updates_view[i] : view_min_dst;
|
|
|
|
}
|
|
if( isWeightFieldUpdated ){
|
|
updates_whgt[i] = c[0]*surf_wf[f.V(0)] + c[1]*surf_wf[f.V(1)] + c[2]*surf_wf[f.V(2)];
|
|
view_min_weight = updates_whgt[i]<view_min_weight ? updates_whgt[i] : view_min_weight;
|
|
view_max_weight = updates_whgt[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; i<vol.band.size(); i++){
|
|
Point3i& voxi = vol.band[i];
|
|
MyVoxel& v = vol.Voxel(voxi);
|
|
|
|
//--- If I know current distance avoid over-shooting, as maximum speed is bound to dist from surface
|
|
if( isDistanceFieldUpdated ){
|
|
max_speed = std::min( vol.getDelta()/2, std::abs(updates_view[i]) );
|
|
k_highdist = exp( -powf(fabs(updates_view[i])-view_max_absdst,2) / sigma2 );
|
|
v.sfield += vcg::sign( max_speed*k_highdist*k_notconverging, updates_view[i] );
|
|
}
|
|
//--- Curvature weight (faster if spiky)
|
|
if( isCurvatureUpdated ){
|
|
k_highcurv = sign(1.0f,updates_curv[i])*exp(-powf(fabs(updates_curv[i])-curv_maxval,2)/curv_maxval);
|
|
|
|
// If a "support" field is specified
|
|
if( isWeightFieldUpdated ){
|
|
v.sfield += updates_whgt[i]*alpha*max_speed*k_highcurv;
|
|
} else {
|
|
v.sfield += max_speed*k_highcurv;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--- show what's being updated
|
|
if( isDistanceFieldUpdated && isCurvatureUpdated && isWeightFieldUpdated )
|
|
qDebug() << "update: E_vd + alpha*E_kh";
|
|
else if( isDistanceFieldUpdated && isCurvatureUpdated )
|
|
qDebug() << "update: E_vd + kost*E_kh";
|
|
else if( isDistanceFieldUpdated )
|
|
qDebug() << "update: E_vd";
|
|
else if( isCurvatureUpdated )
|
|
qDebug() << "update: E_kh";
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------------------------------------//
|
|
// 3) EXTRACT THE NEW EXPLICIT SURFACE
|
|
// TODO
|
|
//
|
|
//-----------------------------------------------------------------------------------------------------------//
|
|
{
|
|
//--- Extract isosurface
|
|
vol.isosurface( surf, 0 );
|
|
|
|
//--- Clear band for next isosurface, clearing the corresponding computation field
|
|
for(unsigned int i=0; i<vol.band.size(); i++){
|
|
Point3i& voxi = vol.band[i];
|
|
MyVoxel& v = vol.Voxel(voxi);
|
|
v.status = 0;
|
|
v.face = 0;
|
|
v.index = 0;
|
|
v.field = NAN;
|
|
}
|
|
vol.band.clear();
|
|
//--- Update correspondences & band
|
|
vol.band.reserve(5*surf.fn);
|
|
vol.updateSurfaceCorrespondence( surf, gridAccell, 2*vol.getDelta() );
|
|
//--- Nothing is updated anymore!!
|
|
isWeightFieldUpdated = false;
|
|
isDistanceFieldUpdated = false;
|
|
isCurvatureUpdated = false;
|
|
isDistanceFieldInit = false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------//
|
|
//
|
|
// RENDERING
|
|
//
|
|
//---------------------------------------------------------------------------------------//
|
|
/// Transfer the average distance stored in face quality to a color for triangles belonging to a certain selection
|
|
void Balloon::selectedFacesQualityToColor(){
|
|
this->rm ^= SURF_VCOLOR; // disable vertex color
|
|
this->rm |= SURF_FCOLOR; // enable face color
|
|
surf.face.EnableColor();
|
|
tri::UpdateColor<CMeshO>::FaceConstant(surf, Color4b::White);
|
|
tri::UpdateColor<CMeshO>::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<float> H;
|
|
tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(surf,H);
|
|
tri::UpdateColor<CMeshO>::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<float> H;
|
|
tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(surf,H);
|
|
tri::UpdateColor<CMeshO>::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<CMeshO> 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<surf.face.size(); fi++){
|
|
if( fi!= 5 )
|
|
continue;
|
|
CFaceO& f = surf.face[fi];
|
|
fcenter = f.P(0) + f.P(1) + f.P(2);
|
|
fcenter = myscale( fcenter, ONETHIRD );
|
|
gridAccell.pos2off( fcenter, off );
|
|
vol.pos2off( fcenter, o );
|
|
gridAccell.off2pos( off, fcenter );
|
|
vcg::drawBox( fcenter, vol.getDelta()*.95, true );
|
|
|
|
|
|
// SHO BLOCK
|
|
qDebug() << "full volume: " << endl << vol.toString(2, o[2]);
|
|
// DEBUG: examine field values around current coordinate
|
|
QString q;
|
|
qDebug();
|
|
q.sprintf("%2.2f %2.2f %2.2f \n%2.2f %2.2f %2.2f \n%2.2f %2.2f %2.2f",
|
|
vol.Val(o[0]-1,o[1]+1,o[2]), vol.Val(o[0],o[1]+1,o[2]), vol.Val(o[0]+1,o[1]+1,o[2]),
|
|
vol.Val(o[0]-1,o[1],o[2]), vol.Val(o[0],o[1],o[2]), vol.Val(o[0]+1,o[1]+0,o[2]),
|
|
vol.Val(o[0]-1,o[1]-1,o[2]), vol.Val(o[0],o[1]-1,o[2]), vol.Val(o[0]+1,o[1]-1,o[2]) );
|
|
qDebug() << q;
|
|
qDebug();
|
|
o = off; // use the gridaccell data
|
|
q.sprintf("%2.2f %2.2f %2.2f \n%2.2f %2.2f %2.2f \n%2.2f %2.2f %2.2f",
|
|
vol.Val(o[0]-1,o[1]+1,o[2]), vol.Val(o[0],o[1]+1,o[2]), vol.Val(o[0]+1,o[1]+1,o[2]),
|
|
vol.Val(o[0]-1,o[1],o[2]), vol.Val(o[0],o[1],o[2]), vol.Val(o[0]+1,o[1]+0,o[2]),
|
|
vol.Val(o[0]-1,o[1]-1,o[2]), vol.Val(o[0],o[1]-1,o[2]), vol.Val(o[0]+1,o[1]-1,o[2]) );
|
|
qDebug() << q;
|
|
}
|
|
#endif
|
|
|
|
glEnable( GL_LIGHTING );
|
|
}
|
|
void Balloon::render_surf_to_vol(){
|
|
if( !vol.isInit() ) return;
|
|
Point3f p, proj;
|
|
glDisable(GL_LIGHTING);
|
|
for(int i=0;i<vol.size(0);i++)
|
|
for(int j=0;j<vol.size(1);j++)
|
|
for(int k=0;k<vol.size(2);k++){
|
|
// if( i!=3 || j!=6 || k!=5 ) continue;
|
|
|
|
MyVoxel& v = vol.Voxel(i,j,k);
|
|
vol.off2pos(i,j,k,p);
|
|
// Only ones belonging to active band
|
|
if( v.status == 2 ){ // && ( tri::Index(surf, v.face)==35||tri::Index(surf, v.face)==34) ){
|
|
assert( v.face != 0 );
|
|
vcg::drawBox(p, .05*vol.getDelta());
|
|
Point3f proj;
|
|
// float dist = vcg::SignedFacePointDistance(*v.face, p, proj);
|
|
vcg::SignedFacePointDistance(*v.face, p, proj);
|
|
//proj = vcg::Barycenter(*v.face);
|
|
vcg::drawSegment( p, proj );
|
|
}
|
|
}
|
|
glEnable(GL_LIGHTING);
|
|
}
|
|
void Balloon::render(GLArea* gla){
|
|
if( rm & SHOW_CLOUD )
|
|
render_cloud();
|
|
if( rm & SHOW_VOLUME )
|
|
vol.render();
|
|
if( rm & SHOW_SURF )
|
|
render_isosurface(gla);
|
|
//if( rm & SHOW_ACCEL )
|
|
// gridAccell.render();
|
|
if( rm & SHOW_3DDDR )
|
|
render_surf_to_acc();
|
|
if( rm & SHOW_SURF_TO_VOL )
|
|
render_surf_to_vol();
|
|
}
|