def applyMatricesToTransformBlendShapeNodeOffsets( blendShapeNode, matrices, shapeData=None, targets=None, optionalData=None, matrixOp=matrixOp_makeLinearCorrective): applyShapeData = bs.getEmptyShapeData() for target in targets: shape = bs.getEmptyShape() applyShapeData['shapes'][target] = shape applyMatricesResult = applyMatricesToTransformShapeDataOffsets( applyShapeData, matrices, targets=targets, optionalData=optionalData, matrixOp=matrixOp) return applyMatricesResult
def apply_pose(self, pose_index, training_type='differential', ground_truth=False): """ apply the pose to rigs in the scene :param int pose_index: the index of the pose to apply :param str training_type: the type of training. Valid options are differential and local_offset :param bool ground_truth: if True, read ground truth data instead """ if self.pose_data is None: self.read_pose_data() if training_type not in self.prediction_data: training_types = [training_type] if training_type == 'differential': training_types.append('anchor') self.read_prediction_data(training_types, ground_truth=ground_truth) cur_pose_data = self.pose_data.iloc[pose_index] for mover in cur_pose_data.index: value = cur_pose_data[mover] all_movers = mc.ls(mover, recursive=True) for m in all_movers: try: mc.setAttr(m, value) except RuntimeError as error_msg: pass shape_data = bs.getEmptyShapeData() shape_data['shapes'][self.TARGET] = bs.getEmptyShape() if training_type == 'local_offset': cur_offset_data = self.prediction_data[training_type].loc[ pose_index] shape_data['shapes'][self.TARGET] = bs.getEmptyShape() exclude_list = [] for vtx_id in cur_offset_data.index: if int(vtx_id) in exclude_list: continue shape_data['shapes'][self.TARGET]['offsets'][int( vtx_id)] = cur_offset_data[vtx_id] elif training_type == 'differential': cur_differential_data = self.prediction_data[training_type].loc[ pose_index] cur_anchor_data = self.prediction_data['anchor'].loc[pose_index] if self._laplacian_matrix is None: self._cal_matrices() col_ct = len(self.vertex_ids['differential']) diff_coord = numpy.ndarray(shape=(col_ct + len(self.vertex_ids['anchor']), 1)) real_coords = [] for axis in range(3): test_index = [] test_coord = [] for i, di in enumerate(self.vertex_ids['differential']): test_index.append(di) test_coord.append(cur_differential_data[di][axis]) diff_coord[i] = self._valences[i] * cur_differential_data[ di][axis] for j, anchor in enumerate(self.vertex_ids['anchor']): if cur_anchor_data is not None: test_coord.append(cur_anchor_data[anchor][axis]) diff_coord[ j + col_ct] = self.anchor_weight * cur_anchor_data[ anchor][axis] else: test_coord.append(0.0) diff_coord[j + col_ct] = 0.0 # calculates (L*) * d, where D* is the transpose of the laplacian matrix, # d is the differential coordinates scaled with valence morphed_diff_coord = self._laplacian_matrix.transpose( ) * diff_coord # reorder the coordinates based on reverse cuthill mckee reordering reordered_diff_coord = numpy.ndarray(shape=(col_ct, 1)) for i in range(col_ct): reordered_diff_coord[i] = morphed_diff_coord[ self._reverse_cuthill_mckee_order[i]] # back substitution to solve two trianglar matrices theta_coord = scipy.linalg.solve_triangular( self._cholesky_matrix, reordered_diff_coord, lower=True, overwrite_b=True, check_finite=False) reordered_real_coord = scipy.linalg.solve_triangular( self._cholesky_matrix.T, theta_coord, lower=False, overwrite_b=True, check_finite=False) # reorder the coordinates to the original order real_coord = [] for i in range(col_ct): real_coord.append( reordered_real_coord[self._inverse_cmo[i]][0]) real_coords.append(real_coord) for i, di in enumerate(self.vertex_ids[training_type]): offset = [ real_coords[0][i], real_coords[1][i], real_coords[2][i] ] shape_data['shapes'][self.TARGET]['offsets'][di] = offset else: raise NotImplementedError( "The training_type %s is not implemented" % training_type) bs.setShapeData(self.blendshape, shape_data, shapes=[self.TARGET]) mc.setAttr(self.blendshape + '.' + self.TARGET, 1.0)
def genMatrix(blendShapeNode, deformedGeometry=None, resultPos=None, resultInd=None, targetName=None): """ This procedure is meant to return transform matrices which describe how a 1-unit offset in each X, Y, Z direction actually transforms to an offset at the end of the subsequent deformer stack. :param str blendShapeNode: the blendShape node whose offsets are in question :param str deformedGeometry: the name of the mesh that's deformed by blendShapeNode :param str targetName: the name of the blendShape target :param list resultPos: the default positions of the points in resultCmp :param list resultInd: the indices of the points in resultCmp. For resultCmp element "<mesh>.vtx[5]", its resultInd element would be 5, for example. """ removeTarget = False if not targetName: targetName = 'CORRECTIVE_DUMMY_TARGET' removeTarget = True # initialize empty shape data shapeData = bs.getEmptyShapeData() shape = bs.getEmptyShape() shapeData['shapes'][targetName] = shape shape['offsets'] = {} if not mc.objExists('%s.%s' % (blendShapeNode, targetName)): shapeIndex = 0 weightIndices = mc.getAttr(blendShapeNode + ".weight", multiIndices=True) if weightIndices: shapeIndex = weightIndices[-1] + 1 attr = '%s.weight[%i]' % (blendShapeNode, shapeIndex) mc.getAttr(attr) mc.aliasAttr(targetName, attr) mc.setAttr(attr, 1.0) # get the x, y and z basis vectors for each point perAxisUnitOffsetVectors = [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)] # X, Y, Z. axes = [] for ii in range(3): for ind in resultInd: shape['offsets'][ind] = perAxisUnitOffsetVectors[ii] bs.setBlendShapeData(blendShapeNode, shapeData) currentAxisOffsetPos = getPositions(deformedGeometry) newCurrentAxisOffsetPos = [currentAxisOffsetPos[j] for j in resultInd] currentAxes = [ e[0] - e[1] for e in zip(newCurrentAxisOffsetPos, resultPos) ] axes.append(currentAxes) if removeTarget: mc.aliasAttr(blendShapeNode + '.' + targetName, remove=True) mc.removeMultiInstance(blendShapeNode + '.weight[' + str(shapeIndex) + ']', b=True) mc.removeMultiInstance(blendShapeNode + '.inputTarget[0].inputTargetGroup[' + str(shapeIndex) + '].inputTargetItem[6000]') mc.removeMultiInstance(blendShapeNode + '.inputTarget[0].inputTargetGroup[' + str(shapeIndex) + ']') xAxes = axes[0] yAxes = axes[1] zAxes = axes[2] nonInvertablePoints = [] nonInvertablePointsSet = set() # use the basis vectors to compute the final position # calculate the shapeMatrix first: matrices = {} for index, xAxis, yAxis, zAxis in itertools.izip(resultInd, xAxes, yAxes, zAxes): shapeMatrix = numpy.array([[xAxis[0], xAxis[1], xAxis[2], 0], [yAxis[0], yAxis[1], yAxis[2], 0], [zAxis[0], zAxis[1], zAxis[2], 0], [0, 0, 0, 1]]) matrices[index] = shapeMatrix result = {} result['matrices'] = matrices invShapeMatrices = {} # calculate the invShapeMatrix first: invShapeMatrices, nonInvertablePointsSet, nonInvertablePoints = invertMatrices( matrices) result['invShapeMatrices'] = invShapeMatrices result['nonInvertablePoints'] = nonInvertablePoints result['nonInvertablePointsSet'] = nonInvertablePointsSet return result