1
2 """ Contains implementations ( or improvements ) to mayas geometric shapes """
3 __docformat__ = "restructuredtext"
4
5 import base
6 from mrv.enum import (create as enum, Element as elm)
7 import maya.OpenMaya as api
8 import logging
9 log = logging.getLogger("mrv.maya.nt.geometry")
10
11 __all__ = ("GeometryShape", "DeformableShape", "ControlPoint", "SurfaceShape",
12 "Mesh")
15 """Contains common methods for all geometry types"""
16 @undoable
18 """Copy lightlinks from one meshShape to another
19
20 :param kwargs:
21 * substitute:
22 if True, default False, the other shape will be put
23 in place of self, effectively receiving it's light-links whereas self losses
24 them. This is practical in case you create a new shape below a transform that
25 had a previously visible and manipulated shape whose external connections you
26 wouuld like to keep"""
27 def freeLogicalIndex( parent_plug ):
28 """:return: a free parent compound index"""
29 ilogical = parent_plug.logicalIndex()
30 array_plug = parent_plug.array()
31 num_elments = array_plug.numElements()
32
33
34
35
36 for iphysical in xrange( num_elments - 1, -1, -1 ):
37 p_plug = array_plug[ iphysical ]
38 try_index = p_plug.logicalIndex() + 1
39 try_plug = array_plug.elementByLogicalIndex( try_index )
40
41 if try_plug.child( 0 ).minput().isNull():
42 return try_index
43
44
45 raise AssertionError( "Did not find valid free index" )
46
47
48 substitute = kwargs.get( "substitute", False )
49 for input_plug in self.message.moutputs():
50 node = input_plug.mwrappedNode()
51 if node.apiType() != api.MFn.kLightLink:
52 continue
53
54
55
56 parent_compound = input_plug.mparent()
57 target_compound_index = -1
58 if substitute:
59 target_compound_index = parent_compound.logicalIndex()
60 else:
61 target_compound_index = freeLogicalIndex(parent_compound)
62
63
64 new_parent_compound = parent_compound.array().elementByLogicalIndex( target_compound_index )
65
66
67
68 if not substitute:
69 light_plug = parent_compound.child( 0 ).minput()
70 if not light_plug.isNull():
71 light_plug.mconnectTo(new_parent_compound.child( 0 ), force=False)
72
73
74
75
76 other.message.mconnectTo(new_parent_compound.child(1))
77
83 pass
84
87 pass
88
91 pass
92
97 """Utility producing components, initialized with the given indices. See `Mesh`
98 for more info. """
99 __slots__ = ('_mesh', '_component')
100
101
102 _int32b = ( 1 << 31 ) - 2
103
105 self._mesh = mesh
106 self._component = component
107
120
140
142 """:return: empty component of our type"""
143 return self._mesh.component(self._component)
144
147 """Utility which produces iterators for the component type
148 it was initialized with. As a bonus, it allows to return
149 quick constrained iterators using the slice and get-item notation"""
150 __slots__ = tuple()
151
154
157
159 """
160 :raise NotImplementedError: if comp needs double-index component, our interface
161 cannot support anything else than SingleIndex components"""
162 if self._component == Mesh.eComponentType.uv:
163 raise NotImplementedError("This Utility does not support iteration using \
164 component-constrained iterators as it can only reproduce \
165 SingleIndexedComponents - create the Component yourself and \
166 use iterComponents to retrieve the iterator instead")
167
177
182
183
187
188
189 iter = property(iterator)
190
191
192
193
194 -class Mesh( SurfaceShape ):
195 """Implemnetation of mesh related methods to make its handling more
196 convenient
197
198 **Component Access**:
199
200 >>> m.cvtx[:] # a complete set of components
201 >>> m.cvtx[1:4] # initialized with 3 indices
202 >>> m.cvtx[1] # initialized with a single index
203 >>> m.cvtx[1,2,3] # initialized with multiple indices
204 >>> m.cf[(1,2,3)] # initialized with list or tuple
205 >>> m.ce[iter(1,2,3)] # initialized from iterator
206 >>> m.ce[api.MIntArray()] # initialized from MIntArray
207
208 """
209
210 eComponentType = enum( elm("vertex", api.MFn.kMeshVertComponent),
211 elm("edge", api.MFn.kMeshEdgeComponent ),
212 elm("face", api.MFn.kMeshPolygonComponent ),
213 elm("uv", api.MFn.kMeshMapComponent ) )
214
215
219
220 return internal
221
222
223
224 for shortname, component in zip(('vtx', 'e', 'f', 'map'), eComponentType):
225 locals()[shortname] = property(_make_component_getter(_SingleIndexedComponentIterator, component))
226
227
228 for shortname, component in zip(('cvtx', 'ce', 'cf', 'cmap'), eComponentType):
229 locals()[shortname] = property(_make_component_getter(_SingleIndexedComponentGenerator, component))
230
231
232
233
234
236 """Copy our tweaks onto another mesh
237
238 :note: we do not check topology for maximum flexibility"""
239 opnts = other.pnts
240 pnts = self.pnts
241 for splug in pnts:
242 opnts.elementByLogicalIndex( splug.logicalIndex() ).msetMObject( splug.asMObject() )
243
244
246 """
247 :return: True if we are nonempty and valid - emptry meshes do not work with the mfnmesh
248 although it should ! Have to catch that case ourselves"""
249 try:
250 self.numVertices()
251 return True
252 except RuntimeError:
253 return False
254
255 @undoable
264
265
266 @undoable
268 """Reset the tweaks on the given mesh shape
269
270 :param tweak_type: the component type(s) whose tweaks are to be removed,
271 valid values are 'vertex' and 'uv' members of the eComponentType enumeration.
272 Pass in a scalar value or a list of tweak types
273 :param keep_tweak_result: if True, the effect of the tweak will be kept. If False,
274 it will be removed. What actually happens depends on the context
275
276 * [referenced] mesh *without* history:
277 copy outMesh to inMesh, resetTweaks
278
279 if referenced, plenty of reference edits are generated, ideally one operates
280 on non-referenced geomtry
281
282 * [referenced] mesh *with* history:
283 put tweakNode into mesh history, copy tweaks onto tweak node
284 :note: currently vertex and uv tweaks will be removed if keep is enabled, thus they must
285 both be specified"""
286 check_types = ( isinstance( tweak_type, ( list, tuple ) ) and tweak_type ) or [ tweak_type ]
287 type_map = {
288 self.eComponentType.vertex : ( "pnts", api.MFnNumericData.k3Float, "polyTweak", api.MFn.kPolyTweak, "tweak" ),
289 self.eComponentType.uv : ( "uvpt", api.MFnNumericData.k2Float, "polyTweakUV", api.MFn.kPolyTweakUV, "uvTweak" )
290 }
291
292 mia = api.MIntArray()
293 for reset_this_type in check_types:
294 try:
295 attrname, datatype, tweak_node_type, tweak_node_type_API, tweakattr = type_map[ reset_this_type ]
296 except KeyError:
297 raise ValueError( "Tweak type %s is not supported" % reset_this_type )
298
299
300
301 if keep_tweak_result:
302 input_plug = self.inMesh.minput()
303
304
305 if input_plug.isNull():
306
307
308 if not ( self.eComponentType.vertex in check_types and self.eComponentType.uv in check_types ):
309 log.warn("Currently vertex AND uv tweaks will be removed if a mesh has no history and a reset is requested")
310
311
312
313
314 out_mesh = self.outMesh.asMObject()
315 self.inMesh.msetMObject( out_mesh )
316 self.cachedInMesh.msetMObject( out_mesh )
317
318
319 return self.resetTweaks( check_types, keep_tweak_result = False )
320 else:
321
322 tweak_node = input_plug.mwrappedNode()
323
324
325 if not tweak_node.hasFn( tweak_node_type_API ):
326 tweak_node = base.createNode( "polyTweak", tweak_node_type, forceNewLeaf = 1 )
327
328
329 input_plug.mconnectTo(tweak_node.inputPolymesh)
330 tweak_node.output.mconnectTo(self.inMesh)
331
332
333 if tweak_node_type_API == api.MFn.kPolyTweakUV:
334 names = list()
335 self.getUVSetNames( names )
336 index = names.index( self.currentUVSetName( ) )
337
338 own_tweak_location_plug = self.uvSet.elementByLogicalIndex( index ).mchildByName('uvSetTweakLocation')
339 tweak_node.uvTweak.elementByLogicalIndex( index ).mconnectTo(own_tweak_location_plug)
340
341
342
343 dtweak_plug = tweak_node.findPlug(tweakattr)
344 stweak_plug = self.findPlug(attrname)
345
346
347
348 stweak_plug.evaluateNumElements()
349
350 mia.clear()
351 stweak_plug.getExistingArrayAttributeIndices(mia)
352 for i in mia:
353 try:
354 tplug = stweak_plug.elementByLogicalIndex(i)
355 except RuntimeError:
356 continue
357 else:
358 dtweak_plug.elementByLogicalIndex(i).msetMObject(tplug.asMObject())
359
360
361
362
363 pass
364
365
366
367 arrayplug = self.findPlug(attrname)
368 dataobj = api.MFnNumericData().create( datatype )
369
370
371 try:
372 for p in arrayplug:
373 p.msetMObject( dataobj )
374 except RuntimeError:
375
376
377
378
379 pass
380
381
383 """:return: A component object able to hold the given component type
384 :param component_type: a member of the `eComponentType` enumeration"""
385 if component_type not in self.eComponentType:
386 raise ValueError("Invalid component type")
387 return base.SingleIndexedComponent.create(component_type.value())
388
389
390
391
392
394 """
395 :return: MItIterator matching your component_type to iteartor over items
396 on this mesh
397 :param component_type:
398 * vertex -> MItMeshVertex
399 * edge -> MItMeshEdge
400 * face -> MItMeshPolygon
401 * uv -> MItMeshFaceVertex
402 :param component: if not kNullObject, the iterator returned will be constrained
403 to the given indices as described by the Component. Use `component` to retrieve
404 a matching component type's instance"""
405 if component_type not in self.eComponentType:
406 raise ValueError("Invalid component type")
407
408 ec = self.eComponentType
409 it_type = { ec.vertex : api.MItMeshVertex,
410 ec.edge : api.MItMeshEdge,
411 ec.face : api.MItMeshPolygon,
412 ec.uv : api.MItMeshFaceVertex}[component_type]
413
414 return it_type(self.dagPath(), component)
415
416
417
418
419 - def copyFrom( self, other, *args, **kwargs ):
420 """Copy tweaks and sets from other onto self
421
422 :param kwargs:
423 * setFilter: if given, default is fSets, you may specify the types of sets to copy
424 if None, no set conenctions will be copied """
425 other.copyTweaksTo( self )
426
427 setfilter = kwargs.pop( "setFilter", Mesh.fSets )
428 if setfilter is not None:
429 other.copyAssignmentsTo( self, setFilter = setfilter )
430
431
432