Skip to content

Commit

Permalink
Configurable makeSignedByWindingNumber (#3288)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fedr authored Sep 1, 2024
1 parent 14b09f1 commit 7feddb8
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 45 deletions.
23 changes: 18 additions & 5 deletions source/MRMesh/MROffset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,15 @@ Expected<Mesh> offsetMesh( const MeshPart & mp, float offset, const OffsetParame

if ( signPostprocess )
{
// Compute signs for initially unsigned distance field
auto sp = subprogress( params.callBack, 0.33f, 0.66f );
auto signRes = makeSignedWithFastWinding( grid, Vector3f::diagonal( voxelSize ), mp.mesh, {}, params.fwn, sp );
auto signRes = makeSignedByWindingNumber( grid, Vector3f::diagonal( voxelSize ), mp.mesh,
{
.fwn = params.fwn,
.windingNumberThreshold = params.windingNumberThreshold,
.windingNumberBeta = params.windingNumberBeta,
.progress = subprogress( params.callBack, 0.33f, 0.66f )
} );
if ( !signRes.has_value() )
return unexpected( signRes.error() );
return unexpected( std::move( signRes.error() ) );
}

// Make offset mesh
Expand All @@ -101,7 +105,16 @@ Expected<Mesh> doubleOffsetMesh( const MeshPart& mp, float offsetA, float offset
{
spdlog::warn( "Cannot use shell for double offset, using offset mode instead." );
}
return levelSetDoubleConvertion( mp, params.voxelSize, offsetA, offsetB, 0, params.fwn, params.callBack );
return doubleOffsetVdb( mp,
{
.voxelSize = params.voxelSize,
.offsetA = offsetA,
.offsetB = offsetB,
.fwn = params.fwn,
.windingNumberThreshold = params.windingNumberThreshold,
.windingNumberBeta = params.windingNumberBeta,
.progress = params.callBack
} );
}
#endif

Expand Down
55 changes: 29 additions & 26 deletions source/MRMesh/MRVDBConversions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ Expected<Mesh> gridToMesh( FloatGrid&& grid, const GridToMeshSettings & settings
return res;
}

VoidOrErrStr makeSignedWithFastWinding( FloatGrid& grid, const Vector3f& voxelSize, const Mesh& refMesh, const AffineXf3f& meshToGridXf, std::shared_ptr<IFastWindingNumber> fwn, ProgressCallback cb /*= {} */ )
VoidOrErrStr makeSignedByWindingNumber( FloatGrid& grid, const Vector3f& voxelSize, const Mesh& refMesh, const MakeSignedByWindingNumberSettings & settings )
{
MR_TIMER

Expand All @@ -425,15 +425,14 @@ VoidOrErrStr makeSignedWithFastWinding( FloatGrid& grid, const Vector3f& voxelSi
const size_t volume = activeBox.volume();

std::vector<float> windVals;
if ( !fwn )
fwn = std::make_shared<FastWindingNumber>( refMesh );
auto fwn = settings.fwn ? settings.fwn : std::make_shared<FastWindingNumber>( refMesh );

const auto gridToMeshXf = meshToGridXf.inverse()
const auto gridToMeshXf = settings.meshToGridXf.inverse()
* AffineXf3f::linear( Matrix3f::scale( voxelSize ) )
* AffineXf3f::translation( Vector3f{ float( minCoord.x() ), float( minCoord.y() ), float( minCoord.z() ) } );
* AffineXf3f::translation( Vector3f{ fromVdb( minCoord ) } );
if ( auto res = fwn->calcFromGrid( windVals,
Vector3i{ dims.x(), dims.y(), dims.z() },
gridToMeshXf, 2.0f, subprogress( cb, 0.0f, 0.8f ) ); !res )
gridToMeshXf, settings.windingNumberBeta, subprogress( settings.progress, 0.0f, 0.8f ) ); !res )
{
return res;
}
Expand All @@ -449,7 +448,7 @@ VoidOrErrStr makeSignedWithFastWinding( FloatGrid& grid, const Vector3f& voxelSi
for ( int j = 0; j < 3; ++j )
coord[j] += pos[j];

auto windVal = std::clamp( 1.0f - 2.0f * windVals[i], -1.0f, 1.0f );
auto windVal = std::clamp( 2 * ( settings.windingNumberThreshold - windVals[i] ), -1.0f, 1.0f );
if ( windVal < 0.0f )
windVal *= -windVal;
else
Expand All @@ -458,7 +457,7 @@ VoidOrErrStr makeSignedWithFastWinding( FloatGrid& grid, const Vector3f& voxelSi
{
val *= windVal;
} );
}, subprogress( cb, 0.8f, 1.0f ) ) )
}, subprogress( settings.progress, 0.8f, 1.0f ) ) )
{
return unexpectedOperationCanceled();
}
Expand All @@ -467,28 +466,27 @@ VoidOrErrStr makeSignedWithFastWinding( FloatGrid& grid, const Vector3f& voxelSi
return {};
}

