Problem with OBJ file importing

Hi All

I’ve been lurking for a while now, but a problem that is driving me crazy has prompted my first post!

I am importing .obj files from Poser into Blender. I am using the obj_io_modif232.py script. Since Poser obj files do not come with a .mtl file (the material specs are in the .pz3 files), I have had to move the line “material = Material.New(name)” from addGlobalMaterial into addMeshMaterial.

This works fine the first time the script is run, however when it is run a second time it crashes Blender. Even more strangely, if I # out the Material.New line from the script the second time it is run, it still crashes.

Anyone have any clues?

Edit: If I delete the mesh created in the first import, then the second import works ok. So a suitable workaround would be if I could delete the mesh in the script, however I haven’t been able to work that out.

Crashes are bad. You can post a bug report either in the tracker or to the python API mailing list.

Martin

Thanks for the response.

I’ve still had no luck fixing the problem.

Is there any way to how a script automatically run when a .blend file is loaded? This would get around my problem.

Can you post links to the script and file you are running?

This is the script I am running - it’s tailored to load Poser based .obj files, but run it and load any obj file that has multiple material groups (usemtl) - do the import twice and Blender will crash. If you # out the Material.New(Name) it doesn’t crash.

#!BPY
"""Registration info for Blender menus:
Name: 'OBJ...'
Blender: 232
Group: 'Import'
Tip: 'Import Wavefront File Format (*.obj)'
"""

#=========================================================================
# Wavefront OBJ Importer/Exporter v1.2
# This is a complete OBJ importer and exporter script
# All Rights Reserved
# [email protected] 


#Version mise a jour pour Blender 228 par jm soler
# 

#=========================================================================
# DESCRIPTION: This script allows for full importing and exporting of
#      .obj files. uv texture coordinates and normals can be exported and
#      imported. .obj groups and materials can also be converted to and
#      from material indexes in Blender.
#
# INSTALLATION:
#      You need the FULL python installation to run this script. You can
#      down load the latest version of PYTHON from http://www.python.org.
#
# INSTRUCTIONS (You definitely want to read this!): 
#      Once the script is loaded in Blender, hit alt-p. This will bring up
#      the main interface panel. You will have a choise of exporting or
#      importing an .obj module. If you are exporting you must have at least
#      one mesh selected in Blender, or you will get an error.  
#      You can change the export filename by entering the path and filename
#      in the dialog.  If you do not enter a path, the path will default to
#      your blender directory. You can change the default path in the script <==== NOTE
#      itself by modifying the variable 'Filename' at the top of the script.  
#
#    IMPORTING:
#      If your OBJ model has uv mapping coordinates, and you want to use them  <===== NOTE 
#      in Blender, you can access them in two ways. The best way is through Blender's
#      realtime UV coordinates which you enable simply by selecting the UV option in
#      the material edit window. This gives you an exact version of the uv coordinates.
#      An older method is to select the "stick" option in the material edit window. I
#      really don't know why anyone would want to use this option since it cannot handle
#      seams and texture overlap, but I left it in just in case someone could use it for
#      something.
#     
#      If your OBJ contains groups, once it has been imported, it may still appear
#      to lack any material indexes. If this happens, it can be remedied      <=== NOTE
#      by going to the mesh editor window, clicking on the mesh selection button, and
#      reselecting the mesh you have just imported. You will now have as many 
#      materials attached to your object as there are groups. You can then select 
#      different groups by doing a material select when you are in edit mode. 
#
#      Finally, you might have problems with certain parts of the object not displaying
#      after you go in and out of edit mode the first time. To fix this, simply go into
#      edit mode again, and select the "remove doubles" option.
#
#
# HISTORY:
#   Nov 13, 2001: Initial Release
#   Nov 16, 2001: Version 1.1 - no longer need to pre-define dummy materials
#   Dec 13, 2001: Version 1.2 - now imports into realtime UV (the UV button in the material edit window), and
#       exports realtime UV. This format is more compatible with the native .OBJ uv format. Should eliminate
#       texture misalignments and seams. Simply press the UV button in the material edit window after importing.
#
#  GetRaw
#================================


