Strongly refactored in order to guarantee better independence between the resampled mesh an the original one

This commit is contained in:
Paolo Cignoni 2014-08-09 00:19:48 +00:00
parent 3784295ac6
commit 03c2648af1
1 changed files with 501 additions and 504 deletions

View File

@ -40,37 +40,40 @@ namespace tri {
/*@{*/
/*@{*/
/** Class Resampler.
This is class reasmpling a mesh using marching cubes methods
@param OLD_MESH_TYPE (Template Parameter) Specifies the type of mesh to be resampled
@param NEW_MESH_TYPE (Template Parameter) Specifies the type of output mesh.
This is class resampling a mesh using marching cubes methods
@param OldMeshType (Template Parameter) Specifies the type of mesh to be resampled
@param NewMeshType (Template Parameter) Specifies the type of output mesh.
All the computations are done in the output mesh scalar type. (e.g. if you have a double mesh and you want to resample int to a float mesh the volume is kept in float)
*/
template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR = vcg::face::PointDistanceBaseFunctor<typename OLD_MESH_TYPE::ScalarType > >
class Resampler : public BasicGrid<FLT>
template <class OldMeshType,
class NewMeshType,
class DISTFUNCTOR = vcg::face::PointDistanceBaseFunctor<typename OldMeshType::ScalarType > >
class Resampler : public BasicGrid<typename NewMeshType::ScalarType>
{
typedef OLD_MESH_TYPE Old_Mesh;
typedef NEW_MESH_TYPE New_Mesh;
typedef typename NewMeshType::ScalarType NewScalarType;
typedef typename NewMeshType::BoxType NewBoxType;
typedef typename NewMeshType::CoordType NewCoordType;
typedef typename NewMeshType::VertexType* NewVertexPointer;
typedef typename NewMeshType::VertexIterator NewVertexIterator;
typedef typename OldMeshType::CoordType OldCoordType;
typedef typename OldMeshType::FaceContainer OldFaceCont;
typedef typename OldMeshType::FaceType OldFaceType;
typedef typename OldMeshType::ScalarType OldScalarType;
//template <class OLD_MESH_TYPE,class NEW_MESH_TYPE>
class Walker : BasicGrid<float>
class Walker : BasicGrid<typename NewMeshType::ScalarType>
{
private:
typedef int VertexIndex;
typedef OLD_MESH_TYPE Old_Mesh;
typedef NEW_MESH_TYPE New_Mesh;
typedef typename New_Mesh::CoordType NewCoordType;
typedef typename New_Mesh::VertexType* VertexPointer;
typedef typename Old_Mesh::FaceContainer FaceCont;
typedef typename vcg::GridStaticPtr<typename Old_Mesh::FaceType> GridType;
typedef typename vcg::GridStaticPtr<OldFaceType, OldScalarType> GridType;
protected:
int SliceSize;
int CurrentSlice;
typedef tri::FaceTmark<Old_Mesh> MarkerFace;
typedef tri::FaceTmark<OldMeshType> MarkerFace;
MarkerFace markerFunctor;
VertexIndex *_x_cs; // indici dell'intersezioni della superficie lungo gli Xedge della fetta corrente
VertexIndex *_y_cs; // indici dell'intersezioni della superficie lungo gli Yedge della fetta corrente
VertexIndex *_z_cs; // indici dell'intersezioni della superficie lungo gli Zedge della fetta corrente
@ -84,21 +87,21 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
field_value* _v_cs;
field_value* _v_ns;
New_Mesh *_newM;
Old_Mesh *_oldM;
NewMeshType *_newM;
OldMeshType *_oldM;
GridType _g;
public:
float max_dim; // the limit value of the search (that takes into account of the offset)
float offset; // an offset value that is always added to the returned value. Useful for extrarting isosurface at a different threshold
NewScalarType max_dim; // the limit value of the search (that takes into account of the offset)
NewScalarType offset; // an offset value that is always added to the returned value. Useful for extrarting isosurface at a different threshold
bool DiscretizeFlag; // if the extracted surface should be discretized or not.
bool MultiSampleFlag;
bool AbsDistFlag; // if true the Distance Field computed is no more a signed one.
Walker(const Box3f &_bbox, Point3i _siz )
Walker(const Box3<NewScalarType> &_bbox, Point3i _siz )
{
this->bbox= _bbox;
this->siz=_siz;
ComputeDimAndVoxel();
this->ComputeDimAndVoxel();
SliceSize = (this->siz.X()+1)*(this->siz.Z()+1);
CurrentSlice = 0;
@ -122,13 +125,13 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
{}
float V(const Point3i &p)
NewScalarType V(const Point3i &p)
{
return V(p.V(0),p.V(1),p.V(2));
}
std::pair<bool,float> VV(int x,int y,int z)
std::pair<bool,NewScalarType> VV(int x,int y,int z)
{
assert ((y==CurrentSlice)||(y==(CurrentSlice+1)));
@ -142,53 +145,48 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
else return _v_ns[index];
}
float V(int x,int y,int z)
NewScalarType V(int x,int y,int z)
{
if(DiscretizeFlag) return VV(x,y,z).second+offset<0?-1:1;
return VV(x,y,z).second+offset;
}
///return true if the distance form the mesh is less than maxdim and return distance
field_value DistanceFromMesh(Point3f &pp,Old_Mesh */*mesh*/)
field_value DistanceFromMesh(OldCoordType &pp)
{
float dist;
typename Old_Mesh::FaceType *f=NULL;
const float max_dist = max_dim;
vcg::Point3f testPt;
OldScalarType dist;
const NewScalarType max_dist = max_dim;
OldCoordType testPt;
this->IPfToPf(pp,testPt);
vcg::Point3f closestNormV,closestNormF;
vcg::Point3f closestPt;
vcg::Point3f pip(-1,-1,-1);
// Note that PointDistanceBaseFunctor does not require the edge and plane precomptued.
// while the PointDistanceFunctor requires them.
OldCoordType closestPt;
DISTFUNCTOR PDistFunct;
f = _g.GetClosest(PDistFunct,markerFunctor,testPt,max_dist,dist,closestPt);
OldFaceType *f = _g.GetClosest(PDistFunct,markerFunctor,testPt,max_dist,dist,closestPt);
if (f==NULL) return field_value(false,0);
if(AbsDistFlag) return field_value(true,dist);
assert(!f->IsD());
bool retIP;
// To compute the interpolated normal we use the more robust function that require to know what is the most orhogonal direction of the face.
OldCoordType pip(-1,-1,-1);
retIP=InterpolationParameters(*f,(*f).cN(),closestPt, pip);
assert(retIP); // this should happen only if the starting mesh has degenerate faces.
const float InterpolationEpsilon = 0.00001f;
const NewScalarType InterpolationEpsilon = 0.00001f;
int zeroCnt=0;
if(pip[0]<InterpolationEpsilon) ++zeroCnt;
if(pip[1]<InterpolationEpsilon) ++zeroCnt;
if(pip[2]<InterpolationEpsilon) ++zeroCnt;
assert(zeroCnt<3);
Point3f dir=(testPt-closestPt).Normalize();
OldCoordType dir=(testPt-closestPt).Normalize();
// Note that the two signs could be discordant.
// Always choose the best one according to where the nearest point falls.
float signBest;
NewScalarType signBest;
// Compute test if the point see the surface normal from inside or outside
// Surface normal for improved robustness is computed both by face and interpolated from vertices.
OldCoordType closestNormV, closestNormF;
if(zeroCnt>0) // we Not are in the middle of the face so the face normal is NOT reliable.
{
closestNormV = (f->V(0)->cN())*pip[0] + (f->V(1)->cN())*pip[1] + (f->V(2)->cN())*pip[2] ;
@ -205,23 +203,23 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
return field_value(true,dist);
}
field_value MultiDistanceFromMesh(Point3f &pp, Old_Mesh */*mesh*/)
field_value MultiDistanceFromMesh(OldCoordType &pp)
{
float distSum=0;
int positiveCnt=0; // positive results counter
const int MultiSample=7;
const Point3f delta[7]={Point3f(0,0,0),
Point3f( 0.2, -0.01, -0.02),
Point3f(-0.2, 0.01, 0.02),
Point3f( 0.01, 0.2, 0.01),
Point3f( 0.03, -0.2, -0.03),
Point3f(-0.02, -0.03, 0.2 ),
Point3f(-0.01, 0.01, -0.2 )};
const OldCoordType delta[7]={OldCoordType(0,0,0),
OldCoordType( 0.2, -0.01, -0.02),
OldCoordType(-0.2, 0.01, 0.02),
OldCoordType( 0.01, 0.2, 0.01),
OldCoordType( 0.03, -0.2, -0.03),
OldCoordType(-0.02, -0.03, 0.2 ),
OldCoordType(-0.01, 0.01, -0.2 )};
for(int qq=0;qq<MultiSample;++qq)
{
Point3f pp2=pp+delta[qq];
field_value ff= DistanceFromMesh(pp2,_oldM);
OldCoordType pp2=pp+delta[qq];
field_value ff= DistanceFromMesh(pp2);
if(ff.first==false) return field_value(false,0);
distSum += fabs(ff.second);
if(ff.second>0) positiveCnt ++;
@ -239,9 +237,9 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
for (int k=0; k<=this->siz.Z(); k++)
{
int index=GetSliceIndex(i,k);
Point3f pp(i,slice,k);
if(this->MultiSampleFlag) slice_values[index] = MultiDistanceFromMesh(pp,_oldM);
else slice_values[index] = DistanceFromMesh(pp,_oldM);
OldCoordType pp(i,slice,k);
if(this->MultiSampleFlag) slice_values[index] = MultiDistanceFromMesh(pp);
else slice_values[index] = DistanceFromMesh(pp);
}
}
//ComputeConsensus(slice,slice_values);
@ -251,7 +249,7 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
For some reasons it can happens that the sign of the computed distance could not correct.
this function tries to correct these issues by flipping the isolated voxels with discordant sign
*/
void ComputeConsensus(int slice, field_value *slice_values)
void ComputeConsensus(int /*slice*/, field_value *slice_values)
{
float max_dist = min(min(this->voxel[0],this->voxel[1]),this->voxel[2]);
int flippedCnt=0;
@ -321,14 +319,14 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
template<class EXTRACTOR_TYPE>
void BuildMesh(Old_Mesh &old_mesh,New_Mesh &new_mesh,EXTRACTOR_TYPE &extractor,vcg::CallBackPos *cb)
void BuildMesh(OldMeshType &old_mesh,NewMeshType &new_mesh,EXTRACTOR_TYPE &extractor,vcg::CallBackPos *cb)
{
_newM=&new_mesh;
_oldM=&old_mesh;
// the following two steps are required to be sure that the point-face distance without precomputed data works well.
tri::UpdateNormal<Old_Mesh>::PerFaceNormalized(old_mesh);
tri::UpdateNormal<Old_Mesh>::PerVertexAngleWeighted(old_mesh);
tri::UpdateNormal<OldMeshType>::PerFaceNormalized(old_mesh);
tri::UpdateNormal<OldMeshType>::PerVertexAngleWeighted(old_mesh);
int _size=(int)old_mesh.fn*100;
_g.Set(_oldM->face.begin(),_oldM->face.end(),_size);
@ -345,11 +343,10 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
NextSlice();
}
extractor.Finalize();
typename New_Mesh::VertexIterator vi;
for(vi=new_mesh.vert.begin();vi!=new_mesh.vert.end();++vi)
for(NewVertexIterator vi=new_mesh.vert.begin();vi!=new_mesh.vert.end();++vi)
if(!(*vi).IsD())
{
IPfToPf((*vi).cP(),(*vi).P());
this->IPfToPf((*vi).cP(),(*vi).P());
}
}
@ -398,7 +395,7 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
bool Exist(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
bool Exist(const vcg::Point3i &p1, const vcg::Point3i &p2, NewVertexPointer &v)
{
int i = p1.X();// - _bbox.min.X())/_cell_size.X();
int z = p1.Z();// - _bbox.min.Z())/_cell_size.Z();
@ -482,16 +479,16 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
///interpolate
NewCoordType Interpolate(const vcg::Point3i &p1, const vcg::Point3i &p2,int dir)
{
float f1 = (float)V(p1);
float f2 = (float)V(p2);
float u = (float) f1/(f1-f2);
NewCoordType ret=vcg::Point3f((float)p1.V(0),(float)p1.V(1),(float)p1.V(2));
ret.V(dir) = (float) p1.V(dir)*(1.f-u) + u*(float)p2.V(dir);
NewScalarType f1 = V(p1);
NewScalarType f2 = V(p2);
NewScalarType u = f1/(f1-f2);
NewCoordType ret(p1.V(0),p1.V(1),p1.V(2));
ret.V(dir) = p1.V(dir)*(1.f-u) + u*p2.V(dir);
return (ret);
}
///if there is a vertex in z axis of a cell return the vertex or create it
void GetXIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
void GetXIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, NewVertexPointer &v)
{
assert(p1.X()+1 == p2.X());
assert(p1.Y() == p2.Y());
@ -507,7 +504,7 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
{
_x_cs[index] = (VertexIndex) _newM->vert.size();
pos = _x_cs[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1 );
Allocator<NewMeshType>::AddVertices( *_newM, 1 );
v = &_newM->vert[pos];
v->P()=Interpolate(p1,p2,0);
return;
@ -519,7 +516,7 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
{
_x_ns[index] = (VertexIndex) _newM->vert.size();
pos = _x_ns[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1 );
Allocator<NewMeshType>::AddVertices( *_newM, 1 );
v = &_newM->vert[pos];
v->P()=Interpolate(p1,p2,0);
return;
@ -530,7 +527,7 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
}
///if there is a vertex in y axis of a cell return the vertex or create it
void GetYIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
void GetYIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, NewVertexPointer &v)
{
assert(p1.X() == p2.X());
assert(p1.Y()+1 == p2.Y());
@ -544,7 +541,7 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
{
_y_cs[index] = (VertexIndex) _newM->vert.size();
pos = _y_cs[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1);
Allocator<NewMeshType>::AddVertices( *_newM, 1);
v = &_newM->vert[ pos ];
v->P()=Interpolate(p1,p2,1);
}
@ -553,7 +550,7 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
}
///if there is a vertex in z axis of a cell return the vertex or create it
void GetZIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, VertexPointer &v)
void GetZIntercept(const vcg::Point3i &p1, const vcg::Point3i &p2, NewVertexPointer &v)
{
assert(p1.X() == p2.X());
assert(p1.Y() == p2.Y());
@ -570,7 +567,7 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
{
_z_cs[index] = (VertexIndex) _newM->vert.size();
pos = _z_cs[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1 );
Allocator<NewMeshType>::AddVertices( *_newM, 1 );
v = &_newM->vert[pos];
v->P()=Interpolate(p1,p2,2);
return;
@ -582,7 +579,7 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
{
_z_ns[index] = (VertexIndex) _newM->vert.size();
pos = _z_ns[index];
Allocator<New_Mesh>::AddVertices( *_newM, 1 );
Allocator<NewMeshType>::AddVertices( *_newM, 1 );
v = &_newM->vert[pos];
v->P()=Interpolate(p1,p2,2);
return;
@ -596,15 +593,15 @@ template <class OLD_MESH_TYPE,class NEW_MESH_TYPE, class FLT, class DISTFUNCTOR
public:
typedef Walker /*< Old_Mesh,New_Mesh>*/ MyWalker;
typedef Walker /*< Old_Mesh,New_Mesh>*/ MyWalker;
typedef vcg::tri::MarchingCubes<New_Mesh, MyWalker> MyMarchingCubes;
typedef vcg::tri::MarchingCubes<NewMeshType, MyWalker> MyMarchingCubes;
///resample the mesh using marching cube algorithm ,the accuracy is the dimension of one cell the parameter
static void Resample(Old_Mesh &old_mesh,New_Mesh &new_mesh, Box3f volumeBox, vcg::Point3<int> accuracy,float max_dist, float thr=0, bool DiscretizeFlag=false, bool MultiSampleFlag=false, bool AbsDistFlag=false, vcg::CallBackPos *cb=0 )
{
///resample the mesh using marching cube algorithm ,the accuracy is the dimension of one cell the parameter
static void Resample(OldMeshType &old_mesh, NewMeshType &new_mesh, NewBoxType volumeBox, vcg::Point3<int> accuracy,float max_dist, float thr=0, bool DiscretizeFlag=false, bool MultiSampleFlag=false, bool AbsDistFlag=false, vcg::CallBackPos *cb=0 )
{
///be sure that the bounding box is updated
vcg::tri::UpdateBounding<Old_Mesh>::Box(old_mesh);
vcg::tri::UpdateBounding<OldMeshType>::Box(old_mesh);
MyWalker walker(volumeBox,accuracy);
@ -615,11 +612,11 @@ static void Resample(Old_Mesh &old_mesh,New_Mesh &new_mesh, Box3f volumeBox, vc
walker.AbsDistFlag = AbsDistFlag;
MyMarchingCubes mc(new_mesh, walker);
walker.BuildMesh(old_mesh,new_mesh,mc,cb);
}
}
};//end class resampler
};//end namespace tri
};//end namespace vcg
}//end namespace tri
}//end namespace vcg
#endif