source: other-projects/playing-in-the-street/summer-2013/trunk/Playing-in-the-Street-WPF/Content/Web/mrdoob-three.js-4862f5f/utils/converters/obj/convert_obj_three.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: 46.3 KB
Line 
1"""Convert Wavefront OBJ / MTL files into Three.js (JSON model version, to be used with ascii / binary loader)
2
3-------------------------
4How to use this converter
5-------------------------
6
7python convert_obj_three.py -i infile.obj -o outfile.js [-m "morphfiles*.obj"] [-c "morphcolors*.obj"] [-a center|centerxz|top|bottom|none] [-s smooth|flat] [-t ascii|binary] [-d invert|normal] [-b] [-e]
8
9Notes:
10 - flags
11 -i infile.obj input OBJ file
12 -o outfile.js output JS file
13 -m "morphfiles*.obj" morph OBJ files (can use wildcards, enclosed in quotes multiple patterns separate by space)
14 -c "morphcolors*.obj" morph colors OBJ files (can use wildcards, enclosed in quotes multiple patterns separate by space)
15 -a center|centerxz|top|bottom|none model alignment
16 -s smooth|flat smooth = export vertex normals, flat = no normals (face normals computed in loader)
17 -t ascii|binary export ascii or binary format (ascii has more features, binary just supports vertices, faces, normals, uvs and materials)
18 -d invert|normal invert transparency
19 -b bake material colors into face colors
20 -x 10.0 scale and truncate
21 -f 2 morph frame sampling step
22
23 - by default:
24 use smooth shading (if there were vertex normals in the original model)
25 will be in ASCII format
26 original model is assumed to use non-inverted transparency / dissolve (0.0 fully transparent, 1.0 fully opaque)
27 no face colors baking
28 no scale and truncate
29 morph frame step = 1 (all files will be processed)
30
31 - binary conversion will create two files:
32 outfile.js (materials)
33 outfile.bin (binary buffers)
34
35--------------------------------------------------
36How to use generated JS file in your HTML document
37--------------------------------------------------
38
39 <script type="text/javascript" src="Three.js"></script>
40
41 ...
42
43 <script type="text/javascript">
44 ...
45
46 // load ascii model
47
48 var jsonLoader = new THREE.JSONLoader();
49 jsonLoader.load( "Model_ascii.js", createScene );
50
51 // load binary model
52
53 var binLoader = new THREE.BinaryLoader();
54 binLoader.load( "Model_bin.js", createScene );
55
56 function createScene( geometry, materials ) {
57
58 var mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial( materials ) );
59
60 }
61
62 ...
63 </script>
64
65-------------------------------------
66Parsers based on formats descriptions
67-------------------------------------
68
69 http://en.wikipedia.org/wiki/Obj
70 http://en.wikipedia.org/wiki/Material_Template_Library
71
72-------------------
73Current limitations
74-------------------
75
76 - for the moment, only diffuse color and texture are used
77 (will need to extend shaders / renderers / materials in Three)
78
79 - texture coordinates can be wrong in canvas renderer
80 (there is crude normalization, but it doesn't
81 work for all cases)
82
83 - smoothing can be turned on/off only for the whole mesh
84
85----------------------------------------------
86How to get proper OBJ + MTL files with Blender
87----------------------------------------------
88
89 0. Remove default cube (press DEL and ENTER)
90
91 1. Import / create model
92
93 2. Select all meshes (Select -> Select All by Type -> Mesh)
94
95 3. Export to OBJ (File -> Export -> Wavefront .obj)
96 - enable following options in exporter
97 Material Groups
98 Rotate X90
99 Apply Modifiers
100 High Quality Normals
101 Copy Images
102 Selection Only
103 Objects as OBJ Objects
104 UVs
105 Normals
106 Materials
107
108 - select empty folder
109 - give your exported file name with "obj" extension
110 - click on "Export OBJ" button
111
112 4. Your model is now all files in this folder (OBJ, MTL, number of images)
113 - this converter assumes all files staying in the same folder,
114 (OBJ / MTL files use relative paths)
115
116 - for WebGL, textures must be power of 2 sized
117
118------
119Author
120------
121AlteredQualia http://alteredqualia.com
122
123"""
124
125import fileinput
126import operator
127import random
128import os.path
129import getopt
130import sys
131import struct
132import math
133import glob
134
135# #####################################################
136# Configuration
137# #####################################################
138ALIGN = "none" # center centerxz bottom top none
139SHADING = "smooth" # smooth flat
140TYPE = "ascii" # ascii binary
141TRANSPARENCY = "normal" # normal invert
142
143TRUNCATE = False
144SCALE = 1.0
145
146FRAMESTEP = 1
147
148BAKE_COLORS = False
149
150# default colors for debugging (each material gets one distinct color):
151# white, red, green, blue, yellow, cyan, magenta
152COLORS = [0xeeeeee, 0xee0000, 0x00ee00, 0x0000ee, 0xeeee00, 0x00eeee, 0xee00ee]
153
154# #####################################################
155# Templates
156# #####################################################
157TEMPLATE_FILE_ASCII = u"""\
158{
159
160 "metadata" :
161 {
162 "formatVersion" : 3.1,
163 "sourceFile" : "%(fname)s",
164 "generatedBy" : "OBJConverter",
165 "vertices" : %(nvertex)d,
166 "faces" : %(nface)d,
167 "normals" : %(nnormal)d,
168 "colors" : %(ncolor)d,
169 "uvs" : %(nuv)d,
170 "materials" : %(nmaterial)d
171 },
172
173 "scale" : %(scale)f,
174
175 "materials": [%(materials)s],
176
177 "vertices": [%(vertices)s],
178
179 "morphTargets": [%(morphTargets)s],
180
181 "morphColors": [%(morphColors)s],
182
183 "normals": [%(normals)s],
184
185 "colors": [%(colors)s],
186
187 "uvs": [[%(uvs)s]],
188
189 "faces": [%(faces)s]
190
191}
192"""
193
194TEMPLATE_FILE_BIN = u"""\
195{
196
197 "metadata" :
198 {
199 "formatVersion" : 3.1,
200 "sourceFile" : "%(fname)s",
201 "generatedBy" : "OBJConverter",
202 "vertices" : %(nvertex)d,
203 "faces" : %(nface)d,
204 "normals" : %(nnormal)d,
205 "uvs" : %(nuv)d,
206 "materials" : %(nmaterial)d
207 },
208
209 "materials": [%(materials)s],
210
211 "buffers": "%(buffers)s"
212
213}
214"""
215
216TEMPLATE_VERTEX = "%f,%f,%f"
217TEMPLATE_VERTEX_TRUNCATE = "%d,%d,%d"
218
219TEMPLATE_N = "%.5g,%.5g,%.5g"
220TEMPLATE_UV = "%.5g,%.5g"
221TEMPLATE_COLOR = "%.3g,%.3g,%.3g"
222TEMPLATE_COLOR_DEC = "%d"
223
224TEMPLATE_MORPH_VERTICES = '\t{ "name": "%s", "vertices": [%s] }'
225TEMPLATE_MORPH_COLORS = '\t{ "name": "%s", "colors": [%s] }'
226
227# #####################################################
228# Utils
229# #####################################################
230def file_exists(filename):
231 """Return true if file exists and is accessible for reading.
232
233 Should be safer than just testing for existence due to links and
234 permissions magic on Unix filesystems.
235
236 @rtype: boolean
237 """
238
239 try:
240 f = open(filename, 'r')
241 f.close()
242 return True
243 except IOError:
244 return False
245
246
247def get_name(fname):
248 """Create model name based of filename ("path/fname.js" -> "fname").
249 """
250
251 return os.path.splitext(os.path.basename(fname))[0]
252
253def bbox(vertices):
254 """Compute bounding box of vertex array.
255 """
256
257 if len(vertices)>0:
258 minx = maxx = vertices[0][0]
259 miny = maxy = vertices[0][1]
260 minz = maxz = vertices[0][2]
261
262 for v in vertices[1:]:
263 if v[0]<minx:
264 minx = v[0]
265 elif v[0]>maxx:
266 maxx = v[0]
267
268 if v[1]<miny:
269 miny = v[1]
270 elif v[1]>maxy:
271 maxy = v[1]
272
273 if v[2]<minz:
274 minz = v[2]
275 elif v[2]>maxz:
276 maxz = v[2]
277
278 return { 'x':[minx,maxx], 'y':[miny,maxy], 'z':[minz,maxz] }
279
280 else:
281 return { 'x':[0,0], 'y':[0,0], 'z':[0,0] }
282
283def translate(vertices, t):
284 """Translate array of vertices by vector t.
285 """
286
287 for i in xrange(len(vertices)):
288 vertices[i][0] += t[0]
289 vertices[i][1] += t[1]
290 vertices[i][2] += t[2]
291
292def center(vertices):
293 """Center model (middle of bounding box).
294 """
295
296 bb = bbox(vertices)
297
298 cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
299 cy = bb['y'][0] + (bb['y'][1] - bb['y'][0])/2.0
300 cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
301
302 translate(vertices, [-cx,-cy,-cz])
303
304def top(vertices):
305 """Align top of the model with the floor (Y-axis) and center it around X and Z.
306 """
307
308 bb = bbox(vertices)
309
310 cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
311 cy = bb['y'][1]
312 cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
313
314 translate(vertices, [-cx,-cy,-cz])
315
316def bottom(vertices):
317 """Align bottom of the model with the floor (Y-axis) and center it around X and Z.
318 """
319
320 bb = bbox(vertices)
321
322 cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
323 cy = bb['y'][0]
324 cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
325
326 translate(vertices, [-cx,-cy,-cz])
327
328def centerxz(vertices):
329 """Center model around X and Z.
330 """
331
332 bb = bbox(vertices)
333
334 cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
335 cy = 0
336 cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
337
338 translate(vertices, [-cx,-cy,-cz])
339
340def normalize(v):
341 """Normalize 3d vector"""
342
343 l = math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
344 if l:
345 v[0] /= l
346 v[1] /= l
347 v[2] /= l
348
349def veckey3(v):
350 return round(v[0], 6), round(v[1], 6), round(v[2], 6)
351
352# #####################################################
353# MTL parser
354# #####################################################
355def texture_relative_path(fullpath):
356 texture_file = os.path.basename(fullpath.replace("\\", "/"))
357 return texture_file
358
359def parse_mtl(fname):
360 """Parse MTL file.
361 """
362
363 materials = {}
364
365 for line in fileinput.input(fname):
366 chunks = line.split()
367 if len(chunks) > 0:
368
369 # Material start
370 # newmtl identifier
371 if chunks[0] == "newmtl":
372 if len(chunks) > 1:
373 identifier = chunks[1]
374 else:
375 identifier = ""
376 if not identifier in materials:
377 materials[identifier] = {}
378
379 # Diffuse color
380 # Kd 1.000 1.000 1.000
381 if chunks[0] == "Kd" and len(chunks) == 4:
382 materials[identifier]["colorDiffuse"] = [float(chunks[1]), float(chunks[2]), float(chunks[3])]
383
384 # Ambient color
385 # Ka 1.000 1.000 1.000
386 if chunks[0] == "Ka" and len(chunks) == 4:
387 materials[identifier]["colorAmbient"] = [float(chunks[1]), float(chunks[2]), float(chunks[3])]
388
389 # Specular color
390 # Ks 1.000 1.000 1.000
391 if chunks[0] == "Ks" and len(chunks) == 4:
392 materials[identifier]["colorSpecular"] = [float(chunks[1]), float(chunks[2]), float(chunks[3])]
393
394 # Specular coefficient
395 # Ns 154.000
396 if chunks[0] == "Ns" and len(chunks) == 2:
397 materials[identifier]["specularCoef"] = float(chunks[1])
398
399 # Transparency
400 # Tr 0.9 or d 0.9
401 if (chunks[0] == "Tr" or chunks[0] == "d") and len(chunks) == 2:
402 if TRANSPARENCY == "invert":
403 materials[identifier]["transparency"] = 1.0 - float(chunks[1])
404 else:
405 materials[identifier]["transparency"] = float(chunks[1])
406
407 # Optical density
408 # Ni 1.0
409 if chunks[0] == "Ni" and len(chunks) == 2:
410 materials[identifier]["opticalDensity"] = float(chunks[1])
411
412 # Diffuse texture
413 # map_Kd texture_diffuse.jpg
414 if chunks[0] == "map_Kd" and len(chunks) == 2:
415 materials[identifier]["mapDiffuse"] = texture_relative_path(chunks[1])
416
417 # Ambient texture
418 # map_Ka texture_ambient.jpg
419 if chunks[0] == "map_Ka" and len(chunks) == 2:
420 materials[identifier]["mapAmbient"] = texture_relative_path(chunks[1])
421
422 # Specular texture
423 # map_Ks texture_specular.jpg
424 if chunks[0] == "map_Ks" and len(chunks) == 2:
425 materials[identifier]["mapSpecular"] = texture_relative_path(chunks[1])
426
427 # Alpha texture
428 # map_d texture_alpha.png
429 if chunks[0] == "map_d" and len(chunks) == 2:
430 materials[identifier]["mapAlpha"] = texture_relative_path(chunks[1])
431
432 # Bump texture
433 # map_bump texture_bump.jpg or bump texture_bump.jpg
434 if (chunks[0] == "map_bump" or chunks[0] == "bump") and len(chunks) == 2:
435 materials[identifier]["mapBump"] = texture_relative_path(chunks[1])
436
437 # Illumination
438 # illum 2
439 #
440 # 0. Color on and Ambient off
441 # 1. Color on and Ambient on
442 # 2. Highlight on
443 # 3. Reflection on and Ray trace on
444 # 4. Transparency: Glass on, Reflection: Ray trace on
445 # 5. Reflection: Fresnel on and Ray trace on
446 # 6. Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
447 # 7. Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
448 # 8. Reflection on and Ray trace off
449 # 9. Transparency: Glass on, Reflection: Ray trace off
450 # 10. Casts shadows onto invisible surfaces
451 if chunks[0] == "illum" and len(chunks) == 2:
452 materials[identifier]["illumination"] = int(chunks[1])
453
454 return materials
455
456# #####################################################
457# OBJ parser
458# #####################################################
459def parse_vertex(text):
460 """Parse text chunk specifying single vertex.
461
462 Possible formats:
463 vertex index
464 vertex index / texture index
465 vertex index / texture index / normal index
466 vertex index / / normal index
467 """
468
469 v = 0
470 t = 0
471 n = 0
472
473 chunks = text.split("/")
474
475 v = int(chunks[0])
476 if len(chunks) > 1:
477 if chunks[1]:
478 t = int(chunks[1])
479 if len(chunks) > 2:
480 if chunks[2]:
481 n = int(chunks[2])
482
483 return { 'v':v, 't':t, 'n':n }
484
485def parse_obj(fname):
486 """Parse OBJ file.
487 """
488
489 vertices = []
490 normals = []
491 uvs = []
492
493 faces = []
494
495 materials = {}
496 material = ""
497 mcounter = 0
498 mcurrent = 0
499
500 mtllib = ""
501
502 # current face state
503 group = 0
504 object = 0
505 smooth = 0
506
507 for line in fileinput.input(fname):
508 chunks = line.split()
509 if len(chunks) > 0:
510
511 # Vertices as (x,y,z) coordinates
512 # v 0.123 0.234 0.345
513 if chunks[0] == "v" and len(chunks) == 4:
514 x = float(chunks[1])
515 y = float(chunks[2])
516 z = float(chunks[3])
517 vertices.append([x,y,z])
518
519 # Normals in (x,y,z) form; normals might not be unit
520 # vn 0.707 0.000 0.707
521 if chunks[0] == "vn" and len(chunks) == 4:
522 x = float(chunks[1])
523 y = float(chunks[2])
524 z = float(chunks[3])
525 normals.append([x,y,z])
526
527 # Texture coordinates in (u,v[,w]) coordinates, w is optional
528 # vt 0.500 -1.352 [0.234]
529 if chunks[0] == "vt" and len(chunks) >= 3:
530 u = float(chunks[1])
531 v = float(chunks[2])
532 w = 0
533 if len(chunks)>3:
534 w = float(chunks[3])
535 uvs.append([u,v,w])
536
537 # Face
538 if chunks[0] == "f" and len(chunks) >= 4:
539 vertex_index = []
540 uv_index = []
541 normal_index = []
542
543
544 # Precompute vert / normal / uv lists
545 # for negative index lookup
546 vertlen = len(vertices) + 1
547 normlen = len(normals) + 1
548 uvlen = len(uvs) + 1
549
550 for v in chunks[1:]:
551 vertex = parse_vertex(v)
552 if vertex['v']:
553 if vertex['v'] < 0:
554 vertex['v'] += vertlen
555 vertex_index.append(vertex['v'])
556 if vertex['t']:
557 if vertex['t'] < 0:
558 vertex['t'] += uvlen
559 uv_index.append(vertex['t'])
560 if vertex['n']:
561 if vertex['n'] < 0:
562 vertex['n'] += normlen
563 normal_index.append(vertex['n'])
564 faces.append({
565 'vertex':vertex_index,
566 'uv':uv_index,
567 'normal':normal_index,
568
569 'material':mcurrent,
570 'group':group,
571 'object':object,
572 'smooth':smooth,
573 })
574
575 # Group
576 if chunks[0] == "g" and len(chunks) == 2:
577 group = chunks[1]
578
579 # Object
580 if chunks[0] == "o" and len(chunks) == 2:
581 object = chunks[1]
582
583 # Materials definition
584 if chunks[0] == "mtllib" and len(chunks) == 2:
585 mtllib = chunks[1]
586
587 # Material
588 if chunks[0] == "usemtl":
589 if len(chunks) > 1:
590 material = chunks[1]
591 else:
592 material = ""
593 if not material in materials:
594 mcurrent = mcounter
595 materials[material] = mcounter
596 mcounter += 1
597 else:
598 mcurrent = materials[material]
599
600 # Smooth shading
601 if chunks[0] == "s" and len(chunks) == 2:
602 smooth = chunks[1]
603
604 return faces, vertices, uvs, normals, materials, mtllib
605
606# #####################################################
607# Generator - faces
608# #####################################################
609def setBit(value, position, on):
610 if on:
611 mask = 1 << position
612 return (value | mask)
613 else:
614 mask = ~(1 << position)
615 return (value & mask)
616
617def generate_face(f, fc):
618 isTriangle = ( len(f['vertex']) == 3 )
619
620 if isTriangle:
621 nVertices = 3
622 else:
623 nVertices = 4
624
625 hasMaterial = True # for the moment OBJs without materials get default material
626
627 hasFaceUvs = False # not supported in OBJ
628 hasFaceVertexUvs = ( len(f['uv']) >= nVertices )
629
630 hasFaceNormals = False # don't export any face normals (as they are computed in engine)
631 hasFaceVertexNormals = ( len(f["normal"]) >= nVertices and SHADING == "smooth" )
632
633 hasFaceColors = BAKE_COLORS
634 hasFaceVertexColors = False # not supported in OBJ
635
636 faceType = 0
637 faceType = setBit(faceType, 0, not isTriangle)
638 faceType = setBit(faceType, 1, hasMaterial)
639 faceType = setBit(faceType, 2, hasFaceUvs)
640 faceType = setBit(faceType, 3, hasFaceVertexUvs)
641 faceType = setBit(faceType, 4, hasFaceNormals)
642 faceType = setBit(faceType, 5, hasFaceVertexNormals)
643 faceType = setBit(faceType, 6, hasFaceColors)
644 faceType = setBit(faceType, 7, hasFaceVertexColors)
645
646 faceData = []
647
648 # order is important, must match order in JSONLoader
649
650 # face type
651 # vertex indices
652 # material index
653 # face uvs index
654 # face vertex uvs indices
655 # face normal index
656 # face vertex normals indices
657 # face color index
658 # face vertex colors indices
659
660 faceData.append(faceType)
661
662 # must clamp in case on polygons bigger than quads
663
664 for i in xrange(nVertices):
665 index = f['vertex'][i] - 1
666 faceData.append(index)
667
668 faceData.append( f['material'] )
669
670 if hasFaceVertexUvs:
671 for i in xrange(nVertices):
672 index = f['uv'][i] - 1
673 faceData.append(index)
674
675 if hasFaceVertexNormals:
676 for i in xrange(nVertices):
677 index = f['normal'][i] - 1
678 faceData.append(index)
679
680 if hasFaceColors:
681 index = fc['material']
682 faceData.append(index)
683
684 return ",".join( map(str, faceData) )
685
686# #####################################################
687# Generator - chunks
688# #####################################################
689def hexcolor(c):
690 return ( int(c[0] * 255) << 16 ) + ( int(c[1] * 255) << 8 ) + int(c[2] * 255)
691
692def generate_vertex(v, option_vertices_truncate, scale):
693 if not option_vertices_truncate:
694 return TEMPLATE_VERTEX % (v[0], v[1], v[2])
695 else:
696 return TEMPLATE_VERTEX_TRUNCATE % (scale * v[0], scale * v[1], scale * v[2])
697
698def generate_normal(n):
699 return TEMPLATE_N % (n[0], n[1], n[2])
700
701def generate_uv(uv):
702 return TEMPLATE_UV % (uv[0], uv[1])
703
704def generate_color_rgb(c):
705 return TEMPLATE_COLOR % (c[0], c[1], c[2])
706
707def generate_color_decimal(c):
708 return TEMPLATE_COLOR_DEC % hexcolor(c)
709
710# #####################################################
711# Morphs
712# #####################################################
713def generate_morph_vertex(name, vertices):
714 vertex_string = ",".join(generate_vertex(v, TRUNCATE, SCALE) for v in vertices)
715 return TEMPLATE_MORPH_VERTICES % (name, vertex_string)
716
717def generate_morph_color(name, colors):
718 color_string = ",".join(generate_color_rgb(c) for c in colors)
719 return TEMPLATE_MORPH_COLORS % (name, color_string)
720
721def extract_material_colors(materials, mtlfilename, basename):
722 """Extract diffuse colors from MTL materials
723 """
724
725 if not materials:
726 materials = { 'default': 0 }
727
728 mtl = create_materials(materials, mtlfilename, basename)
729
730 mtlColorArraySrt = []
731 for m in mtl:
732 if m in materials:
733 index = materials[m]
734 color = mtl[m].get("colorDiffuse", [1,0,0])
735 mtlColorArraySrt.append([index, color])
736
737 mtlColorArraySrt.sort()
738 mtlColorArray = [x[1] for x in mtlColorArraySrt]
739
740 return mtlColorArray
741
742def extract_face_colors(faces, material_colors):
743 """Extract colors from materials and assign them to faces
744 """
745
746 faceColors = []
747
748 for face in faces:
749 material_index = face['material']
750 faceColors.append(material_colors[material_index])
751
752 return faceColors
753
754def generate_morph_targets(morphfiles, n_vertices, infile):
755 skipOriginalMorph = False
756 norminfile = os.path.normpath(infile)
757
758 morphVertexData = []
759
760 for mfilepattern in morphfiles.split():
761
762 matches = glob.glob(mfilepattern)
763 matches.sort()
764
765 indices = range(0, len(matches), FRAMESTEP)
766 for i in indices:
767 path = matches[i]
768
769 normpath = os.path.normpath(path)
770
771 if normpath != norminfile or not skipOriginalMorph:
772
773 name = os.path.basename(normpath)
774
775 morphFaces, morphVertices, morphUvs, morphNormals, morphMaterials, morphMtllib = parse_obj(normpath)
776
777 n_morph_vertices = len(morphVertices)
778
779 if n_vertices != n_morph_vertices:
780
781 print "WARNING: skipping morph [%s] with different number of vertices [%d] than the original model [%d]" % (name, n_morph_vertices, n_vertices)
782
783 else:
784
785 if ALIGN == "center":
786 center(morphVertices)
787 elif ALIGN == "centerxz":
788 centerxz(morphVertices)
789 elif ALIGN == "bottom":
790 bottom(morphVertices)
791 elif ALIGN == "top":
792 top(morphVertices)
793
794 morphVertexData.append((get_name(name), morphVertices))
795 print "adding [%s] with %d vertices" % (name, n_morph_vertices)
796
797 morphTargets = ""
798 if len(morphVertexData):
799 morphTargets = "\n%s\n\t" % ",\n".join(generate_morph_vertex(name, vertices) for name, vertices in morphVertexData)
800
801 return morphTargets
802
803def generate_morph_colors(colorfiles, n_vertices, n_faces):
804 morphColorData = []
805 colorFaces = []
806 materialColors = []
807
808 for mfilepattern in colorfiles.split():
809
810 matches = glob.glob(mfilepattern)
811 matches.sort()
812 for path in matches:
813 normpath = os.path.normpath(path)
814 name = os.path.basename(normpath)
815
816 morphFaces, morphVertices, morphUvs, morphNormals, morphMaterials, morphMtllib = parse_obj(normpath)
817
818 n_morph_vertices = len(morphVertices)
819 n_morph_faces = len(morphFaces)
820
821 if n_vertices != n_morph_vertices:
822
823 print "WARNING: skipping morph color map [%s] with different number of vertices [%d] than the original model [%d]" % (name, n_morph_vertices, n_vertices)
824
825 elif n_faces != n_morph_faces:
826
827 print "WARNING: skipping morph color map [%s] with different number of faces [%d] than the original model [%d]" % (name, n_morph_faces, n_faces)
828
829 else:
830
831 morphMaterialColors = extract_material_colors(morphMaterials, morphMtllib, normpath)
832 morphFaceColors = extract_face_colors(morphFaces, morphMaterialColors)
833 morphColorData.append((get_name(name), morphFaceColors))
834
835 # take first color map for baking into face colors
836
837 if len(colorFaces) == 0:
838 colorFaces = morphFaces
839 materialColors = morphMaterialColors
840
841 print "adding [%s] with %d face colors" % (name, len(morphFaceColors))
842
843 morphColors = ""
844 if len(morphColorData):
845 morphColors = "\n%s\n\t" % ",\n".join(generate_morph_color(name, colors) for name, colors in morphColorData)
846
847 return morphColors, colorFaces, materialColors
848
849# #####################################################
850# Materials
851# #####################################################
852def generate_color(i):
853 """Generate hex color corresponding to integer.
854
855 Colors should have well defined ordering.
856 First N colors are hardcoded, then colors are random
857 (must seed random number generator with deterministic value
858 before getting colors).
859 """
860
861 if i < len(COLORS):
862 #return "0x%06x" % COLORS[i]
863 return COLORS[i]
864 else:
865 #return "0x%06x" % int(0xffffff * random.random())
866 return int(0xffffff * random.random())
867
868def value2string(v):
869 if type(v)==str and v[0:2] != "0x":
870 return '"%s"' % v
871 elif type(v) == bool:
872 return str(v).lower()
873 return str(v)
874
875def generate_materials(mtl, materials):
876 """Generate JS array of materials objects
877
878 JS material objects are basically prettified one-to-one
879 mappings of MTL properties in JSON format.
880 """
881
882 mtl_array = []
883 for m in mtl:
884 if m in materials:
885 index = materials[m]
886
887 # add debug information
888 # materials should be sorted according to how
889 # they appeared in OBJ file (for the first time)
890 # this index is identifier used in face definitions
891 mtl[m]['DbgName'] = m
892 mtl[m]['DbgIndex'] = index
893 mtl[m]['DbgColor'] = generate_color(index)
894
895 if BAKE_COLORS:
896 mtl[m]['vertexColors'] = "face"
897
898 mtl_raw = ",\n".join(['\t"%s" : %s' % (n, value2string(v)) for n,v in sorted(mtl[m].items())])
899 mtl_string = "\t{\n%s\n\t}" % mtl_raw
900 mtl_array.append([index, mtl_string])
901
902 return ",\n\n".join([m for i,m in sorted(mtl_array)])
903
904def generate_mtl(materials):
905 """Generate dummy materials (if there is no MTL file).
906 """
907
908 mtl = {}
909 for m in materials:
910 index = materials[m]
911 mtl[m] = {
912 'DbgName': m,
913 'DbgIndex': index,
914 'DbgColor': generate_color(index)
915 }
916 return mtl
917
918def generate_materials_string(materials, mtlfilename, basename):
919 """Generate final materials string.
920 """
921
922 if not materials:
923 materials = { 'default': 0 }
924
925 mtl = create_materials(materials, mtlfilename, basename)
926 return generate_materials(mtl, materials)
927
928def create_materials(materials, mtlfilename, basename):
929 """Parse MTL file and create mapping between its materials and OBJ materials.
930 Eventual edge cases are handled here (missing materials, missing MTL file).
931 """
932
933 random.seed(42) # to get well defined color order for debug colors
934
935 # default materials with debug colors for when
936 # there is no specified MTL / MTL loading failed,
937 # or if there were no materials / null materials
938
939 mtl = generate_mtl(materials)
940
941 if mtlfilename:
942
943 # create full pathname for MTL (included from OBJ)
944
945 path = os.path.dirname(basename)
946 fname = os.path.join(path, mtlfilename)
947
948 if file_exists(fname):
949
950 # override default materials with real ones from MTL
951 # (where they exist, otherwise keep defaults)
952
953 mtl.update(parse_mtl(fname))
954
955 else:
956
957 print "Couldn't find [%s]" % fname
958
959 return mtl
960
961# #####################################################
962# Faces
963# #####################################################
964def is_triangle_flat(f):
965 return len(f['vertex'])==3 and not (f["normal"] and SHADING == "smooth") and not f['uv']
966
967def is_triangle_flat_uv(f):
968 return len(f['vertex'])==3 and not (f["normal"] and SHADING == "smooth") and len(f['uv'])==3
969
970def is_triangle_smooth(f):
971 return len(f['vertex'])==3 and f["normal"] and SHADING == "smooth" and not f['uv']
972
973def is_triangle_smooth_uv(f):
974 return len(f['vertex'])==3 and f["normal"] and SHADING == "smooth" and len(f['uv'])==3
975
976def is_quad_flat(f):
977 return len(f['vertex'])==4 and not (f["normal"] and SHADING == "smooth") and not f['uv']
978
979def is_quad_flat_uv(f):
980 return len(f['vertex'])==4 and not (f["normal"] and SHADING == "smooth") and len(f['uv'])==4
981
982def is_quad_smooth(f):
983 return len(f['vertex'])==4 and f["normal"] and SHADING == "smooth" and not f['uv']
984
985def is_quad_smooth_uv(f):
986 return len(f['vertex'])==4 and f["normal"] and SHADING == "smooth" and len(f['uv'])==4
987
988def sort_faces(faces):
989 data = {
990 'triangles_flat': [],
991 'triangles_flat_uv': [],
992 'triangles_smooth': [],
993 'triangles_smooth_uv': [],
994
995 'quads_flat': [],
996 'quads_flat_uv': [],
997 'quads_smooth': [],
998 'quads_smooth_uv': []
999 }
1000
1001 for f in faces:
1002 if is_triangle_flat(f):
1003 data['triangles_flat'].append(f)
1004 elif is_triangle_flat_uv(f):
1005 data['triangles_flat_uv'].append(f)
1006 elif is_triangle_smooth(f):
1007 data['triangles_smooth'].append(f)
1008 elif is_triangle_smooth_uv(f):
1009 data['triangles_smooth_uv'].append(f)
1010
1011 elif is_quad_flat(f):
1012 data['quads_flat'].append(f)
1013 elif is_quad_flat_uv(f):
1014 data['quads_flat_uv'].append(f)
1015 elif is_quad_smooth(f):
1016 data['quads_smooth'].append(f)
1017 elif is_quad_smooth_uv(f):
1018 data['quads_smooth_uv'].append(f)
1019
1020 return data
1021
1022# #####################################################
1023# API - ASCII converter
1024# #####################################################
1025def convert_ascii(infile, morphfiles, colorfiles, outfile):
1026 """Convert infile.obj to outfile.js
1027
1028 Here is where everything happens. If you need to automate conversions,
1029 just import this file as Python module and call this method.
1030 """
1031
1032 if not file_exists(infile):
1033 print "Couldn't find [%s]" % infile
1034 return
1035
1036 # parse OBJ / MTL files
1037
1038 faces, vertices, uvs, normals, materials, mtllib = parse_obj(infile)
1039
1040 n_vertices = len(vertices)
1041 n_faces = len(faces)
1042
1043 # align model
1044
1045 if ALIGN == "center":
1046 center(vertices)
1047 elif ALIGN == "centerxz":
1048 centerxz(vertices)
1049 elif ALIGN == "bottom":
1050 bottom(vertices)
1051 elif ALIGN == "top":
1052 top(vertices)
1053
1054 # generate normals string
1055
1056 nnormal = 0
1057 normals_string = ""
1058 if SHADING == "smooth":
1059 normals_string = ",".join(generate_normal(n) for n in normals)
1060 nnormal = len(normals)
1061
1062 # extract morph vertices
1063
1064 morphTargets = generate_morph_targets(morphfiles, n_vertices, infile)
1065
1066 # extract morph colors
1067
1068 morphColors, colorFaces, materialColors = generate_morph_colors(colorfiles, n_vertices, n_faces)
1069
1070 # generate colors string
1071
1072 ncolor = 0
1073 colors_string = ""
1074
1075 if len(colorFaces) < len(faces):
1076 colorFaces = faces
1077 materialColors = extract_material_colors(materials, mtllib, infile)
1078
1079 if BAKE_COLORS:
1080 colors_string = ",".join(generate_color_decimal(c) for c in materialColors)
1081 ncolor = len(materialColors)
1082
1083 # generate ascii model string
1084
1085 text = TEMPLATE_FILE_ASCII % {
1086 "name" : get_name(outfile),
1087 "fname" : os.path.basename(infile),
1088 "nvertex" : len(vertices),
1089 "nface" : len(faces),
1090 "nuv" : len(uvs),
1091 "nnormal" : nnormal,
1092 "ncolor" : ncolor,
1093 "nmaterial" : len(materials),
1094
1095 "materials" : generate_materials_string(materials, mtllib, infile),
1096
1097 "normals" : normals_string,
1098 "colors" : colors_string,
1099 "uvs" : ",".join(generate_uv(uv) for uv in uvs),
1100 "vertices" : ",".join(generate_vertex(v, TRUNCATE, SCALE) for v in vertices),
1101
1102 "morphTargets" : morphTargets,
1103 "morphColors" : morphColors,
1104
1105 "faces" : ",".join(generate_face(f, fc) for f, fc in zip(faces, colorFaces)),
1106
1107 "scale" : SCALE
1108 }
1109
1110 out = open(outfile, "w")
1111 out.write(text)
1112 out.close()
1113
1114 print "%d vertices, %d faces, %d materials" % (len(vertices), len(faces), len(materials))
1115
1116
1117# #############################################################################
1118# API - Binary converter
1119# #############################################################################
1120def dump_materials_to_buffer(faces, buffer):
1121 for f in faces:
1122 data = struct.pack('<H',
1123 f['material'])
1124 buffer.append(data)
1125
1126def dump_vertices3_to_buffer(faces, buffer):
1127 for f in faces:
1128 vi = f['vertex']
1129 data = struct.pack('<III',
1130 vi[0]-1, vi[1]-1, vi[2]-1)
1131 buffer.append(data)
1132
1133def dump_vertices4_to_buffer(faces, buffer):
1134 for f in faces:
1135 vi = f['vertex']
1136 data = struct.pack('<IIII',
1137 vi[0]-1, vi[1]-1, vi[2]-1, vi[3]-1)
1138 buffer.append(data)
1139
1140def dump_normals3_to_buffer(faces, buffer):
1141 for f in faces:
1142 ni = f['normal']
1143 data = struct.pack('<III',
1144 ni[0]-1, ni[1]-1, ni[2]-1)
1145 buffer.append(data)
1146
1147def dump_normals4_to_buffer(faces, buffer):
1148 for f in faces:
1149 ni = f['normal']
1150 data = struct.pack('<IIII',
1151 ni[0]-1, ni[1]-1, ni[2]-1, ni[3]-1)
1152 buffer.append(data)
1153
1154def dump_uvs3_to_buffer(faces, buffer):
1155 for f in faces:
1156 ui = f['uv']
1157 data = struct.pack('<III',
1158 ui[0]-1, ui[1]-1, ui[2]-1)
1159 buffer.append(data)
1160
1161def dump_uvs4_to_buffer(faces, buffer):
1162 for f in faces:
1163 ui = f['uv']
1164 data = struct.pack('<IIII',
1165 ui[0]-1, ui[1]-1, ui[2]-1, ui[3]-1)
1166 buffer.append(data)
1167
1168def add_padding(buffer, n):
1169 if n % 4:
1170 for i in range(4 - n % 4):
1171 data = struct.pack('<B', 0)
1172 buffer.append(data)
1173
1174def convert_binary(infile, outfile):
1175 """Convert infile.obj to outfile.js + outfile.bin
1176 """
1177
1178 if not file_exists(infile):
1179 print "Couldn't find [%s]" % infile
1180 return
1181
1182 binfile = get_name(outfile) + ".bin"
1183
1184 faces, vertices, uvs, normals, materials, mtllib = parse_obj(infile)
1185
1186 if ALIGN == "center":
1187 center(vertices)
1188 elif ALIGN == "centerxz":
1189 centerxz(vertices)
1190 elif ALIGN == "bottom":
1191 bottom(vertices)
1192 elif ALIGN == "top":
1193 top(vertices)
1194
1195 sfaces = sort_faces(faces)
1196
1197 if SHADING == "smooth":
1198 nnormals = len(normals)
1199 else:
1200 nnormals = 0
1201
1202 # ###################
1203 # generate JS file
1204 # ###################
1205
1206 text = TEMPLATE_FILE_BIN % {
1207 "name" : get_name(outfile),
1208
1209 "materials" : generate_materials_string(materials, mtllib, infile),
1210 "buffers" : binfile,
1211
1212 "fname" : os.path.basename(infile),
1213 "nvertex" : len(vertices),
1214 "nface" : len(faces),
1215 "nmaterial" : len(materials),
1216 "nnormal" : nnormals,
1217 "nuv" : len(uvs)
1218 }
1219
1220 out = open(outfile, "w")
1221 out.write(text)
1222 out.close()
1223
1224 # ###################
1225 # generate BIN file
1226 # ###################
1227
1228 buffer = []
1229
1230 # header
1231 # ------
1232 header_bytes = struct.calcsize('<12s')
1233 header_bytes += struct.calcsize('<BBBBBBBB')
1234 header_bytes += struct.calcsize('<IIIIIIIIIII')
1235
1236 # signature
1237 signature = struct.pack('<12s', 'Three.js 003')
1238
1239 # metadata (all data is little-endian)
1240 vertex_coordinate_bytes = 4
1241 normal_coordinate_bytes = 1
1242 uv_coordinate_bytes = 4
1243
1244 vertex_index_bytes = 4
1245 normal_index_bytes = 4
1246 uv_index_bytes = 4
1247 material_index_bytes = 2
1248
1249 # header_bytes unsigned char 1
1250
1251 # vertex_coordinate_bytes unsigned char 1
1252 # normal_coordinate_bytes unsigned char 1
1253 # uv_coordinate_bytes unsigned char 1
1254
1255 # vertex_index_bytes unsigned char 1
1256 # normal_index_bytes unsigned char 1
1257 # uv_index_bytes unsigned char 1
1258 # material_index_bytes unsigned char 1
1259 bdata = struct.pack('<BBBBBBBB', header_bytes,
1260 vertex_coordinate_bytes,
1261 normal_coordinate_bytes,
1262 uv_coordinate_bytes,
1263 vertex_index_bytes,
1264 normal_index_bytes,
1265 uv_index_bytes,
1266 material_index_bytes)
1267
1268 ntri_flat = len(sfaces['triangles_flat'])
1269 ntri_smooth = len(sfaces['triangles_smooth'])
1270 ntri_flat_uv = len(sfaces['triangles_flat_uv'])
1271 ntri_smooth_uv = len(sfaces['triangles_smooth_uv'])
1272
1273 nquad_flat = len(sfaces['quads_flat'])
1274 nquad_smooth = len(sfaces['quads_smooth'])
1275 nquad_flat_uv = len(sfaces['quads_flat_uv'])
1276 nquad_smooth_uv = len(sfaces['quads_smooth_uv'])
1277
1278 # nvertices unsigned int 4
1279 # nnormals unsigned int 4
1280 # nuvs unsigned int 4
1281
1282 # ntri_flat unsigned int 4
1283 # ntri_smooth unsigned int 4
1284 # ntri_flat_uv unsigned int 4
1285 # ntri_smooth_uv unsigned int 4
1286
1287 # nquad_flat unsigned int 4
1288 # nquad_smooth unsigned int 4
1289 # nquad_flat_uv unsigned int 4
1290 # nquad_smooth_uv unsigned int 4
1291 ndata = struct.pack('<IIIIIIIIIII', len(vertices),
1292 nnormals,
1293 len(uvs),
1294 ntri_flat,
1295 ntri_smooth,
1296 ntri_flat_uv,
1297 ntri_smooth_uv,
1298 nquad_flat,
1299 nquad_smooth,
1300 nquad_flat_uv,
1301 nquad_smooth_uv)
1302 buffer.append(signature)
1303 buffer.append(bdata)
1304 buffer.append(ndata)
1305
1306 # 1. vertices
1307 # ------------
1308 # x float 4
1309 # y float 4
1310 # z float 4
1311 for v in vertices:
1312 data = struct.pack('<fff', v[0], v[1], v[2])
1313 buffer.append(data)
1314
1315 # 2. normals
1316 # ---------------
1317 # x signed char 1
1318 # y signed char 1
1319 # z signed char 1
1320 if SHADING == "smooth":
1321 for n in normals:
1322 normalize(n)
1323 data = struct.pack('<bbb', math.floor(n[0]*127+0.5),
1324 math.floor(n[1]*127+0.5),
1325 math.floor(n[2]*127+0.5))
1326 buffer.append(data)
1327
1328 add_padding(buffer, nnormals * 3)
1329
1330 # 3. uvs
1331 # -----------
1332 # u float 4
1333 # v float 4
1334 for uv in uvs:
1335 data = struct.pack('<ff', uv[0], uv[1])
1336 buffer.append(data)
1337
1338 # padding
1339 #data = struct.pack('<BB', 0, 0)
1340 #buffer.append(data)
1341
1342 # 4. flat triangles (vertices + materials)
1343 # ------------------
1344 # a unsigned int 4
1345 # b unsigned int 4
1346 # c unsigned int 4
1347 # ------------------
1348 # m unsigned short 2
1349
1350 dump_vertices3_to_buffer(sfaces['triangles_flat'], buffer)
1351
1352 dump_materials_to_buffer(sfaces['triangles_flat'], buffer)
1353 add_padding(buffer, ntri_flat * 2)
1354
1355 # 5. smooth triangles (vertices + materials + normals)
1356 # -------------------
1357 # a unsigned int 4
1358 # b unsigned int 4
1359 # c unsigned int 4
1360 # -------------------
1361 # na unsigned int 4
1362 # nb unsigned int 4
1363 # nc unsigned int 4
1364 # -------------------
1365 # m unsigned short 2
1366
1367 dump_vertices3_to_buffer(sfaces['triangles_smooth'], buffer)
1368 dump_normals3_to_buffer(sfaces['triangles_smooth'], buffer)
1369
1370 dump_materials_to_buffer(sfaces['triangles_smooth'], buffer)
1371 add_padding(buffer, ntri_smooth * 2)
1372
1373 # 6. flat triangles uv (vertices + materials + uvs)
1374 # --------------------
1375 # a unsigned int 4
1376 # b unsigned int 4
1377 # c unsigned int 4
1378 # --------------------
1379 # ua unsigned int 4
1380 # ub unsigned int 4
1381 # uc unsigned int 4
1382 # --------------------
1383 # m unsigned short 2
1384
1385 dump_vertices3_to_buffer(sfaces['triangles_flat_uv'], buffer)
1386 dump_uvs3_to_buffer(sfaces['triangles_flat_uv'], buffer)
1387
1388 dump_materials_to_buffer(sfaces['triangles_flat_uv'], buffer)
1389 add_padding(buffer, ntri_flat_uv * 2)
1390
1391 # 7. smooth triangles uv (vertices + materials + normals + uvs)
1392 # ----------------------
1393 # a unsigned int 4
1394 # b unsigned int 4
1395 # c unsigned int 4
1396 # --------------------
1397 # na unsigned int 4
1398 # nb unsigned int 4
1399 # nc unsigned int 4
1400 # --------------------
1401 # ua unsigned int 4
1402 # ub unsigned int 4
1403 # uc unsigned int 4
1404 # --------------------
1405 # m unsigned short 2
1406
1407 dump_vertices3_to_buffer(sfaces['triangles_smooth_uv'], buffer)
1408 dump_normals3_to_buffer(sfaces['triangles_smooth_uv'], buffer)
1409 dump_uvs3_to_buffer(sfaces['triangles_smooth_uv'], buffer)
1410
1411 dump_materials_to_buffer(sfaces['triangles_smooth_uv'], buffer)
1412 add_padding(buffer, ntri_smooth_uv * 2)
1413
1414 # 8. flat quads (vertices + materials)
1415 # ------------------
1416 # a unsigned int 4
1417 # b unsigned int 4
1418 # c unsigned int 4
1419 # d unsigned int 4
1420 # --------------------
1421 # m unsigned short 2
1422
1423 dump_vertices4_to_buffer(sfaces['quads_flat'], buffer)
1424
1425 dump_materials_to_buffer(sfaces['quads_flat'], buffer)
1426 add_padding(buffer, nquad_flat * 2)
1427
1428 # 9. smooth quads (vertices + materials + normals)
1429 # -------------------
1430 # a unsigned int 4
1431 # b unsigned int 4
1432 # c unsigned int 4
1433 # d unsigned int 4
1434 # --------------------
1435 # na unsigned int 4
1436 # nb unsigned int 4
1437 # nc unsigned int 4
1438 # nd unsigned int 4
1439 # --------------------
1440 # m unsigned short 2
1441
1442 dump_vertices4_to_buffer(sfaces['quads_smooth'], buffer)
1443 dump_normals4_to_buffer(sfaces['quads_smooth'], buffer)
1444
1445 dump_materials_to_buffer(sfaces['quads_smooth'], buffer)
1446 add_padding(buffer, nquad_smooth * 2)
1447
1448 # 10. flat quads uv (vertices + materials + uvs)
1449 # ------------------
1450 # a unsigned int 4
1451 # b unsigned int 4
1452 # c unsigned int 4
1453 # d unsigned int 4
1454 # --------------------
1455 # ua unsigned int 4
1456 # ub unsigned int 4
1457 # uc unsigned int 4
1458 # ud unsigned int 4
1459 # --------------------
1460 # m unsigned short 2
1461
1462 dump_vertices4_to_buffer(sfaces['quads_flat_uv'], buffer)
1463 dump_uvs4_to_buffer(sfaces['quads_flat_uv'], buffer)
1464
1465 dump_materials_to_buffer(sfaces['quads_flat_uv'], buffer)
1466 add_padding(buffer, nquad_flat_uv * 2)
1467
1468 # 11. smooth quads uv
1469 # -------------------
1470 # a unsigned int 4
1471 # b unsigned int 4
1472 # c unsigned int 4
1473 # d unsigned int 4
1474 # --------------------
1475 # na unsigned int 4
1476 # nb unsigned int 4
1477 # nc unsigned int 4
1478 # nd unsigned int 4
1479 # --------------------
1480 # ua unsigned int 4
1481 # ub unsigned int 4
1482 # uc unsigned int 4
1483 # ud unsigned int 4
1484 # --------------------
1485 # m unsigned short 2
1486
1487 dump_vertices4_to_buffer(sfaces['quads_smooth_uv'], buffer)
1488 dump_normals4_to_buffer(sfaces['quads_smooth_uv'], buffer)
1489 dump_uvs4_to_buffer(sfaces['quads_smooth_uv'], buffer)
1490
1491 dump_materials_to_buffer(sfaces['quads_smooth_uv'], buffer)
1492 add_padding(buffer, nquad_smooth_uv * 2)
1493
1494 path = os.path.dirname(outfile)
1495 fname = os.path.join(path, binfile)
1496
1497 out = open(fname, "wb")
1498 out.write("".join(buffer))
1499 out.close()
1500
1501# #############################################################################
1502# Helpers
1503# #############################################################################
1504def usage():
1505 print "Usage: %s -i filename.obj -o filename.js [-m morphfiles*.obj] [-c morphcolors*.obj] [-a center|top|bottom] [-s flat|smooth] [-t binary|ascii] [-d invert|normal]" % os.path.basename(sys.argv[0])
1506
1507# #####################################################
1508# Main
1509# #####################################################
1510if __name__ == "__main__":
1511
1512 # get parameters from the command line
1513 try:
1514 opts, args = getopt.getopt(sys.argv[1:], "hbi:m:c:b:o:a:s:t:d:x:f:", ["help", "bakecolors", "input=", "morphs=", "colors=", "output=", "align=", "shading=", "type=", "dissolve=", "truncatescale=", "framestep="])
1515
1516 except getopt.GetoptError:
1517 usage()
1518 sys.exit(2)
1519
1520 infile = outfile = ""
1521 morphfiles = ""
1522 colorfiles = ""
1523
1524 for o, a in opts:
1525 if o in ("-h", "--help"):
1526 usage()
1527 sys.exit()
1528
1529 elif o in ("-i", "--input"):
1530 infile = a
1531
1532 elif o in ("-m", "--morphs"):
1533 morphfiles = a
1534
1535 elif o in ("-c", "--colors"):
1536 colorfiles = a
1537
1538 elif o in ("-o", "--output"):
1539 outfile = a
1540
1541 elif o in ("-a", "--align"):
1542 if a in ("top", "bottom", "center", "centerxz", "none"):
1543 ALIGN = a
1544
1545 elif o in ("-s", "--shading"):
1546 if a in ("flat", "smooth"):
1547 SHADING = a
1548
1549 elif o in ("-t", "--type"):
1550 if a in ("binary", "ascii"):
1551 TYPE = a
1552
1553 elif o in ("-d", "--dissolve"):
1554 if a in ("normal", "invert"):
1555 TRANSPARENCY = a
1556
1557 elif o in ("-b", "--bakecolors"):
1558 BAKE_COLORS = True
1559
1560 elif o in ("-x", "--truncatescale"):
1561 TRUNCATE = True
1562 SCALE = float(a)
1563
1564 elif o in ("-f", "--framestep"):
1565 FRAMESTEP = int(a)
1566
1567 if infile == "" or outfile == "":
1568 usage()
1569 sys.exit(2)
1570
1571 print "Converting [%s] into [%s] ..." % (infile, outfile)
1572
1573 if morphfiles:
1574 print "Morphs [%s]" % morphfiles
1575
1576 if colorfiles:
1577 print "Colors [%s]" % colorfiles
1578
1579 if TYPE == "ascii":
1580 convert_ascii(infile, morphfiles, colorfiles, outfile)
1581 elif TYPE == "binary":
1582 convert_binary(infile, outfile)
1583
Note: See TracBrowser for help on using the repository browser.