# ===============================
#   Setup our runtime constants
# ===============================

DEBUG=1         #Set this to "1" to see extra messages
MESHVERSION=3   # If the export file doesn't work,
FILEVERSION=3   # try changing these to "2"

EVENT_PATHCHANGE=     1
EVENT_IMPORT=         2
EVENT_IMPORT_CONT=    3
EVENT_OPTIONS=        4
EVENT_EXPORT=         7
EVENT_EXPORT_CHK=     5
EVENT_EXPORT_CANCEL=  6
EVENT_QUIT=           8
EVENT_EXPORT_ERR=     9
EVENT_TYPE=           10
EVENT_DONE=           11
EVENT_IMPORT_SELECT=  12

# ===============================
# Import our libraries
# ===============================

#import string
#import os
#import struct

try:
    import nt
    os=nt
    os.sep='\\'
except:    
    import posix
    os=posix
    os.sep='/'

def isdir(path):
    try:
        st = os.stat(path)
        return 1 
    except:
        return 0
    
def split(pathname):
         k0=pathname.split(os.sep)
         directory=pathname.replace(k0[len(k0)-1],'')
         Name=k0[len(k0)-1]
         return directory, Name
        
def join(l0,l1):        
     return  l0+os.sep+l1
    
os.isdir=isdir
os.split=split
os.join=join

import math
import Blender
#import Blender210
from Blender import *
from Blender import Types, Object, NMesh, Camera, Lamp
from Blender.Draw import *
from Blender.BGL import *
from Blender import Material
from Blender import Window
from math import pi


# ===============================
# Input Variables
# ===============================

Filename = "C:\\Program Files\\Curious Labs\\Poser4\\Runtime\\Geometries\
ewMaleCasHi\
ewMaleCasHi.obj"

gFilename=Create(Filename)
gAlert   = 0
gNewMeshCreated = 0
type     = 1
exporttype = 1
returncode = 0
operation = "Export"
center = [0,0,0]
rotation = [0,0,0]
Transform = []
multiflag = 0

#================================
# def Fileselect function:
#================================
def ImportFunctionselet(filename):
             global gFilename 
             global ExportOptions
             global ExportType
             global type
             global exporttype
             global operation
             global gAlert
             gFilename.val=filename
             ImportFunction(filename, type)
             operation = "Import"

#================================
def ExitGUI ():
#================================
    Exit()

#================================
def EventGUI (event):
#================================
      global gFilename 
      global ExportOptions
      global ExportType
      global type
      global exporttype
      global operation
      global gAlert

      if (event==EVENT_IMPORT):
         ImportFunction(gFilename.val, type)
         operation = "Import"

      if (event==EVENT_IMPORT_SELECT):         
         Window.FileSelector (ImportFunctionselet, 'IMPORT FILE')


      if (event==EVENT_IMPORT_CONT):
         gAlert = 0
         operation = "Import"
         Draw ()

      if (event==EVENT_EXPORT):
         #ExportFunction(gFilename.val, type)
         operation = "Export"

      if (event==EVENT_EXPORT_CHK):
         #ExportFunctionOK(gFilename.val, type)
         Draw ()
      if (event==EVENT_EXPORT_CANCEL):
         gAlert = 0
         Draw ()
      if (event==EVENT_OPTIONS):
         type = ExportOptions.val
         Draw ()
      if (event==EVENT_TYPE):
         exporttype = ExportType.val
         Draw ()
      if (event==EVENT_EXPORT_ERR):
         gAlert = 0
         Draw ()
      if (event==EVENT_DONE):  
         gAlert = 0
         Draw ()
      if (event==EVENT_QUIT):
         ExitGUI()

