forked from chadmv/cvshapeinverter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcvshapeinverter.py
213 lines (171 loc) · 7.2 KB
/
cvshapeinverter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
"""@brief Inverts a shape through the deformation chain
@author Chad Vernon - [email protected] - www.chadvernon.com
Example usage:
Pose your skinned model in the pose that you want to correct.
Duplicate the mesh and sculpt in your fixes. Select the original skinned model,
shift select the corrected model and run:
import cvshapeinverter
cvshapeinverter.invert()
An inverted shape will be generated which you can then apply as a front of chain blendShape target.
The generated shape will have a live deformer affecting it so edits you make on your corrected mesh
will be inverted through the deformer.
"""
import maya.cmds as cmds
import maya.OpenMaya as OpenMaya
import math
def invert(base=None, corrective=None, name=None):
"""Inverts a shape through the deformation chain.
@param[in] base Deformed base mesh.
@param[in] corrective Sculpted corrective mesh.
@param[in] name Name of the generated inverted shape.
@return The name of the inverted shape.
"""
cmds.loadPlugin('cvshapeinverter_plugin.py', qt=True)
if not base or not corrective:
sel = cmds.ls(sl=True)
if not sel or len(sel) != 2:
cmds.undoInfo(closeChunk=True)
raise RuntimeError, 'Select base then corrective'
base, corrective = sel
# Get points on base mesh
base_points = get_points(base)
point_count = base_points.length()
# Get points on corrective mesh
corrective_points = get_points(corrective)
# Get the intermediate mesh
orig_mesh = get_shape(base, intermediate=True)
# Get the component offset axes
orig_points = get_points(orig_mesh)
x_points = OpenMaya.MPointArray(orig_points)
y_points = OpenMaya.MPointArray(orig_points)
z_points = OpenMaya.MPointArray(orig_points)
cmds.undoInfo(openChunk=True)
for i in range(point_count):
x_points[i].x += 1.0
y_points[i].y += 1.0
z_points[i].z += 1.0
set_points(orig_mesh, x_points)
x_points = get_points(base)
set_points(orig_mesh, y_points)
y_points = get_points(base)
set_points(orig_mesh, z_points)
z_points = get_points(base)
set_points(orig_mesh, orig_points)
# Create the mesh to get the inversion deformer
if not name:
name = '%s_inverted' % corrective
inverted_shapes = cmds.duplicate(base, name=name)[0]
# Delete the unnessary shapes
shapes = cmds.listRelatives(inverted_shapes, children=True, shapes=True, path=True)
for s in shapes:
if cmds.getAttr('%s.intermediateObject' % s):
cmds.delete(s)
set_points(inverted_shapes, orig_points)
# Unlock the transformation attrs
for attr in 'trs':
for x in 'xyz':
cmds.setAttr('%s.%s%s' % (inverted_shapes, attr, x), lock=False)
cmds.setAttr('%s.visibility' % inverted_shapes, 1)
deformer = cmds.deformer(inverted_shapes, type='cvShapeInverter')[0]
# Calculate the inversion matrices
deformer_mobj = get_mobject(deformer)
fn_deformer = OpenMaya.MFnDependencyNode(deformer_mobj)
plug_matrix = fn_deformer.findPlug('inversionMatrix', False)
fn_matrix_data = OpenMaya.MFnMatrixData()
for i in range(point_count):
matrix = OpenMaya.MMatrix()
set_matrix_row(matrix, x_points[i] - base_points[i], 0)
set_matrix_row(matrix, y_points[i] - base_points[i], 1)
set_matrix_row(matrix, z_points[i] - base_points[i], 2)
set_matrix_row(matrix, corrective_points[i], 3)
matrix = matrix.inverse()
matrix_mobj = fn_matrix_data.create(matrix)
plug_matrixElement = plug_matrix.elementByLogicalIndex(i)
plug_matrixElement.setMObject(matrix_mobj)
# Store the base points.
fn_point_data = OpenMaya.MFnPointArrayData()
point_data_mobj = fn_point_data.create(base_points)
plug_deformed_points = fn_deformer.findPlug('deformedPoints', False)
plug_deformed_points.setMObject(point_data_mobj)
cmds.connectAttr('%s.outMesh' % get_shape(corrective), '%s.correctiveMesh' % deformer)
cmds.undoInfo(closeChunk=True)
return inverted_shapes
def get_shape(node, intermediate=False):
"""Returns a shape node from a given transform or shape.
@param[in] node Name of the node.
@param[in] intermediate True to get the intermediate mesh
@return The associated shape node.
"""
if cmds.nodeType(node) == 'transform':
shapes = cmds.listRelatives(node, shapes=True, path=True)
if not shapes:
raise RuntimeError, '%s has no shape' % node
for shape in shapes:
is_intermediate = cmds.getAttr('%s.intermediateObject' % shape)
if intermediate and is_intermediate and cmds.listConnections('%s.worldMesh' % shape,
source=False):
return shape
elif not intermediate and not is_intermediate:
return shape
raise RuntimeError('Could not find shape on node {0}'.format(node))
elif cmds.nodeType(node) in ['mesh', 'nurbsCurve', 'nurbsSurface']:
return node
def get_mobject(node):
"""Gets the dag path of a node.
@param[in] node Name of the node.
@return The dag path of a node.
"""
selection_list = OpenMaya.MSelectionList()
selection_list.add(node)
node_mobj = OpenMaya.MObject()
selection_list.getDependNode(0, node_mobj)
return node_mobj
def get_dag_path(node):
"""Gets the dag path of a node.
@param[in] node Name of the node.
@return The dag path of a node.
"""
selection_list = OpenMaya.MSelectionList()
selection_list.add(node)
path_node = OpenMaya.MDagPath()
selection_list.getDagPath(0, path_node)
return path_node
def get_points(path, space=OpenMaya.MSpace.kObject):
"""Get the control point positions of a geometry node.
@param[in] path Name or dag path of a node.
@param[in] space Space to get the points.
@return The MPointArray of points.
"""
if isinstance(path, basestring):
path = get_dag_path(get_shape(path))
it_geo = OpenMaya.MItGeometry(path)
points = OpenMaya.MPointArray()
it_geo.allPositions(points, space)
return points
def set_points(path, points, space=OpenMaya.MSpace.kObject):
"""Set the control points positions of a geometry node.
@param[in] path Name or dag path of a node.
@param[in] points MPointArray of points.
@param[in] space Space to get the points.
"""
if isinstance(path, str) or isinstance(path, unicode):
path = get_dag_path(get_shape(path))
it_geo = OpenMaya.MItGeometry(path)
it_geo.setAllPositions(points, space)
def set_matrix_row(matrix, new_vector, row):
"""Sets a matrix row with an MVector or MPoint.
@param[in/out] matrix Matrix to set.
@param[in] new_vector Vector to use.
@param[in] row Row number.
"""
set_matrix_cell(matrix, new_vector.x, row, 0)
set_matrix_cell(matrix, new_vector.y, row, 1)
set_matrix_cell(matrix, new_vector.z, row, 2)
def set_matrix_cell(matrix, value, row, column):
"""Sets a matrix cell
@param[in/out] matrix Matrix to set.
@param[in] value Value to set cell.
@param[in] row Row number.
@param[in] column Column number.
"""
OpenMaya.MScriptUtil.setDoubleArray(matrix[row], column, value)