treeform's obj2egg <bug hunt>

I need to convert some obj models in a hurry to egg. Its was easy to do.

the most resent version is here
github.com/treeform/obj2egg/blob … obj2egg.py

If you use this and find it does not work for you just post your obj file here and i will sort make it export and egg. OBJ format is non standard so bear with me as i get all bugs and new features added to it.

EDIT: LOL fixed one bug already!

""" by
    .___..__ .___.___.___.__..__ .  .
      |  [__)[__ [__ [__ |  |[__)|\/|
      |  |  \[___[___|   |__||  \|  |
    obj2egg.py [n##][b][t][s] filename1.obj ...
        -n regenerate normals with # degree smoothing 
            exaple -n30  (normals at less 30 degrees will be smoothed)
        -b make binarmals 
        -t make tangents
        -s show in pview
        
    licensed under WTFPL (http://sam.zoy.org/wtfpl/)  
"""
from pandac.PandaModules import * 
import math

def floats(flaot_list):
    return [ float(number) for number in flaot_list]

def ints(int_list):
    return [ int(number) for number in int_list]

def read_obj(filename):
    file = open(filename)
    egg = EggData() 
    meshName = ""
    points = []
    uvs    = []
    normals= []
    faces  = []
    idx = 0
    for line in file.readlines()+['o']:
        line = line.strip()
        if not line or "#" == line[0]:
            continue
        tokens = line.split()
        if tokens:
            if tokens[0] == "v":   
                points.append(floats(tokens[1:]))
            elif tokens[0] == "vt":  
                uvs.append(floats(tokens[1:]))
            elif tokens[0] == "vn":  
                normals.append(floats(tokens[1:]))
            elif tokens[0] == "f":
                face = [] 
                for token in tokens[1:]:
                    face.append(ints(token.split("/")))
                faces.append(face)
            elif tokens[0] == "g" or tokens[0] == "o":
                if meshName != "":
                    egn = EggGroup(meshName)
                    egg.addChild(egn)
                    evp = EggVertexPool(meshName)
                    egn.addChild(evp)
                    for face in faces:                    
                        ep = EggPolygon()
                        for vertex in face:
                            if len(vertex) == 3: 
                                iPoint, iUv, iNormal = vertex
                                ev = EggVertex()
                                point = points[iPoint-1]
                                ev.setPos(Point3D(point[0],point[1],point[2]))
                                ev.setUv(Point2D(*uvs[iUv-1][0:2]))
                                ev.setNormal(Vec3D(*normals[iNormal-1]))
                                evp.addVertex(ev)
                                ep.addVertex(ev)
                            else:
                                print vertex
                        egn.addChild(ep)
                if len(tokens) > 1 :
                    meshName = tokens[1]  
    return egg


if __name__ == "__main__":
    import getopt
    import sys,os
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hn:bs", ["help","normals","binormals","show"])
    except getopt.error, msg:
        print msg
        print __doc__
        sys.exit(2)
    show = False
    for o, a in opts:
        if o in ("-h", "--help"):
            print __doc__
            sys.exit(0)
        elif o in ("-s", "--show"):
                show = True
    for infile in args:
        try:
            if ".obj" not in infile:
                print "WARNING",infile,"does not look like a valid obj file"
                continue
            egg = read_obj(infile)
            f, e = os.path.splitext(infile)
            outfile = f+".egg"
            for o, a in opts:
                if o in ("-n", "--normals"):
                    egg.recomputeVertexNormals(float(a))
                elif o in ("-b", "--binormals"):
                    egg.recomputeTangentBinormal(GlobPattern(""))
            egg.removeUnusedVertices(GlobPattern(""))
            egg.triangulatePolygons(EggData.TConvex & EggData.TPolygon)
            egg.writeEgg(Filename(outfile))
            if show:
                os.system("pview "+outfile)
        except Exception,e:
            print e
        
1 Like

Wow, this sounds very kewl!

It would be great to have this implemented into Panda3D. (Somewhere in the egg/bam loader, so you can load .obj files directly or so)

my utility does not read mtl files which would contain texture. And obj files are not the best format. Converting it to an egg 1st is always a better option.

Updated the file for Bradamante
now can read textures and does not choke on missing normal input

from pandac.PandaModules import * 
import math

def _float(i):
    try:
        return float(i)
    except:
        return 0

def floats(flaot_list):
    return [ _float(number) for number in flaot_list]

def _int(i):
    try:
        return int(i)
    except:
        return 0

def ints(int_list):
    return [ _int(number) for number in int_list]

def read_mtl(filename):
    textures = {}
    name = "default"
    
    file = open(filename)
    for line in file.readlines():
        if not line or "#" == line[0]:
            continue
        tokens = line.split() 
        if tokens:
            if tokens[0] == "newmtl":
                name = tokens[1]
            elif tokens[0] == "map_Kd":
                textures[name] = tokens[1]
            else:
                print tokens
                
    print  textures
    return textures
                 
