├── imgs └── SphereToPlane.png ├── cpp_src ├── cmake │ └── libigl.cmake ├── CMakeLists.txt ├── README.md └── main.cpp ├── LICENSE ├── README.md ├── src ├── sphere_to_plane_transfer.py └── utils.py ├── .gitignore └── meshes ├── sphere.obj └── grid.obj /imgs/SphereToPlane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rin-23/RobustSkinWeightsTransferCode/HEAD/imgs/SphereToPlane.png -------------------------------------------------------------------------------- /cpp_src/cmake/libigl.cmake: -------------------------------------------------------------------------------- 1 | if(TARGET igl::core) 2 | return() 3 | endif() 4 | 5 | include(FetchContent) 6 | FetchContent_Declare( 7 | libigl 8 | GIT_REPOSITORY https://github.com/libigl/libigl.git 9 | GIT_TAG v2.5.0 10 | ) 11 | FetchContent_MakeAvailable(libigl) 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Rinat Abdrashitov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cpp_src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(skintransfer) 3 | 4 | list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 5 | 6 | # Libigl 7 | include(libigl) 8 | 9 | # Enable the target igl::glfw 10 | # igl_include(glfw) 11 | # Other modules you could enable 12 | #igl_include(embree) 13 | #igl_include(imgui) 14 | #igl_include(opengl) 15 | #igl_include(stb) 16 | #igl_include(predicates) 17 | #igl_include(xml) 18 | #igl_include(copyleft cgal) 19 | #igl_include(copyleft comiso) 20 | igl_include(copyleft core) 21 | #igl_include(copyleft cork) 22 | #igl_include(copyleft tetgen) 23 | #igl_include(restricted matlab) 24 | #igl_include(restricted mosek) 25 | #igl_include(restricted triangle) 26 | 27 | # Add your project files 28 | file(GLOB SRC_FILES *.cpp) 29 | add_executable(${PROJECT_NAME} ${SRC_FILES}) 30 | 31 | # Link igl (and the glfw module) to your project 32 | target_link_libraries(${PROJECT_NAME} PUBLIC 33 | # igl::glfw 34 | ## Other modules you could link to 35 | # igl::embree 36 | # igl::imgui 37 | # igl::opengl 38 | # igl::stb 39 | # igl::predicates 40 | # igl::xml 41 | # igl_copyleft::cgal 42 | # igl_copyleft::comiso 43 | igl_copyleft::core 44 | # igl_copyleft::cork 45 | # igl_copyleft::tetgen 46 | # igl_restricted::matlab 47 | # igl_restricted::mosek 48 | # igl_restricted::triangle 49 | ) 50 | -------------------------------------------------------------------------------- /cpp_src/README.md: -------------------------------------------------------------------------------- 1 | # C++ implementation of the Robust Skin Weights Transfer Algorithm 2 | 3 | Follows the logic of the python implementation one to one. 4 | 5 | ## Compile 6 | 7 | Compile this project using the standard cmake routine: 8 | 9 | mkdir build 10 | cd build 11 | cmake .. 12 | make -j8 13 | 14 | This should find and build the dependencies and create a `skintransfer` binary. 15 | 16 | ## Run 17 | 18 | From within the `build` directory just issue: 19 | 20 | ./skintransfer 21 | 22 | This will run **main** function on a sample data and print out the resulting inpainted weights matrix. 23 | You can read the **main** function to learn how to use the **robust_skin_weights_transfer** 24 | function. 25 | 26 | ## Dependencies 27 | 28 | The only dependencies are STL, Eigen, [libigl](http://libigl.github.io/libigl/). 29 | 30 | The CMake build system will automatically download libigl and its dependencies using 31 | [CMake FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html), 32 | thus requiring no setup on your part. 33 | 34 | ### Use a local copy of libigl 35 | You can use the CMake cache variable `FETCHCONTENT_SOURCE_DIR_LIBIGL` when configuring your CMake project for 36 | the first time to aim it at a local copy of libigl instead. 37 | ``` 38 | cmake -DFETCHCONTENT_SOURCE_DIR_LIBIGL= .. 39 | ``` 40 | When changing this value, do not forget to clear your `CMakeCache.txt`, or to update the cache variable 41 | via `cmake-gui` or `ccmake`. 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Robust Skin Weights Transfer via Weight Inpainting 2 | ![Teaser](https://www.dgp.toronto.edu/~rinat/projects/RobustSkinWeightsTransfer/teaser.jpg) 3 | Sample code for the Siggraph Asia 2023 Technical Communications paper - [Robust Skin Weights Transfer via Weight Inpainting](https://www.dgp.toronto.edu/~rinat/projects/RobustSkinWeightsTransfer/index.html) 4 | 5 | ## Dependencies 6 | 7 | Python bindings for [libigl](https://github.com/libigl/libigl-python-bindings) 8 | 9 | [Polyscope](https://polyscope.run/py/installing/) 10 | 11 | ## Running 12 | ### Simple Transfer 13 | 14 | ```python 15 | python src/sphere_to_plane_transfer.py 16 | ``` 17 | 18 | This will perform a simple transfer of weights from a sphere to the plane above it. 19 | 20 | ![SphereToPlane](imgs/SphereToPlane.png) 21 | 22 | However, the code contains the full implementation of the method, and you can swap 23 | the meshes with any other meshes and load the source skinning weights. 24 | 25 | ### Body to garment transfer 26 | (Coming soon) Load fbx files of a body and cloth meshes. Do the transfer from 27 | the body to cloth and write the result of the transfer into another fbx that can 28 | be loaded in other 3D software (Blender, Unreal, etc.). 29 | 30 | ## Other 3rd party implementations 31 | Blender addon (by Sent From Space) - https://jinxxy.com/SentFromSpaceVR/robust-weight-transfer 32 | 33 | ## Cite 34 | If you use this code for an academic publication, cite it as: 35 | ```bib 36 | @inproceedings{abdrashitov2023robust, 37 | author = {Abdrashitov, Rinat and Raichstat, Kim and Monsen, Jared and Hill, David}, 38 | title = {Robust Skin Weights Transfer via Weight Inpainting}, 39 | year = {2023}, 40 | isbn = {9798400703140}, 41 | publisher = {Association for Computing Machinery}, 42 | address = {New York, NY, USA}, 43 | url = {https://doi.org/10.1145/3610543.3626180}, 44 | doi = {10.1145/3610543.3626180}, 45 | booktitle = {SIGGRAPH Asia 2023 Technical Communications}, 46 | articleno = {25}, 47 | numpages = {4}, 48 | location = {, Sydney, NSW, Australia, }, 49 | series = {SA '23} 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /src/sphere_to_plane_transfer.py: -------------------------------------------------------------------------------- 1 | from utils import find_matches_closest_surface, inpaint, smooth 2 | import polyscope as ps 3 | import igl 4 | import numpy as np 5 | import os 6 | 7 | # Initialize polyscope 8 | ps.init() 9 | 10 | # Get the directory of the current file 11 | current_folder = os.path.dirname(os.path.abspath(__file__)) 12 | 13 | # Load the source mesh 14 | V, F = igl.read_triangle_mesh(current_folder+"/../meshes/sphere.obj") 15 | V1, F1,_,_ = igl.remove_unreferenced(V,F) 16 | if (V.shape[0] != V1.shape[0]): 17 | print("[Warning] Source mesh has unreferenced vertices which were removed") 18 | N1 = igl.per_vertex_normals(V1, F1) 19 | 20 | # Load the target mesh 21 | V, F = igl.read_triangle_mesh(current_folder+"/../meshes/grid.obj") 22 | V2, F2,_,_ = igl.remove_unreferenced(V,F) 23 | if (V.shape[0] != V2.shape[0]): 24 | print("[Warning] Target mesh has unreferenced vertices which were removed") 25 | N2 = igl.per_vertex_normals(V2, F2) 26 | 27 | # You can setup your own skin weights matrix W \in R^(|V1| x num_bones) here 28 | # W = np.load("source_skinweights.npy") 29 | 30 | # For now, generate simple per-vertex data (can be skinning weights but can be any scalar data) 31 | W = np.ones((V1.shape[0], 2)) # our simple rig has only 2 bones 32 | W[:, 0] = 0.3 # first bone has an influence of 0.3 on all vertices 33 | W[:, 1] = 0.7 # second bone has an influence of 0.7 on all vertices 34 | 35 | num_bones = W.shape[1] 36 | 37 | # Register source and target Mesh geometries, plus their Normals 38 | ps.register_surface_mesh("SourceMesh", V1, F1, smooth_shade=True) 39 | ps.register_surface_mesh("TargetMesh", V2, F2, smooth_shade=True) 40 | ps.get_surface_mesh("SourceMesh").add_vector_quantity("Normals", N1, defined_on='vertices', color=(0.2, 0.5, 0.5)) 41 | ps.get_surface_mesh("TargetMesh").add_vector_quantity("Normals", N2, defined_on='vertices', color=(0.2, 0.5, 0.5)) 42 | 43 | # 44 | # Section 3.1 Closest Point Matching 45 | # 46 | dDISTANCE_THRESHOLD = 0.05*igl.bounding_box_diagonal(V2) # threshold distance D 47 | dDISTANCE_THRESHOLD_SQRD = dDISTANCE_THRESHOLD *dDISTANCE_THRESHOLD 48 | dANGLE_THRESHOLD_DEGREES = 30 # threshold angle theta in degrees 49 | 50 | # for every vertex on the target mesh find the closest point on the source mesh and copy weights over 51 | Matched, SkinWeights_interpolated = find_matches_closest_surface(V1,F1,N1,V2,F2,N2,W,dDISTANCE_THRESHOLD_SQRD,dANGLE_THRESHOLD_DEGREES) 52 | 53 | # visualize vertices for which we found a match 54 | ps.get_surface_mesh("TargetMesh").add_scalar_quantity("Matched", Matched, defined_on='vertices', cmap='blues') 55 | 56 | 57 | # 58 | # Section 3.2 Skinning Weights Inpainting 59 | # 60 | InpaintedWeights, success = inpaint(V2, F2, SkinWeights_interpolated, Matched) 61 | 62 | if (success): 63 | # Visualize the weights for each bone 64 | ps.get_surface_mesh("TargetMesh").add_scalar_quantity("Bone1", InpaintedWeights[:,0], defined_on='vertices', cmap='blues') 65 | ps.get_surface_mesh("TargetMesh").add_scalar_quantity("Bone2", InpaintedWeights[:,1], defined_on='vertices', cmap='blues') 66 | 67 | # Optional smoothing 68 | SmoothedInpaintedWeights, VIDs_to_smooth = smooth(V2, F2, InpaintedWeights, Matched, dDISTANCE_THRESHOLD, num_smooth_iter_steps=10, smooth_alpha=0.2) 69 | ps.get_surface_mesh("TargetMesh").add_scalar_quantity("VIDs_to_smooth", VIDs_to_smooth, defined_on='vertices', cmap='blues') 70 | 71 | # Visualize the smoothed weights for each bone 72 | ps.get_surface_mesh("TargetMesh").add_scalar_quantity("SmoothedBone1", InpaintedWeights[:,0], defined_on='vertices', cmap='blues') 73 | ps.get_surface_mesh("TargetMesh").add_scalar_quantity("SmoothedBone2", InpaintedWeights[:,1], defined_on='vertices', cmap='blues') 74 | 75 | ps.show() 76 | 77 | else: 78 | print("[Error] Inpainting failed.") -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | 163 | .DS_Store 164 | *.ini 165 | -------------------------------------------------------------------------------- /src/utils.py: -------------------------------------------------------------------------------- 1 | import igl 2 | import numpy as np 3 | import math 4 | import scipy as sp 5 | 6 | def find_closest_point_on_surface(P, V, F): 7 | """ 8 | Given a number of points find their closest points on the surface of the V,F mesh 9 | 10 | Args: 11 | P: #P by 3, where every row is a point coordinate 12 | V: #V by 3 mesh vertices 13 | F: #F by 3 mesh triangles indices 14 | Returns: 15 | sqrD #P smallest squared distances 16 | I #P primitive indices corresponding to smallest distances 17 | C #P by 3 closest points 18 | B #P by 3 of the barycentric coordinates of the closest point 19 | """ 20 | 21 | sqrD,I,C = igl.point_mesh_squared_distance(P, V, F) 22 | 23 | F_closest = F[I,:] 24 | V1 = V[F_closest[:,0],:] 25 | V2 = V[F_closest[:,1],:] 26 | V3 = V[F_closest[:,2],:] 27 | 28 | B = igl.barycentric_coordinates_tri(C, V1, V2, V3) 29 | 30 | return sqrD,I,C,B 31 | 32 | def interpolate_attribute_from_bary(A,B,I,F): 33 | """ 34 | Interpolate per-vertex attributes A via barycentric coordinates B of the F[I,:] vertices 35 | 36 | Args: 37 | A: #V by N per-vertex attributes 38 | B #B by 3 array of the barycentric coordinates of some points 39 | I #B primitive indices containing the closest point 40 | F: #F by 3 mesh triangle indices 41 | Returns: 42 | A_out #B interpolated attributes 43 | """ 44 | F_closest = F[I,:] 45 | a1 = A[F_closest[:,0],:] 46 | a2 = A[F_closest[:,1],:] 47 | a3 = A[F_closest[:,2],:] 48 | 49 | b1 = B[:,0] 50 | b2 = B[:,1] 51 | b3 = B[:,2] 52 | 53 | b1 = b1.reshape(-1,1) 54 | b2 = b2.reshape(-1,1) 55 | b3 = b3.reshape(-1,1) 56 | 57 | A_out = a1*b1 + a2*b2 + a3*b3 58 | 59 | return A_out 60 | 61 | def normalize_vec(v): 62 | return v/np.linalg.norm(v) 63 | 64 | 65 | def find_matches_closest_surface(V1, F1, N1, V2, F2, N2, W1, dDISTANCE_THRESHOLD_SQRD, dANGLE_THRESHOLD_DEGREES): 66 | """ 67 | For each vertex on the target mesh find a match on the source mesh. 68 | 69 | Args: 70 | V1: #V1 by 3 source mesh vertices 71 | F1: #F1 by 3 source mesh triangles indices 72 | N1: #V1 by 3 source mesh normals 73 | 74 | V2: #V2 by 3 target mesh vertices 75 | F2: #F2 by 3 target mesh triangles indices 76 | N2: #V2 by 3 target mesh normals 77 | 78 | W1: #V1 by num_bones source mesh skin weights 79 | 80 | dDISTANCE_THRESHOLD_SQRD: scalar distance threshold 81 | dANGLE_THRESHOLD_DEGREES: scalar normal threshold 82 | 83 | Returns: 84 | Matched: #V2 array of bools, where Matched[i] is True if we found a good match for vertex i on the source mesh 85 | W2: #V2 by num_bones, where W2[i,:] are skinning weights copied directly from source using closest point method 86 | """ 87 | 88 | Matched = np.zeros(shape = (V2.shape[0]), dtype=bool) 89 | sqrD,I,C,B = find_closest_point_on_surface(V2,V1,F1) 90 | 91 | # for each closest point on the source, interpolate its per-vertex attributes(skin weights and normals) 92 | # using the barycentric coordinates 93 | W2 = interpolate_attribute_from_bary(W1,B,I,F1) 94 | N1_match_interpolated = interpolate_attribute_from_bary(N1,B,I,F1) 95 | 96 | # check that the closest point passes our distance and normal thresholds 97 | for RowIdx in range(0, V2.shape[0]): 98 | n1 = normalize_vec(N1_match_interpolated[RowIdx,:]) 99 | n2 = normalize_vec(N2[RowIdx, :]) 100 | rad_angle = np.arccos(np.dot(n1, n2)) 101 | deg_angle = math.degrees(rad_angle) 102 | if sqrD[RowIdx] <= dDISTANCE_THRESHOLD_SQRD and deg_angle <= dANGLE_THRESHOLD_DEGREES: 103 | Matched[RowIdx] = True 104 | 105 | return Matched, W2 106 | 107 | def is_valid_array(sparse_matrix): 108 | has_invalid_numbers = np.isnan(sparse_matrix.data).any() or np.isinf(sparse_matrix.data).any() 109 | return not has_invalid_numbers 110 | 111 | def inpaint(V2, F2, W2, Matched): 112 | """ 113 | Inpaint weights for all the vertices on the target mesh for which we didnt 114 | find a good match on the source (i.e. Matched[i] == False). 115 | 116 | Args: 117 | V2: #V2 by 3 target mesh vertices 118 | F2: #F2 by 3 target mesh triangles indices 119 | W2: #V2 by num_bones, where W2[i,:] are skinning weights copied directly from source using closest point method 120 | Matched: #V2 array of bools, where Matched[i] is True if we found a good match for vertex i on the source mesh 121 | 122 | Returns: 123 | W_inpainted: #V2 by num_bones, final skinning weights where we inpainted weights for all vertices i where Matched[i] == False 124 | success: true if inpainting succeeded, false otherwise 125 | """ 126 | 127 | # Compute the laplacian 128 | L = 2*igl.cotmatrix(V2, F2) 129 | M = igl.massmatrix(V2, F2, igl.MASSMATRIX_TYPE_VORONOI) 130 | Minv = sp.sparse.diags(1 / M.diagonal()) 131 | 132 | is_valid = is_valid_array(L) 133 | if (not is_valid): 134 | print("[Error] Laplacian is invalid:") 135 | 136 | is_valid = is_valid_array(Minv) 137 | if (not is_valid): 138 | print("[Error] Mass matrix is invalid:") 139 | 140 | Q = -L + L*Minv*L 141 | 142 | is_valid = is_valid_array(Q) 143 | if (not is_valid): 144 | print("[Error] System matrix is invalid:") 145 | 146 | Aeq = sp.sparse.csc_matrix((0, 0)) 147 | Beq = np.array([]) 148 | B = np.zeros(shape = (L.shape[0], W2.shape[1])) 149 | 150 | b = np.array(range(0, int(V2.shape[0])), dtype=int) 151 | b = b[Matched] 152 | bc = W2[Matched,:] 153 | 154 | results, W_inpainted = igl.min_quad_with_fixed(Q, B, b, bc, Aeq, Beq, True) 155 | 156 | return W_inpainted, results 157 | 158 | def smooth(V2, F2, W2, Matched, dDISTANCE_THRESHOLD, num_smooth_iter_steps=10, smooth_alpha=0.2): 159 | """ 160 | Smooth weights in the areas for which weights were inpainted and also their close neighbours. 161 | 162 | Args: 163 | V2: #V2 by 3 target mesh vertices 164 | F2: #F2 by 3 target mesh triangles indices 165 | W2: #V2 by num_bones skinning weights 166 | Matched: #V2 array of bools, where Matched[i] is True if we found a good match for vertex i on the source mesh 167 | dDISTANCE_THRESHOLD_SQRD: scalar distance threshold 168 | num_smooth_iter_steps: scalar number of smoothing steps 169 | smooth_alpha: scalar the smoothing strength 170 | 171 | Returns: 172 | W2_smoothed: #V2 by num_bones new smoothed weights 173 | VIDs_to_smooth: 1D array of vertex IDs for which smoothing was applied 174 | """ 175 | 176 | NotMatched = ~Matched 177 | VIDs_to_smooth = np.array(NotMatched, copy=True) 178 | 179 | adj_list = igl.adjacency_list(F2) 180 | 181 | def get_points_within_distance(V, VID, distance=dDISTANCE_THRESHOLD): 182 | """ 183 | Get all neighbours of vertex VID within dDISTANCE_THRESHOLD 184 | """ 185 | 186 | queue = [] 187 | queue.append(VID) 188 | while len(queue) != 0: 189 | vv = queue.pop() 190 | neigh = adj_list[vv] 191 | for nn in neigh: 192 | if ~VIDs_to_smooth[nn] and np.linalg.norm(V[VID,:]-V[nn]) < distance: 193 | VIDs_to_smooth[nn] = True 194 | if nn not in queue: 195 | queue.append(nn) 196 | 197 | 198 | for i in range(0, V2.shape[0]): 199 | if NotMatched[i]: 200 | get_points_within_distance(V2, i) 201 | 202 | W2_smoothed = np.array(W2, copy=True) 203 | for step_idx in range(0, num_smooth_iter_steps): 204 | for i in range(0, V2.shape[0]): 205 | if VIDs_to_smooth[i]: 206 | neigh = adj_list[i] 207 | num = len(neigh) 208 | weight = W2_smoothed[i,:] 209 | 210 | new_weight = (1-smooth_alpha)*weight 211 | for influence_idx in neigh: 212 | weight_connected = W2_smoothed[influence_idx,:] 213 | new_weight += (weight_connected / num) * smooth_alpha 214 | 215 | W2_smoothed[i,:] = new_weight 216 | 217 | return W2_smoothed, VIDs_to_smooth 218 | 219 | -------------------------------------------------------------------------------- /meshes/sphere.obj: -------------------------------------------------------------------------------- 1 | # 197 points 2 | # 1170 vertices 3 | # 390 primitives 4 | # Bounds: [-0.489073813, -0.5, -0.497260958] to [0.5, 0.5, 0.497260958] 5 | g 6 | v -1.99661727e-08 0.5 -8.88950868e-09 7 | v 8.89369431e-08 -0.5 3.9597257e-08 8 | v 0.111260429 0.487463951 0 9 | v 0.101641454 0.487463951 -0.0452536941 10 | v 0.074447751 0.487463951 -0.082682617 11 | v 0.0343813598 0.487463951 -0.105814956 12 | v -0.0116298869 0.487463951 -0.110650934 13 | v -0.055630222 0.487463951 -0.0963543579 14 | v -0.0900115818 0.487463951 -0.0653972328 15 | v -0.108829126 0.487463951 -0.0231323354 16 | v -0.108829118 0.487463951 0.023132354 17 | v -0.0900115743 0.487463951 0.0653972551 18 | v -0.0556302033 0.487463951 0.0963543653 19 | v -0.0116298674 0.487463951 0.110650934 20 | v 0.0343813784 0.487463951 0.105814949 21 | v 0.0744477734 0.487463951 0.0826826021 22 | v 0.101641469 0.487463951 0.0452536754 23 | v 0.216941819 0.450484455 0 24 | v 0.198186204 0.450484455 -0.0882381871 25 | v 0.145162404 0.450484455 -0.161219195 26 | v 0.0670387074 0.450484455 -0.206323937 27 | v -0.0226766057 0.450484455 -0.215753391 28 | v -0.108470924 0.450484455 -0.187877119 29 | v -0.175509632 0.450484455 -0.127515182 30 | v -0.212201118 0.450484455 -0.0451047234 31 | v -0.212201118 0.450484455 0.0451047607 32 | v -0.175509602 0.450484455 0.127515227 33 | v -0.108470887 0.450484455 0.187877133 34 | v -0.0226765685 0.450484455 0.215753391 35 | v 0.0670387372 0.450484455 0.206323922 36 | v 0.145162433 0.450484455 0.16121918 37 | v 0.198186234 0.450484455 0.0882381573 38 | v 0.311744839 0.390915781 0 39 | v 0.284793079 0.390915781 -0.126798049 40 | v 0.208598003 0.390915781 -0.231671572 41 | v 0.0963344499 0.390915781 -0.296486974 42 | v -0.0325862244 0.390915781 -0.310037076 43 | v -0.155872434 0.390915781 -0.26997894 44 | v -0.252206892 0.390915781 -0.183238998 45 | v -0.304932475 0.390915781 -0.0648153722 46 | v -0.304932445 0.390915781 0.0648154244 47 | v -0.252206862 0.390915781 0.183239058 48 | v -0.15587239 0.390915781 0.26997897 49 | v -0.0325861685 0.390915781 0.310037076 50 | v 0.0963344947 0.390915781 0.296486944 51 | v 0.208598062 0.390915781 0.231671542 52 | v 0.284793109 0.390915781 0.126798004 53 | v 0.390915692 0.311744958 0 54 | v 0.357119232 0.311744958 -0.158999741 55 | v 0.261573642 0.311744958 -0.290506989 56 | v 0.120799586 0.311744958 -0.371782929 57 | v -0.0408618338 0.311744958 -0.388774216 58 | v -0.195457876 0.311744958 -0.338542908 59 | v -0.316257477 0.311744958 -0.229774445 60 | v -0.382373244 0.311744958 -0.0812759101 61 | v -0.382373244 0.311744958 0.0812759846 62 | v -0.316257417 0.311744958 0.22977452 63 | v -0.195457816 0.311744958 0.338542938 64 | v -0.0408617668 0.311744958 0.388774216 65 | v 0.120799638 0.311744958 0.371782899 66 | v 0.261573702 0.311744958 0.290506959 67 | v 0.357119292 0.311744958 0.158999681 68 | v 0.450484395 0.216941938 0 69 | v 0.411537975 0.216941938 -0.183228508 70 | v 0.301432878 0.216941938 -0.33477518 71 | v 0.139207318 0.216941938 -0.42843613 72 | v -0.0470884629 0.216941938 -0.448016614 73 | v -0.225242227 0.216941938 -0.390130937 74 | v -0.364449561 0.216941938 -0.264788061 75 | v -0.440640241 0.216941938 -0.0936609358 76 | v -0.440640211 0.216941938 0.0936610177 77 | v -0.364449501 0.216941938 0.264788121 78 | v -0.225242153 0.216941938 0.390130937 79 | v -0.0470883846 0.216941938 0.448016614 80 | v 0.139207393 0.216941938 0.4284361 81 | v 0.301432967 0.216941938 0.33477512 82 | v 0.411538005 0.216941938 0.183228448 83 | v 0.487463951 0.111260541 0 84 | v 0.445320457 0.111260541 -0.198269457 85 | v 0.326177031 0.111260541 -0.362256348 86 | v 0.150634632 0.111260541 -0.463605791 87 | v -0.05095388 0.111260541 -0.484793574 88 | v -0.243732005 0.111260541 -0.422156155 89 | v -0.394366652 0.111260541 -0.286524087 90 | v -0.476811707 0.111260541 -0.101349413 91 | v -0.476811677 0.111260541 0.101349503 92 | v -0.394366592 0.111260541 0.286524177 93 | v -0.243731931 0.111260541 0.422156185 94 | v -0.050953798 0.111260541 0.484793574 95 | v 0.150634706 0.111260541 0.463605762 96 | v 0.32617712 0.111260541 0.362256289 97 | v 0.445320517 0.111260541 0.198269382 98 | v 0.5 7.4505806e-08 0 99 | v 0.456772715 7.4505806e-08 -0.203368321 100 | v 0.334565282 7.4505806e-08 -0.371572435 101 | v 0.154508486 7.4505806e-08 -0.47552827 102 | v -0.0522642545 7.4505806e-08 -0.497260958 103 | v -0.25000003 7.4505806e-08 -0.433012694 104 | v -0.404508531 7.4505806e-08 -0.293892592 105 | v -0.489073813 7.4505806e-08 -0.103955805 106 | v -0.489073783 7.4505806e-08 0.103955895 107 | v -0.404508471 7.4505806e-08 0.293892682 108 | v -0.249999955 7.4505806e-08 0.433012724 109 | v -0.0522641689 7.4505806e-08 0.497260958 110 | v 0.154508561 7.4505806e-08 0.47552824 111 | v 0.334565371 7.4505806e-08 0.371572375 112 | v 0.456772774 7.4505806e-08 0.203368247 113 | v 0.487463981 -0.111260392 0 114 | v 0.445320487 -0.111260392 -0.198269457 115 | v 0.326177061 -0.111260392 -0.362256348 116 | v 0.150634646 -0.111260392 -0.463605821 117 | v -0.0509538837 -0.111260392 -0.484793603 118 | v -0.24373202 -0.111260392 -0.422156185 119 | v -0.394366682 -0.111260392 -0.286524117 120 | v -0.476811737 -0.111260392 -0.101349421 121 | v -0.476811707 -0.111260392 0.10134951 122 | v -0.394366622 -0.111260392 0.286524206 123 | v -0.243731946 -0.111260392 0.422156215 124 | v -0.050953798 -0.111260392 0.484793603 125 | v 0.150634721 -0.111260392 0.463605791 126 | v 0.32617715 -0.111260392 0.362256289 127 | v 0.445320547 -0.111260392 0.198269397 128 | v 0.450484455 -0.216941804 0 129 | v 0.411538005 -0.216941804 -0.183228537 130 | v 0.301432908 -0.216941804 -0.334775209 131 | v 0.139207348 -0.216941804 -0.42843619 132 | v -0.0470884666 -0.216941804 -0.448016673 133 | v -0.225242257 -0.216941804 -0.390130967 134 | v -0.36444962 -0.216941804 -0.264788091 135 | v -0.440640301 -0.216941804 -0.0936609507 136 | v -0.440640271 -0.216941804 0.0936610326 137 | v -0.364449561 -0.216941804 0.264788181 138 | v -0.225242183 -0.216941804 0.390130997 139 | v -0.0470883921 -0.216941804 0.448016673 140 | v 0.139207408 -0.216941804 0.42843616 141 | v 0.301432997 -0.216941804 0.33477515 142 | v 0.411538064 -0.216941804 0.183228463 143 | v 0.390915781 -0.311744839 0 144 | v 0.357119322 -0.311744839 -0.158999771 145 | v 0.261573702 -0.311744839 -0.290507048 146 | v 0.120799609 -0.311744839 -0.371783018 147 | v -0.040861845 -0.311744839 -0.388774306 148 | v -0.19545792 -0.311744839 -0.338542998 149 | v -0.316257536 -0.311744839 -0.229774505 150 | v -0.382373333 -0.311744839 -0.0812759325 151 | v -0.382373333 -0.311744839 0.0812759995 152 | v -0.316257477 -0.311744839 0.229774579 153 | v -0.195457861 -0.311744839 0.338543028 154 | v -0.040861778 -0.311744839 0.388774306 155 | v 0.120799668 -0.311744839 0.371782988 156 | v 0.261573762 -0.311744839 0.290507019 157 | v 0.357119381 -0.311744839 0.158999711 158 | v 0.311744958 -0.390915692 0 159 | v 0.284793168 -0.390915692 -0.126798093 160 | v 0.208598077 -0.390915692 -0.231671661 161 | v 0.0963344797 -0.390915692 -0.296487093 162 | v -0.0325862356 -0.390915692 -0.310037196 163 | v -0.155872494 -0.390915692 -0.26997906 164 | v -0.252206981 -0.390915692 -0.183239073 165 | v -0.304932594 -0.390915692 -0.0648153946 166 | v -0.304932564 -0.390915692 0.0648154542 167 | v -0.252206951 -0.390915692 0.183239117 168 | v -0.155872449 -0.390915692 0.26997906 169 | v -0.0325861834 -0.390915692 0.310037196 170 | v 0.0963345319 -0.390915692 0.296487063 171 | v 0.208598137 -0.390915692 0.231671631 172 | v 0.284793228 -0.390915692 0.126798049 173 | v 0.216941938 -0.450484395 0 174 | v 0.198186323 -0.450484395 -0.0882382318 175 | v 0.145162478 -0.450484395 -0.161219284 176 | v 0.0670387372 -0.450484395 -0.206324056 177 | v -0.0226766169 -0.450484395 -0.215753511 178 | v -0.108470984 -0.450484395 -0.187877223 179 | v -0.175509736 -0.450484395 -0.127515256 180 | v -0.212201238 -0.450484395 -0.0451047495 181 | v -0.212201223 -0.450484395 0.0451047868 182 | v -0.175509706 -0.450484395 0.127515301 183 | v -0.108470947 -0.450484395 0.187877238 184 | v -0.0226765797 -0.450484395 0.215753511 185 | v 0.0670387745 -0.450484395 0.206324041 186 | v 0.145162523 -0.450484395 0.161219269 187 | v 0.198186338 -0.450484395 0.088238202 188 | v 0.111260548 -0.487463951 0 189 | v 0.101641566 -0.487463951 -0.0452537425 190 | v 0.074447833 -0.487463951 -0.0826827064 191 | v 0.0343813971 -0.487463951 -0.105815075 192 | v -0.011629899 -0.487463951 -0.110651053 193 | v -0.0556302816 -0.487463951 -0.0963544622 194 | v -0.0900116786 -0.487463951 -0.0653972998 195 | v -0.108829238 -0.487463951 -0.0231323596 196 | v -0.108829238 -0.487463951 0.0231323801 197 | v -0.0900116712 -0.487463951 0.0653973222 198 | v -0.0556302629 -0.487463951 0.0963544697 199 | v -0.0116298804 -0.487463951 0.110651053 200 | v 0.0343814157 -0.487463951 0.105815068 201 | v 0.0744478554 -0.487463951 0.0826826915 202 | v 0.10164158 -0.487463951 0.0452537239 203 | g 204 | f 1 3 4 205 | f 1 4 5 206 | f 1 5 6 207 | f 1 6 7 208 | f 1 7 8 209 | f 1 8 9 210 | f 1 9 10 211 | f 1 10 11 212 | f 1 11 12 213 | f 1 12 13 214 | f 1 13 14 215 | f 1 14 15 216 | f 1 15 16 217 | f 1 16 17 218 | f 1 17 3 219 | f 3 19 4 220 | f 3 18 19 221 | f 4 20 5 222 | f 4 19 20 223 | f 5 21 6 224 | f 5 20 21 225 | f 6 22 7 226 | f 6 21 22 227 | f 7 23 8 228 | f 7 22 23 229 | f 8 24 9 230 | f 8 23 24 231 | f 9 25 10 232 | f 9 24 25 233 | f 10 26 11 234 | f 10 25 26 235 | f 11 27 12 236 | f 11 26 27 237 | f 12 28 13 238 | f 12 27 28 239 | f 13 29 14 240 | f 13 28 29 241 | f 14 30 15 242 | f 14 29 30 243 | f 15 31 16 244 | f 15 30 31 245 | f 16 32 17 246 | f 16 31 32 247 | f 17 18 3 248 | f 17 32 18 249 | f 18 34 19 250 | f 18 33 34 251 | f 19 35 20 252 | f 19 34 35 253 | f 20 36 21 254 | f 20 35 36 255 | f 21 37 22 256 | f 21 36 37 257 | f 22 38 23 258 | f 22 37 38 259 | f 23 39 24 260 | f 23 38 39 261 | f 24 40 25 262 | f 24 39 40 263 | f 25 41 26 264 | f 25 40 41 265 | f 26 42 27 266 | f 26 41 42 267 | f 27 43 28 268 | f 27 42 43 269 | f 28 44 29 270 | f 28 43 44 271 | f 29 45 30 272 | f 29 44 45 273 | f 30 46 31 274 | f 30 45 46 275 | f 31 47 32 276 | f 31 46 47 277 | f 32 33 18 278 | f 32 47 33 279 | f 33 49 34 280 | f 33 48 49 281 | f 34 50 35 282 | f 34 49 50 283 | f 35 51 36 284 | f 35 50 51 285 | f 36 52 37 286 | f 36 51 52 287 | f 37 53 38 288 | f 37 52 53 289 | f 38 54 39 290 | f 38 53 54 291 | f 39 55 40 292 | f 39 54 55 293 | f 40 56 41 294 | f 40 55 56 295 | f 41 57 42 296 | f 41 56 57 297 | f 42 58 43 298 | f 42 57 58 299 | f 43 59 44 300 | f 43 58 59 301 | f 44 60 45 302 | f 44 59 60 303 | f 45 61 46 304 | f 45 60 61 305 | f 46 62 47 306 | f 46 61 62 307 | f 47 48 33 308 | f 47 62 48 309 | f 48 64 49 310 | f 48 63 64 311 | f 49 65 50 312 | f 49 64 65 313 | f 50 66 51 314 | f 50 65 66 315 | f 51 67 52 316 | f 51 66 67 317 | f 52 68 53 318 | f 52 67 68 319 | f 53 69 54 320 | f 53 68 69 321 | f 54 70 55 322 | f 54 69 70 323 | f 55 71 56 324 | f 55 70 71 325 | f 56 72 57 326 | f 56 71 72 327 | f 57 73 58 328 | f 57 72 73 329 | f 58 74 59 330 | f 58 73 74 331 | f 59 75 60 332 | f 59 74 75 333 | f 60 76 61 334 | f 60 75 76 335 | f 61 77 62 336 | f 61 76 77 337 | f 62 63 48 338 | f 62 77 63 339 | f 63 79 64 340 | f 63 78 79 341 | f 64 80 65 342 | f 64 79 80 343 | f 65 81 66 344 | f 65 80 81 345 | f 66 82 67 346 | f 66 81 82 347 | f 67 83 68 348 | f 67 82 83 349 | f 68 84 69 350 | f 68 83 84 351 | f 69 85 70 352 | f 69 84 85 353 | f 70 86 71 354 | f 70 85 86 355 | f 71 87 72 356 | f 71 86 87 357 | f 72 88 73 358 | f 72 87 88 359 | f 73 89 74 360 | f 73 88 89 361 | f 74 90 75 362 | f 74 89 90 363 | f 75 91 76 364 | f 75 90 91 365 | f 76 92 77 366 | f 76 91 92 367 | f 77 78 63 368 | f 77 92 78 369 | f 78 94 79 370 | f 78 93 94 371 | f 79 95 80 372 | f 79 94 95 373 | f 80 96 81 374 | f 80 95 96 375 | f 81 97 82 376 | f 81 96 97 377 | f 82 98 83 378 | f 82 97 98 379 | f 83 99 84 380 | f 83 98 99 381 | f 84 100 85 382 | f 84 99 100 383 | f 85 101 86 384 | f 85 100 101 385 | f 86 102 87 386 | f 86 101 102 387 | f 87 103 88 388 | f 87 102 103 389 | f 88 104 89 390 | f 88 103 104 391 | f 89 105 90 392 | f 89 104 105 393 | f 90 106 91 394 | f 90 105 106 395 | f 91 107 92 396 | f 91 106 107 397 | f 92 93 78 398 | f 92 107 93 399 | f 93 109 94 400 | f 93 108 109 401 | f 94 110 95 402 | f 94 109 110 403 | f 95 111 96 404 | f 95 110 111 405 | f 96 112 97 406 | f 96 111 112 407 | f 97 113 98 408 | f 97 112 113 409 | f 98 114 99 410 | f 98 113 114 411 | f 99 115 100 412 | f 99 114 115 413 | f 100 116 101 414 | f 100 115 116 415 | f 101 117 102 416 | f 101 116 117 417 | f 102 118 103 418 | f 102 117 118 419 | f 103 119 104 420 | f 103 118 119 421 | f 104 120 105 422 | f 104 119 120 423 | f 105 121 106 424 | f 105 120 121 425 | f 106 122 107 426 | f 106 121 122 427 | f 107 108 93 428 | f 107 122 108 429 | f 108 124 109 430 | f 108 123 124 431 | f 109 125 110 432 | f 109 124 125 433 | f 110 126 111 434 | f 110 125 126 435 | f 111 127 112 436 | f 111 126 127 437 | f 112 128 113 438 | f 112 127 128 439 | f 113 129 114 440 | f 113 128 129 441 | f 114 130 115 442 | f 114 129 130 443 | f 115 131 116 444 | f 115 130 131 445 | f 116 132 117 446 | f 116 131 132 447 | f 117 133 118 448 | f 117 132 133 449 | f 118 134 119 450 | f 118 133 134 451 | f 119 135 120 452 | f 119 134 135 453 | f 120 136 121 454 | f 120 135 136 455 | f 121 137 122 456 | f 121 136 137 457 | f 122 123 108 458 | f 122 137 123 459 | f 123 139 124 460 | f 123 138 139 461 | f 124 140 125 462 | f 124 139 140 463 | f 125 141 126 464 | f 125 140 141 465 | f 126 142 127 466 | f 126 141 142 467 | f 127 143 128 468 | f 127 142 143 469 | f 128 144 129 470 | f 128 143 144 471 | f 129 145 130 472 | f 129 144 145 473 | f 130 146 131 474 | f 130 145 146 475 | f 131 147 132 476 | f 131 146 147 477 | f 132 148 133 478 | f 132 147 148 479 | f 133 149 134 480 | f 133 148 149 481 | f 134 150 135 482 | f 134 149 150 483 | f 135 151 136 484 | f 135 150 151 485 | f 136 152 137 486 | f 136 151 152 487 | f 137 138 123 488 | f 137 152 138 489 | f 138 154 139 490 | f 138 153 154 491 | f 139 155 140 492 | f 139 154 155 493 | f 140 156 141 494 | f 140 155 156 495 | f 141 157 142 496 | f 141 156 157 497 | f 142 158 143 498 | f 142 157 158 499 | f 143 159 144 500 | f 143 158 159 501 | f 144 160 145 502 | f 144 159 160 503 | f 145 161 146 504 | f 145 160 161 505 | f 146 162 147 506 | f 146 161 162 507 | f 147 163 148 508 | f 147 162 163 509 | f 148 164 149 510 | f 148 163 164 511 | f 149 165 150 512 | f 149 164 165 513 | f 150 166 151 514 | f 150 165 166 515 | f 151 167 152 516 | f 151 166 167 517 | f 152 153 138 518 | f 152 167 153 519 | f 153 169 154 520 | f 153 168 169 521 | f 154 170 155 522 | f 154 169 170 523 | f 155 171 156 524 | f 155 170 171 525 | f 156 172 157 526 | f 156 171 172 527 | f 157 173 158 528 | f 157 172 173 529 | f 158 174 159 530 | f 158 173 174 531 | f 159 175 160 532 | f 159 174 175 533 | f 160 176 161 534 | f 160 175 176 535 | f 161 177 162 536 | f 161 176 177 537 | f 162 178 163 538 | f 162 177 178 539 | f 163 179 164 540 | f 163 178 179 541 | f 164 180 165 542 | f 164 179 180 543 | f 165 181 166 544 | f 165 180 181 545 | f 166 182 167 546 | f 166 181 182 547 | f 167 168 153 548 | f 167 182 168 549 | f 168 184 169 550 | f 168 183 184 551 | f 169 185 170 552 | f 169 184 185 553 | f 170 186 171 554 | f 170 185 186 555 | f 171 187 172 556 | f 171 186 187 557 | f 172 188 173 558 | f 172 187 188 559 | f 173 189 174 560 | f 173 188 189 561 | f 174 190 175 562 | f 174 189 190 563 | f 175 191 176 564 | f 175 190 191 565 | f 176 192 177 566 | f 176 191 192 567 | f 177 193 178 568 | f 177 192 193 569 | f 178 194 179 570 | f 178 193 194 571 | f 179 195 180 572 | f 179 194 195 573 | f 180 196 181 574 | f 180 195 196 575 | f 181 197 182 576 | f 181 196 197 577 | f 182 183 168 578 | f 182 197 183 579 | f 183 2 184 580 | f 184 2 185 581 | f 185 2 186 582 | f 186 2 187 583 | f 187 2 188 584 | f 188 2 189 585 | f 189 2 190 586 | f 190 2 191 587 | f 191 2 192 588 | f 192 2 193 589 | f 193 2 194 590 | f 194 2 195 591 | f 195 2 196 592 | f 196 2 197 593 | f 197 2 183 594 | -------------------------------------------------------------------------------- /cpp_src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | /** 20 | * Given a number of points find their closest points on the surface of the V,F mesh 21 | * 22 | * P: #P by 3, where every row is a point coordinate 23 | * V: #V by 3 mesh vertices 24 | * F: #F by 3 mesh triangles indices 25 | * sqrD #P smallest squared distances 26 | * I #P primitive indices corresponding to smallest distances 27 | * C #P by 3 closest points 28 | * B #P by 3 of the barycentric coordinates of the closest point 29 | */ 30 | void find_closest_point_on_surface(const Eigen::MatrixXd& P, const Eigen::MatrixXd& V, const Eigen::MatrixXi& F, 31 | Eigen::VectorXd& sqrD, Eigen::VectorXi& I, Eigen::MatrixXd& C, Eigen::MatrixXd& B) 32 | { 33 | igl::point_mesh_squared_distance(P, V, F, sqrD, I, C); 34 | 35 | Eigen::MatrixXi F_closest = F(I, Eigen::all); 36 | Eigen::MatrixXd V1 = V(F_closest(Eigen::all, 0), Eigen::all); 37 | Eigen::MatrixXd V2 = V(F_closest(Eigen::all, 1), Eigen::all); 38 | Eigen::MatrixXd V3 = V(F_closest(Eigen::all, 2), Eigen::all); 39 | 40 | igl::barycentric_coordinates(C, V1, V2, V3, B); 41 | } 42 | 43 | 44 | /** 45 | * Interpolate per-vertex attributes A via barycentric coordinates B of the F[I,:] vertices 46 | * 47 | * A: #V by N per-vertex attributes 48 | * B #B by 3 array of the barycentric coordinates of some points 49 | * I #B primitive indices containing the closest point 50 | * F: #F by 3 mesh triangle indices 51 | * A_out #B interpolated attributes 52 | */ 53 | void interpolate_attribute_from_bary(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B, 54 | const Eigen::VectorXi& I, const Eigen::MatrixXi& F, 55 | Eigen::MatrixXd& A_out) 56 | { 57 | Eigen::MatrixXi F_closest = F(I, Eigen::all); 58 | 59 | Eigen::MatrixXd a1 = A(F_closest.col(0), Eigen::all); 60 | Eigen::MatrixXd a2 = A(F_closest(Eigen::all, 1), Eigen::all); 61 | Eigen::MatrixXd a3 = A(F_closest(Eigen::all, 2), Eigen::all); 62 | 63 | Eigen::VectorXd b1 = B(Eigen::all, 0); 64 | Eigen::VectorXd b2 = B(Eigen::all, 1); 65 | Eigen::VectorXd b3 = B(Eigen::all, 2); 66 | 67 | a1.array().colwise() *= b1.array(); 68 | a2.array().colwise() *= b2.array(); 69 | a3.array().colwise() *= b3.array(); 70 | 71 | A_out = a1 + a2 + a3; 72 | } 73 | 74 | 75 | /** 76 | * For each vertex on the target mesh find a match on the source mesh. 77 | * 78 | * V1: #V1 by 3 source mesh vertices 79 | * F1: #F1 by 3 source mesh triangles indices 80 | * N1: #V1 by 3 source mesh normals 81 | * V2: #V2 by 3 target mesh vertices 82 | * F2: #F2 by 3 target mesh triangles indices 83 | * N2: #V2 by 3 target mesh normals 84 | * W1: #V1 by num_bones source mesh skin weights 85 | * dDISTANCE_THRESHOLD_SQRD: distance threshold 86 | * dANGLE_THRESHOLD_DEGREES: normal threshold 87 | * Matched: #V2 array of bools, where Matched[i] is True if we found a good match for vertex i on the source mesh 88 | * W2: #V2 by num_bones, where W2[i,:] are skinning weights copied directly from source using closest point method 89 | */ 90 | void find_matches_closest_surface(const Eigen::MatrixXd& V1, const Eigen::MatrixXi& F1, const Eigen::MatrixXd& N1, 91 | const Eigen::MatrixXd& V2, const Eigen::MatrixXi& F2, const Eigen::MatrixXd& N2, 92 | const Eigen::MatrixXd& W1, 93 | double dDISTANCE_THRESHOLD_SQRD, 94 | double dANGLE_THRESHOLD_DEGREES, 95 | Eigen::MatrixXd& W2, 96 | Eigen::Array& Matched) 97 | { 98 | Matched = Eigen::Array::Constant(V2.rows(), false); 99 | Eigen::VectorXd sqrD; 100 | Eigen::VectorXi I; 101 | Eigen::MatrixXd C, B; 102 | find_closest_point_on_surface(V2, V1, F1, sqrD, I, C, B); 103 | 104 | // for each closest point on the source, interpolate its per-vertex attributes(skin weights and normals) 105 | // using the barycentric coordinates 106 | interpolate_attribute_from_bary(W1, B, I, F1, W2); 107 | 108 | Eigen::MatrixXd N1_match_interpolated; 109 | interpolate_attribute_from_bary(N1, B, I, F1, N1_match_interpolated); 110 | 111 | // check that the closest point passes our distance and normal thresholds 112 | for (int RowIdx = 0; RowIdx < V2.rows(); ++RowIdx) 113 | { 114 | Eigen::VectorXd n1 = N1_match_interpolated.row(RowIdx); 115 | n1.normalize(); 116 | 117 | Eigen::VectorXd n2 = N2.row(RowIdx); 118 | n2.normalize(); 119 | 120 | const double rad_angle = acos(n1.dot(n2)); 121 | const double deg_angle = rad_angle * (180.0 / M_PI); 122 | 123 | if (sqrD(RowIdx) <= dDISTANCE_THRESHOLD_SQRD and deg_angle <= dANGLE_THRESHOLD_DEGREES) 124 | { 125 | Matched(RowIdx) = true; 126 | } 127 | } 128 | } 129 | 130 | 131 | /** 132 | * Inpaint weights for all the vertices on the target mesh for which we didnt 133 | * find a good match on the source (i.e. Matched[i] == False). 134 | * 135 | * V2: #V2 by 3 target mesh vertices 136 | * F2: #F2 by 3 target mesh triangles indices 137 | * W2: #V2 by num_bones, where W2[i,:] are skinning weights copied directly from source using closest point method 138 | * Matched: #V2 array of bools, where Matched[i] is True if we found a good match for vertex i on the source mesh 139 | * W_inpainted: #V2 by num_bones, final skinning weights where we inpainted weights for all vertices i where Matched[i] == False 140 | * success: true if inpainting succeeded, false otherwise 141 | */ 142 | bool inpaint(const Eigen::MatrixXd& V2, const Eigen::MatrixXi& F2, const Eigen::MatrixXd& W2, const Eigen::Array& Matched, Eigen::MatrixXd& W_inpainted) 143 | { 144 | // Compute the laplacian 145 | 146 | Eigen::SparseMatrix L, M, Minv; 147 | igl::cotmatrix(V2, F2, L); 148 | igl::massmatrix(V2, F2, igl::MassMatrixType::MASSMATRIX_TYPE_VORONOI, M); 149 | igl::invert_diag(M, Minv); 150 | 151 | Eigen::SparseMatrix Q = -L + L*Minv*L; 152 | 153 | Eigen::SparseMatrix Aeq; 154 | 155 | Eigen::VectorXd Beq; 156 | 157 | Eigen::MatrixXd B = Eigen::MatrixXd::Zero(L.rows(), W2.cols()); 158 | 159 | Eigen::VectorXi b_all = Eigen::VectorXi::LinSpaced(V2.rows(), 0, V2.rows()-1); 160 | 161 | Eigen::VectorXi b; 162 | igl::slice_mask(b_all, Matched, 1, b); 163 | 164 | Eigen::MatrixXd bc; 165 | igl::slice_mask(W2, Matched, 1, bc); 166 | 167 | igl::min_quad_with_fixed_data mqwf; 168 | igl::min_quad_with_fixed_precompute(Q,b,Aeq,true,mqwf); 169 | 170 | bool result = igl::min_quad_with_fixed_solve(mqwf,B,bc,Beq,W_inpainted); 171 | 172 | return result; 173 | } 174 | 175 | 176 | /** 177 | * Smooth weights in the areas for which weights were inpainted and also their close neighbours. 178 | * 179 | * V2: #V2 by 3 target mesh vertices 180 | * F2: #F2 by 3 target mesh triangles indices 181 | * W2: #V2 by num_bones skinning weights 182 | * Matched: #V2 array of bools, where Matched[i] is True if we found a good match for vertex i on the source mesh 183 | * dDISTANCE_THRESHOLD_SQRD: scalar distance threshold 184 | * num_smooth_iter_steps: scalar number of smoothing steps 185 | * smooth_alpha: scalar the smoothing strength 186 | * W2_smoothed: #V2 by num_bones new smoothed weights 187 | * VIDs_to_smooth: 1D array of vertex IDs for which smoothing was applied 188 | */ 189 | void smooth(Eigen::MatrixXd& W2_smoothed, 190 | Eigen::Array& VIDs_to_smooth, 191 | const Eigen::MatrixXd& V2, 192 | const Eigen::MatrixXi& F2, 193 | const Eigen::MatrixXd& W2, 194 | const Eigen::Array& Matched, 195 | const double dDISTANCE_THRESHOLD, 196 | const double num_smooth_iter_steps=10, 197 | const double smooth_alpha=0.2) 198 | { 199 | Eigen::Array NotMatched = Matched == false; 200 | VIDs_to_smooth = NotMatched; //.array(NotMatched, copy=True) 201 | 202 | std::vector > adj_list; 203 | igl::adjacency_list(F2, adj_list); 204 | 205 | auto get_points_within_distance = [&](const Eigen::MatrixXd& V, const int VID, const double distance) 206 | { 207 | // Get all neighbours of vertex VID within dDISTANCE_THRESHOLD 208 | std::queue queue; 209 | queue.push(VID); 210 | 211 | std::set visited; 212 | visited.insert(VID); 213 | while (!queue.empty()) 214 | { 215 | const int vv = queue.front(); 216 | queue.pop(); 217 | 218 | auto neigh = adj_list[vv]; 219 | for (auto nn : neigh) 220 | { 221 | if (!VIDs_to_smooth[nn] && (V.row(VID) - V.row(nn)).norm() < distance) 222 | { 223 | VIDs_to_smooth[nn] = true; 224 | if (visited.find(nn) == visited.end()) 225 | { 226 | queue.push(nn); 227 | visited.insert(nn); 228 | } 229 | } 230 | } 231 | } 232 | }; 233 | 234 | for (int i = 0; i < V2.rows(); ++i) 235 | { 236 | if (NotMatched[i]) 237 | { 238 | get_points_within_distance(V2, i, dDISTANCE_THRESHOLD); 239 | } 240 | } 241 | 242 | W2_smoothed = W2; 243 | 244 | for (int step_idx = 0; step_idx < num_smooth_iter_steps; ++step_idx) 245 | { 246 | for (int i = 0; i < V2.rows(); ++i) 247 | { 248 | if (VIDs_to_smooth[i]) 249 | { 250 | auto neigh = adj_list[i]; 251 | int num = neigh.size(); 252 | Eigen::VectorXd weight = W2_smoothed.row(i); 253 | 254 | Eigen::VectorXd new_weight = (1.0-smooth_alpha)*weight; 255 | 256 | for (auto influence_idx : neigh) 257 | { 258 | Eigen::VectorXd weight_connected = W2_smoothed.row(influence_idx); 259 | new_weight = new_weight + (smooth_alpha/num) * weight_connected; 260 | } 261 | 262 | W2_smoothed.row(i) = new_weight; 263 | } 264 | } 265 | } 266 | } 267 | 268 | 269 | /** 270 | * Perform robust weight transfer from the source mesh to the target mesh as described 271 | * in https://www.dgp.toronto.edu/~rinat/projects/RobustSkinWeightsTransfer/index.html. 272 | * 273 | * For every vertex on the target mesh, find the closest point on the surface of the source mesh. If that point 274 | * is within the SearchRadius, and their normals differ by less than the NormalThreshold, then we directly copy the 275 | * weights from the source point to the target mesh vertex. For all the vertices we didn't copy the weights directly, 276 | * automatically compute the smooth weights. 277 | * 278 | * V1: #V1 by 3 source mesh vertices 279 | * F1: #F1 by 3 source mesh triangles indices 280 | * W1: #V1 by num_bones source mesh skin weights 281 | * 282 | * V2: #V2 by 3 target mesh vertices 283 | * F2: #F2 by 3 target mesh triangles indices 284 | * W2: #V2 by num_bones target mesh skin weights that were transferred from the source mesh 285 | * 286 | * SearchRadius: Radius for searching the closest point. 287 | * NormalThreshold: Maximum angle (in degrees) difference between target and source point normals to be considred a match. 288 | * 289 | * num_smooth_iter_steps: The number of optional post-processing smoothing iterations applied to the vertices without the match. 290 | * smooth_alpha: The strength of each post-processing smoothing iteration. 291 | */ 292 | bool robust_skin_weights_transfer(const Eigen::MatrixXd& V1, 293 | const Eigen::MatrixXi& F1, 294 | const Eigen::MatrixXd& W1, 295 | const Eigen::MatrixXd& V2, 296 | const Eigen::MatrixXi& F2, 297 | Eigen::MatrixXd& W2, 298 | const double SearchRadius, 299 | const double NormalThreshold, 300 | const int num_smooth_iter_steps, 301 | const double smooth_alpha) 302 | { 303 | Eigen::MatrixXd N1, N2; 304 | igl::per_vertex_normals(V1, F1, N1); 305 | igl::per_vertex_normals(V2, F2, N2); 306 | 307 | const int num_bones = W1.cols(); 308 | 309 | const double SearchRadiusSqrd = SearchRadius * SearchRadius; 310 | 311 | Eigen::Array Matched; 312 | Eigen::MatrixXd SkinWeights_interpolated; 313 | find_matches_closest_surface(V1, F1, N1, V2, F2, N2, W1, SearchRadiusSqrd, NormalThreshold, SkinWeights_interpolated, Matched); 314 | 315 | Eigen::MatrixXd InpaintedWeights; 316 | const bool success = inpaint(V2, F2, SkinWeights_interpolated, Matched, InpaintedWeights); 317 | 318 | if (success) 319 | { 320 | Eigen::MatrixXd SmoothedInpaintedWeights; 321 | Eigen::Array VIDs_to_smooth; 322 | smooth(SmoothedInpaintedWeights, VIDs_to_smooth, V2, F2, InpaintedWeights, Matched, SearchRadius, num_smooth_iter_steps, smooth_alpha); 323 | 324 | W2 = SmoothedInpaintedWeights; 325 | 326 | return true; 327 | } 328 | else 329 | { 330 | return false; 331 | } 332 | } 333 | 334 | /** EXAMPLE RUN */ 335 | int main(int argc, char *argv[]) 336 | { 337 | // Load source and target meshes 338 | Eigen::MatrixXd V1, V2; 339 | Eigen::MatrixXi F1, F2; 340 | igl::read_triangle_mesh("../../meshes/sphere.obj", V1, F1); 341 | igl::read_triangle_mesh("../../meshes/grid.obj", V2, F2); 342 | 343 | // Dummy skinning weights 344 | Eigen::MatrixXd W1 = Eigen::MatrixXd::Zero(V1.rows(), 2); 345 | W1.col(0) = 0.3 * Eigen::VectorXd::Ones(V1.rows()); 346 | W1.col(1) = 0.7 * Eigen::VectorXd::Ones(V1.rows()); 347 | 348 | const double dDISTANCE_THRESHOLD = 0.05*igl::bounding_box_diagonal(V2); // threshold distance D 349 | const double dANGLE_THRESHOLD_DEGREES = 30; // threshold angle theta in degrees 350 | 351 | Eigen::MatrixXd InpaintedWeights; 352 | const bool success = robust_skin_weights_transfer(V1, F1, W1, 353 | V2, F2, InpaintedWeights, 354 | dDISTANCE_THRESHOLD, 355 | dANGLE_THRESHOLD_DEGREES, 356 | 10, 0.2); 357 | 358 | if (success) 359 | { 360 | std::cout << "Computation Succeeded" << std::endl; 361 | std::cout << InpaintedWeights << std::endl; 362 | } 363 | else 364 | { 365 | std::cout << "Computation Failed" << std::endl; 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /meshes/grid.obj: -------------------------------------------------------------------------------- 1 | # 256 points 2 | # 1350 vertices 3 | # 450 primitives 4 | # Bounds: [-0.870227158, 0.555136383, -0.870227158] to [0.870227158, 0.555136383, 0.870227158] 5 | g 6 | v -0.870227158 0.555136383 -0.870227158 7 | v -0.754196882 0.555136383 -0.870227158 8 | v -0.638166547 0.555136383 -0.870227158 9 | v -0.522136271 0.555136383 -0.870227158 10 | v -0.406105995 0.555136383 -0.870227158 11 | v -0.29007569 0.555136383 -0.870227158 12 | v -0.174045429 0.555136383 -0.870227158 13 | v -0.0580150858 0.555136383 -0.870227158 14 | v 0.0580151714 0.555136383 -0.870227158 15 | v 0.174045429 0.555136383 -0.870227158 16 | v 0.290075779 0.555136383 -0.870227158 17 | v 0.406106025 0.555136383 -0.870227158 18 | v 0.522136271 0.555136383 -0.870227158 19 | v 0.638166606 0.555136383 -0.870227158 20 | v 0.754196942 0.555136383 -0.870227158 21 | v 0.870227158 0.555136383 -0.870227158 22 | v -0.870227158 0.555136383 -0.754196882 23 | v -0.754196882 0.555136383 -0.754196882 24 | v -0.638166547 0.555136383 -0.754196882 25 | v -0.522136271 0.555136383 -0.754196882 26 | v -0.406105995 0.555136383 -0.754196882 27 | v -0.29007569 0.555136383 -0.754196882 28 | v -0.174045429 0.555136383 -0.754196882 29 | v -0.0580150858 0.555136383 -0.754196882 30 | v 0.0580151714 0.555136383 -0.754196882 31 | v 0.174045429 0.555136383 -0.754196882 32 | v 0.290075779 0.555136383 -0.754196882 33 | v 0.406106025 0.555136383 -0.754196882 34 | v 0.522136271 0.555136383 -0.754196882 35 | v 0.638166606 0.555136383 -0.754196882 36 | v 0.754196942 0.555136383 -0.754196882 37 | v 0.870227158 0.555136383 -0.754196882 38 | v -0.870227158 0.555136383 -0.638166547 39 | v -0.754196882 0.555136383 -0.638166547 40 | v -0.638166547 0.555136383 -0.638166547 41 | v -0.522136271 0.555136383 -0.638166547 42 | v -0.406105995 0.555136383 -0.638166547 43 | v -0.29007569 0.555136383 -0.638166547 44 | v -0.174045429 0.555136383 -0.638166547 45 | v -0.0580150858 0.555136383 -0.638166547 46 | v 0.0580151714 0.555136383 -0.638166547 47 | v 0.174045429 0.555136383 -0.638166547 48 | v 0.290075779 0.555136383 -0.638166547 49 | v 0.406106025 0.555136383 -0.638166547 50 | v 0.522136271 0.555136383 -0.638166547 51 | v 0.638166606 0.555136383 -0.638166547 52 | v 0.754196942 0.555136383 -0.638166547 53 | v 0.870227158 0.555136383 -0.638166547 54 | v -0.870227158 0.555136383 -0.522136271 55 | v -0.754196882 0.555136383 -0.522136271 56 | v -0.638166547 0.555136383 -0.522136271 57 | v -0.522136271 0.555136383 -0.522136271 58 | v -0.406105995 0.555136383 -0.522136271 59 | v -0.29007569 0.555136383 -0.522136271 60 | v -0.174045429 0.555136383 -0.522136271 61 | v -0.0580150858 0.555136383 -0.522136271 62 | v 0.0580151714 0.555136383 -0.522136271 63 | v 0.174045429 0.555136383 -0.522136271 64 | v 0.290075779 0.555136383 -0.522136271 65 | v 0.406106025 0.555136383 -0.522136271 66 | v 0.522136271 0.555136383 -0.522136271 67 | v 0.638166606 0.555136383 -0.522136271 68 | v 0.754196942 0.555136383 -0.522136271 69 | v 0.870227158 0.555136383 -0.522136271 70 | v -0.870227158 0.555136383 -0.406105995 71 | v -0.754196882 0.555136383 -0.406105995 72 | v -0.638166547 0.555136383 -0.406105995 73 | v -0.522136271 0.555136383 -0.406105995 74 | v -0.406105995 0.555136383 -0.406105995 75 | v -0.29007569 0.555136383 -0.406105995 76 | v -0.174045429 0.555136383 -0.406105995 77 | v -0.0580150858 0.555136383 -0.406105995 78 | v 0.0580151714 0.555136383 -0.406105995 79 | v 0.174045429 0.555136383 -0.406105995 80 | v 0.290075779 0.555136383 -0.406105995 81 | v 0.406106025 0.555136383 -0.406105995 82 | v 0.522136271 0.555136383 -0.406105995 83 | v 0.638166606 0.555136383 -0.406105995 84 | v 0.754196942 0.555136383 -0.406105995 85 | v 0.870227158 0.555136383 -0.406105995 86 | v -0.870227158 0.555136383 -0.29007569 87 | v -0.754196882 0.555136383 -0.29007569 88 | v -0.638166547 0.555136383 -0.29007569 89 | v -0.522136271 0.555136383 -0.29007569 90 | v -0.406105995 0.555136383 -0.29007569 91 | v -0.29007569 0.555136383 -0.29007569 92 | v -0.174045429 0.555136383 -0.29007569 93 | v -0.0580150858 0.555136383 -0.29007569 94 | v 0.0580151714 0.555136383 -0.29007569 95 | v 0.174045429 0.555136383 -0.29007569 96 | v 0.290075779 0.555136383 -0.29007569 97 | v 0.406106025 0.555136383 -0.29007569 98 | v 0.522136271 0.555136383 -0.29007569 99 | v 0.638166606 0.555136383 -0.29007569 100 | v 0.754196942 0.555136383 -0.29007569 101 | v 0.870227158 0.555136383 -0.29007569 102 | v -0.870227158 0.555136383 -0.174045429 103 | v -0.754196882 0.555136383 -0.174045429 104 | v -0.638166547 0.555136383 -0.174045429 105 | v -0.522136271 0.555136383 -0.174045429 106 | v -0.406105995 0.555136383 -0.174045429 107 | v -0.29007569 0.555136383 -0.174045429 108 | v -0.174045429 0.555136383 -0.174045429 109 | v -0.0580150858 0.555136383 -0.174045429 110 | v 0.0580151714 0.555136383 -0.174045429 111 | v 0.174045429 0.555136383 -0.174045429 112 | v 0.290075779 0.555136383 -0.174045429 113 | v 0.406106025 0.555136383 -0.174045429 114 | v 0.522136271 0.555136383 -0.174045429 115 | v 0.638166606 0.555136383 -0.174045429 116 | v 0.754196942 0.555136383 -0.174045429 117 | v 0.870227158 0.555136383 -0.174045429 118 | v -0.870227158 0.555136383 -0.0580150858 119 | v -0.754196882 0.555136383 -0.0580150858 120 | v -0.638166547 0.555136383 -0.0580150858 121 | v -0.522136271 0.555136383 -0.0580150858 122 | v -0.406105995 0.555136383 -0.0580150858 123 | v -0.29007569 0.555136383 -0.0580150858 124 | v -0.174045429 0.555136383 -0.0580150858 125 | v -0.0580150858 0.555136383 -0.0580150858 126 | v 0.0580151714 0.555136383 -0.0580150858 127 | v 0.174045429 0.555136383 -0.0580150858 128 | v 0.290075779 0.555136383 -0.0580150858 129 | v 0.406106025 0.555136383 -0.0580150858 130 | v 0.522136271 0.555136383 -0.0580150858 131 | v 0.638166606 0.555136383 -0.0580150858 132 | v 0.754196942 0.555136383 -0.0580150858 133 | v 0.870227158 0.555136383 -0.0580150858 134 | v -0.870227158 0.555136383 0.0580151714 135 | v -0.754196882 0.555136383 0.0580151714 136 | v -0.638166547 0.555136383 0.0580151714 137 | v -0.522136271 0.555136383 0.0580151714 138 | v -0.406105995 0.555136383 0.0580151714 139 | v -0.29007569 0.555136383 0.0580151714 140 | v -0.174045429 0.555136383 0.0580151714 141 | v -0.0580150858 0.555136383 0.0580151714 142 | v 0.0580151714 0.555136383 0.0580151714 143 | v 0.174045429 0.555136383 0.0580151714 144 | v 0.290075779 0.555136383 0.0580151714 145 | v 0.406106025 0.555136383 0.0580151714 146 | v 0.522136271 0.555136383 0.0580151714 147 | v 0.638166606 0.555136383 0.0580151714 148 | v 0.754196942 0.555136383 0.0580151714 149 | v 0.870227158 0.555136383 0.0580151714 150 | v -0.870227158 0.555136383 0.174045429 151 | v -0.754196882 0.555136383 0.174045429 152 | v -0.638166547 0.555136383 0.174045429 153 | v -0.522136271 0.555136383 0.174045429 154 | v -0.406105995 0.555136383 0.174045429 155 | v -0.29007569 0.555136383 0.174045429 156 | v -0.174045429 0.555136383 0.174045429 157 | v -0.0580150858 0.555136383 0.174045429 158 | v 0.0580151714 0.555136383 0.174045429 159 | v 0.174045429 0.555136383 0.174045429 160 | v 0.290075779 0.555136383 0.174045429 161 | v 0.406106025 0.555136383 0.174045429 162 | v 0.522136271 0.555136383 0.174045429 163 | v 0.638166606 0.555136383 0.174045429 164 | v 0.754196942 0.555136383 0.174045429 165 | v 0.870227158 0.555136383 0.174045429 166 | v -0.870227158 0.555136383 0.290075779 167 | v -0.754196882 0.555136383 0.290075779 168 | v -0.638166547 0.555136383 0.290075779 169 | v -0.522136271 0.555136383 0.290075779 170 | v -0.406105995 0.555136383 0.290075779 171 | v -0.29007569 0.555136383 0.290075779 172 | v -0.174045429 0.555136383 0.290075779 173 | v -0.0580150858 0.555136383 0.290075779 174 | v 0.0580151714 0.555136383 0.290075779 175 | v 0.174045429 0.555136383 0.290075779 176 | v 0.290075779 0.555136383 0.290075779 177 | v 0.406106025 0.555136383 0.290075779 178 | v 0.522136271 0.555136383 0.290075779 179 | v 0.638166606 0.555136383 0.290075779 180 | v 0.754196942 0.555136383 0.290075779 181 | v 0.870227158 0.555136383 0.290075779 182 | v -0.870227158 0.555136383 0.406106025 183 | v -0.754196882 0.555136383 0.406106025 184 | v -0.638166547 0.555136383 0.406106025 185 | v -0.522136271 0.555136383 0.406106025 186 | v -0.406105995 0.555136383 0.406106025 187 | v -0.29007569 0.555136383 0.406106025 188 | v -0.174045429 0.555136383 0.406106025 189 | v -0.0580150858 0.555136383 0.406106025 190 | v 0.0580151714 0.555136383 0.406106025 191 | v 0.174045429 0.555136383 0.406106025 192 | v 0.290075779 0.555136383 0.406106025 193 | v 0.406106025 0.555136383 0.406106025 194 | v 0.522136271 0.555136383 0.406106025 195 | v 0.638166606 0.555136383 0.406106025 196 | v 0.754196942 0.555136383 0.406106025 197 | v 0.870227158 0.555136383 0.406106025 198 | v -0.870227158 0.555136383 0.522136271 199 | v -0.754196882 0.555136383 0.522136271 200 | v -0.638166547 0.555136383 0.522136271 201 | v -0.522136271 0.555136383 0.522136271 202 | v -0.406105995 0.555136383 0.522136271 203 | v -0.29007569 0.555136383 0.522136271 204 | v -0.174045429 0.555136383 0.522136271 205 | v -0.0580150858 0.555136383 0.522136271 206 | v 0.0580151714 0.555136383 0.522136271 207 | v 0.174045429 0.555136383 0.522136271 208 | v 0.290075779 0.555136383 0.522136271 209 | v 0.406106025 0.555136383 0.522136271 210 | v 0.522136271 0.555136383 0.522136271 211 | v 0.638166606 0.555136383 0.522136271 212 | v 0.754196942 0.555136383 0.522136271 213 | v 0.870227158 0.555136383 0.522136271 214 | v -0.870227158 0.555136383 0.638166606 215 | v -0.754196882 0.555136383 0.638166606 216 | v -0.638166547 0.555136383 0.638166606 217 | v -0.522136271 0.555136383 0.638166606 218 | v -0.406105995 0.555136383 0.638166606 219 | v -0.29007569 0.555136383 0.638166606 220 | v -0.174045429 0.555136383 0.638166606 221 | v -0.0580150858 0.555136383 0.638166606 222 | v 0.0580151714 0.555136383 0.638166606 223 | v 0.174045429 0.555136383 0.638166606 224 | v 0.290075779 0.555136383 0.638166606 225 | v 0.406106025 0.555136383 0.638166606 226 | v 0.522136271 0.555136383 0.638166606 227 | v 0.638166606 0.555136383 0.638166606 228 | v 0.754196942 0.555136383 0.638166606 229 | v 0.870227158 0.555136383 0.638166606 230 | v -0.870227158 0.555136383 0.754196942 231 | v -0.754196882 0.555136383 0.754196942 232 | v -0.638166547 0.555136383 0.754196942 233 | v -0.522136271 0.555136383 0.754196942 234 | v -0.406105995 0.555136383 0.754196942 235 | v -0.29007569 0.555136383 0.754196942 236 | v -0.174045429 0.555136383 0.754196942 237 | v -0.0580150858 0.555136383 0.754196942 238 | v 0.0580151714 0.555136383 0.754196942 239 | v 0.174045429 0.555136383 0.754196942 240 | v 0.290075779 0.555136383 0.754196942 241 | v 0.406106025 0.555136383 0.754196942 242 | v 0.522136271 0.555136383 0.754196942 243 | v 0.638166606 0.555136383 0.754196942 244 | v 0.754196942 0.555136383 0.754196942 245 | v 0.870227158 0.555136383 0.754196942 246 | v -0.870227158 0.555136383 0.870227158 247 | v -0.754196882 0.555136383 0.870227158 248 | v -0.638166547 0.555136383 0.870227158 249 | v -0.522136271 0.555136383 0.870227158 250 | v -0.406105995 0.555136383 0.870227158 251 | v -0.29007569 0.555136383 0.870227158 252 | v -0.174045429 0.555136383 0.870227158 253 | v -0.0580150858 0.555136383 0.870227158 254 | v 0.0580151714 0.555136383 0.870227158 255 | v 0.174045429 0.555136383 0.870227158 256 | v 0.290075779 0.555136383 0.870227158 257 | v 0.406106025 0.555136383 0.870227158 258 | v 0.522136271 0.555136383 0.870227158 259 | v 0.638166606 0.555136383 0.870227158 260 | v 0.754196942 0.555136383 0.870227158 261 | v 0.870227158 0.555136383 0.870227158 262 | g 263 | f 1 18 2 264 | f 1 17 18 265 | f 2 19 3 266 | f 2 18 19 267 | f 3 20 4 268 | f 3 19 20 269 | f 4 21 5 270 | f 4 20 21 271 | f 5 22 6 272 | f 5 21 22 273 | f 6 23 7 274 | f 6 22 23 275 | f 7 24 8 276 | f 7 23 24 277 | f 8 25 9 278 | f 8 24 25 279 | f 9 26 10 280 | f 9 25 26 281 | f 10 27 11 282 | f 10 26 27 283 | f 11 28 12 284 | f 11 27 28 285 | f 12 29 13 286 | f 12 28 29 287 | f 13 30 14 288 | f 13 29 30 289 | f 14 31 15 290 | f 14 30 31 291 | f 15 32 16 292 | f 15 31 32 293 | f 17 34 18 294 | f 17 33 34 295 | f 18 35 19 296 | f 18 34 35 297 | f 19 36 20 298 | f 19 35 36 299 | f 20 37 21 300 | f 20 36 37 301 | f 21 38 22 302 | f 21 37 38 303 | f 22 39 23 304 | f 22 38 39 305 | f 23 40 24 306 | f 23 39 40 307 | f 24 41 25 308 | f 24 40 41 309 | f 25 42 26 310 | f 25 41 42 311 | f 26 43 27 312 | f 26 42 43 313 | f 27 44 28 314 | f 27 43 44 315 | f 28 45 29 316 | f 28 44 45 317 | f 29 46 30 318 | f 29 45 46 319 | f 30 47 31 320 | f 30 46 47 321 | f 31 48 32 322 | f 31 47 48 323 | f 33 50 34 324 | f 33 49 50 325 | f 34 51 35 326 | f 34 50 51 327 | f 35 52 36 328 | f 35 51 52 329 | f 36 53 37 330 | f 36 52 53 331 | f 37 54 38 332 | f 37 53 54 333 | f 38 55 39 334 | f 38 54 55 335 | f 39 56 40 336 | f 39 55 56 337 | f 40 57 41 338 | f 40 56 57 339 | f 41 58 42 340 | f 41 57 58 341 | f 42 59 43 342 | f 42 58 59 343 | f 43 60 44 344 | f 43 59 60 345 | f 44 61 45 346 | f 44 60 61 347 | f 45 62 46 348 | f 45 61 62 349 | f 46 63 47 350 | f 46 62 63 351 | f 47 64 48 352 | f 47 63 64 353 | f 49 66 50 354 | f 49 65 66 355 | f 50 67 51 356 | f 50 66 67 357 | f 51 68 52 358 | f 51 67 68 359 | f 52 69 53 360 | f 52 68 69 361 | f 53 70 54 362 | f 53 69 70 363 | f 54 71 55 364 | f 54 70 71 365 | f 55 72 56 366 | f 55 71 72 367 | f 56 73 57 368 | f 56 72 73 369 | f 57 74 58 370 | f 57 73 74 371 | f 58 75 59 372 | f 58 74 75 373 | f 59 76 60 374 | f 59 75 76 375 | f 60 77 61 376 | f 60 76 77 377 | f 61 78 62 378 | f 61 77 78 379 | f 62 79 63 380 | f 62 78 79 381 | f 63 80 64 382 | f 63 79 80 383 | f 65 82 66 384 | f 65 81 82 385 | f 66 83 67 386 | f 66 82 83 387 | f 67 84 68 388 | f 67 83 84 389 | f 68 85 69 390 | f 68 84 85 391 | f 69 86 70 392 | f 69 85 86 393 | f 70 87 71 394 | f 70 86 87 395 | f 71 88 72 396 | f 71 87 88 397 | f 72 89 73 398 | f 72 88 89 399 | f 73 90 74 400 | f 73 89 90 401 | f 74 91 75 402 | f 74 90 91 403 | f 75 92 76 404 | f 75 91 92 405 | f 76 93 77 406 | f 76 92 93 407 | f 77 94 78 408 | f 77 93 94 409 | f 78 95 79 410 | f 78 94 95 411 | f 79 96 80 412 | f 79 95 96 413 | f 81 98 82 414 | f 81 97 98 415 | f 82 99 83 416 | f 82 98 99 417 | f 83 100 84 418 | f 83 99 100 419 | f 84 101 85 420 | f 84 100 101 421 | f 85 102 86 422 | f 85 101 102 423 | f 86 103 87 424 | f 86 102 103 425 | f 87 104 88 426 | f 87 103 104 427 | f 88 105 89 428 | f 88 104 105 429 | f 89 106 90 430 | f 89 105 106 431 | f 90 107 91 432 | f 90 106 107 433 | f 91 108 92 434 | f 91 107 108 435 | f 92 109 93 436 | f 92 108 109 437 | f 93 110 94 438 | f 93 109 110 439 | f 94 111 95 440 | f 94 110 111 441 | f 95 112 96 442 | f 95 111 112 443 | f 97 114 98 444 | f 97 113 114 445 | f 98 115 99 446 | f 98 114 115 447 | f 99 116 100 448 | f 99 115 116 449 | f 100 117 101 450 | f 100 116 117 451 | f 101 118 102 452 | f 101 117 118 453 | f 102 119 103 454 | f 102 118 119 455 | f 103 120 104 456 | f 103 119 120 457 | f 104 121 105 458 | f 104 120 121 459 | f 105 122 106 460 | f 105 121 122 461 | f 106 123 107 462 | f 106 122 123 463 | f 107 124 108 464 | f 107 123 124 465 | f 108 125 109 466 | f 108 124 125 467 | f 109 126 110 468 | f 109 125 126 469 | f 110 127 111 470 | f 110 126 127 471 | f 111 128 112 472 | f 111 127 128 473 | f 113 130 114 474 | f 113 129 130 475 | f 114 131 115 476 | f 114 130 131 477 | f 115 132 116 478 | f 115 131 132 479 | f 116 133 117 480 | f 116 132 133 481 | f 117 134 118 482 | f 117 133 134 483 | f 118 135 119 484 | f 118 134 135 485 | f 119 136 120 486 | f 119 135 136 487 | f 120 137 121 488 | f 120 136 137 489 | f 121 138 122 490 | f 121 137 138 491 | f 122 139 123 492 | f 122 138 139 493 | f 123 140 124 494 | f 123 139 140 495 | f 124 141 125 496 | f 124 140 141 497 | f 125 142 126 498 | f 125 141 142 499 | f 126 143 127 500 | f 126 142 143 501 | f 127 144 128 502 | f 127 143 144 503 | f 129 146 130 504 | f 129 145 146 505 | f 130 147 131 506 | f 130 146 147 507 | f 131 148 132 508 | f 131 147 148 509 | f 132 149 133 510 | f 132 148 149 511 | f 133 150 134 512 | f 133 149 150 513 | f 134 151 135 514 | f 134 150 151 515 | f 135 152 136 516 | f 135 151 152 517 | f 136 153 137 518 | f 136 152 153 519 | f 137 154 138 520 | f 137 153 154 521 | f 138 155 139 522 | f 138 154 155 523 | f 139 156 140 524 | f 139 155 156 525 | f 140 157 141 526 | f 140 156 157 527 | f 141 158 142 528 | f 141 157 158 529 | f 142 159 143 530 | f 142 158 159 531 | f 143 160 144 532 | f 143 159 160 533 | f 145 162 146 534 | f 145 161 162 535 | f 146 163 147 536 | f 146 162 163 537 | f 147 164 148 538 | f 147 163 164 539 | f 148 165 149 540 | f 148 164 165 541 | f 149 166 150 542 | f 149 165 166 543 | f 150 167 151 544 | f 150 166 167 545 | f 151 168 152 546 | f 151 167 168 547 | f 152 169 153 548 | f 152 168 169 549 | f 153 170 154 550 | f 153 169 170 551 | f 154 171 155 552 | f 154 170 171 553 | f 155 172 156 554 | f 155 171 172 555 | f 156 173 157 556 | f 156 172 173 557 | f 157 174 158 558 | f 157 173 174 559 | f 158 175 159 560 | f 158 174 175 561 | f 159 176 160 562 | f 159 175 176 563 | f 161 178 162 564 | f 161 177 178 565 | f 162 179 163 566 | f 162 178 179 567 | f 163 180 164 568 | f 163 179 180 569 | f 164 181 165 570 | f 164 180 181 571 | f 165 182 166 572 | f 165 181 182 573 | f 166 183 167 574 | f 166 182 183 575 | f 167 184 168 576 | f 167 183 184 577 | f 168 185 169 578 | f 168 184 185 579 | f 169 186 170 580 | f 169 185 186 581 | f 170 187 171 582 | f 170 186 187 583 | f 171 188 172 584 | f 171 187 188 585 | f 172 189 173 586 | f 172 188 189 587 | f 173 190 174 588 | f 173 189 190 589 | f 174 191 175 590 | f 174 190 191 591 | f 175 192 176 592 | f 175 191 192 593 | f 177 194 178 594 | f 177 193 194 595 | f 178 195 179 596 | f 178 194 195 597 | f 179 196 180 598 | f 179 195 196 599 | f 180 197 181 600 | f 180 196 197 601 | f 181 198 182 602 | f 181 197 198 603 | f 182 199 183 604 | f 182 198 199 605 | f 183 200 184 606 | f 183 199 200 607 | f 184 201 185 608 | f 184 200 201 609 | f 185 202 186 610 | f 185 201 202 611 | f 186 203 187 612 | f 186 202 203 613 | f 187 204 188 614 | f 187 203 204 615 | f 188 205 189 616 | f 188 204 205 617 | f 189 206 190 618 | f 189 205 206 619 | f 190 207 191 620 | f 190 206 207 621 | f 191 208 192 622 | f 191 207 208 623 | f 193 210 194 624 | f 193 209 210 625 | f 194 211 195 626 | f 194 210 211 627 | f 195 212 196 628 | f 195 211 212 629 | f 196 213 197 630 | f 196 212 213 631 | f 197 214 198 632 | f 197 213 214 633 | f 198 215 199 634 | f 198 214 215 635 | f 199 216 200 636 | f 199 215 216 637 | f 200 217 201 638 | f 200 216 217 639 | f 201 218 202 640 | f 201 217 218 641 | f 202 219 203 642 | f 202 218 219 643 | f 203 220 204 644 | f 203 219 220 645 | f 204 221 205 646 | f 204 220 221 647 | f 205 222 206 648 | f 205 221 222 649 | f 206 223 207 650 | f 206 222 223 651 | f 207 224 208 652 | f 207 223 224 653 | f 209 226 210 654 | f 209 225 226 655 | f 210 227 211 656 | f 210 226 227 657 | f 211 228 212 658 | f 211 227 228 659 | f 212 229 213 660 | f 212 228 229 661 | f 213 230 214 662 | f 213 229 230 663 | f 214 231 215 664 | f 214 230 231 665 | f 215 232 216 666 | f 215 231 232 667 | f 216 233 217 668 | f 216 232 233 669 | f 217 234 218 670 | f 217 233 234 671 | f 218 235 219 672 | f 218 234 235 673 | f 219 236 220 674 | f 219 235 236 675 | f 220 237 221 676 | f 220 236 237 677 | f 221 238 222 678 | f 221 237 238 679 | f 222 239 223 680 | f 222 238 239 681 | f 223 240 224 682 | f 223 239 240 683 | f 225 242 226 684 | f 225 241 242 685 | f 226 243 227 686 | f 226 242 243 687 | f 227 244 228 688 | f 227 243 244 689 | f 228 245 229 690 | f 228 244 245 691 | f 229 246 230 692 | f 229 245 246 693 | f 230 247 231 694 | f 230 246 247 695 | f 231 248 232 696 | f 231 247 248 697 | f 232 249 233 698 | f 232 248 249 699 | f 233 250 234 700 | f 233 249 250 701 | f 234 251 235 702 | f 234 250 251 703 | f 235 252 236 704 | f 235 251 252 705 | f 236 253 237 706 | f 236 252 253 707 | f 237 254 238 708 | f 237 253 254 709 | f 238 255 239 710 | f 238 254 255 711 | f 239 256 240 712 | f 239 255 256 713 | --------------------------------------------------------------------------------