Python API 2.0 Reference
python/api2/py2VertexBufferGenerator.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: vertexBufferGenerator.py
14 #
15 # This plug-in is an example of a custom MPxVertexBufferGenerator.
16 # It provides custom vertex streams based on shader requirements coming from
17 # an MPxShaderOverride. The semanticName() in the MVertexBufferDescriptor is used
18 # to signify a unique identifier for a custom stream.
19 
20 # This plugin is meant to be used in conjunction with the d3d11Shader or cgShader plugins.
21 
22 # The vertexBufferGeneratorGL.cgfx (cgfxShader), vertexBufferGeneratorDX11.fx (dx11Shader) and
23 # vertexBufferGenerator.ogsfx (glslShader) files accompanying this sample
24 # The Names of the streams and the stream data generated by this plugin match what is
25 # expected from the included effects files.
26 # This sample use the MyCustomBufferGenerator to create a custom made streams.
27 
28 # The vertexBufferGenerator2GL.cgfx (cgfxShader), vertexBufferGenerator2DX11.fx (dx11Shader) and
29 # vertexBufferGenerator2.ogsfx (glslShader) files accompanying this sample
30 # The Names of the streams and the stream data generated by this plugin match what is
31 # expected from the included effects files.
32 # This sample use the MyCustomBufferGenerator2 to create a custom made streams
33 # by combining the Position and Normal streams in a single one.
34 
35 def maya_useNewAPI():
36  """
37  The presence of this function tells Maya that the plugin produces, and
38  expects to be passed, objects created using the Maya Python API 2.0.
39  """
40  pass
41 
42 
43 class MyCustomBufferGenerator(omr.MPxVertexBufferGenerator):
44  def __init__(self):
45  omr.MPxVertexBufferGenerator.__init__(self)
46 
47  def getSourceIndexing(self, object, sourceIndexing):
48  # get the mesh from the object
49  mesh = om.MFnMesh(object)
50 
51  # if it is an empty mesh we do nothing.
52  numPolys = mesh.numPolygons
53  if numPolys == 0:
54  return False
55 
56  vertToFaceVertIDs = sourceIndexing.indices()
57  faceNum = 0
58 
59  # for each face
60  for i in range(0, numPolys):
61 
62  # assign a color ID to all vertices in this face.
63  faceColorID = faceNum % 3
64 
65  vertexCount = mesh.polygonVertexCount(i)
66  for x in range(0, vertexCount):
67  # set each face vertex to the face color
68  vertToFaceVertIDs.append(faceColorID)
69 
70  faceNum += 1
71 
72  # assign the source indexing
73  sourceIndexing.setComponentType(omr.MComponentDataIndexing.kFaceVertex)
74 
75  return False
76 
77  def getSourceStreams(self, object, sourceStreams):
78  #No source stream needed
79  return False
80 
81  def createVertexStream(self, object, vertexBuffer, targetIndexing, sharedIndexing, sourceStreams):
82  # get the descriptor from the vertex buffer.
83  # It describes the format and layout of the stream.
84  descriptor = vertexBuffer.descriptor()
85 
86  # we are expecting a float stream.
87  if descriptor.dataType != omr.MGeometry.kFloat:
88  return
89 
90  # we are expecting a float2
91  if descriptor.dimension != 2:
92  return
93 
94  # we are expecting a texture channel
95  if descriptor.semantic != omr.MGeometry.kTexture:
96  return
97 
98  # get the mesh from the current path
99  # if it is not a mesh we do nothing.
100  mesh = om.MFnMesh(object)
101 
102  indices = targetIndexing.indices()
103 
104  vertexCount = len(indices)
105  if vertexCount <= 0:
106  return
107 
108  # fill the data.
109  buffer = vertexBuffer.acquire(vertexCount, True) # writeOnly = True - we don't need the current buffer values
110 
111  inc = sizeof(c_float)
112  address = buffer
113 
114  for i in range(0, vertexCount):
115  # Here we are embedding some custom data into the stream.
116  # The included effects (vertexBufferGeneratorGL.cgfx and
117  # vertexBufferGeneratorDX11.fx) will alternate
118  # red, green, and blue vertex colored triangles based on this input.
119  c_float.from_address(address).value = 1.0
120  address += inc
121 
122  c_float.from_address(address).value = indices[i] # color index
123  address += inc
124 
125  # commit the buffer to signal completion.
126  vertexBuffer.commit(buffer)
127 
128 
129 class MyCustomBufferGenerator2(omr.MPxVertexBufferGenerator):
130  def __init__(self):
131  omr.MPxVertexBufferGenerator.__init__(self)
132 
133  def getSourceIndexing(self, object, sourceIndexing):
134  # get the mesh from the object
135  mesh = om.MFnMesh(object)
136 
137  (vertexCount, vertexList) = mesh.getVertices()
138  vertCount = len(vertexList)
139 
140  vertices = sourceIndexing.indices()
141  for i in range(0, vertCount):
142  vertices.append( vertexList[i] )
143 
144  return True
145 
146  def getSourceStreams(self, object, sourceStreams):
147  sourceStreams.append( "Position" )
148  sourceStreams.append( "Normal" )
149  return True
150 
151  def createVertexStream(self, object, vertexBuffer, targetIndexing, sharedIndexing, sourceStreams):
152  # get the descriptor from the vertex buffer.
153  # It describes the format and layout of the stream.
154  descriptor = vertexBuffer.descriptor()
155 
156  # we are expecting a float or int stream.
157  dataType = descriptor.dataType
158  if dataType != omr.MGeometry.kFloat and dataType != omr.MGeometry.kInt32:
159  return
160 
161  # we are expecting a dimension of 3 or 4
162  dimension = descriptor.dimension
163  if dimension != 4 and dimension != 3:
164  return
165 
166  # we are expecting a texture channel
167  if descriptor.semantic != omr.MGeometry.kTexture:
168  return
169 
170  # get the mesh from the current path
171  # if it is not a mesh we do nothing.
172  mesh = om.MFnMesh(object)
173 
174  indices = targetIndexing.indices()
175 
176  vertexCount = len(indices)
177  if vertexCount <= 0:
178  return
179 
180  positionStream = sourceStreams.getBuffer( "Position" )
181  if positionStream == None or positionStream.descriptor().dataType != omr.MGeometry.kFloat:
182  return
183  positionDimension = positionStream.descriptor().dimension
184  if positionDimension != 3 and positionDimension != 4:
185  return
186 
187  normalStream = sourceStreams.getBuffer( "Normal" )
188  if normalStream == None or normalStream.descriptor().dataType != omr.MGeometry.kFloat:
189  return
190  normalDimension = normalStream.descriptor().dimension
191  if normalDimension != 3 and normalDimension != 4:
192  return
193 
194  positionBuffer = positionStream.map()
195  if positionBuffer != 0:
196  normalBuffer = normalStream.map()
197  if normalBuffer != 0:
198  compositeBuffer = vertexBuffer.acquire(vertexCount, True) # writeOnly = True - we don't need the current buffer values
199  if compositeBuffer != 0:
200 
201  compaddress = compositeBuffer
202  posaddress = positionBuffer
203  normaddress = normalBuffer
204 
205  floatinc = sizeof(c_float)
206  intinc = sizeof(c_int)
207 
208  if dataType == omr.MGeometry.kFloat:
209 
210  for i in range(0, vertexCount):
211  xcompaddr = compaddress
212  ycompaddr = compaddress+floatinc
213  zcompaddr = compaddress+2*floatinc
214  wcompaddr = compaddress+3*floatinc
215 
216  #xposaddr = posaddress
217  yposaddr = posaddress+floatinc
218  zposaddr = posaddress+2*floatinc
219 
220  xnormaddr = normaddress
221  #ynormaddr = normaddress+floatinc
222  znormaddr = normaddress+2*floatinc
223 
224  c_float.from_address(xcompaddr).value = c_float.from_address(yposaddr).value # store position.y
225  c_float.from_address(ycompaddr).value = c_float.from_address(zposaddr).value # store position.z
226  c_float.from_address(zcompaddr).value = c_float.from_address(xnormaddr).value # store normal.x
227  if dimension == 4:
228  c_float.from_address(wcompaddr).value = c_float.from_address(znormaddr).value # store normal.z
229 
230  compaddress += dimension*floatinc
231  posaddress += positionDimension*floatinc
232  normaddress += normalDimension*floatinc
233 
234  elif dataType == omr.MGeometry.kInt32:
235 
236  for i in range(0, vertexCount):
237  xcompaddr = compaddress
238  ycompaddr = compaddress+intinc
239  zcompaddr = compaddress+2*intinc
240  wcompaddr = compaddress+3*intinc
241 
242  #xposaddr = posaddress
243  yposaddr = posaddress+floatinc
244  zposaddr = posaddress+2*floatinc
245 
246  xnormaddr = normaddress
247  #ynormaddr = normaddress+floatinc
248  znormaddr = normaddress+2*floatinc
249 
250  c_int.from_address(xcompaddr).value = c_float.from_address(yposaddr).value * 255 # store position.y
251  c_int.from_address(ycompaddr).value = c_float.from_address(zposaddr).value * 255 # store position.z
252  c_int.from_address(zcompaddr).value = c_float.from_address(xnormaddr).value * 255 # store normal.x
253  if dimension == 4:
254  c_int.from_address(wcompaddr).value = c_float.from_address(znormaddr).value * 255 # store normal.z
255 
256  compaddress += dimension*intinc
257  posaddress += positionDimension*floatinc
258  normaddress += normalDimension*floatinc
259 
260  vertexBuffer.commit(compositeBuffer)
261 
262  normalStream.unmap()
263 
264  positionStream.unmap()
265 
266 # This is the buffer generator creation function registered with the DrawRegistry.
267 # Used to initialize the generator.
268 def createMyCustomBufferGenerator():
269  return MyCustomBufferGenerator()
270 
271 def createMyCustomBufferGenerator2():
272  return MyCustomBufferGenerator2()
273 
274 
275 def getCustomSemantics():
276  if omr.MRenderer.drawAPI() == omr.MRenderer.kDirectX11:
277  # register a generator based on a custom semantic for DX11.
278  # You can use any name in DX11.
279  return ("myCustomStream", "myCustomStreamB")
280  if omr.MRenderer.drawAPI() == omr.MRenderer.kOpenGLCoreProfile:
281  # register a generator based on a custom semantic for OGSFX.
282  # Pretty limited in OGSFX since it only allows standard semantics.
283  # But thanks to the annotations a custom value can be set afterward from glslShader plugin
284  return ("myCustomStream", "myCustomStreamB")
285  if omr.MRenderer.drawAPI() == omr.MRenderer.kOpenGL:
286  # register a generator based on a custom semantic for cg.
287  # Pretty limited in cg so we hook onto the "ATTR" semantics.
288  return ("ATTR8", "ATTR7")
289  return None
290 
291 
292 # The following routines are used to register/unregister
293 # the vertex generators with Maya
294 
295 def initializePlugin(obj):
296  (customSemantic, customSemantic2) = getCustomSemantics()
297 
298  omr.MDrawRegistry.registerVertexBufferGenerator(customSemantic, createMyCustomBufferGenerator)
299  omr.MDrawRegistry.registerVertexBufferGenerator(customSemantic2, createMyCustomBufferGenerator2)
300 
301 def uninitializePlugin(obj):
302  (customSemantic, customSemantic2) = getCustomSemantics()
303 
304  omr.MDrawRegistry.deregisterVertexBufferGenerator(customSemantic)
305  omr.MDrawRegistry.deregisterVertexBufferGenerator(customSemantic2)
306