Python API 2.0 Reference
python/api2/py2CustomPrimitiveGenerator.py
1 # Copyright 2015 Autodesk, Inc. All rights reserved.
2 #
3 # Use of this software is subject to the terms of the Autodesk
4 # license agreement provided at the time of installation or download,
5 # or which otherwise accompanies this software in either electronic
6 # or hard copy form.
7 
8 from builtins import range
9 from ctypes import *
10 import maya.api.OpenMayaRender as omr
11 import maya.api.OpenMaya as om
12 
13 # Example plugin: pyCustomPrimitiveGenerator.py
14 #
15 # This plug-in is an example of a custom MPxPrimitiveGenerator.
16 # It provides custom primitives based on shader requirements coming from
17 # an MPxShaderOverride. The name() in the MIndexBufferDescriptor is used
18 # to signify a unique identifier for a custom buffer.
19 
20 # This primitive generator is provided for demonstration purposes only.
21 # It simply provides a triangle list for mesh objects with no vertex sharing.
22 # A more sophisticated primitive provider could be used to provide patch primitives
23 # for GPU tessellation.
24 
25 # This plugin is meant to be used in conjunction with the dx11Shader, cgfxShader, glslShader or the hwPhongShader plugins.
26 # The customPrimitiveGeneratorDX11.fx (dx11Shader), customPrimitiveGeneratorGL.cgfx (cgfxShader)
27 # and customPrimitiveGenerator.osgfx (glslShader) files accompanying this sample
28 # can be loaded using the appropriate shader plugin.
29 # In any case, the environment variable MAYA_USE_CUSTOMPRIMITIVEGENERATOR needs to be set (any value is fine) for it to be enabled.
30 
31 def maya_useNewAPI():
32  """
33  The presence of this function tells Maya that the plugin produces, and
34  expects to be passed, objects created using the Maya Python API 2.0.
35  """
36  pass
37 
38 
39 class MyCustomPrimitiveGenerator(omr.MPxPrimitiveGenerator):
40  def __init__(self):
41  omr.MPxPrimitiveGenerator.__init__(self)
42 
43  def computeIndexCount(self, object, component):
44  # get the mesh from the object
45  mesh = om.MFnMesh(object)
46 
47  return mesh.numFaceVertices()
48 
49  def generateIndexing(self, object, component, sourceIndexing, targetIndexing, indexBuffer):
50  # get the mesh from the object
51  mesh = om.MFnMesh(object)
52 
53  for x in range(0, len(targetIndexing)):
54  target = targetIndexing[x]
55  if target != None and target.componentType() == omr.MComponentDataIndexing.kFaceVertex:
56  # Get Triangles
57  (triCounts, triVertIDs) = mesh.getTriangleOffsets()
58  numTriVerts = len(triVertIDs)
59 
60  customNumTriVerts = numTriVerts * 2
61  indexData = indexBuffer.acquire(customNumTriVerts, True) # writeOnly = True - we don't need the current buffer values
62  if indexData == 0:
63  return omr.MGeometry.kInvalidPrimitive
64 
65  sharedIndices = target.indices()
66 
67  # Crawl the sharedIndices array to find the last
68  # The new vertices will be added at the end
69  nextNewVertexIndex = 0
70  for idx in range(0, len(sharedIndices)):
71  if sharedIndices[idx] > nextNewVertexIndex:
72  nextNewVertexIndex = sharedIndices[idx]
73  nextNewVertexIndex += 1
74 
75  dataType = indexBuffer.dataType()
76 
77  address = indexData
78  uintinc = sizeof(c_uint)
79  ushortinc = sizeof(c_ushort)
80 
81  newTriId = 0
82  triId = 0
83  while triId < numTriVerts:
84  # split each triangle in two : add new vertex between vertexId1 and vertexId2
85  vertexId0 = sharedIndices[triVertIDs[triId]]
86  vertexId1 = sharedIndices[triVertIDs[triId+1]]
87  vertexId2 = sharedIndices[triVertIDs[triId+2]]
88  triId += 3
89 
90  newVertexIndex = nextNewVertexIndex
91  nextNewVertexIndex += 1
92 
93  # Triangle 0 1 2 become two triangles : 0 1 X and 0 X 2
94  if dataType == omr.MGeometry.kUnsignedInt32:
95  xaddr = address
96  yaddr = address+uintinc
97  zaddr = address+2*uintinc
98  address += 3*uintinc
99 
100  c_uint.from_address(xaddr).value = vertexId0
101  c_uint.from_address(yaddr).value = vertexId1
102  c_uint.from_address(zaddr).value = newVertexIndex
103 
104  xaddr = address
105  yaddr = address+uintinc
106  zaddr = address+2*uintinc
107  address += 3*uintinc
108 
109  c_uint.from_address(xaddr).value = vertexId0
110  c_uint.from_address(yaddr).value = newVertexIndex
111  c_uint.from_address(zaddr).value = vertexId2
112 
113  elif dataType == omr.MGeometry.kUnsignedChar:
114  xaddr = address
115  yaddr = address+ushortinc
116  zaddr = address+2*ushortinc
117  address += 3*ushortinc
118 
119  c_ushort.from_address(xaddr).value = vertexId0
120  c_ushort.from_address(yaddr).value = vertexId1
121  c_ushort.from_address(zaddr).value = newVertexIndex
122 
123  xaddr = address
124  yaddr = address+ushortinc
125  zaddr = address+2*ushortinc
126  address += 3*ushortinc
127 
128  c_ushort.from_address(xaddr).value = vertexId0
129  c_ushort.from_address(yaddr).value = newVertexIndex
130  c_ushort.from_address(zaddr).value = vertexId2
131 
132  indexBuffer.commit(indexData)
133 
134  return (omr.MGeometry.kTriangles, 3)
135  return omr.MGeometry.kInvalidPrimitive
136 
137 # This is the primitive generator creation function registered with the DrawRegistry.
138 # Used to initialize a custom primitive generator.
139 def createMyCustomPrimitiveGenerator():
140  return MyCustomPrimitiveGenerator()
141 
142 ##########################################################################################
143 ##########################################################################################
144 
145 class MyCustomPositionBufferGenerator(omr.MPxVertexBufferGenerator):
146  def __init__(self):
147  omr.MPxVertexBufferGenerator.__init__(self)
148 
149  def getSourceIndexing(self, object, sourceIndexing):
150  # get the mesh from the object
151  mesh = om.MFnMesh(object)
152 
153  (vertexCount, vertexList) = mesh.getVertices()
154  vertCount = len(vertexList)
155 
156  vertices = sourceIndexing.indices()
157  vertices.setLength(vertCount)
158 
159  for i in range(0, vertCount):
160  vertices[i] = vertexList[i]
161 
162  # assign the source indexing
163  sourceIndexing.setComponentType(omr.MComponentDataIndexing.kFaceVertex)
164 
165  return True
166 
167  def getSourceStreams(self, object, sourceStreams):
168  #No source stream needed
169  return False
170 
171  def createVertexStream(self, object, vertexBuffer, targetIndexing, sharedIndexing, sourceStreams):
172  # get the descriptor from the vertex buffer.
173  # It describes the format and layout of the stream.
174  descriptor = vertexBuffer.descriptor()
175 
176  # we are expecting a float stream.
177  if descriptor.dataType != omr.MGeometry.kFloat:
178  return
179 
180  # we are expecting a dimension of 3
181  dimension = descriptor.dimension
182  if dimension != 3:
183  return
184 
185  # we are expecting a position channel
186  if descriptor.semantic != omr.MGeometry.kPosition:
187  return
188 
189  # get the mesh from the current path
190  # if it is not a mesh we do nothing.
191  mesh = om.MFnMesh(object)
192 
193  indices = targetIndexing.indices()
194 
195  vertexCount = len(indices)
196  if vertexCount <= 0:
197  return
198 
199  # Keep track of the vertices that will be used to created a new vertex in-between
200  extraVertices = []
201  # Get Triangles
202  (triCounts, triVertIDs) = mesh.getTriangleOffsets()
203  numTriVerts = len(triVertIDs)
204 
205  sharedIndices = sharedIndexing.indices()
206 
207  triId = 0
208  while triId < numTriVerts:
209  # split each triangle in two : add new vertex between vertexId1 and vertexId2
210  #vertexId0 = sharedIndices[triVertIDs[triId]]
211  vertexId1 = sharedIndices[triVertIDs[triId+1]]
212  vertexId2 = sharedIndices[triVertIDs[triId+2]]
213  triId += 3
214 
215  extraVertices.append( [vertexId1, vertexId2] )
216 
217  newVertexCount = vertexCount + len(extraVertices)
218  customBuffer = vertexBuffer.acquire(newVertexCount, True) # writeOnly = True - we don't need the current buffer values
219  if customBuffer != 0:
220 
221  address = customBuffer
222  floatinc = sizeof(c_float)
223 
224  # Append 'real' vertices position
225  vertId = 0
226  while vertId < vertexCount:
227  vertexId = indices[vertId]
228 
229  point = mesh.getPoint(vertexId)
230 
231  c_float.from_address(address + (vertId * dimension) * floatinc).value = point.x
232  c_float.from_address(address + (vertId * dimension + 1) * floatinc).value = point.y
233  c_float.from_address(address + (vertId * dimension + 2) * floatinc).value = point.z
234 
235  vertId += 1
236 
237  # Append the new vertices position, interpolated from vert1 and vert2
238  for i in range(0, len(extraVertices)):
239  vertexId1 = indices[extraVertices[i][0]]
240  vertexId2 = indices[extraVertices[i][1]]
241 
242  point = mesh.getPoint(vertexId1)
243  point += mesh.getPoint(vertexId2)
244  point /= 2.0
245 
246  c_float.from_address(address + (vertId * dimension) * floatinc).value = point.x
247  c_float.from_address(address + (vertId * dimension + 1) * floatinc).value = point.y
248  c_float.from_address(address + (vertId * dimension + 2) * floatinc).value = point.z
249 
250  vertId += 1
251 
252  vertexBuffer.commit(customBuffer)
253 
254 # This is the buffer generator creation function registered with the DrawRegistry.
255 # Used to initialize the generator.
256 def createMyCustomPositionBufferGenerator():
257  return MyCustomPositionBufferGenerator()
258 
259 ##########################################################################################
260 ##########################################################################################
261 
262 class MyCustomNormalBufferGenerator(omr.MPxVertexBufferGenerator):
263  def __init__(self):
264  omr.MPxVertexBufferGenerator.__init__(self)
265 
266  def getSourceIndexing(self, object, sourceIndexing):
267  # get the mesh from the object
268  mesh = om.MFnMesh(object)
269 
270  (vertexCount, vertexList) = mesh.getVertices()
271  vertCount = len(vertexList)
272 
273  vertices = sourceIndexing.indices()
274  vertices.setLength(vertCount)
275 
276  for i in range(0, vertCount):
277  vertices[i] = vertexList[i]
278 
279  # assign the source indexing
280  sourceIndexing.setComponentType(omr.MComponentDataIndexing.kFaceVertex)
281 
282  return True
283 
284  def getSourceStreams(self, object, sourceStreams):
285  #No source stream needed
286  return False
287 
288  def createVertexStream(self, object, vertexBuffer, targetIndexing, sharedIndexing, sourceStreams):
289  # get the descriptor from the vertex buffer.
290  # It describes the format and layout of the stream.
291  descriptor = vertexBuffer.descriptor()
292 
293  # we are expecting a float stream.
294  if descriptor.dataType != omr.MGeometry.kFloat:
295  return
296 
297  # we are expecting a dimension of 3
298  dimension = descriptor.dimension
299  if dimension != 3:
300  return
301 
302  # we are expecting a normal channel
303  if descriptor.semantic != omr.MGeometry.kNormal:
304  return
305 
306  # get the mesh from the current path
307  # if it is not a mesh we do nothing.
308  mesh = om.MFnMesh(object)
309 
310  indices = targetIndexing.indices()
311 
312  vertexCount = len(indices)
313  if vertexCount <= 0:
314  return
315 
316  # Keep track of the vertices that will be used to created a new vertex in-between
317  extraVertices = []
318  # Get Triangles
319  (triCounts, triVertIDs) = mesh.getTriangleOffsets()
320  numTriVerts = len(triVertIDs)
321 
322  sharedIndices = sharedIndexing.indices()
323 
324  triId = 0
325  while triId < numTriVerts:
326  # split each triangle in two : add new vertex between vertexId1 and vertexId2
327  #vertexId0 = sharedIndices[triVertIDs[triId]]
328  vertexId1 = sharedIndices[triVertIDs[triId+1]]
329  vertexId2 = sharedIndices[triVertIDs[triId+2]]
330  triId += 3
331 
332  extraVertices.append( [vertexId1, vertexId2] )
333 
334  newVertexCount = vertexCount + len(extraVertices)
335  customBuffer = vertexBuffer.acquire(newVertexCount, True) # writeOnly = True - we don't need the current buffer values
336  if customBuffer != 0:
337 
338  address = customBuffer
339  floatinc = sizeof(c_float)
340 
341  normals = mesh.getNormals()
342 
343  # Append 'real' vertices position
344  vertId = 0
345  while vertId < vertexCount:
346  vertexId = indices[vertId]
347 
348  normal = normals[vertId]
349 
350  c_float.from_address(address + (vertId * dimension) * floatinc).value = normal.x
351  c_float.from_address(address + (vertId * dimension + 1) * floatinc).value = normal.y
352  c_float.from_address(address + (vertId * dimension + 2) * floatinc).value = normal.z
353 
354  vertId += 1
355 
356  # Append the new vertices normal, interpolated from vert1 and vert2
357  for i in range(0, len(extraVertices)):
358  vertexId1 = extraVertices[i][0]
359  vertexId2 = extraVertices[i][1]
360 
361  normal = normals[vertexId1]
362  normal += normals[vertexId2]
363  normal /= 2.0
364 
365  c_float.from_address(address + (vertId * dimension) * floatinc).value = normal.x
366  c_float.from_address(address + (vertId * dimension + 1) * floatinc).value = normal.y
367  c_float.from_address(address + (vertId * dimension + 2) * floatinc).value = normal.z
368 
369  vertId += 1
370 
371  vertexBuffer.commit(customBuffer)
372 
373 # This is the buffer generator creation function registered with the DrawRegistry.
374 # Used to initialize the generator.
375 def createMyCustomNormalBufferGenerator():
376  return MyCustomNormalBufferGenerator()
377 
378 ##########################################################################################
379 ##########################################################################################
380 
381 # The following routines are used to register/unregister
382 # the vertex mutators with Maya
383 
384 def initializePlugin(obj):
385 
386  omr.MDrawRegistry.registerPrimitiveGenerator("customPrimitiveTest", createMyCustomPrimitiveGenerator)
387 
388  omr.MDrawRegistry.registerVertexBufferGenerator("customPositionStream", createMyCustomPositionBufferGenerator)
389 
390  omr.MDrawRegistry.registerVertexBufferGenerator("customNormalStream", createMyCustomNormalBufferGenerator)
391 
392 def uninitializePlugin(obj):
393 
394  omr.MDrawRegistry.deregisterPrimitiveGenerator("customPrimitiveTest")
395 
396  omr.MDrawRegistry.deregisterVertexBufferGenerator("customPositionStream")
397 
398  omr.MDrawRegistry.deregisterVertexBufferGenerator("customNormalStream")
399