Skip to content

Commit 3668b6c

Browse files
authored
Merge pull request #153 from kishoreVen/direct-delta-mush-python
Direct delta mush python binding
2 parents 7ab17f2 + f69815a commit 3668b6c

5 files changed

Lines changed: 695 additions & 2 deletions

File tree

cmake/PyiglDownloadExternal.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ function(pyigl_download_test_data)
3737
pyigl_download_project(test_data
3838
SOURCE_DIR "${PYLIBIGL_EXTERNAL}/../data"
3939
GIT_REPOSITORY https://github.com/libigl/libigl-tests-data
40-
GIT_TAG fa8ee953a64a560f24553dd8bda0c6cd4fbb5353
40+
GIT_TAG 19cedf96d70702d8b3a83eb27934780c542356fe
4141
)
4242
endfunction()
4343

@@ -46,7 +46,7 @@ function(pyigl_download_tutorial_data)
4646
pyigl_download_project(tutorial_data
4747
SOURCE_DIR "${PYLIBIGL_EXTERNAL}/../tutorial/data"
4848
GIT_REPOSITORY https://github.com/libigl/libigl-tutorial-data
49-
GIT_TAG 38bbed76692710af038b90c69bf33d6d0f99476d
49+
GIT_TAG c1f9ede366d02e3531ecbaec5e3769312f31cccd
5050
)
5151
endfunction()
5252

src/direct_delta_mush.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#include <common.h>
2+
#include <npe.h>
3+
#include <typedefs.h>
4+
#include <igl/direct_delta_mush.h>
5+
6+
const char* ds_direct_delta_mush = R"igl_Qu8mg5v7(
7+
Perform Direct Delta Mush Skinning.
8+
Computes Direct Delta Mesh Skinning (Variant 0) from
9+
"Direct Delta Mush Skinning and Variants"
10+
11+
Parameters
12+
----------
13+
v #V by 3 list of rest pose vertex positions
14+
t #E*4 by 3 list of bone pose transformations
15+
omega #V by #E*10 list of precomputated matrix values
16+
17+
Returns
18+
-------
19+
u #V by 3 list of output vertex positions
20+
21+
See also
22+
--------
23+
24+
25+
Notes
26+
-----
27+
None
28+
29+
Examples
30+
--------
31+
)igl_Qu8mg5v7";
32+
33+
npe_function(direct_delta_mush)
34+
npe_doc(ds_direct_delta_mush)
35+
36+
npe_arg(v, dense_double)
37+
npe_arg(t, dense_double)
38+
npe_arg(omega, dense_double)
39+
40+
npe_begin_code()
41+
assert_cols_equals(v, 3, "v");
42+
assert_valid_bone_transforms(t, "t");
43+
assert_rows_equals(t, (omega.cols() * 4) / 10, "t");
44+
45+
std::vector<Eigen::Affine3d, Eigen::aligned_allocator<Eigen::Affine3d>>
46+
t_affine(t.rows() / 4);
47+
48+
for(int bone = 0; bone < t_affine.size(); ++bone)
49+
{
50+
t_affine[bone] = Eigen::Affine3d::Identity();
51+
t_affine[bone].matrix().block(0, 0, 3, 4) = t.block(bone * 4, 0, 4, 3).transpose();
52+
}
53+
54+
EigenDenseLike<npe_Matrix_v> u;
55+
igl::direct_delta_mush(v, t_affine, omega, u);
56+
return npe::move(u);
57+
58+
npe_end_code()
59+
60+
const char* ds_direct_delta_mush_precomp = R"igl_Qu8mg5v7(
61+
Do the Omega precomputation necessary for Direct Delta Mush Skinning.
62+
63+
Parameters
64+
----------
65+
v #V by 3 list of rest pose vertex positions
66+
f #F by 3 list of triangle indices into rows of V
67+
w #V by #Edges list of weights
68+
p number of smoothing iterations
69+
lambda rotation smoothing step size
70+
kappa translation smoothness step size
71+
alpha translation smoothness blending weight
72+
73+
Returns
74+
-------
75+
omega : #V by #E*10 list of precomputated matrix values
76+
77+
See also
78+
--------
79+
80+
81+
Notes
82+
-----
83+
None
84+
85+
Examples
86+
--------
87+
)igl_Qu8mg5v7";
88+
89+
npe_function(direct_delta_mush_precomputation)
90+
npe_doc(ds_direct_delta_mush_precomp)
91+
92+
npe_arg(v, dense_double)
93+
npe_arg(f, dense_int, dense_long, dense_longlong)
94+
npe_arg(w, npe_matches(v))
95+
npe_arg(p, int)
96+
npe_arg(lambda, double)
97+
npe_arg(kappa, double)
98+
npe_arg(alpha, double)
99+
100+
npe_begin_code()
101+
assert_valid_3d_tri_mesh(v, f);
102+
103+
Eigen::MatrixXd w_copy = w.template cast<double>();
104+
105+
EigenDenseLike<npe_Matrix_v> omega;
106+
igl::direct_delta_mush_precomputation(v, f, w_copy, p, lambda, kappa, alpha, omega);
107+
return npe::move(omega);
108+
109+
npe_end_code()

