scripted/pyVertexBufferGenerator.py

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