#================================
def DrawGUI():
#================================
      global type
      global exporttype
      global operation 

      glClearColor (0.6,0.6,0.6,0)
      glClear (GL_COLOR_BUFFER_BIT)

      global gFilename
      global gAlert
      global ExportOptions
      global ExportType

      if (gAlert==0):
         # Add in the copyright notice and title
         glRasterPos2d(32, 380)
         Text("Wavefront OBJ Importer/Exporter")
         glRasterPos2d(32, 350)
         Text("Copyright (C) Chris Lynch 2001")

         gFilename=String ("Filename: ",EVENT_PATHCHANGE,32,250,320,32,gFilename.val,255,"Full pathname and filename")
         #Button ("Export",EVENT_EXPORT,32,200,100,32)
         Button ("Import",EVENT_IMPORT,252,200,100,32)
         Button ("Select Import",EVENT_IMPORT_SELECT,355,200,100,32)
         glRasterPos2d(32, 165)
         #Text("Select Export Options:")
         #options = "Export Options %t| Default %x1| Material Layers %x2| Obj Groups %x3| Standard %x4"
         #ExportOptions = Menu (options,EVENT_OPTIONS,200,150,150,32, type)
         Button ("Done",EVENT_QUIT,142,50,100,32)
         glRasterPos2d(32, 115)
         #Text("Export using ")
         #options = "Export Type %t| Mesh Coordinates %x1| Object Coordinates %x2"
         #ExportType = Menu (options,EVENT_TYPE,170,100,180,32, exporttype)
         Button ("Done",EVENT_QUIT,142,50,100,32)

      elif (gAlert==1):
         glRasterPos2i (32,250)
         Text (gFilename.val+ " already exists. Save anyway?")
         Button ("Save",EVENT_EXPORT_CHK,150,200,50,32)
         Button ("Cancel",EVENT_EXPORT_CANCEL,250,200,50,32)
         gAlert = 0
      elif (gAlert==2):
         glRasterPos2i (32,250)
         Text (gFilename.val+ " cannot be found. Check directory and filename.")
         Button ("Continue",EVENT_IMPORT_CONT,32,190,70,32) 
         gAlert = 0
      elif gAlert == 3:
         glRasterPos2i (32,250)
         Text ("No objects selected to export. You must select one or more objects.")
         Button ("Continue",EVENT_EXPORT_ERR,192,200,70,32)
         gAlert = 0
      elif gAlert == 5:
         glRasterPos2i (32,250)
         Text ("Invalid directory path.")
         Button ("Continue",EVENT_EXPORT_ERR,192,200,70,32)
         gAlert = 0
      elif gAlert == 6:
         glRasterPos2i (32,250)
         Text ("Importing. Please wait....")
      else:
         glRasterPos2i (32,250)
         Text (str(operation)+ " of " +str(gFilename.val)+ " done.")
         Button ("Continue",EVENT_DONE,192,200,70,32)
         
#================================
def RegisterGUI ():
#================================
    Register (DrawGUI,None,EventGUI)

#================================
# MAIN SCRIPT
#================================
# Opens a file, writes data in it
# and closes it up.
#================================
RegisterGUI()

#================================
def ImportFunction (importName, type):
#================================       
      global gFilename 
      global gAlert

      gAlert = 6
      Draw ()

      try:
         FILE=open (importName,"r")
         directory, Name = os.split(gFilename.val)

         words = Name.split(".")
         Name = words[0]
         ObjImport(FILE, Name, gFilename.val) 
         FILE.close()

         # Rotate the mesh
         print "Rotating mesh"
         ob = Object.Get('Mesh')
         ob.setEuler(pi/2, 0, 0)

         # Position the camera
         print "Positioning camera"
         camera = Object.Get('Camera')
         camera.setLocation(0.0, -1.5, 0.4)
         camera.setEuler(pi/2, 0, 0)
         camera.setSize(0.2, 0.2, 0.2)

         # Setup the lamp
         print "Setting lamp"
         lamp = Object.Get('Lamp')
         lamp.setLocation(0.0, -5.0, 0.5)
         
         gAlert = 4
         Draw ()
      except IOError:
         gAlert=2
         Draw ()

