Logo Search packages:      
Sourcecode: blender version File versions

obj_import.py

#!BPY
 
"""
Name: 'Wavefront (.obj)...'
Blender: 232
Group: 'Import'
Tooltip: 'Load a Wavefront OBJ File'
"""

__author__ = "Campbell Barton"
__url__ = ["blender", "elysiun"]
__version__ = "0.9"

__bpydoc__ = """\
This script imports OBJ files to Blender.

Usage:

Run this script from "File->Import" menu and then load the desired OBJ file.
"""

# $Id: obj_import.py,v 1.17 2004/12/12 15:28:38 jiri Exp $
#
# --------------------------------------------------------------------------
# OBJ Import v0.9 by Campbell Barton (AKA Ideasman)
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------

NULL_MAT = '(null)' # Name for mesh's that have no mat set.
NULL_IMG = '(null)' # Name for mesh's that have no mat set.

MATLIMIT = 16 # This isnt about to change but probably should not be hard coded.

DIR = ''

#==============================================#
# Return directory, where is file              #
#==============================================#
def pathName(path,name):
      length=len(path)
      for CH in range(1, length):
            if path[length-CH:] == name:
                  path = path[:length-CH]
                  break
      return path

#==============================================#
# Strips the slashes from the back of a string #
#==============================================#
def stripPath(path):
      for CH in range(len(path), 0, -1):
            if path[CH-1] == "/" or path[CH-1] == "\\":
                  path = path[CH:]
                  break
      return path
      
#====================================================#
# Strips the prefix off the name before writing      #
#====================================================#
def stripName(name): # name is a string
      prefixDelimiter = '.'
      return name[ : name.find(prefixDelimiter) ]


from Blender import *

#==================================================================================#
# This gets a mat or creates one of the requested name if none exist.              #
#==================================================================================#
def getMat(matName):
      # Make a new mat
      try:
            return Material.Get(matName)
      except:
            return Material.New(matName)


#==================================================================================#
# This function sets textures defined in .mtl file                                 #
#==================================================================================#
def getImg(img_fileName):
      for i in Image.Get():
            if i.filename == img_fileName:
                  return i
      
      # if we are this far it means the image hasnt been loaded.
      try:
            return Image.Load(img_fileName)
      except:
            print "unable to open", img_fileName
            return



#==================================================================================#
# This function sets textures defined in .mtl file                                 #
#==================================================================================#
def load_mat_image(mat, img_fileName, type, mesh):
      try:
            image = Image.Load(img_fileName)
      except:
            print "unable to open", img_fileName
            return
      
      texture = Texture.New(type)
      texture.setType('Image')
      texture.image = image
      
      # adds textures to faces (Textured/Alt-Z mode)
      # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func.
      if type == 'Kd':
            for f in mesh.faces:
                  if mesh.materials[f.mat].name == mat.name:
                  
                        # the inline usemat command overides the material Image
                        if not f.image:
                          f.image = image
      
      # adds textures for materials (rendering)
      if type == 'Ka':
            mat.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.CMIR)
      if type == 'Kd':
            mat.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.COL)
      if type == 'Ks':
            mat.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC)

#==================================================================================#
# This function loads materials from .mtl file (have to be defined in obj file)    #
#==================================================================================#
def load_mtl(dir, mtl_file, mesh):
      # Remove ./
      if mtl_file[:2] == './':
            mtl_file= mtl_file[2:]
      
      mtl_fileName = dir + mtl_file
      try:
            fileLines= open(mtl_fileName, 'r').readlines()
      except:
            print "unable to open", mtl_fileName
            return
      
      lIdx=0
      while lIdx < len(fileLines):
            l = fileLines[lIdx].split()
      
            # Detect a line that will be ignored
            if len(l) == 0:
                  pass
            elif l[0] == '#' or len(l) == 0:
                  pass
            elif l[0] == 'newmtl':
                  currentMat = getMat(' '.join(l[1:]))
            elif l[0] == 'Ka':
                  currentMat.setMirCol(float(l[1]), float(l[2]), float(l[3]))
            elif l[0] == 'Kd':
                  currentMat.setRGBCol(float(l[1]), float(l[2]), float(l[3]))
            elif l[0] == 'Ks':
                  currentMat.setSpecCol(float(l[1]), float(l[2]), float(l[3]))
            elif l[0] == 'Ns':
                  currentMat.setHardness( int((float(l[1])*0.51)) )
            elif l[0] == 'd':
                  currentMat.setAlpha(float(l[1]))
            elif l[0] == 'Tr':
                  currentMat.setAlpha(float(l[1]))
            elif l[0] == 'map_Ka':
                  img_fileName = dir + l[1]
                  load_mat_image(currentMat, img_fileName, 'Ka', mesh)
            elif l[0] == 'map_Ks':
                  img_fileName = dir + l[1]
                  load_mat_image(currentMat, img_fileName, 'Ks', mesh)
            elif l[0] == 'map_Kd':
                  img_fileName = dir + l[1]
                  load_mat_image(currentMat, img_fileName, 'Kd', mesh)
            lIdx+=1

