Browse Source

Replaced python obj converter with node.js script.

Mr.doob 8 years ago
parent
commit
89cb329fec

+ 9 - 0
utils/converters/README.md

@@ -0,0 +1,9 @@
+Utilities for converting model files to the Three.js JSON format.
+
+## obj2three.js
+
+Usage:
+
+```
+node obj2three.js model.obj
+```

+ 0 - 1604
utils/converters/obj/convert_obj_three.py

@@ -1,1604 +0,0 @@
-"""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)
-        -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
-        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.MultiMaterial( 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
-
-------
-Author
-------
-AlteredQualia http://alteredqualia.com
-
-"""
-
-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
-
-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])
-
-            # 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])]
-
-            # 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])
-
-            # Dissolves
-            # d 0.9
-            if chunks[0] == "d" and len(chunks) == 2:
-                materials[identifier]["opacity"] = float(chunks[1])
-                if materials[identifier]["opacity"] < 1.0:
-                    materials[identifier]["transparent"] = True
-
-            # Transparency
-            # Tr 0.1
-            if chunks[0] == "Tr" and len(chunks) == 2:
-                materials[identifier]["opacity"] = 1.0 - float(chunks[1])
-                if materials[identifier]["opacity"] < 1.0:
-                    materials[identifier]["transparent"] = True
-
-            # 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('<12s', '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("".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=", "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 ("-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)

+ 0 - 1604
utils/converters/obj/convert_obj_three_for_python3.py