#=========================
def ObjImport(file, Name, filename):
#=========================   
    global gNewMeshCreated

    vcount     = 0
    vncount    = 0
    vtcount    = 0
    fcount     = 0
    gcount     = 0
    setcount   = 0
    groupflag  = 0
    objectflag = 0
    mtlflag    = 0
    baseindex  = 0
    basevtcount = 0
    basevncount = 0
    matindex   = 0

    pointList    = []
    uvList       = []
    normalList   = []
    faceList     = []
    materialList = []
    imagelist    = []
    
    uv = [] 
    lines = file.readlines()
    linenumber = 1

    for line in lines:
        words = line.split()
        if words and words[0] == "#":
            pass # ignore comments
        elif words and words[0] == "v":
            vcount = vcount + 1
            x = float(words[1])
            y = float(words[2])
            z = float(words[3])
            pointList.append([x, y, z])

        elif words and words[0] == "vt":
            vtcount = vtcount + 1
            u = float(words[1])
            v = float(words[2])
            uvList.append([u, v])

        elif words and words[0] == "vn":
            vncount = vncount + 1
            i = float(words[1])
            j = float(words[2])
            k = float(words[3])
            normalList.append([i, j, k])

        elif words and words[0] == "f":
            fcount = fcount + 1
            vi = [] # vertex  indices
            ti = [] # texture indices
            ni = [] # normal  indices
            words = words[1:]
            lcount = len(words)
            for index in (xrange(lcount)):
               if words[index].find( "/") == -1:
                     vindex = int(words[index])
                     if vindex < 0: vindex = baseindex + vindex + 1  
                     vi.append(vindex)
               else:
                   vtn = words[index].split( "/")
                   vindex = int(vtn[0])
                   if vindex < 0: vindex = baseindex + vindex + 1 
                   vi.append(vindex) 
            
                   if len(vtn) > 1 and vtn[1]:
                      tindex = int(vtn[1])
                      if tindex < 0: tindex = basevtcount +tindex + 1
                      ti.append(tindex)

                   if len(vtn) > 2 and vtn[2]:
                      nindex = int(vtn[2])
                      if nindex < 0: nindex = basevncount +nindex + 1
                      ni.append(nindex)
            faceList.append([vi, ti, ni, matindex])

        elif words and words[0] == "o":
            ObjectName = words[1]
            objectflag = 1
            #print "Name is %s" % ObjectName

        elif words and words[0] == "g":
            groupflag = 1
            index = len(words)
            if objectflag == 0:
               objectflag = 1
               if index > 1:
                  ObjectName = words[1].join("_")
                  GroupName = words[1].join("_") 
               else:
                  ObjectName = "Default" 
                  GroupName = "Default" 
               #print "Object name is %s" % ObjectName
               #print "Group name is %s" % GroupName
            else:
               if index > 1:
                  GroupName = join(words[1],"_") 
               else:
                  GroupName = "Default" 
               #print "Group name is %s" % GroupName
                  
#            if mtlflag == 0:
#               matindex = AddMeshMaterial(GroupName,materialList, matindex)
            gcount = gcount + 1 
               
            if fcount > 0: 
               baseindex = vcount
               basevncount = vncount
               basevtcount = vtcount

        elif words and words[0] == "mtllib":
            # try to export materials
            print  "Ignoring mtllib ",filename
                 
        elif words and words[0] == "usemtl":
