Skip to content

Commit 205cd9b

Browse files
authored
Merge pull request #675 from nmellado/topologicalmesh-nonmanifold-functor
First attempt to expose a functor to handle non-manifold elements during conversion between Core::TriangleMesh and Core::TopologicalMesh
2 parents 2600039 + 4460ec0 commit 205cd9b

4 files changed

Lines changed: 425 additions & 240 deletions

File tree

src/Core/Geometry/TopologicalMesh.cpp

Lines changed: 21 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ namespace Geometry {
1515

1616
using namespace Utils; // log, AttribXXX
1717

18-
template <typename T>
19-
using PropPair = std::pair<AttribHandle<T>, OpenMesh::HPropHandleT<T>>;
20-
2118
///////////////// HELPERS ///////////////
2219

2320
std::string wedgeInfo( const Ra::Core::Geometry::TopologicalMesh& topo,
@@ -127,56 +124,23 @@ void printWedgesInfo( const Ra::Core::Geometry::TopologicalMesh& topo ) {
127124
}
128125
}
129126

130-
template <typename T>
131-
void addAttribPairToTopo( const TriangleMesh& triMesh,
132-
TopologicalMesh* topoMesh,
133-
AttribManager::pointer_type attr,
134-
std::vector<PropPair<T>>& vprop,
135-
std::vector<OpenMesh::HPropHandleT<T>>& pph ) {
136-
AttribHandle<T> h = triMesh.getAttribHandle<T>( attr->getName() );
137-
if ( attr->getSize() == triMesh.vertices().size() )
138-
{
139-
OpenMesh::HPropHandleT<T> oh;
140-
topoMesh->add_property( oh, attr->getName() );
141-
vprop.push_back( std::make_pair( h, oh ) );
142-
pph.push_back( oh );
143-
}
144-
else
145-
{
146-
LOG( logWARNING ) << "[TopologicalMesh] Skip badly sized attribute " << attr->getName()
147-
<< ".";
148-
}
149-
}
150-
151-
template <typename T>
127+
template <typename P, typename T>
152128
void addAttribPairToCore( TriangleMesh& triMesh,
153129
const TopologicalMesh* topoMesh,
154130
OpenMesh::HPropHandleT<T> oh,
155-
std::vector<PropPair<T>>& vprop ) {
131+
std::vector<P>& vprop ) {
156132
AttribHandle<T> h = triMesh.addAttrib<T>( topoMesh->property( oh ).name() );
157133
vprop.push_back( std::make_pair( h, oh ) );
158134
}
159135

160-
template <typename T>
161-
void copyAttribToTopo( const TriangleMesh& triMesh,
162-
TopologicalMesh* topoMesh,
163-
const std::vector<PropPair<T>>& vprop,
164-
TopologicalMesh::HalfedgeHandle heh,
165-
unsigned int vindex ) {
166-
for ( auto pp : vprop )
167-
{
168-
topoMesh->property( pp.second, heh ) = triMesh.getAttrib( pp.first ).data()[vindex];
169-
}
170-
}
171-
172136
template <typename T>
173137
using HandleAndValueVector = std::vector<std::pair<AttribHandle<T>, T>,
174138
Eigen::aligned_allocator<std::pair<AttribHandle<T>, T>>>;
175139

176-
template <typename T>
140+
template <typename P, typename T>
177141
void copyAttribToCoreVertex( HandleAndValueVector<T>& data,
178142
const TopologicalMesh* topoMesh,
179-
const std::vector<PropPair<T>>& vprop,
143+
const std::vector<P>& vprop,
180144
TopologicalMesh::HalfedgeHandle heh ) {
181145
for ( auto pp : vprop )
182146
{
@@ -196,197 +160,25 @@ void copyAttribToCore( TriangleMesh& triMesh, const HandleAndValueVector<T>& dat
196160
}
197161
}
198162

199-
template <typename T>
200-
void copyAttribToWedgeData( const TriangleMesh& mesh,
201-
unsigned int vindex,
202-
const std::vector<AttribHandle<T>>& attrHandleVec,
203-
VectorArray<T>* to ) {
204-
for ( auto handle : attrHandleVec )
205-
{
206-
auto& attr = mesh.getAttrib<T>( handle );
207-
to->push_back( attr.data()[vindex] );
163+
//! [Default command implementation]
164+
struct DefaultNonManifoldFaceCommand {
165+
/// \brief Initalize with input Ra::Core::Geometry::TriangleMesh
166+
inline void initialize( const TriangleMesh& /*triMesh*/ ) {}
167+
/// \brief Process non-manifold face
168+
inline void process( const std::vector<TopologicalMesh::VertexHandle>& /*face_vhandles*/ ) {
169+
LOG( logWARNING ) << "Invalid face handle returned : face not added (1)";
170+
/// TODO memorize invalid faces for post processing ...
171+
/// see
172+
/// https://www.graphics.rwth-aachen.de/media/openflipper_static/Daily-Builds/Doc/Free/Developer/OBJImporter_8cc_source.html
173+
/// for an exemple of loading
208174
}
209-
}
210-
211-
void copyMeshToWedgeData( const TriangleMesh& mesh,
212-
unsigned int vindex,
213-
const std::vector<AttribHandle<float>>& wprop_float,
214-
const std::vector<AttribHandle<Vector2>>& wprop_vec2,
215-
const std::vector<AttribHandle<Vector3>>& wprop_vec3,
216-
const std::vector<AttribHandle<Vector4>>& wprop_vec4,
217-
TopologicalMesh::WedgeData* wd ) {
218-
219-
copyAttribToWedgeData( mesh, vindex, wprop_float, &wd->m_floatAttrib );
220-
copyAttribToWedgeData( mesh, vindex, wprop_vec2, &wd->m_vector2Attrib );
221-
copyAttribToWedgeData( mesh, vindex, wprop_vec3, &wd->m_vector3Attrib );
222-
copyAttribToWedgeData( mesh, vindex, wprop_vec4, &wd->m_vector4Attrib );
223-
}
224-
225-
TopologicalMesh::TopologicalMesh( const TriangleMesh& triMesh ) {
226-
// initWithWedge( triMesh );
227-
// return;
175+
/// \brief If needed, apply post-processing on the Ra::Core::Geometry::TopologicalMesh
176+
inline void postProcess( TopologicalMesh& /*tm*/ ) {}
177+
//! [Default command implementation]
178+
};
228179

229-
LOG( logINFO ) << "TopologicalMesh: load triMesh with " << triMesh.getIndices().size()
230-
<< " faces and " << triMesh.vertices().size() << " vertices.";
231-
232-
struct hash_vec {
233-
size_t operator()( const Vector3& lvalue ) const {
234-
size_t hx = std::hash<Scalar>()( lvalue[0] );
235-
size_t hy = std::hash<Scalar>()( lvalue[1] );
236-
size_t hz = std::hash<Scalar>()( lvalue[2] );
237-
return ( hx ^ ( hy << 1 ) ) ^ hz;
238-
}
239-
};
240-
// use a hashmap for fast search of existing vertex position
241-
using VertexMap = std::unordered_map<Vector3, TopologicalMesh::VertexHandle, hash_vec>;
242-
VertexMap vertexHandles;
243-
244-
add_property( m_inputTriangleMeshIndexPph );
245-
246-
add_property( m_wedgeIndexPph );
247-
248-
std::vector<PropPair<float>> vprop_float;
249-
std::vector<PropPair<Vector2>> vprop_vec2;
250-
std::vector<PropPair<Vector3>> vprop_vec3;
251-
std::vector<PropPair<Vector4>> vprop_vec4;
252-
253-
// loop over all attribs and build correspondance pair
254-
triMesh.vertexAttribs().for_each_attrib(
255-
[&triMesh, this, &vprop_float, &vprop_vec2, &vprop_vec3, &vprop_vec4]( const auto& attr ) {
256-
// skip builtin attribs
257-
if ( attr->getName() != std::string( "in_position" ) &&
258-
attr->getName() != std::string( "in_normal" ) )
259-
{
260-
if ( attr->isFloat() )
261-
addAttribPairToTopo( triMesh, this, attr, vprop_float, m_floatPph );
262-
else if ( attr->isVector2() )
263-
addAttribPairToTopo( triMesh, this, attr, vprop_vec2, m_vec2Pph );
264-
else if ( attr->isVector3() )
265-
addAttribPairToTopo( triMesh, this, attr, vprop_vec3, m_vec3Pph );
266-
else if ( attr->isVector4() )
267-
addAttribPairToTopo( triMesh, this, attr, vprop_vec4, m_vec4Pph );
268-
else
269-
LOG( logWARNING )
270-
<< "Warning, mesh attribute " << attr->getName()
271-
<< " type is not supported (only float, vec2, vec3 nor vec4 are supported)";
272-
}
273-
} );
274-
// loop over all attribs and build correspondance pair
275-
triMesh.vertexAttribs().for_each_attrib(
276-
[&triMesh, this]( const auto& attr ) {
277-
if ( attr->getSize() != triMesh.vertices().size() )
278-
{
279-
LOG( logWARNING ) << "[TopologicalMesh] Skip badly sized attribute "
280-
<< attr->getName();
281-
}
282-
else if ( attr->getName() != std::string( "in_position" ) )
283-
{
284-
if ( attr->isFloat() )
285-
{
286-
m_wedges.m_wedgeFloatAttribHandles.push_back(
287-
triMesh.getAttribHandle<float>( attr->getName() ) );
288-
m_wedges.addProp<float>( attr->getName() );
289-
}
290-
else if ( attr->isVector2() )
291-
{
292-
m_wedges.m_wedgeVector2AttribHandles.push_back(
293-
triMesh.getAttribHandle<Vector2>( attr->getName() ) );
294-
m_wedges.addProp<Vector2>( attr->getName() );
295-
}
296-
else if ( attr->isVector3() )
297-
{
298-
m_wedges.m_wedgeVector3AttribHandles.push_back(
299-
triMesh.getAttribHandle<Vector3>( attr->getName() ) );
300-
m_wedges.addProp<Vector3>( attr->getName() );
301-
}
302-
else if ( attr->isVector4() )
303-
{
304-
m_wedges.m_wedgeVector4AttribHandles.push_back(
305-
triMesh.getAttribHandle<Vector4>( attr->getName() ) );
306-
m_wedges.addProp<Vector4>( attr->getName() );
307-
}
308-
else
309-
LOG( logWARNING )
310-
<< "Warning, mesh attribute " << attr->getName()
311-
<< " type is not supported (only float, vec2, vec3 nor vec4 are supported)";
312-
}
313-
} );
314-
315-
size_t num_triangles = triMesh.getIndices().size();
316-
317-
for ( unsigned int i = 0; i < num_triangles; i++ )
318-
{
319-
std::vector<TopologicalMesh::VertexHandle> face_vhandles( 3 );
320-
std::vector<TopologicalMesh::Normal> face_normals( 3 );
321-
std::vector<unsigned int> face_vertexIndex( 3 );
322-
std::vector<WedgeIndex> face_wedges( 3 );
323-
const auto& triangle = triMesh.getIndices()[i];
324-
for ( size_t j = 0; j < 3; ++j )
325-
{
326-
unsigned int inMeshVertexIndex = triangle[j];
327-
const Vector3& p = triMesh.vertices()[inMeshVertexIndex];
328-
const Vector3& n = triMesh.normals()[inMeshVertexIndex];
329-
330-
VertexMap::iterator vtr = vertexHandles.find( p );
331-
332-
TopologicalMesh::VertexHandle vh;
333-
if ( vtr == vertexHandles.end() )
334-
{
335-
vh = add_vertex( p );
336-
vertexHandles.insert( vtr, VertexMap::value_type( p, vh ) );
337-
}
338-
else
339-
{ vh = vtr->second; }
340-
341-
face_vhandles[j] = vh;
342-
face_normals[j] = n;
343-
face_vertexIndex[j] = inMeshVertexIndex;
344-
WedgeData wd;
345-
wd.m_position = p;
346-
347-
copyMeshToWedgeData( triMesh,
348-
inMeshVertexIndex,
349-
m_wedges.m_wedgeFloatAttribHandles,
350-
m_wedges.m_wedgeVector2AttribHandles,
351-
m_wedges.m_wedgeVector3AttribHandles,
352-
m_wedges.m_wedgeVector4AttribHandles,
353-
&wd );
354-
355-
face_wedges[j] = m_wedges.add( wd );
356-
}
357-
358-
// Add the face, then add attribs to vh
359-
auto fh = add_face( face_vhandles );
360-
// In case of topological inconsistancy, face will be invalid ...
361-
if ( fh.is_valid() )
362-
{
363-
for ( size_t vindex = 0; vindex < face_vhandles.size(); vindex++ )
364-
{
365-
TopologicalMesh::HalfedgeHandle heh = halfedge_handle( face_vhandles[vindex], fh );
366-
set_normal( heh, face_normals[vindex] );
367-
property( m_inputTriangleMeshIndexPph, heh ) = face_vertexIndex[vindex];
368-
copyAttribToTopo( triMesh, this, vprop_float, heh, face_vertexIndex[vindex] );
369-
copyAttribToTopo( triMesh, this, vprop_vec2, heh, face_vertexIndex[vindex] );
370-
copyAttribToTopo( triMesh, this, vprop_vec3, heh, face_vertexIndex[vindex] );
371-
copyAttribToTopo( triMesh, this, vprop_vec4, heh, face_vertexIndex[vindex] );
372-
property( m_wedgeIndexPph, heh ) = face_wedges[vindex];
373-
}
374-
}
375-
else
376-
{
377-
LOG( logWARNING ) << "Invalid face handle returned : face not added (1)";
378-
// TODO memorize invalid faces for post processing ...
379-
// see
380-
// https://www.graphics.rwth-aachen.de/media/openflipper_static/Daily-Builds/Doc/Free/Developer/OBJImporter_8cc_source.html
381-
// for an exemple of loading
382-
}
383-
face_vhandles.clear();
384-
face_normals.clear();
385-
face_vertexIndex.clear();
386-
}
387-
// LOG( logINFO ) << "TopologicalMesh: load end with " << m_wedges.size() << " wedges ";
388-
// printWedgesInfo( *this );
389-
}
180+
TopologicalMesh::TopologicalMesh( const TriangleMesh& triMesh ) :
181+
TopologicalMesh( triMesh, DefaultNonManifoldFaceCommand() ) {}
390182

391183
void TopologicalMesh::initWithWedge( const TriangleMesh& triMesh ) {
392184

src/Core/Geometry/TopologicalMesh.hpp

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,19 @@ class RA_CORE_API TopologicalMesh : public OpenMesh::PolyMesh_ArrayKernelT<Topol
6969
* attributes on halfedges, so that TriangleMesh vertices with the same 3D
7070
* position are represented only once in the topological mesh.
7171
* \note This is a costly operation.
72+
*
73+
* \tparam NonManifoldFaceCommand Command executed when non-manifold faces are
74+
* found. API and default implementation:
75+
* \snippet Core/Geometry/TopologicalMesh.cpp Default command implementation
76+
*
77+
*/
78+
template <typename NonManifoldFaceCommand>
79+
explicit TopologicalMesh( const Ra::Core::Geometry::TriangleMesh& triMesh,
80+
NonManifoldFaceCommand command );
81+
82+
/**
83+
* \brief Convenience constructor
84+
* \see TopologicalMesh( const Ra::Core::Geometry::TriangleMesh&, NonManifoldFaceCommand)
7285
*/
7386
explicit TopologicalMesh( const Ra::Core::Geometry::TriangleMesh& triMesh );
7487
void initWithWedge( const Ra::Core::Geometry::TriangleMesh& triMesh );
@@ -464,8 +477,13 @@ class RA_CORE_API TopologicalMesh : public OpenMesh::PolyMesh_ArrayKernelT<Topol
464477

465478
private:
466479
class WedgeCollection;
467-
// wedge data and refcount, to maintain deleted status
468-
class Wedge
480+
//
481+
/**
482+
* This private class manage wedge data and refcount, to maintain deleted status
483+
*
484+
* \internal We need to export this class to make it accessible in .inl
485+
*/
486+
class RA_CORE_API Wedge
469487
{
470488
WedgeData m_wedgeData {};
471489
unsigned int m_refCount {0};
@@ -498,8 +516,10 @@ class RA_CORE_API TopologicalMesh : public OpenMesh::PolyMesh_ArrayKernelT<Topol
498516
* This private class manage the wedge collection.
499517
* Most of the data members are public so that the enclosing class can
500518
* easily manage the data.
519+
*
520+
* \internal We need to export this class to make it accessible in .inl
501521
*/
502-
class WedgeCollection
522+
class RA_CORE_API WedgeCollection
503523
{
504524
public:
505525
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
@@ -591,6 +611,40 @@ class RA_CORE_API TopologicalMesh : public OpenMesh::PolyMesh_ArrayKernelT<Topol
591611

592612
WedgeData interpolateWedgeAttributes( const WedgeData&, const WedgeData&, Scalar alpha );
593613

614+
template <typename T>
615+
inline void copyAttribToWedgeData( const TriangleMesh& mesh,
616+
unsigned int vindex,
617+
const std::vector<AttribHandle<T>>& attrHandleVec,
618+
VectorArray<T>* to );
619+
620+
inline void copyMeshToWedgeData( const TriangleMesh& mesh,
621+
unsigned int vindex,
622+
const std::vector<AttribHandle<float>>& wprop_float,
623+
const std::vector<AttribHandle<Vector2>>& wprop_vec2,
624+
const std::vector<AttribHandle<Vector3>>& wprop_vec3,
625+
const std::vector<AttribHandle<Vector4>>& wprop_vec4,
626+
TopologicalMesh::WedgeData* wd );
627+
628+
template <typename T>
629+
using HandleAndValueVector =
630+
std::vector<std::pair<AttribHandle<T>, T>,
631+
Eigen::aligned_allocator<std::pair<AttribHandle<T>, T>>>;
632+
633+
template <typename T>
634+
using PropPair = std::pair<AttribHandle<T>, OpenMesh::HPropHandleT<T>>;
635+
636+
template <typename T>
637+
inline void copyAttribToTopo( const TriangleMesh& triMesh,
638+
const std::vector<PropPair<T>>& vprop,
639+
TopologicalMesh::HalfedgeHandle heh,
640+
unsigned int vindex );
641+
642+
template <typename T>
643+
inline void addAttribPairToTopo( const TriangleMesh& triMesh,
644+
AttribManager::pointer_type attr,
645+
std::vector<PropPair<T>>& vprop,
646+
std::vector<OpenMesh::HPropHandleT<T>>& pph );
647+
594648
void split_copy( EdgeHandle _eh, VertexHandle _vh );
595649
void split( EdgeHandle _eh, VertexHandle _vh );
596650

0 commit comments

Comments
 (0)