#======================================================================#
# Returns unique name of object (preserve overwriting existing meshes) #
#======================================================================#
def getUniqueName(name):
      uniqueInt = 0
      while 1:
            try:
                  ob = Object.Get(name)
                  # Okay, this is working, so lets make a new name
                  name += '.' + str(uniqueInt)
                  uniqueInt +=1
            except:
                  if NMesh.GetRaw(name) == None:
                        return name
                  else:
                        name += '.' + str(uniqueInt)
                        uniqueInt +=1

#==================================================================================#
# This loads data from .obj file                                                   #
#==================================================================================#
def load_obj(file):
      time1 = sys.time()
      def applyMat(mesh, f, mat):
            # Check weather the 16 mat limit has been met.
            if len( meshList[objectName][0].materials ) >= MATLIMIT:
                  print 'Warning, max material limit reached, using an existing material'
                  return meshList[objectName][0]
            
            mIdx = 0
            for m in meshList[objectName][0].materials:
                  if m.getName() == mat.getName():
                        break
                  mIdx+=1
            
            if mIdx == len(mesh.materials):
                  meshList[objectName][0].addMaterial(mat)
            
            f.mat = mIdx
            return f

      # Get the file name with no path or .obj
      fileName = stripName( stripPath(file) )

      mtl_fileName = ''

      DIR = pathName(file, stripPath(file))

      fileLines = open(file, 'r').readlines()



      uvMapList = [(0,0)] # store tuple uv pairs here

      # This dummy vert makes life a whole lot easier-
      # pythons index system then aligns with objs, remove later
      vertList = [NMesh.Vert(0, 0, 0)] # store tuple uv pairs here

      nullMat = getMat(NULL_MAT)
      
      currentMat = nullMat # Use this mat.
      currentImg = NULL_IMG # Null image is a string, otherwise this should be set to an image object.\
      currentSmooth = 0
      
      #==================================================================================#
      # Make split lines, ignore blenk lines or comments.                                #
      #==================================================================================#
      lIdx = 0 
      while lIdx < len(fileLines):
            fileLines[lIdx] = fileLines[lIdx].split()
            lIdx+=1
      
      #==================================================================================#
      # Load all verts first (texture verts too)                                         #
      #==================================================================================#
      lIdx = 0
      print len(fileLines)
      while lIdx < len(fileLines):
            
            l = fileLines[lIdx]
            if len(l) == 0:
                  fileLines.pop(lIdx)
                  lIdx-=1                 
                  
            elif l[0] == 'v':
                  # This is a new vert, make a new mesh
                  vertList.append( NMesh.Vert(float(l[1]), float(l[2]), float(l[3]) ) )
                  fileLines.pop(lIdx)
                  lIdx-=1

            
            # UV COORDINATE
            elif l[0] == 'vt':
                  # This is a new vert, make a new mesh
                  uvMapList.append( (float(l[1]), float(l[2])) )
                  fileLines.pop(lIdx)
                  lIdx-=1
            lIdx+=1
      
      
      # Here we store a boolean list of which verts are used or not
      # no we know weather to add them to the current mesh
      # This is an issue with global vertex indicies being translated to per mesh indicies
      # like blenders, we start with a dummy just like the vert.
      # -1 means unused, any other value refers to the local mesh index of the vert.

      # objectName has a char in front of it that determins weather its a group or object.
      # We ignore it when naming the object.
      objectName = 'omesh' # If we cant get one, use this
      meshList = {}
      meshList[objectName] = (NMesh.GetRaw(), [-1]*len(vertList)) # Mesh/meshList[objectName][1]
      meshList[objectName][0].verts.append(vertList[0])
      meshList[objectName][0].hasFaceUV(1)

      #==================================================================================#
      # Load all faces into objects, main loop                                           #
      #==================================================================================#
      lIdx = 0
      # Face and Object loading LOOP
      while lIdx < len(fileLines):
            l = fileLines[lIdx]
            
            # VERTEX
            if l[0] == 'v':
                  pass
                  
            # Comment
            if l[0] == '#':
                  pass              
            
            # VERTEX NORMAL
            elif l[0] == 'vn':
                  pass
            
            # UV COORDINATE
            elif l[0] == 'vt':
                  pass
            
            # FACE
            elif l[0] == 'f': 
                  # Make a face with the correct material.
                  f = NMesh.Face()
                  f = applyMat(meshList[objectName][0], f, currentMat)
                  f.smooth = currentSmooth
                  if currentImg != NULL_IMG: f.image = currentImg

                  # Set up vIdxLs : Verts
                  # Set up vtIdxLs : UV
                  # Start with a dummy objects so python accepts OBJs 1 is the first index.
                  vIdxLs = []
                  vtIdxLs = []
                  fHasUV = len(uvMapList)-1 # Assume the face has a UV until it sho it dosent, if there are no UV coords then this will start as 0.
                  for v in l[1:]:
                        # OBJ files can have // or / to seperate vert/texVert/normal
                        # this is a bit of a pain but we must deal with it.
                        objVert = v.split('/', -1)
                        
                        # Vert Index - OBJ supports negative index assignment (like python)
                        
                        vIdxLs.append(int(objVert[0]))
                        if fHasUV:
                              # UV
                              if len(objVert) == 1:
                                    vtIdxLs.append(int(objVert[0])) # Sticky UV coords
                              elif objVert[1] != '': # Its possible that theres no texture vert just he vert and normal eg 1//2
                                    vtIdxLs.append(int(objVert[1])) # Seperate UV coords
                              else:
                                    fHasUV = 0

                              # Dont add a UV to the face if its larger then the UV coord list
                              # The OBJ file would have to be corrupt or badly written for thi to happen
                              # but account for it anyway.
                              if len(vtIdxLs) > 0:
                                    if vtIdxLs[-1] > len(uvMapList):
                                          fHasUV = 0
                                          print 'badly written OBJ file, invalid references to UV Texture coordinates.'
                  
                  # Quads only, we could import quads using the method below but it polite to import a quad as a quad.
                  if len(vIdxLs) == 4:
                        for i in [0,1,2,3]:
                              if meshList[objectName][1][vIdxLs[i]] == -1:
                                    meshList[objectName][0].verts.append(vertList[vIdxLs[i]])
                                    f.v.append(meshList[objectName][0].verts[-1])
                                    meshList[objectName][1][vIdxLs[i]] = len(meshList[objectName][0].verts)-1
                              else:
                                    f.v.append(meshList[objectName][0].verts[meshList[objectName][1][vIdxLs[i]]])
                        
                        # UV MAPPING
                        if fHasUV:
                              f.uv.extend([uvMapList[ vtIdxLs[0] ],uvMapList[ vtIdxLs[1] ],uvMapList[ vtIdxLs[2] ],uvMapList[ vtIdxLs[3] ]])
                              #for i in [0,1,2,3]:
                              #     f.uv.append( uvMapList[ vtIdxLs[i] ] )

                        if f.v > 0:
                              f = applyMat(meshList[objectName][0], f, currentMat)
                              if currentImg != NULL_IMG:
                                    f.image = currentImg        
                              meshList[objectName][0].faces.append(f) # move the face onto the mesh
                              if len(meshList[objectName][0].faces[-1]) > 0:
                                    meshList[objectName][0].faces[-1].smooth = currentSmooth

                  elif len(vIdxLs) >= 3: # This handles tri's and fans
                        for i in range(len(vIdxLs)-2):
                              f = NMesh.Face()
                              f = applyMat(meshList[objectName][0], f, currentMat)
                              for ii in [0, i+1, i+2]:
                                    
                                    if meshList[objectName][1][vIdxLs[ii]] == -1:
                                          meshList[objectName][0].verts.append(vertList[vIdxLs[ii]])
                                          f.v.append(meshList[objectName][0].verts[-1])
                                          meshList[objectName][1][vIdxLs[ii]] = len(meshList[objectName][0].verts)-1
                                    else:
                                          f.v.append(meshList[objectName][0].verts[meshList[objectName][1][vIdxLs[ii]]])

                              # UV MAPPING
                              if fHasUV:
                                    f.uv.extend([uvMapList[ vtIdxLs[0] ], uvMapList[ vtIdxLs[i+1] ], uvMapList[ vtIdxLs[i+2] ]])

                              if f.v > 0:
                                    f = applyMat(meshList[objectName][0], f, currentMat)
                                    if currentImg != NULL_IMG:
                                          f.image = currentImg        
                                    meshList[objectName][0].faces.append(f) # move the face onto the mesh
                                    if len(meshList[objectName][0].faces[-1]) > 0:
                                          meshList[objectName][0].faces[-1].smooth = currentSmooth
            
            
            # FACE SMOOTHING
            elif l[0] == 's':
                  if l[1] == 'off': currentSmooth = 0
                  else: currentSmooth = 1
                  # print "smoothing", currentSmooth

            # OBJECT / GROUP
            elif l[0] == 'o' or l[0] == 'g':
                  # This makes sure that if an object and a group have the same name then
                  # they are not put into the same object.
                  
                  # Only make a new group.object name if the verts in the existing object have been used, this is obscure
                  # but some files face groups seperating verts and faces which results in silly things. (no groups have names.)
                  if len(l) == 1 and len( meshList[objectName][0].faces ) == 0:
                        pass
                  
                  else:
                        newObjectName = l[0] + '_'                      
                        
                        # if there is no groups name then make gp_1, gp_2, gp_100 etc
                        
                        if len(l) == 1: # No name given, make a unique name up.
                              
                              unique_count = 0
                              while newObjectName in meshList.keys():
                                    newObjectName = l[0] + '_' + str(unique_count)
                                    unique_count +=1
                        else: # The the object/group name given
                              newObjectName += '_'.join(l[1:])
                        
                        # Assign the new name
                        objectName = newObjectName
                              
                        # If we havnt written to this mesh before then do so.
                        # if we have then we'll just keep appending to it, this is required for soem files.
                        if objectName not in meshList.keys():
                              meshList[objectName] = (NMesh.GetRaw(), [-1]*len(vertList))
                              meshList[objectName][0].hasFaceUV(1)
                              meshList[objectName][0].verts.append( vertList[0] )
                        

            # MATERIAL
            elif l[0] == 'usemtl':
                  if l[1] == '(null)':
                        currentMat = getMat(NULL_MAT)
                  else:
                        currentMat = getMat(' '.join(l[1:])) # Use join in case of spaces
            
            # MATERIAL
            elif l[0] == 'usemat':
                  if l[1] == '(null)':
                        currentImg = NULL_IMG
                  else:
                        currentImg = getImg(DIR + ' '.join(l[1:])) # Use join in case of spaces 
            
            # MATERIAL FILE
            elif l[0] == 'mtllib':
                  mtl_fileName = ' '.join(l[1:])
            
            lIdx+=1

      
      #==============================================#
      # Write all meshs in the dictionary            #
      #==============================================#  
      for mk in meshList.keys():
            # Applies material properties to materials alredy on the mesh as well as Textures.
            if mtl_fileName != '':
                  load_mtl(DIR, mtl_fileName, meshList[mk][0])
            if len(meshList[mk][0].verts) >1:
                  meshList[mk][0].verts.pop(0)
                  
                  name = getUniqueName(mk)
                  ob = NMesh.PutRaw(meshList[mk][0], name)
                  ob.name = name

      print "obj import time: ", sys.time() - time1

Window.FileSelector(load_obj, 'Import Wavefront OBJ')


# For testing compatibility
#import os
#for obj in os.listdir('/obj/'):
#     if obj[-3:] == 'obj':
#           print obj
#           newScn = Scene.New(obj)
#           newScn.makeCurrent()
#           load_obj('/obj/' + obj)


Generated by  Doxygen 1.6.0   Back to index