1#!/usr/bin/env python 2# 3# This file is part of libigl, a simple c++ geometry processing library. 4# 5# Copyright (C) 2017 Sebastian Koch <s.koch@tu-berlin.de> and Daniele Panozzo <daniele.panozzo@gmail.com> 6# 7# This Source Code Form is subject to the terms of the Mozilla Public License 8# v. 2.0. If a copy of the MPL was not distributed with this file, You can 9# obtain one at http://mozilla.org/MPL/2.0/. 10import sys, os 11from math import sin, cos, pi 12 13# Add the igl library to the modules search path 14import math 15 16sys.path.insert(0, os.getcwd() + "/../") 17import pyigl as igl 18 19from shared import TUTORIAL_SHARED_PATH, check_dependencies, print_usage 20 21dependencies = ["glfw"] 22check_dependencies(dependencies) 23 24 25def pre_draw(viewer): 26 global recompute, anim_t, poses, C, BE, P, U, M, anim_t_dir 27 28 if recompute: 29 # Find pose interval 30 begin = int(math.floor(anim_t)) % len(poses) 31 end = int(math.floor(anim_t) + 1) % len(poses) 32 t = anim_t - math.floor(anim_t) 33 34 # Interpolate pose and identity 35 anim_pose = igl.RotationList() 36 for e in range(len(poses[begin])): 37 anim_pose.append(poses[begin][e].slerp(t, poses[end][e])) 38 39 # Propagate relative rotations via FK to retrieve absolute transformations 40 vQ = igl.RotationList() 41 vT = [] 42 igl.forward_kinematics(C, BE, P, anim_pose, vQ, vT) 43 dim = C.cols() 44 T = igl.eigen.MatrixXd(BE.rows() * (dim + 1), dim) 45 for e in range(BE.rows()): 46 a = igl.eigen.Affine3d.Identity() 47 a.translate(vT[e]) 48 a.rotate(vQ[e]) 49 T.setBlock(e * (dim + 1), 0, dim + 1, dim, a.matrix().transpose().block(0, 0, dim + 1, dim)) 50 51 # Compute deformation via LBS as matrix multiplication 52 if use_dqs: 53 igl.dqs(V, W, vQ, vT, U) 54 else: 55 U = M * T 56 57 # Also deform skeleton edges 58 CT = igl.eigen.MatrixXd() 59 BET = igl.eigen.MatrixXi() 60 igl.deform_skeleton(C, BE, T, CT, BET) 61 62 viewer.data().set_vertices(U) 63 viewer.data().set_edges(CT, BET, sea_green) 64 viewer.data().compute_normals() 65 if viewer.core.is_animating: 66 anim_t += anim_t_dir 67 else: 68 recompute = False 69 70 return False 71 72 73def key_down(viewer, key, mods): 74 global recompute, use_dqs, animation 75 recompute = True 76 if key == ord('D') or key == ord('d'): 77 use_dqs = not use_dqs 78 viewer.core.is_animating = False 79 animation = False 80 if use_dqs: 81 print("Switched to Dual Quaternion Skinning") 82 else: 83 print("Switched to Linear Blend Skinning") 84 elif key == ord(' '): 85 if animation: 86 viewer.core.is_animating = False 87 animation = False 88 else: 89 viewer.core.is_animating = True 90 animation = True 91 return False 92 93 94if __name__ == "__main__": 95 keys = {"d": "toggle between LBS and DQS", 96 "space": "toggle animation"} 97 98 print_usage(keys) 99 100 V = igl.eigen.MatrixXd() 101 F = igl.eigen.MatrixXi() 102 C = igl.eigen.MatrixXd() 103 BE = igl.eigen.MatrixXi() 104 P = igl.eigen.MatrixXi() 105 W = igl.eigen.MatrixXd() 106 M = igl.eigen.MatrixXd() 107 108 sea_green = igl.eigen.MatrixXd([[70. / 255., 252. / 255., 167. / 255.]]) 109 110 anim_t = 0.0 111 anim_t_dir = 0.015 112 use_dqs = False 113 recompute = True 114 animation = False # Flag needed as there is some synchronization problem with viewer.core.is_animating 115 116 poses = [[]] 117 118 igl.readOBJ(TUTORIAL_SHARED_PATH + "arm.obj", V, F) 119 U = igl.eigen.MatrixXd(V) 120 igl.readTGF(TUTORIAL_SHARED_PATH + "arm.tgf", C, BE) 121 122 # retrieve parents for forward kinematics 123 igl.directed_edge_parents(BE, P) 124 rest_pose = igl.RotationList() 125 igl.directed_edge_orientations(C, BE, rest_pose) 126 poses = [[igl.eigen.Quaterniond.Identity() for i in range(4)] for j in range(4)] 127 128 twist = igl.eigen.Quaterniond(pi, igl.eigen.MatrixXd([1, 0, 0])) 129 poses[1][2] = rest_pose[2] * twist * rest_pose[2].conjugate() 130 bend = igl.eigen.Quaterniond(-pi * 0.7, igl.eigen.MatrixXd([0, 0, 1])) 131 poses[3][2] = rest_pose[2] * bend * rest_pose[2].conjugate() 132 133 igl.readDMAT(TUTORIAL_SHARED_PATH + "arm-weights.dmat", W) 134 igl.lbs_matrix(V, W, M) 135 136 # Plot the mesh with pseudocolors 137 viewer = igl.glfw.Viewer() 138 viewer.data().set_mesh(U, F) 139 viewer.data().set_edges(C, BE, sea_green) 140 viewer.data().show_lines = False 141 viewer.data().show_overlay_depth = False 142 viewer.data().line_width = 1 143 viewer.core.trackball_angle.normalize() 144 viewer.callback_pre_draw = pre_draw 145 viewer.callback_key_down = key_down 146 viewer.core.is_animating = False 147 viewer.core.camera_zoom = 2.5 148 viewer.core.animation_max_fps = 30.0 149 viewer.launch() 150