123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623 |
- """
- ------
- Original Author - Formerly Python 2 version / file: convert_obj_three.py
- ------
- AlteredQualia http://alteredqualia.com
- ------
- Upgraded - Python 3 version / file: convert_obj_three_for_python3.py
- ------
- Leroy T
- Convert Wavefront OBJ / MTL files into Three.js (JSON model version, to be used with ascii / binary loader)
- -------------------------
- How to use this converter
- -------------------------
- python 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]
- Notes:
- - flags
- -i infile.obj input OBJ file
- -o outfile.js output JS file
- -m "morphfiles*.obj" morph OBJ files (can use wildcards, enclosed in quotes multiple patterns separate by space)
- -c "morphcolors*.obj" morph colors OBJ files (can use wildcards, enclosed in quotes multiple patterns separate by space)
- -a center|centerxz|top|bottom|none model alignment
- -s smooth|flat smooth = export vertex normals, flat = no normals (face normals computed in loader)
- -t ascii|binary export ascii or binary format (ascii has more features, binary just supports vertices, faces, normals, uvs and materials)
- -d invert|normal invert transparency
- -b bake material colors into face colors
- -x 10.0 scale and truncate
- -f 2 morph frame sampling step
- - by default:
- use smooth shading (if there were vertex normals in the original model)
- will be in ASCII format
- original model is assumed to use non-inverted transparency / dissolve (0.0 fully transparent, 1.0 fully opaque)
- no face colors baking
- no scale and truncate
- morph frame step = 1 (all files will be processed)
- - binary conversion will create two files:
- outfile.js (materials)
- outfile.bin (binary buffers)
- --------------------------------------------------
- How to use generated JS file in your HTML document
- --------------------------------------------------
- <script type="text/javascript" src="Three.js"></script>
- ...
- <script type="text/javascript">
- ...
- // load ascii model
- var jsonLoader = new THREE.JSONLoader();
- jsonLoader.load( "Model_ascii.js", createScene );
- // load binary model
- var binLoader = new THREE.BinaryLoader();
- binLoader.load( "Model_bin.js", createScene );
- function createScene( geometry, materials ) {
- var mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial( materials ) );
- }
- ...
- </script>
- -------------------------------------
- Parsers based on formats descriptions
- -------------------------------------
- http://en.wikipedia.org/wiki/Obj
- http://en.wikipedia.org/wiki/Material_Template_Library
- -------------------
- Current limitations
- -------------------
- - for the moment, only diffuse color and texture are used
- (will need to extend shaders / renderers / materials in Three)
- - texture coordinates can be wrong in canvas renderer
- (there is crude normalization, but it doesn't
- work for all cases)
- - smoothing can be turned on/off only for the whole mesh
- ----------------------------------------------
- How to get proper OBJ + MTL files with Blender
- ----------------------------------------------
- 0. Remove default cube (press DEL and ENTER)
- 1. Import / create model
- 2. Select all meshes (Select -> Select All by Type -> Mesh)
- 3. Export to OBJ (File -> Export -> Wavefront .obj)
- - enable following options in exporter
- Material Groups
- Rotate X90
- Apply Modifiers
- High Quality Normals
- Copy Images
- Selection Only
- Objects as OBJ Objects
- UVs
- Normals
- Materials
- - select empty folder
- - give your exported file name with "obj" extension
- - click on "Export OBJ" button
- 4. Your model is now all files in this folder (OBJ, MTL, number of images)
- - this converter assumes all files staying in the same folder,
- (OBJ / MTL files use relative paths)
- - for WebGL, textures must be power of 2 sized
- """
- import fileinput
- import operator
- import random
- import os.path
- import getopt
- import sys
- import struct
- import math
- import glob
- # #####################################################
- # Configuration
- # #####################################################
- ALIGN = "none" # center centerxz bottom top none
- SHADING = "smooth" # smooth flat
- TYPE = "ascii" # ascii binary
- TRANSPARENCY = "normal" # normal invert
- TRUNCATE = False
- SCALE = 1.0
- FRAMESTEP = 1
- BAKE_COLORS = False
- # default colors for debugging (each material gets one distinct color):
- # white, red, green, blue, yellow, cyan, magenta
- COLORS = [0xeeeeee, 0xee0000, 0x00ee00, 0x0000ee, 0xeeee00, 0x00eeee, 0xee00ee]
- # #####################################################
- # Templates
- # #####################################################
- TEMPLATE_FILE_ASCII = u"""\
- {
- "metadata" :
- {
- "formatVersion" : 3.1,
- "sourceFile" : "%(fname)s",
- "generatedBy" : "OBJConverter",
- "vertices" : %(nvertex)d,
- "faces" : %(nface)d,
- "normals" : %(nnormal)d,
- "colors" : %(ncolor)d,
- "uvs" : %(nuv)d,
- "materials" : %(nmaterial)d
- },
- "scale" : %(scale)f,
- "materials": [%(materials)s],
- "vertices": [%(vertices)s],
- "morphTargets": [%(morphTargets)s],
- "morphColors": [%(morphColors)s],
- "normals": [%(normals)s],
- "colors": [%(colors)s],
- "uvs": [[%(uvs)s]],
- "faces": [%(faces)s]
- }
- """
- TEMPLATE_FILE_BIN = u"""\
- {
- "metadata" :
- {
- "formatVersion" : 3.1,
- "sourceFile" : "%(fname)s",
- "generatedBy" : "OBJConverter",
- "vertices" : %(nvertex)d,
- "faces" : %(nface)d,
- "normals" : %(nnormal)d,
- "uvs" : %(nuv)d,
- "materials" : %(nmaterial)d
- },
- "materials": [%(materials)s],
- "buffers": "%(buffers)s"
- }
- """
- TEMPLATE_VERTEX = "%f,%f,%f"
- TEMPLATE_VERTEX_TRUNCATE = "%d,%d,%d"
- TEMPLATE_N = "%.5g,%.5g,%.5g"
- TEMPLATE_UV = "%.5g,%.5g"
- TEMPLATE_COLOR = "%.3g,%.3g,%.3g"
- TEMPLATE_COLOR_DEC = "%d"
- TEMPLATE_MORPH_VERTICES = '\t{ "name": "%s", "vertices": [%s] }'
- TEMPLATE_MORPH_COLORS = '\t{ "name": "%s", "colors": [%s] }'
- # #####################################################
- # Utils
- # #####################################################
- def file_exists(filename):
- """Return true if file exists and is accessible for reading.
- Should be safer than just testing for existence due to links and
- permissions magic on Unix filesystems.
- @rtype: boolean
- """
- try:
- f = open(filename, 'r')
- f.close()
- return True
- except IOError:
- return False
- def get_name(fname):
- """Create model name based of filename ("path/fname.js" -> "fname").
- """
- return os.path.splitext(os.path.basename(fname))[0]
- def bbox(vertices):
- """Compute bounding box of vertex array.
- """
- if len(vertices)>0:
- minx = maxx = vertices[0][0]
- miny = maxy = vertices[0][1]
- minz = maxz = vertices[0][2]
- for v in vertices[1:]:
- if v[0]<minx:
- minx = v[0]
- elif v[0]>maxx:
- maxx = v[0]
- if v[1]<miny:
- miny = v[1]
- elif v[1]>maxy:
- maxy = v[1]
- if v[2]<minz:
- minz = v[2]
- elif v[2]>maxz:
- maxz = v[2]
- return { 'x':[minx,maxx], 'y':[miny,maxy], 'z':[minz,maxz] }
- else:
- return { 'x':[0,0], 'y':[0,0], 'z':[0,0] }
- def translate(vertices, t):
- """Translate array of vertices by vector t.
- """
- for i in xrange(len(vertices)):
- vertices[i][0] += t[0]
- vertices[i][1] += t[1]
- vertices[i][2] += t[2]
- def center(vertices):
- """Center model (middle of bounding box).
- """
- bb = bbox(vertices)
- cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
- cy = bb['y'][0] + (bb['y'][1] - bb['y'][0])/2.0
- cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
- translate(vertices, [-cx,-cy,-cz])
- def top(vertices):
- """Align top of the model with the floor (Y-axis) and center it around X and Z.
- """
- bb = bbox(vertices)
- cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
- cy = bb['y'][1]
- cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
- translate(vertices, [-cx,-cy,-cz])
- def bottom(vertices):
- """Align bottom of the model with the floor (Y-axis) and center it around X and Z.
- """
- bb = bbox(vertices)
- cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
- cy = bb['y'][0]
- cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
- translate(vertices, [-cx,-cy,-cz])
- def centerxz(vertices):
- """Center model around X and Z.
- """
- bb = bbox(vertices)
- cx = bb['x'][0] + (bb['x'][1] - bb['x'][0])/2.0
- cy = 0
- cz = bb['z'][0] + (bb['z'][1] - bb['z'][0])/2.0
- translate(vertices, [-cx,-cy,-cz])
- def normalize(v):
- """Normalize 3d vector"""
- l = math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
- if l:
- v[0] /= l
- v[1] /= l
- v[2] /= l
- def veckey3(v):
- return round(v[0], 6), round(v[1], 6), round(v[2], 6)
- # #####################################################
- # MTL parser
- # #####################################################
- def texture_relative_path(fullpath):
- texture_file = os.path.basename(fullpath.replace("\\", "/"))
- return texture_file
- def parse_mtl(fname):
- """Parse MTL file.
- """
- materials = {}
- previous_line = ""
- for line in fileinput.input(fname):
- line = previous_line + line
- if line[-2:-1] == '\\':
- previous_line = line[:-2]
- continue
- previous_line = ""
- # Only split once initially for single-parameter tags that might have additional spaces in
- # their values (i.e. "newmtl Material with spaces").
- chunks = line.split(None, 1)
- if len(chunks) > 0:
- if len(chunks) > 1:
- chunks[1] = chunks[1].strip()
- # Material start
- # newmtl identifier
- if chunks[0] == "newmtl":
- if len(chunks) > 1:
- identifier = chunks[1]
- else:
- identifier = ""
- if not identifier in materials:
- materials[identifier] = {}
- # Diffuse texture
- # map_Kd texture_diffuse.jpg
- if chunks[0] == "map_Kd" and len(chunks) == 2:
- materials[identifier]["mapDiffuse"] = texture_relative_path(chunks[1])
- # Ambient texture
- # map_Ka texture_ambient.jpg
- if chunks[0] == "map_Ka" and len(chunks) == 2:
- materials[identifier]["mapAmbient"] = texture_relative_path(chunks[1])
- # Specular texture
- # map_Ks texture_specular.jpg
- if chunks[0] == "map_Ks" and len(chunks) == 2:
- materials[identifier]["mapSpecular"] = texture_relative_path(chunks[1])
- # Alpha texture
- # map_d texture_alpha.png
- if chunks[0] == "map_d" and len(chunks) == 2:
- materials[identifier]["transparent"] = True
- materials[identifier]["mapAlpha"] = texture_relative_path(chunks[1])
- # Bump texture
- # map_bump texture_bump.jpg or bump texture_bump.jpg
- if (chunks[0] == "map_bump" or chunks[0] == "bump") and len(chunks) == 2:
- materials[identifier]["mapBump"] = texture_relative_path(chunks[1])
- # Split the remaining parameters.
- if len(chunks) > 1:
- chunks = [chunks[0]] + chunks[1].split()
- # Diffuse color
- # Kd 1.000 1.000 1.000
- if chunks[0] == "Kd" and len(chunks) == 4:
- materials[identifier]["colorDiffuse"] = [float(chunks[1]), float(chunks[2]), float(chunks[3])]
- # Ambient color
- # Ka 1.000 1.000 1.000
- if chunks[0] == "Ka" and len(chunks) == 4:
- materials[identifier]["colorAmbient"] = [float(chunks[1]), float(chunks[2]), float(chunks[3])]
- # Specular color
- # Ks 1.000 1.000 1.000
- if chunks[0] == "Ks" and len(chunks) == 4:
- materials[identifier]["colorSpecular"] = [float(chunks[1]), float(chunks[2]), float(chunks[3])]
- # Specular coefficient
- # Ns 154.000
- if chunks[0] == "Ns" and len(chunks) == 2:
- materials[identifier]["specularCoef"] = float(chunks[1])
- # Transparency
- # Tr 0.9 or d 0.9
- if (chunks[0] == "Tr" or chunks[0] == "d") and len(chunks) == 2:
- materials[identifier]["transparent"] = True
- if TRANSPARENCY == "invert":
- materials[identifier]["opacity"] = float(chunks[1])
- else:
- materials[identifier]["opacity"] = 1.0 - float(chunks[1])
- # Optical density
- # Ni 1.0
- if chunks[0] == "Ni" and len(chunks) == 2:
- materials[identifier]["opticalDensity"] = float(chunks[1])
- # Illumination
- # illum 2
- #
- # 0. Color on and Ambient off
- # 1. Color on and Ambient on
- # 2. Highlight on
- # 3. Reflection on and Ray trace on
- # 4. Transparency: Glass on, Reflection: Ray trace on
- # 5. Reflection: Fresnel on and Ray trace on
- # 6. Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
- # 7. Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
- # 8. Reflection on and Ray trace off
- # 9. Transparency: Glass on, Reflection: Ray trace off
- # 10. Casts shadows onto invisible surfaces
- if chunks[0] == "illum" and len(chunks) == 2:
- materials[identifier]["illumination"] = int(chunks[1])
- return materials
- # #####################################################
- # OBJ parser
- # #####################################################
- def parse_vertex(text):
- """Parse text chunk specifying single vertex.
- Possible formats:
- vertex index
- vertex index / texture index
- vertex index / texture index / normal index
- vertex index / / normal index
- """
- v = 0
- t = 0
- n = 0
- chunks = text.split("/")
- v = int(chunks[0])
- if len(chunks) > 1:
- if chunks[1]:
- t = int(chunks[1])
- if len(chunks) > 2:
- if chunks[2]:
- n = int(chunks[2])
- return { 'v':v, 't':t, 'n':n }
- def parse_obj(fname):
- """Parse OBJ file.
- """
- vertices = []
- normals = []
- uvs = []
- faces = []
- materials = {}
- material = ""
- mcounter = 0
- mcurrent = 0
- mtllib = ""
- # current face state
- group = 0
- object = 0
- smooth = 0
- previous_line = ""
- for line in fileinput.input(fname):
- line = previous_line + line
- if line[-2:-1] == '\\':
- previous_line = line[:-2]
- continue
- previous_line = ""
- # Only split once initially for single-parameter tags that might have additional spaces in
- # their values (i.e. "usemtl Material with spaces").
- chunks = line.split(None, 1)
- if len(chunks) > 0:
- if len(chunks) > 1:
- chunks[1] = chunks[1].strip()
- # Group
- if chunks[0] == "g" and len(chunks) == 2:
- group = chunks[1]
- # Object
- if chunks[0] == "o" and len(chunks) == 2:
- object = chunks[1]
- # Materials definition
- if chunks[0] == "mtllib" and len(chunks) == 2:
- mtllib = chunks[1]
- # Material
- if chunks[0] == "usemtl":
- if len(chunks) > 1:
- material = chunks[1]
- else:
- material = ""
- if not material in materials:
- mcurrent = mcounter
- materials[material] = mcounter
- mcounter += 1
- else:
- mcurrent = materials[material]
- # Split the remaining parameters.
- if len(chunks) > 1:
- chunks = [chunks[0]] + chunks[1].split()
- # Vertices as (x,y,z) coordinates
- # v 0.123 0.234 0.345
- if chunks[0] == "v" and len(chunks) == 4:
- x = float(chunks[1])
- y = float(chunks[2])
- z = float(chunks[3])
- vertices.append([x,y,z])
- # Normals in (x,y,z) form; normals might not be unit
- # vn 0.707 0.000 0.707
- if chunks[0] == "vn" and len(chunks) == 4:
- x = float(chunks[1])
- y = float(chunks[2])
- z = float(chunks[3])
- normals.append([x,y,z])
- # Texture coordinates in (u,v[,w]) coordinates, w is optional
- # vt 0.500 -1.352 [0.234]
- if chunks[0] == "vt" and len(chunks) >= 3:
- u = float(chunks[1])
- v = float(chunks[2])
- w = 0
- if len(chunks)>3:
- w = float(chunks[3])
- uvs.append([u,v,w])
- # Face
- if chunks[0] == "f" and len(chunks) >= 4:
- vertex_index = []
- uv_index = []
- normal_index = []
- # Precompute vert / normal / uv lists
- # for negative index lookup
- vertlen = len(vertices) + 1
- normlen = len(normals) + 1
- uvlen = len(uvs) + 1
- for v in chunks[1:]:
- vertex = parse_vertex(v)
- if vertex['v']:
- if vertex['v'] < 0:
- vertex['v'] += vertlen
- vertex_index.append(vertex['v'])
- if vertex['t']:
- if vertex['t'] < 0:
- vertex['t'] += uvlen
- uv_index.append(vertex['t'])
- if vertex['n']:
- if vertex['n'] < 0:
- vertex['n'] += normlen
- normal_index.append(vertex['n'])
- faces.append({
- 'vertex':vertex_index,
- 'uv':uv_index,
- 'normal':normal_index,
- 'material':mcurrent,
- 'group':group,
- 'object':object,
- 'smooth':smooth,
- })
- # Smooth shading
- if chunks[0] == "s" and len(chunks) == 2:
- smooth = chunks[1]
- return faces, vertices, uvs, normals, materials, mtllib
- # #####################################################
- # Generator - faces
- # #####################################################
- def setBit(value, position, on):
- if on:
- mask = 1 << position
- return (value | mask)
- else:
- mask = ~(1 << position)
- return (value & mask)
- def generate_face(f, fc):
- isTriangle = ( len(f['vertex']) == 3 )
- if isTriangle:
- nVertices = 3
- else:
- nVertices = 4
- hasMaterial = True # for the moment OBJs without materials get default material
- hasFaceUvs = False # not supported in OBJ
- hasFaceVertexUvs = ( len(f['uv']) >= nVertices )
- hasFaceNormals = False # don't export any face normals (as they are computed in engine)
- hasFaceVertexNormals = ( len(f["normal"]) >= nVertices and SHADING == "smooth" )
- hasFaceColors = BAKE_COLORS
- hasFaceVertexColors = False # not supported in OBJ
- faceType = 0
- faceType = setBit(faceType, 0, not isTriangle)
- faceType = setBit(faceType, 1, hasMaterial)
- faceType = setBit(faceType, 2, hasFaceUvs)
- faceType = setBit(faceType, 3, hasFaceVertexUvs)
- faceType = setBit(faceType, 4, hasFaceNormals)
- faceType = setBit(faceType, 5, hasFaceVertexNormals)
- faceType = setBit(faceType, 6, hasFaceColors)
- faceType = setBit(faceType, 7, hasFaceVertexColors)
- faceData = []
- # order is important, must match order in JSONLoader
- # face type
- # vertex indices
- # material index
- # face uvs index
- # face vertex uvs indices
- # face normal index
- # face vertex normals indices
- # face color index
- # face vertex colors indices
- faceData.append(faceType)
- # must clamp in case on polygons bigger than quads
- for i in xrange(nVertices):
- index = f['vertex'][i] - 1
- faceData.append(index)
- faceData.append( f['material'] )
- if hasFaceVertexUvs:
- for i in xrange(nVertices):
- index = f['uv'][i] - 1
- faceData.append(index)
- if hasFaceVertexNormals:
- for i in xrange(nVertices):
- index = f['normal'][i] - 1
- faceData.append(index)
- if hasFaceColors:
- index = fc['material']
- faceData.append(index)
- return ",".join( map(str, faceData) )
- # #####################################################
- # Generator - chunks
- # #####################################################
- def hexcolor(c):
- return ( int(c[0] * 255) << 16 ) + ( int(c[1] * 255) << 8 ) + int(c[2] * 255)
- def generate_vertex(v, option_vertices_truncate, scale):
- if not option_vertices_truncate:
- return TEMPLATE_VERTEX % (v[0], v[1], v[2])
- else:
- return TEMPLATE_VERTEX_TRUNCATE % (scale * v[0], scale * v[1], scale * v[2])
- def generate_normal(n):
- return TEMPLATE_N % (n[0], n[1], n[2])
- def generate_uv(uv):
- return TEMPLATE_UV % (uv[0], uv[1])
- def generate_color_rgb(c):
- return TEMPLATE_COLOR % (c[0], c[1], c[2])
- def generate_color_decimal(c):
- return TEMPLATE_COLOR_DEC % hexcolor(c)
- # #####################################################
- # Morphs
- # #####################################################
- def generate_morph_vertex(name, vertices):
- vertex_string = ",".join(generate_vertex(v, TRUNCATE, SCALE) for v in vertices)
- return TEMPLATE_MORPH_VERTICES % (name, vertex_string)
- def generate_morph_color(name, colors):
- color_string = ",".join(generate_color_rgb(c) for c in colors)
- return TEMPLATE_MORPH_COLORS % (name, color_string)
- def extract_material_colors(materials, mtlfilename, basename):
- """Extract diffuse colors from MTL materials
- """
- if not materials:
- materials = { 'default': 0 }
- mtl = create_materials(materials, mtlfilename, basename)
- mtlColorArraySrt = []
- for m in mtl:
- if m in materials:
- index = materials[m]
- color = mtl[m].get("colorDiffuse", [1,0,0])
- mtlColorArraySrt.append([index, color])
- mtlColorArraySrt.sort()
- mtlColorArray = [x[1] for x in mtlColorArraySrt]
- return mtlColorArray
- def extract_face_colors(faces, material_colors):
- """Extract colors from materials and assign them to faces
- """
- faceColors = []
- for face in faces:
- material_index = face['material']
- faceColors.append(material_colors[material_index])
- return faceColors
- def generate_morph_targets(morphfiles, n_vertices, infile):
- skipOriginalMorph = False
- norminfile = os.path.normpath(infile)
- morphVertexData = []
- for mfilepattern in morphfiles.split():
- matches = glob.glob(mfilepattern)
- matches.sort()
- indices = range(0, len(matches), FRAMESTEP)
- for i in indices:
- path = matches[i]
- normpath = os.path.normpath(path)
- if normpath != norminfile or not skipOriginalMorph:
- name = os.path.basename(normpath)
- morphFaces, morphVertices, morphUvs, morphNormals, morphMaterials, morphMtllib = parse_obj(normpath)
- n_morph_vertices = len(morphVertices)
- if n_vertices != n_morph_vertices:
- print("WARNING: skipping morph [%s] with different number of vertices [%d] than the original model [%d]" % (name, n_morph_vertices, n_vertices))
- else:
- if ALIGN == "center":
- center(morphVertices)
- elif ALIGN == "centerxz":
- centerxz(morphVertices)
- elif ALIGN == "bottom":
- bottom(morphVertices)
- elif ALIGN == "top":
- top(morphVertices)
- morphVertexData.append((get_name(name), morphVertices))
- print("adding [%s] with %d vertices" % (name, n_morph_vertices))
- morphTargets = ""
- if len(morphVertexData):
- morphTargets = "\n%s\n\t" % ",\n".join(generate_morph_vertex(name, vertices) for name, vertices in morphVertexData)
- return morphTargets
- def generate_morph_colors(colorfiles, n_vertices, n_faces):
- morphColorData = []
- colorFaces = []
- materialColors = []
- for mfilepattern in colorfiles.split():
- matches = glob.glob(mfilepattern)
- matches.sort()
- for path in matches:
- normpath = os.path.normpath(path)
- name = os.path.basename(normpath)
- morphFaces, morphVertices, morphUvs, morphNormals, morphMaterials, morphMtllib = parse_obj(normpath)
- n_morph_vertices = len(morphVertices)
- n_morph_faces = len(morphFaces)
- if n_vertices != n_morph_vertices:
- print("WARNING: skipping morph color map [%s] with different number of vertices [%d] than the original model [%d]" % (name, n_morph_vertices, n_vertices))
- elif n_faces != n_morph_faces:
- print("WARNING: skipping morph color map [%s] with different number of faces [%d] than the original model [%d]" % (name, n_morph_faces, n_faces))
- else:
- morphMaterialColors = extract_material_colors(morphMaterials, morphMtllib, normpath)
- morphFaceColors = extract_face_colors(morphFaces, morphMaterialColors)
- morphColorData.append((get_name(name), morphFaceColors))
- # take first color map for baking into face colors
- if len(colorFaces) == 0:
- colorFaces = morphFaces
- materialColors = morphMaterialColors
- print("adding [%s] with %d face colors" % (name, len(morphFaceColors)))
- morphColors = ""
- if len(morphColorData):
- morphColors = "\n%s\n\t" % ",\n".join(generate_morph_color(name, colors) for name, colors in morphColorData)
- return morphColors, colorFaces, materialColors
- # #####################################################
- # Materials
- # #####################################################
- def generate_color(i):
- """Generate hex color corresponding to integer.
- Colors should have well defined ordering.
- First N colors are hardcoded, then colors are random
- (must seed random number generator with deterministic value
- before getting colors).
- """
- if i < len(COLORS):
- #return "0x%06x" % COLORS[i]
- return COLORS[i]
- else:
- #return "0x%06x" % int(0xffffff * random.random())
- return int(0xffffff * random.random())
- def value2string(v):
- if type(v)==str and v[0:2] != "0x":
- return '"%s"' % v
- elif type(v) == bool:
- return str(v).lower()
- return str(v)
- def generate_materials(mtl, materials):
- """Generate JS array of materials objects
- JS material objects are basically prettified one-to-one
- mappings of MTL properties in JSON format.
- """
- mtl_array = []
- for m in mtl:
- if m in materials:
- index = materials[m]
- # add debug information
- # materials should be sorted according to how
- # they appeared in OBJ file (for the first time)
- # this index is identifier used in face definitions
- mtl[m]['DbgName'] = m
- mtl[m]['DbgIndex'] = index
- mtl[m]['DbgColor'] = generate_color(index)
- if BAKE_COLORS:
- mtl[m]['vertexColors'] = "face"
- mtl_raw = ",\n".join(['\t"%s" : %s' % (n, value2string(v)) for n,v in sorted(mtl[m].items())])
- mtl_string = "\t{\n%s\n\t}" % mtl_raw
- mtl_array.append([index, mtl_string])
- return ",\n\n".join([m for i,m in sorted(mtl_array)])
- def generate_mtl(materials):
- """Generate dummy materials (if there is no MTL file).
- """
- mtl = {}
- for m in materials:
- index = materials[m]
- mtl[m] = {
- 'DbgName': m,
- 'DbgIndex': index,
- 'DbgColor': generate_color(index)
- }
- return mtl
- def generate_materials_string(materials, mtlfilename, basename):
- """Generate final materials string.
- """
- if not materials:
- materials = { 'default': 0 }
- mtl = create_materials(materials, mtlfilename, basename)
- return generate_materials(mtl, materials)
- def create_materials(materials, mtlfilename, basename):
- """Parse MTL file and create mapping between its materials and OBJ materials.
- Eventual edge cases are handled here (missing materials, missing MTL file).
- """
- random.seed(42) # to get well defined color order for debug colors
- # default materials with debug colors for when
- # there is no specified MTL / MTL loading failed,
- # or if there were no materials / null materials
- mtl = generate_mtl(materials)
- if mtlfilename:
- # create full pathname for MTL (included from OBJ)
- path = os.path.dirname(basename)
- fname = os.path.join(path, mtlfilename)
- if file_exists(fname):
- # override default materials with real ones from MTL
- # (where they exist, otherwise keep defaults)
- mtl.update(parse_mtl(fname))
- else:
- print("Couldn't find [%s]" % fname)
- return mtl
- # #####################################################
- # Faces
- # #####################################################
- def is_triangle_flat(f):
- return len(f['vertex'])==3 and not (f["normal"] and SHADING == "smooth") and not f['uv']
- def is_triangle_flat_uv(f):
- return len(f['vertex'])==3 and not (f["normal"] and SHADING == "smooth") and len(f['uv'])==3
- def is_triangle_smooth(f):
- return len(f['vertex'])==3 and f["normal"] and SHADING == "smooth" and not f['uv']
- def is_triangle_smooth_uv(f):
- return len(f['vertex'])==3 and f["normal"] and SHADING == "smooth" and len(f['uv'])==3
- def is_quad_flat(f):
- return len(f['vertex'])==4 and not (f["normal"] and SHADING == "smooth") and not f['uv']
- def is_quad_flat_uv(f):
- return len(f['vertex'])==4 and not (f["normal"] and SHADING == "smooth") and len(f['uv'])==4
- def is_quad_smooth(f):
- return len(f['vertex'])==4 and f["normal"] and SHADING == "smooth" and not f['uv']
- def is_quad_smooth_uv(f):
- return len(f['vertex'])==4 and f["normal"] and SHADING == "smooth" and len(f['uv'])==4
- def sort_faces(faces):
- data = {
- 'triangles_flat': [],
- 'triangles_flat_uv': [],
- 'triangles_smooth': [],
- 'triangles_smooth_uv': [],
- 'quads_flat': [],
- 'quads_flat_uv': [],
- 'quads_smooth': [],
- 'quads_smooth_uv': []
- }
- for f in faces:
- if is_triangle_flat(f):
- data['triangles_flat'].append(f)
- elif is_triangle_flat_uv(f):
- data['triangles_flat_uv'].append(f)
- elif is_triangle_smooth(f):
- data['triangles_smooth'].append(f)
- elif is_triangle_smooth_uv(f):
- data['triangles_smooth_uv'].append(f)
- elif is_quad_flat(f):
- data['quads_flat'].append(f)
- elif is_quad_flat_uv(f):
- data['quads_flat_uv'].append(f)
- elif is_quad_smooth(f):
- data['quads_smooth'].append(f)
- elif is_quad_smooth_uv(f):
- data['quads_smooth_uv'].append(f)
- return data
- # #####################################################
- # API - ASCII converter
- # #####################################################
- def convert_ascii(infile, morphfiles, colorfiles, outfile):
- """Convert infile.obj to outfile.js
- Here is where everything happens. If you need to automate conversions,
- just import this file as Python module and call this method.
- """
- if not file_exists(infile):
- print("Couldn't find [%s]" % infile)
- return
- # parse OBJ / MTL files
- faces, vertices, uvs, normals, materials, mtllib = parse_obj(infile)
- n_vertices = len(vertices)
- n_faces = len(faces)
- # align model
- if ALIGN == "center":
- center(vertices)
- elif ALIGN == "centerxz":
- centerxz(vertices)
- elif ALIGN == "bottom":
- bottom(vertices)
- elif ALIGN == "top":
- top(vertices)
- # generate normals string
- nnormal = 0
- normals_string = ""
- if SHADING == "smooth":
- normals_string = ",".join(generate_normal(n) for n in normals)
- nnormal = len(normals)
- # extract morph vertices
- morphTargets = generate_morph_targets(morphfiles, n_vertices, infile)
- # extract morph colors
- morphColors, colorFaces, materialColors = generate_morph_colors(colorfiles, n_vertices, n_faces)
- # generate colors string
- ncolor = 0
- colors_string = ""
- if len(colorFaces) < len(faces):
- colorFaces = faces
- materialColors = extract_material_colors(materials, mtllib, infile)
- if BAKE_COLORS:
- colors_string = ",".join(generate_color_decimal(c) for c in materialColors)
- ncolor = len(materialColors)
- # generate ascii model string
- text = TEMPLATE_FILE_ASCII % {
- "name" : get_name(outfile),
- "fname" : os.path.basename(infile),
- "nvertex" : len(vertices),
- "nface" : len(faces),
- "nuv" : len(uvs),
- "nnormal" : nnormal,
- "ncolor" : ncolor,
- "nmaterial" : len(materials),
- "materials" : generate_materials_string(materials, mtllib, infile),
- "normals" : normals_string,
- "colors" : colors_string,
- "uvs" : ",".join(generate_uv(uv) for uv in uvs),
- "vertices" : ",".join(generate_vertex(v, TRUNCATE, SCALE) for v in vertices),
- "morphTargets" : morphTargets,
- "morphColors" : morphColors,
- "faces" : ",".join(generate_face(f, fc) for f, fc in zip(faces, colorFaces)),
- "scale" : SCALE
- }
- out = open(outfile, "w")
- out.write(text)
- out.close()
- print("%d vertices, %d faces, %d materials" % (len(vertices), len(faces), len(materials)))
- # #############################################################################
- # API - Binary converter
- # #############################################################################
- def dump_materials_to_buffer(faces, buffer):
- for f in faces:
- data = struct.pack('<H',
- f['material'])
- buffer.append(data)
- def dump_vertices3_to_buffer(faces, buffer):
- for f in faces:
- vi = f['vertex']
- data = struct.pack('<III',
- vi[0]-1, vi[1]-1, vi[2]-1)
- buffer.append(data)
- def dump_vertices4_to_buffer(faces, buffer):
- for f in faces:
- vi = f['vertex']
- data = struct.pack('<IIII',
- vi[0]-1, vi[1]-1, vi[2]-1, vi[3]-1)
- buffer.append(data)
- def dump_normals3_to_buffer(faces, buffer):
- for f in faces:
- ni = f['normal']
- data = struct.pack('<III',
- ni[0]-1, ni[1]-1, ni[2]-1)
- buffer.append(data)
- def dump_normals4_to_buffer(faces, buffer):
- for f in faces:
- ni = f['normal']
- data = struct.pack('<IIII',
- ni[0]-1, ni[1]-1, ni[2]-1, ni[3]-1)
- buffer.append(data)
- def dump_uvs3_to_buffer(faces, buffer):
- for f in faces:
- ui = f['uv']
- data = struct.pack('<III',
- ui[0]-1, ui[1]-1, ui[2]-1)
- buffer.append(data)
- def dump_uvs4_to_buffer(faces, buffer):
- for f in faces:
- ui = f['uv']
- data = struct.pack('<IIII',
- ui[0]-1, ui[1]-1, ui[2]-1, ui[3]-1)
- buffer.append(data)
- def add_padding(buffer, n):
- if n % 4:
- for i in range(4 - n % 4):
- data = struct.pack('<B', 0)
- buffer.append(data)
- def convert_binary(infile, outfile):
- """Convert infile.obj to outfile.js + outfile.bin
- """
- if not file_exists(infile):
- print("Couldn't find [%s]" % infile)
- return
- binfile = get_name(outfile) + ".bin"
- faces, vertices, uvs, normals, materials, mtllib = parse_obj(infile)
- if ALIGN == "center":
- center(vertices)
- elif ALIGN == "centerxz":
- centerxz(vertices)
- elif ALIGN == "bottom":
- bottom(vertices)
- elif ALIGN == "top":
- top(vertices)
- sfaces = sort_faces(faces)
- if SHADING == "smooth":
- nnormals = len(normals)
- else:
- nnormals = 0
- # ###################
- # generate JS file
- # ###################
- text = TEMPLATE_FILE_BIN % {
- "name" : get_name(outfile),
- "materials" : generate_materials_string(materials, mtllib, infile),
- "buffers" : binfile,
- "fname" : os.path.basename(infile),
- "nvertex" : len(vertices),
- "nface" : len(faces),
- "nmaterial" : len(materials),
- "nnormal" : nnormals,
- "nuv" : len(uvs)
- }
- out = open(outfile, "w")
- out.write(text)
- out.close()
- # ###################
- # generate BIN file
- # ###################
- buffer = []
- # header
- # ------
- header_bytes = struct.calcsize('<12s')
- header_bytes += struct.calcsize('<BBBBBBBB')
- header_bytes += struct.calcsize('<IIIIIIIIIII')
- # signature
- signature = struct.pack(b'<12s', b'Three.js 003')
- # metadata (all data is little-endian)
- vertex_coordinate_bytes = 4
- normal_coordinate_bytes = 1
- uv_coordinate_bytes = 4
- vertex_index_bytes = 4
- normal_index_bytes = 4
- uv_index_bytes = 4
- material_index_bytes = 2
- # header_bytes unsigned char 1
- # vertex_coordinate_bytes unsigned char 1
- # normal_coordinate_bytes unsigned char 1
- # uv_coordinate_bytes unsigned char 1
- # vertex_index_bytes unsigned char 1
- # normal_index_bytes unsigned char 1
- # uv_index_bytes unsigned char 1
- # material_index_bytes unsigned char 1
- bdata = struct.pack('<BBBBBBBB', header_bytes,
- vertex_coordinate_bytes,
- normal_coordinate_bytes,
- uv_coordinate_bytes,
- vertex_index_bytes,
- normal_index_bytes,
- uv_index_bytes,
- material_index_bytes)
- ntri_flat = len(sfaces['triangles_flat'])
- ntri_smooth = len(sfaces['triangles_smooth'])
- ntri_flat_uv = len(sfaces['triangles_flat_uv'])
- ntri_smooth_uv = len(sfaces['triangles_smooth_uv'])
- nquad_flat = len(sfaces['quads_flat'])
- nquad_smooth = len(sfaces['quads_smooth'])
- nquad_flat_uv = len(sfaces['quads_flat_uv'])
- nquad_smooth_uv = len(sfaces['quads_smooth_uv'])
- # nvertices unsigned int 4
- # nnormals unsigned int 4
- # nuvs unsigned int 4
- # ntri_flat unsigned int 4
- # ntri_smooth unsigned int 4
- # ntri_flat_uv unsigned int 4
- # ntri_smooth_uv unsigned int 4
- # nquad_flat unsigned int 4
- # nquad_smooth unsigned int 4
- # nquad_flat_uv unsigned int 4
- # nquad_smooth_uv unsigned int 4
- ndata = struct.pack('<IIIIIIIIIII', len(vertices),
- nnormals,
- len(uvs),
- ntri_flat,
- ntri_smooth,
- ntri_flat_uv,
- ntri_smooth_uv,
- nquad_flat,
- nquad_smooth,
- nquad_flat_uv,
- nquad_smooth_uv)
- buffer.append(signature)
- buffer.append(bdata)
- buffer.append(ndata)
- # 1. vertices
- # ------------
- # x float 4
- # y float 4
- # z float 4
- for v in vertices:
- data = struct.pack('<fff', v[0], v[1], v[2])
- buffer.append(data)
- # 2. normals
- # ---------------
- # x signed char 1
- # y signed char 1
- # z signed char 1
- if SHADING == "smooth":
- for n in normals:
- normalize(n)
- data = struct.pack('<bbb', math.floor(n[0]*127+0.5),
- math.floor(n[1]*127+0.5),
- math.floor(n[2]*127+0.5))
- buffer.append(data)
- add_padding(buffer, nnormals * 3)
- # 3. uvs
- # -----------
- # u float 4
- # v float 4
- for uv in uvs:
- data = struct.pack('<ff', uv[0], uv[1])
- buffer.append(data)
- # padding
- #data = struct.pack('<BB', 0, 0)
- #buffer.append(data)
- # 4. flat triangles (vertices + materials)
- # ------------------
- # a unsigned int 4
- # b unsigned int 4
- # c unsigned int 4
- # ------------------
- # m unsigned short 2
- dump_vertices3_to_buffer(sfaces['triangles_flat'], buffer)
- dump_materials_to_buffer(sfaces['triangles_flat'], buffer)
- add_padding(buffer, ntri_flat * 2)
- # 5. smooth triangles (vertices + materials + normals)
- # -------------------
- # a unsigned int 4
- # b unsigned int 4
- # c unsigned int 4
- # -------------------
- # na unsigned int 4
- # nb unsigned int 4
- # nc unsigned int 4
- # -------------------
- # m unsigned short 2
- dump_vertices3_to_buffer(sfaces['triangles_smooth'], buffer)
- dump_normals3_to_buffer(sfaces['triangles_smooth'], buffer)
- dump_materials_to_buffer(sfaces['triangles_smooth'], buffer)
- add_padding(buffer, ntri_smooth * 2)
- # 6. flat triangles uv (vertices + materials + uvs)
- # --------------------
- # a unsigned int 4
- # b unsigned int 4
- # c unsigned int 4
- # --------------------
- # ua unsigned int 4
- # ub unsigned int 4
- # uc unsigned int 4
- # --------------------
- # m unsigned short 2
- dump_vertices3_to_buffer(sfaces['triangles_flat_uv'], buffer)
- dump_uvs3_to_buffer(sfaces['triangles_flat_uv'], buffer)
- dump_materials_to_buffer(sfaces['triangles_flat_uv'], buffer)
- add_padding(buffer, ntri_flat_uv * 2)
- # 7. smooth triangles uv (vertices + materials + normals + uvs)
- # ----------------------
- # a unsigned int 4
- # b unsigned int 4
- # c unsigned int 4
- # --------------------
- # na unsigned int 4
- # nb unsigned int 4
- # nc unsigned int 4
- # --------------------
- # ua unsigned int 4
- # ub unsigned int 4
- # uc unsigned int 4
- # --------------------
- # m unsigned short 2
- dump_vertices3_to_buffer(sfaces['triangles_smooth_uv'], buffer)
- dump_normals3_to_buffer(sfaces['triangles_smooth_uv'], buffer)
- dump_uvs3_to_buffer(sfaces['triangles_smooth_uv'], buffer)
- dump_materials_to_buffer(sfaces['triangles_smooth_uv'], buffer)
- add_padding(buffer, ntri_smooth_uv * 2)
- # 8. flat quads (vertices + materials)
- # ------------------
- # a unsigned int 4
- # b unsigned int 4
- # c unsigned int 4
- # d unsigned int 4
- # --------------------
- # m unsigned short 2
- dump_vertices4_to_buffer(sfaces['quads_flat'], buffer)
- dump_materials_to_buffer(sfaces['quads_flat'], buffer)
- add_padding(buffer, nquad_flat * 2)
- # 9. smooth quads (vertices + materials + normals)
- # -------------------
- # a unsigned int 4
- # b unsigned int 4
- # c unsigned int 4
- # d unsigned int 4
- # --------------------
- # na unsigned int 4
- # nb unsigned int 4
- # nc unsigned int 4
- # nd unsigned int 4
- # --------------------
- # m unsigned short 2
- dump_vertices4_to_buffer(sfaces['quads_smooth'], buffer)
- dump_normals4_to_buffer(sfaces['quads_smooth'], buffer)
- dump_materials_to_buffer(sfaces['quads_smooth'], buffer)
- add_padding(buffer, nquad_smooth * 2)
- # 10. flat quads uv (vertices + materials + uvs)
- # ------------------
- # a unsigned int 4
- # b unsigned int 4
- # c unsigned int 4
- # d unsigned int 4
- # --------------------
- # ua unsigned int 4
- # ub unsigned int 4
- # uc unsigned int 4
- # ud unsigned int 4
- # --------------------
- # m unsigned short 2
- dump_vertices4_to_buffer(sfaces['quads_flat_uv'], buffer)
- dump_uvs4_to_buffer(sfaces['quads_flat_uv'], buffer)
- dump_materials_to_buffer(sfaces['quads_flat_uv'], buffer)
- add_padding(buffer, nquad_flat_uv * 2)
- # 11. smooth quads uv
- # -------------------
- # a unsigned int 4
- # b unsigned int 4
- # c unsigned int 4
- # d unsigned int 4
- # --------------------
- # na unsigned int 4
- # nb unsigned int 4
- # nc unsigned int 4
- # nd unsigned int 4
- # --------------------
- # ua unsigned int 4
- # ub unsigned int 4
- # uc unsigned int 4
- # ud unsigned int 4
- # --------------------
- # m unsigned short 2
- dump_vertices4_to_buffer(sfaces['quads_smooth_uv'], buffer)
- dump_normals4_to_buffer(sfaces['quads_smooth_uv'], buffer)
- dump_uvs4_to_buffer(sfaces['quads_smooth_uv'], buffer)
- dump_materials_to_buffer(sfaces['quads_smooth_uv'], buffer)
- add_padding(buffer, nquad_smooth_uv * 2)
- path = os.path.dirname(outfile)
- fname = os.path.join(path, binfile)
- out = open(fname, "wb")
- out.write(b"".join(buffer))
- out.close()
- # #############################################################################
- # Helpers
- # #############################################################################
- def usage():
- 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]))
- # #####################################################
- # Main
- # #####################################################
- if __name__ == "__main__":
- # get parameters from the command line
- try:
- 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="])
- except getopt.GetoptError:
- usage()
- sys.exit(2)
- infile = outfile = ""
- morphfiles = ""
- colorfiles = ""
- for o, a in opts:
- if o in ("-h", "--help"):
- usage()
- sys.exit()
- elif o in ("-i", "--input"):
- infile = a
- elif o in ("-m", "--morphs"):
- morphfiles = a
- elif o in ("-c", "--colors"):
- colorfiles = a
- elif o in ("-o", "--output"):
- outfile = a
- elif o in ("-a", "--align"):
- if a in ("top", "bottom", "center", "centerxz", "none"):
- ALIGN = a
- elif o in ("-s", "--shading"):
- if a in ("flat", "smooth"):
- SHADING = a
- elif o in ("-t", "--type"):
- if a in ("binary", "ascii"):
- TYPE = a
- elif o in ("-d", "--dissolve"):
- if a in ("normal", "invert"):
- TRANSPARENCY = a
- elif o in ("-b", "--bakecolors"):
- BAKE_COLORS = True
- elif o in ("-x", "--truncatescale"):
- TRUNCATE = True
- SCALE = float(a)
- elif o in ("-f", "--framestep"):
- FRAMESTEP = int(a)
- if infile == "" or outfile == "":
- usage()
- sys.exit(2)
- print("Converting [%s] into [%s] ..." % (infile, outfile))
- if morphfiles:
- print("Morphs [%s]" % morphfiles)
- if colorfiles:
- print("Colors [%s]" % colorfiles)
- if TYPE == "ascii":
- convert_ascii(infile, morphfiles, colorfiles, outfile)
- elif TYPE == "binary":
- convert_binary(infile, outfile)
|