.OBJ to JSON python script

I have developed a small script in python to convert OBJs to JSON, well suited for WebGL rendering.
It is not very optimized, and it only support OBJs with one mesh with UVs and Normals.
It computes the bounding box and clamp to 3 decimals all the values to avoid some ugly values.
It support indexed meshes and coordinates swap for 3ds MAX exported meshes.

The code after the jump.

Usage: call the script passing the obj filename as a parameter
You can add a second parameter specifying a name for the object, this is useful when using the output file as a script include in your code instead of a AJAX call. Then the output will be: myname = {…}

Enjoy it!

#!/usr/bin/python
# .OBJ to JSON
# by Javi Agenjo (javi.agenjo@gmail.com) 26/6/11
# only works with OBJs with normals and uvs
import os,sys
import json,math
 
version = "0.4"
 
if len(sys.argv) < 2:
    print "Error: Filename parameter missing"
    exit(1)
 
filename = sys.argv[1]
meshname = ""
objectname = ''
 
if len(sys.argv) > 2:
    objectname = sys.argv[2]
 
use_3dsmax = False
if "-max" in sys.argv: use_3dsmax = True
 
use_indexed = True
if "-indexed" in sys.argv: use_indexed = True
 
indexed_vertices = []
indexed_normals = []
indexed_uvs = []
 
positions = []
normals = []
uvs = []
indices = []
 
unique_indexed = 0
indexed_triplets = {}
 
def add(v):
    global positions,normals,uvs,indexed_vertices,indexed_normals,indexed_uvs
    t = v.split("/")
    #print "*"+v+"*"
    positions.append( indexed_vertices[ int(t[0])-1 ] )    
    uvs.append( indexed_uvs[ int(t[1])-1 ] )
    normals.append( indexed_normals[ int(t[2])-1 ] )
    return len(positions) - 1
 
def addIndexed(v):
    global indexed_triplets, indices
    i = indexed_triplets.get(v)
    if i == None:
        i = add(v)
	indexed_triplets[v] = i
    indices.append(i)
 
def array2string(a):
    r = "["
    for v in a:
        for n in v:
            r += str(n) + ","
    return r[:-1] + "]"
 
def linealize(a):
    r = []
    for v in a:
        for n in v:
            r.append(n)
    return r
 
def computeMinMax(a):
    min_v = [1000000,1000000,1000000]
    max_v = [-1000000,-1000000,-1000000]
    for v in a:
        min_v[0] = min( min_v[0], v[0] )
        min_v[1] = min( min_v[1], v[1] )
        min_v[2] = min( min_v[2], v[2] )
        max_v[0] = max( max_v[0], v[0] )
        max_v[1] = max( max_v[1], v[1] )
        max_v[2] = max( max_v[2], v[2] )
    return (min_v,max_v)
 
try:
    fin = open(filename)
except IOError:
    print "Error: file cannot be opened"
    exit(1)
 
try:
    #if True:
    for line in fin.readlines():
	#print line
        line = line.replace("\r\n","")
        line = line.replace("  "," ")
	line = line.strip()
        words = line.split(' ')
        #print ":::" + words[0]
        if words[0] == '#':
            continue
 
        if words[0] == 'v':
            if use_3dsmax:
                indexed_vertices.append( [ -1 * float(words[1]), float(words[3]), float(words[2]) ])
            else:
                indexed_vertices.append( [ float(words[1]), float(words[2]) ,float(words[3]) ])    
        elif words[0] == 'vn':
            if use_3dsmax:
	        indexed_normals.append( [ -1 * float(words[1]), float(words[3]) , float(words[2]) ])
	    else:
	        indexed_normals.append( [ float(words[1]), float(words[2]) , float(words[3]) ])
        elif words[0] == 'vt':
            indexed_uvs.append( [ float(words[1]), float(words[2]) ])
        elif words[0] == 'f':
            size = len(words)
            #0 is f, #1 first vertex, start in 2, ends when there is one remaining
            for i in range(2,size-1):
                #print "T:"+line+"=>["+str(i)+"]"+words[i]+" len: "+str(len(words))
		if use_indexed:
                    addIndexed(words[1])
                    addIndexed(words[i])
                    addIndexed(words[i+1])
		else:
                    add(words[1])
                    add(words[i])
                    add(words[i+1])
        elif words[0] == 'g' and len(words) > 1:
	    #print words
            meshname = words[1]
except:
    print "Error, problem parsing OBJ"
    exit(1)
 
fin.close()
 
if len(positions) == 0:
    print "Error, no OBJ info found"
    exit(1)
 
minmax = computeMinMax(indexed_vertices)
 
obj = {}
obj["version"] = version
obj["filename"] = filename
obj["meshname"] = meshname
obj["positions"] = linealize(positions)
obj["normals"] = linealize(normals)
if len(indices):
    obj["indices"] = indices
obj["uvs"] = linealize(uvs)
obj["aabb_min"] = minmax[0]
obj["aabb_max"] = minmax[1]
obj["numvertices"] = len(positions)
 
from json import encoder
encoder.FLOAT_REPR = lambda o: format(o, '.3f')
 
if objectname != '':
    print objectname + " = ",
print json.dumps(obj)
exit(0)

Leave a Reply


2 + = ten