|
24 | 24 | #include <momentum/character_solver/skeleton_error_function.h> |
25 | 25 | #include <momentum/character_solver/state_error_function.h> |
26 | 26 | #include <momentum/character_solver/vertex_projection_error_function.h> |
| 27 | +#include <momentum/character_solver/vertex_vertex_distance_error_function.h> |
27 | 28 | #include <pymomentum/solver2/solver2_utility.h> |
28 | 29 |
|
29 | 30 | #include <fmt/format.h> |
@@ -1238,6 +1239,162 @@ distance is greater than zero (ie. the point being above).)") |
1238 | 1239 | py::arg("name") = std::optional<std::vector<std::string>>{}); |
1239 | 1240 | } |
1240 | 1241 |
|
| 1242 | +void defVertexVertexDistanceErrorFunction(py::module_& m) { |
| 1243 | + py::class_<mm::VertexVertexDistanceConstraintT<float>>( |
| 1244 | + m, "VertexVertexDistanceConstraint") |
| 1245 | + .def( |
| 1246 | + "__repr__", |
| 1247 | + [](const mm::VertexVertexDistanceConstraintT<float>& self) { |
| 1248 | + return fmt::format( |
| 1249 | + "VertexVertexDistanceConstraint(vertex_index1={}, vertex_index2={}, weight={}, target_distance={})", |
| 1250 | + self.vertexIndex1, |
| 1251 | + self.vertexIndex2, |
| 1252 | + self.weight, |
| 1253 | + self.targetDistance); |
| 1254 | + }) |
| 1255 | + .def_readonly( |
| 1256 | + "vertex_index1", |
| 1257 | + &mm::VertexVertexDistanceConstraintT<float>::vertexIndex1, |
| 1258 | + "The index of the first vertex") |
| 1259 | + .def_readonly( |
| 1260 | + "vertex_index2", |
| 1261 | + &mm::VertexVertexDistanceConstraintT<float>::vertexIndex2, |
| 1262 | + "The index of the second vertex") |
| 1263 | + .def_readonly( |
| 1264 | + "weight", |
| 1265 | + &mm::VertexVertexDistanceConstraintT<float>::weight, |
| 1266 | + "The weight of the constraint") |
| 1267 | + .def_readonly( |
| 1268 | + "target_distance", |
| 1269 | + &mm::VertexVertexDistanceConstraintT<float>::targetDistance, |
| 1270 | + "The target distance between the two vertices"); |
| 1271 | + |
| 1272 | + py::class_< |
| 1273 | + mm::VertexVertexDistanceErrorFunctionT<float>, |
| 1274 | + mm::SkeletonErrorFunction, |
| 1275 | + std::shared_ptr<mm::VertexVertexDistanceErrorFunctionT<float>>>( |
| 1276 | + m, |
| 1277 | + "VertexVertexDistanceErrorFunction", |
| 1278 | + R"(Error function that minimizes the distance between pairs of vertices on the character's mesh. |
| 1279 | +
|
| 1280 | +This is useful for constraints where you want to maintain specific distances between |
| 1281 | +different parts of the character mesh, such as keeping fingers at a certain distance |
| 1282 | +or maintaining the width of body parts.)") |
| 1283 | + .def( |
| 1284 | + "__repr__", |
| 1285 | + [](const mm::VertexVertexDistanceErrorFunctionT<float>& self) { |
| 1286 | + return fmt::format( |
| 1287 | + "VertexVertexDistanceErrorFunction(weight={}, num_constraints={})", |
| 1288 | + self.getWeight(), |
| 1289 | + self.numConstraints()); |
| 1290 | + }) |
| 1291 | + .def( |
| 1292 | + py::init<>( |
| 1293 | + [](const mm::Character& character, float weight) |
| 1294 | + -> std::shared_ptr< |
| 1295 | + mm::VertexVertexDistanceErrorFunctionT<float>> { |
| 1296 | + validateWeight(weight, "weight"); |
| 1297 | + auto result = std::make_shared< |
| 1298 | + mm::VertexVertexDistanceErrorFunctionT<float>>(character); |
| 1299 | + result->setWeight(weight); |
| 1300 | + return result; |
| 1301 | + }), |
| 1302 | + R"(Initialize a VertexVertexDistanceErrorFunction. |
| 1303 | +
|
| 1304 | + :param character: The character to use. |
| 1305 | + :param weight: The weight applied to the error function.)", |
| 1306 | + py::keep_alive<1, 2>(), |
| 1307 | + py::arg("character"), |
| 1308 | + py::kw_only(), |
| 1309 | + py::arg("weight") = 1.0f) |
| 1310 | + .def( |
| 1311 | + "add_constraint", |
| 1312 | + [](mm::VertexVertexDistanceErrorFunctionT<float>& self, |
| 1313 | + int vertexIndex1, |
| 1314 | + int vertexIndex2, |
| 1315 | + float weight, |
| 1316 | + float targetDistance) { |
| 1317 | + validateVertexIndex( |
| 1318 | + vertexIndex1, "vertex_index1", self.getCharacter()); |
| 1319 | + validateVertexIndex( |
| 1320 | + vertexIndex2, "vertex_index2", self.getCharacter()); |
| 1321 | + validateWeight(weight, "weight"); |
| 1322 | + self.addConstraint( |
| 1323 | + vertexIndex1, vertexIndex2, weight, targetDistance); |
| 1324 | + }, |
| 1325 | + R"(Adds a vertex-to-vertex distance constraint to the error function. |
| 1326 | +
|
| 1327 | + :param vertex_index1: The index of the first vertex. |
| 1328 | + :param vertex_index2: The index of the second vertex. |
| 1329 | + :param weight: The weight of the constraint. |
| 1330 | + :param target_distance: The desired distance between the two vertices.)", |
| 1331 | + py::arg("vertex_index1"), |
| 1332 | + py::arg("vertex_index2"), |
| 1333 | + py::arg("weight"), |
| 1334 | + py::arg("target_distance")) |
| 1335 | + .def( |
| 1336 | + "add_constraints", |
| 1337 | + [](mm::VertexVertexDistanceErrorFunctionT<float>& self, |
| 1338 | + const py::array_t<int>& vertexIndex1, |
| 1339 | + const py::array_t<int>& vertexIndex2, |
| 1340 | + const py::array_t<float>& weight, |
| 1341 | + const py::array_t<float>& targetDistance) { |
| 1342 | + ArrayShapeValidator validator; |
| 1343 | + const int nConsIdx = -1; |
| 1344 | + validator.validate( |
| 1345 | + vertexIndex1, "vertex_index1", {nConsIdx}, {"n_cons"}); |
| 1346 | + validateVertexIndex( |
| 1347 | + vertexIndex1, "vertex_index1", self.getCharacter()); |
| 1348 | + validator.validate( |
| 1349 | + vertexIndex2, "vertex_index2", {nConsIdx}, {"n_cons"}); |
| 1350 | + validateVertexIndex( |
| 1351 | + vertexIndex2, "vertex_index2", self.getCharacter()); |
| 1352 | + validator.validate(weight, "weight", {nConsIdx}, {"n_cons"}); |
| 1353 | + validateWeights(weight, "weight"); |
| 1354 | + validator.validate( |
| 1355 | + targetDistance, "target_distance", {nConsIdx}, {"n_cons"}); |
| 1356 | + |
| 1357 | + auto vertexIndex1Acc = vertexIndex1.unchecked<1>(); |
| 1358 | + auto vertexIndex2Acc = vertexIndex2.unchecked<1>(); |
| 1359 | + auto weightAcc = weight.unchecked<1>(); |
| 1360 | + auto targetDistanceAcc = targetDistance.unchecked<1>(); |
| 1361 | + |
| 1362 | + py::gil_scoped_release release; |
| 1363 | + |
| 1364 | + for (py::ssize_t i = 0; i < vertexIndex1.shape(0); ++i) { |
| 1365 | + self.addConstraint( |
| 1366 | + vertexIndex1Acc(i), |
| 1367 | + vertexIndex2Acc(i), |
| 1368 | + weightAcc(i), |
| 1369 | + targetDistanceAcc(i)); |
| 1370 | + } |
| 1371 | + }, |
| 1372 | + R"(Adds multiple vertex-to-vertex distance constraints to the error function. |
| 1373 | +
|
| 1374 | + :param vertex_index1: A numpy array of indices for the first vertices. |
| 1375 | + :param vertex_index2: A numpy array of indices for the second vertices. |
| 1376 | + :param weight: A numpy array of weights for the constraints. |
| 1377 | + :param target_distance: A numpy array of desired distances between vertex pairs.)", |
| 1378 | + py::arg("vertex_index1"), |
| 1379 | + py::arg("vertex_index2"), |
| 1380 | + py::arg("weight"), |
| 1381 | + py::arg("target_distance")) |
| 1382 | + .def( |
| 1383 | + "clear_constraints", |
| 1384 | + &mm::VertexVertexDistanceErrorFunctionT<float>::clearConstraints, |
| 1385 | + "Clears all vertex-to-vertex distance constraints from the error function.") |
| 1386 | + .def_property_readonly( |
| 1387 | + "constraints", |
| 1388 | + [](const mm::VertexVertexDistanceErrorFunctionT<float>& self) { |
| 1389 | + return self.getConstraints(); |
| 1390 | + }, |
| 1391 | + "Returns the list of vertex-to-vertex distance constraints.") |
| 1392 | + .def( |
| 1393 | + "num_constraints", |
| 1394 | + &mm::VertexVertexDistanceErrorFunctionT<float>::numConstraints, |
| 1395 | + "Returns the number of constraints."); |
| 1396 | +} |
| 1397 | + |
1241 | 1398 | } // namespace |
1242 | 1399 |
|
1243 | 1400 | void addErrorFunctions(py::module_& m) { |
@@ -2237,6 +2394,9 @@ rotation matrix to a target rotation.)") |
2237 | 2394 |
|
2238 | 2395 | // Vertex Projection error function |
2239 | 2396 | defVertexProjectionErrorFunction(m); |
| 2397 | + |
| 2398 | + // Vertex-to-vertex distance error function |
| 2399 | + defVertexVertexDistanceErrorFunction(m); |
2240 | 2400 | } |
2241 | 2401 |
|
2242 | 2402 | } // namespace pymomentum |
0 commit comments