Expected<Mesh> levelSetDoubleConvertion( const MeshPart& mp, float voxelSize,
float offsetA, float offsetB, float adaptivity, std::shared_ptr<IFastWindingNumber> fwn, ProgressCallback cb /*= {} */ )
Expected<Mesh> doubleOffsetVdb( const MeshPart& mp, const DoubleOffsetSettings & settings )
{
MR_TIMER

auto offsetInVoxelsA = offsetA / voxelSize;
auto offsetInVoxelsB = offsetB / voxelSize;
auto offsetInVoxelsA = settings.offsetA / settings.voxelSize;
auto offsetInVoxelsB = settings.offsetB / settings.voxelSize;

if ( !reportProgress( cb, 0.0f ) )
if ( !reportProgress( settings.progress, 0.0f ) )
return unexpectedOperationCanceled();

std::vector<openvdb::Vec3s> points;
std::vector<openvdb::Vec3I> tris;
std::vector<openvdb::Vec4I> quads;
convertToVDMMesh( mp, AffineXf3f(), Vector3f::diagonal( voxelSize ), points, tris );
convertToVDMMesh( mp, AffineXf3f(), Vector3f::diagonal( settings.voxelSize ), points, tris );

if ( !reportProgress( cb, 0.1f ) )
if ( !reportProgress( settings.progress, 0.1f ) )
return unexpectedOperationCanceled();

const bool needSignUpdate = !mp.mesh.topology.isClosed( mp.region );

auto sp = subprogress( cb, 0.1f, needSignUpdate ? 0.2f : 0.3f );
auto sp = subprogress( settings.progress, 0.1f, needSignUpdate ? 0.2f : 0.3f );
openvdb::math::Transform::Ptr xform = openvdb::math::Transform::createLinearTransform();
ProgressInterrupter interrupter1( sp );
auto grid = MakeFloatGrid(
Expand All @@ -503,34 +501,39 @@ Expected<Mesh> levelSetDoubleConvertion( const MeshPart& mp, float voxelSize,

if ( needSignUpdate )
{
sp = subprogress( cb, 0.2f, 0.3f );
auto signRes = makeSignedWithFastWinding( grid, Vector3f::diagonal(voxelSize), mp.mesh, {}, fwn, sp );
auto signRes = makeSignedByWindingNumber( grid, Vector3f::diagonal( settings.voxelSize ), mp.mesh,
{
.fwn = settings.fwn,
.windingNumberThreshold = settings.windingNumberThreshold,
.windingNumberBeta = settings.windingNumberBeta,
.progress = subprogress( settings.progress, 0.2f, 0.3f )
} );
if ( !signRes.has_value() )
return unexpected( signRes.error() );
}

openvdb::tools::volumeToMesh( *grid, points, tris, quads, offsetInVoxelsA, adaptivity );
openvdb::tools::volumeToMesh( *grid, points, tris, quads, offsetInVoxelsA, settings.adaptivity );

if ( !reportProgress( cb, 0.5f ) )
if ( !reportProgress( settings.progress, 0.5f ) )
return unexpectedOperationCanceled();
sp = subprogress( cb, 0.5f, 0.7f );
sp = subprogress( settings.progress, 0.5f, 0.7f );

ProgressInterrupter interrupter2( sp );
grid = MakeFloatGrid( openvdb::tools::meshToLevelSet<openvdb::FloatGrid, ProgressInterrupter>
( interrupter2, *xform, points, tris, quads, std::abs( offsetInVoxelsB ) + 1 ) );

if ( interrupter2.getWasInterrupted() || !reportProgress( cb, 0.9f ) )
if ( interrupter2.getWasInterrupted() || !reportProgress( settings.progress, 0.9f ) )
return unexpectedOperationCanceled();

auto expTriMesh = gridToTriMesh( *grid, GridToMeshSettings{
.voxelSize = Vector3f::diagonal( voxelSize ),
.voxelSize = Vector3f::diagonal( settings.voxelSize ),
.isoValue = offsetInVoxelsB,
.adaptivity = adaptivity,
.cb = subprogress( cb, 0.9f, 0.95f )
.adaptivity = settings.adaptivity,
.cb = subprogress( settings.progress, 0.9f, 0.95f )
} );

Mesh res = Mesh::fromTriMesh( std::move( *expTriMesh ) );
if ( !reportProgress( cb, 1.0f ) )
if ( !reportProgress( settings.progress, 1.0f ) )
return unexpectedOperationCanceled();
return res;
}
Expand Down
70 changes: 56 additions & 14 deletions source/MRMesh/MRVDBConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,61 @@ MRMESH_API Expected<Mesh> gridToMesh( const FloatGrid& grid, const GridToMeshSet
/// deletes grid in the middle to reduce peak memory consumption
MRMESH_API Expected<Mesh> gridToMesh( FloatGrid&& grid, const GridToMeshSettings & settings );

struct MakeSignedByWindingNumberSettings
{
/// defines the mapping from mesh reference from to grid reference frame
AffineXf3f meshToGridXf;

/// defines particular implementation of IFastWindingNumber interface that will compute windings. If it is not specified, default FastWindingNumber is used
std::shared_ptr<IFastWindingNumber> fwn;

/// positive distance if winding number below or equal this threshold;
/// ideal threshold: 0.5 for closed meshes; 0.0 for planar meshes
float windingNumberThreshold = 0.5f;

/// determines the precision of fast approximation: the more the better, minimum value is 1
float windingNumberBeta = 2;

/// to report algorithm's progress and to cancel it
ProgressCallback progress;
};

/// set signs for unsigned distance field grid using refMesh FastWindingNumber;
/// \param meshToGridXf defines the mapping from mesh reference from to grid reference frame
/// \param fwn defines particular implementation of IFastWindingNumber interface that will compute windings. If it is not specified, default FastWindingNumber is used
MRMESH_API VoidOrErrStr makeSignedWithFastWinding( FloatGrid& grid, const Vector3f& voxelSize, const Mesh& refMesh,
const AffineXf3f& meshToGridXf = {}, std::shared_ptr<IFastWindingNumber> fwn = {}, ProgressCallback cb = {} );

// performs convention from mesh to levelSet and back with offsetA, and than same with offsetB
// allowed only for closed meshes
// adaptivity - [0.0;1.0] ratio of combining small triangles into bigger ones
// (curvature can be lost on high values)
/// \param fwn defines particular implementation of IFastWindingNumber interface that will compute windings. If it is not specified, default FastWindingNumber is used
MRMESH_API Expected<Mesh> levelSetDoubleConvertion( const MeshPart& mp,
float voxelSize, float offsetA, float offsetB, float adaptivity = 0.0f, std::shared_ptr<IFastWindingNumber> fwn = {}, ProgressCallback cb = {} );

}
/// not only the sign but the magnitudes of distances in the grid are changed to make smooth transition from positive to negative
MRMESH_API VoidOrErrStr makeSignedByWindingNumber( FloatGrid& grid, const Vector3f& voxelSize, const Mesh& refMesh,
const MakeSignedByWindingNumberSettings & settings );

struct DoubleOffsetSettings
{
/// the size of voxel in intermediate voxel grid representation
float voxelSize = 0;

/// the amount of first offset
float offsetA = 0;

/// the amount of second offset
float offsetB = 0;

/// in [0; 1] - ratio of combining small triangles into bigger ones (curvature can be lost on high values)
float adaptivity = 0;

/// defines particular implementation of IFastWindingNumber interface that will compute windings. If it is not specified, default FastWindingNumber is used
std::shared_ptr<IFastWindingNumber> fwn;

/// positive distance if winding number below or equal this threshold;
/// ideal threshold: 0.5 for closed meshes; 0.0 for planar meshes
float windingNumberThreshold = 0.5f;

/// determines the precision of fast approximation: the more the better, minimum value is 1
float windingNumberBeta = 2;

/// to report algorithm's progress and to cancel it
ProgressCallback progress;
};

/// performs convention from mesh to voxel grid and back with offsetA, and than same with offsetB;
/// if input mesh is not closed then the sign of distance field will be obtained using generalized winding number computation
MRMESH_API Expected<Mesh> doubleOffsetVdb( const MeshPart& mp, const DoubleOffsetSettings & settings );

} //namespace MR
#endif

0 comments on commit 7feddb8

Please sign in to comment.