#            if mtlflag == 1:
            name = words[1]
            matindex = AddMeshMaterial(name, materialList, matindex) 
        elif words:   
            print "%s: %s" % (linenumber, words)
        linenumber = linenumber + 1
    file.close()

    # import in Blender
 
    print "import into Blender ..."
    mesh   = NMesh.GetRaw ()

    i = 0
    while i < vcount:
      x, y, z = pointList[i] 
      vert=NMesh.Vert(x, y, z)
      mesh.verts.append(vert)
      i=i+1

    if vtcount > 0:
       #mesh.hasFaceUV() = 1
       print ("Object has uv coordinates")
 
    if len(materialList) > 0:
       for m in materialList:
          print m
          try:
            M=Material.Get(m)
            print M
            mesh.materials.append(M) 
            print "SetMode " + str(M.getMode())
            M.setMode('TexFace')
          except:
            print "Material.Get failed"
            pass

    total = len(faceList)
    i = 0

    for f in faceList:
        if i%1000 == 0:
          print ("Progress = "+ str(i)+"/"+ str(total))

        i = i + 1
        vi, ti, ni, matindex = f
        face=NMesh.Face()
        if len(materialList) > 0:
           face.mat = matindex

        limit = len(vi)
        setcount = setcount + len(vi)
        c = 0    
    
        while c < limit:
          m = vi[c]-1
          if vtcount > 0 and len(ti) > c:
             n = ti[c]-1
          if vncount > 0 and len(ni) > c:
             p = ni[c]-1

          if vtcount > 0:
             try:
                  u, v = uvList[n]
             except:
                  pass 

             """ 
        #  multiply uv coordinates by 2 and add 1. Apparently blender uses uv range of 1 to 3 (not 0 to 1). 
             mesh.verts[m].uvco[0] = (u*2)+1
             mesh.verts[m].uvco[1] = (v*2)+1
            """

          if vncount > 0:
             if p > len(normalList):
                print("normal len = " +str(len(normalList))+ " vector len = " +str(len(pointList)))
                print("p = " +str(p))
             x, y, z = normalList[p]  
             mesh.verts[m].no[0] = x
             mesh.verts[m].no[1] = y
             mesh.verts[m].no[2] = z
          c = c+1  
      
        if len(vi) < 5:
          for index in vi:
            face.v.append (mesh.verts[index-1])
  
          if vtcount > 0:  
            for index in ti:
               u, v = uvList[index-1]
               face.uv.append((u,v))
               
            if len(imagelist)>0:
                face.image=imagelist[0]
                #print
                
          if vcount>0:
             face.smooth=1

          mesh.faces.append(face) 

    print "all other (general) polygons ..."
    for f in faceList:
        vi, ti, ni, matindex = f 
        if len(vi) > 4:
            # export the polygon as edges
            print ("Odd face, vertices = "+ str(len(vi)))
            for i in range(len(vi)-2):
               face = NMesh.Face()
               if len(materialList) > 0:
                  face.mat = matindex
               face.v.append(mesh.verts[vi[0]-1])
               face.v.append(mesh.verts[vi[i+1]-1])
               face.v.append(mesh.verts[vi[i+2]-1])

               if vtcount > 0: 
                  if len(ti) > i+2:
                     u, v = uvList[ti[0]-1]
                     face.uv.append((u,v))
                     u, v = uvList[ti[i+1]-1]
                     face.uv.append((u,v))
                     u, v = uvList[ti[i+2]-1]
                     face.uv.append((u,v))

               mesh.faces.append(face)
      
    #print "Object = "
    print "Doing PutRaw with name = " + Name
    NMesh.PutRaw(mesh, Name,1)

    print ("Total number of vertices is "+ str(vcount))
    print ("Total number of faces is "+ str(len(faceList)))
    print ("Total number of sets is "+ str(setcount))


    print("Finished importing " +str(Name)+ ".obj")

#=========================================
def AddMeshMaterial(name, materialList, matindex):
#=========================================
    
   index = 0
   found = 0 
   limit = len(materialList)

   while index < limit:
     if materialList[index] == name:
        matindex = index 
        found = 1
        index = limit
     index = index + 1
   
   if found == 0:
      materialList.append(name)
      matindex = len(materialList)-1

      # Now add a new material
      Material.New(name)
        
   return matindex

This may be of some help to you, its a script that allows for obj import AND export.

http://www.zoo-logique.org/3D.Blender/scripts_python/obj_io_modif228.py

I’ve have personally used it to do morph targets for Poser figures when I had poser 5 and it worked like a charm. The only problem was getting it to do more than one body part, which I never found out how to do.

If you figure out to do a full body morph target with Blender, please email me, my email is [email protected]

Best Wishes.

Last version !
http://jmsoler.free.fr/util/blenderfile/py/obj_io_modif236e.py

Whats missing from Blenders packeged Importer/Exporter that stops you from using them?