def read_obj(filename):
    file = open(filename)
    egg = EggData() 
    meshName = ""
    textures = {}
    texture = None
    points = []
    uvs    = []
    normals= []
    faces  = []
    idx = 0
    for line in file.readlines()+['o']:
        line = line.strip()
        if not line or "#" == line[0]:
            continue
        tokens = line.split()
        if tokens:
            if tokens[0] == "v":   
                points.append(floats(tokens[1:]))
            elif tokens[0] == "vt":  
                uvs.append(floats(tokens[1:]))
            elif tokens[0] == "vn":  
                normals.append(floats(tokens[1:]))
            elif tokens[0] == "f":
                face = [] 
                for token in tokens[1:]:
                    face.append(ints(token.split("/")))
                faces.append(face)
            elif tokens[0] == "g" or tokens[0] == "o":
                if meshName != "":
                    egn = EggGroup(meshName)
                    egg.addChild(egn)
                    if texture and texture in textures:
                        et = EggTexture(texture,textures[texture])
                    
                    evp = EggVertexPool(meshName)
                    egn.addChild(evp)
                    for face in faces:
                        ep = EggPolygon()    
                        if et: ep.addTexture(et)               
                        for vertex in face:
                            if len(vertex) == 3: 
                                iPoint, iUv, iNormal = vertex
                                ev = EggVertex()
                                point = points[iPoint-1]
                                ev.setPos(Point3D(point[0],point[1],point[2]))
                                ev.setUv(Point2D(*uvs[iUv-1][0:2]))
                                ev.setNormal(Vec3D(*normals[iNormal-1]))
                                evp.addVertex(ev)
                                ep.addVertex(ev)
                            else:
                                print vertex
                        egn.addChild(ep)
                if len(tokens) > 1 :
                    meshName = tokens[1]  
            elif tokens[0] == "mtllib":
                textures.update(read_mtl(tokens[1]))
            elif tokens[0] == "usemtl":
                texture = tokens[1]
            else:
                print tokens[0],"unkown"
    
    return egg


if __name__ == "__main__":
    import getopt
    import sys,os
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hn:bs", ["help","normals","binormals","show"])
    except getopt.error, msg:
        print msg
        print __doc__
        sys.exit(2)
    show = False
    for o, a in opts:
        if o in ("-h", "--help"):
            print __doc__
            sys.exit(0)
        elif o in ("-s", "--show"):
                show = True
    for infile in args:
        if ".obj" not in infile:
            print "WARNING",infile,"does not look like a valid obj file"
            continue
        egg = read_obj(infile)
        f, e = os.path.splitext(infile)
        outfile = f+".egg"
        for o, a in opts:
            if o in ("-n", "--normals"):
                egg.recomputeVertexNormals(float(a))
            elif o in ("-b", "--binormals"):
                egg.recomputeTangentBinormal(GlobPattern(""))
        egg.removeUnusedVertices(GlobPattern(""))
        egg.triangulatePolygons(EggData.TConvex & EggData.TPolygon)
        egg.writeEgg(Filename(outfile))
        if show:
            os.system("pview "+outfile)

Is there a new version of this wonderful little application? :slight_smile:

I have used it a little to convert my files, but I sometimes have some problems with the result.
On one object I had two textures. But inside pview only one was used, on the whole model. I solved this by splitting the object into two, but still in the same file, and everything worked just fine. I then added a third object into the file and suddenly one texture was used everywhere again, although the file contained three objects.
It feels a bit random.

The models are done in Silo, might that be why?
Can’t post an example at the moment but will do once I have access to the files again.
Thankful for any help. :slight_smile:

Edit: This turned up in a recent google session, I wonder why I missed it in the first place. Will try it when I can find some time: andyp.pfastergames.com/2008/05/2 … gg-format/

This looks like just what the doctor ordered. I’ve got an obj file I want to make into an egg, though I’ll admit it’s a bit non-standard and unfortunately this app, as is, doesn’t quite achieve what I’m looking for.

My obj is just a two point poly chain, so when you open it up it’s got some tags (v) for vertices and some tags (l) for lines. Unfortunately your app here doesn’t understand the lines, and the egg it outputs is empty.

All I really want is the vertices, with no other information.If the egg format can support 2 point polys then the lines would be nice, in case I can use them later, but they aren’t strictly necessary.

My goal is to make a bunch of vertices in my modeling app, Lightwave, and then import it into panda so I can put some other nodes at the locations of those vertices. Those nodes will be used for AI pathfinding. I don’t plan on using the vertices for anything more than their location data, I’ll just dump them when I have it. Do you think you could modify this sweet little app to shell out an egg full of vertices for me?

My obj file is here and the mtl is here, but it’s not the final one I’m going to use. I’m eventually hoping to have many of these files for many different maps in the game.

I don’t have time to solve your problem at the moment. Obj format is pretty simple and i invite you to make the required changes yourself.

I have setup a git hub account with the 3 versions. Please feel free to contribute.

github.com/treeform/obj2egg

The program I found at the end of that link you just posted actually works fine, it outputs an egg with all the verts and the lines too. So thankfully I won’t have to wrack my poor artist brain, useless thing that it is, to get what I need. Thanks a bunch Treeform, this utility saves my arse.

I am glad it worked for you!

Hey guys, I tried using this, but after I type in the command it just blinks and doesn’t do anything.

I have obj2egg.py with test.obj in the same folder, I go there in cmd and type “obj2egg.py -n30 -b -t -s test.obj”

I don’t know what I’m doing wrong.

Is there an error?

maybe try? “python obj2egg.py -n30 -b -t -s test.obj”

Does this still work?

I don’t know. I think panda3d has a native way to load obj files now.

You can in fact load .obj model files in Panda3D, starting from version 1.10. Just add  `load-file-type p3assimp`  to your config.prc file in the etc folder of your Panda3D installation, and you can then load .obj models using  `model = self.loader.load_model("my_model.obj")` .```

From https://stackoverflow.com/a/53840204

It works! Take’s an unusually long time to render tho :frowning: a few minutes for a simple grass texture