src/include/common.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,21 @@ void assert_valid_2d_tri_mesh(const TV& v, const TF& f, std::string v_name="v",
311311
".shape = [" + std::to_string(f.rows()) + ", " + std::to_string(f.cols()) + "]");
312312
}
313313
}
314+
315+
template <typename TV>
316+
void assert_valid_bone_transforms(const TV& t, std::string t_name="t") {
317+
if (t.rows() <= 0) {
318+
throw pybind11::value_error("Invalid number of transforms, " + t_name + " has zero rows (" + t_name +
319+
".shape = [" + std::to_string(t.rows()) + ", " + std::to_string(t.cols()) + "]) ");
320+
}
321+
322+
if (t.cols() != 3) {
323+
throw pybind11::value_error("Invalid number of cols in transforms, " + t_name + " must have shape [#bones * 4, 3] but got " + t_name +
324+
".shape = [" + std::to_string(t.rows()) + ", " + std::to_string(t.cols()) + "]");
325+
}
326+
327+
if (t.rows() % 4 != 0) {
328+
throw pybind11::value_error("Invalid number of rows in transforms, " + t_name + " must have shape [#bones * 4, 3] but got " + t_name +
329+
".shape = [" + std::to_string(t.rows()) + ", " + std::to_string(t.cols()) + "].");
330+
}
331+
}

tests/test_basic.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,6 +1531,40 @@ def test_lbs_matrix(self):
15311531
self.assertTrue(M.shape[0] == V.shape[0])
15321532
self.assertTrue(M.shape[1] == W.shape[1]*4)
15331533

1534+
def test_direct_delta_mush(self):
1535+
V, F = igl.read_triangle_mesh(os.path.join(self.test_path, "arm.obj"))
1536+
W = igl.read_dmat(os.path.join(self.test_path, "arm-weights.dmat"))
1537+
_, BE, _, _, _, _ = igl.read_tgf(os.path.join(self.test_path, "arm.tgf"))
1538+
1539+
# Use same values as tutorial
1540+
# https://github.com/libigl/libigl/blob/main/tutorial/408_DirectDeltaMush/main.cpp
1541+
p = 20
1542+
l = 3
1543+
k = 1
1544+
a = 0.8
1545+
omega = igl.direct_delta_mush_precomputation(V, F,W, p, l, k, a)
1546+
1547+
self.assertTrue(omega.shape[0] == V.shape[0])
1548+
self.assertTrue(omega.shape[1] == BE.shape[0] * 10)
1549+
self.assertTrue(omega.dtype == np.double)
1550+
1551+
T = np.zeros((BE.shape[0]*4, 3))
1552+
I = np.eye(3)
1553+
for i in range(0, T.shape[0], 4):
1554+
T[i:i+3, :] = I
1555+
1556+
U = igl.direct_delta_mush(V, T, omega)
1557+
1558+
self.assertTrue(U.shape[0] == V.shape[0])
1559+
self.assertTrue(U.shape[1] == 3)
1560+
self.assertTrue(U.dtype == np.double)
1561+
self.assertFalse(np.isnan(U).any())
1562+
1563+
def test_direct_delta_mush_precomputation(self):
1564+
# covered in test_direct_delta_mush
1565+
pass
1566+
1567+
15341568
def test_point_mesh_squared_distance(self):
15351569
dist, i, c = igl.point_mesh_squared_distance(
15361570
np.array([0., 0., 0.]), self.v1, self.f1)

0 commit comments

Comments
 (0)