I have tested the OBJ I/O with many files- batch imported over 100 for testing and try to keep a diverse selection of obj files in that selection.

If I can add your file it might help me keep compatibility with other obj’s. could you please post a link to your file?

Speed : io_obj is 10 time faster (at least) .

The .obj import/exporters that come with Blender are not very good. I have all kinds of problems with them. I am just too busy/lazy to add bug reports to the bug database. JMS script is the one I use mostly and I don’t have many problems, although the occasionaly zbrush model won’t import…

JMS: I banchmarked our scripts.
My Exporter is a little over twice as fast as yours.
But my importer is 10 times slower.
However it is more compatible. - I found some models that mine could import but yours couldent,

Your obj exporter is quite buggy.

First of all it uses Object.Get() which will export all selected objects in ALL SCENES.

2nd - For scenes where object selection state isnt initialzed it throws an error.
When I first tried your exporter (2.36e) I got the error.

Around line 900


  Objects = [o for o  in Blender.Object.Get() 

           if o.sel==1 and o.getType()== 'Mesh' 

              and (o.getData().faces!=[] or onlysegment(o))] 

For scenes where selections arnt initialized (non active scenes) this raises an error


Writing povexport_jms...
Traceback (most recent call last):
  File "<string>", line 246, in EventGUI
  File "<string>", line 392, in ExportFunction
  File "<string>", line 411, in ExportFunctionOK
  File "<string>", line 904, in ObjExport
RuntimeError: Internal error: could not find objects selection state.

The correct way to do this is.


for ob in [object for object in Scene.GetCurrent().getChildren() if object.sel]
# or just use
for ob in Object.GetSelected()

3rd - Your obj importer does not import all obj files. I have a list of OBJs I use to test compatibility. I didnt make any of them, there all downloaded off the net from various places, and some have different problems.

Try importing
http://www.esil.univ-mrs.fr/~jlmari/Enseignement/Online/OBJ/womanhead.obj
Can also be found in this archive
http://andrewd.ces.clemson.edu/courses/mobj/data/

Error is… (Line numbers will be a bit out since I added 2 lines to print the time)


index :  2 , words :  ['g', 'eyebrow']
index :  2 , words :  ['g', 'head']
index :  2 , words :  ['g', 'pupil']
index :  2 , words :  ['g', 'head']
index :  2 , words :  ['g', 'lips']
index :  2 , words :  ['g', 'teeth']
index :  2 , words :  ['g', 'head']
index :  2 , words :  ['g', 'lips']
index :  2 , words :  ['g', 'iris']
import into Blender ...
materialList ['default_', 'head_', 'pupil_', 'eyebrow_', 'iris_', 'eyewhite_', 'lips_', 'teeth_']
Progress = 0/2940
Traceback (most recent call last):
  File "<string>", line 281, in DrawGUI
  File "<string>", line 213, in ImportFunctionselet
  File "<string>", line 364, in ImportFunction
  File "<string>", line 771, in ObjImport
UnboundLocalError: local variable 'p' referenced before assignment

fails on


          if vncount > 0:

             if p > len(normalList): # <--------------- This line

                print("normal len = " +str(len(normalList))+ " vector len = " +str(len(pointList)))

                print("p = " +str(p))

             x, y, z = normalList[p]  

             mesh.verts[m].no[0] = x

             mesh.verts[m].no[1] = y

             mesh.verts[m].no[2] = z

          c = c+1  

Anyhow since I see where my importer is slow, Ill fix it up for 2.37a, a little competition is good Eh?

Too funny : you should read the message just above your’s .

It is not my script but an old one written by chris lynch a long time ago . I just made a few modifs to use it with blender 2.28 and read zbrush file and so on …

This file seems to have a problem with normal coords . And rhe obj_io can read it now .

in fact, faces with more than 4 vertices are not created with smoothness . The obj_io needs improvement, it’s clear . But i have no time for this and with a little luck the obj_import script will be quite usable soon .

new version :
http://jmsoler.free.fr/util/blenderfile/py/obj_io_modif236f.py