C++ demo for "Winding Numbers on Discrete Surfaces" by Nicole Feng, Mark Gillespie, and Keenan Crane, presented at SIGGRAPH 2023.
Paper PDF (4.4mb): link
Project page with links to paper, pseudocode, supplementals, & videos: link
SIGGRAPH talk (10 minutes): link
If this code contributes to academic work, please cite as:
@article{Feng:2023:WND,
author = {Feng, Nicole and Gillespie, Mark and Crane, Keenan},
title = {Winding Numbers on Discrete Surfaces},
year = {2023},
issue_date = {August 2023},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
volume = {42},
number = {4},
issn = {0730-0301},
url = {https://doi.org/10.1145/3592401},
doi = {10.1145/3592401},
journal = {ACM Trans. Graph.},
month = {jul},
articleno = {36}
}
The program relies on Gurobi to solve a linear program, so you must first install Gurobi. Those affiliated with an university can get the academic version for free. Otherwise, a free trial is available.
To get CMake to find your Gurobi installation, you may need to change the path in Line 1 of cmake/modules/FindGUROBI.cmake
.
git clone --recursive https://github.com/nzfeng/SWN.git
cd SWN
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j8 # or however many cores you want to use
bin/main /path/to/mesh --c=/path/to/curve
A Polyscope GUI will open.
You can pass several arguments to the command line, including some flags which are also shown in the GUI.
flag | purpose |
---|---|
--c , --curve=input.txt |
Filepath to input curve. |
--o , --output=[meshname]_w.obj |
File to save winding number function to, consisting of the mesh along with homogeneous texture coordinates. |
--r , --approximateResidual |
Use reduced-size linear program to approximate the residual function, instead of solving a more expensive LP. Off by default. |
|--V
, --verbose
| Verbose output. |
|--help
| Display help |
The input mesh may be an obj
, ply
, off
, or stl
. See the geometry-central website for up-to-date information on supported file types.
The curve may either be specified within the mesh file, or in a separate file which can be passed as a second command line argument.
If the curve is specified within the mesh file, the curve can be specified as OBJ line elements. A line is specified by the character l
followed by two or more vertex indices, which indicates that the curve includes an oriented edge between each pair of adjacent vertices. The curve can also be specified as a dual 1-chain, using the character c
followed by two or more faces indices, which indicates that the curve jumps by +1 between each pair of adjacent faces. Using a dual 1-chain is usually not necessary; you might want to use a dual 1-chain only if the surface is non-orientable. Note: Since the dual version wasn't part of our original paper, supporting dual 1-chain input is still under construction at the moment.
If the curve is specified within a separate file, the curve can be specified using barycentric points along the surface. A barycentric point may either be of type "vertex" and specified in the form
v [vertex index]
or of type "edge",
e [edge index] [barycentric coordinate between 0 and 1]
Edges of the curve are specified by the character l
followed by two or more indices of barycentric points -- where barycentric points are indexed according to their line order in the file -- indicating that the curve includes an oriented edge between each pair of adjacent barycentric points. Element indices and indices of barycentric points are by default 1-indexed following OBJ convention, but one may also include the directive #offset 0
to use 0-indexing. See e.g. data/annulus.txt
or data/bunny-heart.txt
for examples of this file format.
If some parts of the curve are not constrained to mesh edges, the mesh will be cut such that the curve conforms to the new, mutated mesh (but the curve geometry itself will remain unchanged.) The caveat is that the curve must initially be continuous and linear within each triangle face.
Depending on the curve input, the surface may need to be re-meshed for optimal output.
First, SWN is formulated for curves that conform to mesh edges. However, you can still specify curves generically as sequences of barycentric points along the surface, with the condition that the curve is continuous and linear within each triangle face. If this is the case, the surface will be re-meshed so that the curve lies entirely along mesh edges (with no change to the curve geometry.)
Second, since curve endpoints are omitted from the solve (Section 2.3.2 in the paper), curves that span only one mesh edge will be ignored. Such edges will be subdivided so that these parts of the curve will not be ignored.
The GUI can perform intrinsic re-meshing for better numerical behavior. If you choose to solve on an intrinsic mesh, exporting the solution will export the solution on the common subdivision mesh.
Notes:
- Only manifold meshes can be intrinsically re-triangulated.
- Delaunay refinement may not terminate with a minimum angle value >30 degrees. If this happens, I recommend picking an angle value of 25-28 degrees. Other times you may need to start with an even lower angle threshold, and gradually increase.
From the GUI menu, you can export the solution, as well as other intermediate functions, as an OBJ file containing both the mesh and texture coordinates. Whenever one calls an export function, the data that gets exported is from most recently computed solution.
From the GUI menu, you can also export curves as OBJ line elements. In most 3D graphics software, how smooth a curve is rendered depends on its connectivity (i.e., there can be noticeable gaps between curves specified as separate segments.) Therefore, this program will automatically greedily compute the connected components of the curve before exporting, so that the curve can be written with maximum connectivity and hence yield maximum smoothness during rendering. I have included an example Blender file that loads in a mesh and curve, and renders the winding number function stored as texture coordinates on the mesh; see the section on "Visualization" below.
For better visualization of the solution around curve endpoints, we implemented a projective interpolation (see Figure 4 from the paper.) The Polyscope shader in this program is already set up to perform this special interpolation, so solutions will look already good in the GUI.
When you export a solution, we output texture coordinates for the mesh in homogeneous coordinates, where rather than the standard uv coordinates, we output 3-dimensional uvw texture coordinates. To visualize these textures, the 3d coordinates are linearly interpolated across each triangle. Then, for each pixel the first two coordinates are divided by the last coordinate to obtain the final uv texture coordinates. This can be done in a shader, or via shader nodes in Blender -- see Example.blend
for an example.
The render/
directory contains an example Blender file (Example.blend
) that can load and visualize meshes and curves, with the SWN solution. The Blender file should open to a Python script in the Scripting
workspace. You can load your own mesh & solution (encoded as texture coordinates) by changing the mesh filepath in the script and clicking on Run Script
. This will load your model and visualize the correctly-interpolated solution.