@@ -1,1604 +0,0 @@
-"""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)
-        -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
-        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.MultiMaterial( 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
-
-------
-Author
-------
-AlteredQualia http://alteredqualia.com
-
-"""
-
-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
-
-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 range(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])
-
-            # 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])]
-
-            # 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])
-
-            # Dissolves
-            # d 0.9
-            if chunks[0] == "d" and len(chunks) == 2:
-                materials[identifier]["opacity"] = float(chunks[1])
-                if materials[identifier]["opacity"] < 1.0:
-                    materials[identifier]["transparent"] = True
-
-            # Transparency
-            # Tr 0.1
-            if chunks[0] == "Tr" and len(chunks) == 2:
-                materials[identifier]["opacity"] = 1.0 - float(chunks[1])
-                if materials[identifier]["opacity"] < 1.0:
-                    materials[identifier]["transparent"] = True
-
-            # 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 range(nVertices):
-        index = f['vertex'][i] - 1
-        faceData.append(index)
-
-    faceData.append( f['material'] )
-
-    if hasFaceVertexUvs:
-        for i in range(nVertices):
-            index = f['uv'][i] - 1
-            faceData.append(index)
-
-    if hasFaceVertexNormals:
-        for i in range(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 ("-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)

+ 0 - 504
utils/converters/obj/split_obj.py

@@ -1,504 +0,0 @@
-"""Split single OBJ model into mutliple OBJ files by materials
-
--------------------------------------
-How to use
--------------------------------------
-
-python split_obj.py -i infile.obj -o outfile
-
-Will generate:
-
-outfile_000.obj
-outfile_001.obj
-
-...
-
-outfile_XXX.obj
-
--------------------------------------
-Parser based on format description
--------------------------------------
-
-    http://en.wikipedia.org/wiki/Obj
-
-------
-Author
-------
-AlteredQualia http://alteredqualia.com
-
-"""
-
-import fileinput
-import operator
-import random
-import os.path
-import getopt
-import sys
-import struct
-import math
-import glob
-
-# #####################################################
-# Configuration
-# #####################################################
-TRUNCATE = False
-SCALE = 1.0
-
-
-# #####################################################
-# Templates
-# #####################################################
-TEMPLATE_OBJ = u"""\
-################################
-# OBJ generated by split_obj.py
-################################
-# Faces:    %(nfaces)d
-# Vertices: %(nvertices)d
-# Normals:  %(nnormals)d
-# UVs:      %(nuvs)d
-################################
-
-# vertices
-
-%(vertices)s
-
-# normals
-
-%(normals)s
-
-# uvs
-
-%(uvs)s
-
-# faces
-
-%(faces)s
-"""
-
-TEMPLATE_VERTEX = "v %f %f %f"
-TEMPLATE_VERTEX_TRUNCATE = "v %d %d %d"
-
-TEMPLATE_NORMAL = "vn %.5g %.5g %.5g"
-TEMPLATE_UV = "vt %.5g %.5g"
-
-TEMPLATE_FACE3_V = "f %d %d %d"
-TEMPLATE_FACE4_V = "f %d %d %d %d"
-
-TEMPLATE_FACE3_VT = "f %d/%d %d/%d %d/%d"
-TEMPLATE_FACE4_VT = "f %d/%d %d/%d %d/%d %d/%d"
-
-TEMPLATE_FACE3_VN = "f %d//%d %d//%d %d//%d"
-TEMPLATE_FACE4_VN = "f %d//%d %d//%d %d//%d %d//%d"
-
-TEMPLATE_FACE3_VTN = "f %d/%d/%d %d/%d/%d %d/%d/%d"
-TEMPLATE_FACE4_VTN = "f %d/%d/%d %d/%d/%d %d/%d/%d %d/%d/%d"
-
-
-# #####################################################
-# 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
-
-# #####################################################
-# 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 = {}
-    mcounter = 0
-    mcurrent = 0
-
-    mtllib = ""
-
-    # current face state
-    group = 0
-    object = 0
-    smooth = 0
-
-    for line in fileinput.input(fname):
-        chunks = line.split()
-        if len(chunks) > 0:
-
-            # 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 = []
-
-                for v in chunks[1:]:
-                    vertex = parse_vertex(v)
-                    if vertex['v']:
-                        vertex_index.append(vertex['v'])
-                    if vertex['t']:
-                        uv_index.append(vertex['t'])
-                    if vertex['n']:
-                        normal_index.append(vertex['n'])
-
-                faces.append({
-                    'vertex':vertex_index,
-                    'uv':uv_index,
-                    'normal':normal_index,
-
-                    'material':mcurrent,
-                    'group':group,
-                    'object':object,
-                    'smooth':smooth,
-                    })
-
-            # 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" and len(chunks) == 2:
-                material = chunks[1]
-                if not material in materials:
-                    mcurrent = mcounter
-                    materials[material] = mcounter
-                    mcounter += 1
-                else:
-                    mcurrent = materials[material]
-
-            # Smooth shading
-            if chunks[0] == "s" and len(chunks) == 2:
-                smooth = chunks[1]
-
-    return faces, vertices, uvs, normals, materials, mtllib
-
-# #############################################################################
-# API - Breaker
-# #############################################################################
-def break_obj(infile, outfile):
-    """Break infile.obj to outfile.obj
-    """
-
-    if not file_exists(infile):
-        print "Couldn't find [%s]" % infile
-        return
-
-    faces, vertices, uvs, normals, materials, mtllib = parse_obj(infile)
-
-    # sort faces by materials
-
-    chunks = {}
-
-    for face in faces:
-        material = face["material"]
-        if not material in chunks:
-            chunks[material] = {"faces": [], "vertices": set(), "normals": set(), "uvs": set()}
-
-        chunks[material]["faces"].append(face)
-
-    # extract unique vertex / normal / uv indices used per chunk
-
-    for material in chunks:
-        chunk = chunks[material]
-        for face in chunk["faces"]:
-            for i in face["vertex"]:
-                chunk["vertices"].add(i)
-
-            for i in face["normal"]:
-                chunk["normals"].add(i)
-
-            for i in face["uv"]:
-                chunk["uvs"].add(i)
-
-    # generate new OBJs
-
-    for mi, material in enumerate(chunks):
-        chunk = chunks[material]
-
-        # generate separate vertex / normal / uv index lists for each chunk
-        # (including mapping from original to new indices)
-
-        # get well defined order
-
-        new_vertices = list(chunk["vertices"])
-        new_normals = list(chunk["normals"])
-        new_uvs = list(chunk["uvs"])
-
-        # map original => new indices
-
-        vmap = {}
-        for i, v in enumerate(new_vertices):
-            vmap[v] = i + 1
-
-        nmap = {}
-        for i, n in enumerate(new_normals):
-            nmap[n] = i + 1
-
-        tmap = {}
-        for i, t in enumerate(new_uvs):
-            tmap[t] = i + 1
-
-
-        # vertices
-
-        pieces = []
-        for i in new_vertices:
-            vertex = vertices[i-1]
-            txt = TEMPLATE_VERTEX % (vertex[0], vertex[1], vertex[2])
-            pieces.append(txt)
-
-        str_vertices = "\n".join(pieces)
-
-        # normals
-
-        pieces = []
-        for i in new_normals:
-            normal = normals[i-1]
-            txt = TEMPLATE_NORMAL % (normal[0], normal[1], normal[2])
-            pieces.append(txt)
-
-        str_normals = "\n".join(pieces)
-
-        # uvs
-
-        pieces = []
-        for i in new_uvs:
-            uv = uvs[i-1]
-            txt = TEMPLATE_UV % (uv[0], uv[1])
-            pieces.append(txt)
-
-        str_uvs = "\n".join(pieces)
-
-        # faces
-
-        pieces = []
-
-        for face in chunk["faces"]:
-
-            txt = ""
-
-            fv = face["vertex"]
-            fn = face["normal"]
-            ft = face["uv"]
-
-            if len(fv) == 3:
-
-                va = vmap[fv[0]]
-                vb = vmap[fv[1]]
-                vc = vmap[fv[2]]
-
-                if len(fn) == 3 and len(ft) == 3:
-                    na = nmap[fn[0]]
-                    nb = nmap[fn[1]]
-                    nc = nmap[fn[2]]
-
-                    ta = tmap[ft[0]]
-                    tb = tmap[ft[1]]
-                    tc = tmap[ft[2]]
-
-                    txt = TEMPLATE_FACE3_VTN % (va, ta, na, vb, tb, nb, vc, tc, nc)
-
-                elif len(fn) == 3:
-                    na = nmap[fn[0]]
-                    nb = nmap[fn[1]]
-                    nc = nmap[fn[2]]
-
-                    txt = TEMPLATE_FACE3_VN % (va, na, vb, nb, vc, nc)
-
-                elif len(ft) == 3:
-                    ta = tmap[ft[0]]
-                    tb = tmap[ft[1]]
-                    tc = tmap[ft[2]]
-
-                    txt = TEMPLATE_FACE3_VT % (va, ta, vb, tb, vc, tc)
-
-                else:
-                    txt = TEMPLATE_FACE3_V % (va, vb, vc)
-
-            elif len(fv) == 4:
-
-                va = vmap[fv[0]]
-                vb = vmap[fv[1]]
-                vc = vmap[fv[2]]
-                vd = vmap[fv[3]]
-
-                if len(fn) == 4 and len(ft) == 4:
-                    na = nmap[fn[0]]
-                    nb = nmap[fn[1]]
-                    nc = nmap[fn[2]]
-                    nd = nmap[fn[3]]
-
-                    ta = tmap[ft[0]]
-                    tb = tmap[ft[1]]
-                    tc = tmap[ft[2]]
-                    td = tmap[ft[3]]
-
-                    txt = TEMPLATE_FACE4_VTN % (va, ta, na, vb, tb, nb, vc, tc, nc, vd, td, nd)
-
-                elif len(fn) == 4:
-                    na = nmap[fn[0]]
-                    nb = nmap[fn[1]]
-                    nc = nmap[fn[2]]
-                    nd = nmap[fn[3]]
-
-                    txt = TEMPLATE_FACE4_VN % (va, na, vb, nb, vc, nc, vd, nd)
-
-                elif len(ft) == 4:
-                    ta = tmap[ft[0]]
-                    tb = tmap[ft[1]]
-                    tc = tmap[ft[2]]
-                    td = tmap[ft[3]]
-
-                    txt = TEMPLATE_FACE4_VT % (va, ta, vb, tb, vc, tc, vd, td)
-
-                else:
-                    txt = TEMPLATE_FACE4_V % (va, vb, vc, vd)
-
-            pieces.append(txt)
-
-
-        str_faces = "\n".join(pieces)
-
-        # generate OBJ string
-
-        content = TEMPLATE_OBJ % {
-        "nfaces"        : len(chunk["faces"]),
-        "nvertices"     : len(new_vertices),
-        "nnormals"      : len(new_normals),
-        "nuvs"          : len(new_uvs),
-
-        "vertices"      : str_vertices,
-        "normals"       : str_normals,
-        "uvs"           : str_uvs,
-        "faces"         : str_faces
-        }
-
-        # write OBJ file
-
-        outname = "%s_%03d.obj" % (outfile, mi)
-
-        f = open(outname, "w")
-        f.write(content)
-        f.close()
-
-
-# #############################################################################
-# Helpers
-# #############################################################################
-def usage():
-    print "Usage: %s -i filename.obj -o prefix" % os.path.basename(sys.argv[0])
-
-# #####################################################
-# Main
-# #####################################################
-if __name__ == "__main__":
-
-    # get parameters from the command line
-
-    try:
-        opts, args = getopt.getopt(sys.argv[1:], "hi:o:x:", ["help", "input=", "output=", "truncatescale="])
-
-    except getopt.GetoptError:
-        usage()
-        sys.exit(2)
-
-    infile = outfile = ""
-
-    for o, a in opts:
-        if o in ("-h", "--help"):
-            usage()
-            sys.exit()
-
-        elif o in ("-i", "--input"):
-            infile = a
-
-        elif o in ("-o", "--output"):
-            outfile = a
-
-        elif o in ("-x", "--truncatescale"):
-            TRUNCATE = True
-            SCALE = float(a)
-
-    if infile == "" or outfile == "":
-        usage()
-        sys.exit(2)
-
-    print "Splitting [%s] into [%s_XXX.obj] ..." % (infile, outfile)
-
-    break_obj(infile, outfile)
-

+ 30 - 0
utils/converters/obj2three.js

@@ -0,0 +1,30 @@
+var fs = require( 'fs' );
+var path = require( 'path' );
+
+if ( process.argv.length <= 2 ) {
+
+	console.log( "Usage: " + path.basename( __filename ) + " model.obj" );
+	process.exit( - 1 );
+
+}
+
+//
+
+var PRECISION = 6;
+
+function parseNumber( key, value ) {
+
+	return typeof value === 'number' ? parseFloat( value.toFixed( PRECISION ) ) : value;
+
+}
+
+THREE = require( '../../build/three.js' );
+require( '../../examples/js/loaders/OBJLoader.js' );
+
+var file = process.argv[ 2 ];
+var loader = new THREE.OBJLoader();
+
+var text = fs.readFileSync( file, 'utf8' );
+
+var content = JSON.stringify( loader.parse( text ).toJSON(), parseNumber );
+fs.writeFileSync( path.basename( file, '.obj' ) + '.json', content, 'utf8' );