source: other-projects/playing-in-the-street/summer-2013/trunk/Playing-in-the-Street-WPF/Content/Web/mrdoob-three.js-4862f5f/utils/exporters/maya/plug-ins/threeJsFileTranslator.py@ 28897

Last change on this file since 28897 was 28897, checked in by davidb, 10 years ago

GUI front-end to server base plus web page content

File size: 9.7 KB
Line 
1__author__ = 'Chris Lewis'
2__version__ = '0.1.0'
3__email__ = '[email protected]'
4
5import sys
6import json
7
8import maya.cmds as mc
9from maya.OpenMaya import *
10from maya.OpenMayaMPx import *
11
12kPluginTranslatorTypeName = 'Three.js'
13kOptionScript = 'ThreeJsExportScript'
14kDefaultOptionsString = '0'
15
16FLOAT_PRECISION = 8
17
18
19# adds decimal precision to JSON encoding
20class DecimalEncoder(json.JSONEncoder):
21 def _iterencode(self, o, markers=None):
22 if isinstance(o, float):
23 s = str(o)
24 if '.' in s and len(s[s.index('.'):]) > FLOAT_PRECISION - 1:
25 s = '%.{0}f'.format(FLOAT_PRECISION) % o
26 while '.' in s and s[-1] == '0':
27 s = s[:-1] # this actually removes the last "0" from the string
28 if s[-1] == '.': # added this test to avoid leaving "0." instead of "0.0",
29 s += '0' # which would throw an error while loading the file
30 return (s for s in [s])
31 return super(DecimalEncoder, self)._iterencode(o, markers)
32
33
34class ThreeJsError(Exception):
35 pass
36
37
38class ThreeJsWriter(object):
39 def __init__(self):
40 self.componentKeys = ['vertices', 'normals', 'colors', 'uvs', 'materials', 'faces']
41
42 def _parseOptions(self, optionsString):
43 self.options = dict([(x, False) for x in self.componentKeys])
44 optionsString = optionsString[2:] # trim off the "0;" that Maya adds to the options string
45 for option in optionsString.split(' '):
46 self.options[option] = True
47
48 def _updateOffsets(self):
49 for key in self.componentKeys:
50 if key == 'uvs':
51 continue
52 self.offsets[key] = len(getattr(self, key))
53 for i in range(len(self.uvs)):
54 self.offsets['uvs'][i] = len(self.uvs[i])
55
56 def _getTypeBitmask(self, options):
57 bitmask = 0
58 if options['materials']:
59 bitmask |= 2
60 if options['uvs']:
61 bitmask |= 8
62 if options['normals']:
63 bitmask |= 32
64 if options['colors']:
65 bitmask |= 128
66 return bitmask
67
68 def _exportMesh(self, dagPath, component):
69 mesh = MFnMesh(dagPath)
70 options = self.options.copy()
71 self._updateOffsets()
72
73 # export vertex data
74 if options['vertices']:
75 try:
76 iterVerts = MItMeshVertex(dagPath, component)
77 while not iterVerts.isDone():
78 point = iterVerts.position(MSpace.kWorld)
79 self.vertices += [point.x, point.y, point.z]
80 iterVerts.next()
81 except:
82 options['vertices'] = False
83
84 # export material data
85 # TODO: actually parse material data
86 materialIndices = MIntArray()
87 if options['materials']:
88 try:
89 shaders = MObjectArray()
90 mesh.getConnectedShaders(0, shaders, materialIndices)
91 while len(self.materials) < shaders.length():
92 self.materials.append({}) # placeholder material definition
93 except:
94 self.materials = [{}]
95
96 # export uv data
97 if options['uvs']:
98 try:
99 uvLayers = []
100 mesh.getUVSetNames(uvLayers)
101 while len(uvLayers) > len(self.uvs):
102 self.uvs.append([])
103 self.offsets['uvs'].append(0)
104 for i, layer in enumerate(uvLayers):
105 uList = MFloatArray()
106 vList = MFloatArray()
107 mesh.getUVs(uList, vList, layer)
108 for j in xrange(uList.length()):
109 self.uvs[i] += [uList[j], vList[j]]
110 except:
111 options['uvs'] = False
112
113 # export normal data
114 if options['normals']:
115 try:
116 normals = MFloatVectorArray()
117 mesh.getNormals(normals, MSpace.kWorld)
118 for i in xrange(normals.length()):
119 point = normals[i]
120 self.normals += [point.x, point.y, point.z]
121 except:
122 options['normals'] = False
123
124 # export color data
125 if options['colors']:
126 try:
127 colors = MColorArray()
128 mesh.getColors(colors)
129 for i in xrange(colors.length()):
130 color = colors[i]
131 # uncolored vertices are set to (-1, -1, -1). Clamps colors to (0, 0, 0).
132 self.colors += [max(color.r, 0), max(color.g, 0), max(color.b, 0)]
133 except:
134 options['colors'] = False
135
136 # export face data
137 if not options['vertices']:
138 return
139 bitmask = self._getTypeBitmask(options)
140 iterPolys = MItMeshPolygon(dagPath, component)
141 while not iterPolys.isDone():
142 self.faces.append(bitmask)
143 # export face vertices
144 verts = MIntArray()
145 iterPolys.getVertices(verts)
146 for i in xrange(verts.length()):
147 self.faces.append(verts[i] + self.offsets['vertices'])
148 # export face vertex materials
149 if options['materials']:
150 if materialIndices.length():
151 self.faces.append(materialIndices[iterPolys.index()])
152 # export face vertex uvs
153 if options['uvs']:
154 util = MScriptUtil()
155 uvPtr = util.asIntPtr()
156 for i, layer in enumerate(uvLayers):
157 for j in xrange(verts.length()):
158 iterPolys.getUVIndex(j, uvPtr, layer)
159 uvIndex = util.getInt(uvPtr)
160 self.faces.append(uvIndex + self.offsets['uvs'][i])
161 # export face vertex normals
162 if options['normals']:
163 for i in xrange(3):
164 normalIndex = iterPolys.normalIndex(i)
165 self.faces.append(normalIndex + self.offsets['normals'])
166 # export face vertex colors
167 if options['colors']:
168 colors = MIntArray()
169 iterPolys.getColorIndices(colors)
170 for i in xrange(colors.length()):
171 self.faces.append(colors[i] + self.offsets['colors'])
172 iterPolys.next()
173
174 def _getMeshes(self, nodes):
175 meshes = []
176 for node in nodes:
177 if mc.nodeType(node) == 'mesh':
178 meshes.append(node)
179 else:
180 for child in mc.listRelatives(node, s=1):
181 if mc.nodeType(child) == 'mesh':
182 meshes.append(child)
183 return meshes
184
185 def _exportMeshes(self):
186 # export all
187 if self.accessMode == MPxFileTranslator.kExportAccessMode:
188 mc.select(self._getMeshes(mc.ls(typ='mesh')))
189 # export selection
190 elif self.accessMode == MPxFileTranslator.kExportActiveAccessMode:
191 mc.select(self._getMeshes(mc.ls(sl=1)))
192 else:
193 raise ThreeJsError('Unsupported access mode: {0}'.format(self.accessMode))
194 dups = [mc.duplicate(mesh)[0] for mesh in mc.ls(sl=1)]
195 combined = mc.polyUnite(dups, mergeUVSets=1, ch=0) if len(dups) > 1 else dups[0]
196 mc.polyTriangulate(combined)
197 mc.select(combined)
198 sel = MSelectionList()
199 MGlobal.getActiveSelectionList(sel)
200 mDag = MDagPath()
201 mComp = MObject()
202 sel.getDagPath(0, mDag, mComp)
203 self._exportMesh(mDag, mComp)
204 mc.delete(combined)
205
206 def write(self, path, optionString, accessMode):
207 self.path = path
208 self._parseOptions(optionString)
209 self.accessMode = accessMode
210 self.root = dict(metadata=dict(formatVersion=3))
211 self.offsets = dict()
212 for key in self.componentKeys:
213 setattr(self, key, [])
214 self.offsets[key] = 0
215 self.offsets['uvs'] = []
216 self.uvs = []
217
218 self._exportMeshes()
219
220 # add the component buffers to the root JSON object
221 for key in self.componentKeys:
222 buffer_ = getattr(self, key)
223 if buffer_:
224 self.root[key] = buffer_
225
226 # materials are required for parsing
227 if not self.root.has_key('materials'):
228 self.root['materials'] = [{}]
229
230 # write the file
231 with file(self.path, 'w') as f:
232 f.write(json.dumps(self.root, separators=(',',':'), cls=DecimalEncoder))
233
234
235class ThreeJsTranslator(MPxFileTranslator):
236 def __init__(self):
237 MPxFileTranslator.__init__(self)
238
239 def haveWriteMethod(self):
240 return True
241
242 def filter(self):
243 return '*.js'
244
245 def defaultExtension(self):
246 return 'js'
247
248 def writer(self, fileObject, optionString, accessMode):
249 path = fileObject.fullName()
250 writer = ThreeJsWriter()
251 writer.write(path, optionString, accessMode)
252
253
254def translatorCreator():
255 return asMPxPtr(ThreeJsTranslator())
256
257
258def initializePlugin(mobject):
259 mplugin = MFnPlugin(mobject)
260 try:
261 mplugin.registerFileTranslator(kPluginTranslatorTypeName, None, translatorCreator, kOptionScript, kDefaultOptionsString)
262 except:
263 sys.stderr.write('Failed to register translator: %s' % kPluginTranslatorTypeName)
264 raise
265
266
267def uninitializePlugin(mobject):
268 mplugin = MFnPlugin(mobject)
269 try:
270 mplugin.deregisterFileTranslator(kPluginTranslatorTypeName)
271 except:
272 sys.stderr.write('Failed to deregister translator: %s' % kPluginTranslatorTypeName)
273 raise
Note: See TracBrowser for help on using the repository browser.